mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-630: Merge editing of "argv" based parameters.
This commit is contained in:
@@ -27,6 +27,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
var ManagedClient = $injector.get('ManagedClient');
|
var ManagedClient = $injector.get('ManagedClient');
|
||||||
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 ScrollState = $injector.get('ScrollState');
|
var ScrollState = $injector.get('ScrollState');
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
@@ -248,7 +249,15 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
*
|
*
|
||||||
* @type ScrollState
|
* @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;
|
$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.
|
* 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) {
|
$scope.$watch('menu.shown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
|
||||||
|
|
||||||
// Send clipboard data if menu is hidden
|
// Send clipboard and argument value data once menu is hidden
|
||||||
if (!menuShown && menuShownPreviousState)
|
if (!menuShown && menuShownPreviousState) {
|
||||||
$scope.$broadcast('guacClipboard', $scope.client.clipboardData);
|
$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
|
// Disable client keyboard if the menu is shown
|
||||||
$scope.client.clientProperties.keyboardEnabled = !menuShown;
|
$scope.client.clientProperties.keyboardEnabled = !menuShown;
|
||||||
|
|
||||||
@@ -805,6 +832,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
// Set client-specific menu actions
|
// Set client-specific menu actions
|
||||||
$scope.clientMenuActions = [ DISCONNECT_MENU_ACTION ];
|
$scope.clientMenuActions = [ DISCONNECT_MENU_ACTION ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @borrows Protocol.getNamespace
|
||||||
|
*/
|
||||||
|
$scope.getProtocolNamespace = Protocol.getNamespace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently-visible filesystem within the filesystem menu, if the
|
* The currently-visible filesystem within the filesystem menu, if the
|
||||||
* filesystem menu is open. If no filesystem is currently visible, this
|
* filesystem menu is open. If no filesystem is currently visible, this
|
||||||
|
@@ -96,6 +96,14 @@
|
|||||||
</div>
|
</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"
|
||||||
|
model="menu.connectionParameters"
|
||||||
|
model-only="true"></guac-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Input method -->
|
<!-- Input method -->
|
||||||
<div class="menu-section" id="keyboard-settings">
|
<div class="menu-section" id="keyboard-settings">
|
||||||
<h3>{{'CLIENT.SECTION_HEADER_INPUT_METHOD' | translate}}</h3>
|
<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 ClientProperties = $injector.get('ClientProperties');
|
||||||
var ClientIdentifier = $injector.get('ClientIdentifier');
|
var ClientIdentifier = $injector.get('ClientIdentifier');
|
||||||
var ClipboardData = $injector.get('ClipboardData');
|
var ClipboardData = $injector.get('ClipboardData');
|
||||||
|
var ManagedArgument = $injector.get('ManagedArgument');
|
||||||
var ManagedClientState = $injector.get('ManagedClientState');
|
var ManagedClientState = $injector.get('ManagedClientState');
|
||||||
var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail');
|
var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail');
|
||||||
var ManagedDisplay = $injector.get('ManagedDisplay');
|
var ManagedDisplay = $injector.get('ManagedDisplay');
|
||||||
@@ -44,6 +45,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
var connectionService = $injector.get('connectionService');
|
var connectionService = $injector.get('connectionService');
|
||||||
var preferenceService = $injector.get('preferenceService');
|
var preferenceService = $injector.get('preferenceService');
|
||||||
var requestService = $injector.get('requestService');
|
var requestService = $injector.get('requestService');
|
||||||
|
var schemaService = $injector.get('schemaService');
|
||||||
var tunnelService = $injector.get('tunnelService');
|
var tunnelService = $injector.get('tunnelService');
|
||||||
var guacAudio = $injector.get('guacAudio');
|
var guacAudio = $injector.get('guacAudio');
|
||||||
var guacHistory = $injector.get('guacHistory');
|
var guacHistory = $injector.get('guacHistory');
|
||||||
@@ -117,6 +119,23 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
*/
|
*/
|
||||||
this.title = template.title;
|
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
|
* The most recently-generated thumbnail for this connection, as
|
||||||
* stored within the local connection history. If no thumbnail is
|
* 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();
|
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
|
// Handle any received clipboard data
|
||||||
client.onclipboard = function clientClipboardReceived(stream, mimetype) {
|
client.onclipboard = function clientClipboardReceived(stream, mimetype) {
|
||||||
|
|
||||||
@@ -522,11 +579,16 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
client.connect(connectString);
|
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) {
|
if (clientIdentifier.type === ClientIdentifier.Types.CONNECTION) {
|
||||||
connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id)
|
$q.all({
|
||||||
.then(function connectionRetrieved(connection) {
|
connection : connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id),
|
||||||
managedClient.name = managedClient.title = connection.name;
|
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);
|
}, 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
|
* Produces a sharing link for the given ManagedClient using the given
|
||||||
* sharing profile. The resulting sharing link, and any required login
|
* sharing profile. The resulting sharing link, and any required login
|
||||||
|
Reference in New Issue
Block a user