From 69374f081822b2bdce2253cc04a1dcdc9f62552a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 7 Apr 2015 22:25:33 -0700 Subject: [PATCH 1/2] GUAC-1126: Move menu actions into own class. Make rendering of menu actions generic. --- .../app/navigation/directives/guacUserMenu.js | 59 +++++++++----- .../navigation/templates/guacUserMenu.html | 16 +--- .../webapp/app/navigation/types/MenuAction.js | 77 +++++++++++++++++++ 3 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 guacamole/src/main/webapp/app/navigation/types/MenuAction.js diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js index c9c09f7d2..d5fd0a901 100644 --- a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js @@ -73,15 +73,6 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() */ var document = $document[0]; - /** - * 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. * @@ -125,18 +116,6 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() */ $scope.pages = null; - // Retrieve current permissions - permissionService.getPermissions(authenticationService.getCurrentUserID()) - .success(function permissionsRetrieved(permissions) { - - // Check whether the current user can change their own password - $scope.canChangePassword = PermissionSet.hasUserPermission( - permissions, PermissionSet.ObjectPermissionType.UPDATE, - authenticationService.getCurrentUserID() - ); - - }); - // Retrieve the main pages from the user page service userPageService.getMainPages() .then(function retrievedMainPages(pages) { @@ -258,6 +237,44 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() }); }; + /** + * Action which logs out the current user, redirecting them to back + * to the login screen after logout completes. + */ + var LOGOUT_ACTION = { + name : 'USER_MENU.ACTION_LOGOUT', + className : 'logout', + callback : $scope.logout + }; + + /** + * Action which shows the password update dialog. + */ + var CHANGE_PASSWORD_ACTION = { + name : 'USER_MENU.ACTION_CHANGE_PASSWORD', + className : 'change-password', + callback : $scope.showPasswordUpdate + }; + + /** + * All available actions for the current user. + */ + $scope.actions = [ LOGOUT_ACTION ]; + + // Retrieve current permissions + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsRetrieved(permissions) { + + // Add action for changing password if permission is granted + if (PermissionSet.hasUserPermission(permissions, + PermissionSet.ObjectPermissionType.UPDATE, + authenticationService.getCurrentUserID())) + $scope.actions.unshift(CHANGE_PASSWORD_ACTION); + + + }); + + // Close menu when use clicks anywhere else document.body.addEventListener('click', function clickOutsideMenu() { $scope.$apply(function closeMenu() { diff --git a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html index 3c00345ae..fa287ac71 100644 --- a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html @@ -36,18 +36,10 @@ - -
  • - - {{'USER_MENU.ACTION_CHANGE_PASSWORD' | translate}} - -
  • - - -
  • - - {{'USER_MENU.ACTION_LOGOUT' | translate}} + +
  • + + {{action.name | translate}}
  • diff --git a/guacamole/src/main/webapp/app/navigation/types/MenuAction.js b/guacamole/src/main/webapp/app/navigation/types/MenuAction.js new file mode 100644 index 000000000..eb8c7b72b --- /dev/null +++ b/guacamole/src/main/webapp/app/navigation/types/MenuAction.js @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 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. + */ + +/** + * Provides the MenuAction class definition. + */ +angular.module('navigation').factory('MenuAction', [function defineMenuAction() { + + /** + * Creates a new MenuAction, which pairs an arbitrary callback with + * an action name. The name of this action will ultimately be presented to + * the user when the user when this action's associated menu is open. + * + * @constructor + * @param {String} name + * The name of this action. + * + * @param {Function} callback + * The callback to call when the user elects to perform this action. + * + * @param {String} className + * The CSS class to associate with this action, if any. + */ + var MenuAction = function MenuAction(name, callback, className) { + + /** + * Reference to this MenuAction. + * + * @type MenuAction + */ + var action = this; + + /** + * The CSS class associated with this action. + * + * @type String + */ + this.className = className; + + /** + * The name of this action. + * + * @type String + */ + this.name = name; + + /** + * The callback to call when this action is performed. + * + * @type Function + */ + this.callback = callback; + + }; + + return MenuAction; + +}]); From e460fde6ed73a5576c56c2a78f7d4b4165dda741 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 7 Apr 2015 22:49:41 -0700 Subject: [PATCH 2/2] GUAC-1126: Add client-specific disconnect action to menu. Style with danger. --- .../client/controllers/clientController.js | 13 ++++++++ .../main/webapp/app/client/styles/client.css | 10 +++++- .../main/webapp/app/client/styles/menu.css | 31 ------------------ .../webapp/app/client/templates/client.html | 3 +- .../app/navigation/directives/guacUserMenu.js | 10 ++++++ .../app/navigation/styles/user-menu.css | 10 ++++++ .../navigation/templates/guacUserMenu.html | 7 ++++ guacamole/src/main/webapp/images/x-shadow.png | Bin 839 -> 0 bytes guacamole/src/main/webapp/images/x.png | Bin 0 -> 591 bytes 9 files changed, 50 insertions(+), 34 deletions(-) delete mode 100644 guacamole/src/main/webapp/images/x-shadow.png create mode 100644 guacamole/src/main/webapp/images/x.png diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js index 7faa9c758..5986fdfe1 100644 --- a/guacamole/src/main/webapp/app/client/controllers/clientController.js +++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js @@ -544,6 +544,19 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams }; + /** + * Action which immediately disconnects the currently-connected client, if + * any. + */ + var DISCONNECT_MENU_ACTION = { + name : 'CLIENT.ACTION_DISCONNECT', + className : 'danger disconnect', + callback : $scope.disconnect + }; + + // Set client-specific menu actions + $scope.clientMenuActions = [ DISCONNECT_MENU_ACTION ]; + // Clean up when view destroyed $scope.$on('$destroy', function clientViewDestroyed() { diff --git a/guacamole/src/main/webapp/app/client/styles/client.css b/guacamole/src/main/webapp/app/client/styles/client.css index db405fff1..8dde10db0 100644 --- a/guacamole/src/main/webapp/app/client/styles/client.css +++ b/guacamole/src/main/webapp/app/client/styles/client.css @@ -117,4 +117,12 @@ body.client { width: auto; height: auto; -} \ No newline at end of file +} + +.client .user-menu .options li a.disconnect { + background-repeat: no-repeat; + background-size: 1em; + background-position: 0.75em center; + padding-left: 2.5em; + background-image: url('images/x.png'); +} diff --git a/guacamole/src/main/webapp/app/client/styles/menu.css b/guacamole/src/main/webapp/app/client/styles/menu.css index 3895d6324..c35949f01 100644 --- a/guacamole/src/main/webapp/app/client/styles/menu.css +++ b/guacamole/src/main/webapp/app/client/styles/menu.css @@ -81,37 +81,6 @@ } -.menu-content .header button.close { - - margin: 0 0.75em; - padding: 0; - width: 1.5em; - height: 1.5em; - min-width: 0; - - box-shadow: none; - -moz-border-radius: 1.5em; - -webkit-border-radius: 1.5em; - -khtml-border-radius: 1.5em; - border-radius: 1.5em; - - -moz-background-size: 0.75em; - -webkit-background-size: 0.75em; - -khtml-background-size: 0.75em; - background-size: 0.75em; - - background-image: url('images/x-shadow.png'); - background-repeat: no-repeat; - background-position: center; - - vertical-align: middle; - -ms-flex-align-self: center; - -moz-align-self: center; - -webkit-align-self: center; - align-self: center; - -} - .menu-body { -ms-flex: 1 1 auto; diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html index 8cb709c36..01e0fa547 100644 --- a/guacamole/src/main/webapp/app/client/templates/client.html +++ b/guacamole/src/main/webapp/app/client/templates/client.html @@ -59,8 +59,7 @@

    {{client.name}}

    - - +
    diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js index d5fd0a901..1868cda06 100644 --- a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js @@ -30,6 +30,16 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() restrict: 'E', replace: true, scope: { + + /** + * Optional array of actions which are specific to this particular + * location, as these actions may not be appropriate for other + * locations which contain the user menu. + * + * @type MenuAction[] + */ + localActions : '=' + }, templateUrl: 'app/navigation/templates/guacUserMenu.html', diff --git a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css index 9c331fbcd..ba54e150d 100644 --- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css @@ -215,6 +215,16 @@ background-image: url('images/action-icons/guac-logout-dark.png'); } +.user-menu .options li a.danger { + color: white; + font-weight: bold; + background-color: #A43; +} + +.user-menu .options li a.danger:hover { + background-color: #C54; +} + .user-menu .password-dialog { visibility: hidden; opacity: 0; diff --git a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html index fa287ac71..564d95173 100644 --- a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html @@ -28,6 +28,13 @@