Merge branch 'relative_paths' into 3.1

Closes #51
This commit is contained in:
Dan Stillman 2013-02-21 06:25:21 -05:00
commit ab1d7a0f25
10 changed files with 592 additions and 110 deletions

View File

@ -2017,4 +2017,257 @@ function openInViewer(uri, newTab) {
var win = ww.openWindow(null, uri, null, features + ",width=775,height=575", null);
}
}
}
Zotero_Preferences.Attachment_Base_Directory = {
choosePath: function () {
// Get existing base directory
var oldBasePath = Zotero.Prefs.get('baseAttachmentPath');
if (oldBasePath) {
var oldBasePathFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try {
oldBasePathFile.persistentDescriptor = oldBasePath;
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
oldBasePathFile = null;
}
}
//Prompt user to choose new base path
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
if (oldBasePathFile) {
fp.displayDirectory = oldBasePathFile;
}
fp.init(window, Zotero.getString('attachmentBasePath.selectDir'), nsIFilePicker.modeGetFolder);
fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() != nsIFilePicker.returnOK) {
return false;
}
var newBasePathFile = fp.file;
if (oldBasePathFile && oldBasePathFile.equals(newBasePathFile)) {
Zotero.debug("Base directory hasn't changed");
return false;
}
// Find all current attachments with relative attachment paths
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE '"
+ Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%'";
var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE];
var oldRelativeAttachmentIDs = Zotero.DB.columnQuery(sql, params) || [];
//Find all attachments on the new base path
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=?";
var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE];
var allAttachments = Zotero.DB.columnQuery(sql,params);
var newAttachmentPaths = {};
var numNewAttachments = 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++) {
let attachmentID = allAttachments[i];
try {
let attachment = Zotero.Items.get(attachmentID);
attachmentFile.persistentDescriptor = attachment.attachmentPath;
}
catch (e) {
// Don't deal with bad attachment paths. Just skip them.
continue;
}
// 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
let isExistingRelativeAttachment = oldRelativeAttachmentIDs.indexOf(attachmentID) != -1;
if (isExistingRelativeAttachment && oldBasePathFile) {
let relFile = attachmentFile.clone();
let relPath = attachmentFile.getRelativeDescriptor(oldBasePathFile);
relFile.setRelativeDescriptor(newBasePathFile, relPath);
if (relFile.exists()) {
numNewAttachments++;
continue;
}
}
// Files within the new base directory need to be updated to use
// relative paths (or, if the new base directory is an ancestor or
// descendant of the old one, new relative paths)
if (Zotero.File.directoryContains(newBasePathFile, attachmentFile)) {
newAttachmentPaths[attachmentID] = isExistingRelativeAttachment
? attachmentFile.persistentDescriptor : null;
numNewAttachments++;
}
// Existing relative attachments not within the new base directory
// will be converted to absolute paths
else if (isExistingRelativeAttachment && oldBasePathFile) {
newAttachmentPaths[attachmentID] = attachmentFile.persistentDescriptor;
numOldAttachments++;
}
}
//Confirm change of the base path
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var chooseStrPrefix = 'attachmentBasePath.chooseNewPath.';
var clearStrPrefix = 'attachmentBasePath.clearBasePath.';
var title = Zotero.getString(chooseStrPrefix + 'title');
var msg1 = Zotero.getString(chooseStrPrefix + 'message') + "\n\n", msg2 = msg3 = "";
switch (numNewAttachments) {
case 0:
break;
case 1:
msg2 += Zotero.getString(chooseStrPrefix + 'existingAttachments.singular') + " ";
break;
default:
msg2 += Zotero.getString(chooseStrPrefix + 'existingAttachments.plural', numNewAttachments) + " ";
}
switch (numOldAttachments) {
case 0:
break;
case 1:
msg3 += Zotero.getString(clearStrPrefix + 'existingAttachments.singular');
break;
default:
msg3 += Zotero.getString(clearStrPrefix + 'existingAttachments.plural', numOldAttachments);
}
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(
null,
title,
(msg1 + msg2 + msg3).trim(),
buttonFlags,
Zotero.getString(chooseStrPrefix + 'button'),
null,
null,
null,
{}
);
if (index == 1) {
return false;
}
// Set new data directory
Zotero.debug("Setting new base directory");
Zotero.Prefs.set('baseAttachmentPath', newBasePathFile.persistentDescriptor);
Zotero.Prefs.set('saveRelativeAttachmentPath', true);
// Resave all attachments on base path (so that their paths become relative)
// and all other relative attachments (so that their paths become absolute)
for (let id in newAttachmentPaths) {
let attachment = Zotero.Items.get(id);
if (newAttachmentPaths[id]) {
attachment.attachmentPath = newAttachmentPaths[id];
attachment.save();
}
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"]
.createInstance(Components.interfaces.nsILocalFile);
try {
file.persistentDescriptor = desc;
}
catch (e) {
return asFile ? null : '';
}
return asFile ? file : file.path;
},
clearPath: function () {
// Find all current attachments with relative paths
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE '"
+ Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%'";
var params = [Zotero.Attachments.LINK_MODE_LINKED_FILE];
var relativeAttachmentIDs = Zotero.DB.columnQuery(sql, params) || [];
// Prompt for confirmation
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var strPrefix = 'attachmentBasePath.clearBasePath.';
var title = Zotero.getString(strPrefix + 'title');
var msg = Zotero.getString(strPrefix + 'message');
switch (relativeAttachmentIDs.length) {
case 0:
break;
case 1:
msg += "\n\n" + Zotero.getString(strPrefix + 'existingAttachments.singular');
break;
default:
msg += "\n\n" + Zotero.getString(strPrefix + 'existingAttachments.plural',
relativeAttachmentIDs.length);
}
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(
window,
title,
msg,
buttonFlags,
Zotero.getString(strPrefix + 'button'),
null,
null,
null,
{}
);
if (index == 1) {
return false;
}
// Disable relative path saving and then resave all relative
// attachments so that their absolute paths are stored
Zotero.debug('Clearing base directory');
Zotero.Prefs.set('saveRelativeAttachmentPath', false);
for (var i=0; i<relativeAttachmentIDs.length; i++) {
Zotero.Items.get(relativeAttachmentIDs[i]).updateAttachmentPath();
}
Zotero.Prefs.set('baseAttachmentPath', '');
},
updateUI: function () {
var filefield = document.getElementById('baseAttachmentPath');
var file = this.getPath(true);
filefield.file = file;
if (file) {
filefield.label = file.path;
}
else {
filefield.label = '';
}
document.getElementById('resetBasePath').disabled = !Zotero.Prefs.get('baseAttachmentPath');
}
}

