mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +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
|
// Required types
|
||||||
var ConnectionGroup = $injector.get('ConnectionGroup');
|
var ConnectionGroup = $injector.get('ConnectionGroup');
|
||||||
var ManagedClient = $injector.get('ManagedClient');
|
var ManagedClient = $injector.get('ManagedClient');
|
||||||
|
var ManagedClientGroup = $injector.get('ManagedClientGroup');
|
||||||
var ManagedClientState = $injector.get('ManagedClientState');
|
var ManagedClientState = $injector.get('ManagedClientState');
|
||||||
var ManagedFilesystem = $injector.get('ManagedFilesystem');
|
var ManagedFilesystem = $injector.get('ManagedFilesystem');
|
||||||
var Protocol = $injector.get('Protocol');
|
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).
|
* @borrows ManagedClientGroup.getName
|
||||||
* Each key is the ID of the connection used by that client.
|
|
||||||
*
|
|
||||||
* @type Object.<String, ManagedClient>
|
|
||||||
*/
|
*/
|
||||||
$scope.otherClients = {};
|
$scope.getName = ManagedClientGroup.getName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the contents of $scope.clients and $scope.otherClients to
|
* Reloads the contents of $scope.clientGroup to reflect the client IDs
|
||||||
* reflect the client IDs currently listed in the URL.
|
* currently listed in the URL.
|
||||||
*/
|
*/
|
||||||
var updateAttachedClients = function updateAttachedClients() {
|
var updateAttachedClients = function updateAttachedClients() {
|
||||||
|
|
||||||
var ids = $routeParams.id.split(/[ +]/);
|
var previousClients = $scope.clientGroup ? $scope.clientGroup.clients.slice() : [];
|
||||||
|
detachCurrentGroup();
|
||||||
|
|
||||||
$scope.clients = [];
|
$scope.clientGroup = guacClientManager.getManagedClientGroup($routeParams.id);
|
||||||
$scope.otherClients = angular.extend({}, guacClientManager.getManagedClients());
|
$scope.clientGroup.attached = true;
|
||||||
|
|
||||||
// Separate active clients by whether they should be displayed within
|
// Ensure menu is closed if updated view is not a modification of the
|
||||||
// the current view
|
// current view (has no clients in common). The menu should remain open
|
||||||
ids.forEach(function groupClients(id) {
|
// only while the current view is being modified, not when navigating
|
||||||
$scope.clients.push(guacClientManager.getManagedClient(id));
|
// to an entirely different view.
|
||||||
delete $scope.otherClients[id];
|
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);
|
$scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client);
|
||||||
|
|
||||||
// Disable client keyboard if the menu is shown
|
// 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;
|
client.clientProperties.keyboardEnabled = !menuShown;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -606,15 +636,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
$scope.isConnectionUnstable = function isConnectionUnstable() {
|
$scope.isConnectionUnstable = function isConnectionUnstable() {
|
||||||
|
return _.findIndex($scope.clientGroup.clients, client => client.clientState.tunnelUnstable) !== -1;
|
||||||
var unstable = false;
|
|
||||||
|
|
||||||
angular.forEach($scope.clients, function checkStability(client) {
|
|
||||||
unstable |= client.clientState.tunnelUnstable;
|
|
||||||
});
|
|
||||||
|
|
||||||
return unstable;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -830,22 +852,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
|
|
||||||
// Clean up when view destroyed
|
// Clean up when view destroyed
|
||||||
$scope.$on('$destroy', function clientViewDestroyed() {
|
$scope.$on('$destroy', function clientViewDestroyed() {
|
||||||
|
detachCurrentGroup();
|
||||||
// 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
@@ -29,6 +29,7 @@ angular.module('client').directive('guacClientPanel', ['$injector', function gua
|
|||||||
var sessionStorageFactory = $injector.get('sessionStorageFactory');
|
var sessionStorageFactory = $injector.get('sessionStorageFactory');
|
||||||
|
|
||||||
// Required types
|
// Required types
|
||||||
|
var ManagedClientGroup = $injector.get('ManagedClientGroup');
|
||||||
var ManagedClientState = $injector.get('ManagedClientState');
|
var ManagedClientState = $injector.get('ManagedClientState');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,12 +50,12 @@ angular.module('client').directive('guacClientPanel', ['$injector', function gua
|
|||||||
scope: {
|
scope: {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ManagedClient instances associated with the active
|
* The ManagedClientGroup instances associated with the active
|
||||||
* connections to be displayed within this panel.
|
* connections to be displayed within this panel.
|
||||||
*
|
*
|
||||||
* @type ManagedClient[]|Object.<String, ManagedClient>
|
* @type ManagedClientGroup[]
|
||||||
*/
|
*/
|
||||||
clients : '='
|
clientGroups : '='
|
||||||
|
|
||||||
},
|
},
|
||||||
templateUrl: 'app/client/templates/guacClientPanel.html',
|
templateUrl: 'app/client/templates/guacClientPanel.html',
|
||||||
@@ -75,71 +76,68 @@ angular.module('client').directive('guacClientPanel', ['$injector', function gua
|
|||||||
$scope.panelHidden = panelHidden;
|
$scope.panelHidden = panelHidden;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this panel currently has any clients associated
|
* Returns whether this panel currently has any client groups
|
||||||
* with it.
|
* associated with it.
|
||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
* true if at least one client is associated with this panel,
|
* true if at least one client group is associated with this
|
||||||
* false otherwise.
|
* panel, false otherwise.
|
||||||
*/
|
*/
|
||||||
$scope.hasClients = function hasClients() {
|
$scope.hasClientGroups = function hasClientGroups() {
|
||||||
return !!_.find($scope.clients, $scope.isManaged);
|
return $scope.clientGroups && $scope.clientGroups.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the status of the given client has changed in a
|
* @borrows ManagedClientGroup.getIdentifier
|
||||||
* way that requires the user's attention. This may be due to an
|
*/
|
||||||
* error, or due to a server-initiated disconnect.
|
$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
|
* @param {ManagedClientGroup} clientGroup
|
||||||
* The client to test.
|
* The client group to test.
|
||||||
*
|
*
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
* true if the given client requires the user's attention,
|
* true if the given client requires the user's attention,
|
||||||
* false otherwise.
|
* 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
|
// Test whether the client has encountered an error
|
||||||
switch (client.clientState.connectionState) {
|
switch (client.clientState.connectionState) {
|
||||||
case ManagedClientState.ConnectionState.CONNECTION_ERROR:
|
case ManagedClientState.ConnectionState.CONNECTION_ERROR:
|
||||||
case ManagedClientState.ConnectionState.TUNNEL_ERROR:
|
case ManagedClientState.ConnectionState.TUNNEL_ERROR:
|
||||||
case ManagedClientState.ConnectionState.DISCONNECTED:
|
case ManagedClientState.ConnectionState.DISCONNECTED:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
}) !== -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given client is currently being managed by
|
* Initiates an orderly disconnect of all clients within the given
|
||||||
* the guacClientManager service.
|
* 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
|
* @param {ManagedClientGroup} clientGroup
|
||||||
* The client to test.
|
* The group of clients to disconnect.
|
||||||
*
|
|
||||||
* @returns {Boolean}
|
|
||||||
* true if the given client is being managed by the
|
|
||||||
* guacClientManager service, false otherwise.
|
|
||||||
*/
|
*/
|
||||||
$scope.isManaged = function isManaged(client) {
|
$scope.disconnect = function disconnect(clientGroup) {
|
||||||
return !!guacClientManager.getManagedClients()[client.id];
|
guacClientManager.removeManagedClientGroup(ManagedClientGroup.getIdentifier(clientGroup));
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -32,50 +32,17 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient
|
|||||||
directive.scope = {
|
directive.scope = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Guacamole clients that should be displayed in an evenly-tiled
|
* The group of Guacamole clients that should be displayed in an
|
||||||
* grid arrangement.
|
* evenly-tiled grid arrangement.
|
||||||
*
|
*
|
||||||
* @type ManagedClient[]
|
* @type ManagedClientGroup
|
||||||
*/
|
*/
|
||||||
clients : '='
|
clientGroup : '='
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
directive.controller = ['$scope', '$injector', '$element',
|
directive.controller = ['$scope', '$injector', '$element',
|
||||||
function guacTiledListController($scope, $injector, $element) {
|
function guacTiledClientsController($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());
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigns keyboard focus to the given client, allowing that client to
|
* 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.
|
* The client that should receive keyboard focus.
|
||||||
*/
|
*/
|
||||||
$scope.assignFocus = function assignFocus(client) {
|
$scope.assignFocus = function assignFocus(client) {
|
||||||
|
|
||||||
|
// Clear focus of all other clients
|
||||||
|
$scope.clientGroup.clients.forEach(client => {
|
||||||
|
client.clientProperties.focused = false;
|
||||||
|
});
|
||||||
|
|
||||||
client.clientProperties.focused = true;
|
client.clientProperties.focused = true;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,10 +72,10 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
$scope.hasMultipleClients = function hasMultipleClients() {
|
$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
|
* Returns the CSS width that should be applied to each tile to
|
||||||
* achieve an even arrangement.
|
* 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.
|
* The CSS width that should be applied to each tile.
|
||||||
*/
|
*/
|
||||||
$scope.getTileWidth = function getTileWidth() {
|
$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.
|
* The CSS height that should be applied to each tile.
|
||||||
*/
|
*/
|
||||||
$scope.getTileHeight = function getTileHeight() {
|
$scope.getTileHeight = function getTileHeight() {
|
||||||
return Math.floor(100 / getRows()) + '%';
|
return Math.floor(100 / $scope.clientGroup.rows) + '%';
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 || '...';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
@@ -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) {
|
function guacClientManager($injector) {
|
||||||
|
|
||||||
// Required types
|
// Required types
|
||||||
var ManagedClient = $injector.get('ManagedClient');
|
var ManagedClient = $injector.get('ManagedClient');
|
||||||
|
var ManagedClientGroup = $injector.get('ManagedClientGroup');
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
var $window = $injector.get('$window');
|
var $window = $injector.get('$window');
|
||||||
@@ -56,6 +57,53 @@ angular.module('client').factory('guacClientManager', ['$injector',
|
|||||||
return storedManagedClients();
|
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
|
* Removes the existing ManagedClient associated with the connection having
|
||||||
* the given ID, if any. If no such a ManagedClient already exists, this
|
* 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}
|
* @returns {Boolean}
|
||||||
* true if an existing client was removed, false otherwise.
|
* true if an existing client was removed, false otherwise.
|
||||||
*/
|
*/
|
||||||
service.removeManagedClient = function replaceManagedClient(id) {
|
service.removeManagedClient = function removeManagedClient(id) {
|
||||||
|
|
||||||
var managedClients = storedManagedClients();
|
var managedClients = storedManagedClients();
|
||||||
|
|
||||||
// Remove client if it exists
|
// Remove client if it exists
|
||||||
if (id in managedClients) {
|
if (id in managedClients) {
|
||||||
|
|
||||||
|
// Pull client out of any containing groups
|
||||||
|
ungroupManagedClient(id);
|
||||||
|
|
||||||
// Disconnect and remove
|
// Disconnect and remove
|
||||||
managedClients[id].client.disconnect();
|
managedClients[id].client.disconnect();
|
||||||
delete managedClients[id];
|
delete managedClients[id];
|
||||||
@@ -102,11 +153,31 @@ angular.module('client').factory('guacClientManager', ['$injector',
|
|||||||
*/
|
*/
|
||||||
service.replaceManagedClient = function replaceManagedClient(id) {
|
service.replaceManagedClient = function replaceManagedClient(id) {
|
||||||
|
|
||||||
// Disconnect any existing client
|
var managedClients = storedManagedClients();
|
||||||
service.removeManagedClient(id);
|
var managedClientGroups = storedManagedClientGroups();
|
||||||
|
|
||||||
// Set new client
|
// Remove client if it exists
|
||||||
return storedManagedClients()[id] = ManagedClient.getInstance(id);
|
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();
|
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
|
// Create new managed client if it doesn't already exist
|
||||||
if (!(id in managedClients))
|
if (!(id in managedClients))
|
||||||
managedClients[id] = ManagedClient.getInstance(id);
|
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() {
|
service.clear = function clear() {
|
||||||
|
|
||||||
@@ -146,8 +295,9 @@ angular.module('client').factory('guacClientManager', ['$injector',
|
|||||||
for (var id in managedClients)
|
for (var id in managedClients)
|
||||||
managedClients[id].client.disconnect();
|
managedClients[id].client.disconnect();
|
||||||
|
|
||||||
// Clear managed clients
|
// Clear managed clients and client groups
|
||||||
storedManagedClients({});
|
storedManagedClients({});
|
||||||
|
storedManagedClientGroups([]);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -9,12 +9,7 @@
|
|||||||
<div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch">
|
<div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch">
|
||||||
|
|
||||||
<!-- All connections in current display -->
|
<!-- All connections in current display -->
|
||||||
<guac-tiled-clients clients="clients"></guac-tiled-clients>
|
<guac-tiled-clients client-group="clientGroup"></guac-tiled-clients>
|
||||||
|
|
||||||
<!-- All other active connections -->
|
|
||||||
<div id="other-connections">
|
|
||||||
<guac-client-panel clients="otherClients"></guac-client-panel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -52,9 +47,9 @@
|
|||||||
|
|
||||||
<!-- Stationary header -->
|
<!-- Stationary header -->
|
||||||
<div class="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">
|
<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">
|
<div class="all-connections">
|
||||||
<guac-group-list-filter connection-groups="rootConnectionGroups"
|
<guac-group-list-filter connection-groups="rootConnectionGroups"
|
||||||
filtered-connection-groups="filteredRootConnectionGroups"
|
filtered-connection-groups="filteredRootConnectionGroups"
|
||||||
|
@@ -1,29 +1,27 @@
|
|||||||
<div class="client-panel"
|
<div class="client-panel"
|
||||||
ng-class="{ 'has-clients': hasClients(), 'hidden' : panelHidden() }">
|
ng-class="{ 'has-clients': hasClientGroups(), 'hidden' : panelHidden() }">
|
||||||
|
|
||||||
<!-- Toggle panel visibility -->
|
<!-- Toggle panel visibility -->
|
||||||
<div class="client-panel-handle" ng-click="togglePanel()"></div>
|
<div class="client-panel-handle" ng-click="togglePanel()"></div>
|
||||||
|
|
||||||
<!-- List of connection thumbnails -->
|
<!-- List of connection thumbnails -->
|
||||||
<ul class="client-panel-connection-list">
|
<ul class="client-panel-connection-list">
|
||||||
<li ng-repeat="client in clients | toArray | orderBy: [ '-value.lastUsed', 'value.title' ]"
|
<li ng-repeat="clientGroup in clientGroups"
|
||||||
ng-class="{ 'needs-attention' : hasStatusUpdate(client.value) }"
|
ng-if="!clientGroup.attached"
|
||||||
ng-show="isManaged(client.value)"
|
ng-class="{ 'needs-attention' : hasStatusUpdate(clientGroup) }"
|
||||||
class="client-panel-connection">
|
class="client-panel-connection">
|
||||||
|
|
||||||
<!-- Close 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 }}"
|
<img ng-attr-alt="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}"
|
||||||
ng-attr-title="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}"
|
ng-attr-title="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}"
|
||||||
src="images/x.png">
|
src="images/x.png">
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Thumbnail -->
|
<!-- Thumbnail -->
|
||||||
<a href="#/client/{{client.value.id}}">
|
<a href="#/client/{{ getIdentifier(clientGroup) }}">
|
||||||
<div class="thumbnail">
|
<guac-tiled-thumbnails client-group="clientGroup"></guac-tiled-thumbnails>
|
||||||
<guac-thumbnail client="client.value"></guac-thumbnail>
|
<div class="name">{{ getTitle(clientGroup) }}</div>
|
||||||
</div>
|
|
||||||
<div class="name">{{ client.value.title }}</div>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
<ul class="tiled-client-list" ng-class="{ 'multiple-clients' : hasMultipleClients() }">
|
<ul class="tiled-client-list" ng-class="{ 'multiple-clients' : hasMultipleClients() }">
|
||||||
|
|
||||||
<li class="client-tile"
|
<li class="client-tile"
|
||||||
ng-repeat="client in clients"
|
ng-repeat="client in clientGroup.clients"
|
||||||
ng-style="{ 'width' : getTileWidth(), 'height' : getTileHeight() }"
|
ng-style="{ 'width' : getTileWidth(), 'height' : getTileHeight() }"
|
||||||
ng-class="{ 'focused' : client.clientProperties.focused }"
|
ng-class="{ 'focused' : client.clientProperties.focused }"
|
||||||
ng-click="assignFocus(client)">
|
ng-click="assignFocus(client)">
|
||||||
|
|
||||||
<h3>{{ getClientTitle(client) }}</h3>
|
<h3>{{ client.title }}</h3>
|
||||||
<guac-client client="client"></guac-client>
|
<guac-client client="client"></guac-client>
|
||||||
|
|
||||||
<!-- Client-specific status/error dialog -->
|
<!-- 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
|
* Object which serves as a surrogate interface, encapsulating a Guacamole
|
||||||
* client while it is active, allowing it to be detached and reattached
|
* client while it is active, allowing it to be maintained in the
|
||||||
* from different client views.
|
* background. One or more ManagedClients are grouped within
|
||||||
|
* ManagedClientGroups before being attached to the client view.
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {ManagedClient|Object} [template={}]
|
* @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() {
|
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) {
|
controller: ['$scope', '$injector', function guacRecentConnectionsController($scope, $injector) {
|
||||||
|
|
||||||
// Required types
|
// Required types
|
||||||
var ActiveConnection = $injector.get('ActiveConnection');
|
|
||||||
var ClientIdentifier = $injector.get('ClientIdentifier');
|
var ClientIdentifier = $injector.get('ClientIdentifier');
|
||||||
var RecentConnection = $injector.get('RecentConnection');
|
var RecentConnection = $injector.get('RecentConnection');
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
var guacClientManager = $injector.get('guacClientManager');
|
|
||||||
var guacHistory = $injector.get('guacHistory');
|
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.
|
* 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.
|
* 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}
|
* @returns {Boolean}
|
||||||
* true if recent (or active) connections are present, false
|
* true if recent connections are present, false otherwise.
|
||||||
* otherwise.
|
|
||||||
*/
|
*/
|
||||||
$scope.hasRecentConnections = function hasRecentConnections() {
|
$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) {
|
$scope.$watch("rootGroups", function setRootGroups(rootGroups) {
|
||||||
|
|
||||||
// Clear connection arrays
|
// Clear connection arrays
|
||||||
$scope.activeConnections = [];
|
|
||||||
$scope.recentConnections = [];
|
$scope.recentConnections = [];
|
||||||
|
|
||||||
// Produce collection of visible objects
|
// 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
|
// Add any recent connections that are visible
|
||||||
guacHistory.recentConnections.forEach(function addRecentConnection(historyEntry) {
|
guacHistory.recentConnections.forEach(function addRecentConnection(historyEntry) {
|
||||||
|
|
||||||
// Add recent connections for history entries with associated visible objects
|
// 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];
|
var object = visibleObjects[historyEntry.id];
|
||||||
$scope.recentConnections.push(new RecentConnection(object.name, historyEntry));
|
$scope.recentConnections.push(new RecentConnection(object.name, historyEntry));
|
||||||
|
@@ -3,23 +3,6 @@
|
|||||||
<!-- Text displayed if no recent connections exist -->
|
<!-- Text displayed if no recent connections exist -->
|
||||||
<p class="placeholder" ng-hide="hasRecentConnections()">{{'HOME.INFO_NO_RECENT_CONNECTIONS' | translate}}</p>
|
<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 -->
|
<!-- All recent connections -->
|
||||||
<div ng-repeat="recentConnection in recentConnections" class="connection">
|
<div ng-repeat="recentConnection in recentConnections" class="connection">
|
||||||
<a href="#/client/{{recentConnection.entry.id}}">
|
<a href="#/client/{{recentConnection.entry.id}}">
|
||||||
|
@@ -24,11 +24,12 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
function indexController($scope, $injector) {
|
function indexController($scope, $injector) {
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
var $document = $injector.get('$document');
|
var $document = $injector.get('$document');
|
||||||
var $route = $injector.get('$route');
|
var $route = $injector.get('$route');
|
||||||
var $window = $injector.get('$window');
|
var $window = $injector.get('$window');
|
||||||
var clipboardService = $injector.get('clipboardService');
|
var clipboardService = $injector.get('clipboardService');
|
||||||
var guacNotification = $injector.get('guacNotification');
|
var guacNotification = $injector.get('guacNotification');
|
||||||
|
var guacClientManager = $injector.get('guacClientManager');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error that prevents the current page from rendering at all. If no
|
* 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;
|
$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
|
* The message to display to the user as instructions for the login
|
||||||
* process.
|
* process.
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
#other-connections .client-panel {
|
#other-connections .client-panel {
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
@@ -80,6 +80,11 @@
|
|||||||
<div id="content" ng-view>
|
<div id="content" ng-view>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- All active connections -->
|
||||||
|
<div id="other-connections">
|
||||||
|
<guac-client-panel client-groups="getManagedClientGroups()"></guac-client-panel>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Polyfills -->
|
<!-- Polyfills -->
|
||||||
|
Reference in New Issue
Block a user