GUACAMOLE-724: Control only the currently-focused client with client-specific menu options.

This commit is contained in:
Michael Jumper
2021-06-16 21:59:10 -07:00
parent 3f4c6a4cd1
commit d0b1fb7d7f
5 changed files with 101 additions and 43 deletions

View File

@@ -156,10 +156,20 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
*/
$scope.applyParameterChanges = function applyParameterChanges() {
angular.forEach($scope.menu.connectionParameters, function sendArgv(value, name) {
ManagedClient.setArgument($scope.client, name, value);
if ($scope.focusedClient)
ManagedClient.setArgument($scope.focusedClient, name, value);
});
};
/**
* The currently-focused client within the current ManagedClientGroup. If
* there is no current group, no client is focused, or multiple clients are
* focused, this will be null.
*
* @type ManagedClient
*/
$scope.focusedClient = null;
/**
* The set of clients that should be attached to the client UI. This will
* be immediately initialized by a call to updateAttachedClients() below.
@@ -173,6 +183,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
*/
$scope.getName = ManagedClientGroup.getName;
/**
* @borrows ManagedClientGroup.getTitle
*/
$scope.getTitle = ManagedClientGroup.getTitle;
/**
* Reloads the contents of $scope.clientGroup to reflect the client IDs
* currently listed in the URL.
@@ -231,6 +246,27 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
// reloading the route
$scope.$on('$routeUpdate', updateAttachedClients);
/**
* Returns the currently-focused ManagedClient. If there is no such client,
* or multiple clients are focused, null is returned.
*
* @returns {ManagedClient}
* The currently-focused client, or null if there are no focused
* clients or if multiple clients are focused.
*/
$scope.getFocusedClient = function getFocusedClient() {
var managedClientGroup = $scope.clientGroup;
if (managedClientGroup) {
var focusedClients = _.filter(managedClientGroup.clients, client => client.clientProperties.focused);
if (focusedClients.length === 1)
return focusedClients[0];
}
return null;
};
/**
* The root connection groups of the connection hierarchy that should be
* presented to the user for selecting a different connection, as a map of
@@ -456,11 +492,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
$scope.applyParameterChanges();
}
// Obtain snapshot of current editable connection parameters when menu
// is opened
else if (menuShown)
$scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client);
// Disable client keyboard if the menu is shown
angular.forEach($scope.clientGroup.clients, function updateKeyboardEnabled(client) {
client.clientProperties.keyboardEnabled = !menuShown;
@@ -468,8 +499,25 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
});
// Automatically track and cache the currently-focused client
$scope.$watch('getFocusedClient()', function focusedClientChanged(client) {
// Apply any parameter changes when focus is changing (as
// applyParameterChanges() depends on the value of focusedClient, this
// must be called BEFORE updating focusedClient
$scope.applyParameterChanges();
$scope.focusedClient = client;
// Update available connection parameters, if there is a focused
// client
$scope.menu.connectionParameters = $scope.focusedClient ?
ManagedClient.getArgumentModel($scope.focusedClient) : {};
});
// Update page icon when thumbnail changes
$scope.$watch('client.thumbnail.canvas', function thumbnailChanged(canvas) {
$scope.$watch('focusedClient.thumbnail.canvas', function thumbnailChanged(canvas) {
iconService.setIcons(canvas);
});
@@ -487,7 +535,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
});
// Pull sharing profiles once the tunnel UUID is known
$scope.$watch('client.tunnel.uuid', function retrieveSharingProfiles(uuid) {
$scope.$watch('focusedClient.tunnel.uuid', function retrieveSharingProfiles(uuid) {
// Only pull sharing profiles if tunnel UUID is actually available
if (!uuid)
@@ -510,7 +558,8 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
* The sharing profile to use to generate the sharing link.
*/
$scope.share = function share(sharingProfile) {
ManagedClient.createShareLink($scope.client, sharingProfile);
if ($scope.focusedClient)
ManagedClient.createShareLink($scope.focusedClient, sharingProfile);
};
/**
@@ -521,7 +570,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
* link, false otherwise.
*/
$scope.isShared = function isShared() {
return ManagedClient.isShared($scope.client);
return !!$scope.focusedClient && ManagedClient.isShared($scope.focusedClient);
};
/**
@@ -534,9 +583,12 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
*/
$scope.getShareLinkCount = function getShareLinkCount() {
if (!$scope.focusedClient)
return 0;
// Count total number of links within the ManagedClient's share link map
var linkCount = 0;
for (var dummy in $scope.client.shareLinks)
for (var dummy in $scope.focusedClient.shareLinks)
linkCount++;
return linkCount;
@@ -625,7 +677,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
});
// Update page title when client title changes
$scope.$watch('client.title', function clientTitleChanged(title) {
$scope.$watch('getTitle(clientGroup)', function clientTitleChanged(title) {
$scope.page.title = title;
});
@@ -678,13 +730,17 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
};
/**
* Immediately disconnects the currently-connected client, if any.
* Immediately disconnects all currently-focused clients, if any.
*/
$scope.disconnect = function disconnect() {
// Disconnect if client is available
if ($scope.client)
$scope.client.client.disconnect();
if ($scope.clientGroup) {
$scope.clientGroup.clients.forEach(client => {
if (client.clientProperties.focused)
client.client.disconnect();
});
}
// Hide menu
$scope.menu.shown = false;
@@ -804,13 +860,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
*/
$scope.uploadFiles = function uploadFiles(files) {
// Ignore file uploads if no attached client
if (!$scope.client)
return;
// Upload each file
for (var i = 0; i < files.length; i++)
ManagedClient.uploadFile($scope.client, files[i], $scope.filesystemMenuContents);
ManagedClient.uploadFile($scope.filesystemMenuContents.client, files[i], $scope.filesystemMenuContents);
};

View File

@@ -28,14 +28,6 @@ angular.module('client').directive('guacFileBrowser', [function guacFileBrowser(
replace: true,
scope: {
/**
* The client whose file transfers should be managed by this
* directive.
*
* @type ManagedClient
*/
client : '=',
/**
* @type ManagedFilesystem
*/
@@ -116,7 +108,7 @@ angular.module('client').directive('guacFileBrowser', [function guacFileBrowser(
* The file to download.
*/
$scope.downloadFile = function downloadFile(file) {
ManagedFilesystem.downloadFile($scope.client, $scope.filesystem, file.streamName);
ManagedFilesystem.downloadFile($scope.filesystem, file.streamName);
};
/**

View File

@@ -87,7 +87,7 @@
translate="CLIENT.HELP_SHARE_LINK"
translate-values="{LINKS : getShareLinkCount()}"></p>
<table>
<tr ng-repeat="link in client.shareLinks | toArray | orderBy: value.name">
<tr ng-repeat="link in focusedClient.shareLinks | toArray | orderBy: value.name">
<th>{{link.value.name}}</th>
<td><a href="{{link.value.href}}" target="_blank">{{link.value.href}}</a></td>
</tr>
@@ -105,19 +105,19 @@
</div>
<!-- Devices -->
<div class="menu-section" id="devices" ng-show="client.filesystems.length">
<div class="menu-section" id="devices" ng-if="focusedClient.filesystems.length">
<h3>{{'CLIENT.SECTION_HEADER_DEVICES' | translate}}</h3>
<div class="content">
<div class="device filesystem" ng-repeat="filesystem in client.filesystems" ng-click="showFilesystemMenu(filesystem)">
<div class="device filesystem" ng-repeat="filesystem in focusedClient.filesystems" ng-click="showFilesystemMenu(filesystem)">
{{filesystem.name}}
</div>
</div>
</div>
<!-- Connection parameters which may be modified while the connection is open -->
<div class="menu-section connection-parameters" id="connection-settings" ng-show="client.protocol">
<guac-form namespace="getProtocolNamespace(client.protocol)"
content="client.forms"
<div class="menu-section connection-parameters" id="connection-settings" ng-if="focusedClient.protocol">
<guac-form namespace="getProtocolNamespace(focusedClient.protocol)"
content="focusedClient.forms"
model="menu.connectionParameters"
model-only="true"></guac-form>
</div>

View File

@@ -20,7 +20,7 @@
/**
* Provides the ManagedClientGroup class used by the guacClientManager service.
*/
angular.module('client').factory('ManagedClientGroup', [function defineManagedClientGroup() {
angular.module('client').factory('ManagedClientGroup', ['$injector', function defineManagedClientGroup($injector) {
/**
* Object which serves as a grouping of ManagedClients. Each
@@ -201,7 +201,15 @@ angular.module('client').factory('ManagedClientGroup', [function defineManagedCl
* ManagedClientGroup.
*/
ManagedClientGroup.getName = function getName(group) {
return _.filter(group.clients, (client => !!client.name)).map(client => client.name).join(', ') || '...';
// Generate a name from ONLY the focused clients, unless there are no
// focused clients
var relevantClients = _.filter(group.clients, client => client.clientProperties.focused);
if (!relevantClients.length)
relevantClients = group.clients;
return _.filter(relevantClients, (client => !!client.name)).map(client => client.name).join(', ') || '...';
};
return ManagedClientGroup;

View File

@@ -42,6 +42,14 @@ angular.module('client').factory('ManagedFilesystem', ['$rootScope', '$injector'
// Use empty object by default
template = template || {};
/**
* The client that originally received the "filesystem" instruction
* that resulted in the creation of this ManagedFilesystem.
*
* @type ManagedClient
*/
this.client = template.client;
/**
* The Guacamole filesystem object, as received via a "filesystem"
* instruction.
@@ -171,10 +179,11 @@ angular.module('client').factory('ManagedFilesystem', ['$rootScope', '$injector'
* @returns {ManagedFilesystem}
* The newly-created ManagedFilesystem.
*/
ManagedFilesystem.getInstance = function getInstance(object, name) {
ManagedFilesystem.getInstance = function getInstance(client, object, name) {
// Init new filesystem object
var managedFilesystem = new ManagedFilesystem({
client : client,
object : object,
name : name,
root : new ManagedFilesystem.File({
@@ -196,9 +205,6 @@ angular.module('client').factory('ManagedFilesystem', ['$rootScope', '$injector'
* client and filesystem. The browser will automatically start the
* download upon completion of this function.
*
* @param {ManagedClient} managedClient
* The ManagedClient from which the file is to be downloaded.
*
* @param {ManagedFilesystem} managedFilesystem
* The ManagedFilesystem from which the file is to be downloaded. Any
* path information provided must be relative to this filesystem.
@@ -206,7 +212,7 @@ angular.module('client').factory('ManagedFilesystem', ['$rootScope', '$injector'
* @param {String} path
* The full, absolute path of the file to download.
*/
ManagedFilesystem.downloadFile = function downloadFile(managedClient, managedFilesystem, path) {
ManagedFilesystem.downloadFile = function downloadFile(managedFilesystem, path) {
// Request download
managedFilesystem.object.requestInputStream(path, function downloadStreamReceived(stream, mimetype) {
@@ -215,7 +221,7 @@ angular.module('client').factory('ManagedFilesystem', ['$rootScope', '$injector'
var filename = path.match(/(.*[\\/])?(.*)/)[2];
// Start download
tunnelService.downloadStream(managedClient.tunnel.uuid, stream, mimetype, filename);
tunnelService.downloadStream(managedFilesystem.client.tunnel.uuid, stream, mimetype, filename);
});