From 57888762837e82d601b07a0f10deab8cdbd669c2 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 22 Apr 2015 21:13:46 -0700 Subject: [PATCH 1/3] GUAC-1053: Fix warning regarding resource path of PATCH method for active connection service. --- .../basic/rest/activeconnection/ActiveConnectionRESTService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java index 1a47582bc..107f97ef8 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java @@ -147,7 +147,6 @@ public class ActiveConnectionRESTService { * If an error occurs while deleting the active connections. */ @PATCH - @Path("/") @AuthProviderRESTExposure public void patchTunnels(@QueryParam("token") String authToken, List> patches) throws GuacamoleException { From c2b23027080e7b200358134e070b0c8bc0231cd9 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 22 Apr 2015 22:39:57 -0700 Subject: [PATCH 2/3] GUAC-1053 New endpoint for listing languages, JS service for retrieving languages, and settings section for choosing language. --- .../net/basic/rest/RESTServletModule.java | 14 +- .../rest/language/LanguageRESTService.java | 159 ++++++++++++++++++ .../net/basic/rest/language/package-info.java | 27 +++ .../webapp/app/rest/services/cacheService.js | 12 +- .../app/rest/services/languageService.js | 64 +++++++ .../directives/guacSettingsPreferences.js | 44 ++++- .../settings/services/preferenceService.js | 12 +- .../templates/settingsPreferences.html | 17 +- .../src/main/webapp/translations/en_US.json | 6 +- 9 files changed, 340 insertions(+), 15 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/package-info.java create mode 100644 guacamole/src/main/webapp/app/rest/services/languageService.js diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index 91cade593..e87ac7e54 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -32,6 +32,7 @@ import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService; import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService; import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRESTService; import org.glyptodon.guacamole.net.basic.rest.activeconnection.ActiveConnectionRESTService; +import org.glyptodon.guacamole.net.basic.rest.language.LanguageRESTService; import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService; /** @@ -45,13 +46,14 @@ public class RESTServletModule extends ServletModule { protected void configureServlets() { // Set up the API endpoints - bind(ClipboardRESTService.class); - bind(ConnectionRESTService.class); - bind(ConnectionGroupRESTService.class); - bind(ProtocolRESTService.class); - bind(UserRESTService.class); - bind(TokenRESTService.class); bind(ActiveConnectionRESTService.class); + bind(ClipboardRESTService.class); + bind(ConnectionGroupRESTService.class); + bind(ConnectionRESTService.class); + bind(LanguageRESTService.class); + bind(ProtocolRESTService.class); + bind(TokenRESTService.class); + bind(UserRESTService.class); // Set up the servlet and JSON mappings bind(GuiceContainer.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java new file mode 100644 index 000000000..9706ca2e5 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java @@ -0,0 +1,159 @@ +/* + * 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. + */ + +package org.glyptodon.guacamole.net.basic.rest.language; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.servlet.ServletContext; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A REST Service for handling the listing of languages. + * + * @author James Muehlner + */ +@Path("/languages") +@Produces(MediaType.APPLICATION_JSON) +public class LanguageRESTService { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(LanguageRESTService.class); + + /** + * The path to the translation folder within the webapp. + */ + private static final String TRANSLATION_PATHS = "/translations"; + + /** + * The JSON property for the human readable display name. + */ + private static final String LANGUAGE_DISPLAY_NAME_KEY = "NAME"; + + /** + * The Jackson parser for parsing the language JSON files. + */ + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * The regular expression to use for parsing the language key from the + * filename. + */ + private static final Pattern LANGUAGE_KEY_PATTERN = Pattern.compile(".*/([a-z]+_[A-Z]+)\\.json"); + + /** + * Returns a map of all available language keys to their corresponding + * human-readable names. + * + * @param authToken + * The authentication token that is used to authenticate the user + * performing the operation. + * + * @param servletContext + * The ServletContext associated with the request. + * + * @return + * A list of languages defined in the system, consisting of + * language display name and key. + * + * @throws GuacamoleException + * If an error occurs while retrieving the available languages. + */ + @GET + @AuthProviderRESTExposure + public Map getLanguages(@QueryParam("token") String authToken, + @Context ServletContext servletContext) throws GuacamoleException { + + // Get the paths of all the translation files + Set resourcePaths = servletContext.getResourcePaths(TRANSLATION_PATHS); + + // If no translation files found, return an empty map + if (resourcePaths == null) + return Collections.EMPTY_MAP; + + Map languageMap = new HashMap(); + + // Iterate through all the found language files and add them to the return map + for(String resourcePath : resourcePaths) { + + // Get input stream for language file + InputStream languageFileStream = servletContext.getResourceAsStream(resourcePath); + if (languageFileStream == null) { + logger.warn("Unable to read language resource \"{}\"", resourcePath); + continue; + } + + try { + + // Parse language key from filename + String languageKey; + Matcher languageKeyMatcher = LANGUAGE_KEY_PATTERN.matcher(resourcePath); + if (!languageKeyMatcher.matches() || (languageKey = languageKeyMatcher.group(1)) == null) { + logger.warn("Invalid language file name: \"{}\"", resourcePath); + continue; + } + + // Get name node of language + JsonNode tree = mapper.readTree(languageFileStream); + JsonNode nameNode = tree.get(LANGUAGE_DISPLAY_NAME_KEY); + + // Attempt to read language name from node + String languageName; + if (nameNode == null || (languageName = nameNode.getTextValue()) == null) { + logger.warn("Root-level \"" + LANGUAGE_DISPLAY_NAME_KEY + "\" string missing or invalid in language file \"{}\"", resourcePath); + languageName = languageKey; + } + + // Add language key/name pair to map + languageMap.put(languageKey, languageName); + + } + catch (IOException e) { + logger.warn("Unable to read language resource \"{}\": {}", resourcePath, e.getMessage()); + logger.debug("Error while reading language resource.", e); + } + } + + return languageMap; + } + +} \ No newline at end of file diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/package-info.java new file mode 100644 index 000000000..e0e7ad697 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/package-info.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * Classes related to the language retrieval aspect of the Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest.language; + diff --git a/guacamole/src/main/webapp/app/rest/services/cacheService.js b/guacamole/src/main/webapp/app/rest/services/cacheService.js index f21dd6388..12ee0f275 100644 --- a/guacamole/src/main/webapp/app/rest/services/cacheService.js +++ b/guacamole/src/main/webapp/app/rest/services/cacheService.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Glyptodon LLC + * 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 @@ -41,6 +41,13 @@ angular.module('rest').factory('cacheService', ['$injector', */ service.connections = $cacheFactory('API-CONNECTIONS'); + /** + * Cache used by languageService. + * + * @type $cacheFactory.Cache + */ + service.languages = $cacheFactory('API-LANGUAGES'); + /** * Cache used by protocolService. * @@ -59,8 +66,9 @@ angular.module('rest').factory('cacheService', ['$injector', * Clear all caches defined in this service. */ service.clearCaches = function clearCaches() { - service.protocols.removeAll(); service.connections.removeAll(); + service.languages.removeAll(); + service.protocols.removeAll(); service.users.removeAll(); }; diff --git a/guacamole/src/main/webapp/app/rest/services/languageService.js b/guacamole/src/main/webapp/app/rest/services/languageService.js new file mode 100644 index 000000000..65c9d60d8 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/services/languageService.js @@ -0,0 +1,64 @@ +/* + * 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. + */ + +/** + * Service for operating on language metadata via the REST API. + */ +angular.module('rest').factory('languageService', ['$injector', + function languageService($injector) { + + // Required services + var $http = $injector.get('$http'); + var authenticationService = $injector.get('authenticationService'); + var cacheService = $injector.get('cacheService'); + + var service = {}; + + /** + * Makes a request to the REST API to get the list of languages, returning + * a promise that provides a map of language names by language key if + * successful. + * + * @returns {Promise.>} + * A promise which will resolve with a map of language names by + * language key upon success. + */ + service.getLanguages = function getLanguages() { + + // Build HTTP parameters set + var httpParameters = { + token : authenticationService.getCurrentToken() + }; + + // Retrieve available protocols + return $http({ + cache : cacheService.languages, + method : 'GET', + url : 'api/languages', + params : httpParameters + }); + + }; + + return service; + +}]); diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js index 4a5e0e194..71a2ffe12 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js @@ -30,8 +30,7 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe restrict: 'E', replace: true, - scope: { - }, + scope: {}, templateUrl: 'app/settings/templates/settingsPreferences.html', controller: ['$scope', '$injector', function settingsPreferencesController($scope, $injector) { @@ -40,11 +39,13 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe var PermissionSet = $injector.get('PermissionSet'); // Required services + var $translate = $injector.get('$translate'); var authenticationService = $injector.get('authenticationService'); var guacNotification = $injector.get('guacNotification'); - var userService = $injector.get('userService'); + var languageService = $injector.get('languageService'); var permissionService = $injector.get('permissionService'); var preferenceService = $injector.get('preferenceService'); + var userService = $injector.get('userService'); /** * An action to be provided along with the object sent to @@ -71,6 +72,21 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe * @type Object. */ $scope.preferences = preferenceService.preferences; + + /** + * A map of all available language keys to their human-readable + * names. + * + * @type Object. + */ + $scope.languages = null; + + /** + * Switches the active display langugae to the chosen language. + */ + $scope.changeLanguage = function changeLanguage() { + $translate.use($scope.preferences.language); + }; /** * The new password for the user. @@ -151,6 +167,12 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe }; + // Retrieve defined languages + languageService.getLanguages() + .success(function languagesRetrieved(languages) { + $scope.languages = languages; + }); + // Retrieve current permissions permissionService.getPermissions(username) .success(function permissionsRetrieved(permissions) { @@ -161,7 +183,23 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe }); + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.canChangePassword !== null + && $scope.languages !== null; + + }; + }] }; + + }]); diff --git a/guacamole/src/main/webapp/app/settings/services/preferenceService.js b/guacamole/src/main/webapp/app/settings/services/preferenceService.js index ade3c7228..e1da5a44d 100644 --- a/guacamole/src/main/webapp/app/settings/services/preferenceService.js +++ b/guacamole/src/main/webapp/app/settings/services/preferenceService.js @@ -28,8 +28,9 @@ angular.module('settings').factory('preferenceService', ['$injector', function preferenceService($injector) { // Required services - var $window = $injector.get('$window'); var $rootScope = $injector.get('$rootScope'); + var $translate = $injector.get('$translate'); + var $window = $injector.get('$window'); var service = {}; @@ -95,7 +96,14 @@ angular.module('settings').factory('preferenceService', ['$injector', * * @type String */ - inputMethod : service.inputMethods.NONE + inputMethod : service.inputMethods.NONE, + + /** + * The selected language. + * + * @type String + */ + language : $translate.use() }; diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html b/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html index d2b0a63cf..7ac8fe7d8 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html @@ -1,4 +1,4 @@ -
+
+ +
+

{{'SETTINGS_PREFERENCES.HELP_LANGUAGE' | translate}}

+ + +
+ + + + + +
{{'SETTINGS_PREFERENCES.FIELD_HEADER_LANGUAGE' | translate}}
+
+
+

{{'SETTINGS_PREFERENCES.HELP_UPDATE_PASSWORD' | translate}}

diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index 3d72affc9..f0c7ab92a 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -1,5 +1,7 @@ { - + + "NAME" : "English (US)", + "APP" : { "ACTION_ACKNOWLEDGE" : "OK", @@ -411,6 +413,7 @@ "ERROR_PASSWORD_BLANK" : "Your password cannot be blank.", "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH", + "FIELD_HEADER_LANGUAGE" : "Display language:", "FIELD_HEADER_PASSWORD" : "Password:", "FIELD_HEADER_PASSWORD_OLD" : "Current Password:", "FIELD_HEADER_PASSWORD_NEW" : "New Password:", @@ -422,6 +425,7 @@ "HELP_INPUT_METHOD_NONE" : "@:CLIENT.HELP_INPUT_METHOD_NONE", "HELP_INPUT_METHOD_OSK" : "@:CLIENT.HELP_INPUT_METHOD_OSK", "HELP_INPUT_METHOD_TEXT" : "@:CLIENT.HELP_INPUT_METHOD_TEXT", + "HELP_LANGUAGE" : "Select a different language below to change the language of all text within Guacamole. Available choices will depend on which languages are installed.", "HELP_MOUSE_MODE_ABSOLUTE" : "@:CLIENT.HELP_MOUSE_MODE_ABSOLUTE", "HELP_MOUSE_MODE_RELATIVE" : "@:CLIENT.HELP_MOUSE_MODE_RELATIVE", "HELP_UPDATE_PASSWORD" : "If you wish to change your password, enter your current password and the desired new password below, and click \"Update Password\". The change will take effect immediately.", From 8e3e27b8cd32c40d1c362089eee059a9cc50b06f Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 22 Apr 2015 22:52:23 -0700 Subject: [PATCH 3/3] GUAC-1053 Fixed problems highlighted in pull request comments. --- .../net/basic/rest/language/LanguageRESTService.java | 10 +++++----- .../main/webapp/app/rest/services/languageService.js | 2 +- .../app/settings/directives/guacSettingsPreferences.js | 2 -- .../styles/{update-password.css => preferences.css} | 5 +++-- .../app/settings/templates/settingsPreferences.html | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) rename guacamole/src/main/webapp/app/settings/styles/{update-password.css => preferences.css} (94%) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java index 9706ca2e5..8c1131bb8 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/language/LanguageRESTService.java @@ -62,7 +62,7 @@ public class LanguageRESTService { /** * The path to the translation folder within the webapp. */ - private static final String TRANSLATION_PATHS = "/translations"; + private static final String TRANSLATION_PATH = "/translations"; /** * The JSON property for the human readable display name. @@ -92,8 +92,8 @@ public class LanguageRESTService { * The ServletContext associated with the request. * * @return - * A list of languages defined in the system, consisting of - * language display name and key. + * A map of languages defined in the system, of language key to + * display name. * * @throws GuacamoleException * If an error occurs while retrieving the available languages. @@ -104,7 +104,7 @@ public class LanguageRESTService { @Context ServletContext servletContext) throws GuacamoleException { // Get the paths of all the translation files - Set resourcePaths = servletContext.getResourcePaths(TRANSLATION_PATHS); + Set resourcePaths = servletContext.getResourcePaths(TRANSLATION_PATH); // If no translation files found, return an empty map if (resourcePaths == null) @@ -113,7 +113,7 @@ public class LanguageRESTService { Map languageMap = new HashMap(); // Iterate through all the found language files and add them to the return map - for(String resourcePath : resourcePaths) { + for (String resourcePath : resourcePaths) { // Get input stream for language file InputStream languageFileStream = servletContext.getResourceAsStream(resourcePath); diff --git a/guacamole/src/main/webapp/app/rest/services/languageService.js b/guacamole/src/main/webapp/app/rest/services/languageService.js index 65c9d60d8..407a6a900 100644 --- a/guacamole/src/main/webapp/app/rest/services/languageService.js +++ b/guacamole/src/main/webapp/app/rest/services/languageService.js @@ -49,7 +49,7 @@ angular.module('rest').factory('languageService', ['$injector', token : authenticationService.getCurrentToken() }; - // Retrieve available protocols + // Retrieve available languages return $http({ cache : cacheService.languages, method : 'GET', diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js index 71a2ffe12..482529a37 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js @@ -200,6 +200,4 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe }] }; - - }]); diff --git a/guacamole/src/main/webapp/app/settings/styles/update-password.css b/guacamole/src/main/webapp/app/settings/styles/preferences.css similarity index 94% rename from guacamole/src/main/webapp/app/settings/styles/update-password.css rename to guacamole/src/main/webapp/app/settings/styles/preferences.css index 3cf4d6332..eadf49020 100644 --- a/guacamole/src/main/webapp/app/settings/styles/update-password.css +++ b/guacamole/src/main/webapp/app/settings/styles/preferences.css @@ -20,7 +20,8 @@ * THE SOFTWARE. */ -.preferences .update-password .form { +.preferences .update-password .form, +.preferences .language .form { padding-left: 0.5em; border-left: 3px solid rgba(0, 0, 0, 0.125); -} +} \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html b/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html index 7ac8fe7d8..525fde30c 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html @@ -22,7 +22,7 @@ --> -
+

{{'SETTINGS_PREFERENCES.HELP_LANGUAGE' | translate}}