diff --git a/guacamole-common-js/src/main/webapp/modules/Client.js b/guacamole-common-js/src/main/webapp/modules/Client.js index d30075d4f..2281930e0 100644 --- a/guacamole-common-js/src/main/webapp/modules/Client.js +++ b/guacamole-common-js/src/main/webapp/modules/Client.js @@ -689,13 +689,17 @@ Guacamole.Client = function(tunnel) { this.onerror = null; /** - * Fired when a message is received from the remote tunnel and needs to be - * displayed to the user. + * Fired when an arbitrary message is received from the tunnel that should + * be processed by the client. * * @event - * @param {String} message - * The message sent by the remote end of the tunnel that should be - * displayed for the user. + * @param {!number} msgcode + * A status code sent by the remote server that indicates the nature of + * the message that is being sent to the client. + * + * @param {string[]} args + * An array of arguments to be processed with the message sent to the + * client. */ this.onmsg = null; @@ -1413,7 +1417,7 @@ Guacamole.Client = function(tunnel) { "msg" : function(parameters) { - if (guac_client.onmsg) guac_client.onmsg(parameters[0]); + if (guac_client.onmsg) guac_client.onmsg(parseInt(parameters[0]), parameters.slice(1)); }, @@ -1841,3 +1845,31 @@ Guacamole.Client.DefaultTransferFunction = { } }; + +/** + * A list of possible messages that can be sent by the server for processing + * by the client. + * + * @type {!Object.} + */ +Guacamole.Client.Message = { + + /** + * A client message that indicates that a user has joined an existing + * connection. This message xpects a single additional argument - the + * username of the user who has joined the connection. + * + * @type {!number} + */ + "USER_JOINED": 0x0001, + + /** + * A client message that indicates that a user has left an existing + * connection. This message expects a single additional argument - the + * username of the user who has left the connection. + * + * @type {!number} + */ + "USER_LEFT": 0x0002 + +}; diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java index a8e443102..3da4fac4b 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java @@ -56,8 +56,8 @@ public class GuacamoleProtocolVersion { /** * Protocol version 1.5.0, which introduces the "msg" instruction, allowing * the server to send message notifications that will be displayed on the - * client. The version also adds support for the "name" handshake - * instruction, allowing guacd to store the name of the user who is + * client. The version also adds support for the "username" handshake + * instruction, allowing guacd to store the username of the user who is * accessing the connection. */ public static final GuacamoleProtocolVersion VERSION_1_5_0 = new GuacamoleProtocolVersion(1, 5, 0); diff --git a/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js b/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js index 9e63edb18..bf937d20a 100644 --- a/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js +++ b/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js @@ -28,15 +28,54 @@ angular.module('client').directive('guacClientMessage', [function guacClientMess scope: { /** - * The message to display. + * The message to display to the client. * - * @type String + * @type {!ManagedClientMessage} */ message : '=' }, - templateUrl: 'app/client/templates/guacClientMessage.html' + templateUrl: 'app/client/templates/guacClientMessage.html', + + controller: ['$scope', '$injector', '$element', + function guacClientMessageController($scope, $injector, $element) { + + const ManagedClientMessage = $injector.get('ManagedClientMessage'); + + /** + * Uses the msgcode to retrieve the correct translation key for + * the client message. + * + * @returns {string} + */ + $scope.getMessageKey = function getMessageKey() { + + let msgString = "DEFAULT"; + if (Object.values(Guacamole.Client.Message).includes($scope.message.msgcode)) + msgString = Object.keys(Guacamole.Client.Message).find(key => Guacamole.Client.Message[key] === $scope.message.msgcode); + + return "CLIENT.CLIENT_MESSAGE_" + msgString.toUpperCase(); + }; + + /** + * Returns a set of key/value object pairs that represent the + * arguments provided as part of the message in the form + * ARGS[0] = value. + * + * @returns {Object} + */ + $scope.getMessageArgs = function getMessageArgs() { + return $scope.message.args.reduce( + function(acc, value, index) { + acc[`ARGS[${index}]`] = value; + return acc; + }, + {} + ); + }; + + }] }; }]); diff --git a/guacamole/src/main/frontend/src/app/client/templates/guacClientMessage.html b/guacamole/src/main/frontend/src/app/client/templates/guacClientMessage.html index 95b06e7ee..472d02fef 100644 --- a/guacamole/src/main/frontend/src/app/client/templates/guacClientMessage.html +++ b/guacamole/src/main/frontend/src/app/client/templates/guacClientMessage.html @@ -1,6 +1,8 @@
-

