Commit b2b2d591 by alexandre

Merge branch 'master' into layout-proposals

parents 268942d7 f1a89ed3
,annie,annie-X580VD,18.05.2018 15:30,file:///home/annie/.config/libreoffice/4;
\ No newline at end of file
#! /usr/bin/env bash
echo "var lang = {"
cat playground/templates/playground/underscore/*.mtpl | \
tr "<" "\n" | \
grep "t(" | \
cut -d "'" -f 2 | \
sort | \
uniq | \
sort | \
xargs -0 | while read -r line ; do
echo " \"${line}\": \"\"",
done
echo "}"
......@@ -26,6 +26,8 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'guardian',
'adminsortable2',
'taggit',
......@@ -114,12 +116,18 @@ STATICFILES_FINDERS = (
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # this is default
'guardian.backends.ObjectPermissionBackend',
)
# cf <https://stackoverflow.com/questions/30871033/django-rest-framework-remove-csrf>
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework.authentication.TokenAuthentication',
'playground.authentication.CsrfExemptSessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
'rest_framework.authentication.BasicAuthentication',
)
}
......@@ -128,7 +136,7 @@ COMPRESS_PRECOMPILERS = (
('text/css', './node_modules/.bin/postcss {infile} --use postcss-cssnext --use postcss-import'),
)
COMPRESS_ENABLED = True
COMPRESS_ENABLED = False
COMPRESS_CSS_FILTERS = [
'compressor.filters.css_default.CssAbsoluteFilter',
......
......@@ -25,9 +25,10 @@ from playground import views
router = routers.DefaultRouter()
router.register(r'attachments', views.AttachmentViewSet)
router.register(r'scores', views.ScoreViewSet)
router.register(r'users', views.UserViewSet)
urlpatterns = [
urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(router.urls)),
......@@ -35,4 +36,4 @@ urlpatterns = [
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^', include('playground.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
]
from django.contrib import admin
from guardian.admin import GuardedModelAdmin
from adminsortable2.admin import SortableAdminMixin, SortableInlineAdminMixin
from .models import Attachment, Score, FeaturedScore
......@@ -12,7 +13,7 @@ class AttachmentAdmin(admin.ModelAdmin):
pass
class ScoreAdmin(admin.ModelAdmin):
class ScoreAdmin(GuardedModelAdmin):
pass
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2018-06-01 11:30
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('playground', '0018_featuredscore'),
]
operations = [
migrations.AlterModelOptions(
name='score',
options={'ordering': ['title'], 'permissions': (('edit_score', 'Edit score'),)},
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2018-06-01 11:33
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('playground', '0019_auto_20180601_1130'),
]
operations = [
migrations.AlterModelOptions(
name='score',
options={'ordering': ['title'], 'permissions': (('edit_score', 'Can edit score'),)},
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2018-06-01 13:20
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('playground', '0020_auto_20180601_1133'),
]
operations = [
migrations.AlterModelOptions(
name='score',
options={'ordering': ['title'], 'permissions': (('view_score', 'Can view score'),)},
),
]
......@@ -38,9 +38,12 @@ class Score(models.Model):
return self.title
def get_absolute_url(self):
return "/playground/score/#{}".format(self.id)
return "/partitions/{}".format(self.id)
class Meta:
permissions = (
('view_score', 'Can view score'),
)
ordering = ['title']
......
from django.contrib.auth.models import User
from .models import Attachment, Score
from rest_framework import serializers
from rest_framework_recursive.fields import RecursiveField
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
class UserSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
class Meta:
model = User
fields = ['username', 'id']
class AttachmentSerializer(serializers.HyperlinkedModelSerializer):
......@@ -14,14 +26,51 @@ class AttachmentSerializer(serializers.HyperlinkedModelSerializer):
# read_only_fields = ('attachment',)
class PermissionListSerializer(serializers.ListSerializer):
def to_representation(self, obj):
perms = get_users_with_perms(obj, attach_perms=True)
return [{"username": k.username, "permissions": v} for k, v in perms.items()]
def to_internal_value(self, data):
# return data
# I don't understand why I can't just return data
return {"permissions": data}
class PermissionSerializer(serializers.Serializer):
class Meta:
list_serializer_class = PermissionListSerializer
class ScoreSerializer(TaggitSerializer, serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
tags = TagListSerializerField()
permissions = PermissionSerializer(source="*", many=True)
class Meta:
model = Score
fields = '__all__'
def create(self, request, *args, **kwargs):
instance = super().create(request, *args, **kwargs)
assign_perm("view_score", request.user, instance)
return instance
def update(self, instance, validated_data):
instance = super(ScoreSerializer, self).update(instance, validated_data)
permissions = validated_data["permissions"]
for i in permissions:
username = i.get("username")
perms = i.get("permissions")
user = User.objects.get(username=username)
for perm in get_user_perms(user, instance):
remove_perm(perm, user, instance)
for perm in perms:
assign_perm(perm, user, instance)
return instance
class ScoreLightSerializer(serializers.HyperlinkedModelSerializer):
"""A serializer exposing just a subsets of field when we don't need the full
......
......@@ -430,7 +430,10 @@ section.hidden { display: none; }
break-before: avoid;
}
#permissions { display: none; }
#sidebar[data-expanded="true"] .score-meta { display: block; }
#sidebar[data-expanded="true"] #permissions { display: block; }
......@@ -900,7 +903,68 @@ li + li .alt-symbol { display: none; }
/* subaxis colors, according to the level of nesting */
<<<<<<< HEAD
li form { color: var(--row-color); }
=======
li form { color: var(--color-01); }
li li form { color: var(--color-02); }
li li li form { color: var(--color-03); }
li li li li form { color: var(--color-04); }
li li li li li form { color: var(--color-05); }
li li li li li li form { color: var(--color-06); }
li li li li li li li form { color: var(--color-07); }
li li li li li li li li form { color: var(--color-08); }
li li li li li li li li li form { color: var(--color-09); }
li li li li li li li li li li form { color: var(--color-10); }
li li li li li li li li li li li form { color: var(--color-11); }
li li li li li li li li li li li li form { color: var(--color-12); }
li li li li li li li li li li li li li form { color: var(--color-13); }
li li li li li li li li li li li li li li form { color: var(--color-14); }
.attachment-upload {
margin-top: 1.5em;
}
.attachment-field.removed {
display: none;
}
>>>>>>> master
#help {
position: relative;
......@@ -920,6 +984,7 @@ li form { color: var(--row-color); }
#help.is-collapsed .help__content { display: none; }
<<<<<<< HEAD
.actant, .adresse, #aspect_vue, .options, .header {
font-size: 55%;
}
......@@ -945,3 +1010,7 @@ li .icon--tag { color: white } */
/* .header { position: absolute; left: calc(-1 * var(--row-indent)); color: white !important; background-color: var(--row-color); font-size: 90%; padding: 2px 2px 0px 0px; width: var(--row-indent); text-align: right;}
li .icon--tag { color: white } */
.permission-list label { display: inline; }
......@@ -10,9 +10,9 @@ window.W = window.W || {};
region: 'body',
onStart: function(options) {
var userModel = new W.UserModel();
this.showView(new W.BaseView({model: userModel}));
userModel.fetch();
var userAuthModel = new W.UserAuthModel();
this.showView(new W.BaseView({model: userAuthModel}));
userAuthModel.fetch();
var router = new W.ScoreRouter({application: this});
......@@ -28,9 +28,7 @@ window.W = window.W || {};
});
/** Starts the URL handling framework */
Backbone.history.start({
pushState: true,
});
Backbone.history.start({ pushState: true });
}
});
})();
// Might have a model? A model means saved to the server?
// Otherwise a more simple object
// How to differentiate between synced model and file? Marker?
W.AttachmentField = Backbone.Marionette.CollectionView.extend({
childView: W.AttachmentField,
ui: {
'title': 'input[name="title"]',
'file': 'input[name="file"]'
},
events: {
'click button': 'delete'
},
template: '#field-attachment-template',
delete: function (e) {
e.preventDefault();
var r = confirm("Vous êtes sur le point de supprimer ce pièces-jointes. Continuer?");
if (r) {
this.model = null;
this.render();
}
},
value: function () {
if (this.model) {
return this.model;
} else {
return {
title: this.ui.title.val(),
file: this.ui.file.get(0).files[0]
}
}
}
});
(function () {
var model = {}, callback;
removeQueue.add((function remove (accept, reject) {
model.destroy({
success: function () {
if (callback) {
callback();
}
accept();
},
error: reject
})
}));
})()
(function () {
var model = {}, callback;
uploadQueue.add((function upload (accept, reject) {
var formData = new FormData();
formData.append('title', entry.title);
formData.append('attachment', entry.file);
$.ajax({
type: "POST",
url: '/api/attachments/',
enctype: 'multipart/form-data',
data: formData,
cache: false,
contentType: false,
processData: false,
success: _.bind(function (data) {
model.set(data);
if (callback) {
callback();
}
accept();
}, this),
error: reject,
dataType: 'json'
});
}));
}) ();
\ No newline at end of file
......@@ -6,6 +6,12 @@ window.W = window.W || {};
(function(undefined) {
'use strict';
W.UserCollection = Backbone.Collection.extend({
url: '/api/users/',
model: W.UserModel
});
W.ScoreCollection = Backbone.PageableCollection.extend({
url: '/api/scores/',
......@@ -41,4 +47,9 @@ window.W = window.W || {};
W.LineCollection = Backbone.Collection.extend({
model: W.LineModel
});
W.PermissionCollection = Backbone.Collection.extend({
model: W.PermissionModel
});
})();
......@@ -12,26 +12,36 @@ window.W = window.W || {};
var view = new W.HomeView();
baseView.showChildView('main', view);
},
about: function () {
var baseView = this.getOption('application').getView();
var view = new W.AboutView();
baseView.showChildView('main', view);
},
userList: function () {
var baseView = this.getOption('application').getView();
var view = new W.UserListView();
baseView.showChildView('main', view);
},
userDetail: function () {
var baseView = this.getOption('application').getView();
var view = new W.UserDetailView();
baseView.showChildView('main', view);
},
scoreList: function () {
var baseView = this.getOption('application').getView();
var view = new W.ScoreListView();
baseView.showChildView('main', view);
},
scoreDetail: function (id) {
var baseView = this.getOption('application').getView();
......
;
window.W = window.W || {};
(function () {
// Extended version of the _.template function, also supports T{key} to have text translated
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
var templateSettings = {
evaluate: /<%([\s\S]+?)%>/g,
interpolate: /<%=([\s\S]+?)%>/g,
escape: /<%-([\s\S]+?)%>/g,
translate: /t\{([\s\S]+?)\}/g,
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
// NB: `oldSettings` only exists for backwards compatibility.
template = function(text, settings, oldSettings) {
if (!settings && oldSettings) settings = oldSettings;
settings = _.defaults({}, settings, templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.translate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, translate, evaluate, offset) {
source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
index = offset + match.length;
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
} else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
} else if (translate) {
source += "'+\n((__t=(W.utils.translate('" + translate + "')))==null?'':__t)+\n'";
} else if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
// Adobe VMs need the match returned to produce the correct offset.
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + 'return __p;\n';
var render;
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled source as a convenience for precompilation.
var argument = settings.variable || 'obj';
template.source = 'function(' + argument + '){\n' + source + '}';
return template;
};
W.extendedTemplate = template;
})();
;
window.W = window.W || {};
W.lang = W.lang || {};
W.lang.en = {
"Adresse": "",
"Aide": "Help",
"Annuler": "Cancel",
"À propos": "About",
"au moins": "at least",
"Auteur de la partition": "Score author",
// "Auteur de la perfomance": "",
"Auteur de la performance": "Performance author",
"Axe": "Axis",
"Bienvenue": "Welcome",
"choisir": "choose",
// "Code": "",
// "Commandement": "",
// "Condition": "",
"Créer": "Create",
"Créer une nouvelle partition": "Create a new score",
"Destination": "Destination",
"Dupliquer": "Duplicate",
"Durée": "Length",
"éditer": "Edit",
"Éditer": "Edit",
// "Effectif": "",
// "effectuer l’axe": "",
"Enregistrer": "Save",
"En savoir plus": "Further inforrmation",
"entre": "between",
"Etes-vous sur de vouloir vous deconnecter?": "Are you sure you want to disconnect",
"exactement": "exactly",
"Export": "Export",
"Genre": "Genre",
// "indeterminé": "undefined",
"indéterminé": "undefined",
"Indications": "Indications",
"Langue": "Language",
// "Mise": "",
"Modifié le": "Modified on",
"Partitions": "Scores",
"Partitions existantes": "Existing score",
"Partitions mises à jour récemment": "Recently updated scores",
"performeurs": "performers",
"performeur(s)": "performer(s)",
"Prescription": "",
"Présentation": "",
"Publié le": "Published on",
"rechercher": "search",
"S'inscrire": "Register",
"Se connecter": "Log in",
"Se déconnecter": "Log out",
"Supprimer": "Delete",
"Tags": "Tags",
"Terme": "",
"Titre": "Title",
"Traduction": "Translation",
"Transcription": "Transcription",
"Type": "Type",
"Type de partition": "Score type",
"Un logiciel pour noter l’action performée, développé par W": "A software for scoring performed action, developed by W",
"Welcome": "Bienvenue",
}
;
window.W = window.W || {};
W.lang = W.lang || {};
W.lang.fr = {
}
......@@ -6,43 +6,30 @@ window.W = window.W || {};
(function(undefined) {
'use strict';
// This is for Django to allow POSTing forms
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {