diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js index 1614524b9..b613838e4 100644 --- a/guacamole/src/main/webapp/app/client/controllers/clientController.js +++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js @@ -517,9 +517,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams } - // Hide any status dialog - $scope.showStatus(false); - }); }]); diff --git a/guacamole/src/main/webapp/app/home/controllers/homeController.js b/guacamole/src/main/webapp/app/home/controllers/homeController.js index a8d3caccb..c96f3a053 100644 --- a/guacamole/src/main/webapp/app/home/controllers/homeController.js +++ b/guacamole/src/main/webapp/app/home/controllers/homeController.js @@ -28,20 +28,61 @@ angular.module('home').controller('homeController', ['$scope', '$injector', // Get required types var ConnectionGroup = $injector.get("ConnectionGroup"); + var PermissionSet = $injector.get("PermissionSet"); // Get required services - var connectionGroupService = $injector.get("connectionGroupService"); + var authenticationService = $injector.get("authenticationService"); + var connectionGroupService = $injector.get("connectionGroupService"); + var permissionService = $injector.get("permissionService"); - // Set status to loading until we have all the connections and groups loaded - $scope.loading = true; + /** + * The root connection group, or null if the connection group hierarchy has + * not yet been loaded. + * + * @type ConnectionGroup + */ + $scope.rootConnectionGroup = null; + + /** + * Whether the current user has sufficient permissions to use the + * management interface. If permissions have not yet been loaded, this will + * be null. + * + * @type Boolean + */ + $scope.canManageGuacamole = null; + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.rootConnectionGroup !== null + && $scope.canManageGuacamole !== null; + + }; // Retrieve root group and all descendants connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER) .success(function rootGroupRetrieved(rootConnectionGroup) { - $scope.rootConnectionGroup = rootConnectionGroup; - $scope.loading = false; + }); + // Retrieve current permissions + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsRetrieved(permissions) { + + // Determine whether the current user can access the management UI + $scope.canManageGuacamole = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE); + }); }]); diff --git a/guacamole/src/main/webapp/app/home/templates/home.html b/guacamole/src/main/webapp/app/home/templates/home.html index 9e8bf7eb8..887b93cad 100644 --- a/guacamole/src/main/webapp/app/home/templates/home.html +++ b/guacamole/src/main/webapp/app/home/templates/home.html @@ -20,26 +20,30 @@ THE SOFTWARE. --> -
+
- +
- -

