mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
Merge pull request #149 from glyptodon/settings-page
GUAC-1053: Move manage sections into tabbed settings page
This commit is contained in:
@@ -104,6 +104,10 @@ angular.module('form').directive('guacForm', [function form() {
|
|||||||
*/
|
*/
|
||||||
$scope.getSectionHeader = function getSectionHeader(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')
|
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
||||||
+ '.SECTION_HEADER_' + translationStringService.canonicalize(form.name);
|
+ '.SECTION_HEADER_' + translationStringService.canonicalize(form.name);
|
||||||
|
|
||||||
@@ -128,6 +132,10 @@ angular.module('form').directive('guacForm', [function form() {
|
|||||||
*/
|
*/
|
||||||
$scope.getFieldHeader = function getFieldHeader(field) {
|
$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')
|
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
||||||
+ '.FIELD_HEADER_' + translationStringService.canonicalize(field.name);
|
+ '.FIELD_HEADER_' + translationStringService.canonicalize(field.name);
|
||||||
|
|
||||||
|
@@ -131,8 +131,8 @@ angular.module('form').directive('guacFormField', [function formField() {
|
|||||||
*/
|
*/
|
||||||
$scope.getFieldOption = function getFieldOption(value) {
|
$scope.getFieldOption = function getFieldOption(value) {
|
||||||
|
|
||||||
// Don't bother if the model is not yet defined
|
// If no field, or no value, then no corresponding translation string
|
||||||
if (!$scope.field)
|
if (!$scope.field || !$scope.field.name || !value)
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
||||||
|
@@ -125,30 +125,12 @@ angular.module('index').config(['$routeProvider', '$locationProvider',
|
|||||||
resolve : { routeToUserHomePage: routeToUserHomePage }
|
resolve : { routeToUserHomePage: routeToUserHomePage }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Connection management screen
|
// Management screen
|
||||||
.when('/manage/modules/connections/', {
|
.when('/settings/:tab', {
|
||||||
title : 'APP.NAME',
|
title : 'APP.NAME',
|
||||||
bodyClassName : 'manage',
|
bodyClassName : 'settings',
|
||||||
templateUrl : 'app/manage/templates/manageConnections.html',
|
templateUrl : 'app/settings/templates/settings.html',
|
||||||
controller : 'manageConnectionsController',
|
controller : 'settingsController',
|
||||||
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',
|
|
||||||
resolve : { updateCurrentToken: updateCurrentToken }
|
resolve : { updateCurrentToken: updateCurrentToken }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -34,5 +34,6 @@ angular.module('index', [
|
|||||||
'ngTouch',
|
'ngTouch',
|
||||||
'notification',
|
'notification',
|
||||||
'pascalprecht.translate',
|
'pascalprecht.translate',
|
||||||
'rest'
|
'rest',
|
||||||
|
'settings'
|
||||||
]);
|
]);
|
||||||
|
@@ -317,7 +317,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
|
|||||||
* Cancels all pending edits, returning to the management page.
|
* Cancels all pending edits, returning to the management page.
|
||||||
*/
|
*/
|
||||||
$scope.cancel = function cancel() {
|
$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
|
// Save the connection
|
||||||
connectionService.saveConnection($scope.connection)
|
connectionService.saveConnection($scope.connection)
|
||||||
.success(function savedConnection() {
|
.success(function savedConnection() {
|
||||||
$location.path('/manage/modules/connections/');
|
$location.path('/settings/connections');
|
||||||
})
|
})
|
||||||
|
|
||||||
// Notify of any errors
|
// Notify of any errors
|
||||||
@@ -389,7 +389,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
|
|||||||
// Delete the connection
|
// Delete the connection
|
||||||
connectionService.deleteConnection($scope.connection)
|
connectionService.deleteConnection($scope.connection)
|
||||||
.success(function deletedConnection() {
|
.success(function deletedConnection() {
|
||||||
$location.path('/manage/modules/connections/');
|
$location.path('/settings/connections');
|
||||||
})
|
})
|
||||||
|
|
||||||
// Notify of any errors
|
// Notify of any errors
|
||||||
|
@@ -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;
|
|
||||||
});
|
|
||||||
|
|
||||||
}]);
|
|
@@ -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];
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
@@ -472,7 +472,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
|||||||
* Cancels all pending edits, returning to the management page.
|
* Cancels all pending edits, returning to the management page.
|
||||||
*/
|
*/
|
||||||
$scope.cancel = function cancel() {
|
$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
|
// Upon success, save any changed permissions
|
||||||
permissionService.patchPermissions($scope.user.username, permissionsAdded, permissionsRemoved)
|
permissionService.patchPermissions($scope.user.username, permissionsAdded, permissionsRemoved)
|
||||||
.success(function patchedUserPermissions() {
|
.success(function patchedUserPermissions() {
|
||||||
$location.path('/manage/modules/users/');
|
$location.path('/settings/users');
|
||||||
})
|
})
|
||||||
|
|
||||||
// Notify of any errors
|
// Notify of any errors
|
||||||
@@ -560,7 +560,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
|||||||
// Delete the user
|
// Delete the user
|
||||||
userService.deleteUser($scope.user)
|
userService.deleteUser($scope.user)
|
||||||
.success(function deletedUser() {
|
.success(function deletedUser() {
|
||||||
$location.path('/manage/modules/users/');
|
$location.path('/settings/users');
|
||||||
})
|
})
|
||||||
|
|
||||||
// Notify of any errors
|
// Notify of any errors
|
||||||
|
@@ -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 = "";
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -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
|
||||||
|
|
||||||
|
};
|
||||||
|
}]);
|
@@ -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
|
* Logs out the current user, redirecting them to back to the login
|
||||||
* screen after logout completes.
|
* screen after logout completes.
|
||||||
|
@@ -137,20 +137,16 @@ angular.module('navigation').factory('userPageService', ['$injector',
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the main pages that the current user can visit. This can
|
* Returns all settings pages that the current user can visit. This can
|
||||||
* include the home page, manage pages, etc. In the case that there are no
|
* include any of the various manage pages.
|
||||||
* 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
|
* @param {PermissionSet} permissions
|
||||||
* The permissions for the current user.
|
* The permissions for the current user.
|
||||||
*
|
*
|
||||||
* @returns {Page[]}
|
* @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 = [];
|
var pages = [];
|
||||||
|
|
||||||
@@ -202,18 +198,20 @@ angular.module('navigation').factory('userPageService', ['$injector',
|
|||||||
|
|
||||||
// A user must be a system administrator to manage sessions
|
// A user must be a system administrator to manage sessions
|
||||||
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER);
|
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER);
|
||||||
|
|
||||||
// Only include the home page in the list of main pages if the user
|
// If user can manage sessions, add link to sessions management page
|
||||||
// can navigate elsewhere.
|
if (canManageSessions) {
|
||||||
var homePage = generateHomePage(rootGroup);
|
pages.push(new Page(
|
||||||
if (homePage === SYSTEM_HOME_PAGE)
|
'USER_MENU.ACTION_MANAGE_SESSIONS',
|
||||||
pages.push(homePage);
|
'/settings/sessions'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// If user can manage users, add link to user management page
|
// If user can manage users, add link to user management page
|
||||||
if (canManageUsers) {
|
if (canManageUsers) {
|
||||||
pages.push(new Page(
|
pages.push(new Page(
|
||||||
'USER_MENU.ACTION_MANAGE_USERS',
|
'USER_MENU.ACTION_MANAGE_USERS',
|
||||||
'/manage/modules/users/'
|
'/settings/users'
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,15 +219,69 @@ angular.module('navigation').factory('userPageService', ['$injector',
|
|||||||
if (canManageConnections) {
|
if (canManageConnections) {
|
||||||
pages.push(new Page(
|
pages.push(new Page(
|
||||||
'USER_MENU.ACTION_MANAGE_CONNECTIONS',
|
'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
|
return deferred.promise;
|
||||||
if (canManageSessions) {
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(
|
pages.push(new Page(
|
||||||
'USER_MENU.ACTION_MANAGE_SESSIONS',
|
'USER_MENU.ACTION_MANAGE_SETTINGS',
|
||||||
'/manage/modules/sessions/'
|
settingsPages[0].url
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -139,8 +139,6 @@
|
|||||||
top: 100%;
|
top: 100%;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: -1px;
|
left: -1px;
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
background: #EEE;
|
background: #EEE;
|
||||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.125);
|
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 {
|
.user-menu .user-menu-dropdown.open .options {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
@@ -161,50 +164,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-menu .options li a {
|
.user-menu .options li a {
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: black;
|
color: black;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 0.75em;
|
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 {
|
.user-menu .options li a:hover {
|
||||||
background-color: #CDA;
|
background-color: #CDA;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-menu .options li a.disabled,
|
.user-menu .options li a.current,
|
||||||
.user-menu .options li a.disabled:hover {
|
.user-menu .options li a.current:hover {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
opacity: 0.25;
|
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="#/"] {
|
.user-menu .options li a[href="#/"] {
|
||||||
background-image: url('images/action-icons/guac-home-dark.png');
|
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="#/settings/users"],
|
||||||
background-image: url('images/user-icons/guac-user.png');
|
.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[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.change-password {
|
.user-menu .options li a.change-password {
|
||||||
|
@@ -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>
|
@@ -26,31 +26,30 @@
|
|||||||
<div class="menu-indicator"></div>
|
<div class="menu-indicator"></div>
|
||||||
|
|
||||||
<!-- Menu options -->
|
<!-- Menu options -->
|
||||||
<ul class="options">
|
<div class="options">
|
||||||
|
|
||||||
<!-- Local actions -->
|
<!-- Local actions -->
|
||||||
<li ng-repeat="action in localActions">
|
<ul class="action-list">
|
||||||
<a ng-class="action.className" ng-click="action.callback()">
|
<li ng-repeat="action in localActions">
|
||||||
{{action.name | translate}}
|
<a ng-class="action.className" ng-click="action.callback()">
|
||||||
</a>
|
{{action.name | translate}}
|
||||||
</li>
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<!-- Navigation links -->
|
<!-- Navigation links -->
|
||||||
<li ng-repeat="page in pages">
|
<guac-page-list pages="pages"></guac-page-list>
|
||||||
<a class="home" ng-click="navigateToPage(page)"
|
|
||||||
ng-class="{disabled: isPageDisabled(page)}" href="#{{page.url}}">
|
|
||||||
{{page.name | translate}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<li ng-repeat="action in actions">
|
<ul class="action-list">
|
||||||
<a ng-class="action.className" ng-click="action.callback()">
|
<li ng-repeat="action in actions">
|
||||||
{{action.name | translate}}
|
<a ng-class="action.className" ng-click="action.callback()">
|
||||||
</a>
|
{{action.name | translate}}
|
||||||
</li>
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Password dialog -->
|
<!-- Password dialog -->
|
||||||
|
@@ -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;
|
||||||
|
});
|
||||||
|
|
||||||
|
}]);
|
@@ -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;
|
||||||
|
});
|
||||||
|
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
}]);
|
@@ -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];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
}]);
|
@@ -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 = "";
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
}]);
|
33
guacamole/src/main/webapp/app/settings/settingsModule.js
Normal file
33
guacamole/src/main/webapp/app/settings/settingsModule.js
Normal 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'
|
||||||
|
]);
|
@@ -20,15 +20,15 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.manage table.session-list {
|
.settings table.session-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manage table.session-list tr.session:hover {
|
.settings table.session-list tr.session:hover {
|
||||||
background: #CDA;
|
background: #CDA;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manage table.session-list .select-session {
|
.settings table.session-list .select-session {
|
||||||
min-width: 2em;
|
min-width: 2em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
69
guacamole/src/main/webapp/app/settings/styles/settings.css
Normal file
69
guacamole/src/main/webapp/app/settings/styles/settings.css
Normal 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;
|
||||||
|
}
|
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<!-- Active user count -->
|
<!-- Active user count -->
|
||||||
<span class="activeUserCount" ng-show="item.getActiveConnections()">
|
<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>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -23,7 +23,7 @@
|
|||||||
/**
|
/**
|
||||||
* A service for defining the ActiveConnectionWrapper class.
|
* A service for defining the ActiveConnectionWrapper class.
|
||||||
*/
|
*/
|
||||||
angular.module('manage').factory('ActiveConnectionWrapper', [
|
angular.module('settings').factory('ActiveConnectionWrapper', [
|
||||||
function defineActiveConnectionWrapper() {
|
function defineActiveConnectionWrapper() {
|
||||||
|
|
||||||
/**
|
/**
|
@@ -10,7 +10,8 @@
|
|||||||
"ACTION_DELETE_SESSIONS" : "Kill Sessions",
|
"ACTION_DELETE_SESSIONS" : "Kill Sessions",
|
||||||
"ACTION_LOGIN" : "Login",
|
"ACTION_LOGIN" : "Login",
|
||||||
"ACTION_LOGOUT" : "Logout",
|
"ACTION_LOGOUT" : "Logout",
|
||||||
"ACTION_MANAGE_CONNECTIONS" : "Manage Connections",
|
"ACTION_MANAGE_CONNECTIONS" : "Connections",
|
||||||
|
"ACTION_MANAGE_SETTINGS" : "Settings",
|
||||||
"ACTION_MANAGE_SESSIONS" : "Active Sessions",
|
"ACTION_MANAGE_SESSIONS" : "Active Sessions",
|
||||||
"ACTION_MANAGE_USERS" : "Users",
|
"ACTION_MANAGE_USERS" : "Users",
|
||||||
"ACTION_NAVIGATE_BACK" : "Back",
|
"ACTION_NAVIGATE_BACK" : "Back",
|
||||||
@@ -154,8 +155,6 @@
|
|||||||
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
||||||
"ACTION_CLONE" : "@:APP.ACTION_CLONE",
|
"ACTION_CLONE" : "@:APP.ACTION_CLONE",
|
||||||
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
|
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
|
||||||
"ACTION_NEW_CONNECTION" : "New Connection",
|
|
||||||
"ACTION_NEW_CONNECTION_GROUP" : "New Group",
|
|
||||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||||
|
|
||||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection",
|
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection",
|
||||||
@@ -167,14 +166,10 @@
|
|||||||
|
|
||||||
"FORMAT_HISTORY_START" : "@:APP.FORMAT_DATE_TIME_PRECISE",
|
"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_DURATION_UNKNOWN" : "--",
|
||||||
"INFO_CONNECTION_ACTIVE_NOW" : "Active Now",
|
"INFO_CONNECTION_ACTIVE_NOW" : "Active Now",
|
||||||
"INFO_CONNECTION_NOT_USED" : "This connection has not yet been used.",
|
"INFO_CONNECTION_NOT_USED" : "This connection has not yet been used.",
|
||||||
|
|
||||||
"SECTION_HEADER_CONNECTIONS" : "Manage Connections",
|
|
||||||
"SECTION_HEADER_EDIT_CONNECTION" : "Edit Connection",
|
"SECTION_HEADER_EDIT_CONNECTION" : "Edit Connection",
|
||||||
"SECTION_HEADER_HISTORY" : "Usage History",
|
"SECTION_HEADER_HISTORY" : "Usage History",
|
||||||
"SECTION_HEADER_PARAMETERS" : "Parameters",
|
"SECTION_HEADER_PARAMETERS" : "Parameters",
|
||||||
@@ -216,7 +211,6 @@
|
|||||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||||
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
||||||
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
|
"ACTION_DELETE" : "@:APP.ACTION_DELETE",
|
||||||
"ACTION_NEW_USER" : "New User",
|
|
||||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||||
|
|
||||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete User",
|
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete User",
|
||||||
@@ -233,45 +227,14 @@
|
|||||||
"FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
|
"FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
|
||||||
"FIELD_HEADER_USERNAME" : "Username:",
|
"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_CONNECTIONS" : "Connections",
|
||||||
"SECTION_HEADER_EDIT_USER" : "Edit User",
|
"SECTION_HEADER_EDIT_USER" : "Edit User",
|
||||||
"SECTION_HEADER_PERMISSIONS" : "Permissions",
|
"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?"
|
"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" : {
|
"PROTOCOL_RDP" : {
|
||||||
|
|
||||||
"FIELD_HEADER_COLOR_DEPTH" : "Color depth:",
|
"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" : {
|
"USER_MENU" : {
|
||||||
|
|
||||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||||
@@ -422,6 +448,7 @@
|
|||||||
"ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT",
|
"ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT",
|
||||||
"ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS",
|
"ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS",
|
||||||
"ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS",
|
"ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS",
|
||||||
|
"ACTION_MANAGE_SETTINGS" : "@:APP.ACTION_MANAGE_SETTINGS",
|
||||||
"ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS",
|
"ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS",
|
||||||
"ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME",
|
"ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME",
|
||||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||||
|
Reference in New Issue
Block a user