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

{{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

+
+

{{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}

+ +
-

{{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}

+

{{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}

-
- {{'MANAGE.ACTION_NAVIGATE_HOME' | translate}} - {{'MANAGE.ACTION_LOGOUT' | translate}} +
+

{{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

+
-

{{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}

-

{{'MANAGE.SECTION_HEADER_USERS' | translate}}

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

{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

+
+

{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}

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

{{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

+

{{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

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

{{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}

+

{{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}

{{'MANAGE_CONNECTION.INFO_CONNECTION_NOT_USED' | translate}}

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

{{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

+
+

{{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}

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

{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

+
+

{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}

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

{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}

+

{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}

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

{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}

+

{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}

+ + + + + + + + + + + + +
{{'USER_MENU.FIELD_HEADER_PASSWORD_OLD' | translate}}
{{'USER_MENU.FIELD_HEADER_PASSWORD_NEW' | translate}}
{{'USER_MENU.FIELD_HEADER_PASSWORD_NEW_AGAIN' | translate}}
+
+ + +
+ + +
+
+ +
diff --git a/guacamole/src/main/webapp/app/userMenu/userMenuModule.js b/guacamole/src/main/webapp/app/userMenu/userMenuModule.js new file mode 100644 index 000000000..b08453e51 --- /dev/null +++ b/guacamole/src/main/webapp/app/userMenu/userMenuModule.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Module for displaying a user-oriented menu, containing the current username + * and options for navigation, changing the user's password, logging out, etc. + */ +angular.module('userMenu', []); diff --git a/guacamole/src/main/webapp/images/action-icons/guac-config-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-config-dark.png new file mode 100644 index 000000000..132a253b5 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-config-dark.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-home-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-home-dark.png new file mode 100644 index 000000000..e1e35d4b1 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-home-dark.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-key-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-key-dark.png new file mode 100644 index 000000000..90ef0ffa7 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-key-dark.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-logout-dark.png b/guacamole/src/main/webapp/images/action-icons/guac-logout-dark.png new file mode 100644 index 000000000..bfb9e1119 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-logout-dark.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-open-downward.png b/guacamole/src/main/webapp/images/action-icons/guac-open-downward.png new file mode 100644 index 000000000..cfa9885d8 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-open-downward.png differ diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index de751955e..7e31dc77a 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -114,22 +114,6 @@ "HOME" : { - "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", - "ACTION_CHANGE_PASSWORD" : "@:APP.ACTION_CHANGE_PASSWORD", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_MANAGE" : "@:APP.ACTION_MANAGE", - "ACTION_SAVE" : "@:APP.ACTION_SAVE", - - "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - - "ERROR_PASSWORD_BLANK" : "Your password cannot be blank.", - "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", - - "FIELD_HEADER_PASSWORD_OLD" : "Current Password:", - "FIELD_HEADER_PASSWORD_NEW" : "New Password:", - "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:", - "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", "INFO_NO_RECENT_CONNECTIONS" : "No recent connections.", @@ -155,8 +139,6 @@ "MANAGE" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", "ACTION_NEW_USER" : "New User", "ACTION_NEW_CONNECTION" : "New Connection", "ACTION_NEW_CONNECTION_GROUP" : "New Group", @@ -182,8 +164,6 @@ "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection", @@ -215,8 +195,6 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection Group", @@ -240,8 +218,6 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_DELETE" : "@:APP.ACTION_DELETE", - "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete User", @@ -403,6 +379,29 @@ "NAME" : "VNC" + }, + + "USER_MENU" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CHANGE_PASSWORD" : "@:APP.ACTION_CHANGE_PASSWORD", + "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", + "ACTION_MANAGE" : "@:APP.ACTION_MANAGE", + "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "ERROR_PASSWORD_BLANK" : "Your password cannot be blank.", + "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", + + "FIELD_HEADER_PASSWORD_OLD" : "Current Password:", + "FIELD_HEADER_PASSWORD_NEW" : "New Password:", + "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:", + + "PASSWORD_CHANGED" : "Password changed." + } }