diff --git a/platform/entanglement/src/actions/CopyAction.js b/platform/entanglement/src/actions/CopyAction.js index a8dbf95d4c..62ce5619ba 100644 --- a/platform/entanglement/src/actions/CopyAction.js +++ b/platform/entanglement/src/actions/CopyAction.js @@ -26,6 +26,20 @@ define( function (AbstractComposeAction) { "use strict"; + /* + function CopyAction(locationService, copyService, context) { + return new AbstractComposeAction ( + locationService, + copyService, + context, + "Duplicate", + "to a location" + ); + } + + return CopyAction; + */ + /** * The CopyAction is available from context menus and allows a user to * deep copy an object to another location of their choosing. @@ -34,15 +48,21 @@ define( * @constructor * @memberof platform/entanglement */ - function CopyAction($log, locationService, copyService, dialogService, notificationService, context) { + function CopyAction($log, locationService, copyService, dialogService, + notificationService, context) { this.dialogService = dialogService; this.notificationService = notificationService; this.$log = $log; - AbstractComposeAction.call(this, locationService, copyService, context, "Duplicate", "to a location"); + //Extend the behaviour of the Abstract Compose Action + AbstractComposeAction.call(this, locationService, copyService, + context, "Duplicate", "to a location"); } - CopyAction.prototype = Object.create(AbstractComposeAction.prototype); - + /** + * Executes the CopyAction. The CopyAction uses the default behaviour of + * the AbstractComposeAction, but extends it to support notification + * updates of progress on copy. + */ CopyAction.prototype.perform = function() { var self = this, notification, @@ -51,8 +71,19 @@ define( unknownProgress: false, severity: "info", }; - + + /* + Show banner notification of copy progress. + */ function progress(phase, totalObjects, processed){ + /* + Copy has two distinct phases. In the first phase a copy plan is + made in memory. During this phase of execution, the user is + shown a blocking 'modal' dialog. + + In the second phase, the copying is taking place, and the user + is shown non-invasive banner notifications at the bottom of the screen. + */ if (phase.toLowerCase() === 'preparing'){ self.dialogService.showBlockingMessage({ title: "Preparing to copy objects", @@ -62,19 +93,22 @@ define( } else if (phase.toLowerCase() === "copying") { self.dialogService.dismiss(); if (!notification) { - notification = self.notificationService.notify(notificationModel); + notification = self.notificationService + .notify(notificationModel); } notificationModel.progress = (processed / totalObjects) * 100; - notificationModel.title = ["Copied ", processed, "of ", totalObjects, "objects"].join(" "); + notificationModel.title = ["Copied ", processed, "of ", + totalObjects, "objects"].join(" "); } } - AbstractComposeAction.prototype.perform.call(this, progress) - .then(function(){ - notification.dismiss(); - self.notificationService.info("Copying complete."); - }) - .catch(function (error){ + AbstractComposeAction.prototype.perform.call(this) + .then( + function(){ + notification.dismiss(); + self.notificationService.info("Copying complete."); + }, + function(error){ self.$log.error("Error copying objects. ", error); //Show more general error message self.notificationService.notify({ @@ -82,7 +116,11 @@ define( severity: "error", hint: error.message }); - }); + + }, + function(notification){ + progress(notification.phase, notification.totalObjects, notification.processed); + }) }; return CopyAction; } diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index 220084f7a5..710c0ef78b 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -68,7 +68,7 @@ define( * object being copied. The clones are all full composed with * references to their own children. */ - CopyService.prototype.buildCopyPlan = function(domainObject, parent) { + CopyService.prototype.buildCopyPlan = function(domainObject, parent, progress) { var clones = [], $q = this.$q, self = this; @@ -99,6 +99,8 @@ define( delete modelClone.model.persisted; delete modelClone.model.modified; return $q.when(originalObject.useCapability('composition')).then(function(composees){ + + progress({phase: "preparing"}); return (composees || []).reduce(function(promise, composee){ //If the object is composed of other // objects, chain a promise.. @@ -120,6 +122,7 @@ define( }); }); }; + return copy(domainObject, parent).then(function(){ return clones; }); @@ -142,7 +145,7 @@ define( clone.model.persisted = self.now(); return self.persistenceService.createObject(clone.persistenceSpace, clone.id, clone.model) .then(function(){ - progress && progress("copying", objectClones.length, ++persisted); + progress && progress({phase: "copying", totalObjects: objectClones.length, processed: ++persisted}); }); })).then(function(){ return objectClones @@ -184,13 +187,15 @@ define( * @returns a promise that will be completed with the clone of * domainObject when the duplication is successful. */ - CopyService.prototype.perform = function (domainObject, parent, progress) { - var $q = this.$q; + CopyService.prototype.perform = function (domainObject, parent) { + var $q = this.$q, + deferred = $q.defer(); if (this.validate(domainObject, parent)) { - progress && progress("preparing"); - return this.buildCopyPlan(domainObject, parent) - .then(this.persistObjects(progress)) - .then(this.addClonesToParent(parent, progress)); + this.buildCopyPlan(domainObject, parent, deferred.notify) + .then(this.persistObjects(deferred.notify)) + .then(this.addClonesToParent(parent, deferred.notify)) + .then(deferred.resolve); + return deferred.promise; } else { throw new Error( "Tried to copy objects without validating first."