Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Unified Diff: ui/file_manager/file_manager/foreground/js/file_transfer_controller.js

Issue 2930443002: Show confirmation dialog when copy/move operation affect other members. (Closed)
Patch Set: Remove some comment Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index f368ca67e6c16c39d5c5e68f64956a04aafb4677..9c2ff51589f8f6980646c9871e6fc0d9d1a7a368 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -21,6 +21,10 @@ var FileAsyncData;
* @param {!ListContainer} listContainer List container.
* @param {!MultiProfileShareDialog} multiProfileShareDialog Share dialog to be
* used to share files from another profile.
+ * @param {function(FileManagerUI.ActionType, string, !Array<string>):
+ * !Promise<boolean>} confirmationCallback called when operation requires
+ * user's confirmation. The opeartion will be executed if the return value
+ * resolved to true.
* @param {!ProgressCenter} progressCenter To notify starting copy operation.
* @param {!FileOperationManager} fileOperationManager File operation manager
* instance.
@@ -34,9 +38,10 @@ var FileAsyncData;
* @constructor
*/
function FileTransferController(
- doc, listContainer, directoryTree, multiProfileShareDialog, progressCenter,
- fileOperationManager, metadataModel, thumbnailModel, directoryModel,
- volumeManager, selectionHandler, shouldShowCommandFor) {
+ doc, listContainer, directoryTree, multiProfileShareDialog,
+ confirmationCallback, progressCenter, fileOperationManager, metadataModel,
+ thumbnailModel, directoryModel, volumeManager, selectionHandler,
+ shouldShowCommandFor) {
/**
* @private {!Document}
* @const
@@ -91,6 +96,13 @@ function FileTransferController(
*/
this.multiProfileShareDialog_ = multiProfileShareDialog;
+ /**
+ * @private {function(FileManagerUI.ActionType, string, !Array<string>):
+ * Promise<boolean>}
+ * @const
+ */
+ this.confirmationCallback_ = confirmationCallback;
+
/**
* @private {!ProgressCenter}
* @const
@@ -220,12 +232,14 @@ FileTransferController.DRAG_LABEL_Y_OFFSET_ = -32;
*
* @param {!Array<string>} sourceURLs URLs of source entries.
* @param {!DirectoryEntry} destinationEntry Destination directory.
+ * @param {EntryLocation} destinationLocationInfo Location info of the
+ * destination directory.
* @param {boolean} isMove true if move, false if copy.
* @constructor
* @struct
*/
FileTransferController.PastePlan = function(
- sourceURLs, destinationEntry, isMove) {
+ sourceURLs, destinationEntry, destinationLocationInfo, isMove) {
/**
* @type {!Array<string>}
* @const
@@ -237,6 +251,11 @@ FileTransferController.PastePlan = function(
*/
this.destinationEntry = destinationEntry;
+ /**
+ * @type {EntryLocation}
+ */
+ this.destinationLocationInfo = destinationLocationInfo;
+
/**
* @type {boolean}
* @const
@@ -244,6 +263,88 @@ FileTransferController.PastePlan = function(
this.isMove = isMove;
};
+/**
+ * Judges whether the planned operation requires user's confirmation.
+ *
+ * @return {!Promise<{title: string, body: !Array<string>}>} Resolves to null if
fukino 2017/06/09 07:05:46 The returned value and the method description soun
yamaguchi 2017/06/09 09:49:14 Done.
+ * the operation doesn't require confirmation. Otherwise, the value gives
+ * the title and the body text sentences for a confirmation dialog box.
+ */
+FileTransferController.PastePlan.prototype.shouldShowConfirmation = function() {
+ return util.URLsToEntries(this.sourceURLs).then(function(entriesResult) {
+ var sourceEntries = entriesResult.entries;
+ if (sourceEntries.length == 0) {
+ return null;
+ }
+ var source = {
+ isTeamDrive: util.isTeamDriveEntry(sourceEntries[0]),
+ teamDriveName: util.getTeamDriveName(sourceEntries[0])
+ };
+ var destination = {
+ isTeamDrive: util.isTeamDriveEntry(this.destinationEntry),
+ teamDriveName: util.getTeamDriveName(this.destinationEntry)
+ };
+ if (!this.destinationLocationInfo) {
+ // Unknown type of destination location. Allow without confirmation for
fukino 2017/06/09 07:05:46 I think we should abort paste operation (and outpu
yamaguchi 2017/06/09 09:49:15 Done.
+ // backward compatibility.
+ return null;
+ }
+ /** @type {!Array<string>} */
+ var message_body = [];
+ var title = '';
+ if (this.isMove) {
+ if (source.isTeamDrive) {
+ if (destination.isTeamDrive &&
+ source.teamDriveName != destination.teamDriveName) {
+ // Moving from a Team Drive to another Team Drive.
+ title = strf(
+ 'DRIVE_CONFIRM_MOVE_BETWEEN_TEAM_DRIVES_TITLE',
+ destination.teamDriveName);
+ message_body.push(strf(
+ 'DRIVE_CONFIRM_MOVE_FROM_TEAM_DRIVE_BODY', source.teamDriveName));
+ message_body.push(strf(
+ 'DRIVE_CONFIRM_COPY_TO_TEAM_DRIVE_BODY',
+ destination.teamDriveName));
+ // TODO(yamaguchi): notify ownership transfer if the two Team Drives
+ // belong to different domains.
+ } else if (!destination.isTeamDrive) {
+ // Moving out of Team Drive.
+ title = strf(
+ 'DRIVE_CONFIRM_MOVE_FROM_TEAM_DRIVE_TITLE',
+ util.getRootTypeLabel(this.destinationLocationInfo));
+ message_body.push(strf(
+ 'DRIVE_CONFIRM_MOVE_FROM_TEAM_DRIVE_BODY', source.teamDriveName));
+ // TODO(yamaguchi): Warn if the operation moves at least one directory
+ // to My Drive, as it's no undoable.
+ }
+ } else if (destination.isTeamDrive) {
+ title = strf(
+ 'DRIVE_CONFIRM_MOVE_TO_TEAM_DRIVE_TITLE',
+ destination.teamDriveName);
+ message_body.push(strf(
+ 'DRIVE_CONFIRM_MOVE_TO_TEAM_DRIVE_BODY',
+ destination.teamDriveName));
+ }
+ } else if (destination.isTeamDrive) {
+ // Copying to Team Drive.
+ if (!(source.isTeamDrive &&
+ source.teamDriveName == destination.teamDriveName)) {
+ // This is not a copy within the same Team Drive.
+ title = strf(
+ 'DRIVE_CONFIRM_COPY_TO_TEAM_DRIVE_TITLE',
+ destination.teamDriveName);
+ message_body.push(strf(
+ 'DRIVE_CONFIRM_COPY_TO_TEAM_DRIVE_BODY',
+ destination.teamDriveName));
+ }
+ }
+ if (!title) {
fukino 2017/06/09 07:05:46 "Confirmation is unnecessary when title is falsy v
yamaguchi 2017/06/09 09:49:14 Revised to use NONE enum value.
+ return null;
+ }
+ return {title: title, body: message_body};
+ }.bind(this));
+};
+
/**
* Converts list of urls to list of Entries with granting R/W permissions to
* them, which is essential when pasting files from a different profile.
@@ -536,12 +637,13 @@ FileTransferController.prototype.getMultiProfileShareEntries_ =
* Collects parameters of paste operation by the given command and the current
* system clipboard.
*
- * @return {FileTransferController.PastePlan}
+ * @return {!FileTransferController.PastePlan}
*/
FileTransferController.prototype.preparePaste = function(
clipboardData, opt_destinationEntry, opt_effect) {
var sourceURLs = clipboardData.getData('fs/sources') ?
- clipboardData.getData('fs/sources').split('\n') : [];
+ clipboardData.getData('fs/sources').split('\n') :
fukino 2017/06/09 07:05:46 Was this line break forced by presubmit?
yamaguchi 2017/06/09 09:49:14 No, I had just overlooked it.
+ [];
// effectAllowed set in copy/paste handlers stay uninitialized. DnD handlers
// work fine.
var effectAllowed = clipboardData.effectAllowed !== 'uninitialized' ?
@@ -551,8 +653,10 @@ FileTransferController.prototype.preparePaste = function(
var toMove = util.isDropEffectAllowed(effectAllowed, 'move') &&
(!util.isDropEffectAllowed(effectAllowed, 'copy') ||
opt_effect === 'move');
+
return new FileTransferController.PastePlan(
- sourceURLs, destinationEntry, toMove);
+ sourceURLs, destinationEntry,
+ this.volumeManager_.getLocationInfo(destinationEntry), toMove);
};
/**
@@ -564,13 +668,26 @@ FileTransferController.prototype.preparePaste = function(
* @param {string=} opt_effect Desired drop/paste effect. Could be
* 'move'|'copy' (default is copy). Ignored if conflicts with
* |clipboardData.effectAllowed|.
- * @return {string} Either "copy" or "move".
+ * @return {!Promise<string>} Either "copy" or "move".
*/
FileTransferController.prototype.paste = function(
clipboardData, opt_destinationEntry, opt_effect) {
var pastePlan =
this.preparePaste(clipboardData, opt_destinationEntry, opt_effect);
- return this.executePaste(pastePlan);
+ return pastePlan.shouldShowConfirmation().then(function(message) {
+ if (!message) {
+ return Promise.resolve(this.executePaste(pastePlan));
+ }
+ this.confirmationCallback_(
+ pastePlan.isMove ? FileManagerUI.ActionType.MOVE :
+ FileManagerUI.ActionType.COPY,
+ message.title, message.body)
+ .then(function(userApproved) {
+ if (userApproved) {
+ this.executePaste(pastePlan);
+ }
+ }.bind(this));
+ }.bind(this));
};
/**
@@ -1229,16 +1346,16 @@ FileTransferController.prototype.onPaste_ = function(event) {
return;
}
event.preventDefault();
- var effect = this.paste(assert(event.clipboardData), destination);
-
- // On cut, we clear the clipboard after the file is pasted/moved so we don't
- // try to move/delete the original file again.
- if (effect === 'move') {
- this.simulateCommand_('cut', function(event) {
- event.preventDefault();
- event.clipboardData.setData('fs/clear', '');
- });
- }
+ this.paste(assert(event.clipboardData), destination).then(function(effect) {
+ // On cut, we clear the clipboard after the file is pasted/moved so we don't
+ // try to move/delete the original file again.
+ if (effect === 'move') {
+ this.simulateCommand_('cut', function(event) {
+ event.preventDefault();
+ event.clipboardData.setData('fs/clear', '');
+ });
+ }
+ }.bind(this));
};
/**

Powered by Google App Engine
This is Rietveld 408576698