mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-27 15:13:07 +00:00 
			
		
		
		
	GUACAMOLE-724: Abstract away groups of running clients within their own type.
This commit is contained in:
		| @@ -26,6 +26,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|     // Required types | ||||
|     var ConnectionGroup    = $injector.get('ConnectionGroup'); | ||||
|     var ManagedClient      = $injector.get('ManagedClient'); | ||||
|     var ManagedClientGroup = $injector.get('ManagedClientGroup'); | ||||
|     var ManagedClientState = $injector.get('ManagedClientState'); | ||||
|     var ManagedFilesystem  = $injector.get('ManagedFilesystem'); | ||||
|     var Protocol           = $injector.get('Protocol'); | ||||
| @@ -152,37 +153,66 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The client which should be attached to the client UI. | ||||
|      * The set of clients that should be attached to the client UI. This will | ||||
|      * be immediately initialized by a call to updateAttachedClients() below. | ||||
|      * | ||||
|      * @type ManagedClient[] | ||||
|      * @type ManagedClientGroup[] | ||||
|      */ | ||||
|     $scope.clients = []; | ||||
|     $scope.clientGroup = null; | ||||
|  | ||||
|     /** | ||||
|      * All active clients which are not any current client ($scope.clients). | ||||
|      * Each key is the ID of the connection used by that client. | ||||
|      * | ||||
|      * @type Object.<String, ManagedClient> | ||||
|      * @borrows ManagedClientGroup.getName | ||||
|      */ | ||||
|     $scope.otherClients = {}; | ||||
|     $scope.getName = ManagedClientGroup.getName; | ||||
|  | ||||
|     /** | ||||
|      * Reloads the contents of $scope.clients and $scope.otherClients to | ||||
|      * reflect the client IDs currently listed in the URL. | ||||
|      * Reloads the contents of $scope.clientGroup to reflect the client IDs | ||||
|      * currently listed in the URL. | ||||
|      */ | ||||
|     var updateAttachedClients = function updateAttachedClients() { | ||||
|  | ||||
|         var ids = $routeParams.id.split(/[ +]/); | ||||
|         var previousClients = $scope.clientGroup ? $scope.clientGroup.clients.slice() : []; | ||||
|         detachCurrentGroup(); | ||||
|  | ||||
|         $scope.clients = []; | ||||
|         $scope.otherClients = angular.extend({}, guacClientManager.getManagedClients()); | ||||
|         $scope.clientGroup = guacClientManager.getManagedClientGroup($routeParams.id); | ||||
|         $scope.clientGroup.attached = true; | ||||
|  | ||||
|         // Separate active clients by whether they should be displayed within | ||||
|         // the current view | ||||
|         ids.forEach(function groupClients(id) { | ||||
|             $scope.clients.push(guacClientManager.getManagedClient(id)); | ||||
|             delete $scope.otherClients[id]; | ||||
|         }); | ||||
|         // Ensure menu is closed if updated view is not a modification of the | ||||
|         // current view (has no clients in common). The menu should remain open | ||||
|         // only while the current view is being modified, not when navigating | ||||
|         // to an entirely different view. | ||||
|         if (_.isEmpty(_.intersection(previousClients, $scope.clientGroup.clients))) | ||||
|             $scope.menu.shown = false; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Detaches the ManagedClientGroup currently attached to the client | ||||
|      * interface via $scope.clientGroup such that the interface can be safely | ||||
|      * cleaned up or another ManagedClientGroup can take its place. | ||||
|      */ | ||||
|     var detachCurrentGroup = function detachCurrentGroup() { | ||||
|  | ||||
|         var managedClientGroup = $scope.clientGroup; | ||||
|         if (managedClientGroup) { | ||||
|  | ||||
|             // Flag group as detached | ||||
|             managedClientGroup.attached = false; | ||||
|  | ||||
|             // Remove all disconnected clients from management (the user has | ||||
|             // seen their status) | ||||
|             _.filter(managedClientGroup.clients, client => { | ||||
|  | ||||
|                 var connectionState = client.clientState.connectionState; | ||||
|                 return connectionState === ManagedClientState.ConnectionState.DISCONNECTED | ||||
|                  || connectionState === ManagedClientState.ConnectionState.TUNNEL_ERROR | ||||
|                  || connectionState === ManagedClientState.ConnectionState.CLIENT_ERROR; | ||||
|  | ||||
|             }).forEach(client => { | ||||
|                 guacClientManager.removeManagedClient(client.id); | ||||
|             }); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }; | ||||
|  | ||||
| @@ -424,7 +454,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|             $scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client); | ||||
|  | ||||
|         // Disable client keyboard if the menu is shown | ||||
|         angular.forEach($scope.clients, function updateKeyboardEnabled(client) { | ||||
|         angular.forEach($scope.clientGroup.clients, function updateKeyboardEnabled(client) { | ||||
|             client.clientProperties.keyboardEnabled = !menuShown; | ||||
|         }); | ||||
|  | ||||
| @@ -606,15 +636,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|      *     otherwise. | ||||
|      */ | ||||
|     $scope.isConnectionUnstable = function isConnectionUnstable() { | ||||
|  | ||||
|         var unstable = false; | ||||
|  | ||||
|         angular.forEach($scope.clients, function checkStability(client) { | ||||
|             unstable |= client.clientState.tunnelUnstable; | ||||
|         }); | ||||
|  | ||||
|         return unstable; | ||||
|  | ||||
|         return _.findIndex($scope.clientGroup.clients, client => client.clientState.tunnelUnstable) !== -1; | ||||
|     }; | ||||
|  | ||||
|  | ||||
| @@ -830,22 +852,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|  | ||||
|     // Clean up when view destroyed | ||||
|     $scope.$on('$destroy', function clientViewDestroyed() { | ||||
|  | ||||
|         // Remove client from client manager if no longer connected | ||||
|         var managedClient = $scope.client; | ||||
|         if (managedClient) { | ||||
|  | ||||
|             // Get current connection state | ||||
|             var connectionState = managedClient.clientState.connectionState; | ||||
|  | ||||
|             // If disconnected, remove from management | ||||
|             if (connectionState === ManagedClientState.ConnectionState.DISCONNECTED | ||||
|              || connectionState === ManagedClientState.ConnectionState.TUNNEL_ERROR | ||||
|              || connectionState === ManagedClientState.ConnectionState.CLIENT_ERROR) | ||||
|                 guacClientManager.removeManagedClient(managedClient.id); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         detachCurrentGroup(); | ||||
|     }); | ||||
|  | ||||
| }]); | ||||
|   | ||||
| @@ -29,6 +29,7 @@ angular.module('client').directive('guacClientPanel', ['$injector', function gua | ||||
|     var sessionStorageFactory = $injector.get('sessionStorageFactory'); | ||||
|  | ||||
|     // Required types | ||||
|     var ManagedClientGroup = $injector.get('ManagedClientGroup'); | ||||
|     var ManagedClientState = $injector.get('ManagedClientState'); | ||||
|  | ||||
|     /** | ||||
| @@ -49,12 +50,12 @@ angular.module('client').directive('guacClientPanel', ['$injector', function gua | ||||
|         scope: { | ||||
|  | ||||
|             /** | ||||
|              * The ManagedClient instances associated with the active | ||||
|              * The ManagedClientGroup instances associated with the active | ||||
|              * connections to be displayed within this panel. | ||||
|              *  | ||||
|              * @type ManagedClient[]|Object.<String, ManagedClient> | ||||
|              * @type ManagedClientGroup[] | ||||
|              */ | ||||
|             clients : '=' | ||||
|             clientGroups : '=' | ||||
|  | ||||
|         }, | ||||
|         templateUrl: 'app/client/templates/guacClientPanel.html', | ||||
| @@ -75,71 +76,68 @@ angular.module('client').directive('guacClientPanel', ['$injector', function gua | ||||
|             $scope.panelHidden = panelHidden; | ||||
|  | ||||
|             /** | ||||
|              * Returns whether this panel currently has any clients associated | ||||
|              * with it. | ||||
|              * Returns whether this panel currently has any client groups | ||||
|              * associated with it. | ||||
|              * | ||||
|              * @return {Boolean} | ||||
|              *     true if at least one client is associated with this panel, | ||||
|              *     false otherwise. | ||||
|              *     true if at least one client group is associated with this | ||||
|              *     panel, false otherwise. | ||||
|              */ | ||||
|             $scope.hasClients = function hasClients() { | ||||
|                 return !!_.find($scope.clients, $scope.isManaged); | ||||
|             $scope.hasClientGroups = function hasClientGroups() { | ||||
|                 return $scope.clientGroups && $scope.clientGroups.length; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns whether the status of the given client has changed in a | ||||
|              * way that requires the user's attention. This may be due to an | ||||
|              * error, or due to a server-initiated disconnect. | ||||
|              * @borrows ManagedClientGroup.getIdentifier | ||||
|              */ | ||||
|             $scope.getIdentifier = ManagedClientGroup.getIdentifier; | ||||
|  | ||||
|             /** | ||||
|              * @borrows ManagedClientGroup.getTitle | ||||
|              */ | ||||
|             $scope.getTitle = ManagedClientGroup.getTitle; | ||||
|  | ||||
|             /** | ||||
|              * Returns whether the status of any client within the given client | ||||
|              * group has changed in a way that requires the user's attention. | ||||
|              * This may be due to an error, or due to a server-initiated | ||||
|              * disconnect. | ||||
|              * | ||||
|              * @param {ManagedClient} client | ||||
|              *     The client to test. | ||||
|              * @param {ManagedClientGroup} clientGroup | ||||
|              *     The client group to test. | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if the given client requires the user's attention, | ||||
|              *     false otherwise. | ||||
|              */ | ||||
|             $scope.hasStatusUpdate = function hasStatusUpdate(client) { | ||||
|             $scope.hasStatusUpdate = function hasStatusUpdate(clientGroup) { | ||||
|                 return _.findIndex(clientGroup.clients, (client) => { | ||||
|  | ||||
|                 // Test whether the client has encountered an error | ||||
|                 switch (client.clientState.connectionState) { | ||||
|                     case ManagedClientState.ConnectionState.CONNECTION_ERROR: | ||||
|                     case ManagedClientState.ConnectionState.TUNNEL_ERROR: | ||||
|                     case ManagedClientState.ConnectionState.DISCONNECTED: | ||||
|                         return true; | ||||
|                 } | ||||
|                     // Test whether the client has encountered an error | ||||
|                     switch (client.clientState.connectionState) { | ||||
|                         case ManagedClientState.ConnectionState.CONNECTION_ERROR: | ||||
|                         case ManagedClientState.ConnectionState.TUNNEL_ERROR: | ||||
|                         case ManagedClientState.ConnectionState.DISCONNECTED: | ||||
|                             return true; | ||||
|                     } | ||||
|  | ||||
|                 return false; | ||||
|                     return false; | ||||
|  | ||||
|                 }) !== -1; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns whether the given client is currently being managed by | ||||
|              * the guacClientManager service. | ||||
|              * Initiates an orderly disconnect of all clients within the given | ||||
|              * group. The clients are removed from management such that | ||||
|              * attempting to connect to any of the same connections will result | ||||
|              * in new connections being established, rather than displaying a | ||||
|              * notification that the connection has ended. | ||||
|              * | ||||
|              * @param {ManagedClient} client | ||||
|              *     The client to test. | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if the given client is being managed by the | ||||
|              *     guacClientManager service, false otherwise. | ||||
|              * @param {ManagedClientGroup} clientGroup | ||||
|              *     The group of clients to disconnect. | ||||
|              */ | ||||
|             $scope.isManaged = function isManaged(client) { | ||||
|                 return !!guacClientManager.getManagedClients()[client.id]; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Initiates an orderly disconnect of the given client. The client | ||||
|              * is removed from management such that attempting to connect to | ||||
|              * the same connection will result in a new connection being | ||||
|              * established, rather than displaying a notification that the | ||||
|              * connection has ended. | ||||
|              * | ||||
|              * @param {type} client | ||||
|              * @returns {undefined} | ||||
|              */ | ||||
|             $scope.disconnect = function disconnect(client) { | ||||
|                 client.client.disconnect(); | ||||
|                 guacClientManager.removeManagedClient(client.id); | ||||
|             $scope.disconnect = function disconnect(clientGroup) { | ||||
|                 guacClientManager.removeManagedClientGroup(ManagedClientGroup.getIdentifier(clientGroup)); | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|   | ||||
| @@ -32,50 +32,17 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient | ||||
|     directive.scope = { | ||||
|  | ||||
|         /** | ||||
|          * The Guacamole clients that should be displayed in an evenly-tiled | ||||
|          * grid arrangement. | ||||
|          * The group of Guacamole clients that should be displayed in an | ||||
|          * evenly-tiled grid arrangement. | ||||
|          * | ||||
|          * @type ManagedClient[] | ||||
|          * @type ManagedClientGroup | ||||
|          */ | ||||
|         clients : '=' | ||||
|         clientGroup : '=' | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     directive.controller = ['$scope', '$injector', '$element', | ||||
|             function guacTiledListController($scope, $injector, $element) { | ||||
|  | ||||
|         /** | ||||
|          * Returns the number of columns that should be used to evenly arrange | ||||
|          * all provided clients in a tiled grid. | ||||
|          * | ||||
|          * @returns {Number} | ||||
|          *     The number of columns that should be used for the grid of | ||||
|          *     clients. | ||||
|          */ | ||||
|         var getColumns = function getColumns() { | ||||
|  | ||||
|             if (!$scope.clients || !$scope.clients.length) | ||||
|                 return 0; | ||||
|  | ||||
|             return Math.ceil(Math.sqrt($scope.clients.length)); | ||||
|  | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Returns the number of rows that should be used to evenly arrange all | ||||
|          * provided clients in a tiled grid. | ||||
|          * | ||||
|          * @returns {Number} | ||||
|          *     The number of rows that should be used for the grid of clients. | ||||
|          */ | ||||
|         var getRows = function getRows() { | ||||
|  | ||||
|             if (!$scope.clients || !$scope.clients.length) | ||||
|                 return 0; | ||||
|  | ||||
|             return Math.ceil($scope.clients.length / getColumns()); | ||||
|  | ||||
|         }; | ||||
|             function guacTiledClientsController($scope, $injector, $element) { | ||||
|  | ||||
|         /** | ||||
|          * Assigns keyboard focus to the given client, allowing that client to | ||||
| @@ -86,7 +53,14 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient | ||||
|          *     The client that should receive keyboard focus. | ||||
|          */ | ||||
|         $scope.assignFocus = function assignFocus(client) { | ||||
|  | ||||
|             // Clear focus of all other clients | ||||
|             $scope.clientGroup.clients.forEach(client => { | ||||
|                 client.clientProperties.focused = false; | ||||
|             }); | ||||
|  | ||||
|             client.clientProperties.focused = true; | ||||
|  | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -98,10 +72,10 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient | ||||
|          *     otherwise. | ||||
|          */ | ||||
|         $scope.hasMultipleClients = function hasMultipleClients() { | ||||
|             return $scope.clients && $scope.clients.length > 1; | ||||
|             return $scope.clientGroup && $scope.clientGroup.clients.length > 1; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|        /** | ||||
|          * Returns the CSS width that should be applied to each tile to | ||||
|          * achieve an even arrangement. | ||||
|          * | ||||
| @@ -109,7 +83,7 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient | ||||
|          *     The CSS width that should be applied to each tile. | ||||
|          */ | ||||
|         $scope.getTileWidth = function getTileWidth() { | ||||
|             return Math.floor(100 / getColumns()) + '%'; | ||||
|             return Math.floor(100 / $scope.clientGroup.columns) + '%'; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -120,22 +94,7 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient | ||||
|          *     The CSS height that should be applied to each tile. | ||||
|          */ | ||||
|         $scope.getTileHeight = function getTileHeight() { | ||||
|             return Math.floor(100 / getRows()) + '%'; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Returns the display title of the given Guacamole client. If the | ||||
|          * title is not yet known, a placeholder title will be returned. | ||||
|          * | ||||
|          * @param {ManagedClient} client | ||||
|          *     The client whose title should be retrieved. | ||||
|          * | ||||
|          * @returns {String} | ||||
|          *     The title of the given client, or a placeholder title if the | ||||
|          *     client's title is not yet known. | ||||
|          */ | ||||
|         $scope.getClientTitle = function getClientTitle(client) { | ||||
|             return client.title || '...'; | ||||
|             return Math.floor(100 / $scope.clientGroup.rows) + '%'; | ||||
|         }; | ||||
|  | ||||
|     }]; | ||||
|   | ||||
| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|  * 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 displaying a group of Guacamole clients as a non-interactive | ||||
|  * thumbnail of tiled client displays. | ||||
|  */ | ||||
| angular.module('client').directive('guacTiledThumbnails', [function guacTiledThumbnails() { | ||||
|  | ||||
|     var directive = { | ||||
|         restrict: 'E', | ||||
|         replace: true, | ||||
|         templateUrl: 'app/client/templates/guacTiledThumbnails.html' | ||||
|     }; | ||||
|  | ||||
|     directive.scope = { | ||||
|  | ||||
|         /** | ||||
|          * The group of clients to display as a thumbnail of tiled client | ||||
|          * displays. | ||||
|          * | ||||
|          * @type ManagedClientGroup | ||||
|          */ | ||||
|         clientGroup : '=' | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     directive.controller = ['$scope', '$injector', '$element', | ||||
|             function guacTiledThumbnailsController($scope, $injector, $element) { | ||||
|  | ||||
|        /** | ||||
|          * Returns the CSS width that should be applied to each tile to | ||||
|          * achieve an even arrangement. | ||||
|          * | ||||
|          * @returns {String} | ||||
|          *     The CSS width that should be applied to each tile. | ||||
|          */ | ||||
|         $scope.getTileWidth = function getTileWidth() { | ||||
|             return Math.floor(100 / $scope.clientGroup.columns) + '%'; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Returns the CSS height that should be applied to each tile to | ||||
|          * achieve an even arrangement. | ||||
|          * | ||||
|          * @returns {String} | ||||
|          *     The CSS height that should be applied to each tile. | ||||
|          */ | ||||
|         $scope.getTileHeight = function getTileHeight() { | ||||
|             return Math.floor(100 / $scope.clientGroup.rows) + '%'; | ||||
|         }; | ||||
|  | ||||
|     }]; | ||||
|  | ||||
|     return directive; | ||||
|  | ||||
| }]); | ||||
| @@ -24,7 +24,8 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|         function guacClientManager($injector) { | ||||
|  | ||||
|     // Required types | ||||
|     var ManagedClient = $injector.get('ManagedClient'); | ||||
|     var ManagedClient      = $injector.get('ManagedClient'); | ||||
|     var ManagedClientGroup = $injector.get('ManagedClientGroup'); | ||||
|  | ||||
|     // Required services | ||||
|     var $window               = $injector.get('$window'); | ||||
| @@ -56,6 +57,53 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|         return storedManagedClients(); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Getter/setter which retrieves or sets the array of all active managed | ||||
|      * client groups. | ||||
|      * | ||||
|      * @type Function | ||||
|      */ | ||||
|     var storedManagedClientGroups = sessionStorageFactory.create([], function destroyClientGroupStorage() { | ||||
|  | ||||
|         // Disconnect all clients when storage is destroyed | ||||
|         service.clear(); | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     /** | ||||
|      * Returns an array of all managed client groups. | ||||
|      * | ||||
|      * @returns {ManagedClientGroup[]>} | ||||
|      *     An array of all active managed client groups. | ||||
|      */ | ||||
|     service.getManagedClientGroups = function getManagedClientGroups() { | ||||
|         return storedManagedClientGroups(); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Removes the ManagedClient with the given ID from all | ||||
|      * ManagedClientGroups, automatically adjusting the tile size of the | ||||
|      * clients that remain in each group. All client groups that are empty | ||||
|      * after the client is removed will also be removed. | ||||
|      * | ||||
|      * @param {string} id | ||||
|      *     The ID of the ManagedClient to remove. | ||||
|      */ | ||||
|     var ungroupManagedClient = function ungroupManagedClient(id) { | ||||
|  | ||||
|         var managedClientGroups = storedManagedClientGroups(); | ||||
|  | ||||
|         // Remove client from all groups | ||||
|         managedClientGroups.forEach(group => { | ||||
|             _.remove(group.clients, client => (client.id === id)); | ||||
|             ManagedClientGroup.recalculateTiles(group); | ||||
|         }); | ||||
|  | ||||
|         // Remove any groups that are now empty | ||||
|         _.remove(managedClientGroups, group => !group.clients.length); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Removes the existing ManagedClient associated with the connection having | ||||
|      * the given ID, if any. If no such a ManagedClient already exists, this | ||||
| @@ -67,13 +115,16 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|      * @returns {Boolean} | ||||
|      *     true if an existing client was removed, false otherwise. | ||||
|      */ | ||||
|     service.removeManagedClient = function replaceManagedClient(id) { | ||||
|  | ||||
|     service.removeManagedClient = function removeManagedClient(id) { | ||||
|          | ||||
|         var managedClients = storedManagedClients(); | ||||
|  | ||||
|         // Remove client if it exists | ||||
|         if (id in managedClients) { | ||||
|  | ||||
|             // Pull client out of any containing groups | ||||
|             ungroupManagedClient(id); | ||||
|  | ||||
|             // Disconnect and remove | ||||
|             managedClients[id].client.disconnect(); | ||||
|             delete managedClients[id]; | ||||
| @@ -102,11 +153,31 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|      */ | ||||
|     service.replaceManagedClient = function replaceManagedClient(id) { | ||||
|  | ||||
|         // Disconnect any existing client | ||||
|         service.removeManagedClient(id); | ||||
|         var managedClients = storedManagedClients(); | ||||
|         var managedClientGroups = storedManagedClientGroups(); | ||||
|  | ||||
|         // Set new client | ||||
|         return storedManagedClients()[id] = ManagedClient.getInstance(id); | ||||
|         // Remove client if it exists | ||||
|         if (id in managedClients) { | ||||
|  | ||||
|             var hadFocus = managedClients[id].clientProperties.focused; | ||||
|             managedClients[id].client.disconnect(); | ||||
|             delete managedClients[id]; | ||||
|              | ||||
|             // Remove client from all groups | ||||
|             managedClientGroups.forEach(group => { | ||||
|  | ||||
|                 var index = _.findIndex(group.clients, client => (client.id === id)); | ||||
|                 if (index === -1) | ||||
|                     return; | ||||
|  | ||||
|                 group.clients[index] = managedClients[id] = ManagedClient.getInstance(id); | ||||
|                 managedClients[id].clientProperties.focused = hadFocus; | ||||
|  | ||||
|             }); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return managedClients[id]; | ||||
|  | ||||
|     }; | ||||
|  | ||||
| @@ -126,6 +197,10 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|  | ||||
|         var managedClients = storedManagedClients(); | ||||
|  | ||||
|         // Ensure any existing client is removed from its containing group | ||||
|         // prior to being returned | ||||
|         ungroupManagedClient(id); | ||||
|  | ||||
|         // Create new managed client if it doesn't already exist | ||||
|         if (!(id in managedClients)) | ||||
|             managedClients[id] = ManagedClient.getInstance(id); | ||||
| @@ -136,7 +211,81 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Disconnects and removes all currently-connected clients. | ||||
|      * Returns the ManagedClientGroup having the given ID. If no such | ||||
|      * ManagedClientGroup exists, a new ManagedClientGroup is created by | ||||
|      * extracting the relevant connections from the ID. | ||||
|      * | ||||
|      * @param {String} id | ||||
|      *     The ID of the ManagedClientGroup to retrieve or create. | ||||
|      * | ||||
|      * @returns {ManagedClientGroup} | ||||
|      *     The ManagedClientGroup having the given ID. | ||||
|      */ | ||||
|     service.getManagedClientGroup = function getManagedClientGroup(id) { | ||||
|  | ||||
|         var clients = []; | ||||
|         var clientIds = ManagedClientGroup.getClientIdentifiers(id); | ||||
|  | ||||
|         // Separate active clients by whether they should be displayed within | ||||
|         // the current view | ||||
|         clientIds.forEach(function groupClients(id) { | ||||
|             clients.push(service.getManagedClient(id)); | ||||
|         }); | ||||
|  | ||||
|         if (clients.length === 1) { | ||||
|             clients[0].clientProperties.focused = true; | ||||
|         } | ||||
|  | ||||
|         var group = new ManagedClientGroup({ | ||||
|             clients : clients | ||||
|         }); | ||||
|  | ||||
|         var managedClientGroups = storedManagedClientGroups(); | ||||
|         managedClientGroups.push(group); | ||||
|         return group; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Removes the existing ManagedClientGroup having the given ID, if any, | ||||
|      * disconnecting and removing all ManagedClients associated with that | ||||
|      * group. If no such a ManagedClientGroup currently exists, this function | ||||
|      * has no effect. | ||||
|      * | ||||
|      * @param {String} id | ||||
|      *     The ID of the ManagedClientGroup to remove. | ||||
|      *  | ||||
|      * @returns {Boolean} | ||||
|      *     true if a ManagedClientGroup was removed, false otherwise. | ||||
|      */ | ||||
|     service.removeManagedClientGroup = function removeManagedClientGroup(id) { | ||||
|  | ||||
|         var managedClients = storedManagedClients(); | ||||
|         var managedClientGroups = storedManagedClientGroups(); | ||||
|  | ||||
|         // Remove all matching groups (there SHOULD only be one) | ||||
|         var removed = _.remove(managedClientGroups, (group) => ManagedClientGroup.getIdentifier(group) === id); | ||||
|  | ||||
|         // Disconnect all clients associated with the removed group(s) | ||||
|         removed.forEach((group) => { | ||||
|             group.clients.forEach((client) => { | ||||
|  | ||||
|                 var id = client.id; | ||||
|                 if (managedClients[id]) { | ||||
|                     managedClients[id].client.disconnect(); | ||||
|                     delete managedClients[id]; | ||||
|                 } | ||||
|  | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         return !!removed.length; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Disconnects and removes all currently-connected clients and client | ||||
|      * groups. | ||||
|      */ | ||||
|     service.clear = function clear() { | ||||
|  | ||||
| @@ -146,8 +295,9 @@ angular.module('client').factory('guacClientManager', ['$injector', | ||||
|         for (var id in managedClients) | ||||
|             managedClients[id].client.disconnect(); | ||||
|  | ||||
|         // Clear managed clients | ||||
|         // Clear managed clients and client groups | ||||
|         storedManagedClients({}); | ||||
|         storedManagedClientGroups([]); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -9,12 +9,7 @@ | ||||
|             <div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch"> | ||||
|  | ||||
|                 <!-- All connections in current display --> | ||||
|                 <guac-tiled-clients clients="clients"></guac-tiled-clients> | ||||
|  | ||||
|                 <!-- All other active connections --> | ||||
|                 <div id="other-connections"> | ||||
|                     <guac-client-panel clients="otherClients"></guac-client-panel> | ||||
|                 </div> | ||||
|                 <guac-tiled-clients client-group="clientGroup"></guac-tiled-clients> | ||||
|  | ||||
|             </div> | ||||
|  | ||||
| @@ -52,9 +47,9 @@ | ||||
|  | ||||
|             <!-- Stationary header --> | ||||
|             <div class="header"> | ||||
|                 <h2 ng-hide="rootConnectionGroups">{{client.name}}</h2> | ||||
|                 <h2 ng-hide="rootConnectionGroups">{{ getName(clientGroup) }}</h2> | ||||
|                 <h2 class="connection-select-menu" ng-show="rootConnectionGroups"> | ||||
|                     <guac-menu menu-title="client.name" interactive="true"> | ||||
|                     <guac-menu menu-title="getName(clientGroup)" interactive="true"> | ||||
|                         <div class="all-connections"> | ||||
|                             <guac-group-list-filter connection-groups="rootConnectionGroups" | ||||
|                                 filtered-connection-groups="filteredRootConnectionGroups" | ||||
|   | ||||
| @@ -1,29 +1,27 @@ | ||||
| <div class="client-panel" | ||||
|      ng-class="{ 'has-clients': hasClients(), 'hidden' : panelHidden() }"> | ||||
|      ng-class="{ 'has-clients': hasClientGroups(), 'hidden' : panelHidden() }"> | ||||
|  | ||||
|     <!-- Toggle panel visibility --> | ||||
|     <div class="client-panel-handle" ng-click="togglePanel()"></div> | ||||
|  | ||||
|     <!-- List of connection thumbnails --> | ||||
|     <ul class="client-panel-connection-list"> | ||||
|         <li ng-repeat="client in clients | toArray | orderBy: [ '-value.lastUsed', 'value.title' ]" | ||||
|             ng-class="{ 'needs-attention' : hasStatusUpdate(client.value) }" | ||||
|             ng-show="isManaged(client.value)" | ||||
|         <li ng-repeat="clientGroup in clientGroups" | ||||
|             ng-if="!clientGroup.attached" | ||||
|             ng-class="{ 'needs-attention' : hasStatusUpdate(clientGroup) }" | ||||
|             class="client-panel-connection"> | ||||
|  | ||||
|             <!-- Close connection --> | ||||
|             <button class="close-other-connection" ng-click="disconnect(client.value)"> | ||||
|             <button class="close-other-connection" ng-click="disconnect(clientGroup)"> | ||||
|                 <img ng-attr-alt="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}" | ||||
|                      ng-attr-title="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}" | ||||
|                      src="images/x.png"> | ||||
|             </button> | ||||
|  | ||||
|             <!-- Thumbnail --> | ||||
|             <a href="#/client/{{client.value.id}}"> | ||||
|                 <div class="thumbnail"> | ||||
|                     <guac-thumbnail client="client.value"></guac-thumbnail> | ||||
|                 </div> | ||||
|                 <div class="name">{{ client.value.title }}</div> | ||||
|             <a href="#/client/{{ getIdentifier(clientGroup) }}"> | ||||
|                 <guac-tiled-thumbnails client-group="clientGroup"></guac-tiled-thumbnails> | ||||
|                 <div class="name">{{ getTitle(clientGroup) }}</div> | ||||
|             </a> | ||||
|  | ||||
|         </li> | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| <ul class="tiled-client-list" ng-class="{  'multiple-clients' : hasMultipleClients() }"> | ||||
|  | ||||
|     <li class="client-tile" | ||||
|         ng-repeat="client in clients" | ||||
|         ng-repeat="client in clientGroup.clients" | ||||
|         ng-style="{ 'width' : getTileWidth(), 'height' : getTileHeight() }" | ||||
|         ng-class="{ 'focused' : client.clientProperties.focused }" | ||||
|         ng-click="assignFocus(client)"> | ||||
|  | ||||
|         <h3>{{ getClientTitle(client) }}</h3> | ||||
|         <h3>{{ client.title }}</h3> | ||||
|         <guac-client client="client"></guac-client> | ||||
|  | ||||
|         <!-- Client-specific status/error dialog --> | ||||
|   | ||||
| @@ -0,0 +1,7 @@ | ||||
| <ul class="tiled-client-list"> | ||||
|     <li class="client-tile" | ||||
|         ng-repeat="client in clientGroup.clients" | ||||
|         ng-style="{ 'width' : getTileWidth(), 'height' : getTileHeight() }"> | ||||
|         <guac-thumbnail client="client"></guac-thumbnail> | ||||
|     </li> | ||||
| </ul> | ||||
| @@ -62,8 +62,9 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|  | ||||
|     /** | ||||
|      * Object which serves as a surrogate interface, encapsulating a Guacamole | ||||
|      * client while it is active, allowing it to be detached and reattached | ||||
|      * from different client views. | ||||
|      * client while it is active, allowing it to be maintained in the | ||||
|      * background. One or more ManagedClients are grouped within | ||||
|      * ManagedClientGroups before being attached to the client view. | ||||
|      *  | ||||
|      * @constructor | ||||
|      * @param {ManagedClient|Object} [template={}] | ||||
|   | ||||
| @@ -0,0 +1,209 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Provides the ManagedClientGroup class used by the guacClientManager service. | ||||
|  */ | ||||
| angular.module('client').factory('ManagedClientGroup', [function defineManagedClientGroup() { | ||||
|  | ||||
|     /** | ||||
|      * Object which serves as a grouping of ManagedClients. Each | ||||
|      * ManagedClientGroup may be attached, detached, and reattached dynamically | ||||
|      * from different client views, with its contents automatically displayed | ||||
|      * in a tiled arrangment if needed. | ||||
|      *  | ||||
|      * @constructor | ||||
|      * @param {ManagedClientGroup|Object} [template={}] | ||||
|      *     The object whose properties should be copied within the new | ||||
|      *     ManagedClientGroup. | ||||
|      */ | ||||
|     var ManagedClientGroup = function ManagedClientGroup(template) { | ||||
|  | ||||
|         // Use empty object by default | ||||
|         template = template || {}; | ||||
|  | ||||
|         /** | ||||
|          * Whether this ManagedClientGroup is currently attached to the client | ||||
|          * interface (true) or is running in the background (false). | ||||
|          * | ||||
|          * @type {boolean} | ||||
|          * @default false | ||||
|          */ | ||||
|         this.attached = template.attached || false; | ||||
|  | ||||
|         /** | ||||
|          * The clients that should be displayed within the client interface | ||||
|          * when this group is attached. | ||||
|          * | ||||
|          * @type {ManagedClient[]} | ||||
|          * @default [] | ||||
|          */ | ||||
|         this.clients = template.clients || []; | ||||
|  | ||||
|         /** | ||||
|          * The number of rows that should be used when arranging the clients | ||||
|          * within this group in a grid. By default, this value is automatically | ||||
|          * calculated from the number of clients. | ||||
|          * | ||||
|          * @type {number} | ||||
|          */ | ||||
|         this.rows = template.rows || ManagedClientGroup.getRows(this); | ||||
|  | ||||
|         /** | ||||
|          * The number of columns that should be used when arranging the clients | ||||
|          * within this group in a grid. By default, this value is automatically | ||||
|          * calculated from the number of clients. | ||||
|          * | ||||
|          * @type {number} | ||||
|          */ | ||||
|         this.columns = template.columns || ManagedClientGroup.getColumns(this); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Updates the number of rows and columns stored within the given | ||||
|      * ManagedClientGroup such that the clients within the group are evenly | ||||
|      * distributed. This function should be called whenever the size of a | ||||
|      * group changes. | ||||
|      * | ||||
|      * @param {ManagedClientGroup} group | ||||
|      *     The ManagedClientGroup that should be updated. | ||||
|      */ | ||||
|     ManagedClientGroup.recalculateTiles = function recalculateTiles(group) { | ||||
|  | ||||
|         var recalculated = new ManagedClientGroup({ | ||||
|             clients : group.clients | ||||
|         }); | ||||
|  | ||||
|         group.rows = recalculated.rows; | ||||
|         group.columns = recalculated.columns; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns the unique ID representing the given ManagedClientGroup. The ID | ||||
|      * of each ManagedClientGroup consists simply of the IDs of all its | ||||
|      * ManagedClients, separated by periods. | ||||
|      * | ||||
|      * @param {ManagedClientGroup} group | ||||
|      *     The ManagedClientGroup to determine the ID of. | ||||
|      * | ||||
|      * @returns {string} | ||||
|      *     The unique ID representing the given ManagedClientGroup. | ||||
|      */ | ||||
|     ManagedClientGroup.getIdentifier = function getIdentifier(group) { | ||||
|         return _.map(group.clients, client => client.id).join('.'); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns an array of client identifiers for all clients contained within | ||||
|      * the given ManagedClientGroup. Order of the identifiers is preserved | ||||
|      * with respect to the order of the clients within the group. | ||||
|      * | ||||
|      * @param {ManagedClientGroup|string} group | ||||
|      *     The ManagedClientGroup to retrieve the client identifiers from, | ||||
|      *     or its ID. | ||||
|      * | ||||
|      * @returns {string[]} | ||||
|      *     The client identifiers of all clients contained within the given | ||||
|      *     ManagedClientGroup. | ||||
|      */ | ||||
|     ManagedClientGroup.getClientIdentifiers = function getClientIdentifiers(group) { | ||||
|  | ||||
|         if (_.isString(group)) | ||||
|             return group.split(/\./); | ||||
|  | ||||
|         return group.clients.map(client => client.id); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns the number of columns that should be used to evenly arrange | ||||
|      * all provided clients in a tiled grid. | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      *     The number of columns that should be used for the grid of | ||||
|      *     clients. | ||||
|      */ | ||||
|     ManagedClientGroup.getColumns = function getColumns(group) { | ||||
|  | ||||
|         if (!group.clients.length) | ||||
|             return 0; | ||||
|  | ||||
|         return Math.ceil(Math.sqrt(group.clients.length)); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns the number of rows that should be used to evenly arrange all | ||||
|      * provided clients in a tiled grid. | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      *     The number of rows that should be used for the grid of clients. | ||||
|      */ | ||||
|     ManagedClientGroup.getRows = function getRows(group) { | ||||
|  | ||||
|         if (!group.clients.length) | ||||
|             return 0; | ||||
|  | ||||
|         return Math.ceil(group.clients.length / ManagedClientGroup.getColumns(group)); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns the title which should be displayed as the page title if the | ||||
|      * given client group is attached to the interface. | ||||
|      * | ||||
|      * @param {ManagedClientGroup} group | ||||
|      *     The ManagedClientGroup to determine the title of. | ||||
|      * | ||||
|      * @returns {string} | ||||
|      *     The title of the given ManagedClientGroup. | ||||
|      */ | ||||
|     ManagedClientGroup.getTitle = function getTitle(group) { | ||||
|  | ||||
|         // Use client-specific title if only one client | ||||
|         if (group.clients.length === 1) | ||||
|             return group.clients[0].title; | ||||
|  | ||||
|         // With multiple clients, somehow combining multiple page titles would | ||||
|         // be confusing. Instead, use the combined names. | ||||
|         return ManagedClientGroup.getName(group); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns the combined names of all clients within the given | ||||
|      * ManagedClientGroup, as determined by the names of the associated | ||||
|      * connections or connection groups. | ||||
|      * | ||||
|      * @param {ManagedClientGroup} group | ||||
|      *     The ManagedClientGroup to determine the name of. | ||||
|      * | ||||
|      * @returns {string} | ||||
|      *     The combined names of all clients within the given | ||||
|      *     ManagedClientGroup. | ||||
|      */ | ||||
|     ManagedClientGroup.getName = function getName(group) { | ||||
|         return _.filter(group.clients, (client => !!client.name)).map(client => client.name).join(', ') || '...'; | ||||
|     }; | ||||
|  | ||||
|     return ManagedClientGroup; | ||||
|  | ||||
| }]); | ||||
| @@ -18,7 +18,8 @@ | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * A directive which displays the contents of a connection group. | ||||
|  * A directive which displays the recently-accessed connections nested beneath | ||||
|  * each of the given connection groups. | ||||
|  */ | ||||
| angular.module('home').directive('guacRecentConnections', [function guacRecentConnections() { | ||||
|  | ||||
| @@ -44,21 +45,12 @@ angular.module('home').directive('guacRecentConnections', [function guacRecentCo | ||||
|         controller: ['$scope', '$injector', function guacRecentConnectionsController($scope, $injector) { | ||||
|  | ||||
|             // Required types | ||||
|             var ActiveConnection = $injector.get('ActiveConnection'); | ||||
|             var ClientIdentifier = $injector.get('ClientIdentifier'); | ||||
|             var RecentConnection = $injector.get('RecentConnection'); | ||||
|  | ||||
|             // Required services | ||||
|             var guacClientManager = $injector.get('guacClientManager'); | ||||
|             var guacHistory       = $injector.get('guacHistory'); | ||||
|  | ||||
|             /** | ||||
|              * Array of all known and visible active connections. | ||||
|              * | ||||
|              * @type ActiveConnection[] | ||||
|              */ | ||||
|             $scope.activeConnections = []; | ||||
|  | ||||
|             /** | ||||
|              * Array of all known and visible recently-used connections. | ||||
|              * | ||||
| @@ -68,16 +60,12 @@ angular.module('home').directive('guacRecentConnections', [function guacRecentCo | ||||
|  | ||||
|             /** | ||||
|              * Returns whether recent connections are available for display. | ||||
|              * Note that, for the sake of this directive, recent connections | ||||
|              * include any currently-active connections, even if they are not | ||||
|              * yet in the history. | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if recent (or active) connections are present, false | ||||
|              *     otherwise. | ||||
|              *     true if recent connections are present, false otherwise. | ||||
|              */ | ||||
|             $scope.hasRecentConnections = function hasRecentConnections() { | ||||
|                 return !!($scope.activeConnections.length || $scope.recentConnections.length); | ||||
|                 return !!$scope.recentConnections.length; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
| @@ -149,7 +137,6 @@ angular.module('home').directive('guacRecentConnections', [function guacRecentCo | ||||
|             $scope.$watch("rootGroups", function setRootGroups(rootGroups) { | ||||
|  | ||||
|                 // Clear connection arrays | ||||
|                 $scope.activeConnections = []; | ||||
|                 $scope.recentConnections = []; | ||||
|  | ||||
|                 // Produce collection of visible objects | ||||
| @@ -160,29 +147,11 @@ angular.module('home').directive('guacRecentConnections', [function guacRecentCo | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 var managedClients = guacClientManager.getManagedClients(); | ||||
|  | ||||
|                 // Add all active connections | ||||
|                 for (var id in managedClients) { | ||||
|  | ||||
|                     // Get corresponding managed client | ||||
|                     var client = managedClients[id]; | ||||
|  | ||||
|                     // Add active connections for clients with associated visible objects | ||||
|                     if (id in visibleObjects) { | ||||
|  | ||||
|                         var object = visibleObjects[id]; | ||||
|                         $scope.activeConnections.push(new ActiveConnection(object.name, client)); | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|                 // Add any recent connections that are visible | ||||
|                 guacHistory.recentConnections.forEach(function addRecentConnection(historyEntry) { | ||||
|  | ||||
|                     // Add recent connections for history entries with associated visible objects | ||||
|                     if (historyEntry.id in visibleObjects && !(historyEntry.id in managedClients)) { | ||||
|                     if (historyEntry.id in visibleObjects) { | ||||
|  | ||||
|                         var object = visibleObjects[historyEntry.id]; | ||||
|                         $scope.recentConnections.push(new RecentConnection(object.name, historyEntry)); | ||||
|   | ||||
| @@ -3,23 +3,6 @@ | ||||
|     <!-- Text displayed if no recent connections exist --> | ||||
|     <p class="placeholder" ng-hide="hasRecentConnections()">{{'HOME.INFO_NO_RECENT_CONNECTIONS' | translate}}</p> | ||||
|  | ||||
|     <!-- All active connections --> | ||||
|     <div ng-repeat="activeConnection in activeConnections" class="connection"> | ||||
|         <a href="#/client/{{activeConnection.client.id}}"> | ||||
|  | ||||
|             <!-- Connection thumbnail --> | ||||
|             <div class="thumbnail"> | ||||
|                 <guac-thumbnail client="activeConnection.client"></guac-thumbnail> | ||||
|             </div> | ||||
|  | ||||
|             <!-- Connection name --> | ||||
|             <div class="caption"> | ||||
|                 <span class="name">{{activeConnection.name}}</span> | ||||
|             </div> | ||||
|  | ||||
|         </a> | ||||
|     </div> | ||||
|      | ||||
|     <!-- All recent connections --> | ||||
|     <div ng-repeat="recentConnection in recentConnections" class="connection"> | ||||
|         <a href="#/client/{{recentConnection.entry.id}}"> | ||||
|   | ||||
| @@ -24,11 +24,12 @@ angular.module('index').controller('indexController', ['$scope', '$injector', | ||||
|         function indexController($scope, $injector) { | ||||
|  | ||||
|     // Required services | ||||
|     var $document        = $injector.get('$document'); | ||||
|     var $route           = $injector.get('$route'); | ||||
|     var $window          = $injector.get('$window'); | ||||
|     var clipboardService = $injector.get('clipboardService'); | ||||
|     var guacNotification = $injector.get('guacNotification'); | ||||
|     var $document         = $injector.get('$document'); | ||||
|     var $route            = $injector.get('$route'); | ||||
|     var $window           = $injector.get('$window'); | ||||
|     var clipboardService  = $injector.get('clipboardService'); | ||||
|     var guacNotification  = $injector.get('guacNotification'); | ||||
|     var guacClientManager = $injector.get('guacClientManager'); | ||||
|  | ||||
|     /** | ||||
|      * The error that prevents the current page from rendering at all. If no | ||||
| @@ -43,6 +44,14 @@ angular.module('index').controller('indexController', ['$scope', '$injector', | ||||
|      */ | ||||
|     $scope.guacNotification = guacNotification; | ||||
|  | ||||
|     /** | ||||
|      * All currently-active connections, grouped into their corresponding | ||||
|      * tiled views. | ||||
|      * | ||||
|      * @type ManagedClientGroup[] | ||||
|      */ | ||||
|     $scope.getManagedClientGroups = guacClientManager.getManagedClientGroups; | ||||
|  | ||||
|     /** | ||||
|      * The message to display to the user as instructions for the login | ||||
|      * process. | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
| #other-connections .client-panel { | ||||
| 
 | ||||
|     display: none; | ||||
|     position: absolute; | ||||
|     position: fixed; | ||||
|     right: 0; | ||||
|     bottom: 0; | ||||
| 
 | ||||
| @@ -80,6 +80,11 @@ | ||||
|             <div id="content" ng-view> | ||||
|             </div> | ||||
|  | ||||
|             <!-- All active connections --> | ||||
|             <div id="other-connections"> | ||||
|                 <guac-client-panel client-groups="getManagedClientGroups()"></guac-client-panel> | ||||
|             </div> | ||||
|  | ||||
|         </div> | ||||
|  | ||||
|         <!-- Polyfills --> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user