mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-1293: Add client-side support for join/leave notification.
This commit is contained in:
@@ -791,6 +791,24 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
|||||||
ManagedClient.uploadFile($scope.filesystemMenuContents.client, files[i], $scope.filesystemMenuContents);
|
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
|
* Determines whether the attached client group has any associated file
|
||||||
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
message : '='
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
templateUrl: 'app/client/templates/guacClientMessage.html'
|
||||||
|
|
||||||
|
};
|
||||||
|
}]);
|
@@ -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;
|
||||||
|
|
||||||
|
}]
|
||||||
|
|
||||||
|
};
|
||||||
|
}]);
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -44,6 +44,11 @@
|
|||||||
<div id="connection-warning" ng-show="isConnectionUnstable()">
|
<div id="connection-warning" ng-show="isConnectionUnstable()">
|
||||||
{{'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">
|
||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="client-message" ng-click="clear()">
|
||||||
|
|
||||||
|
<!-- Message text -->
|
||||||
|
<p class="client-message-text">{{ message }}</p>
|
||||||
|
|
||||||
|
</div>
|
@@ -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>
|
@@ -173,6 +173,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
* @type ManagedFilesystem[]
|
* @type ManagedFilesystem[]
|
||||||
*/
|
*/
|
||||||
this.filesystems = template.filesystems || [];
|
this.filesystems = template.filesystems || [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All messages that have been sent to the client that should be
|
||||||
|
* displayed.
|
||||||
|
*
|
||||||
|
* @type String[]
|
||||||
|
*/
|
||||||
|
this.messages = template.messages || [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All available share links generated for the this ManagedClient via
|
* All available share links generated for the this ManagedClient via
|
||||||
@@ -486,6 +494,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle messages received from guacd to display to the client.
|
||||||
|
client.onmsg = function clientMessage(message) {
|
||||||
|
|
||||||
|
$rootScope.$apply(function updateMessages() {
|
||||||
|
managedClient.messages.push(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
// Automatically update the client thumbnail
|
// Automatically update the client thumbnail
|
||||||
client.onsync = function syncReceived() {
|
client.onsync = function syncReceived() {
|
||||||
@@ -892,11 +909,27 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
|||||||
return false;
|
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,
|
* Returns whether the given client has any associated file transfers,
|
||||||
* regardless of those file transfers' state.
|
* regardless of those file transfers' state.
|
||||||
*
|
*
|
||||||
|
* @param {GuacamoleClient} client
|
||||||
|
* The client for which file transfers should be checked.
|
||||||
|
*
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* true if there are any file transfers associated with the
|
* true if there are any file transfers associated with the
|
||||||
* given client, false otherwise.
|
* given client, false otherwise.
|
||||||
|
@@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||||
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
||||||
|
"ACTION_CLEAR_CLIENT_MESSAGES" : "Clear",
|
||||||
"ACTION_CLEAR_COMPLETED_TRANSFERS" : "Clear",
|
"ACTION_CLEAR_COMPLETED_TRANSFERS" : "Clear",
|
||||||
"ACTION_CONTINUE" : "@:APP.ACTION_CONTINUE",
|
"ACTION_CONTINUE" : "@:APP.ACTION_CONTINUE",
|
||||||
"ACTION_DISCONNECT" : "Disconnect",
|
"ACTION_DISCONNECT" : "Disconnect",
|
||||||
@@ -138,11 +139,13 @@
|
|||||||
"NAME_MOUSE_MODE_ABSOLUTE" : "Touchscreen",
|
"NAME_MOUSE_MODE_ABSOLUTE" : "Touchscreen",
|
||||||
"NAME_MOUSE_MODE_RELATIVE" : "Touchpad",
|
"NAME_MOUSE_MODE_RELATIVE" : "Touchpad",
|
||||||
|
|
||||||
"SECTION_HEADER_CLIPBOARD" : "Clipboard",
|
"SECTION_HEADER_CLIENT_MESSAGES" : "Messages",
|
||||||
"SECTION_HEADER_DEVICES" : "Devices",
|
"SECTION_HEADER_CLIPBOARD" : "Clipboard",
|
||||||
"SECTION_HEADER_DISPLAY" : "Display",
|
"SECTION_HEADER_DEVICES" : "Devices",
|
||||||
"SECTION_HEADER_FILE_TRANSFERS" : "File Transfers",
|
"SECTION_HEADER_DISPLAY" : "Display",
|
||||||
"SECTION_HEADER_INPUT_METHOD" : "Input method",
|
"SECTION_HEADER_FILE_TRANSFERS" : "File Transfers",
|
||||||
|
"SECTION_HEADER_INPUT_METHOD" : "Input method",
|
||||||
|
|
||||||
"SECTION_HEADER_MOUSE_MODE" : "Mouse emulation mode",
|
"SECTION_HEADER_MOUSE_MODE" : "Mouse emulation mode",
|
||||||
|
|
||||||
"TEXT_ZOOM_AUTO_FIT" : "Automatically fit to browser window",
|
"TEXT_ZOOM_AUTO_FIT" : "Automatically fit to browser window",
|
||||||
|
Reference in New Issue
Block a user