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:",