Merge 1.5.0 changes back to master.

This commit is contained in:
Mike Jumper
2023-01-10 21:59:48 -08:00
17 changed files with 585 additions and 10 deletions

View File

@@ -791,6 +791,24 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
ManagedClient.uploadFile($scope.filesystemMenuContents.client, files[i], $scope.filesystemMenuContents);
};
/**
* 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

View File

@@ -0,0 +1,87 @@
/*
* 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;
},
{}
);
};
}]
};
}]);

View File

@@ -0,0 +1,76 @@
/*
* 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;
}]
};
}]);

View File

@@ -0,0 +1,23 @@
/*
* 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;
}

View File

@@ -0,0 +1,122 @@
/*
* 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;
}
}

View File

@@ -44,6 +44,11 @@
<div id="connection-warning" ng-show="isConnectionUnstable()">
{{'CLIENT.TEXT_CLIENT_STATUS_UNSTABLE' | translate}}
</div>
<!-- Message dialog -->
<div id="message-dialog" ng-show="hasMessages()">
<guac-message-dialog client-group="clientGroup"></guac-message-dialog>
</div>
<!-- Menu -->
<div class="menu" ng-class="{open: menu.shown}" id="guac-menu">

View File

@@ -0,0 +1,8 @@
<div class="client-message" ng-click="clear()">
<!-- Message text -->
<p class="client-message-text"
translate="{{ getMessageKey() }}"
translate-values="{{ getMessageArgs() }}"></p>
</div>

View File

@@ -0,0 +1,21 @@
<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>

View File

@@ -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');
@@ -173,6 +174,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
* @type ManagedFilesystem[]
*/
this.filesystems = template.filesystems || [];
/**
* All messages that have been sent to the client that should be
* displayed.
*
* @type ManagedClientMessage[]
*/
this.messages = template.messages || [];
/**
* All available share links generated for the this ManagedClient via
@@ -486,6 +495,19 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
});
};
// Handle messages received from guacd to display to the client.
client.onmsg = function clientMessage(msgcode, args) {
msg = new ManagedClientMessage();
msg.msgcode = msgcode;
msg.args = args;
$rootScope.$apply(function updateMessages() {
managedClient.messages.push(msg);
});
};
// Automatically update the client thumbnail
client.onsync = function syncReceived() {
@@ -892,11 +914,27 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
return false;
};
/**
* 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,
* regardless of those file transfers' state.
*
* @param {GuacamoleClient} client
* The client for which file transfers should be checked.
*
* @returns {boolean}
* true if there are any file transfers associated with the
* given client, false otherwise.

View File

@@ -0,0 +1,59 @@
/*
* 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;
}]);

View File

@@ -60,6 +60,7 @@
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_CLEAR_CLIENT_MESSAGES" : "Clear",
"ACTION_CLEAR_COMPLETED_TRANSFERS" : "Clear",
"ACTION_CONTINUE" : "@:APP.ACTION_CONTINUE",
"ACTION_DISCONNECT" : "Disconnect",
@@ -128,6 +129,10 @@
"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.",
"NAME_INPUT_METHOD_NONE" : "None",
"NAME_INPUT_METHOD_OSK" : "On-screen keyboard",
"NAME_INPUT_METHOD_TEXT" : "Text input",
@@ -138,11 +143,13 @@
"NAME_MOUSE_MODE_ABSOLUTE" : "Touchscreen",
"NAME_MOUSE_MODE_RELATIVE" : "Touchpad",
"SECTION_HEADER_CLIPBOARD" : "Clipboard",
"SECTION_HEADER_DEVICES" : "Devices",
"SECTION_HEADER_DISPLAY" : "Display",
"SECTION_HEADER_FILE_TRANSFERS" : "File Transfers",
"SECTION_HEADER_INPUT_METHOD" : "Input method",
"SECTION_HEADER_CLIENT_MESSAGES" : "Messages",
"SECTION_HEADER_CLIPBOARD" : "Clipboard",
"SECTION_HEADER_DEVICES" : "Devices",
"SECTION_HEADER_DISPLAY" : "Display",
"SECTION_HEADER_FILE_TRANSFERS" : "File Transfers",
"SECTION_HEADER_INPUT_METHOD" : "Input method",
"SECTION_HEADER_MOUSE_MODE" : "Mouse emulation mode",
"TEXT_ZOOM_AUTO_FIT" : "Automatically fit to browser window",

View File

@@ -340,6 +340,11 @@ public class TunnelRequestService {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
AuthenticatedUser authenticatedUser = session.getAuthenticatedUser();
UserContext userContext = session.getUserContext(authProviderIdentifier);
// Attempt to get the user's name and set it for the tunnel client.
String name = authenticatedUser.getIdentifier();
if (name != null)
info.setName(name);
try {