{{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

-
- -
+ + + +

{{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

+
+ +
+ + +

{{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}

+
+ +
- -

{{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}

-
-
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/index/controllers/indexController.js b/guacamole/src/main/webapp/app/index/controllers/indexController.js index 6ef71eac5..3e7f1e733 100644 --- a/guacamole/src/main/webapp/app/index/controllers/indexController.js +++ b/guacamole/src/main/webapp/app/index/controllers/indexController.js @@ -26,16 +26,11 @@ angular.module('index').controller('indexController', ['$scope', '$injector', function indexController($scope, $injector) { - // Required types - var PermissionSet = $injector.get("PermissionSet"); - // Required services - var $document = $injector.get("$document"); - var $location = $injector.get("$location"); - var $q = $injector.get("$q"); - var $window = $injector.get("$window"); - var authenticationService = $injector.get("authenticationService"); - var permissionService = $injector.get("permissionService"); + var $document = $injector.get("$document"); + var $location = $injector.get("$location"); + var $window = $injector.get("$window"); + var authenticationService = $injector.get("authenticationService"); /** * The current status notification, or false if no status is currently @@ -52,27 +47,35 @@ angular.module('index').controller('indexController', ['$scope', '$injector', */ $scope.notifications = []; - // Put some useful variables in the top level scope + /** + * Basic page-level information. + */ $scope.page = { + + /** + * The title of the page. + * + * @type String + */ title: '', + + /** + * The name of the CSS class to apply to the page body, if any. + * + * @type String + */ bodyClassName: '' + }; - $scope.currentUserID = null; - $scope.currentUserIsAdmin = false; - $scope.currentUserHasUpdate = false; - $scope.currentUserPermissions = null; + + /** + * The ID of the most recently shown notification, or 0 if no notifications + * have yet been shown. + * + * @type Number + */ var notificationUniqueID = 0; - // A promise to be fulfilled when all basic user permissions are loaded. - var permissionsLoaded= $q.defer(); - $scope.basicPermissionsLoaded = permissionsLoaded.promise; - - $scope.currentUserID = authenticationService.getCurrentUserID(); - - // If the user is unknown, force a login - if(!$scope.currentUserID) - $location.path('/login'); - /** * Shows or hides the given notification as a modal status. If a status * notification is currently shown, no further statuses will be shown @@ -150,25 +153,6 @@ angular.module('index').controller('indexController', ['$scope', '$injector', } }; - // Allow the permissions to be reloaded elsewhere if needed - $scope.loadBasicPermissions = function loadBasicPermissions() { - - permissionService.getPermissions($scope.currentUserID).success(function fetchCurrentUserPermissions(permissions) { - $scope.currentUserPermissions = permissions; - - // Whether the user has system-wide admin permission - $scope.currentUserIsAdmin = PermissionSet.hasSystemPermission($scope.currentUserPermissions, PermissionSet.SystemPermissionType.ADMINISTER); - - // Whether the user can update at least one object - $scope.currentUserHasUpdate = $scope.currentUserIsAdmin - || PermissionSet.hasConnectionPermission($scope.currentUserPermissions, "UPDATE") - || PermissionSet.hasConnectionGroupPermission($scope.currentUserPermissions, "UPDATE") - || PermissionSet.hasUserPermission($scope.currentUserPermissions, "UPDATE"); - - permissionsLoaded.resolve(); - }); - }; - // Provide simple mechanism for logging out the current user $scope.logout = function logout() { authenticationService.logout()['finally'](function logoutComplete() { @@ -176,9 +160,6 @@ angular.module('index').controller('indexController', ['$scope', '$injector', }); }; - // Try to load them now - $scope.loadBasicPermissions(); - // Create event listeners at the global level var keyboard = new Guacamole.Keyboard($document[0]); @@ -217,12 +198,17 @@ angular.module('index').controller('indexController', ['$scope', '$injector', // Update title and CSS class upon navigation $scope.$on('$routeChangeSuccess', function(event, current, previous) { + // Set title var title = current.$$route.title; if (title) $scope.page.title = title; + // Set body CSS class $scope.page.bodyClassName = current.$$route.bodyClassName || ''; + // Hide any status dialog + $scope.showStatus(false); + }); }]); diff --git a/guacamole/src/main/webapp/app/index/styles/loading.css b/guacamole/src/main/webapp/app/index/styles/loading.css index 159cace25..da1d8df9f 100644 --- a/guacamole/src/main/webapp/app/index/styles/loading.css +++ b/guacamole/src/main/webapp/app/index/styles/loading.css @@ -25,6 +25,14 @@ min-height: 200px; } +.view.loading { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + .loading * { visibility: hidden; } diff --git a/guacamole/src/main/webapp/app/login/controllers/loginController.js b/guacamole/src/main/webapp/app/login/controllers/loginController.js index 7283f4c28..9f8078733 100644 --- a/guacamole/src/main/webapp/app/login/controllers/loginController.js +++ b/guacamole/src/main/webapp/app/login/controllers/loginController.js @@ -52,8 +52,6 @@ angular.module('login').controller('loginController', ['$scope', '$injector', // Redirect to main view upon success .success(function success(data, status, headers, config) { - // Set up the basic permissions for the user - $scope.loadBasicPermissions(); $location.path('/'); }) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 16058192b..0f57ff032 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -60,6 +60,59 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i */ var identifier = $routeParams.id; + /** + * All known protocols. + * + * @type Object. + */ + $scope.protocols = null; + + /** + * The root connection group of the connection group hierarchy. + * + * @type ConnectionGroup + */ + $scope.rootGroup = null; + + /** + * The connection being modified. + * + * @type Connection + */ + $scope.connection = null; + + /** + * The parameter name/value pairs associated with the connection being + * modified. + * + * @type Object. + */ + $scope.parameters = null; + + /** + * The usage history of the connection being modified. + * + * @type HistoryEntryWrapper[] + */ + $scope.historyEntryWrappers = null; + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.protocols !== null + && $scope.rootGroup !== null + && $scope.connection !== null + && $scope.parameters !== null + && $scope.historyEntryWrappers !== null; + + }; + // Pull connection group hierarchy connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE) .success(function connectionGroupReceived(rootGroup) { diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js index 38010b37a..31f751ea0 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js @@ -55,6 +55,35 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' */ var identifier = $routeParams.id; + /** + * The root connection group of the connection group hierarchy. + * + * @type ConnectionGroup + */ + $scope.rootGroup = null; + + /** + * The connection group being modified. + * + * @type ConnectionGroup + */ + $scope.connectionGroup = null; + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.rootGroup !== null + && $scope.connectionGroup !== null; + + }; + + // Pull connection group hierarchy connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE) .success(function connectionGroupReceived(rootGroup) { diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageController.js b/guacamole/src/main/webapp/app/manage/controllers/manageController.js index b4ae57306..84733fe55 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageController.js @@ -32,7 +32,9 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', var User = $injector.get('User'); // Required services + var authenticationService = $injector.get('authenticationService'); var connectionGroupService = $injector.get('connectionGroupService'); + var permissionService = $injector.get('permissionService'); var userService = $injector.get('userService'); /** @@ -47,6 +49,46 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', } }; + /** + * Whether the current user can manage users. If the current permissions + * have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canManageUsers = null; + + /** + * Whether the current user can manage connections. If the current + * permissions have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canManageConnections = null; + + /** + * Whether the current user can create new users. If the current + * permissions have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canCreateUsers = null; + + /** + * Whether the current user can create new connections. If the current + * permissions have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canCreateConnections = null; + + /** + * Whether the current user can create new connection groups. If the + * current permissions have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canCreateConnectionGroups = null; + /** * The name of the new user to create, if any, when user creation is * requested via newUser(). @@ -54,6 +96,58 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', * @type String */ $scope.newUsername = ""; + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.users !== null + && $scope.rootGroup !== null + && $scope.canManageUsers !== null + && $scope.canManageConnections !== null + && $scope.canCreateUsers !== null + && $scope.canCreateConnections !== null + && $scope.canCreateConnectionGroups !== null; + + }; + + // Retrieve current permissions + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsRetrieved(permissions) { + + // Determine whether the current user can create new users + $scope.canCreateUsers = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER); + + // Determine whether the current user can create new users + $scope.canCreateConnections = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION); + + // Determine whether the current user can create new users + $scope.canCreateConnectionGroups = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP); + + // Determine whether the current user can manage other users + $scope.canManageUsers = + $scope.canCreateUsers + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE); + + // Determine whether the current user can manage other connections + $scope.canManageConnections = + $scope.canCreateConnections + || $scope.canCreateConnectionGroups + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE); + + }); // Retrieve all connections for which we have UPDATE permission connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index bf860a78e..e7642149f 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -57,6 +57,42 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto */ var username = $routeParams.id; + /** + * The user being modified. + * + * @type User + */ + $scope.user = null; + + /** + * All permissions associated with the user being modified. + * + * @type PermissionFlagSet + */ + $scope.permissionFlags = null; + + /** + * The root connection group of the connection group hierarchy. + * + * @type ConnectionGroup + */ + $scope.rootGroup = null; + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.user !== null + && $scope.permissionFlags !== null + && $scope.rootGroup !== null; + + }; + // Pull user data userService.getUser(username).success(function userReceived(user) { $scope.user = user; diff --git a/guacamole/src/main/webapp/app/manage/templates/manage.html b/guacamole/src/main/webapp/app/manage/templates/manage.html index 433516a9e..d079c3fe4 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manage.html +++ b/guacamole/src/main/webapp/app/manage/templates/manage.html @@ -20,53 +20,67 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - +
-

{{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

-
+ -

{{'MANAGE.SECTION_HEADER_USERS' | translate}}

-
+

{{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

-

{{'MANAGE.HELP_USERS' | translate}}

+ +
+

{{'MANAGE.SECTION_HEADER_USERS' | translate}}

+
- -
- - -
+

{{'MANAGE.HELP_USERS' | translate}}

- -
-
-
-
- {{user.username}} + +
+ + +
+ + +
+
-

{{'MANAGE.SECTION_HEADER_CONNECTIONS' | translate}}

-
+ +
+

{{'MANAGE.SECTION_HEADER_CONNECTIONS' | translate}}

+
-

{{'MANAGE.HELP_CONNECTIONS' | translate}}

+

{{'MANAGE.HELP_CONNECTIONS' | translate}}

- - + +
diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html index 241dc1e97..4ffe4fe08 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html @@ -20,83 +20,87 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - +
- -

{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

-
- - - - - - - - - - - - - - - - - - - - - - -
{{'MANAGE_CONNECTION.FIELD_HEADER_NAME' | translate}}
{{'MANAGE_CONNECTION.FIELD_HEADER_LOCATION' | translate}} - -
{{'MANAGE_CONNECTION.FIELD_HEADER_PROTOCOL' | translate}} - -
-
+ - -

{{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

-
- - - - - - - -
{{getProtocolParameterName(connection.protocol, parameter.name) | translate}} - -
-
- - -
- - - -
- - -

{{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}

-
-

{{'MANAGE_CONNECTION.INFO_CONNECTION_NOT_USED' | translate}}

- - + +

{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

+
+
+ + - - - + + + - - - - - - + + + + + + - -
{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_USERNAME' | translate}}{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_START' | translate}}{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_DURATION' | translate}}{{'MANAGE_CONNECTION.FIELD_HEADER_NAME' | translate}}
{{wrapper.entry.username}}{{wrapper.entry.startDate | date:'short'}}{{wrapper.durationText | translate:"{VALUE: wrapper.duration.value, UNIT: wrapper.duration.unit}"}}
{{'MANAGE_CONNECTION.FIELD_HEADER_LOCATION' | translate}} + +
+ + + + + {{'MANAGE_CONNECTION.FIELD_HEADER_PROTOCOL' | translate}} + + + + + +
+ + +

{{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

+
+ + + + + + + +
{{getProtocolParameterName(connection.protocol, parameter.name) | translate}} + +
+
+ + +
+ + + +
+ + +

{{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}

+
+

{{'MANAGE_CONNECTION.INFO_CONNECTION_NOT_USED' | translate}}

+ + + + + + + + + + + + + + + +
{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_USERNAME' | translate}}{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_START' | translate}}{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_DURATION' | translate}}
{{wrapper.entry.username}}{{wrapper.entry.startDate | date:'short'}}{{wrapper.durationText | translate:"{VALUE: wrapper.duration.value, UNIT: wrapper.duration.unit}"}}
+
+
diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html index f853162db..169717dfb 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html @@ -20,46 +20,50 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - +
- -

{{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

-
- - - - - - - - - - - - - - - - - - - - - - -
{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_NAME' | translate}}
{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_LOCATION' | translate}} - -
{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_TYPE' | translate}} - -
-
+ - -
- - - -
\ No newline at end of file + +

{{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

+
+ + + + + + + + + + + + + + + + + + + + + + +
{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_NAME' | translate}}
{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_LOCATION' | translate}} + +
{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_TYPE' | translate}} + +
+
+ + +
+ + + +
+ +
diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUser.html b/guacamole/src/main/webapp/app/manage/templates/manageUser.html index a460077b8..b3dd7f5b7 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageUser.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUser.html @@ -20,58 +20,62 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - +
- -

{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

-
- - - - - - - - - - - - - - - - -
{{'MANAGE_USER.FIELD_HEADER_USERNAME' | translate}}{{user.username}}
{{'MANAGE_USER.FIELD_HEADER_PASSWORD' | translate}}
{{'MANAGE_USER.FIELD_HEADER_PASSWORD_AGAIN' | translate}}
-
+ - -

{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}

-
- - - - - -
{{systemPermissionType.label | translate}}
-
- - -

{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}

-
- -
+ +

{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

+
+ + + + + + + + + + + + + + + + +
{{'MANAGE_USER.FIELD_HEADER_USERNAME' | translate}}{{user.username}}
{{'MANAGE_USER.FIELD_HEADER_PASSWORD' | translate}}
{{'MANAGE_USER.FIELD_HEADER_PASSWORD_AGAIN' | translate}}
+
+ + +

{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}

+
+ + + + + +
{{systemPermissionType.label | translate}}
+
+ + +

{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}

+
+ +
+ + +
+ + + +
- -
- - -