You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@guacamole.apache.org by mike-jumper <gi...@git.apache.org> on 2018/05/02 03:15:46 UTC

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

GitHub user mike-jumper opened a pull request:

    https://github.com/apache/guacamole-client/pull/282

    GUACAMOLE-220: Extract common base for management UI.

    A necessary basis for the user group management interface (which adds yet another management tab), these changes extract the common base code used by the various existing management tabs, which up until now has been largely duplicated.
    
    New directives have been added to take care of data source tabs, permission management, and the save/cancel/clone/delete buttons. The permission test logic driving those buttons and other parts of each management tab has been moved into common class, as well.
    
    This is a net increase in code due to the amount of documentation added, and a massive net decrease in the amount of code within the management controllers.

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/mike-jumper/guacamole-client extract-common-ui

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/guacamole-client/pull/282.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #282
    
----
commit 94ad1f9f349826ede932ea843ed87f58cc76d598
Author: Michael Jumper <mj...@...>
Date:   2018-04-29T06:05:27Z

    GUACAMOLE-220: Separate system/connection permission editing into directives.

commit 507202d1f3c6b5e96e9f2a6d6f341331af9d34c0
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T06:24:15Z

    GUACAMOLE-220: Define abstract object for querying the management-related actions a user may take on a particular object or type of object.

commit 4f43ddc4203a59c7aea6834cfd8b5961c74ba57e
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T07:03:52Z

    GUACAMOLE-220: Migrate user management controller to ManagementPermissions.

commit e045da132c729d0567f256708d204d607c053f9f
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T16:46:46Z

    GUACAMOLE-220: Add common directive for displaying the save/clone/cancel/delete buttons shared by all object management pages.

commit 0414cdd3edc4f5ba11900750ed26782e335de7b2
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T16:53:26Z

    GUACAMOLE-220: Migrate user management screen to new, common management button directive.

commit 331f46d61010e38cfeb8acb5cb3d4b40fde10de2
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T18:22:31Z

    GUACAMOLE-220: Migrate connection management screen to common buttons and permission logic.

commit a21c7f1a84a793135ffa27bb1e2b565f6dcd1805
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T18:54:38Z

    GUACAMOLE-220: Migrate connection group management screen to common buttons and permission logic. Add required clone option.

commit bf4fdd6a9bd11fce7da0419e56dc502da6956c5c
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T19:24:33Z

    GUACAMOLE-220: Migrate sharing profile management screen to common buttons and permission logic.

commit 5ab4ae8625e00bbdbb69bdd06eda19903e46d927
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T19:42:36Z

    GUACAMOLE-220: Move common protocol namespace/name retrieval to Protocol class.

commit e071ceaedd3da4439f97a156fc0bdfac23b93b5c
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T23:29:25Z

    GUACAMOLE-220: "Management", not "managment".

commit b1600bf13ddf015eae64fe5acdf0463e462412cb
Author: Michael Jumper <mj...@...>
Date:   2018-05-01T23:39:02Z

    GUACAMOLE-220: Include the identifier of the associated object within ManagementPermissions.

commit 8f6dd666f0f603242615ebbdab20c283d8f9f2b7
Author: Michael Jumper <mj...@...>
Date:   2018-05-02T00:18:48Z

    GUACAMOLE-220: Move data source tabs to separate directive.

commit b0a625cdeac44be74fa3a75cec317f10e6620bca
Author: Michael Jumper <mj...@...>
Date:   2018-05-02T00:19:40Z

    GUACAMOLE-220: Remove unnecessary userExists() function.

----


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185388716
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    --- End diff --
    
    I can restore the original usage of built-in `forEach()` if you prefer.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by jmuehlner <gi...@git.apache.org>.
