Merge pull request #149 from glyptodon/settings-page

GUAC-1053: Move manage sections into tabbed settings page
This commit is contained in:
James Muehlner
2015-04-18 18:52:07 -07:00
33 changed files with 1407 additions and 1046 deletions

View File

@@ -104,6 +104,10 @@ angular.module('form').directive('guacForm', [function form() {
*/
$scope.getSectionHeader = function getSectionHeader(form) {
// If no form, or no name, then no header
if (!form || !form.name)
return '';
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
+ '.SECTION_HEADER_' + translationStringService.canonicalize(form.name);
@@ -128,6 +132,10 @@ angular.module('form').directive('guacForm', [function form() {
*/
$scope.getFieldHeader = function getFieldHeader(field) {
// If no field, or no name, then no header
if (!field || !field.name)
return '';
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
+ '.FIELD_HEADER_' + translationStringService.canonicalize(field.name);

View File

@@ -131,8 +131,8 @@ angular.module('form').directive('guacFormField', [function formField() {
*/
$scope.getFieldOption = function getFieldOption(value) {
// Don't bother if the model is not yet defined
if (!$scope.field)
// If no field, or no value, then no corresponding translation string
if (!$scope.field || !$scope.field.name || !value)
return '';
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')

View File

@@ -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 }
})

View File

@@ -34,5 +34,6 @@ angular.module('index', [
'ngTouch',
'notification',
'pascalprecht.translate',
'rest'
'rest',
'settings'
]);

View File

@@ -317,7 +317,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
* Cancels all pending edits, returning to the management page.
*/
$scope.cancel = function cancel() {
$location.path('/manage/modules/connections/');
$location.path('/settings/connections');
};
/**
@@ -339,7 +339,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
// Save the connection
connectionService.saveConnection($scope.connection)
.success(function savedConnection() {
$location.path('/manage/modules/connections/');
$location.path('/settings/connections');
})
// Notify of any errors
@@ -389,7 +389,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
// Delete the connection
connectionService.deleteConnection($scope.connection)
.success(function deletedConnection() {
$location.path('/manage/modules/connections/');
$location.path('/settings/connections');
})
// Notify of any errors

View File

@@ -1,156 +0,0 @@
/*
* 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 controller for the connection and connection group administration page.
*/
angular.module('manage').controller('manageConnectionsController', ['$scope', '$injector',
function manageConnectionsController($scope, $injector) {
// Required types
var ConnectionGroup = $injector.get('ConnectionGroup');
var PermissionSet = $injector.get('PermissionSet');
// Required services
var $location = $injector.get('$location');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
// 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_CONNECTION.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
guacNotification.showStatus(false);
}
};
/**
* The root connection group of the connection group hierarchy.
*
* @type ConnectionGroup
*/
$scope.rootGroup = null;
/**
* Whether the current user can manage connections. If the current
* permissions have not yet been loaded, this will be null.
*
* @type Boolean
*/
$scope.canManageConnections = null;
/**
* Whether the current user can create new connections. If the current
* permissions have not yet been loaded, this will be null.
*
* @type Boolean
*/
$scope.canCreateConnections = null;
/**
* Whether the current user can create new connection groups. If the
* current permissions have not yet been loaded, this will be null.
*
* @type Boolean
*/
$scope.canCreateConnectionGroups = null;
/**
* 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.rootGroup !== null
&& $scope.permissions !== null
&& $scope.canManageConnections !== null
&& $scope.canCreateConnections !== null
&& $scope.canCreateConnectionGroups !== null;
};
// Retrieve current permissions
permissionService.getPermissions(currentUserID)
.success(function permissionsRetrieved(permissions) {
$scope.permissions = permissions;
// Ignore permission to update root group
PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER);
// Determine whether the current user can create new users
$scope.canCreateConnections =
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION);
// Determine whether the current user can create new users
$scope.canCreateConnectionGroups =
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP);
// Determine whether the current user can manage other connections or groups
$scope.canManageConnections =
// Permission to manage connections
$scope.canCreateConnections
|| PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
|| PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE)
// Permission to manage groups
|| $scope.canCreateConnectionGroups
|| PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
|| PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE);
// Return to home if there's nothing to do here
if (!$scope.canManageConnections)
$location.path('/');
});
// Retrieve all connections for which we have UPDATE or DELETE permission
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER,
[PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE])
.success(function connectionGroupReceived(rootGroup) {
$scope.rootGroup = rootGroup;
});
}]);

View File

@@ -1,349 +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 session administration page.
*/
angular.module('manage').controller('manageSessionsController', ['$scope', '$injector',
function manageSessionsController($scope, $injector) {
// Required types
var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper');
var ConnectionGroup = $injector.get('ConnectionGroup');
var SortOrder = $injector.get('SortOrder');
// Required services
var $filter = $injector.get('$filter');
var $translate = $injector.get('$translate');
var activeConnectionService = $injector.get('activeConnectionService');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
/**
* All permissions associated with the current user, or null if the user's
* permissions have not yet been loaded.
*
* @type PermissionSet
*/
$scope.permissions = null;
/**
* The ActiveConnectionWrappers of all active sessions accessible by the
* current user, or null if the active sessions have not yet been loaded.
*
* @type ActiveConnectionWrapper[]
*/
$scope.wrappers = null;
/**
* SortOrder instance which maintains the sort order of the visible
* connection wrappers.
*
* @type SortOrder
*/
$scope.wrapperOrder = new SortOrder([
'activeConnection.username',
'startDate',
'activeConnection.remoteHost',
'name'
]);
/**
* Array of all wrapper properties that are filterable.
*
* @type String[]
*/
$scope.filteredWrapperProperties = [
'activeConnection.username',
'startDate',
'activeConnection.remoteHost',
'name'
];
/**
* All active connections, if known, or null if active connections have not
* yet been loaded.
*
* @type ActiveConnection
*/
var activeConnections = null;
/**
* Map of all visible connections by object identifier, or null if visible
* connections have not yet been loaded.
*
* @type Object.<String, Connection>
*/
var connections = null;
/**
* The date format for use for session-related dates.
*
* @type String
*/
var sessionDateFormat = null;
/**
* Map of all currently-selected active connection wrappers by identifier.
*
* @type Object.<String, ActiveConnectionWrapper>
*/
var selectedWrappers = {};
/**
* Adds the given connection to the internal set of visible
* connections.
*
* @param {Connection} connection
* The connection to add to the internal set of visible connections.
*/
var addConnection = function addConnection(connection) {
// Add given connection to set of visible connections
connections[connection.identifier] = connection;
};
/**
* Adds all descendant connections of the given connection group to the
* internal set of connections.
*
* @param {ConnectionGroup} connectionGroup
* The connection group whose descendant connections should be added to
* the internal set of connections.
*/
var addDescendantConnections = function addDescendantConnections(connectionGroup) {
// Add all child connections
if (connectionGroup.childConnections)
connectionGroup.childConnections.forEach(addConnection);
// Add all child connection groups
if (connectionGroup.childConnectionGroups)
connectionGroup.childConnectionGroups.forEach(addDescendantConnections);
};
/**
* Wraps all loaded active connections, storing the resulting array within
* the scope. If required data has not yet finished loading, this function
* has no effect.
*/
var wrapActiveConnections = function wrapActiveConnections() {
// Abort if not all required data is available
if (!activeConnections || !connections || !sessionDateFormat)
return;
// Wrap all active connections for sake of display
$scope.wrappers = [];
for (var identifier in activeConnections) {
var activeConnection = activeConnections[identifier];
var connection = connections[activeConnection.connectionIdentifier];
$scope.wrappers.push(new ActiveConnectionWrapper(
connection.name,
$filter('date')(activeConnection.startDate, sessionDateFormat),
activeConnection
));
}
};
// Query the user's permissions
permissionService.getPermissions(authenticationService.getCurrentUserID())
.success(function permissionsReceived(retrievedPermissions) {
$scope.permissions = retrievedPermissions;
});
// Retrieve all connections
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER)
.success(function connectionGroupReceived(retrievedRootGroup) {
// Load connections from retrieved group tree
connections = {};
addDescendantConnections(retrievedRootGroup);
// Attempt to produce wrapped list of active connections
wrapActiveConnections();
});
// Query active sessions
activeConnectionService.getActiveConnections().success(function sessionsRetrieved(retrievedActiveConnections) {
// Store received list
activeConnections = retrievedActiveConnections;
// Attempt to produce wrapped list of active connections
wrapActiveConnections();
});
// Get session date format
$translate('MANAGE_SESSION.FORMAT_STARTDATE').then(function sessionDateFormatReceived(retrievedSessionDateFormat) {
// Store received date format
sessionDateFormat = retrievedSessionDateFormat;
// Attempt to produce wrapped list of active connections
wrapActiveConnections();
});
/**
* 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.wrappers !== null
&& $scope.sessionDateFormat !== null
&& $scope.permissions !== null;
};
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "MANAGE_SESSION.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "MANAGE_SESSION.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to showStatus which
* immediately deletes the currently selected sessions.
*/
var DELETE_ACTION = {
name : "MANAGE_SESSION.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
deleteSessionsImmediately();
guacNotification.showStatus(false);
}
};
/**
* Immediately deletes the selected sessions, without prompting the user for
* confirmation.
*/
var deleteSessionsImmediately = function deleteSessionsImmediately() {
// Perform deletion
activeConnectionService.deleteActiveConnections(Object.keys(selectedWrappers))
.success(function activeConnectionsDeleted() {
// Remove deleted connections from wrapper array
$scope.wrappers = $scope.wrappers.filter(function activeConnectionStillExists(wrapper) {
return !(wrapper.activeConnection.identifier in selectedWrappers);
});
// Clear selection
selectedWrappers = {};
})
// Notify of any errors
.error(function activeConnectionDeletionFailed(error) {
guacNotification.showStatus({
'className' : 'error',
'title' : 'MANAGE_SESSION.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
});
};
/**
* Delete all selected sessions, prompting the user first to confirm that
* deletion is desired.
*/
$scope.deleteSessions = function deleteSessions() {
// Confirm deletion request
guacNotification.showStatus({
'title' : 'MANAGE_SESSION.DIALOG_HEADER_CONFIRM_DELETE',
'text' : 'MANAGE_SESSION.TEXT_CONFIRM_DELETE',
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});
};
/**
* Returns whether the selected sessions can be deleted.
*
* @returns {Boolean}
* true if selected sessions can be deleted, false otherwise.
*/
$scope.canDeleteSessions = function canDeleteSessions() {
// We can delete sessions if at least one is selected
for (var identifier in selectedWrappers)
return true;
return false;
};
/**
* Called whenever an active connection wrapper changes selected status.
*
* @param {ActiveConnectionWrapper} wrapper
* The wrapper whose selected status has changed.
*/
$scope.wrapperSelectionChange = function wrapperSelectionChange(wrapper) {
// Add wrapper to map if selected
if (wrapper.checked)
selectedWrappers[wrapper.activeConnection.identifier] = wrapper;
// Otherwise, remove wrapper from map
else
delete selectedWrappers[wrapper.activeConnection.identifier];
};
}]);

View File

@@ -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

View File

@@ -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 = "";
};
}]);

View File

@@ -1,57 +0,0 @@
<!--
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.
-->
<div class="view" ng-class="{loading: !isLoaded()}">
<div class="header">
<h2>{{'MANAGE_CONNECTION.SECTION_HEADER_CONNECTIONS' | translate}}</h2>
<guac-user-menu></guac-user-menu>
</div>
<!-- Connection management -->
<div class="settings section connections">
<p>{{'MANAGE_CONNECTION.HELP_CONNECTIONS' | translate}}</p>
<!-- Form action buttons -->
<div class="action-buttons">
<a class="add-connection button"
ng-show="canCreateConnections"
href="#/manage/connections/">{{'MANAGE_CONNECTION.ACTION_NEW_CONNECTION' | translate}}</a>
<a class="add-connection-group button"
ng-show="canCreateConnectionGroups"
href="#/manage/connectionGroups/">{{'MANAGE_CONNECTION.ACTION_NEW_CONNECTION_GROUP' | translate}}</a>
</div>
<!-- List of accessible connections and groups -->
<div class="connection-list">
<guac-group-list
page-size="25"
connection-group="rootGroup"
connection-template="'app/manage/templates/connection.html'"
connection-group-template="'app/manage/templates/connectionGroup.html'"/>
</div>
</div>
</div>

View File

@@ -1,86 +0,0 @@
<!--
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.
-->
<div class="view" ng-class="{loading: !isLoaded()}">
<div class="header">
<h2>{{'MANAGE_SESSION.SECTION_HEADER_SESSIONS' | translate}}</h2>
<guac-user-menu></guac-user-menu>
</div>
<!-- User Session management -->
<div class="settings section sessions">
<p>{{'MANAGE_SESSION.HELP_SESSIONS' | translate}}</p>
<!-- Form action buttons -->
<div class="action-buttons">
<button class="delete-sessions danger" ng-disabled="!canDeleteSessions()" ng-click="deleteSessions()">{{'MANAGE_SESSION.ACTION_DELETE' | translate}}</button>
</div>
<!-- Session filter -->
<guac-filter filtered-items="filteredWrappers" items="wrappers"
placeholder="'MANAGE_SESSION.FIELD_PLACEHOLDER_FILTER' | translate"
properties="filteredWrapperProperties"></guac-filter>
<!-- List of current user sessions -->
<table class="sorted session-list">
<thead>
<tr>
<th class="select-session"></th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'activeConnection.username'">
{{'MANAGE_SESSION.TABLE_HEADER_SESSION_USERNAME' | translate}}
</th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'startDate'">
{{'MANAGE_SESSION.TABLE_HEADER_SESSION_STARTDATE' | translate}}
</th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'activeConnection.remoteHost'">
{{'MANAGE_SESSION.TABLE_HEADER_SESSION_REMOTEHOST' | translate}}
</th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'name'">
{{'MANAGE_SESSION.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="wrapper in wrapperPage" class="session">
<td class="select-session">
<input ng-change="wrapperSelectionChange(wrapper)" type="checkbox" ng-model="wrapper.checked" />
</td>
<td>{{wrapper.activeConnection.username}}</td>
<td>{{wrapper.startDate}}</td>
<td>{{wrapper.activeConnection.remoteHost}}</td>
<td>{{wrapper.name}}</td>
</tr>
</tbody>
</table>
<!-- Text displayed if no sessions exist -->
<p class="placeholder" ng-hide="wrapperPage.length">
{{'MANAGE_SESSION.INFO_NO_SESSIONS' | translate}}
</p>
<!-- Pager for session list -->
<guac-pager page="wrapperPage" page-size="25"
items="filteredWrappers | orderBy : wrapperOrder.predicate"></guac-pager>
</div>
</div>

View File

@@ -1,56 +0,0 @@
<!--
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.
-->
<div class="view" ng-class="{loading: !isLoaded()}">
<div class="header">
<h2>{{'MANAGE_USER.SECTION_HEADER_USERS' | translate}}</h2>
<guac-user-menu></guac-user-menu>
</div>
<!-- User management -->
<div class="settings section users">
<p>{{'MANAGE_USER.HELP_USERS' | translate}}</p>
<!-- Form action buttons -->
<div class="action-buttons" ng-show="canCreateUsers">
<input type="text" ng-model="newUsername" class="name username" autocorrect="off" autocapitalize="off"/>
<button class="add-user" ng-click="newUser()">{{'MANAGE_USER.ACTION_NEW_USER' | translate}}</button>
</div>
<!-- List of users this user has access to -->
<div class="user-list">
<div ng-repeat="user in userPage" class="user list-item">
<a ng-href="#/manage/users/{{user.username}}">
<div class="caption">
<div class="icon user"></div>
<span class="name">{{user.username}}</span>
</div>
</a>
</div>
</div>
<!-- Pager controls for user list -->
<guac-pager page="userPage" page-size="25" items="users | orderBy : 'username'"></guac-pager>
</div>
</div>

View File

@@ -0,0 +1,74 @@
/*
* 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 which provides a list of links to specific pages.
*/
angular.module('navigation').directive('guacPageList', [function guacPageList() {
return {
restrict: 'E',
replace: true,
scope: {
/**
* The array of pages to display.
*
* @type Page[]
*/
pages : '='
},
templateUrl: 'app/navigation/templates/guacPageList.html',
controller: ['$scope', '$injector', function guacPageListController($scope, $injector) {
// Get required services
var $location = $injector.get('$location');
/**
* Navigate to the given page.
*
* @param {Page} page
* The page to navigate to.
*/
$scope.navigateToPage = function navigateToPage(page) {
$location.path(page.url);
};
/**
* Tests whether the given page is the page currently being viewed.
*
* @param {Page} page
* The page to test.
*
* @returns {Boolean}
* true if the given page is the current page, false otherwise.
*/
$scope.isCurrentPage = function isCurrentPage(page) {
return $location.url() === page.url;
};
}] // end controller
};
}]);

View File

@@ -214,29 +214,6 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu()
};
/**
* Navigate to the given page.
*
* @param {Page} page
* The page to navigate to.
*/
$scope.navigateToPage = function navigateToPage(page) {
$location.path(page.url);
};
/**
* Tests whether the given page should be disabled.
*
* @param {Page} page
* The page to test.
*
* @returns {Boolean}
* true if the given page should be disabled, false otherwise.
*/
$scope.isPageDisabled = function isPageDisabled(page) {
return $location.url() === page.url;
};
/**
* Logs out the current user, redirecting them to back to the login
* screen after logout completes.

View File

@@ -137,20 +137,16 @@ angular.module('navigation').factory('userPageService', ['$injector',
};
/**
* Returns all the main pages that the current user can visit. This can
* include the home page, manage pages, etc. In the case that there are no
* applicable pages of this sort, it may return a client page.
* Returns all settings pages that the current user can visit. This can
* include any of the various manage pages.
*
* @param {ConnectionGroup} rootGroup
* The root of the connection group tree for the current user.
*
* @param {PermissionSet} permissions
* The permissions for the current user.
*
* @returns {Page[]}
* An array of all main pages that the current user can visit.
* An array of all settings pages that the current user can visit.
*/
var generateMainPages = function generateMainPages(rootGroup, permissions) {
var generateSettingsPages = function generateSettingsPages(permissions) {
var pages = [];
@@ -202,18 +198,20 @@ angular.module('navigation').factory('userPageService', ['$injector',
// A user must be a system administrator to manage sessions
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER);
// Only include the home page in the list of main pages if the user
// can navigate elsewhere.
var homePage = generateHomePage(rootGroup);
if (homePage === SYSTEM_HOME_PAGE)
pages.push(homePage);
// If user can manage sessions, add link to sessions management page
if (canManageSessions) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_SESSIONS',
'/settings/sessions'
));
}
// If user can manage users, add link to user management page
if (canManageUsers) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_USERS',
'/manage/modules/users/'
'/settings/users'
));
}
@@ -221,15 +219,69 @@ angular.module('navigation').factory('userPageService', ['$injector',
if (canManageConnections) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_CONNECTIONS',
'/manage/modules/connections/'
'/settings/connections'
));
}
return pages;
};
/**
* Returns a promise which resolves to an array of all settings pages that
* the current user can visit. This can include any of the various manage
* pages.
*
* @returns {Promise.<Page[]>}
* A promise which resolves to an array of all settings pages that the
* current user can visit.
*/
service.getSettingsPages = function getSettingsPages() {
var deferred = $q.defer();
// Retrieve current permissions, resolving main pages if possible
// Resolve promise using settings pages derived from permissions
permissionService.getPermissions(authenticationService.getCurrentUserID())
.success(function permissionsRetrieved(permissions) {
deferred.resolve(generateSettingsPages(permissions));
});
// If user can manage sessions, add link to sessions management page
if (canManageSessions) {
return deferred.promise;
};
/**
* Returns all the main pages that the current user can visit. This can
* include the home page, manage pages, etc. In the case that there are no
* applicable pages of this sort, it may return a client page.
*
* @param {ConnectionGroup} rootGroup
* The root of the connection group tree for the current user.
*
* @param {PermissionSet} permissions
* The permissions for the current user.
*
* @returns {Page[]}
* An array of all main pages that the current user can visit.
*/
var generateMainPages = function generateMainPages(rootGroup, permissions) {
var pages = [];
// Get home page and settings pages
var homePage = generateHomePage(rootGroup);
var settingsPages = generateSettingsPages(permissions);
// Only include the home page in the list of main pages if the user
// can navigate elsewhere.
if (homePage === SYSTEM_HOME_PAGE || settingsPages.length)
pages.push(homePage);
// Add generic link to the first-available settings page
if (settingsPages.length) {
pages.push(new Page(
'USER_MENU.ACTION_MANAGE_SESSIONS',
'/manage/modules/sessions/'
'USER_MENU.ACTION_MANAGE_SETTINGS',
settingsPages[0].url
));
}

View File

@@ -139,8 +139,6 @@
top: 100%;
right: 0;
left: -1px;
margin: 0;
padding: 0;
background: #EEE;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.125);
@@ -151,6 +149,11 @@
}
.user-menu .options ul {
margin: 0;
padding: 0;
}
.user-menu .user-menu-dropdown.open .options {
visibility: visible;
}
@@ -161,50 +164,40 @@
}
.user-menu .options li a {
display: block;
cursor: pointer;
color: black;
text-decoration: none;
padding: 0.75em;
background-repeat: no-repeat;
background-size: 1em;
background-position: 0.75em center;
padding-left: 2.5em;
background-image: url('images/protocol-icons/guac-monitor.png');
}
.user-menu .options li a:hover {
background-color: #CDA;
}
.user-menu .options li a.disabled,
.user-menu .options li a.disabled:hover {
.user-menu .options li a.current,
.user-menu .options li a.current:hover {
background-color: transparent;
cursor: default;
opacity: 0.25;
}
.user-menu .options li a.home,
.user-menu .options li a.manage-users,
.user-menu .options li a.manage-connections,
.user-menu .options li a.manage-sessions,
.user-menu .options li a.change-password,
.user-menu .options li a.logout {
background-repeat: no-repeat;
background-size: 1em;
background-position: 0.75em center;
padding-left: 2.5em;
}
.user-menu .options li a[href="#/"] {
background-image: url('images/action-icons/guac-home-dark.png');
}
.user-menu .options li a[href="#/manage/modules/users/"] {
background-image: url('images/user-icons/guac-user.png');
}
.user-menu .options li a[href="#/manage/modules/connections/"] {
background-image: url('images/protocol-icons/guac-monitor.png');
}
.user-menu .options li a[href="#/manage/modules/sessions/"] {
background-image: url('images/protocol-icons/guac-plug.png');
.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');
}
.user-menu .options li a.change-password {

View File

@@ -0,0 +1,32 @@
<ul class="page-list">
<!--
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.
-->
<!-- Navigation links -->
<li ng-repeat="page in pages">
<a class="home" ng-click="navigateToPage(page)"
ng-class="{current: isCurrentPage(page)}" href="#{{page.url}}">
{{page.name | translate}}
</a>
</li>
</ul>

View File

@@ -26,31 +26,30 @@
<div class="menu-indicator"></div>
<!-- Menu options -->
<ul class="options">
<div class="options">
<!-- Local actions -->
<li ng-repeat="action in localActions">
<a ng-class="action.className" ng-click="action.callback()">
{{action.name | translate}}
</a>
</li>
<ul class="action-list">
<li ng-repeat="action in localActions">
<a ng-class="action.className" ng-click="action.callback()">
{{action.name | translate}}
</a>
</li>
</ul>
<!-- Navigation links -->
<li ng-repeat="page in pages">
<a class="home" ng-click="navigateToPage(page)"
ng-class="{disabled: isPageDisabled(page)}" href="#{{page.url}}">
{{page.name | translate}}
</a>
</li>
<guac-page-list pages="pages"></guac-page-list>
<!-- Actions -->
<li ng-repeat="action in actions">
<a ng-class="action.className" ng-click="action.callback()">
{{action.name | translate}}
</a>
</li>
<ul class="action-list">
<li ng-repeat="action in actions">
<a ng-class="action.className" ng-click="action.callback()">
{{action.name | translate}}
</a>
</li>
</ul>
</ul>
</div>
</div>
<!-- Password dialog -->

View File

@@ -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;
});
}]);