View File

@ -716,6 +716,7 @@ To add a new preference:
image="chrome://zotero/skin/prefs-advanced.png"
helpTopic="advanced">
<preferences>
<preference id="pref-baseAttachmentPath" name="extensions.zotero.baseAttachmentPath" type="string"/>
<preference id="pref-useDataDir" name="extensions.zotero.useDataDir" type="bool"/>
<preference id="pref-dataDir" name="extensions.zotero.dataDir" type="string"/>
<preference id="pref-export-displayCharsetOption" name="extensions.zotero.export.displayCharsetOption" type="bool"/>
@ -725,98 +726,139 @@ To add a new preference:
<preference id="pref-openURL-version" name="extensions.zotero.openURL.version" type="string"/>
</preferences>
<groupbox>
<caption label="&zotero.preferences.dataDir;"/>
<tabbox id="zotero-prefpane-advanced-tabs">
<tabs>
<!-- TODO: localize -->
<tab label="General"/>
<tab label="Files and Folders"/>
</tabs>
<radiogroup id="dataDir" preference="pref-useDataDir" onsyncfrompreference="onDataDirLoad();" onsynctopreference="return onDataDirUpdate(event);">
<radio label="&zotero.preferences.dataDir.useProfile;" value="false"/>
<hbox>
<radio label="&zotero.preferences.dataDir.custom;" value="true"/>
<textbox id="dataDirPath" preference="pref-dataDir" onsyncfrompreference="return getDataDirPath();" readonly="true" flex="1"/>
<button label="&zotero.preferences.dataDir.choose;" oncommand="var file = Zotero.chooseZoteroDirectory(true); if (!file) { event.stopPropagation(); }"/>
</hbox>
</radiogroup>
<hbox>
<button label="&zotero.preferences.dataDir.reveal;" oncommand="revealDataDirectory()"/>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.dbMaintenance;"/>
<hbox>
<button label="&zotero.preferences.dbMaintenance.integrityCheck;" oncommand="runIntegrityCheck()"/>
<button label="&zotero.preferences.dbMaintenance.resetTranslators;" oncommand="resetTranslators()"/>
<button label="&zotero.preferences.dbMaintenance.resetStyles;" oncommand="resetStyles()"/>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.debugOutputLogging;"/>
<!-- This doesn't wrap without an explicit width -->
<vbox>
<description width="45em">&zotero.preferences.debugOutputLogging.message;</description>
</vbox>
<hbox align="center">
<button id="debug-output-enable" oncommand="Zotero_Preferences.Debug_Output.toggleStore()"/>
<label id="debug-output-lines" style="margin-right: 0"/>
<label value="&zotero.preferences.debugOutputLogging.linesLogged;"/>
<checkbox preference="pref-debug-output-enableAfterRestart" label="&zotero.preferences.debugOutputLogging.enableAfterRestart;" style="margin-left: 1.5em"/>
</hbox>
<hbox align="center">
<button id="debug-output-view" label="&zotero.preferences.debugOutputLogging.viewOutput;" oncommand="Zotero_Preferences.Debug_Output.view()"/>
<button id="debug-output-clear" label="&zotero.preferences.debugOutputLogging.clearOutput;" oncommand="Zotero_Preferences.Debug_Output.clear()"/>
<button id="debug-output-submit" label="&zotero.preferences.debugOutputLogging.submitToServer;" oncommand="Zotero_Preferences.Debug_Output.submit()"/>
<progressmeter id="debug-output-submit-progress" mode="undetermined" hidden="true"/>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.openurl.caption;"/>
<hbox align="center">
<!-- vbox prevents some weird vertical stretching of the menulist -->
<vbox flex="1">
<menulist id="openURLMenu" oncommand="onOpenURLSelected();">
<menupopup>
<menuseparator/>
<menuitem label="&zotero.preferences.openurl.custom;" value="custom" selected="true"/>
</menupopup>
</menulist>
</vbox>
<button id="openURLSearchButton" label="&zotero.preferences.openurl.search;" oncommand="populateOpenURLResolvers()"/>
</hbox>
<hbox align="center">
<label value="&zotero.preferences.openurl.server;"/>
<textbox id="openURLServerField" flex="1" oninput="onOpenURLCustomized();" preference="pref-openURL-resolver"/>
</hbox>
<hbox align="center">
<label value="&zotero.preferences.openurl.version;" control="openURLVersionMenu"/>
<menulist id="openURLVersionMenu" oncommand="onOpenURLCustomized();" preference="pref-openURL-version">
<menupopup>
<menuitem label="0.1" value="0.1"/>
<menuitem label="1.0" value="1.0"/>
</menupopup>
</menulist>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.miscellaneous;"/>
<hbox align="center">
<button id="openAboutConfig" label="&zotero.preferences.openAboutConfig;" oncommand="openInViewer('about:config')"/>
<button id="openCSLEdit" label="&zotero.preferences.openCSLEdit;" oncommand="openInViewer('chrome://zotero/content/tools/csledit.xul', true)"/>
<button id="openCSLPreview" label="&zotero.preferences.openCSLPreview;" oncommand="openInViewer('chrome://zotero/content/tools/cslpreview.xul', true)"/>
</hbox>
</groupbox>
<separator/>
<tabpanels id="zotero-prefpane-advanced-tabpanels">
<tabpanel id="zotero-prefpane-advanced-general-tab" orient="vertical">
<groupbox>
<caption label="&zotero.preferences.debugOutputLogging;"/>
<!-- This doesn't wrap without an explicit width -->
<vbox>
<description width="45em">&zotero.preferences.debugOutputLogging.message;</description>
</vbox>
<hbox align="center">
<button id="debug-output-enable" oncommand="Zotero_Preferences.Debug_Output.toggleStore()"/>
<label id="debug-output-lines" style="margin-right: 0"/>
<label value="&zotero.preferences.debugOutputLogging.linesLogged;"/>
<checkbox preference="pref-debug-output-enableAfterRestart" label="&zotero.preferences.debugOutputLogging.enableAfterRestart;" style="margin-left: 1.5em"/>
</hbox>
<hbox align="center">
<button id="debug-output-view" label="&zotero.preferences.debugOutputLogging.viewOutput;" oncommand="Zotero_Preferences.Debug_Output.view()"/>
<button id="debug-output-clear" label="&zotero.preferences.debugOutputLogging.clearOutput;" oncommand="Zotero_Preferences.Debug_Output.clear()"/>
<button id="debug-output-submit" label="&zotero.preferences.debugOutputLogging.submitToServer;" oncommand="Zotero_Preferences.Debug_Output.submit()"/>
<progressmeter id="debug-output-submit-progress" mode="undetermined" hidden="true"/>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.openurl.caption;"/>
<hbox align="center">
<!-- vbox prevents some weird vertical stretching of the menulist -->
<vbox flex="1">
<menulist id="openURLMenu" oncommand="onOpenURLSelected();">
<menupopup>
<menuseparator/>
<menuitem label="&zotero.preferences.openurl.custom;" value="custom" selected="true"/>
</menupopup>
</menulist>
</vbox>
<button id="openURLSearchButton" label="&zotero.preferences.openurl.search;" oncommand="populateOpenURLResolvers()"/>
</hbox>
<hbox align="center">
<label value="&zotero.preferences.openurl.server;"/>
<textbox id="openURLServerField" flex="1" oninput="onOpenURLCustomized();" preference="pref-openURL-resolver"/>
</hbox>
<hbox align="center">
<label value="&zotero.preferences.openurl.version;" control="openURLVersionMenu"/>
<menulist id="openURLVersionMenu" oncommand="onOpenURLCustomized();" preference="pref-openURL-version">
<menupopup>
<menuitem label="0.1" value="0.1"/>
<menuitem label="1.0" value="1.0"/>
</menupopup>
</menulist>
</hbox>
</groupbox>
<groupbox id="zotero-prefpane-advanced-miscellaneous">
<caption label="&zotero.preferences.miscellaneous;"/>
<hbox id="zotero-prefpane-advanced-openbuttons" align="center">
<button id="openAboutConfig" label="&zotero.preferences.openAboutConfig;" oncommand="openInViewer('about:config')"/>
<button id="openCSLEdit" label="&zotero.preferences.openCSLEdit;" oncommand="openInViewer('chrome://zotero/content/tools/csledit.xul', true)"/>
<button id="openCSLPreview" label="&zotero.preferences.openCSLPreview;" oncommand="openInViewer('chrome://zotero/content/tools/cslpreview.xul', true)"/>
</hbox>
</groupbox>
</tabpanel>
<tabpanel orient="vertical">
<groupbox>
<caption label="&zotero.preferences.attachmentBaseDir.caption;"/>
<!-- This doesn't wrap without an explicit width -->
<vbox>
<description width="45em">&zotero.preferences.attachmentBaseDir.message;</description>
</vbox>
<hbox align="center">
<label value="&zotero.preferences.attachmentBaseDir.basePath;"/>
<filefield id="baseAttachmentPath"
preference="pref-baseAttachmentPath"
onsyncfrompreference="Zotero_Preferences.Attachment_Base_Directory.updateUI()"
preference-editable="true"
readonly="true"
flex="1"
tabindex="-1"/>
<button label="&zotero.preferences.attachmentBaseDir.selectBasePath;"
oncommand="Zotero_Preferences.Attachment_Base_Directory.choosePath()"/>
</hbox>
<hbox>
<button id="resetBasePath"
label="&zotero.preferences.attachmentBaseDir.resetBasePath;"
oncommand="Zotero_Preferences.Attachment_Base_Directory.clearPath()"/>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.dataDir;"/>
<radiogroup id="dataDir" preference="pref-useDataDir" onsyncfrompreference="onDataDirLoad();" onsynctopreference="return onDataDirUpdate(event);">
<radio label="&zotero.preferences.dataDir.useProfile;" value="false"/>
<hbox>
<radio label="&zotero.preferences.dataDir.custom;" value="true"/>
<textbox id="dataDirPath" preference="pref-dataDir" onsyncfrompreference="return getDataDirPath();" readonly="true" flex="1"/>
<button label="&zotero.preferences.dataDir.choose;" oncommand="var file = Zotero.chooseZoteroDirectory(true); if (!file) { event.stopPropagation(); }"/>
</hbox>
</radiogroup>
<hbox>
<button label="&zotero.preferences.dataDir.reveal;" oncommand="revealDataDirectory()"/>
</hbox>
</groupbox>
<groupbox>
<caption label="&zotero.preferences.dbMaintenance;"/>
<hbox>
<button label="&zotero.preferences.dbMaintenance.integrityCheck;" oncommand="runIntegrityCheck()"/>
<button label="&zotero.preferences.dbMaintenance.resetTranslators;" oncommand="resetTranslators()"/>
<button label="&zotero.preferences.dbMaintenance.resetStyles;" oncommand="resetStyles()"/>
</hbox>
</groupbox>
</tabpanel>
</tabpanels>
</tabbox>
</prefpane>
<!-- These mess up the prefwindow (more) if they come before the prefpanes
@ -829,5 +871,5 @@ To add a new preference:
observerService.notifyObservers(null, "charsetmenu-selected", "other");
]]>
</script>
<script src="preferences.js"/>
<script src="preferences.js" type="application/javascript;version=1.8"/>
</prefwindow>

