diff --git a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js
index 837f4e632..3d51f4b52 100644
--- a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js
+++ b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js
@@ -125,30 +125,12 @@ angular.module('index').config(['$routeProvider', '$locationProvider',
resolve : { routeToUserHomePage: routeToUserHomePage }
})
- // Connection management screen
- .when('/manage/modules/connections/', {
+ // Management screen
+ .when('/settings/:tab', {
title : 'APP.NAME',
- bodyClassName : 'manage',
- templateUrl : 'app/manage/templates/manageConnections.html',
- controller : 'manageConnectionsController',
- resolve : { updateCurrentToken: updateCurrentToken }
- })
-
- // User management screen
- .when('/manage/modules/users/', {
- title : 'APP.NAME',
- bodyClassName : 'manage',
- templateUrl : 'app/manage/templates/manageUsers.html',
- controller : 'manageUsersController',
- resolve : { updateCurrentToken: updateCurrentToken }
- })
-
- // Session management screen
- .when('/manage/modules/sessions/', {
- title : 'APP.NAME',
- bodyClassName : 'manage',
- templateUrl : 'app/manage/templates/manageSessions.html',
- controller : 'manageSessionsController',
+ bodyClassName : 'settings',
+ templateUrl : 'app/settings/templates/settings.html',
+ controller : 'settingsController',
resolve : { updateCurrentToken: updateCurrentToken }
})
diff --git a/guacamole/src/main/webapp/app/index/indexModule.js b/guacamole/src/main/webapp/app/index/indexModule.js
index a1d927330..99fb457fa 100644
--- a/guacamole/src/main/webapp/app/index/indexModule.js
+++ b/guacamole/src/main/webapp/app/index/indexModule.js
@@ -34,5 +34,6 @@ angular.module('index', [
'ngTouch',
'notification',
'pascalprecht.translate',
- 'rest'
+ 'rest',
+ 'settings'
]);
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
index 1a43744be..d45491dc9 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
@@ -472,7 +472,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
* Cancels all pending edits, returning to the management page.
*/
$scope.cancel = function cancel() {
- $location.path('/manage/modules/users/');
+ $location.path('/settings/users');
};
/**
@@ -498,7 +498,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
// Upon success, save any changed permissions
permissionService.patchPermissions($scope.user.username, permissionsAdded, permissionsRemoved)
.success(function patchedUserPermissions() {
- $location.path('/manage/modules/users/');
+ $location.path('/settings/users');
})
// Notify of any errors
@@ -560,7 +560,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
// Delete the user
userService.deleteUser($scope.user)
.success(function deletedUser() {
- $location.path('/manage/modules/users/');
+ $location.path('/settings/users');
})
// Notify of any errors
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js
deleted file mode 100644
index d50b5d5b5..000000000
--- a/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/**
- * The controller for the user administration page.
- */
-angular.module('manage').controller('manageUsersController', ['$scope', '$injector',
- function manageUsersController($scope, $injector) {
-
- // Required types
- var PermissionSet = $injector.get('PermissionSet');
- var User = $injector.get('User');
-
- // Required services
- var $location = $injector.get('$location');
- var authenticationService = $injector.get('authenticationService');
- var guacNotification = $injector.get('guacNotification');
- var permissionService = $injector.get('permissionService');
- var userService = $injector.get('userService');
-
- // Identifier of the current user
- var currentUserID = authenticationService.getCurrentUserID();
-
- /**
- * An action to be provided along with the object sent to showStatus which
- * closes the currently-shown status dialog.
- */
- var ACKNOWLEDGE_ACTION = {
- name : "MANAGE_USER.ACTION_ACKNOWLEDGE",
- // Handle action
- callback : function acknowledgeCallback() {
- guacNotification.showStatus(false);
- }
- };
-
- /**
- * All visible users.
- *
- * @type User[]
- */
- $scope.users = null;
-
- /**
- * Whether the current user can manage users. If the current permissions
- * have not yet been loaded, this will be null.
- *
- * @type Boolean
- */
- $scope.canManageUsers = null;
-
- /**
- * Whether the current user can create new users. If the current
- * permissions have not yet been loaded, this will be null.
- *
- * @type Boolean
- */
- $scope.canCreateUsers = null;
-
- /**
- * The name of the new user to create, if any, when user creation is
- * requested via newUser().
- *
- * @type String
- */
- $scope.newUsername = "";
-
- /**
- * All permissions associated with the current user, or null if the user's
- * permissions have not yet been loaded.
- *
- * @type PermissionSet
- */
- $scope.permissions = null;
-
- /**
- * Returns whether critical data has completed being loaded.
- *
- * @returns {Boolean}
- * true if enough data has been loaded for the user interface to be
- * useful, false otherwise.
- */
- $scope.isLoaded = function isLoaded() {
-
- return $scope.users !== null
- && $scope.permissions !== null
- && $scope.canManageUsers !== null
- && $scope.canCreateUsers !== null;
-
- };
-
- // Retrieve current permissions
- permissionService.getPermissions(currentUserID)
- .success(function permissionsRetrieved(permissions) {
-
- $scope.permissions = permissions;
-
- // Determine whether the current user can create new users
- $scope.canCreateUsers =
- PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
- || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER);
-
- // Determine whether the current user can manage other users
- $scope.canManageUsers =
- $scope.canCreateUsers
- || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
- || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE);
-
- // Return to home if there's nothing to do here
- if (!$scope.canManageUsers)
- $location.path('/');
-
- });
-
- // Retrieve all users for whom we have UPDATE or DELETE permission
- userService.getUsers([PermissionSet.ObjectPermissionType.UPDATE,
- PermissionSet.ObjectPermissionType.DELETE])
- .success(function usersReceived(users) {
-
- // Display only other users, not self
- $scope.users = users.filter(function isNotSelf(user) {
- return user.username !== currentUserID;
- });
-
- });
-
- /**
- * Creates a new user having the username specified in the user creation
- * interface.
- */
- $scope.newUser = function newUser() {
-
- // Create user skeleton
- var user = new User({
- username: $scope.newUsername || ''
- });
-
- // Create specified user
- userService.createUser(user)
-
- // Add user to visible list upon success
- .success(function userCreated() {
- $scope.users.push(user);
- })
-
- // Notify of any errors
- .error(function userCreationFailed(error) {
- guacNotification.showStatus({
- 'className' : 'error',
- 'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR',
- 'text' : error.message,
- 'actions' : [ ACKNOWLEDGE_ACTION ]
- });
- });
-
- // Reset username
- $scope.newUsername = "";
-
- };
-
-}]);
diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUsers.html b/guacamole/src/main/webapp/app/manage/templates/manageUsers.html
deleted file mode 100644
index e1231a262..000000000
--- a/guacamole/src/main/webapp/app/manage/templates/manageUsers.html
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js
index e06e7a398..e6606dfd0 100644
--- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js
+++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js
@@ -203,7 +203,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
if (canManageUsers) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_USERS',
- '/manage/modules/users/'
+ '/settings/users'
));
}
@@ -211,7 +211,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
if (canManageConnections) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_CONNECTIONS',
- '/manage/modules/connections/'
+ '/settings/connections'
));
}
@@ -219,7 +219,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
if (canManageSessions) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_SESSIONS',
- '/manage/modules/sessions/'
+ '/settings/sessions'
));
}
diff --git a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css
index ae7daa2ef..b8d162157 100644
--- a/guacamole/src/main/webapp/app/navigation/styles/user-menu.css
+++ b/guacamole/src/main/webapp/app/navigation/styles/user-menu.css
@@ -198,9 +198,9 @@
background-image: url('images/action-icons/guac-home-dark.png');
}
-.user-menu .options li a[href="#/manage/modules/users/"],
-.user-menu .options li a[href="#/manage/modules/connections/"],
-.user-menu .options li a[href="#/manage/modules/sessions/"] {
+.user-menu .options li a[href="#/settings/users"],
+.user-menu .options li a[href="#/settings/connections"],
+.user-menu .options li a[href="#/settings/sessions"] {
background-image: url('images/action-icons/guac-config-dark.png');
}
diff --git a/guacamole/src/main/webapp/app/settings/controllers/settingsController.js b/guacamole/src/main/webapp/app/settings/controllers/settingsController.js
new file mode 100644
index 000000000..ce1a26aef
--- /dev/null
+++ b/guacamole/src/main/webapp/app/settings/controllers/settingsController.js
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * The controller for the general settings page.
+ */
+angular.module('manage').controller('settingsController', ['$scope', '$injector',
+ function settingsController($scope, $injector) {
+
+ // Required services
+ var $routeParams = $injector.get('$routeParams');
+ var userPageService = $injector.get('userPageService');
+
+ /**
+ * The array of settings pages available to the current user, or null if
+ * not yet known.
+ *
+ * @type Page[]
+ */
+ $scope.settingsPages = null;
+
+ /**
+ * The currently-selected settings tab. This may be 'users', 'connections',
+ * or 'sessions'.
+ *
+ * @type String
+ */
+ $scope.activeTab = $routeParams.tab;
+
+ // Retrieve settings pages
+ userPageService.getSettingsPages()
+ .then(function settingsPagesRetrieved(pages) {
+ $scope.settingsPages = pages;
+ });
+
+}]);
diff --git a/guacamole/src/main/webapp/app/settings/directives/manageUsers.js b/guacamole/src/main/webapp/app/settings/directives/manageUsers.js
new file mode 100644
index 000000000..86b775cd7
--- /dev/null
+++ b/guacamole/src/main/webapp/app/settings/directives/manageUsers.js
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * A directive for managing all users in the system.
+ */
+angular.module('settings').directive('guacManageUsers', [function guacManageUsers() {
+
+ return {
+ // Element only
+ restrict: 'E',
+ replace: true,
+
+ scope: {
+ },
+
+ templateUrl: 'app/settings/templates/manageUsers.html',
+ controller: ['$scope', '$injector', function manageUsersController($scope, $injector) {
+
+ // Required types
+ var PermissionSet = $injector.get('PermissionSet');
+ var User = $injector.get('User');
+
+ // Required services
+ var $location = $injector.get('$location');
+ var authenticationService = $injector.get('authenticationService');
+ var guacNotification = $injector.get('guacNotification');
+ var permissionService = $injector.get('permissionService');
+ var userService = $injector.get('userService');
+
+ // Identifier of the current user
+ var currentUserID = authenticationService.getCurrentUserID();
+
+ /**
+ * An action to be provided along with the object sent to showStatus which
+ * closes the currently-shown status dialog.
+ */
+ var ACKNOWLEDGE_ACTION = {
+ name : "MANAGE_USER.ACTION_ACKNOWLEDGE",
+ // Handle action
+ callback : function acknowledgeCallback() {
+ guacNotification.showStatus(false);
+ }
+ };
+
+ /**
+ * All visible users.
+ *
+ * @type User[]
+ */
+ $scope.users = null;
+
+ /**
+ * Whether the current user can manage users. If the current permissions
+ * have not yet been loaded, this will be null.
+ *
+ * @type Boolean
+ */
+ $scope.canManageUsers = null;
+
+ /**
+ * Whether the current user can create new users. If the current
+ * permissions have not yet been loaded, this will be null.
+ *
+ * @type Boolean
+ */
+ $scope.canCreateUsers = null;
+
+ /**
+ * The name of the new user to create, if any, when user creation is
+ * requested via newUser().
+ *
+ * @type String
+ */
+ $scope.newUsername = "";
+
+ /**
+ * All permissions associated with the current user, or null if the user's
+ * permissions have not yet been loaded.
+ *
+ * @type PermissionSet
+ */
+ $scope.permissions = null;
+
+ /**
+ * Returns whether critical data has completed being loaded.
+ *
+ * @returns {Boolean}
+ * true if enough data has been loaded for the user interface to be
+ * useful, false otherwise.
+ */
+ $scope.isLoaded = function isLoaded() {
+
+ return $scope.users !== null
+ && $scope.permissions !== null
+ && $scope.canManageUsers !== null
+ && $scope.canCreateUsers !== null;
+
+ };
+
+ // Retrieve current permissions
+ permissionService.getPermissions(currentUserID)
+ .success(function permissionsRetrieved(permissions) {
+
+ $scope.permissions = permissions;
+
+ // Determine whether the current user can create new users
+ $scope.canCreateUsers =
+ PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
+ || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER);
+
+ // Determine whether the current user can manage other users
+ $scope.canManageUsers =
+ $scope.canCreateUsers
+ || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
+ || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE);
+
+ // Return to home if there's nothing to do here
+ if (!$scope.canManageUsers)
+ $location.path('/');
+
+ });
+
+ // Retrieve all users for whom we have UPDATE or DELETE permission
+ userService.getUsers([PermissionSet.ObjectPermissionType.UPDATE,
+ PermissionSet.ObjectPermissionType.DELETE])
+ .success(function usersReceived(users) {
+
+ // Display only other users, not self
+ $scope.users = users.filter(function isNotSelf(user) {
+ return user.username !== currentUserID;
+ });
+
+ });
+
+ /**
+ * Creates a new user having the username specified in the user creation
+ * interface.
+ */
+ $scope.newUser = function newUser() {
+
+ // Create user skeleton
+ var user = new User({
+ username: $scope.newUsername || ''
+ });
+
+ // Create specified user
+ userService.createUser(user)
+
+ // Add user to visible list upon success
+ .success(function userCreated() {
+ $scope.users.push(user);
+ })
+
+ // Notify of any errors
+ .error(function userCreationFailed(error) {
+ guacNotification.showStatus({
+ 'className' : 'error',
+ 'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR',
+ 'text' : error.message,
+ 'actions' : [ ACKNOWLEDGE_ACTION ]
+ });
+ });
+
+ // Reset username
+ $scope.newUsername = "";
+
+ };
+
+ }]
+ };
+
+}]);
diff --git a/guacamole/src/main/webapp/app/settings/settingsModule.js b/guacamole/src/main/webapp/app/settings/settingsModule.js
new file mode 100644
index 000000000..b3d8c0665
--- /dev/null
+++ b/guacamole/src/main/webapp/app/settings/settingsModule.js
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * The module for manipulation of general settings. This is distinct from the
+ * "manage" module, which deals only with administrator-level system management.
+ */
+angular.module('settings', [
+ 'groupList',
+ 'list',
+ 'navigation',
+ 'notification',
+ 'rest'
+]);
diff --git a/guacamole/src/main/webapp/app/settings/styles/settings.css b/guacamole/src/main/webapp/app/settings/styles/settings.css
new file mode 100644
index 000000000..9684adc25
--- /dev/null
+++ b/guacamole/src/main/webapp/app/settings/styles/settings.css
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+.settings .header {
+ margin-bottom: 0;
+}
+
+.settings-tabs .page-list {
+ margin: 0;
+ padding: 0;
+ background: rgba(0, 0, 0, 0.0125);
+}
+
+.settings-tabs .page-list li {
+ display: inline-block;
+ list-style: none;
+}
+
+.settings-tabs .page-list li a[href] {
+ display: block;
+ color: black;
+ text-decoration: none;
+ padding: 0.75em 1em;
+}
+
+.settings-tabs .page-list li a[href]:visited {
+ color: black;
+}
+
+.settings-tabs .page-list li a[href]:hover {
+ background-color: #CDA;
+}
+
+.settings-tabs .page-list li a[href].current,
+.settings-tabs .page-list li a[href].current:hover {
+ background: rgba(0,0,0,0.3);
+ cursor: default;
+}
diff --git a/guacamole/src/main/webapp/app/settings/templates/manageUsers.html b/guacamole/src/main/webapp/app/settings/templates/manageUsers.html
new file mode 100644
index 000000000..2ee778b21
--- /dev/null
+++ b/guacamole/src/main/webapp/app/settings/templates/manageUsers.html
@@ -0,0 +1,48 @@
+
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/settings/templates/settings.html b/guacamole/src/main/webapp/app/settings/templates/settings.html
new file mode 100644
index 000000000..2766f31d4
--- /dev/null
+++ b/guacamole/src/main/webapp/app/settings/templates/settings.html
@@ -0,0 +1,38 @@
+
+
+