From 8154b0324bb504438c6bfe4551bf50fa32f9755d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Fri, 6 Nov 2015 15:44:27 +0100 Subject: [PATCH] Added possibility to reset password. Work on user app in general. --- openslides/core/static/templates/index.html | 2 +- openslides/users/static/js/users/site.js | 525 +++++++++++------- .../templates/users/user-detail-password.html | 37 ++ .../templates/users/user-detail-profile.html | 3 + openslides/users/urls.py | 4 + openslides/users/views.py | 24 +- 6 files changed, 384 insertions(+), 211 deletions(-) create mode 100644 openslides/users/static/templates/users/user-detail-password.html diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html index e8d40ff11..ef4bf28e8 100644 --- a/openslides/core/static/templates/index.html +++ b/openslides/core/static/templates/index.html @@ -37,7 +37,7 @@ Edit profile
  • - + Change password diff --git a/openslides/users/static/js/users/site.js b/openslides/users/static/js/users/site.js index 686509415..c0839e078 100644 --- a/openslides/users/static/js/users/site.js +++ b/openslides/users/static/js/users/site.js @@ -69,6 +69,13 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users']) url: '/profile', controller: 'UserProfileCtrl', }) + .state('users.user.detail.password', { + views: { + '@users.user': {}, + }, + url: '/password', + controller: 'UserPasswordCtrl', + }) .state('users.user.import', { url: '/import', controller: 'UserImportCtrl', @@ -161,13 +168,18 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users']) } ]) -.run(function(operator, $rootScope, $http) { - // Put the operator into the root scope - $http.get('/users/whoami/').success(function(data) { - operator.setUser(data.user_id); - }); - $rootScope.operator = operator; -}) +.run([ + 'operator', + '$rootScope', + '$http', + function(operator, $rootScope, $http) { + // Put the operator into the root scope + $http.get('/users/whoami/').success(function(data) { + operator.setUser(data.user_id); + }); + $rootScope.operator = operator; + } +]) /* * Directive to check for permissions @@ -176,65 +188,68 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users']) * * TODO: find a way not to copy the code. */ -.directive('osPerms', ['$animate', function($animate) { - return { - multiElement: true, - transclude: 'element', - priority: 600, - terminal: true, - restrict: 'A', - $$tlb: true, - link: function($scope, $element, $attr, ctrl, $transclude) { - var block, childScope, previousElements, perms; - if ($attr.osPerms[0] === '!') { - perms = _.trimLeft($attr.osPerms, '!'); - } else { - perms = $attr.osPerms; - } - $scope.$watch( - function (scope) { - return scope.operator.hasPerms(perms); - }, - function (value) { - if ($attr.osPerms[0] === '!') { - value = !value; - } - if (value) { - if (!childScope) { - $transclude(function(clone, newScope) { - childScope = newScope; - clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); - // Note: We only need the first/last node of the cloned nodes. - // However, we need to keep the reference to the jqlite wrapper as it might be changed later - // by a directive with templateUrl when its template arrives. - block = { - clone: clone - }; - $animate.enter(clone, $element.parent(), $element); - }); - } - } else { - if (previousElements) { - previousElements.remove(); - previousElements = null; - } - if (childScope) { - childScope.$destroy(); - childScope = null; - } - if (block) { - previousElements = getBlockNodes(block.clone); - $animate.leave(previousElements).then(function() { - previousElements = null; - }); - block = null; - } - } +.directive('osPerms', [ + '$animate', + function($animate) { + return { + multiElement: true, + transclude: 'element', + priority: 600, + terminal: true, + restrict: 'A', + $$tlb: true, + link: function($scope, $element, $attr, ctrl, $transclude) { + var block, childScope, previousElements, perms; + if ($attr.osPerms[0] === '!') { + perms = _.trimLeft($attr.osPerms, '!'); + } else { + perms = $attr.osPerms; } - ); - } - }; -}]) + $scope.$watch( + function (scope) { + return scope.operator.hasPerms(perms); + }, + function (value) { + if ($attr.osPerms[0] === '!') { + value = !value; + } + if (value) { + if (!childScope) { + $transclude(function(clone, newScope) { + childScope = newScope; + clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); + // Note: We only need the first/last node of the cloned nodes. + // However, we need to keep the reference to the jqlite wrapper as it might be changed later + // by a directive with templateUrl when its template arrives. + block = { + clone: clone + }; + $animate.enter(clone, $element.parent(), $element); + }); + } + } else { + if (previousElements) { + previousElements.remove(); + previousElements = null; + } + if (childScope) { + childScope.$destroy(); + childScope = null; + } + if (block) { + previousElements = getBlockNodes(block.clone); + $animate.leave(previousElements).then(function() { + previousElements = null; + }); + block = null; + } + } + } + ); + } + }; + } +]) /* * Like osPerms but does only hide the DOM-Elements @@ -338,168 +353,260 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users']) } ]) -.controller('UserDetailCtrl', function($scope, User, user, Group) { - User.bindOne(user.id, $scope, 'user'); - Group.bindAll({}, $scope, 'groups'); -}) +.controller('UserDetailCtrl', [ + '$scope', + 'User', + 'user', + 'Group', + function($scope, User, user, Group) { + User.bindOne(user.id, $scope, 'user'); + Group.bindAll({}, $scope, 'groups'); + } +]) -.controller('UserCreateCtrl', function($scope, $state, User, Group) { - Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); - $scope.user = {}; - $scope.save = function (user) { - if (!user.groups) { - user.groups = []; - } - User.create(user).then( - function(success) { - $state.go('users.user.list'); +.controller('UserCreateCtrl', [ + '$scope', + '$state', + 'User', + 'Group', + function($scope, $state, User, Group) { + Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); + $scope.user = {}; + $scope.save = function (user) { + if (!user.groups) { + user.groups = []; } - ); - }; -}) - -.controller('UserUpdateCtrl', function($scope, $state, User, user, Group) { - Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); - $scope.user = user; // autoupdate is not activated - $scope.save = function (user) { - if (!user.groups) { - user.groups = []; - } - User.save(user).then( - function(success) { - $state.go('users.user.list'); - } - ); - }; -}) - -.controller('UserProfileCtrl', function($scope, $state, User, user) { - $scope.user = user; // autoupdate is not activated - $scope.save = function (user) { - User.save(user).then( - function(success) { - // TODO: show success message - console.log("profile saved"); - } - ); - }; -}) - -.controller('UserImportCtrl', function($scope, $state, User) { - // import from textarea - $scope.importByLine = function () { - $scope.users = $scope.userlist[0].split("\n"); - $scope.importcounter = 0; - $scope.users.forEach(function(name) { - // Split each full name in first and last name. - // The last word is set as last name, rest is the first name(s). - // (e.g.: "Max Martin Mustermann" -> last_name = "Mustermann") - var names = name.split(" "); - var last_name = names.slice(-1)[0]; - var first_name = names.slice(0, -1).join(" "); - var user = { - first_name: first_name, - last_name: last_name, - groups: [] - }; User.create(user).then( function(success) { - $scope.importcounter++; + $state.go('users.user.list'); } ); - }); - }; + }; + } +]) - // import from csv file - $scope.csv = { - content: null, - header: true, - separator: ',', - result: null - }; - - $scope.importByCSV = function (result) { - var obj = JSON.parse(JSON.stringify(result)); - $scope.csvimporting = true; - $scope.csvlines = Object.keys(obj).length; - $scope.csvimportcounter = 0; - for (var i = 0; i < obj.length; i++) { - var user = {}; - user.title = obj[i].titel; - user.first_name = obj[i].first_name; - user.last_name = obj[i].last_name; - user.structure_level = obj[i].structure_level; - user.groups = []; - if (obj[i].groups !== '') { - var groups = obj[i].groups.replace('"','').split(","); - groups.forEach(function(group) { - user.groups.push(group); - console.log(group); - }); +.controller('UserUpdateCtrl', [ + '$scope', + '$state', + 'User', + 'user', + 'Group', + function($scope, $state, User, user, Group) { + Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); + $scope.user = user; // autoupdate is not activated + $scope.save = function (user) { + if (!user.groups) { + user.groups = []; } - user.comment = obj[i].comment; - User.create(user).then( + User.save(user).then( function(success) { - $scope.csvimportcounter++; + $state.go('users.user.list'); } ); - } - $scope.csvimported = true; - }; + }; + } +]) - $scope.clear = function () { - $scope.csv.result = null; - }; -}) +.controller('UserProfileCtrl', [ + '$scope', + '$state', + 'User', + 'user', + function($scope, $state, User, user) { + $scope.user = user; // autoupdate is not activated + $scope.save = function (user) { + User.save(user).then( + function(success) { + $state.go('users.user.list'); + }, + function(error) { + $scope.formError = error; + } + ); + }; + } +]) -.controller('GroupListCtrl', function($scope, Group) { - Group.bindAll({}, $scope, 'groups'); - - // delete selected group - $scope.delete = function (group) { - Group.destroy(group.id); - }; -}) - -.controller('GroupCreateCtrl', function($scope, $state, Group, permissions) { - // get all permissions - $scope.permissions = permissions.data.actions.POST.permissions.choices; - $scope.group = {}; - $scope.save = function (group) { - Group.create(group).then( - function(success) { - $state.go('users.group.list'); +.controller('UserPasswordCtrl', [ + '$scope', + '$state', + '$http', + 'user', + function($scope, $state, $http, user) { + $scope.user = user; // autoupdate is not activated + $scope.save = function (user) { + if ($scope.newPassword != $scope.newPassword2) { + $scope.newPassword = $scope.newPassword2 = ''; + $scope.formError = 'Password confirmation does not match.'; + } else { + $http.post( + '/users/setpassword/', + {'old_password': $scope.oldPassword, 'new_password': $scope.newPassword} + ).then( + function(data) { + // Success. + $state.go('users.user.list'); + }, + function(data) { + // Error, e. g. wrong old password. + $scope.oldPassword = $scope.newPassword = $scope.newPassword2 = ''; + $scope.formError = data; + } + ); } - ); - }; -}) + }; + } +]) -.controller('GroupUpdateCtrl', function($scope, $state, Group, permissions, group) { - // get all permissions - $scope.permissions = permissions.data.actions.POST.permissions.choices; - $scope.group = group; // autoupdate is not activated - $scope.save = function (group) { - Group.save(group).then( - function(success) { - $state.go('users.group.list'); +.controller('UserImportCtrl', [ + '$scope', + '$state', + 'User', + function($scope, $state, User) { + // import from textarea + $scope.importByLine = function () { + $scope.users = $scope.userlist[0].split("\n"); + $scope.importcounter = 0; + $scope.users.forEach(function(name) { + // Split each full name in first and last name. + // The last word is set as last name, rest is the first name(s). + // (e.g.: "Max Martin Mustermann" -> last_name = "Mustermann") + var names = name.split(" "); + var last_name = names.slice(-1)[0]; + var first_name = names.slice(0, -1).join(" "); + var user = { + first_name: first_name, + last_name: last_name, + groups: [] + }; + User.create(user).then( + function(success) { + $scope.importcounter++; + } + ); + }); + }; + + // import from csv file + $scope.csv = { + content: null, + header: true, + separator: ',', + result: null + }; + + $scope.importByCSV = function (result) { + var obj = JSON.parse(JSON.stringify(result)); + $scope.csvimporting = true; + $scope.csvlines = Object.keys(obj).length; + $scope.csvimportcounter = 0; + for (var i = 0; i < obj.length; i++) { + var user = {}; + user.title = obj[i].titel; + user.first_name = obj[i].first_name; + user.last_name = obj[i].last_name; + user.structure_level = obj[i].structure_level; + user.groups = []; + if (obj[i].groups !== '') { + var groups = obj[i].groups.replace('"','').split(","); + groups.forEach(function(group) { + user.groups.push(group); + console.log(group); + }); + } + user.comment = obj[i].comment; + User.create(user).then( + function(success) { + $scope.csvimportcounter++; + } + ); } - ); - }; -}) + $scope.csvimported = true; + }; -.controller('GroupDetailCtrl', function($scope, Group, group) { - Group.bindOne(group.id, $scope, 'group'); -}) + $scope.clear = function () { + $scope.csv.result = null; + }; + } +]) -.controller('userMenu', function($scope, $http, DS, User, operator) { - $scope.logout = function() { - $http.post('/users/logout/').success(function(data) { - operator.setUser(null); - // TODO: remove all data from cache and reload page - // DS.flush(); - }); - }; -}); +.controller('GroupListCtrl', [ + '$scope', + 'Group', + function($scope, Group) { + Group.bindAll({}, $scope, 'groups'); + + // delete selected group + $scope.delete = function (group) { + Group.destroy(group.id); + }; + } +]) + +.controller('GroupCreateCtrl', [ + '$scope', + '$state', + 'Group', + 'permissions', + function($scope, $state, Group, permissions) { + // get all permissions + $scope.permissions = permissions.data.actions.POST.permissions.choices; + $scope.group = {}; + $scope.save = function (group) { + Group.create(group).then( + function(success) { + $state.go('users.group.list'); + } + ); + }; + } +]) + +.controller('GroupUpdateCtrl', [ + '$scope', + '$state', + 'Group', + 'permissions', + 'group', + function($scope, $state, Group, permissions, group) { + // get all permissions + $scope.permissions = permissions.data.actions.POST.permissions.choices; + $scope.group = group; // autoupdate is not activated + $scope.save = function (group) { + Group.save(group).then( + function(success) { + $state.go('users.group.list'); + } + ); + }; + } +]) + +.controller('GroupDetailCtrl', [ + '$scope', + 'Group', + 'group', + function($scope, Group, group) { + Group.bindOne(group.id, $scope, 'group'); + } +]) + +.controller('userMenu', [ + '$scope', + '$http', + 'DS', + 'User', + 'operator', + function($scope, $http, DS, User, operator) { + $scope.logout = function() { + $http.post('/users/logout/').success(function(data) { + operator.setUser(null); + // TODO: remove all data from cache and reload page + // DS.flush(); + }); + }; + } +]); // this is code from angular.js. Find a way to call this function from this file function getBlockNodes(nodes) { diff --git a/openslides/users/static/templates/users/user-detail-password.html b/openslides/users/static/templates/users/user-detail-password.html new file mode 100644 index 000000000..d75862e19 --- /dev/null +++ b/openslides/users/static/templates/users/user-detail-password.html @@ -0,0 +1,37 @@ +

    Change password

    + +

    + {{ formError }} +

    +
    +
    + + +
    +
    + + +
    +
    + + +
    + + +
    diff --git a/openslides/users/static/templates/users/user-detail-profile.html b/openslides/users/static/templates/users/user-detail-profile.html index 3612c93ca..0fb6c703d 100644 --- a/openslides/users/static/templates/users/user-detail-profile.html +++ b/openslides/users/static/templates/users/user-detail-profile.html @@ -1,5 +1,8 @@

    Edit profil

    +

    + {{ formError }} +

    diff --git a/openslides/users/urls.py b/openslides/users/urls.py index cc495fd34..1e3742b83 100644 --- a/openslides/users/urls.py +++ b/openslides/users/urls.py @@ -18,6 +18,10 @@ urlpatterns = patterns( views.WhoAmIView.as_view(), name='user_whoami'), + url(r'^setpassword/$', + views.SetPasswordView.as_view(), + name='user_setpassword'), + # PDF url(r'^print/$', views.UsersListPDF.as_view(), diff --git a/openslides/users/views.py b/openslides/users/views.py index 51e45507d..85bef6add 100644 --- a/openslides/users/views.py +++ b/openslides/users/views.py @@ -5,7 +5,13 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from ..core.config import config -from ..utils.rest_api import ModelViewSet, Response, detail_route, status +from ..utils.rest_api import ( + ModelViewSet, + Response, + ValidationError, + detail_route, + status, +) from ..utils.views import APIView, PDFView from .models import Group, User from .pdf import users_passwords_to_pdf, users_to_pdf @@ -232,6 +238,22 @@ class WhoAmIView(APIView): **context) +class SetPasswordView(APIView): + """ + Users can set a new password for themselves. + """ + http_method_names = ['post'] + + def post(self, request, *args, **kwargs): + user = request.user + if user.check_password(request.data['old_password']): + user.set_password(request.data['new_password']) + user.save() + else: + raise ValidationError(_('Password does not match.')) + return super().post(request, *args, **kwargs) + + # Views to generate PDFs class UsersListPDF(PDFView):