Update linked attachment base directory code

- Replace nsIFile and persistent descriptors with OS.File and string paths
- Add tests for base dir settings
This commit is contained in:
Dan Stillman 2015-09-29 04:05:13 -04:00
parent 9e356a7e63
commit 88627adcdb
3 changed files with 217 additions and 78 deletions

View File

@ -323,69 +323,77 @@ Zotero_Preferences.Advanced = {
Zotero_Preferences.Attachment_Base_Directory = { Zotero_Preferences.Attachment_Base_Directory = {
choosePath: function () { getPath: function () {
// Get existing base directory var oldPath = Zotero.Prefs.get('baseAttachmentPath');
var oldBasePath = Zotero.Prefs.get('baseAttachmentPath'); if (oldPath) {
if (oldBasePath) {
var oldBasePathFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try { try {
oldBasePathFile.persistentDescriptor = oldBasePath; return OS.Path.normalize(oldPath);
} }
catch (e) { catch (e) {
Zotero.debug(e, 1); Zotero.logError(e);
Components.utils.reportError(e); return false;
oldBasePathFile = null;
} }
} }
},
choosePath: Zotero.Promise.coroutine(function* () {
var oldPath = this.getPath();
//Prompt user to choose new base path //Prompt user to choose new base path
if (oldPath) {
var oldPathFile = Zotero.File.pathToFile(oldPath);
}
var nsIFilePicker = Components.interfaces.nsIFilePicker; var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"] var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker); .createInstance(nsIFilePicker);
if (oldBasePathFile) { if (oldPathFile) {
fp.displayDirectory = oldBasePathFile; fp.displayDirectory = oldPathFile;
} }
fp.init(window, Zotero.getString('attachmentBasePath.selectDir'), nsIFilePicker.modeGetFolder); fp.init(window, Zotero.getString('attachmentBasePath.selectDir'), nsIFilePicker.modeGetFolder);
fp.appendFilters(nsIFilePicker.filterAll); fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() != nsIFilePicker.returnOK) { if (fp.show() != nsIFilePicker.returnOK) {
return false; return false;
} }
var newBasePathFile = fp.file; var newPath = OS.Path.normalize(fp.file.path);
if (oldBasePathFile && oldBasePathFile.equals(newBasePathFile)) { if (oldPath && oldPath == newPath) {
Zotero.debug("Base directory hasn't changed"); Zotero.debug("Base directory hasn't changed");
return false; return false;
} }
return changePath(newPath);
}),
changePath: Zotero.Promise.coroutine(function* (basePath) {
// Find all current attachments with relative attachment paths // Find all current attachments with relative attachment paths
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE '" var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE '"
+ Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%'"; + Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%'";
var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE]; var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE];
var oldRelativeAttachmentIDs = Zotero.DB.columnQuery(sql, params) || []; var oldRelativeAttachmentIDs = yield Zotero.DB.columnQueryAsync(sql, params);
//Find all attachments on the new base path //Find all attachments on the new base path
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=?"; var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=?";
var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE]; var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE];
var allAttachments = Zotero.DB.columnQuery(sql,params); var allAttachments = yield Zotero.DB.columnQueryAsync(sql, params);
var newAttachmentPaths = {}; var newAttachmentPaths = {};
var numNewAttachments = 0; var numNewAttachments = 0;
var numOldAttachments = 0; var numOldAttachments = 0;
var attachmentFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
for (let i=0; i<allAttachments.length; i++) { for (let i=0; i<allAttachments.length; i++) {
let attachmentID = allAttachments[i]; let attachmentID = allAttachments[i];
let attachmentPath;
let relPath = false let relPath = false
try { try {
let attachment = Zotero.Items.get(attachmentID); let attachment = yield Zotero.Items.getAsync(attachmentID);
// This will return FALSE for relative paths if base directory // This will return FALSE for relative paths if base directory
// isn't currently set // isn't currently set
attachmentFile = attachment.getFile(false, true); attachmentPath = attachment.getFilePath();
// Get existing relative path // Get existing relative path
let path = attachment.attachmentPath; let storedPath = attachment.attachmentPath;
if (path.indexOf(Zotero.Attachments.BASE_PATH_PLACEHOLDER) == 0) { if (storedPath.startsWith(Zotero.Attachments.BASE_PATH_PLACEHOLDER)) {
relPath = path.substr(Zotero.Attachments.BASE_PATH_PLACEHOLDER.length); relPath = storedPath.substr(Zotero.Attachments.BASE_PATH_PLACEHOLDER.length);
} }
} }
catch (e) { catch (e) {
@ -397,10 +405,7 @@ Zotero_Preferences.Attachment_Base_Directory = {
// If a file with the same relative path exists within the new base directory, // If a file with the same relative path exists within the new base directory,
// don't touch the attachment, since it will continue to work // don't touch the attachment, since it will continue to work
if (relPath) { if (relPath) {
let relFile = Components.classes["@mozilla.org/file/local;1"] if (yield OS.File.exists(OS.Path.join(basePath, relPath))) {
.createInstance(Components.interfaces.nsILocalFile);
relFile.setRelativeDescriptor(newBasePathFile, relPath);
if (relFile.exists()) {
numNewAttachments++; numNewAttachments++;
continue; continue;
} }
@ -409,15 +414,14 @@ Zotero_Preferences.Attachment_Base_Directory = {
// Files within the new base directory need to be updated to use // Files within the new base directory need to be updated to use
// relative paths (or, if the new base directory is an ancestor or // relative paths (or, if the new base directory is an ancestor or
// descendant of the old one, new relative paths) // descendant of the old one, new relative paths)
if (attachmentFile && Zotero.File.directoryContains(newBasePathFile, attachmentFile)) { if (attachmentPath && Zotero.File.directoryContains(basePath, attachmentPath)) {
newAttachmentPaths[attachmentID] = relPath newAttachmentPaths[attachmentID] = relPath ? attachmentPath : null;
? attachmentFile.persistentDescriptor : null;
numNewAttachments++; numNewAttachments++;
} }
// Existing relative attachments not within the new base directory // Existing relative attachments not within the new base directory
// will be converted to absolute paths // will be converted to absolute paths
else if (relPath && oldBasePathFile) { else if (relPath && this.getPath()) {
newAttachmentPaths[attachmentID] = attachmentFile.persistentDescriptor; newAttachmentPaths[attachmentID] = attachmentPath;
numOldAttachments++; numOldAttachments++;
} }
} }
@ -475,50 +479,41 @@ Zotero_Preferences.Attachment_Base_Directory = {
// Set new data directory // Set new data directory
Zotero.debug("Setting new base directory"); Zotero.debug("Setting new base directory");
Zotero.Prefs.set('baseAttachmentPath', newBasePathFile.persistentDescriptor); Zotero.Prefs.set('baseAttachmentPath', basePath);
Zotero.Prefs.set('saveRelativeAttachmentPath', true); Zotero.Prefs.set('saveRelativeAttachmentPath', true);
// Resave all attachments on base path (so that their paths become relative) // Resave all attachments on base path (so that their paths become relative)
// and all other relative attachments (so that their paths become absolute) // and all other relative attachments (so that their paths become absolute)
for (let id in newAttachmentPaths) { yield Zotero.Utilities.Internal.forEachChunkAsync(
let attachment = Zotero.Items.get(id); Object.keys(newAttachmentPaths),
if (newAttachmentPaths[id]) { 100,
attachment.attachmentPath = newAttachmentPaths[id]; function (chunk) {
attachment.save({ return Zotero.DB.executeTransaction(function* () {
skipDateModifiedUpdate: true for (let id of chunk) {
}); let attachment = Zotero.Items.get(id);
if (newAttachmentPaths[id]) {
attachment.attachmentPath = newAttachmentPaths[id];
}
else {
attachment.attachmentPath = attachment.getFilePath();
}
yield attachment.save({
skipDateModifiedUpdate: true
});
}
})
} }
else { );
attachment.updateAttachmentPath();
}
}
return newBasePathFile.persistentDescriptor;
},
getPath: function (asFile) {
var desc = Zotero.Prefs.get('baseAttachmentPath');
if (desc == '') {
return asFile ? null : '';
}
var file = Components.classes["@mozilla.org/file/local;1"] return true;
.createInstance(Components.interfaces.nsILocalFile); }),
try {
file.persistentDescriptor = desc;
}
catch (e) {
return asFile ? null : '';
}
return asFile ? file : file.path;
},
clearPath: function () { clearPath: Zotero.Promise.coroutine(function* () {
// Find all current attachments with relative paths // Find all current attachments with relative paths
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE '" var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE '"
+ Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%'"; + Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%'";
var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE]; var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE];
var relativeAttachmentIDs = Zotero.DB.columnQuery(sql, params) || []; var relativeAttachmentIDs = yield Zotero.DB.columnQueryAsync(sql, params);
// Prompt for confirmation // Prompt for confirmation
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
@ -562,26 +557,40 @@ Zotero_Preferences.Attachment_Base_Directory = {
// attachments so that their absolute paths are stored // attachments so that their absolute paths are stored
Zotero.debug('Clearing base directory'); Zotero.debug('Clearing base directory');
Zotero.Prefs.set('saveRelativeAttachmentPath', false); Zotero.Prefs.set('saveRelativeAttachmentPath', false);
for (var i=0; i<relativeAttachmentIDs.length; i++) {
Zotero.Items.get(relativeAttachmentIDs[i]).updateAttachmentPath(); yield Zotero.Utilities.Internal.forEachChunkAsync(
} relativeAttachmentIDs,
100,
function (chunk) {
return Zotero.DB.executeTransaction(function* () {
for (let id of chunk) {
let attachment = yield Zotero.Items.getAsync(id);
attachment.attachmentPath = attachment.getFilePath();
yield attachment.save({
skipDateModifiedUpdate: true
});
}
}.bind(this));
}.bind(this)
);
Zotero.Prefs.set('baseAttachmentPath', ''); Zotero.Prefs.set('baseAttachmentPath', '');
}, }),
updateUI: function () { updateUI: Zotero.Promise.coroutine(function* () {
var filefield = document.getElementById('baseAttachmentPath'); var filefield = document.getElementById('baseAttachmentPath');
var file = this.getPath(true); var path = Zotero.Prefs.get('baseAttachmentPath');
filefield.file = file; Components.utils.import("resource://gre/modules/osfile.jsm");
if (file) { if (yield OS.File.exists(path)) {
filefield.label = file.path; filefield.file = Zotero.File.pathToFile(path);
filefield.label = path;
} }
else { else {
filefield.label = ''; filefield.label = '';
} }
document.getElementById('resetBasePath').disabled = !path;
document.getElementById('resetBasePath').disabled = !Zotero.Prefs.get('baseAttachmentPath'); })
}
}; };

View File

@ -154,7 +154,8 @@
readonly="true" readonly="true"
flex="1" flex="1"
tabindex="-1"/> tabindex="-1"/>
<button label="&zotero.preferences.attachmentBaseDir.selectBasePath;" <button id="baseAttachmentPathButton"
label="&zotero.preferences.attachmentBaseDir.selectBasePath;"
oncommand="Zotero_Preferences.Attachment_Base_Directory.choosePath()"/> oncommand="Zotero_Preferences.Attachment_Base_Directory.choosePath()"/>
</hbox> </hbox>

View File

@ -0,0 +1,129 @@
describe("Advanced Preferences", function () {
describe("Files & Folders", function () {
describe("Linked Attachment Base Directory", function () {
var setBaseDirectory = Zotero.Promise.coroutine(function* (basePath) {
var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", {
pane: 'zotero-prefpane-advanced',
tabIndex: 1
});
// Wait for tab to load
var doc = win.document;
var prefwindow = doc.documentElement;
var defer = Zotero.Promise.defer();
var pane = doc.getElementById('zotero-prefpane-advanced');
if (!pane.loaded) {
pane.addEventListener('paneload', function () {
defer.resolve();
})
yield defer.promise;
}
var promise = waitForDialog();
yield win.Zotero_Preferences.Attachment_Base_Directory.changePath(basePath);
yield promise;
win.close();
});
var clearBaseDirectory = Zotero.Promise.coroutine(function* (basePath) {
var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", {
pane: 'zotero-prefpane-advanced',
tabIndex: 1
});
// Wait for tab to load
var doc = win.document;
var prefwindow = doc.documentElement;
var defer = Zotero.Promise.defer();
var pane = doc.getElementById('zotero-prefpane-advanced');
if (!pane.loaded) {
pane.addEventListener('paneload', function () {
defer.resolve();
})
yield defer.promise;
}
var promise = waitForDialog();
yield win.Zotero_Preferences.Attachment_Base_Directory.clearPath();
yield promise;
win.close();
});
beforeEach(function () {
Zotero.Prefs.clear('baseAttachmentPath');
Zotero.Prefs.clear('saveRelativeAttachmentPath');
});
it("should set new base directory", function* () {
var basePath = getTestDataDirectory().path;
yield setBaseDirectory(basePath);
assert.equal(Zotero.Prefs.get('baseAttachmentPath'), basePath);
assert.isTrue(Zotero.Prefs.get('saveRelativeAttachmentPath'));
})
it("should clear base directory", function* () {
var basePath = getTestDataDirectory().path;
yield setBaseDirectory(basePath);
yield clearBaseDirectory();
assert.equal(Zotero.Prefs.get('baseAttachmentPath'), '');
assert.isFalse(Zotero.Prefs.get('saveRelativeAttachmentPath'));
})
it("should change absolute path of linked attachment under new base dir to prefixed path", function* () {
var file = getTestDataDirectory();
file.append('test.png');
var attachment = yield Zotero.Attachments.linkFromFile({ file });
assert.equal(attachment.attachmentPath, file.path);
var basePath = getTestDataDirectory().path;
yield setBaseDirectory(basePath);
assert.equal(
attachment.attachmentPath,
Zotero.Attachments.BASE_PATH_PLACEHOLDER + 'test.png'
);
})
it("should change prefixed path to absolute when changing base directory", function* () {
var basePath = getTestDataDirectory().path;
yield setBaseDirectory(basePath);
var file = getTestDataDirectory();
file.append('test.png');
var attachment = yield Zotero.Attachments.linkFromFile({ file });
assert.equal(
attachment.attachmentPath,
Zotero.Attachments.BASE_PATH_PLACEHOLDER + 'test.png'
);
var basePath = Zotero.getTempDirectory().path;
yield setBaseDirectory(basePath);
assert.equal(attachment.attachmentPath, file.path);
})
it("should change prefixed path to absolute when clearing base directory", function* () {
var basePath = getTestDataDirectory().path;
yield setBaseDirectory(basePath);
var file = getTestDataDirectory();
file.append('test.png');
var attachment = yield Zotero.Attachments.linkFromFile({ file });
assert.equal(
attachment.attachmentPath,
Zotero.Attachments.BASE_PATH_PLACEHOLDER + 'test.png'
);
yield clearBaseDirectory();
assert.equal(Zotero.Prefs.get('baseAttachmentPath'), '');
assert.isFalse(Zotero.Prefs.get('saveRelativeAttachmentPath'));
assert.equal(attachment.attachmentPath, file.path);
})
})
})
})