mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 09:03:21 +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