mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUACAMOLE-1293: Merge add list/count of current users joined to a connection.
This commit is contained in:
		| @@ -690,7 +690,10 @@ Guacamole.Client = function(tunnel) { | |||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * Fired when an arbitrary message is received from the tunnel that should |      * Fired when an arbitrary message is received from the tunnel that should | ||||||
|      * be processed by the client. |      * be processed by the client. By default, additional message-specific | ||||||
|  |      * events such as "onjoin" and "onleave" will fire for the received message | ||||||
|  |      * after this event has been processed. An event handler for "onmsg" need | ||||||
|  |      * not be supplied if "onjoin" and/or "onleave" will be used. | ||||||
|      *  |      *  | ||||||
|      * @event |      * @event | ||||||
|      * @param {!number} msgcode |      * @param {!number} msgcode | ||||||
| @@ -700,9 +703,49 @@ Guacamole.Client = function(tunnel) { | |||||||
|      * @param {string[]} args |      * @param {string[]} args | ||||||
|      *     An array of arguments to be processed with the message sent to the |      *     An array of arguments to be processed with the message sent to the | ||||||
|      *     client. |      *     client. | ||||||
|  |      * | ||||||
|  |      * @return {boolean} | ||||||
|  |      *     true if message-specific events such as "onjoin" and | ||||||
|  |      *     "onleave" should be fired for this message, false otherwise. If | ||||||
|  |      *     no value is returned, message-specific events will be allowed to | ||||||
|  |      *     fire. | ||||||
|      */ |      */ | ||||||
|     this.onmsg = null; |     this.onmsg = null; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Fired when a user joins a shared connection. | ||||||
|  |      * | ||||||
|  |      * @event | ||||||
|  |      * @param {!string} userID | ||||||
|  |      *     A unique value representing this specific user's connection to the | ||||||
|  |      *     shared connection. This value is generated by the server and is | ||||||
|  |      *     guaranteed to be unique relative to other users of the connection. | ||||||
|  |      * | ||||||
|  |      * @param {!string} name | ||||||
|  |      *     A human-readable name representing the user that joined, such as | ||||||
|  |      *     their username. This value is provided by the web application during | ||||||
|  |      *     the connection handshake and is not necessarily unique relative to | ||||||
|  |      *     other users of the connection. | ||||||
|  |      */ | ||||||
|  |     this.onjoin = null; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Fired when a user leaves a shared connection. | ||||||
|  |      * | ||||||
|  |      * @event | ||||||
|  |      * @param {!string} userID | ||||||
|  |      *     A unique value representing this specific user's connection to the | ||||||
|  |      *     shared connection. This value is generated by the server and is | ||||||
|  |      *     guaranteed to be unique relative to other users of the connection. | ||||||
|  |      * | ||||||
|  |      * @param {!string} name | ||||||
|  |      *     A human-readable name representing the user that left, such as their | ||||||
|  |      *     username. This value is provided by the web application during the | ||||||
|  |      *     connection handshake and is not necessarily unique relative to other | ||||||
|  |      *     users of the connection. | ||||||
|  |      */ | ||||||
|  |     this.onleave = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Fired when a audio stream is created. The stream provided to this event |      * Fired when a audio stream is created. The stream provided to this event | ||||||
|      * handler will contain its own event handlers for received data. |      * handler will contain its own event handlers for received data. | ||||||
| @@ -1417,7 +1460,39 @@ Guacamole.Client = function(tunnel) { | |||||||
|          |          | ||||||
|         "msg" : function(parameters) { |         "msg" : function(parameters) { | ||||||
|  |  | ||||||
|             if (guac_client.onmsg) guac_client.onmsg(parseInt(parameters[0]), parameters.slice(1)); |             var userID; | ||||||
|  |             var username; | ||||||
|  |  | ||||||
|  |             // Fire general message handling event first | ||||||
|  |             var allowDefault = true; | ||||||
|  |             var msgid = parseInt(parameters[0]); | ||||||
|  |             if (guac_client.onmsg) { | ||||||
|  |                 allowDefault = guac_client.onmsg(msgid, parameters.slice(1)); | ||||||
|  |                 if (allowDefault === undefined) | ||||||
|  |                     allowDefault = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Fire message-specific convenience events if not prevented by the | ||||||
|  |             // "onmsg" handler | ||||||
|  |             if (allowDefault) { | ||||||
|  |                 switch (msgid) { | ||||||
|  |  | ||||||
|  |                     case Guacamole.Client.Message.USER_JOINED: | ||||||
|  |                         userID = parameters[1]; | ||||||
|  |                         username = parameters[2]; | ||||||
|  |                         if (guac_client.onjoin) | ||||||
|  |                             guac_client.onjoin(userID, username); | ||||||
|  |                         break; | ||||||
|  |  | ||||||
|  |                     case Guacamole.Client.Message.USER_LEFT: | ||||||
|  |                         userID = parameters[1]; | ||||||
|  |                         username = parameters[2]; | ||||||
|  |                         if (guac_client.onleave) | ||||||
|  |                             guac_client.onleave(userID, username); | ||||||
|  |                         break; | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|              |              | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -792,24 +792,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | |||||||
|  |  | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     /** |  | ||||||
|      * Determines whether the attached client group has any associated client |  | ||||||
|      * messages to display. |  | ||||||
|      *  |  | ||||||
|      * @returns {Boolean} |  | ||||||
|      *     true if there are messages to display; otherwise false. |  | ||||||
|      */ |  | ||||||
|     $scope.hasMessages = function hasMessages() { |  | ||||||
|          |  | ||||||
|         // No client group means no messages |  | ||||||
|         if (!$scope.clientGroup) |  | ||||||
|             return false; |  | ||||||
|          |  | ||||||
|         // Otherwise, find messages within the clients in the group. |  | ||||||
|         return _.findIndex($scope.clientGroup.clients, ManagedClient.hasMessages) !== -1; |  | ||||||
|          |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Determines whether the attached client group has any associated file |      * Determines whether the attached client group has any associated file | ||||||
|      * transfers, regardless of those file transfers' state. |      * transfers, regardless of those file transfers' state. | ||||||
|   | |||||||
| @@ -1,87 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Directive which displays a message for the client. |  | ||||||
|  */ |  | ||||||
| angular.module('client').directive('guacClientMessage', [function guacClientMessage() { |  | ||||||
|  |  | ||||||
|     return { |  | ||||||
|         restrict: 'E', |  | ||||||
|         replace: true, |  | ||||||
|         scope: { |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * The message to display to the client. |  | ||||||
|              *  |  | ||||||
|              * @type {!ManagedClientMessage} |  | ||||||
|              */ |  | ||||||
|             message : '=' |  | ||||||
|  |  | ||||||
|         }, |  | ||||||
|  |  | ||||||
|         templateUrl: 'app/client/templates/guacClientMessage.html', |  | ||||||
|          |  | ||||||
|         controller: ['$scope', '$injector', '$element', |  | ||||||
|                 function guacClientMessageController($scope, $injector, $element) { |  | ||||||
|              |  | ||||||
|             // Required types |  | ||||||
|             const ManagedClientMessage = $injector.get('ManagedClientMessage'); |  | ||||||
|              |  | ||||||
|             // Required services |  | ||||||
|             var translationStringService = $injector.get('translationStringService'); |  | ||||||
|              |  | ||||||
|             /** |  | ||||||
|              * 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.MESSAGE_" + translationStringService.canonicalize(msgString); |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             /** |  | ||||||
|              * Returns a set of key/value object pairs that represent the |  | ||||||
|              * arguments provided as part of the message in the form |  | ||||||
|              * "ARGS_0 = value". Guacamole's translation system relies on |  | ||||||
|              * the arguments being available in this format in order to be able |  | ||||||
|              * to handle substituting values for an arbitrary list of arguments. |  | ||||||
|              *  |  | ||||||
|              * @returns {Object} |  | ||||||
|              */ |  | ||||||
|             $scope.getMessageArgs = function getMessageArgs() { |  | ||||||
|                 return $scope.message.args.reduce( |  | ||||||
|                     function(acc, value, index) { |  | ||||||
|                         acc[`ARGS_${index}`] = value; |  | ||||||
|                         return acc; |  | ||||||
|                     }, |  | ||||||
|                     {} |  | ||||||
|                 ); |  | ||||||
|             }; |  | ||||||
|                  |  | ||||||
|         }] |  | ||||||
|  |  | ||||||
|     }; |  | ||||||
| }]); |  | ||||||
| @@ -0,0 +1,196 @@ | |||||||
|  | /* | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * A directive that displays a status indicator showing the number of users | ||||||
|  |  * joined to a connection. The specific usernames of those users are visible in | ||||||
|  |  * a tooltip on mouseover, and small notifications are displayed as users | ||||||
|  |  * join/leave the connection. | ||||||
|  |  */ | ||||||
|  | angular.module('client').directive('guacClientUserCount', [function guacClientUserCount() { | ||||||
|  |  | ||||||
|  |     const directive = { | ||||||
|  |         restrict: 'E', | ||||||
|  |         replace: true, | ||||||
|  |         templateUrl: 'app/client/templates/guacClientUserCount.html' | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     directive.scope = { | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The client whose current users should be displayed. | ||||||
|  |          *  | ||||||
|  |          * @type ManagedClient | ||||||
|  |          */ | ||||||
|  |         client : '=' | ||||||
|  |          | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     directive.controller = ['$scope', '$injector', '$element', | ||||||
|  |         function guacClientUserCountController($scope, $injector, $element) { | ||||||
|  |  | ||||||
|  |         // Required services | ||||||
|  |         var $translate = $injector.get('$translate'); | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The maximum number of messages displayed by this directive at any | ||||||
|  |          * given time. Old messages will be discarded as necessary to ensure | ||||||
|  |          * the number of messages displayed never exceeds this value. | ||||||
|  |          * | ||||||
|  |          * @constant | ||||||
|  |          * @type number | ||||||
|  |          */ | ||||||
|  |         var MAX_MESSAGES = 3; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The list that should contain any notifications regarding users | ||||||
|  |          * joining or leaving the connection. | ||||||
|  |          * | ||||||
|  |          * @type HTMLUListElement | ||||||
|  |          */ | ||||||
|  |         var messages = $element.find('.client-user-count-messages')[0]; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Map of the usernames of all users of the current connection to the | ||||||
|  |          * number of concurrent connections those users have to the current | ||||||
|  |          * connection. | ||||||
|  |          * | ||||||
|  |          * @type Object.<string, number> | ||||||
|  |          */ | ||||||
|  |         $scope.userCounts = {}; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Displays a message noting that a change related to a particular user | ||||||
|  |          * of this connection has occurred. | ||||||
|  |          * | ||||||
|  |          * @param {!string} str | ||||||
|  |          *     The key of the translation string containing the message to | ||||||
|  |          *     display. This translation key must accept "USERNAME" as the | ||||||
|  |          *     name of the translation parameter containing the username of | ||||||
|  |          *     the user in question. | ||||||
|  |          * | ||||||
|  |          * @param {!string} username | ||||||
|  |          *     The username of the user in question. | ||||||
|  |          */ | ||||||
|  |         var notify = function notify(str, username) { | ||||||
|  |             $translate(str, { 'USERNAME' : username }).then(function translationReady(text) { | ||||||
|  |  | ||||||
|  |                 if (messages.childNodes.length === 3) | ||||||
|  |                     messages.removeChild(messages.lastChild); | ||||||
|  |  | ||||||
|  |                 var message = document.createElement('li'); | ||||||
|  |                 message.className = 'client-user-count-message'; | ||||||
|  |                 message.textContent = text; | ||||||
|  |                 messages.insertBefore(message, messages.firstChild); | ||||||
|  |  | ||||||
|  |                 // Automatically remove the notification after its "fadeout" | ||||||
|  |                 // animation ends. NOTE: This will not fire if the element is | ||||||
|  |                 // not visible at all. | ||||||
|  |                 message.addEventListener('animationend', function animationEnded() { | ||||||
|  |                     messages.removeChild(message); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |             }); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Displays a message noting that a particular user has joined the | ||||||
|  |          * current connection. | ||||||
|  |          *  | ||||||
|  |          * @param {!string} username | ||||||
|  |          *     The username of the user that joined. | ||||||
|  |          */ | ||||||
|  |         var notifyUserJoined = function notifyUserJoined(username) { | ||||||
|  |             notify('CLIENT.TEXT_USER_JOINED', username); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Displays a message noting that a particular user has left the | ||||||
|  |          * current connection. | ||||||
|  |          *  | ||||||
|  |          * @param {!string} username | ||||||
|  |          *     The username of the user that left. | ||||||
|  |          */ | ||||||
|  |         var notifyUserLeft = function notifyUserLeft(username) { | ||||||
|  |             notify('CLIENT.TEXT_USER_LEFT', username); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The ManagedClient attached to this directive at the time the | ||||||
|  |          * notification update scope watch was last invoked. This is necessary | ||||||
|  |          * as $scope.$watchGroup() does not allow for the callback to know | ||||||
|  |          * whether the scope was previously uninitialized (it's "oldValues" | ||||||
|  |          * parameter receives a copy of the new values if there are no old | ||||||
|  |          * values). | ||||||
|  |          * | ||||||
|  |          * @type ManagedClient | ||||||
|  |          */ | ||||||
|  |         var oldClient = null; | ||||||
|  |  | ||||||
|  |         // Update visible notifications as users join/leave | ||||||
|  |         $scope.$watchGroup([ 'client', 'client.userCount' ], function usersChanged() { | ||||||
|  |  | ||||||
|  |             // Resynchronize directive with state of any attached client when | ||||||
|  |             // the client changes, to ensure notifications are only shown for | ||||||
|  |             // future changes in users present | ||||||
|  |             if (oldClient !== $scope.client) { | ||||||
|  |  | ||||||
|  |                 $scope.userCounts = {}; | ||||||
|  |                 oldClient = $scope.client; | ||||||
|  |  | ||||||
|  |                 angular.forEach($scope.client.users, function initUsers(connections, username) { | ||||||
|  |                     var count = Object.keys(connections).length; | ||||||
|  |                     $scope.userCounts[username] = count; | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 return; | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Display join/leave notifications for users who are currently | ||||||
|  |             // connected but whose connection counts have changed | ||||||
|  |             angular.forEach($scope.client.users, function addNewUsers(connections, username) { | ||||||
|  |  | ||||||
|  |                 var count = Object.keys(connections).length; | ||||||
|  |                 var known = $scope.userCounts[username] || 0; | ||||||
|  |  | ||||||
|  |                 if (count > known) | ||||||
|  |                     notifyUserJoined(username); | ||||||
|  |                 else if (count < known) | ||||||
|  |                     notifyUserLeft(username); | ||||||
|  |  | ||||||
|  |                 $scope.userCounts[username] = count; | ||||||
|  |  | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             // Display leave notifications for users who are no longer connected | ||||||
|  |             angular.forEach($scope.userCounts, function removeOldUsers(count, username) { | ||||||
|  |                 if (!$scope.client.users[username]) { | ||||||
|  |                     notifyUserLeft(username); | ||||||
|  |                     delete $scope.userCounts[username]; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     }]; | ||||||
|  |  | ||||||
|  |     return directive; | ||||||
|  |  | ||||||
|  | }]); | ||||||
| @@ -1,76 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Directive which displays all client messages. |  | ||||||
|  */ |  | ||||||
| angular.module('client').directive('guacMessageDialog', [function guacMessageDialog() { |  | ||||||
|  |  | ||||||
|     return { |  | ||||||
|         restrict: 'E', |  | ||||||
|         replace: true, |  | ||||||
|         scope: { |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * The client group whose messages should be managed by this |  | ||||||
|              * directive. |  | ||||||
|              *  |  | ||||||
|              * @type ManagedClientGroup |  | ||||||
|              */ |  | ||||||
|             clientGroup : '=' |  | ||||||
|  |  | ||||||
|         }, |  | ||||||
|  |  | ||||||
|         templateUrl: 'app/client/templates/guacMessageDialog.html', |  | ||||||
|         controller: ['$scope', '$injector', function guacMessageDialogController($scope, $injector) { |  | ||||||
|  |  | ||||||
|             // Required types |  | ||||||
|             const ManagedClient            = $injector.get('ManagedClient'); |  | ||||||
|             const ManagedClientGroup       = $injector.get('ManagedClientGroup'); |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * Removes all messages. |  | ||||||
|              */ |  | ||||||
|             $scope.clearAllMessages = function clearAllMessages() { |  | ||||||
|                  |  | ||||||
|                 // Nothing to clear if no client group attached |  | ||||||
|                 if (!$scope.clientGroup) |  | ||||||
|                     return; |  | ||||||
|  |  | ||||||
|                 // Remove each client's messages |  | ||||||
|                 $scope.clientGroup.clients.forEach(client =>  { |  | ||||||
|                     client.messages = []; |  | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * @borrows ManagedClientGroup.hasMultipleClients |  | ||||||
|              */ |  | ||||||
|             $scope.hasMultipleClients = ManagedClientGroup.hasMultipleClients; |  | ||||||
|  |  | ||||||
|             /** |  | ||||||
|              * @borrows ManagedClient.hasMessages |  | ||||||
|              */ |  | ||||||
|             $scope.hasMessages = ManagedClient.hasMessages; |  | ||||||
|  |  | ||||||
|         }] |  | ||||||
|  |  | ||||||
|     }; |  | ||||||
| }]); |  | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| p.client-message-text { |  | ||||||
|     margin: 5px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -1,122 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #message-dialog { |  | ||||||
|  |  | ||||||
|     position: absolute; |  | ||||||
|     right: 0; |  | ||||||
|     bottom: 0; |  | ||||||
|     z-index: 20; |  | ||||||
|  |  | ||||||
|     font-size: 0.8em; |  | ||||||
|  |  | ||||||
|     width: 3in; |  | ||||||
|     max-width: 100%; |  | ||||||
|     max-height: 3in; |  | ||||||
|      |  | ||||||
|     background: white; |  | ||||||
|     opacity: 0.75; |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #message-dialog .message-dialog-box { |  | ||||||
|  |  | ||||||
|     /* IE10 */ |  | ||||||
|     display: -ms-flexbox; |  | ||||||
|     -ms-flex-align: stretch; |  | ||||||
|     -ms-flex-direction: column; |  | ||||||
|  |  | ||||||
|     /* Ancient Mozilla */ |  | ||||||
|     display: -moz-box; |  | ||||||
|     -moz-box-align: stretch; |  | ||||||
|     -moz-box-orient: vertical; |  | ||||||
|  |  | ||||||
|     /* Ancient WebKit */ |  | ||||||
|     display: -webkit-box; |  | ||||||
|     -webkit-box-align: stretch; |  | ||||||
|     -webkit-box-orient: vertical; |  | ||||||
|  |  | ||||||
|     /* Old WebKit */ |  | ||||||
|     display: -webkit-flex; |  | ||||||
|     -webkit-align-items: stretch; |  | ||||||
|     -webkit-flex-direction: column; |  | ||||||
|  |  | ||||||
|     /* W3C */ |  | ||||||
|     display: flex; |  | ||||||
|     align-items: stretch; |  | ||||||
|     flex-direction: column; |  | ||||||
|  |  | ||||||
|     max-width: inherit; |  | ||||||
|     max-height: inherit; |  | ||||||
|  |  | ||||||
|     border: 1px solid rgba(0, 0, 0, 0.5); |  | ||||||
|     box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #message-dialog .message-dialog-box .header { |  | ||||||
|     -ms-flex: 0 0 auto; |  | ||||||
|     -moz-box-flex: 0; |  | ||||||
|     -webkit-box-flex: 0; |  | ||||||
|     -webkit-flex: 0 0 auto; |  | ||||||
|     flex: 0 0 auto; |  | ||||||
|     margin-bottom: 5px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #message-dialog .message-dialog-box .client-message-body { |  | ||||||
|  |  | ||||||
|     -ms-flex: 1 1 auto; |  | ||||||
|     -moz-box-flex: 1; |  | ||||||
|     -webkit-box-flex: 1; |  | ||||||
|     -webkit-flex: 1 1 auto; |  | ||||||
|     flex: 1 1 auto; |  | ||||||
|  |  | ||||||
|     overflow: auto; |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Shrink maximum height if viewport is too small for default 3in dialog. |  | ||||||
|  */ |  | ||||||
| @media all and (max-height: 3in) { |  | ||||||
|  |  | ||||||
|     #message-dialog { |  | ||||||
|         max-height: 1.5in; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * If viewport is too small for even the 1.5in dialog, fit all available space. |  | ||||||
|  */ |  | ||||||
| @media all and (max-height: 1.5in) { |  | ||||||
|  |  | ||||||
|     #message-dialog { |  | ||||||
|         height: 100%; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #message-dialog .message-dialog-box { |  | ||||||
|         position: absolute; |  | ||||||
|         left:   0.5em; |  | ||||||
|         top:    0.5em; |  | ||||||
|         right:  0.5em; |  | ||||||
|         bottom: 0.5em; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -110,10 +110,15 @@ | |||||||
| } | } | ||||||
|  |  | ||||||
| .tiled-client-grid .client-tile .client-tile-header .client-tile-name { | .tiled-client-grid .client-tile .client-tile-header .client-tile-name { | ||||||
|  |  | ||||||
|     -webkit-box-flex: 1; |     -webkit-box-flex: 1; | ||||||
|     -webkit-flex: 1; |     -webkit-flex: 1; | ||||||
|     -ms-flex: 1; |     -ms-flex: 1; | ||||||
|     flex: 1; |     flex: 1; | ||||||
|  |  | ||||||
|  |     padding: 0 0.5em; | ||||||
|  |     margin-bottom: -0.125em; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .tiled-client-grid .client-tile .main { | .tiled-client-grid .client-tile .main { | ||||||
| @@ -136,3 +141,132 @@ | |||||||
| .tiled-client-grid .shared .client-tile-shared-indicator { | .tiled-client-grid .shared .client-tile-shared-indicator { | ||||||
|     display: inline; |     display: inline; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count { | ||||||
|  |  | ||||||
|  |     visibility: hidden; | ||||||
|  |  | ||||||
|  |     display: block; | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0; | ||||||
|  |     top: 0; | ||||||
|  |     z-index: 1; | ||||||
|  |  | ||||||
|  |     border-radius: 0.25em; | ||||||
|  |     padding: 0.125em 0.75em; | ||||||
|  |     margin: 0.5em; | ||||||
|  |  | ||||||
|  |     background: #055; | ||||||
|  |     color: white; | ||||||
|  |     font-weight: bold; | ||||||
|  |     font-size: 0.8em; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count::before { | ||||||
|  |  | ||||||
|  |     content: ' '; | ||||||
|  |     display: inline-block; | ||||||
|  |  | ||||||
|  |     margin-bottom: -0.2em; | ||||||
|  |     padding-right: 0.25em; | ||||||
|  |     width: 1em; | ||||||
|  |     height: 1em; | ||||||
|  |  | ||||||
|  |     background: center / contain no-repeat url('images/user-icons/guac-user-white.svg'); | ||||||
|  |     background-size: contain; | ||||||
|  |     background-position: center; | ||||||
|  |     background-repeat: no-repeat; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-users, | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-messages { | ||||||
|  |  | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0; | ||||||
|  |  | ||||||
|  |     margin: 0; | ||||||
|  |     padding: 0; | ||||||
|  |     margin-top: 0.5em; | ||||||
|  |     list-style: none; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-users, | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-message { | ||||||
|  |     border-radius: 0.25em; | ||||||
|  |     background: black; | ||||||
|  |     color: white; | ||||||
|  |     padding: 0.5em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-message { | ||||||
|  |     white-space: nowrap; | ||||||
|  |     animation: 1s linear 3s fadeout; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-tile-header .client-user-count { | ||||||
|  |     display: inline-block; | ||||||
|  |     position: relative; | ||||||
|  |     white-space: nowrap; | ||||||
|  |     background: black; | ||||||
|  |     padding-left: 0.5em; | ||||||
|  |     padding-right: 0.75em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-tile-header .client-user-count::before { | ||||||
|  |     padding-right: 0.75em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .joined .client-user-count { | ||||||
|  |     visibility: visible; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-users { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count:hover .client-user-count-users { | ||||||
|  |     display: block; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-user::after { | ||||||
|  |     content: ', '; | ||||||
|  |     margin-right: 0.25em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-user:last-child::after { | ||||||
|  |     content: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-user { | ||||||
|  |     display: inline-block; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-users { | ||||||
|  |     width: 256px; | ||||||
|  |     max-width: 75vw; | ||||||
|  |     white-space: normal; | ||||||
|  |     border: 1px solid #333; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tiled-client-grid .client-user-count .client-user-count-users::before { | ||||||
|  |  | ||||||
|  |     content: ' '; | ||||||
|  |     display: block; | ||||||
|  |  | ||||||
|  |     position: absolute; | ||||||
|  |     right: 0.5em; | ||||||
|  |     top: -0.5em; | ||||||
|  |  | ||||||
|  |     width: 1em; | ||||||
|  |     height: 1em; | ||||||
|  |  | ||||||
|  |     background: black; | ||||||
|  |     border: 1px solid #333; | ||||||
|  |     border-right: none; | ||||||
|  |     border-bottom: none; | ||||||
|  |     transform: rotate(45deg); | ||||||
|  |  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -45,11 +45,6 @@ | |||||||
|         {{'CLIENT.TEXT_CLIENT_STATUS_UNSTABLE' | translate}} |         {{'CLIENT.TEXT_CLIENT_STATUS_UNSTABLE' | translate}} | ||||||
|     </div> |     </div> | ||||||
|      |      | ||||||
|     <!-- Message dialog --> |  | ||||||
|     <div id="message-dialog" ng-show="hasMessages()"> |  | ||||||
|         <guac-message-dialog client-group="clientGroup"></guac-message-dialog> |  | ||||||
|     </div> |  | ||||||
|  |  | ||||||
|     <!-- Menu --> |     <!-- Menu --> | ||||||
|     <div class="menu" ng-class="{open: menu.shown}" id="guac-menu"> |     <div class="menu" ng-class="{open: menu.shown}" id="guac-menu"> | ||||||
|         <div class="menu-content" ng-if="menu.shown" guac-touch-drag="menuDrag"> |         <div class="menu-content" ng-if="menu.shown" guac-touch-drag="menuDrag"> | ||||||
|   | |||||||
| @@ -1,8 +0,0 @@ | |||||||
| <div class="client-message" ng-click="clear()"> |  | ||||||
|  |  | ||||||
|     <!-- Message text --> |  | ||||||
|     <p class="client-message-text" |  | ||||||
|        translate="{{ getMessageKey() }}" |  | ||||||
|        translate-values="{{ getMessageArgs() }}"></p> |  | ||||||
|  |  | ||||||
| </div> |  | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | <div class="client-user-count" title="{{ instance }}"> | ||||||
|  |     <span class="client-user-count-value">{{ client.userCount }}</span> | ||||||
|  |     <ul class="client-user-count-messages"></ul> | ||||||
|  |     <ul class="client-user-count-users"> | ||||||
|  |         <li class="client-user-count-user" ng-repeat="user in userCounts | toArray | orderBy: key" | ||||||
|  |             translate="CLIENT.INFO_USER_COUNT" | ||||||
|  |             translate-values="{ USERNAME : user.key, COUNT : user.value }"></li> | ||||||
|  |     </ul> | ||||||
|  | </div> | ||||||
| @@ -1,21 +0,0 @@ | |||||||
| <div class="message-dialog-box"> |  | ||||||
|      |  | ||||||
|     <!-- Message dialog header --> |  | ||||||
|     <div class="header"> |  | ||||||
|         <h2>{{'CLIENT.SECTION_HEADER_CLIENT_MESSAGES' | translate}}</h2> |  | ||||||
|         <button ng-click="clearAllMessages()">{{'CLIENT.ACTION_CLEAR_CLIENT_MESSAGES' | translate}}</button> |  | ||||||
|     </div> |  | ||||||
|  |  | ||||||
|     <!-- Received messages --> |  | ||||||
|     <div class="client-messages-body"> |  | ||||||
|         <div class="client-messages-body-section" ng-repeat="client in clientGroup.clients" ng-show="hasMessages(client)"> |  | ||||||
|             <h3 ng-show="hasMultipleClients(clientGroup)">{{ client.name }}</h3> |  | ||||||
|             <div class="messages"> |  | ||||||
|                 <guac-client-message |  | ||||||
|                     message="message" |  | ||||||
|                     ng-repeat="message in client.messages"> |  | ||||||
|                 </guac-client-message> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
| </div> |  | ||||||
| @@ -5,12 +5,14 @@ | |||||||
|             <div class="client-tile" ng-if="client" |             <div class="client-tile" ng-if="client" | ||||||
|                 ng-class="{ |                 ng-class="{ | ||||||
|                     'focused' : client.clientProperties.focused, |                     'focused' : client.clientProperties.focused, | ||||||
|                     'shared'  : isShared(client) |                     'shared'  : isShared(client), | ||||||
|  |                     'joined'  : client.userCount | ||||||
|                 }" |                 }" | ||||||
|                 guac-click="getFocusAssignmentCallback(client)"> |                 guac-click="getFocusAssignmentCallback(client)"> | ||||||
|                 <h3 class="client-tile-header" ng-if="hasMultipleClients(clientGroup)"> |                 <h3 class="client-tile-header" ng-if="hasMultipleClients(clientGroup)"> | ||||||
|                     <img class="client-tile-shared-indicator" src="images/share-white.svg"> |                     <img class="client-tile-shared-indicator" src="images/share-white.svg"> | ||||||
|                     <span class="client-tile-name">{{ client.title }}</span> |                     <span class="client-tile-name">{{ client.title }}</span> | ||||||
|  |                     <guac-client-user-count client="client"></guac-client-user-count> | ||||||
|                     <img ng-click="onClose({ '$client' : client })" |                     <img ng-click="onClose({ '$client' : client })" | ||||||
|                          class="client-tile-disconnect" |                          class="client-tile-disconnect" | ||||||
|                          ng-attr-alt="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}" |                          ng-attr-alt="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}" | ||||||
| @@ -21,6 +23,9 @@ | |||||||
|  |  | ||||||
|                 <!-- Client-specific status/error dialog --> |                 <!-- Client-specific status/error dialog --> | ||||||
|                 <guac-client-notification client="client"></guac-client-notification> |                 <guac-client-notification client="client"></guac-client-notification> | ||||||
|  |  | ||||||
|  |                 <guac-client-user-count client="client" ng-if="!hasMultipleClients(clientGroup)"></guac-client-user-count> | ||||||
|  |  | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ | |||||||
|  * under the License. |  * under the License. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | /* global Guacamole, _ */ | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Provides the ManagedClient class used by the guacClientManager service. |  * Provides the ManagedClient class used by the guacClientManager service. | ||||||
|  */ |  */ | ||||||
| @@ -28,7 +30,6 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | |||||||
|     const ClientIdentifier       = $injector.get('ClientIdentifier'); |     const ClientIdentifier       = $injector.get('ClientIdentifier'); | ||||||
|     const ClipboardData          = $injector.get('ClipboardData'); |     const ClipboardData          = $injector.get('ClipboardData'); | ||||||
|     const ManagedArgument        = $injector.get('ManagedArgument'); |     const ManagedArgument        = $injector.get('ManagedArgument'); | ||||||
|     const ManagedClientMessage   = $injector.get('ManagedClientMessage'); |  | ||||||
|     const ManagedClientState     = $injector.get('ManagedClientState'); |     const ManagedClientState     = $injector.get('ManagedClientState'); | ||||||
|     const ManagedClientThumbnail = $injector.get('ManagedClientThumbnail'); |     const ManagedClientThumbnail = $injector.get('ManagedClientThumbnail'); | ||||||
|     const ManagedDisplay         = $injector.get('ManagedDisplay'); |     const ManagedDisplay         = $injector.get('ManagedDisplay'); | ||||||
| @@ -176,12 +177,23 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | |||||||
|         this.filesystems = template.filesystems || []; |         this.filesystems = template.filesystems || []; | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * All messages that have been sent to the client that should be |          * The current number of users sharing this connection, excluding the | ||||||
|          * displayed. |          * user that originally started the connection. Duplicate connections | ||||||
|          *  |          * from the same user are included in this total. | ||||||
|          * @type ManagedClientMessage[] |  | ||||||
|          */ |          */ | ||||||
|         this.messages = template.messages || []; |         this.userCount = template.userCount || 0; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * All users currently sharing this connection, excluding the user that | ||||||
|  |          * originally started the connection. If the connection is not shared, | ||||||
|  |          * this object will be empty. This map consists of key/value pairs | ||||||
|  |          * where each key is the user's username and each value is an object | ||||||
|  |          * tracking the unique connections currently used by that user (a map | ||||||
|  |          * of Guacamole protocol user IDs to boolean values). | ||||||
|  |          * | ||||||
|  |          * @type Object.<string, Object.<string, boolean>> | ||||||
|  |          */ | ||||||
|  |         this.users = template.users || {}; | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * All available share links generated for the this ManagedClient via |          * All available share links generated for the this ManagedClient via | ||||||
| @@ -496,17 +508,34 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | |||||||
|             }); |             }); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         // Handle messages received from guacd to display to the client. |         // Update user count when a new user joins | ||||||
|         client.onmsg = function clientMessage(msgcode, args) { |         client.onjoin = function userJoined(id, username) { | ||||||
|  |             $rootScope.$apply(function usersChanged() { | ||||||
|  |  | ||||||
|             msg = new ManagedClientMessage(); |                 var connections = managedClient.users[username] || {}; | ||||||
|             msg.msgcode = msgcode; |                 managedClient.users[username] = connections; | ||||||
|             msg.args = args; |  | ||||||
|  |                 managedClient.userCount++; | ||||||
|  |                 connections[id] = true; | ||||||
|  |  | ||||||
|             $rootScope.$apply(function updateMessages() { |  | ||||||
|                 managedClient.messages.push(msg); |  | ||||||
|             }); |             }); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // Update user count when a user leaves | ||||||
|  |         client.onleave = function userLeft(id, username) { | ||||||
|  |             $rootScope.$apply(function usersChanged() { | ||||||
|  |  | ||||||
|  |                 var connections = managedClient.users[username] || {}; | ||||||
|  |                 managedClient.users[username] = connections; | ||||||
|  |  | ||||||
|  |                 managedClient.userCount--; | ||||||
|  |                 delete connections[id]; | ||||||
|  |  | ||||||
|  |                 // Delete user entry after no connections remain | ||||||
|  |                 if (_.isEmpty(connections)) | ||||||
|  |                     delete managedClient.users[username]; | ||||||
|  |  | ||||||
|  |             }); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         // Automatically update the client thumbnail |         // Automatically update the client thumbnail | ||||||
| @@ -915,19 +944,6 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | |||||||
|  |  | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     /** |  | ||||||
|      * Returns whether the given client has any associated messages to display. |  | ||||||
|      *  |  | ||||||
|      * @param {GuacamoleClient} client |  | ||||||
|      *     The client for which messages should be checked. |  | ||||||
|      *      |  | ||||||
|      * @returns {Boolean} |  | ||||||
|      *     true if the given client has any messages, otherwise false. |  | ||||||
|      */ |  | ||||||
|     ManagedClient.hasMessages = function hasMessages(client) { |  | ||||||
|         return !!(client && client.messages && client.messages.length); |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns whether the given client has any associated file transfers, |      * Returns whether the given client has any associated file transfers, | ||||||
|      * regardless of those file transfers' state. |      * regardless of those file transfers' state. | ||||||
|   | |||||||
| @@ -1,59 +0,0 @@ | |||||||
| /* |  | ||||||
|  * 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. |  | ||||||
|          *  |  | ||||||
|          * @type Number |  | ||||||
|          */ |  | ||||||
|         this.msgcode = template.msgcode; |  | ||||||
|          |  | ||||||
|         /** |  | ||||||
|          * Any arguments that should be passed through the translation system |  | ||||||
|          * and displayed as part of the message. |  | ||||||
|          *  |  | ||||||
|          * @type String[] |  | ||||||
|          */ |  | ||||||
|         this.args = template.args; |  | ||||||
|          |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return ManagedClientMessage; |  | ||||||
|  |  | ||||||
| }]); |  | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M474.341 504.195c.005 31.957-.398 24.504-.398 58.628H208.914c0-52.862-.398-8.867-.398-58.628 0-47.522 67.134-73.133 131.412-73.133 64.277 0 134.405 18.656 134.413 73.133z" style="fill:#fff;fill-opacity:1;stroke:#000;stroke-width:.09023736;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0" transform="translate(-33.342 -44.364) scale(.19138)"/><path d="M48.002 28.63a16.506 16.506 0 1 1-33.012 0 16.506 16.506 0 1 1 33.012 0z" style="fill:#fff;fill-rule:evenodd;stroke:none" transform="translate(.504 -8)"/></svg> | ||||||
| After Width: | Height: | Size: 635 B | 
| @@ -128,10 +128,7 @@ | |||||||
|  |  | ||||||
|         "INFO_CONNECTION_SHARED" : "This connection is now shared.", |         "INFO_CONNECTION_SHARED" : "This connection is now shared.", | ||||||
|         "INFO_NO_FILE_TRANSFERS" : "No file transfers.", |         "INFO_NO_FILE_TRANSFERS" : "No file transfers.", | ||||||
|  |         "INFO_USER_COUNT"        : "{USERNAME}{COUNT, plural, one{} other{ (#)}}", | ||||||
|         "MESSAGE_DEFAULT"      : "", |  | ||||||
|         "MESSAGE_USER_JOINED"  : "User {ARGS_1} has joined the connection.", |  | ||||||
|         "MESSAGE_USER_LEFT"    : "User {ARGS_1} has left the connection.", |  | ||||||
|  |  | ||||||
|         "NAME_INPUT_METHOD_NONE"   : "None", |         "NAME_INPUT_METHOD_NONE"   : "None", | ||||||
|         "NAME_INPUT_METHOD_OSK"    : "On-screen keyboard", |         "NAME_INPUT_METHOD_OSK"    : "On-screen keyboard", | ||||||
| @@ -158,6 +155,8 @@ | |||||||
|         "TEXT_CLIENT_STATUS_DISCONNECTED" : "You have been disconnected.", |         "TEXT_CLIENT_STATUS_DISCONNECTED" : "You have been disconnected.", | ||||||
|         "TEXT_CLIENT_STATUS_UNSTABLE"     : "The network connection to the Guacamole server appears unstable.", |         "TEXT_CLIENT_STATUS_UNSTABLE"     : "The network connection to the Guacamole server appears unstable.", | ||||||
|         "TEXT_CLIENT_STATUS_WAITING"      : "Connected to Guacamole. Waiting for response...", |         "TEXT_CLIENT_STATUS_WAITING"      : "Connected to Guacamole. Waiting for response...", | ||||||
|  |         "TEXT_USER_JOINED"                : "{USERNAME} has joined the connection.", | ||||||
|  |         "TEXT_USER_LEFT"                  : "{USERNAME} has left the connection.", | ||||||
|         "TEXT_RECONNECT_COUNTDOWN"        : "Reconnecting in {REMAINING} {REMAINING, plural, one{second} other{seconds}}...", |         "TEXT_RECONNECT_COUNTDOWN"        : "Reconnecting in {REMAINING} {REMAINING, plural, one{second} other{seconds}}...", | ||||||
|         "TEXT_FILE_TRANSFER_PROGRESS"     : "{PROGRESS} {UNIT, select, b{B} kb{KB} mb{MB} gb{GB} other{}}", |         "TEXT_FILE_TRANSFER_PROGRESS"     : "{PROGRESS} {UNIT, select, b{B} kb{KB} mb{MB} gb{GB} other{}}", | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user