View File

@@ -0,0 +1,170 @@
/*
* 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.
*/
/**
* A directive for managing all connections and connection groups in the system.
*/
angular.module('settings').directive('guacSettingsConnections', [function guacSettingsConnections() {
return {
// Element only
restrict: 'E',
replace: true,
scope: {
},
templateUrl: 'app/settings/templates/settingsConnections.html',
controller: ['$scope', '$injector', function settingsConnectionsController($scope, $injector) {
// Required types
var ConnectionGroup = $injector.get('ConnectionGroup');
var PermissionSet = $injector.get('PermissionSet');
// Required services
var $location = $injector.get('$location');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
// 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 : "SETTINGS_CONNECTIONS.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
guacNotification.showStatus(false);
}
};
/**
* The root connection group of the connection group hierarchy.
*
* @type ConnectionGroup
*/
$scope.rootGroup = null;
/**
* Whether the current user can manage connections. If the current
* permissions have not yet been loaded, this will be null.
*
* @type Boolean
*/
$scope.canManageConnections = null;
/**
* Whether the current user can create new connections. If the
* current permissions have not yet been loaded, this will be null.
*
* @type Boolean
*/
$scope.canCreateConnections = null;
/**
* Whether the current user can create new connection groups. If
* the current permissions have not yet been loaded, this will be
* null.
*
* @type Boolean
*/
$scope.canCreateConnectionGroups = null;
/**
* 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.rootGroup !== null
&& $scope.permissions !== null
&& $scope.canManageConnections !== null
&& $scope.canCreateConnections !== null
&& $scope.canCreateConnectionGroups !== null;
};
// Retrieve current permissions
permissionService.getPermissions(currentUserID)
.success(function permissionsRetrieved(permissions) {
$scope.permissions = permissions;
// Ignore permission to update root group
PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER);
// Determine whether the current user can create new users
$scope.canCreateConnections =
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION);
// Determine whether the current user can create new users
$scope.canCreateConnectionGroups =
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP);
// Determine whether the current user can manage other connections or groups
$scope.canManageConnections =
// Permission to manage connections
$scope.canCreateConnections
|| PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
|| PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE)
// Permission to manage groups
|| $scope.canCreateConnectionGroups
|| PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
|| PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE);
// Return to home if there's nothing to do here
if (!$scope.canManageConnections)
$location.path('/');
});
// Retrieve all connections for which we have UPDATE or DELETE permission
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER,
[PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE])
.success(function connectionGroupReceived(rootGroup) {
$scope.rootGroup = rootGroup;
});
}]
};
}]);

View File

@@ -0,0 +1,367 @@
/*
* 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 active Guacamole sessions.
*/
angular.module('settings').directive('guacSettingsSessions', [function guacSettingsSessions() {
return {
// Element only
restrict: 'E',
replace: true,
scope: {
},
templateUrl: 'app/settings/templates/settingsSessions.html',
controller: ['$scope', '$injector', function settingsSessionsController($scope, $injector) {
// Required types
var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper');
var ConnectionGroup = $injector.get('ConnectionGroup');
var SortOrder = $injector.get('SortOrder');
// Required services
var $filter = $injector.get('$filter');
var $translate = $injector.get('$translate');
var activeConnectionService = $injector.get('activeConnectionService');
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var guacNotification = $injector.get('guacNotification');
var permissionService = $injector.get('permissionService');
/**
* All permissions associated with the current user, or null if the
* user's permissions have not yet been loaded.
*
* @type PermissionSet
*/
$scope.permissions = null;
/**
* The ActiveConnectionWrappers of all active sessions accessible
* by the current user, or null if the active sessions have not yet
* been loaded.
*
* @type ActiveConnectionWrapper[]
*/
$scope.wrappers = null;
/**
* SortOrder instance which maintains the sort order of the visible
* connection wrappers.
*
* @type SortOrder
*/
$scope.wrapperOrder = new SortOrder([
'activeConnection.username',
'startDate',
'activeConnection.remoteHost',
'name'
]);
/**
* Array of all wrapper properties that are filterable.
*
* @type String[]
*/
$scope.filteredWrapperProperties = [
'activeConnection.username',
'startDate',
'activeConnection.remoteHost',
'name'
];
/**
* All active connections, if known, or null if active connections
* have not yet been loaded.
*
* @type ActiveConnection
*/
var activeConnections = null;
/**
* Map of all visible connections by object identifier, or null if
* visible connections have not yet been loaded.
*
* @type Object.<String, Connection>
*/
var connections = null;
/**
* The date format for use for session-related dates.
*
* @type String
*/
var sessionDateFormat = null;
/**
* Map of all currently-selected active connection wrappers by
* identifier.
*
* @type Object.<String, ActiveConnectionWrapper>
*/
var selectedWrappers = {};
/**
* Adds the given connection to the internal set of visible
* connections.
*
* @param {Connection} connection
* The connection to add to the internal set of visible
* connections.
*/
var addConnection = function addConnection(connection) {
// Add given connection to set of visible connections
connections[connection.identifier] = connection;
};
/**
* Adds all descendant connections of the given connection group to
* the internal set of connections.
*
* @param {ConnectionGroup} connectionGroup
* The connection group whose descendant connections should be
* added to the internal set of connections.
*/
var addDescendantConnections = function addDescendantConnections(connectionGroup) {
// Add all child connections
if (connectionGroup.childConnections)
connectionGroup.childConnections.forEach(addConnection);
// Add all child connection groups
if (connectionGroup.childConnectionGroups)
connectionGroup.childConnectionGroups.forEach(addDescendantConnections);
};
/**
* Wraps all loaded active connections, storing the resulting array
* within the scope. If required data has not yet finished loading,
* this function has no effect.
*/
var wrapActiveConnections = function wrapActiveConnections() {
// Abort if not all required data is available
if (!activeConnections || !connections || !sessionDateFormat)
return;
// Wrap all active connections for sake of display
$scope.wrappers = [];
for (var identifier in activeConnections) {
var activeConnection = activeConnections[identifier];
var connection = connections[activeConnection.connectionIdentifier];
$scope.wrappers.push(new ActiveConnectionWrapper(
connection.name,
$filter('date')(activeConnection.startDate, sessionDateFormat),
activeConnection
));
}
};
// Query the user's permissions
permissionService.getPermissions(authenticationService.getCurrentUserID())
.success(function permissionsReceived(retrievedPermissions) {
$scope.permissions = retrievedPermissions;
});
// Retrieve all connections
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER)
.success(function connectionGroupReceived(retrievedRootGroup) {
// Load connections from retrieved group tree
connections = {};
addDescendantConnections(retrievedRootGroup);
// Attempt to produce wrapped list of active connections
wrapActiveConnections();
});
// Query active sessions
activeConnectionService.getActiveConnections().success(function sessionsRetrieved(retrievedActiveConnections) {
// Store received list
activeConnections = retrievedActiveConnections;
// Attempt to produce wrapped list of active connections
wrapActiveConnections();
});
// Get session date format
$translate('SETTINGS_SESSIONS.FORMAT_STARTDATE').then(function sessionDateFormatReceived(retrievedSessionDateFormat) {
// Store received date format
sessionDateFormat = retrievedSessionDateFormat;
// Attempt to produce wrapped list of active connections
wrapActiveConnections();
});
/**
* 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.wrappers !== null
&& $scope.sessionDateFormat !== null
&& $scope.permissions !== null;
};
/**
* An action to be provided along with the object sent to
* showStatus which closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "SETTINGS_SESSIONS.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to
* showStatus which closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "SETTINGS_SESSIONS.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
guacNotification.showStatus(false);
}
};
/**
* An action to be provided along with the object sent to
* showStatus which immediately deletes the currently selected
* sessions.
*/
var DELETE_ACTION = {
name : "SETTINGS_SESSIONS.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
deleteSessionsImmediately();
guacNotification.showStatus(false);
}
};
/**
* Immediately deletes the selected sessions, without prompting the
* user for confirmation.
*/
var deleteSessionsImmediately = function deleteSessionsImmediately() {
// Perform deletion
activeConnectionService.deleteActiveConnections(Object.keys(selectedWrappers))
.success(function activeConnectionsDeleted() {
// Remove deleted connections from wrapper array
$scope.wrappers = $scope.wrappers.filter(function activeConnectionStillExists(wrapper) {
return !(wrapper.activeConnection.identifier in selectedWrappers);
});
// Clear selection
selectedWrappers = {};
})
// Notify of any errors
.error(function activeConnectionDeletionFailed(error) {
guacNotification.showStatus({
'className' : 'error',
'title' : 'SETTINGS_SESSIONS.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
});
};
/**
* Delete all selected sessions, prompting the user first to
* confirm that deletion is desired.
*/
$scope.deleteSessions = function deleteSessions() {
// Confirm deletion request
guacNotification.showStatus({
'title' : 'SETTINGS_SESSIONS.DIALOG_HEADER_CONFIRM_DELETE',
'text' : 'SETTINGS_SESSIONS.TEXT_CONFIRM_DELETE',
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});
};
/**
* Returns whether the selected sessions can be deleted.
*
* @returns {Boolean}
* true if selected sessions can be deleted, false otherwise.
*/
$scope.canDeleteSessions = function canDeleteSessions() {
// We can delete sessions if at least one is selected
for (var identifier in selectedWrappers)
return true;
return false;
};
/**
* Called whenever an active connection wrapper changes selected
* status.
*
* @param {ActiveConnectionWrapper} wrapper
* The wrapper whose selected status has changed.
*/
$scope.wrapperSelectionChange = function wrapperSelectionChange(wrapper) {
// Add wrapper to map if selected
if (wrapper.checked)
selectedWrappers[wrapper.activeConnection.identifier] = wrapper;
// Otherwise, remove wrapper from map
else
delete selectedWrappers[wrapper.activeConnection.identifier];
};
}]
};
}]);

View File

@@ -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('guacSettingsUsers', [function guacSettingsUsers() {
return {
// Element only
restrict: 'E',
replace: true,
scope: {
},
templateUrl: 'app/settings/templates/settingsUsers.html',
controller: ['$scope', '$injector', function settingsUsersController($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 : "SETTINGS_USERS.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' : 'SETTINGS_USERS.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
});
// Reset username
$scope.newUsername = "";
};
}]
};
}]);

View File

@@ -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'
]);

