Skip to content

Commit

Permalink
Add Zotero.File.rename() (extracted from Zotero.Item::renameAttachmen…
Browse files Browse the repository at this point in the history
…tFile())
  • Loading branch information
dstillman committed Feb 27, 2018
1 parent b5cc0f9 commit f5b1ee4
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 66 deletions.
97 changes: 31 additions & 66 deletions chrome/content/zotero/xpcom/data/item.js
Expand Up @@ -2432,102 +2432,67 @@ Zotero.Item.prototype.fileExistsCached = function () {
* -2 - Error renaming
* false - Attachment file not found
*/
Zotero.Item.prototype.renameAttachmentFile = Zotero.Promise.coroutine(function* (newName, overwrite=false, unique=false) {
var origPath = yield this.getFilePathAsync();
Zotero.Item.prototype.renameAttachmentFile = async function (newName, overwrite = false, unique = false) {
var origPath = await this.getFilePathAsync();
if (!origPath) {
Zotero.debug("Attachment file not found in renameAttachmentFile()", 2);
return false;
}

try {
var origName = OS.Path.basename(origPath);
var origModDate = (yield OS.File.stat(origPath)).lastModificationDate;

newName = Zotero.File.getValidFileName(newName);
let origName = OS.Path.basename(origPath);
if (this.isImportedAttachment()) {
var origModDate = (await OS.File.stat(origPath)).lastModificationDate;
}

// Ignore if no change
// No change
if (origName === newName) {
Zotero.debug("Filename has not changed");
return true;
}

var parentDir = OS.Path.dirname(origPath);
var destPath = OS.Path.join(parentDir, newName);
var destName = OS.Path.basename(destPath);
// Get root + extension, if there is one
var pos = destName.lastIndexOf('.');
if (pos > 0) {
var root = destName.substr(0, pos);
var ext = destName.substr(pos + 1);
}
else {
var root = destName;
}

// Update mod time and clear hash so the file syncs
// TODO: use an integer counter instead of mod time for change detection
// Update mod time first, because it may fail for read-only files on Windows
yield OS.File.setDates(origPath, null, null);
var result;
var incr = 0;
while (true) {
// If filename already exists, add a numeric suffix to the end of the root, before
// the extension if there is one
if (incr) {
if (ext) {
destName = root + ' ' + (incr + 1) + '.' + ext;
}
else {
destName = root + ' ' + (incr + 1);
}
destPath = OS.Path.join(parentDir, destName);
}

try {
result = yield OS.File.move(origPath, destPath, { noOverwrite: !overwrite })
}
catch (e) {
if (e instanceof OS.File.Error) {
if (e.becauseExists) {
// Increment number to create unique suffix
if (unique) {
incr++;
continue;
}
// If no overwriting or making unique and file exists, return -1
return -1;
}
}
throw e;
}
break;
}
if (result) {
return result;
if (this.isImportedAttachment()) {
await OS.File.setDates(origPath, null, null);
}

yield this.relinkAttachmentFile(destPath);
newName = await Zotero.File.rename(
origPath,
newName,
{
overwrite,
unique
}
);
let destPath = OS.Path.join(OS.Path.dirname(origPath), newName);

await this.relinkAttachmentFile(destPath);

if (this.isImportedAttachment()) {
this.attachmentSyncedHash = null;
this.attachmentSyncState = "to_upload";
yield this.saveTx({ skipAll: true });
await this.saveTx({ skipAll: true });
}

return true;
}
catch (e) {
Zotero.logError(e);

// Restore original modification date in case we managed to change it
try {
OS.File.setDates(origPath, null, origModDate);
} catch (e) {
Zotero.debug(e, 2);
if (this.isImportedAttachment()) {
try {
OS.File.setDates(origPath, null, origModDate);
} catch (e) {
Zotero.debug(e, 2);
}
}
Zotero.debug(e);
Components.utils.reportError(e);

return -2;
}
});
};


/**
Expand Down
76 changes: 76 additions & 0 deletions chrome/content/zotero/xpcom/file.js
Expand Up @@ -427,6 +427,82 @@ Zotero.File = new function(){
});


/**
* Rename file within its parent directory
*
* @param {String} file - File path
* @param {String} newName
* @param {Object} [options]
* @param {Boolean} [options.overwrite=false] - Overwrite file if one exists
* @param {Boolean} [options.unique=false] - Add suffix to create unique filename if necessary
* @return {String|false} - New filename, or false if destination file exists and `overwrite` not set
*/
this.rename = async function (file, newName, options = {}) {
var overwrite = options.overwrite || false;
var unique = options.unique || false;

var origPath = file;
var origName = OS.Path.basename(origPath);
newName = Zotero.File.getValidFileName(newName);

// Ignore if no change
if (origName === newName) {
Zotero.debug("Filename has not changed");
return origName;
}

var parentDir = OS.Path.dirname(origPath);
var destPath = OS.Path.join(parentDir, newName);
var destName = OS.Path.basename(destPath);
// Get root + extension, if there is one
var pos = destName.lastIndexOf('.');
if (pos > 0) {
var root = destName.substr(0, pos);
var ext = destName.substr(pos + 1);
}
else {
var root = destName;
}

var incr = 0;
while (true) {
// If filename already exists, add a numeric suffix to the end of the root, before
// the extension if there is one
if (incr) {
if (ext) {
destName = root + ' ' + (incr + 1) + '.' + ext;
}
else {
destName = root + ' ' + (incr + 1);
}
destPath = OS.Path.join(parentDir, destName);
}

try {
Zotero.debug(`Renaming ${origPath} to ${OS.Path.basename(destPath)}`);
Zotero.debug(destPath);
await OS.File.move(origPath, destPath, { noOverwrite: !overwrite })
}
catch (e) {
if (e instanceof OS.File.Error) {
if (e.becauseExists) {
// Increment number to create unique suffix
if (unique) {
incr++;
continue;
}
// No overwriting or making unique and file exists
return false;
}
}
throw e;
}
break;
}
return destName;
};


/**
* Delete a file if it exists, asynchronously
*
Expand Down
36 changes: 36 additions & 0 deletions test/tests/fileTest.js
Expand Up @@ -97,6 +97,42 @@ describe("Zotero.File", function () {
});


describe("#rename()", function () {
it("should rename a file", async function () {
var tmpDir = await getTempDirectory();
var sourceFile = OS.Path.join(tmpDir, 'a');
var destFile = OS.Path.join(tmpDir, 'b');
await Zotero.File.putContentsAsync(sourceFile, '');
await Zotero.File.rename(sourceFile, 'b');
assert.isTrue(await OS.File.exists(destFile));
});

it("should overwrite an existing file if `overwrite` is true", async function () {
var tmpDir = await getTempDirectory();
var sourceFile = OS.Path.join(tmpDir, 'a');
var destFile = OS.Path.join(tmpDir, 'b');
await Zotero.File.putContentsAsync(sourceFile, 'a');
await Zotero.File.putContentsAsync(destFile, 'b');
await Zotero.File.rename(sourceFile, 'b', { overwrite: true });
assert.isTrue(await OS.File.exists(destFile));
assert.equal(await Zotero.File.getContentsAsync(destFile), 'a');
});

it("should get a unique name if target file exists and `unique` is true", async function () {
var tmpDir = await getTempDirectory();
var sourceFile = OS.Path.join(tmpDir, 'a');
var destFile = OS.Path.join(tmpDir, 'b');
await Zotero.File.putContentsAsync(sourceFile, 'a');
await Zotero.File.putContentsAsync(destFile, 'b');
var newFilename = await Zotero.File.rename(sourceFile, 'b', { unique: true });
var realDestFile = OS.Path.join(tmpDir, newFilename);
assert.equal(newFilename, 'b 2');
assert.isTrue(await OS.File.exists(realDestFile));
assert.equal(await Zotero.File.getContentsAsync(realDestFile), 'a');
});
});


describe("#getClosestDirectory()", function () {
it("should return directory for file that exists", function* () {
var tmpDir = yield getTempDirectory();
Expand Down

0 comments on commit f5b1ee4

Please sign in to comment.