From 38250acf40f833b4a226f315e30e2983c23445d0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 16 Apr 2015 20:41:36 -0700 Subject: [PATCH 01/13] GUAC-1053: Allow separate retrieval of settings pages from userPageService. --- .../navigation/services/userPageService.js | 72 +++++++++++++++---- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index 792c55045..4a42cfa59 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -137,20 +137,16 @@ angular.module('navigation').factory('userPageService', ['$injector', }; /** - * Returns all the main pages that the current user can visit. This can - * include the home page, manage pages, etc. In the case that there are no - * applicable pages of this sort, it may return a client page. + * Returns all settings pages that the current user can visit. This can + * include any of the various manage pages. * - * @param {ConnectionGroup} rootGroup - * The root of the connection group tree for the current user. - * * @param {PermissionSet} permissions * The permissions for the current user. * * @returns {Page[]} - * An array of all main pages that the current user can visit. + * An array of all settings pages that the current user can visit. */ - var generateMainPages = function generateMainPages(rootGroup, permissions) { + var generateSettingsPages = function generateSettingsPages(permissions) { var pages = []; @@ -203,12 +199,6 @@ angular.module('navigation').factory('userPageService', ['$injector', // A user must be a system administrator to manage sessions PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER); - // Only include the home page in the list of main pages if the user - // can navigate elsewhere. - var homePage = generateHomePage(rootGroup); - if (homePage === SYSTEM_HOME_PAGE) - pages.push(homePage); - // If user can manage users, add link to user management page if (canManageUsers) { pages.push(new Page( @@ -236,6 +226,60 @@ angular.module('navigation').factory('userPageService', ['$injector', return pages; }; + /** + * Returns a promise which resolves to an array of all settings pages that + * the current user can visit. This can include any of the various manage + * pages. + * + * @returns {Promise.} + * A promise which resolves to an array of all settings pages that the + * current user can visit. + */ + service.getSettingsPages = function getSettingsPages() { + + var deferred = $q.defer(); + + // Retrieve current permissions, resolving main pages if possible + // Resolve promise using settings pages derived from permissions + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsRetrieved(permissions) { + deferred.resolve(generateSettingsPages(permissions)); + }); + + return deferred.promise; + + }; + + /** + * Returns all the main pages that the current user can visit. This can + * include the home page, manage pages, etc. In the case that there are no + * applicable pages of this sort, it may return a client page. + * + * @param {ConnectionGroup} rootGroup + * The root of the connection group tree for the current user. + * + * @param {PermissionSet} permissions + * The permissions for the current user. + * + * @returns {Page[]} + * An array of all main pages that the current user can visit. + */ + var generateMainPages = function generateMainPages(rootGroup, permissions) { + + var pages = []; + + // Only include the home page in the list of main pages if the user + // can navigate elsewhere. + var homePage = generateHomePage(rootGroup); + if (homePage === SYSTEM_HOME_PAGE) + pages.push(homePage); + + // Add any settings pages + pages = pages.concat(generateSettingsPages(permissions)); + + return pages; + }; + /** * Returns a promise which resolves to an array of all main pages that the * current user can visit. This can include the home page, manage pages, From fbe177231ddc419d132b832afd930167b99a8910 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 16 Apr 2015 22:18:34 -0700 Subject: [PATCH 02/13] GUAC-1053: Only display one link for settings within the user menu. --- .../app/navigation/services/userPageService.js | 10 ++++++++-- .../main/webapp/app/navigation/styles/user-menu.css | 12 +++--------- guacamole/src/main/webapp/translations/en_US.json | 2 ++ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index 4a42cfa59..e06e7a398 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -274,8 +274,14 @@ angular.module('navigation').factory('userPageService', ['$injector', if (homePage === SYSTEM_HOME_PAGE) pages.push(homePage); - // Add any settings pages - pages = pages.concat(generateSettingsPages(permissions)); + // Add generic link to the first-available settings page + var settingsPages = generateSettingsPages(permissions); + if (settingsPages.length) { + pages.push(new Page( + 'USER_MENU.ACTION_MANAGE_SETTINGS', + settingsPages[0].url + )); + } return pages; }; 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 ba54e150d..617ec2be4 100644 --- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css @@ -195,16 +195,10 @@ background-image: url('images/action-icons/guac-home-dark.png'); } -.user-menu .options li a[href="#/manage/modules/users/"] { - background-image: url('images/user-icons/guac-user.png'); -} - -.user-menu .options li a[href="#/manage/modules/connections/"] { - background-image: url('images/protocol-icons/guac-monitor.png'); -} - +.user-menu .options li a[href="#/manage/modules/users/"], +.user-menu .options li a[href="#/manage/modules/connections/"], .user-menu .options li a[href="#/manage/modules/sessions/"] { - background-image: url('images/protocol-icons/guac-plug.png'); + background-image: url('images/action-icons/guac-config-dark.png'); } .user-menu .options li a.change-password { diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index c3d07bee5..476336861 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -11,6 +11,7 @@ "ACTION_LOGIN" : "Login", "ACTION_LOGOUT" : "Logout", "ACTION_MANAGE_CONNECTIONS" : "Manage Connections", + "ACTION_MANAGE_SETTINGS" : "Settings", "ACTION_MANAGE_SESSIONS" : "Active Sessions", "ACTION_MANAGE_USERS" : "Users", "ACTION_NAVIGATE_BACK" : "Back", @@ -422,6 +423,7 @@ "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", "ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS", "ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS", + "ACTION_MANAGE_SETTINGS" : "@:APP.ACTION_MANAGE_SETTINGS", "ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS", "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", "ACTION_SAVE" : "@:APP.ACTION_SAVE", From e0a805cb6fab2d3ddb9c539f71b4ed31c97d9f3d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 15:12:49 -0700 Subject: [PATCH 03/13] GUAC-1053: Split listing of page links from guacUserMenu into guacPageList. --- .../app/navigation/directives/guacPageList.js | 74 +++++++++++++++++++ .../app/navigation/directives/guacUserMenu.js | 23 ------ .../app/navigation/styles/user-menu.css | 11 ++- .../navigation/templates/guacPageList.html | 32 ++++++++ .../navigation/templates/guacUserMenu.html | 35 +++++---- 5 files changed, 130 insertions(+), 45 deletions(-) create mode 100644 guacamole/src/main/webapp/app/navigation/directives/guacPageList.js create mode 100644 guacamole/src/main/webapp/app/navigation/templates/guacPageList.html diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacPageList.js b/guacamole/src/main/webapp/app/navigation/directives/guacPageList.js new file mode 100644 index 000000000..bd5f909c5 --- /dev/null +++ b/guacamole/src/main/webapp/app/navigation/directives/guacPageList.js @@ -0,0 +1,74 @@ +/* + * 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 list of links to specific pages. + */ +angular.module('navigation').directive('guacPageList', [function guacPageList() { + + return { + restrict: 'E', + replace: true, + scope: { + + /** + * The array of pages to display. + * + * @type Page[] + */ + pages : '=' + + }, + + templateUrl: 'app/navigation/templates/guacPageList.html', + controller: ['$scope', '$injector', function guacPageListController($scope, $injector) { + + // Get required services + var $location = $injector.get('$location'); + + /** + * Navigate to the given page. + * + * @param {Page} page + * The page to navigate to. + */ + $scope.navigateToPage = function navigateToPage(page) { + $location.path(page.url); + }; + + /** + * Tests whether the given page is the page currently being viewed. + * + * @param {Page} page + * The page to test. + * + * @returns {Boolean} + * true if the given page is the current page, false otherwise. + */ + $scope.isCurrentPage = function isCurrentPage(page) { + return $location.url() === page.url; + }; + + }] // end controller + + }; +}]); diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js index 1868cda06..aff6fff4a 100644 --- a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js @@ -214,29 +214,6 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() }; - /** - * Navigate to the given page. - * - * @param {Page} page - * The page to navigate to. - */ - $scope.navigateToPage = function navigateToPage(page) { - $location.path(page.url); - }; - - /** - * Tests whether the given page should be disabled. - * - * @param {Page} page - * The page to test. - * - * @returns {Boolean} - * true if the given page should be disabled, false otherwise. - */ - $scope.isPageDisabled = function isPageDisabled(page) { - return $location.url() === page.url; - }; - /** * Logs out the current user, redirecting them to back to the login * screen after logout completes. 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 617ec2be4..ae7daa2ef 100644 --- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css @@ -139,8 +139,6 @@ top: 100%; right: 0; left: -1px; - margin: 0; - padding: 0; background: #EEE; box-shadow: 0 2px 2px rgba(0, 0, 0, 0.125); @@ -151,6 +149,11 @@ } +.user-menu .options ul { + margin: 0; + padding: 0; +} + .user-menu .user-menu-dropdown.open .options { visibility: visible; } @@ -172,8 +175,8 @@ background-color: #CDA; } -.user-menu .options li a.disabled, -.user-menu .options li a.disabled:hover { +.user-menu .options li a.current, +.user-menu .options li a.current:hover { background-color: transparent; cursor: default; opacity: 0.25; diff --git a/guacamole/src/main/webapp/app/navigation/templates/guacPageList.html b/guacamole/src/main/webapp/app/navigation/templates/guacPageList.html new file mode 100644 index 000000000..d303c8efc --- /dev/null +++ b/guacamole/src/main/webapp/app/navigation/templates/guacPageList.html @@ -0,0 +1,32 @@ + diff --git a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html index 564d95173..ab5d1d9bf 100644 --- a/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/navigation/templates/guacUserMenu.html @@ -26,31 +26,30 @@ - + From 82dee4640c3277367aaac3e4b2c04dce3d8b76ff Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 16:36:13 -0700 Subject: [PATCH 04/13] GUAC-1053: Migrate to tabbed settings screen. Create user management tab. --- .../app/index/config/indexRouteConfig.js | 28 +-- .../src/main/webapp/app/index/indexModule.js | 3 +- .../controllers/manageUserController.js | 6 +- .../controllers/manageUsersController.js | 179 ---------------- .../app/manage/templates/manageUsers.html | 56 ----- .../navigation/services/userPageService.js | 6 +- .../app/navigation/styles/user-menu.css | 6 +- .../controllers/settingsController.js | 55 +++++ .../app/settings/directives/manageUsers.js | 192 ++++++++++++++++++ .../webapp/app/settings/settingsModule.js | 33 +++ .../webapp/app/settings/styles/settings.css | 57 ++++++ .../app/settings/templates/manageUsers.html | 48 +++++ .../app/settings/templates/settings.html | 38 ++++ .../src/main/webapp/translations/en_US.json | 6 + 14 files changed, 445 insertions(+), 268 deletions(-) delete mode 100644 guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js delete mode 100644 guacamole/src/main/webapp/app/manage/templates/manageUsers.html create mode 100644 guacamole/src/main/webapp/app/settings/controllers/settingsController.js create mode 100644 guacamole/src/main/webapp/app/settings/directives/manageUsers.js create mode 100644 guacamole/src/main/webapp/app/settings/settingsModule.js create mode 100644 guacamole/src/main/webapp/app/settings/styles/settings.css create mode 100644 guacamole/src/main/webapp/app/settings/templates/manageUsers.html create mode 100644 guacamole/src/main/webapp/app/settings/templates/settings.html diff --git a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js index 837f4e632..3d51f4b52 100644 --- a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js +++ b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js @@ -125,30 +125,12 @@ angular.module('index').config(['$routeProvider', '$locationProvider', resolve : { routeToUserHomePage: routeToUserHomePage } }) - // Connection management screen - .when('/manage/modules/connections/', { + // Management screen + .when('/settings/:tab', { title : 'APP.NAME', - bodyClassName : 'manage', - templateUrl : 'app/manage/templates/manageConnections.html', - controller : 'manageConnectionsController', - resolve : { updateCurrentToken: updateCurrentToken } - }) - - // User management screen - .when('/manage/modules/users/', { - title : 'APP.NAME', - bodyClassName : 'manage', - templateUrl : 'app/manage/templates/manageUsers.html', - controller : 'manageUsersController', - resolve : { updateCurrentToken: updateCurrentToken } - }) - - // Session management screen - .when('/manage/modules/sessions/', { - title : 'APP.NAME', - bodyClassName : 'manage', - templateUrl : 'app/manage/templates/manageSessions.html', - controller : 'manageSessionsController', + bodyClassName : 'settings', + templateUrl : 'app/settings/templates/settings.html', + controller : 'settingsController', resolve : { updateCurrentToken: updateCurrentToken } }) diff --git a/guacamole/src/main/webapp/app/index/indexModule.js b/guacamole/src/main/webapp/app/index/indexModule.js index a1d927330..99fb457fa 100644 --- a/guacamole/src/main/webapp/app/index/indexModule.js +++ b/guacamole/src/main/webapp/app/index/indexModule.js @@ -34,5 +34,6 @@ angular.module('index', [ 'ngTouch', 'notification', 'pascalprecht.translate', - 'rest' + 'rest', + 'settings' ]); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index 1a43744be..d45491dc9 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -472,7 +472,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto * Cancels all pending edits, returning to the management page. */ $scope.cancel = function cancel() { - $location.path('/manage/modules/users/'); + $location.path('/settings/users'); }; /** @@ -498,7 +498,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto // Upon success, save any changed permissions permissionService.patchPermissions($scope.user.username, permissionsAdded, permissionsRemoved) .success(function patchedUserPermissions() { - $location.path('/manage/modules/users/'); + $location.path('/settings/users'); }) // Notify of any errors @@ -560,7 +560,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto // Delete the user userService.deleteUser($scope.user) .success(function deletedUser() { - $location.path('/manage/modules/users/'); + $location.path('/settings/users'); }) // Notify of any errors diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js deleted file mode 100644 index d50b5d5b5..000000000 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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. - */ - -/** - * The controller for the user administration page. - */ -angular.module('manage').controller('manageUsersController', ['$scope', '$injector', - function manageUsersController($scope, $injector) { - - // Required types - var PermissionSet = $injector.get('PermissionSet'); - var User = $injector.get('User'); - - // Required services - var $location = $injector.get('$location'); - var authenticationService = $injector.get('authenticationService'); - var guacNotification = $injector.get('guacNotification'); - var permissionService = $injector.get('permissionService'); - var userService = $injector.get('userService'); - - // Identifier of the current user - var currentUserID = authenticationService.getCurrentUserID(); - - /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. - */ - var ACKNOWLEDGE_ACTION = { - name : "MANAGE_USER.ACTION_ACKNOWLEDGE", - // Handle action - callback : function acknowledgeCallback() { - guacNotification.showStatus(false); - } - }; - - /** - * All visible users. - * - * @type User[] - */ - $scope.users = null; - - /** - * 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 create new users. If the current - * permissions have not yet been loaded, this will be null. - * - * @type Boolean - */ - $scope.canCreateUsers = null; - - /** - * The name of the new user to create, if any, when user creation is - * requested via newUser(). - * - * @type String - */ - $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. - * - * @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.permissions !== null - && $scope.canManageUsers !== null - && $scope.canCreateUsers !== null; - - }; - - // Retrieve current permissions - permissionService.getPermissions(currentUserID) - .success(function permissionsRetrieved(permissions) { - - $scope.permissions = 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 manage other users - $scope.canManageUsers = - $scope.canCreateUsers - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); - - // Return to home if there's nothing to do here - if (!$scope.canManageUsers) - $location.path('/'); - - }); - - // Retrieve all users for whom we have UPDATE or DELETE permission - userService.getUsers([PermissionSet.ObjectPermissionType.UPDATE, - PermissionSet.ObjectPermissionType.DELETE]) - .success(function usersReceived(users) { - - // Display only other users, not self - $scope.users = users.filter(function isNotSelf(user) { - return user.username !== currentUserID; - }); - - }); - - /** - * Creates a new user having the username specified in the user creation - * interface. - */ - $scope.newUser = function newUser() { - - // Create user skeleton - var user = new User({ - username: $scope.newUsername || '' - }); - - // Create specified user - userService.createUser(user) - - // Add user to visible list upon success - .success(function userCreated() { - $scope.users.push(user); - }) - - // Notify of any errors - .error(function userCreationFailed(error) { - guacNotification.showStatus({ - 'className' : 'error', - 'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR', - 'text' : error.message, - 'actions' : [ ACKNOWLEDGE_ACTION ] - }); - }); - - // Reset username - $scope.newUsername = ""; - - }; - -}]); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUsers.html b/guacamole/src/main/webapp/app/manage/templates/manageUsers.html deleted file mode 100644 index e1231a262..000000000 --- a/guacamole/src/main/webapp/app/manage/templates/manageUsers.html +++ /dev/null @@ -1,56 +0,0 @@ - - -
- -
-

{{'MANAGE_USER.SECTION_HEADER_USERS' | translate}}

- -
- - -
-

{{'MANAGE_USER.HELP_USERS' | translate}}

- - -
- - -
- - - - - - -
- -
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index e06e7a398..e6606dfd0 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -203,7 +203,7 @@ angular.module('navigation').factory('userPageService', ['$injector', if (canManageUsers) { pages.push(new Page( 'USER_MENU.ACTION_MANAGE_USERS', - '/manage/modules/users/' + '/settings/users' )); } @@ -211,7 +211,7 @@ angular.module('navigation').factory('userPageService', ['$injector', if (canManageConnections) { pages.push(new Page( 'USER_MENU.ACTION_MANAGE_CONNECTIONS', - '/manage/modules/connections/' + '/settings/connections' )); } @@ -219,7 +219,7 @@ angular.module('navigation').factory('userPageService', ['$injector', if (canManageSessions) { pages.push(new Page( 'USER_MENU.ACTION_MANAGE_SESSIONS', - '/manage/modules/sessions/' + '/settings/sessions' )); } 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 ae7daa2ef..b8d162157 100644 --- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css @@ -198,9 +198,9 @@ background-image: url('images/action-icons/guac-home-dark.png'); } -.user-menu .options li a[href="#/manage/modules/users/"], -.user-menu .options li a[href="#/manage/modules/connections/"], -.user-menu .options li a[href="#/manage/modules/sessions/"] { +.user-menu .options li a[href="#/settings/users"], +.user-menu .options li a[href="#/settings/connections"], +.user-menu .options li a[href="#/settings/sessions"] { background-image: url('images/action-icons/guac-config-dark.png'); } diff --git a/guacamole/src/main/webapp/app/settings/controllers/settingsController.js b/guacamole/src/main/webapp/app/settings/controllers/settingsController.js new file mode 100644 index 000000000..ce1a26aef --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/controllers/settingsController.js @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/** + * The controller for the general settings page. + */ +angular.module('manage').controller('settingsController', ['$scope', '$injector', + function settingsController($scope, $injector) { + + // Required services + var $routeParams = $injector.get('$routeParams'); + var userPageService = $injector.get('userPageService'); + + /** + * The array of settings pages available to the current user, or null if + * not yet known. + * + * @type Page[] + */ + $scope.settingsPages = null; + + /** + * The currently-selected settings tab. This may be 'users', 'connections', + * or 'sessions'. + * + * @type String + */ + $scope.activeTab = $routeParams.tab; + + // Retrieve settings pages + userPageService.getSettingsPages() + .then(function settingsPagesRetrieved(pages) { + $scope.settingsPages = pages; + }); + +}]); diff --git a/guacamole/src/main/webapp/app/settings/directives/manageUsers.js b/guacamole/src/main/webapp/app/settings/directives/manageUsers.js new file mode 100644 index 000000000..86b775cd7 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/directives/manageUsers.js @@ -0,0 +1,192 @@ +/* + * 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 for managing all users in the system. + */ +angular.module('settings').directive('guacManageUsers', [function guacManageUsers() { + + return { + // Element only + restrict: 'E', + replace: true, + + scope: { + }, + + templateUrl: 'app/settings/templates/manageUsers.html', + controller: ['$scope', '$injector', function manageUsersController($scope, $injector) { + + // Required types + var PermissionSet = $injector.get('PermissionSet'); + var User = $injector.get('User'); + + // Required services + var $location = $injector.get('$location'); + var authenticationService = $injector.get('authenticationService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); + var userService = $injector.get('userService'); + + // Identifier of the current user + var currentUserID = authenticationService.getCurrentUserID(); + + /** + * An action to be provided along with the object sent to showStatus which + * closes the currently-shown status dialog. + */ + var ACKNOWLEDGE_ACTION = { + name : "MANAGE_USER.ACTION_ACKNOWLEDGE", + // Handle action + callback : function acknowledgeCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * All visible users. + * + * @type User[] + */ + $scope.users = null; + + /** + * 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 create new users. If the current + * permissions have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canCreateUsers = null; + + /** + * The name of the new user to create, if any, when user creation is + * requested via newUser(). + * + * @type String + */ + $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. + * + * @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.permissions !== null + && $scope.canManageUsers !== null + && $scope.canCreateUsers !== null; + + }; + + // Retrieve current permissions + permissionService.getPermissions(currentUserID) + .success(function permissionsRetrieved(permissions) { + + $scope.permissions = 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 manage other users + $scope.canManageUsers = + $scope.canCreateUsers + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); + + // Return to home if there's nothing to do here + if (!$scope.canManageUsers) + $location.path('/'); + + }); + + // Retrieve all users for whom we have UPDATE or DELETE permission + userService.getUsers([PermissionSet.ObjectPermissionType.UPDATE, + PermissionSet.ObjectPermissionType.DELETE]) + .success(function usersReceived(users) { + + // Display only other users, not self + $scope.users = users.filter(function isNotSelf(user) { + return user.username !== currentUserID; + }); + + }); + + /** + * Creates a new user having the username specified in the user creation + * interface. + */ + $scope.newUser = function newUser() { + + // Create user skeleton + var user = new User({ + username: $scope.newUsername || '' + }); + + // Create specified user + userService.createUser(user) + + // Add user to visible list upon success + .success(function userCreated() { + $scope.users.push(user); + }) + + // Notify of any errors + .error(function userCreationFailed(error) { + guacNotification.showStatus({ + 'className' : 'error', + 'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR', + 'text' : error.message, + 'actions' : [ ACKNOWLEDGE_ACTION ] + }); + }); + + // Reset username + $scope.newUsername = ""; + + }; + + }] + }; + +}]); diff --git a/guacamole/src/main/webapp/app/settings/settingsModule.js b/guacamole/src/main/webapp/app/settings/settingsModule.js new file mode 100644 index 000000000..b3d8c0665 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/settingsModule.js @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/** + * The module for manipulation of general settings. This is distinct from the + * "manage" module, which deals only with administrator-level system management. + */ +angular.module('settings', [ + 'groupList', + 'list', + 'navigation', + 'notification', + 'rest' +]); diff --git a/guacamole/src/main/webapp/app/settings/styles/settings.css b/guacamole/src/main/webapp/app/settings/styles/settings.css new file mode 100644 index 000000000..9684adc25 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/styles/settings.css @@ -0,0 +1,57 @@ +/* + * 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. + */ + +.settings .header { + margin-bottom: 0; +} + +.settings-tabs .page-list { + margin: 0; + padding: 0; + background: rgba(0, 0, 0, 0.0125); +} + +.settings-tabs .page-list li { + display: inline-block; + list-style: none; +} + +.settings-tabs .page-list li a[href] { + display: block; + color: black; + text-decoration: none; + padding: 0.75em 1em; +} + +.settings-tabs .page-list li a[href]:visited { + color: black; +} + +.settings-tabs .page-list li a[href]:hover { + background-color: #CDA; +} + +.settings-tabs .page-list li a[href].current, +.settings-tabs .page-list li a[href].current:hover { + background: rgba(0,0,0,0.3); + cursor: default; +} diff --git a/guacamole/src/main/webapp/app/settings/templates/manageUsers.html b/guacamole/src/main/webapp/app/settings/templates/manageUsers.html new file mode 100644 index 000000000..2ee778b21 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/manageUsers.html @@ -0,0 +1,48 @@ +
+ + + +

{{'MANAGE_USER.HELP_USERS' | translate}}

+ + +
+ + +
+ + + + + + + +
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/settings/templates/settings.html b/guacamole/src/main/webapp/app/settings/templates/settings.html new file mode 100644 index 000000000..2766f31d4 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/settings.html @@ -0,0 +1,38 @@ + + +
+ +
+

{{'MANAGE_SETTINGS.SECTION_HEADER_MANAGE_SETTINGS' | translate}}

+ +
+ + +
+ +
+ + + + +
diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index 476336861..1c4a69183 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -273,6 +273,12 @@ }, + "MANAGE_SETTINGS" : { + + "SECTION_HEADER_MANAGE_SETTINGS" : "Settings" + + }, + "PROTOCOL_RDP" : { "FIELD_HEADER_COLOR_DEPTH" : "Color depth:", From ed984da8d1c3737ffcf1633b43c5f4f06dd0ff81 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 16:52:51 -0700 Subject: [PATCH 05/13] GUAC-1053: Create connection/group management tab. --- .../controllers/manageConnectionController.js | 6 +- .../manageConnectionsController.js | 156 ---------------- .../manage/templates/manageConnections.html | 57 ------ .../settings/directives/manageConnections.js | 169 ++++++++++++++++++ .../templates/connection.html | 0 .../templates/connectionGroup.html | 0 .../settings/templates/manageConnections.html | 48 +++++ .../app/settings/templates/settings.html | 3 +- 8 files changed, 222 insertions(+), 217 deletions(-) delete mode 100644 guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js delete mode 100644 guacamole/src/main/webapp/app/manage/templates/manageConnections.html create mode 100644 guacamole/src/main/webapp/app/settings/directives/manageConnections.js rename guacamole/src/main/webapp/app/{manage => settings}/templates/connection.html (100%) rename guacamole/src/main/webapp/app/{manage => settings}/templates/connectionGroup.html (100%) create mode 100644 guacamole/src/main/webapp/app/settings/templates/manageConnections.html diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 08700c893..989875a69 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -317,7 +317,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i * Cancels all pending edits, returning to the management page. */ $scope.cancel = function cancel() { - $location.path('/manage/modules/connections/'); + $location.path('/settings/connections'); }; /** @@ -339,7 +339,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i // Save the connection connectionService.saveConnection($scope.connection) .success(function savedConnection() { - $location.path('/manage/modules/connections/'); + $location.path('/settings/connections'); }) // Notify of any errors @@ -389,7 +389,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i // Delete the connection connectionService.deleteConnection($scope.connection) .success(function deletedConnection() { - $location.path('/manage/modules/connections/'); + $location.path('/settings/connections'); }) // Notify of any errors diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js deleted file mode 100644 index 76978ec10..000000000 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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. - */ - -/** - * The controller for the connection and connection group administration page. - */ -angular.module('manage').controller('manageConnectionsController', ['$scope', '$injector', - function manageConnectionsController($scope, $injector) { - - // Required types - var ConnectionGroup = $injector.get('ConnectionGroup'); - var PermissionSet = $injector.get('PermissionSet'); - - // Required services - var $location = $injector.get('$location'); - var authenticationService = $injector.get('authenticationService'); - var connectionGroupService = $injector.get('connectionGroupService'); - var guacNotification = $injector.get('guacNotification'); - var permissionService = $injector.get('permissionService'); - - // Identifier of the current user - var currentUserID = authenticationService.getCurrentUserID(); - - /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. - */ - var ACKNOWLEDGE_ACTION = { - name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE", - // Handle action - callback : function acknowledgeCallback() { - guacNotification.showStatus(false); - } - }; - - /** - * The root connection group of the connection group hierarchy. - * - * @type ConnectionGroup - */ - $scope.rootGroup = 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 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; - - /** - * 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. - * - * @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.permissions !== null - && $scope.canManageConnections !== null - && $scope.canCreateConnections !== null - && $scope.canCreateConnectionGroups !== null; - - }; - - // Retrieve current permissions - 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); - - // 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 connections or groups - $scope.canManageConnections = - - // Permission to manage connections - $scope.canCreateConnections - || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) - - // Permission to manage groups - || $scope.canCreateConnectionGroups - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); - - // Return to home if there's nothing to do here - if (!$scope.canManageConnections) - $location.path('/'); - - }); - - // Retrieve all connections for which we have UPDATE or DELETE permission - connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, - [PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE]) - .success(function connectionGroupReceived(rootGroup) { - $scope.rootGroup = rootGroup; - }); - -}]); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnections.html b/guacamole/src/main/webapp/app/manage/templates/manageConnections.html deleted file mode 100644 index 84773c8e7..000000000 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnections.html +++ /dev/null @@ -1,57 +0,0 @@ - - -
- -
-

{{'MANAGE_CONNECTION.SECTION_HEADER_CONNECTIONS' | translate}}

- -
- - -
-

{{'MANAGE_CONNECTION.HELP_CONNECTIONS' | translate}}

- - - - - -
- -
-
- -
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/settings/directives/manageConnections.js b/guacamole/src/main/webapp/app/settings/directives/manageConnections.js new file mode 100644 index 000000000..1df0f8cac --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/directives/manageConnections.js @@ -0,0 +1,169 @@ +/* + * 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. + */ + +/** + * A directive for managing all connections and connection groups in the system. + */ +angular.module('settings').directive('guacManageConnections', [function guacManageConnections() { + + return { + // Element only + restrict: 'E', + replace: true, + + scope: { + }, + + templateUrl: 'app/settings/templates/manageConnections.html', + controller: ['$scope', '$injector', function manageConnectionsController($scope, $injector) { + + // Required types + var ConnectionGroup = $injector.get('ConnectionGroup'); + var PermissionSet = $injector.get('PermissionSet'); + + // Required services + var $location = $injector.get('$location'); + var authenticationService = $injector.get('authenticationService'); + var connectionGroupService = $injector.get('connectionGroupService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); + + // Identifier of the current user + var currentUserID = authenticationService.getCurrentUserID(); + + /** + * An action to be provided along with the object sent to showStatus which + * closes the currently-shown status dialog. + */ + var ACKNOWLEDGE_ACTION = { + name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE", + // Handle action + callback : function acknowledgeCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * The root connection group of the connection group hierarchy. + * + * @type ConnectionGroup + */ + $scope.rootGroup = 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 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; + + /** + * 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. + * + * @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.permissions !== null + && $scope.canManageConnections !== null + && $scope.canCreateConnections !== null + && $scope.canCreateConnectionGroups !== null; + + }; + + // Retrieve current permissions + 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); + + // 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 connections or groups + $scope.canManageConnections = + + // Permission to manage connections + $scope.canCreateConnections + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) + + // Permission to manage groups + || $scope.canCreateConnectionGroups + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); + + // Return to home if there's nothing to do here + if (!$scope.canManageConnections) + $location.path('/'); + + }); + + // Retrieve all connections for which we have UPDATE or DELETE permission + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, + [PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE]) + .success(function connectionGroupReceived(rootGroup) { + $scope.rootGroup = rootGroup; + }); + + }] + }; + +}]); diff --git a/guacamole/src/main/webapp/app/manage/templates/connection.html b/guacamole/src/main/webapp/app/settings/templates/connection.html similarity index 100% rename from guacamole/src/main/webapp/app/manage/templates/connection.html rename to guacamole/src/main/webapp/app/settings/templates/connection.html diff --git a/guacamole/src/main/webapp/app/manage/templates/connectionGroup.html b/guacamole/src/main/webapp/app/settings/templates/connectionGroup.html similarity index 100% rename from guacamole/src/main/webapp/app/manage/templates/connectionGroup.html rename to guacamole/src/main/webapp/app/settings/templates/connectionGroup.html diff --git a/guacamole/src/main/webapp/app/settings/templates/manageConnections.html b/guacamole/src/main/webapp/app/settings/templates/manageConnections.html new file mode 100644 index 000000000..444865eca --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/manageConnections.html @@ -0,0 +1,48 @@ +
+ + + +

{{'MANAGE_CONNECTION.HELP_CONNECTIONS' | translate}}

+ + + + + +
+ +
+
diff --git a/guacamole/src/main/webapp/app/settings/templates/settings.html b/guacamole/src/main/webapp/app/settings/templates/settings.html index 2766f31d4..8edb4d4bf 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settings.html +++ b/guacamole/src/main/webapp/app/settings/templates/settings.html @@ -33,6 +33,7 @@ THE SOFTWARE. - + + From 3fa3d1ba2b362efcdbc91effc8c95ae5071d35e0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 17:03:45 -0700 Subject: [PATCH 06/13] GUAC-1053: Create session management tab. --- .../controllers/manageSessionsController.js | 349 ----------------- .../app/manage/templates/manageSessions.html | 86 ----- .../app/settings/directives/manageSessions.js | 363 ++++++++++++++++++ .../{manage => settings}/styles/sessions.css | 6 +- .../webapp/app/settings/styles/settings.css | 11 + .../settings/templates/manageSessions.html | 77 ++++ .../app/settings/templates/settings.html | 1 + .../types/ActiveConnectionWrapper.js | 2 +- 8 files changed, 456 insertions(+), 439 deletions(-) delete mode 100644 guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js delete mode 100644 guacamole/src/main/webapp/app/manage/templates/manageSessions.html create mode 100644 guacamole/src/main/webapp/app/settings/directives/manageSessions.js rename guacamole/src/main/webapp/app/{manage => settings}/styles/sessions.css (90%) create mode 100644 guacamole/src/main/webapp/app/settings/templates/manageSessions.html rename guacamole/src/main/webapp/app/{manage => settings}/types/ActiveConnectionWrapper.js (97%) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js deleted file mode 100644 index f89f19a32..000000000 --- a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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. - */ - -/** - * The controller for the user session administration page. - */ -angular.module('manage').controller('manageSessionsController', ['$scope', '$injector', - function manageSessionsController($scope, $injector) { - - // Required types - var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper'); - var ConnectionGroup = $injector.get('ConnectionGroup'); - var SortOrder = $injector.get('SortOrder'); - - // Required services - var $filter = $injector.get('$filter'); - var $translate = $injector.get('$translate'); - var activeConnectionService = $injector.get('activeConnectionService'); - var authenticationService = $injector.get('authenticationService'); - var connectionGroupService = $injector.get('connectionGroupService'); - var guacNotification = $injector.get('guacNotification'); - var permissionService = $injector.get('permissionService'); - - /** - * All permissions associated with the current user, or null if the user's - * permissions have not yet been loaded. - * - * @type PermissionSet - */ - $scope.permissions = null; - - /** - * The ActiveConnectionWrappers of all active sessions accessible by the - * current user, or null if the active sessions have not yet been loaded. - * - * @type ActiveConnectionWrapper[] - */ - $scope.wrappers = null; - - /** - * SortOrder instance which maintains the sort order of the visible - * connection wrappers. - * - * @type SortOrder - */ - $scope.wrapperOrder = new SortOrder([ - 'activeConnection.username', - 'startDate', - 'activeConnection.remoteHost', - 'name' - ]); - - /** - * Array of all wrapper properties that are filterable. - * - * @type String[] - */ - $scope.filteredWrapperProperties = [ - 'activeConnection.username', - 'startDate', - 'activeConnection.remoteHost', - 'name' - ]; - - /** - * All active connections, if known, or null if active connections have not - * yet been loaded. - * - * @type ActiveConnection - */ - var activeConnections = null; - - /** - * Map of all visible connections by object identifier, or null if visible - * connections have not yet been loaded. - * - * @type Object. - */ - var connections = null; - - /** - * The date format for use for session-related dates. - * - * @type String - */ - var sessionDateFormat = null; - - /** - * Map of all currently-selected active connection wrappers by identifier. - * - * @type Object. - */ - var selectedWrappers = {}; - - /** - * Adds the given connection to the internal set of visible - * connections. - * - * @param {Connection} connection - * The connection to add to the internal set of visible connections. - */ - var addConnection = function addConnection(connection) { - - // Add given connection to set of visible connections - connections[connection.identifier] = connection; - - }; - - /** - * Adds all descendant connections of the given connection group to the - * internal set of connections. - * - * @param {ConnectionGroup} connectionGroup - * The connection group whose descendant connections should be added to - * the internal set of connections. - */ - var addDescendantConnections = function addDescendantConnections(connectionGroup) { - - // Add all child connections - if (connectionGroup.childConnections) - connectionGroup.childConnections.forEach(addConnection); - - // Add all child connection groups - if (connectionGroup.childConnectionGroups) - connectionGroup.childConnectionGroups.forEach(addDescendantConnections); - - }; - - /** - * Wraps all loaded active connections, storing the resulting array within - * the scope. If required data has not yet finished loading, this function - * has no effect. - */ - var wrapActiveConnections = function wrapActiveConnections() { - - // Abort if not all required data is available - if (!activeConnections || !connections || !sessionDateFormat) - return; - - // Wrap all active connections for sake of display - $scope.wrappers = []; - for (var identifier in activeConnections) { - - var activeConnection = activeConnections[identifier]; - var connection = connections[activeConnection.connectionIdentifier]; - - $scope.wrappers.push(new ActiveConnectionWrapper( - connection.name, - $filter('date')(activeConnection.startDate, sessionDateFormat), - activeConnection - )); - - } - - }; - - // Query the user's permissions - permissionService.getPermissions(authenticationService.getCurrentUserID()) - .success(function permissionsReceived(retrievedPermissions) { - $scope.permissions = retrievedPermissions; - }); - - // Retrieve all connections - connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER) - .success(function connectionGroupReceived(retrievedRootGroup) { - - // Load connections from retrieved group tree - connections = {}; - addDescendantConnections(retrievedRootGroup); - - // Attempt to produce wrapped list of active connections - wrapActiveConnections(); - - }); - - // Query active sessions - activeConnectionService.getActiveConnections().success(function sessionsRetrieved(retrievedActiveConnections) { - - // Store received list - activeConnections = retrievedActiveConnections; - - // Attempt to produce wrapped list of active connections - wrapActiveConnections(); - - }); - - // Get session date format - $translate('MANAGE_SESSION.FORMAT_STARTDATE').then(function sessionDateFormatReceived(retrievedSessionDateFormat) { - - // Store received date format - sessionDateFormat = retrievedSessionDateFormat; - - // Attempt to produce wrapped list of active connections - wrapActiveConnections(); - - }); - - /** - * 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.wrappers !== null - && $scope.sessionDateFormat !== null - && $scope.permissions !== null; - - }; - - /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. - */ - var ACKNOWLEDGE_ACTION = { - name : "MANAGE_SESSION.ACTION_ACKNOWLEDGE", - // Handle action - callback : function acknowledgeCallback() { - guacNotification.showStatus(false); - } - }; - - /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. - */ - var CANCEL_ACTION = { - name : "MANAGE_SESSION.ACTION_CANCEL", - // Handle action - callback : function cancelCallback() { - guacNotification.showStatus(false); - } - }; - - /** - * An action to be provided along with the object sent to showStatus which - * immediately deletes the currently selected sessions. - */ - var DELETE_ACTION = { - name : "MANAGE_SESSION.ACTION_DELETE", - className : "danger", - // Handle action - callback : function deleteCallback() { - deleteSessionsImmediately(); - guacNotification.showStatus(false); - } - }; - - /** - * Immediately deletes the selected sessions, without prompting the user for - * confirmation. - */ - var deleteSessionsImmediately = function deleteSessionsImmediately() { - - // Perform deletion - activeConnectionService.deleteActiveConnections(Object.keys(selectedWrappers)) - .success(function activeConnectionsDeleted() { - - // Remove deleted connections from wrapper array - $scope.wrappers = $scope.wrappers.filter(function activeConnectionStillExists(wrapper) { - return !(wrapper.activeConnection.identifier in selectedWrappers); - }); - - // Clear selection - selectedWrappers = {}; - - }) - - // Notify of any errors - .error(function activeConnectionDeletionFailed(error) { - guacNotification.showStatus({ - 'className' : 'error', - 'title' : 'MANAGE_SESSION.DIALOG_HEADER_ERROR', - 'text' : error.message, - 'actions' : [ ACKNOWLEDGE_ACTION ] - }); - }); - - }; - - /** - * Delete all selected sessions, prompting the user first to confirm that - * deletion is desired. - */ - $scope.deleteSessions = function deleteSessions() { - // Confirm deletion request - guacNotification.showStatus({ - 'title' : 'MANAGE_SESSION.DIALOG_HEADER_CONFIRM_DELETE', - 'text' : 'MANAGE_SESSION.TEXT_CONFIRM_DELETE', - 'actions' : [ DELETE_ACTION, CANCEL_ACTION] - }); - }; - - /** - * Returns whether the selected sessions can be deleted. - * - * @returns {Boolean} - * true if selected sessions can be deleted, false otherwise. - */ - $scope.canDeleteSessions = function canDeleteSessions() { - - // We can delete sessions if at least one is selected - for (var identifier in selectedWrappers) - return true; - - return false; - - }; - - /** - * Called whenever an active connection wrapper changes selected status. - * - * @param {ActiveConnectionWrapper} wrapper - * The wrapper whose selected status has changed. - */ - $scope.wrapperSelectionChange = function wrapperSelectionChange(wrapper) { - - // Add wrapper to map if selected - if (wrapper.checked) - selectedWrappers[wrapper.activeConnection.identifier] = wrapper; - - // Otherwise, remove wrapper from map - else - delete selectedWrappers[wrapper.activeConnection.identifier]; - - }; - -}]); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html deleted file mode 100644 index 2b39290e1..000000000 --- a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html +++ /dev/null @@ -1,86 +0,0 @@ - - -
- -
-

{{'MANAGE_SESSION.SECTION_HEADER_SESSIONS' | translate}}

- -
- - -
-

{{'MANAGE_SESSION.HELP_SESSIONS' | translate}}

- - -
- -
- - - - - - - - - - - - - - - - - - - - - - - - -
- {{'MANAGE_SESSION.TABLE_HEADER_SESSION_USERNAME' | translate}} - - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_STARTDATE' | translate}} - - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_REMOTEHOST' | translate}} - - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}} -
- - {{wrapper.activeConnection.username}}{{wrapper.startDate}}{{wrapper.activeConnection.remoteHost}}{{wrapper.name}}
- - -

- {{'MANAGE_SESSION.INFO_NO_SESSIONS' | translate}} -

- - - -
- -
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/settings/directives/manageSessions.js b/guacamole/src/main/webapp/app/settings/directives/manageSessions.js new file mode 100644 index 000000000..fd053acf9 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/directives/manageSessions.js @@ -0,0 +1,363 @@ +/* + * 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 for managing all active Guacamole sessions. + */ +angular.module('settings').directive('guacManageSessions', [function guacManageSessions() { + + return { + // Element only + restrict: 'E', + replace: true, + + scope: { + }, + + templateUrl: 'app/settings/templates/manageSessions.html', + controller: ['$scope', '$injector', + function manageSessionsController($scope, $injector) { + + // Required types + var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper'); + var ConnectionGroup = $injector.get('ConnectionGroup'); + var SortOrder = $injector.get('SortOrder'); + + // Required services + var $filter = $injector.get('$filter'); + var $translate = $injector.get('$translate'); + var activeConnectionService = $injector.get('activeConnectionService'); + var authenticationService = $injector.get('authenticationService'); + var connectionGroupService = $injector.get('connectionGroupService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); + + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + + /** + * The ActiveConnectionWrappers of all active sessions accessible by the + * current user, or null if the active sessions have not yet been loaded. + * + * @type ActiveConnectionWrapper[] + */ + $scope.wrappers = null; + + /** + * SortOrder instance which maintains the sort order of the visible + * connection wrappers. + * + * @type SortOrder + */ + $scope.wrapperOrder = new SortOrder([ + 'activeConnection.username', + 'startDate', + 'activeConnection.remoteHost', + 'name' + ]); + + /** + * Array of all wrapper properties that are filterable. + * + * @type String[] + */ + $scope.filteredWrapperProperties = [ + 'activeConnection.username', + 'startDate', + 'activeConnection.remoteHost', + 'name' + ]; + + /** + * All active connections, if known, or null if active connections have not + * yet been loaded. + * + * @type ActiveConnection + */ + var activeConnections = null; + + /** + * Map of all visible connections by object identifier, or null if visible + * connections have not yet been loaded. + * + * @type Object. + */ + var connections = null; + + /** + * The date format for use for session-related dates. + * + * @type String + */ + var sessionDateFormat = null; + + /** + * Map of all currently-selected active connection wrappers by identifier. + * + * @type Object. + */ + var selectedWrappers = {}; + + /** + * Adds the given connection to the internal set of visible + * connections. + * + * @param {Connection} connection + * The connection to add to the internal set of visible connections. + */ + var addConnection = function addConnection(connection) { + + // Add given connection to set of visible connections + connections[connection.identifier] = connection; + + }; + + /** + * Adds all descendant connections of the given connection group to the + * internal set of connections. + * + * @param {ConnectionGroup} connectionGroup + * The connection group whose descendant connections should be added to + * the internal set of connections. + */ + var addDescendantConnections = function addDescendantConnections(connectionGroup) { + + // Add all child connections + if (connectionGroup.childConnections) + connectionGroup.childConnections.forEach(addConnection); + + // Add all child connection groups + if (connectionGroup.childConnectionGroups) + connectionGroup.childConnectionGroups.forEach(addDescendantConnections); + + }; + + /** + * Wraps all loaded active connections, storing the resulting array within + * the scope. If required data has not yet finished loading, this function + * has no effect. + */ + var wrapActiveConnections = function wrapActiveConnections() { + + // Abort if not all required data is available + if (!activeConnections || !connections || !sessionDateFormat) + return; + + // Wrap all active connections for sake of display + $scope.wrappers = []; + for (var identifier in activeConnections) { + + var activeConnection = activeConnections[identifier]; + var connection = connections[activeConnection.connectionIdentifier]; + + $scope.wrappers.push(new ActiveConnectionWrapper( + connection.name, + $filter('date')(activeConnection.startDate, sessionDateFormat), + activeConnection + )); + + } + + }; + + // Query the user's permissions + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsReceived(retrievedPermissions) { + $scope.permissions = retrievedPermissions; + }); + + // Retrieve all connections + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER) + .success(function connectionGroupReceived(retrievedRootGroup) { + + // Load connections from retrieved group tree + connections = {}; + addDescendantConnections(retrievedRootGroup); + + // Attempt to produce wrapped list of active connections + wrapActiveConnections(); + + }); + + // Query active sessions + activeConnectionService.getActiveConnections().success(function sessionsRetrieved(retrievedActiveConnections) { + + // Store received list + activeConnections = retrievedActiveConnections; + + // Attempt to produce wrapped list of active connections + wrapActiveConnections(); + + }); + + // Get session date format + $translate('MANAGE_SESSION.FORMAT_STARTDATE').then(function sessionDateFormatReceived(retrievedSessionDateFormat) { + + // Store received date format + sessionDateFormat = retrievedSessionDateFormat; + + // Attempt to produce wrapped list of active connections + wrapActiveConnections(); + + }); + + /** + * 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.wrappers !== null + && $scope.sessionDateFormat !== null + && $scope.permissions !== null; + + }; + + /** + * An action to be provided along with the object sent to showStatus which + * closes the currently-shown status dialog. + */ + var ACKNOWLEDGE_ACTION = { + name : "MANAGE_SESSION.ACTION_ACKNOWLEDGE", + // Handle action + callback : function acknowledgeCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * An action to be provided along with the object sent to showStatus which + * closes the currently-shown status dialog. + */ + var CANCEL_ACTION = { + name : "MANAGE_SESSION.ACTION_CANCEL", + // Handle action + callback : function cancelCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * An action to be provided along with the object sent to showStatus which + * immediately deletes the currently selected sessions. + */ + var DELETE_ACTION = { + name : "MANAGE_SESSION.ACTION_DELETE", + className : "danger", + // Handle action + callback : function deleteCallback() { + deleteSessionsImmediately(); + guacNotification.showStatus(false); + } + }; + + /** + * Immediately deletes the selected sessions, without prompting the user for + * confirmation. + */ + var deleteSessionsImmediately = function deleteSessionsImmediately() { + + // Perform deletion + activeConnectionService.deleteActiveConnections(Object.keys(selectedWrappers)) + .success(function activeConnectionsDeleted() { + + // Remove deleted connections from wrapper array + $scope.wrappers = $scope.wrappers.filter(function activeConnectionStillExists(wrapper) { + return !(wrapper.activeConnection.identifier in selectedWrappers); + }); + + // Clear selection + selectedWrappers = {}; + + }) + + // Notify of any errors + .error(function activeConnectionDeletionFailed(error) { + guacNotification.showStatus({ + 'className' : 'error', + 'title' : 'MANAGE_SESSION.DIALOG_HEADER_ERROR', + 'text' : error.message, + 'actions' : [ ACKNOWLEDGE_ACTION ] + }); + }); + + }; + + /** + * Delete all selected sessions, prompting the user first to confirm that + * deletion is desired. + */ + $scope.deleteSessions = function deleteSessions() { + // Confirm deletion request + guacNotification.showStatus({ + 'title' : 'MANAGE_SESSION.DIALOG_HEADER_CONFIRM_DELETE', + 'text' : 'MANAGE_SESSION.TEXT_CONFIRM_DELETE', + 'actions' : [ DELETE_ACTION, CANCEL_ACTION] + }); + }; + + /** + * Returns whether the selected sessions can be deleted. + * + * @returns {Boolean} + * true if selected sessions can be deleted, false otherwise. + */ + $scope.canDeleteSessions = function canDeleteSessions() { + + // We can delete sessions if at least one is selected + for (var identifier in selectedWrappers) + return true; + + return false; + + }; + + /** + * Called whenever an active connection wrapper changes selected status. + * + * @param {ActiveConnectionWrapper} wrapper + * The wrapper whose selected status has changed. + */ + $scope.wrapperSelectionChange = function wrapperSelectionChange(wrapper) { + + // Add wrapper to map if selected + if (wrapper.checked) + selectedWrappers[wrapper.activeConnection.identifier] = wrapper; + + // Otherwise, remove wrapper from map + else + delete selectedWrappers[wrapper.activeConnection.identifier]; + + }; + + }] + }; + +}]); diff --git a/guacamole/src/main/webapp/app/manage/styles/sessions.css b/guacamole/src/main/webapp/app/settings/styles/sessions.css similarity index 90% rename from guacamole/src/main/webapp/app/manage/styles/sessions.css rename to guacamole/src/main/webapp/app/settings/styles/sessions.css index ad8a7307e..e13a38293 100644 --- a/guacamole/src/main/webapp/app/manage/styles/sessions.css +++ b/guacamole/src/main/webapp/app/settings/styles/sessions.css @@ -20,15 +20,15 @@ * THE SOFTWARE. */ -.manage table.session-list { +.settings table.session-list { width: 100%; } -.manage table.session-list tr.session:hover { +.settings table.session-list tr.session:hover { background: #CDA; } -.manage table.session-list .select-session { +.settings table.session-list .select-session { min-width: 2em; text-align: center; } diff --git a/guacamole/src/main/webapp/app/settings/styles/settings.css b/guacamole/src/main/webapp/app/settings/styles/settings.css index 9684adc25..fe685865a 100644 --- a/guacamole/src/main/webapp/app/settings/styles/settings.css +++ b/guacamole/src/main/webapp/app/settings/styles/settings.css @@ -24,6 +24,17 @@ margin-bottom: 0; } +.settings table.properties th { + text-align: left; + font-weight: normal; + padding-right: 1em; +} + +.settings .action-buttons { + text-align: center; + margin-bottom: 1em; +} + .settings-tabs .page-list { margin: 0; padding: 0; diff --git a/guacamole/src/main/webapp/app/settings/templates/manageSessions.html b/guacamole/src/main/webapp/app/settings/templates/manageSessions.html new file mode 100644 index 000000000..3c1b1aff1 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/manageSessions.html @@ -0,0 +1,77 @@ +
+ + + +

{{'MANAGE_SESSION.HELP_SESSIONS' | translate}}

+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ {{'MANAGE_SESSION.TABLE_HEADER_SESSION_USERNAME' | translate}} + + {{'MANAGE_SESSION.TABLE_HEADER_SESSION_STARTDATE' | translate}} + + {{'MANAGE_SESSION.TABLE_HEADER_SESSION_REMOTEHOST' | translate}} + + {{'MANAGE_SESSION.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}} +
+ + {{wrapper.activeConnection.username}}{{wrapper.startDate}}{{wrapper.activeConnection.remoteHost}}{{wrapper.name}}
+ + +

+ {{'MANAGE_SESSION.INFO_NO_SESSIONS' | translate}} +

+ + + +
diff --git a/guacamole/src/main/webapp/app/settings/templates/settings.html b/guacamole/src/main/webapp/app/settings/templates/settings.html index 8edb4d4bf..4caefed64 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settings.html +++ b/guacamole/src/main/webapp/app/settings/templates/settings.html @@ -35,5 +35,6 @@ THE SOFTWARE. + diff --git a/guacamole/src/main/webapp/app/manage/types/ActiveConnectionWrapper.js b/guacamole/src/main/webapp/app/settings/types/ActiveConnectionWrapper.js similarity index 97% rename from guacamole/src/main/webapp/app/manage/types/ActiveConnectionWrapper.js rename to guacamole/src/main/webapp/app/settings/types/ActiveConnectionWrapper.js index cb2c43e3d..85ffe02e7 100644 --- a/guacamole/src/main/webapp/app/manage/types/ActiveConnectionWrapper.js +++ b/guacamole/src/main/webapp/app/settings/types/ActiveConnectionWrapper.js @@ -23,7 +23,7 @@ /** * A service for defining the ActiveConnectionWrapper class. */ -angular.module('manage').factory('ActiveConnectionWrapper', [ +angular.module('settings').factory('ActiveConnectionWrapper', [ function defineActiveConnectionWrapper() { /** From 419949787f6f7458e813ee19a53d0f8b90d3b22e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 17:06:42 -0700 Subject: [PATCH 07/13] GUAC-1053: Give active sessions tab priority. --- .../app/navigation/services/userPageService.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index e6606dfd0..fbd8b9fd7 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -198,6 +198,14 @@ angular.module('navigation').factory('userPageService', ['$injector', // A user must be a system administrator to manage sessions PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER); + + // If user can manage sessions, add link to sessions management page + if (canManageSessions) { + pages.push(new Page( + 'USER_MENU.ACTION_MANAGE_SESSIONS', + '/settings/sessions' + )); + } // If user can manage users, add link to user management page if (canManageUsers) { @@ -214,15 +222,7 @@ angular.module('navigation').factory('userPageService', ['$injector', '/settings/connections' )); } - - // If user can manage sessions, add link to sessions management page - if (canManageSessions) { - pages.push(new Page( - 'USER_MENU.ACTION_MANAGE_SESSIONS', - '/settings/sessions' - )); - } - + return pages; }; From 32e51bd812a9bfd71b57a32b4404286ad261e909 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 20:46:18 -0700 Subject: [PATCH 08/13] GUAC-1053: Clean up comment wrapping, now that everything is two levels deeper. --- .../settings/directives/manageConnections.js | 21 +++--- .../app/settings/directives/manageSessions.js | 65 ++++++++++--------- .../app/settings/directives/manageUsers.js | 24 +++---- 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/guacamole/src/main/webapp/app/settings/directives/manageConnections.js b/guacamole/src/main/webapp/app/settings/directives/manageConnections.js index 1df0f8cac..1be07b656 100644 --- a/guacamole/src/main/webapp/app/settings/directives/manageConnections.js +++ b/guacamole/src/main/webapp/app/settings/directives/manageConnections.js @@ -51,8 +51,8 @@ angular.module('settings').directive('guacManageConnections', [function guacMana var currentUserID = authenticationService.getCurrentUserID(); /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. + * An action to be provided along with the object sent to + * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE", @@ -78,24 +78,25 @@ angular.module('settings').directive('guacManageConnections', [function guacMana $scope.canManageConnections = null; /** - * Whether the current user can create new connections. If the current - * permissions have not yet been loaded, this will be 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. + * 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; /** - * All permissions associated with the current user, or null if the user's - * permissions have not yet been loaded. + * All permissions associated with the current user, or null if the + * user's permissions have not yet been loaded. * * @type PermissionSet */ @@ -105,8 +106,8 @@ angular.module('settings').directive('guacManageConnections', [function guacMana * 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. + * true if enough data has been loaded for the user interface + * to be useful, false otherwise. */ $scope.isLoaded = function isLoaded() { diff --git a/guacamole/src/main/webapp/app/settings/directives/manageSessions.js b/guacamole/src/main/webapp/app/settings/directives/manageSessions.js index fd053acf9..7375f5dba 100644 --- a/guacamole/src/main/webapp/app/settings/directives/manageSessions.js +++ b/guacamole/src/main/webapp/app/settings/directives/manageSessions.js @@ -52,16 +52,17 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS var permissionService = $injector.get('permissionService'); /** - * All permissions associated with the current user, or null if the user's - * permissions have not yet been loaded. + * All permissions associated with the current user, or null if the + * user's permissions have not yet been loaded. * * @type PermissionSet */ $scope.permissions = null; /** - * The ActiveConnectionWrappers of all active sessions accessible by the - * current user, or null if the active sessions have not yet been loaded. + * The ActiveConnectionWrappers of all active sessions accessible + * by the current user, or null if the active sessions have not yet + * been loaded. * * @type ActiveConnectionWrapper[] */ @@ -93,16 +94,16 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS ]; /** - * All active connections, if known, or null if active connections have not - * yet been loaded. + * All active connections, if known, or null if active connections + * have not yet been loaded. * * @type ActiveConnection */ var activeConnections = null; /** - * Map of all visible connections by object identifier, or null if visible - * connections have not yet been loaded. + * Map of all visible connections by object identifier, or null if + * visible connections have not yet been loaded. * * @type Object. */ @@ -116,7 +117,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS var sessionDateFormat = null; /** - * Map of all currently-selected active connection wrappers by identifier. + * Map of all currently-selected active connection wrappers by + * identifier. * * @type Object. */ @@ -127,7 +129,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS * connections. * * @param {Connection} connection - * The connection to add to the internal set of visible connections. + * The connection to add to the internal set of visible + * connections. */ var addConnection = function addConnection(connection) { @@ -137,12 +140,12 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * Adds all descendant connections of the given connection group to the - * internal set of connections. + * Adds all descendant connections of the given connection group to + * the internal set of connections. * * @param {ConnectionGroup} connectionGroup - * The connection group whose descendant connections should be added to - * the internal set of connections. + * The connection group whose descendant connections should be + * added to the internal set of connections. */ var addDescendantConnections = function addDescendantConnections(connectionGroup) { @@ -157,9 +160,9 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * Wraps all loaded active connections, storing the resulting array within - * the scope. If required data has not yet finished loading, this function - * has no effect. + * Wraps all loaded active connections, storing the resulting array + * within the scope. If required data has not yet finished loading, + * this function has no effect. */ var wrapActiveConnections = function wrapActiveConnections() { @@ -229,8 +232,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS * 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. + * true if enough data has been loaded for the user interface + * to be useful, false otherwise. */ $scope.isLoaded = function isLoaded() { @@ -241,8 +244,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. + * An action to be provided along with the object sent to + * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { name : "MANAGE_SESSION.ACTION_ACKNOWLEDGE", @@ -253,8 +256,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. + * An action to be provided along with the object sent to + * showStatus which closes the currently-shown status dialog. */ var CANCEL_ACTION = { name : "MANAGE_SESSION.ACTION_CANCEL", @@ -265,8 +268,9 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * An action to be provided along with the object sent to showStatus which - * immediately deletes the currently selected sessions. + * An action to be provided along with the object sent to + * showStatus which immediately deletes the currently selected + * sessions. */ var DELETE_ACTION = { name : "MANAGE_SESSION.ACTION_DELETE", @@ -279,8 +283,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * Immediately deletes the selected sessions, without prompting the user for - * confirmation. + * Immediately deletes the selected sessions, without prompting the + * user for confirmation. */ var deleteSessionsImmediately = function deleteSessionsImmediately() { @@ -311,8 +315,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * Delete all selected sessions, prompting the user first to confirm that - * deletion is desired. + * Delete all selected sessions, prompting the user first to + * confirm that deletion is desired. */ $scope.deleteSessions = function deleteSessions() { // Confirm deletion request @@ -340,7 +344,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }; /** - * Called whenever an active connection wrapper changes selected status. + * Called whenever an active connection wrapper changes selected + * status. * * @param {ActiveConnectionWrapper} wrapper * The wrapper whose selected status has changed. diff --git a/guacamole/src/main/webapp/app/settings/directives/manageUsers.js b/guacamole/src/main/webapp/app/settings/directives/manageUsers.js index 86b775cd7..093302a34 100644 --- a/guacamole/src/main/webapp/app/settings/directives/manageUsers.js +++ b/guacamole/src/main/webapp/app/settings/directives/manageUsers.js @@ -51,8 +51,8 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser var currentUserID = authenticationService.getCurrentUserID(); /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. + * An action to be provided along with the object sent to + * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { name : "MANAGE_USER.ACTION_ACKNOWLEDGE", @@ -70,8 +70,8 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser $scope.users = null; /** - * Whether the current user can manage users. If the current permissions - * have not yet been loaded, this will be null. + * Whether the current user can manage users. If the current + * permissions have not yet been loaded, this will be null. * * @type Boolean */ @@ -86,16 +86,16 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser $scope.canCreateUsers = null; /** - * The name of the new user to create, if any, when user creation is - * requested via newUser(). + * The name of the new user to create, if any, when user creation + * is requested via newUser(). * * @type String */ $scope.newUsername = ""; /** - * All permissions associated with the current user, or null if the user's - * permissions have not yet been loaded. + * All permissions associated with the current user, or null if the + * user's permissions have not yet been loaded. * * @type PermissionSet */ @@ -105,8 +105,8 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser * 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. + * true if enough data has been loaded for the user interface + * to be useful, false otherwise. */ $scope.isLoaded = function isLoaded() { @@ -153,8 +153,8 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser }); /** - * Creates a new user having the username specified in the user creation - * interface. + * Creates a new user having the username specified in the user + * creation interface. */ $scope.newUser = function newUser() { From e3210fc64ecf65d7fd3cdd0190eddc1d1bc1e29f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 21:08:34 -0700 Subject: [PATCH 09/13] GUAC-1053: Move settings-specific things into own namespace. --- ...nections.js => guacSettingsConnections.js} | 8 +- ...ageSessions.js => guacSettingsSessions.js} | 21 ++-- .../{manageUsers.js => guacSettingsUsers.js} | 10 +- .../app/settings/templates/connection.html | 2 +- .../app/settings/templates/settings.html | 8 +- ...nections.html => settingsConnections.html} | 6 +- ...ageSessions.html => settingsSessions.html} | 16 +-- .../{manageUsers.html => settingsUsers.html} | 4 +- .../src/main/webapp/translations/en_US.json | 107 +++++++++++------- 9 files changed, 100 insertions(+), 82 deletions(-) rename guacamole/src/main/webapp/app/settings/directives/{manageConnections.js => guacSettingsConnections.js} (95%) rename guacamole/src/main/webapp/app/settings/directives/{manageSessions.js => guacSettingsSessions.js} (94%) rename guacamole/src/main/webapp/app/settings/directives/{manageUsers.js => guacSettingsUsers.js} (94%) rename guacamole/src/main/webapp/app/settings/templates/{manageConnections.html => settingsConnections.html} (86%) rename guacamole/src/main/webapp/app/settings/templates/{manageSessions.html => settingsSessions.html} (81%) rename guacamole/src/main/webapp/app/settings/templates/{manageUsers.html => settingsUsers.html} (92%) diff --git a/guacamole/src/main/webapp/app/settings/directives/manageConnections.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js similarity index 95% rename from guacamole/src/main/webapp/app/settings/directives/manageConnections.js rename to guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js index 1be07b656..a900aed11 100644 --- a/guacamole/src/main/webapp/app/settings/directives/manageConnections.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js @@ -23,7 +23,7 @@ /** * A directive for managing all connections and connection groups in the system. */ -angular.module('settings').directive('guacManageConnections', [function guacManageConnections() { +angular.module('settings').directive('guacSettingsConnections', [function guacSettingsConnections() { return { // Element only @@ -33,8 +33,8 @@ angular.module('settings').directive('guacManageConnections', [function guacMana scope: { }, - templateUrl: 'app/settings/templates/manageConnections.html', - controller: ['$scope', '$injector', function manageConnectionsController($scope, $injector) { + templateUrl: 'app/settings/templates/settingsConnections.html', + controller: ['$scope', '$injector', function settingsConnectionsController($scope, $injector) { // Required types var ConnectionGroup = $injector.get('ConnectionGroup'); @@ -55,7 +55,7 @@ angular.module('settings').directive('guacManageConnections', [function guacMana * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { - name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE", + name : "SETTINGS_CONNECTIONS.ACTION_ACKNOWLEDGE", // Handle action callback : function acknowledgeCallback() { guacNotification.showStatus(false); diff --git a/guacamole/src/main/webapp/app/settings/directives/manageSessions.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js similarity index 94% rename from guacamole/src/main/webapp/app/settings/directives/manageSessions.js rename to guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js index 7375f5dba..e478fbd69 100644 --- a/guacamole/src/main/webapp/app/settings/directives/manageSessions.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js @@ -23,7 +23,7 @@ /** * A directive for managing all active Guacamole sessions. */ -angular.module('settings').directive('guacManageSessions', [function guacManageSessions() { +angular.module('settings').directive('guacSettingsSessions', [function guacSettingsSessions() { return { // Element only @@ -33,9 +33,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS scope: { }, - templateUrl: 'app/settings/templates/manageSessions.html', - controller: ['$scope', '$injector', - function manageSessionsController($scope, $injector) { + templateUrl: 'app/settings/templates/settingsSessions.html', + controller: ['$scope', '$injector', function settingsSessionsController($scope, $injector) { // Required types var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper'); @@ -218,7 +217,7 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS }); // Get session date format - $translate('MANAGE_SESSION.FORMAT_STARTDATE').then(function sessionDateFormatReceived(retrievedSessionDateFormat) { + $translate('SETTINGS_SESSIONS.FORMAT_STARTDATE').then(function sessionDateFormatReceived(retrievedSessionDateFormat) { // Store received date format sessionDateFormat = retrievedSessionDateFormat; @@ -248,7 +247,7 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { - name : "MANAGE_SESSION.ACTION_ACKNOWLEDGE", + name : "SETTINGS_SESSIONS.ACTION_ACKNOWLEDGE", // Handle action callback : function acknowledgeCallback() { guacNotification.showStatus(false); @@ -260,7 +259,7 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS * showStatus which closes the currently-shown status dialog. */ var CANCEL_ACTION = { - name : "MANAGE_SESSION.ACTION_CANCEL", + name : "SETTINGS_SESSIONS.ACTION_CANCEL", // Handle action callback : function cancelCallback() { guacNotification.showStatus(false); @@ -273,7 +272,7 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS * sessions. */ var DELETE_ACTION = { - name : "MANAGE_SESSION.ACTION_DELETE", + name : "SETTINGS_SESSIONS.ACTION_DELETE", className : "danger", // Handle action callback : function deleteCallback() { @@ -306,7 +305,7 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS .error(function activeConnectionDeletionFailed(error) { guacNotification.showStatus({ 'className' : 'error', - 'title' : 'MANAGE_SESSION.DIALOG_HEADER_ERROR', + 'title' : 'SETTINGS_SESSIONS.DIALOG_HEADER_ERROR', 'text' : error.message, 'actions' : [ ACKNOWLEDGE_ACTION ] }); @@ -321,8 +320,8 @@ angular.module('settings').directive('guacManageSessions', [function guacManageS $scope.deleteSessions = function deleteSessions() { // Confirm deletion request guacNotification.showStatus({ - 'title' : 'MANAGE_SESSION.DIALOG_HEADER_CONFIRM_DELETE', - 'text' : 'MANAGE_SESSION.TEXT_CONFIRM_DELETE', + 'title' : 'SETTINGS_SESSIONS.DIALOG_HEADER_CONFIRM_DELETE', + 'text' : 'SETTINGS_SESSIONS.TEXT_CONFIRM_DELETE', 'actions' : [ DELETE_ACTION, CANCEL_ACTION] }); }; diff --git a/guacamole/src/main/webapp/app/settings/directives/manageUsers.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js similarity index 94% rename from guacamole/src/main/webapp/app/settings/directives/manageUsers.js rename to guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js index 093302a34..20c877b46 100644 --- a/guacamole/src/main/webapp/app/settings/directives/manageUsers.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js @@ -23,7 +23,7 @@ /** * A directive for managing all users in the system. */ -angular.module('settings').directive('guacManageUsers', [function guacManageUsers() { +angular.module('settings').directive('guacSettingsUsers', [function guacSettingsUsers() { return { // Element only @@ -33,8 +33,8 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser scope: { }, - templateUrl: 'app/settings/templates/manageUsers.html', - controller: ['$scope', '$injector', function manageUsersController($scope, $injector) { + templateUrl: 'app/settings/templates/settingsUsers.html', + controller: ['$scope', '$injector', function settingsUsersController($scope, $injector) { // Required types var PermissionSet = $injector.get('PermissionSet'); @@ -55,7 +55,7 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser * showStatus which closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { - name : "MANAGE_USER.ACTION_ACKNOWLEDGE", + name : "SETTINGS_USERS.ACTION_ACKNOWLEDGE", // Handle action callback : function acknowledgeCallback() { guacNotification.showStatus(false); @@ -175,7 +175,7 @@ angular.module('settings').directive('guacManageUsers', [function guacManageUser .error(function userCreationFailed(error) { guacNotification.showStatus({ 'className' : 'error', - 'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR', + 'title' : 'SETTINGS_USERS.DIALOG_HEADER_ERROR', 'text' : error.message, 'actions' : [ ACKNOWLEDGE_ACTION ] }); diff --git a/guacamole/src/main/webapp/app/settings/templates/connection.html b/guacamole/src/main/webapp/app/settings/templates/connection.html index b96e3dfa7..97960a2dd 100644 --- a/guacamole/src/main/webapp/app/settings/templates/connection.html +++ b/guacamole/src/main/webapp/app/settings/templates/connection.html @@ -33,7 +33,7 @@ - {{'MANAGE_CONNECTION.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.getActiveConnections()}'}} + {{'SETTINGS_CONNECTIONS.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.getActiveConnections()}'}} diff --git a/guacamole/src/main/webapp/app/settings/templates/settings.html b/guacamole/src/main/webapp/app/settings/templates/settings.html index 4caefed64..59ac3d35e 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settings.html +++ b/guacamole/src/main/webapp/app/settings/templates/settings.html @@ -23,7 +23,7 @@ THE SOFTWARE.
-

{{'MANAGE_SETTINGS.SECTION_HEADER_MANAGE_SETTINGS' | translate}}

+

{{'SETTINGS.SECTION_HEADER_SETTINGS' | translate}}

@@ -33,8 +33,8 @@ THE SOFTWARE.
- - - + + + diff --git a/guacamole/src/main/webapp/app/settings/templates/manageConnections.html b/guacamole/src/main/webapp/app/settings/templates/settingsConnections.html similarity index 86% rename from guacamole/src/main/webapp/app/settings/templates/manageConnections.html rename to guacamole/src/main/webapp/app/settings/templates/settingsConnections.html index 444865eca..38860b259 100644 --- a/guacamole/src/main/webapp/app/settings/templates/manageConnections.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsConnections.html @@ -22,18 +22,18 @@ --> -

{{'MANAGE_CONNECTION.HELP_CONNECTIONS' | translate}}

+

{{'SETTINGS_CONNECTIONS.HELP_CONNECTIONS' | translate}}

{{'MANAGE_CONNECTION.ACTION_NEW_CONNECTION' | translate}} + href="#/manage/connections/">{{'SETTINGS_CONNECTIONS.ACTION_NEW_CONNECTION' | translate}} {{'MANAGE_CONNECTION.ACTION_NEW_CONNECTION_GROUP' | translate}} + href="#/manage/connectionGroups/">{{'SETTINGS_CONNECTIONS.ACTION_NEW_CONNECTION_GROUP' | translate}}
diff --git a/guacamole/src/main/webapp/app/settings/templates/manageSessions.html b/guacamole/src/main/webapp/app/settings/templates/settingsSessions.html similarity index 81% rename from guacamole/src/main/webapp/app/settings/templates/manageSessions.html rename to guacamole/src/main/webapp/app/settings/templates/settingsSessions.html index 3c1b1aff1..405c9d332 100644 --- a/guacamole/src/main/webapp/app/settings/templates/manageSessions.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsSessions.html @@ -22,16 +22,16 @@ --> -

{{'MANAGE_SESSION.HELP_SESSIONS' | translate}}

+

{{'SETTINGS_SESSIONS.HELP_SESSIONS' | translate}}

- +
@@ -40,16 +40,16 @@ - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_USERNAME' | translate}} + {{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_USERNAME' | translate}} - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_STARTDATE' | translate}} + {{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_STARTDATE' | translate}} - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_REMOTEHOST' | translate}} + {{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_REMOTEHOST' | translate}} - {{'MANAGE_SESSION.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}} + {{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}} @@ -68,7 +68,7 @@

- {{'MANAGE_SESSION.INFO_NO_SESSIONS' | translate}} + {{'SETTINGS_SESSIONS.INFO_NO_SESSIONS' | translate}}

diff --git a/guacamole/src/main/webapp/app/settings/templates/manageUsers.html b/guacamole/src/main/webapp/app/settings/templates/settingsUsers.html similarity index 92% rename from guacamole/src/main/webapp/app/settings/templates/manageUsers.html rename to guacamole/src/main/webapp/app/settings/templates/settingsUsers.html index 2ee778b21..79290cb07 100644 --- a/guacamole/src/main/webapp/app/settings/templates/manageUsers.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsUsers.html @@ -22,12 +22,12 @@ --> -

{{'MANAGE_USER.HELP_USERS' | translate}}

+

{{'SETTINGS_USERS.HELP_USERS' | translate}}

- +
diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index 1c4a69183..ddd336aea 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -155,8 +155,6 @@ "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_NEW_CONNECTION" : "New Connection", - "ACTION_NEW_CONNECTION_GROUP" : "New Group", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection", @@ -168,14 +166,10 @@ "FORMAT_HISTORY_START" : "@:APP.FORMAT_DATE_TIME_PRECISE", - "HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.", - - "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", "INFO_CONNECTION_DURATION_UNKNOWN" : "--", "INFO_CONNECTION_ACTIVE_NOW" : "Active Now", "INFO_CONNECTION_NOT_USED" : "This connection has not yet been used.", - "SECTION_HEADER_CONNECTIONS" : "Manage Connections", "SECTION_HEADER_EDIT_CONNECTION" : "Edit Connection", "SECTION_HEADER_HISTORY" : "Usage History", "SECTION_HEADER_PARAMETERS" : "Parameters", @@ -217,7 +211,6 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_NEW_USER" : "New User", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete User", @@ -234,51 +227,14 @@ "FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN", "FIELD_HEADER_USERNAME" : "Username:", - "HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.", - "SECTION_HEADER_CONNECTIONS" : "Connections", "SECTION_HEADER_EDIT_USER" : "Edit User", "SECTION_HEADER_PERMISSIONS" : "Permissions", - "SECTION_HEADER_USERS" : "Users", "TEXT_CONFIRM_DELETE" : "Users cannot be restored after they have been deleted. Are you sure you want to delete this user?" }, - "MANAGE_SESSION" : { - - "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", - "ACTION_DELETE" : "Kill Sessions", - - "DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions", - "DIALOG_HEADER_ERROR" : "Error", - - "FIELD_PLACEHOLDER_FILTER" : "Filter", - - "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", - - "HELP_SESSIONS" : "All currently-active Guacamole sessions are listed here. If you wish to kill one or more sessions, check the box next to those sessions and click \"Kill Sessions\". Killing a session will immediately disconnect the user from the associated connection.", - - "INFO_NO_SESSIONS" : "No active sessions", - - "SECTION_HEADER_SESSIONS" : "Active Sessions", - - "TABLE_HEADER_SESSION_USERNAME" : "Username", - "TABLE_HEADER_SESSION_STARTDATE" : "Active since", - "TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host", - "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", - - "TEXT_CONFIRM_DELETE" : "Are you sure you want to kill all selected sessions? The users using these sessions will be immediately disconnected." - - }, - - "MANAGE_SETTINGS" : { - - "SECTION_HEADER_MANAGE_SETTINGS" : "Settings" - - }, - "PROTOCOL_RDP" : { "FIELD_HEADER_COLOR_DEPTH" : "Color depth:", @@ -421,6 +377,69 @@ }, + "SETTINGS" : { + + "SECTION_HEADER_SETTINGS" : "Settings" + + }, + + "SETTINGS_CONNECTIONS" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_NEW_CONNECTION" : "New Connection", + "ACTION_NEW_CONNECTION_GROUP" : "New Group", + + "DIALOG_HEADER_ERROR" : "Error", + + "HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.", + + "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", + + "SECTION_HEADER_CONNECTIONS" : "Manage Connections" + + }, + + "SETTINGS_USERS" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_NEW_USER" : "New User", + + "DIALOG_HEADER_ERROR" : "Error", + + "HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.", + + "SECTION_HEADER_USERS" : "Users" + + }, + + "SETTINGS_SESSIONS" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_DELETE" : "Kill Sessions", + + "DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions", + "DIALOG_HEADER_ERROR" : "Error", + + "FIELD_PLACEHOLDER_FILTER" : "Filter", + + "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", + + "HELP_SESSIONS" : "All currently-active Guacamole sessions are listed here. If you wish to kill one or more sessions, check the box next to those sessions and click \"Kill Sessions\". Killing a session will immediately disconnect the user from the associated connection.", + + "INFO_NO_SESSIONS" : "No active sessions", + + "SECTION_HEADER_SESSIONS" : "Active Sessions", + + "TABLE_HEADER_SESSION_USERNAME" : "Username", + "TABLE_HEADER_SESSION_STARTDATE" : "Active since", + "TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host", + "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", + + "TEXT_CONFIRM_DELETE" : "Are you sure you want to kill all selected sessions? The users using these sessions will be immediately disconnected." + + }, + "USER_MENU" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", From 42b99775e88e3eadbad56619e6a6a3b6b16b0da8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 21:12:26 -0700 Subject: [PATCH 10/13] GUAC-1053: Rename "Manage Connections" to "Connections". --- guacamole/src/main/webapp/translations/en_US.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index ddd336aea..c4a00f81f 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -10,7 +10,7 @@ "ACTION_DELETE_SESSIONS" : "Kill Sessions", "ACTION_LOGIN" : "Login", "ACTION_LOGOUT" : "Logout", - "ACTION_MANAGE_CONNECTIONS" : "Manage Connections", + "ACTION_MANAGE_CONNECTIONS" : "Connections", "ACTION_MANAGE_SETTINGS" : "Settings", "ACTION_MANAGE_SESSIONS" : "Active Sessions", "ACTION_MANAGE_USERS" : "Users", @@ -395,7 +395,7 @@ "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", - "SECTION_HEADER_CONNECTIONS" : "Manage Connections" + "SECTION_HEADER_CONNECTIONS" : "Connections" }, From f0d7b6f8673abb199df56e8211e070b3a9778860 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 21:15:37 -0700 Subject: [PATCH 11/13] GUAC-1053: Distinguish tabs from page with border. --- guacamole/src/main/webapp/app/settings/styles/settings.css | 1 + 1 file changed, 1 insertion(+) diff --git a/guacamole/src/main/webapp/app/settings/styles/settings.css b/guacamole/src/main/webapp/app/settings/styles/settings.css index fe685865a..961fdc5a0 100644 --- a/guacamole/src/main/webapp/app/settings/styles/settings.css +++ b/guacamole/src/main/webapp/app/settings/styles/settings.css @@ -39,6 +39,7 @@ margin: 0; padding: 0; background: rgba(0, 0, 0, 0.0125); + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } .settings-tabs .page-list li { From dbeb68cc1d44d1197f4734639f49278c283cc6c0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 21:23:01 -0700 Subject: [PATCH 12/13] GUAC-1053: Properly handle undefined form/field/option names. --- guacamole/src/main/webapp/app/form/directives/form.js | 8 ++++++++ .../src/main/webapp/app/form/directives/formField.js | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/app/form/directives/form.js b/guacamole/src/main/webapp/app/form/directives/form.js index 8623e6f57..f22b9952b 100644 --- a/guacamole/src/main/webapp/app/form/directives/form.js +++ b/guacamole/src/main/webapp/app/form/directives/form.js @@ -104,6 +104,10 @@ angular.module('form').directive('guacForm', [function form() { */ $scope.getSectionHeader = function getSectionHeader(form) { + // If no form, or no name, then no header + if (!form || !form.name) + return ''; + return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') + '.SECTION_HEADER_' + translationStringService.canonicalize(form.name); @@ -128,6 +132,10 @@ angular.module('form').directive('guacForm', [function form() { */ $scope.getFieldHeader = function getFieldHeader(field) { + // If no field, or no name, then no header + if (!field || !field.name) + return ''; + return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') + '.FIELD_HEADER_' + translationStringService.canonicalize(field.name); diff --git a/guacamole/src/main/webapp/app/form/directives/formField.js b/guacamole/src/main/webapp/app/form/directives/formField.js index bf9b35434..60b440267 100644 --- a/guacamole/src/main/webapp/app/form/directives/formField.js +++ b/guacamole/src/main/webapp/app/form/directives/formField.js @@ -131,8 +131,8 @@ angular.module('form').directive('guacFormField', [function formField() { */ $scope.getFieldOption = function getFieldOption(value) { - // Don't bother if the model is not yet defined - if (!$scope.field) + // If no field, or no value, then no corresponding translation string + if (!$scope.field || !$scope.field.name || !value) return ''; return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') From 9fa5a25aa9446867a7aed3cc2bec298f98973d34 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 Apr 2015 21:38:26 -0700 Subject: [PATCH 13/13] GUAC-1053: Fix styling of connection home pages (when needed). --- .../navigation/services/userPageService.js | 8 +++++--- .../app/navigation/styles/user-menu.css | 20 ++++++++----------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index fbd8b9fd7..2ff396d56 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -268,14 +268,16 @@ angular.module('navigation').factory('userPageService', ['$injector', var pages = []; + // Get home page and settings pages + var homePage = generateHomePage(rootGroup); + var settingsPages = generateSettingsPages(permissions); + // Only include the home page in the list of main pages if the user // can navigate elsewhere. - var homePage = generateHomePage(rootGroup); - if (homePage === SYSTEM_HOME_PAGE) + if (homePage === SYSTEM_HOME_PAGE || settingsPages.length) pages.push(homePage); // Add generic link to the first-available settings page - var settingsPages = generateSettingsPages(permissions); if (settingsPages.length) { pages.push(new Page( 'USER_MENU.ACTION_MANAGE_SETTINGS', 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 b8d162157..b2a649233 100644 --- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css @@ -164,11 +164,19 @@ } .user-menu .options li a { + display: block; cursor: pointer; color: black; text-decoration: none; padding: 0.75em; + + background-repeat: no-repeat; + background-size: 1em; + background-position: 0.75em center; + padding-left: 2.5em; + background-image: url('images/protocol-icons/guac-monitor.png'); + } .user-menu .options li a:hover { @@ -182,18 +190,6 @@ opacity: 0.25; } -.user-menu .options li a.home, -.user-menu .options li a.manage-users, -.user-menu .options li a.manage-connections, -.user-menu .options li a.manage-sessions, -.user-menu .options li a.change-password, -.user-menu .options li a.logout { - background-repeat: no-repeat; - background-size: 1em; - background-position: 0.75em center; - padding-left: 2.5em; -} - .user-menu .options li a[href="#/"] { background-image: url('images/action-icons/guac-home-dark.png'); }