diff --git a/guacamole/src/main/webapp/app/form/directives/form.js b/guacamole/src/main/webapp/app/form/directives/form.js new file mode 100644 index 000000000..fbe5af881 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/directives/form.js @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/** + * A directive that allows editing of a collection of fields. + */ +angular.module('form').directive('guacForm', [function form() { + + return { + // Element only + restrict: 'E', + replace: true, + scope: { + + /** + * The translation namespace of the translation strings that will + * be generated for all fields. This namespace is absolutely + * required. If this namespace is omitted, all generated + * translation strings will be placed within the MISSING_NAMESPACE + * namespace, as a warning. + * + * @type String + */ + namespace : '=', + + /** + * The fields to display. + * + * @type Field[] + */ + fields : '=', + + /** + * The object which will receive all field values. Each field value + * will be assigned to the property of this object having the same + * name. + * + * @type Object. + */ + model : '=' + + }, + templateUrl: 'app/form/templates/form.html', + controller: ['$scope', '$injector', function formController($scope, $injector) { + + // Required services + var translationStringService = $injector.get('translationStringService'); + + /** + * The object which will receive all field values. Normally, this + * will be the object provided within the "model" attribute. If + * no such object has been provided, a blank model will be used + * instead as a placeholder, such that the fields of this form + * will have something to bind to. + * + * @type Object. + */ + $scope.values = {}; + + /** + * Produces the translation string for the header of the given + * field. The translation string will be of the form: + * + * NAMESPACE.FIELD_HEADER_NAME + * + * where NAMESPACE is the namespace provided to the + * directive and NAME is the field name transformed + * via translationStringService.canonicalize(). + * + * @param {Field} field + * The field for which to produce the translation string. + * + * @returns {String} + * The translation string which produces the translated header + * of the field. + */ + $scope.getFieldHeader = function getFieldHeader(field) { + + return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') + + '.FIELD_HEADER_' + translationStringService.canonicalize(field.name); + + }; + + // Update string value and re-assign to model when field is changed + $scope.$watch('model', function setModel(model) { + + // Assign new model only if provided + if (model) + $scope.values = model; + + // Otherwise, use blank model + else + $scope.values = {}; + + }); + + }] // end controller + }; + +}]); diff --git a/guacamole/src/main/webapp/app/form/directives/formField.js b/guacamole/src/main/webapp/app/form/directives/formField.js new file mode 100644 index 000000000..bf9b35434 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/directives/formField.js @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/** + * A directive that allows editing of a field. + */ +angular.module('form').directive('guacFormField', [function formField() { + + return { + // Element only + restrict: 'E', + replace: true, + scope: { + + /** + * The translation namespace of the translation strings that will + * be generated for this field. This namespace is absolutely + * required. If this namespace is omitted, all generated + * translation strings will be placed within the MISSING_NAMESPACE + * namespace, as a warning. + * + * @type String + */ + namespace : '=', + + /** + * The field to display. + * + * @type Field + */ + field : '=', + + /** + * The property which contains this fields current value. When this + * field changes, the property will be updated accordingly. + * + * @type String + */ + model : '=' + + }, + templateUrl: 'app/form/templates/formField.html', + controller: ['$scope', '$injector', function formFieldController($scope, $injector) { + + // Required services + var translationStringService = $injector.get('translationStringService'); + + /** + * The type to use for password input fields. By default, password + * input fields have type 'password', and are thus masked. + * + * @type String + * @default 'password' + */ + $scope.passwordInputType = 'password'; + + /** + * Returns a string which describes the action the next call to + * togglePassword() will have. + * + * @return {String} + * A string which describes the action the next call to + * togglePassword() will have. + */ + $scope.getTogglePasswordHelpText = function getTogglePasswordHelpText() { + + // If password is hidden, togglePassword() will show the password + if ($scope.passwordInputType === 'password') + return 'FORM.HELP_SHOW_PASSWORD'; + + // If password is shown, togglePassword() will hide the password + return 'FORM.HELP_HIDE_PASSWORD'; + + }; + + /** + * Toggles visibility of the field contents, if this field is a + * password field. Initially, password contents are masked + * (invisible). + */ + $scope.togglePassword = function togglePassword() { + + // If password is hidden, show the password + if ($scope.passwordInputType === 'password') + $scope.passwordInputType = 'text'; + + // If password is shown, hide the password + else + $scope.passwordInputType = 'password'; + + }; + + /** + * Produces the translation string for the given field option + * value. The translation string will be of the form: + * + * NAMESPACE.FIELD_OPTION_NAME_VALUE + * + * where NAMESPACE is the namespace provided to the + * directive, NAME is the field name transformed + * via translationStringService.canonicalize(), and + * VALUE is the option value transformed via + * translationStringService.canonicalize() + * + * @param {String} value + * The name of the option value. + * + * @returns {String} + * The translation string which produces the translated name of the + * value specified. + */ + $scope.getFieldOption = function getFieldOption(value) { + + // Don't bother if the model is not yet defined + if (!$scope.field) + return ''; + + return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') + + '.FIELD_OPTION_' + translationStringService.canonicalize($scope.field.name) + + '_' + translationStringService.canonicalize(value || 'EMPTY'); + + }; + + /** + * Translates the given string field value into an appropriately- + * typed value as dictated by the attributes of the field, + * exposing that typed value within the scope as + * $scope.typedValue. + * + * @param {String} modelValue + * The current string value of the field. + */ + var setTypedValue = function setTypedValue(modelValue) { + + // Don't bother if the model is not yet defined + if (!$scope.field) + return; + + // Coerce numeric strings to numbers + if ($scope.field.type === 'NUMERIC') + $scope.typedValue = (modelValue ? Number($scope.field.value) : null); + + // Coerce boolean strings to boolean values + else if ($scope.field.type === 'BOOLEAN') + $scope.typedValue = (modelValue === $scope.field.value); + + // All other field types are represented internally as strings + else + $scope.typedValue = modelValue || ''; + + }; + + /** + * Translates the given typed field value into a string as dictated + * by the attributes of the field, assigning that string value to + * the model. + * + * @param {String|Number|Boolean} typedValue + * The current value of the field, as an appropriate JavaScript + * type. + */ + var setModelValue = function setModelValue(typedValue) { + + // Don't bother if the model is not yet defined + if (!$scope.field) + return; + + // Convert numeric values back into strings + if ($scope.field.type === 'NUMERIC') { + if (!typedValue) + $scope.model = ''; + else + $scope.model = typedValue.toString(); + } + + // Convert boolean values back into strings based on field description + else if ($scope.field.type === 'BOOLEAN') + $scope.model = (typedValue ? $scope.field.value : ''); + + // All other field types are already strings + else + $scope.model = typedValue || ''; + + }; + + // Update string value and re-assign to model when field is changed + $scope.$watch('field', function setField(field) { + setTypedValue($scope.model); + setModelValue($scope.typedValue); + }); + + // Update typed value when model is changed + $scope.$watch('model', function setModel(model) { + setTypedValue(model); + }); + + // Update string value in model when typed value is changed + $scope.$watch('typedValue', function typedValueChanged(typedValue) { + setModelValue(typedValue); + }); + + }] // end controller + }; + +}]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/form/formModule.js b/guacamole/src/main/webapp/app/form/formModule.js new file mode 100644 index 000000000..a3e1f4a33 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/formModule.js @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Module for displaying dynamic forms. + */ +angular.module('form', ['locale']); diff --git a/guacamole/src/main/webapp/app/form/styles/form-field.css b/guacamole/src/main/webapp/app/form/styles/form-field.css new file mode 100644 index 000000000..b40dcbf14 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/styles/form-field.css @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Keep toggle-password icon on same line */ +.form-field .password-field { + white-space: nowrap; +} + +/* Generic 1x1em icon/button */ +.form-field .password-field .icon.toggle-password { + + display: inline-block; + opacity: 0.5; + cursor: default; + + background-repeat: no-repeat; + background-size: 1em; + width: 1em; + height: 1em; + +} + +/* Icon for unmasking passwords */ +.form-field .password-field input[type=password] ~ .icon.toggle-password { + background-image: url('images/action-icons/guac-show-pass.png'); +} + +/* Icon for masking passwords */ +.form-field .password-field input[type=text] ~ .icon.toggle-password { + background-image: url('images/action-icons/guac-hide-pass.png'); +} diff --git a/guacamole/src/main/webapp/app/form/styles/form.css b/guacamole/src/main/webapp/app/form/styles/form.css new file mode 100644 index 000000000..1c37abbb3 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/styles/form.css @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +table.form th { + text-align: left; + font-weight: normal; + padding-right: 1em; +} diff --git a/guacamole/src/main/webapp/app/form/templates/form.html b/guacamole/src/main/webapp/app/form/templates/form.html new file mode 100644 index 000000000..5f93f52cf --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/form.html @@ -0,0 +1,32 @@ + + + + + + + + + +
{{getFieldHeader(field) | translate}} + +
diff --git a/guacamole/src/main/webapp/app/manage/templates/connectionParameter.html b/guacamole/src/main/webapp/app/form/templates/formField.html similarity index 57% rename from guacamole/src/main/webapp/app/manage/templates/connectionParameter.html rename to guacamole/src/main/webapp/app/form/templates/formField.html index 7cbd07c10..ebcf3573f 100644 --- a/guacamole/src/main/webapp/app/manage/templates/connectionParameter.html +++ b/guacamole/src/main/webapp/app/form/templates/formField.html @@ -1,4 +1,4 @@ -
+
- - - - + + + + - -
+ +
- - + + - - + +
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index fda9e28a3..08700c893 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -266,9 +266,41 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i $scope.parameters = {}; } + /** + * Returns the translation string namespace for the protocol having the + * given name. The namespace will be of the form: + * + * PROTOCOL_NAME + * + * where NAME is the protocol name transformed via + * translationStringService.canonicalize(). + * + * @param {String} protocolName + * The name of the protocol. + * + * @returns {String} + * The translation namespace for the protocol specified, or null if no + * namespace could be generated. + */ + $scope.getNamespace = function getNamespace(protocolName) { + + // Do not generate a namespace if no protocol is selected + if (!protocolName) + return null; + + return 'PROTOCOL_' + translationStringService.canonicalize(protocolName); + + }; + /** * Given the internal name of a protocol, produces the translation string - * for the localized version of that protocol's name. + * for the localized version of that protocol's name. The translation + * string will be of the form: + * + * NAMESPACE.NAME + * + * where NAMESPACE is the namespace generated from + * $scope.getNamespace(). * * @param {String} protocolName * The name of the protocol. @@ -278,27 +310,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i * protocol specified. */ $scope.getProtocolName = function getProtocolName(protocolName) { - return 'PROTOCOL_' + translationStringService.canonicalize(protocolName) + '.NAME'; - }; - - /** - * Given the internal name of a protocol and the internal name of a - * parameter for that protocol, produces the translation string - * for the localized, human-readable name of that protocol parameter. - * - * @param {String} protocolName - * The name of the protocol. - * - * @param {String} parameterName - * The name of the protocol parameter. - * - * @returns {String} - * The translation string which produces the translated name of the - * protocol parameter specified. - */ - $scope.getProtocolParameterName = function getProtocolParameterName(protocolName, parameterName) { - return 'PROTOCOL_' + translationStringService.canonicalize(protocolName) - + '.FIELD_HEADER_' + translationStringService.canonicalize(parameterName); + return $scope.getNamespace(protocolName) + '.NAME'; }; /** diff --git a/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js b/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js deleted file mode 100644 index 8ca090163..000000000 --- a/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2014 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -/** - * A directive that allows editing of a connection parameter. - */ -angular.module('manage').directive('guacConnectionParameter', [function connectionParameter() { - - return { - // Element only - restrict: 'E', - replace: true, - scope: { - - /** - * The protocol this parameter is associated with. - * - * @type Protocol - */ - protocol : '=', - - /** - * The unique name of this parameter within the protocol - * definition. - * - * @type String - */ - name : '=', - - /** - * The current map of parameter names to their corresponding string - * values. - * - * @type Object. - */ - parameters : '=' - - }, - templateUrl: 'app/manage/templates/connectionParameter.html', - controller: ['$scope', '$injector', function connectionParameterController($scope, $injector) { - - // Required services - var $q = $injector.get('$q'); - var translationStringService = $injector.get('translationStringService'); - - /** - * The type to use for password input fields. By default, password - * input fields have type 'password', and are thus masked. - * - * @type String - * @default 'password' - */ - $scope.passwordInputType = 'password'; - - /** - * Returns a string which describes the action the next call to - * togglePassword() will have. - * - * @return {String} - * A string which describes the action the next call to - * togglePassword() will have. - */ - $scope.getTogglePasswordHelpText = function getTogglePasswordHelpText() { - - // If password is hidden, togglePassword() will show the password - if ($scope.passwordInputType === 'password') - return 'MANAGE.HELP_SHOW_PASSWORD'; - - // If password is shown, togglePassword() will hide the password - return 'MANAGE.HELP_HIDE_PASSWORD'; - - }; - - /** - * Toggles visibility of the parameter contents, if this parameter - * is a password parameter. Initially, password contents are - * masked (invisible). - */ - $scope.togglePassword = function togglePassword() { - - // If password is hidden, show the password - if ($scope.passwordInputType === 'password') - $scope.passwordInputType = 'text'; - - // If password is shown, hide the password - else - $scope.passwordInputType = 'password'; - - }; - - /** - * Deferred load of the parameter definition, pending availability - * of the protocol definition as a whole. - * - * @type Deferred - */ - var parameterDefinitionAvailable = $q.defer(); - - /** - * Populates the parameter definition on the scope as - * $scope.parameter if both the parameter name and - * protocol definition are available. If either are unavailable, - * this function has no effect. - */ - var retrieveParameterDefinition = function retrieveParameterDefinition() { - - // Both name and protocol are needed to retrieve the parameter definition - if (!$scope.name || !$scope.protocol) - return; - - // Once protocol definition is available, locate parameter definition by name - $scope.protocol.parameters.forEach(function findParameter(parameter) { - if (parameter.name === $scope.name) { - $scope.parameter = parameter; - parameterDefinitionAvailable.resolve(parameter); - } - }); - - }; - - // Load parameter definition once protocol definition is available. - $scope.$watch('name', retrieveParameterDefinition); - $scope.$watch('protocol', retrieveParameterDefinition); - - // Update typed value when parameter set is changed - $scope.$watch('parameters', function setParameters(parameters) { - - // Don't bother if no parameters were provided - if (!parameters) - return; - - // Wait for parameter definition - parameterDefinitionAvailable.promise.then(function setTypedValue() { - - // Pull parameter value - var value = parameters[$scope.name]; - - // Coerce numeric strings to numbers - if ($scope.parameter.type === 'NUMERIC') - $scope.typedValue = (value ? Number(value) : null); - - // Coerce boolean strings to boolean values - else if ($scope.parameter.type === 'BOOLEAN') - $scope.typedValue = (value === $scope.parameter.value); - - // All other parameter types are represented internally as strings - else - $scope.typedValue = value || ''; - - }); - - }); - - // Update string value in parameter set when typed value is changed - $scope.$watch('typedValue', function typedValueChanged(typedValue) { - - // Don't bother if there's nothing to set - if (!$scope.parameters) - return; - - // Wait for parameter definition - parameterDefinitionAvailable.promise.then(function setValue() { - - // Convert numeric values back into strings - if ($scope.parameter.type === 'NUMERIC') { - if (!typedValue) - $scope.parameters[$scope.name] = ''; - else - $scope.parameters[$scope.name] = typedValue.toString(); - } - - // Convert boolean values back into strings based on protocol description - else if ($scope.parameter.type === 'BOOLEAN') - $scope.parameters[$scope.name] = (typedValue ? $scope.parameter.value : ''); - - // All other parameter types are already strings - else - $scope.parameters[$scope.name] = typedValue || ''; - - }); - - }); // end watch typedValue - - /** - * Given the internal name of a protocol, the internal name of a - * parameter for that protocol, and the internal name for a valid - * value of that parameter, produces the translation string for the - * localized, human-readable name of that parameter value. - * - * @param {String} protocolName - * The name of the protocol. - * - * @param {String} parameterName - * The name of the protocol parameter. - * - * @param {String} parameterValue - * The name of the parameter value. - * - * @returns {String} - * The translation string which produces the translated name of the - * parameter value specified. - */ - $scope.getProtocolParameterOption = function getProtocolParameterOption(protocolName, parameterName, parameterValue) { - return 'PROTOCOL_' + translationStringService.canonicalize(protocolName) - + '.FIELD_OPTION_' + translationStringService.canonicalize(parameterName) - + '_' + translationStringService.canonicalize(parameterValue || 'EMPTY'); - }; - - }] // end controller - }; - -}]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/manageModule.js b/guacamole/src/main/webapp/app/manage/manageModule.js index 7c2a2cfc3..5fde9859b 100644 --- a/guacamole/src/main/webapp/app/manage/manageModule.js +++ b/guacamole/src/main/webapp/app/manage/manageModule.js @@ -24,6 +24,7 @@ * The module for the administration functionality. */ angular.module('manage', [ + 'form', 'groupList', 'list', 'locale', diff --git a/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css b/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css index 11c78f105..e198c03de 100644 --- a/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css +++ b/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css @@ -21,37 +21,8 @@ */ /* Do not stretch connection parameters to fit available area */ -.connection-parameter input[type=text], -.connection-parameter input[type=password], -.connection-parameter input[type=number] { +.connection-parameters input[type=text], +.connection-parameters input[type=password], +.connection-parameters input[type=number] { width: auto; } - -/* Keep toggle-password icon on same line */ -.connection-parameter .password-field { - white-space: nowrap; -} - -/* Generic 1x1em icon/button */ -.connection-parameter .password-field .icon.toggle-password { - - display: inline-block; - opacity: 0.5; - cursor: default; - - background-repeat: no-repeat; - background-size: 1em; - width: 1em; - height: 1em; - -} - -/* Icon for unmasking passwords */ -.connection-parameter .password-field input[type=password] ~ .icon.toggle-password { - background-image: url('images/action-icons/guac-show-pass.png'); -} - -/* Icon for masking passwords */ -.connection-parameter .password-field input[type=text] ~ .icon.toggle-password { - background-image: url('images/action-icons/guac-hide-pass.png'); -} \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html index 6f0505959..0a8635b56 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html @@ -59,17 +59,10 @@ THE SOFTWARE.

{{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}

-
- - - - - - - -
{{getProtocolParameterName(connection.protocol, parameter.name) | translate}} - -
+
+
diff --git a/guacamole/src/main/webapp/app/rest/types/ProtocolParameter.js b/guacamole/src/main/webapp/app/rest/types/Field.js similarity index 86% rename from guacamole/src/main/webapp/app/rest/types/ProtocolParameter.js rename to guacamole/src/main/webapp/app/rest/types/Field.js index 34d87c81e..89f4832b5 100644 --- a/guacamole/src/main/webapp/app/rest/types/ProtocolParameter.js +++ b/guacamole/src/main/webapp/app/rest/types/Field.js @@ -21,20 +21,20 @@ */ /** - * Service which defines the ProtocolParameter class. + * Service which defines the Field class. */ -angular.module('rest').factory('ProtocolParameter', [function defineProtocolParameter() { +angular.module('rest').factory('Field', [function defineField() { /** * The object returned by REST API calls when representing the data - * associated with a configuration parameter of a remote desktop protocol. + * associated with a field or configuration parameter. * * @constructor - * @param {ProtocolParameter|Object} [template={}] + * @param {Field|Object} [template={}] * The object whose properties should be copied within the new - * ProtocolParameter. + * Field. */ - var ProtocolParameter = function ProtocolParameter(template) { + var Field = function Field(template) { // Use empty object by default template = template || {}; @@ -56,12 +56,12 @@ angular.module('rest').factory('ProtocolParameter', [function defineProtocolPara /** * The type string defining which values this parameter may contain, * as well as what properties are applicable. Valid types are listed - * within ProtocolParameter.Type. + * within Field.Type. * * @type String - * @default ProtocolParameter.Type.TEXT + * @default Field.Type.TEXT */ - this.type = template.type || ProtocolParameter.Type.TEXT; + this.type = template.type || Field.Type.TEXT; /** * The value to set the parameter to, in the case of a BOOLEAN @@ -75,7 +75,7 @@ angular.module('rest').factory('ProtocolParameter', [function defineProtocolPara * All possible legal values for this parameter. This property is only * applicable to ENUM type parameters. * - * @type ProtocolParameterOption[] + * @type FieldOption[] */ this.options = template.options; @@ -84,7 +84,7 @@ angular.module('rest').factory('ProtocolParameter', [function defineProtocolPara /** * All valid protocol parameter types. */ - ProtocolParameter.Type = { + Field.Type = { /** * The type string associated with parameters that may contain a single @@ -147,6 +147,6 @@ angular.module('rest').factory('ProtocolParameter', [function defineProtocolPara }; - return ProtocolParameter; + return Field; }]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/rest/types/ProtocolParameterOption.js b/guacamole/src/main/webapp/app/rest/types/FieldOption.js similarity index 79% rename from guacamole/src/main/webapp/app/rest/types/ProtocolParameterOption.js rename to guacamole/src/main/webapp/app/rest/types/FieldOption.js index 09401be48..7c4f9a933 100644 --- a/guacamole/src/main/webapp/app/rest/types/ProtocolParameterOption.js +++ b/guacamole/src/main/webapp/app/rest/types/FieldOption.js @@ -21,20 +21,20 @@ */ /** - * Service which defines the ProtocolParameterOption class. + * Service which defines the FieldOption class. */ -angular.module('rest').factory('ProtocolParameterOption', [function defineProtocolParameterOption() { +angular.module('rest').factory('FieldOption', [function defineFieldOption() { /** * The object returned by REST API calls when representing a single possible - * legal value of a configuration parameter of a remote desktop protocol. + * legal value of a field. * * @constructor - * @param {ProtocolParameterOption|Object} [template={}] + * @param {FieldOption|Object} [template={}] * The object whose properties should be copied within the new - * ProtocolParameterOption. + * FieldOption. */ - var ProtocolParameterOption = function ProtocolParameterOption(template) { + var FieldOption = function FieldOption(template) { // Use empty object by default template = template || {}; @@ -56,6 +56,6 @@ angular.module('rest').factory('ProtocolParameterOption', [function defineProtoc }; - return ProtocolParameterOption; + return FieldOption; }]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/rest/types/Protocol.js b/guacamole/src/main/webapp/app/rest/types/Protocol.js index 02b6ead27..121673a55 100644 --- a/guacamole/src/main/webapp/app/rest/types/Protocol.js +++ b/guacamole/src/main/webapp/app/rest/types/Protocol.js @@ -57,7 +57,7 @@ angular.module('rest').factory('Protocol', [function defineProtocol() { * An array of all known parameters for this protocol, their types, * and other information. * - * @type ProtocolParameter[] + * @type Field[] * @default [] */ this.parameters = template.parameters || []; diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index bee9e7896..c3d07bee5 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -117,6 +117,13 @@ }, + "FORM" : { + + "HELP_SHOW_PASSWORD" : "Click to show password", + "HELP_HIDE_PASSWORD" : "Click to hide password" + + }, + "HOME" : { "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", @@ -161,8 +168,6 @@ "FORMAT_HISTORY_START" : "@:APP.FORMAT_DATE_TIME_PRECISE", "HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.", - "HELP_SHOW_PASSWORD" : "Click to show password", - "HELP_HIDE_PASSWORD" : "Click to hide password", "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", "INFO_CONNECTION_DURATION_UNKNOWN" : "--",