diff --git a/locales/en-US/editor.properties b/locales/en-US/editor.properties
index 4572045bb62..debf22083fd 100644
--- a/locales/en-US/editor.properties
+++ b/locales/en-US/editor.properties
@@ -21,6 +21,7 @@ DIRECTORY_NAME=Directory Name
# File Open/Save Error strings
+FILE_EXISTS_HEADER=The file already exists.
# The {0} here will be replaced by an actual error message
OPEN_DIALOG_ERROR=An error occurred when showing the open file dialog. (error {0})
# {0} will be replaced with a filename and {1} will be replaced by an error message
@@ -94,6 +95,8 @@ SAVE_AND_OVERWRITE=Overwrite
DELETE=Delete
BUTTON_YES=Yes
BUTTON_NO=No
+USE_IMPORTED=Use Imported
+KEEP_EXISTING=Keep Existing
# Quick Edit
@@ -136,6 +139,7 @@ DND_SUCCESS_UNTAR_TITLE=Untar Completed Successfully
DND_SUCCESS_UNZIP=Successfully unzipped {0}.
# {0} will be replaced by a tar filename
DND_SUCCESS_UNTAR=Successfully untarred {0}.
+DND_FILE_REPLACE=A file named \"{0}\" already exists in this location. Do you want to use the imported file or keep the existing?
# Image Viewer
IMAGE_DIMENSIONS={0} (width) × {1} (height) pixels
diff --git a/src/filesystem/impls/filer/ArchiveUtils.js b/src/filesystem/impls/filer/ArchiveUtils.js
index a28abd563d6..3153a2e4ffe 100644
--- a/src/filesystem/impls/filer/ArchiveUtils.js
+++ b/src/filesystem/impls/filer/ArchiveUtils.js
@@ -16,6 +16,15 @@ define(function (require, exports, module) {
var Buffer = Filer.Buffer;
var Path = Filer.Path;
var fs = Filer.fs();
+ var Dialogs = require("widgets/Dialogs");
+ var DefaultDialogs = require("widgets/DefaultDialogs");
+ var Strings = require("strings");
+ var StringUtils = require("utils/StringUtils");
+
+ // These are const variables
+ var CANCEL_OPERATION = -1,
+ OVERWRITE_OPERATION = 1,
+ KEEP_EXISTING_OPERATION = 2;
// Mac and Windows clutter zip files with extra files/folders we don't need
function skipFile(filename) {
@@ -57,6 +66,49 @@ define(function (require, exports, module) {
});
}
+ function showOverwriteWarning(path, callback) {
+ var filepath = stripRoot(path);
+ Dialogs.showModalDialog(
+ DefaultDialogs.DIALOG_ID_INFO,
+ Strings.FILE_EXISTS_HEADER,
+ StringUtils.format(Strings.DND_FILE_REPLACE, filepath),
+ [
+ {
+ className : Dialogs.DIALOG_BTN_CLASS_NORMAL,
+ id : Dialogs.DIALOG_BTN_CANCEL,
+ text : Strings.CANCEL
+ },
+ {
+ className : Dialogs.DIALOG_BTN_CLASS_NORMAL,
+ id : Dialogs.DIALOG_BTN_IMPORT,
+ text : Strings.USE_IMPORTED
+ },
+ {
+ className : Dialogs.DIALOG_BTN_CLASS_PRIMARY,
+ id : Dialogs.DIALOG_BTN_OK,
+ text : Strings.KEEP_EXISTING
+ }
+ ]
+ ).getPromise().then(function (id) {
+ var result;
+ if (id === Dialogs.DIALOG_BTN_IMPORT) {
+ result = OVERWRITE_OPERATION;
+ } else if (id === Dialogs.DIALOG_BTN_OK) {
+ result = KEEP_EXISTING_OPERATION;
+ } else if (id === Dialogs.DIALOG_BTN_CANCEL) {
+ result = CANCEL_OPERATION;
+ }
+ callback(null, result);
+ }, callback);
+ }
+
+ function stripRoot(path) {
+ var root = StartupState.project("root");
+ var rootRegex = new RegExp("^" + root + "\/?");
+
+ return path.replace(rootRegex, "");
+ }
+
// zipfile can be a path (string) to a zipfile, or raw binary data.
function unzip(zipfile, options, callback) {
if(typeof options === 'function') {
@@ -100,21 +152,30 @@ define(function (require, exports, module) {
} else {
// XXX: some zip files don't seem to be structured such that dirs
// get created before files. Create base dir if not there yet.
- fs.stat(basedir, function(err, stats) {
- if(err) {
- if(err.code !== "ENOENT") {
- return callback(err);
- }
-
- fs.mkdirp(basedir, function(err) {
- if(err) {
- return callback(err);
- }
- fs.writeFile(path.absPath, path.data, callback);
- });
- } else {
- fs.writeFile(path.absPath, path.data, callback);
+ fs.mkdirp(basedir, function (err) {
+ if (err) {
+ return callback(err);
}
+ fs.stat(path.absPath, function(err, stats) {
+ if(err && err.code !== "ENOENT") {
+ return callback(err);
+ }
+ if (stats.type === "FILE") {
+ showOverwriteWarning(path.absPath, function (err, result) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (result === OVERWRITE_OPERATION) {
+ fs.writeFile(path.absPath, path.data, callback);
+ } else if (result === KEEP_EXISTING_OPERATION) {
+ callback();
+ } else if (result === CANCEL_OPERATION) {
+ callback(new Error("Operation Cancelled"));
+ }
+ });
+ }
+ });
});
}
}
@@ -227,11 +288,33 @@ define(function (require, exports, module) {
}
fs.mkdirp(basedir, function(err) {
- if(err && err.code !== "EEXIST") {
+ if(err) {
return callback(err);
}
- fs.writeFile(path, new Buffer(data), {encoding: null}, callback);
+ fs.stat(path, function (err, stats) {
+ if (err && err.code !== "ENOENT") {
+ return callback(err);
+ }
+
+ if (stats.type !== "FILE") {
+ return callback();
+ }
+
+ showOverwriteWarning(path, function (err, result) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (result === OVERWRITE_OPERATION) {
+ fs.writeFile(path, new Buffer(data), {encoding: null}, callback);
+ } else if (result === KEEP_EXISTING_OPERATION) {
+ return callback();
+ } else if (result === CANCEL_OPERATION) {
+ callback(new Error("Operation Cancelled"));
+ }
+ });
+ });
});
}
@@ -244,6 +327,10 @@ define(function (require, exports, module) {
function writeCallback(err) {
if(err) {
+ if (err.message === "Operation Cancelled") {
+ finish(err);
+ return;
+ }
console.error("[Bramble untar] couldn't extract file", err);
}
diff --git a/src/filesystem/impls/filer/lib/WebKitFileImport.js b/src/filesystem/impls/filer/lib/WebKitFileImport.js
index 342aa52a0ea..8c7b738fa1a 100644
--- a/src/filesystem/impls/filer/lib/WebKitFileImport.js
+++ b/src/filesystem/impls/filer/lib/WebKitFileImport.js
@@ -38,6 +38,7 @@ define(function (require, exports, module) {
StringUtils = require("utils/StringUtils"),
Filer = require("filesystem/impls/filer/BracketsFiler"),
Path = Filer.Path,
+ fs = Filer.fs(),
Content = require("filesystem/impls/filer/lib/content"),
LanguageManager = require("language/LanguageManager"),
StartupState = require("bramble/StartupState"),
@@ -134,13 +135,13 @@ define(function (require, exports, module) {
});
}
- function handleRegularFile(deferred, file, filename, buffer, encoding) {
+ function saveFile(deferred, file, filename, buffer, encoding) {
file.write(buffer, {encoding: encoding}, function(err) {
if (err) {
onError(deferred, filename, err);
return;
}
-
+
// See if this file is worth trying to open in the editor or not
if(shouldOpenFile(filename, encoding)) {
pathList.push(filename);
@@ -150,11 +151,55 @@ define(function (require, exports, module) {
});
}
+ function handleRegularFile(deferred, file, filename, buffer, encoding) {
+ fs.exists(filename, function(doesExist) {
+ if (doesExist) {
+ console.log("File: ", filename, " already exists!");
+
+ // File exists. Prompt user for action
+ Dialogs.showModalDialog(
+ DefaultDialogs.DIALOG_ID_INFO,
+ Strings.FILE_EXISTS_HEADER,
+ StringUtils.format(Strings.DND_FILE_REPLACE, FileUtils.getBaseName(filename)),
+ [
+ {
+ className : Dialogs.DIALOG_BTN_CLASS_NORMAL,
+ id : Dialogs.DIALOG_BTN_CANCEL,
+ text : Strings.CANCEL
+ },
+ {
+ className : Dialogs.DIALOG_BTN_CLASS_NORMAL,
+ id : Dialogs.DIALOG_BTN_IMPORT,
+ text : Strings.USE_IMPORTED
+ },
+ {
+ className : Dialogs.DIALOG_BTN_CLASS_PRIMARY,
+ id : Dialogs.DIALOG_BTN_OK,
+ text : Strings.KEEP_EXISTING
+ }
+ ]
+ )
+ .done(function(id) {
+ if (id === Dialogs.DIALOG_BTN_IMPORT) {
+ // Override file per user's request
+ saveFile(deferred, file, filename, buffer, encoding);
+ }
+ });
+ } else {
+ // File doesn't exist. Save without prompt
+ saveFile(deferred, file, filename, buffer, encoding);
+ }
+ });
+ }
+
function handleZipFile(deferred, file, filename, buffer, encoding) {
var basename = Path.basename(filename);
ArchiveUtils.unzip(buffer, { root: parentPath }, function(err) {
if (err) {
+ if (err.message === "Operation Cancelled") {
+ return deferred.resolve();
+ }
onError(deferred, filename, new Error(Strings.DND_ERROR_UNZIP));
return;
}
@@ -168,6 +213,9 @@ define(function (require, exports, module) {
ArchiveUtils.untar(buffer, { root: parentPath }, function(err) {
if (err) {
+ if (err.message === "Operation Cancelled") {
+ return deferred.resolve();
+ }
onError(deferred, filename, new Error(Strings.DND_ERROR_UNTAR));
return;
}
diff --git a/src/utils/DragAndDrop.js b/src/utils/DragAndDrop.js
index ed33a5d1da6..7bc5ed23dcd 100644
--- a/src/utils/DragAndDrop.js
+++ b/src/utils/DragAndDrop.js
@@ -140,7 +140,6 @@ define(function (require, exports, module) {
return Async.doInParallel(paths, function (path, idx) {
var result = new $.Deferred();
-
// Only open files.
FileSystem.resolve(path, function (err, item) {
if (!err && item.isFile) {
@@ -182,6 +181,7 @@ define(function (require, exports, module) {
return result.promise();
}, false)
.fail(function () {
+ console.log("fail");
function errorToString(err) {
if (err === ERR_MULTIPLE_ITEMS_WITH_DIR) {
return Strings.ERROR_MIXED_DRAGDROP;
diff --git a/src/widgets/Dialogs.js b/src/widgets/Dialogs.js
index ba4ad93fc8f..bcbc851c0df 100644
--- a/src/widgets/Dialogs.js
+++ b/src/widgets/Dialogs.js
@@ -43,6 +43,7 @@ define(function (require, exports, module) {
DIALOG_BTN_OK = "ok",
DIALOG_BTN_DONTSAVE = "dontsave",
DIALOG_BTN_SAVE_AS = "save_as",
+ DIALOG_BTN_IMPORT = "import",
DIALOG_CANCELED = "_canceled",
DIALOG_BTN_DOWNLOAD = "download";
@@ -443,6 +444,7 @@ define(function (require, exports, module) {
window.addEventListener("resize", setDialogMaxSize);
exports.DIALOG_BTN_CANCEL = DIALOG_BTN_CANCEL;
+ exports.DIALOG_BTN_IMPORT = DIALOG_BTN_IMPORT;
exports.DIALOG_BTN_OK = DIALOG_BTN_OK;
exports.DIALOG_BTN_DONTSAVE = DIALOG_BTN_DONTSAVE;
exports.DIALOG_BTN_SAVE_AS = DIALOG_BTN_SAVE_AS;