View File

@@ -20,15 +20,15 @@
* THE SOFTWARE.
*/
.manage table.session-list {
.settings table.session-list {
width: 100%;
}
.manage table.session-list tr.session:hover {
.settings table.session-list tr.session:hover {
background: #CDA;
}
.manage table.session-list .select-session {
.settings table.session-list .select-session {
min-width: 2em;
text-align: center;
}

View File

@@ -0,0 +1,69 @@
/*
* 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 table.properties th {
text-align: left;
font-weight: normal;
padding-right: 1em;
}
.settings .action-buttons {
text-align: center;
margin-bottom: 1em;
}
.settings-tabs .page-list {
margin: 0;
padding: 0;
background: rgba(0, 0, 0, 0.0125);
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.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;
}

View File

@@ -33,7 +33,7 @@
<!-- Active user count -->
<span class="activeUserCount" ng-show="item.getActiveConnections()">
{{'MANAGE_CONNECTION.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.getActiveConnections()}'}}
{{'SETTINGS_CONNECTIONS.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.getActiveConnections()}'}}
</span>
</div>

View File

@@ -0,0 +1,40 @@
<!--
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.
-->
<div class="view">
<div class="header">
<h2>{{'SETTINGS.SECTION_HEADER_SETTINGS' | translate}}</h2>
<guac-user-menu></guac-user-menu>
</div>
<!-- Available tabs -->
<div class="settings-tabs">
<guac-page-list pages="settingsPages"></guac-page-list>
</div>
<!-- Selected tab -->
<guac-settings-users ng-if="activeTab === 'users'"></guac-settings-users>
<guac-settings-connections ng-if="activeTab === 'connections'"></guac-settings-connections>
<guac-settings-sessions ng-if="activeTab === 'sessions'"></guac-settings-sessions>
</div>

View File

@@ -0,0 +1,48 @@
<div class="settings section connections" ng-class="{loading: !isLoaded()}">
<!--
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.
-->
<!-- Connection management -->
<p>{{'SETTINGS_CONNECTIONS.HELP_CONNECTIONS' | translate}}</p>
<!-- Form action buttons -->
<div class="action-buttons">
<a class="add-connection button"
ng-show="canCreateConnections"
href="#/manage/connections/">{{'SETTINGS_CONNECTIONS.ACTION_NEW_CONNECTION' | translate}}</a>
<a class="add-connection-group button"
ng-show="canCreateConnectionGroups"
href="#/manage/connectionGroups/">{{'SETTINGS_CONNECTIONS.ACTION_NEW_CONNECTION_GROUP' | translate}}</a>
</div>
<!-- List of accessible connections and groups -->
<div class="connection-list">
<guac-group-list
page-size="25"
connection-group="rootGroup"
connection-template="'app/settings/templates/connection.html'"
connection-group-template="'app/settings/templates/connectionGroup.html'"/>
</div>
</div>

View File

@@ -0,0 +1,77 @@
<div class="settings section sessions" ng-class="{loading: !isLoaded()}">
<!--
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.
-->
<!-- User Session management -->
<p>{{'SETTINGS_SESSIONS.HELP_SESSIONS' | translate}}</p>
<!-- Form action buttons -->
<div class="action-buttons">
<button class="delete-sessions danger" ng-disabled="!canDeleteSessions()" ng-click="deleteSessions()">{{'SETTINGS_SESSIONS.ACTION_DELETE' | translate}}</button>
</div>
<!-- Session filter -->
<guac-filter filtered-items="filteredWrappers" items="wrappers"
placeholder="'SETTINGS_SESSIONS.FIELD_PLACEHOLDER_FILTER' | translate"
properties="filteredWrapperProperties"></guac-filter>
<!-- List of current user sessions -->
<table class="sorted session-list">
<thead>
<tr>
<th class="select-session"></th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'activeConnection.username'">
{{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_USERNAME' | translate}}
</th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'startDate'">
{{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_STARTDATE' | translate}}
</th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'activeConnection.remoteHost'">
{{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_REMOTEHOST' | translate}}
</th>
<th guac-sort-order="wrapperOrder" guac-sort-property="'name'">
{{'SETTINGS_SESSIONS.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="wrapper in wrapperPage" class="session">
<td class="select-session">
<input ng-change="wrapperSelectionChange(wrapper)" type="checkbox" ng-model="wrapper.checked" />
</td>
<td>{{wrapper.activeConnection.username}}</td>
<td>{{wrapper.startDate}}</td>
<td>{{wrapper.activeConnection.remoteHost}}</td>
<td>{{wrapper.name}}</td>
</tr>
</tbody>
</table>
<!-- Text displayed if no sessions exist -->
<p class="placeholder" ng-hide="wrapperPage.length">
{{'SETTINGS_SESSIONS.INFO_NO_SESSIONS' | translate}}
</p>
<!-- Pager for session list -->
<guac-pager page="wrapperPage" page-size="25"
items="filteredWrappers | orderBy : wrapperOrder.predicate"></guac-pager>
</div>

View File

@@ -0,0 +1,48 @@
<div class="settings section users" ng-class="{loading: !isLoaded()}">
<!--
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.
-->
<!-- User management -->
<p>{{'SETTINGS_USERS.HELP_USERS' | translate}}</p>
<!-- Form action buttons -->
<div class="action-buttons" ng-show="canCreateUsers">
<input type="text" ng-model="newUsername" class="name username" autocorrect="off" autocapitalize="off"/>
<button class="add-user" ng-click="newUser()">{{'SETTINGS_USERS.ACTION_NEW_USER' | translate}}</button>
</div>
<!-- List of users this user has access to -->
<div class="user-list">
<div ng-repeat="user in userPage" class="user list-item">
<a ng-href="#/manage/users/{{user.username}}">
<div class="caption">
<div class="icon user"></div>
<span class="name">{{user.username}}</span>
</div>
</a>
</div>
</div>
<!-- Pager controls for user list -->
<guac-pager page="userPage" page-size="25" items="users | orderBy : 'username'"></guac-pager>
</div>

View File

@@ -23,7 +23,7 @@
/**
* A service for defining the ActiveConnectionWrapper class.
*/
angular.module('manage').factory('ActiveConnectionWrapper', [
angular.module('settings').factory('ActiveConnectionWrapper', [
function defineActiveConnectionWrapper() {
/**

View File

@@ -10,7 +10,8 @@
"ACTION_DELETE_SESSIONS" : "Kill Sessions",
"ACTION_LOGIN" : "Login",
"ACTION_LOGOUT" : "Logout",
"ACTION_MANAGE_CONNECTIONS" : "Manage Connections",
"ACTION_MANAGE_CONNECTIONS" : "Connections",
"ACTION_MANAGE_SETTINGS" : "Settings",
"ACTION_MANAGE_SESSIONS" : "Active Sessions",
"ACTION_MANAGE_USERS" : "Users",
"ACTION_NAVIGATE_BACK" : "Back",
@@ -154,8 +155,6 @@
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_CLONE" : "@:APP.ACTION_CLONE",
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
"ACTION_NEW_CONNECTION" : "New Connection",
"ACTION_NEW_CONNECTION_GROUP" : "New Group",
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection",
@@ -167,14 +166,10 @@
"FORMAT_HISTORY_START" : "@:APP.FORMAT_DATE_TIME_PRECISE",
"HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.",
"INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
"INFO_CONNECTION_DURATION_UNKNOWN" : "--",
"INFO_CONNECTION_ACTIVE_NOW" : "Active Now",
"INFO_CONNECTION_NOT_USED" : "This connection has not yet been used.",
"SECTION_HEADER_CONNECTIONS" : "Manage Connections",
"SECTION_HEADER_EDIT_CONNECTION" : "Edit Connection",
"SECTION_HEADER_HISTORY" : "Usage History",
"SECTION_HEADER_PARAMETERS" : "Parameters",
@@ -216,7 +211,6 @@
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
"ACTION_NEW_USER" : "New User",
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete User",
@@ -233,45 +227,14 @@
"FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
"FIELD_HEADER_USERNAME" : "Username:",
"HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.",
"SECTION_HEADER_CONNECTIONS" : "Connections",
"SECTION_HEADER_EDIT_USER" : "Edit User",
"SECTION_HEADER_PERMISSIONS" : "Permissions",
"SECTION_HEADER_USERS" : "Users",
"TEXT_CONFIRM_DELETE" : "Users cannot be restored after they have been deleted. Are you sure you want to delete this user?"
},
"MANAGE_SESSION" : {
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_DELETE" : "Kill Sessions",
"DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions",
"DIALOG_HEADER_ERROR" : "Error",
"FIELD_PLACEHOLDER_FILTER" : "Filter",
"FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
"HELP_SESSIONS" : "All currently-active Guacamole sessions are listed here. If you wish to kill one or more sessions, check the box next to those sessions and click \"Kill Sessions\". Killing a session will immediately disconnect the user from the associated connection.",
"INFO_NO_SESSIONS" : "No active sessions",
"SECTION_HEADER_SESSIONS" : "Active Sessions",
"TABLE_HEADER_SESSION_USERNAME" : "Username",
"TABLE_HEADER_SESSION_STARTDATE" : "Active since",
"TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host",
"TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name",
"TEXT_CONFIRM_DELETE" : "Are you sure you want to kill all selected sessions? The users using these sessions will be immediately disconnected."
},
"PROTOCOL_RDP" : {
"FIELD_HEADER_COLOR_DEPTH" : "Color depth:",
@@ -414,6 +377,69 @@
},
"SETTINGS" : {
"SECTION_HEADER_SETTINGS" : "Settings"
},
"SETTINGS_CONNECTIONS" : {
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_NEW_CONNECTION" : "New Connection",
"ACTION_NEW_CONNECTION_GROUP" : "New Group",
"DIALOG_HEADER_ERROR" : "Error",
"HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.",
"INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
"SECTION_HEADER_CONNECTIONS" : "Connections"
},
"SETTINGS_USERS" : {
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_NEW_USER" : "New User",
"DIALOG_HEADER_ERROR" : "Error",
"HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.",
"SECTION_HEADER_USERS" : "Users"
},
"SETTINGS_SESSIONS" : {
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_DELETE" : "Kill Sessions",
"DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions",
"DIALOG_HEADER_ERROR" : "Error",
"FIELD_PLACEHOLDER_FILTER" : "Filter",
"FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
"HELP_SESSIONS" : "All currently-active Guacamole sessions are listed here. If you wish to kill one or more sessions, check the box next to those sessions and click \"Kill Sessions\". Killing a session will immediately disconnect the user from the associated connection.",
"INFO_NO_SESSIONS" : "No active sessions",
"SECTION_HEADER_SESSIONS" : "Active Sessions",
"TABLE_HEADER_SESSION_USERNAME" : "Username",
"TABLE_HEADER_SESSION_STARTDATE" : "Active since",
"TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host",
"TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name",
"TEXT_CONFIRM_DELETE" : "Are you sure you want to kill all selected sessions? The users using these sessions will be immediately disconnected."
},
"USER_MENU" : {
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
@@ -422,6 +448,7 @@
"ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT",
"ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS",
"ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS",
"ACTION_MANAGE_SETTINGS" : "@:APP.ACTION_MANAGE_SETTINGS",
"ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS",
"ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME",
"ACTION_SAVE" : "@:APP.ACTION_SAVE",