From 9a8aa1467441e068b488ea544acca65321dc65d3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 11 Mar 2015 17:58:31 -0700 Subject: [PATCH 01/13] GUAC-1120: Move logout panel to common user menu. --- .../app/home/controllers/homeController.js | 174 +----------- .../src/main/webapp/app/home/homeModule.js | 2 +- .../src/main/webapp/app/home/styles/home.css | 39 --- .../main/webapp/app/home/templates/home.html | 33 +-- .../app/index/controllers/indexController.js | 13 +- .../src/main/webapp/app/index/styles/ui.css | 6 - .../controllers/manageConnectionController.js | 23 +- .../manageConnectionGroupController.js | 11 + .../manage/controllers/manageController.js | 11 + .../controllers/manageUserController.js | 13 +- .../main/webapp/app/manage/manageModule.js | 2 +- .../webapp/app/manage/templates/manage.html | 6 +- .../manage/templates/manageConnection.html | 6 +- .../templates/manageConnectionGroup.html | 6 +- .../app/manage/templates/manageUser.html | 6 +- .../app/userMenu/directives/guacUserMenu.js | 250 ++++++++++++++++++ .../webapp/app/userMenu/styles/user-menu.css | 60 +++++ .../app/userMenu/templates/guacUserMenu.html | 55 ++++ .../webapp/app/userMenu/userMenuModule.js | 27 ++ .../src/main/webapp/translations/en_US.json | 47 ++-- 20 files changed, 487 insertions(+), 303 deletions(-) create mode 100644 guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js create mode 100644 guacamole/src/main/webapp/app/userMenu/styles/user-menu.css create mode 100644 guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html create mode 100644 guacamole/src/main/webapp/app/userMenu/userMenuModule.js diff --git a/guacamole/src/main/webapp/app/home/controllers/homeController.js b/guacamole/src/main/webapp/app/home/controllers/homeController.js index 601b18ae5..a50763302 100644 --- a/guacamole/src/main/webapp/app/home/controllers/homeController.js +++ b/guacamole/src/main/webapp/app/home/controllers/homeController.js @@ -28,13 +28,11 @@ angular.module('home').controller('homeController', ['$scope', '$injector', // Get required types var ConnectionGroup = $injector.get("ConnectionGroup"); - var PermissionSet = $injector.get("PermissionSet"); // Get required services var authenticationService = $injector.get("authenticationService"); var connectionGroupService = $injector.get("connectionGroupService"); var permissionService = $injector.get("permissionService"); - var userService = $injector.get("userService"); /** * The root connection group, or null if the connection group hierarchy has @@ -45,44 +43,12 @@ angular.module('home').controller('homeController', ['$scope', '$injector', $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. + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. * - * @type Boolean + * @type PermissionSet */ - $scope.canManageGuacamole = null; - - /** - * Whether the current user has sufficient permissions to change - * his/her own password. If permissions have not yet been loaded, this will - * be null. - * - * @type Boolean - */ - $scope.canChangePassword = null; - - /** - * Whether the password edit dialog should be shown. - * - * @type Boolean - */ - $scope.showPasswordDialog = false; - - /** - * The new password for the user. - * - * @type String - */ - $scope.newPassword = null; - - /** - * The password match for the user. The update password action will fail if - * $scope.newPassword !== $scope.passwordMatch. - * - * @type String - */ - $scope.newPasswordMatch = null; + $scope.permissions = null; /** * Returns whether critical data has completed being loaded. @@ -94,8 +60,7 @@ angular.module('home').controller('homeController', ['$scope', '$injector', $scope.isLoaded = function isLoaded() { return $scope.rootConnectionGroup !== null - && $scope.canManageGuacamole !== null - && $scope.canChangePassword !== null; + && $scope.permissions !== null; }; @@ -105,135 +70,10 @@ angular.module('home').controller('homeController', ['$scope', '$injector', $scope.rootConnectionGroup = rootConnectionGroup; }); - // Identifier of the current user - var currentUserID = authenticationService.getCurrentUserID(); - // Retrieve current permissions - permissionService.getPermissions(currentUserID) + permissionService.getPermissions(authenticationService.getCurrentUserID()) .success(function permissionsRetrieved(permissions) { - - // Determine whether the current user can change his/her own password - $scope.canChangePassword = - PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, currentUserID) - && PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.READ, currentUserID); - - // Ignore permission to update root group - PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER); - - // Ignore permission to update self - PermissionSet.removeUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, currentUserID); - - // Determine whether the current user needs access to the management UI - $scope.canManageGuacamole = - - // System permissions - PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) - || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER) - || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION) - || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP) - - // Permission to update objects - || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - - // Permission to delete objects - || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) - - // Permission to administer objects - || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER); - + $scope.permissions = permissions; }); - /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. - */ - var ACKNOWLEDGE_ACTION = { - name : "HOME.ACTION_ACKNOWLEDGE", - // Handle action - callback : function acknowledgeCallback() { - $scope.showStatus(false); - } - }; - - /** - * Show the password update dialog. - */ - $scope.showPasswordUpdate = function showPasswordUpdate() { - - // Show the dialog - $scope.showPasswordDialog = true; - }; - - /** - * Close the password update dialog. - */ - $scope.closePasswordUpdate = function closePasswordUpdate() { - - // Clear the password fields and close the dialog - $scope.oldPassword = null; - $scope.newPassword = null; - $scope.newPasswordMatch = null; - $scope.showPasswordDialog = false; - }; - - /** - * Update the current user's password to the password currently set within - * the password change dialog. - */ - $scope.updatePassword = function updatePassword() { - - // Verify passwords match - if ($scope.newPasswordMatch !== $scope.newPassword) { - $scope.showStatus({ - className : 'error', - title : 'HOME.DIALOG_HEADER_ERROR', - text : 'HOME.ERROR_PASSWORD_MISMATCH', - actions : [ ACKNOWLEDGE_ACTION ] - }); - return; - } - - // Verify that the new password is not blank - if (!$scope.newPassword) { - $scope.showStatus({ - className : 'error', - title : 'HOME.DIALOG_HEADER_ERROR', - text : 'HOME.ERROR_PASSWORD_BLANK', - actions : [ ACKNOWLEDGE_ACTION ] - }); - return; - } - - // Save the user with the new password - userService.updateUserPassword(currentUserID, $scope.oldPassword, $scope.newPassword) - .success(function passwordUpdated() { - - // Close the password update dialog - $scope.closePasswordUpdate(); - - // Indicate that the password has been changed - $scope.showStatus({ - text : 'HOME.PASSWORD_CHANGED', - actions : [ ACKNOWLEDGE_ACTION ] - }); - }) - - // Notify of any errors - .error(function passwordUpdateFailed(error) { - $scope.showStatus({ - className : 'error', - title : 'HOME.DIALOG_HEADER_ERROR', - 'text' : error.message, - actions : [ ACKNOWLEDGE_ACTION ] - }); - }); - - }; - }]); diff --git a/guacamole/src/main/webapp/app/home/homeModule.js b/guacamole/src/main/webapp/app/home/homeModule.js index 146ad4ced..97dbbcc97 100644 --- a/guacamole/src/main/webapp/app/home/homeModule.js +++ b/guacamole/src/main/webapp/app/home/homeModule.js @@ -20,4 +20,4 @@ * THE SOFTWARE. */ -angular.module('home', ['client', 'history', 'groupList', 'rest']); +angular.module('home', ['client', 'history', 'groupList', 'rest', 'userMenu']); diff --git a/guacamole/src/main/webapp/app/home/styles/home.css b/guacamole/src/main/webapp/app/home/styles/home.css index 9754d74af..4aca95213 100644 --- a/guacamole/src/main/webapp/app/home/styles/home.css +++ b/guacamole/src/main/webapp/app/home/styles/home.css @@ -20,12 +20,6 @@ * THE SOFTWARE. */ -div.logout-panel { - padding: 0.45em; - text-align: right; - float: right; -} - .history-unavailable div.recent-connections { display: none; } @@ -69,36 +63,3 @@ div.recent-connections div.connection { max-width: 75%; overflow: hidden; } - -.password-dialog { - visibility: hidden; - opacity: 0; - -webkit-transition: visibility 0.125s, opacity 0.125s; - -moz-transition: visibility 0.125s, opacity 0.125s; - -ms-transition: visibility 0.125s, opacity 0.125s; - -o-transition: visibility 0.125s, opacity 0.125s; - transition: visibility 0.125s, opacity 0.125s; - position: absolute; - background: white; - padding: 1em; - border: 1px solid rgba(0, 0, 0, 0.25); - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); - margin: 1em; - right: 0; - top: 0; - z-index: 1; -} - -.password-dialog.shown { - visibility: visible; - opacity: 1; - -webkit-transition: opacity 0.125s; - -moz-transition: opacity 0.125s; - -ms-transition: opacity 0.125s; - -o-transition: opacity 0.125s; - transition: opacity 0.125s; -} - -.password-dialog .fields { - width: 100%; -} \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/home/templates/home.html b/guacamole/src/main/webapp/app/home/templates/home.html index 2fb88ed51..6dc4f6a74 100644 --- a/guacamole/src/main/webapp/app/home/templates/home.html +++ b/guacamole/src/main/webapp/app/home/templates/home.html @@ -24,37 +24,8 @@
-
- {{'HOME.ACTION_CHANGE_PASSWORD' | translate}} -
- -
- - - - - - - - - - - - - -
{{'HOME.FIELD_HEADER_PASSWORD_OLD' | translate}}
{{'HOME.FIELD_HEADER_PASSWORD_NEW' | translate}}
{{'HOME.FIELD_HEADER_PASSWORD_NEW_AGAIN' | translate}}
-
- - -
- - -
-
- - {{'HOME.ACTION_MANAGE' | translate}} - {{'HOME.ACTION_LOGOUT' | translate}} -
+ +

{{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

diff --git a/guacamole/src/main/webapp/app/index/controllers/indexController.js b/guacamole/src/main/webapp/app/index/controllers/indexController.js index 3e7f1e733..59e657423 100644 --- a/guacamole/src/main/webapp/app/index/controllers/indexController.js +++ b/guacamole/src/main/webapp/app/index/controllers/indexController.js @@ -27,10 +27,8 @@ angular.module('index').controller('indexController', ['$scope', '$injector', function indexController($scope, $injector) { // Required services - var $document = $injector.get("$document"); - var $location = $injector.get("$location"); - var $window = $injector.get("$window"); - var authenticationService = $injector.get("authenticationService"); + var $document = $injector.get("$document"); + var $window = $injector.get("$window"); /** * The current status notification, or false if no status is currently @@ -153,13 +151,6 @@ angular.module('index').controller('indexController', ['$scope', '$injector', } }; - // Provide simple mechanism for logging out the current user - $scope.logout = function logout() { - authenticationService.logout()['finally'](function logoutComplete() { - $location.path('/login'); - }); - }; - // Create event listeners at the global level var keyboard = new Guacamole.Keyboard($document[0]); diff --git a/guacamole/src/main/webapp/app/index/styles/ui.css b/guacamole/src/main/webapp/app/index/styles/ui.css index f17797341..1be5a3a96 100644 --- a/guacamole/src/main/webapp/app/index/styles/ui.css +++ b/guacamole/src/main/webapp/app/index/styles/ui.css @@ -289,12 +289,6 @@ div.section { background-image: url('images/group-icons/guac-open.png'); } -div.logout-panel { - padding: 0.45em; - text-align: right; - float: right; -} - .history th, .history td { padding-left: 1em; diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 13f59dd54..b414aa250 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -128,6 +128,14 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i */ $scope.canCloneConnection = null; + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + /** * Returns whether critical data has completed being loaded. * @@ -142,6 +150,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i && $scope.connection !== null && $scope.parameters !== null && $scope.historyEntryWrappers !== null + && $scope.permissions !== null && $scope.canSaveConnection !== null && $scope.canDeleteConnection !== null && $scope.canCloneConnection !== null; @@ -159,21 +168,23 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i permissionService.getPermissions(authenticationService.getCurrentUserID()) .success(function permissionsReceived(permissions) { - // Check if the connection is new or if the user has UPDATE permission - $scope.canSaveConnection = + $scope.permissions = permissions; + + // Check if the connection is new or if the user has UPDATE permission + $scope.canSaveConnection = !identifier || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier); - // Check if connection is not new and the user has DELETE permission - $scope.canDeleteConnection = + // Check if connection is not new and the user has DELETE permission + $scope.canDeleteConnection = !!identifier && ( PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier) ); - // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions - $scope.canCloneConnection = + // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions + $scope.canCloneConnection = !!identifier && ( PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || ( PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js index 94813b8d5..dfd8c5f40 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js @@ -85,6 +85,14 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' */ $scope.hasDeletePermission = null; + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + /** * Returns whether critical data has completed being loaded. * @@ -96,6 +104,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' return $scope.rootGroup !== null && $scope.connectionGroup !== null + && $scope.permissions !== null && $scope.canSaveConnectionGroup !== null && $scope.canDeleteConnectionGroup !== null; @@ -105,6 +114,8 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' permissionService.getPermissions(authenticationService.getCurrentUserID()) .success(function permissionsReceived(permissions) { + $scope.permissions = permissions; + // Check if the connection group is new or if the user has UPDATE permission $scope.canSaveConnectionGroup = !identifier diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageController.js b/guacamole/src/main/webapp/app/manage/controllers/manageController.js index f757234c5..36ba0983a 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageController.js @@ -115,6 +115,14 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', */ $scope.newUsername = ""; + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + /** * Returns whether critical data has completed being loaded. * @@ -126,6 +134,7 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', return $scope.users !== null && $scope.rootGroup !== null + && $scope.permissions !== null && $scope.canManageUsers !== null && $scope.canManageConnections !== null && $scope.canCreateUsers !== null @@ -138,6 +147,8 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', permissionService.getPermissions(currentUserID) .success(function permissionsRetrieved(permissions) { + $scope.permissions = permissions; + // Ignore permission to update root group PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index 9aa16627a..d7619f798 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -93,6 +93,14 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto */ $scope.hasDeletePermission = null; + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + /** * Returns whether critical data has completed being loaded. * @@ -105,6 +113,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto return $scope.user !== null && $scope.permissionFlags !== null && $scope.rootGroup !== null + && $scope.permissions !== null && $scope.canSaveUser !== null && $scope.canDeleteUser !== null; @@ -129,7 +138,9 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto // Query the user's permissions for the current connection permissionService.getPermissions(authenticationService.getCurrentUserID()) .success(function permissionsReceived(permissions) { - + + $scope.permissions = permissions; + // Check if the user is new or if the user has UPDATE permission $scope.canSaveUser = !username diff --git a/guacamole/src/main/webapp/app/manage/manageModule.js b/guacamole/src/main/webapp/app/manage/manageModule.js index e32aa54d1..d6261ada6 100644 --- a/guacamole/src/main/webapp/app/manage/manageModule.js +++ b/guacamole/src/main/webapp/app/manage/manageModule.js @@ -23,5 +23,5 @@ /** * The module for the administration functionality. */ -angular.module('manage', ['groupList', 'locale', 'pager', 'rest']); +angular.module('manage', ['groupList', 'locale', 'pager', 'rest', 'userMenu']); diff --git a/guacamole/src/main/webapp/app/manage/templates/manage.html b/guacamole/src/main/webapp/app/manage/templates/manage.html index b5834a33a..483da6d4f 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manage.html +++ b/guacamole/src/main/webapp/app/manage/templates/manage.html @@ -22,10 +22,8 @@ THE SOFTWARE.
- + +

{{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html index 78bd187bc..723c29c89 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html @@ -22,10 +22,8 @@ THE SOFTWARE.
- + +

{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html index 81b4b78ee..2e5fe1257 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html @@ -22,10 +22,8 @@ THE SOFTWARE.
- + +

{{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUser.html b/guacamole/src/main/webapp/app/manage/templates/manageUser.html index 7f9112d3e..a066249f0 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageUser.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUser.html @@ -22,10 +22,8 @@ THE SOFTWARE.
- + +

{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

diff --git a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js new file mode 100644 index 000000000..fc2095ec5 --- /dev/null +++ b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A directive which provides a user-oriented menu containing options for + * navigation and configuration. + */ +angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { + + return { + restrict: 'E', + replace: true, + scope: { + + /** + * The permissions associated with the user for whom this menu is + * being displayed. + * + * @type PermissionSet + */ + permissions : '=' + + }, + + templateUrl: 'app/userMenu/templates/guacUserMenu.html', + controller: ['$scope', '$injector', function guacUserMenuController($scope, $injector) { + + // Get required types + var ConnectionGroup = $injector.get("ConnectionGroup"); + var PermissionSet = $injector.get("PermissionSet"); + + // Get required services + var $location = $injector.get("$location"); + var authenticationService = $injector.get("authenticationService"); + var userService = $injector.get("userService"); + + /** + * An action to be provided along with the object sent to + * showStatus which closes the currently-shown status dialog. + */ + var ACKNOWLEDGE_ACTION = { + name : "HOME.ACTION_ACKNOWLEDGE", + // Handle action + callback : function acknowledgeCallback() { + $scope.showStatus(false); + } + }; + + /** + * 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; + + /** + * Whether the current user has sufficient permissions to change + * his/her own password. If permissions have not yet been loaded, + * this will be null. + * + * @type Boolean + */ + $scope.canChangePassword = null; + + /** + * Whether the password edit dialog should be shown. + * + * @type Boolean + */ + $scope.showPasswordDialog = false; + + /** + * The new password for the user. + * + * @type String + */ + $scope.newPassword = null; + + /** + * The password match for the user. The update password action will + * fail if $scope.newPassword !== $scope.passwordMatch. + * + * @type String + */ + $scope.newPasswordMatch = null; + + /** + * The username of the current user. + * + * @type String + */ + $scope.username = authenticationService.getCurrentUserID(); + + // Update available menu options when permissions are changed + $scope.$watch('permissions', function permissionsChanged(permissions) { + + // Permissions are unknown if no permissions are provided + if (!permissions) { + $scope.canChangePassword = null; + $scope.canManageGuacamole = null; + return; + } + + // Determine whether the current user can change his/her own password + $scope.canChangePassword = + PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, $scope.username) + && PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.READ, $scope.username); + + // Ignore permission to update root group + PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER); + + // Ignore permission to update self + PermissionSet.removeUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, $scope.username); + + // Determine whether the current user needs access to the management UI + $scope.canManageGuacamole = + + // System permissions + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP) + + // Permission to update objects + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + + // Permission to delete objects + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) + + // Permission to administer objects + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER) + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER) + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER); + + }); + + /** + * Show the password update dialog. + */ + $scope.showPasswordUpdate = function showPasswordUpdate() { + + // Show the dialog + $scope.showPasswordDialog = true; + }; + + /** + * Close the password update dialog. + */ + $scope.closePasswordUpdate = function closePasswordUpdate() { + + // Clear the password fields and close the dialog + $scope.oldPassword = null; + $scope.newPassword = null; + $scope.newPasswordMatch = null; + $scope.showPasswordDialog = false; + }; + + /** + * Update the current user's password to the password currently set within + * the password change dialog. + */ + $scope.updatePassword = function updatePassword() { + + // Verify passwords match + if ($scope.newPasswordMatch !== $scope.newPassword) { + $scope.showStatus({ + className : 'error', + title : 'HOME.DIALOG_HEADER_ERROR', + text : 'HOME.ERROR_PASSWORD_MISMATCH', + actions : [ ACKNOWLEDGE_ACTION ] + }); + return; + } + + // Verify that the new password is not blank + if (!$scope.newPassword) { + $scope.showStatus({ + className : 'error', + title : 'HOME.DIALOG_HEADER_ERROR', + text : 'HOME.ERROR_PASSWORD_BLANK', + actions : [ ACKNOWLEDGE_ACTION ] + }); + return; + } + + // Save the user with the new password + userService.updateUserPassword($scope.username, $scope.oldPassword, $scope.newPassword) + .success(function passwordUpdated() { + + // Close the password update dialog + $scope.closePasswordUpdate(); + + // Indicate that the password has been changed + $scope.showStatus({ + text : 'HOME.PASSWORD_CHANGED', + actions : [ ACKNOWLEDGE_ACTION ] + }); + }) + + // Notify of any errors + .error(function passwordUpdateFailed(error) { + $scope.showStatus({ + className : 'error', + title : 'HOME.DIALOG_HEADER_ERROR', + 'text' : error.message, + actions : [ ACKNOWLEDGE_ACTION ] + }); + }); + + }; + + /** + * Logs out the current user, redirecting them to back to the login + * screen after logout completes. + */ + $scope.logout = function logout() { + authenticationService.logout()['finally'](function logoutComplete() { + $location.path('/login'); + }); + }; + + }] // end controller + + }; +}]); diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css new file mode 100644 index 000000000..75e79b45a --- /dev/null +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +.user-menu { + padding: 0.45em; + text-align: right; + float: right; +} + +.user-menu .password-dialog { + visibility: hidden; + opacity: 0; + -webkit-transition: visibility 0.125s, opacity 0.125s; + -moz-transition: visibility 0.125s, opacity 0.125s; + -ms-transition: visibility 0.125s, opacity 0.125s; + -o-transition: visibility 0.125s, opacity 0.125s; + transition: visibility 0.125s, opacity 0.125s; + position: absolute; + background: white; + padding: 1em; + border: 1px solid rgba(0, 0, 0, 0.25); + box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); + margin: 1em; + right: 0; + top: 0; + z-index: 1; +} + +.user-menu .password-dialog.shown { + visibility: visible; + opacity: 1; + -webkit-transition: opacity 0.125s; + -moz-transition: opacity 0.125s; + -ms-transition: opacity 0.125s; + -o-transition: opacity 0.125s; + transition: opacity 0.125s; +} + +.user-menu .password-dialog .fields { + width: 100%; +} diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html new file mode 100644 index 000000000..0f9cdbad9 --- /dev/null +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -0,0 +1,55 @@ +
+ + + {{'USER_MENU.ACTION_CHANGE_PASSWORD' | translate}} +
+ +
+ + + + + + + + + + + + + +
{{'USER_MENU.FIELD_HEADER_PASSWORD_OLD' | translate}}
{{'USER_MENU.FIELD_HEADER_PASSWORD_NEW' | translate}}
{{'USER_MENU.FIELD_HEADER_PASSWORD_NEW_AGAIN' | translate}}
+
+ + +
+ + +
+
+ + {{'USER_MENU.ACTION_NAVIGATE_HOME' | translate}} + {{'USER_MENU.ACTION_MANAGE' | translate}} + {{'USER_MENU.ACTION_LOGOUT' | translate}} + +
diff --git a/guacamole/src/main/webapp/app/userMenu/userMenuModule.js b/guacamole/src/main/webapp/app/userMenu/userMenuModule.js new file mode 100644 index 000000000..b08453e51 --- /dev/null +++ b/guacamole/src/main/webapp/app/userMenu/userMenuModule.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Module for displaying a user-oriented menu, containing the current username + * and options for navigation, changing the user's password, logging out, etc. + */ +angular.module('userMenu', []); diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index de751955e..7e31dc77a 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -114,22 +114,6 @@ "HOME" : { - "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", - "ACTION_CHANGE_PASSWORD" : "@:APP.ACTION_CHANGE_PASSWORD", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_MANAGE" : "@:APP.ACTION_MANAGE", - "ACTION_SAVE" : "@:APP.ACTION_SAVE", - - "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - - "ERROR_PASSWORD_BLANK" : "Your password cannot be blank.", - "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", - - "FIELD_HEADER_PASSWORD_OLD" : "Current Password:", - "FIELD_HEADER_PASSWORD_NEW" : "New Password:", - "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:", - "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", "INFO_NO_RECENT_CONNECTIONS" : "No recent connections.", @@ -155,8 +139,6 @@ "MANAGE" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", "ACTION_NEW_USER" : "New User", "ACTION_NEW_CONNECTION" : "New Connection", "ACTION_NEW_CONNECTION_GROUP" : "New Group", @@ -182,8 +164,6 @@ "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection", @@ -215,8 +195,6 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection Group", @@ -240,8 +218,6 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete User", @@ -403,6 +379,29 @@ "NAME" : "VNC" + }, + + "USER_MENU" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CHANGE_PASSWORD" : "@:APP.ACTION_CHANGE_PASSWORD", + "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", + "ACTION_MANAGE" : "@:APP.ACTION_MANAGE", + "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "ERROR_PASSWORD_BLANK" : "Your password cannot be blank.", + "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", + + "FIELD_HEADER_PASSWORD_OLD" : "Current Password:", + "FIELD_HEADER_PASSWORD_NEW" : "New Password:", + "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:", + + "PASSWORD_CHANGED" : "Password changed." + } } From 191046712c36bb10488ade6125d1a48ec24d5903 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 11 Mar 2015 18:30:46 -0700 Subject: [PATCH 02/13] GUAC-1120: Add rudimentary menu styling. --- .../webapp/app/userMenu/styles/user-menu.css | 19 ++++++++++++++++++- .../app/userMenu/templates/guacUserMenu.html | 16 +++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index 75e79b45a..0e6430121 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -22,10 +22,27 @@ .user-menu { padding: 0.45em; - text-align: right; float: right; } +.user-menu a.username { + display: block; + margin-left: 0; + margin-right: 0; + margin-bottom: 0; +} + +.user-menu .options { + margin: 0; + padding: 0; + background: gray; +} + +.user-menu .options li { + padding: 0.25em 0.5em; + list-style-type: none; +} + .user-menu .password-dialog { visibility: hidden; opacity: 0; diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index 0f9cdbad9..08069f539 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -21,7 +21,17 @@ THE SOFTWARE. --> - {{'USER_MENU.ACTION_CHANGE_PASSWORD' | translate}} + {{username}} + + + + +
@@ -47,9 +57,5 @@
- - {{'USER_MENU.ACTION_NAVIGATE_HOME' | translate}} - {{'USER_MENU.ACTION_MANAGE' | translate}} - {{'USER_MENU.ACTION_LOGOUT' | translate}}
From 563a1d1af23772920b34fb0b028e04cf25839c76 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 11 Mar 2015 19:43:55 -0700 Subject: [PATCH 03/13] GUAC-1120: Improve user menu style. Add dark icons. --- .../webapp/app/userMenu/styles/user-menu.css | 74 ++++++++++++++++-- .../app/userMenu/templates/guacUserMenu.html | 2 +- .../images/action-icons/guac-config-dark.png | Bin 0 -> 966 bytes .../images/action-icons/guac-home-dark.png | Bin 0 -> 780 bytes .../images/action-icons/guac-key-dark.png | Bin 0 -> 728 bytes .../images/action-icons/guac-logout-dark.png | Bin 0 -> 1032 bytes 6 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 guacamole/src/main/webapp/images/action-icons/guac-config-dark.png create mode 100644 guacamole/src/main/webapp/images/action-icons/guac-home-dark.png create mode 100644 guacamole/src/main/webapp/images/action-icons/guac-key-dark.png create mode 100644 guacamole/src/main/webapp/images/action-icons/guac-logout-dark.png diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index 0e6430121..895e2a998 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -21,28 +21,90 @@ */ .user-menu { - padding: 0.45em; float: right; + position: relative; } .user-menu a.username { + display: block; - margin-left: 0; - margin-right: 0; - margin-bottom: 0; + + margin: 0; + min-width: 1.75in; + + font-size: 1.25em; + font-weight: bold; + padding: .75em .5em; + border-left: 1px solid rgba(0,0,0,0.125); + background: rgba(0,0,0,0.04); + + padding-left: 2em; + background-repeat: no-repeat; + background-size: 1em; + background-position: 0.5em 0.75em; + background-image: url('images/user-icons/guac-user.png'); + } .user-menu .options { + + position: absolute; + right: 0; margin: 0; padding: 0; - background: gray; + min-width: 100%; + + background: #EEE; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.125); + border-left: 1px solid rgba(0,0,0,0.125); + border-bottom: 1px solid rgba(0,0,0,0.125); + z-index: 0; + } .user-menu .options li { - padding: 0.25em 0.5em; + padding: 0; list-style-type: none; } +.user-menu .options li a { + display: block; + cursor: default; + color: black; + text-decoration: none; + padding: 0.35em 0.5em; +} + +.user-menu .options li a:hover { + background: rgba(0, 0, 0, 0.125); +} + +.user-menu .options li a.home, +.user-menu .options li a.manage, +.user-menu .options li a.change-password, +.user-menu .options li a.logout { + background-repeat: no-repeat; + background-size: 1em; + background-position: 0.5em 0.45em; + padding-left: 2em; +} + +.user-menu .options li a.home { + background-image: url('images/action-icons/guac-home-dark.png'); +} + +.user-menu .options li a.manage { + background-image: url('images/action-icons/guac-config-dark.png'); +} + +.user-menu .options li a.change-password { + background-image: url('images/action-icons/guac-key-dark.png'); +} + +.user-menu .options li a.logout { + background-image: url('images/action-icons/guac-logout-dark.png'); +} + .user-menu .password-dialog { visibility: hidden; opacity: 0; diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index 08069f539..0a2cb0850 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -21,7 +21,7 @@ THE SOFTWARE. --> - {{username}} + {{username}}
    diff --git a/guacamole/src/main/webapp/images/action-icons/guac-config-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-config-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..132a253b5650567df506c7be7fe40f26b17c811b GIT binary patch literal 966 zcmV;%13CPOP)t><;6gdehr51rt7O|IF>(Bi<1m5N(Q@$Rclo|#ufyWln-@t^{ z`nunGz^X&=h|Fp~4uLgKvb`0pb*2i81Ixf*7nulH0UCf#SsS7ob+JDvK8$CMqle&6 z#L+*%o_w`2d0zuIfb$H2oAq35)x`Fgw6!N~Yw=~g5CDwe&DsHufSViwH|veq?j*d= z1VWy{KZKeZgnkM>69{Qmf#<+X3F!Td_?j;fQc4X1KY`&M9sB_t0~b;OCV>gy8Stb> z``0pI{>^;R3XD3)4T()#yGOoxK+S>VY|30y$LMb6$nQ5}?AI+i`{Mg>Y5;&m;2m(~=-^V!Y~Rii zfVR0h2xD~7y^>fKod5vlGSJ+}u(9r8m$x1>DW|2&b1u6E%jJYO~6k4itAg7dArHN3W!eKKMw!jFzDH_t^;b0^jjaB4uG#rno`1;EwxE*p=mwnt2`2w7lAjfekzUF^Vu+o>L!Zo>Y zQL;a;$`*NDejj+{DE?7v{kkk!Vb}8BlWcDvCj4Qdpdtb7I*9HT75x7=8TycuO!dQ*Hq)$07*qoM6N<$f?g}JqyPW_ literal 0 HcmV?d00001 diff --git a/guacamole/src/main/webapp/images/action-icons/guac-home-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-home-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e1e35d4b14e8436e7b60df637e92b21242c0c162 GIT binary patch literal 780 zcmV+n1M~ceP)OvIE0DT9)lnusVhrL0(3NKHv8tZrelHy=X-u%-#PF5ephBj zo})m{!vQz|2as$?D&!J?q{WhM0FNcDN{oP+#V%kY@CLAoCV{<)%q0Qyhk+menGf_E z7y$~I1OO}ouFhh-TK9pTOaTDa0TT_F5A+EbjAL$UcPcFD0B{ZHYI5ida0a+&Rg;u7 z2FyTI}`EY?EcilkeW-;qQyBJ(SNo51cEa<@akmw5v~ z(hgt@=#4Rf?MZwKs2nBWLCoeOjp7{5F9ogxLuoIEi*qo)9k>mw&0Rl0(h1;63e0yz zS`GtCDywcO(MMoSB*hTW54=l}`9R-*p_TvuM}g@Snh*2`I0tkz1pw%*RB)CNjRU<6 z0RYwmuQFsl&}U#*C;;FfFqJ{`fo6cSQ0{~L15hb0E@#+yweA7Sf<8+h@H|K61APDn z1Z)Pn>!b@0Q<~@v1Mfqw|7xfwC@HOH=v&R~Wjo8^N#K0Q^$QX89EK~|-E2_;cDD6o zC}bbKtuyXtMEi9^$TP-5#%)QYDVo`8&;d9A2avgF^%K=4=}073fZ9{VjF1 z^PUL#JHP=r00-co!5HH;<+H@F*hD<1%!vSaW@abrd~%P)3Bcb04!{9800-az9DoCG z0C|l@J3=cXdfv2|P1Si;C(Zx0zE*rSvze9w_!e*g4!{9qHU0wF)NZDyt`Q>u0000< KMNUMnLSTZZLr4Vx literal 0 HcmV?d00001 diff --git a/guacamole/src/main/webapp/images/action-icons/guac-key-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-key-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..90ef0ffa717141c58d959d37cab82e58fe92a813 GIT binary patch literal 728 zcmV;}0w?{6P)xb-k(s9{p!Fwt;(f7D7V>>h#wcz&VlaK`>>52|xmn03-kjKmt%-0CN$1o~<%~i3k9$RXG=! z3JV*jg0}C}U!%Zv+GGo=i>(D*2X0n27buV`Yy@5x06!^dtad?A(kw9T{Vg!>7J!-U z0LEPcFf#+LR!w`vbmj!G+p`i}>0Js>l&#`=3V@^q;DYyL=oawIEdWWsf%~3yzzA^0 z^CNI6Bug%Z&^F+R=OB2R3a&uKbZExA5Sjtrcosqm$&hOybOZS8SqOb6Ti0_t10dTH z=T-^sBHQQZb_Set8wAa4GjQ1RTi`gwK8gVRh#8rqlrZ>lvhRm^TVbbwfoOik>gb>I z1V@3pz_&#Me+V3k?T;j&HJGTVDJAgk?L2R#&I0q&G0000< KMNUMnLSTX;f-8{# literal 0 HcmV?d00001 diff --git a/guacamole/src/main/webapp/images/action-icons/guac-logout-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-logout-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..bfb9e111922fc993ff0dda1770915b649d8ef95e GIT binary patch literal 1032 zcmV+j1o!)iP)cc zK~#9!?VDd{Phl9xKhO5F;es$BBjm!8jk23He~J}b{uIiUReip7%ZP_dM_Oe4poi zexj<_EC~P+DFs#mlA(EffGeu{Aq*f?0~)~92NbC)L0(zFt^l=fThL!W3z!_>{4>J< zfYtIJ0FSnBwga#jcw#dE?|@~%Y2&V}v;n_?13(4v#bzUT3n&6wZ1w^l4Na~B{@CgU z`+-VeHPCJ|54dN%#BH02&}U#RaL5=-E194ICxI0}o6S_X6<7k?usIg!1U3SDOpI@B zFjNcF*d7fw+73X*BS{g-7LhzF^8q3<7Pt;9wGIFgnFKTg8&Vl35z)ox8+)@b)v*#0 zTzr9{;M{X>``kVmS?=*%Gq8Zs)d*Z2>HHozf`2s$bKK+qs|_ghXj_ewF!lHAs2&%Q z_7U9RW1L*&Nb>+!C;?su=>>)o-@U*v-eD5( zz>Kpdl`64FimG-2Wu$l}ks!jNsy{_!lhFqI05Bl*lR^>4|0*$$wyCxO(C=#7oWc3K z!T^BVpVk6b+{q6=DJo@zjmsB3&Ncy!mo_v3(_*OsgTQDr*`8A# zaX}gI45wx=zC1uxf2-<2pd9FM-JWhVp*o$hf6zo|0EkAD;%f1D<|t0hV18@?Fdz(9 zCcDgRiHg_)Fc560YNfFO?lRdaKs|5<7!ykX1_5!`Y>6fhGPNJ)*m`cH-C&ZL7{Mu% z?elPzy^(fnQTMY0T93b zJuV`}CcGV7tWc<`FRTE-fG{2?0A>IaRP{>OHQ+B-O~wQ0UaFP=0000 Date: Wed, 11 Mar 2015 22:50:53 -0700 Subject: [PATCH 04/13] GUAC-1120: Use flex layout for headers containing the user menu. --- .../main/webapp/app/home/templates/home.html | 10 ++-- .../src/main/webapp/app/index/styles/ui.css | 45 ++++++++++++++-- .../webapp/app/manage/templates/manage.html | 8 +-- .../manage/templates/manageConnection.html | 12 ++--- .../templates/manageConnectionGroup.html | 8 +-- .../app/manage/templates/manageUser.html | 12 ++--- .../webapp/app/userMenu/styles/user-menu.css | 52 +++++++++++++++---- .../app/userMenu/templates/guacUserMenu.html | 20 +++---- 8 files changed, 119 insertions(+), 48 deletions(-) diff --git a/guacamole/src/main/webapp/app/home/templates/home.html b/guacamole/src/main/webapp/app/home/templates/home.html index 6dc4f6a74..7b3b5fc99 100644 --- a/guacamole/src/main/webapp/app/home/templates/home.html +++ b/guacamole/src/main/webapp/app/home/templates/home.html @@ -24,17 +24,17 @@
    - - - -

    {{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

    +
    +

    {{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

    + +
    -

    {{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}

    +

    {{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}

    - - - -

    {{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

    +
    +

    {{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

    + +
    diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html index 723c29c89..41c8db37b 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html @@ -22,11 +22,11 @@ THE SOFTWARE.
    - - - -

    {{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

    +
    +

    {{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

    + +
    @@ -58,7 +58,7 @@ THE SOFTWARE. -

    {{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

    +

    {{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

    @@ -81,7 +81,7 @@ THE SOFTWARE. -

    {{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}

    +

    {{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}

    {{'MANAGE_CONNECTION.INFO_CONNECTION_NOT_USED' | translate}}

    diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html index 2e5fe1257..7986c735d 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html @@ -22,11 +22,11 @@ THE SOFTWARE.
    - - - -

    {{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

    +
    +

    {{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

    + +
    diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUser.html b/guacamole/src/main/webapp/app/manage/templates/manageUser.html index a066249f0..65c34eb06 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageUser.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUser.html @@ -22,11 +22,11 @@ THE SOFTWARE.
    - - - -

    {{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

    +
    +

    {{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

    + +
    @@ -48,7 +48,7 @@ THE SOFTWARE. -

    {{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}

    +

    {{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}

    @@ -60,7 +60,7 @@ THE SOFTWARE. -

    {{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}

    +

    {{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}

    - {{username}} - - - +
    From ddbfacdf2f6bed4852c10ae157d40f8a51259d57 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 11 Mar 2015 23:13:37 -0700 Subject: [PATCH 05/13] GUAC-1120: Use consistent styles for highlighting. Adjust for contrast. Only show menu options when open. --- .../webapp/app/userMenu/styles/user-menu.css | 63 ++++++++++++++++--- .../app/userMenu/templates/guacUserMenu.html | 2 +- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index b32e3c529..9da715785 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -20,11 +20,38 @@ * THE SOFTWARE. */ -.user-menu .user-menu-dropdown { +.user-menu { - position: relative; - border-left: 1px solid rgba(0,0,0,0.125); - background: rgba(0,0,0,0.04); + /* IE10 */ + display: -ms-flexbox; + -ms-flex-align: stretch; + -ms-flex-direction: row; + + /* Ancient Mozilla */ + display: -moz-box; + -moz-box-align: stretch; + -moz-box-orient: horizontal; + + /* Ancient WebKit */ + display: -webkit-box; + -webkit-box-align: stretch; + -webkit-box-orient: horizontal; + + /* Old WebKit */ + display: -webkit-flex; + -webkit-align-items: stretch; + -webkit-flex-direction: row; + + /* W3C */ + display: flex; + align-items: stretch; + flex-direction: row; + + z-index: 5; + +} + +.user-menu .user-menu-dropdown { /* IE10 */ display: -ms-flexbox; @@ -51,12 +78,26 @@ align-items: center; flex-direction: row; - z-index: 5; - +} + +.user-menu .user-menu-dropdown { + position: relative; + border-left: 1px solid rgba(0,0,0,0.125); + background: rgba(0,0,0,0.04); +} + +.user-menu .user-menu-dropdown:hover { + background: rgba(0,0,0,0.02); +} + +.user-menu .user-menu-dropdown.open, +.user-menu .user-menu-dropdown.open:hover { + background: rgba(0,0,0,0.3); } .user-menu .username { + cursor: default; margin: 0; min-width: 1.75in; @@ -80,6 +121,8 @@ .user-menu .options { + visibility: hidden; + position: absolute; top: 100%; right: 0; @@ -94,6 +137,10 @@ } +.user-menu .user-menu-dropdown.open .options { + visibility: visible; +} + .user-menu .options li { padding: 0; list-style-type: none; @@ -101,14 +148,14 @@ .user-menu .options li a { display: block; - cursor: default; + cursor: pointer; color: black; text-decoration: none; padding: 0.35em 0.5em; } .user-menu .options li a:hover { - background: rgba(0, 0, 0, 0.125); + background: #CDA; } .user-menu .options li a.home, diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index db2bd0731..1a1dce763 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -21,7 +21,7 @@ THE SOFTWARE. --> -
    +
    {{username}}
    From 1c75e9792dc6f98b7a3209cc54480befcc7a63b2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 11 Mar 2015 23:25:32 -0700 Subject: [PATCH 06/13] GUAC-1120: Toggle visibility of user menu upon click. --- .../webapp/app/userMenu/directives/guacUserMenu.js | 14 ++++++++++++++ .../app/userMenu/templates/guacUserMenu.html | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js index fc2095ec5..584192726 100644 --- a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js @@ -105,6 +105,13 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { */ $scope.newPasswordMatch = null; + /** + * Whether the contents of the user menu are currently shown. + * + * @type Boolean + */ + $scope.menuShown = false; + /** * The username of the current user. * @@ -159,6 +166,13 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { }); + /** + * Toggles visibility of the user menu. + */ + $scope.toggleMenu = function toggleMenu() { + $scope.menuShown = !$scope.menuShown; + }; + /** * Show the password update dialog. */ diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index 1a1dce763..d6d373c62 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -21,7 +21,7 @@ THE SOFTWARE. --> -
    +
    {{username}}
    From 0f4662daffdd53f490b89aa4b9403a8b78985877 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 00:00:50 -0700 Subject: [PATCH 07/13] GUAC-1120: Close menu when user clicks outside menu. --- .../app/userMenu/directives/guacUserMenu.js | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js index 584192726..77c1f5895 100644 --- a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js @@ -42,13 +42,14 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { }, templateUrl: 'app/userMenu/templates/guacUserMenu.html', - controller: ['$scope', '$injector', function guacUserMenuController($scope, $injector) { + controller: ['$scope', '$injector', '$element', function guacUserMenuController($scope, $injector, $element) { // Get required types var ConnectionGroup = $injector.get("ConnectionGroup"); var PermissionSet = $injector.get("PermissionSet"); // Get required services + var $document = $injector.get("$document"); var $location = $injector.get("$location"); var authenticationService = $injector.get("authenticationService"); var userService = $injector.get("userService"); @@ -64,7 +65,21 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { $scope.showStatus(false); } }; - + + /** + * The outermost element of the user menu directive. + * + * @type Element + */ + var element = $element[0]; + + /** + * The main document object. + * + * @type Document + */ + var document = $document[0]; + /** * Whether the current user has sufficient permissions to use the * management interface. If permissions have not yet been loaded, @@ -258,6 +273,18 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { }); }; + // Close menu when use clicks anywhere else + document.body.addEventListener("click", function clickOutsideMenu() { + $scope.$apply(function closeMenu() { + $scope.menuShown = false; + }); + }, false); + + // Prevent click within menu from triggering the outside-menu handler + element.addEventListener("click", function clickInsideMenu(e) { + e.stopPropagation(); + }, false); + }] // end controller }; From eb86fadbcc954f58347372a6d6e336bc0534b251 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 11:53:15 -0700 Subject: [PATCH 08/13] GUAC-1120: Increase menu spacing and hover contrast. Add menu indicator (down arrow). --- .../webapp/app/userMenu/styles/user-menu.css | 28 +++++++++++++----- .../app/userMenu/templates/guacUserMenu.html | 1 + .../action-icons/guac-open-downward.png | Bin 0 -> 282 bytes 3 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 guacamole/src/main/webapp/images/action-icons/guac-open-downward.png diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index 9da715785..f780c7cc6 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -87,7 +87,7 @@ } .user-menu .user-menu-dropdown:hover { - background: rgba(0,0,0,0.02); + background: rgba(0,0,0,0.01); } .user-menu .user-menu-dropdown.open, @@ -103,12 +103,11 @@ font-size: 1.25em; font-weight: bold; - padding: .75em .5em; + padding: 0.5em 2em; - padding-left: 2em; background-repeat: no-repeat; background-size: 1em; - background-position: 0.5em 0.75em; + background-position: 0.5em center; background-image: url('images/user-icons/guac-user.png'); -ms-flex: 0 0 auto; @@ -119,6 +118,21 @@ } +.user-menu .menu-indicator { + + position: absolute; + right: 0; + top: 0; + bottom: 0; + + width: 2em; + background-repeat: no-repeat; + background-size: 1em; + background-position: center center; + background-image: url('images/action-icons/guac-open-downward.png'); + +} + .user-menu .options { visibility: hidden; @@ -151,7 +165,7 @@ cursor: pointer; color: black; text-decoration: none; - padding: 0.35em 0.5em; + padding: 0.75em; } .user-menu .options li a:hover { @@ -164,8 +178,8 @@ .user-menu .options li a.logout { background-repeat: no-repeat; background-size: 1em; - background-position: 0.5em 0.45em; - padding-left: 2em; + background-position: 0.75em center; + padding-left: 2.5em; } .user-menu .options li a.home { diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index d6d373c62..2943b8b84 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -23,6 +23,7 @@
    {{username}}
    +
      diff --git a/guacamole/src/main/webapp/images/action-icons/guac-open-downward.png b/guacamole/src/main/webapp/images/action-icons/guac-open-downward.png new file mode 100644 index 0000000000000000000000000000000000000000..cfa9885d88d077ecd7379356d70d5662820f470b GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4B@mpRJL!(}-p_os-CHCU z&i=9KImg|j({0^enC;84bo%}=wOg>QQ$M@UAf_fVxh3)OX?~YQ z=yX+OAL;mey#}>4m8OU8ADRAKaMn@z=-N3?rahVPWY&{QANau5Jn*++P)_*Yw%%XN P8YJN9>gTe~DWM4fv=d~f literal 0 HcmV?d00001 From fea085fd7b6edf5554e05fcdb23c691e5fab8cc5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 12:49:32 -0700 Subject: [PATCH 09/13] GUAC-1120: Ensure proper rendering of menu on old Firefox. --- guacamole/src/main/webapp/app/index/styles/ui.css | 1 + guacamole/src/main/webapp/app/userMenu/styles/user-menu.css | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/app/index/styles/ui.css b/guacamole/src/main/webapp/app/index/styles/ui.css index 3e20f1eb3..4c8ca5fb9 100644 --- a/guacamole/src/main/webapp/app/index/styles/ui.css +++ b/guacamole/src/main/webapp/app/index/styles/ui.css @@ -67,6 +67,7 @@ h2 { margin-bottom: 1em; margin-top: 0; border-top: none; + width: 100%; /* IE10 */ display: -ms-flexbox; diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index f780c7cc6..aa1f1f70f 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -47,8 +47,6 @@ align-items: stretch; flex-direction: row; - z-index: 5; - } .user-menu .user-menu-dropdown { @@ -149,6 +147,8 @@ border-left: 1px solid rgba(0,0,0,0.125); border-bottom: 1px solid rgba(0,0,0,0.125); + z-index: 5; + } .user-menu .user-menu-dropdown.open .options { From 0aff80164251aff4faca5fee81a9863b3e9b66f9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 14:21:18 -0700 Subject: [PATCH 10/13] GUAC-1120: Display menu options as disabled if they are not relevant. Navigate explicitly via click event, in case browser depends on click propagation to handle href, but keep href for sake of "open in new tab", etc. --- .../app/userMenu/directives/guacUserMenu.js | 33 ++++++++++++++++++- .../webapp/app/userMenu/styles/user-menu.css | 9 ++++- .../app/userMenu/templates/guacUserMenu.html | 33 ++++++++++++++++--- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js index 77c1f5895..042254422 100644 --- a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js @@ -80,6 +80,23 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { */ var document = $document[0]; + /** + * Whether the option to go to the home screen is disabled. + * + * @type Boolean + */ + $scope.homeDisabled = ($location.path() === '/'); + + /** + * Whether the option to go to the management interface is + * disabled. Note that shis is different from canManageGuacamole, + * which deals with whether permission to manage is granted. A user + * may have permission, yet see this option as currently disabled. + * + * @type Boolean + */ + $scope.manageDisabled = ($location.path() === '/manage/'); + /** * Whether the current user has sufficient permissions to use the * management interface. If permissions have not yet been loaded, @@ -263,13 +280,27 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { }; + /** + * Navigates to the home screen. + */ + $scope.navigateHome = function navigateHome() { + $location.path('/'); + }; + + /** + * Navigates to the management interface. + */ + $scope.manage = function manage() { + $location.path('/manage/'); + }; + /** * Logs out the current user, redirecting them to back to the login * screen after logout completes. */ $scope.logout = function logout() { authenticationService.logout()['finally'](function logoutComplete() { - $location.path('/login'); + $location.path('/login/'); }); }; diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index aa1f1f70f..8405abc96 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -169,7 +169,14 @@ } .user-menu .options li a:hover { - background: #CDA; + background-color: #CDA; +} + +.user-menu .options li a.disabled, +.user-menu .options li a.disabled:hover { + background-color: transparent; + cursor: default; + opacity: 0.25; } .user-menu .options li a.home, diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index 2943b8b84..b49c956f9 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -27,10 +27,35 @@
    From 9da1026241f8bcb1914066b8b1c63f20ee4df113 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 14:22:39 -0700 Subject: [PATCH 11/13] GUAC-1120: Restore password dialog styling. --- guacamole/src/main/webapp/app/userMenu/styles/user-menu.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index 8405abc96..149da9c2b 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -215,6 +215,7 @@ transition: visibility 0.125s, opacity 0.125s; position: absolute; background: white; + text-align: right; padding: 1em; border: 1px solid rgba(0, 0, 0, 0.25); box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); @@ -224,6 +225,10 @@ z-index: 8; } +.user-menu .password-dialog .action-buttons { + margin: 0; +} + .user-menu .password-dialog.shown { visibility: visible; opacity: 1; From 91dfa04a525b4dd9738f8bfce341ae8932ab3810 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 16:00:03 -0700 Subject: [PATCH 12/13] GUAC-1120: Fix incorrect translation string names. --- .../webapp/app/userMenu/directives/guacUserMenu.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js index 042254422..a3a50746a 100644 --- a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js @@ -59,7 +59,7 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { - name : "HOME.ACTION_ACKNOWLEDGE", + name : "USER_MENU.ACTION_ACKNOWLEDGE", // Handle action callback : function acknowledgeCallback() { $scope.showStatus(false); @@ -236,8 +236,8 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { if ($scope.newPasswordMatch !== $scope.newPassword) { $scope.showStatus({ className : 'error', - title : 'HOME.DIALOG_HEADER_ERROR', - text : 'HOME.ERROR_PASSWORD_MISMATCH', + title : 'USER_MENU.DIALOG_HEADER_ERROR', + text : 'USER_MENU.ERROR_PASSWORD_MISMATCH', actions : [ ACKNOWLEDGE_ACTION ] }); return; @@ -247,8 +247,8 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { if (!$scope.newPassword) { $scope.showStatus({ className : 'error', - title : 'HOME.DIALOG_HEADER_ERROR', - text : 'HOME.ERROR_PASSWORD_BLANK', + title : 'USER_MENU.DIALOG_HEADER_ERROR', + text : 'USER_MENU.ERROR_PASSWORD_BLANK', actions : [ ACKNOWLEDGE_ACTION ] }); return; @@ -263,7 +263,7 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { // Indicate that the password has been changed $scope.showStatus({ - text : 'HOME.PASSWORD_CHANGED', + text : 'USER_MENU.PASSWORD_CHANGED', actions : [ ACKNOWLEDGE_ACTION ] }); }) @@ -272,7 +272,7 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { .error(function passwordUpdateFailed(error) { $scope.showStatus({ className : 'error', - title : 'HOME.DIALOG_HEADER_ERROR', + title : 'USER_MENU.DIALOG_HEADER_ERROR', 'text' : error.message, actions : [ ACKNOWLEDGE_ACTION ] }); From ad439499154acefbc2b7f4e8691453603f5292ca Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 12 Mar 2015 16:00:43 -0700 Subject: [PATCH 13/13] GUAC-1120: Only align password dialog fields to the right. --- guacamole/src/main/webapp/app/userMenu/styles/user-menu.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index 149da9c2b..f46665c3a 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -215,7 +215,6 @@ transition: visibility 0.125s, opacity 0.125s; position: absolute; background: white; - text-align: right; padding: 1em; border: 1px solid rgba(0, 0, 0, 0.25); box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); @@ -225,7 +224,12 @@ z-index: 8; } +.user-menu .password-dialog .fields { + text-align: right; +} + .user-menu .password-dialog .action-buttons { + text-align: center; margin: 0; }