OpenSlides/openslides/users/static/js/users/site.js
2016-06-28 10:18:43 +02:00

1045 lines
35 KiB
JavaScript

(function () {
'use strict';
angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
.config([
'mainMenuProvider',
'gettext',
function (mainMenuProvider, gettext) {
mainMenuProvider.register({
'ui_sref': 'users.user.list',
'img_class': 'user',
'title': gettext('Participants'),
'weight': 500,
'perm': 'users.can_see_name',
});
}
])
.config([
'$stateProvider',
function($stateProvider) {
$stateProvider
.state('users', {
url: '/users',
abstract: true,
template: "<ui-view/>",
})
.state('users.user', {
abstract: true,
template: "<ui-view/>",
})
.state('users.user.list', {
resolve: {
users: function(User) {
return User.findAll();
},
groups: function(Group) {
return Group.findAll();
}
}
})
.state('users.user.create', {
resolve: {
groups: function(Group) {
return Group.findAll();
}
}
})
.state('users.user.detail', {
resolve: {
user: function(User, $stateParams) {
return User.find($stateParams.id);
},
groups: function(Group) {
return Group.findAll();
}
}
})
// Redirects to user detail view and opens user edit form dialog, uses edit url.
// Used by $state.go(..) from core/site.js only (for edit current slide button).
// (from users list controller use UserForm factory instead to open dialog in front of
// current view without redirect)
.state('users.user.detail.update', {
onEnter: ['$stateParams', '$state', 'ngDialog', 'User',
function($stateParams, $state, ngDialog, User) {
ngDialog.open({
template: 'static/templates/users/user-form.html',
controller: 'UserUpdateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: {
user: function() {return User.find($stateParams.id);}
}
});
}
]
})
.state('users.user.detail.profile', {
views: {
'@users.user': {},
},
url: '/profile',
controller: 'UserProfileCtrl',
})
.state('users.user.detail.password', {
views: {
'@users.user': {},
},
url: '/password',
controller: 'UserPasswordCtrl',
})
.state('users.user.import', {
url: '/import',
controller: 'UserImportCtrl',
resolve: {
groups: function(Group) {
return Group.findAll();
}
}
})
// groups
.state('users.group', {
url: '/groups',
abstract: true,
template: "<ui-view/>",
})
.state('users.group.list', {
resolve: {
groups: function(Group) {
return Group.findAll();
}
}
})
.state('users.group.create', {
resolve: {
permissions: function(Group) {
return Group.getPermissions();
}
}
})
.state('users.group.detail', {
resolve: {
group: function(Group, $stateParams) {
return Group.find($stateParams.id);
},
permissions: function(Group) {
return Group.getPermissions();
}
}
})
.state('users.group.detail.update', {
views: {
'@users.group': {}
},
resolve: {
permissions: function(Group) {
return Group.getPermissions();
}
}
})
.state('login', {
template: null,
url: '/login',
params: { guest_enabled: false },
onEnter: ['$state', '$stateParams', 'ngDialog', function($state, $stateParams, ngDialog) {
ngDialog.open({
template: 'static/templates/core/login-form.html',
controller: 'LoginFormCtrl',
showClose: $stateParams.guest_enabled,
closeByEscape: $stateParams.guest_enabled,
closeByDocument: $stateParams.guest_enabled,
});
}]
});
}
])
.run([
'operator',
'$rootScope',
'$http',
'$state',
'Group',
function(operator, $rootScope, $http, $state, Group) {
// Put the operator into the root scope
$http.get('/users/whoami/').success(function(data) {
operator.setUser(data.user_id);
$rootScope.guest_enabled = data.guest_enabled;
if (data.user_id === null && !data.guest_enabled) {
// redirect to login dialog if use is not logged in
$state.go('login', {guest_enabled: data.guest_enabled});
}
});
$rootScope.operator = operator;
// Load all Groups. They are needed later
Group.findAll();
}
])
/*
* Directive to check for permissions
*
* This is the Code from angular.js ngIf.
*
* 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;
}
}
}
);
}
};
}
])
// Service for generic assignment form (create and update)
.factory('UserForm', [
'$http',
'gettextCatalog',
'Editor',
'Group',
'Mediafile',
function ($http, gettextCatalog, Editor, Group, Mediafile) {
return {
// ngDialog for user form
getDialog: function (user) {
var resolve;
if (user) {
resolve = {
user: function(User) {return User.find(user.id);}
};
}
return {
template: 'static/templates/users/user-form.html',
controller: (user) ? 'UserUpdateCtrl' : 'UserCreateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: (resolve) ? resolve : null
};
},
// angular-formly fields for user form
getFormFields: function (hideOnCreateForm) {
var images = Mediafile.getAllImages();
return [
{
key: 'username',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Username')
},
hide: hideOnCreateForm
},
{
key: 'title',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Title'),
description: gettextCatalog.getString('Will be shown before the name.')
}
},
{
key: 'first_name',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('First name')
}
},
{
key: 'last_name',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Last name')
}
},
{
key: 'structure_level',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Structure level'),
description: gettextCatalog.getString('Will be shown after the name.')
}
},
{
key: 'groups_id',
type: 'select-multiple',
templateOptions: {
label: gettextCatalog.getString('Groups'),
options: Group.getAll(),
ngOptions: 'option.id as option.name | translate for option in to.options | ' +
'filter: {id: "!1"} | filter: {id: "!2"}',
placeholder: gettextCatalog.getString('Select or search a group ...')
}
},
{
key: 'default_password',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Default password'),
description: '',
addonRight: { text: 'Reset', class: 'fa fa-undo', onClick:
function (options, scope) {
$http.post(
'/rest/users/user/' + scope.model.id + '/reset_password/',
{'password': scope.model.default_password})
.then(function() {
options.templateOptions.description =
gettextCatalog.getString('Password successfully resetted to:') +
' ' + scope.model.default_password;
});
}
}
}
},
{
key: 'comment',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Comment'),
description: gettextCatalog.getString('Only for notes.')
}
},
{
key: 'about_me',
type: 'editor',
templateOptions: {
label: gettextCatalog.getString('About me'),
},
data: {
tinymceOption: Editor.getOptions(images)
}
},
{
key: 'is_present',
type: 'checkbox',
templateOptions: {
label: gettextCatalog.getString('Is present'),
description: gettextCatalog.getString('Designates whether this user is in the room or not.')
},
defaultValue: true
},
{
key: 'is_active',
type: 'checkbox',
templateOptions: {
label: gettextCatalog.getString('Is active'),
description: gettextCatalog.getString(
'Designates whether this user should be treated as ' +
'active. Unselect this instead of deleting the account.')
},
defaultValue: true
}];
}
};
}
])
.controller('UserListCtrl', [
'$scope',
'$state',
'ngDialog',
'UserForm',
'User',
'Group',
function($scope, $state, ngDialog, UserForm, User, Group) {
User.bindAll({}, $scope, 'users');
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
$scope.alert = {};
$scope.groupFilter = undefined;
// setup table sorting
$scope.sortColumn = 'first_name'; //TODO: sort by first OR last name
$scope.filterPresent = '';
$scope.reverse = false;
// function to sort by clicked column
$scope.toggleSort = function ( column ) {
if ( $scope.sortColumn === column ) {
$scope.reverse = !$scope.reverse;
}
$scope.sortColumn = column;
};
// pagination
$scope.currentPage = 1;
$scope.itemsPerPage = 100;
$scope.limitBegin = 0;
$scope.pageChanged = function() {
$scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
};
// open new/edit dialog
$scope.openDialog = function (user) {
ngDialog.open(UserForm.getDialog(user));
};
// save changed user
$scope.save = function (user) {
User.save(user).then(
function(success) {
//user.quickEdit = false;
$scope.alert.show = false;
},
function(error){
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = { type: 'danger', msg: message, show: true };
});
};
// *** select mode functions ***
$scope.isSelectMode = false;
// check all checkboxes
$scope.checkAll = function () {
angular.forEach($scope.users, function (user) {
user.selected = $scope.selectedAll;
});
};
// uncheck all checkboxes if isSelectMode is closed
$scope.uncheckAll = function () {
if (!$scope.isSelectMode) {
$scope.selectedAll = false;
angular.forEach($scope.users, function (user) {
user.selected = false;
});
}
};
// delete all selected users
$scope.deleteMultiple = function () {
angular.forEach($scope.users, function (user) {
if (user.selected) {
User.destroy(user.id);
}
});
$scope.isSelectMode = false;
$scope.uncheckAll();
};
// delete single user
$scope.delete = function (user) {
User.destroy(user.id);
};
// add group for selected users
$scope.addGroupMultiple = function (group) {
angular.forEach($scope.users, function (user) {
if (user.selected) {
user.groups_id.push(group);
User.save(user);
}
});
$scope.isSelectMode = false;
$scope.uncheckAll();
};
// remove group for selected users
$scope.removeGroupMultiple = function (group) {
angular.forEach($scope.users, function (user) {
if (user.selected) {
var groupIndex = user.groups_id.indexOf(parseInt(group));
if (groupIndex > -1) {
user.groups_id.splice(groupIndex, 1);
User.save(user);
}
}
});
$scope.isSelectMode = false;
$scope.uncheckAll();
};
}
])
.controller('UserDetailCtrl', [
'$scope',
'ngDialog',
'UserForm',
'User',
'user',
'Group',
function($scope, ngDialog, UserForm, User, user, Group) {
User.bindOne(user.id, $scope, 'user');
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
// open edit dialog
$scope.openDialog = function (user) {
ngDialog.open(UserForm.getDialog(user));
};
}
])
.controller('UserCreateCtrl', [
'$scope',
'$state',
'User',
'UserForm',
'Group',
function($scope, $state, User, UserForm, Group) {
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
$scope.alert = {};
// get all form fields
$scope.formFields = UserForm.getFormFields(true);
// save user
$scope.save = function (user) {
if (!user.groups_id) {
user.groups_id = [];
}
User.create(user).then(
function(success) {
$scope.closeThisDialog();
},
function (error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
])
.controller('UserUpdateCtrl', [
'$scope',
'$state',
'$http',
'User',
'UserForm',
'Group',
'user',
function($scope, $state, $http, User, UserForm, Group, user) {
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
$scope.alert = {};
// set initial values for form model by create deep copy of user object
// so list/detail view is not updated while editing
$scope.model = angular.copy(user);
// get all form fields
$scope.formFields = UserForm.getFormFields();
// save user
$scope.save = function (user) {
if (!user.groups_id) {
user.groups_id = [];
}
// inject the changed user (copy) object back into DS store
User.inject(user);
// save change user object on server
User.save(user).then(
function (success) {
$scope.closeThisDialog();
},
function (error) {
// save error: revert all changes by restore
// (refresh) original user object from server
User.refresh(user);
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
])
.controller('UserProfileCtrl', [
'$scope',
'$state',
'Editor',
'User',
'user',
function($scope, $state, Editor, User, user) {
$scope.user = user; // autoupdate is not activated
$scope.tinymceOption = Editor.getOptions();
$scope.save = function (user) {
User.save(user, { method: 'PATCH' }).then(
function(success) {
$state.go('users.user.list');
},
function(error) {
$scope.formError = error;
}
);
};
}
])
.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 (response) {
// Success.
$state.go('users.user.list');
},
function (response) {
// Error, e. g. wrong old password.
$scope.oldPassword = $scope.newPassword = $scope.newPassword2 = '';
$scope.formError = response.data.detail;
}
);
}
};
}
])
.controller('UserImportCtrl', [
'$scope',
'gettext',
'User',
'Group',
function($scope, gettext, User, Group) {
// import from textarea
$scope.importByLine = function () {
$scope.usernames = $scope.userlist[0].split("\n");
$scope.importcounter = 0;
$scope.usernames.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_id: []
};
User.create(user).then(
function(success) {
$scope.importcounter++;
}
);
});
};
// *** csv import ***
// set initial data for csv import
$scope.users = [];
$scope.separator = ',';
$scope.encoding = 'UTF-8';
$scope.encodingOptions = ['UTF-8', 'ISO-8859-1'];
$scope.accept = '.csv, .txt';
$scope.csv = {
content: null,
header: true,
headerVisible: false,
separator: $scope.separator,
separatorVisible: false,
encoding: $scope.encoding,
encodingVisible: false,
accept: $scope.accept,
result: null
};
// set csv file encoding
$scope.setEncoding = function () {
$scope.csv.encoding = $scope.encoding;
};
// set csv file encoding
$scope.setSeparator = function () {
$scope.csv.separator = $scope.separator;
};
// pagination
$scope.currentPage = 1;
$scope.itemsPerPage = 100;
$scope.limitBegin = 0;
$scope.pageChanged = function() {
$scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
};
// detect if csv file is loaded
$scope.$watch('csv.result', function () {
$scope.users = [];
var quotionRe = /^"(.*)"$/;
angular.forEach($scope.csv.result, function (user) {
// title
if (user.title) {
user.title = user.title.replace(quotionRe, '$1');
}
// first name
if (user.first_name) {
user.first_name = user.first_name.replace(quotionRe, '$1');
}
// last name
if (user.last_name) {
user.last_name = user.last_name.replace(quotionRe, '$1');
}
if (!user.first_name && !user.last_name) {
user.importerror = true;
user.name_error = gettext('Error: First or last name is required.');
}
// structure level
if (user.structure_level) {
user.structure_level = user.structure_level.replace(quotionRe, '$1');
}
// groups
if (user.groups) {
var csvGroups = user.groups.replace(quotionRe, '$1').split(",");
user.groups_id = [];
user.groupnames = [];
if (csvGroups !== '') {
// All group objects are already loaded via the resolve statement from ui-router.
var allGroups = Group.getAll();
csvGroups.forEach(function(csvGroup) {
allGroups.forEach(function (allGroup) {
if (csvGroup == allGroup.id) {
user.groups_id.push(allGroup.id);
user.groupnames.push(allGroup.name);
}
});
});
}
} else {
user.groups_id = [];
}
// comment
if (user.comment) {
user.comment = user.comment.replace(quotionRe, '$1');
}
// is active
if (user.is_active) {
user.is_active = user.is_active.replace(quotionRe, '$1');
if (user.is_active == '1') {
user.is_active = true;
} else {
user.is_active = false;
}
} else {
user.is_active = false;
}
$scope.users.push(user);
});
});
// import from csv file
$scope.import = function () {
$scope.csvImporting = true;
angular.forEach($scope.users, function (user) {
if (!user.importerror) {
User.create(user).then(
function(success) {
user.imported = true;
}
);
}
});
$scope.csvimported = true;
};
$scope.clear = function () {
$scope.csv.result = null;
};
// download CSV example file
$scope.downloadCSVExample = function () {
var element = document.getElementById('downloadLink');
var csvRows = [
// column header line
['title', 'first_name', 'last_name', 'structure_level', 'groups', 'comment', 'is_active'],
// example entries
['Dr.', 'Max', 'Mustermann', 'Berlin', '"3,4"', 'xyz', '1'],
['', 'John', 'Doe', 'Washington', '3', 'abc', '1'],
['', 'Fred', 'Bloggs', 'London', '', '', ''],
];
var csvString = csvRows.join("%0A");
element.href = 'data:text/csv;charset=utf-8,' + csvString;
element.download = 'users-example.csv';
element.target = '_blank';
};
}
])
.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;
$scope.group = {};
$scope.save = function (group) {
if (!group.permissions) {
group.permissions = [];
}
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;
$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',
'permissions',
function($scope, Group, group, permissions) {
Group.bindOne(group.id, $scope, 'group');
$scope.groupPermissionNames = [];
// get display names of group permissions
// from an object array with all available permissions [{display_name, value}]
angular.forEach(group.permissions, function(permValue) {
angular.forEach(permissions, function(p) {
if (p.value == permValue) {
$scope.groupPermissionNames.push(p.display_name);
}
});
});
}
])
.controller('userMenu', [
'$scope',
'$http',
'DS',
'User',
'operator',
'ngDialog',
function($scope, $http, DS, User, operator, ngDialog) {
$scope.logout = function () {
$http.post('/users/logout/').then(function (response) {
operator.setUser(null);
window.location.reload();
});
};
}
])
.controller('LoginFormCtrl', [
'$rootScope',
'$scope',
'$http',
'$stateParams',
'operator',
function ($rootScope, $scope, $http, $stateParams, operator ) {
$scope.alerts = [];
// get login info-text from server
$http.get('/users/login/').success(function(data) {
if(data.info_text) {
$scope.alerts.push({
type: 'success',
msg: data.info_text
});
}
});
// close alert function
$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
};
// check if guest login is allowed
$scope.guestAllowed = $rootScope.guest_enabled;
// login
$scope.login = function () {
$scope.alerts = [];
$http.post(
'/users/login/',
{'username': $scope.username, 'password': $scope.password}
).then(
function (response) {
// Success: User logged in.
operator.setUser(response.data.user_id);
$scope.closeThisDialog();
setTimeout(function(){
window.location.replace('/');
}, 1000);
},
function (response) {
// Error: Username or password is not correct.
$scope.alerts.push({
type: 'danger',
msg: response.data.detail
});
}
);
};
// guest login
$scope.guestLogin = function () {
$scope.closeThisDialog();
};
}
])
// Mark all users strings for translation in JavaScript.
.config([
'gettext',
function (gettext) {
// permission strings (see models.py of each Django app)
// agenda
gettext('Can see agenda');
gettext('Can manage agenda');
gettext('Can see hidden items and time scheduling of agenda');
gettext('Can put oneself on the list of speakers');
// assignments
gettext('Can see elections');
gettext('Can nominate another participant');
gettext('Can nominate oneself');
gettext('Can manage elections');
// core
gettext('Can see the projector');
gettext('Can manage the projector');
gettext('Can see the front page');
gettext('Can manage tags');
gettext('Can manage configuration');
gettext('Can use the chat');
// mediafiles
gettext('Can see the list of files');
gettext('Can upload files');
gettext('Can manage files');
// motions
gettext('Can see motions');
gettext('Can create motions');
gettext('Can support motions');
gettext('Can manage motions');
// users
gettext('Can see names of users');
gettext('Can see extra data of users (e.g. present and comment)');
gettext('Can manage users');
// config strings in users/config_variables.py
gettext('[Place for your welcome and help text.]');
gettext('Sort users by first name');
gettext('Disable for sorting by last name');
gettext('Participants');
gettext('Sorting');
gettext('Welcome to OpenSlides');
gettext('Title for access data and welcome PDF');
gettext('PDF');
gettext('Help text for access data and welcome PDF');
gettext('System URL');
gettext('Used for QRCode in PDF of access data.');
gettext('WLAN name (SSID)');
gettext('Used for WLAN QRCode in PDF of access data.');
gettext('WLAN password');
gettext('Used for WLAN QRCode in PDF of access data.');
gettext('WLAN encryption');
gettext('Used for WLAN QRCode in PDF of access data.');
gettext('WEP');
gettext('WPA/WPA2');
gettext('No encryption');
}
]);
// this is code from angular.js. Find a way to call this function from this file
function getBlockNodes(nodes) {
// TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
// collection, otherwise update the original collection.
var node = nodes[0];
var endNode = nodes[nodes.length - 1];
var blockNodes = [node];
do {
node = node.nextSibling;
if (!node) break;
blockNodes.push(node);
} while (node !== endNode);
return $(blockNodes);
}
}());