From 726613cbc8ea51020f66accd8bfb38cccad7af7f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 7 Aug 2016 23:48:55 -0700 Subject: [PATCH 1/2] GUACAMOLE-5: Include sharing profiles within connection management display. --- .../directives/guacSettingsConnections.js | 93 ++++++++++++++++++- .../app/settings/styles/connection-list.css | 8 +- .../settings/templates/connectionGroup.html | 5 + .../settings/templates/newSharingProfile.html | 3 + .../templates/settingsConnections.html | 2 + .../settings/templates/sharingProfile.html | 9 ++ .../src/main/webapp/translations/en.json | 1 + 7 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 guacamole/src/main/webapp/app/settings/templates/newSharingProfile.html create mode 100644 guacamole/src/main/webapp/app/settings/templates/sharingProfile.html diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js index b174a8da0..6616853ee 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js @@ -169,6 +169,30 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe }; + /** + * Returns whether the current user can create new sharing profiles + * within the current data source. + * + * @return {Boolean} + * true if the current user can create new sharing profiles + * within the current data source, false otherwise. + */ + $scope.canCreateSharingProfiles = function canCreateSharingProfiles() { + + // Abort if permissions have not yet loaded + if (!$scope.permissions) + return false; + + // Can create sharing profiles if adminstrator or have explicit permission + if (PermissionSet.hasSystemPermission($scope.permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission($scope.permissions, PermissionSet.SystemPermissionType.CREATE_SHARING_PROFILE)) + return true; + + // Current data source does not allow sharing profile creation + return false; + + }; + /** * Returns whether the current user can create new connections or * connection groups or make changes to existing connections or @@ -188,7 +212,9 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe return false; // Creating connections/groups counts as management - if ($scope.canCreateConnections() || $scope.canCreateConnectionGroups()) + if ($scope.canCreateConnections() + || $scope.canCreateConnectionGroups() + || $scope.canCreateSharingProfiles()) return true; // Can manage connections if granted explicit update or delete @@ -206,6 +232,34 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe }; + /** + * Returns whether the current user can update the connection having + * the given identifier within the current data source. + * + * @param {String} identifier + * The identifier of the connection to check. + * + * @return {Boolean} + * true if the current user can update the connection having the + * given identifier within the current data source, false + * otherwise. + */ + $scope.canUpdateConnection = function canUpdateConnection(identifier) { + + // Abort if permissions have not yet loaded + if (!$scope.permissions) + return false; + + // Can update the connection if adminstrator or have explicit permission + if (PermissionSet.hasSystemPermission($scope.permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasConnectionPermission($scope.permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier)) + return true; + + // Current data sources does not allow the connection to be updated + return false; + + }; + /** * Returns whether the current user can update the connection group * having the given identifier within the current data source. @@ -276,6 +330,38 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe }; + /** + * Adds connection-specific contextual actions to the given array of + * GroupListItems. Each contextual action will be represented by a + * new GroupListItem. + * + * @param {GroupListItem[]} items + * The array of GroupListItems to which new GroupListItems + * representing connection-specific contextual actions should + * be added. + * + * @param {GroupListItem} [parent] + * The GroupListItem representing the connection which contains + * the given array of GroupListItems, if known. + */ + var addConnectionActions = function addConnectionActions(items, parent) { + + // Do nothing if we lack permission to modify the parent at all + if (parent && !$scope.canUpdateConnection(parent.identifier)) + return; + + // Add action for creating a child sharing profile, if the user + // has permission to do so + if ($scope.canCreateSharingProfiles()) + items.push(new GroupListItem({ + type : 'new-sharing-profile', + dataSource : $scope.dataSource, + weight : 1, + wrappedItem : parent + })); + + }; + /** * Decorates the given GroupListItem, including all descendants, * adding contextual actions. @@ -291,6 +377,11 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe if (item.type === GroupListItem.Type.CONNECTION_GROUP) addConnectionGroupActions(item.children, item); + // If the item is a connection, add actions specific to + // connections + else if (item.type === GroupListItem.Type.CONNECTION) + addConnectionActions(item.children, item); + // Decorate all children angular.forEach(item.children, decorateItem); diff --git a/guacamole/src/main/webapp/app/settings/styles/connection-list.css b/guacamole/src/main/webapp/app/settings/styles/connection-list.css index 7bf35538a..efcb9dc3b 100644 --- a/guacamole/src/main/webapp/app/settings/styles/connection-list.css +++ b/guacamole/src/main/webapp/app/settings/styles/connection-list.css @@ -18,7 +18,8 @@ */ .settings.connections .connection-list .new-connection, -.settings.connections .connection-list .new-connection-group { +.settings.connections .connection-list .new-connection-group, +.settings.connections .connection-list .new-sharing-profile { opacity: 0.5; font-style: italic; } @@ -28,7 +29,10 @@ .settings.connections .connection-list .new-connection a:visited, .settings.connections .connection-list .new-connection-group a, .settings.connections .connection-list .new-connection-group a:hover, -.settings.connections .connection-list .new-connection-group a:visited { +.settings.connections .connection-list .new-connection-group a:visited, +.settings.connections .connection-list .new-sharing-profile a, +.settings.connections .connection-list .new-sharing-profile a:hover, +.settings.connections .connection-list .new-sharing-profile a:visited { text-decoration:none; color: black; } diff --git a/guacamole/src/main/webapp/app/settings/templates/connectionGroup.html b/guacamole/src/main/webapp/app/settings/templates/connectionGroup.html index 8b480f1b0..ccf4592be 100644 --- a/guacamole/src/main/webapp/app/settings/templates/connectionGroup.html +++ b/guacamole/src/main/webapp/app/settings/templates/connectionGroup.html @@ -1,4 +1,9 @@ + +
+ + {{item.name}} +
diff --git a/guacamole/src/main/webapp/app/settings/templates/newSharingProfile.html b/guacamole/src/main/webapp/app/settings/templates/newSharingProfile.html new file mode 100644 index 000000000..12303e8e6 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/newSharingProfile.html @@ -0,0 +1,3 @@ + + {{'SETTINGS_CONNECTIONS.ACTION_NEW_SHARING_PROFILE' | translate}} + diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsConnections.html b/guacamole/src/main/webapp/app/settings/templates/settingsConnections.html index dfdf686ef..0b1e22ca5 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settingsConnections.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsConnections.html @@ -37,9 +37,11 @@ templates="{ 'connection' : 'app/settings/templates/connection.html', + 'sharing-profile' : 'app/settings/templates/sharingProfile.html', 'connection-group' : 'app/settings/templates/connectionGroup.html', 'new-connection' : 'app/settings/templates/newConnection.html', + 'new-sharing-profile' : 'app/settings/templates/newSharingProfile.html', 'new-connection-group' : 'app/settings/templates/newConnectionGroup.html' }"/> diff --git a/guacamole/src/main/webapp/app/settings/templates/sharingProfile.html b/guacamole/src/main/webapp/app/settings/templates/sharingProfile.html new file mode 100644 index 000000000..bce7edc1f --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/sharingProfile.html @@ -0,0 +1,9 @@ + + + +
+ + + {{item.name}} + +
diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index 01f332e18..f934a22de 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -557,6 +557,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_NEW_CONNECTION" : "New Connection", "ACTION_NEW_CONNECTION_GROUP" : "New Group", + "ACTION_NEW_SHARING_PROFILE" : "New Sharing Profile", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", From db02d9a7fbfb0fcfbc69c8a1d67edcf64da6d7bd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 7 Aug 2016 23:50:41 -0700 Subject: [PATCH 2/2] GUACAMOLE-5: Implement management of sharing profiles. --- .../app/index/config/indexRouteConfig.js | 9 + .../manageSharingProfileController.js | 404 ++++++++++++++++++ .../templates/manageSharingProfile.html | 44 ++ .../src/main/webapp/translations/de.json | 16 + .../src/main/webapp/translations/en.json | 21 + .../src/main/webapp/translations/fr.json | 16 + .../src/main/webapp/translations/it.json | 16 + .../src/main/webapp/translations/nl.json | 16 + .../src/main/webapp/translations/ru.json | 16 + 9 files changed, 558 insertions(+) create mode 100644 guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js create mode 100644 guacamole/src/main/webapp/app/manage/templates/manageSharingProfile.html diff --git a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js index 0eb9fbc54..904ab85b4 100644 --- a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js +++ b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js @@ -137,6 +137,15 @@ angular.module('index').config(['$routeProvider', '$locationProvider', resolve : { updateCurrentToken: updateCurrentToken } }) + // Sharing profile editor + .when('/manage/:dataSource/sharingProfiles/:id?', { + title : 'APP.NAME', + bodyClassName : 'manage', + templateUrl : 'app/manage/templates/manageSharingProfile.html', + controller : 'manageSharingProfileController', + resolve : { updateCurrentToken: updateCurrentToken } + }) + // Connection group editor .when('/manage/:dataSource/connectionGroups/:id?', { title : 'APP.NAME', diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js new file mode 100644 index 000000000..96657d173 --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js @@ -0,0 +1,404 @@ +/* + * 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. + */ + +/** + * The controller for editing or creating sharing profiles. + */ +angular.module('manage').controller('manageSharingProfileController', ['$scope', '$injector', + function manageSharingProfileController($scope, $injector) { + + // Required types + var SharingProfile = $injector.get('SharingProfile'); + var PermissionSet = $injector.get('PermissionSet'); + + // Required services + var $location = $injector.get('$location'); + var $routeParams = $injector.get('$routeParams'); + var authenticationService = $injector.get('authenticationService'); + var connectionService = $injector.get('connectionService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); + var schemaService = $injector.get('schemaService'); + var sharingProfileService = $injector.get('sharingProfileService'); + var translationStringService = $injector.get('translationStringService'); + + /** + * An action which can be provided along with the object sent to showStatus + * to allow the user to acknowledge (and close) the currently-shown status + * dialog. + */ + var ACKNOWLEDGE_ACTION = { + name : "MANAGE_SHARING_PROFILE.ACTION_ACKNOWLEDGE", + callback : function acknowledgeCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * An action to be provided along with the object sent to showStatus which + * closes the currently-shown status dialog, effectively canceling the + * operation which was pending user confirmation. + */ + var CANCEL_ACTION = { + name : "MANAGE_SHARING_PROFILE.ACTION_CANCEL", + callback : function cancelCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * The unique identifier of the data source containing the sharing profile + * being edited. + * + * @type String + */ + $scope.selectedDataSource = $routeParams.dataSource; + + /** + * The identifier of the original sharing profile from which this sharing + * profile is being cloned. Only valid if this is a new sharing profile. + * + * @type String + */ + var cloneSourceIdentifier = $location.search().clone; + + /** + * The identifier of the sharing profile being edited. If a new sharing + * profile is being created, this will not be defined. + * + * @type String + */ + var identifier = $routeParams.id; + + /** + * Map of protocol name to corresponding Protocol object. + * + * @type Object. + */ + $scope.protocols = null; + + /** + * The sharing profile being modified. + * + * @type SharingProfile + */ + $scope.sharingProfile = null; + + /** + * The parameter name/value pairs associated with the sharing profile being + * modified. + * + * @type Object. + */ + $scope.parameters = null; + + /** + * Whether the user can save the sharing profile being edited. This could be + * updating an existing sharing profile, or creating a new sharing profile. + * + * @type Boolean + */ + $scope.canSaveSharingProfile = null; + + /** + * Whether the user can delete the sharing profile being edited. + * + * @type Boolean + */ + $scope.canDeleteSharingProfile = null; + + /** + * Whether the user can clone the sharing profile being edited. + * + * @type Boolean + */ + $scope.canCloneSharingProfile = null; + + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + + /** + * All available sharing profile attributes. This is only the set of + * attribute definitions, organized as logical groupings of attributes, not + * attribute values. + * + * @type Form[] + */ + $scope.attributes = null; + + /** + * 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.protocols !== null + && $scope.sharingProfile !== null + && $scope.primaryConnection !== null + && $scope.parameters !== null + && $scope.permissions !== null + && $scope.attributes !== null + && $scope.canSaveSharingProfile !== null + && $scope.canDeleteSharingProfile !== null + && $scope.canCloneSharingProfile !== null; + + }; + + // Pull sharing profile attribute schema + schemaService.getSharingProfileAttributes($scope.selectedDataSource) + .success(function attributesReceived(attributes) { + $scope.attributes = attributes; + }); + + // Query the user's permissions for the current sharing profile + permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername()) + .success(function permissionsReceived(permissions) { + + $scope.permissions = permissions; + + // The sharing profile can be saved if it is new or if the user has + // UPDATE permission for that sharing profile (either explicitly or by + // virtue of being an administrator) + $scope.canSaveSharingProfile = + !identifier + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSharingProfilePermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier); + + // The sharing profile can be saved only if it exists and the user has + // DELETE permission (either explicitly or by virtue of being an + // administrator) + $scope.canDeleteSharingProfile = + !!identifier && ( + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSharingProfilePermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier) + ); + + // The sharing profile can be cloned only if it exists, the user has + // UPDATE permission on the sharing profile being cloned (is able to + // read parameters), and the user can create new sharing profiles + $scope.canCloneSharingProfile = + !!identifier && ( + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || ( + PermissionSet.hasSharingProfilePermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier) + && PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_SHARING_PROFILE) + ) + ); + + }); + + // Get protocol metadata + schemaService.getProtocols($scope.selectedDataSource) + .success(function protocolsReceived(protocols) { + $scope.protocols = protocols; + }); + + // If we are editing an existing sharing profile, pull its data + if (identifier) { + + // Pull data from existing sharing profile + sharingProfileService.getSharingProfile($scope.selectedDataSource, identifier) + .success(function sharingProfileRetrieved(sharingProfile) { + $scope.sharingProfile = sharingProfile; + }); + + // Pull sharing profile parameters + sharingProfileService.getSharingProfileParameters($scope.selectedDataSource, identifier) + .success(function parametersReceived(parameters) { + $scope.parameters = parameters; + }); + + } + + // If we are cloning an existing sharing profile, pull its data instead + else if (cloneSourceIdentifier) { + + // Pull data from cloned sharing profile + sharingProfileService.getSharingProfile($scope.selectedDataSource, cloneSourceIdentifier) + .success(function sharingProfileRetrieved(sharingProfile) { + + // Store data of sharing profile being cloned + $scope.sharingProfile = sharingProfile; + + // Clear the identifier field because this sharing profile is new + delete $scope.sharingProfile.identifier; + + }); + + // Pull sharing profile parameters from cloned sharing profile + sharingProfileService.getSharingProfileParameters($scope.selectedDataSource, cloneSourceIdentifier) + .success(function parametersReceived(parameters) { + $scope.parameters = parameters; + }); + + } + + // If we are creating a new sharing profile, populate skeleton sharing + // profile data + else { + + $scope.sharingProfile = new SharingProfile({ + primaryConnectionIdentifier : $location.search().parent + }); + + $scope.parameters = {}; + + } + + // Populate primary connection once its identifier is known + $scope.$watch('sharingProfile.primaryConnectionIdentifier', + function retrievePrimaryConnection(identifier) { + + // Pull data from existing sharing profile + connectionService.getConnection($scope.selectedDataSource, identifier) + .success(function connectionRetrieved(connection) { + $scope.primaryConnection = connection; + }); + + }); + + /** + * 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); + + }; + + /** + * Cancels all pending edits, returning to the management page. + */ + $scope.cancel = function cancel() { + $location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); + }; + + /** + * Cancels all pending edits, opening an edit page for a new sharing profile + * which is prepopulated with the data from the sharing profile currently + * being edited. + */ + $scope.cloneSharingProfile = function cloneSharingProfile() { + $location.path('/manage/' + encodeURIComponent($scope.selectedDataSource) + '/sharingProfiles').search('clone', identifier); + }; + + /** + * Saves the sharing profile, creating a new sharing profile or updating + * the existing sharing profile. + */ + $scope.saveSharingProfile = function saveSharingProfile() { + + $scope.sharingProfile.parameters = $scope.parameters; + + // Save the sharing profile + sharingProfileService.saveSharingProfile($scope.selectedDataSource, $scope.sharingProfile) + .success(function savedSharingProfile() { + $location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); + }) + + // Notify of any errors + .error(function sharingProfileSaveFailed(error) { + guacNotification.showStatus({ + 'className' : 'error', + 'title' : 'MANAGE_SHARING_PROFILE.DIALOG_HEADER_ERROR', + 'text' : error.message, + 'actions' : [ ACKNOWLEDGE_ACTION ] + }); + }); + + }; + + /** + * An action to be provided along with the object sent to showStatus which + * immediately deletes the current sharing profile. + */ + var DELETE_ACTION = { + name : "MANAGE_SHARING_PROFILE.ACTION_DELETE", + className : "danger", + // Handle action + callback : function deleteCallback() { + deleteSharingProfileImmediately(); + guacNotification.showStatus(false); + } + }; + + /** + * Immediately deletes the current sharing profile, without prompting the + * user for confirmation. + */ + var deleteSharingProfileImmediately = function deleteSharingProfileImmediately() { + + // Delete the sharing profile + sharingProfileService.deleteSharingProfile($scope.selectedDataSource, $scope.sharingProfile) + .success(function deletedSharingProfile() { + $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); + }) + + // Notify of any errors + .error(function sharingProfileDeletionFailed(error) { + guacNotification.showStatus({ + 'className' : 'error', + 'title' : 'MANAGE_SHARING_PROFILE.DIALOG_HEADER_ERROR', + 'text' : error.message, + 'actions' : [ ACKNOWLEDGE_ACTION ] + }); + }); + + }; + + /** + * Deletes the sharing profile, prompting the user first to confirm that + * deletion is desired. + */ + $scope.deleteSharingProfile = function deleteSharingProfile() { + + // Confirm deletion request + guacNotification.showStatus({ + 'title' : 'MANAGE_SHARING_PROFILE.DIALOG_HEADER_CONFIRM_DELETE', + 'text' : 'MANAGE_SHARING_PROFILE.TEXT_CONFIRM_DELETE', + 'actions' : [ DELETE_ACTION, CANCEL_ACTION] + }); + + }; + +}]); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageSharingProfile.html b/guacamole/src/main/webapp/app/manage/templates/manageSharingProfile.html new file mode 100644 index 000000000..121e4fe49 --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/templates/manageSharingProfile.html @@ -0,0 +1,44 @@ +
+ + +
+

{{'MANAGE_SHARING_PROFILE.SECTION_HEADER_EDIT_SHARING_PROFILE' | translate}}

+ +
+
+ + + + + + + + + +
{{'MANAGE_SHARING_PROFILE.FIELD_HEADER_NAME' | translate}}
{{'MANAGE_SHARING_PROFILE.FIELD_HEADER_PRIMARY_CONNECTION' | translate}}{{primaryConnection.name}}
+
+ + +
+ +
+ + +

{{'MANAGE_SHARING_PROFILE.SECTION_HEADER_PARAMETERS' | translate}}

+
+ +
+ + +
+ + + + +
+ +
diff --git a/guacamole/src/main/webapp/translations/de.json b/guacamole/src/main/webapp/translations/de.json index 67c8a1519..9bdf78730 100644 --- a/guacamole/src/main/webapp/translations/de.json +++ b/guacamole/src/main/webapp/translations/de.json @@ -231,6 +231,22 @@ }, + "MANAGE_SHARING_PROFILE" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "FIELD_HEADER_NAME" : "Name:", + + "SECTION_HEADER_PARAMETERS" : "Parameter" + + }, + "MANAGE_USER" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index f934a22de..af3d8c576 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -238,6 +238,27 @@ }, + "MANAGE_SHARING_PROFILE" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Sharing Profile", + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "FIELD_HEADER_NAME" : "Name:", + "FIELD_HEADER_PRIMARY_CONNECTION" : "Primary Connection:", + + "SECTION_HEADER_EDIT_SHARING_PROFILE" : "Edit Sharing Profile", + "SECTION_HEADER_PARAMETERS" : "Parameters", + + "TEXT_CONFIRM_DELETE" : "Sharing profiles cannot be restored after they have been deleted. Are you sure you want to delete this sharing profile?" + + }, + "MANAGE_USER" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", diff --git a/guacamole/src/main/webapp/translations/fr.json b/guacamole/src/main/webapp/translations/fr.json index b2199979e..ca4a5bdc5 100644 --- a/guacamole/src/main/webapp/translations/fr.json +++ b/guacamole/src/main/webapp/translations/fr.json @@ -231,6 +231,22 @@ }, + "MANAGE_SHARING_PROFILE" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "FIELD_HEADER_NAME" : "Nom:", + + "SECTION_HEADER_PARAMETERS" : "Paramètres" + + }, + "MANAGE_USER" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", diff --git a/guacamole/src/main/webapp/translations/it.json b/guacamole/src/main/webapp/translations/it.json index 45e800909..e84e67f48 100644 --- a/guacamole/src/main/webapp/translations/it.json +++ b/guacamole/src/main/webapp/translations/it.json @@ -219,6 +219,22 @@ }, + "MANAGE_SHARING_PROFILE" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "FIELD_HEADER_NAME" : "Name:", + + "SECTION_HEADER_PARAMETERS" : "Parametri" + + }, + "MANAGE_USER" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", diff --git a/guacamole/src/main/webapp/translations/nl.json b/guacamole/src/main/webapp/translations/nl.json index 413ebbd18..da1df107a 100644 --- a/guacamole/src/main/webapp/translations/nl.json +++ b/guacamole/src/main/webapp/translations/nl.json @@ -231,6 +231,22 @@ }, + "MANAGE_SHARING_PROFILE" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "FIELD_HEADER_NAME" : "Naam:", + + "SECTION_HEADER_PARAMETERS" : "Parameters" + + }, + "MANAGE_USER" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", diff --git a/guacamole/src/main/webapp/translations/ru.json b/guacamole/src/main/webapp/translations/ru.json index 8f2ef69b0..5e305a580 100644 --- a/guacamole/src/main/webapp/translations/ru.json +++ b/guacamole/src/main/webapp/translations/ru.json @@ -219,6 +219,22 @@ }, + "MANAGE_SHARING_PROFILE" : { + + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", + + "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", + + "FIELD_HEADER_NAME" : "Название:", + + "SECTION_HEADER_PARAMETERS" : "Настройки" + + }, + "MANAGE_USER" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",