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.SECTION_HEADER_RECENT_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_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}}
+
@@ -60,7 +58,7 @@ THE SOFTWARE.
- {{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}
+
@@ -83,7 +81,7 @@ THE SOFTWARE.
- {{'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}}
+
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}}
+
@@ -50,7 +48,7 @@ THE SOFTWARE.
- {{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}
+
@@ -62,7 +60,7 @@ THE SOFTWARE.
- {{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | 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."
+
}
}