Add Zotero.File.rename() (extracted from Zotero.Item::renameAttachmentFile())
This commit is contained in:
parent
b5cc0f918a
commit
f5b1ee44f3
|
@ -2432,102 +2432,67 @@ Zotero.Item.prototype.fileExistsCached = function () {
|
||||||
* -2 - Error renaming
|
* -2 - Error renaming
|
||||||
* false - Attachment file not found
|
* false - Attachment file not found
|
||||||
*/
|
*/
|
||||||
Zotero.Item.prototype.renameAttachmentFile = Zotero.Promise.coroutine(function* (newName, overwrite=false, unique=false) {
|
Zotero.Item.prototype.renameAttachmentFile = async function (newName, overwrite = false, unique = false) {
|
||||||
var origPath = yield this.getFilePathAsync();
|
var origPath = await this.getFilePathAsync();
|
||||||
if (!origPath) {
|
if (!origPath) {
|
||||||
Zotero.debug("Attachment file not found in renameAttachmentFile()", 2);
|
Zotero.debug("Attachment file not found in renameAttachmentFile()", 2);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var origName = OS.Path.basename(origPath);
|
let origName = OS.Path.basename(origPath);
|
||||||
var origModDate = (yield OS.File.stat(origPath)).lastModificationDate;
|
if (this.isImportedAttachment()) {
|
||||||
|
var origModDate = (await OS.File.stat(origPath)).lastModificationDate;
|
||||||
|
}
|
||||||
|
|
||||||
newName = Zotero.File.getValidFileName(newName);
|
// No change
|
||||||
|
|
||||||
// Ignore if no change
|
|
||||||
if (origName === newName) {
|
if (origName === newName) {
|
||||||
Zotero.debug("Filename has not changed");
|
Zotero.debug("Filename has not changed");
|
||||||
return true;
|
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
|
// Update mod time and clear hash so the file syncs
|
||||||
// TODO: use an integer counter instead of mod time for change detection
|
// 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
|
// Update mod time first, because it may fail for read-only files on Windows
|
||||||
yield OS.File.setDates(origPath, null, null);
|
if (this.isImportedAttachment()) {
|
||||||
var result;
|
await OS.File.setDates(origPath, null, null);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
if (this.isImportedAttachment()) {
|
||||||
this.attachmentSyncedHash = null;
|
this.attachmentSyncedHash = null;
|
||||||
this.attachmentSyncState = "to_upload";
|
this.attachmentSyncState = "to_upload";
|
||||||
yield this.saveTx({ skipAll: true });
|
await this.saveTx({ skipAll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
|
||||||
// Restore original modification date in case we managed to change it
|
// Restore original modification date in case we managed to change it
|
||||||
try {
|
if (this.isImportedAttachment()) {
|
||||||
OS.File.setDates(origPath, null, origModDate);
|
try {
|
||||||
} catch (e) {
|
OS.File.setDates(origPath, null, origModDate);
|
||||||
Zotero.debug(e, 2);
|
} catch (e) {
|
||||||
|
Zotero.debug(e, 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Zotero.debug(e);
|
|
||||||
Components.utils.reportError(e);
|
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
* Delete a file if it exists, asynchronously
|
||||||
*
|
*
|
||||||
|
|
|
@ -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 () {
|
describe("#getClosestDirectory()", function () {
|
||||||
it("should return directory for file that exists", function* () {
|
it("should return directory for file that exists", function* () {
|
||||||
var tmpDir = yield getTempDirectory();
|
var tmpDir = yield getTempDirectory();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user