mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-630: Allow parameters received via "argv" streams to be edited within the Guacamole menu.
This commit is contained in:
@@ -27,6 +27,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
var ManagedClient = $injector.get('ManagedClient');
|
||||
var ManagedClientState = $injector.get('ManagedClientState');
|
||||
var ManagedFilesystem = $injector.get('ManagedFilesystem');
|
||||
var Protocol = $injector.get('Protocol');
|
||||
var ScrollState = $injector.get('ScrollState');
|
||||
|
||||
// Required services
|
||||
@@ -248,7 +249,15 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
*
|
||||
* @type ScrollState
|
||||
*/
|
||||
scrollState : new ScrollState()
|
||||
scrollState : new ScrollState(),
|
||||
|
||||
/**
|
||||
* The current desired values of all editable connection parameters as
|
||||
* a set of name/value pairs, including any changes made by the user.
|
||||
*
|
||||
* @type {Object.<String, String>}
|
||||
*/
|
||||
connectionParameters : {}
|
||||
|
||||
};
|
||||
|
||||
@@ -257,6 +266,16 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
$scope.menu.shown = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies any changes to connection parameters made by the user within the
|
||||
* Guacamole menu.
|
||||
*/
|
||||
$scope.applyParameterChanges = function applyParameterChanges() {
|
||||
angular.forEach($scope.menu.connectionParameters, function sendArgv(value, name) {
|
||||
ManagedClient.setArgument($scope.client, name, value);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* The client which should be attached to the client UI.
|
||||
*
|
||||
@@ -429,12 +448,20 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
|
||||
});
|
||||
|
||||
// Update client state/behavior as visibility of the Guacamole menu changes
|
||||
$scope.$watch('menu.shown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
|
||||
|
||||
// Send clipboard data if menu is hidden
|
||||
if (!menuShown && menuShownPreviousState)
|
||||
// Send clipboard and argument value data once menu is hidden
|
||||
if (!menuShown && menuShownPreviousState) {
|
||||
$scope.$broadcast('guacClipboard', $scope.client.clipboardData);
|
||||
|
||||
$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
|
||||
$scope.client.clientProperties.keyboardEnabled = !menuShown;
|
||||
|
||||
@@ -805,6 +832,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
// Set client-specific menu actions
|
||||
$scope.clientMenuActions = [ DISCONNECT_MENU_ACTION ];
|
||||
|
||||
/**
|
||||
* @borrows Protocol.getNamespace
|
||||
*/
|
||||
$scope.getProtocolNamespace = Protocol.getNamespace;
|
||||
|
||||
/**
|
||||
* The currently-visible filesystem within the filesystem menu, if the
|
||||
* filesystem menu is open. If no filesystem is currently visible, this
|
||||
|
@@ -96,6 +96,14 @@
|
||||
</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"
|
||||
model="menu.connectionParameters"
|
||||
model-only="true"></guac-form>
|
||||
</div>
|
||||
|
||||
<!-- Input method -->
|
||||
<div class="menu-section" id="keyboard-settings">
|
||||
<h3>{{'CLIENT.SECTION_HEADER_INPUT_METHOD' | translate}}</h3>
|
||||
|
152
guacamole/src/main/webapp/app/client/types/ManagedArgument.js
Normal file
152
guacamole/src/main/webapp/app/client/types/ManagedArgument.js
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 ManagedArgument class used by ManagedClient.
|
||||
*/
|
||||
angular.module('client').factory('ManagedArgument', ['$q', function defineManagedArgument($q) {
|
||||
|
||||
/**
|
||||
* Object which represents an argument (connection parameter) which may be
|
||||
* changed by the user while the connection is open.
|
||||
*
|
||||
* @constructor
|
||||
* @param {ManagedArgument|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* ManagedArgument.
|
||||
*/
|
||||
var ManagedArgument = function ManagedArgument(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The name of the connection parameter.
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
this.name = template.name;
|
||||
|
||||
/**
|
||||
* The current value of the connection parameter.
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
this.value = template.value;
|
||||
|
||||
/**
|
||||
* A valid, open output stream which may be used to apply a new value
|
||||
* to the connection parameter.
|
||||
*
|
||||
* @type {Guacamole.OutputStream}
|
||||
*/
|
||||
this.stream = template.stream;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Requests editable access to a given connection parameter, returning a
|
||||
* promise which is resolved with a ManagedArgument instance that provides
|
||||
* such access if the parameter is indeed editable.
|
||||
*
|
||||
* @param {ManagedClient} managedClient
|
||||
* The ManagedClient instance associated with the connection for which
|
||||
* an editable version of the connection parameter is being retrieved.
|
||||
*
|
||||
* @param {String} name
|
||||
* The name of the connection parameter.
|
||||
*
|
||||
* @param {String} value
|
||||
* The current value of the connection parameter, as received from a
|
||||
* prior, inbound "argv" stream.
|
||||
*
|
||||
* @returns {Promise.<ManagedArgument>}
|
||||
* A promise which is resolved with the new ManagedArgument instance
|
||||
* once the requested parameter has been verified as editable.
|
||||
*/
|
||||
ManagedArgument.getInstance = function getInstance(managedClient, name, value) {
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
// Create internal, fully-populated instance of ManagedArgument, to be
|
||||
// returned only once mutability of the associated connection parameter
|
||||
// has been verified
|
||||
var managedArgument = new ManagedArgument({
|
||||
name : name,
|
||||
value : value,
|
||||
stream : managedClient.client.createArgumentValueStream('text/plain', name)
|
||||
});
|
||||
|
||||
// The connection parameter is editable only if a successful "ack" is
|
||||
// received
|
||||
managedArgument.stream.onack = function ackReceived(status) {
|
||||
if (status.isError())
|
||||
deferred.reject(status);
|
||||
else
|
||||
deferred.resolve(managedArgument);
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the given editable argument (connection parameter) to the given
|
||||
* value, updating the behavior of the associated connection in real-time.
|
||||
* If successful, the ManagedArgument provided cannot be used for future
|
||||
* calls to setValue() and must be replaced with a new instance. This
|
||||
* function only has an effect if the new parameter value is different from
|
||||
* the current value.
|
||||
*
|
||||
* @param {ManagedArgument} managedArgument
|
||||
* The ManagedArgument instance associated with the connection
|
||||
* parameter being modified.
|
||||
*
|
||||
* @param {String} value
|
||||
* The new value to assign to the connection parameter.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if the connection parameter was sent and the provided
|
||||
* ManagedArgument instance may no longer be used for future setValue()
|
||||
* calls, false if the connection parameter was NOT sent as it has not
|
||||
* changed.
|
||||
*/
|
||||
ManagedArgument.setValue = function setValue(managedArgument, value) {
|
||||
|
||||
// Stream new value only if value has changed
|
||||
if (value !== managedArgument.value) {
|
||||
|
||||
var writer = new Guacamole.StringWriter(managedArgument.stream);
|
||||
writer.sendText(value);
|
||||
writer.sendEnd();
|
||||
|
||||
// ManagedArgument instance is no longer usable
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// No parameter value change was attempted and the ManagedArgument
|
||||
// instance may be reused
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
return ManagedArgument;
|
||||
|
||||
}]);
|
@@ -27,6 +27,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
var ClientProperties = $injector.get('ClientProperties');
|
||||
var ClientIdentifier = $injector.get('ClientIdentifier');
|
||||
var ClipboardData = $injector.get('ClipboardData');
|
||||
var ManagedArgument = $injector.get('ManagedArgument');
|
||||
var ManagedClientState = $injector.get('ManagedClientState');
|
||||
var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail');
|
||||
var ManagedDisplay = $injector.get('ManagedDisplay');
|
||||
@@ -44,6 +45,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
var connectionService = $injector.get('connectionService');
|
||||
var preferenceService = $injector.get('preferenceService');
|
||||
var requestService = $injector.get('requestService');
|
||||
var schemaService = $injector.get('schemaService');
|
||||
var tunnelService = $injector.get('tunnelService');
|
||||
var guacAudio = $injector.get('guacAudio');
|
||||
var guacHistory = $injector.get('guacHistory');
|
||||
@@ -117,6 +119,23 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
*/
|
||||
this.title = template.title;
|
||||
|
||||
/**
|
||||
* The name which uniquely identifies the protocol of the connection in
|
||||
* use. If the protocol cannot be determined, such as when a connection
|
||||
* group is in use, this will be null.
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
this.protocol = template.protocol || null;
|
||||
|
||||
/**
|
||||
* An array of forms describing all known parameters for the connection
|
||||
* in use, including those which may not be editable.
|
||||
*
|
||||
* @type {Form[]}
|
||||
*/
|
||||
this.forms = template.forms || [];
|
||||
|
||||
/**
|
||||
* The most recently-generated thumbnail for this connection, as
|
||||
* stored within the local connection history. If no thumbnail is
|
||||
@@ -179,6 +198,17 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
*/
|
||||
this.clientProperties = template.clientProperties || new ClientProperties();
|
||||
|
||||
/**
|
||||
* All editable arguments (connection parameters), stored by their
|
||||
* names. Arguments will only be present within this set if their
|
||||
* current values have been exposed by the server via an inbound "argv"
|
||||
* stream and the server has confirmed that the value may be changed
|
||||
* through a successful "ack" to an outbound "argv" stream.
|
||||
*
|
||||
* @type {Object.<String, ManagedArgument>}
|
||||
*/
|
||||
this.arguments = template.arguments || {};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -448,6 +478,33 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
|
||||
};
|
||||
|
||||
// Test for argument mutability whenever an argument value is
|
||||
// received
|
||||
client.onargv = function clientArgumentValueReceived(stream, mimetype, name) {
|
||||
|
||||
// Ignore arguments which do not use a mimetype currently supported
|
||||
// by the web application
|
||||
if (mimetype !== 'text/plain')
|
||||
return;
|
||||
|
||||
var reader = new Guacamole.StringReader(stream);
|
||||
|
||||
// Assemble received data into a single string
|
||||
var value = '';
|
||||
reader.ontext = function textReceived(text) {
|
||||
value += text;
|
||||
};
|
||||
|
||||
// Test mutability once stream is finished, storing the current
|
||||
// value for the argument only if it is mutable
|
||||
reader.onend = function textComplete() {
|
||||
ManagedArgument.getInstance(managedClient, name, value).then(function argumentIsMutable(argument) {
|
||||
managedClient.arguments[name] = argument;
|
||||
}, function ignoreImmutableArguments() {});
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// Handle any received clipboard data
|
||||
client.onclipboard = function clientClipboardReceived(stream, mimetype) {
|
||||
|
||||
@@ -522,11 +579,16 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
client.connect(connectString);
|
||||
});
|
||||
|
||||
// If using a connection, pull connection name
|
||||
// If using a connection, pull connection name and protocol information
|
||||
if (clientIdentifier.type === ClientIdentifier.Types.CONNECTION) {
|
||||
connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id)
|
||||
.then(function connectionRetrieved(connection) {
|
||||
managedClient.name = managedClient.title = connection.name;
|
||||
$q.all({
|
||||
connection : connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id),
|
||||
protocols : schemaService.getProtocols(clientIdentifier.dataSource)
|
||||
})
|
||||
.then(function dataRetrieved(values) {
|
||||
managedClient.name = managedClient.title = values.connection.name;
|
||||
managedClient.protocol = values.connection.protocol;
|
||||
managedClient.forms = values.protocols[values.connection.protocol].connectionForms;
|
||||
}, requestService.WARN);
|
||||
}
|
||||
|
||||
@@ -619,6 +681,52 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns the given value to the connection parameter having the given
|
||||
* name, updating the behavior of the connection in real-time. If the
|
||||
* connection parameter is not editable, this function has no effect.
|
||||
*
|
||||
* @param {ManagedClient} managedClient
|
||||
* The ManagedClient instance associated with the active connection
|
||||
* being modified.
|
||||
*
|
||||
* @param {String} name
|
||||
* The name of the connection parameter to modify.
|
||||
*
|
||||
* @param {String} value
|
||||
* The value to attempt to assign to the given connection parameter.
|
||||
*/
|
||||
ManagedClient.setArgument = function setArgument(managedClient, name, value) {
|
||||
var managedArgument = managedClient.arguments[name];
|
||||
if (managedArgument && ManagedArgument.setValue(managedArgument, value))
|
||||
delete managedClient.arguments[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the current values of all editable connection parameters as a
|
||||
* set of name/value pairs suitable for use as the model of a form which
|
||||
* edits those parameters.
|
||||
*
|
||||
* @param {ManagedClient} client
|
||||
* The ManagedClient instance associated with the active connection
|
||||
* whose parameter values are being retrieved.
|
||||
*
|
||||
* @returns {Object.<String, String>}
|
||||
* A new set of name/value pairs containing the current values of all
|
||||
* editable parameters.
|
||||
*/
|
||||
ManagedClient.getArgumentModel = function getArgumentModel(client) {
|
||||
|
||||
var model = {};
|
||||
|
||||
angular.forEach(client.arguments, function addModelEntry(managedArgument) {
|
||||
model[managedArgument.name] = managedArgument.value;
|
||||
});
|
||||
|
||||
return model;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Produces a sharing link for the given ManagedClient using the given
|
||||
* sharing profile. The resulting sharing link, and any required login
|
||||
|
Reference in New Issue
Block a user