GUAC-963: Manage file downloads.

This commit is contained in:
Michael Jumper
2014-12-30 01:10:03 -08:00
parent c779fff2d1
commit bfb973b783
3 changed files with 174 additions and 99 deletions

View File

@@ -559,67 +559,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
};
// Mapping of download stream index to notification object
var downloadNotifications = {};
// Mapping of download stream index to notification ID
var downloadNotificationIDs = {};
$scope.$on('guacClientFileDownloadStart', function handleClientFileDownloadStart(event, guacClient, streamIndex, mimetype, filename) {
$scope.$apply(function() {
var notification = {
className : 'download',
title : 'CLIENT.DIALOG_TITLE_FILE_TRANSFER',
text : filename
};
downloadNotifications[streamIndex] = notification;
downloadNotificationIDs[streamIndex] = $scope.addNotification(notification);
});
});
$scope.$on('guacClientFileDownloadProgress', function handleClientFileDownloadProgress(event, guacClient, streamIndex, mimetype, filename, length) {
$scope.$apply(function() {
var notification = downloadNotifications[streamIndex];
if (notification)
notification.progress = getFileProgress('CLIENT.TEXT_FILE_TRANSFER_PROGRESS', length);
});
});
$scope.$on('guacClientFileDownloadEnd', function handleClientFileDownloadEnd(event, guacClient, streamIndex, mimetype, filename, blob) {
$scope.$apply(function() {
var notification = downloadNotifications[streamIndex];
var notificationID = downloadNotificationIDs[streamIndex];
/**
* Saves the current file.
*/
var saveFile = function saveFile() {
saveAs(blob, filename);
$scope.removeNotification(notificationID);
delete downloadNotifications[streamIndex];
delete downloadNotificationIDs[streamIndex];
};
// Add download action and remove progress indicator
if (notificationID && notification) {
delete notification.progress;
notification.actions = [
{
name : 'CLIENT.ACTION_SAVE_FILE',
callback : saveFile
}
];
}
});
});
// Clean up when view destroyed
$scope.$on('$destroy', function clientViewDestroyed() {

View File

@@ -27,10 +27,11 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
function defineManagedClient($rootScope, $injector) {
// Required types
var ClientProperties = $injector.get('ClientProperties');
var ManagedClientState = $injector.get('ManagedClientState');
var ManagedDisplay = $injector.get('ManagedDisplay');
var ManagedFileUpload = $injector.get('ManagedFileUpload');
var ClientProperties = $injector.get('ClientProperties');
var ManagedClientState = $injector.get('ManagedClientState');
var ManagedDisplay = $injector.get('ManagedDisplay');
var ManagedFileDownload = $injector.get('ManagedFileDownload');
var ManagedFileUpload = $injector.get('ManagedFileUpload');
// Required services
var $window = $injector.get('$window');
@@ -100,6 +101,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
*/
this.clipboardData = template.clipboardData;
/**
* All downloaded files. As files are downloaded, their progress can be
* observed through the elements of this array. It is intended that
* this array be manipulated externally as needed.
*
* @type ManagedFileDownload[]
*/
this.downloads = template.downloads || [];
/**
* All uploaded files. As files are uploaded, their progress can be
* observed through the elements of this array. It is intended that
@@ -366,43 +376,16 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
};
// Handle any received files
client.onfile = function clientFileReceived(stream, mimetype, filename) {
$rootScope.$apply(function startDownload() {
managedClient.downloads.push(ManagedFileDownload.getInstance(stream, mimetype, filename));
});
};
// Manage the client display
managedClient.managedDisplay = ManagedDisplay.getInstance(client.getDisplay());
/* TODO: Restore file transfer again */
/*
// Handle any received files
client.onfile = function onClientFile(stream, mimetype, filename) {
// Begin file download
var guacFileStartEvent = $rootScope.$emit('guacClientFileDownloadStart', client, stream.index, mimetype, filename);
if (!guacFileStartEvent.defaultPrevented) {
var blob_reader = new Guacamole.BlobReader(stream, mimetype);
// Update progress as data is received
blob_reader.onprogress = function onprogress() {
$rootScope.$emit('guacClientFileDownloadProgress', client, stream.index, mimetype, filename, blob_reader.getLength());
stream.sendAck("Received", Guacamole.Status.Code.SUCCESS);
};
// When complete, prompt for download
blob_reader.onend = function onend() {
$rootScope.$emit('guacClientFileDownloadEnd', client, stream.index, mimetype, filename, blob_reader.getBlob());
};
stream.sendAck("Ready", Guacamole.Status.Code.SUCCESS);
}
// Respond with UNSUPPORTED if download (default action) canceled within event handler
else
stream.sendAck("Download canceled", Guacamole.Status.Code.UNSUPPORTED);
};
*/
// Connect the Guacamole client
client.connect(getConnectString(id, connectionParameters));

View File

@@ -0,0 +1,153 @@
/*
* 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.
*/
/**
* Provides the ManagedFileDownload class used by the guacClientManager service.
*/
angular.module('client').factory('ManagedFileDownload', ['$rootScope', '$injector',
function defineManagedFileDownload($rootScope, $injector) {
// Required types
var ManagedFileTransferState = $injector.get('ManagedFileTransferState');
/**
* Object which serves as a surrogate interface, encapsulating a Guacamole
* file download while it is active, allowing it to be detached and
* reattached from different client views.
*
* @constructor
* @param {ManagedFileDownload|Object} [template={}]
* The object whose properties should be copied within the new
* ManagedFileDownload.
*/
var ManagedFileDownload = function ManagedFileDownload(template) {
// Use empty object by default
template = template || {};
/**
* The current state of the file transfer stream.
*
* @type ManagedFileTransferState
*/
this.transferState = template.transferState || new ManagedFileTransferState();
/**
* The mimetype of the file being transferred.
*
* @type String
*/
this.mimetype = template.mimetype;
/**
* The filename of the file being transferred.
*
* @type String
*/
this.filename = template.filename;
/**
* The number of bytes transferred so far.
*
* @type Number
*/
this.progress = template.progress;
/**
* A blob containing the complete downloaded file. This is available
* only after the download has finished.
*
* @type Blob
*/
this.blob = template.blob;
};
/**
* Creates a new ManagedFileDownload which downloads the contents of the
* given stream as a file having the given mimetype and filename.
*
* @param {Guacamole.InputStream} stream
* The stream whose contents should be downloaded as a file.
*
* @param {String} mimetype
* The mimetype of the stream contents.
*
* @param {String} filename
* The filename of the file being received over the steram.
*
* @return {ManagedFileDownload}
* A new ManagedFileDownload object which can be used to track the
* progress of the download.
*/
ManagedFileDownload.getInstance = function getInstance(stream, mimetype, filename) {
// Init new file download object
var managedFileDownload = new ManagedFileDownload({
mimetype : mimetype,
filename : filename,
progress : 0,
transferState : new ManagedFileTransferState({
streamState : ManagedFileTransferState.StreamState.OPEN
})
});
// Begin file download
var blob_reader = new Guacamole.BlobReader(stream, mimetype);
// Update progress as data is received
blob_reader.onprogress = function onprogress() {
// Update progress
$rootScope.$apply(function downloadStreamProgress() {
managedFileDownload.progress = blob_reader.getLength();
});
// Signal server that data was received
stream.sendAck("Received", Guacamole.Status.Code.SUCCESS);
};
// Save blob and close stream when complete
blob_reader.onend = function onend() {
$rootScope.$apply(function downloadStreamEnd() {
// Save blob
managedFileDownload.blob = blob_reader.getBlob();
// Mark stream as closed
ManagedFileTransferState.setStreamState(managedFileDownload.transferState,
ManagedFileTransferState.StreamState.CLOSED);
});
};
// Signal server that data is ready to be received
stream.sendAck("Ready", Guacamole.Status.Code.SUCCESS);
return managedFileDownload;
};
return ManagedFileDownload;
}]);