angular single page application
This commit is contained in:
parent
afc295b2ec
commit
7171a71919
20
bower.json
20
bower.json
@ -2,9 +2,21 @@
|
||||
"name": "OpenSlides",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap": "3.3.1",
|
||||
"datatables-bootstrap3-plugin": "0.2.0",
|
||||
"jquery": "1.11.2",
|
||||
"jquery.cookie" : "1.4.1"
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
70
openslides/agenda/static/js/agenda/agenda.js
Normal file
70
openslides/agenda/static/js/agenda/agenda.js
Normal file
@ -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
|
||||
};
|
||||
});
|
@ -0,0 +1,2 @@
|
||||
<h1>{{ item.get_title }}</h1>
|
||||
{{ item.text }}
|
8
openslides/agenda/static/templates/agenda/item-form.html
Normal file
8
openslides/agenda/static/templates/agenda/item-form.html
Normal file
@ -0,0 +1,8 @@
|
||||
<h1 ng-if="item.id">{{ item.title }}</h1>
|
||||
<h1 ng-if="!item.id">Neuer Eintrag</h1>
|
||||
|
||||
<form>
|
||||
Titel: <input type="text" ng-model="item.title"><br>
|
||||
Text:<br> <textarea ng-model="item.text"></textarea><br>
|
||||
<input type="submit" ng-click="save(item)" value="Save" />
|
||||
</form>
|
9
openslides/agenda/static/templates/agenda/item-list.html
Normal file
9
openslides/agenda/static/templates/agenda/item-list.html
Normal file
@ -0,0 +1,9 @@
|
||||
<ul>
|
||||
<li ng-repeat="item in items">
|
||||
<a ng-href="agenda/{{ item.id }}/">{{ item.get_title }}</a>
|
||||
<a ng-href="agenda/{{ item.id }}/edit/">Bearbeiten</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a ng-href="agenda/new/">{{ gettext('New') }}</a>
|
||||
{{ test_singular }}
|
||||
{{ test_plural }}
|
68
openslides/assignment/static/js/assignment/assignment.js
Normal file
68
openslides/assignment/static/js/assignment/assignment.js
Normal file
@ -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
|
||||
};
|
||||
});
|
||||
|
@ -0,0 +1,2 @@
|
||||
<h1>{{ assignment.name }}</h1>
|
||||
{{ assignment.description }}
|
@ -0,0 +1,8 @@
|
||||
<h1 ng-if="assignment.id">{{ assignment.name }}</h1>
|
||||
<h1 ng-if="!assignment.id">Neue Assignment</h1>
|
||||
|
||||
<form>
|
||||
Titel: <input type="text" ng-model="assignment.name"><br>
|
||||
Beschreibung:<br> <textarea ng-model="assignment.description"></textarea><br>
|
||||
<input type="submit" ng-click="save(assignment)" value="Save" />
|
||||
</form>
|
@ -0,0 +1,7 @@
|
||||
<ul>
|
||||
<li ng-repeat="assignment in assignments">
|
||||
<a ng-href="assignment/{{ assignment.id }}/">{{ assignment.name }}</a>
|
||||
<a ng-href="assignment/{{ assignment.id }}/edit/">Bearbeiten</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a ng-href="assignment/new/">Neu</a>
|
376
openslides/core/static/css/app.css
Normal file
376
openslides/core/static/css/app.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
105
openslides/core/static/js/app.js
Normal file
105
openslides/core/static/js/app.js
Normal file
@ -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/'
|
||||
});
|
||||
});
|
4
openslides/core/static/templates/dashboard.html
Normal file
4
openslides/core/static/templates/dashboard.html
Normal file
@ -0,0 +1,4 @@
|
||||
<h1>Dashboard</h1>
|
||||
<a ng-href="assignment">Assignments</a>
|
||||
<a ng-href="agenda">Agenda</a>
|
||||
<a ng-href="user">User</a>
|
101
openslides/core/static/templates/index.html
Normal file
101
openslides/core/static/templates/index.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html lang="en" ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html lang="en" ng-app="OpenSlidesApp" class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<base href="/">
|
||||
<title>OpenSlides</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="static/css/openslides-libs.css">
|
||||
<link rel="stylesheet" href="static/css/app.css">
|
||||
<script src="static/js/openslides-libs.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navbar -->
|
||||
<nav id="header" class="navbar">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a href="/" class="logo"><img src="/static/img/logo.png" alt="OpenSlides" /></a>
|
||||
<span class="title optional">{{ config('event_name') }}</span>
|
||||
</div>
|
||||
{% block loginbutton %}
|
||||
<div class="navbar-right">
|
||||
<!-- login/logout button -->
|
||||
<div class="btn-group">
|
||||
{% if user.is_authenticated %}
|
||||
<a href="#" data-toggle="dropdown" class="btn btn-default dropdown-toggle">
|
||||
<span class="glyphicon glyphicon-user" aria-hidden="true"></span>
|
||||
<span class="optional-small">{{ user.username }}</span>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li><a href="{% url 'user_settings' %}">
|
||||
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
|
||||
{{ gettext("Edit profile") }}</a></li>
|
||||
<li><a href="{% url 'password_change' %}">
|
||||
<span class="glyphicon glyphicon-lock" aria-hidden="true"></span>
|
||||
{% trans "Change password" %}</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="{% url 'user_logout' %}">
|
||||
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span>
|
||||
{% trans "Logout" %}</a></li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<a href="{% url 'user_login' %}" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-log-in" aria-hidden="true"></span>
|
||||
{{ gettext("Login") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</nav>
|
||||
<!-- Container -->
|
||||
<div class="container-fluid" id="container">
|
||||
<div class="row">
|
||||
|
||||
<!-- Sidebar navigation (main menu) -->
|
||||
<div class="col-md-2 leftmenu lefticon">
|
||||
<ul>
|
||||
{% for entry in main_menu_entries %}
|
||||
<li{% if entry.is_active %} class="active"{% endif %}>
|
||||
<a href="{{ entry.get_url }}" class="tooltip-right">
|
||||
<!--TODO-->
|
||||
<span class="glyphicon {{ entry.get_icon_css_class }}" aria-hidden="true"></span>
|
||||
<span class="text">{{ entry }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div id="content" class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<footer>
|
||||
<small>
|
||||
© Copyright 2011–2015 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a> | <a href="{% url 'core_version' %}">Version</a>
|
||||
</small>
|
||||
</footer>
|
||||
</div><!--/#content-->
|
||||
|
||||
</div><!--/.row-->
|
||||
</div><!--/#container-->
|
||||
|
||||
|
||||
<script src="static/js/app.js"></script>
|
||||
<script src="static/js/agenda/agenda.js"></script>
|
||||
<script src="static/js/assignment/assignment.js"></script>
|
||||
<script src="static/js/user/user.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -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
|
||||
|
220
openslides/locale/de/javascript.po
Normal file
220
openslides/locale/de/javascript.po
Normal file
@ -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 <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\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 ""
|
220
openslides/locale/en/javascript.po
Normal file
220
openslides/locale/en/javascript.po
Normal file
@ -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 <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\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 ""
|
@ -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'),
|
||||
)
|
||||
|
91
openslides/users/static/js/user/user.js
Normal file
91
openslides/users/static/js/user/user.js
Normal file
@ -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
|
||||
};
|
||||
});
|
3
openslides/users/static/templates/user/user-detail.html
Normal file
3
openslides/users/static/templates/user/user-detail.html
Normal file
@ -0,0 +1,3 @@
|
||||
<h1>Persönliche Daten</h1>
|
||||
{{ user.get_short_name() }}
|
||||
|
8
openslides/users/static/templates/user/user-form.html
Normal file
8
openslides/users/static/templates/user/user-form.html
Normal file
@ -0,0 +1,8 @@
|
||||
<h1 ng-if="user.id">{{ user.get_short_name() }}</h1>
|
||||
<h1 ng-if="!user.id">{{ gettext("New User") }}</h1>
|
||||
|
||||
<form>
|
||||
firstname: <input type="text" ng-model="user.first_name"><br>
|
||||
lastname: <input type="text" ng-model="user.last_name"><br>
|
||||
<input type="submit" ng-click="save(user)" value="Save" />
|
||||
</form>
|
7
openslides/users/static/templates/user/user-list.html
Normal file
7
openslides/users/static/templates/user/user-list.html
Normal file
@ -0,0 +1,7 @@
|
||||
<ul>
|
||||
<li ng-repeat="user in users">
|
||||
<a ng-href="user/{{ user.id }}/">{{ user.get_short_name() }}</a>
|
||||
<a ng-href="user/{{ user.id }}/edit/">{{ gettext('Edit') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a ng-href="user/new/">{{ gettext('New') }}</a>
|
@ -101,8 +101,13 @@ 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()
|
||||
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(
|
||||
|
@ -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<name>[-\w]+/[-\w]+)/(?P<id>[-\w]+)/$', path)
|
||||
match = re.match(r'^/rest/(?P<collection>[-\w]+/[-\w]+)/(?P<id>[-\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')
|
||||
|
@ -9,6 +9,7 @@
|
||||
"gulp-minify-css": "~0.3.11",
|
||||
"gulp-uglify": "~1.0.2",
|
||||
"main-bower-files": "~2.4.1",
|
||||
"yargs": "~1.3.3"
|
||||
"yargs": "~1.3.3",
|
||||
"po2json": "~0.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
@ -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):
|
||||
|
@ -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)
|
@ -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')
|
Loading…
Reference in New Issue
Block a user