From ac6f3de60da09760bf84067ea31b26ac18996b49 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sun, 8 Feb 2015 14:57:02 +0100 Subject: [PATCH] Used AngularUI Router for more generic URL routing. --- bower.json | 3 +- openslides/agenda/static/js/agenda/agenda.js | 49 +++-- .../static/templates/agenda/item-list.html | 10 +- .../static/js/assignment/assignment.js | 68 ------- .../static/js/assignments/assignments.js | 69 +++++++ .../templates/assignment/assignment-list.html | 7 - .../assignment-detail.html | 0 .../assignment-form.html | 0 .../assignments/assignment-list.html | 7 + openslides/core/static/js/app.js | 107 ++--------- openslides/core/static/js/core.js | 176 ++++++++++++++++++ .../core/static/templates/dashboard.html | 6 +- openslides/core/static/templates/index.html | 7 +- openslides/urls.py | 5 + .../js/{user/user.js => users/users.js} | 53 +++--- .../static/templates/user/user-list.html | 7 - .../{user => users}/user-detail.html | 1 - .../templates/{user => users}/user-form.html | 0 .../static/templates/users/user-list.html | 7 + requirements_production.txt | 2 +- 20 files changed, 338 insertions(+), 246 deletions(-) delete mode 100644 openslides/assignment/static/js/assignment/assignment.js create mode 100644 openslides/assignment/static/js/assignments/assignments.js delete mode 100644 openslides/assignment/static/templates/assignment/assignment-list.html rename openslides/assignment/static/templates/{assignment => assignments}/assignment-detail.html (100%) rename openslides/assignment/static/templates/{assignment => assignments}/assignment-form.html (100%) create mode 100644 openslides/assignment/static/templates/assignments/assignment-list.html create mode 100644 openslides/core/static/js/core.js rename openslides/users/static/js/{user/user.js => users/users.js} (56%) delete mode 100644 openslides/users/static/templates/user/user-list.html rename openslides/users/static/templates/{user => users}/user-detail.html (98%) rename openslides/users/static/templates/{user => users}/user-form.html (100%) create mode 100644 openslides/users/static/templates/users/user-list.html diff --git a/bower.json b/bower.json index 1c33da2da..839d98d77 100644 --- a/bower.json +++ b/bower.json @@ -8,8 +8,7 @@ "bootstrap": "~3.3.1", "datatables-bootstrap3-plugin": "~0.2.0", "angular": "~1.3.11", - "angular-cookies": "~1.3.11", - "angular-route": "~1.3.11", + "angular-ui-router": "~0.2.13", "angular-data": "~1.5.3", "sockjs": "~0.3.4", "jed": "~1.1.0" diff --git a/openslides/agenda/static/js/agenda/agenda.js b/openslides/agenda/static/js/agenda/agenda.js index 4827224e6..b720052fd 100644 --- a/openslides/agenda/static/js/agenda/agenda.js +++ b/openslides/agenda/static/js/agenda/agenda.js @@ -1,39 +1,37 @@ angular.module('OpenSlidesApp.agenda', []) -.config(['$routeProvider', function($routeProvider) { - $routeProvider - .when('/agenda', { - templateUrl: 'static/templates/agenda/item-list.html', - controller: 'ItemListCtrl', +.config(function($stateProvider) { + $stateProvider + .state('agenda', { + url: '/agenda', + abstract: true, + template: "", + }) + .state('agenda.item', { + abstract: true, + template: "", + }) + .state('agenda.item.list', { resolve: { items: function(Agenda) { return Agenda.findAll(); } } }) - .when('/agenda/new', { - templateUrl: 'static/templates/agenda/item-form.html', - controller: 'ItemCreateCtrl' - }) - .when('/agenda/:id', { - templateUrl: 'static/templates/agenda/item-detail.html', - controller: 'ItemDetailCtrl', + .state('agenda.item.create', {}) + .state('agenda.item.detail', { resolve: { - item: function(Agenda, $route) { - return Agenda.find($route.current.params.id); + item: function(Agenda, $stateParams) { + return Agenda.find($stateParams.id); } } }) - .when('/agenda/:id/edit', { - templateUrl: 'static/templates/agenda/item-form.html', - controller: 'ItemUpdateCtrl', - resolve: { - item: function(Agenda, $route) { - return Agenda.find($route.current.params.id); - } + .state('agenda.item.detail.update', { + views: { + '@agenda.item': {} } }); -}]) +}) .factory('Agenda', function(DS) { return DS.defineResource({ @@ -48,20 +46,21 @@ angular.module('OpenSlidesApp.agenda', []) $scope.test_singular = i18n.ngettext('test', 'tests', 1); }) -.controller('ItemDetailCtrl', function($scope, $routeParams, Agenda) { - Agenda.bindOne($scope, 'item', $routeParams.id); +.controller('ItemDetailCtrl', function($scope, Agenda, item) { + Agenda.bindOne($scope, 'item', item.id); }) .controller('ItemCreateCtrl', function($scope, Agenda) { $scope.item = {}; $scope.save = function (item) { item.weight = 0; // TODO: the rest_api should do this + item.tags = []; // TODO: the rest_api should do this Agenda.create(item); // TODO: redirect to list-view }; }) -.controller('ItemUpdateCtrl', function($scope, $routeParams, Agenda, item) { +.controller('ItemUpdateCtrl', function($scope, Agenda, item) { $scope.item = item; // do not use Agenda.binOne(...) so autoupdate is not activated $scope.save = function (item) { Agenda.save(item); diff --git a/openslides/agenda/static/templates/agenda/item-list.html b/openslides/agenda/static/templates/agenda/item-list.html index bb2485b15..b98d3da53 100644 --- a/openslides/agenda/static/templates/agenda/item-list.html +++ b/openslides/agenda/static/templates/agenda/item-list.html @@ -1,9 +1,9 @@ -{{ gettext('New') }} -{{ test_singular }} -{{ test_plural }} +{{ gettext('New') }} +
{{ test_singular }} +
{{ test_plural }} diff --git a/openslides/assignment/static/js/assignment/assignment.js b/openslides/assignment/static/js/assignment/assignment.js deleted file mode 100644 index 9e4f2a219..000000000 --- a/openslides/assignment/static/js/assignment/assignment.js +++ /dev/null @@ -1,68 +0,0 @@ -angular.module('OpenSlidesApp.assignment', []) - -.config(['$routeProvider', function($routeProvider) { - $routeProvider - .when('/assignment', { - templateUrl: 'static/templates/assignment/assignment-list.html', - controller: 'AssignmentListCtrl', - resolve: { - assignments: function(Assignment) { - return Assignment.findAll(); - } - } - }) - .when('/assignment/new', { - templateUrl: 'static/templates/assignment/assignment-form.html', - controller: 'AssignmentCreateCtrl' - }) - .when('/assignment/:id', { - templateUrl: 'static/templates/assignment/assignment-detail.html', - controller: 'AssignmentDetailCtrl', - resolve: { - assignment: function(Assignment, $route) { - return Assignment.find($route.current.params.id); - } - } - }) - .when('/assignment/:id/edit', { - templateUrl: 'static/templates/assignment/assignment-form.html', - controller: 'AssignmentUpdateCtrl', - resolve: { - assignment: function(Assignment, $route) { - return Assignment.find($route.current.params.id); - } - } - }); -}]) - -.factory('Assignment', function(DS) { - return DS.defineResource({ - name: 'assignment/assignment', - endpoint: '/rest/assignment/assignment/' - }); -}) - -.controller('AssignmentListCtrl', function($scope, Assignment) { - Assignment.bindAll($scope, 'assignments'); -}) - -.controller('AssignmentDetailCtrl', function($scope, $routeParams, Assignment) { - Assignment.bindOne($scope, 'assignment', $routeParams.id) -}) - -.controller('AssignmentCreateCtrl', function($scope, Assignment) { - $scope.assignment = {}; - $scope.save = function(assignment) { - Assignment.create(assignment); - // TODO: redirect to list-view - }; -}) - -.controller('AssignmentUpdateCtrl', function($scope, $routeParams, Assignment, assignment) { - $scope.assignment = assignment; // do not use .binOne(...) so autoupdate is not activated - $scope.save = function (assignment) { - Assignment.save(assignment); - // TODO: redirect to list-view - }; -}); - diff --git a/openslides/assignment/static/js/assignments/assignments.js b/openslides/assignment/static/js/assignments/assignments.js new file mode 100644 index 000000000..071a2976a --- /dev/null +++ b/openslides/assignment/static/js/assignments/assignments.js @@ -0,0 +1,69 @@ +angular.module('OpenSlidesApp.assignments', []) + +.config(function($stateProvider) { + $stateProvider + .state('assignments', { + url: '/assignment', + abstract: true, + template: "", + }) + .state('assignments.assignment', { + abstract: true, + template: "", + }) + .state('assignments.assignment.list', { + resolve: { + assignments: function(Assignment) { + return Assignment.findAll(); + } + } + }) + .state('assignments.assignment.create', {}) + .state('assignments.assignment.detail', { + controller: 'AssignmentDetailCtrl', + resolve: { + assignment: function(Assignment, $stateParams) { + return Assignment.find($stateParams.id); + } + } + }) + .state('assignments.assignment.detail.update', { + views: { + '@assignments.assignment': {} + } + }); +}) + +.factory('Assignment', function(DS) { + return DS.defineResource({ + name: 'assignment/assignment', + endpoint: '/rest/assignment/assignment/' + }); +}) + +.controller('AssignmentListCtrl', function($scope, Assignment) { + Assignment.bindAll($scope, 'assignments'); +}) + +.controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) { + Assignment.bindOne($scope, 'assignment', assignment.id) +}) + +.controller('AssignmentCreateCtrl', function($scope, Assignment) { + $scope.assignment = {}; + $scope.save = function(assignment) { + assignment.posts = 1; + assignment.tags = []; // TODO: the rest_api should do this + Assignment.create(assignment); + // TODO: redirect to list-view + }; +}) + +.controller('AssignmentUpdateCtrl', function($scope, Assignment, assignment) { + $scope.assignment = assignment; // do not use .binOne(...) so autoupdate is not activated + $scope.save = function (assignment) { + Assignment.save(assignment); + // TODO: redirect to list-view + }; +}); + diff --git a/openslides/assignment/static/templates/assignment/assignment-list.html b/openslides/assignment/static/templates/assignment/assignment-list.html deleted file mode 100644 index 404e8344a..000000000 --- a/openslides/assignment/static/templates/assignment/assignment-list.html +++ /dev/null @@ -1,7 +0,0 @@ - -Neu diff --git a/openslides/assignment/static/templates/assignment/assignment-detail.html b/openslides/assignment/static/templates/assignments/assignment-detail.html similarity index 100% rename from openslides/assignment/static/templates/assignment/assignment-detail.html rename to openslides/assignment/static/templates/assignments/assignment-detail.html diff --git a/openslides/assignment/static/templates/assignment/assignment-form.html b/openslides/assignment/static/templates/assignments/assignment-form.html similarity index 100% rename from openslides/assignment/static/templates/assignment/assignment-form.html rename to openslides/assignment/static/templates/assignments/assignment-form.html diff --git a/openslides/assignment/static/templates/assignments/assignment-list.html b/openslides/assignment/static/templates/assignments/assignment-list.html new file mode 100644 index 000000000..b9594dccd --- /dev/null +++ b/openslides/assignment/static/templates/assignments/assignment-list.html @@ -0,0 +1,7 @@ + +Neu diff --git a/openslides/core/static/js/app.js b/openslides/core/static/js/app.js index ad815ab02..9d1259697 100644 --- a/openslides/core/static/js/app.js +++ b/openslides/core/static/js/app.js @@ -1,105 +1,20 @@ angular.module('OpenSlidesApp', [ - 'ngRoute', + 'ui.router', 'angular-data.DS', - 'ngCookies', + 'OpenSlidesApp.core', 'OpenSlidesApp.agenda', - 'OpenSlidesApp.assignment', - 'OpenSlidesApp.user', + 'OpenSlidesApp.assignments', + 'OpenSlidesApp.users', ]) -.config(function($routeProvider, $locationProvider) { - $routeProvider - .when('/', { - templateUrl: 'static/templates/dashboard.html' - }) - .otherwise({redirectTo: '/'}); +.config(function($urlRouterProvider, $locationProvider) { + // define fallback url and html5Mode + $urlRouterProvider.otherwise('/'); $locationProvider.html5Mode(true); }) -.run(function($http, $cookies) { - $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken; - $http.defaults.headers.put['X-CSRFToken'] = $cookies.csrftoken; -}) - -.run(function(DS, autoupdate) { - autoupdate.on_message(function(data) { - // TODO: when MODEL.find() is called after this - // a new request is fired. This could be a bug in DS - if (data.status_code == 200) { - DS.inject(data.collection, data.data) - } - // TODO: handle other statuscodes - }); -}) - -.run(function($rootScope, i18n) { - // Puts the gettext methods into each scope. - // Uses the methods that are known by xgettext by default. - methods = ['gettext', 'dgettext', 'dcgettext', 'ngettext', 'dngettext', - 'pgettext', 'dpgettext']; - _.forEach(methods, function(method) { - $rootScope[method] = _.bind(i18n[method], i18n); - }); -}) - -.run(function($rootScope, Config) { - // Puts the config object into each scope. - // TODO: maybe rootscope.config has to set before findAll() is finished - Config.findAll().then(function() {; - $rootScope.config = function(key) { - return Config.get(key).value; - } - }); -}) - -.factory('autoupdate', function() { - //TODO: use config here - var url = "http://" + location.host + "/sockjs"; - - var Autoupdate = { - socket: null, - message_receivers: [], - connect: function() { - var autoupdate = this; - this.socket = new SockJS(url); - - this.socket.onmessage = function(event) { - _.forEach(autoupdate.message_receivers, function(receiver) { - receiver(event.data); - }); - } - - this.socket.onclose = function() { - setTimeout(autoupdate.connect, 5000); - } - }, - on_message: function(receiver) { - this.message_receivers.push(receiver); - } - }; - Autoupdate.connect(); - return Autoupdate; -}) - -.factory('i18n', function($http) { - // TODO: there is a bug(?) in jed. I had to call val_idx++; in line 285 - // TODO: make the language variable and changeable at runtime - var i18n = new Jed({ - 'domain': 'de', - 'locale_data': {'de': {"": {}}}, - }); // TODO: use promise here - $http.get('/static/i18n/de.json') - .success(function(data) { - // TODO: check data. - i18n.options.locale_data['de'] = data; - }); - return i18n; -}) - -.factory('Config', function(DS) { - return DS.defineResource({ - name: 'config/config', - idAttribute: 'key', - endpoint: '/rest/config/config/' - }); +.config(function($httpProvider) { + // Combine the django csrf system with the angular csrf system + $httpProvider.defaults.xsrfCookieName = 'csrftoken'; + $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; }); diff --git a/openslides/core/static/js/core.js b/openslides/core/static/js/core.js new file mode 100644 index 000000000..1c24d545f --- /dev/null +++ b/openslides/core/static/js/core.js @@ -0,0 +1,176 @@ +angular.module('OpenSlidesApp.core', []) + +.config(function($stateProvider) { + // Use stateProvider.decorator to give default values to our states + $stateProvider.decorator('views', function(state, parent) { + + + var result = {}, + views = parent(state); + + if (state.abstract) { + return views; + } + + angular.forEach(views, function(config, name) { + // Sets default values for templateUrl + var patterns = state.name.split('.'), + templateUrl, + controller, + defaultControllers = { + create: 'CreateCtrl', + update: 'UpdateCtrl', + list: 'ListCtrl', + detail: 'DetailCtrl', + }; + + // templateUrl + if (_.last(patterns).match(/(create|update)/)) { + // When state_patterns is in the form "app.module.create" or + // "app.module.update", use the form template. + templateUrl = 'static/templates/' + patterns[0] + '/' + patterns[1] + '-form.html' + } else { + // Replaces the first point through a slash (the app name) + var appName = state.name.replace('.', '/'); + // Replaces any folowing points though a - + templateUrl = 'static/templates/' + appName.replace(/\./g, '-') + '.html'; + } + config.templateUrl = config.templateUrl || templateUrl; + + // controller + if (patterns.length >= 3) { + controller = _.capitalize(patterns[1]) + defaultControllers[_.last(patterns)]; + config.controller = config.controller || controller; + } + result[name] = config; + }); + return result; + }) + + .decorator('url', function(state, parent) { + var defaultUrl; + + if (state.abstract) { + defaultUrl = ''; + } else { + var patterns = state.name.split('.'), + defaultUrls = { + create: '/new', + update: '/edit', + list: '', + detail: '/:id', + }; + + defaultUrl = defaultUrls[_.last(patterns)]; + } + + state.url = state.url || defaultUrl; + return parent(state) + }); +}) + +.config(function($stateProvider, $urlRouterProvider, $locationProvider) { + // Core urls + $urlRouterProvider.otherwise('/'); + + $stateProvider + .state('dashboard', { + url: '/', + templateUrl: 'static/templates/dashboard.html' + }); + + $locationProvider.html5Mode(true); +}) + +.run(function(DS, autoupdate) { + autoupdate.on_message(function(data) { + // TODO: when MODEL.find() is called after this + // a new request is fired. This could be a bug in DS + if (data.status_code == 200) { + DS.inject(data.collection, data.data) + } + // TODO: handle other statuscodes + }); +}) + +.run(function($rootScope, i18n) { + // Puts the gettext methods into each scope. + // Uses the methods that are known by xgettext by default. + methods = ['gettext', 'dgettext', 'dcgettext', 'ngettext', 'dngettext', + 'pgettext', 'dpgettext']; + _.forEach(methods, function(method) { + $rootScope[method] = _.bind(i18n[method], i18n); + }); +}) + +.run(function($rootScope, i18n) { + // Puts the gettext methods into each scope. + // Uses the methods that are known by xgettext by default. + methods = ['gettext', 'dgettext', 'dcgettext', 'ngettext', 'dngettext', + 'pgettext', 'dpgettext']; + _.forEach(methods, function(method) { + $rootScope[method] = _.bind(i18n[method], i18n); + }); +}) + +.run(function($rootScope, Config) { + // Puts the config object into each scope. + // TODO: maybe rootscope.config has to set before findAll() is finished + Config.findAll().then(function() {; + $rootScope.config = function(key) { + return Config.get(key).value; + } + }); +}) + +.factory('autoupdate', function() { + //TODO: use config here + var url = "http://" + location.host + "/sockjs"; + + var Autoupdate = { + socket: null, + message_receivers: [], + connect: function() { + var autoupdate = this; + this.socket = new SockJS(url); + + this.socket.onmessage = function(event) { + _.forEach(autoupdate.message_receivers, function(receiver) { + receiver(event.data); + }); + } + + this.socket.onclose = function() { + setTimeout(autoupdate.connect, 5000); + } + }, + on_message: function(receiver) { + this.message_receivers.push(receiver); + } + }; + Autoupdate.connect(); + return Autoupdate; +}) + +.factory('i18n', function($http) { + // TODO: there is a bug(?) in jed. I had to call val_idx++; in line 285 + // TODO: make the language variable and changeable at runtime + var i18n = new Jed({ + 'domain': 'de', + 'locale_data': {'de': {"": {}}}, + }); // TODO: use promise here + $http.get('/static/i18n/de.json') + .success(function(data) { + // TODO: check data. + i18n.options.locale_data['de'] = data; + }); + return i18n; +}) + +.factory('Config', function(DS) { + return DS.defineResource({ + name: 'config/config', + idAttribute: 'key', + endpoint: '/rest/config/config/' + }); +}); diff --git a/openslides/core/static/templates/dashboard.html b/openslides/core/static/templates/dashboard.html index 8ea14ece0..24c08a0ea 100644 --- a/openslides/core/static/templates/dashboard.html +++ b/openslides/core/static/templates/dashboard.html @@ -1,4 +1,4 @@

Dashboard

-Assignments -Agenda -User +Assignments +Agenda +User diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html index e1872c894..02e161343 100644 --- a/openslides/core/static/templates/index.html +++ b/openslides/core/static/templates/index.html @@ -78,7 +78,7 @@
-
+

@@ -94,8 +94,9 @@ + - - + + diff --git a/openslides/urls.py b/openslides/urls.py index f814842b0..fc4c4d4b2 100644 --- a/openslides/urls.py +++ b/openslides/urls.py @@ -14,6 +14,11 @@ urlpatterns = patterns( # TODO: add "special" urls, for example pdf views etc. url(r'^user.*', IndexView.as_view()), + + # activate next line go get more angular views + # url(r'^$', IndexView.as_view()), + # url(r'^assignment.*', IndexView.as_view()), + # url(r'^agenda.*', IndexView.as_view()), ) diff --git a/openslides/users/static/js/user/user.js b/openslides/users/static/js/users/users.js similarity index 56% rename from openslides/users/static/js/user/user.js rename to openslides/users/static/js/users/users.js index eba4e3a08..251363466 100644 --- a/openslides/users/static/js/user/user.js +++ b/openslides/users/static/js/users/users.js @@ -1,39 +1,37 @@ -angular.module('OpenSlidesApp.user', []) +angular.module('OpenSlidesApp.users', []) -.config(['$routeProvider', function($routeProvider) { - $routeProvider - .when('/user', { - templateUrl: 'static/templates/user/user-list.html', - controller: 'UserListCtrl', +.config(function($stateProvider) { + $stateProvider + .state('users', { + url: '/user', + abstract: true, + template: "", + }) + .state('users.user', { + abstract: true, + template: "", + }) + .state('users.user.list', { resolve: { users: function(User) { return User.findAll(); } } }) - .when('/user/new', { - templateUrl: 'static/templates/user/user-form.html', - controller: 'UserCreateCtrl' - }) - .when('/user/:id', { - templateUrl: 'static/templates/user/user-detail.html', - controller: 'UserDetailCtrl', + .state('users.user.create', {}) + .state('users.user.detail', { resolve: { - user: function(User, $route) { - return User.find($route.current.params.id); + user: function(User, $stateParams) { + return User.find($stateParams.id); } } }) - .when('/user/:id/edit', { - templateUrl: 'static/templates/user/user-form.html', - controller: 'UserUpdateCtrl', - resolve: { - user: function(User, $route) { - return User.find($route.current.params.id); - } + .state('users.user.detail.update', { + views: { + '@users.user': {} } }); -}]) +}) .factory('User', function(DS) { return DS.defineResource({ @@ -59,7 +57,6 @@ angular.module('OpenSlidesApp.user', []) }) .factory('Group', function(DS) { - // TODO: the rest api for group does not exist at the moment return DS.defineResource({ name: 'users/group', endpoint: '/rest/users/group/' @@ -70,8 +67,8 @@ angular.module('OpenSlidesApp.user', []) User.bindAll($scope, 'users'); }) -.controller('UserDetailCtrl', function($scope, $routeParams, User) { - User.bindOne($scope, 'user', $routeParams.id); +.controller('UserDetailCtrl', function($scope, User, user) { + User.bindOne($scope, 'user', user.id); }) .controller('UserCreateCtrl', function($scope, User) { @@ -82,9 +79,9 @@ angular.module('OpenSlidesApp.user', []) }; }) -.controller('UserUpdateCtrl', function($scope, $routeParams, User, user) { +.controller('UserUpdateCtrl', function($scope, User, user) { $scope.user = user; // do not use Agenda.binOne(...) so autoupdate is not activated - $scope.save = function (user) { + $scope.save = function(user) { User.save(user); // TODO: redirect to list-view }; diff --git a/openslides/users/static/templates/user/user-list.html b/openslides/users/static/templates/user/user-list.html deleted file mode 100644 index c3cf7075f..000000000 --- a/openslides/users/static/templates/user/user-list.html +++ /dev/null @@ -1,7 +0,0 @@ - -{{ gettext('New') }} diff --git a/openslides/users/static/templates/user/user-detail.html b/openslides/users/static/templates/users/user-detail.html similarity index 98% rename from openslides/users/static/templates/user/user-detail.html rename to openslides/users/static/templates/users/user-detail.html index 9ed8dee2d..37cf64918 100644 --- a/openslides/users/static/templates/user/user-detail.html +++ b/openslides/users/static/templates/users/user-detail.html @@ -1,3 +1,2 @@

Persönliche Daten

{{ user.get_short_name() }} - diff --git a/openslides/users/static/templates/user/user-form.html b/openslides/users/static/templates/users/user-form.html similarity index 100% rename from openslides/users/static/templates/user/user-form.html rename to openslides/users/static/templates/users/user-form.html diff --git a/openslides/users/static/templates/users/user-list.html b/openslides/users/static/templates/users/user-list.html new file mode 100644 index 000000000..c432ff438 --- /dev/null +++ b/openslides/users/static/templates/users/user-list.html @@ -0,0 +1,7 @@ + +{{ gettext('New') }} diff --git a/requirements_production.txt b/requirements_production.txt index 532cf54f8..19c9fc499 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -5,7 +5,7 @@ bleach>=1.4,<1.5 django-ckeditor-updated>=4.2.3,<4.4 django-haystack>=2.1,<2.4 django-mptt>=0.6,<0.7 -djangorestframework>=3.0.1,<3.1 +djangorestframework>=3.0.1,<3.0.5 jsonfield>=0.9.19,<1.1 natsort>=3.2,<3.6 reportlab>=3.0,<3.2