mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	Merge pull request #150 from glyptodon/preferences
GUAC-1053: Add preferences tab to settings screen
This commit is contained in:
		| @@ -34,6 +34,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | |||||||
|     var $location         = $injector.get('$location'); |     var $location         = $injector.get('$location'); | ||||||
|     var guacClientManager = $injector.get('guacClientManager'); |     var guacClientManager = $injector.get('guacClientManager'); | ||||||
|     var guacNotification  = $injector.get('guacNotification'); |     var guacNotification  = $injector.get('guacNotification'); | ||||||
|  |     var preferenceService = $injector.get('preferenceService'); | ||||||
|     var userPageService   = $injector.get('userPageService'); |     var userPageService   = $injector.get('userPageService'); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -196,7 +197,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | |||||||
|          * |          * | ||||||
|          * @type String |          * @type String | ||||||
|          */ |          */ | ||||||
|         inputMethod : 'none', |         inputMethod : preferenceService.preferences.inputMethod, | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * The current scroll state of the menu. |          * The current scroll state of the menu. | ||||||
|   | |||||||
| @@ -23,7 +23,10 @@ | |||||||
| /** | /** | ||||||
|  * A service for generating new guacClient properties objects. |  * A service for generating new guacClient properties objects. | ||||||
|  */ |  */ | ||||||
| angular.module('client').factory('ClientProperties', [function defineClientProperties() { | angular.module('client').factory('ClientProperties', ['$injector', function defineClientProperties($injector) { | ||||||
|  |  | ||||||
|  |     // Required services | ||||||
|  |     var preferenceService = $injector.get('preferenceService'); | ||||||
|          |          | ||||||
|     /** |     /** | ||||||
|      * Object used for interacting with a guacClient directive. |      * Object used for interacting with a guacClient directive. | ||||||
| @@ -81,7 +84,7 @@ angular.module('client').factory('ClientProperties', [function defineClientPrope | |||||||
|          *  |          *  | ||||||
|          * @type Boolean |          * @type Boolean | ||||||
|          */ |          */ | ||||||
|         this.emulateAbsoluteMouse = template.emulateAbsoluteMouse || true; |         this.emulateAbsoluteMouse = template.emulateAbsoluteMouse || preferenceService.preferences.emulateAbsoluteMouse; | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * The relative Y coordinate of the scroll offset of the display within |          * The relative Y coordinate of the scroll offset of the display within | ||||||
|   | |||||||
| @@ -79,29 +79,35 @@ input[type="submit"]:disabled, button:disabled, button.danger:disabled { | |||||||
|     opacity: 0.75; |     opacity: 0.75; | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.logout, .button.manage, .button.back, .button.home, .button.change-password { | .button.logout, .button.manage, .button.back, .button.home, .button.change-password, | ||||||
|  | button.logout, button.manage, button.back, button.home, button.change-password { | ||||||
|     background-repeat: no-repeat; |     background-repeat: no-repeat; | ||||||
|     background-size: 1em; |     background-size: 1em; | ||||||
|     background-position: 0.5em 0.45em; |     background-position: 0.5em 0.45em; | ||||||
|     padding-left: 1.8em; |     padding-left: 1.8em; | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.logout { | .button.logout, | ||||||
|  | button.logout { | ||||||
|     background-image: url('images/action-icons/guac-logout.png'); |     background-image: url('images/action-icons/guac-logout.png'); | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.manage { | .button.manage, | ||||||
|  | button.manage { | ||||||
|     background-image: url('images/action-icons/guac-config.png'); |     background-image: url('images/action-icons/guac-config.png'); | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.back { | .button.back, | ||||||
|  | button.back { | ||||||
|     background-image: url('images/action-icons/guac-back.png'); |     background-image: url('images/action-icons/guac-back.png'); | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.home { | .button.home, | ||||||
|  | button.home { | ||||||
|     background-image: url('images/action-icons/guac-home.png'); |     background-image: url('images/action-icons/guac-home.png'); | ||||||
| } | } | ||||||
|  |  | ||||||
| .button.change-password { | .button.change-password, | ||||||
|  | button.change-password { | ||||||
|     background-image: url('images/action-icons/guac-key.png'); |     background-image: url('images/action-icons/guac-key.png'); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -96,6 +96,7 @@ h2 { | |||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .header ~ * .header, | ||||||
| .header ~ .header { | .header ~ .header { | ||||||
|     margin-top: 1em; |     margin-top: 1em; | ||||||
|     border-top: 1px solid rgba(0, 0, 0, 0.125); |     border-top: 1px solid rgba(0, 0, 0, 0.125); | ||||||
|   | |||||||
| @@ -45,30 +45,12 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() | |||||||
|         templateUrl: 'app/navigation/templates/guacUserMenu.html', |         templateUrl: 'app/navigation/templates/guacUserMenu.html', | ||||||
|         controller: ['$scope', '$injector', '$element', function guacUserMenuController($scope, $injector, $element) { |         controller: ['$scope', '$injector', '$element', function guacUserMenuController($scope, $injector, $element) { | ||||||
|  |  | ||||||
|             // Get required types |  | ||||||
|             var PermissionSet = $injector.get('PermissionSet'); |  | ||||||
|              |  | ||||||
|             // Get required services |             // Get required services | ||||||
|             var $document             = $injector.get('$document'); |             var $document             = $injector.get('$document'); | ||||||
|             var $location             = $injector.get('$location'); |             var $location             = $injector.get('$location'); | ||||||
|             var authenticationService = $injector.get('authenticationService'); |             var authenticationService = $injector.get('authenticationService'); | ||||||
|             var guacNotification      = $injector.get('guacNotification'); |  | ||||||
|             var permissionService     = $injector.get("permissionService"); |  | ||||||
|             var userService           = $injector.get('userService'); |  | ||||||
|             var userPageService       = $injector.get('userPageService'); |             var userPageService       = $injector.get('userPageService'); | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * An action to be provided along with the object sent to |  | ||||||
|              * showStatus which closes the currently-shown status dialog. |  | ||||||
|              */ |  | ||||||
|             var ACKNOWLEDGE_ACTION = { |  | ||||||
|                 name        : 'USER_MENU.ACTION_ACKNOWLEDGE', |  | ||||||
|                 // Handle action |  | ||||||
|                 callback    : function acknowledgeCallback() { |  | ||||||
|                     guacNotification.showStatus(false); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             /** |             /** | ||||||
|              * The outermost element of the user menu directive. |              * The outermost element of the user menu directive. | ||||||
|              * |              * | ||||||
| @@ -83,28 +65,6 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() | |||||||
|              */ |              */ | ||||||
|             var document = $document[0]; |             var document = $document[0]; | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * Whether the password edit dialog should be shown. |  | ||||||
|              * |  | ||||||
|              * @type Boolean |  | ||||||
|              */ |  | ||||||
|             $scope.showPasswordDialog = false; |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * The new password for the user. |  | ||||||
|              * |  | ||||||
|              * @type String |  | ||||||
|              */ |  | ||||||
|             $scope.newPassword = null; |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * The password match for the user. The update password action will |  | ||||||
|              * fail if $scope.newPassword !== $scope.passwordMatch. |  | ||||||
|              * |  | ||||||
|              * @type String |  | ||||||
|              */ |  | ||||||
|             $scope.newPasswordMatch = null; |  | ||||||
|  |  | ||||||
|             /** |             /** | ||||||
|              * Whether the contents of the user menu are currently shown. |              * Whether the contents of the user menu are currently shown. | ||||||
|              * |              * | ||||||
| @@ -139,81 +99,6 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() | |||||||
|                 $scope.menuShown = !$scope.menuShown; |                 $scope.menuShown = !$scope.menuShown; | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * Show the password update dialog. |  | ||||||
|              */ |  | ||||||
|             $scope.showPasswordUpdate = function showPasswordUpdate() { |  | ||||||
|                  |  | ||||||
|                 // Show the dialog |  | ||||||
|                 $scope.showPasswordDialog = true; |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             /** |  | ||||||
|              * Close the password update dialog. |  | ||||||
|              */ |  | ||||||
|             $scope.closePasswordUpdate = function closePasswordUpdate() { |  | ||||||
|                  |  | ||||||
|                 // Clear the password fields and close the dialog |  | ||||||
|                 $scope.oldPassword        = null; |  | ||||||
|                 $scope.newPassword        = null; |  | ||||||
|                 $scope.newPasswordMatch   = null; |  | ||||||
|                 $scope.showPasswordDialog = false; |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             /** |  | ||||||
|              * Update the current user's password to the password currently set within |  | ||||||
|              * the password change dialog. |  | ||||||
|              */ |  | ||||||
|             $scope.updatePassword = function updatePassword() { |  | ||||||
|  |  | ||||||
|                 // Verify passwords match |  | ||||||
|                 if ($scope.newPasswordMatch !== $scope.newPassword) { |  | ||||||
|                     guacNotification.showStatus({ |  | ||||||
|                         className  : 'error', |  | ||||||
|                         title      : 'USER_MENU.DIALOG_HEADER_ERROR', |  | ||||||
|                         text       : 'USER_MENU.ERROR_PASSWORD_MISMATCH', |  | ||||||
|                         actions    : [ ACKNOWLEDGE_ACTION ] |  | ||||||
|                     }); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 // Verify that the new password is not blank |  | ||||||
|                 if (!$scope.newPassword) { |  | ||||||
|                     guacNotification.showStatus({ |  | ||||||
|                         className  : 'error', |  | ||||||
|                         title      : 'USER_MENU.DIALOG_HEADER_ERROR', |  | ||||||
|                         text       : 'USER_MENU.ERROR_PASSWORD_BLANK', |  | ||||||
|                         actions    : [ ACKNOWLEDGE_ACTION ] |  | ||||||
|                     }); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 // Save the user with the new password |  | ||||||
|                 userService.updateUserPassword($scope.username, $scope.oldPassword, $scope.newPassword) |  | ||||||
|                 .success(function passwordUpdated() { |  | ||||||
|                  |  | ||||||
|                     // Close the password update dialog |  | ||||||
|                     $scope.closePasswordUpdate(); |  | ||||||
|  |  | ||||||
|                     // Indicate that the password has been changed |  | ||||||
|                     guacNotification.showStatus({ |  | ||||||
|                         text    : 'USER_MENU.PASSWORD_CHANGED', |  | ||||||
|                         actions : [ ACKNOWLEDGE_ACTION ] |  | ||||||
|                     }); |  | ||||||
|                 }) |  | ||||||
|                  |  | ||||||
|                 // Notify of any errors |  | ||||||
|                 .error(function passwordUpdateFailed(error) { |  | ||||||
|                     guacNotification.showStatus({ |  | ||||||
|                         className  : 'error', |  | ||||||
|                         title      : 'USER_MENU.DIALOG_HEADER_ERROR', |  | ||||||
|                         'text'       : error.message, |  | ||||||
|                         actions    : [ ACKNOWLEDGE_ACTION ] |  | ||||||
|                     }); |  | ||||||
|                 }); |  | ||||||
|                  |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             /** |             /** | ||||||
|              * Logs out the current user, redirecting them to back to the login |              * Logs out the current user, redirecting them to back to the login | ||||||
|              * screen after logout completes. |              * screen after logout completes. | ||||||
| @@ -234,34 +119,11 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() | |||||||
|                 callback  : $scope.logout |                 callback  : $scope.logout | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * Action which shows the password update dialog. |  | ||||||
|              */ |  | ||||||
|             var CHANGE_PASSWORD_ACTION = { |  | ||||||
|                 name      : 'USER_MENU.ACTION_CHANGE_PASSWORD', |  | ||||||
|                 className : 'change-password', |  | ||||||
|                 callback  : $scope.showPasswordUpdate |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             /** |             /** | ||||||
|              * All available actions for the current user. |              * All available actions for the current user. | ||||||
|              */ |              */ | ||||||
|             $scope.actions = [ LOGOUT_ACTION ]; |             $scope.actions = [ LOGOUT_ACTION ]; | ||||||
|  |  | ||||||
|             // Retrieve current permissions |  | ||||||
|             permissionService.getPermissions(authenticationService.getCurrentUserID()) |  | ||||||
|             .success(function permissionsRetrieved(permissions) { |  | ||||||
|  |  | ||||||
|                 // Add action for changing password if permission is granted |  | ||||||
|                 if (PermissionSet.hasUserPermission(permissions, |  | ||||||
|                         PermissionSet.ObjectPermissionType.UPDATE,  |  | ||||||
|                         authenticationService.getCurrentUserID())) |  | ||||||
|                     $scope.actions.unshift(CHANGE_PASSWORD_ACTION); |  | ||||||
|                          |  | ||||||
|  |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|             // Close menu when use clicks anywhere else |             // Close menu when use clicks anywhere else | ||||||
|             document.body.addEventListener('click', function clickOutsideMenu() { |             document.body.addEventListener('click', function clickOutsideMenu() { | ||||||
|                 $scope.$apply(function closeMenu() { |                 $scope.$apply(function closeMenu() { | ||||||
|   | |||||||
| @@ -223,6 +223,12 @@ angular.module('navigation').factory('userPageService', ['$injector', | |||||||
|             )); |             )); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // Add link to user preferences (always accessible) | ||||||
|  |         pages.push(new Page( | ||||||
|  |             'USER_MENU.ACTION_MANAGE_PREFERENCES', | ||||||
|  |             '/settings/preferences' | ||||||
|  |         )); | ||||||
|  |  | ||||||
|         return pages; |         return pages; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -196,14 +196,11 @@ | |||||||
|  |  | ||||||
| .user-menu .options li a[href="#/settings/users"], | .user-menu .options li a[href="#/settings/users"], | ||||||
| .user-menu .options li a[href="#/settings/connections"], | .user-menu .options li a[href="#/settings/connections"], | ||||||
| .user-menu .options li a[href="#/settings/sessions"] { | .user-menu .options li a[href="#/settings/sessions"], | ||||||
|  | .user-menu .options li a[href="#/settings/preferences"] { | ||||||
|     background-image: url('images/action-icons/guac-config-dark.png'); |     background-image: url('images/action-icons/guac-config-dark.png'); | ||||||
| } | } | ||||||
|  |  | ||||||
| .user-menu .options li a.change-password { |  | ||||||
|     background-image: url('images/action-icons/guac-key-dark.png'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .user-menu .options li a.logout { | .user-menu .options li a.logout { | ||||||
|     background-image: url('images/action-icons/guac-logout-dark.png'); |     background-image: url('images/action-icons/guac-logout-dark.png'); | ||||||
| } | } | ||||||
| @@ -217,45 +214,3 @@ | |||||||
| .user-menu .options li a.danger:hover { | .user-menu .options li a.danger:hover { | ||||||
|     background-color: #C54; |     background-color: #C54; | ||||||
| } | } | ||||||
|  |  | ||||||
| .user-menu .password-dialog { |  | ||||||
|     visibility: hidden; |  | ||||||
|     opacity: 0; |  | ||||||
|     -webkit-transition: visibility 0.125s, opacity 0.125s; |  | ||||||
|     -moz-transition: visibility 0.125s, opacity 0.125s; |  | ||||||
|     -ms-transition: visibility 0.125s, opacity 0.125s; |  | ||||||
|     -o-transition: visibility 0.125s, opacity 0.125s; |  | ||||||
|     transition: visibility 0.125s, opacity 0.125s; |  | ||||||
|     position: absolute; |  | ||||||
|     background: white; |  | ||||||
|     padding: 1em; |  | ||||||
|     border: 1px solid rgba(0, 0, 0, 0.25); |  | ||||||
|     box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); |  | ||||||
|     margin: 1em; |  | ||||||
|     right: 0; |  | ||||||
|     top: 0; |  | ||||||
|     z-index: 8; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .user-menu .password-dialog .fields { |  | ||||||
|     text-align: right; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .user-menu .password-dialog .action-buttons { |  | ||||||
|     text-align: center; |  | ||||||
|     margin: 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .user-menu .password-dialog.shown { |  | ||||||
|     visibility: visible; |  | ||||||
|     opacity: 1; |  | ||||||
|     -webkit-transition: opacity 0.125s; |  | ||||||
|     -moz-transition: opacity 0.125s; |  | ||||||
|     -ms-transition: opacity 0.125s; |  | ||||||
|     -o-transition: opacity 0.125s; |  | ||||||
|     transition: opacity 0.125s; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .user-menu .password-dialog .fields { |  | ||||||
|     width: 100%; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -52,31 +52,4 @@ | |||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <!-- Password dialog --> |  | ||||||
|     <div class="password-dialog" ng-class="{shown: showPasswordDialog}"> |  | ||||||
|         <!-- Password editor --> |  | ||||||
|         <div class="section"> |  | ||||||
|             <table class="fields"> |  | ||||||
|                 <tr> |  | ||||||
|                     <th>{{'USER_MENU.FIELD_HEADER_PASSWORD_OLD' | translate}}</th> |  | ||||||
|                     <td><input ng-model="oldPassword" type="password" /></td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <th>{{'USER_MENU.FIELD_HEADER_PASSWORD_NEW' | translate}}</th> |  | ||||||
|                     <td><input ng-model="newPassword" type="password" /></td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <th>{{'USER_MENU.FIELD_HEADER_PASSWORD_NEW_AGAIN' | translate}}</th> |  | ||||||
|                     <td><input ng-model="newPasswordMatch" type="password" /></td> |  | ||||||
|                 </tr> |  | ||||||
|             </table> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <!-- Form action buttons --> |  | ||||||
|         <div class="action-buttons"> |  | ||||||
|             <button ng-click="updatePassword()">{{'USER_MENU.ACTION_SAVE' | translate}}</button> |  | ||||||
|             <button ng-click="closePasswordUpdate()">{{'USER_MENU.ACTION_CANCEL' | translate}}</button> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
|  |  | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -46,6 +46,17 @@ angular.module('manage').controller('settingsController', ['$scope', '$injector' | |||||||
|      */ |      */ | ||||||
|     $scope.activeTab = $routeParams.tab; |     $scope.activeTab = $routeParams.tab; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether the list of all available settings tabs should be shown. | ||||||
|  |      * | ||||||
|  |      * @returns {Boolean} | ||||||
|  |      *     true if the list of available settings tabs should be shown, false | ||||||
|  |      *     otherwise. | ||||||
|  |      */ | ||||||
|  |     $scope.showAvailableTabs = function showAvailableTabs() { | ||||||
|  |         return !!$scope.settingsPages && $scope.settingsPages.length > 1; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     // Retrieve settings pages |     // Retrieve settings pages | ||||||
|     userPageService.getSettingsPages() |     userPageService.getSettingsPages() | ||||||
|     .then(function settingsPagesRetrieved(pages) { |     .then(function settingsPagesRetrieved(pages) { | ||||||
|   | |||||||
| @@ -0,0 +1,167 @@ | |||||||
|  | /* | ||||||
|  |  * 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 preferences local to the current user. | ||||||
|  |  */ | ||||||
|  | angular.module('settings').directive('guacSettingsPreferences', [function guacSettingsPreferences() { | ||||||
|  |      | ||||||
|  |     return { | ||||||
|  |         // Element only | ||||||
|  |         restrict: 'E', | ||||||
|  |         replace: true, | ||||||
|  |  | ||||||
|  |         scope: { | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         templateUrl: 'app/settings/templates/settingsPreferences.html', | ||||||
|  |         controller: ['$scope', '$injector', function settingsPreferencesController($scope, $injector) { | ||||||
|  |  | ||||||
|  |             // Get required types | ||||||
|  |             var PermissionSet = $injector.get('PermissionSet'); | ||||||
|  |  | ||||||
|  |             // Required services | ||||||
|  |             var authenticationService = $injector.get('authenticationService'); | ||||||
|  |             var guacNotification      = $injector.get('guacNotification'); | ||||||
|  |             var userService           = $injector.get('userService'); | ||||||
|  |             var permissionService     = $injector.get('permissionService'); | ||||||
|  |             var preferenceService     = $injector.get('preferenceService'); | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * An action to be provided along with the object sent to | ||||||
|  |              * showStatus which closes the currently-shown status dialog. | ||||||
|  |              */ | ||||||
|  |             var ACKNOWLEDGE_ACTION = { | ||||||
|  |                 name        : 'SETTINGS_PREFERENCES.ACTION_ACKNOWLEDGE', | ||||||
|  |                 // Handle action | ||||||
|  |                 callback    : function acknowledgeCallback() { | ||||||
|  |                     guacNotification.showStatus(false); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The username of the current user. | ||||||
|  |              * | ||||||
|  |              * @type String | ||||||
|  |              */ | ||||||
|  |             var username = authenticationService.getCurrentUserID(); | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * All currently-set preferences, or their defaults if not yet set. | ||||||
|  |              * | ||||||
|  |              * @type Object.<String, Object> | ||||||
|  |              */ | ||||||
|  |             $scope.preferences = preferenceService.preferences; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The new password for the user. | ||||||
|  |              * | ||||||
|  |              * @type String | ||||||
|  |              */ | ||||||
|  |             $scope.newPassword = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The password match for the user. The update password action will | ||||||
|  |              * fail if $scope.newPassword !== $scope.passwordMatch. | ||||||
|  |              * | ||||||
|  |              * @type String | ||||||
|  |              */ | ||||||
|  |             $scope.newPasswordMatch = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Whether the current user can change their own password, or null | ||||||
|  |              * if this is not yet known. | ||||||
|  |              * | ||||||
|  |              * @type Boolean | ||||||
|  |              */ | ||||||
|  |             $scope.canChangePassword = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Update the current user's password to the password currently set within | ||||||
|  |              * the password change dialog. | ||||||
|  |              */ | ||||||
|  |             $scope.updatePassword = function updatePassword() { | ||||||
|  |  | ||||||
|  |                 // Verify passwords match | ||||||
|  |                 if ($scope.newPasswordMatch !== $scope.newPassword) { | ||||||
|  |                     guacNotification.showStatus({ | ||||||
|  |                         className  : 'error', | ||||||
|  |                         title      : 'SETTINGS_PREFERENCES.DIALOG_HEADER_ERROR', | ||||||
|  |                         text       : 'SETTINGS_PREFERENCES.ERROR_PASSWORD_MISMATCH', | ||||||
|  |                         actions    : [ ACKNOWLEDGE_ACTION ] | ||||||
|  |                     }); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 // Verify that the new password is not blank | ||||||
|  |                 if (!$scope.newPassword) { | ||||||
|  |                     guacNotification.showStatus({ | ||||||
|  |                         className  : 'error', | ||||||
|  |                         title      : 'SETTINGS_PREFERENCES.DIALOG_HEADER_ERROR', | ||||||
|  |                         text       : 'SETTINGS_PREFERENCES.ERROR_PASSWORD_BLANK', | ||||||
|  |                         actions    : [ ACKNOWLEDGE_ACTION ] | ||||||
|  |                     }); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 // Save the user with the new password | ||||||
|  |                 userService.updateUserPassword(username, $scope.oldPassword, $scope.newPassword) | ||||||
|  |                 .success(function passwordUpdated() { | ||||||
|  |                  | ||||||
|  |                     // Clear the password fields | ||||||
|  |                     $scope.oldPassword      = null; | ||||||
|  |                     $scope.newPassword      = null; | ||||||
|  |                     $scope.newPasswordMatch = null; | ||||||
|  |  | ||||||
|  |                     // Indicate that the password has been changed | ||||||
|  |                     guacNotification.showStatus({ | ||||||
|  |                         text    : 'SETTINGS_PREFERENCES.INFO_PASSWORD_CHANGED', | ||||||
|  |                         actions : [ ACKNOWLEDGE_ACTION ] | ||||||
|  |                     }); | ||||||
|  |                 }) | ||||||
|  |                  | ||||||
|  |                 // Notify of any errors | ||||||
|  |                 .error(function passwordUpdateFailed(error) { | ||||||
|  |                     guacNotification.showStatus({ | ||||||
|  |                         className  : 'error', | ||||||
|  |                         title      : 'SETTINGS_PREFERENCES.DIALOG_HEADER_ERROR', | ||||||
|  |                         'text'       : error.message, | ||||||
|  |                         actions    : [ ACKNOWLEDGE_ACTION ] | ||||||
|  |                     }); | ||||||
|  |                 }); | ||||||
|  |                  | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             // Retrieve current permissions | ||||||
|  |             permissionService.getPermissions(username) | ||||||
|  |             .success(function permissionsRetrieved(permissions) { | ||||||
|  |  | ||||||
|  |                 // Add action for changing password if permission is granted | ||||||
|  |                 $scope.canChangePassword = PermissionSet.hasUserPermission(permissions, | ||||||
|  |                         PermissionSet.ObjectPermissionType.UPDATE, username); | ||||||
|  |                          | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         }] | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  | }]); | ||||||
| @@ -0,0 +1,117 @@ | |||||||
|  | /* | ||||||
|  |  * 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 service for setting and retrieving browser-local preferences. Preferences | ||||||
|  |  * may be any JSON-serializable type. | ||||||
|  |  */ | ||||||
|  | angular.module('settings').factory('preferenceService', ['$injector', | ||||||
|  |         function preferenceService($injector) { | ||||||
|  |  | ||||||
|  |     // Required services | ||||||
|  |     var $window    = $injector.get('$window'); | ||||||
|  |     var $rootScope = $injector.get('$rootScope'); | ||||||
|  |  | ||||||
|  |     var service = {}; | ||||||
|  |  | ||||||
|  |     // The parameter name for getting the history from local storage | ||||||
|  |     var GUAC_PREFERENCES_STORAGE_KEY = "GUAC_PREFERENCES"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * All currently-set preferences, as name/value pairs. Each property name | ||||||
|  |      * corresponds to the name of a preference. | ||||||
|  |      * | ||||||
|  |      * @type Object.<String, Object> | ||||||
|  |      */ | ||||||
|  |     service.preferences = { | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Whether translation of touch to mouse events should emulate an | ||||||
|  |          * absolute pointer device, or a relative pointer device. | ||||||
|  |          *  | ||||||
|  |          * @type Boolean | ||||||
|  |          */ | ||||||
|  |         emulateAbsoluteMouse : true, | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The default input method. This may be either "none", "osk", or | ||||||
|  |          * "text". | ||||||
|  |          * | ||||||
|  |          * @type String | ||||||
|  |          */ | ||||||
|  |         inputMethod : null | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Persists the current values of all preferences, if possible. | ||||||
|  |      */ | ||||||
|  |     service.save = function save() { | ||||||
|  |  | ||||||
|  |         // Save updated preferences, ignore inability to use localStorage | ||||||
|  |         try { | ||||||
|  |             if (localStorage) | ||||||
|  |                 localStorage.setItem(GUAC_PREFERENCES_STORAGE_KEY, JSON.stringify(service.preferences)); | ||||||
|  |         } | ||||||
|  |         catch (ignore) {} | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Get stored preferences, ignore inability to use localStorage | ||||||
|  |     try { | ||||||
|  |  | ||||||
|  |         if (localStorage) { | ||||||
|  |             var preferencesJSON = localStorage.getItem(GUAC_PREFERENCES_STORAGE_KEY); | ||||||
|  |             if (preferencesJSON) | ||||||
|  |                 service.preferences = JSON.parse(preferencesJSON); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     catch (ignore) {} | ||||||
|  |  | ||||||
|  |     // Choose reasonable default input method based on best-guess at platform | ||||||
|  |     if (service.preferences.inputMethod === null) { | ||||||
|  |  | ||||||
|  |         // Use text input by default if platform likely lacks physical keyboard | ||||||
|  |         if (/android|ipad|iphone/i.test(navigator.userAgent)) | ||||||
|  |             service.preferences.inputMethod = 'text'; | ||||||
|  |         else | ||||||
|  |             service.preferences.inputMethod = 'none'; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Persist settings when window is unloaded | ||||||
|  |     $window.addEventListener('unload', service.save); | ||||||
|  |  | ||||||
|  |     // Persist settings upon navigation  | ||||||
|  |     $rootScope.$on('$routeChangeSuccess', function handleNavigate() { | ||||||
|  |         service.save(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     // Persist settings upon logout | ||||||
|  |     $rootScope.$on('guacLogout', function handleLogout() { | ||||||
|  |         service.save(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return service; | ||||||
|  |  | ||||||
|  | }]); | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | /* | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | .preferences .input-method .caption { | ||||||
|  |     margin-left: 2em; | ||||||
|  |     margin-right: 2em; | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								guacamole/src/main/webapp/app/settings/styles/mouse-mode.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								guacamole/src/main/webapp/app/settings/styles/mouse-mode.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | /* | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | .preferences .mouse-mode .choices { | ||||||
|  |     text-align: center; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .preferences .mouse-mode .choice { | ||||||
|  |     display: inline-block; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .preferences .mouse-mode .choice .figure { | ||||||
|  |     display: inline-block; | ||||||
|  |     vertical-align: middle; | ||||||
|  |     width: 75%; | ||||||
|  |     max-width: 320px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .preferences .mouse-mode .figure img { | ||||||
|  |     display: block; | ||||||
|  |     width: 100%; | ||||||
|  |     max-width: 320px; | ||||||
|  |     margin: 1em auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .preferences .mouse-mode .caption { | ||||||
|  |     text-align: left; | ||||||
|  | } | ||||||
| @@ -32,7 +32,7 @@ | |||||||
|  |  | ||||||
| .settings .action-buttons { | .settings .action-buttons { | ||||||
|     text-align: center; |     text-align: center; | ||||||
|     margin-bottom: 1em; |     margin: 1em 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .settings-tabs .page-list { | .settings-tabs .page-list { | ||||||
|   | |||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | /* | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | .preferences .update-password .form { | ||||||
|  |     padding-left: 0.5em; | ||||||
|  |     border-left: 3px solid rgba(0, 0, 0, 0.125); | ||||||
|  | } | ||||||
| @@ -29,12 +29,13 @@ THE SOFTWARE. | |||||||
|  |  | ||||||
|     <!-- Available tabs --> |     <!-- Available tabs --> | ||||||
|     <div class="settings-tabs"> |     <div class="settings-tabs"> | ||||||
|         <guac-page-list pages="settingsPages"></guac-page-list> |         <guac-page-list pages="settingsPages" ng-show="showAvailableTabs()"></guac-page-list> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <!-- Selected tab --> |     <!-- Selected tab --> | ||||||
|     <guac-settings-users       ng-if="activeTab === 'users'"></guac-settings-users> |     <guac-settings-users       ng-if="activeTab === 'users'"></guac-settings-users> | ||||||
|     <guac-settings-connections ng-if="activeTab === 'connections'"></guac-settings-connections> |     <guac-settings-connections ng-if="activeTab === 'connections'"></guac-settings-connections> | ||||||
|     <guac-settings-sessions    ng-if="activeTab === 'sessions'"></guac-settings-sessions> |     <guac-settings-sessions    ng-if="activeTab === 'sessions'"></guac-settings-sessions> | ||||||
|  |     <guac-settings-preferences ng-if="activeTab === 'preferences'"></guac-settings-preferences> | ||||||
|  |  | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -0,0 +1,106 @@ | |||||||
|  | <div class="preferences"> | ||||||
|  |     <!-- | ||||||
|  |     Copyright 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. | ||||||
|  |     --> | ||||||
|  |  | ||||||
|  |     <!-- Password update --> | ||||||
|  |     <div class="settings section update-password" ng-show="canChangePassword"> | ||||||
|  |         <p>{{'SETTINGS_PREFERENCES.HELP_UPDATE_PASSWORD' | translate}}</p> | ||||||
|  |  | ||||||
|  |         <!-- Password editor --> | ||||||
|  |         <div class="form"> | ||||||
|  |             <table class="fields"> | ||||||
|  |                 <tr> | ||||||
|  |                     <th>{{'SETTINGS_PREFERENCES.FIELD_HEADER_PASSWORD_OLD' | translate}}</th> | ||||||
|  |                     <td><input ng-model="oldPassword" type="password" /></td> | ||||||
|  |                 </tr> | ||||||
|  |                 <tr> | ||||||
|  |                     <th>{{'SETTINGS_PREFERENCES.FIELD_HEADER_PASSWORD_NEW' | translate}}</th> | ||||||
|  |                     <td><input ng-model="newPassword" type="password" /></td> | ||||||
|  |                 </tr> | ||||||
|  |                 <tr> | ||||||
|  |                     <th>{{'SETTINGS_PREFERENCES.FIELD_HEADER_PASSWORD_NEW_AGAIN' | translate}}</th> | ||||||
|  |                     <td><input ng-model="newPasswordMatch" type="password" /></td> | ||||||
|  |                 </tr> | ||||||
|  |             </table> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <!-- Form action buttons --> | ||||||
|  |         <div class="action-buttons"> | ||||||
|  |             <button class="change-password" ng-click="updatePassword()">{{'SETTINGS_PREFERENCES.ACTION_UPDATE_PASSWORD' | translate}}</button> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- Input method --> | ||||||
|  |     <h2 class="header">{{'SETTINGS_PREFERENCES.SECTION_HEADER_DEFAULT_INPUT_METHOD' | translate}}</h2> | ||||||
|  |     <div class="settings section input-method"> | ||||||
|  |         <p>{{'SETTINGS_PREFERENCES.HELP_DEFAULT_INPUT_METHOD' | translate}}</p> | ||||||
|  |         <div class="choices"> | ||||||
|  |  | ||||||
|  |             <!-- No IME --> | ||||||
|  |             <div class="choice"> | ||||||
|  |                 <label><input id="ime-none" name="input-method" ng-model="preferences.inputMethod" type="radio" value="none"/> {{'SETTINGS_PREFERENCES.NAME_INPUT_METHOD_NONE' | translate}}</label> | ||||||
|  |                 <p class="caption"><label for="ime-none">{{'SETTINGS_PREFERENCES.HELP_INPUT_METHOD_NONE' | translate}}</label></p> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <!-- Text input --> | ||||||
|  |             <div class="choice"> | ||||||
|  |                 <label><input id="ime-text" name="input-method" ng-model="preferences.inputMethod" type="radio" value="text"/> {{'SETTINGS_PREFERENCES.NAME_INPUT_METHOD_TEXT' | translate}}</label> | ||||||
|  |                 <p class="caption"><label for="ime-text">{{'SETTINGS_PREFERENCES.HELP_INPUT_METHOD_TEXT' | translate}} </label></p> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <!-- Guac OSK --> | ||||||
|  |             <div class="choice"> | ||||||
|  |                 <label><input id="ime-osk" name="input-method" ng-model="preferences.inputMethod" type="radio" value="osk"/> {{'SETTINGS_PREFERENCES.NAME_INPUT_METHOD_OSK' | translate}}</label> | ||||||
|  |                 <p class="caption"><label for="ime-osk">{{'SETTINGS_PREFERENCES.HELP_INPUT_METHOD_OSK' | translate}}</label></p> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- Mouse mode --> | ||||||
|  |     <h2 class="header">{{'SETTINGS_PREFERENCES.SECTION_HEADER_DEFAULT_MOUSE_MODE' | translate}}</h2> | ||||||
|  |     <div class="settings section mouse-mode"> | ||||||
|  |         <p>{{'SETTINGS_PREFERENCES.HELP_DEFAULT_MOUSE_MODE' | translate}}</p> | ||||||
|  |         <div class="choices"> | ||||||
|  |  | ||||||
|  |             <!-- Touchscreen --> | ||||||
|  |             <div class="choice"> | ||||||
|  |                 <input name="mouse-mode" ng-model="preferences.emulateAbsoluteMouse" type="radio" ng-value="true" checked="checked" id="absolute"/> | ||||||
|  |                 <div class="figure"> | ||||||
|  |                     <label for="absolute"><img src="images/settings/touchscreen.png" alt="{{'SETTINGS_PREFERENCES.NAME_MOUSE_MODE_ABSOLUTE' | translate}}"/></label> | ||||||
|  |                     <p class="caption"><label for="absolute">{{'SETTINGS_PREFERENCES.HELP_MOUSE_MODE_ABSOLUTE' | translate}}</label></p> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <!-- Touchpad --> | ||||||
|  |             <div class="choice"> | ||||||
|  |                 <input name="mouse-mode" ng-model="preferences.emulateAbsoluteMouse" type="radio" ng-value="false" id="relative"/> | ||||||
|  |                 <div class="figure"> | ||||||
|  |                     <label for="relative"><img src="images/settings/touchpad.png" alt="{{'SETTINGS_PREFERENCES.NAME_MOUSE_MODE_RELATIVE' | translate}}"/></label> | ||||||
|  |                     <p class="caption"><label for="relative">{{'SETTINGS_PREFERENCES.HELP_MOUSE_MODE_RELATIVE' | translate}}</label></p> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  | </div> | ||||||
| @@ -4,19 +4,20 @@ | |||||||
|  |  | ||||||
|         "ACTION_ACKNOWLEDGE"        : "OK", |         "ACTION_ACKNOWLEDGE"        : "OK", | ||||||
|         "ACTION_CANCEL"             : "Cancel", |         "ACTION_CANCEL"             : "Cancel", | ||||||
|         "ACTION_CHANGE_PASSWORD"    : "Change Password", |  | ||||||
|         "ACTION_CLONE"              : "Clone", |         "ACTION_CLONE"              : "Clone", | ||||||
|         "ACTION_DELETE"             : "Delete", |         "ACTION_DELETE"             : "Delete", | ||||||
|         "ACTION_DELETE_SESSIONS"    : "Kill Sessions", |         "ACTION_DELETE_SESSIONS"    : "Kill Sessions", | ||||||
|         "ACTION_LOGIN"              : "Login", |         "ACTION_LOGIN"              : "Login", | ||||||
|         "ACTION_LOGOUT"             : "Logout", |         "ACTION_LOGOUT"             : "Logout", | ||||||
|         "ACTION_MANAGE_CONNECTIONS" : "Connections", |         "ACTION_MANAGE_CONNECTIONS" : "Connections", | ||||||
|  |         "ACTION_MANAGE_PREFERENCES" : "Preferences", | ||||||
|         "ACTION_MANAGE_SETTINGS"    : "Settings", |         "ACTION_MANAGE_SETTINGS"    : "Settings", | ||||||
|         "ACTION_MANAGE_SESSIONS"    : "Active Sessions", |         "ACTION_MANAGE_SESSIONS"    : "Active Sessions", | ||||||
|         "ACTION_MANAGE_USERS"       : "Users", |         "ACTION_MANAGE_USERS"       : "Users", | ||||||
|         "ACTION_NAVIGATE_BACK"      : "Back", |         "ACTION_NAVIGATE_BACK"      : "Back", | ||||||
|         "ACTION_NAVIGATE_HOME"      : "Home", |         "ACTION_NAVIGATE_HOME"      : "Home", | ||||||
|         "ACTION_SAVE"               : "Save", |         "ACTION_SAVE"               : "Save", | ||||||
|  |         "ACTION_UPDATE_PASSWORD"    : "Update Password", | ||||||
|  |  | ||||||
|         "DIALOG_HEADER_ERROR" : "Error", |         "DIALOG_HEADER_ERROR" : "Error", | ||||||
|  |  | ||||||
| @@ -399,6 +400,43 @@ | |||||||
|  |  | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     "SETTINGS_PREFERENCES" : { | ||||||
|  |  | ||||||
|  |         "ACTION_ACKNOWLEDGE"        : "@:APP.ACTION_ACKNOWLEDGE", | ||||||
|  |         "ACTION_CANCEL"             : "@:APP.ACTION_CANCEL", | ||||||
|  |         "ACTION_UPDATE_PASSWORD"    : "@:APP.ACTION_UPDATE_PASSWORD", | ||||||
|  |  | ||||||
|  |         "DIALOG_HEADER_ERROR"    : "@:APP.DIALOG_HEADER_ERROR", | ||||||
|  |  | ||||||
|  |         "ERROR_PASSWORD_BLANK"    : "Your password cannot be blank.", | ||||||
|  |         "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", | ||||||
|  |  | ||||||
|  |         "FIELD_HEADER_PASSWORD"           : "Password:", | ||||||
|  |         "FIELD_HEADER_PASSWORD_OLD"       : "Current Password:", | ||||||
|  |         "FIELD_HEADER_PASSWORD_NEW"       : "New Password:", | ||||||
|  |         "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:", | ||||||
|  |         "FIELD_HEADER_USERNAME"           : "Username:", | ||||||
|  |          | ||||||
|  |         "HELP_DEFAULT_INPUT_METHOD" : "The default input method determines how keyboard events are received by Guacamole. Changing this setting may be necessary when using a mobile device, or when typing through an IME. This setting can be overridden on a per-connection basis within the Guacamole menu.", | ||||||
|  |         "HELP_DEFAULT_MOUSE_MODE"   : "The default mouse emulation mode determines how the remote mouse will behave in new connections with respect to touches. This setting can be overridden on a per-connection basis within the Guacamole menu.", | ||||||
|  |         "HELP_INPUT_METHOD_NONE"    : "@:CLIENT.HELP_INPUT_METHOD_NONE", | ||||||
|  |         "HELP_INPUT_METHOD_OSK"     : "@:CLIENT.HELP_INPUT_METHOD_OSK", | ||||||
|  |         "HELP_INPUT_METHOD_TEXT"    : "@:CLIENT.HELP_INPUT_METHOD_TEXT", | ||||||
|  |         "HELP_MOUSE_MODE_ABSOLUTE"  : "@:CLIENT.HELP_MOUSE_MODE_ABSOLUTE", | ||||||
|  |         "HELP_MOUSE_MODE_RELATIVE"  : "@:CLIENT.HELP_MOUSE_MODE_RELATIVE", | ||||||
|  |         "HELP_UPDATE_PASSWORD"      : "If you wish to change your password, enter your current password and the desired new password below, and click \"Update Password\". The change will take effect immediately.", | ||||||
|  |  | ||||||
|  |         "INFO_PASSWORD_CHANGED" : "Password changed.", | ||||||
|  |  | ||||||
|  |         "NAME_INPUT_METHOD_NONE" : "@:CLIENT.NAME_INPUT_METHOD_NONE", | ||||||
|  |         "NAME_INPUT_METHOD_OSK"  : "@:CLIENT.NAME_INPUT_METHOD_OSK", | ||||||
|  |         "NAME_INPUT_METHOD_TEXT" : "@:CLIENT.NAME_INPUT_METHOD_TEXT", | ||||||
|  |  | ||||||
|  |         "SECTION_HEADER_DEFAULT_INPUT_METHOD" : "Default Input Method", | ||||||
|  |         "SECTION_HEADER_DEFAULT_MOUSE_MODE"   : "Default Mouse Emulation Mode" | ||||||
|  |  | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     "SETTINGS_USERS" : { |     "SETTINGS_USERS" : { | ||||||
|  |  | ||||||
|         "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE", |         "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE", | ||||||
| @@ -442,27 +480,13 @@ | |||||||
|  |  | ||||||
|     "USER_MENU" : { |     "USER_MENU" : { | ||||||
|  |  | ||||||
|         "ACTION_ACKNOWLEDGE"        : "@:APP.ACTION_ACKNOWLEDGE", |  | ||||||
|         "ACTION_CANCEL"             : "@:APP.ACTION_CANCEL", |  | ||||||
|         "ACTION_CHANGE_PASSWORD"    : "@:APP.ACTION_CHANGE_PASSWORD", |  | ||||||
|         "ACTION_LOGOUT"             : "@:APP.ACTION_LOGOUT", |         "ACTION_LOGOUT"             : "@:APP.ACTION_LOGOUT", | ||||||
|         "ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS", |         "ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS", | ||||||
|  |         "ACTION_MANAGE_PREFERENCES" : "@:APP.ACTION_MANAGE_PREFERENCES", | ||||||
|         "ACTION_MANAGE_SESSIONS"    : "@:APP.ACTION_MANAGE_SESSIONS", |         "ACTION_MANAGE_SESSIONS"    : "@:APP.ACTION_MANAGE_SESSIONS", | ||||||
|         "ACTION_MANAGE_SETTINGS"    : "@:APP.ACTION_MANAGE_SETTINGS", |         "ACTION_MANAGE_SETTINGS"    : "@:APP.ACTION_MANAGE_SETTINGS", | ||||||
|         "ACTION_MANAGE_USERS"       : "@:APP.ACTION_MANAGE_USERS", |         "ACTION_MANAGE_USERS"       : "@:APP.ACTION_MANAGE_USERS", | ||||||
|         "ACTION_NAVIGATE_HOME"      : "@:APP.ACTION_NAVIGATE_HOME", |         "ACTION_NAVIGATE_HOME"      : "@:APP.ACTION_NAVIGATE_HOME" | ||||||
|         "ACTION_SAVE"               : "@:APP.ACTION_SAVE", |  | ||||||
|  |  | ||||||
|         "DIALOG_HEADER_ERROR"    : "@:APP.DIALOG_HEADER_ERROR", |  | ||||||
|  |  | ||||||
|         "ERROR_PASSWORD_BLANK"    : "Your password cannot be blank.", |  | ||||||
|         "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", |  | ||||||
|          |  | ||||||
|         "FIELD_HEADER_PASSWORD_OLD"       : "Current Password:", |  | ||||||
|         "FIELD_HEADER_PASSWORD_NEW"       : "New Password:", |  | ||||||
|         "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:", |  | ||||||
|          |  | ||||||
|         "PASSWORD_CHANGED" : "Password changed." |  | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user