Merge pull request #1665 from normanjaeckel/ProfilePassword

Added possibility to reset password. Work on user app in general.
This commit is contained in:
Norman Jäckel 2015-11-08 22:58:30 +01:00
commit ce52dde13a
6 changed files with 384 additions and 211 deletions

View File

@ -37,7 +37,7 @@
<translate>Edit profile</translate> <translate>Edit profile</translate>
</a> </a>
<li> <li>
<a href="#TODO"> <a ui-sref="users.user.detail.password({ id: operator.user.id })">
<i class="fa fa-key"></i> <i class="fa fa-key"></i>
<translate>Change password</translate> <translate>Change password</translate>
</a> </a>

View File

@ -69,6 +69,13 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
url: '/profile', url: '/profile',
controller: 'UserProfileCtrl', controller: 'UserProfileCtrl',
}) })
.state('users.user.detail.password', {
views: {
'@users.user': {},
},
url: '/password',
controller: 'UserPasswordCtrl',
})
.state('users.user.import', { .state('users.user.import', {
url: '/import', url: '/import',
controller: 'UserImportCtrl', controller: 'UserImportCtrl',
@ -161,13 +168,18 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
} }
]) ])
.run(function(operator, $rootScope, $http) { .run([
// Put the operator into the root scope 'operator',
$http.get('/users/whoami/').success(function(data) { '$rootScope',
operator.setUser(data.user_id); '$http',
}); function(operator, $rootScope, $http) {
$rootScope.operator = operator; // 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 * 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. * TODO: find a way not to copy the code.
*/ */
.directive('osPerms', ['$animate', function($animate) { .directive('osPerms', [
return { '$animate',
multiElement: true, function($animate) {
transclude: 'element', return {
priority: 600, multiElement: true,
terminal: true, transclude: 'element',
restrict: 'A', priority: 600,
$$tlb: true, terminal: true,
link: function($scope, $element, $attr, ctrl, $transclude) { restrict: 'A',
var block, childScope, previousElements, perms; $$tlb: true,
if ($attr.osPerms[0] === '!') { link: function($scope, $element, $attr, ctrl, $transclude) {
perms = _.trimLeft($attr.osPerms, '!'); var block, childScope, previousElements, perms;
} else { if ($attr.osPerms[0] === '!') {
perms = $attr.osPerms; perms = _.trimLeft($attr.osPerms, '!');
} } else {
$scope.$watch( perms = $attr.osPerms;
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;
}
}
} }
); $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 * 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) { .controller('UserDetailCtrl', [
User.bindOne(user.id, $scope, 'user'); '$scope',
Group.bindAll({}, $scope, 'groups'); '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) { .controller('UserCreateCtrl', [
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); '$scope',
$scope.user = {}; '$state',
$scope.save = function (user) { 'User',
if (!user.groups) { 'Group',
user.groups = []; function($scope, $state, User, Group) {
} Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
User.create(user).then( $scope.user = {};
function(success) { $scope.save = function (user) {
$state.go('users.user.list'); 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( User.create(user).then(
function(success) { function(success) {
$scope.importcounter++; $state.go('users.user.list');
} }
); );
}); };
}; }
])
// import from csv file .controller('UserUpdateCtrl', [
$scope.csv = { '$scope',
content: null, '$state',
header: true, 'User',
separator: ',', 'user',
result: null 'Group',
}; function($scope, $state, User, user, Group) {
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
$scope.importByCSV = function (result) { $scope.user = user; // autoupdate is not activated
var obj = JSON.parse(JSON.stringify(result)); $scope.save = function (user) {
$scope.csvimporting = true; if (!user.groups) {
$scope.csvlines = Object.keys(obj).length; user.groups = [];
$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.save(user).then(
User.create(user).then(
function(success) { function(success) {
$scope.csvimportcounter++; $state.go('users.user.list');
} }
); );
} };
$scope.csvimported = true; }
}; ])
$scope.clear = function () { .controller('UserProfileCtrl', [
$scope.csv.result = null; '$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) { .controller('UserPasswordCtrl', [
Group.bindAll({}, $scope, 'groups'); '$scope',
'$state',
// delete selected group '$http',
$scope.delete = function (group) { 'user',
Group.destroy(group.id); function($scope, $state, $http, user) {
}; $scope.user = user; // autoupdate is not activated
}) $scope.save = function (user) {
if ($scope.newPassword != $scope.newPassword2) {
.controller('GroupCreateCtrl', function($scope, $state, Group, permissions) { $scope.newPassword = $scope.newPassword2 = '';
// get all permissions $scope.formError = 'Password confirmation does not match.';
$scope.permissions = permissions.data.actions.POST.permissions.choices; } else {
$scope.group = {}; $http.post(
$scope.save = function (group) { '/users/setpassword/',
Group.create(group).then( {'old_password': $scope.oldPassword, 'new_password': $scope.newPassword}
function(success) { ).then(
$state.go('users.group.list'); 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) { .controller('UserImportCtrl', [
// get all permissions '$scope',
$scope.permissions = permissions.data.actions.POST.permissions.choices; '$state',
$scope.group = group; // autoupdate is not activated 'User',
$scope.save = function (group) { function($scope, $state, User) {
Group.save(group).then( // import from textarea
function(success) { $scope.importByLine = function () {
$state.go('users.group.list'); $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) { $scope.clear = function () {
Group.bindOne(group.id, $scope, 'group'); $scope.csv.result = null;
}) };
}
])
.controller('userMenu', function($scope, $http, DS, User, operator) { .controller('GroupListCtrl', [
$scope.logout = function() { '$scope',
$http.post('/users/logout/').success(function(data) { 'Group',
operator.setUser(null); function($scope, Group) {
// TODO: remove all data from cache and reload page Group.bindAll({}, $scope, 'groups');
// DS.flush();
}); // 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 // this is code from angular.js. Find a way to call this function from this file
function getBlockNodes(nodes) { function getBlockNodes(nodes) {

View File

@ -0,0 +1,37 @@
<h1>Change password</h1>
<p ng-if='formError' class="text-danger">
<strong>{{ formError }}</strong>
</p>
<form name="userForm" >
<div class="form-group">
<label for="inputOldPassword" translate>Old password</label>
<input type="password"
ng-model="oldPassword"
class="form-control"
name="inputOldPassword"
required>
</div>
<div class="form-group">
<label for="inputNewPassword" translate>New password</label>
<input type="password"
ng-model="newPassword"
class="form-control"
name="inputNewPassword"
required>
</div>
<div class="form-group">
<label for="inputNewPassword2" translate>Confirm new password</label>
<input type="password"
ng-model="newPassword2"
class="form-control"
name="inputNewPassword2"
required>
</div>
<button type="submit" ng-click="save(user)" class="btn btn-primary" translate>
Save
</button>
<button ui-sref="users.user.list" class="btn btn-default" translate>
Cancel
</button>
</form>

View File

@ -1,5 +1,8 @@
<h1>Edit profil</h1> <h1>Edit profil</h1>
<p ng-if='formError' class="text-danger">
<strong>{{ formError }}</strong>
</p>
<form name="userForm" > <form name="userForm" >
<div class="form-group"> <div class="form-group">
<label for="inputUsername" translate>Username</label> <label for="inputUsername" translate>Username</label>

View File

@ -18,6 +18,10 @@ urlpatterns = patterns(
views.WhoAmIView.as_view(), views.WhoAmIView.as_view(),
name='user_whoami'), name='user_whoami'),
url(r'^setpassword/$',
views.SetPasswordView.as_view(),
name='user_setpassword'),
# PDF # PDF
url(r'^print/$', url(r'^print/$',
views.UsersListPDF.as_view(), views.UsersListPDF.as_view(),

View File

@ -5,7 +5,13 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from ..core.config import config 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 ..utils.views import APIView, PDFView
from .models import Group, User from .models import Group, User
from .pdf import users_passwords_to_pdf, users_to_pdf from .pdf import users_passwords_to_pdf, users_to_pdf
@ -232,6 +238,22 @@ class WhoAmIView(APIView):
**context) **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 # Views to generate PDFs
class UsersListPDF(PDFView): class UsersListPDF(PDFView):