diff --git a/guacamole/src/main/frontend/src/app/client/controllers/clientController.js b/guacamole/src/main/frontend/src/app/client/controllers/clientController.js
index 8cacc77de..6a486fe9a 100644
--- a/guacamole/src/main/frontend/src/app/client/controllers/clientController.js
+++ b/guacamole/src/main/frontend/src/app/client/controllers/clientController.js
@@ -468,8 +468,21 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
else if (menuShownPreviousState)
$scope.applyParameterChanges($scope.focusedClient);
+ /* Broadcast changes to the menu display state */
+ $scope.$broadcast('guacMenuShown', menuShown);
+
});
+ // Toggle the menu when the guacClientToggleMenu event is received
+ $scope.$on('guacToggleMenu',
+ () => $scope.menu.shown = !$scope.menu.shown);
+
+ // Show the menu when the guacClientShowMenu event is received
+ $scope.$on('guacShowMenu', () => $scope.menu.shown = true);
+
+ // Hide the menu when the guacClientHideMenu event is received
+ $scope.$on('guacHideMenu', () => $scope.menu.shown = false);
+
// Automatically track and cache the currently-focused client
$scope.$on('guacClientFocused', function focusedClientChanged(event, newFocusedClient) {
@@ -489,10 +502,9 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
// Automatically update connection parameters that have been modified
// for the current focused client
- $scope.$on('guacClientArgumentsUpdated', function focusedClientChanged(event, focusedClient) {
+ $scope.$on('guacClientArgumentsUpdated', function argumentsChanged(event, focusedClient) {
- // Update available connection parameters, if the updated arguments are
- // for the current focused client - otherwise ignore them
+ // Ignore any updated arguments not for the current focused client
if ($scope.focusedClient && $scope.focusedClient === focusedClient)
$scope.menu.connectionParameters = ManagedClient.getArgumentModel(focusedClient);
diff --git a/guacamole/src/main/frontend/src/app/client/directives/guacTiledClients.js b/guacamole/src/main/frontend/src/app/client/directives/guacTiledClients.js
index e43c2603f..52289bcd0 100644
--- a/guacamole/src/main/frontend/src/app/client/directives/guacTiledClients.js
+++ b/guacamole/src/main/frontend/src/app/client/directives/guacTiledClients.js
@@ -62,6 +62,9 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient
directive.controller = ['$scope', '$injector', '$element',
function guacTiledClientsController($scope, $injector, $element) {
+ // Required services
+ const $rootScope = $injector.get('$rootScope');
+
// Required types
const ManagedClient = $injector.get('ManagedClient');
const ManagedClientGroup = $injector.get('ManagedClientGroup');
@@ -89,12 +92,17 @@ angular.module('client').directive('guacTiledClients', [function guacTiledClient
// Notify whenever identify of currently-focused client changes
$scope.$watch('getFocusedClient()', function focusedClientChanged(focusedClient) {
- $scope.$emit('guacClientFocused', focusedClient);
+ $rootScope.$broadcast('guacClientFocused', focusedClient);
});
// Notify whenever arguments of currently-focused client changes
$scope.$watch('getFocusedClient().arguments', function focusedClientParametersChanged() {
- $scope.$emit('guacClientArgumentsUpdated', $scope.getFocusedClient());
+ $rootScope.$broadcast('guacClientArgumentsUpdated', $scope.getFocusedClient());
+ }, true);
+
+ // Notify whenever protocol of currently-focused client changes
+ $scope.$watch('getFocusedClient().protocol', function focusedClientParametersChanged() {
+ $rootScope.$broadcast('guacClientProtocolUpdated', $scope.getFocusedClient());
}, true);
/**
diff --git a/guacamole/src/main/frontend/src/app/client/templates/client.html b/guacamole/src/main/frontend/src/app/client/templates/client.html
index 829d5ef7d..66dca3a77 100644
--- a/guacamole/src/main/frontend/src/app/client/templates/client.html
+++ b/guacamole/src/main/frontend/src/app/client/templates/client.html
@@ -124,6 +124,7 @@
diff --git a/guacamole/src/main/frontend/src/app/client/types/ManagedArgument.js b/guacamole/src/main/frontend/src/app/client/types/ManagedArgument.js
index 247d9f679..5e676bfcb 100644
--- a/guacamole/src/main/frontend/src/app/client/types/ManagedArgument.js
+++ b/guacamole/src/main/frontend/src/app/client/types/ManagedArgument.js
@@ -58,6 +58,16 @@ angular.module('client').factory('ManagedArgument', ['$q', function defineManage
*/
this.stream = template.stream;
+ /**
+ * True if this argument has been modified in the webapp, but yet to
+ * be confirmed by guacd, or false in any other case. A pending
+ * argument cannot be modified again, and must be recreated before
+ * editing is enabled again.
+ *
+ * @type {boolean}
+ */
+ this.pending = false;
+
};
/**
@@ -110,9 +120,9 @@ angular.module('client').factory('ManagedArgument', ['$q', function defineManage
* 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.
+ * calls to setValue() and will be read-only until 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
@@ -120,33 +130,24 @@ angular.module('client').factory('ManagedArgument', ['$q', function defineManage
*
* @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) {
+ // Stream new value only if value has changed and a change is not
+ // already pending
+ if (!managedArgument.pending && value !== managedArgument.value) {
var writer = new Guacamole.StringWriter(managedArgument.stream);
writer.sendText(value);
writer.sendEnd();
// ManagedArgument instance is no longer usable
- return true;
+ managedArgument.pending = true;
}
- // No parameter value change was attempted and the ManagedArgument
- // instance may be reused
- return false;
-
};
return ManagedArgument;
-}]);
\ No newline at end of file
+}]);
diff --git a/guacamole/src/main/frontend/src/app/client/types/ManagedClient.js b/guacamole/src/main/frontend/src/app/client/types/ManagedClient.js
index 5dc8bce44..01769ea94 100644
--- a/guacamole/src/main/frontend/src/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/frontend/src/app/client/types/ManagedClient.js
@@ -836,8 +836,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
*/
ManagedClient.setArgument = function setArgument(managedClient, name, value) {
var managedArgument = managedClient.arguments[name];
- if (managedArgument && ManagedArgument.setValue(managedArgument, value))
- delete managedClient.arguments[name];
+ managedArgument && ManagedArgument.setValue(managedArgument, value);
};
/**
@@ -1007,4 +1006,4 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
return ManagedClient;
-}]);
\ No newline at end of file
+}]);
diff --git a/guacamole/src/main/frontend/src/app/form/directives/form.js b/guacamole/src/main/frontend/src/app/form/directives/form.js
index 8d35774c7..4de051bfd 100644
--- a/guacamole/src/main/frontend/src/app/form/directives/form.js
+++ b/guacamole/src/main/frontend/src/app/form/directives/form.js
@@ -17,6 +17,7 @@
* under the License.
*/
+/* global _ */
/**
* A directive that allows editing of a collection of fields.
@@ -79,7 +80,19 @@ angular.module('form').directive('guacForm', [function form() {
*
* @type String
*/
- focused : '='
+ focused : '=',
+
+ /**
+ * The client associated with this form, if any.
+ *
+ * NOTE: If the provided client has any managed arguments in the
+ * pending state, any fields with the same name rendered by this
+ * form will be disabled. The fields will be re-enabled when guacd
+ * sends an updated argument with a the same name.
+ *
+ * @type ManagedClient
+ */
+ client: '='
},
templateUrl: 'app/form/templates/form.html',
@@ -240,6 +253,28 @@ angular.module('form').directive('guacForm', [function form() {
};
+
+ /**
+ * Returns whether the given field should be disabled (read-only)
+ * when presented to the current user.
+ *
+ * @param {Field} field
+ * The field to check.
+ *
+ * @returns {Boolean}
+ * true if the given field should be disabled, false otherwise.
+ */
+ $scope.isDisabled = function isDisabled(field) {
+
+ /*
+ * The field is disabled if either the form as a whole is disabled,
+ * or if a client is provided to the directive, and the field is
+ * marked as pending.
+ */
+ return $scope.disabled ||
+ _.get($scope.client, ['arguments', field.name, 'pending']);
+ };
+
/**
* Returns whether at least one of the given fields should be
* displayed to the current user.
diff --git a/guacamole/src/main/frontend/src/app/form/directives/formField.js b/guacamole/src/main/frontend/src/app/form/directives/formField.js
index 4fcead1d6..73ffb127e 100644
--- a/guacamole/src/main/frontend/src/app/form/directives/formField.js
+++ b/guacamole/src/main/frontend/src/app/form/directives/formField.js
@@ -68,7 +68,14 @@ angular.module('form').directive('guacFormField', [function formField() {
*
* @type Boolean
*/
- focused : '='
+ focused : '=',
+
+ /**
+ * The client associated with this form field, if any.
+ *
+ * @type ManagedClient
+ */
+ client: '='
},
templateUrl: 'app/form/templates/formField.html',
diff --git a/guacamole/src/main/frontend/src/app/form/templates/form.html b/guacamole/src/main/frontend/src/app/form/templates/form.html
index 397a210c5..642c57783 100644
--- a/guacamole/src/main/frontend/src/app/form/templates/form.html
+++ b/guacamole/src/main/frontend/src/app/form/templates/form.html
@@ -10,9 +10,10 @@