diff --git a/CHANGELOG b/CHANGELOG index 2e3c2a67b..af86ca679 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,6 +37,7 @@ Users: - Added support for password validation using Django or custom validators e. g. for minimum password length [#3200]. - Fixed compare of duplicated users while csv user import [#3201]. +- Added fast mass import for users [#3290]. Core: - No reload on logoff. OpenSlides is now a full single page diff --git a/openslides/users/serializers.py b/openslides/users/serializers.py index 4c57f65a0..5b82e068b 100644 --- a/openslides/users/serializers.py +++ b/openslides/users/serializers.py @@ -72,16 +72,22 @@ class UserFullSerializer(ModelSerializer): data.get('last_name', '')) return data - def create(self, validated_data): + def prepare_password(self, validated_data): """ - Creates the user. Sets the default password. + Sets the default password. """ # Prepare setup password. if not validated_data.get('default_password'): validated_data['default_password'] = User.objects.generate_password() validated_data['password'] = make_password(validated_data['default_password'], '', 'md5') + return validated_data + + def create(self, validated_data): + """ + Creates the user. + """ # Perform creation in the database and return new user. - user = super().create(validated_data) + user = super().create(self.prepare_password(validated_data)) # TODO: This autoupdate call is redundant (required by issue #2727). See #2736. inform_changed_data(user) return user diff --git a/openslides/users/static/js/users/site.js b/openslides/users/static/js/users/site.js index 176066ffd..2439683ed 100644 --- a/openslides/users/static/js/users/site.js +++ b/openslides/users/static/js/users/site.js @@ -755,13 +755,12 @@ angular.module('OpenSlidesApp.users.site', [ .controller('UserUpdateCtrl', [ '$scope', '$state', - '$http', 'User', 'UserForm', 'Group', 'userId', 'ErrorMessage', - function($scope, $state, $http, User, UserForm, Group, userId, ErrorMessage) { + function($scope, $state, User, UserForm, Group, userId, ErrorMessage) { Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); $scope.alert = {}; // set initial values for form model by create deep copy of user object @@ -898,34 +897,45 @@ angular.module('OpenSlidesApp.users.site', [ .controller('UserImportCtrl', [ '$scope', + '$http', '$q', 'gettext', 'gettextCatalog', 'User', 'Group', 'UserCsvExport', - function($scope, $q, gettext, gettextCatalog, User, Group, UserCsvExport) { + 'ErrorMessage', + function($scope, $http, $q, gettext, gettextCatalog, User, Group, UserCsvExport, ErrorMessage) { // import from textarea $scope.importByLine = function () { - $scope.usernames = $scope.userlist[0].split("\n"); - $scope.importcounter = 0; - $scope.usernames.forEach(function(name) { + var usernames = $scope.userlist[0].split("\n"); + // Ignore empty lines. + /*usernames = _.filter(usernames, function (name) { + return name !== ''; + });*/ + var users = _.map(usernames, 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 = { + return { first_name: first_name, last_name: last_name, - groups_id: [] + groups_id: [], }; - User.create(user).then( - function(success) { - $scope.importcounter++; - } - ); + }); + $http.post('/rest/users/user/mass_import/', { + users: users + }).then(function (success) { + $scope.alert = { + show: true, + type: 'success', + msg: success.data.detail, + }; + }, function (error) { + $scope.alert = ErrorMessage.forAlert(error); }); }; @@ -955,6 +965,7 @@ angular.module('OpenSlidesApp.users.site', [ 'groups', 'comment', 'is_active', 'is_present', 'is_committee', 'default_password']; $scope.users = []; $scope.onCsvChange = function (csv) { + $scope.csvImporting = false; // All user objects are already loaded via the resolve statement from ui-router. var users = User.getAll(); $scope.users = []; @@ -967,7 +978,8 @@ angular.module('OpenSlidesApp.users.site', [ } }); $scope.duplicates = 0; - _.forEach(csvUsers, function (user) { + _.forEach(csvUsers, function (user, index) { + user.importTrackId = index; user.selected = true; if (!user.first_name && !user.last_name) { user.importerror = true; @@ -1108,6 +1120,11 @@ angular.module('OpenSlidesApp.users.site', [ var allGroups = Group.getAll(); var existingUsers = User.getAll(); + // For option 'delete existing user' on duplicates + var deletePromises = []; + // Array of users for mass import + var usersToBeImported = []; + _.forEach($scope.users, function (user) { if (user.selected && !user.importerror) { // Assign all groups @@ -1124,7 +1141,6 @@ angular.module('OpenSlidesApp.users.site', [ // Do nothing on duplicateAction==duplicateActions[0] (keep original) if (user.duplicate && (user.duplicateAction == $scope.duplicateActions[1])) { // delete existing user - var deletePromises = []; existingUsers.forEach(function(user_) { user_.fullname = [ user_.title, @@ -1140,30 +1156,45 @@ angular.module('OpenSlidesApp.users.site', [ deletePromises.push(User.destroy(user_.id)); } }); - $q.all(deletePromises).then(function() { - User.create(user).then( - function(success) { - user.imported = true; - } - ); - }); + usersToBeImported.push(user); } else if (!user.duplicate || (user.duplicateAction == $scope.duplicateActions[2])) { // create user - User.create(user).then( - function(success) { - user.imported = true; - } - ); + usersToBeImported.push(user); } } }); - $scope.csvimported = true; + $q.all(deletePromises).then(function () { + $http.post('/rest/users/user/mass_import/', { + users: usersToBeImported + }).then(function (success) { + _.forEach(success.data.importedTrackIds, function (trackId) { + _.find($scope.users, function (user) { + return user.importTrackId === trackId; + }).imported = true; + }); + $scope.csvimported = true; + }, function (error) { + $scope.alert = ErrorMessage.forAlert(error); + }); + }); }); }; $scope.clear = function () { $scope.users = null; }; + $scope.excludeImportedUsers = function () { + $scope.users = _.filter($scope.users, function (user) { + return !user.imported; + }); + $scope.csvImporting = false; + $scope.calcStats(); + }; + $scope.someImportedUsers = function () { + return _.some($scope.users, function (user) { + return user.imported; + }); + }; // download CSV example file $scope.downloadCSVExample = function () { UserCsvExport.downloadExample(); diff --git a/openslides/users/static/templates/users/user-import.html b/openslides/users/static/templates/users/user-import.html index 34b51184e..4b7926a2f 100644 --- a/openslides/users/static/templates/users/user-import.html +++ b/openslides/users/static/templates/users/user-import.html @@ -11,6 +11,10 @@
Copy and paste your participant names in this textbox. Keep each person in a single line.
@@ -23,11 +27,6 @@