From 250039aaf965e46337a45d0965df644284afd414 Mon Sep 17 00:00:00 2001 From: Mike Jumper Date: Sat, 28 Jan 2023 23:47:50 -0800 Subject: [PATCH 1/5] GUACAMOLE-1293: Replace message list with overall shared user count. --- .../client/controllers/clientController.js | 18 --- .../client/directives/guacClientMessage.js | 87 ------------- .../client/directives/guacMessageDialog.js | 76 ----------- .../src/app/client/styles/client-message.css | 23 ---- .../src/app/client/styles/message-dialog.css | 122 ------------------ .../app/client/styles/tiled-client-grid.css | 60 +++++++++ .../src/app/client/templates/client.html | 5 - .../client/templates/guacClientMessage.html | 8 -- .../client/templates/guacMessageDialog.html | 21 --- .../client/templates/guacTiledClients.html | 9 +- .../src/app/client/types/ManagedClient.js | 86 +++++++----- .../app/client/types/ManagedClientMessage.js | 59 --------- .../src/images/user-icons/guac-user-white.svg | 1 + 13 files changed, 126 insertions(+), 449 deletions(-) delete mode 100644 guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js delete mode 100644 guacamole/src/main/frontend/src/app/client/directives/guacMessageDialog.js delete mode 100644 guacamole/src/main/frontend/src/app/client/styles/client-message.css delete mode 100644 guacamole/src/main/frontend/src/app/client/styles/message-dialog.css delete mode 100644 guacamole/src/main/frontend/src/app/client/templates/guacClientMessage.html delete mode 100644 guacamole/src/main/frontend/src/app/client/templates/guacMessageDialog.html delete mode 100644 guacamole/src/main/frontend/src/app/client/types/ManagedClientMessage.js create mode 100644 guacamole/src/main/frontend/src/images/user-icons/guac-user-white.svg diff --git a/guacamole/src/main/frontend/src/app/client/controllers/clientController.js b/guacamole/src/main/frontend/src/app/client/controllers/clientController.js index 3a0df8c11..2821146f8 100644 --- a/guacamole/src/main/frontend/src/app/client/controllers/clientController.js +++ b/guacamole/src/main/frontend/src/app/client/controllers/clientController.js @@ -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 * transfers, regardless of those file transfers' state. diff --git a/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js b/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js deleted file mode 100644 index 5348179d0..000000000 --- a/guacamole/src/main/frontend/src/app/client/directives/guacClientMessage.js +++ /dev/null @@ -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; - }, - {} - ); - }; - - }] - - }; -}]); diff --git a/guacamole/src/main/frontend/src/app/client/directives/guacMessageDialog.js b/guacamole/src/main/frontend/src/app/client/directives/guacMessageDialog.js deleted file mode 100644 index 16ca81d4a..000000000 --- a/guacamole/src/main/frontend/src/app/client/directives/guacMessageDialog.js +++ /dev/null @@ -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; - - }] - - }; -}]); diff --git a/guacamole/src/main/frontend/src/app/client/styles/client-message.css b/guacamole/src/main/frontend/src/app/client/styles/client-message.css deleted file mode 100644 index 21b905672..000000000 --- a/guacamole/src/main/frontend/src/app/client/styles/client-message.css +++ /dev/null @@ -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; -} - diff --git a/guacamole/src/main/frontend/src/app/client/styles/message-dialog.css b/guacamole/src/main/frontend/src/app/client/styles/message-dialog.css deleted file mode 100644 index 2a90c672d..000000000 --- a/guacamole/src/main/frontend/src/app/client/styles/message-dialog.css +++ /dev/null @@ -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; - } - -} diff --git a/guacamole/src/main/frontend/src/app/client/styles/tiled-client-grid.css b/guacamole/src/main/frontend/src/app/client/styles/tiled-client-grid.css index 209ab8e59..6d768e132 100644 --- a/guacamole/src/main/frontend/src/app/client/styles/tiled-client-grid.css +++ b/guacamole/src/main/frontend/src/app/client/styles/tiled-client-grid.css @@ -110,10 +110,15 @@ } .tiled-client-grid .client-tile .client-tile-header .client-tile-name { + -webkit-box-flex: 1; -webkit-flex: 1; -ms-flex: 1; flex: 1; + + padding: 0 0.5em; + margin-bottom: -0.125em; + } .tiled-client-grid .client-tile .main { @@ -136,3 +141,58 @@ .tiled-client-grid .shared .client-tile-shared-indicator { 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-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; +} diff --git a/guacamole/src/main/frontend/src/app/client/templates/client.html b/guacamole/src/main/frontend/src/app/client/templates/client.html index 7cf976c0a..829d5ef7d 100644 --- a/guacamole/src/main/frontend/src/app/client/templates/client.html +++ b/guacamole/src/main/frontend/src/app/client/templates/client.html @@ -45,11 +45,6 @@ {{'CLIENT.TEXT_CLIENT_STATUS_UNSTABLE' | translate}} - -
- -
- diff --git a/guacamole/src/main/frontend/src/translations/en.json b/guacamole/src/main/frontend/src/translations/en.json index 208b4588b..77b59d19d 100644 --- a/guacamole/src/main/frontend/src/translations/en.json +++ b/guacamole/src/main/frontend/src/translations/en.json @@ -128,10 +128,7 @@ "INFO_CONNECTION_SHARED" : "This connection is now shared.", "INFO_NO_FILE_TRANSFERS" : "No file transfers.", - - "MESSAGE_DEFAULT" : "", - "MESSAGE_USER_JOINED" : "User {ARGS_1} has joined the connection.", - "MESSAGE_USER_LEFT" : "User {ARGS_1} has left the connection.", + "INFO_USER_COUNT" : "{USERNAME}{COUNT, plural, one{} other{ (#)}}", "NAME_INPUT_METHOD_NONE" : "None", "NAME_INPUT_METHOD_OSK" : "On-screen keyboard", @@ -158,6 +155,8 @@ "TEXT_CLIENT_STATUS_DISCONNECTED" : "You have been disconnected.", "TEXT_CLIENT_STATUS_UNSTABLE" : "The network connection to the Guacamole server appears unstable.", "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_FILE_TRANSFER_PROGRESS" : "{PROGRESS} {UNIT, select, b{B} kb{KB} mb{MB} gb{GB} other{}}", From b7e3f73ffa1eefc81eaf1eb1030e58cd3d117aa0 Mon Sep 17 00:00:00 2001 From: Mike Jumper Date: Mon, 30 Jan 2023 12:07:50 -0800 Subject: [PATCH 4/5] GUACAMOLE-1293: Clarify message parameter mapping with variable naming. --- guacamole-common-js/src/main/webapp/modules/Client.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/Client.js b/guacamole-common-js/src/main/webapp/modules/Client.js index 8f5017543..a11f8d420 100644 --- a/guacamole-common-js/src/main/webapp/modules/Client.js +++ b/guacamole-common-js/src/main/webapp/modules/Client.js @@ -1451,6 +1451,9 @@ Guacamole.Client = function(tunnel) { "msg" : function(parameters) { + var userID; + var username; + var msgid = parseInt(parameters[0]); if (guac_client.onmsg) guac_client.onmsg(msgid, parameters.slice(1)); @@ -1458,13 +1461,17 @@ Guacamole.Client = function(tunnel) { switch (msgid) { case Guacamole.Client.Message.USER_JOINED: + userID = parameters[1]; + username = parameters[2]; if (guac_client.onjoin) - guac_client.onjoin(parameters[1] /* User ID */, parameters[2] /* Name */); + 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(parameters[1] /* User ID */, parameters[2] /* Name */); + guac_client.onleave(userID, username); break; } From bd913274152fcc12a12e26955bb3acdaa71e52e8 Mon Sep 17 00:00:00 2001 From: Mike Jumper Date: Tue, 31 Jan 2023 14:50:54 -0800 Subject: [PATCH 5/5] GUACAMOLE-1293: Allow "onmsg" to determine whether "onjoin" and "onleave" fire. --- .../src/main/webapp/modules/Client.js | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/Client.js b/guacamole-common-js/src/main/webapp/modules/Client.js index a11f8d420..a62b5246d 100644 --- a/guacamole-common-js/src/main/webapp/modules/Client.js +++ b/guacamole-common-js/src/main/webapp/modules/Client.js @@ -690,7 +690,10 @@ Guacamole.Client = function(tunnel) { /** * 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 * @param {!number} msgcode @@ -700,6 +703,12 @@ Guacamole.Client = function(tunnel) { * @param {string[]} args * An array of arguments to be processed with the message sent to the * 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; @@ -1454,26 +1463,35 @@ Guacamole.Client = function(tunnel) { var userID; var username; + // Fire general message handling event first + var allowDefault = true; var msgid = parseInt(parameters[0]); - if (guac_client.onmsg) - guac_client.onmsg(msgid, parameters.slice(1)); + if (guac_client.onmsg) { + allowDefault = guac_client.onmsg(msgid, parameters.slice(1)); + if (allowDefault === undefined) + allowDefault = true; + } - switch (msgid) { + // 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_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; + case Guacamole.Client.Message.USER_LEFT: + userID = parameters[1]; + username = parameters[2]; + if (guac_client.onleave) + guac_client.onleave(userID, username); + break; + } } },