diff --git a/bower.json b/bower.json
index d0082fb0b..1c33da2da 100644
--- a/bower.json
+++ b/bower.json
@@ -1,10 +1,22 @@
{
- "name": "OpenSlides",
- "private": true,
- "dependencies": {
- "bootstrap": "3.3.1",
- "datatables-bootstrap3-plugin": "0.2.0",
- "jquery": "1.11.2",
- "jquery.cookie" : "1.4.1"
+ "name": "OpenSlides",
+ "private": true,
+ "dependencies": {
+ "lodash": "~3.0.1",
+ "jquery": "~1.11.2",
+ "jquery.cookie": "~1.4.1",
+ "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-data": "~1.5.3",
+ "sockjs": "~0.3.4",
+ "jed": "~1.1.0"
+ },
+ "overrides": {
+ "jed": {
+ "main": "jed.js"
}
+ }
}
diff --git a/make/commands.py b/make/commands.py
index 7fcd5da7f..e21db86da 100644
--- a/make/commands.py
+++ b/make/commands.py
@@ -102,3 +102,21 @@ def clear(args=None):
call('find -name "*.pyc" -delete')
call('find -name "*.orig" -delete')
call('find -type d -empty -delete')
+
+@command('po',
+ help="Generates the po-file for javascript")
+def po(args=None):
+ # TODO: in the value "" there has to be the entry:
+ # "plural_forms: nplurals=2; plural=(n != 1);"
+ call('find openslides/ -iname "*.js" -or -iname "*.html" | '
+ 'xargs xgettext --from-code=UTF-8 --language=JavaScript '
+ '--output=openslides/locale/en/javascript.po')
+
+
+@argument('-l', '--language')
+@command('po2json',
+ help="Generates json for a translated po file")
+def po2json(args=None):
+ lang = args.language
+ call('node_modules/.bin/po2json openslides/locale/%s/javascript.po openslides/static/i18n/%s.json' %
+ (lang, lang))
diff --git a/openslides/agenda/static/js/agenda/agenda.js b/openslides/agenda/static/js/agenda/agenda.js
new file mode 100644
index 000000000..4827224e6
--- /dev/null
+++ b/openslides/agenda/static/js/agenda/agenda.js
@@ -0,0 +1,70 @@
+angular.module('OpenSlidesApp.agenda', [])
+
+.config(['$routeProvider', function($routeProvider) {
+ $routeProvider
+ .when('/agenda', {
+ templateUrl: 'static/templates/agenda/item-list.html',
+ controller: 'ItemListCtrl',
+ 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',
+ resolve: {
+ item: function(Agenda, $route) {
+ return Agenda.find($route.current.params.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);
+ }
+ }
+ });
+}])
+
+.factory('Agenda', function(DS) {
+ return DS.defineResource({
+ name: 'agenda/item',
+ endpoint: '/rest/agenda/item/'
+ });
+})
+
+.controller('ItemListCtrl', function($scope, Agenda, i18n) {
+ Agenda.bindAll($scope, 'items');
+ $scope.test_plural = i18n.ngettext('test', 'tests', 2);
+ $scope.test_singular = i18n.ngettext('test', 'tests', 1);
+})
+
+.controller('ItemDetailCtrl', function($scope, $routeParams, Agenda) {
+ Agenda.bindOne($scope, 'item', $routeParams.id);
+})
+
+.controller('ItemCreateCtrl', function($scope, Agenda) {
+ $scope.item = {};
+ $scope.save = function (item) {
+ item.weight = 0; // TODO: the rest_api should do this
+ Agenda.create(item);
+ // TODO: redirect to list-view
+ };
+})
+
+.controller('ItemUpdateCtrl', function($scope, $routeParams, Agenda, item) {
+ $scope.item = item; // do not use Agenda.binOne(...) so autoupdate is not activated
+ $scope.save = function (item) {
+ Agenda.save(item);
+ // TODO: redirect to list-view
+ };
+});
diff --git a/openslides/agenda/static/templates/agenda/item-detail.html b/openslides/agenda/static/templates/agenda/item-detail.html
new file mode 100644
index 000000000..09811fd77
--- /dev/null
+++ b/openslides/agenda/static/templates/agenda/item-detail.html
@@ -0,0 +1,2 @@
+
{{ item.get_title }}
+{{ item.text }}
diff --git a/openslides/agenda/static/templates/agenda/item-form.html b/openslides/agenda/static/templates/agenda/item-form.html
new file mode 100644
index 000000000..f2330908c
--- /dev/null
+++ b/openslides/agenda/static/templates/agenda/item-form.html
@@ -0,0 +1,8 @@
+{{ item.title }}
+Neuer Eintrag
+
+
diff --git a/openslides/agenda/static/templates/agenda/item-list.html b/openslides/agenda/static/templates/agenda/item-list.html
new file mode 100644
index 000000000..bb2485b15
--- /dev/null
+++ b/openslides/agenda/static/templates/agenda/item-list.html
@@ -0,0 +1,9 @@
+
+{{ gettext('New') }}
+{{ test_singular }}
+{{ test_plural }}
diff --git a/openslides/assignment/static/js/assignment/assignment.js b/openslides/assignment/static/js/assignment/assignment.js
new file mode 100644
index 000000000..9e4f2a219
--- /dev/null
+++ b/openslides/assignment/static/js/assignment/assignment.js
@@ -0,0 +1,68 @@
+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/templates/assignment/assignment-detail.html b/openslides/assignment/static/templates/assignment/assignment-detail.html
new file mode 100644
index 000000000..3b2050169
--- /dev/null
+++ b/openslides/assignment/static/templates/assignment/assignment-detail.html
@@ -0,0 +1,2 @@
+{{ assignment.name }}
+{{ assignment.description }}
diff --git a/openslides/assignment/static/templates/assignment/assignment-form.html b/openslides/assignment/static/templates/assignment/assignment-form.html
new file mode 100644
index 000000000..9e8e15f5f
--- /dev/null
+++ b/openslides/assignment/static/templates/assignment/assignment-form.html
@@ -0,0 +1,8 @@
+{{ assignment.name }}
+Neue Assignment
+
+
diff --git a/openslides/assignment/static/templates/assignment/assignment-list.html b/openslides/assignment/static/templates/assignment/assignment-list.html
new file mode 100644
index 000000000..404e8344a
--- /dev/null
+++ b/openslides/assignment/static/templates/assignment/assignment-list.html
@@ -0,0 +1,7 @@
+
+Neu
diff --git a/openslides/core/static/css/app.css b/openslides/core/static/css/app.css
new file mode 100644
index 000000000..ce7c324b3
--- /dev/null
+++ b/openslides/core/static/css/app.css
@@ -0,0 +1,376 @@
+/*
+ * OpenSlides default template styles of the web interface
+ */
+
+body {
+ background-color: #FBFBFB;
+}
+
+/* Header */
+#header {
+ background-color: #333333;
+ background-image: -moz-linear-gradient(top, #444444, #222222);
+ background-image: -ms-linear-gradient(top, #444444, #222222);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
+ background-image: -webkit-linear-gradient(top, #444444, #222222);
+ background-image: -o-linear-gradient(top, #444444, #222222);
+ background-image: linear-gradient(top, #444444, #222222);
+ box-shadow: 0 0 7px rgba(0,0,0,0.6);
+ border-radius: 0;
+ height: 35px;
+ margin-bottom: 20px;
+ padding: 7px 20px 0;
+ position: relative;
+}
+#header .logo img {
+ height: 30px;
+ padding-left: 3px;
+}
+#header .title {
+ font-size: 16px;
+ color: #999999;
+ position: absolute;
+ margin: 8px 0 0 50px;
+}
+#searchform {
+ margin-top: 0px;
+}
+
+footer {
+ margin-bottom: 20px;
+}
+
+/* Headings and Links */
+h1 {
+ border-bottom: 1px solid #EEEEEE;
+ margin: 0px 0 30px;
+ padding-bottom: 9px;
+}
+a:hover {
+ text-decoration: none;
+}
+
+/* Login page */
+#login-page.container {
+ width: 320px;
+ margin-top: 20px;
+}
+#login-page h2 {
+ margin-left: 30px;
+ margin-bottom: 20px;
+}
+#login-page h2 img {
+ width: 250px;
+}
+#login-page .well {
+ background-color: white;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px 6px 6px 6px;
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ margin-top: 20px;
+ padding-bottom: 20px;
+ line-height: 45px;
+}
+
+/* Log */
+#log {
+ padding-left: 14px;
+}
+
+/** Utils **/
+tr.offline td, li.offline {
+ background-color: #EAEAEA !important;
+}
+tr.activeline td, li.activeline {
+ background-color: #bed4de !important;
+}
+.nopadding {
+ padding: 0;
+}
+.alert form {
+ margin-bottom: 0;
+}
+tr.total td {
+ border-top: 1px solid #333333;
+}
+.nobr {
+ white-space: nowrap;
+}
+.right {
+ float: right;
+}
+.indentation {
+ margin-left: 12px;
+}
+.mini_width {
+ width: 1px;
+}
+/* show optional column */
+.optional {
+ display: auto;
+}
+.user_details fieldset {
+ margin-bottom: 10px;
+}
+.user_details legend {
+ margin-bottom: 5px;
+}
+.user_details label {
+ font-weight: bold;
+ margin: 10px 0 0 0;
+}
+.user_details label:after {
+ content: ":";
+}
+
+/** Colors **/
+.grey {
+ color: grey;
+}
+
+/** Forms **/
+input, textarea {
+ width: 320px;
+}
+.small-form input {
+ width: 55px;
+}
+.normal-form input {
+ width: 320px;
+}
+textarea {
+ height: 100px;
+}
+.help-inline {
+ font-size: 11px;
+}
+.errorlist{
+ margin: 0;
+}
+.errorlist li {
+ list-style: none outside none;
+}
+form .required label:after {
+ content: " *";
+}
+legend + .control-group {
+ margin-top: 0px !important;
+}
+#id_permissions {
+ height: 310px;
+ width: auto;
+}
+#id_users {
+ height: 110px;
+ width: auto;
+}
+#dataTable_filter input {
+ width: auto;
+}
+#dataTable {
+ clear: none;
+}
+#dataTable_wrapper .row-fluid:after {
+ clear: none;
+}
+.searchresults li {
+ margin-bottom: 15px;
+}
+.searchresults li .app {
+ color: #999999;
+}
+.highlighted {
+ font-weight: bold;
+}
+
+/* ckeditor plugin insertpre: textarea style */
+table.cke_dialog_contents textarea {
+ font-family: monospace !important;
+}
+
+
+/** Left sidebar navigation **/
+.leftmenu ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+.leftmenu ul ul {
+ display: none;
+ margin-left: 35px;
+ margin-top: -1px;
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
+}
+.leftmenu ul li {
+ display: block;
+ width: 100%;
+ line-height: 30px;
+}
+.leftmenu ul li a {
+ border-style: none solid solid;
+ border-width: 0 1px 1px;
+ border-color: #dddddd;
+ color: #666666;
+ display: block;
+ font-weight: bold;
+ background-color: #ffffff;
+ padding: 0;
+}
+.leftmenu ul li:first-child a {
+ border-top: 1px solid #DDDDDD;
+}
+.leftmenu ul li a .glyphicon {
+ display: inline-block;
+ background: #f9f9f9;
+ padding: 8px 10px 6px;
+ margin: 0 5px 0 0;
+ border-right: 1px solid #dddddd;
+}
+.leftmenu ul li a, .leftmenu ul li a .glyphicon {
+ -webkit-transition: background 0.2s ease-in-out;
+ -moz-transition: background 0.2s ease-in-out;
+ -ms-transition: background 0.2s ease-in-out;
+ -o-transition: background 0.2s ease-in-out;
+ transition: background 0.2s ease-in-out;
+}
+.leftmenu ul li a:hover {
+ background-color: #f5f5f5;
+ color: #000000;
+}
+.leftmenu ul li a:hover .glyphicon {
+ background-color: #efefef;
+}
+.leftmenu ul li.active a {
+ background-color: #333333;
+ color: #ffffff;
+}
+.leftmenu ul li.active a .glyphicon {
+ background-color: #333333;
+ border-right: 1px solid #444444;
+}
+.leftmenu ul li.hider a {
+ margin-top: 5px;
+ height: 20px;
+}
+.leftmenu.lefticon > ul {
+ width: 37px !important;
+}
+.leftmenu.lefticon ul ul {
+ position: absolute;
+ z-index: 20;
+ margin-top: -34px;
+}
+.leftmenu.lefticon > ul > li > a > span.text {
+ display: none;
+}
+.leftmenu.lefticon ul ul > li > a {
+ min-width: 200px !important;
+}
+.leftmenu.lefticon span.text {
+ padding-right: 15px;
+}
+
+/** Icons **/
+/* TODO: Move some of them to the respective apps. */
+.icon-assignment {
+ background: url("../img/glyphicons_041_charts.png") no-repeat !important;
+ width: 25px;
+ margin-left: 10px !important;
+}
+.leftmenu ul li.active a .glyphicon.icon-assignment {
+ background-image: url("../img/glyphicons_041_charts_white.png") !important;
+}
+.status_link .icon-on, .icon-checked-new {
+ background-image: url("../img/glyphicons_152_check.png");
+ background-position: 0;
+}
+.icon-checked-new_white {
+ background-image: url("../img/glyphicons_152_check_white.png");
+ background-position: 0;
+}
+.status_link .icon-off, .icon-unchecked-new {
+ background-image: url("../img/glyphicons_153_unchecked.png");
+ background-position: 0;
+}
+.icon-summary {
+ background-image: url("../img/glyphicons_154_more_windows.png");
+ background-position: 0;
+}
+.icon-summary.icon-white {
+ background-image: url("../img/glyphicons_154_more_windows_white.png");
+ background-position: 0;
+}
+.icon-login {
+ background-image: url("../img/glyphicons_044_keys.png");
+ background-position: 0;
+}
+.icon-group {
+ background-image: url("../img/glyphicons_043_group.png");
+ background-position: 0;
+}
+.icon-import {
+ background-image: url("../img/glyphicons_358_file_import.png");
+ background-position: 0;
+}
+.icon-delete {
+ background-image: url("../img/glyphicons_256_delete.png");
+ background-position: 0;
+}
+.icon-add-user {
+ background-image: url("../img/glyphicons_006_user_add.png");
+ background-position: 0;
+}
+.icon-clock {
+ background-image: url("../img/glyphicons_054_clock.png");
+ background-position: 0;
+}
+.icon-speaker {
+ background-image: url("../img/glyphicons_300_microphone.png");
+ background-position: 0;
+}
+
+/** Responsive **/
+@media (max-width: 767px) {
+ body {
+ padding: 0;
+ }
+ .row-fluid .leftmenu {
+ float: left;
+ width: auto;
+ }
+ #content {
+ margin: 0 5px 0 45px;
+ width: auto;
+ }
+ /* hide optional column */
+ .optional, #searchform #id_q {
+ display: none;
+ }
+ #searchform button {
+ border-radius: 4px;
+ }
+}
+
+@media (max-width: 480px) {
+ body {
+ padding: 0;
+ }
+ .row-fluid .leftmenu {
+ float: left;
+ width: auto;
+ }
+ #header {
+ padding: 7px 5px 0;
+ }
+ #content {
+ margin: 0 5px 0 45px;
+ width: auto;
+ }
+ /* hide optional column */
+ .optional, .optional-small{
+ display: none;
+ }
+ #menu-overview .manage, .agenda_list .manage {
+ width: 50px !important;
+ }
+}
diff --git a/openslides/core/static/js/app.js b/openslides/core/static/js/app.js
new file mode 100644
index 000000000..ad815ab02
--- /dev/null
+++ b/openslides/core/static/js/app.js
@@ -0,0 +1,105 @@
+angular.module('OpenSlidesApp', [
+ 'ngRoute',
+ 'angular-data.DS',
+ 'ngCookies',
+ 'OpenSlidesApp.agenda',
+ 'OpenSlidesApp.assignment',
+ 'OpenSlidesApp.user',
+])
+
+.config(function($routeProvider, $locationProvider) {
+ $routeProvider
+ .when('/', {
+ templateUrl: 'static/templates/dashboard.html'
+ })
+ .otherwise({redirectTo: '/'});
+ $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/'
+ });
+});
diff --git a/openslides/core/static/templates/dashboard.html b/openslides/core/static/templates/dashboard.html
new file mode 100644
index 000000000..8ea14ece0
--- /dev/null
+++ b/openslides/core/static/templates/dashboard.html
@@ -0,0 +1,4 @@
+Dashboard
+Assignments
+Agenda
+User
diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html
new file mode 100644
index 000000000..e1872c894
--- /dev/null
+++ b/openslides/core/static/templates/index.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+ OpenSlides
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/openslides/core/views.py b/openslides/core/views.py
index 90a6150a1..45fd3f86b 100644
--- a/openslides/core/views.py
+++ b/openslides/core/views.py
@@ -1,5 +1,6 @@
from django.conf import settings
from django.contrib import messages
+from django.contrib.staticfiles import finders
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.db import IntegrityError
@@ -8,6 +9,7 @@ from django.template import RequestContext
from django.utils.importlib import import_module
from django.utils.translation import ugettext as _
from haystack.views import SearchView as _SearchView
+from django.http import HttpResponse
from openslides import get_version as get_openslides_version
from openslides import get_git_commit_id, RELEASE
@@ -24,6 +26,21 @@ from .exceptions import TagException
from .serializers import CustomSlideSerializer, TagSerializer
+class IndexView(utils_views.View):
+ """
+ The primary view for OpenSlides using AngularJS.
+
+ The default base template is 'openslides/core/static/templates/index.html'.
+ You can override it by simply adding a custom 'templates/index.html' file
+ to the custom staticfiles directory. See STATICFILES_DIRS in settings.py.
+ """
+
+ def get(self, *args, **kwargs):
+ with open(finders.find('templates/index.html')) as f:
+ content = f.read()
+ return HttpResponse(content)
+
+
class DashboardView(utils_views.AjaxMixin, utils_views.TemplateView):
"""
Overview over all possible slides, the overlays and a live view: the
diff --git a/openslides/locale/de/javascript.po b/openslides/locale/de/javascript.po
new file mode 100644
index 000000000..e517fab8c
--- /dev/null
+++ b/openslides/locale/de/javascript.po
@@ -0,0 +1,220 @@
+# Language file of OpenSlides used by transifex:
+# https://www.transifex.com/projects/p/openslides/
+
+msgid ""
+msgstr ""
+"Project-Id-Version: 2.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-02-01 11:55+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"plural_forms: nplurals=2; plural=(n != 1);"
+
+#: openslides/agenda/static/js/agenda/agenda.js:51
+msgid "test"
+msgid_plural "tests"
+msgstr[0] "test-de"
+msgstr[1] "tests-de"
+
+#: openslides/agenda/static/js/agenda.js:14
+#, javascript-format
+msgid ", of which %s are hidden."
+msgstr ""
+
+#: openslides/agenda/static/templates/agenda/item-list.html:7
+msgid "New"
+msgstr "Neu"
+
+#: openslides/core/static/js/jquery/datepicker-config.js:2
+#: openslides/core/static/js/jquery/datepicker-config.js:32
+msgid "en"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:3
+msgid "previous month"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:4
+msgid "next month"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:6
+msgid "January"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:6
+msgid "February"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:6
+msgid "March"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:7
+msgid "April"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:7
+#: openslides/core/static/js/jquery/datepicker-config.js:13
+msgid "May"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:7
+msgid "June"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:8
+msgid "July"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:8
+msgid "August"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:8
+msgid "September"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:9
+msgid "October"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:9
+msgid "November"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:9
+msgid "December"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:12
+msgid "Jan"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:12
+msgid "Feb"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:12
+msgid "Mar"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:13
+msgid "Apr"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:13
+msgid "Jun"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:14
+msgid "Jul"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:14
+msgid "Aug"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:14
+msgid "Sep"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:15
+msgid "Oct"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:15
+msgid "Nov"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:15
+msgid "Dec"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Sunday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Monday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Tuesday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Wednesday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:19
+msgid "Thursday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:19
+msgid "Friday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:19
+msgid "Saturday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "Su"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "Mo"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "Tu"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "We"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:23
+#: openslides/core/static/js/jquery/datepicker-config.js:27
+msgid "Th"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:23
+#: openslides/core/static/js/jquery/datepicker-config.js:27
+msgid "Fr"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:23
+#: openslides/core/static/js/jquery/datepicker-config.js:27
+msgid "Sa"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:38
+msgid "Time"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:39
+msgid "Hour"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:40
+msgid "Minute"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:41
+msgid "Current time"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:42
+msgid "Close"
+msgstr ""
diff --git a/openslides/locale/en/javascript.po b/openslides/locale/en/javascript.po
new file mode 100644
index 000000000..18c00c475
--- /dev/null
+++ b/openslides/locale/en/javascript.po
@@ -0,0 +1,220 @@
+# Language file of OpenSlides used by transifex:
+# https://www.transifex.com/projects/p/openslides/
+
+msgid ""
+msgstr ""
+"Project-Id-Version: 2.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-02-01 11:55+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"plural_forms: nplurals=2; plural=(n != 1);"
+
+#: openslides/agenda/static/js/agenda/agenda.js:51
+msgid "test"
+msgid_plural "tests"
+msgstr[0] ""
+msgstr[1] ""
+
+#: openslides/agenda/static/js/agenda.js:14
+#, javascript-format
+msgid ", of which %s are hidden."
+msgstr ""
+
+#: openslides/agenda/static/templates/agenda/item-list.html:7
+msgid "New"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:2
+#: openslides/core/static/js/jquery/datepicker-config.js:32
+msgid "en"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:3
+msgid "previous month"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:4
+msgid "next month"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:6
+msgid "January"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:6
+msgid "February"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:6
+msgid "March"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:7
+msgid "April"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:7
+#: openslides/core/static/js/jquery/datepicker-config.js:13
+msgid "May"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:7
+msgid "June"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:8
+msgid "July"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:8
+msgid "August"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:8
+msgid "September"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:9
+msgid "October"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:9
+msgid "November"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:9
+msgid "December"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:12
+msgid "Jan"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:12
+msgid "Feb"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:12
+msgid "Mar"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:13
+msgid "Apr"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:13
+msgid "Jun"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:14
+msgid "Jul"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:14
+msgid "Aug"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:14
+msgid "Sep"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:15
+msgid "Oct"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:15
+msgid "Nov"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:15
+msgid "Dec"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Sunday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Monday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Tuesday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:18
+msgid "Wednesday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:19
+msgid "Thursday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:19
+msgid "Friday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:19
+msgid "Saturday"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "Su"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "Mo"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "Tu"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:22
+#: openslides/core/static/js/jquery/datepicker-config.js:26
+msgid "We"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:23
+#: openslides/core/static/js/jquery/datepicker-config.js:27
+msgid "Th"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:23
+#: openslides/core/static/js/jquery/datepicker-config.js:27
+msgid "Fr"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:23
+#: openslides/core/static/js/jquery/datepicker-config.js:27
+msgid "Sa"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:38
+msgid "Time"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:39
+msgid "Hour"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:40
+msgid "Minute"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:41
+msgid "Current time"
+msgstr ""
+
+#: openslides/core/static/js/jquery/datepicker-config.js:42
+msgid "Close"
+msgstr ""
diff --git a/openslides/urls.py b/openslides/urls.py
index 31191e2ae..7867b62f4 100644
--- a/openslides/urls.py
+++ b/openslides/urls.py
@@ -1,23 +1,24 @@
-from django.conf import settings
from django.conf.urls import include, patterns, url
-from openslides.core.views import ErrorView
-from openslides.utils.plugins import get_urlpatterns
+from openslides.core.views import IndexView, ErrorView
from openslides.utils.rest_api import router
handler403 = ErrorView.as_view(status_code=403)
handler404 = ErrorView.as_view(status_code=404)
handler500 = ErrorView.as_view(status_code=500)
-urlpatterns = []
+urlpatterns = patterns(
+ '',
+ url(r'^rest/', include(router.urls)),
+ # TODO: add "special" urls, for example pdf views etc.
+ url(r'^user.*', IndexView.as_view()),
+)
+
+
+# Deprecated.
js_info_dict = {'packages': []}
-for plugin in settings.INSTALLED_PLUGINS:
- plugin_urlpatterns = get_urlpatterns(plugin)
- if plugin_urlpatterns:
- urlpatterns += plugin_urlpatterns
- js_info_dict['packages'].append(plugin)
urlpatterns += patterns(
'',
@@ -32,13 +33,6 @@ urlpatterns += patterns(
(r'^ckeditor/', include('ckeditor.urls')),
)
-urlpatterns += patterns(
- '',
- url(r'^api/', include(router.urls)),
- # TODO: Remove the next line if you are sure.
- # url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
-)
-
# TODO: move this patterns into core or the participant app
from openslides.users.views import UserSettingsView, UserPasswordSettingsView
urlpatterns += patterns(
@@ -53,11 +47,11 @@ urlpatterns += patterns(
'django.contrib.auth.views.logout_then_login',
name='user_logout'),
- url(r'^usersettings/$',
+ url(r'^myusersettings/$',
UserSettingsView.as_view(),
name='user_settings'),
- url(r'^usersettings/changepassword/$',
+ url(r'^myusersettings/changepassword/$',
UserPasswordSettingsView.as_view(),
name='password_change'),
)
diff --git a/openslides/users/static/js/user/user.js b/openslides/users/static/js/user/user.js
new file mode 100644
index 000000000..eba4e3a08
--- /dev/null
+++ b/openslides/users/static/js/user/user.js
@@ -0,0 +1,91 @@
+angular.module('OpenSlidesApp.user', [])
+
+.config(['$routeProvider', function($routeProvider) {
+ $routeProvider
+ .when('/user', {
+ templateUrl: 'static/templates/user/user-list.html',
+ controller: 'UserListCtrl',
+ 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',
+ resolve: {
+ user: function(User, $route) {
+ return User.find($route.current.params.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);
+ }
+ }
+ });
+}])
+
+.factory('User', function(DS) {
+ return DS.defineResource({
+ name: 'users/user',
+ endpoint: '/rest/users/user/',
+ methods: {
+ get_short_name: function() {
+ // should be the same as in the python user model.
+ var firstName = _.trim(this.first_name),
+ lastName = _.trim(this.last_name),
+ name;
+
+ if (firstName && lastName) {
+ // TODO: check config
+ name = [firstName, lastName].join(' ');
+ } else {
+ name = firstName || lastName || this.username;
+ }
+ return name;
+ },
+ }
+ });
+})
+
+.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/'
+ });
+})
+
+.controller('UserListCtrl', function($scope, User, i18n) {
+ User.bindAll($scope, 'users');
+})
+
+.controller('UserDetailCtrl', function($scope, $routeParams, User) {
+ User.bindOne($scope, 'user', $routeParams.id);
+})
+
+.controller('UserCreateCtrl', function($scope, User) {
+ $scope.user = {};
+ $scope.save = function (user) {
+ User.create(user);
+ // TODO: redirect to list-view
+ };
+})
+
+.controller('UserUpdateCtrl', function($scope, $routeParams, User, user) {
+ $scope.user = user; // do not use Agenda.binOne(...) so autoupdate is not activated
+ $scope.save = function (user) {
+ User.save(user);
+ // TODO: redirect to list-view
+ };
+});
diff --git a/openslides/users/static/templates/user/user-detail.html b/openslides/users/static/templates/user/user-detail.html
new file mode 100644
index 000000000..9ed8dee2d
--- /dev/null
+++ b/openslides/users/static/templates/user/user-detail.html
@@ -0,0 +1,3 @@
+Persönliche Daten
+{{ user.get_short_name() }}
+
diff --git a/openslides/users/static/templates/user/user-form.html b/openslides/users/static/templates/user/user-form.html
new file mode 100644
index 000000000..dc6afa301
--- /dev/null
+++ b/openslides/users/static/templates/user/user-form.html
@@ -0,0 +1,8 @@
+{{ user.get_short_name() }}
+{{ gettext("New User") }}
+
+
diff --git a/openslides/users/static/templates/user/user-list.html b/openslides/users/static/templates/user/user-list.html
new file mode 100644
index 000000000..c3cf7075f
--- /dev/null
+++ b/openslides/users/static/templates/user/user-list.html
@@ -0,0 +1,7 @@
+
+{{ gettext('New') }}
diff --git a/openslides/utils/autoupdate.py b/openslides/utils/autoupdate.py
index 5b4a854d8..841bce4a0 100644
--- a/openslides/utils/autoupdate.py
+++ b/openslides/utils/autoupdate.py
@@ -101,9 +101,14 @@ class OpenSlidesSockJSConnection(SockJSConnection):
# Send out internal HTTP request to get data from the REST api.
for waiter in cls.waiters:
# Read waiter's former cookies and parse session cookie to new header object.
- session_cookie = waiter.connection_info.cookies[settings.SESSION_COOKIE_NAME]
headers = HTTPHeaders()
- headers.add('Cookie', '%s=%s' % (settings.SESSION_COOKIE_NAME, session_cookie.value))
+ try:
+ session_cookie = waiter.connection_info.cookies[settings.SESSION_COOKIE_NAME]
+ except KeyError:
+ # There is no session cookie
+ pass
+ else:
+ headers.add('Cookie', '%s=%s' % (settings.SESSION_COOKIE_NAME, session_cookie.value))
# Setup uncompressed request.
request = HTTPRequest(
url=url,
diff --git a/openslides/utils/rest_api.py b/openslides/utils/rest_api.py
index a57c7f7cc..262774042 100644
--- a/openslides/utils/rest_api.py
+++ b/openslides/utils/rest_api.py
@@ -45,7 +45,7 @@ def get_collection_and_id_from_url(url):
Raises OpenSlidesError if the URL is invalid.
"""
path = urlparse(url).path
- match = re.match(r'^/api/(?P[-\w]+/[-\w]+)/(?P[-\w]+)/$', path)
+ match = re.match(r'^/rest/(?P[-\w]+/[-\w]+)/(?P[-\w]+)/$', path)
if not match:
raise OpenSlidesError('Invalid REST api URL: %s' % url)
- return match.group('name'), match.group('id')
+ return match.group('collection'), match.group('id')
diff --git a/package.json b/package.json
index a196eb79a..3923e7480 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,15 @@
{
- "name": "OpenSlides",
- "private": true,
- "devDependencies": {
- "bower": "~1.3.12",
- "gulp": "~3.8.10",
- "gulp-concat": "~2.4.3",
- "gulp-if": "~1.2.5",
- "gulp-minify-css": "~0.3.11",
- "gulp-uglify": "~1.0.2",
- "main-bower-files": "~2.4.1",
- "yargs": "~1.3.3"
- }
+ "name": "OpenSlides",
+ "private": true,
+ "devDependencies": {
+ "bower": "~1.3.12",
+ "gulp": "~3.8.10",
+ "gulp-concat": "~2.4.3",
+ "gulp-if": "~1.2.5",
+ "gulp-minify-css": "~0.3.11",
+ "gulp-uglify": "~1.0.2",
+ "main-bower-files": "~2.4.1",
+ "yargs": "~1.3.3",
+ "po2json": "~0.3.2"
+ }
}
diff --git a/tests/integration/users/__init__.py b/tests/integration/users/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/integration/users/test_views.py b/tests/integration/users/test_views.py
deleted file mode 100644
index f978768d5..000000000
--- a/tests/integration/users/test_views.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from openslides.utils.test import TestCase
-from django.test.client import Client
-
-
-class UserViews(TestCase):
- def setUp(self):
- self.client = Client()
- self.client.login(username='admin', password='admin')
-
- def test_user_list(self):
- response = self.client.get('/user/')
-
- self.assertTemplateUsed(response, 'users/user_list.html')
- self.assertEqual(response.status_code, 200)
diff --git a/tests/old/plugin_api/test_plugin_api.py b/tests/old/plugin_api/test_plugin_api.py
index df4951ec2..3e0d58ee6 100644
--- a/tests/old/plugin_api/test_plugin_api.py
+++ b/tests/old/plugin_api/test_plugin_api.py
@@ -1,8 +1,5 @@
-from imp import reload
-
from django.test.client import Client
from django.test.utils import override_settings
-from django.core.urlresolvers import clear_url_caches
from openslides.utils.test import TestCase
@@ -19,13 +16,6 @@ class TestPluginOne(TestCase):
self.assertContains(response, '(Short description of test plugin Sah9aiQuae5hoocai7ai)')
self.assertContains(response, '– Version test_version_string_MoHonepahfofiree6Iej')
- def test_url_patterns(self):
- from openslides import urls
- reload(urls)
- clear_url_caches()
- response = self.admin_client.get('/test_plugin_one_url_Eexea4nie1fexaax3oX7/')
- self.assertRedirects(response, '/version/')
-
@override_settings(INSTALLED_PLUGINS=('tests.old.plugin_api.test_plugin_two',))
class TestPluginTwo(TestCase):
diff --git a/tests/old/users/test_umlaut_user.py b/tests/old/users/test_umlaut_user.py
deleted file mode 100644
index c245c434b..000000000
--- a/tests/old/users/test_umlaut_user.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from django.test.client import Client
-from django.test.utils import override_settings
-
-from openslides.users.models import Group, User
-from openslides.utils.test import TestCase
-
-
-@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.PBKDF2PasswordHasher',))
-class TestUmlautUser(TestCase):
- """
- Tests persons with umlauts in there name.
- """
-
- def setUp(self):
- self.user = User.objects.create_user('äöü', 'äöü')
- self.client = Client()
- self.client.login(username='äöü', password='äöü')
-
- def test_login(self):
- client = Client()
- response = client.post('/login/', {'username': 'äöü',
- 'password': 'äöüß'})
- self.assertEqual(response.status_code, 200)
-
- response = client.post('/login/', {'username': 'äöü',
- 'password': 'äöü'})
- self.assertEqual(response.status_code, 302)
-
- def test_logout(self):
- response = self.client.get('/logout/')
- self.assertEqual(response.status_code, 302)
-
- def test_permission(self):
- response = self.client.get('/user/1/edit/')
- self.assertEqual(response.status_code, 403)
-
- self.user.groups.add(Group.objects.get(pk=4))
-
- response = self.client.get('/user/1/edit/')
- self.assertEqual(response.status_code, 200)
diff --git a/tests/old/users/test_views.py b/tests/old/users/test_views.py
deleted file mode 100644
index c1f439423..000000000
--- a/tests/old/users/test_views.py
+++ /dev/null
@@ -1,223 +0,0 @@
-import re
-
-from django.contrib.auth.models import Permission
-from django.contrib.contenttypes.models import ContentType
-from django.test.client import Client
-
-from openslides.config.api import config
-from openslides.users.api import get_registered_group
-from openslides.users.models import Group, User
-from openslides.utils.test import TestCase
-
-
-class UserViews(TestCase):
- """
- Tests some views for users.
- """
- def setUp(self):
- self.admin = User.objects.get(pk=1)
- self.client = Client()
- self.client.login(username='admin', password='admin')
-
- def test_create(self):
- response = self.client.get('/user/new/')
-
- self.assertTemplateUsed(response, 'users/user_form.html')
- self.assertContains(response, 'New user')
- response = self.client.post('/user/new/', {'first_name': 'test_name_ho8hui2niz4nohSupahb'})
- self.assertRedirects(response, '/user/')
-
- def test_create_multiple(self):
- response = self.client.get('/user/new_multiple/')
- self.assertTemplateUsed(response, 'users/user_form_multiple.html')
- self.assertContains(response, 'New multiple users')
- self.assertEqual(User.objects.count(), 1)
- block = ('first_name_ksdjfhkjsdhf75utgeitrten last_name_khonizt958zh8fh\n'
- 'first_name_1_bmgnf7z8ru first_name_2_kjc98vivt last_name_dfg76kjkjuibv')
- response = self.client.post('/user/new_multiple/',
- {'users_block': block})
- self.assertEqual(User.objects.count(), 3)
-
- def test_update(self):
- response = self.client.get('/user/1/edit/')
-
- self.assertTemplateUsed(response, 'users/user_form.html')
- self.assertContains(response, 'Edit user')
-
- response = self.client.post(
- '/user/1/edit/',
- {'username': 'test_name_unaewae5Ir0saijeac2I',
- 'first_name': 'test_name_aJi5jaizaVingaeF3Ohj',
- 'groups': '4',
- 'is_active': 'yes'})
-
- self.assertRedirects(response, '/user/')
-
- def test_activate(self):
- response = self.client.get('/user/1/status/activate/')
- self.assertEqual(response.status_code, 302)
-
- def test_reset_password(self):
- self.admin.default_password = new_password = 'password_ohweleeh1Shee5wibo1I'
- self.admin.save()
- self.client.post('/user/1/reset_password/', {'yes': 'yes'})
- self.assertTrue(self.client.login(username='admin', password=new_password))
-
-
-class GroupViews(TestCase):
- """
- Tests the detail view for groups and later also the other views.
- """
- def setUp(self):
- self.user_1 = User.objects.get(pk=1)
- self.user_1.first_name = 'admins_first_name'
- self.user_1.save()
-
- self.user_2 = User.objects.create(last_name='uquahx3Wohtieph9baer',
- first_name='aWei4ien6Se0vie0xeiv',
- username='aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer')
- self.delegate = Group.objects.get(pk=3)
- self.user_1.groups.add(self.delegate)
- self.user_2.groups.add(self.delegate)
-
- self.client = Client()
- self.client.login(username='admin', password='admin')
-
- def test_detail(self):
- response = self.client.get('/user/group/3/')
- pattern = r'Administrator, admins_first_name|uquahx3Wohtieph9baer, aWei4ien6Se0vie0xeiv'
- match = re.findall(pattern, response.content.decode('utf8'))
- self.assertEqual(match[0], 'Administrator, admins_first_name')
- self.assertEqual(match[1], 'uquahx3Wohtieph9baer, aWei4ien6Se0vie0xeiv')
-
- config['users_sort_users_by_first_name'] = True
- self.assertTrue(config['users_sort_users_by_first_name'])
- response = self.client.get('/user/group/3/')
- pattern = r'admins_first_name Administrator|aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer'
- match = re.findall(pattern, response.content.decode('utf8'))
- self.assertEqual(match[1], 'admins_first_name Administrator')
- self.assertEqual(match[0], 'aWei4ien6Se0vie0xeiv uquahx3Wohtieph9baer')
-
- def test_create(self):
- response = self.client.get('/user/group/new/')
-
- self.assertTemplateUsed(response, 'users/group_form.html')
- self.assertContains(response, 'New group')
-
- response = self.client.post('/user/group/new/', {'name': 'test_group_name_Oeli1aeXoobohv8eikai'})
-
- self.assertRedirects(response, '/user/group/')
-
- def test_update(self):
- response = self.client.get('/user/group/1/edit/')
-
- self.assertTemplateUsed(response, 'users/group_form.html')
- self.assertContains(response, 'Edit group')
-
- response = self.client.post('/user/group/1/edit/', {'name': 'test_group_name_ahFeicoz5jedie4Fop0U'})
-
- self.assertRedirects(response, '/user/group/')
-
-
-class LockoutProtection(TestCase):
- """
- Tests that a manager user can not lockout himself by doing
- something that removes his last permission to manage users. Tests
- also that he can see the user app (although there is no absolute
- protection).
- """
- def setUp(self):
- self.user = User.objects.get(pk=1)
- self.user.groups.add(Group.objects.get(pk=4))
- self.client = Client()
- self.client.login(username='admin', password='admin')
- self.assertEqual(User.objects.count(), 1)
- self.assertEqual(Group.objects.count(), 4)
- self.assertFalse(self.user.is_superuser)
-
- def test_delete_yourself(self):
- response = self.client.get('/user/1/del/')
- self.assertRedirects(response, '/user/1/')
- self.assertTrue('You can not delete yourself.' in response.cookies['messages'].value)
- response = self.client.post('/user/1/del/',
- {'yes': 'yes'})
- self.assertTrue('You can not delete yourself.' in response.cookies['messages'].value)
- self.assertRedirects(response, '/user/')
- self.assertEqual(User.objects.count(), 1)
-
- def test_delete_last_manager_group(self):
- response = self.client.get('/user/group/4/del/')
- self.assertRedirects(response, '/user/group/4/')
- self.assertTrue('You can not delete the last group containing the permission '
- 'to manage users you are in.' in response.cookies['messages'].value)
- response = self.client.post('/user/group/4/del/',
- {'yes': 'yes'})
- self.assertTrue('You can not delete the last group containing the permission '
- 'to manage users you are in.' in response.cookies['messages'].value)
- self.assertRedirects(response, '/user/group/')
- self.assertEqual(Group.objects.count(), 4)
-
- def test_remove_user_from_last_manager_group_via_UserUpdateView(self):
- response = self.client.post('/user/1/edit/',
- {'username': 'arae0eQu8eeghoogeik0',
- 'groups': '3'})
- self.assertFormError(
- response=response,
- form='form',
- field=None,
- errors='You can not remove the last group containing the permission to manage users.')
-
- def test_remove_user_from_last_manager_group_via_GroupUpdateView(self):
- User.objects.get_or_create(username='foo', pk=2)
- response = self.client.post('/user/group/4/edit/',
- {'name': 'ChaeFaev4leephaiChae',
- 'users': '2'})
- self.assertFormError(
- response=response,
- form='form',
- field=None,
- errors='You can not remove yourself from the last group containing the permission to manage users.')
-
- def test_remove_perm_from_last_manager_group(self):
- response = self.client.post('/user/group/4/edit/',
- {'name': 'ChaeFaev4leephaiChae',
- 'users': '1',
- 'permissions': []})
- self.assertFormError(
- response=response,
- form='form',
- field=None,
- errors='You can not remove the permission to manage users from the last group you are in.')
-
- def test_remove_permission_user_can_see_name_from_registered(self):
- self.assertTrue(self.user.has_perm('users.can_see_name'))
- # Remove perm from registered group
- can_see_perm = Permission.objects.get(
- content_type=ContentType.objects.get(app_label='users', model='user'),
- codename='can_see_name')
- get_registered_group().permissions.remove(can_see_perm)
- # Reload user
- self.user = User.objects.get(pk=1)
- self.assertTrue(self.user.has_perm('users.can_see_name'))
-
-
-class TestUserSettings(TestCase):
- def setUp(self):
- self.admin = User.objects.get(pk=1)
- self.admin_client = Client()
- self.admin_client.login(username='admin', password='admin')
-
- def test_get(self):
- response = self.admin_client.get('/usersettings/')
- self.assertEqual(response.status_code, 200)
-
- def test_post(self):
- response = self.admin_client.post('/usersettings/', {
- 'username': 'new_name',
- 'language': 'de'})
-
- self.assertRedirects(response, '/usersettings/')
-
- admin = User.objects.get(pk=1)
-
- self.assertEqual(admin.username, 'new_name')