Github user jmuehlner commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185387927
  
    --- Diff: guacamole/src/main/webapp/app/manage/templates/manageConnection.html ---
    @@ -41,7 +41,7 @@ <h2>{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}</h2>
         <!-- Connection attributes section -->
         <div class="attributes">
             <guac-form namespace="'CONNECTION_ATTRIBUTES'" content="attributes"
    -                   model="connection.attributes" model-only="!canChangeAllAttributes()"></guac-form>
    +                   model="connection.attributes" model-only="managementPermissions.canChangeAllAttributes"></guac-form>
    --- End diff --
    
    Are you missing a `!` here?


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by necouchman <gi...@git.apache.org>.
Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185629538
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/connectionPermissionEditor.js ---
    @@ -0,0 +1,406 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * A directive for manipulating the connection permissions granted within a
    + * given {@link PermissionFlagSet}, tracking the specific permissions added or
    + * removed within a separate pair of {@link PermissionSet} objects.
    + */
    +angular.module('manage').directive('connectionPermissionEditor', ['$injector',
    +    function connectionPermissionEditor($injector) {
    +
    +    // Required types
    +    var ConnectionGroup   = $injector.get('ConnectionGroup');
    +    var GroupListItem     = $injector.get('GroupListItem');
    +    var PermissionSet     = $injector.get('PermissionSet');
    +
    +    // Required services
    +    var connectionGroupService = $injector.get('connectionGroupService');
    +    var dataSourceService      = $injector.get('dataSourceService');
    +    var requestService         = $injector.get('requestService');
    +
    +    var directive = {
    +
    +        // Element only
    +        restrict: 'E',
    +        replace: true,
    +
    +        scope: {
    +
    +            /**
    +             * The unique identifier of the data source associated with the
    +             * permissions being manipulated.
    +             *
    +             * @type String
    +             */
    +            dataSource : '=',
    +
    +            /**
    +             * The current state of the permissions being manipulated. This
    +             * {@link PemissionFlagSet} will be modified as changes are made
    +             * through this permission editor.
    +             *
    +             * @type PermissionFlagSet
    +             */
    +            permissionFlags : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsAdded : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    --- End diff --
    
    "The set of permissions that have been added" - or perhaps "removed" :-).


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185389485
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    +               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry));
                 });
     
    -        }, requestService.WARN);
    +        });
    +    };
     
    -        // Pull connection parameters
    -        connectionService.getConnectionParameters($scope.selectedDataSource, identifier)
    -        .then(function parametersReceived(parameters) {
    -            $scope.parameters = parameters;
    -        }, requestService.WARN);
    -    }
    -    
    -    // If we are cloning an existing connection, pull its data instead
    -    else if (cloneSourceIdentifier) {
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for cloning that existing
    +     * connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection
    +     *     to be cloned.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection being cloned.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     cloning the given connection.
    +     */
    +    var loadClonedConnection = function loadClonedConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection : connectionService.getConnection(dataSource, identifier),
    +            parameters : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
    +
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
    -        // Pull data from cloned connection
    -        connectionService.getConnection($scope.selectedDataSource, cloneSourceIdentifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -            
                 // Clear the identifier field because this connection is new
                 delete $scope.connection.identifier;
    -        }, requestService.WARN);
     
    -        // Do not pull connection history
    -        $scope.historyEntryWrappers = [];
    -        
    -        // Pull connection parameters from cloned connection
    -        connectionService.getConnectionParameters($scope.selectedDataSource, cloneSourceIdentifier)
    -        .then(function parametersReceived(parameters) {
    -            $scope.parameters = parameters;
    -        }, requestService.WARN);
    -    }
    -
    -    // If we are creating a new connection, populate skeleton connection data
    -    else {
    +            // Cloned connections have no history
    +            $scope.historyEntryWrappers = [];
    +
    +        });
    +    };
    +
    +    /**
    +     * Loads skeleton connection data, preparing the interface for creating a
    +     * new connection.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     creating a new connection.
    +     */
    +    var loadSkeletonConnection = function loadSkeletonConnection() {
    +
    +        // Use skeleton connection object with no associated permissions,
    +        // history, or parameters
             $scope.connection = new Connection({
                 protocol         : 'vnc',
                 parentIdentifier : $location.search().parent
             });
             $scope.historyEntryWrappers = [];
             $scope.parameters = {};
    -    }
     
    -    /**
    -     * Returns whether the current user can change/set all connection
    -     * attributes for the connection being edited, regardless of whether those
    -     * attributes are already explicitly associated with that connection.
    -     *
    -     * @returns {Boolean}
    -     *     true if the current user can change all attributes for the
    -     *     connection being edited, regardless of whether those attributes are
    -     *     already explicitly associated with that connection, false otherwise.
    -     */
    -    $scope.canChangeAllAttributes = function canChangeAllAttributes() {
    -
    -        // All attributes can be set if we are creating the connection
    -        return !identifier;
    +        return $q.resolve();
     
         };
     
         /**
    -     * Returns the translation string namespace for the protocol having the
    -     * given name. The namespace will be of the form:
    -     *
    -     * <code>PROTOCOL_NAME</code>
    +     * Loads the data requred for performing the management task requested
    --- End diff --
    
    Ops.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by jmuehlner <gi...@git.apache.org>.
Github user jmuehlner commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185389788
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    --- End diff --
    
    I have no problem with `angular.forEach`. :shipit: 


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by jmuehlner <gi...@git.apache.org>.
Github user jmuehlner commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185387232
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    --- End diff --
    
    Just curious - why do you prefer `angular.forEach` here instead of the built in array `forEach`?


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185388396
  
    --- Diff: guacamole/src/main/webapp/app/manage/templates/manageConnection.html ---
    @@ -41,7 +41,7 @@ <h2>{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}</h2>
         <!-- Connection attributes section -->
         <div class="attributes">
             <guac-form namespace="'CONNECTION_ATTRIBUTES'" content="attributes"
    -                   model="connection.attributes" model-only="!canChangeAllAttributes()"></guac-form>
    +                   model="connection.attributes" model-only="managementPermissions.canChangeAllAttributes"></guac-form>
    --- End diff --
    
    Yes!


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by jmuehlner <gi...@git.apache.org>.
Github user jmuehlner commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185389179
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    +               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry));
                 });
     
    -        }, requestService.WARN);
    +        });
    +    };
     
    -        // Pull connection parameters
    -        connectionService.getConnectionParameters($scope.selectedDataSource, identifier)
    -        .then(function parametersReceived(parameters) {
    -            $scope.parameters = parameters;
    -        }, requestService.WARN);
    -    }
    -    
    -    // If we are cloning an existing connection, pull its data instead
    -    else if (cloneSourceIdentifier) {
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for cloning that existing
    +     * connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection
    +     *     to be cloned.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection being cloned.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     cloning the given connection.
    +     */
    +    var loadClonedConnection = function loadClonedConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection : connectionService.getConnection(dataSource, identifier),
    +            parameters : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
    +
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
    -        // Pull data from cloned connection
    -        connectionService.getConnection($scope.selectedDataSource, cloneSourceIdentifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -            
                 // Clear the identifier field because this connection is new
                 delete $scope.connection.identifier;
    -        }, requestService.WARN);
     
    -        // Do not pull connection history
    -        $scope.historyEntryWrappers = [];
    -        
    -        // Pull connection parameters from cloned connection
    -        connectionService.getConnectionParameters($scope.selectedDataSource, cloneSourceIdentifier)
    -        .then(function parametersReceived(parameters) {
    -            $scope.parameters = parameters;
    -        }, requestService.WARN);
    -    }
    -
    -    // If we are creating a new connection, populate skeleton connection data
    -    else {
    +            // Cloned connections have no history
    +            $scope.historyEntryWrappers = [];
    +
    +        });
    +    };
    +
    +    /**
    +     * Loads skeleton connection data, preparing the interface for creating a
    +     * new connection.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     creating a new connection.
    +     */
    +    var loadSkeletonConnection = function loadSkeletonConnection() {
    +
    +        // Use skeleton connection object with no associated permissions,
    +        // history, or parameters
             $scope.connection = new Connection({
                 protocol         : 'vnc',
                 parentIdentifier : $location.search().parent
             });
             $scope.historyEntryWrappers = [];
             $scope.parameters = {};
    -    }
     
    -    /**
    -     * Returns whether the current user can change/set all connection
    -     * attributes for the connection being edited, regardless of whether those
    -     * attributes are already explicitly associated with that connection.
    -     *
    -     * @returns {Boolean}
    -     *     true if the current user can change all attributes for the
    -     *     connection being edited, regardless of whether those attributes are
    -     *     already explicitly associated with that connection, false otherwise.
    -     */
    -    $scope.canChangeAllAttributes = function canChangeAllAttributes() {
    -
    -        // All attributes can be set if we are creating the connection
    -        return !identifier;
    +        return $q.resolve();
     
         };
     
         /**
    -     * Returns the translation string namespace for the protocol having the
    -     * given name. The namespace will be of the form:
    -     *
    -     * <code>PROTOCOL_NAME</code>
    +     * Loads the data requred for performing the management task requested
    --- End diff --
    
    `requred`?


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by necouchman <gi...@git.apache.org>.
Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185634576
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/managementButtons.js ---
    @@ -0,0 +1,201 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * Directive which displays a set of object management buttons (save, delete,
    + * clone, etc.) representing the actions available to the current user in
    + * context of the object being edited/created.
    + */
    +angular.module('manage').directive('managementButtons', ['$injector',
    +    function managementButtons($injector) {
    +
    +    // Required services
    +    var guacNotification = $injector.get('guacNotification');
    +
    +    var directive = {
    +
    +        restrict    : 'E',
    +        replace     : true,
    +        templateUrl : 'app/manage/templates/managementButtons.html',
    +
    +        scope : {
    +
    +            /**
    +             * The translation namespace associated with all applicable
    +             * translation strings. This directive requires at least the
    +             * following translation strings within the given namespace:
    +             *
    +             *     - ACTION_CANCEL
    +             *     - ACTION_CLONE
    +             *     - ACTION_DELETE
    +             *     - ACTION_SAVE
    +             *     - DIALOG_HEADER_CONFIRM_DELETE
    +             *     - TEXT_CONFIRM_DELETE
    +             *
    +             * @type String
    +             */
    +            namespace : '=',
    +
    +            /**
    +             * The permissions which dictate the management actions available
    +             * to the current user.
    +             *
    +             * @type ManagementPermissions
    +             */
    +            permissions : '=',
    +
    +            /**
    +             * The function to invoke to save the arbitrary object being edited
    +             * if the current user has permission to do so. The provided
    +             * function MUST return a promise which is resolved if the save
    +             * operation succeeds and is rejected with an {@link Error} if the
    +             * save operation fails.
    +             *
    +             * @type Function
    +             */
    +            save : '&',
    +
    +            /**
    +             * The function to invoke when the current user chooses to clone
    +             * the object being edited. The provided function MUST perform the
    +             * actions necessary to produce an interface which will clone the
    +             * object.
    +             *
    +             * @type Function
    +             */
    +            clone : '&',
    +
    +            /**
    +             * The function to invoke to delete the arbitrary object being edited
    +             * if the current user has permission to do so. The provided
    +             * function MUST return a promise which is resolved if the delete
    +             * operation succeeds and is rejected with an {@link Error} if the
    +             * delete operation fails.
    +             *
    +             * @type Function
    +             */
    +            delete : '&',
    +
    +            /**
    +             * The function to invoke when the current user chooses to cancel
    +             * the edit in progress, or when a save/delete operation has
    +             * succeeded. The provided function MUST perform the actions
    +             * necessary to return the user to a reasonable starting point.
    +             *
    +             * @type Function
    +             */
    +            return : '&'
    +
    +        }
    +
    +    };
    +
    +    directive.controller = ['$scope', function managementButtonsController($scope) {
    +
    +        /**
    +         * An action to be provided along with the object sent to showStatus which
    +         * immediately deletes the current connection.
    +         */
    +        var DELETE_ACTION = {
    +            name      : $scope.namespace + '.ACTION_DELETE',
    +            className : 'danger',
    +            callback  : function deleteCallback() {
    +                deleteObjectImmediately();
    +                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     : $scope.namespace + '.ACTION_CANCEL',
    +            callback : function cancelCallback() {
    +                guacNotification.showStatus(false);
    +            }
    +        };
    +
    +        /**
    +         * Invokes the provided return function to navigate the user back to
    +         * the page they started from.
    +         */
    +        var navigateBack = function navigateBack() {
    +            $scope['return']($scope.$parent);
    +        };
    +
    +        /**
    +         * Invokes the provided delete function, immediately deleting the
    +         * current object without prompting the user for confirmation. If
    +         * deletion is successful, the user is navigated back to the page they
    +         * started from. If the deletion fails, an error notification is
    +         * displayed.
    +         */
    +        var deleteObjectImmediately = function deleteObjectImmediately() {
    +            $scope['delete']($scope.$parent).then(navigateBack, guacNotification.SHOW_REQUEST_ERROR);
    +        };
    +
    +        /**
    +         * Cancels all pending edits, returning to the page the user started
    +         * from.
    +         */
    +        $scope.cancel = navigateBack;
    +
    +        /**
    +         * Cancels all pending edits, invoking the provided clone function to
    +         * open an edit page for a new object which is prepopulated with the
    +         * data from the current object.
    +         */
    +        $scope.cloneObject = function cloneObject () {
    +            $scope.clone($scope.$parent);
    +        };
    +
    +        /**
    +         * Invokes the provided save function to saves the current object. If
    --- End diff --
    
    to *save* the


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by necouchman <gi...@git.apache.org>.
Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185627142
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageUserController.js ---
    @@ -361,727 +187,176 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
         };
     
         /**
    -     * Returns whether the current user can save the user being edited within
    -     * the given data source. Saving will create or update that user depending
    -     * on whether the user already exists.
    +     * Loads the data associated with the user having the given username,
    +     * preparing the interface for making modifications to that existing user.
          *
    -     * @param {String} [dataSource]
    -     *     The identifier of the data source to check. If omitted, this will
    -     *     default to the currently-selected data source.
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the user to
    +     *     load.
          *
    -     * @returns {Boolean}
    -     *     true if the current user can save changes to the user being edited,
    -     *     false otherwise.
    -     */
    -    $scope.canSaveUser = function canSaveUser(dataSource) {
    -
    -        // Do not check if permissions are not yet loaded
    -        if (!$scope.permissions)
    -            return false;
    -
    -        // Use currently-selected data source if unspecified
    -        dataSource = dataSource || selectedDataSource;
    -
    -        // The administrator can always save users
    -        if (PermissionSet.hasSystemPermission($scope.permissions[dataSource],
    -                PermissionSet.SystemPermissionType.ADMINISTER))
    -            return true;
    -
    -        // If user does not exist, can only save if we have permission to create users
    -        if (!$scope.userExists(dataSource))
    -           return PermissionSet.hasSystemPermission($scope.permissions[dataSource],
    -               PermissionSet.SystemPermissionType.CREATE_USER);
    -
    -        // Otherwise, can only save if we have permission to update this user
    -        return PermissionSet.hasUserPermission($scope.permissions[dataSource],
    -            PermissionSet.ObjectPermissionType.UPDATE, username);
    -
    -    };
    -
    -    /**
    -     * Returns whether the current user can clone the user being edited within
    -     * the given data source.
    +     * @param {String} username
    +     *     The username of the user to load.
          *
    -     * @param {String} [dataSource]
    -     *     The identifier of the data source to check. If omitted, this will
    -     *     default to the currently-selected data source.
    -     *
    -     * @returns {Boolean}
    -     *     true if the current user can clone the user being edited, false
    -     *     otherwise.
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given user.
          */
    -    $scope.canCloneUser = function canCloneUser(dataSource) {
    -
    -        // Do not check if permissions are not yet loaded
    -        if (!$scope.permissions)
    -            return false;
    -
    -        // Use currently-selected data source if unspecified
    -        dataSource = dataSource || selectedDataSource;
    -
    -        // If we are not editing an existing user, we cannot clone
    -        if (!$scope.userExists(selectedDataSource))
    -            return false;
    -
    -        // The administrator can always clone users
    -        if (PermissionSet.hasSystemPermission($scope.permissions[dataSource],
    -                PermissionSet.SystemPermissionType.ADMINISTER))
    -            return true;
    -
    -        // Otherwise we need explicit CREATE_USER permission
    -        return PermissionSet.hasSystemPermission($scope.permissions[dataSource],
    -            PermissionSet.SystemPermissionType.CREATE_USER);
    -
    -    };
    -
    -    /**
    -     * Returns whether the current user can delete the user being edited from
    -     * the given data source.
    -     *
    -     * @param {String} [dataSource]
    -     *     The identifier of the data source to check. If omitted, this will
    -     *     default to the currently-selected data source.
    -     *
    -     * @returns {Boolean}
    -     *     true if the current user can delete the user being edited, false
    -     *     otherwise.
    -     */
    -    $scope.canDeleteUser = function canDeleteUser(dataSource) {
    -
    -        // Do not check if permissions are not yet loaded
    -        if (!$scope.permissions)
    -            return false;
    -
    -        // Use currently-selected data source if unspecified
    -        dataSource = dataSource || selectedDataSource;
    -
    -        // Can't delete what doesn't exist
    -        if (!$scope.userExists(dataSource))
    -            return false;
    -
    -        // The administrator can always delete users
    -        if (PermissionSet.hasSystemPermission($scope.permissions[dataSource],
    -                PermissionSet.SystemPermissionType.ADMINISTER))
    -            return true;
    -
    -        // Otherwise, require explicit DELETE permission on the user
    -        return PermissionSet.hasUserPermission($scope.permissions[dataSource],
    -            PermissionSet.ObjectPermissionType.DELETE, username);
    -
    -    };
    -
    -    /**
    -     * Returns whether the user being edited within the given data source is
    -     * read-only, and thus cannot be modified by the current user.
    -     *
    -     * @param {String} [dataSource]
    -     *     The identifier of the data source to check. If omitted, this will
    -     *     default to the currently-selected data source.
    -     *
    -     * @returns {Boolean}
    -     *     true if the user being edited is actually read-only and cannot be
    -     *     edited at all, false otherwise.
    -     */
    -    $scope.isReadOnly = function isReadOnly(dataSource) {
    -
    -        // Use currently-selected data source if unspecified
    -        dataSource = dataSource || selectedDataSource;
    -
    -        // User is read-only if they cannot be saved
    -        return !$scope.canSaveUser(dataSource);
    -
    -    };
    -
    -    // Update visible account pages whenever available users/permissions changes
    -    $scope.$watchGroup(['users', 'permissions'], function updateAccountPages() {
    -
    -        // Generate pages for each applicable data source
    -        $scope.accountPages = [];
    -        angular.forEach(dataSources, function addAccountPage(dataSource) {
    -
    -            // Determine whether data source contains this user
    -            var linked   = $scope.userExists(dataSource);
    -            var readOnly = $scope.isReadOnly(dataSource);
    -
    -            // Account is not relevant if it does not exist and cannot be
    -            // created
    -            if (!linked && readOnly)
    -                return;
    -
    -            // Only the selected data source is relevant when cloning
    -            if (cloneSourceUsername && dataSource !== selectedDataSource)
    -                return;
    -
    -            // Determine class name based on read-only / linked status
    -            var className;
    -            if (readOnly)    className = 'read-only';
    -            else if (linked) className = 'linked';
    -            else             className = 'unlinked';
    -
    -            // Add page entry
    -            $scope.accountPages.push(new PageDefinition({
    -                name      : translationStringService.canonicalize('DATA_SOURCE_' + dataSource) + '.NAME',
    -                url       : '/manage/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username || ''),
    -                className : className
    -            }));
    -
    -        });
    -
    -    });
    -
    -    // Pull user attribute schema
    -    schemaService.getUserAttributes(selectedDataSource).then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, requestService.WARN);
    -
    -    // Pull user data and permissions if we are editing an existing user
    -    if (username) {
    -
    -        // Pull user data
    -        dataSourceService.apply(userService.getUser, dataSources, username)
    -        .then(function usersReceived(users) {
    +    var loadExistingUser = function loadExistingUser(dataSource, username) {
    +        return $q.all({
    +            users : dataSourceService.apply(userService.getUser, dataSources, username),
    +            permissions : permissionService.getPermissions(dataSource, username)
    +        })
    +        .then(function userDataRetrieved(values) {
     
    -            // Get user for currently-selected data source
    -            $scope.users = users;
    -            $scope.user  = users[selectedDataSource];
    +            $scope.users = values.users;
    +            $scope.user  = values.users[dataSource];
     
                 // Create skeleton user if user does not exist
                 if (!$scope.user)
                     $scope.user = new User({
                         'username' : username
                     });
     
    -        }, requestService.WARN);
    -
    -        // The current user will be associated with username of the existing
    -        // user in the retrieved permission set
    -        $scope.selfUsername = username;
    -
    -        // Pull user permissions
    -        permissionService.getPermissions(selectedDataSource, username).then(function gotPermissions(permissions) {
    -            $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions);
    -        })
    -
    -        // If permissions cannot be retrieved, use empty permissions
    -        ['catch'](requestService.createErrorCallback(function permissionRetrievalFailed() {
    -            $scope.permissionFlags = new PermissionFlagSet();
    -        }));
    -    }
    -
    -    // If we are cloning an existing user, pull his/her data instead
    -    else if (cloneSourceUsername) {
    -
    -        dataSourceService.apply(userService.getUser, dataSources, cloneSourceUsername)
    -        .then(function usersReceived(users) {
    -
    -            // Get user for currently-selected data source
    -            $scope.users = {};
    -            $scope.user  = users[selectedDataSource];
    -
    -        }, requestService.WARN);
    -
    -        // The current user will be associated with cloneSourceUsername in the
    -        // retrieved permission set
    -        $scope.selfUsername = cloneSourceUsername;
    +            // The current user will be associated with username of the existing
    +            // user in the retrieved permission set
    +            $scope.selfUsername = username;
    +            $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions);
     
    -        // Pull user permissions
    -        permissionService.getPermissions(selectedDataSource, cloneSourceUsername)
    -        .then(function gotPermissions(permissions) {
    -            $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions);
    -            permissionsAdded = permissions;
    -        })
    -
    -        // If permissions cannot be retrieved, use empty permissions
    -        ['catch'](requestService.createErrorCallback(function permissionRetrievalFailed() {
    -            $scope.permissionFlags = new PermissionFlagSet();
    -        }));
    -    }
    -
    -    // Use skeleton data if we are creating a new user
    -    else {
    -
    -        // No users exist regardless of data source if there is no username
    -        $scope.users = {};
    -
    -        // Use skeleton user object with no associated permissions
    -        $scope.user = new User();
    -        $scope.permissionFlags = new PermissionFlagSet();
    -
    -    }
    -
    -    /**
    -     * Expands all items within the tree descending from the given
    -     * GroupListItem which have at least one descendant for which explicit READ
    -     * permission is granted. The expanded state of all other items is left
    -     * untouched.
    -     *
    -     * @param {GroupListItem} item
    -     *     The GroupListItem which should be conditionally expanded depending
    -     *     on whether READ permission is granted for any of its descendants.
    -     *
    -     * @param {PemissionFlagSet} flags
    -     *     The set of permissions which should be used to determine whether the
    -     *     given item and its descendants are expanded.
    -     */
    -    var expandReadable = function expandReadable(item, flags) {
    -
    -        // If the current item is expandable and has defined children,
    -        // determine whether it should be expanded
    -        if (item.expandable && item.children) {
    -            angular.forEach(item.children, function expandReadableChild(child) {
    -
    -                // Determine whether the user has READ permission for the
    -                // current child object
    -                var readable = false;
    -                switch (child.type) {
    -
    -                    case GroupListItem.Type.CONNECTION:
    -                        readable = flags.connectionPermissions.READ[child.identifier];
    -                        break;
    -
    -                    case GroupListItem.Type.CONNECTION_GROUP:
    -                        readable = flags.connectionGroupPermissions.READ[child.identifier];
    -                        break;
    -
    -                    case GroupListItem.Type.SHARING_PROFILE:
    -                        readable = flags.sharingProfilePermissions.READ[child.identifier];
    -                        break;
    -
    -                }
    -
    -                // The parent should be expanded by default if the child is
    -                // expanded by default OR the user has READ permission on the
    -                // child
    -                item.expanded |= expandReadable(child, flags) || readable;
    -
    -            });
    -        }
    -
    -        return item.expanded;
    -
    -    };
    -
    -
    -    // Retrieve all connections for which we have ADMINISTER permission
    -    dataSourceService.apply(
    -        connectionGroupService.getConnectionGroupTree,
    -        [selectedDataSource],
    -        ConnectionGroup.ROOT_IDENTIFIER,
    -        [PermissionSet.ObjectPermissionType.ADMINISTER]
    -    )
    -    .then(function connectionGroupReceived(rootGroups) {
    -
    -        // Convert all received ConnectionGroup objects into GroupListItems
    -        $scope.rootGroups = {};
    -        angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
    -            $scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
             });
    -
    -    }, requestService.WARN);
    -    
    -    // Query the user's permissions for the current user
    -    dataSourceService.apply(
    -        permissionService.getEffectivePermissions,
    -        dataSources,
    -        currentUsername
    -    )
    -    .then(function permissionsReceived(permissions) {
    -        $scope.permissions = permissions;
    -    }, requestService.WARN);
    -
    -    // Update default expanded state whenever connection groups and associated
    -    // permissions change
    -    $scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
    -        angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
    -
    -            // Automatically expand all objects with any descendants for which
    -            // the user has READ permission
    -            if ($scope.permissionFlags)
    -                expandReadable(rootGroup, $scope.permissionFlags);
    -
    -        });
    -    });
    -
    -    /**
    -     * Available system permission types, as translation string / internal
    -     * value pairs.
    -     * 
    -     * @type Object[]
    -     */
    -    $scope.systemPermissionTypes = [
    -        {
    -            label: "MANAGE_USER.FIELD_HEADER_ADMINISTER_SYSTEM",
    -            value: PermissionSet.SystemPermissionType.ADMINISTER
    -        },
    -        {
    -            label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
    -            value: PermissionSet.SystemPermissionType.CREATE_USER
    -        },
    -        {
    -            label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
    -            value: PermissionSet.SystemPermissionType.CREATE_CONNECTION
    -        },
    -        {
    -            label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS",
    -            value: PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP
    -        },
    -        {
    -            label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES",
    -            value: PermissionSet.SystemPermissionType.CREATE_SHARING_PROFILE
    -        }
    -    ];
    -
    -    /**
    -     * The set of permissions that will be added to the user when the user is
    -     * saved. Permissions will only be present in this set if they are
    -     * manually added, and not later manually removed before saving.
    -     *
    -     * @type PermissionSet
    -     */
    -    var permissionsAdded = new PermissionSet();
    -
    -    /**
    -     * The set of permissions that will be removed from the user when the user 
    -     * is saved. Permissions will only be present in this set if they are
    -     * manually removed, and not later manually added before saving.
    -     *
    -     * @type PermissionSet
    -     */
    -    var permissionsRemoved = new PermissionSet();
    -
    -    /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the addition of the given system permission.
    -     * 
    -     * @param {String} type
    -     *     The system permission to add, as defined by
    -     *     PermissionSet.SystemPermissionType.
    -     */
    -    var addSystemPermission = function addSystemPermission(type) {
    -
    -        // If permission was previously removed, simply un-remove it
    -        if (PermissionSet.hasSystemPermission(permissionsRemoved, type))
    -            PermissionSet.removeSystemPermission(permissionsRemoved, type);
    -
    -        // Otherwise, explicitly add the permission
    -        else
    -            PermissionSet.addSystemPermission(permissionsAdded, type);
    -
         };
     
         /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the removal of the given system permission.
    +     * Loads the data associated with the user having the given username,
    +     * preparing the interface for cloning that existing user.
          *
    -     * @param {String} type
    -     *     The system permission to remove, as defined by
    -     *     PermissionSet.SystemPermissionType.
    -     */
    -    var removeSystemPermission = function removeSystemPermission(type) {
    -
    -        // If permission was previously added, simply un-add it
    -        if (PermissionSet.hasSystemPermission(permissionsAdded, type))
    -            PermissionSet.removeSystemPermission(permissionsAdded, type);
    -
    -        // Otherwise, explicitly remove the permission
    -        else
    -            PermissionSet.addSystemPermission(permissionsRemoved, type);
    -
    -    };
    -
    -    /**
    -     * Notifies the controller that a change has been made to the given
    -     * system permission for the user being edited.
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the user to
    +     *     be cloned.
          *
    -     * @param {String} type
    -     *     The system permission that was changed, as defined by
    -     *     PermissionSet.SystemPermissionType.
    -     */
    -    $scope.systemPermissionChanged = function systemPermissionChanged(type) {
    -
    -        // Determine current permission setting
    -        var granted = $scope.permissionFlags.systemPermissions[type];
    -
    -        // Add/remove permission depending on flag state
    -        if (granted)
    -            addSystemPermission(type);
    -        else
    -            removeSystemPermission(type);
    -
    -    };
    -
    -    /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the addition of the given user permission.
    -     * 
    -     * @param {String} type
    -     *     The user permission to add, as defined by
    -     *     PermissionSet.ObjectPermissionType.
    +     * @param {String} username
    +     *     The username of the user being cloned.
          *
    -     * @param {String} identifier
    -     *     The identifier of the user affected by the permission being added.
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     cloning the given user.
          */
    -    var addUserPermission = function addUserPermission(type, identifier) {
    -
    -        // If permission was previously removed, simply un-remove it
    -        if (PermissionSet.hasUserPermission(permissionsRemoved, type, identifier))
    -            PermissionSet.removeUserPermission(permissionsRemoved, type, identifier);
    -
    -        // Otherwise, explicitly add the permission
    -        else
    -            PermissionSet.addUserPermission(permissionsAdded, type, identifier);
    -
    -    };
    -
    -    /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the removal of the given user permission.
    -     *
    -     * @param {String} type
    -     *     The user permission to remove, as defined by
    -     *     PermissionSet.ObjectPermissionType.
    -     *
    -     * @param {String} identifier
    -     *     The identifier of the user affected by the permission being removed.
    -     */
    -    var removeUserPermission = function removeUserPermission(type, identifier) {
    +    var loadClonedUser = function loadClonedUser(dataSource, username) {
    +        return $q.all({
    +            users : dataSourceService.apply(userService.getUser, [dataSource], username),
    +            permissions : permissionService.getPermissions(dataSource, username)
    +        })
    +        .then(function userDataRetrieved(values) {
     
    -        // If permission was previously added, simply un-add it
    -        if (PermissionSet.hasUserPermission(permissionsAdded, type, identifier))
    -            PermissionSet.removeUserPermission(permissionsAdded, type, identifier);
    +            $scope.users = {};
    +            $scope.user  = values.users[dataSource];
     
    -        // Otherwise, explicitly remove the permission
    -        else
    -            PermissionSet.addUserPermission(permissionsRemoved, type, identifier);
    +            // The current user will be associated with cloneSourceUsername in the
    +            // retrieved permission set
    +            $scope.selfUsername = username;
    +            $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions);
    +            $scope.permissionsAdded = values.permissions;
     
    +        });
         };
     
         /**
    -     * Notifies the controller that a change has been made to the given user
    -     * permission for the user being edited.
    +     * Loads skeleton user data, preparing the interface for creating a new
    +     * user.
          *
    -     * @param {String} type
    -     *     The user permission that was changed, as defined by
    -     *     PermissionSet.ObjectPermissionType.
    -     *
    -     * @param {String} identifier
    -     *     The identifier of the user affected by the changed permission.
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     creating a new user.
          */
    -    $scope.userPermissionChanged = function userPermissionChanged(type, identifier) {
    -
    -        // Determine current permission setting
    -        var granted = $scope.permissionFlags.userPermissions[type][identifier];
    -
    -        // Add/remove permission depending on flag state
    -        if (granted)
    -            addUserPermission(type, identifier);
    -        else
    -            removeUserPermission(type, identifier);
    +    var loadSkeletonUser = function loadSkeletonUser() {
     
    -    };
    +        // No users exist regardless of data source if there is no username
    +        $scope.users = {};
     
    -    /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the addition of the given connection permission.
    -     * 
    -     * @param {String} identifier
    -     *     The identifier of the connection to add READ permission for.
    -     */
    -    var addConnectionPermission = function addConnectionPermission(identifier) {
    +        // Use skeleton user object with no associated permissions
    +        $scope.user = new User();
    +        $scope.permissionFlags = new PermissionFlagSet();
     
    -        // If permission was previously removed, simply un-remove it
    -        if (PermissionSet.hasConnectionPermission(permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    -            PermissionSet.removeConnectionPermission(permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +        // As no permissions are yet associated with the user, it is safe to
    +        // use any non-empty username as a placeholder for self-referential
    +        // permissions
    +        $scope.selfUsername = 'SELF';
     
    -        // Otherwise, explicitly add the permission
    -        else
    -            PermissionSet.addConnectionPermission(permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +        return $q.resolve();
     
         };
     
         /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the removal of the given connection permission.
    +     * Loads the data required for performing the management task requested
    +     * through the route parameters given at load time, automatically preparing
    +     * the interface for editing an existing user, cloning an existing user, or
    +     * creating an entirely new user.
          *
    -     * @param {String} identifier
    -     *     The identifier of the connection to remove READ permission for.
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared
    +     *     for performing the requested management task.
          */
    -    var removeConnectionPermission = function removeConnectionPermission(identifier) {
    +    var loadRequestedUser = function loadRequestedUser() {
     
    -        // If permission was previously added, simply un-add it
    -        if (PermissionSet.hasConnectionPermission(permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
    -            PermissionSet.removeConnectionPermission(permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +        // Pull user data and permissions if we are editing an existing user
    +        if (username)
    +            return loadExistingUser($scope.dataSource, username);
     
    -        // Otherwise, explicitly remove the permission
    -        else
    -            PermissionSet.addConnectionPermission(permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +        // If we are cloning an existing user, pull his/her data instead
    +        if (cloneSourceUsername)
    +            return loadClonedUser($scope.dataSource, cloneSourceUsername);
     
    -    };
    -
    -    /**
    -     * Updates the permissionsAdded and permissionsRemoved permission sets to
    -     * reflect the addition of the given connection group permission.
    -     * 
    -     * @param {String} identifier
    -     *     The identifier of the connection group to add READ permission for.
    -     */
    -    var addConnectionGroupPermission = function addConnectionGroupPermission(identifier) {
    -
    -        // If permission was previously removed, simply un-remove it
    -        if (PermissionSet.hasConnectionGroupPermission(permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    -            PermissionSet.removeConnectionGroupPermission(permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    -
    -        // Otherwise, explicitly add the permission
    -        else
    -            PermissionSet.addConnectionGroupPermission(permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +        return loadSkeletonUser();
    --- End diff --
    
    Nitpick: in the other places where you updated this, you commented on this action.  For consistency's sake, I'd say either remove the comments above the other's or add one, here.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185388553
  
    --- Diff: guacamole/src/main/webapp/app/manage/templates/manageConnection.html ---
    @@ -41,7 +41,7 @@ <h2>{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}</h2>
         <!-- Connection attributes section -->
         <div class="attributes">
             <guac-form namespace="'CONNECTION_ATTRIBUTES'" content="attributes"
    -                   model="connection.attributes" model-only="!canChangeAllAttributes()"></guac-form>
    +                   model="connection.attributes" model-only="managementPermissions.canChangeAllAttributes"></guac-form>
    --- End diff --
    
    OK. I've rebased away the mistake.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by necouchman <gi...@git.apache.org>.
Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185631879
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/connectionPermissionEditor.js ---
    @@ -0,0 +1,406 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * A directive for manipulating the connection permissions granted within a
    + * given {@link PermissionFlagSet}, tracking the specific permissions added or
    + * removed within a separate pair of {@link PermissionSet} objects.
    + */
    +angular.module('manage').directive('connectionPermissionEditor', ['$injector',
    +    function connectionPermissionEditor($injector) {
    +
    +    // Required types
    +    var ConnectionGroup   = $injector.get('ConnectionGroup');
    +    var GroupListItem     = $injector.get('GroupListItem');
    +    var PermissionSet     = $injector.get('PermissionSet');
    +
    +    // Required services
    +    var connectionGroupService = $injector.get('connectionGroupService');
    +    var dataSourceService      = $injector.get('dataSourceService');
    +    var requestService         = $injector.get('requestService');
    +
    +    var directive = {
    +
    +        // Element only
    +        restrict: 'E',
    +        replace: true,
    +
    +        scope: {
    +
    +            /**
    +             * The unique identifier of the data source associated with the
    +             * permissions being manipulated.
    +             *
    +             * @type String
    +             */
    +            dataSource : '=',
    +
    +            /**
    +             * The current state of the permissions being manipulated. This
    +             * {@link PemissionFlagSet} will be modified as changes are made
    +             * through this permission editor.
    +             *
    +             * @type PermissionFlagSet
    +             */
    +            permissionFlags : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsAdded : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsRemoved : '='
    +
    +        },
    +
    +        templateUrl: 'app/manage/templates/connectionPermissionEditor.html'
    +
    +    };
    +
    +    directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
    +
    +        /**
    +         * Array of all connection properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionProperties = [
    +            'name',
    +            'protocol'
    +        ];
    +
    +        /**
    +         * Array of all connection group properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionGroupProperties = [
    +            'name'
    +        ];
    +
    +        /**
    +         * A map of data source identifiers to the root connection groups within
    +         * thost data sources. As only one data source is applicable to any
    +         * particular permission set being edited/created, this will only
    +         * contain a single key.
    +         *
    +         * @type Object.<String, GroupListItem>
    +         */
    +        $scope.rootGroups = null;
    +
    +        // Retrieve all connections for which we have ADMINISTER permission
    +        dataSourceService.apply(
    +            connectionGroupService.getConnectionGroupTree,
    +            [$scope.dataSource],
    +            ConnectionGroup.ROOT_IDENTIFIER,
    +            [PermissionSet.ObjectPermissionType.ADMINISTER]
    +        )
    +        .then(function connectionGroupReceived(rootGroups) {
    +
    +            // Convert all received ConnectionGroup objects into GroupListItems
    +            $scope.rootGroups = {};
    +            angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
    +                $scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
    +            });
    +
    +        }, requestService.WARN);
    +
    +        /**
    +         * Expands all items within the tree descending from the given
    +         * GroupListItem which have at least one descendant for which explicit
    +         * READ permission is granted. The expanded state of all other items is
    +         * left untouched.
    +         *
    +         * @param {GroupListItem} item
    +         *     The GroupListItem which should be conditionally expanded
    +         *     depending on whether READ permission is granted for any of its
    +         *     descendants.
    +         *
    +         * @param {PemissionFlagSet} flags
    +         *     The set of permissions which should be used to determine whether
    +         *     the given item and its descendants are expanded.
    +         */
    +        var expandReadable = function expandReadable(item, flags) {
    +
    +            // If the current item is expandable and has defined children,
    +            // determine whether it should be expanded
    +            if (item.expandable && item.children) {
    +                angular.forEach(item.children, function expandReadableChild(child) {
    +
    +                    // Determine whether the permission set contains READ
    +                    // permission for the current child object
    +                    var readable = false;
    +                    switch (child.type) {
    +
    +                        case GroupListItem.Type.CONNECTION:
    +                            readable = flags.connectionPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.CONNECTION_GROUP:
    +                            readable = flags.connectionGroupPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.SHARING_PROFILE:
    +                            readable = flags.sharingProfilePermissions.READ[child.identifier];
    +                            break;
    +
    +                    }
    +
    +                    // The parent should be expanded by default if the child is
    +                    // expanded by default OR the permission set contains READ
    +                    // permission on the child
    +                    item.expanded |= expandReadable(child, flags) || readable;
    +
    +                });
    +            }
    +
    +            return item.expanded;
    +
    +        };
    +
    +        // Update default expanded state whenever connection groups and
    +        // associated permissions change
    +        $scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
    +
    +            if (!$scope.rootGroups || !$scope.permissionFlags)
    +                return;
    +
    +            angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
    +
    +                // Automatically expand all objects with any descendants for
    +                // which the permission set contains READ permission
    +                expandReadable(rootGroup, $scope.permissionFlags);
    +
    +            });
    +
    +        });
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to add READ permission for.
    +         */
    +        var addConnectionPermission = function addConnectionPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to remove READ permission for.
    +         */
    +        var removeConnectionPermission = function removeConnectionPermission(identifier) {
    +
    +            // If permission was previously added, simply un-add it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly remove the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection group permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection group to add READ permission
    +         *     for.
    +         */
    +        var addConnectionGroupPermission = function addConnectionGroupPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to remove READ permission for.
    --- End diff --
    
    connection *group* to remove


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185999925
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/connectionPermissionEditor.js ---
    @@ -0,0 +1,406 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * A directive for manipulating the connection permissions granted within a
    + * given {@link PermissionFlagSet}, tracking the specific permissions added or
    + * removed within a separate pair of {@link PermissionSet} objects.
    + */
    +angular.module('manage').directive('connectionPermissionEditor', ['$injector',
    +    function connectionPermissionEditor($injector) {
    +
    +    // Required types
    +    var ConnectionGroup   = $injector.get('ConnectionGroup');
    +    var GroupListItem     = $injector.get('GroupListItem');
    +    var PermissionSet     = $injector.get('PermissionSet');
    +
    +    // Required services
    +    var connectionGroupService = $injector.get('connectionGroupService');
    +    var dataSourceService      = $injector.get('dataSourceService');
    +    var requestService         = $injector.get('requestService');
    +
    +    var directive = {
    +
    +        // Element only
    +        restrict: 'E',
    +        replace: true,
    +
    +        scope: {
    +
    +            /**
    +             * The unique identifier of the data source associated with the
    +             * permissions being manipulated.
    +             *
    +             * @type String
    +             */
    +            dataSource : '=',
    +
    +            /**
    +             * The current state of the permissions being manipulated. This
    +             * {@link PemissionFlagSet} will be modified as changes are made
    +             * through this permission editor.
    +             *
    +             * @type PermissionFlagSet
    +             */
    +            permissionFlags : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsAdded : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsRemoved : '='
    +
    +        },
    +
    +        templateUrl: 'app/manage/templates/connectionPermissionEditor.html'
    +
    +    };
    +
    +    directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
    +
    +        /**
    +         * Array of all connection properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionProperties = [
    +            'name',
    +            'protocol'
    +        ];
    +
    +        /**
    +         * Array of all connection group properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionGroupProperties = [
    +            'name'
    +        ];
    +
    +        /**
    +         * A map of data source identifiers to the root connection groups within
    +         * thost data sources. As only one data source is applicable to any
    +         * particular permission set being edited/created, this will only
    +         * contain a single key.
    +         *
    +         * @type Object.<String, GroupListItem>
    +         */
    +        $scope.rootGroups = null;
    +
    +        // Retrieve all connections for which we have ADMINISTER permission
    +        dataSourceService.apply(
    +            connectionGroupService.getConnectionGroupTree,
    +            [$scope.dataSource],
    +            ConnectionGroup.ROOT_IDENTIFIER,
    +            [PermissionSet.ObjectPermissionType.ADMINISTER]
    +        )
    +        .then(function connectionGroupReceived(rootGroups) {
    +
    +            // Convert all received ConnectionGroup objects into GroupListItems
    +            $scope.rootGroups = {};
    +            angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
    +                $scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
    +            });
    +
    +        }, requestService.WARN);
    +
    +        /**
    +         * Expands all items within the tree descending from the given
    +         * GroupListItem which have at least one descendant for which explicit
    +         * READ permission is granted. The expanded state of all other items is
    +         * left untouched.
    +         *
    +         * @param {GroupListItem} item
    +         *     The GroupListItem which should be conditionally expanded
    +         *     depending on whether READ permission is granted for any of its
    +         *     descendants.
    +         *
    +         * @param {PemissionFlagSet} flags
    +         *     The set of permissions which should be used to determine whether
    +         *     the given item and its descendants are expanded.
    +         */
    +        var expandReadable = function expandReadable(item, flags) {
    +
    +            // If the current item is expandable and has defined children,
    +            // determine whether it should be expanded
    +            if (item.expandable && item.children) {
    +                angular.forEach(item.children, function expandReadableChild(child) {
    +
    +                    // Determine whether the permission set contains READ
    +                    // permission for the current child object
    +                    var readable = false;
    +                    switch (child.type) {
    +
    +                        case GroupListItem.Type.CONNECTION:
    +                            readable = flags.connectionPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.CONNECTION_GROUP:
    +                            readable = flags.connectionGroupPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.SHARING_PROFILE:
    +                            readable = flags.sharingProfilePermissions.READ[child.identifier];
    +                            break;
    +
    +                    }
    +
    +                    // The parent should be expanded by default if the child is
    +                    // expanded by default OR the permission set contains READ
    +                    // permission on the child
    +                    item.expanded |= expandReadable(child, flags) || readable;
    +
    +                });
    +            }
    +
    +            return item.expanded;
    +
    +        };
    +
    +        // Update default expanded state whenever connection groups and
    +        // associated permissions change
    +        $scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
    +
    +            if (!$scope.rootGroups || !$scope.permissionFlags)
    +                return;
    +
    +            angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
    +
    +                // Automatically expand all objects with any descendants for
    +                // which the permission set contains READ permission
    +                expandReadable(rootGroup, $scope.permissionFlags);
    +
    +            });
    +
    +        });
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to add READ permission for.
    +         */
    +        var addConnectionPermission = function addConnectionPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to remove READ permission for.
    +         */
    +        var removeConnectionPermission = function removeConnectionPermission(identifier) {
    +
    +            // If permission was previously added, simply un-add it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly remove the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection group permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection group to add READ permission
    +         *     for.
    +         */
    +        var addConnectionGroupPermission = function addConnectionGroupPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to remove READ permission for.
    --- End diff --
    
    Same here - fixed, but GitHub decided to be magic.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185999873
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/connectionPermissionEditor.js ---
    @@ -0,0 +1,406 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * A directive for manipulating the connection permissions granted within a
    + * given {@link PermissionFlagSet}, tracking the specific permissions added or
    + * removed within a separate pair of {@link PermissionSet} objects.
    + */
    +angular.module('manage').directive('connectionPermissionEditor', ['$injector',
    +    function connectionPermissionEditor($injector) {
    +
    +    // Required types
    +    var ConnectionGroup   = $injector.get('ConnectionGroup');
    +    var GroupListItem     = $injector.get('GroupListItem');
    +    var PermissionSet     = $injector.get('PermissionSet');
    +
    +    // Required services
    +    var connectionGroupService = $injector.get('connectionGroupService');
    +    var dataSourceService      = $injector.get('dataSourceService');
    +    var requestService         = $injector.get('requestService');
    +
    +    var directive = {
    +
    +        // Element only
    +        restrict: 'E',
    +        replace: true,
    +
    +        scope: {
    +
    +            /**
    +             * The unique identifier of the data source associated with the
    +             * permissions being manipulated.
    +             *
    +             * @type String
    +             */
    +            dataSource : '=',
    +
    +            /**
    +             * The current state of the permissions being manipulated. This
    +             * {@link PemissionFlagSet} will be modified as changes are made
    +             * through this permission editor.
    +             *
    +             * @type PermissionFlagSet
    +             */
    +            permissionFlags : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsAdded : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsRemoved : '='
    +
    +        },
    +
    +        templateUrl: 'app/manage/templates/connectionPermissionEditor.html'
    +
    +    };
    +
    +    directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
    +
    +        /**
    +         * Array of all connection properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionProperties = [
    +            'name',
    +            'protocol'
    +        ];
    +
    +        /**
    +         * Array of all connection group properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionGroupProperties = [
    +            'name'
    +        ];
    +
    +        /**
    +         * A map of data source identifiers to the root connection groups within
    +         * thost data sources. As only one data source is applicable to any
    +         * particular permission set being edited/created, this will only
    +         * contain a single key.
    +         *
    +         * @type Object.<String, GroupListItem>
    +         */
    +        $scope.rootGroups = null;
    +
    +        // Retrieve all connections for which we have ADMINISTER permission
    +        dataSourceService.apply(
    +            connectionGroupService.getConnectionGroupTree,
    +            [$scope.dataSource],
    +            ConnectionGroup.ROOT_IDENTIFIER,
    +            [PermissionSet.ObjectPermissionType.ADMINISTER]
    +        )
    +        .then(function connectionGroupReceived(rootGroups) {
    +
    +            // Convert all received ConnectionGroup objects into GroupListItems
    +            $scope.rootGroups = {};
    +            angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
    +                $scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
    +            });
    +
    +        }, requestService.WARN);
    +
    +        /**
    +         * Expands all items within the tree descending from the given
    +         * GroupListItem which have at least one descendant for which explicit
    +         * READ permission is granted. The expanded state of all other items is
    +         * left untouched.
    +         *
    +         * @param {GroupListItem} item
    +         *     The GroupListItem which should be conditionally expanded
    +         *     depending on whether READ permission is granted for any of its
    +         *     descendants.
    +         *
    +         * @param {PemissionFlagSet} flags
    +         *     The set of permissions which should be used to determine whether
    +         *     the given item and its descendants are expanded.
    +         */
    +        var expandReadable = function expandReadable(item, flags) {
    +
    +            // If the current item is expandable and has defined children,
    +            // determine whether it should be expanded
    +            if (item.expandable && item.children) {
    +                angular.forEach(item.children, function expandReadableChild(child) {
    +
    +                    // Determine whether the permission set contains READ
    +                    // permission for the current child object
    +                    var readable = false;
    +                    switch (child.type) {
    +
    +                        case GroupListItem.Type.CONNECTION:
    +                            readable = flags.connectionPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.CONNECTION_GROUP:
    +                            readable = flags.connectionGroupPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.SHARING_PROFILE:
    +                            readable = flags.sharingProfilePermissions.READ[child.identifier];
    +                            break;
    +
    +                    }
    +
    +                    // The parent should be expanded by default if the child is
    +                    // expanded by default OR the permission set contains READ
    +                    // permission on the child
    +                    item.expanded |= expandReadable(child, flags) || readable;
    +
    +                });
    +            }
    +
    +            return item.expanded;
    +
    +        };
    +
    +        // Update default expanded state whenever connection groups and
    +        // associated permissions change
    +        $scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
    +
    +            if (!$scope.rootGroups || !$scope.permissionFlags)
    +                return;
    +
    +            angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
    +
    +                // Automatically expand all objects with any descendants for
    +                // which the permission set contains READ permission
    +                expandReadable(rootGroup, $scope.permissionFlags);
    +
    +            });
    +
    +        });
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to add READ permission for.
    +         */
    +        var addConnectionPermission = function addConnectionPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to remove READ permission for.
    +         */
    +        var removeConnectionPermission = function removeConnectionPermission(identifier) {
    +
    +            // If permission was previously added, simply un-add it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly remove the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection group permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection group to add READ permission
    +         *     for.
    +         */
    +        var addConnectionGroupPermission = function addConnectionGroupPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    --- End diff --
    
    Should now be fixed, though GitHub seems to have magically moved this comment up to the part of the code where "connection" is actually correct.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/guacamole-client/pull/282


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185388275
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    --- End diff --
    
    I suppose it's mainly out of habit here. Since `historyEntries` should always be defined, either would work here. Any browsers which would lack `Array.prototype.forEach()` (IE8 and older) are also unable to run Guacamole for more fundamental reasons, and we do use it elsewhere in the webapp.
    
    Were that not the case, `angular.forEach()` has the benefit that it automatically deals with `null` and `undefined` values.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185952527
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/managementButtons.js ---
    @@ -0,0 +1,201 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * Directive which displays a set of object management buttons (save, delete,
    + * clone, etc.) representing the actions available to the current user in
    + * context of the object being edited/created.
    + */
    +angular.module('manage').directive('managementButtons', ['$injector',
    +    function managementButtons($injector) {
    +
    +    // Required services
    +    var guacNotification = $injector.get('guacNotification');
    +
    +    var directive = {
    +
    +        restrict    : 'E',
    +        replace     : true,
    +        templateUrl : 'app/manage/templates/managementButtons.html',
    +
    +        scope : {
    +
    +            /**
    +             * The translation namespace associated with all applicable
    +             * translation strings. This directive requires at least the
    +             * following translation strings within the given namespace:
    +             *
    +             *     - ACTION_CANCEL
    +             *     - ACTION_CLONE
    +             *     - ACTION_DELETE
    +             *     - ACTION_SAVE
    +             *     - DIALOG_HEADER_CONFIRM_DELETE
    +             *     - TEXT_CONFIRM_DELETE
    +             *
    +             * @type String
    +             */
    +            namespace : '=',
    --- End diff --
    
    Text binding would probably be better, yes. I'll make the change.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by necouchman <gi...@git.apache.org>.
Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185631746
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/connectionPermissionEditor.js ---
    @@ -0,0 +1,406 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * A directive for manipulating the connection permissions granted within a
    + * given {@link PermissionFlagSet}, tracking the specific permissions added or
    + * removed within a separate pair of {@link PermissionSet} objects.
    + */
    +angular.module('manage').directive('connectionPermissionEditor', ['$injector',
    +    function connectionPermissionEditor($injector) {
    +
    +    // Required types
    +    var ConnectionGroup   = $injector.get('ConnectionGroup');
    +    var GroupListItem     = $injector.get('GroupListItem');
    +    var PermissionSet     = $injector.get('PermissionSet');
    +
    +    // Required services
    +    var connectionGroupService = $injector.get('connectionGroupService');
    +    var dataSourceService      = $injector.get('dataSourceService');
    +    var requestService         = $injector.get('requestService');
    +
    +    var directive = {
    +
    +        // Element only
    +        restrict: 'E',
    +        replace: true,
    +
    +        scope: {
    +
    +            /**
    +             * The unique identifier of the data source associated with the
    +             * permissions being manipulated.
    +             *
    +             * @type String
    +             */
    +            dataSource : '=',
    +
    +            /**
    +             * The current state of the permissions being manipulated. This
    +             * {@link PemissionFlagSet} will be modified as changes are made
    +             * through this permission editor.
    +             *
    +             * @type PermissionFlagSet
    +             */
    +            permissionFlags : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsAdded : '=',
    +
    +            /**
    +             * The set of permissions that have been added, relative to the
    +             * initial state of the permissions being manipulated.
    +             *
    +             * @type PermissionSet
    +             */
    +            permissionsRemoved : '='
    +
    +        },
    +
    +        templateUrl: 'app/manage/templates/connectionPermissionEditor.html'
    +
    +    };
    +
    +    directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
    +
    +        /**
    +         * Array of all connection properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionProperties = [
    +            'name',
    +            'protocol'
    +        ];
    +
    +        /**
    +         * Array of all connection group properties that are filterable.
    +         *
    +         * @type String[]
    +         */
    +        $scope.filteredConnectionGroupProperties = [
    +            'name'
    +        ];
    +
    +        /**
    +         * A map of data source identifiers to the root connection groups within
    +         * thost data sources. As only one data source is applicable to any
    +         * particular permission set being edited/created, this will only
    +         * contain a single key.
    +         *
    +         * @type Object.<String, GroupListItem>
    +         */
    +        $scope.rootGroups = null;
    +
    +        // Retrieve all connections for which we have ADMINISTER permission
    +        dataSourceService.apply(
    +            connectionGroupService.getConnectionGroupTree,
    +            [$scope.dataSource],
    +            ConnectionGroup.ROOT_IDENTIFIER,
    +            [PermissionSet.ObjectPermissionType.ADMINISTER]
    +        )
    +        .then(function connectionGroupReceived(rootGroups) {
    +
    +            // Convert all received ConnectionGroup objects into GroupListItems
    +            $scope.rootGroups = {};
    +            angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
    +                $scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
    +            });
    +
    +        }, requestService.WARN);
    +
    +        /**
    +         * Expands all items within the tree descending from the given
    +         * GroupListItem which have at least one descendant for which explicit
    +         * READ permission is granted. The expanded state of all other items is
    +         * left untouched.
    +         *
    +         * @param {GroupListItem} item
    +         *     The GroupListItem which should be conditionally expanded
    +         *     depending on whether READ permission is granted for any of its
    +         *     descendants.
    +         *
    +         * @param {PemissionFlagSet} flags
    +         *     The set of permissions which should be used to determine whether
    +         *     the given item and its descendants are expanded.
    +         */
    +        var expandReadable = function expandReadable(item, flags) {
    +
    +            // If the current item is expandable and has defined children,
    +            // determine whether it should be expanded
    +            if (item.expandable && item.children) {
    +                angular.forEach(item.children, function expandReadableChild(child) {
    +
    +                    // Determine whether the permission set contains READ
    +                    // permission for the current child object
    +                    var readable = false;
    +                    switch (child.type) {
    +
    +                        case GroupListItem.Type.CONNECTION:
    +                            readable = flags.connectionPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.CONNECTION_GROUP:
    +                            readable = flags.connectionGroupPermissions.READ[child.identifier];
    +                            break;
    +
    +                        case GroupListItem.Type.SHARING_PROFILE:
    +                            readable = flags.sharingProfilePermissions.READ[child.identifier];
    +                            break;
    +
    +                    }
    +
    +                    // The parent should be expanded by default if the child is
    +                    // expanded by default OR the permission set contains READ
    +                    // permission on the child
    +                    item.expanded |= expandReadable(child, flags) || readable;
    +
    +                });
    +            }
    +
    +            return item.expanded;
    +
    +        };
    +
    +        // Update default expanded state whenever connection groups and
    +        // associated permissions change
    +        $scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
    +
    +            if (!$scope.rootGroups || !$scope.permissionFlags)
    +                return;
    +
    +            angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
    +
    +                // Automatically expand all objects with any descendants for
    +                // which the permission set contains READ permission
    +                expandReadable(rootGroup, $scope.permissionFlags);
    +
    +            });
    +
    +        });
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to add READ permission for.
    +         */
    +        var addConnectionPermission = function addConnectionPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection to remove READ permission for.
    +         */
    +        var removeConnectionPermission = function removeConnectionPermission(identifier) {
    +
    +            // If permission was previously added, simply un-add it
    +            if (PermissionSet.hasConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly remove the permission
    +            else
    +                PermissionSet.addConnectionPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the addition of the given connection group permission.
    +         *
    +         * @param {String} identifier
    +         *     The identifier of the connection group to add READ permission
    +         *     for.
    +         */
    +        var addConnectionGroupPermission = function addConnectionGroupPermission(identifier) {
    +
    +            // If permission was previously removed, simply un-remove it
    +            if (PermissionSet.hasConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier))
    +                PermissionSet.removeConnectionGroupPermission($scope.permissionsRemoved, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +            // Otherwise, explicitly add the permission
    +            else
    +                PermissionSet.addConnectionGroupPermission($scope.permissionsAdded, PermissionSet.ObjectPermissionType.READ, identifier);
    +
    +        };
    +
    +        /**
    +         * Updates the permissionsAdded and permissionsRemoved permission sets
    +         * to reflect the removal of the given connection permission.
    --- End diff --
    
    connection *group* permission


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by mike-jumper <gi...@git.apache.org>.
Github user mike-jumper commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185389504
  
    --- Diff: guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js ---
    @@ -157,205 +137,178 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
          */
         $scope.isLoaded = function isLoaded() {
     
    -        return $scope.protocols            !== null
    -            && $scope.rootGroup            !== null
    -            && $scope.connection           !== null
    -            && $scope.parameters           !== null
    -            && $scope.historyDateFormat    !== null
    -            && $scope.historyEntryWrappers !== null
    -            && $scope.permissions          !== null
    -            && $scope.attributes           !== null
    -            && $scope.canSaveConnection    !== null
    -            && $scope.canDeleteConnection  !== null
    -            && $scope.canCloneConnection   !== null;
    +        return $scope.protocols             !== null
    +            && $scope.rootGroup             !== null
    +            && $scope.connection            !== null
    +            && $scope.parameters            !== null
    +            && $scope.historyDateFormat     !== null
    +            && $scope.historyEntryWrappers  !== null
    +            && $scope.managementPermissions !== null
    +            && $scope.attributes            !== null;
     
         };
     
    -    // Pull connection attribute schema
    -    schemaService.getConnectionAttributes($scope.selectedDataSource)
    -    .then(function attributesReceived(attributes) {
    -        $scope.attributes = attributes;
    -    }, 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);
    -    
    -    // Query the user's permissions for the current connection
    -    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
    -    .then(function permissionsReceived(permissions) {
    -                
    -        $scope.permissions = permissions;
    -                        
    -        // Check if the connection is new or if the user has UPDATE permission
    -        $scope.canSaveConnection =
    -               !identifier
    -            || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -            || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier);
    -            
    -        // Check if connection is not new and the user has DELETE permission
    -        $scope.canDeleteConnection =
    -            !!identifier && (
    -                   PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
    -               ||  PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier)
    -            );
    -                
    -        // Check if the connection is not new and the user has UPDATE and CREATE_CONNECTION permissions
    -        $scope.canCloneConnection =
    -            !!identifier && (
    -               PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || (
    -                       PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)
    -                   &&  PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION)
    -               )
    -            );
    -    
    -    }, requestService.WARN);
    -   
    -    // Get protocol metadata
    -    schemaService.getProtocols($scope.selectedDataSource)
    -    .then(function protocolsReceived(protocols) {
    -        $scope.protocols = protocols;
    -    }, requestService.WARN);
    -
    -    // Get history date format
    -    $translate('MANAGE_CONNECTION.FORMAT_HISTORY_START').then(function historyDateFormatReceived(historyDateFormat) {
    -        $scope.historyDateFormat = historyDateFormat;
    -    }, angular.noop);
    -
    -    // If we are editing an existing connection, pull its data
    -    if (identifier) {
    -
    -        // Pull data from existing connection
    -        connectionService.getConnection($scope.selectedDataSource, identifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -        }, requestService.WARN);
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for making modifications to that
    +     * existing connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection to
    +     *     load.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection to load.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     editing the given connection.
    +     */
    +    var loadExistingConnection = function loadExistingConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection     : connectionService.getConnection(dataSource, identifier),
    +            historyEntries : connectionService.getConnectionHistory(dataSource, identifier),
    +            parameters     : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
     
    -        // Pull connection history
    -        connectionService.getConnectionHistory($scope.selectedDataSource, identifier)
    -        .then(function historyReceived(historyEntries) {
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
                 // Wrap all history entries for sake of display
                 $scope.historyEntryWrappers = [];
    -            historyEntries.forEach(function wrapHistoryEntry(historyEntry) {
    -               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); 
    +            angular.forEach(values.historyEntries, function wrapHistoryEntry(historyEntry) {
    +               $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry));
                 });
     
    -        }, requestService.WARN);
    +        });
    +    };
     
    -        // Pull connection parameters
    -        connectionService.getConnectionParameters($scope.selectedDataSource, identifier)
    -        .then(function parametersReceived(parameters) {
    -            $scope.parameters = parameters;
    -        }, requestService.WARN);
    -    }
    -    
    -    // If we are cloning an existing connection, pull its data instead
    -    else if (cloneSourceIdentifier) {
    +    /**
    +     * Loads the data associated with the connection having the given
    +     * identifier, preparing the interface for cloning that existing
    +     * connection.
    +     *
    +     * @param {String} dataSource
    +     *     The unique identifier of the data source containing the connection
    +     *     to be cloned.
    +     *
    +     * @param {String} identifier
    +     *     The identifier of the connection being cloned.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     cloning the given connection.
    +     */
    +    var loadClonedConnection = function loadClonedConnection(dataSource, identifier) {
    +        return $q.all({
    +            connection : connectionService.getConnection(dataSource, identifier),
    +            parameters : connectionService.getConnectionParameters(dataSource, identifier)
    +        })
    +        .then(function connectionDataRetrieved(values) {
    +
    +            $scope.connection = values.connection;
    +            $scope.parameters = values.parameters;
     
    -        // Pull data from cloned connection
    -        connectionService.getConnection($scope.selectedDataSource, cloneSourceIdentifier)
    -        .then(function connectionRetrieved(connection) {
    -            $scope.connection = connection;
    -            
                 // Clear the identifier field because this connection is new
                 delete $scope.connection.identifier;
    -        }, requestService.WARN);
     
    -        // Do not pull connection history
    -        $scope.historyEntryWrappers = [];
    -        
    -        // Pull connection parameters from cloned connection
    -        connectionService.getConnectionParameters($scope.selectedDataSource, cloneSourceIdentifier)
    -        .then(function parametersReceived(parameters) {
    -            $scope.parameters = parameters;
    -        }, requestService.WARN);
    -    }
    -
    -    // If we are creating a new connection, populate skeleton connection data
    -    else {
    +            // Cloned connections have no history
    +            $scope.historyEntryWrappers = [];
    +
    +        });
    +    };
    +
    +    /**
    +     * Loads skeleton connection data, preparing the interface for creating a
    +     * new connection.
    +     *
    +     * @returns {Promise}
    +     *     A promise which is resolved when the interface has been prepared for
    +     *     creating a new connection.
    +     */
    +    var loadSkeletonConnection = function loadSkeletonConnection() {
    +
    +        // Use skeleton connection object with no associated permissions,
    +        // history, or parameters
             $scope.connection = new Connection({
                 protocol         : 'vnc',
                 parentIdentifier : $location.search().parent
             });
             $scope.historyEntryWrappers = [];
             $scope.parameters = {};
    -    }
     
    -    /**
    -     * Returns whether the current user can change/set all connection
    -     * attributes for the connection being edited, regardless of whether those
    -     * attributes are already explicitly associated with that connection.
    -     *
    -     * @returns {Boolean}
    -     *     true if the current user can change all attributes for the
    -     *     connection being edited, regardless of whether those attributes are
    -     *     already explicitly associated with that connection, false otherwise.
    -     */
    -    $scope.canChangeAllAttributes = function canChangeAllAttributes() {
    -
    -        // All attributes can be set if we are creating the connection
    -        return !identifier;
    +        return $q.resolve();
     
         };
     
         /**
    -     * Returns the translation string namespace for the protocol having the
    -     * given name. The namespace will be of the form:
    -     *
    -     * <code>PROTOCOL_NAME</code>
    +     * Loads the data requred for performing the management task requested
    --- End diff --
    
    Fxed.


---

[GitHub] guacamole-client pull request #282: GUACAMOLE-220: Extract common base for m...

Posted by necouchman <gi...@git.apache.org>.
Github user necouchman commented on a diff in the pull request:

    https://github.com/apache/guacamole-client/pull/282#discussion_r185633950
  
    --- Diff: guacamole/src/main/webapp/app/manage/directives/managementButtons.js ---
    @@ -0,0 +1,201 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements.  See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership.  The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License.  You may obtain a copy of the License at
    + *
    + *   http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied.  See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +/**
    + * Directive which displays a set of object management buttons (save, delete,
    + * clone, etc.) representing the actions available to the current user in
    + * context of the object being edited/created.
    + */
    +angular.module('manage').directive('managementButtons', ['$injector',
    +    function managementButtons($injector) {
    +
    +    // Required services
    +    var guacNotification = $injector.get('guacNotification');
    +
    +    var directive = {
    +
    +        restrict    : 'E',
    +        replace     : true,
    +        templateUrl : 'app/manage/templates/managementButtons.html',
    +
    +        scope : {
    +
    +            /**
    +             * The translation namespace associated with all applicable
    +             * translation strings. This directive requires at least the
    +             * following translation strings within the given namespace:
    +             *
    +             *     - ACTION_CANCEL
    +             *     - ACTION_CLONE
    +             *     - ACTION_DELETE
    +             *     - ACTION_SAVE
    +             *     - DIALOG_HEADER_CONFIRM_DELETE
    +             *     - TEXT_CONFIRM_DELETE
    +             *
    +             * @type String
    +             */
    +            namespace : '=',
    --- End diff --
    
    Does this need to be a model binding (=) or would this be better as a text binding (@)?  I don't think the namespace would ever be modified within this directive, correct, so it should just be passing it from outside into this directive/controller, right?  Is there a reason to do it this way vs. text binding?  Just curious.


---