diff --git a/guacamole/src/main/webapp/app/client/directives/guacFileTransfer.js b/guacamole/src/main/webapp/app/client/directives/guacFileTransfer.js new file mode 100644 index 000000000..87b7ae949 --- /dev/null +++ b/guacamole/src/main/webapp/app/client/directives/guacFileTransfer.js @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Directive which displays an active file transfer, providing links for + * downloads, if applicable. + */ +angular.module('client').directive('guacFileTransfer', [function guacFileTransfer() { + + return { + restrict: 'E', + replace: true, + scope: { + + /** + * The file transfer to display. + * + * @type ManagedFileUpload|ManagedFileDownload + */ + transfer : '=' + + }, + + templateUrl: 'app/client/templates/guacFileTransfer.html', + controller: ['$scope', '$injector', function guacFileTransferController($scope, $injector) { + + /* STUB */ + + }] + + }; +}]); diff --git a/guacamole/src/main/webapp/app/client/directives/guacFileTransferManager.js b/guacamole/src/main/webapp/app/client/directives/guacFileTransferManager.js new file mode 100644 index 000000000..d49e94288 --- /dev/null +++ b/guacamole/src/main/webapp/app/client/directives/guacFileTransferManager.js @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Directive which displays all active file transfers. + */ +angular.module('client').directive('guacFileTransferManager', [function guacFileTransferManager() { + + return { + restrict: 'E', + replace: true, + scope: { + + /** + * The client whose file transfers should be managed by this + * directive. + * + * @type ManagerClient + */ + client : '=' + + }, + + templateUrl: 'app/client/templates/guacFileTransferManager.html', + controller: ['$scope', '$injector', function guacFileTransferManagerController($scope, $injector) { + + // Required types + var ManagedClient = $injector.get('ManagedClient'); + var ManagedFileTransferState = $injector.get('ManagedFileTransferState'); + + /** + * Determines whether the attached client has associated file + * transfers, regardless of those file transfers' state. + * + * @returns {Boolean} + * true if there are any file transfers associated with the + * attached client, false otherise. + */ + $scope.hasTransfers = function hasTransfers() { + + // There are no file transfers if there is no client + if (!$scope.client) + return false; + + return !!($scope.client.uploads.length || $scope.client.downloads.length); + + }; + + /** + * Determines whether the given file transfer state indicates an + * in-progress transfer. + * + * @param {ManagedFileTransferState} transferState + * The file transfer state to check. + * + * @returns {Boolean} + * true if the given file transfer state indicates an in- + * progress transfer, false otherwise. + */ + var isInProgress = function isInProgress(transferState) { + switch (transferState.streamState) { + + // IDLE or OPEN file transfers are active + case ManagedFileTransferState.StreamState.IDLE: + case ManagedFileTransferState.StreamState.OPEN: + return true; + + // All others are not active + default: + return false; + + } + }; + + /** + * Removes all file transfers which are not currently in-progress. + */ + $scope.clearCompletedTransfers = function clearCompletedTransfers() { + + // Nothing to clear if no client attached + if (!$scope.client) + return; + + // Remove completed uploads + $scope.client.uploads = $scope.client.uploads.filter(function isUploadInProgress(upload) { + return isInProgress(upload.transferState); + }); + + // Remove completed downloads + $scope.client.downloads = $scope.client.downloads.filter(function isDownloadInProgress(download) { + return isInProgress(download.transferState); + }); + + }; + + /** + * Begins a file upload through the attached Guacamole client for + * each file in the given FileList. + * + * @param {FileList} files + * The files to upload. + */ + $scope.uploadFiles = function uploadFiles(files) { + + // Ignore file uploads if no attached client + if (!$scope.client) + return; + + // Upload each file + for (var i = 0; i < files.length; i++) + ManagedClient.uploadFile($scope.client, files[i]); + + }; + + }] + + }; +}]); diff --git a/guacamole/src/main/webapp/app/client/styles/transfer-manager.css b/guacamole/src/main/webapp/app/client/styles/transfer-manager.css new file mode 100644 index 000000000..e2b9c5f0d --- /dev/null +++ b/guacamole/src/main/webapp/app/client/styles/transfer-manager.css @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +.transfer-manager .action-buttons { + text-align: center; +} + +.transfer-manager .no-transfers { + + color: rgba(255, 255, 255, 0.5); + text-shadow: -1px -1px rgba(0, 0, 0, 0.5); + opacity: 0.5; + + font-size: 2em; + font-weight: bolder; + text-align: center; + +} + +.transfer-manager .transfer { + margin: 1em; +} diff --git a/guacamole/src/main/webapp/app/client/styles/transfer.css b/guacamole/src/main/webapp/app/client/styles/transfer.css new file mode 100644 index 000000000..d69df0d4e --- /dev/null +++ b/guacamole/src/main/webapp/app/client/styles/transfer.css @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +.transfer { + position: relative; + padding: 0.5em; + font-size: 0.75em; +} + +.transfer .filename { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + width: 100%; + margin-bottom: 0.5em; +} + +.transfer .text { + position: relative; + text-align: center; + margin-top: 0.5em; +} + +@keyframes transfer-progress { + from {background-position: 0px 0px;} + to {background-position: 64px 0px;} +} + +@-webkit-keyframes transfer-progress { + from {background-position: 0px 0px;} + to {background-position: 64px 0px;} +} + +.transfer .progress { + + width: 100%; + background: #C2C2C2 url('images/progress.png'); + background-size: 16px 16px; + -moz-background-size: 16px 16px; + -webkit-background-size: 16px 16px; + -khtml-background-size: 16px 16px; + + animation-name: transfer-progress; + animation-duration: 2s; + animation-timing-function: linear; + animation-iteration-count: infinite; + + -webkit-animation-name: transfer-progress; + -webkit-animation-duration: 2s; + -webkit-animation-timing-function: linear; + -webkit-animation-iteration-count: infinite; + + padding: 0.25em; + + border: 1px solid gray; + + position: absolute; + top: 0; + left: 0; + bottom: 0; + opacity: 0.25; + +} + +.transfer .progress .bar { + background: #A3D655; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 0; +} diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html index 8a071ed53..cabe77362 100644 --- a/guacamole/src/main/webapp/app/client/templates/client.html +++ b/guacamole/src/main/webapp/app/client/templates/client.html @@ -60,13 +60,21 @@ {{'CLIENT.ACTION_DISCONNECT' | translate}}

{{client.name}}

- + +

{{'CLIENT.SECTION_HEADER_CLIPBOARD' | translate}}

{{'CLIENT.HELP_CLIPBOARD' | translate}}

+ +

{{'CLIENT.SECTION_HEADER_FILE_TRANSFERS' | translate}}

+
+ +
+ +

{{'CLIENT.SECTION_HEADER_INPUT_METHOD' | translate}}

@@ -91,6 +99,7 @@
+

{{'CLIENT.SECTION_HEADER_MOUSE_MODE' | translate}}

{{'CLIENT.HELP_MOUSE_MODE' | translate}}

@@ -115,6 +124,7 @@
+

{{'CLIENT.SECTION_HEADER_DISPLAY' | translate}}

@@ -124,11 +134,5 @@
- - - -
- -
diff --git a/guacamole/src/main/webapp/app/client/templates/guacFileTransfer.html b/guacamole/src/main/webapp/app/client/templates/guacFileTransfer.html new file mode 100644 index 000000000..89a1e76a0 --- /dev/null +++ b/guacamole/src/main/webapp/app/client/templates/guacFileTransfer.html @@ -0,0 +1,33 @@ +
+ + + +
{{transfer.filename}}
+ + +
{{transfer.progress}}
+ + +
+ +
diff --git a/guacamole/src/main/webapp/app/client/templates/guacFileTransferManager.html b/guacamole/src/main/webapp/app/client/templates/guacFileTransferManager.html new file mode 100644 index 000000000..964294e7c --- /dev/null +++ b/guacamole/src/main/webapp/app/client/templates/guacFileTransferManager.html @@ -0,0 +1,43 @@ +
+ + + +

{{'CLIENT.INFO_NO_FILE_TRANSFERS' | translate}}

+ + +
+ +
+ + +
+ +
+ + +
+ {{'CLIENT.ACTION_UPLOAD_FILES' | translate}} + {{'CLIENT.ACTION_CLEAR_COMPLETED_TRANSFERS' | translate}} +
+ +
diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index cc0fee696..8d0998ee1 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -17,11 +17,13 @@ "CLIENT" : { - "ACTION_RECONNECT" : "Reconnect", - "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_DISCONNECT" : "Disconnect", - "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", - "ACTION_SAVE_FILE" : "@:APP.ACTION_SAVE", + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CLEAR_COMPLETED_TRANSFERS" : "Clear Completed Transfers", + "ACTION_DISCONNECT" : "Disconnect", + "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", + "ACTION_RECONNECT" : "Reconnect", + "ACTION_SAVE_FILE" : "@:APP.ACTION_SAVE", + "ACTION_UPLOAD_FILES" : "Upload Files", "DIALOG_HEADER_CONNECTING" : "Connecting", "DIALOG_HEADER_CONNECTION_ERROR" : "Connection Error", @@ -69,6 +71,8 @@ "HELP_MOUSE_MODE_ABSOLUTE" : "Tap to click. The click occurs at the location of the touch.", "HELP_MOUSE_MODE_RELATIVE" : "Drag to move the mouse pointer and tap to click. The click occurs at the location of the pointer.", + "INFO_NO_FILE_TRANSFERS" : "No file transfers.", + "NAME_INPUT_METHOD_NONE" : "None", "NAME_INPUT_METHOD_OSK" : "On-screen keyboard", "NAME_INPUT_METHOD_TEXT" : "Text input", @@ -79,10 +83,11 @@ "NAME_MOUSE_MODE_ABSOLUTE" : "Touchscreen", "NAME_MOUSE_MODE_RELATIVE" : "Touchpad", - "SECTION_HEADER_CLIPBOARD" : "Clipboard", - "SECTION_HEADER_INPUT_METHOD" : "Input method", - "SECTION_HEADER_DISPLAY" : "Display", - "SECTION_HEADER_MOUSE_MODE" : "Mouse emulation mode", + "SECTION_HEADER_CLIPBOARD" : "Clipboard", + "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", "TEXT_CLIENT_STATUS_IDLE" : "Idle.", @@ -101,10 +106,10 @@ "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", "ACTION_MANAGE" : "@:APP.ACTION_MANAGE", - "INFO_NO_RECENT_CONNECTIONS" : "No recent Connections.", + "INFO_NO_RECENT_CONNECTIONS" : "No recent connections.", - "SECTION_HEADER_RECENT_CONNECTIONS" : "Recent Connections", - "SECTION_HEADER_ALL_CONNECTIONS" : "All Connections" + "SECTION_HEADER_ALL_CONNECTIONS" : "All Connections", + "SECTION_HEADER_RECENT_CONNECTIONS" : "Recent Connections" },