GUACAMOLE-220: Migrate connection group management screen to common buttons and permission logic. Add required clone option.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/7e1dbf7d Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/7e1dbf7d Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/7e1dbf7d Branch: refs/heads/master Commit: 7e1dbf7d11f51f7acd8b3ad39e30695bbda15abd Parents: 00fee4a Author: Michael Jumper <mjum...@apache.org> Authored: Tue May 1 11:54:38 2018 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Tue May 1 20:58:28 2018 -0700 ---------------------------------------------------------------------- .../manageConnectionGroupController.js | 321 ++++++++++--------- .../manage/templates/manageConnectionGroup.html | 14 +- guacamole/src/main/webapp/translations/en.json | 1 + 3 files changed, 174 insertions(+), 162 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7e1dbf7d/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js index de29aff..842c395 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js @@ -24,15 +24,16 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' function manageConnectionGroupController($scope, $injector) { // Required types - var ConnectionGroup = $injector.get('ConnectionGroup'); - var PermissionSet = $injector.get('PermissionSet'); + var ConnectionGroup = $injector.get('ConnectionGroup'); + var ManagementPermissions = $injector.get('ManagementPermissions'); + var PermissionSet = $injector.get('PermissionSet'); // Required services var $location = $injector.get('$location'); + var $q = $injector.get('$q'); var $routeParams = $injector.get('$routeParams'); var authenticationService = $injector.get('authenticationService'); var connectionGroupService = $injector.get('connectionGroupService'); - var guacNotification = $injector.get('guacNotification'); var permissionService = $injector.get('permissionService'); var requestService = $injector.get('requestService'); var schemaService = $injector.get('schemaService'); @@ -46,6 +47,15 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' $scope.selectedDataSource = $routeParams.dataSource; /** + * The identifier of the original connection group from which this + * connection group is being cloned. Only valid if this is a new + * connection group. + * + * @type String + */ + var cloneSourceIdentifier = $location.search().clone; + + /** * The identifier of the connection group being edited. If a new connection * group is being created, this will not be defined. * @@ -54,6 +64,23 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' var identifier = $routeParams.id; /** + * Available connection group types, as translation string / internal value + * pairs. + * + * @type Object[] + */ + $scope.types = [ + { + label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_ORGANIZATIONAL", + value: ConnectionGroup.Type.ORGANIZATIONAL + }, + { + label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_BALANCING", + value : ConnectionGroup.Type.BALANCING + } + ]; + + /** * The root connection group of the connection group hierarchy. * * @type ConnectionGroup @@ -68,26 +95,13 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' $scope.connectionGroup = null; /** - * Whether the user has UPDATE permission for the current connection group. - * - * @type Boolean - */ - $scope.hasUpdatePermission = null; - - /** - * Whether the user has DELETE permission for the current connection group. - * - * @type Boolean - */ - $scope.hasDeletePermission = null; - - /** - * All permissions associated with the current user, or null if the user's - * permissions have not yet been loaded. + * The managment-related actions that the current user may perform on the + * connection group currently being created/modified, or null if the current + * user's permissions have not yet been loaded. * - * @type PermissionSet + * @type ManagementPermissions */ - $scope.permissions = null; + $scope.managementPermissions = null; /** * All available connection group attributes. This is only the set of @@ -109,177 +123,172 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' return $scope.rootGroup !== null && $scope.connectionGroup !== null - && $scope.permissions !== null - && $scope.attributes !== null - && $scope.canSaveConnectionGroup !== null - && $scope.canDeleteConnectionGroup !== null; + && $scope.managementPermissions !== null + && $scope.attributes !== null; }; - - // Pull connection group attribute schema - schemaService.getConnectionGroupAttributes($scope.selectedDataSource) - .then(function attributesReceived(attributes) { - $scope.attributes = attributes; - }, requestService.WARN); - - // Query the user's permissions for the current connection group - permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) - .then(function permissionsReceived(permissions) { - - $scope.permissions = permissions; - - // Check if the connection group is new or if the user has UPDATE permission - $scope.canSaveConnectionGroup = - !identifier - || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier); - - // Check if connection group is not new and the user has DELETE permission - $scope.canDeleteConnectionGroup = - !!identifier && ( - PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier) - ); - - }, requestService.WARN); - - - // Pull connection group hierarchy - connectionGroupService.getConnectionGroupTree( - $scope.selectedDataSource, - ConnectionGroup.ROOT_IDENTIFIER, - [PermissionSet.ObjectPermissionType.ADMINISTER] - ) - .then(function connectionGroupReceived(rootGroup) { - $scope.rootGroup = rootGroup; - }, requestService.WARN); - // If we are editing an existing connection group, pull its data - if (identifier) { - connectionGroupService.getConnectionGroup($scope.selectedDataSource, identifier) + /** + * Loads the data associated with the connection group having the given + * identifier, preparing the interface for making modifications to that + * existing connection group. + * + * @param {String} dataSource + * The unique identifier of the data source containing the connection + * group to load. + * + * @param {String} identifier + * The identifier of the connection group to load. + * + * @returns {Promise} + * A promise which is resolved when the interface has been prepared for + * editing the given connection group. + */ + var loadExistingConnectionGroup = function loadExistingConnectionGroup(dataSource, identifier) { + return connectionGroupService.getConnectionGroup( + dataSource, + identifier + ) .then(function connectionGroupReceived(connectionGroup) { $scope.connectionGroup = connectionGroup; - }, requestService.WARN); - } - - // If we are creating a new connection group, populate skeleton connection group data - else - $scope.connectionGroup = new ConnectionGroup({ - parentIdentifier : $location.search().parent }); + }; /** - * Available connection group types, as translation string / internal value - * pairs. - * - * @type Object[] + * Loads the data associated with the connection group having the given + * identifier, preparing the interface for cloning that existing + * connection group. + * + * @param {String} dataSource + * The unique identifier of the data source containing the connection + * group to be cloned. + * + * @param {String} identifier + * The identifier of the connection group being cloned. + * + * @returns {Promise} + * A promise which is resolved when the interface has been prepared for + * cloning the given connection group. */ - $scope.types = [ - { - label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_ORGANIZATIONAL", - value: ConnectionGroup.Type.ORGANIZATIONAL - }, - { - label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_BALANCING", - value : ConnectionGroup.Type.BALANCING - } - ]; + var loadClonedConnectionGroup = function loadClonedConnectionGroup(dataSource, identifier) { + return connectionGroupService.getConnectionGroup( + dataSource, + identifier + ) + .then(function connectionGroupReceived(connectionGroup) { + $scope.connectionGroup = connectionGroup; + delete $scope.connectionGroup.identifier; + }); + }; /** - * Returns whether the current user can change/set all connection group - * attributes for the connection group being edited, regardless of whether - * those attributes are already explicitly associated with that connection - * group. + * Loads skeleton connection group data, preparing the interface for + * creating a new connection group. * - * @returns {Boolean} - * true if the current user can change all attributes for the - * connection group being edited, regardless of whether those - * attributes are already explicitly associated with that connection - * group, false otherwise. + * @returns {Promise} + * A promise which is resolved when the interface has been prepared for + * creating a new connection group. */ - $scope.canChangeAllAttributes = function canChangeAllAttributes() { + var loadSkeletonConnectionGroup = function loadSkeletonConnectionGroup() { - // All attributes can be set if we are creating the connection group - return !identifier; + // Use skeleton connection group object with specified parent + $scope.connectionGroup = new ConnectionGroup({ + parentIdentifier : $location.search().parent + }); - }; + return $q.resolve(); - /** - * Cancels all pending edits, returning to the management page. - */ - $scope.cancel = function cancel() { - $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); }; - + /** - * Saves the connection group, creating a new connection group or updating - * the existing connection group. + * Loads the data requred for performing the management task requested + * through the route parameters given at load time, automatically preparing + * the interface for editing an existing connection group, cloning an + * existing connection group, or creating an entirely new connection group. + * + * @returns {Promise} + * A promise which is resolved when the interface has been prepared + * for performing the requested management task. */ - $scope.saveConnectionGroup = function saveConnectionGroup() { + var loadRequestedConnectionGroup = function loadRequestedConnectionGroup() { + + // If we are editing an existing connection group, pull its data + if (identifier) + return loadExistingConnectionGroup($scope.selectedDataSource, identifier); - // Save the connection - connectionGroupService.saveConnectionGroup($scope.selectedDataSource, $scope.connectionGroup) - .then(function savedConnectionGroup() { - $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); - }, guacNotification.SHOW_REQUEST_ERROR); + // If we are cloning an existing connection group, pull its data + // instead + if (cloneSourceIdentifier) + return loadClonedConnectionGroup($scope.selectedDataSource, cloneSourceIdentifier); + + // If we are creating a new connection group, populate skeleton + // connection group data + return loadSkeletonConnectionGroup(); }; - + + // Query the user's permissions for the current connection group + $q.all({ + connectionGroupData : loadRequestedConnectionGroup(), + attributes : schemaService.getConnectionGroupAttributes($scope.selectedDataSource), + permissions : permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()), + rootGroup : connectionGroupService.getConnectionGroupTree($scope.selectedDataSource, ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER]) + }) + .then(function connectionGroupDataRetrieved(values) { + + $scope.attributes = values.attributes; + $scope.rootGroup = values.rootGroup; + + $scope.managementPermissions = ManagementPermissions.fromPermissionSet( + values.permissions, + PermissionSet.SystemPermissionType.CREATE_CONNECTION, + PermissionSet.hasConnectionPermission, + identifier); + + }, requestService.WARN); + /** - * An action to be provided along with the object sent to showStatus which - * immediately deletes the current connection group. + * Cancels all pending edits, returning to the main list of connections + * within the selected data source. */ - var DELETE_ACTION = { - name : "MANAGE_CONNECTION_GROUP.ACTION_DELETE", - className : "danger", - // Handle action - callback : function deleteCallback() { - deleteConnectionGroupImmediately(); - guacNotification.showStatus(false); - } + $scope.returnToConnectionList = function returnToConnectionList() { + $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); }; /** - * An action to be provided along with the object sent to showStatus which - * closes the currently-shown status dialog. + * Cancels all pending edits, opening an edit page for a new connection + * group which is prepopulated with the data from the connection group + * currently being edited. */ - var CANCEL_ACTION = { - name : "MANAGE_CONNECTION_GROUP.ACTION_CANCEL", - // Handle action - callback : function cancelCallback() { - guacNotification.showStatus(false); - } + $scope.cloneConnectionGroup = function cloneConnectionGRoup() { + $location.path('/manage/' + encodeURIComponent($scope.selectedDataSource) + '/connectionGroups').search('clone', identifier); }; /** - * Immediately deletes the current connection group, without prompting the - * user for confirmation. + * Saves the current connection group, creating a new connection group or + * updating the existing connection group, returning a promise which is + * resolved if the save operation succeeds and rejected if the save + * operation fails. + * + * @returns {Promise} + * A promise which is resolved if the save operation succeeds and is + * rejected with an {@link Error} if the save operation fails. */ - var deleteConnectionGroupImmediately = function deleteConnectionGroupImmediately() { - - // Delete the connection group - connectionGroupService.deleteConnectionGroup($scope.selectedDataSource, $scope.connectionGroup) - .then(function deletedConnectionGroup() { - $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); - }, guacNotification.SHOW_REQUEST_ERROR); - + $scope.saveConnectionGroup = function saveConnectionGroup() { + return connectionGroupService.saveConnectionGroup($scope.selectedDataSource, $scope.connectionGroup); }; - + /** - * Deletes the connection group, prompting the user first to confirm that - * deletion is desired. + * Deletes the current connection group, returning a promise which is + * resolved if the delete operation succeeds and rejected if the delete + * operation fails. + * + * @returns {Promise} + * A promise which is resolved if the delete operation succeeds and is + * rejected with an {@link Error} if the delete operation fails. */ $scope.deleteConnectionGroup = function deleteConnectionGroup() { - - // Confirm deletion request - guacNotification.showStatus({ - 'title' : 'MANAGE_CONNECTION_GROUP.DIALOG_HEADER_CONFIRM_DELETE', - 'text' : { - key : 'MANAGE_CONNECTION_GROUP.TEXT_CONFIRM_DELETE' - }, - 'actions' : [ DELETE_ACTION, CANCEL_ACTION] - }); - + return connectionGroupService.deleteConnectionGroup($scope.selectedDataSource, $scope.connectionGroup); }; }]); http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7e1dbf7d/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html index d4c6613..926dc11 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html @@ -41,14 +41,16 @@ <!-- Connection group attributes section --> <div class="attributes"> <guac-form namespace="'CONNECTION_GROUP_ATTRIBUTES'" content="attributes" - model="connectionGroup.attributes" model-only="!canChangeAllAttributes()"></guac-form> + model="connectionGroup.attributes" model-only="!managementPermissions.canChangeAllAttributes"></guac-form> </div> <!-- Form action buttons --> - <div class="action-buttons"> - <button ng-show="canSaveConnectionGroup" ng-click="saveConnectionGroup()">{{'MANAGE_CONNECTION_GROUP.ACTION_SAVE' | translate}}</button> - <button ng-click="cancel()">{{'MANAGE_CONNECTION_GROUP.ACTION_CANCEL' | translate}}</button> - <button ng-show="canDeleteConnectionGroup" ng-click="deleteConnectionGroup()" class="danger">{{'MANAGE_CONNECTION_GROUP.ACTION_DELETE' | translate}}</button> - </div> + <management-buttons namespace="'MANAGE_CONNECTION_GROUP'" + permissions="managementPermissions" + save="saveConnectionGroup()" + delete="deleteConnectionGroup()" + clone="cloneConnectionGroup()" + return="returnToConnectionList()"> + </management-buttons> </div> http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/7e1dbf7d/guacamole/src/main/webapp/translations/en.json ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index a97406e..ca8acb8 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -234,6 +234,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE",