2018-01-22 13:01:38 -08:00
|
|
|
'use strict';
|
|
|
|
|
|
2020-06-03 23:08:05 +02:00
|
|
|
/* global angular */
|
|
|
|
|
/* global Clipboard */
|
|
|
|
|
/* global async */
|
2022-02-26 14:09:36 +01:00
|
|
|
/* global ROLES */
|
2020-06-03 23:08:05 +02:00
|
|
|
/* global $ */
|
2018-01-24 16:20:56 +01:00
|
|
|
|
2020-11-13 16:44:39 +01:00
|
|
|
angular.module('Application').controller('UsersController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) {
|
2020-02-24 12:56:13 +01:00
|
|
|
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastUserManager) $location.path('/'); });
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
$scope.ready = false;
|
2020-08-10 13:59:44 -07:00
|
|
|
$scope.users = []; // users of current page
|
2020-01-09 16:21:20 +01:00
|
|
|
$scope.allUsersById = [];
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.groups = [];
|
|
|
|
|
$scope.groupsById = { };
|
|
|
|
|
$scope.config = Client.getConfig();
|
|
|
|
|
$scope.userInfo = Client.getUserInfo();
|
2018-01-23 17:09:47 +01:00
|
|
|
|
2020-11-23 17:25:47 +01:00
|
|
|
$scope.roles = [];
|
2020-08-10 13:59:44 -07:00
|
|
|
$scope.allUsers = []; // all the users and not just current page, have to load this for group assignment
|
2019-11-05 22:08:48 +01:00
|
|
|
|
2019-01-16 14:02:08 +01:00
|
|
|
$scope.userSearchString = '';
|
2019-01-15 13:30:37 +01:00
|
|
|
$scope.currentPage = 1;
|
2023-05-10 13:52:41 +02:00
|
|
|
$scope.pageItems = localStorage.cloudronPageSize || 15;
|
2019-01-16 14:02:08 +01:00
|
|
|
$scope.userRefreshBusy = true;
|
2019-01-15 13:30:37 +01:00
|
|
|
|
2022-02-07 16:56:44 +01:00
|
|
|
$scope.userStates = [
|
2022-02-09 14:35:33 +01:00
|
|
|
{ state: 'ALL', value: null, label: 'All Users' },
|
|
|
|
|
{ state: 'ACTIVE', value: true, label: 'Active Users' },
|
|
|
|
|
{ state: 'INACTIVE', value: false, label: 'Inactive Users' }
|
2022-02-07 16:56:44 +01:00
|
|
|
];
|
2022-02-09 14:35:33 +01:00
|
|
|
$translate(['users.stateFilter.all', 'users.stateFilter.active', 'users.stateFilter.inactive']).then(function (tr) {
|
|
|
|
|
if (tr['users.stateFilter.all']) $scope.userStates.find(function (a) { return a.state === 'ALL'; }).label = tr['users.stateFilter.all'];
|
|
|
|
|
if (tr['users.stateFilter.active']) $scope.userStates.find(function (a) { return a.state === 'ACTIVE'; }).label = tr['users.stateFilter.active'];
|
|
|
|
|
if (tr['users.stateFilter.inactive']) $scope.userStates.find(function (a) { return a.state === 'INACTIVE'; }).label = tr['users.stateFilter.inactive'];
|
|
|
|
|
});
|
|
|
|
|
|
2022-02-07 16:56:44 +01:00
|
|
|
$scope.userStateFilter = $scope.userStates[0];
|
|
|
|
|
$scope.$watch('userStateFilter', function (newVal, oldVal) {
|
|
|
|
|
if (newVal === oldVal) return;
|
|
|
|
|
$scope.updateFilter();
|
|
|
|
|
});
|
|
|
|
|
|
2018-07-26 23:58:25 -07:00
|
|
|
$scope.groupMembers = function (group) {
|
2020-01-09 16:21:20 +01:00
|
|
|
return group.userIds.filter(function (uid) { return !!$scope.allUsersById[uid]; }).map(function (uid) { return $scope.allUsersById[uid].username || $scope.allUsersById[uid].email; }).join(' ');
|
2018-07-26 23:58:25 -07:00
|
|
|
};
|
|
|
|
|
|
2020-03-07 14:05:58 -08:00
|
|
|
$scope.canEdit = function (user) {
|
|
|
|
|
let roleInt1 = $scope.roles.findIndex(function (role) { return role.id === $scope.userInfo.role; });
|
|
|
|
|
let roleInt2 = $scope.roles.findIndex(function (role) { return role.id === user.role; });
|
|
|
|
|
|
|
|
|
|
return (roleInt1 - roleInt2) >= 0;
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-26 14:09:36 +01:00
|
|
|
$scope.canImpersonate = function (user) {
|
|
|
|
|
// only admins can impersonate
|
|
|
|
|
if (!$scope.userInfo.isAtLeastAdmin) return false;
|
|
|
|
|
|
|
|
|
|
// only users with username can be impersonated
|
|
|
|
|
if (!user.username) return false;
|
|
|
|
|
|
|
|
|
|
// normal admins cannot impersonate owners
|
|
|
|
|
if (!$scope.userInfo.isAtLeastOwner && [ ROLES.OWNER ].indexOf(user.role) !== -1) return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-13 13:22:21 +01:00
|
|
|
$scope.userImport = {
|
|
|
|
|
busy: false,
|
|
|
|
|
done: false,
|
|
|
|
|
error: null,
|
|
|
|
|
percent: 0,
|
|
|
|
|
success: 0,
|
|
|
|
|
users: [],
|
2022-02-17 14:42:24 -08:00
|
|
|
sendInvite: false,
|
2022-01-13 13:22:21 +01:00
|
|
|
|
|
|
|
|
reset: function () {
|
|
|
|
|
$scope.userImport.busy = false;
|
|
|
|
|
$scope.userImport.error = null;
|
|
|
|
|
$scope.userImport.users = [];
|
|
|
|
|
$scope.userImport.percent = 0;
|
|
|
|
|
$scope.userImport.success = 0;
|
|
|
|
|
$scope.userImport.done = false;
|
2022-02-17 14:42:24 -08:00
|
|
|
$scope.userImport.sendInvite = false;
|
2022-01-13 13:22:21 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleFileChanged: function () {
|
|
|
|
|
$scope.userImport.reset();
|
|
|
|
|
|
|
|
|
|
var fileInput = document.getElementById('userImportFileInput');
|
|
|
|
|
if (!fileInput.files || !fileInput.files[0]) return;
|
|
|
|
|
|
|
|
|
|
var file = fileInput.files[0];
|
2022-01-20 17:38:47 +01:00
|
|
|
if (file.type !== 'application/json' && file.type !== 'text/csv') return console.log('Unsupported file type.');
|
2022-01-13 13:22:21 +01:00
|
|
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.addEventListener('load', function () {
|
|
|
|
|
$scope.$apply(function () {
|
2022-02-17 14:42:24 -08:00
|
|
|
$scope.userImport.users = [];
|
|
|
|
|
var users = [];
|
2022-01-20 17:38:47 +01:00
|
|
|
if (file.type === 'text/csv') {
|
|
|
|
|
var lines = reader.result.split('\n');
|
|
|
|
|
if (lines.length === 0) return $scope.userImport.error = { file: 'Imported file has no lines' };
|
|
|
|
|
|
2022-02-17 14:42:24 -08:00
|
|
|
for (var i = 0; i < lines.length; i++) {
|
|
|
|
|
var line = lines[i].trim();
|
|
|
|
|
if (!line) continue;
|
2022-01-20 17:38:47 +01:00
|
|
|
var items = line.split(',');
|
2022-02-17 14:42:24 -08:00
|
|
|
if (items.length !== 5) {
|
|
|
|
|
$scope.userImport.error = { file: 'Line ' + (i+1) + ' has wrong column count. Expecting 5' };
|
2022-02-17 17:50:47 -08:00
|
|
|
return;
|
2022-02-17 14:42:24 -08:00
|
|
|
}
|
|
|
|
|
users.push({
|
|
|
|
|
username: items[0].trim(),
|
|
|
|
|
email: items[1].trim(),
|
|
|
|
|
fallbackEmail: items[2].trim(),
|
|
|
|
|
displayName: items[3].trim(),
|
|
|
|
|
role: items[4].trim()
|
2022-01-20 17:38:47 +01:00
|
|
|
});
|
2022-02-17 14:42:24 -08:00
|
|
|
}
|
2022-01-20 17:38:47 +01:00
|
|
|
} else {
|
|
|
|
|
try {
|
2022-02-17 14:42:24 -08:00
|
|
|
users = JSON.parse(reader.result).map(function (user) {
|
|
|
|
|
return {
|
|
|
|
|
username: user.username,
|
|
|
|
|
email: user.email,
|
|
|
|
|
fallbackEmail: user.fallbackEmail,
|
|
|
|
|
displayName: user.displayName,
|
|
|
|
|
role: user.role
|
|
|
|
|
};
|
|
|
|
|
});
|
2022-01-20 17:38:47 +01:00
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Failed to parse users.', e);
|
2022-02-17 14:42:24 -08:00
|
|
|
$scope.userImport.error = { file: 'Imported file is not valid JSON:' + e.message };
|
2022-01-20 17:38:47 +01:00
|
|
|
}
|
2022-01-13 13:22:21 +01:00
|
|
|
}
|
2022-02-17 14:42:24 -08:00
|
|
|
$scope.userImport.users = users;
|
2022-01-13 13:22:21 +01:00
|
|
|
});
|
|
|
|
|
}, false);
|
|
|
|
|
reader.readAsText(file);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
show: function () {
|
|
|
|
|
$scope.userImport.reset();
|
|
|
|
|
|
|
|
|
|
// named so no duplactes
|
|
|
|
|
document.getElementById('userImportFileInput').addEventListener('change', $scope.userImport.handleFileChanged);
|
|
|
|
|
|
|
|
|
|
$('#userImportModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
openFileInput: function () {
|
|
|
|
|
$('#userImportFileInput').click();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
import: function () {
|
|
|
|
|
$scope.userImport.percent = 0;
|
|
|
|
|
$scope.userImport.success = 0;
|
|
|
|
|
$scope.userImport.done = false;
|
|
|
|
|
$scope.userImport.error = { import: [] };
|
|
|
|
|
$scope.userImport.busy = true;
|
|
|
|
|
|
|
|
|
|
var processed = 0;
|
|
|
|
|
|
|
|
|
|
async.eachSeries($scope.userImport.users, function (user, callback) {
|
2022-02-17 14:42:24 -08:00
|
|
|
Client.addUser(user, function (error, userId) {
|
2022-01-13 13:22:21 +01:00
|
|
|
if (error) $scope.userImport.error.import.push({ error: error, user: user });
|
|
|
|
|
else ++$scope.userImport.success;
|
|
|
|
|
|
|
|
|
|
++processed;
|
|
|
|
|
$scope.userImport.percent = 100 * processed / $scope.userImport.users.length;
|
|
|
|
|
|
2022-02-17 14:42:24 -08:00
|
|
|
if (!error && $scope.userImport.sendInvite) {
|
|
|
|
|
console.log('sending', userId, user.email);
|
|
|
|
|
Client.sendInviteEmail(userId, user.email, function (error) {
|
|
|
|
|
if (error) console.error('Failed to send invite.', error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 13:22:21 +01:00
|
|
|
callback();
|
|
|
|
|
});
|
|
|
|
|
}, function (error) {
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.userImport.busy = false;
|
|
|
|
|
$scope.userImport.done = true;
|
2022-02-17 14:42:24 -08:00
|
|
|
if ($scope.userImport.success) {
|
|
|
|
|
refresh();
|
|
|
|
|
refreshAllUsers();
|
|
|
|
|
}
|
2022-01-13 13:22:21 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-22 09:28:41 +01:00
|
|
|
// supported types are 'json' and 'csv'
|
|
|
|
|
$scope.userExport = function (type) {
|
2022-02-14 14:55:04 +01:00
|
|
|
Client.getAllUsers(function (error, result) {
|
2022-01-13 13:22:21 +01:00
|
|
|
if (error) {
|
|
|
|
|
Client.error('Failed to list users. Full error in the webinspector.');
|
|
|
|
|
return console.error('Failed to list users.', error);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 09:28:41 +01:00
|
|
|
var content = '';
|
2022-02-16 21:02:46 -08:00
|
|
|
if (type === 'json') {
|
|
|
|
|
content = JSON.stringify(result.map(function (user) {
|
|
|
|
|
return {
|
|
|
|
|
id: user.id,
|
|
|
|
|
username: user.username,
|
|
|
|
|
email: user.email,
|
2022-02-16 21:23:01 -08:00
|
|
|
fallbackEmail: user.fallbackEmail,
|
2022-02-16 21:02:46 -08:00
|
|
|
displayName: user.displayName,
|
2022-02-16 21:23:01 -08:00
|
|
|
role: user.role,
|
2022-02-16 21:02:46 -08:00
|
|
|
active: user.active
|
|
|
|
|
};
|
|
|
|
|
}), null, 2);
|
|
|
|
|
} else if (type === 'csv') {
|
|
|
|
|
content = result.map(function (user) {
|
2022-02-16 21:23:01 -08:00
|
|
|
return [ user.id, user.username, user.email, user.fallbackEmail, user.displayName, user.role, user.active ].join(',');
|
2022-02-16 21:02:46 -08:00
|
|
|
}).join('\n');
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-01-22 09:28:41 +01:00
|
|
|
|
2022-02-17 16:37:48 -08:00
|
|
|
var file = new Blob([ content ], { type: type === 'json' ? 'application/json' : 'text/csv' });
|
2022-01-13 13:22:21 +01:00
|
|
|
var a = document.createElement('a');
|
|
|
|
|
a.href = URL.createObjectURL(file);
|
2022-02-17 14:42:24 -08:00
|
|
|
a.download = type === 'json' ? 'users.json' : 'users.csv';
|
2022-01-13 13:22:21 +01:00
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.userremove = {
|
|
|
|
|
busy: false,
|
2020-03-06 12:23:50 -08:00
|
|
|
error: null,
|
2018-01-22 13:01:38 -08:00
|
|
|
userInfo: {},
|
|
|
|
|
|
|
|
|
|
show: function (userInfo) {
|
2020-03-06 12:23:50 -08:00
|
|
|
$scope.userremove.error = null;
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.userremove.userInfo = userInfo;
|
|
|
|
|
|
|
|
|
|
$('#userRemoveModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.userremove.busy = true;
|
|
|
|
|
|
2019-07-02 20:22:06 -07:00
|
|
|
Client.removeUser($scope.userremove.userInfo.id, function (error) {
|
|
|
|
|
$scope.userremove.busy = false;
|
2018-07-05 13:32:45 -07:00
|
|
|
|
2020-03-06 13:15:57 -08:00
|
|
|
if (error && error.statusCode === 403) return $scope.userremove.error = error.message;
|
2020-03-06 12:23:50 -08:00
|
|
|
else if (error) return console.error('Unable to delete user.', error);
|
2018-07-05 13:32:45 -07:00
|
|
|
|
2019-07-02 20:22:06 -07:00
|
|
|
$scope.userremove.userInfo = {};
|
2018-07-05 13:32:45 -07:00
|
|
|
|
2019-07-02 20:22:06 -07:00
|
|
|
refresh();
|
2020-02-27 15:10:56 +01:00
|
|
|
refreshAllUsers();
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2019-07-02 20:22:06 -07:00
|
|
|
$('#userRemoveModal').modal('hide');
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.useradd = {
|
|
|
|
|
busy: false,
|
|
|
|
|
alreadyTaken: false,
|
|
|
|
|
error: {},
|
|
|
|
|
email: '',
|
2021-10-26 23:39:15 +02:00
|
|
|
fallbackEmail: '',
|
2018-01-22 13:01:38 -08:00
|
|
|
username: '',
|
|
|
|
|
displayName: '',
|
2018-07-24 22:38:53 -07:00
|
|
|
selectedGroups: [],
|
2020-02-24 12:22:07 +01:00
|
|
|
role: 'user',
|
2021-09-16 20:17:37 +02:00
|
|
|
sendInvite: false,
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
show: function () {
|
|
|
|
|
$scope.useradd.error = {};
|
|
|
|
|
$scope.useradd.email = '';
|
2021-10-26 23:39:15 +02:00
|
|
|
$scope.useradd.fallbackEmail = '';
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.useradd.username = '';
|
|
|
|
|
$scope.useradd.displayName = '';
|
2018-07-24 22:38:53 -07:00
|
|
|
$scope.useradd.selectedGroups = [];
|
2020-02-24 12:22:07 +01:00
|
|
|
$scope.useradd.role = 'user';
|
2021-09-16 20:17:37 +02:00
|
|
|
$scope.useradd.sendInvite = false;
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2022-03-30 09:18:20 -07:00
|
|
|
$scope.useraddForm.$setUntouched();
|
|
|
|
|
$scope.useraddForm.$setPristine();
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
$('#userAddModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.useradd.busy = true;
|
|
|
|
|
|
|
|
|
|
$scope.useradd.alreadyTaken = false;
|
|
|
|
|
$scope.useradd.error.email = null;
|
2021-10-26 23:39:15 +02:00
|
|
|
$scope.useradd.error.fallbackEmail = null;
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.useradd.error.username = null;
|
|
|
|
|
$scope.useradd.error.displayName = null;
|
|
|
|
|
|
2018-07-26 15:52:06 -07:00
|
|
|
var user = {
|
|
|
|
|
username: $scope.useradd.username || null,
|
|
|
|
|
email: $scope.useradd.email,
|
2021-10-26 23:39:15 +02:00
|
|
|
fallbackEmail: $scope.useradd.fallbackEmail,
|
2018-07-26 15:52:06 -07:00
|
|
|
displayName: $scope.useradd.displayName,
|
2020-02-21 21:12:25 +01:00
|
|
|
role: $scope.useradd.role
|
2018-07-26 15:52:06 -07:00
|
|
|
};
|
|
|
|
|
|
2021-08-10 13:53:28 -07:00
|
|
|
Client.addUser(user, function (error, userId) {
|
2018-07-24 22:38:53 -07:00
|
|
|
if (error) {
|
|
|
|
|
$scope.useradd.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error.statusCode === 409) {
|
|
|
|
|
if (error.message.toLowerCase().indexOf('email') !== -1) {
|
|
|
|
|
$scope.useradd.error.email = 'Email already taken';
|
2022-03-30 09:18:20 -07:00
|
|
|
$scope.useraddForm.email.$setPristine();
|
2018-07-24 22:38:53 -07:00
|
|
|
$('#inputUserAddEmail').focus();
|
|
|
|
|
} else if (error.message.toLowerCase().indexOf('username') !== -1 || error.message.toLowerCase().indexOf('mailbox') !== -1) {
|
|
|
|
|
$scope.useradd.error.username = 'Username already taken';
|
2022-03-30 09:18:20 -07:00
|
|
|
$scope.useraddForm.username.$setPristine();
|
2018-07-24 22:38:53 -07:00
|
|
|
$('#inputUserAddUsername').focus();
|
|
|
|
|
} else {
|
|
|
|
|
// should not happen!!
|
|
|
|
|
console.error(error.message);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
} else if (error.statusCode === 400) {
|
|
|
|
|
if (error.message.toLowerCase().indexOf('email') !== -1) {
|
|
|
|
|
$scope.useradd.error.email = 'Invalid Email';
|
|
|
|
|
$scope.useradd.error.emailAttempted = $scope.useradd.email;
|
2022-03-30 09:18:20 -07:00
|
|
|
$scope.useraddForm.email.$setPristine();
|
2018-07-24 22:38:53 -07:00
|
|
|
$('#inputUserAddEmail').focus();
|
|
|
|
|
} else if (error.message.toLowerCase().indexOf('username') !== -1) {
|
|
|
|
|
$scope.useradd.error.username = error.message;
|
2022-03-30 09:18:20 -07:00
|
|
|
$scope.useraddForm.username.$setPristine();
|
2018-07-24 22:38:53 -07:00
|
|
|
$('#inputUserAddUsername').focus();
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Unable to create user.', error.statusCode, error.message);
|
|
|
|
|
}
|
|
|
|
|
return;
|
2018-01-22 13:01:38 -08:00
|
|
|
} else {
|
2018-07-24 22:38:53 -07:00
|
|
|
return console.error('Unable to create user.', error.statusCode, error.message);
|
2018-01-22 13:01:38 -08:00
|
|
|
}
|
|
|
|
|
}
|
2018-07-24 22:38:53 -07:00
|
|
|
|
|
|
|
|
var groupIds = $scope.useradd.selectedGroups.map(function (g) { return g.id; });
|
|
|
|
|
|
2021-09-16 08:40:25 +02:00
|
|
|
Client.setGroups(userId, groupIds, function (error) {
|
2018-07-24 22:38:53 -07:00
|
|
|
$scope.useradd.busy = false;
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-08-17 16:01:40 -07:00
|
|
|
if (error) return console.error(error);
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2021-10-27 19:57:57 +02:00
|
|
|
if ($scope.useradd.sendInvite) Client.sendInviteEmail(userId, user.email, function (error) { if (error) console.error('Failed to send invite.', error); });
|
2021-09-16 20:17:37 +02:00
|
|
|
|
2018-07-24 22:38:53 -07:00
|
|
|
refresh();
|
2020-02-27 15:10:56 +01:00
|
|
|
refreshAllUsers();
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-07-24 22:38:53 -07:00
|
|
|
$('#userAddModal').modal('hide');
|
|
|
|
|
});
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.useredit = {
|
|
|
|
|
busy: false,
|
2021-09-16 13:21:55 +02:00
|
|
|
reset2FABusy: false,
|
2018-01-22 13:01:38 -08:00
|
|
|
error: {},
|
|
|
|
|
userInfo: {},
|
2019-08-30 13:32:20 +02:00
|
|
|
|
|
|
|
|
// form fields
|
2022-01-12 16:20:50 -08:00
|
|
|
username: '',
|
2018-01-22 13:01:38 -08:00
|
|
|
email: '',
|
|
|
|
|
fallbackEmail: '',
|
2018-01-25 18:17:40 +01:00
|
|
|
aliases: {},
|
2018-07-24 15:17:51 -07:00
|
|
|
displayName: '',
|
2019-08-08 07:39:43 -07:00
|
|
|
active: false,
|
2019-08-30 13:32:20 +02:00
|
|
|
source: '',
|
2018-07-24 21:36:50 -07:00
|
|
|
selectedGroups: [],
|
2020-02-17 14:05:26 +01:00
|
|
|
role: '',
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
show: function (userInfo) {
|
|
|
|
|
$scope.useredit.error = {};
|
2022-01-12 16:20:50 -08:00
|
|
|
$scope.useredit.username = userInfo.username;
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.useredit.email = userInfo.email;
|
2018-07-24 15:17:51 -07:00
|
|
|
$scope.useredit.displayName = userInfo.displayName;
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.useredit.fallbackEmail = userInfo.fallbackEmail;
|
|
|
|
|
$scope.useredit.userInfo = userInfo;
|
2018-07-24 21:36:50 -07:00
|
|
|
$scope.useredit.selectedGroups = userInfo.groupIds.map(function (gid) { return $scope.groupsById[gid]; });
|
2019-08-08 07:39:43 -07:00
|
|
|
$scope.useredit.active = userInfo.active;
|
2019-08-30 13:32:20 +02:00
|
|
|
$scope.useredit.source = userInfo.source;
|
2020-02-24 12:22:07 +01:00
|
|
|
$scope.useredit.role = userInfo.role;
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
$scope.useredit_form.$setPristine();
|
|
|
|
|
$scope.useredit_form.$setUntouched();
|
|
|
|
|
|
|
|
|
|
$('#userEditModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.useredit.error = {};
|
|
|
|
|
$scope.useredit.busy = true;
|
|
|
|
|
|
2018-01-26 15:39:35 +01:00
|
|
|
var userId = $scope.useredit.userInfo.id;
|
2018-01-22 13:01:38 -08:00
|
|
|
var data = {
|
2020-03-05 16:23:27 -08:00
|
|
|
id: userId
|
2018-01-22 13:01:38 -08:00
|
|
|
};
|
|
|
|
|
|
2020-03-05 16:23:27 -08:00
|
|
|
// only send if not the current active user
|
|
|
|
|
if (userId !== $scope.userInfo.id) {
|
|
|
|
|
data.active = $scope.useredit.active;
|
|
|
|
|
data.role = $scope.useredit.role;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 13:32:20 +02:00
|
|
|
// only change those if it is a local user
|
|
|
|
|
if (!$scope.useredit.source) {
|
2022-01-12 16:20:50 -08:00
|
|
|
// username is settable only if it was empty previously. it's editable for the "lock" profiles feature
|
|
|
|
|
if (!$scope.useredit.userInfo.username) data.username = $scope.useredit.username;
|
2019-08-30 13:32:20 +02:00
|
|
|
data.email = $scope.useredit.email;
|
|
|
|
|
data.displayName = $scope.useredit.displayName;
|
|
|
|
|
data.fallbackEmail = $scope.useredit.fallbackEmail;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
Client.updateUser(data, function (error) {
|
|
|
|
|
if (error) {
|
|
|
|
|
$scope.useredit.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error.statusCode === 409) {
|
2022-01-12 16:20:50 -08:00
|
|
|
if (error.message.toLowerCase().indexOf('email') !== -1) {
|
|
|
|
|
$scope.useredit.error.email = 'Email already taken';
|
|
|
|
|
} else if (error.message.toLowerCase().indexOf('username') !== -1) {
|
|
|
|
|
$scope.useredit.error.username = 'Username already taken';
|
|
|
|
|
}
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.useredit_form.email.$setPristine();
|
|
|
|
|
$('#inputUserEditEmail').focus();
|
|
|
|
|
} else {
|
2020-10-23 11:47:37 -07:00
|
|
|
$scope.useredit.error.generic = error.message;
|
2018-01-22 13:01:38 -08:00
|
|
|
console.error('Unable to update user:', error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 21:36:50 -07:00
|
|
|
var groupIds = $scope.useredit.selectedGroups.map(function (g) { return g.id; });
|
|
|
|
|
|
|
|
|
|
Client.setGroups(data.id, groupIds, function (error) {
|
2018-04-01 20:53:49 +02:00
|
|
|
$scope.useredit.busy = false;
|
2018-07-24 22:38:53 -07:00
|
|
|
|
|
|
|
|
if (error) return console.error('Unable to update groups for user:', error);
|
2018-04-01 20:53:49 +02:00
|
|
|
|
2020-03-05 16:14:03 -08:00
|
|
|
refreshUsers(false);
|
2018-04-01 20:53:49 +02:00
|
|
|
|
|
|
|
|
$('#userEditModal').modal('hide');
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
});
|
2021-09-16 13:21:55 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
reset2FA: function () {
|
|
|
|
|
$scope.useredit.reset2FABusy = true;
|
|
|
|
|
|
|
|
|
|
Client.disableTwoFactorAuthenticationByUserId($scope.useredit.userInfo.id, function (error) {
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
2021-10-19 19:08:53 -07:00
|
|
|
$timeout(function () {
|
|
|
|
|
$scope.useredit.userInfo.twoFactorAuthenticationEnabled = false;
|
|
|
|
|
$scope.useredit.reset2FABusy = false;
|
|
|
|
|
}, 3000);
|
2021-09-16 13:21:55 +02:00
|
|
|
});
|
2018-01-22 13:01:38 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.groupAdd = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
name: '',
|
2018-08-05 22:19:54 -07:00
|
|
|
selectedUsers: [],
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
show: function () {
|
|
|
|
|
$scope.groupAdd.busy = false;
|
|
|
|
|
|
|
|
|
|
$scope.groupAdd.error = {};
|
|
|
|
|
$scope.groupAdd.name = '';
|
|
|
|
|
|
|
|
|
|
$scope.groupAddForm.$setUntouched();
|
|
|
|
|
$scope.groupAddForm.$setPristine();
|
|
|
|
|
|
|
|
|
|
$('#groupAddModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.groupAdd.busy = true;
|
|
|
|
|
$scope.groupAdd.error = {};
|
|
|
|
|
|
2018-08-05 22:19:54 -07:00
|
|
|
Client.createGroup($scope.groupAdd.name, function (error, result) {
|
|
|
|
|
if (error) {
|
|
|
|
|
$scope.groupAdd.busy = false;
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-08-05 22:19:54 -07:00
|
|
|
if (error.statusCode === 409) {
|
|
|
|
|
$scope.groupAdd.error.name = 'Name already taken';
|
|
|
|
|
$scope.groupAddForm.name.$setPristine();
|
|
|
|
|
$('#groupAddName').focus();
|
|
|
|
|
return;
|
|
|
|
|
} else if (error.statusCode === 400) {
|
|
|
|
|
$scope.groupAdd.error.name = error.message;
|
|
|
|
|
$scope.groupAddForm.name.$setPristine();
|
|
|
|
|
$('#groupAddName').focus();
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
return console.error('Unable to create group.', error.statusCode, error.message);
|
|
|
|
|
}
|
2018-01-22 13:01:38 -08:00
|
|
|
}
|
|
|
|
|
|
2018-08-05 22:19:54 -07:00
|
|
|
var userIds = $scope.groupAdd.selectedUsers.map(function (u) { return u.id; });
|
|
|
|
|
|
|
|
|
|
Client.setGroupMembers(result.id, userIds, function (error) {
|
|
|
|
|
$scope.groupAdd.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) return console.error('Unable to add memebers.', error.statusCode, error.message);
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
|
|
|
|
|
$('#groupAddModal').modal('hide');
|
|
|
|
|
});
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-18 17:45:09 -07:00
|
|
|
$scope.groupEdit = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
groupInfo: {},
|
|
|
|
|
name: '',
|
2020-06-05 08:18:40 +02:00
|
|
|
source: '',
|
2018-07-24 22:20:00 -07:00
|
|
|
selectedUsers: [],
|
2021-02-18 17:01:49 +01:00
|
|
|
selectedApps: [],
|
|
|
|
|
selectedAppsOriginal: [],
|
|
|
|
|
apps: [],
|
2018-07-24 22:20:00 -07:00
|
|
|
|
2018-06-18 17:45:09 -07:00
|
|
|
show: function (groupInfo) {
|
|
|
|
|
$scope.groupEdit.error = {};
|
|
|
|
|
$scope.groupEdit.groupInfo = groupInfo;
|
|
|
|
|
$scope.groupEdit.name = groupInfo.name;
|
2020-06-05 08:18:40 +02:00
|
|
|
$scope.groupEdit.source = groupInfo.source;
|
2020-01-09 16:21:20 +01:00
|
|
|
$scope.groupEdit.selectedUsers = groupInfo.userIds.map(function (uid) { return $scope.allUsersById[uid]; });
|
2021-02-18 17:01:49 +01:00
|
|
|
$scope.groupEdit.apps = Client.getInstalledApps();
|
|
|
|
|
|
|
|
|
|
$scope.groupEdit.selectedApps = Client.getInstalledApps().filter(function (app) {
|
|
|
|
|
if (app.accessRestriction === null || !Array.isArray(app.accessRestriction.groups)) return false;
|
|
|
|
|
return app.accessRestriction.groups.indexOf(groupInfo.id) !== -1;
|
|
|
|
|
});
|
|
|
|
|
angular.copy($scope.groupEdit.selectedApps, $scope.groupEdit.selectedAppsOriginal);
|
2018-06-18 17:45:09 -07:00
|
|
|
|
|
|
|
|
$scope.groupEdit_form.$setPristine();
|
|
|
|
|
$scope.groupEdit_form.$setUntouched();
|
|
|
|
|
|
|
|
|
|
$('#groupEditModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.groupEdit.busy = true;
|
|
|
|
|
$scope.groupEdit.error = {};
|
|
|
|
|
|
2018-07-26 11:38:20 -07:00
|
|
|
Client.updateGroup($scope.groupEdit.groupInfo.id, $scope.groupEdit.name, function (error) {
|
2018-07-24 22:31:22 -07:00
|
|
|
if (error) {
|
|
|
|
|
$scope.groupEdit.busy = false;
|
2018-06-18 17:45:09 -07:00
|
|
|
|
2018-07-24 22:31:22 -07:00
|
|
|
if (error.statusCode === 409) {
|
|
|
|
|
$scope.groupEdit.error.name = 'Name already taken';
|
|
|
|
|
$scope.groupEditForm.name.$setPristine();
|
|
|
|
|
$('#groupEditName').focus();
|
|
|
|
|
return;
|
|
|
|
|
} else if (error.statusCode === 400) {
|
|
|
|
|
$scope.groupEdit.error.name = error.message;
|
|
|
|
|
$scope.groupEditForm.name.$setPristine();
|
|
|
|
|
$('#groupEditName').focus();
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
return console.error('Unable to edit group.', error.statusCode, error.message);
|
|
|
|
|
}
|
2018-06-18 17:45:09 -07:00
|
|
|
}
|
|
|
|
|
|
2018-07-24 22:20:00 -07:00
|
|
|
var userIds = $scope.groupEdit.selectedUsers.map(function (u) { return u.id; });
|
|
|
|
|
|
|
|
|
|
Client.setGroupMembers($scope.groupEdit.groupInfo.id, userIds, function (error) {
|
2021-02-18 17:01:49 +01:00
|
|
|
if (error) {
|
|
|
|
|
$scope.groupEdit.busy = false;
|
|
|
|
|
return console.error('Unable to set group members.', error.statusCode, error.message);
|
|
|
|
|
}
|
2018-07-24 22:20:00 -07:00
|
|
|
|
2021-02-18 17:01:49 +01:00
|
|
|
// find apps where ACL has changed
|
|
|
|
|
var addedApps = $scope.groupEdit.selectedApps.filter(function (a) {
|
|
|
|
|
return !$scope.groupEdit.selectedAppsOriginal.find(function (b) { return b.id === a.id; });
|
|
|
|
|
});
|
|
|
|
|
var removedApps = $scope.groupEdit.selectedAppsOriginal.filter(function (a) {
|
|
|
|
|
return !$scope.groupEdit.selectedApps.find(function (b) { return b.id === a.id; });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
async.eachSeries(addedApps, function (app, callback) {
|
|
|
|
|
var accessRestriction = app.accessRestriction;
|
|
|
|
|
if (!accessRestriction) accessRestriction = { users: [], groups: [] };
|
|
|
|
|
if (!Array.isArray(accessRestriction.groups)) accessRestriction.groups = [];
|
|
|
|
|
|
|
|
|
|
accessRestriction.groups.push($scope.groupEdit.groupInfo.id);
|
|
|
|
|
|
|
|
|
|
Client.configureApp(app.id, 'access_restriction', { accessRestriction: accessRestriction }, callback);
|
|
|
|
|
}, function (error) {
|
|
|
|
|
if (error) {
|
|
|
|
|
$scope.groupEdit.busy = false;
|
|
|
|
|
return console.error('Unable to set added app access.', error.statusCode, error.message);
|
|
|
|
|
}
|
2018-06-18 17:45:09 -07:00
|
|
|
|
2021-02-18 17:01:49 +01:00
|
|
|
async.eachSeries(removedApps, function (app, callback) {
|
|
|
|
|
var accessRestriction = app.accessRestriction;
|
|
|
|
|
if (!accessRestriction) accessRestriction = { users: [], groups: [] };
|
|
|
|
|
if (!Array.isArray(accessRestriction.groups)) accessRestriction.groups = [];
|
|
|
|
|
|
|
|
|
|
var deleted = accessRestriction.groups.splice(accessRestriction.groups.indexOf($scope.groupEdit.groupInfo.id), 1);
|
|
|
|
|
|
|
|
|
|
// if not found return early
|
|
|
|
|
if (deleted.length === 0) return callback();
|
|
|
|
|
|
|
|
|
|
Client.configureApp(app.id, 'access_restriction', { accessRestriction: accessRestriction }, callback);
|
|
|
|
|
}, function (error) {
|
|
|
|
|
$scope.groupEdit.busy = false;
|
|
|
|
|
if (error) return console.error('Unable to set removed app access.', error.statusCode, error.message);
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
|
|
|
|
|
// refresh apps to reflect change
|
|
|
|
|
Client.refreshInstalledApps();
|
2018-07-24 22:20:00 -07:00
|
|
|
|
2021-02-18 17:01:49 +01:00
|
|
|
$('#groupEditModal').modal('hide');
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-07-24 22:20:00 -07:00
|
|
|
});
|
2018-06-18 17:45:09 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.groupRemove = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
group: null,
|
|
|
|
|
memberCount: 0,
|
|
|
|
|
|
|
|
|
|
show: function (group) {
|
|
|
|
|
$scope.groupRemove.busy = false;
|
|
|
|
|
|
|
|
|
|
$scope.groupRemove.error = {};
|
|
|
|
|
|
|
|
|
|
$scope.groupRemove.group = angular.copy(group);
|
|
|
|
|
|
|
|
|
|
Client.getGroup(group.id, function (error, result) {
|
|
|
|
|
if (error) return console.error('Unable to fetch group information.', error.statusCode, error.message);
|
|
|
|
|
|
|
|
|
|
$scope.groupRemove.memberCount = result.userIds.length;
|
|
|
|
|
|
|
|
|
|
$('#groupRemoveModal').modal('show');
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.groupRemove.busy = true;
|
|
|
|
|
$scope.groupRemove.error = {};
|
|
|
|
|
|
2019-05-13 23:55:54 +02:00
|
|
|
Client.removeGroup($scope.groupRemove.group.id, function (error) {
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.groupRemove.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) return console.error('Unable to remove group.', error.statusCode, error.message);
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
$('#groupRemoveModal').modal('hide');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.isMe = function (user) {
|
|
|
|
|
return user.username === Client.getUserInfo().username;
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-16 14:35:17 +02:00
|
|
|
$scope.passwordReset = {
|
2018-08-17 16:01:40 -07:00
|
|
|
busy: false,
|
2021-09-16 14:35:17 +02:00
|
|
|
resetLink: '',
|
2018-08-17 16:01:40 -07:00
|
|
|
user: null,
|
2021-10-27 18:36:41 +02:00
|
|
|
email: '',
|
2022-11-10 13:43:27 +01:00
|
|
|
emailError: null,
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-08-17 16:01:40 -07:00
|
|
|
show: function (user) {
|
2021-10-27 21:59:30 +02:00
|
|
|
$scope.passwordReset.busy = false;
|
|
|
|
|
$scope.passwordReset.resetLink = '';
|
2021-09-16 14:35:17 +02:00
|
|
|
$scope.passwordReset.user = user;
|
2021-10-27 18:36:41 +02:00
|
|
|
$scope.passwordReset.email = user.fallbackEmail || user.email;
|
2022-11-10 13:43:27 +01:00
|
|
|
$scope.passwordReset.emailError = null;
|
2021-10-27 18:36:41 +02:00
|
|
|
|
|
|
|
|
Client.getPasswordResetLink(user.id, function (error, result) {
|
|
|
|
|
if (error) return console.error('Failed to get password reset link.', error);
|
2018-08-17 16:01:40 -07:00
|
|
|
|
2021-10-27 18:36:41 +02:00
|
|
|
$scope.passwordReset.resetLink = result.passwordResetLink;
|
|
|
|
|
|
|
|
|
|
$('#passwordResetModal').modal('show');
|
|
|
|
|
});
|
2018-08-17 16:01:40 -07:00
|
|
|
},
|
|
|
|
|
|
2021-10-27 18:36:41 +02:00
|
|
|
sendEmail: function () {
|
2021-09-16 14:35:17 +02:00
|
|
|
$scope.passwordReset.busy = true;
|
2022-11-10 13:43:27 +01:00
|
|
|
$scope.passwordReset.emailError = null;
|
2021-04-15 10:54:55 +02:00
|
|
|
|
2021-10-27 18:36:41 +02:00
|
|
|
Client.sendPasswordResetEmail($scope.passwordReset.user.id, $scope.passwordReset.email, function (error) {
|
|
|
|
|
$scope.passwordReset.busy = false;
|
2021-04-15 10:54:55 +02:00
|
|
|
|
2022-11-10 13:43:27 +01:00
|
|
|
if (error) {
|
|
|
|
|
$scope.passwordReset.emailError = error.message;
|
|
|
|
|
} else {
|
|
|
|
|
$scope.passwordReset.emailError = '';
|
|
|
|
|
}
|
2018-08-17 16:01:40 -07:00
|
|
|
});
|
|
|
|
|
}
|
2018-01-22 13:01:38 -08:00
|
|
|
};
|
|
|
|
|
|
2022-04-24 22:11:35 +02:00
|
|
|
$scope.makeLocal = {
|
|
|
|
|
busy: false,
|
|
|
|
|
user: null,
|
|
|
|
|
|
|
|
|
|
show: function (user) {
|
|
|
|
|
$scope.makeLocal.busy = false;
|
|
|
|
|
$scope.makeLocal.user = user;
|
|
|
|
|
|
|
|
|
|
$('#makeLocalModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.makeLocal.busy = false;
|
|
|
|
|
|
|
|
|
|
Client.makeUserLocal($scope.makeLocal.user.id, function (error) {
|
|
|
|
|
if (error) return console.error('Failed to make user local.', error);
|
|
|
|
|
|
|
|
|
|
$scope.makeLocal.busy = false;
|
|
|
|
|
|
|
|
|
|
refreshUsers();
|
|
|
|
|
|
|
|
|
|
$('#makeLocalModal').modal('hide');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-16 15:46:26 +02:00
|
|
|
$scope.invitation = {
|
|
|
|
|
busy: false,
|
|
|
|
|
inviteLink: '',
|
|
|
|
|
user: null,
|
2021-10-27 19:57:57 +02:00
|
|
|
email: '',
|
2021-09-16 15:46:26 +02:00
|
|
|
|
|
|
|
|
show: function (user) {
|
2021-10-27 21:59:30 +02:00
|
|
|
$scope.invitation.busy = false;
|
|
|
|
|
$scope.invitation.inviteLink = '';
|
2021-09-16 15:46:26 +02:00
|
|
|
$scope.invitation.user = user;
|
2021-10-27 21:59:30 +02:00
|
|
|
$scope.invitation.email = user.email;
|
2021-10-27 19:57:57 +02:00
|
|
|
|
|
|
|
|
Client.getInviteLink(user.id, function (error, result) {
|
|
|
|
|
if (error) return console.error('Failed to get invite link.', error);
|
|
|
|
|
|
2021-10-27 21:59:30 +02:00
|
|
|
$scope.invitation.inviteLink = result.inviteLink;
|
2021-09-16 15:46:26 +02:00
|
|
|
|
2021-10-27 19:57:57 +02:00
|
|
|
$('#invitationModal').modal('show');
|
|
|
|
|
});
|
2021-09-16 15:46:26 +02:00
|
|
|
},
|
|
|
|
|
|
2021-10-27 19:57:57 +02:00
|
|
|
sendEmail: function () {
|
2021-09-16 15:46:26 +02:00
|
|
|
$scope.invitation.busy = true;
|
|
|
|
|
|
2021-10-27 19:57:57 +02:00
|
|
|
Client.sendInviteEmail($scope.invitation.user.id, $scope.invitation.email, function (error) {
|
|
|
|
|
if (error) return console.error('Failed to send invite email.', error);
|
2021-09-16 15:46:26 +02:00
|
|
|
|
2021-10-27 19:57:57 +02:00
|
|
|
$scope.invitation.busy = false;
|
2021-09-16 15:46:26 +02:00
|
|
|
|
2021-10-28 10:20:41 -07:00
|
|
|
Client.notify($translate.instant('users.invitationNotification.title'), $translate.instant('users.invitationNotification.body', { email: $scope.invitation.email }), false, 'success');
|
2021-09-16 15:46:26 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-17 15:53:11 +02:00
|
|
|
// https://stackoverflow.com/questions/1497481/javascript-password-generator
|
|
|
|
|
function generatePassword() {
|
|
|
|
|
var length = 12,
|
2022-06-22 17:56:24 -07:00
|
|
|
charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
|
|
|
|
|
retVal = '';
|
2021-09-17 15:53:11 +02:00
|
|
|
for (var i = 0, n = charset.length; i < length; ++i) {
|
|
|
|
|
retVal += charset.charAt(Math.floor(Math.random() * n));
|
|
|
|
|
}
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.setGhost = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: null,
|
2021-09-17 16:08:13 +02:00
|
|
|
success: false,
|
2021-09-17 15:53:11 +02:00
|
|
|
user: null,
|
|
|
|
|
password: '',
|
|
|
|
|
|
|
|
|
|
show: function (user) {
|
|
|
|
|
$scope.setGhost.busy = false;
|
2021-09-17 16:08:13 +02:00
|
|
|
$scope.setGhost.success = false;
|
2021-09-17 15:53:11 +02:00
|
|
|
$scope.setGhost.error = null;
|
|
|
|
|
$scope.setGhost.user = user;
|
2021-11-24 16:35:54 +01:00
|
|
|
$scope.setGhost.password = '';
|
2021-09-17 15:53:11 +02:00
|
|
|
|
|
|
|
|
$('#setGhostModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
2021-11-24 16:35:54 +01:00
|
|
|
generatePassword: function () {
|
|
|
|
|
$scope.setGhost.password = generatePassword();
|
|
|
|
|
},
|
|
|
|
|
|
2021-09-17 15:53:11 +02:00
|
|
|
submit: function () {
|
|
|
|
|
$scope.setGhost.busy = true;
|
|
|
|
|
|
|
|
|
|
Client.setGhost($scope.setGhost.user.id, $scope.setGhost.password, null, function (error) {
|
|
|
|
|
$scope.setGhost.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
$scope.setGhost.error = error.message;
|
|
|
|
|
return console.error(error);
|
|
|
|
|
}
|
2021-09-17 16:08:13 +02:00
|
|
|
|
|
|
|
|
$scope.setGhost.success = true;
|
2021-09-17 15:53:11 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-25 16:14:14 -07:00
|
|
|
function getUsers(callback) {
|
2019-01-16 14:02:08 +01:00
|
|
|
var users = [];
|
2018-06-25 16:14:14 -07:00
|
|
|
|
2022-02-07 16:56:44 +01:00
|
|
|
Client.getUsers($scope.userSearchString, $scope.userStateFilter.value, $scope.currentPage, $scope.pageItems, function (error, results) {
|
2018-06-25 16:14:14 -07:00
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
2021-12-09 21:21:21 +01:00
|
|
|
async.eachOf(results, function (result, index, iteratorDone) {
|
2018-06-25 16:14:14 -07:00
|
|
|
Client.getUser(result.id, function (error, user) {
|
|
|
|
|
if (error) return iteratorDone(error);
|
|
|
|
|
|
2020-09-16 16:03:14 -07:00
|
|
|
users[index] = user; // keep the sorting order
|
2018-06-25 16:14:14 -07:00
|
|
|
|
|
|
|
|
iteratorDone();
|
|
|
|
|
});
|
|
|
|
|
}, function (error) {
|
|
|
|
|
callback(error, users);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-10 13:59:44 -07:00
|
|
|
function refreshUsers(showBusy) { // loads users on current page only
|
2020-03-05 16:14:03 -08:00
|
|
|
if (showBusy) $scope.userRefreshBusy = true;
|
2019-01-15 13:30:37 +01:00
|
|
|
|
|
|
|
|
getUsers(function (error, result) {
|
|
|
|
|
if (error) return console.error('Unable to get user listing.', error);
|
|
|
|
|
|
|
|
|
|
angular.copy(result, $scope.users);
|
|
|
|
|
|
|
|
|
|
$scope.ready = true;
|
|
|
|
|
$scope.userRefreshBusy = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-23 11:12:26 +02:00
|
|
|
function refreshGroups(callback) {
|
2020-08-10 13:59:44 -07:00
|
|
|
Client.getGroups(function (error, result) {
|
2022-04-23 11:12:26 +02:00
|
|
|
if (error) {
|
|
|
|
|
if (callback) return callback(error);
|
|
|
|
|
else return console.error('Unable to get group listing.', error);
|
|
|
|
|
}
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-07-24 22:20:00 -07:00
|
|
|
angular.copy(result, $scope.groups);
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.groupsById = { };
|
|
|
|
|
for (var i = 0; i < result.length; i++) {
|
|
|
|
|
$scope.groupsById[result[i].id] = result[i];
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-23 11:12:26 +02:00
|
|
|
if (callback) callback();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refresh() {
|
|
|
|
|
refreshGroups(function (error) {
|
|
|
|
|
if (error) return console.error('Unable to get group listing.', error);
|
2020-03-05 16:14:03 -08:00
|
|
|
refreshUsers(true);
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-15 13:30:37 +01:00
|
|
|
$scope.showNextPage = function () {
|
|
|
|
|
$scope.currentPage++;
|
|
|
|
|
refreshUsers();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.showPrevPage = function () {
|
|
|
|
|
if ($scope.currentPage > 1) $scope.currentPage--;
|
|
|
|
|
else $scope.currentPage = 1;
|
|
|
|
|
refreshUsers();
|
|
|
|
|
};
|
|
|
|
|
|
2021-12-09 21:21:21 +01:00
|
|
|
$scope.updateFilter = function () {
|
2019-01-15 13:30:37 +01:00
|
|
|
refreshUsers();
|
|
|
|
|
};
|
|
|
|
|
|
2020-08-10 13:59:44 -07:00
|
|
|
function refreshAllUsers() { // this loads all users on Cloudron, not just current page
|
2022-02-14 14:55:04 +01:00
|
|
|
Client.getAllUsers(function (error, results) {
|
2019-11-05 22:08:48 +01:00
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.allUsers = results;
|
2020-01-09 16:21:20 +01:00
|
|
|
|
|
|
|
|
$scope.allUsersById = {};
|
|
|
|
|
for (var i = 0; i < results.length; i++) {
|
|
|
|
|
$scope.allUsersById[results[i].id] = results[i];
|
|
|
|
|
}
|
2019-11-05 22:08:48 +01:00
|
|
|
});
|
2020-02-27 15:10:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-26 16:48:51 +01:00
|
|
|
Client.onReady(function () {
|
2022-01-13 07:47:34 -08:00
|
|
|
refresh();
|
|
|
|
|
refreshAllUsers();
|
|
|
|
|
|
2020-11-23 17:25:47 +01:00
|
|
|
// Order matters for permissions used in canEdit
|
2020-03-06 12:33:36 -08:00
|
|
|
$scope.roles = [
|
2020-11-23 17:25:47 +01:00
|
|
|
{ id: 'user', name: $translate.instant('users.role.user'), disabled: false },
|
|
|
|
|
{ id: 'usermanager', name: $translate.instant('users.role.usermanager'), disabled: false },
|
2021-12-02 09:29:33 -08:00
|
|
|
{ id: 'mailmanager', name: $translate.instant('users.role.mailmanager'), disabled: false },
|
2020-11-23 17:25:47 +01:00
|
|
|
{ id: 'admin', name: $translate.instant('users.role.admin'), disabled: !$scope.user.isAtLeastAdmin },
|
|
|
|
|
{ id: 'owner', name: $translate.instant('users.role.owner'), disabled: !$scope.user.isAtLeastOwner }
|
2020-03-06 12:33:36 -08:00
|
|
|
];
|
2021-12-09 21:24:51 +01:00
|
|
|
|
|
|
|
|
// give search the initial focus
|
|
|
|
|
setTimeout(function () { $('#userSearchInput').focus(); }, 1);
|
2020-02-26 16:48:51 +01:00
|
|
|
});
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
// setup all the dialog focus handling
|
2018-06-18 17:45:09 -07:00
|
|
|
['userAddModal', 'userRemoveModal', 'userEditModal', 'groupAddModal', 'groupEditModal', 'groupRemoveModal'].forEach(function (id) {
|
2018-01-22 13:01:38 -08:00
|
|
|
$('#' + id).on('shown.bs.modal', function () {
|
2023-08-09 23:53:36 +02:00
|
|
|
$(this).find('[autofocus]:first').focus();
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2021-10-27 22:41:02 +02:00
|
|
|
new Clipboard('#passwordResetLinkClipboardButton').on('success', function(e) {
|
|
|
|
|
$('#passwordResetLinkClipboardButton').tooltip({
|
2018-01-22 13:01:38 -08:00
|
|
|
title: 'Copied!',
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
}).tooltip('show');
|
|
|
|
|
|
2021-10-27 22:41:02 +02:00
|
|
|
$timeout(function () { $('#passwordResetLinkClipboardButton').tooltip('hide'); }, 2000);
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
e.clearSelection();
|
2021-10-27 22:41:02 +02:00
|
|
|
}).on('error', function(/*e*/) {
|
|
|
|
|
$('#passwordResetLinkClipboardButton').tooltip({
|
|
|
|
|
title: 'Press Ctrl+C to copy',
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
}).tooltip('show');
|
|
|
|
|
|
|
|
|
|
$timeout(function () { $('#passwordResetLinkClipboardButton').tooltip('hide'); }, 2000);
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
|
2021-10-27 22:41:02 +02:00
|
|
|
new Clipboard('#invitationLinkClipboardButton').on('success', function(e) {
|
|
|
|
|
$('#invitationLinkClipboardButton').tooltip({
|
|
|
|
|
title: 'Copied!',
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
}).tooltip('show');
|
|
|
|
|
|
|
|
|
|
$timeout(function () { $('#invitationLinkClipboardButton').tooltip('hide'); }, 2000);
|
|
|
|
|
|
|
|
|
|
e.clearSelection();
|
|
|
|
|
}).on('error', function(/*e*/) {
|
|
|
|
|
$('#invitationLinkClipboardButton').tooltip({
|
2018-01-22 13:01:38 -08:00
|
|
|
title: 'Press Ctrl+C to copy',
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
}).tooltip('show');
|
|
|
|
|
|
2021-10-27 22:41:02 +02:00
|
|
|
$timeout(function () { $('#invitationLinkClipboardButton').tooltip('hide'); }, 2000);
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
|
2021-10-27 22:41:02 +02:00
|
|
|
new Clipboard('#setGhostClipboardButton').on('success', function(e) {
|
2021-09-17 15:53:11 +02:00
|
|
|
$('#setGhostClipboardButton').tooltip({
|
|
|
|
|
title: 'Copied!',
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
}).tooltip('show');
|
|
|
|
|
|
|
|
|
|
$timeout(function () { $('#setGhostClipboardButton').tooltip('hide'); }, 2000);
|
|
|
|
|
|
|
|
|
|
e.clearSelection();
|
2021-10-27 22:41:02 +02:00
|
|
|
}).on('error', function(/*e*/) {
|
2021-09-17 15:53:11 +02:00
|
|
|
$('#setGhostClipboardButton').tooltip({
|
|
|
|
|
title: 'Press Ctrl+C to copy',
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
}).tooltip('show');
|
|
|
|
|
|
|
|
|
|
$timeout(function () { $('#setGhostClipboardButton').tooltip('hide'); }, 2000);
|
|
|
|
|
});
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
$('.modal-backdrop').remove();
|
|
|
|
|
}]);
|