View File

@ -49,9 +49,6 @@ To add a new preference:
<preference id="pref-showIn" name="extensions.zotero.showIn" type="int"/>
<preference id="pref-statusBarIcon" name="extensions.zotero.statusBarIcon" type="int"/>
<preference id="pref-launchNonNativeFiles" name="extensions.zotero.launchNonNativeFiles" type="bool"/>
<preference id="pref-zoteroDotOrgVersionHeader"
name="extensions.zotero.zoteroDotOrgVersionHeader"
type="bool"/>
<preference id="pref-parseEndNoteMIMETypes"
name="extensions.zotero.parseEndNoteMIMETypes"
type="bool" onchange="Zotero.MIMETypeHandler.init()"/>
@ -83,10 +80,6 @@ To add a new preference:
</grid>
</groupbox>
<groupbox id="zotero-prefpane-miscellaneous-groupbox">
<checkbox label="&zotero.preferences.zoteroDotOrgVersionHeader;"
tooltiptext="&zotero.preferences.zoteroDotOrgVersionHeader.tooltip;"
preference="pref-zoteroDotOrgVersionHeader"
insertbefore="automaticSnapshots-checkbox"/>
<checkbox id="launchNonNativeFiles-checkbox"
preference="pref-launchNonNativeFiles"
onsyncfrompreference="return !document.getElementById(this.getAttribute('preference')).value"
@ -171,5 +164,28 @@ To add a new preference:
<separator/>
</prefpane>
<prefpane id="zotero-prefpane-advanced">
<preferences id="zotero-prefpane-general-preferences">
<preference id="pref-zoteroDotOrgVersionHeader"
name="extensions.zotero.zoteroDotOrgVersionHeader"
type="bool"/>
</preferences>
<tabbox id="zotero-prefpane-advanced-tabs">
<tabpanels id="zotero-prefpane-advanced-tabpanels">
<tabpanel id="zotero-prefpane-advanced-general-tab">
<groupbox id="zotero-prefpane-advanced-miscellaneous">
<checkbox label="&zotero.preferences.zoteroDotOrgVersionHeader;"
tooltiptext="&zotero.preferences.zoteroDotOrgVersionHeader.tooltip;"
preference="pref-zoteroDotOrgVersionHeader"
insertbefore="zotero-prefpane-advanced-openbuttons"/>
<separator class="thin"
insertbefore="zotero-prefpane-advanced-openbuttons"/>
</groupbox>
</tabpanel>
</tabpanels>
</tabbox>
</prefpane>
</prefwindow>
</overlay>