{{ message }}

+

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 6c8c6a1f1..18c0ceba0 100644 --- a/guacamole/src/main/frontend/src/app/client/types/ManagedClient.js +++ b/guacamole/src/main/frontend/src/app/client/types/ManagedClient.js @@ -28,6 +28,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', const ClientIdentifier = $injector.get('ClientIdentifier'); const ClipboardData = $injector.get('ClipboardData'); const ManagedArgument = $injector.get('ManagedArgument'); + const ManagedClientMessage = $injector.get('ManagedClientMessage'); const ManagedClientState = $injector.get('ManagedClientState'); const ManagedClientThumbnail = $injector.get('ManagedClientThumbnail'); const ManagedDisplay = $injector.get('ManagedDisplay'); @@ -178,7 +179,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', * All messages that have been sent to the client that should be * displayed. * - * @type String[] + * @type ManagedClientMessage[] */ this.messages = template.messages || []; @@ -496,10 +497,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', }; // Handle messages received from guacd to display to the client. - client.onmsg = function clientMessage(message) { + client.onmsg = function clientMessage(msgcode, args) { + + msg = new ManagedClientMessage(); + msg.msgcode = msgcode; + msg.args = args; $rootScope.$apply(function updateMessages() { - managedClient.messages.push(message); + managedClient.messages.push(msg); }); }; diff --git a/guacamole/src/main/frontend/src/app/client/types/ManagedClientMessage.js b/guacamole/src/main/frontend/src/app/client/types/ManagedClientMessage.js new file mode 100644 index 000000000..9a7c2e18a --- /dev/null +++ b/guacamole/src/main/frontend/src/app/client/types/ManagedClientMessage.js @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Provides the ManagedClientMessage class used for messages displayed in + * a ManagedClient. + */ +angular.module('client').factory('ManagedClientMessage', [function defineManagedClientMessage() { + + /** + * Object which represents a message to be displayed to a Guacamole client. + * + * @constructor + * @param {ManagedClientMessage|Object} [template={}] + * The object whose properties should be copied within the new + * ManagedClientMessage. + */ + var ManagedClientMessage = function ManagedClientMessage(template) { + + // Use empty object by default + template = template || {}; + + /** + * The message code sent by the server that will be used to locate the + * message within the Guacamole translation framework. + */ + this.msgcode = template.msgcode; + + /** + * Any arguments that should be passed through the translation system + * and displayed as part of the message. + */ + this.args = template.args; + + }; + + return ManagedClientMessage; + +}]); \ No newline at end of file diff --git a/guacamole/src/main/frontend/src/translations/en.json b/guacamole/src/main/frontend/src/translations/en.json index 3b5bf4291..c19fed13d 100644 --- a/guacamole/src/main/frontend/src/translations/en.json +++ b/guacamole/src/main/frontend/src/translations/en.json @@ -72,6 +72,10 @@ "ACTION_SHARE" : "@:APP.ACTION_SHARE", "ACTION_UPLOAD_FILES" : "Upload Files", + "CLIENT_MESSAGE_DEFAULT" : "", + "CLIENT_MESSAGE_JOINED" : "User {ARGS[0]} has joined the connection.", + "CLIENT_MESSAGE_LEFT" : "User {ARGS[0]} has left the connection.", + "DIALOG_HEADER_CONNECTING" : "Connecting", "DIALOG_HEADER_CONNECTION_ERROR" : "Connection Error", "DIALOG_HEADER_DISCONNECTED" : "Disconnected",