Files
cloudron-box/dashboard/public/views/users.html

524 lines
37 KiB
HTML
Raw Normal View History

2018-01-22 13:01:38 -08:00
<!-- Modal add user -->
<div class="modal fade" id="userAddModal" tabindex="-1" role="dialog">
2020-11-13 16:44:39 +01:00
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'users.addUserDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
2024-01-19 23:35:02 +01:00
<form name="useraddForm" role="form" ng-submit="userAdd.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (useraddForm.displayName.$dirty && useraddForm.displayName.$invalid) || (!useraddForm.displayName.$dirty && userAdd.error.displayName) }">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.user.fullName' | tr }}</label>
2024-01-19 23:35:02 +01:00
<input type="text" class="form-control" ng-model="userAdd.displayName" name="displayName" id="inputUserAddDisplayName" autofocus autocomplete="off" placeholder="{{ 'users.user.displayNamePlaceholder' | tr }}">
<div class="control-label" ng-show="(!useraddForm.displayName.$dirty && userAdd.error.displayName) || (useraddForm.displayName.$dirty && useraddForm.displayName.$invalid) || (!useraddForm.displayName.$dirty && userAdd.error.displayName)">
2022-03-30 09:18:20 -07:00
<small ng-show="useraddForm.displayName.$error.displayName">{{ 'users.user.errorNotValidFullName' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useraddForm.displayName.$dirty && userAdd.error.displayName">{{ userAdd.error.displayName }}</small>
2020-11-13 16:44:39 +01:00
</div>
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-class="{ 'has-error': (useraddForm.email.$dirty && useraddForm.email.$invalid) || (!useraddForm.email.$dirty && userAdd.error.email) }">
2022-05-26 14:42:52 -07:00
<label class="control-label">{{ 'users.user.primaryEmail' | tr }} <sup><a ng-href="https://docs.cloudron.io/profile/#primary-email" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></label>
2024-01-19 23:35:02 +01:00
<input type="email" class="form-control" ng-model="userAdd.email" name="email" id="inputUserAddEmail" required>
<div class="control-label" ng-show="(!useraddForm.email.$dirty && userAdd.error.email) || (useraddForm.email.$dirty && useraddForm.email.$invalid) || (!useraddForm.email.$dirty && userAdd.error.email)">
2022-03-30 09:18:20 -07:00
<small ng-show="useraddForm.email.$error.required">{{ 'users.user.errorEmailRequired' | tr }}</small>
<small ng-show="useraddForm.email.$error.email">{{ 'users.user.errorInvalidEmail' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useraddForm.email.$dirty && userAdd.error.email">{{ userAdd.error.email }}</small>
2018-01-22 13:01:38 -08:00
</div>
2020-11-13 16:44:39 +01:00
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-class="{ 'has-error': (useraddForm.fallbackEmail.$dirty && useraddForm.fallbackEmail.$invalid) || (!useraddForm.fallbackEmail.$dirty && userAdd.error.fallbackEmail) }">
2022-05-26 14:42:52 -07:00
<label class="control-label">{{ 'users.user.recoveryEmail' | tr }} <sup><a ng-href="https://docs.cloudron.io/profile/#password-recovery-email" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></label>
2024-01-19 23:35:02 +01:00
<input type="email" class="form-control" ng-model="userAdd.fallbackEmail" name="fallbackEmail" id="inputUserAddFallbackEmail" placeholder="{{ 'users.user.fallbackEmailPlaceholder' | tr }}">
<div class="control-label" ng-show="(!useraddForm.fallbackEmail.$dirty && userAdd.error.fallbackEmail) || (useraddForm.fallbackEmail.$dirty && useraddForm.fallbackEmail.$invalid) || (!useraddForm.fallbackEmail.$dirty && userAdd.error.fallbackEmail)">
2022-03-30 09:18:20 -07:00
<small ng-show="useraddForm.fallbackEmail.$error.required">{{ 'users.user.errorEmailRequired' | tr }}</small>
<small ng-show="useraddForm.fallbackEmail.$error.fallbackEmail">{{ 'users.user.errorInvalidEmail' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useraddForm.fallbackEmail.$dirty && userAdd.error.fallbackEmail">{{ userAdd.error.fallbackEmail }}</small>
</div>
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-class="{ 'has-error': (useraddForm.username.$dirty && useraddForm.username.$invalid) || (!useraddForm.username.$dirty && userAdd.error.username) }">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.user.username' | tr }}</label>
2024-01-19 23:35:02 +01:00
<input type="text" class="form-control" ng-model="userAdd.username" name="username" id="inputUserAddUsername" ng-required="config.profileLocked" placeholder="{{ config.profileLocked ? '' : ('users.user.usernamePlaceholder' | tr) }}">
<div class="control-label" ng-show="(!useraddForm.username.$dirty && userAdd.error.username) || (useraddForm.username.$dirty && useraddForm.username.$invalid) || (!useraddForm.username.$dirty && userAdd.error.username)">
<small ng-show="useraddForm.username.$error.username">{{ 'users.user.errorInvalidUsername' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useraddForm.username.$dirty && userAdd.error.username">{{ userAdd.error.username }}</small>
2018-01-22 13:01:38 -08:00
</div>
2020-11-13 16:44:39 +01:00
</div>
<div class="form-group" ng-show="userInfo.isAtLeastAdmin">
<label class="control-label">{{ 'users.user.role' | tr }} <sup><a ng-href="https://docs.cloudron.io/user-management/#roles" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<div class="control-label">
2024-01-19 23:35:02 +01:00
<select class="form-control" ng-model="userAdd.role" ng-options="role.id as role.name disable when role.disabled for role in roles"></select>
2018-01-22 13:01:38 -08:00
</div>
2020-11-13 16:44:39 +01:00
</div>
<div class="form-group">
<label class="control-label">{{ 'users.user.groups' | tr }}</label>
<div class="control-label">
<div ng-show="groups.length === 0">{{ 'users.user.noGroups' | tr }}</div>
2024-03-01 18:37:34 +01:00
<!-- local groups. they can have local and external users . angular cannot filter empty strings - https://github.com/angular/angular.js/issues/7890 -->
<multiselect ng-show="groups.length !== 0" ng-model="userAdd.selectedLocalGroups" options="group.name for group in groups | filter:{ source: '!ldap' }" data-compare-by="name" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
2020-11-13 16:44:39 +01:00
</div>
</div>
<div class="checkbox">
<label>
2024-01-19 23:35:02 +01:00
<input type="checkbox" ng-model="userAdd.sendInvite" id="inputUserAddSendInvite"> {{ 'users.addUserDialog.sendInviteCheckbox' | tr }}
</label>
</div>
2024-01-19 23:35:02 +01:00
<input class="ng-hide" type="submit" ng-disabled="useraddForm.$invalid || userAdd.busy"/>
2020-11-13 16:44:39 +01:00
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
2024-01-19 23:35:02 +01:00
<button type="button" class="btn btn-success" ng-click="userAdd.submit()" ng-disabled="useraddForm.$invalid || userAdd.busy"><i class="fa fa-circle-notch fa-spin" ng-show="userAdd.busy"></i> {{ 'users.addUserDialog.addUserAction' | tr }}</button>
2020-11-13 16:44:39 +01:00
</div>
2018-01-22 13:01:38 -08:00
</div>
2020-11-13 16:44:39 +01:00
</div>
2018-01-22 13:01:38 -08:00
</div>
<!-- Modal remove user -->
<div class="modal fade" id="userRemoveModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2024-01-19 23:35:02 +01:00
<h4 class="modal-title">{{ 'users.deleteUserDialog.title' | tr:{ username: (userRemove.userInfo.username || userRemove.userInfo.email) } }}</h4>
</div>
<div class="modal-body">
2024-01-19 23:35:02 +01:00
<p class="text-bold text-danger" ng-show="userRemove.error">{{ userRemove.error }}</p>
<p ng-hide="userRemove.error">{{ 'users.deleteUserDialog.description' | tr }}</p>
</div>
<div class="modal-footer">
2020-11-13 16:44:39 +01:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
2024-01-19 23:35:02 +01:00
<button type="button" class="btn btn-danger" ng-click="userRemove.submit()" ng-hide="userRemove.error" ng-disabled="userRemove.busy"><i class="fa fa-circle-notch fa-spin" ng-show="userRemove.busy"></i> {{ 'users.deleteUserDialog.deleteAction' | tr }}</button>
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>
2018-01-22 13:01:38 -08:00
</div>
<!-- Modal edit user -->
<div class="modal fade" id="userEditModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2024-01-19 23:35:02 +01:00
<h4 class="modal-title">{{ 'users.editUserDialog.title' | tr:{ username: (userEdit.userInfo.username || userEdit.userInfo.email) } }}</h4>
</div>
<div class="modal-body">
2024-01-19 23:35:02 +01:00
<div ng-show="userEdit.source">
2020-11-13 16:44:39 +01:00
<p class="text-warning">{{ 'users.editUserDialog.externalLdapWarning' | tr }}</p>
2024-01-19 23:35:02 +01:00
<p><label>{{ 'users.user.displayName' | tr }}</label><br/><input type="text" class="form-control" ng-disabled="true" ng-model="userEdit.displayName">
<p><label>{{ 'users.user.email' | tr }}</label><br/><input type="text" class="form-control" ng-disabled="true" ng-model="userEdit.email"></p>
</div>
2024-01-19 23:35:02 +01:00
<form name="useredit_form" role="form" ng-submit="userEdit.submit()" autocomplete="off">
<p class="has-error text-center" ng-show="userEdit.error.generic">{{ userEdit.error.generic }}</p>
2020-10-23 11:47:37 -07:00
<!-- when user profiles are locked, this provides a way for the admin to set the username -->
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-hide="userEdit.source || userEdit.userInfo.username" ng-class="{ 'has-error': (useredit_form.username.$dirty && useredit_form.username.$invalid) || (!useredit_form.username.$dirty && userEdit.error.username) }">
<label class="control-label">{{ 'users.user.username' | tr }}</label>
2024-01-19 23:35:02 +01:00
<div class="control-label" ng-show="(!useredit_form.username.$dirty && userEdit.error.username) || (useredit_form.username.$dirty && useredit_form.username.$invalid) || (!useredit_form.username.$dirty && userEdit.error.username)">
<small ng-show="!useredit_form.username.$dirty && userEdit.error.username">{{ userEdit.error.username }}</small>
</div>
2024-01-19 23:35:02 +01:00
<input type="text" class="form-control" ng-model="userEdit.username" name="username" autocomplete="off">
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-hide="userEdit.source" ng-class="{ 'has-error': (useredit_form.displayName.$dirty && useredit_form.displayName.$invalid) || (!useredit_form.displayName.$dirty && userEdit.error.displayName) }">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.user.displayName' | tr }}</label>
2024-01-19 23:35:02 +01:00
<div class="control-label" ng-show="(!useredit_form.displayName.$dirty && userEdit.error.displayName) || (useredit_form.displayName.$dirty && useredit_form.displayName.$invalid) || (!useredit_form.displayName.$dirty && userEdit.error.displayName)">
2020-11-13 16:44:39 +01:00
<small ng-show="useredit_form.displayName.$error.required">{{ 'users.user.errorDisplayNameRequired' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useredit_form.displayName.$dirty && userEdit.error.displayName">{{ userEdit.error.displayName }}</small>
2018-01-22 13:01:38 -08:00
</div>
2024-01-19 23:35:02 +01:00
<input type="text" class="form-control" ng-model="userEdit.displayName" name="displayName" required autofocus autocomplete="off">
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-hide="userEdit.source" ng-class="{ 'has-error': (useredit_form.email.$dirty && useredit_form.email.$invalid) || (!useredit_form.email.$dirty && userEdit.error.email) }">
2022-05-26 14:42:52 -07:00
<label class="control-label">{{ 'users.user.primaryEmail' | tr }} <sup><a ng-href="https://docs.cloudron.io/profile/#primary-email" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></label>
2024-01-19 23:35:02 +01:00
<div class="control-label" ng-show="(!useredit_form.email.$dirty && userEdit.error.email) || (useredit_form.email.$dirty && useredit_form.email.$invalid) || (!useredit_form.email.$dirty && userEdit.error.email)">
2020-11-13 16:44:39 +01:00
<small ng-show="useredit_form.email.$error.required">{{ 'users.user.errorEmailRequired' | tr }}</small>
<small ng-show="useredit_form.email.$error.email">{{ 'users.user.errorInvalidEmail' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useredit_form.email.$dirty && userEdit.error.email">{{ userEdit.error.email }}</small>
2018-01-22 13:01:38 -08:00
</div>
2024-01-19 23:35:02 +01:00
<input type="email" class="form-control" ng-model="userEdit.email" name="email" required>
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-hide="userEdit.source" ng-class="{ 'has-error': (useredit_form.fallbackEmail.$dirty && useredit_form.fallbackEmail.$invalid) || (!useredit_form.fallbackEmail.$dirty && userEdit.error.fallbackEmail) }">
2022-05-26 14:42:52 -07:00
<label class="control-label">{{ 'users.user.recoveryEmail' | tr }} <sup><a ng-href="https://docs.cloudron.io/profile/#password-recovery-email" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></label>
2024-01-19 23:35:02 +01:00
<div class="control-label" ng-show="(!useredit_form.fallbackEmail.$dirty && userEdit.error.fallbackEmail) || (useredit_form.fallbackEmail.$dirty && useredit_form.fallbackEmail.$invalid) || (!useredit_form.fallbackEmail.$dirty && userEdit.error.fallbackEmail)">
2020-11-13 16:44:39 +01:00
<small ng-show="useredit_form.fallbackEmail.$error.fallbackEmail">{{ 'users.user.errorInvalidEmail' | tr }}</small>
2024-01-19 23:35:02 +01:00
<small ng-show="!useredit_form.fallbackEmail.$dirty && userEdit.error.fallbackEmail">{{ userEdit.error.fallbackEmail }}</small>
2018-01-22 13:01:38 -08:00
</div>
2024-01-19 23:35:02 +01:00
<input type="fallbackEmail" class="form-control" ng-model="userEdit.fallbackEmail" name="fallbackEmail">
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-show="!isMe(userEdit.userInfo) && userInfo.isAtLeastAdmin">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.user.role' | tr }} <sup><a ng-href="https://docs.cloudron.io/user-management/#roles" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<div class="control-label">
2024-01-19 23:35:02 +01:00
<select class="form-control" ng-model="userEdit.role" ng-options="role.id as role.name disable when role.disabled for role in roles"></select>
2020-02-17 14:05:26 +01:00
</div>
</div>
2020-02-22 17:51:30 +01:00
<div class="form-group">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.user.groups' | tr }}</label>
2020-02-22 17:51:30 +01:00
<div class="control-label">
<div ng-switch on="groups.length">
<div ng-switch-when="0">{{ 'users.user.noGroups' | tr }}</div>
<div ng-switch-default>
2024-03-01 18:37:34 +01:00
<!-- local groups. they can have local and external users . angular cannot filter empty strings - https://github.com/angular/angular.js/issues/7890 -->
<multiselect ng-show="hasLocalGroups" ng-model="userEdit.selectedLocalGroups" options="group.name for group in groups | filter:{ source: '!ldap' }" data-compare-by="id" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
</div>
</div>
2020-02-22 17:51:30 +01:00
</div>
</div>
2024-03-01 18:37:34 +01:00
<div class="form-group" ng-show="userEdit.externalGroups.length">
<!-- remote groups. cannot be edited -->
<label class="control-label">{{ 'users.user.ldapGroups' | tr }}</label>
<div><span ng-repeat="group in userEdit.externalGroups">{{ group.name }}</span></div>
</div>
2024-01-19 23:35:02 +01:00
<div class="form-group" ng-hide="isMe(userEdit.userInfo)">
<div class="checkbox">
<label>
2024-01-19 23:35:02 +01:00
<input type="checkbox" ng-model="userEdit.active"> {{ 'users.user.activeCheckbox' | tr }} <sup><a ng-href="https://docs.cloudron.io/user-management/#disable-user" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup>
</label>
</div>
</div>
2024-01-19 23:35:02 +01:00
<input class="hide" type="submit" ng-disabled="useredit_form.$invalid || userEdit.busy"/>
</form>
<hr/>
<div ng-hide="userEdit.source && config.external2FA">
2024-01-19 23:35:02 +01:00
<p ng-hide="userEdit.userInfo.twoFactorAuthenticationEnabled">{{ 'users.passwordResetDialog.no2FASetup' | tr }}</p>
<p ng-show="userEdit.userInfo.twoFactorAuthenticationEnabled">{{ 'users.passwordResetDialog.2FAIsSetup' | tr }}</p>
<button type="button" class="btn btn-danger" ng-click="userEdit.reset2FA()" ng-disabled="!userEdit.userInfo.twoFactorAuthenticationEnabled || userEdit.reset2FABusy"><i class="fa fa-circle-notch fa-spin" ng-show="userEdit.reset2FABusy"></i> {{ 'users.passwordResetDialog.reset2FAAction' | tr }}</button>
</div>
<div ng-show="userEdit.source && config.external2FA"> {{ 'users.user.external2FA' | tr }}</div>
</div>
<div class="modal-footer">
2020-11-13 16:44:39 +01:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
2024-01-19 23:35:02 +01:00
<button type="button" class="btn btn-success" ng-click="userEdit.submit()" ng-disabled="useredit_form.$invalid || userEdit.busy"><i class="fa fa-circle-notch fa-spin" ng-show="userEdit.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>
2018-01-22 13:01:38 -08:00
</div>
<!-- Modal add group -->
<div class="modal fade" id="groupAddModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2020-11-13 16:44:39 +01:00
<h4 class="modal-title">{{ 'users.addGroupDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<form name="groupAddForm" role="form" novalidate ng-submit="groupAdd.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (groupAddForm.name.$dirty && groupAddForm.name.$invalid) || (!groupAddForm.name.$dirty && groupAdd.error.name) }">
2020-11-13 16:44:39 +01:00
<label class="control-label" for="groupAddName">{{ 'users.group.name' | tr }}</label>
<div class="control-label" ng-show="(!groupAddForm.name.$dirty && groupAdd.error.name) || (groupAddForm.name.$dirty && groupAddForm.name.$invalid) || (!groupAddForm.name.$dirty && groupAdd.error.name)">
2020-11-13 16:44:39 +01:00
<small ng-show="groupAddForm.name.$error.required">{{ 'users.group.errorNameRequired' | tr }}</small>
<small ng-show="groupAddForm.name.$error.minlength">{{ 'users.group.errorNameTooShort' | tr }}</small>
<small ng-show="groupAddForm.name.$error.maxlength">{{ 'users.group.errorNameTooLong' | tr }}</small>
<small ng-show="!groupAddForm.name.$dirty && groupAdd.error.name">{{ groupAdd.error.name }}</small>
2018-01-22 13:01:38 -08:00
</div>
<input type="text" class="form-control" ng-model="groupAdd.name" id="groupAddName" name="name" ng-maxlength="200" ng-minlength="1" required autofocus>
</div>
<div class="form-group">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.group.users' | tr }}</label>
<div class="control-label">
<multiselect ng-model="groupAdd.selectedUsers" options="(user.username || user.email) for user in allUsers" data-compare-by="email" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
2018-01-22 13:01:38 -08:00
</div>
</div>
<input class="hide" type="submit" ng-disabled="groupAddForm.$invalid || groupAdd.busy"/>
</form>
</div>
<div class="modal-footer">
2020-11-13 16:44:39 +01:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="groupAdd.submit()" ng-disabled="groupAddForm.$invalid || groupAdd.busy"><i class="fa fa-circle-notch fa-spin" ng-show="groupAdd.busy"></i> {{ 'users.group.addGroupAction' | tr }}</button>
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>
2018-01-22 13:01:38 -08:00
</div>
<!-- Modal edit group -->
<div class="modal fade" id="groupEditModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2020-11-13 16:44:39 +01:00
<h4 class="modal-title">{{ 'users.editGroupDialog.title' | tr:{ name: groupEdit.groupInfo.name } }}</h4>
</div>
<div class="modal-body">
2020-11-13 16:44:39 +01:00
<p class="text-warning" ng-show="groupEdit.source">{{ 'users.editGroupDialog.externalLdapWarning' | tr }}</p>
2020-06-05 08:18:40 +02:00
<form name="groupEdit_form" role="form" ng-submit="groupEdit.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': groupEditForm.groupName.$invalid }">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.group.name' | tr }}</label>
2020-06-05 08:18:40 +02:00
<input type="text" class="form-control" ng-model="groupEdit.name" name="groupName" ng-disabled="groupEdit.busy || groupEdit.source" autofocus>
</div>
<div class="form-group">
2020-11-13 16:44:39 +01:00
<label class="control-label">{{ 'users.group.users' | tr }}</label>
<div class="control-label">
<multiselect ng-hide="groupEdit.source" ng-model="groupEdit.selectedUsers" ng-disabled="groupEdit.busy" options="(user.username || user.email) for user in allUsers" data-compare-by="email" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
2024-03-01 18:37:34 +01:00
<div ng-show="groupEdit.source"><span ng-repeat="user in groupEdit.selectedUsers"> {{ (user.username || user.email) }}</span></div>
</div>
</div>
<div class="form-group">
<label class="control-label">Access to Apps</label>
<div class="control-label">
<multiselect ng-model="groupEdit.selectedApps" options="(app.label || app.fqdn) for app in groupEdit.apps" data-compare-by="fqdn" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
</div>
</div>
<input class="hide" type="submit" ng-disabled="groupEdit_form.$invalid || groupEdit.busy"/>
</form>
</div>
<div class="modal-footer">
2020-11-13 16:44:39 +01:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="groupEdit.submit()" ng-disabled="groupEdit_form.$invalid || groupEdit.busy"><i class="fa fa-circle-notch fa-spin" ng-show="groupEdit.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
2018-01-22 13:01:38 -08:00
<!-- Modal remove group -->
<div class="modal fade" id="groupRemoveModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2020-11-13 16:44:39 +01:00
<h4 class="modal-title">{{ 'users.deleteGroupDialog.title' | tr:{ name: groupRemove.group.name } }}</h4>
</div>
<div class="modal-body">
<div ng-show="groupRemove.memberCount" class="text-danger">
2020-11-13 16:44:39 +01:00
<b>{{ 'users.deleteGroupDialog.description' | tr:{ memberCount: groupRemove.memberCount } }}</b>
<br/>
<br/>
2018-01-22 13:01:38 -08:00
</div>
</div>
<div class="modal-footer">
2020-11-13 16:44:39 +01:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-danger" ng-click="groupRemove.submit()" ng-disabled="groupRemove.busy"><i class="fa fa-circle-notch fa-spin" ng-show="groupRemove.busy"></i> {{ 'users.deleteGroupDialog.deleteAction' | tr }}</button>
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>
2018-01-22 13:01:38 -08:00
</div>
2021-09-16 14:35:17 +02:00
<!-- Modal password reset -->
<div class="modal fade" id="passwordResetModal" tabindex="-1" role="dialog">
2020-11-13 16:44:39 +01:00
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2021-09-16 14:35:17 +02:00
<h4 class="modal-title">{{ 'users.passwordResetDialog.title' | tr:{ username: (passwordReset.user.username || passwordReset.user.email) } }}</h4>
2020-11-13 16:44:39 +01:00
</div>
<div class="modal-body">
2021-10-27 19:41:17 +02:00
<div class="form-group">
<label class="control-label">{{ 'users.passwordResetDialog.descriptionLink' | tr }}</label>
<div class="input-group">
<input type="text" id="passwordResetLinkInput" class="form-control" ng-value="passwordReset.resetLink" readonly/>
<span class="input-group-btn">
2021-10-27 22:41:02 +02:00
<button class="btn btn-primary" id="passwordResetLinkClipboardButton" type="button" data-clipboard-target="#passwordResetLinkInput"><i class="fa fa-clipboard"></i></button>
2021-10-27 19:41:17 +02:00
</span>
</div>
2021-04-15 17:16:15 +02:00
</div>
<br/>
2021-10-27 19:41:17 +02:00
<div class="form-group">
<label class="control-label">{{ 'users.passwordResetDialog.descriptionEmail' | tr }}</label>
<div class="input-group">
<input type="email" ng-change="passwordReset.emailError = null" class="form-control" ng-model="passwordReset.email"/>
2021-10-27 19:41:17 +02:00
<span class="input-group-btn">
<button type="button" class="btn btn-primary" ng-click="passwordReset.sendEmail()" ng-disabled="passwordReset.busy"><i class="fa fa-circle-notch fa-spin" ng-show="passwordReset.busy"></i> {{ 'users.passwordResetDialog.sendAction' | tr }}</button>
</span>
2021-04-15 17:16:15 +02:00
</div>
2018-01-22 13:01:38 -08:00
</div>
<div class="text-success text-small text-bold" ng-show="passwordReset.emailError === ''">{{ 'profile.passwordResetNotification.body' | tr:{ email: passwordReset.email } }}</div>
<div class="text-danger text-small text-bold" ng-show="passwordReset.emailError !== ''">{{ passwordReset.emailError }}</div>
2020-11-13 16:44:39 +01:00
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
2020-11-13 16:44:39 +01:00
</div>
2018-01-22 13:01:38 -08:00
</div>
2020-11-13 16:44:39 +01:00
</div>
2018-01-22 13:01:38 -08:00
</div>
<!-- Modal invitation -->
<div class="modal fade" id="invitationModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'users.invitationDialog.title' | tr:{ username: (invitation.user.username || invitation.user.email) } }}</h4>
</div>
2021-10-27 22:35:58 +02:00
<div class="modal-body">
<div class="form-group">
2021-10-27 19:57:57 +02:00
<label class="control-label">{{ 'users.invitationDialog.descriptionLink' | tr }}</label>
<div class="input-group">
<input type="text" id="invitationLinkInput" class="form-control" ng-value="invitation.inviteLink" readonly/>
<span class="input-group-btn">
2021-10-27 22:41:02 +02:00
<button class="btn btn-primary" id="invitationLinkClipboardButton" type="button" data-clipboard-target="#invitationLinkInput"><i class="fa fa-clipboard"></i></button>
2021-10-27 19:57:57 +02:00
</span>
</div>
</div>
<br/>
<div class="form-group">
<label class="control-label">{{ 'users.invitationDialog.descriptionEmail' | tr }}</label>
<div class="input-group">
2021-10-27 21:59:30 +02:00
<input type="email" class="form-control" ng-model="invitation.email"/>
2021-10-27 19:57:57 +02:00
<span class="input-group-btn">
<button type="button" class="btn btn-primary" ng-click="invitation.sendEmail()" ng-disabled="invitation.busy"><i class="fa fa-circle-notch fa-spin" ng-show="invitation.busy"></i> {{ 'users.invitationDialog.sendAction' | tr }}</button>
</span>
</div>
</div>
</div>
<div class="modal-footer">
2021-10-27 19:57:57 +02:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
</div>
</div>
</div>
</div>
2021-09-17 15:53:11 +02:00
<!-- Modal set ghost -->
<div class="modal fade" id="setGhostModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2021-09-17 16:08:13 +02:00
<h4 class="modal-title">{{ 'users.setGhostDialog.title' | tr: { username: setGhost.user.username} }}</h4>
2021-09-17 15:53:11 +02:00
</div>
<div class="modal-body">
2021-10-27 19:32:09 +02:00
<p>{{ 'users.setGhostDialog.description' | tr }}</p>
2021-09-17 15:53:11 +02:00
<form name="setGhostForm" role="form" novalidate ng-submit="setGhost.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': setGhost.error }">
2021-09-17 16:08:13 +02:00
<label class="control-label" for="setGhostPassword">{{ 'users.setGhostDialog.password' | tr }}</label>
2021-09-17 15:53:11 +02:00
<div class="control-label" ng-show="setGhost.error">
<small ng-show="setGhost.error">{{ setGhost.error }}</small>
</div>
<div class="input-group">
<input type="text" id="setGhostPassword" class="form-control" name="ghostPassword" ng-model="setGhost.password" required ng-readonly="setGhost.success"/>
2021-09-17 15:53:11 +02:00
<span class="input-group-btn">
2024-12-10 12:58:22 +01:00
<button class="btn btn-default" ng-hide="setGhost.success" type="button" uib-tooltip="{{ 'users.setGhostDialog.generatePassword' | tr }}" ng-click="setGhost.generatePassword()"><i class="fa fa-key"></i></button>
2021-11-24 16:35:54 +01:00
<button class="btn btn-default" ng-show="setGhost.success" type="button" id="setGhostClipboardButton" data-clipboard-target="#setGhostPassword"><i class="fa fa-clipboard"></i></button>
2021-09-17 15:53:11 +02:00
</span>
</div>
</div>
<input class="hide" type="submit" ng-disabled="setGhostForm.$invalid || setGhost.busy"/>
</form>
</div>
<div class="modal-footer">
2021-09-17 16:08:13 +02:00
<button type="button" class="btn btn-default" ng-show="setGhost.success" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-default" ng-hide="setGhost.success" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
2021-10-19 20:09:00 -07:00
<button type="button" class="btn btn-success" ng-hide="setGhost.success" ng-click="setGhost.submit()" ng-disabled="setGhostForm.$invalid || setGhost.busy"><i class="fa fa-circle-notch fa-spin" ng-show="setGhost.busy"></i> {{ 'users.setGhostDialog.setPassword' | tr }}</button>
2021-09-17 15:53:11 +02:00
</div>
</div>
</div>
</div>
2018-01-22 13:01:38 -08:00
<div class="content content-large">
<h1 class="section-header">{{ 'main.navbar.users' | tr }}</h1>
2022-02-08 15:05:27 +01:00
<div>
<div class="users-toolbar">
<input type="text" id="userSearchInput" class="form-control" style="max-width: 350px;" ng-model="userSearchString" ng-model-options="{ debounce: 1000 }" ng-change="updateFilter()" placeholder="{{ 'main.searchPlaceholder' | tr }}"/>
<multiselect ng-model="userStateFilter" ms-header="{{ 'apps.stateFilterHeader' | tr }}" ms-selected="{{ userStateFilter }}" options="state.label for state in userStates" data-multiple="false"></multiselect>
<div style="flex-grow: 1;"></div>
2024-01-19 23:35:02 +01:00
<button class="btn btn-primary btn-outline" ng-click="userAdd.show()">
2022-02-08 15:05:27 +01:00
<i class="fa fa-user-plus"></i> {{ 'users.newUserAction' | tr }}
</button>
</div>
<div class="card card-large">
2018-01-22 13:01:38 -08:00
<div class="grid-item-top">
<div class="row ng-hide" ng-show="userRefreshBusy">
<div class="col-lg-12 text-center">
<h2><i class="fa fa-circle-notch fa-spin"></i></h2>
2018-01-22 13:01:38 -08:00
</div>
</div>
<div class="row ng-hide" ng-hide="userRefreshBusy">
<div class="col-lg-12">
<table class="table table-hover" style="margin: 0;">
<thead>
<tr>
<th style="width: 0.5%;"></th>
2020-11-13 16:44:39 +01:00
<th style="width:45%">{{ 'users.users.user' | tr }}</th>
<th style="width:49.5%" class="hidden-xs hidden-sm">{{ 'users.users.groups' | tr }}</th>
2020-11-11 22:50:57 +01:00
<th style="width: 5%" class="text-right">{{ 'main.actions' | tr }}</th>
</tr>
</thead>
<tbody>
<tr ng-show="users.length === 0">
2020-11-13 16:44:39 +01:00
<td colspan="5" class="text-center text-muted">{{ 'users.users.empty' | tr }}</td>
</tr>
2019-08-08 07:39:43 -07:00
<tr ng-repeat="user in users" ng-class="{'text-muted': !user.active}">
<td style="min-width: 33.5px;">
2021-12-02 17:58:43 -08:00
<i class="fas fa-crown arrow" ng-show="user.active && user.role === 'owner'" uib-tooltip="{{ 'users.users.superadminTooltip' | tr }}"></i>
<i class="fa fa-user-tie arrow" ng-show="user.active && user.role === 'admin'" uib-tooltip="{{ 'users.users.adminTooltip' | tr }}"></i>
<i class="fas fa-users-cog arrow" ng-show="user.active && user.role === 'usermanager'" uib-tooltip="{{ 'users.users.usermanagerTooltip' | tr }}"></i>
2021-12-02 17:58:43 -08:00
<i class="fas fa-mail-bulk arrow" ng-show="user.active && user.role === 'mailmanager'" uib-tooltip="{{ 'users.users.mailmanagerTooltip' | tr }}"></i>
2020-11-13 16:44:39 +01:00
<i class="fa fa-ban" ng-show="!user.active" uib-tooltip="{{ 'users.users.inactiveTooltip' | tr }}"></i>
</td>
2024-01-19 23:35:02 +01:00
<td class="hand elide-table-cell" ng-click="canEdit(user) && userEdit.show(user)" ng-show="user.username">
2020-11-13 16:44:39 +01:00
{{ user.displayName }} &nbsp; <span class="text-muted">{{ user.username }}</span> &nbsp; <i ng-show="user.source" class="far fa-address-book" uib-tooltip="{{ 'users.users.externalLdapTooltip' | tr }}"></i>
</td>
2024-01-19 23:35:02 +01:00
<td class="hand elide-table-cell" ng-click="canEdit(user) && userEdit.show(user)" ng-hide="user.username">
<span class="text-muted" uib-tooltip="{{ 'users.users.notActivatedYetTooltip' | tr }}">{{ user.email }}</span>
</td>
2024-01-19 23:35:02 +01:00
<td class="text-left hand elide-table-cell hidden-xs hidden-sm" ng-click="canEdit(user) && userEdit.show(user)">
<span class="group-badge" ng-repeat="groupId in user.groupIds">
{{ groupsById[groupId].name }}
</span>
</td>
2018-01-22 13:01:38 -08:00
<td class="text-right no-wrap" style="vertical-align: bottom">
2022-04-24 22:11:35 +02:00
<button ng-disabled="!canEdit(user)" ng-show="!user.inviteAccepted && !isMe(user) && !user.source" class="btn btn-xs btn-default" ng-click="invitation.show(user)" uib-tooltip="{{ 'users.users.invitationTooltip' | tr }}"><i class="fas fa-paper-plane"></i></button>
<button ng-disabled="!canEdit(user)" ng-show="user.inviteAccepted && !user.source" class="btn btn-xs btn-default" ng-click="passwordReset.show(user)" uib-tooltip="{{ 'users.users.resetPasswordTooltip' | tr }}"><i class="fas fa-key"></i></button>
<button ng-disabled="!canImpersonate(user)" class="btn btn-xs btn-default" ng-click="setGhost.show(user)" uib-tooltip="{{ 'users.users.setGhostTooltip' | tr }}"><i class="fas fa-user-secret"></i></button>
2024-01-19 23:35:02 +01:00
<button ng-disabled="!canEdit(user)" class="btn btn-xs btn-default" ng-click="userEdit.show(user)" uib-tooltip="{{ 'users.users.editUserTooltip' | tr }}"><i class="fa fa-pencil-alt"></i></button>
<button ng-disabled="!canEdit(user) || isMe(user)" class="btn btn-xs btn-danger" ng-click="userRemove.show(user)" uib-tooltip="{{ 'users.users.removeUserTooltip' | tr }}"><i class="far fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
<br/>
2022-03-05 17:33:23 +01:00
<div class="pull-left">
{{ 'users.users.count' | tr:{ count: allUsers.length } }}
</div>
<div class="pull-right">
<button class="btn btn-default btn-outline btn-xs" ng-click="showPrevPage()" ng-class="{ 'btn-primary': currentPage > 1 }" ng-disabled="userRefreshBusy || currentPage <= 1"><i class="fa fa-angle-double-left"></i> {{ 'main.pagination.prev' | tr }}</button>
<span style="margin: 0 5px; line-height: 1.5; font-size: 12px;">{{ currentPage }}</span>
<button class="btn btn-default btn-outline btn-xs" ng-click="showNextPage()" ng-class="{ 'btn-primary': users.length > pageItems }" ng-disabled="userRefreshBusy || users.length < pageItems">{{ 'main.pagination.next' | tr }} <i class="fa fa-angle-double-right"></i></button>
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>
2018-01-22 13:01:38 -08:00
</div>
<h3 class="section-header">
{{ 'users.groups.title' | tr }}
<button class="btn btn-primary btn-outline" ng-click="groupAdd.show()"><i class="fa fa-plus"></i> {{ 'users.groups.newGroupAction' | tr }}</button>
</h3>
2018-01-22 13:01:38 -08:00
<div class="card card-large">
2020-11-13 16:44:39 +01:00
<div class="grid-item-top">
<div class="row ng-hide" ng-show="!ready">
<div class="col-lg-12 text-center">
<h2><i class="fa fa-circle-notch fa-spin"></i></h2>
</div>
</div>
<div class="row animateMeOpacity ng-hide" ng-show="ready">
<div class="col-lg-12">
<table class="table table-hover" style="margin: 0;">
<thead>
<tr>
<th style="width: 45%">{{ 'users.groups.name' | tr }}</th>
<th style="width: 49.5%" class="hidden-xs hidden-sm">{{ 'users.groups.users' | tr }}</th>
<th style="width: 5%" class="text-right">{{ 'main.actions' | tr }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="group in groups">
<td class="hand elide-table-cell" style="text-overflow: ellipsis; white-space: nowrap;" ng-click="groupEdit.show(group)">
{{ group.name }} &nbsp; <i ng-show="group.source" class="far fa-address-book" uib-tooltip="{{ 'users.groups.externalLdapTooltip' | tr }}"></i>
</td>
<td class="hand elide-table-cell" style="text-overflow: ellipsis; white-space: nowrap;" ng-click="groupEdit.show(group)">
{{ groupMembers(group) }}
</td>
<td class="text-right no-wrap" style="vertical-align: bottom">
<button class="btn btn-xs btn-default" ng-click="groupEdit.show(group)" uib-tooltip="Edit Group"><i class="fa fa-pencil-alt"></i></button>
<button class="btn btn-xs btn-danger" ng-click="groupRemove.show(group)" uib-tooltip="Remove Group"><i class="far fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
2018-01-22 13:01:38 -08:00
</div>
2020-11-13 16:44:39 +01:00
</div>
2018-01-22 13:01:38 -08:00
</div>
</div>