Commit 5efa07b9 authored by gijs's avatar gijs

Merge branch 'features-alex' of gitlab.constantvzw.org:osp/work.w

parents d14da9c7 9abe67fb
......@@ -51,6 +51,11 @@ INSTALLED_APPS = [
'playground',
'ckeditor',
# Django filer
'easy_thumbnails',
'filer',
'mptt',
]
MIDDLEWARE = [
......@@ -143,7 +148,7 @@ REST_FRAMEWORK = {
# Django Compressor setup
COMPRESS_PRECOMPILERS = (
('text/css', './node_modules/.bin/postcss {infile} --use postcss-cssnext --use postcss-import'),
('text/css', './node_modules/.bin/postcss {infile} --use postcss-cssnext --use postcss-import --use postcss-selector-matches'),
)
COMPRESS_ENABLED = False
......@@ -159,11 +164,11 @@ ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = "/compte"
CKEDITOR_CONFIGS = {
'default': {
'extraPlugins': ','.join([
'codesnippet',
'codesnippet', 'detail',
]),
'toolbar': 'Custom',
'toolbar_Custom': [
['Format'],
['Format', 'Detail'],
# ['Pull-out', 'Sidebar', 'Footer'],
['Bold', 'Italic', '-', 'Subscript', 'Superscript'],
['CodeSnippet'],
......
......@@ -4,19 +4,57 @@
<head>
<title>{{ flatpage.title }}</title>
<link rel="stylesheet" href="{% static 'playground/vendors/reset.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'playground/css/styles.css' %}">
{% compress css %}
<link rel="stylesheet" type="text/css" href="{% static 'playground/css/styles.css' %}">
{% endcompress %}
</head>
<body class="help__content">
<body class="help__content content">
{% load flatpages %}
{% get_flatpages as flatpages %}
<ul>
{% for page in flatpages|dictsort:"title" %}
<li><a href="/pages/{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
<!-- <ul> -->
<!-- {% for page in flatpages|dictsort:"title" %} -->
<!-- <li><a href="/pages/{{ page.url }}">{{ page.title }}</a></li> -->
<!-- {% endfor %} -->
<!-- </ul> -->
{{ flatpage.content }}
<script src="{% static 'playground/vendors/jquery.min.js' %}"></script>
<script charset="utf-8">
$('h3').each(function() {
$(this)
.click(function() { $(this).toggleClass('help__section--closed'); })
.addClass('help__section help__section--closed')
.nextUntil("h3, h2, h1")
.wrapAll('<div class="help__inner"></div>');
});
// Taken from <https://gist.github.com/mathewbyrne/1280286>
function slugify(text) {
return text
.toString()
.toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, "") // strip accents
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}
var $toc = $('<ol class="help__toc"></ol>')
$("h2").each(function() {
var txt = $(this).text();
var id = slugify(txt);
$(this).attr('id', id);
var $li = $('<li></li>');
var $a = $('<a></a>').attr('href', '#' + id).text(txt);
$li.append($a);
$toc.append($li);
});
$toc.insertBefore($("h2").first());
</script>
</body>
</html>
......@@ -29,6 +29,7 @@ router = routers.DefaultRouter()
router.register(r'attachments', views.AttachmentViewSet)
router.register(r'scores', views.ScoreViewSet)
router.register(r'users', views.UserViewSet)
router.register(r'pages', views.FlatPageViewSet)
urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + [
......@@ -41,6 +42,8 @@ urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + [
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^filer/', include('filer.urls')),
url(r'pages/', include('django.contrib.flatpages.urls')),
url(r'^', include('playground.urls')),
......
......@@ -6,9 +6,18 @@ from taggit_serializer.serializers import (TagListSerializerField,
TaggitSerializer)
from guardian.shortcuts import assign_perm, get_users_with_perms, get_user_perms, remove_perm
from django.contrib.auth.models import User
from django.contrib.flatpages.models import FlatPage
class FlatPageSerializer(serializers.HyperlinkedModelSerializer):
# id = serializers.ReadOnlyField()
class Meta:
model = FlatPage
fields = ['url', 'title', 'content']
class UserSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
......@@ -43,6 +52,7 @@ class PermissionSerializer(serializers.Serializer):
list_serializer_class = PermissionListSerializer
class ScoreSerializer(TaggitSerializer, serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
tags = TagListSerializerField()
......@@ -52,9 +62,10 @@ class ScoreSerializer(TaggitSerializer, serializers.HyperlinkedModelSerializer):
model = Score
fields = '__all__'
def create(self, request, *args, **kwargs):
instance = super().create(request, *args, **kwargs)
assign_perm("view_score", request.user, instance)
def create(self, validated_data):
validated_data.pop('permissions', None)
instance = Score.objects.create(**validated_data)
assign_perm("view_score", self.context['request'].user, instance)
return instance
def update(self, instance, validated_data):
......
Copyright (c) 2018 Ayhan Akilli
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# Details Widget
This widget handles details elements (`<details>` and `<summary>`). No configuration necessary.
## Example
You can see this plugin in action @ https://akilli.github.io/rte/ck4/
'use strict';
(function (CKEDITOR) {
CKEDITOR.plugins.setLang('detail', 'de', {
title: 'Details'
});
})(CKEDITOR);
'use strict';
(function (CKEDITOR) {
CKEDITOR.plugins.setLang('detail', 'en', {
title: 'Details'
});
})(CKEDITOR);
'use strict';
(function (CKEDITOR) {
CKEDITOR.dtd['$editable']['summary'] = 1;
CKEDITOR.plugins.add('detail', {
requires: 'widget',
icons: 'detail',
hidpi: true,
lang: 'de,en',
init: function (editor) {
editor.widgets.add('detail', {
button: editor.lang.detail.title,
template: '<details><summary>Summary</summary><div class="details-content"></div></details>',
editables: {
summary: {
selector: 'summary',
allowedContent: {}
},
content: {
selector: '.details-content'
}
},
allowedContent: 'details summary',
requiredContent: 'details; summary',
upcast: function (el) {
if (el.name !== 'details') {
return false;
}
var summary = el.getFirst('summary');
var content = new CKEDITOR.htmlParser.element('div', {'class': 'details-content'});
if (!!summary && summary.children.length > 0 && summary.children[0].type === CKEDITOR.NODE_ELEMENT) {
summary.setHtml(summary.children[0].getHtml());
} else if (!!summary && summary.children.length > 0 && summary.children[0].type === CKEDITOR.NODE_TEXT) {
summary.setHtml(summary.children[0].value);
} else if (!!summary) {
summary.setHtml('Summary');
} else {
summary = new CKEDITOR.htmlParser.element('summary');
summary.setHtml('Summary');
el.add(summary, 0);
}
el.add(content, 1);
if (el.children.length > 2) {
content.children = el.children.slice(2);
el.children = el.children.slice(0, 2);
}
return true;
},
downcast: function (el) {
el.attributes = [];
el.children = el.children.slice(0, 1);
el.setHtml(el.getHtml() + this.editables.content.getData());
},
init: function () {
var summary = this.element.getChild(0);
summary.on('keyup', function (ev) {
if (ev.data['$'].keyCode === 32) {
ev.data['$'].preventDefault();
editor.insertText(' ');
}
});
}
});
}
});
})(CKEDITOR);
......@@ -150,8 +150,7 @@ textarea {
button,
input {
font-family: 'GothicA1', sans-serif;
/* font-size: 19px; */
/* font-size: 90%; */
font-size: inherit;
line-height: 21px;
font-weight: 500;
}
......@@ -243,6 +242,7 @@ section.hidden { display: none; }
.modal__title { margin-bottom: 1em; }
/* TODO: Bemize */
.modal label { display: none; }
.modal input[type="email"],
......@@ -292,15 +292,13 @@ section.hidden { display: none; }
/* Main menu (sidebar)
========================================================================== */
.main-header .content {
/* width: 300px; */
padding: 30px;
}
.main-header { width: 300px; }
.main-header.is-collapsed { margin-left: -300px; }
.logo { margin: 21px 0 42px 0; }
.main-header__logo {
margin-top: 21px;
margin-bottom: 42px;
}
.logo__name {
font-size: 3em;
......@@ -1223,7 +1221,7 @@ li .icon--tag { color: white } */
color: rgb(202, 027, 027);
}
#create { margin-bottom: 1em;}
/* #create { margin-top: 1em;} */
.create-form { display: none; }
#create:hover .create-form { display: flex; }
......@@ -1258,6 +1256,8 @@ li .icon--tag { color: white } */
border-bottom: 2px solid;
padding-bottom: 6px;
/* margin-bottom: 18px; */
flex: 1 1 100px;
width: 100px;
}
.create-form input[type="text"]::placeholder { /* Most modern browsers support this now. */
......@@ -1273,28 +1273,87 @@ li .icon--tag { color: white } */
Help
========================================================================== */
.page__help { width: 500px; }
.page__help.is-collapsed { margin-right: -500px; }
.help__frame {
overflow: auto;
width: 500px;
width: 100%;
height: 100%;
transition: all .2s;
}
.help { width: 500px; }
.help.is-collapsed { margin-right: -500px; }
.help__content {
color: #173a42;
background-color: white;
padding: 30px;
font-size: 85%;
line-height: 1.35;
}
.help__content h2 { margin-top: 28pt; text-align: center; margin-bottom: 14pt; }
.help__content h2 {
/* position: sticky; */
/* background-color: white; */
/* top: 0; */
letter-spacing: 1px;
text-transform: uppercase;
}
.help__content h2,
.help__content h3 {
font-weight: bold;
padding-top: 28pt;
padding-bottom: 14pt;
}
.help__content p + p { margin-top: 14pt; }
.help__content h2:first-of-type { padding-top: 0; }
.help__content :matches(p, ul, ol, pre) + :matches(p, ul, ol, pre) { margin-top: 14pt; }
.help__content :matches(ul, ol) { margin-left: 2em; text-indent: -.75em; }
.help__content li:before { content: "– "; }
.help__content code { color: darkgrey; margin-left: 1.25em; font-size: 75%}
.help__section::before { content: "▼ "; }
.help__section--closed::before { content: "▶ "; }
.help__section--closed + .help__inner { display: none; }
.help__content img,
.help__content pre {
overflow: hidden;
max-width: 100%;
}
.help__toc {
/* position: sticky; */
/* top: 0; */
/* background-color: white; */
border-bottom: 2px solid;
margin-left: 84pt!important;
padding-bottom: 14pt;
/* padding-top: 14pt; */
margin-bottom: 28pt;
text-indent: 0!important;
font-weight: bold;
/* background-color: #173a42; */
/* color: white; */
/* padding: 14pt; */
}
.help__toc li {
display: inline-block;
margin-right: 1em;
}
.help__toc li::before {
content: "";
}
.help__toc li + li::before {
/* content: " | "; */
}
......@@ -1357,7 +1416,6 @@ li .icon--tag { color: white } */
.panel__toggle {
box-sizing: border-box;
line-height: 30px;
/* outline: 1px solid white; */
padding-right: 30px;
position: absolute;
text-align: right;
......@@ -1418,6 +1476,31 @@ li .icon--tag { color: white } */
}
.btn {
font-size: 85% !important;
color: var(--text-color);
border: none;
padding: .3em 1em .2em 1em;
background: transparent;
margin-top: .15em;
font-family: 'GothicA1', sans-serif;
font-size: inherit;
line-height: 21px;
font-weight: 500;
}
.btn:hover {
background-color: var(--background-color-button);
}
.search__form {
display: flex;
}
.search__textfield { width: 30px; flex: 1 1 30px;
}
.search__submit {
}
......@@ -11,6 +11,8 @@ window.W = window.W || {};
var baseView = this.getOption('application').getView();
var view = new W.HomeView();
baseView.showChildView('main', view);
baseView.getChildView('help').triggerMethod("hide");
},
......@@ -18,6 +20,7 @@ window.W = window.W || {};
var baseView = this.getOption('application').getView();
var view = new W.AboutView();
baseView.showChildView('main', view);
baseView.getChildView('help').triggerMethod("hide");
},
......@@ -25,6 +28,8 @@ window.W = window.W || {};
var baseView = this.getOption('application').getView();
var view = new W.UserListView();
baseView.showChildView('main', view);
baseView.getChildView('help').triggerMethod("hide");
},
......@@ -32,6 +37,8 @@ window.W = window.W || {};
var baseView = this.getOption('application').getView();
var view = new W.UserDetailView();
baseView.showChildView('main', view);
baseView.getChildView('help').triggerMethod("hide");
},
......@@ -39,6 +46,8 @@ window.W = window.W || {};
var baseView = this.getOption('application').getView();
var view = new W.ScoreListView();
baseView.showChildView('main', view);
baseView.getChildView('help').triggerMethod("hide");
},
......
......@@ -9,7 +9,7 @@ window.W = window.W || {};
W.ScoreRouter = Marionette.AppRouter.extend({
appRoutes: {
'': 'home',
'apropos(/)': 'about',
'la-notation-w(/)': 'about',
'partitions(/)': 'scoreList',
'partitions/:id(/)': 'scoreDetail',
'users(/)': 'userList',
......
......@@ -6,6 +6,26 @@ window.W.utils = window.W.utils || {};
(function (undefined) {
'use strict';
W.utils.animatedScroll = function (el, distance, duration) {
var start = Date.now(),
step = Math.max(distance / (duration / 60), 3),
tick = function () {
if (Date.now() - start > duration) {
// Animation is taking longer than requested
// scroll the remaining distance at onces
el.scrollBy(0, distance);
} else if (distance > 0) {
el.scrollBy(0, step);
distance -= step;
window.requestAnimationFrame(tick)
}
};
window.requestAnimationFrame(tick);
};
W.utils.getUserLanguage = function () {
var lang = W.utils.getCookie("lang");
......
......@@ -5,24 +5,6 @@ window.W = window.W || {};
(function (undefined) {
'use strict';
function animatedScroll (el, distance, duration) {
var start = Date.now(),
step = Math.max(distance / (duration / 60), 3),
tick = function () {
if (Date.now() - start > duration) {
// Animation is taking longer than requested
// scroll the remaining distance at onces
el.scrollBy(0, distance);
} else if (distance > 0) {
el.scrollBy(0, step);
distance -= step;
window.requestAnimationFrame(tick)
}
}
window.requestAnimationFrame(tick);
}
var MyDatetimeFormatter = _.extend({}, Backgrid.CellFormatter.prototype, {
fromRaw: function (rawValue, model) {
moment.locale('fr');
......@@ -2015,12 +1997,14 @@ window.W = window.W || {};
template: '#search-template',
triggers: {
'click .search': 'search',
'submit form': 'search'
},
onSearch: function (view, event) {
this.collection.queryParams.search = this.$el.find('.search-input').val();
this.collection.fetch();
return false;
},
});
......@@ -2481,7 +2465,8 @@ window.W = window.W || {};
template: '#home-template',
regions: {
latest_updated: '#latest_updated'
latest_updated: '#latest_updated',
user_scores: '#user_scores'
},
attributes: {
......@@ -2494,9 +2479,9 @@ window.W = window.W || {};
document.title = 'Notation W';
var scoreCollection = new W.ScoreCollection([], {
url: '/test',
// url: '/test',
state: {
// pageSize: 10,
pageSize: 5,
// firstPage: 1,
// currentPage: 1,
sortKey: "updated_at",
......@@ -2516,6 +2501,31 @@ window.W = window.W || {};
that.showChildView('latest_updated', myListView);
}
});
var scoreCollection2 = new W.ScoreCollection([], {
// All the `state` and `queryParams` key value pairs are merged with
// the defaults too.
state: {
pageSize: 5,
firstPage: 1,
currentPage: 1,
sortKey: "updated_at",
order: -1
}
});