View File

@ -28,6 +28,7 @@ Zotero.Attachments = new function(){
this.LINK_MODE_IMPORTED_URL = 1;
this.LINK_MODE_LINKED_FILE = 2;
this.LINK_MODE_LINKED_URL = 3;
this.BASE_PATH_PLACEHOLDER = 'attachments:';
this.importFromFile = importFromFile;
this.linkFromFile = linkFromFile;

View File

@ -1522,6 +1522,41 @@ Zotero.Item.prototype.save = function() {
var path = this.attachmentPath;
var syncState = this.attachmentSyncState;
// Save attachment within attachment base directory as relative path
if (this.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE && path) {
let basePath = Zotero.Prefs.get('baseAttachmentPath');
if (basePath != '' && Zotero.Prefs.get('saveRelativeAttachmentPath')) {
let baseDir = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try {
baseDir.persistentDescriptor = basePath;
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
baseDir = null;
}
if (baseDir) {
let attachmentFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try {
attachmentFile.initWithPath(path);
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
attachmentFile = null;
}
if (attachmentFile && Zotero.File.directoryContains(baseDir, attachmentFile)) {
path = Zotero.Attachments.BASE_PATH_PLACEHOLDER
+ attachmentFile.getRelativeDescriptor(baseDir);
}
}
}
}
var bindParams = [
itemID,
parent ? parent : null,
@ -1916,6 +1951,41 @@ Zotero.Item.prototype.save = function() {
var path = this.attachmentPath;
var syncState = this.attachmentSyncState;
// Save attachment within attachment base directory as relative path
if (this.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE && path) {
let basePath = Zotero.Prefs.get('baseAttachmentPath');
if (basePath != '' && Zotero.Prefs.get('saveRelativeAttachmentPath')) {
let baseDir = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try {
baseDir.persistentDescriptor = basePath;
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
baseDir = null;
}
if (baseDir) {
let attachmentFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try {
attachmentFile.initWithPath(path);
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
attachmentFile = null;
}
if (attachmentFile && Zotero.File.directoryContains(baseDir, attachmentFile)) {
path = Zotero.Attachments.BASE_PATH_PLACEHOLDER
+ attachmentFile.getRelativeDescriptor(baseDir);
}
}
}
}
var bindParams = [
parent ? parent : null,
{ int: linkMode },
@ -3172,20 +3242,58 @@ Zotero.Item.prototype.__defineGetter__('attachmentPath', function () {
return undefined;
}
if (this._attachmentPath !== null) {
return this._attachmentPath;
}
var pathIsRelative = false;
if (!this.id) {
if (this._attachmentPath !== null) {
pathIsRelative = this._attachmentPath.indexOf(Zotero.Attachments.BASE_PATH_PLACEHOLDER) == 0
&& this.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE;
if (!pathIsRelative) {
return this._attachmentPath;
}
}
else if (!this.id) {
return '';
}
var sql = "SELECT path FROM itemAttachments WHERE itemID=?";
var path = Zotero.DB.valueQuery(sql, this.id);
if (!path) {
path = '';
else {
var sql = "SELECT path FROM itemAttachments WHERE itemID=?";
var path = Zotero.DB.valueQuery(sql, this.id);
if (!path) {
this._attachmentPath = '';
return this._attachmentPath;
}
this._attachmentPath = path;
pathIsRelative = path.indexOf(Zotero.Attachments.BASE_PATH_PLACEHOLDER) == 0
&& this.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE;
}
this._attachmentPath = path;
if (pathIsRelative) {
var basePath = Zotero.Prefs.get('baseAttachmentPath');
// If the base path has been cleared, don't try to recreate the full attachment path
if (basePath == '') {
return '';
}
var baseDir = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
try {
baseDir.persistentDescriptor = basePath;
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
return '';
}
var relativePath = this._attachmentPath.substr(
Zotero.Attachments.BASE_PATH_PLACEHOLDER.length
);
var attachmentFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
attachmentFile.setRelativeDescriptor(baseDir,relativePath);
return attachmentFile.persistentDescriptor;
}
return path;
});
@ -3215,6 +3323,21 @@ Zotero.Item.prototype.__defineSetter__('attachmentPath', function (val) {
});
/**
* Force an update of the attachment path and resave the item
*
* This is used when changing the attachment base directory, since relative
* path handling is done on item save.
*/
Zotero.Item.prototype.updateAttachmentPath = function () {
if (!this._changedAttachmentData) {
this._changedAttachmentData = {};
}
this._changedAttachmentData.path = true;
this.save();
};
Zotero.Item.prototype.__defineGetter__('attachmentSyncState', function () {
if (!this.isAttachment()) {
return undefined;

View File

@ -268,6 +268,32 @@ Zotero.File = new function(){
}
/**
* Check whether a directory is an ancestor directory of another directory/file
*/
this.directoryContains = function (dir, file) {
if (!dir.isDirectory()) {
throw new Error("dir must be a directory");
}
if (dir.exists()) {
dir.normalize();
}
if (file.exists()) {
file.normalize();
}
if (!dir.path) {
throw new Error("dir.path is empty");
}
if (!file.path) {
throw new Error("file.path is empty");
}
return file.path.indexOf(dir.path) == 0;
}
/**
* Strip potentially invalid characters
*

View File

@ -3396,6 +3396,7 @@ var ZoteroPane = new function()
}
var file = item.getFile();
Zotero.debug("Opening " + file.path);
if (file) {
if(forceExternalViewer !== undefined) {
var externalViewer = forceExternalViewer;

View File

@ -177,6 +177,12 @@
<!ENTITY zotero.preferences.dataDir.choose "Choose...">
<!ENTITY zotero.preferences.dataDir.reveal "Show Data Directory">
<!ENTITY zotero.preferences.attachmentBaseDir.caption "Linked Attachment Base Directory">
<!ENTITY zotero.preferences.attachmentBaseDir.message "Zotero will use relative paths for linked file attachments within the base directory, allowing you to access files on different computers as long as the file structure within the base directory remains the same.">
<!ENTITY zotero.preferences.attachmentBaseDir.basePath "Base directory:">
<!ENTITY zotero.preferences.attachmentBaseDir.selectBasePath "Choose…">
<!ENTITY zotero.preferences.attachmentBaseDir.resetBasePath "Revert to Absolute Paths…">
<!ENTITY zotero.preferences.dbMaintenance "Database Maintenance">
<!ENTITY zotero.preferences.dbMaintenance.integrityCheck "Check Database Integrity">
<!ENTITY zotero.preferences.dbMaintenance.resetTranslatorsAndStyles "Reset Translators and Styles...">

View File

@ -69,6 +69,18 @@ errorReport.stepsToReproduce = Steps to Reproduce:
errorReport.expectedResult = Expected result:
errorReport.actualResult = Actual result:
attachmentBasePath.selectDir = Choose Base Directory
attachmentBasePath.chooseNewPath.title = Confirm New Base Directory
attachmentBasePath.chooseNewPath.message = Linked file attachments below this directory will be saved using relative paths.
attachmentBasePath.chooseNewPath.existingAttachments.singular = One existing attachment was found within the new base directory.
attachmentBasePath.chooseNewPath.existingAttachments.plural = %S existing attachments were found within the new base directory.
attachmentBasePath.chooseNewPath.button = Change Base Directory Setting
attachmentBasePath.clearBasePath.title = Revert to Absolute Paths
attachmentBasePath.clearBasePath.message = New linked file attachments will be saved using absolute paths.
attachmentBasePath.clearBasePath.existingAttachments.singular = One existing attachment within the old base directory will be converted to use an absolute path.
attachmentBasePath.clearBasePath.existingAttachments.plural = %S existing attachments within the old base directory will be converted to use absolute paths.
attachmentBasePath.clearBasePath.button = Clear Base Directory Setting
dataDir.notFound = The Zotero data directory could not be found.
dataDir.previousDir = Previous directory:
dataDir.useProfileDir = Use %S profile directory
@ -794,4 +806,4 @@ connector.standaloneOpen = Your database cannot be accessed because Zotero Sta
firstRunGuidance.saveIcon = Zotero has found a reference on this page. Click this icon in the address bar to save the reference to your Zotero library.
firstRunGuidance.authorMenu = Zotero lets you specify editors and translators, too. You can turn an author into an editor or translator by selecting from this menu.
firstRunGuidance.quickFormat = Type a title or author to search for a reference.\n\nAfter you've made your selection, click the bubble or press Ctrl-\u2193 to add page numbers, prefixes, or suffixes. You can also include a page number along with your search terms to add it directly.\n\nYou can edit citations directly in the word processor document.
firstRunGuidance.quickFormatMac = Type a title or author to search for a reference.\n\nAfter you've made your selection, click the bubble or press Cmd-\u2193 to add page numbers, prefixes, or suffixes. You can also include a page number along with your search terms to add it directly.\n\nYou can edit citations directly in the word processor document.
firstRunGuidance.quickFormatMac = Type a title or author to search for a reference.\n\nAfter you've made your selection, click the bubble or press Cmd-\u2193 to add page numbers, prefixes, or suffixes. You can also include a page number along with your search terms to add it directly.\n\nYou can edit citations directly in the word processor document.

View File

@ -6,6 +6,8 @@
pref("extensions.zotero.firstRun2", true);
pref("extensions.zotero@chnm.gmu.edu.description", "chrome://zotero/locale/zotero.properties");
pref("extensions.zotero.saveRelativeAttachmentPath", false);
pref("extensions.zotero.baseAttachmentPath", '');
pref("extensions.zotero.useDataDir", false);
pref("extensions.zotero.dataDir", '');
pref("extensions.zotero.lastDataDir", '');