
Adds a submenu for quickly generating and submitting debug output, with a button to copy the Debug ID to the clipboard and a one-click option to restart with logging enabled. Debug output can also now be viewed in real-time using the new debug output viewer window (previously available only via a command-line flag). The locale switcher has moved to the top of the Advanced prefpane (where Debug Output Logging used to go) in a Miscellaneous section, and there's a new "Advanced Configuration" section (mirroring Thunderbird) at the bottom for "Config Editor". Closes #1108
815 lines
25 KiB
JavaScript
815 lines
25 KiB
JavaScript
/*
|
||
***** BEGIN LICENSE BLOCK *****
|
||
|
||
Copyright © 2006–2013 Center for History and New Media
|
||
George Mason University, Fairfax, Virginia, USA
|
||
http://zotero.org
|
||
|
||
This file is part of Zotero.
|
||
|
||
Zotero is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU Affero General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
Zotero is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU Affero General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Affero General Public License
|
||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
***** END LICENSE BLOCK *****
|
||
*/
|
||
|
||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||
|
||
Zotero_Preferences.Advanced = {
|
||
_openURLResolvers: null,
|
||
|
||
|
||
init: function () {
|
||
Zotero_Preferences.Keys.init();
|
||
|
||
// Show Memory Info button if the Error Console menu option is enabled
|
||
if (Zotero.Prefs.get('devtools.errorconsole.enabled', true)) {
|
||
document.getElementById('memory-info').hidden = false;
|
||
}
|
||
|
||
this.onDataDirLoad();
|
||
this.refreshLocale();
|
||
},
|
||
|
||
|
||
migrateDataDirectory: Zotero.Promise.coroutine(function* () {
|
||
var currentDir = Zotero.DataDirectory.dir;
|
||
var defaultDir = Zotero.DataDirectory.defaultDir;
|
||
if (currentDir == defaultDir) {
|
||
Zotero.debug("Already using default directory");
|
||
return;
|
||
}
|
||
|
||
Components.utils.import("resource://zotero/config.js")
|
||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||
.getService(Components.interfaces.nsIPromptService);
|
||
|
||
// If there's a migration marker, point data directory back to the current location and remove
|
||
// it to trigger the migration again
|
||
var marker = OS.Path.join(defaultDir, Zotero.DataDirectory.MIGRATION_MARKER);
|
||
if (yield OS.File.exists(marker)) {
|
||
Zotero.Prefs.clear('dataDir');
|
||
Zotero.Prefs.clear('useDataDir');
|
||
yield OS.File.remove(marker);
|
||
try {
|
||
yield OS.File.remove(OS.Path.join(defaultDir, '.DS_Store'));
|
||
}
|
||
catch (e) {}
|
||
}
|
||
|
||
// ~/Zotero exists and is non-empty
|
||
if ((yield OS.File.exists(defaultDir)) && !(yield Zotero.File.directoryIsEmpty(defaultDir))) {
|
||
let buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||
let index = ps.confirmEx(
|
||
window,
|
||
Zotero.getString('general.error'),
|
||
Zotero.getString('zotero.preferences.advanced.migrateDataDir.directoryExists1', defaultDir)
|
||
+ "\n\n"
|
||
+ Zotero.getString('zotero.preferences.advanced.migrateDataDir.directoryExists2'),
|
||
buttonFlags,
|
||
Zotero.getString('general.showDirectory'),
|
||
null, null, null, {}
|
||
);
|
||
if (index == 0) {
|
||
yield Zotero.File.reveal(
|
||
// Windows opens the directory, which might be confusing here, so open parent instead
|
||
Zotero.isWin ? OS.Path.dirname(defaultDir) : defaultDir
|
||
);
|
||
}
|
||
return;
|
||
}
|
||
|
||
var additionalText = '';
|
||
if (Zotero.isWin) {
|
||
try {
|
||
let numItems = yield Zotero.DB.valueQueryAsync(
|
||
"SELECT COUNT(*) FROM itemAttachments WHERE linkMode IN (?, ?)",
|
||
[Zotero.Attachments.LINK_MODE_IMPORTED_FILE, Zotero.Attachments.LINK_MODE_IMPORTED_URL]
|
||
);
|
||
if (numItems > 100) {
|
||
additionalText = '\n\n' + Zotero.getString(
|
||
'zotero.preferences.advanced.migrateDataDir.manualMigration',
|
||
[Zotero.appName, defaultDir, ZOTERO_CONFIG.CLIENT_NAME]
|
||
);
|
||
}
|
||
}
|
||
catch (e) {
|
||
Zotero.logError(e);
|
||
}
|
||
}
|
||
|
||
// Prompt to restart
|
||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||
var index = ps.confirmEx(window,
|
||
Zotero.getString('zotero.preferences.advanced.migrateDataDir.title'),
|
||
Zotero.getString(
|
||
'zotero.preferences.advanced.migrateDataDir.directoryWillBeMoved',
|
||
[ZOTERO_CONFIG.CLIENT_NAME, defaultDir]
|
||
) + '\n\n'
|
||
+ Zotero.getString(
|
||
'zotero.preferences.advanced.migrateDataDir.appMustBeRestarted', Zotero.appName
|
||
) + additionalText,
|
||
buttonFlags,
|
||
Zotero.getString('general.continue'),
|
||
null, null, null, {}
|
||
);
|
||
|
||
if (index == 0) {
|
||
yield Zotero.DataDirectory.markForMigration(currentDir);
|
||
Zotero.Utilities.Internal.quitZotero(true);
|
||
}
|
||
}),
|
||
|
||
|
||
runIntegrityCheck: Zotero.Promise.coroutine(function* () {
|
||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||
.getService(Components.interfaces.nsIPromptService);
|
||
|
||
var ok = yield Zotero.DB.integrityCheck();
|
||
if (ok) {
|
||
ok = yield Zotero.Schema.integrityCheck();
|
||
if (!ok) {
|
||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||
var index = ps.confirmEx(window,
|
||
Zotero.getString('general.failed'),
|
||
Zotero.getString('db.integrityCheck.failed') + "\n\n" +
|
||
Zotero.getString('db.integrityCheck.repairAttempt') + " " +
|
||
Zotero.getString('db.integrityCheck.appRestartNeeded', Zotero.appName),
|
||
buttonFlags,
|
||
Zotero.getString('db.integrityCheck.fixAndRestart', Zotero.appName),
|
||
null, null, null, {}
|
||
);
|
||
|
||
if (index == 0) {
|
||
// Safety first
|
||
yield Zotero.DB.backupDatabase();
|
||
|
||
// Fix the errors
|
||
yield Zotero.Schema.integrityCheck(true);
|
||
|
||
// And run the check again
|
||
ok = yield Zotero.Schema.integrityCheck();
|
||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING);
|
||
if (ok) {
|
||
var str = 'success';
|
||
var msg = Zotero.getString('db.integrityCheck.errorsFixed');
|
||
}
|
||
else {
|
||
var str = 'failed';
|
||
var msg = Zotero.getString('db.integrityCheck.errorsNotFixed')
|
||
+ "\n\n" + Zotero.getString('db.integrityCheck.reportInForums');
|
||
}
|
||
|
||
ps.confirmEx(window,
|
||
Zotero.getString('general.' + str),
|
||
msg,
|
||
buttonFlags,
|
||
Zotero.getString('general.restartApp', Zotero.appName),
|
||
null, null, null, {}
|
||
);
|
||
|
||
var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
|
||
.getService(Components.interfaces.nsIAppStartup);
|
||
appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit
|
||
| Components.interfaces.nsIAppStartup.eRestart);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
try {
|
||
yield Zotero.DB.vacuum();
|
||
}
|
||
catch (e) {
|
||
Zotero.logError(e);
|
||
ok = false;
|
||
}
|
||
}
|
||
var str = ok ? 'passed' : 'failed';
|
||
|
||
ps.alert(window,
|
||
Zotero.getString('general.' + str),
|
||
Zotero.getString('db.integrityCheck.' + str)
|
||
+ (!ok ? "\n\n" + Zotero.getString('db.integrityCheck.dbRepairTool') : ''));
|
||
}),
|
||
|
||
|
||
resetTranslatorsAndStyles: function () {
|
||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||
.getService(Components.interfaces.nsIPromptService);
|
||
|
||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||
|
||
var index = ps.confirmEx(null,
|
||
Zotero.getString('general.warning'),
|
||
Zotero.getString('zotero.preferences.advanced.resetTranslatorsAndStyles.changesLost'),
|
||
buttonFlags,
|
||
Zotero.getString('zotero.preferences.advanced.resetTranslatorsAndStyles'),
|
||
null, null, null, {});
|
||
|
||
if (index == 0) {
|
||
Zotero.Schema.resetTranslatorsAndStyles()
|
||
.then(function () {
|
||
if (Zotero_Preferences.Export) {
|
||
Zotero_Preferences.Export.populateQuickCopyList();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
|
||
resetTranslators: async function () {
|
||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||
.getService(Components.interfaces.nsIPromptService);
|
||
|
||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||
|
||
var index = ps.confirmEx(null,
|
||
Zotero.getString('general.warning'),
|
||
Zotero.getString('zotero.preferences.advanced.resetTranslators.changesLost'),
|
||
buttonFlags,
|
||
Zotero.getString('zotero.preferences.advanced.resetTranslators'),
|
||
null, null, null, {});
|
||
|
||
if (index == 0) {
|
||
let button = document.getElementById('reset-translators-button');
|
||
button.disabled = true;
|
||
try {
|
||
await Zotero.Schema.resetTranslators();
|
||
if (Zotero_Preferences.Export) {
|
||
Zotero_Preferences.Export.populateQuickCopyList();
|
||
}
|
||
}
|
||
finally {
|
||
button.disabled = false;
|
||
}
|
||
}
|
||
},
|
||
|
||
|
||
resetStyles: async function () {
|
||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||
.getService(Components.interfaces.nsIPromptService);
|
||
|
||
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
||
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
||
|
||
var index = ps.confirmEx(null,
|
||
Zotero.getString('general.warning'),
|
||
Zotero.getString('zotero.preferences.advanced.resetStyles.changesLost'),
|
||
buttonFlags,
|
||
Zotero.getString('zotero.preferences.advanced.resetStyles'),
|
||
null, null, null, {});
|
||
|
||
if (index == 0) {
|
||
let button = document.getElementById('reset-styles-button');
|
||
button.disabled = true;
|
||
try {
|
||
await Zotero.Schema.resetStyles()
|
||
if (Zotero_Preferences.Export) {
|
||
Zotero_Preferences.Export.populateQuickCopyList();
|
||
}
|
||
}
|
||
finally {
|
||
button.disabled = false;
|
||
}
|
||
}
|
||
},
|
||
|
||
|
||
onDataDirLoad: function () {
|
||
var useDataDir = Zotero.Prefs.get('useDataDir');
|
||
var dataDir = Zotero.Prefs.get('lastDataDir') || Zotero.Prefs.get('dataDir');
|
||
var currentDir = Zotero.DataDirectory.dir;
|
||
var defaultDataDir = Zotero.DataDirectory.defaultDir;
|
||
|
||
// Change "Use profile directory" label to home directory location unless using profile dir
|
||
if (useDataDir || currentDir == defaultDataDir) {
|
||
document.getElementById('default-data-dir').setAttribute(
|
||
'label', Zotero.getString('dataDir.default', Zotero.DataDirectory.defaultDir)
|
||
);
|
||
}
|
||
|
||
// Don't show custom data dir as in-use if set to the default
|
||
if (dataDir == defaultDataDir) {
|
||
useDataDir = false;
|
||
}
|
||
|
||
document.getElementById('data-dir-path').setAttribute('disabled', !useDataDir);
|
||
document.getElementById('migrate-data-dir').setAttribute(
|
||
'hidden', !Zotero.DataDirectory.canMigrate()
|
||
);
|
||
|
||
return useDataDir;
|
||
},
|
||
|
||
|
||
onDataDirUpdate: Zotero.Promise.coroutine(function* (event, forceNew) {
|
||
var radiogroup = document.getElementById('data-dir');
|
||
var newUseDataDir = radiogroup.selectedIndex == 1;
|
||
|
||
if (!forceNew && newUseDataDir && !this._usingDefaultDataDir()) {
|
||
return;
|
||
}
|
||
|
||
// This call shows a filepicker if needed, forces a restart if required, and does nothing if
|
||
// cancel was pressed or value hasn't changed
|
||
yield Zotero.DataDirectory.choose(
|
||
true,
|
||
!newUseDataDir,
|
||
() => Zotero_Preferences.openURL('https://zotero.org/support/zotero_data')
|
||
);
|
||
radiogroup.selectedIndex = this._usingDefaultDataDir() ? 0 : 1;
|
||
}),
|
||
|
||
|
||
chooseDataDir: function(event) {
|
||
document.getElementById('data-dir').selectedIndex = 1;
|
||
this.onDataDirUpdate(event, true);
|
||
},
|
||
|
||
|
||
getDataDirPath: function () {
|
||
// TEMP: lastDataDir can be removed once old persistent descriptors have been
|
||
// converted, which they are in getZoteroDirectory() in 5.0
|
||
var prefValue = Zotero.Prefs.get('lastDataDir') || Zotero.Prefs.get('dataDir');
|
||
|
||
// Don't show path if the default
|
||
if (prefValue == Zotero.DataDirectory.defaultDir) {
|
||
return '';
|
||
}
|
||
|
||
return prefValue || '';
|
||
},
|
||
|
||
|
||
_usingDefaultDataDir: function () {
|
||
// Legacy profile directory location
|
||
if (!Zotero.Prefs.get('useDataDir')) {
|
||
return true;
|
||
}
|
||
|
||
var dataDir = Zotero.Prefs.get('lastDataDir') || Zotero.Prefs.get('dataDir');
|
||
// Default home directory location
|
||
if (dataDir == Zotero.DataDirectory.defaultDir) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
|
||
populateOpenURLResolvers: function () {
|
||
var openURLMenu = document.getElementById('openURLMenu');
|
||
|
||
this._openURLResolvers = Zotero.OpenURL.discoverResolvers();
|
||
var i = 0;
|
||
for (let r of this._openURLResolvers) {
|
||
openURLMenu.insertItemAt(i, r.name);
|
||
if (r.url == Zotero.Prefs.get('openURL.resolver') && r.version == Zotero.Prefs.get('openURL.version')) {
|
||
openURLMenu.selectedIndex = i;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
var button = document.getElementById('openURLSearchButton');
|
||
switch (this._openURLResolvers.length) {
|
||
case 0:
|
||
var num = 'zero';
|
||
break;
|
||
case 1:
|
||
var num = 'singular';
|
||
break;
|
||
default:
|
||
var num = 'plural';
|
||
}
|
||
|
||
button.setAttribute('label', Zotero.getString('zotero.preferences.openurl.resolversFound.' + num, this._openURLResolvers.length));
|
||
},
|
||
|
||
|
||
onOpenURLSelected: function () {
|
||
var openURLServerField = document.getElementById('openURLServerField');
|
||
var openURLVersionMenu = document.getElementById('openURLVersionMenu');
|
||
var openURLMenu = document.getElementById('openURLMenu');
|
||
|
||
if(openURLMenu.value == "custom")
|
||
{
|
||
openURLServerField.focus();
|
||
}
|
||
else
|
||
{
|
||
openURLServerField.value = this._openURLResolvers[openURLMenu.selectedIndex]['url'];
|
||
openURLVersionMenu.value = this._openURLResolvers[openURLMenu.selectedIndex]['version'];
|
||
Zotero.Prefs.set("openURL.resolver", this._openURLResolvers[openURLMenu.selectedIndex]['url']);
|
||
Zotero.Prefs.set("openURL.version", this._openURLResolvers[openURLMenu.selectedIndex]['version']);
|
||
}
|
||
},
|
||
|
||
onOpenURLCustomized: function () {
|
||
document.getElementById('openURLMenu').value = "custom";
|
||
},
|
||
|
||
|
||
_getAutomaticLocaleMenuLabel: function () {
|
||
return Zotero.getString(
|
||
'zotero.preferences.locale.automaticWithLocale',
|
||
Zotero.Locale.availableLocales[Zotero.locale] || Zotero.locale
|
||
);
|
||
},
|
||
|
||
|
||
refreshLocale: function () {
|
||
var matchOS = Zotero.Prefs.get('intl.locale.matchOS', true);
|
||
var autoLocaleName, currentValue;
|
||
|
||
// If matching OS, get the name of the current locale
|
||
if (matchOS) {
|
||
autoLocaleName = this._getAutomaticLocaleMenuLabel();
|
||
currentValue = 'automatic';
|
||
}
|
||
// Otherwise get the name of the locale specified in the pref
|
||
else {
|
||
let branch = Services.prefs.getBranch("");
|
||
let locale = branch.getComplexValue(
|
||
'general.useragent.locale', Components.interfaces.nsIPrefLocalizedString
|
||
);
|
||
autoLocaleName = Zotero.getString('zotero.preferences.locale.automatic');
|
||
currentValue = locale;
|
||
}
|
||
|
||
// Populate menu
|
||
var menu = document.getElementById('locale-menu');
|
||
var menupopup = menu.firstChild;
|
||
menupopup.textContent = '';
|
||
// Show "Automatic (English)", "Automatic (Français)", etc.
|
||
menu.appendItem(autoLocaleName, 'automatic');
|
||
menu.menupopup.appendChild(document.createElement('menuseparator'));
|
||
// Add all available locales
|
||
for (let locale in Zotero.Locale.availableLocales) {
|
||
menu.appendItem(Zotero.Locale.availableLocales[locale], locale);
|
||
}
|
||
menu.value = currentValue;
|
||
},
|
||
|
||
onLocaleChange: function () {
|
||
var menu = document.getElementById('locale-menu');
|
||
if (menu.value == 'automatic') {
|
||
// Changed if not already set to automatic (unless we have the automatic locale name,
|
||
// meaning we just switched away to the same manual locale and back to automatic)
|
||
var changed = !Zotero.Prefs.get('intl.locale.matchOS', true)
|
||
&& menu.label != this._getAutomaticLocaleMenuLabel();
|
||
Zotero.Prefs.set('intl.locale.matchOS', true, true);
|
||
}
|
||
else {
|
||
// Changed if moving to a locale other than the current one
|
||
var changed = Zotero.locale != menu.value
|
||
Zotero.Prefs.set('intl.locale.matchOS', false, true);
|
||
Zotero.Prefs.set('general.useragent.locale', menu.value, true);
|
||
}
|
||
|
||
if (!changed) {
|
||
return;
|
||
}
|
||
|
||
var ps = Services.prompt;
|
||
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
|
||
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
||
var index = ps.confirmEx(null,
|
||
Zotero.getString('general.restartRequired'),
|
||
Zotero.getString('general.restartRequiredForChange', Zotero.appName),
|
||
buttonFlags,
|
||
Zotero.getString('general.restartNow'),
|
||
Zotero.getString('general.restartLater'),
|
||
null, null, {});
|
||
|
||
if (index == 0) {
|
||
Zotero.Utilities.Internal.quitZotero(true);
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
Zotero_Preferences.Attachment_Base_Directory = {
|
||
getPath: function () {
|
||
var oldPath = Zotero.Prefs.get('baseAttachmentPath');
|
||
if (oldPath) {
|
||
try {
|
||
return OS.Path.normalize(oldPath);
|
||
}
|
||
catch (e) {
|
||
Zotero.logError(e);
|
||
return false;
|
||
}
|
||
}
|
||
},
|
||
|
||
|
||
choosePath: Zotero.Promise.coroutine(function* () {
|
||
var oldPath = this.getPath();
|
||
|
||
//Prompt user to choose new base path
|
||
if (oldPath) {
|
||
var oldPathFile = Zotero.File.pathToFile(oldPath);
|
||
}
|
||
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||
.createInstance(nsIFilePicker);
|
||
if (oldPathFile) {
|
||
fp.displayDirectory = oldPathFile;
|
||
}
|
||
fp.init(window, Zotero.getString('attachmentBasePath.selectDir'), nsIFilePicker.modeGetFolder);
|
||
fp.appendFilters(nsIFilePicker.filterAll);
|
||
if (fp.show() != nsIFilePicker.returnOK) {
|
||
return false;
|
||
}
|
||
var newPath = OS.Path.normalize(fp.file.path);
|
||
|
||
if (oldPath && oldPath == newPath) {
|
||
Zotero.debug("Base directory hasn't changed");
|
||
return false;
|
||
}
|
||
|
||
return this.changePath(newPath);
|
||
}),
|
||
|
||
|
||
changePath: Zotero.Promise.coroutine(function* (basePath) {
|
||
// Find all current attachments with relative attachment paths
|
||
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE ?";
|
||
var params = [
|
||
Zotero.Attachments.LINK_MODE_LINKED_FILE,
|
||
Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%"
|
||
];
|
||
var oldRelativeAttachmentIDs = yield Zotero.DB.columnQueryAsync(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 = yield Zotero.DB.columnQueryAsync(sql, params);
|
||
var newAttachmentPaths = {};
|
||
var numNewAttachments = 0;
|
||
var numOldAttachments = 0;
|
||
for (let i=0; i<allAttachments.length; i++) {
|
||
let attachmentID = allAttachments[i];
|
||
let attachmentPath;
|
||
let relPath = false
|
||
|
||
try {
|
||
let attachment = yield Zotero.Items.getAsync(attachmentID);
|
||
// This will return FALSE for relative paths if base directory
|
||
// isn't currently set
|
||
attachmentPath = attachment.getFilePath();
|
||
// Get existing relative path
|
||
let storedPath = attachment.attachmentPath;
|
||
if (storedPath.startsWith(Zotero.Attachments.BASE_PATH_PLACEHOLDER)) {
|
||
relPath = storedPath.substr(Zotero.Attachments.BASE_PATH_PLACEHOLDER.length);
|
||
}
|
||
}
|
||
catch (e) {
|
||
// Don't deal with bad attachment paths. Just skip them.
|
||
Zotero.debug(e, 2);
|
||
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
|
||
if (relPath) {
|
||
if (yield OS.File.exists(OS.Path.join(basePath, relPath))) {
|
||
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 (attachmentPath && Zotero.File.directoryContains(basePath, attachmentPath)) {
|
||
newAttachmentPaths[attachmentID] = relPath ? attachmentPath : null;
|
||
numNewAttachments++;
|
||
}
|
||
// Existing relative attachments not within the new base directory
|
||
// will be converted to absolute paths
|
||
else if (relPath && this.getPath()) {
|
||
newAttachmentPaths[attachmentID] = attachmentPath;
|
||
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', basePath);
|
||
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)
|
||
yield Zotero.Utilities.Internal.forEachChunkAsync(
|
||
Object.keys(newAttachmentPaths),
|
||
100,
|
||
function (chunk) {
|
||
return Zotero.DB.executeTransaction(function* () {
|
||
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
|
||
});
|
||
}
|
||
})
|
||
}
|
||
);
|
||
|
||
return true;
|
||
}),
|
||
|
||
|
||
clearPath: Zotero.Promise.coroutine(function* () {
|
||
// Find all current attachments with relative paths
|
||
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode=? AND path LIKE ?";
|
||
var params = [
|
||
Zotero.Attachments.LINK_MODE_LINKED_FILE,
|
||
Zotero.Attachments.BASE_PATH_PLACEHOLDER + "%"
|
||
];
|
||
var relativeAttachmentIDs = yield Zotero.DB.columnQueryAsync(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);
|
||
|
||
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', '');
|
||
}),
|
||
|
||
|
||
updateUI: Zotero.Promise.coroutine(function* () {
|
||
var filefield = document.getElementById('baseAttachmentPath');
|
||
var path = Zotero.Prefs.get('baseAttachmentPath');
|
||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||
if (yield OS.File.exists(path)) {
|
||
filefield.file = Zotero.File.pathToFile(path);
|
||
filefield.label = path;
|
||
}
|
||
else {
|
||
filefield.label = '';
|
||
}
|
||
document.getElementById('resetBasePath').disabled = !path;
|
||
})
|
||
};
|
||
|
||
|
||
Zotero_Preferences.Keys = {
|
||
init: function () {
|
||
var rows = document.getElementById('zotero-prefpane-advanced-keys-tab').getElementsByTagName('row');
|
||
for (var i=0; i<rows.length; i++) {
|
||
// Display the appropriate modifier keys for the platform
|
||
let label = rows[i].firstChild.nextSibling;
|
||
if (label.className == 'modifier') {
|
||
label.value = Zotero.isMac ? Zotero.getString('general.keys.cmdShift') : Zotero.getString('general.keys.ctrlShift');
|
||
}
|
||
}
|
||
|
||
var textboxes = document.getElementById('zotero-keys-rows').getElementsByTagName('textbox');
|
||
for (let i=0; i<textboxes.length; i++) {
|
||
let textbox = textboxes[i];
|
||
textbox.value = textbox.value.toUpperCase();
|
||
// .value takes care of the initial value, and this takes care of direct pref changes
|
||
// while the window is open
|
||
textbox.setAttribute('onsyncfrompreference', 'return Zotero_Preferences.Keys.capitalizePref(this.id)');
|
||
textbox.setAttribute('oninput', 'this.value = this.value.toUpperCase()');
|
||
}
|
||
},
|
||
|
||
|
||
capitalizePref: function (id) {
|
||
var elem = document.getElementById(id);
|
||
var pref = document.getElementById(elem.getAttribute('preference'));
|
||
if (pref.value) {
|
||
return pref.value.toUpperCase();
|
||
}
|
||
}
|
||
};
|