2006-10-04 17:16:56 +00:00
|
|
|
/*
|
|
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
|
2009-12-28 09:47:49 +00:00
|
|
|
Copyright © 2009 Center for History and New Media
|
|
|
|
George Mason University, Fairfax, Virginia, USA
|
|
|
|
http://zotero.org
|
2006-10-04 17:16:56 +00:00
|
|
|
|
2009-12-28 09:47:49 +00:00
|
|
|
This file is part of Zotero.
|
2006-10-04 17:16:56 +00:00
|
|
|
|
2009-12-28 09:47:49 +00:00
|
|
|
Zotero is free software: you can redistribute it and/or modify
|
2011-05-18 18:34:22 +00:00
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
2009-12-28 09:47:49 +00:00
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2006-10-04 17:16:56 +00:00
|
|
|
|
2009-12-28 09:47:49 +00:00
|
|
|
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
|
2011-05-18 18:34:22 +00:00
|
|
|
GNU Affero General Public License for more details.
|
2009-12-28 09:47:49 +00:00
|
|
|
|
2011-05-18 18:34:22 +00:00
|
|
|
You should have received a copy of the GNU Affero General Public License
|
2009-12-28 09:47:49 +00:00
|
|
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
2006-10-04 17:16:56 +00:00
|
|
|
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
2012-07-16 21:50:14 -04:00
|
|
|
// Commonly used imports accessible anywhere
|
2018-02-25 12:55:54 -05:00
|
|
|
Components.utils.importGlobalProperties(["XMLHttpRequest"]);
|
2014-12-09 18:37:43 -05:00
|
|
|
Components.utils.import("resource://zotero/config.js");
|
2012-07-16 21:50:14 -04:00
|
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
2023-12-03 03:54:05 -05:00
|
|
|
var { OS } = ChromeUtils.importESModule("chrome://zotero/content/osfile.mjs");
|
2017-09-12 02:35:06 -04:00
|
|
|
Components.classes["@mozilla.org/net/osfileconstantsservice;1"]
|
|
|
|
.getService(Components.interfaces.nsIOSFileConstantsService)
|
|
|
|
.init();
|
2012-07-16 21:50:14 -04:00
|
|
|
|
2023-11-06 01:26:47 -05:00
|
|
|
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
2023-07-20 05:34:09 -04:00
|
|
|
XPCOMUtils.defineLazyModuleGetters(globalThis, {
|
|
|
|
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
|
2024-04-12 10:29:59 -07:00
|
|
|
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
2023-07-20 05:34:09 -04:00
|
|
|
});
|
2023-12-03 04:43:25 -05:00
|
|
|
const { CommandLineOptions } = ChromeUtils.importESModule("chrome://zotero/content/modules/commandLineOptions.mjs");
|
2016-12-26 15:17:19 -05:00
|
|
|
Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
|
|
|
|
|
2006-02-21 17:01:06 +00:00
|
|
|
/*
|
|
|
|
* Core functions
|
|
|
|
*/
|
2011-06-14 00:36:21 +00:00
|
|
|
(function(){
|
2006-05-27 00:20:27 +00:00
|
|
|
// Privileged (public) methods
|
2006-07-27 08:45:48 +00:00
|
|
|
this.getStorageDirectory = getStorageDirectory;
|
2006-03-20 21:47:22 +00:00
|
|
|
this.debug = debug;
|
2007-10-23 07:11:59 +00:00
|
|
|
this.setFontSize = setFontSize;
|
2006-03-20 21:47:22 +00:00
|
|
|
this.flattenArguments = flattenArguments;
|
Closes #259, auto-complete of tags
Addresses #260, Add auto-complete to search window
- New XPCOM autocomplete component for Zotero data -- can be used by setting the autocompletesearch attribute of a textbox to 'zotero' and passing a search scope with the autocompletesearchparam attribute. Additional parameters can be passed by appending them to the autocompletesearchparam value with a '/', e.g. 'tag/2732' (to exclude tags that show up in item 2732)
- Tag entry now uses more or less the same interface as metadata -- no more popup window -- note that tab isn't working properly yet, and there's no way to quickly enter multiple tags (though it's now considerably quicker than it was before)
- Autocomplete for tags, excluding any tags already set for the current item
- Standalone note windows now register with the Notifier (since tags needed item modification notifications to work properly), which will help with #282, "Notes opened in separate windows need item notification"
- Tags are now retrieved in alphabetical order
- Scholar.Item.replaceTag(oldTagID, newTag), with a single notify
- Scholar.getAncestorByTagName(elem, tagName) -- walk up the DOM tree from an element until an element with the specified tag name is found (also checks with 'xul:' prefix, for use in XBL), or false if not found -- probably shouldn't be used too widely, since it's doing string comparisons, but better than specifying, say, nine '.parentNode' properties, and makes for more resilient code
A few notes:
- Autocomplete in Minefield seems to self-destruct after using it in the same field a few times, taking down saving of the field with it -- this may or may not be my fault, but it makes Zotero more or less unusable in 3.0 at the moment. Sorry. (I use 3.0 myself for development, so I'll work on it.)
- This would have been much, much easier if having an autocomplete textbox (which uses an XBL-generated popup for the suggestions) within a popup (as it is in the independent note edit panes) didn't introduce all sorts of crazy bugs that had to be defeated with annoying hackery -- one side effect of this is that at the moment you can't close the tags popup with the Escape key
- Independent note windows now need to pull in itemPane.js to function properly, which is a bit messy and not ideal, but less messy and more ideal than duplicating all the dual-state editor and tabindex logic would be
- Hitting tab in a tag field not only doesn't work but also breaks things until the next window refresh.
- There are undoubtedly other bugs.
2006-09-07 08:07:48 +00:00
|
|
|
this.getAncestorByTagName = getAncestorByTagName;
|
2015-03-09 14:25:49 -04:00
|
|
|
this.reinit = reinit; // defined in zotero-service.js
|
2006-05-27 00:20:27 +00:00
|
|
|
|
2006-06-15 06:13:02 +00:00
|
|
|
// Public properties
|
2007-10-23 07:11:59 +00:00
|
|
|
this.initialized = false;
|
|
|
|
this.skipLoading = false;
|
2009-12-29 22:21:54 +00:00
|
|
|
this.startupError;
|
2022-05-06 02:33:24 -04:00
|
|
|
Object.defineProperty(this, 'startupErrorHandler', {
|
|
|
|
get: () => _startupErrorHandler,
|
2022-06-09 03:00:54 -04:00
|
|
|
enumerable: true,
|
|
|
|
configurable: true
|
|
|
|
});
|
|
|
|
Object.defineProperty(this, 'resourcesDir', {
|
|
|
|
get: () => {
|
|
|
|
// AChrome is app/chrome
|
|
|
|
return FileUtils.getDir('AChrom', []).parent.parent.path;
|
|
|
|
},
|
|
|
|
enumerable: true,
|
|
|
|
configurable: true
|
2022-05-06 02:33:24 -04:00
|
|
|
});
|
2006-06-15 06:13:02 +00:00
|
|
|
this.version;
|
2006-08-30 19:18:43 +00:00
|
|
|
this.platform;
|
2006-09-06 07:04:02 +00:00
|
|
|
this.locale;
|
2007-10-23 07:11:59 +00:00
|
|
|
this.dir; // locale direction: 'ltr' or 'rtl'
|
2006-08-30 21:56:52 +00:00
|
|
|
this.isMac;
|
2007-10-23 07:11:59 +00:00
|
|
|
this.isWin;
|
|
|
|
this.initialURL; // used by Schema to show the changelog on upgrades
|
2017-05-31 16:28:47 +01:00
|
|
|
this.Promise = require('resource://zotero/bluebird.js');
|
2.0b3 megacommit
- Support for group libraries
- General support for multiple libraries of different types
- Streamlined sync support
- Using solely libraryID and key rather than itemID, and removed all itemID-changing code
- Combined two requests for increased performance and decreased server load
- Added warning on user account change
- Provide explicit error message on SSL failure
- Removed snapshot and link toolbar buttons and changed browser context menu options and drags to create parent items + snapshots
- Closes #786, Add numPages field
- Fixes #1063, Duplicate item with tags broken in Sync Preview
- Added better purging of deleted tags
- Added local user key before first sync
- Add clientDateModified to all objects for more flexibility in syncing
- Added new triples-based Relation object type, currently used to store links between items copied between local and group libraries
- Updated zotero.org translator for groups
- Additional trigger-based consistency checks
- Fixed broken URL drag in Firefox 3.5
- Disabled zeroconf menu option (no longer functional)
Developer-specific changes:
- Overhauled data layer
- Data object constructors no longer take arguments (return to 1.0-like API)
- Existing objects can be retrieved by setting id or library/key properties
- id/library/key must be set for new objects before other fields
- New methods:
- ZoteroPane.getSelectedLibraryID()
- ZoteroPane.getSelectedGroup(asID)
- ZoteroPane.addItemFromDocument(doc, itemType, saveSnapshot)
- ZoteroPane.addItemFromURL(url, itemType)
- ZoteroPane.canEdit()
- Zotero.CollectionTreeView.selectLibrary(libraryID)
- New Zotero.URI methods
- Changed methods
- Many data object methods now take a libraryID
- ZoteroPane.addAttachmentFromPage(link, itemID)
- Removed saveItem and saveAttachments parameters from Zotero.Translate constructor
- translate() now takes a libraryID, null for local library, or false to not save items (previously on constructor)
- saveAttachments is now a translate() parameter
- Zotero.flattenArguments() better handles passed objects
- Zotero.File.getFileHash() (not currently used)
2009-05-14 18:23:40 +00:00
|
|
|
|
2019-01-22 00:10:14 -05:00
|
|
|
this.getMainWindow = function () {
|
|
|
|
return Services.wm.getMostRecentWindow("navigator:browser");
|
|
|
|
};
|
2023-08-03 05:11:26 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {ChromeWindow[]} - An array of open windows
|
|
|
|
*/
|
|
|
|
this.getMainWindows = function () {
|
|
|
|
var enumerator = Services.wm.getEnumerator("navigator:browser");
|
|
|
|
var windows = [];
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
windows.push(enumerator.getNext());
|
|
|
|
}
|
|
|
|
return windows;
|
|
|
|
};
|
2019-01-22 00:10:14 -05:00
|
|
|
|
2011-01-31 20:28:42 +00:00
|
|
|
this.getActiveZoteroPane = function() {
|
Data directory migration
This adds a new button to the Advanced prefs to migrate the data directory to
$HOME/Zotero. The button only appears if the data directory is set to the
default location within a profile directory (including the other program from
the one running, even though that's technically stored as a custom data
directory).
On Mac/Linux, directories within the data directory are moved with /bin/mv. On
Windows, or if that fails, they're copied recursively using OS.File.move()
(which annoyingly doesn't reliably support directory moving). The former should
be instantaneous on most systems (unless the data directory or 'storage' were
on a different filesystem from $HOME).
If the database fails to transfer, migration fails and the data directory
setting remains on the old directory. If the database transfers but other files
fail, the data directory setting is updated. In both cases, the user is
encouraged to migrate remaining files manually with a button that reveals the
directories and quits the program.
This isn't yet tested on Linux or Windows, and migration isn't yet suggested
automatically.
Adds Zotero.File.reveal(), Zotero.File.directoryIsEmpty(), and
Zotero.File.moveDirectory().
2016-11-12 14:20:34 -05:00
|
|
|
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
|
|
|
return win ? win.ZoteroPane : null;
|
2011-01-31 20:28:42 +00:00
|
|
|
};
|
|
|
|
|
2019-06-10 02:37:54 -04:00
|
|
|
this.getZoteroPanes = function () {
|
|
|
|
var enumerator = Services.wm.getEnumerator("navigator:browser");
|
|
|
|
var zps = [];
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
let win = enumerator.getNext();
|
|
|
|
if (!win.ZoteroPane) continue;
|
|
|
|
zps.push(win.ZoteroPane);
|
|
|
|
}
|
|
|
|
return zps;
|
|
|
|
};
|
|
|
|
|
2009-08-08 07:38:34 +00:00
|
|
|
/**
|
|
|
|
* @property {Boolean} locked Whether all Zotero panes are locked
|
|
|
|
* with an overlay
|
|
|
|
*/
|
2022-05-06 02:33:24 -04:00
|
|
|
Object.defineProperty(
|
|
|
|
this,
|
|
|
|
'locked',
|
|
|
|
{
|
|
|
|
get: () => _locked,
|
|
|
|
set: (lock) => {
|
|
|
|
var wasLocked = _locked;
|
|
|
|
_locked = lock;
|
|
|
|
|
|
|
|
if (!wasLocked && lock) {
|
|
|
|
this.unlockDeferred = Zotero.Promise.defer();
|
|
|
|
this.unlockPromise = this.unlockDeferred.promise;
|
|
|
|
}
|
|
|
|
else if (wasLocked && !lock) {
|
|
|
|
Zotero.debug("Running unlock callbacks");
|
|
|
|
this.unlockDeferred.resolve();
|
|
|
|
}
|
|
|
|
},
|
2022-06-09 03:00:54 -04:00
|
|
|
enumerable: true,
|
|
|
|
configurable: true
|
2013-08-11 20:51:16 -04:00
|
|
|
}
|
2022-05-06 02:33:24 -04:00
|
|
|
);
|
2009-08-08 07:38:34 +00:00
|
|
|
|
2020-03-13 17:17:02 -04:00
|
|
|
/**
|
|
|
|
* @property {Boolean} crashed - True if the application needs to be restarted
|
|
|
|
*/
|
|
|
|
this.crashed = false;
|
|
|
|
|
2011-07-03 04:15:49 +00:00
|
|
|
/**
|
|
|
|
* @property {Boolean} closing True if the application is closing.
|
|
|
|
*/
|
|
|
|
this.closing = false;
|
|
|
|
|
2013-08-11 20:51:16 -04:00
|
|
|
this.unlockDeferred;
|
|
|
|
this.unlockPromise;
|
2016-12-02 16:52:28 -05:00
|
|
|
this.initializationDeferred;
|
|
|
|
this.initializationPromise;
|
2013-08-11 20:51:16 -04:00
|
|
|
|
2016-07-21 14:07:27 -04:00
|
|
|
this.hiDPISuffix = "";
|
|
|
|
|
2007-10-23 07:11:59 +00:00
|
|
|
var _startupErrorHandler;
|
|
|
|
var _localizedStringBundle;
|
2009-08-09 19:39:32 +00:00
|
|
|
|
2013-08-11 20:51:16 -04:00
|
|
|
var _locked = false;
|
2011-06-14 00:36:21 +00:00
|
|
|
var _shutdownListeners = [];
|
2016-11-16 01:05:22 -05:00
|
|
|
var _progressMessage;
|
2009-08-09 19:39:32 +00:00
|
|
|
var _progressMeters;
|
2011-09-21 23:00:20 +00:00
|
|
|
var _progressPopup;
|
2009-08-09 19:39:32 +00:00
|
|
|
var _lastPercentage;
|
2006-06-15 06:13:02 +00:00
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
// whether we are waiting for another Zotero process to release its DB lock
|
|
|
|
var _waitingForDBLock = false;
|
|
|
|
|
2013-06-06 19:31:09 -04:00
|
|
|
/**
|
|
|
|
* Maintains nsITimers to be used when Zotero.wait() completes (to reduce performance penalty
|
|
|
|
* of initializing new objects)
|
|
|
|
*/
|
|
|
|
var _waitTimers = [];
|
|
|
|
|
2012-07-25 21:44:08 -04:00
|
|
|
/**
|
|
|
|
* Maintains nsITimerCallbacks to be used when Zotero.wait() completes
|
|
|
|
*/
|
|
|
|
var _waitTimerCallbacks = [];
|
2011-05-31 19:59:07 +00:00
|
|
|
|
2013-06-06 19:31:09 -04:00
|
|
|
/**
|
|
|
|
* Maintains running nsITimers in global scope, so that they don't disappear randomly
|
|
|
|
*/
|
2016-05-17 11:49:13 -04:00
|
|
|
var _runningTimers = new Map();
|
2013-06-06 19:31:09 -04:00
|
|
|
|
2016-12-02 16:52:28 -05:00
|
|
|
var _startupTime = new Date();
|
2012-02-13 20:42:32 -05:00
|
|
|
// Errors that were in the console at startup
|
|
|
|
var _startupErrors = [];
|
|
|
|
// Number of errors to maintain in the recent errors buffer
|
|
|
|
const ERROR_BUFFER_SIZE = 25;
|
|
|
|
// A rolling buffer of the last ERROR_BUFFER_SIZE errors
|
|
|
|
var _recentErrors = [];
|
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
/**
|
2006-02-21 17:01:06 +00:00
|
|
|
* Initialize the extension
|
2013-08-11 20:51:16 -04:00
|
|
|
*
|
2016-11-01 03:22:28 -04:00
|
|
|
* @return {Promise<Boolean>}
|
2006-02-21 17:01:06 +00:00
|
|
|
*/
|
2022-05-07 01:13:51 -04:00
|
|
|
this.init = async function (options) {
|
2007-10-23 07:11:59 +00:00
|
|
|
if (this.initialized || this.skipLoading) {
|
2006-05-27 00:20:27 +00:00
|
|
|
return false;
|
2006-03-20 21:47:22 +00:00
|
|
|
}
|
2006-05-27 00:20:27 +00:00
|
|
|
|
2016-12-02 16:52:28 -05:00
|
|
|
this.locked = true;
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
this.initializationDeferred = Zotero.Promise.defer();
|
2013-08-11 20:51:16 -04:00
|
|
|
this.initializationPromise = this.initializationDeferred.promise;
|
2016-12-02 16:52:28 -05:00
|
|
|
this.uiReadyDeferred = Zotero.Promise.defer();
|
|
|
|
this.uiReadyPromise = this.uiReadyDeferred.promise;
|
2013-08-11 20:51:16 -04:00
|
|
|
|
2013-11-30 01:55:48 -05:00
|
|
|
if (options) {
|
2016-11-12 22:06:19 -05:00
|
|
|
let opts = [
|
|
|
|
'openPane',
|
2016-11-13 04:54:57 -05:00
|
|
|
'test',
|
2016-11-12 22:06:19 -05:00
|
|
|
'automatedTest',
|
|
|
|
'skipBundledFiles'
|
|
|
|
];
|
|
|
|
opts.filter(opt => options[opt]).forEach(opt => this[opt] = true);
|
2017-09-11 03:49:06 -04:00
|
|
|
|
|
|
|
this.forceDataDir = options.forceDataDir;
|
2013-11-30 01:55:48 -05:00
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
|
2013-06-06 02:37:19 -04:00
|
|
|
this.mainThread = Services.tm.mainThread;
|
2009-08-09 10:51:12 +00:00
|
|
|
|
2015-02-23 03:31:52 -05:00
|
|
|
this.clientName = ZOTERO_CONFIG.CLIENT_NAME;
|
|
|
|
|
2018-07-05 17:35:16 +02:00
|
|
|
this.platformVersion = Services.appinfo.platformVersion;
|
|
|
|
this.platformMajorVersion = parseInt(this.platformVersion.match(/^[0-9]+/)[0]);
|
2010-11-02 21:39:54 +00:00
|
|
|
this.isFx = true;
|
2017-01-17 02:58:22 -05:00
|
|
|
this.isClient = true;
|
2023-07-11 05:34:04 -04:00
|
|
|
this.isStandalone = true;
|
2016-11-01 03:22:28 -04:00
|
|
|
|
2024-05-29 06:48:12 -04:00
|
|
|
this.version = Services.appinfo.version;
|
|
|
|
this.isBetaBuild = Zotero.version.includes('-beta');
|
|
|
|
this.isDevBuild = Zotero.version.includes('-dev');
|
|
|
|
this.isSourceBuild = Zotero.version.includes('SOURCE');
|
2016-11-01 03:22:28 -04:00
|
|
|
|
|
|
|
// OS platform
|
|
|
|
var win = Components.classes["@mozilla.org/appshell/appShellService;1"]
|
|
|
|
.getService(Components.interfaces.nsIAppShellService)
|
|
|
|
.hiddenDOMWindow;
|
2024-04-06 04:38:51 -04:00
|
|
|
var os = Services.appinfo.OS;
|
|
|
|
this.isMac = os == 'Darwin';
|
|
|
|
this.isWin = os == 'WINNT';
|
|
|
|
this.isLinux = os == 'Linux';
|
|
|
|
|
|
|
|
// aarch64, x86_64, x86
|
|
|
|
this.arch = Services.appinfo.XPCOMABI.split('-')[0];
|
2016-11-01 03:22:28 -04:00
|
|
|
|
|
|
|
// Browser
|
|
|
|
Zotero.browser = "g";
|
|
|
|
|
2024-01-26 07:32:34 -05:00
|
|
|
// TEMP: Disable automatic safe mode until we can figure out why some shutdowns are
|
|
|
|
// counting as crashes
|
|
|
|
var branch = Services.prefs.getBranch("toolkit.startup.");
|
|
|
|
if (branch.getIntPref('recent_crashes', 0) > 2) {
|
|
|
|
branch.clearUserPref('recent_crashes');
|
2023-07-11 05:34:04 -04:00
|
|
|
}
|
|
|
|
|
2018-07-30 10:00:53 +02:00
|
|
|
Zotero.Intl.init();
|
2019-10-27 03:38:08 -04:00
|
|
|
if (this.restarting) return;
|
2018-02-25 12:55:54 -05:00
|
|
|
|
2022-05-07 01:13:51 -04:00
|
|
|
await Zotero.Prefs.init();
|
2016-12-02 17:25:12 -05:00
|
|
|
Zotero.Debug.init(options && options.forceDebugLog);
|
|
|
|
|
2023-07-11 05:34:04 -04:00
|
|
|
// Make sure that Zotero isn't running as root
|
|
|
|
if (!Zotero.isWin) _checkRoot();
|
2016-11-01 03:22:28 -04:00
|
|
|
|
2018-09-06 00:53:50 -04:00
|
|
|
if (!_checkExecutableLocation()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-01 03:22:28 -04:00
|
|
|
try {
|
2022-05-07 01:13:51 -04:00
|
|
|
await Zotero.DataDirectory.init();
|
2017-09-12 02:35:06 -04:00
|
|
|
if (this.restarting) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-26 22:41:26 -05:00
|
|
|
var dataDir = Zotero.DataDirectory.dir;
|
2016-11-01 03:22:28 -04:00
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
// Zotero dir not found
|
2023-12-04 05:05:34 -05:00
|
|
|
if (e.name == 'NotFoundError') {
|
2017-07-31 05:40:34 -04:00
|
|
|
let foundInDefault = false;
|
|
|
|
try {
|
2022-05-07 01:13:51 -04:00
|
|
|
foundInDefault = (await OS.File.exists(Zotero.DataDirectory.defaultDir))
|
|
|
|
&& (await OS.File.exists(
|
2017-07-31 05:40:34 -04:00
|
|
|
OS.Path.join(
|
|
|
|
Zotero.DataDirectory.defaultDir,
|
|
|
|
Zotero.DataDirectory.getDatabaseFilename()
|
|
|
|
)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
|
|
|
|
2017-09-14 18:49:01 -04:00
|
|
|
let previousDir = Zotero.Prefs.get('lastDataDir')
|
|
|
|
|| Zotero.Prefs.get('dataDir')
|
|
|
|
|| e.dataDir;
|
2017-07-31 05:40:34 -04:00
|
|
|
Zotero.startupError = foundInDefault
|
|
|
|
? Zotero.getString(
|
|
|
|
'dataDir.notFound.defaultFound',
|
|
|
|
[
|
|
|
|
Zotero.clientName,
|
|
|
|
previousDir,
|
|
|
|
Zotero.DataDirectory.defaultDir
|
|
|
|
]
|
|
|
|
)
|
|
|
|
: Zotero.getString('dataDir.notFound', Zotero.clientName);
|
2024-02-14 13:22:35 +02:00
|
|
|
_startupErrorHandler = async function() {
|
2023-12-04 05:19:57 -05:00
|
|
|
var ps = Services.prompt;
|
2017-07-31 05:40:34 -04:00
|
|
|
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
|
|
|
|
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING
|
|
|
|
+ ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING;
|
2016-11-01 03:22:28 -04:00
|
|
|
// TEMP: lastDataDir can be removed once old persistent descriptors have been
|
|
|
|
// converted, which they are in getZoteroDirectory() in 5.0
|
2017-07-31 05:40:34 -04:00
|
|
|
if (foundInDefault) {
|
|
|
|
let index = ps.confirmEx(null,
|
|
|
|
Zotero.getString('general.error'),
|
|
|
|
Zotero.startupError,
|
|
|
|
buttonFlags,
|
|
|
|
Zotero.getString('dataDir.useNewLocation'),
|
|
|
|
Zotero.getString('general.quit'),
|
|
|
|
Zotero.getString('general.locate'),
|
|
|
|
null, {}
|
|
|
|
);
|
|
|
|
// Revert to home directory
|
|
|
|
if (index == 0) {
|
|
|
|
Zotero.DataDirectory.set(Zotero.DataDirectory.defaultDir);
|
|
|
|
Zotero.Utilities.Internal.quit(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Locate data directory
|
|
|
|
else if (index == 2) {
|
2024-02-14 13:22:35 +02:00
|
|
|
await Zotero.DataDirectory.choose(true);
|
2017-07-31 05:40:34 -04:00
|
|
|
}
|
|
|
|
|
2013-08-12 03:06:54 -04:00
|
|
|
}
|
2017-07-31 05:40:34 -04:00
|
|
|
else {
|
|
|
|
let index = ps.confirmEx(null,
|
|
|
|
Zotero.getString('general.error'),
|
2017-09-14 18:49:01 -04:00
|
|
|
Zotero.startupError
|
|
|
|
+ (previousDir
|
|
|
|
? '\n\n' + Zotero.getString('dataDir.previousDir') + ' ' + previousDir
|
|
|
|
: ''),
|
2017-07-31 05:40:34 -04:00
|
|
|
buttonFlags,
|
|
|
|
Zotero.getString('general.quit'),
|
|
|
|
Zotero.getString('dataDir.useDefaultLocation'),
|
|
|
|
Zotero.getString('general.locate'),
|
|
|
|
null, {}
|
|
|
|
);
|
|
|
|
// Revert to home directory
|
|
|
|
if (index == 1) {
|
|
|
|
Zotero.DataDirectory.set(Zotero.DataDirectory.defaultDir);
|
|
|
|
Zotero.Utilities.Internal.quit(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Locate data directory
|
|
|
|
else if (index == 2) {
|
2024-02-14 13:22:35 +02:00
|
|
|
await Zotero.DataDirectory.choose(true);
|
2017-07-31 05:40:34 -04:00
|
|
|
}
|
2013-08-12 03:06:54 -04:00
|
|
|
}
|
2010-09-20 02:24:07 +00:00
|
|
|
}
|
2016-11-01 03:22:28 -04:00
|
|
|
return;
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2016-11-01 03:22:28 -04:00
|
|
|
// DEBUG: handle more startup errors
|
|
|
|
else {
|
Data directory migration
This adds a new button to the Advanced prefs to migrate the data directory to
$HOME/Zotero. The button only appears if the data directory is set to the
default location within a profile directory (including the other program from
the one running, even though that's technically stored as a custom data
directory).
On Mac/Linux, directories within the data directory are moved with /bin/mv. On
Windows, or if that fails, they're copied recursively using OS.File.move()
(which annoyingly doesn't reliably support directory moving). The former should
be instantaneous on most systems (unless the data directory or 'storage' were
on a different filesystem from $HOME).
If the database fails to transfer, migration fails and the data directory
setting remains on the old directory. If the database transfers but other files
fail, the data directory setting is updated. In both cases, the user is
encouraged to migrate remaining files manually with a button that reveals the
directories and quits the program.
This isn't yet tested on Linux or Windows, and migration isn't yet suggested
automatically.
Adds Zotero.File.reveal(), Zotero.File.directoryIsEmpty(), and
Zotero.File.moveDirectory().
2016-11-12 14:20:34 -05:00
|
|
|
throw e;
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2016-11-01 03:22:28 -04:00
|
|
|
}
|
|
|
|
|
2018-08-17 02:18:35 -04:00
|
|
|
if (!this.forceDataDir) {
|
2022-05-07 01:13:51 -04:00
|
|
|
await Zotero.DataDirectory.checkForMigration(
|
2018-08-17 02:18:35 -04:00
|
|
|
dataDir, Zotero.DataDirectory.defaultDir
|
|
|
|
);
|
|
|
|
if (this.skipLoading) {
|
|
|
|
return;
|
Data directory migration
This adds a new button to the Advanced prefs to migrate the data directory to
$HOME/Zotero. The button only appears if the data directory is set to the
default location within a profile directory (including the other program from
the one running, even though that's technically stored as a custom data
directory).
On Mac/Linux, directories within the data directory are moved with /bin/mv. On
Windows, or if that fails, they're copied recursively using OS.File.move()
(which annoyingly doesn't reliably support directory moving). The former should
be instantaneous on most systems (unless the data directory or 'storage' were
on a different filesystem from $HOME).
If the database fails to transfer, migration fails and the data directory
setting remains on the old directory. If the database transfers but other files
fail, the data directory setting is updated. In both cases, the user is
encouraged to migrate remaining files manually with a button that reveals the
directories and quits the program.
This isn't yet tested on Linux or Windows, and migration isn't yet suggested
automatically.
Adds Zotero.File.reveal(), Zotero.File.directoryIsEmpty(), and
Zotero.File.moveDirectory().
2016-11-12 14:20:34 -05:00
|
|
|
}
|
|
|
|
|
2022-05-07 01:13:51 -04:00
|
|
|
await Zotero.DataDirectory.checkForLostLegacy();
|
2018-08-17 02:18:35 -04:00
|
|
|
if (this.restarting) {
|
|
|
|
return;
|
Data directory migration
This adds a new button to the Advanced prefs to migrate the data directory to
$HOME/Zotero. The button only appears if the data directory is set to the
default location within a profile directory (including the other program from
the one running, even though that's technically stored as a custom data
directory).
On Mac/Linux, directories within the data directory are moved with /bin/mv. On
Windows, or if that fails, they're copied recursively using OS.File.move()
(which annoyingly doesn't reliably support directory moving). The former should
be instantaneous on most systems (unless the data directory or 'storage' were
on a different filesystem from $HOME).
If the database fails to transfer, migration fails and the data directory
setting remains on the old directory. If the database transfers but other files
fail, the data directory setting is updated. In both cases, the user is
encouraged to migrate remaining files manually with a button that reveals the
directories and quits the program.
This isn't yet tested on Linux or Windows, and migration isn't yet suggested
automatically.
Adds Zotero.File.reveal(), Zotero.File.directoryIsEmpty(), and
Zotero.File.moveDirectory().
2016-11-12 14:20:34 -05:00
|
|
|
}
|
2016-11-01 03:22:28 -04:00
|
|
|
}
|
Data directory migration
This adds a new button to the Advanced prefs to migrate the data directory to
$HOME/Zotero. The button only appears if the data directory is set to the
default location within a profile directory (including the other program from
the one running, even though that's technically stored as a custom data
directory).
On Mac/Linux, directories within the data directory are moved with /bin/mv. On
Windows, or if that fails, they're copied recursively using OS.File.move()
(which annoyingly doesn't reliably support directory moving). The former should
be instantaneous on most systems (unless the data directory or 'storage' were
on a different filesystem from $HOME).
If the database fails to transfer, migration fails and the data directory
setting remains on the old directory. If the database transfers but other files
fail, the data directory setting is updated. In both cases, the user is
encouraged to migrate remaining files manually with a button that reveals the
directories and quits the program.
This isn't yet tested on Linux or Windows, and migration isn't yet suggested
automatically.
Adds Zotero.File.reveal(), Zotero.File.directoryIsEmpty(), and
Zotero.File.moveDirectory().
2016-11-12 14:20:34 -05:00
|
|
|
|
2018-08-17 02:18:35 -04:00
|
|
|
// Make sure data directory isn't in Dropbox, etc.
|
2022-05-07 01:13:51 -04:00
|
|
|
await Zotero.DataDirectory.checkForUnsafeLocation(dataDir);
|
2018-08-17 02:18:35 -04:00
|
|
|
|
2021-08-23 20:02:57 +03:00
|
|
|
Services.obs.addObserver({
|
|
|
|
observe: function () {
|
|
|
|
Zotero.Session.save();
|
|
|
|
}
|
|
|
|
}, "quit-application-granted", false);
|
|
|
|
|
2016-11-01 03:22:28 -04:00
|
|
|
// Register shutdown handler to call Zotero.shutdown()
|
|
|
|
var _shutdownObserver = {observe:function() { Zotero.shutdown().done() }};
|
|
|
|
Services.obs.addObserver(_shutdownObserver, "quit-application", false);
|
|
|
|
|
|
|
|
// Get startup errors
|
|
|
|
try {
|
2020-07-05 18:15:13 -04:00
|
|
|
let messages = Services.console.getMessageArray();
|
|
|
|
_startupErrors = messages.filter(msg => _shouldKeepError(msg));
|
2016-11-01 03:22:28 -04:00
|
|
|
} catch(e) {
|
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
|
|
|
// Register error observer
|
|
|
|
Services.console.registerListener(ConsoleListener);
|
|
|
|
|
|
|
|
// Add shutdown listener to remove quit-application observer and console listener
|
|
|
|
this.addShutdownListener(function() {
|
|
|
|
Services.obs.removeObserver(_shutdownObserver, "quit-application", false);
|
|
|
|
Services.console.unregisterListener(ConsoleListener);
|
|
|
|
});
|
|
|
|
|
2022-08-05 00:43:42 -04:00
|
|
|
var success = await _initFull();
|
|
|
|
if (!success) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-17 02:18:35 -04:00
|
|
|
|
2023-07-11 05:34:04 -04:00
|
|
|
Zotero.Standalone.init();
|
2022-08-05 00:43:42 -04:00
|
|
|
await Zotero.initComplete();
|
2024-03-27 11:54:53 +02:00
|
|
|
// Ingest command line arguments that were not handled due to late command line handler registration.
|
|
|
|
Zotero.CommandLineIngester.ingest();
|
2022-05-07 01:13:51 -04:00
|
|
|
};
|
2011-09-20 21:59:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Triggers events when initialization finishes
|
|
|
|
*/
|
2022-08-05 00:43:42 -04:00
|
|
|
this.initComplete = async function() {
|
2011-09-20 21:59:28 +00:00
|
|
|
if(Zotero.initialized) return;
|
2013-08-11 20:51:16 -04:00
|
|
|
|
|
|
|
Zotero.debug("Running initialization callbacks");
|
2013-11-05 15:52:40 -05:00
|
|
|
delete this.startupError;
|
2011-06-14 00:36:21 +00:00
|
|
|
this.initialized = true;
|
2013-08-11 20:51:16 -04:00
|
|
|
this.initializationDeferred.resolve();
|
2011-06-14 00:36:21 +00:00
|
|
|
|
|
|
|
if(!Zotero.isFirstLoadThisSession) {
|
|
|
|
// trigger zotero-reloaded event
|
|
|
|
Zotero.debug('Triggering "zotero-reloaded" event');
|
2013-06-05 17:54:40 -04:00
|
|
|
Services.obs.notifyObservers(Zotero, "zotero-reloaded", null);
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
|
|
|
|
2011-08-16 18:28:55 +00:00
|
|
|
Zotero.debug('Triggering "zotero-loaded" event');
|
2013-06-05 17:54:40 -04:00
|
|
|
Services.obs.notifyObservers(Zotero, "zotero-loaded", null);
|
2022-08-05 00:43:42 -04:00
|
|
|
|
2022-10-06 07:17:28 +03:00
|
|
|
Zotero.debug('Initializing Word Processor plugins');
|
|
|
|
Zotero.Integration.init();
|
2022-08-05 00:43:42 -04:00
|
|
|
await Zotero.Plugins.init();
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
|
|
|
|
2016-11-10 00:55:30 -05:00
|
|
|
|
2016-12-02 16:52:28 -05:00
|
|
|
this.uiIsReady = function () {
|
|
|
|
if (this.uiReadyPromise.isPending()) {
|
|
|
|
Zotero.debug("User interface ready in " + (new Date() - _startupTime) + " ms");
|
|
|
|
this.uiReadyDeferred.resolve();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
/**
|
|
|
|
* Initialization function to be called only if Zotero is in full mode
|
2013-08-11 20:51:16 -04:00
|
|
|
*
|
|
|
|
* @return {Promise:Boolean}
|
2011-06-14 00:36:21 +00:00
|
|
|
*/
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
var _initFull = Zotero.Promise.coroutine(function* () {
|
2016-11-12 22:06:40 -05:00
|
|
|
if (!(yield _initDB())) return false;
|
|
|
|
|
2008-09-16 20:11:27 +00:00
|
|
|
Zotero.VersionHeader.init();
|
|
|
|
|
2016-06-27 19:40:38 +03:00
|
|
|
// Check for data reset/restore
|
2016-11-26 22:41:26 -05:00
|
|
|
var dataDir = Zotero.DataDirectory.dir;
|
|
|
|
var restoreFile = OS.Path.join(dataDir, 'restore-from-server');
|
|
|
|
var resetDataDirFile = OS.Path.join(dataDir, 'reset-data-directory');
|
2016-06-27 19:40:38 +03:00
|
|
|
|
|
|
|
var result = yield Zotero.Promise.all([OS.File.exists(restoreFile), OS.File.exists(resetDataDirFile)]);
|
|
|
|
if (result.some(r => r)) {
|
|
|
|
[Zotero.restoreFromServer, Zotero.resetDataDir] = result;
|
2009-01-02 00:35:09 +00:00
|
|
|
try {
|
2017-07-12 01:16:25 -04:00
|
|
|
yield Zotero.DB.closeDatabase();
|
|
|
|
|
2009-01-02 00:35:09 +00:00
|
|
|
// TODO: better error handling
|
|
|
|
|
|
|
|
// TODO: prompt for location
|
|
|
|
// TODO: Back up database
|
2017-07-12 01:16:25 -04:00
|
|
|
// TODO: Reset translators and styles
|
2009-01-02 00:35:09 +00:00
|
|
|
|
|
|
|
|
2016-06-27 19:40:38 +03:00
|
|
|
|
|
|
|
if (Zotero.restoreFromServer) {
|
2017-07-12 01:16:25 -04:00
|
|
|
let dbfile = Zotero.DataDirectory.getDatabase();
|
|
|
|
Zotero.debug("Deleting " + dbfile);
|
|
|
|
yield OS.File.remove(dbfile, { ignoreAbsent: true });
|
|
|
|
let storageDir = OS.Path.join(dataDir, 'storage');
|
|
|
|
Zotero.debug("Deleting " + storageDir.path);
|
|
|
|
OS.File.removeDir(storageDir, { ignoreAbsent: true }),
|
2016-06-27 19:40:38 +03:00
|
|
|
yield OS.File.remove(restoreFile);
|
|
|
|
Zotero.restoreFromServer = true;
|
2017-07-12 01:16:25 -04:00
|
|
|
}
|
|
|
|
else if (Zotero.resetDataDir) {
|
2016-06-27 19:40:38 +03:00
|
|
|
Zotero.initAutoSync = true;
|
2017-07-12 01:16:25 -04:00
|
|
|
|
|
|
|
// Clear some user prefs
|
|
|
|
[
|
|
|
|
'sync.server.username',
|
|
|
|
'sync.storage.username'
|
|
|
|
].forEach(p => Zotero.Prefs.clear(p));
|
|
|
|
|
|
|
|
// Clear data directory
|
|
|
|
Zotero.debug("Deleting data directory files");
|
|
|
|
let lastError;
|
|
|
|
// Delete all files in directory rather than removing directory, in case it's
|
|
|
|
// a symlink
|
2018-09-26 02:34:42 -04:00
|
|
|
yield Zotero.File.iterateDirectory(dataDir, async function (entry) {
|
|
|
|
// Don't delete some files
|
|
|
|
if (entry.name == 'pipes') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Zotero.debug("Deleting " + entry.path);
|
|
|
|
try {
|
|
|
|
if (entry.isDir) {
|
|
|
|
await OS.File.removeDir(entry.path);
|
2017-07-12 01:16:25 -04:00
|
|
|
}
|
2018-09-26 02:34:42 -04:00
|
|
|
else {
|
|
|
|
await OS.File.remove(entry.path);
|
2017-07-12 01:16:25 -04:00
|
|
|
}
|
|
|
|
}
|
2018-09-26 02:34:42 -04:00
|
|
|
// Keep trying to delete as much as we can
|
|
|
|
catch (e) {
|
|
|
|
lastError = e;
|
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
2017-07-12 01:16:25 -04:00
|
|
|
});
|
|
|
|
if (lastError) {
|
|
|
|
throw lastError;
|
|
|
|
}
|
2016-06-27 19:40:38 +03:00
|
|
|
}
|
2017-07-12 01:16:25 -04:00
|
|
|
Zotero.debug("Done with reset");
|
2009-01-02 00:35:09 +00:00
|
|
|
|
2017-07-12 01:16:25 -04:00
|
|
|
if (!(yield _initDB())) return false;
|
2009-01-02 00:35:09 +00:00
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
// Restore from backup?
|
|
|
|
alert(e);
|
2016-11-10 00:55:30 -05:00
|
|
|
return false;
|
2009-01-02 00:35:09 +00:00
|
|
|
}
|
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
|
2013-05-01 06:29:31 -04:00
|
|
|
Zotero.HTTP.triggerProxyAuth();
|
|
|
|
|
2007-10-23 07:11:59 +00:00
|
|
|
// Add notifier queue callbacks to the DB layer
|
2018-02-08 02:07:44 -05:00
|
|
|
Zotero.DB.addCallback('begin', id => Zotero.Notifier.begin(id));
|
|
|
|
Zotero.DB.addCallback('commit', id => Zotero.Notifier.commit(null, id));
|
|
|
|
Zotero.DB.addCallback('rollback', id => Zotero.Notifier.reset(id));
|
2007-10-23 07:11:59 +00:00
|
|
|
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
try {
|
2013-08-11 20:51:16 -04:00
|
|
|
// Require >=2.1b3 database to ensure proper locking
|
2023-07-11 05:34:04 -04:00
|
|
|
let dbSystemVersion = yield Zotero.Schema.getDBVersion('system');
|
|
|
|
if (dbSystemVersion > 0 && dbSystemVersion < 31) {
|
2023-12-04 05:19:57 -05:00
|
|
|
let ps = Services.prompt;
|
2023-07-11 05:34:04 -04:00
|
|
|
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
|
|
|
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING)
|
|
|
|
+ (ps.BUTTON_POS_2) * (ps.BUTTON_TITLE_IS_STRING)
|
|
|
|
+ ps.BUTTON_POS_2_DEFAULT;
|
|
|
|
var index = ps.confirmEx(
|
|
|
|
null,
|
|
|
|
Zotero.getString('dataDir.incompatibleDbVersion.title'),
|
|
|
|
Zotero.getString('dataDir.incompatibleDbVersion.text'),
|
|
|
|
buttonFlags,
|
|
|
|
Zotero.getString('general.useDefault'),
|
|
|
|
Zotero.getString('dataDir.chooseNewDataDirectory'),
|
|
|
|
Zotero.getString('general.quit'),
|
|
|
|
null,
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
var quit = false;
|
|
|
|
|
|
|
|
// Default location
|
|
|
|
if (index == 0) {
|
|
|
|
Zotero.Prefs.set("useDataDir", false)
|
2013-08-11 20:51:16 -04:00
|
|
|
|
2023-07-11 05:34:04 -04:00
|
|
|
Services.startup.quit(
|
|
|
|
Components.interfaces.nsIAppStartup.eAttemptQuit
|
|
|
|
| Components.interfaces.nsIAppStartup.eRestart
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// Select new data directory
|
|
|
|
else if (index == 1) {
|
|
|
|
let dir = yield Zotero.DataDirectory.choose(true);
|
|
|
|
if (!dir) {
|
2013-08-11 20:51:16 -04:00
|
|
|
quit = true;
|
|
|
|
}
|
|
|
|
}
|
2023-07-11 05:34:04 -04:00
|
|
|
else {
|
|
|
|
quit = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (quit) {
|
|
|
|
Services.startup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw true;
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2016-11-16 12:41:49 -05:00
|
|
|
var updated = yield Zotero.Schema.updateSchema({
|
2017-07-30 00:04:33 -04:00
|
|
|
onBeforeUpdate: (options = {}) => {
|
|
|
|
if (options.minor) return;
|
2016-11-22 03:41:15 -05:00
|
|
|
try {
|
|
|
|
Zotero.showZoteroPaneProgressMeter(
|
|
|
|
Zotero.getString('upgrade.status')
|
|
|
|
)
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
|
|
|
}
|
2016-11-16 12:41:49 -05:00
|
|
|
});
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
}
|
|
|
|
catch (e) {
|
2015-12-30 04:31:57 -05:00
|
|
|
Zotero.logError(e);
|
2013-11-24 16:08:46 -05:00
|
|
|
|
2015-12-30 04:31:57 -05:00
|
|
|
if (e instanceof Zotero.DB.IncompatibleVersionException) {
|
2015-03-30 14:45:53 -04:00
|
|
|
let kbURL = "https://www.zotero.org/support/kb/newer_db_version";
|
2016-02-06 04:59:15 -05:00
|
|
|
let msg = (e.dbClientVersion
|
|
|
|
? Zotero.getString('startupError.incompatibleDBVersion',
|
|
|
|
[Zotero.clientName, e.dbClientVersion])
|
|
|
|
: Zotero.getString('startupError.zoteroVersionIsOlder')) + "\n\n"
|
2015-04-15 16:03:47 -04:00
|
|
|
+ Zotero.getString('startupError.zoteroVersionIsOlder.current', Zotero.version)
|
2015-12-30 04:31:57 -05:00
|
|
|
+ "\n\n"
|
|
|
|
+ Zotero.getString('startupError.zoteroVersionIsOlder.upgrade',
|
|
|
|
ZOTERO_CONFIG.DOMAIN_NAME);
|
2011-06-14 00:36:21 +00:00
|
|
|
Zotero.startupError = msg;
|
2013-11-07 05:33:39 -06:00
|
|
|
_startupErrorHandler = function() {
|
2023-12-04 05:19:57 -05:00
|
|
|
var ps = Services.prompt;
|
2013-11-07 05:33:39 -06:00
|
|
|
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
2013-11-13 00:31:45 -05:00
|
|
|
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
|
2015-12-30 04:31:57 -05:00
|
|
|
+ (ps.BUTTON_POS_2) * (ps.BUTTON_TITLE_IS_STRING)
|
2013-11-07 05:33:39 -06:00
|
|
|
+ ps.BUTTON_POS_0_DEFAULT;
|
|
|
|
|
2013-11-13 00:31:45 -05:00
|
|
|
var index = ps.confirmEx(
|
|
|
|
null,
|
|
|
|
Zotero.getString('general.error'),
|
|
|
|
Zotero.startupError,
|
|
|
|
buttonFlags,
|
2019-05-19 21:30:20 -04:00
|
|
|
Zotero.getString('general.checkForUpdates'),
|
2015-12-30 04:31:57 -05:00
|
|
|
null,
|
|
|
|
Zotero.getString('general.moreInformation'),
|
|
|
|
null,
|
|
|
|
{}
|
2013-11-13 00:31:45 -05:00
|
|
|
);
|
2013-11-07 05:33:39 -06:00
|
|
|
|
2015-12-30 04:31:57 -05:00
|
|
|
// "Check for Update" button
|
2019-05-19 21:30:20 -04:00
|
|
|
if (index === 0) {
|
2024-04-02 06:42:35 -04:00
|
|
|
Zotero.openCheckForUpdatesWindow({ modal: true });
|
2013-11-07 05:33:39 -06:00
|
|
|
}
|
2015-12-30 04:31:57 -05:00
|
|
|
// Load More Info page
|
|
|
|
else if (index == 2) {
|
2018-07-27 16:44:28 +02:00
|
|
|
let uri = Services.io.newURI(kbURL, null, null);
|
2015-12-30 04:31:57 -05:00
|
|
|
let handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
|
|
|
|
.getService(Components.interfaces.nsIExternalProtocolService)
|
|
|
|
.getProtocolHandlerInfo('http');
|
|
|
|
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
|
|
|
|
handler.launchWithURI(uri, null);
|
|
|
|
}
|
2013-11-07 05:33:39 -06:00
|
|
|
};
|
2015-12-30 04:31:57 -05:00
|
|
|
throw e;
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2013-11-24 16:08:46 -05:00
|
|
|
|
2017-08-05 01:09:57 +02:00
|
|
|
let stack = e.stack ? Zotero.Utilities.Internal.filterStack(e.stack) : null;
|
|
|
|
Zotero.startupError = Zotero.getString('startupError.databaseUpgradeError')
|
|
|
|
+ "\n\n"
|
|
|
|
+ (stack || e);
|
2015-12-30 04:31:57 -05:00
|
|
|
throw e;
|
2016-11-10 00:55:30 -05:00
|
|
|
}
|
|
|
|
|
2023-12-14 15:01:55 -05:00
|
|
|
const { ZoteroProtocolHandler } = ChromeUtils.import(
|
|
|
|
`chrome://zotero/content/ZoteroProtocolHandler.jsm`
|
|
|
|
);
|
|
|
|
ZoteroProtocolHandler.init();
|
|
|
|
|
2024-03-03 23:55:32 -05:00
|
|
|
const { ZoteroAutoComplete } = ChromeUtils.import(
|
|
|
|
`chrome://zotero/content/zotero-autocomplete.js`
|
|
|
|
);
|
|
|
|
ZoteroAutoComplete.init();
|
|
|
|
|
2016-11-10 00:55:30 -05:00
|
|
|
yield Zotero.Users.init();
|
|
|
|
yield Zotero.Libraries.init();
|
|
|
|
|
Type/field handling overhaul
This changes the way item types, item fields, creator types, and CSL
mappings are defined and handled, in preparation for updated types and
fields.
Instead of being predefined in SQL files or code, type/field info is
read from a bundled JSON file shared with other parts of the Zotero
ecosystem [1], referred to as the "global schema". Updates to the
bundled schema file are automatically applied to the database at first
run, allowing changes to be made consistently across apps.
When syncing, invalid JSON properties are now rejected instead of being
ignored and processed later, which will allow for schema changes to be
made without causing problems in existing clients. We considered many
alternative approaches, but this approach is by far the simplest,
safest, and most transparent to the user.
For now, there are no actual changes to types and fields, since we'll
first need to do a sync cut-off for earlier versions that don't reject
invalid properties.
For third-party code, the main change is that type and field IDs should
no longer be hard-coded, since they may not be consistent in new
installs. For example, code should use `Zotero.ItemTypes.getID('note')`
instead of hard-coding `1`.
[1] https://github.com/zotero/zotero-schema
2019-05-16 04:56:46 -04:00
|
|
|
yield Zotero.ID.init();
|
2016-11-10 00:55:30 -05:00
|
|
|
yield Zotero.ItemTypes.init();
|
|
|
|
yield Zotero.ItemFields.init();
|
|
|
|
yield Zotero.CreatorTypes.init();
|
|
|
|
yield Zotero.FileTypes.init();
|
|
|
|
yield Zotero.CharacterSets.init();
|
|
|
|
yield Zotero.RelationPredicates.init();
|
|
|
|
|
2021-08-23 20:02:57 +03:00
|
|
|
yield Zotero.Session.init();
|
|
|
|
|
2016-11-10 00:55:30 -05:00
|
|
|
Zotero.locked = false;
|
|
|
|
|
|
|
|
// Initialize various services
|
|
|
|
if(Zotero.Prefs.get("httpServer.enabled")) {
|
|
|
|
Zotero.Server.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
yield Zotero.Fulltext.init();
|
|
|
|
|
|
|
|
Zotero.Notifier.registerObserver(Zotero.Tags, 'setting', 'tags');
|
|
|
|
|
|
|
|
yield Zotero.Sync.Data.Local.init();
|
|
|
|
yield Zotero.Sync.Data.Utilities.init();
|
2019-06-22 05:28:02 -04:00
|
|
|
Zotero.Sync.Storage.Local.init();
|
2016-11-10 00:55:30 -05:00
|
|
|
Zotero.Sync.Runner = new Zotero.Sync.Runner_Module;
|
2016-12-29 21:24:08 -05:00
|
|
|
Zotero.Sync.EventListeners.init();
|
2017-07-04 18:03:13 -04:00
|
|
|
Zotero.Streamer = new Zotero.Streamer_Module;
|
|
|
|
Zotero.Streamer.init();
|
2016-11-10 00:55:30 -05:00
|
|
|
|
|
|
|
Zotero.MIMETypeHandler.init();
|
|
|
|
yield Zotero.Proxies.init();
|
|
|
|
|
|
|
|
// Initialize keyboard shortcuts
|
|
|
|
Zotero.Keys.init();
|
|
|
|
|
2018-07-05 18:12:22 +02:00
|
|
|
Zotero.Date.init();
|
2016-11-10 00:55:30 -05:00
|
|
|
Zotero.LocateManager.init();
|
|
|
|
yield Zotero.Collections.init();
|
|
|
|
yield Zotero.Items.init();
|
|
|
|
yield Zotero.Searches.init();
|
|
|
|
yield Zotero.Tags.init();
|
|
|
|
yield Zotero.Creators.init();
|
|
|
|
yield Zotero.Groups.init();
|
|
|
|
yield Zotero.Relations.init();
|
2019-06-06 08:47:43 -04:00
|
|
|
yield Zotero.Retractions.init();
|
2021-05-07 10:18:14 +03:00
|
|
|
yield Zotero.Dictionaries.init();
|
2021-08-23 20:02:57 +03:00
|
|
|
Zotero.Reader.init();
|
2016-11-10 00:55:30 -05:00
|
|
|
|
Type/field handling overhaul
This changes the way item types, item fields, creator types, and CSL
mappings are defined and handled, in preparation for updated types and
fields.
Instead of being predefined in SQL files or code, type/field info is
read from a bundled JSON file shared with other parts of the Zotero
ecosystem [1], referred to as the "global schema". Updates to the
bundled schema file are automatically applied to the database at first
run, allowing changes to be made consistently across apps.
When syncing, invalid JSON properties are now rejected instead of being
ignored and processed later, which will allow for schema changes to be
made without causing problems in existing clients. We considered many
alternative approaches, but this approach is by far the simplest,
safest, and most transparent to the user.
For now, there are no actual changes to types and fields, since we'll
first need to do a sync cut-off for earlier versions that don't reject
invalid properties.
For third-party code, the main change is that type and field IDs should
no longer be hard-coded, since they may not be consistent in new
installs. For example, code should use `Zotero.ItemTypes.getID('note')`
instead of hard-coding `1`.
[1] https://github.com/zotero/zotero-schema
2019-05-16 04:56:46 -04:00
|
|
|
// Migrate fields from Extra that can be moved to item fields after a schema update
|
2020-03-22 15:24:46 -04:00
|
|
|
yield Zotero.Schema.migrateExtraFields();
|
Type/field handling overhaul
This changes the way item types, item fields, creator types, and CSL
mappings are defined and handled, in preparation for updated types and
fields.
Instead of being predefined in SQL files or code, type/field info is
read from a bundled JSON file shared with other parts of the Zotero
ecosystem [1], referred to as the "global schema". Updates to the
bundled schema file are automatically applied to the database at first
run, allowing changes to be made consistently across apps.
When syncing, invalid JSON properties are now rejected instead of being
ignored and processed later, which will allow for schema changes to be
made without causing problems in existing clients. We considered many
alternative approaches, but this approach is by far the simplest,
safest, and most transparent to the user.
For now, there are no actual changes to types and fields, since we'll
first need to do a sync cut-off for earlier versions that don't reject
invalid properties.
For third-party code, the main change is that type and field IDs should
no longer be hard-coded, since they may not be consistent in new
installs. For example, code should use `Zotero.ItemTypes.getID('note')`
instead of hard-coding `1`.
[1] https://github.com/zotero/zotero-schema
2019-05-16 04:56:46 -04:00
|
|
|
|
2016-11-10 00:55:30 -05:00
|
|
|
// Load all library data except for items, which are loaded when libraries are first
|
|
|
|
// clicked on or if otherwise necessary
|
|
|
|
yield Zotero.Promise.each(
|
|
|
|
Zotero.Libraries.getAll(),
|
|
|
|
library => Zotero.Promise.coroutine(function* () {
|
|
|
|
yield Zotero.SyncedSettings.loadAll(library.libraryID);
|
|
|
|
if (library.libraryType != 'feed') {
|
|
|
|
yield Zotero.Collections.loadAll(library.libraryID);
|
|
|
|
yield Zotero.Searches.loadAll(library.libraryID);
|
|
|
|
}
|
|
|
|
})()
|
|
|
|
);
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
|
2016-05-13 01:20:57 -04:00
|
|
|
Zotero.Items.startEmptyTrashTimer();
|
2016-06-23 05:36:38 -04:00
|
|
|
|
|
|
|
yield Zotero.QuickCopy.init();
|
|
|
|
Zotero.addShutdownListener(() => Zotero.QuickCopy.uninit());
|
|
|
|
|
2016-05-13 01:20:57 -04:00
|
|
|
Zotero.Feeds.init();
|
2016-05-17 11:50:26 -04:00
|
|
|
Zotero.addShutdownListener(() => Zotero.Feeds.uninit());
|
2016-05-13 01:20:57 -04:00
|
|
|
|
2017-09-01 17:46:20 -04:00
|
|
|
Zotero.Schema.schemaUpdatePromise.then(Zotero.purgeDataObjects.bind(Zotero));
|
|
|
|
|
2013-08-11 20:51:16 -04:00
|
|
|
return true;
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
}
|
|
|
|
catch (e) {
|
2016-11-10 00:55:30 -05:00
|
|
|
Zotero.logError(e);
|
|
|
|
if (!Zotero.startupError) {
|
2021-01-17 03:38:30 -05:00
|
|
|
Zotero.startupError = Zotero.getString('startupError', Zotero.appName) + "\n\n"
|
|
|
|
+ Zotero.getString('db.integrityCheck.reportInForums') + "\n\n"
|
2022-05-12 05:49:53 -04:00
|
|
|
+ e.message ? (e.message + "\n\n" + e.stack) : e;
|
2016-11-10 00:55:30 -05:00
|
|
|
}
|
2011-01-13 20:52:15 +00:00
|
|
|
return false;
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
}
|
|
|
|
});
|
2011-06-14 00:36:21 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes the DB connection
|
|
|
|
*/
|
2014-08-09 18:01:28 -04:00
|
|
|
var _initDB = Zotero.Promise.coroutine(function* (haveReleasedLock) {
|
2018-06-02 04:07:05 -04:00
|
|
|
// Initialize main database connection
|
|
|
|
Zotero.DB = new Zotero.DBConnection('zotero');
|
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
try {
|
|
|
|
// Test read access
|
2014-08-09 18:01:28 -04:00
|
|
|
yield Zotero.DB.test();
|
2011-06-14 00:36:21 +00:00
|
|
|
|
2016-11-27 00:06:02 -05:00
|
|
|
let dbfile = Zotero.DataDirectory.getDatabase();
|
2013-12-13 17:19:56 -05:00
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
// Test write access on Zotero data directory
|
2023-12-04 05:05:34 -05:00
|
|
|
if (!Zotero.File.pathToFile(PathUtils.parent(dbfile)).isWritable()) {
|
|
|
|
var msg = 'Cannot write to ' + PathUtils.parent(dbfile) + '/';
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
|
|
|
// Test write access on Zotero database
|
2016-11-27 00:06:02 -05:00
|
|
|
else if (!Zotero.File.pathToFile(dbfile).isWritable()) {
|
|
|
|
var msg = 'Cannot write to ' + dbfile;
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
var msg = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg) {
|
|
|
|
var e = {
|
|
|
|
name: 'NS_ERROR_FILE_ACCESS_DENIED',
|
|
|
|
message: msg,
|
2016-10-15 20:20:09 +01:00
|
|
|
toString: function () { return this.message; }
|
2011-06-14 00:36:21 +00:00
|
|
|
};
|
|
|
|
throw (e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e) {
|
2017-07-31 05:40:34 -04:00
|
|
|
if (_checkDataDirAccessError(e)) {}
|
2016-04-04 06:33:15 -04:00
|
|
|
// Storage busy
|
2017-07-31 05:40:34 -04:00
|
|
|
else if (e.message.includes('2153971713')) {
|
2023-05-26 04:38:12 +03:00
|
|
|
Zotero.startupError = Zotero.getString('startupError.databaseInUse');
|
2017-07-31 05:40:34 -04:00
|
|
|
}
|
|
|
|
else {
|
2017-08-05 01:09:57 +02:00
|
|
|
let stack = e.stack ? Zotero.Utilities.Internal.filterStack(e.stack) : null;
|
2021-01-17 03:38:30 -05:00
|
|
|
Zotero.startupError = Zotero.getString('startupError', Zotero.appName) + "\n\n"
|
|
|
|
+ Zotero.getString('db.integrityCheck.reportInForums') + "\n\n"
|
|
|
|
+ (stack || e);
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 03:10:09 -04:00
|
|
|
Zotero.debug(e.toString(), 1);
|
|
|
|
Components.utils.reportError(e); // DEBUG: doesn't always work
|
2011-06-14 00:36:21 +00:00
|
|
|
Zotero.skipLoading = true;
|
|
|
|
return false;
|
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
|
|
|
|
return true;
|
2014-08-09 18:01:28 -04:00
|
|
|
});
|
2007-10-23 07:11:59 +00:00
|
|
|
|
2017-07-31 05:40:34 -04:00
|
|
|
|
|
|
|
function _checkDataDirAccessError(e) {
|
|
|
|
if (e.name != 'NS_ERROR_FILE_ACCESS_DENIED' && !e.message.includes('2152857621')) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var msg = Zotero.getString('dataDir.databaseCannotBeOpened', Zotero.clientName)
|
|
|
|
+ "\n\n"
|
|
|
|
+ Zotero.getString('dataDir.checkPermissions', Zotero.clientName);
|
|
|
|
// If already using default directory, just show it
|
|
|
|
if (Zotero.DataDirectory.dir == Zotero.DataDirectory.defaultDir) {
|
|
|
|
msg += "\n\n" + Zotero.getString('dataDir.location', Zotero.DataDirectory.dir);
|
|
|
|
}
|
|
|
|
// Otherwise suggest moving to default, since there's a good chance this is due to security
|
|
|
|
// software preventing Zotero from accessing the selected directory (particularly if it's
|
|
|
|
// a Firefox profile)
|
|
|
|
else {
|
|
|
|
msg += "\n\n"
|
|
|
|
+ Zotero.getString('dataDir.moveToDefaultLocation', Zotero.clientName)
|
|
|
|
+ "\n\n"
|
|
|
|
+ Zotero.getString(
|
|
|
|
'dataDir.migration.failure.full.current', Zotero.DataDirectory.dir
|
|
|
|
)
|
|
|
|
+ "\n"
|
|
|
|
+ Zotero.getString(
|
|
|
|
'dataDir.migration.failure.full.recommended', Zotero.DataDirectory.defaultDir
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Zotero.startupError = msg;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-17 00:49:26 -05:00
|
|
|
this.shutdown = Zotero.Promise.coroutine(function* () {
|
2008-11-14 13:43:01 +00:00
|
|
|
Zotero.debug("Shutting down Zotero");
|
2011-06-14 00:36:21 +00:00
|
|
|
|
|
|
|
try {
|
2011-07-03 04:15:49 +00:00
|
|
|
// set closing to true
|
|
|
|
Zotero.closing = true;
|
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
// run shutdown listener
|
2022-08-05 00:43:42 -04:00
|
|
|
let shutdownPromises = [];
|
2016-01-17 00:49:26 -05:00
|
|
|
for (let listener of _shutdownListeners) {
|
2011-08-24 06:29:02 +00:00
|
|
|
try {
|
2022-08-05 00:43:42 -04:00
|
|
|
shutdownPromises.push(listener());
|
|
|
|
}
|
|
|
|
catch(e) {
|
2011-08-24 06:29:16 +00:00
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
2011-08-24 06:29:02 +00:00
|
|
|
}
|
2022-08-05 00:43:42 -04:00
|
|
|
yield Promise.all(shutdownPromises);
|
2011-06-14 00:36:21 +00:00
|
|
|
|
2016-01-17 00:49:26 -05:00
|
|
|
if (Zotero.DB) {
|
2013-12-13 17:19:56 -05:00
|
|
|
// close DB
|
2016-01-17 00:49:26 -05:00
|
|
|
yield Zotero.DB.closeDatabase(true)
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
|
|
|
} catch(e) {
|
2016-01-17 00:49:26 -05:00
|
|
|
Zotero.logError(e);
|
2011-06-14 00:36:21 +00:00
|
|
|
}
|
2016-01-17 00:49:26 -05:00
|
|
|
});
|
2006-08-01 18:01:56 +00:00
|
|
|
|
|
|
|
|
2016-11-26 22:41:26 -05:00
|
|
|
this.getProfileDirectory = function () {
|
|
|
|
Zotero.warn("Zotero.getProfileDirectory() is deprecated -- use Zotero.Profile.dir");
|
|
|
|
return Zotero.File.pathToFile(Zotero.Profile.dir);
|
2010-09-20 02:24:07 +00:00
|
|
|
}
|
Merged revisions 3080-3081,3084,3087-3088,3090,3092,3099-3103,3113-3114,3132,3134-3143,3145,3148-3151,3154-3159,3165,3174,3194,3234-3235,3239-3240,3244,3246-3254,3258-3262,3268,3270,3274,3279,3286-3288,3294-3295 from 1.0 branch via svnmerge
2008-09-01 01:54:00 +00:00
|
|
|
|
2016-10-30 22:47:42 -04:00
|
|
|
this.getZoteroDirectory = function () {
|
2016-11-26 22:41:26 -05:00
|
|
|
Zotero.warn("Zotero.getZoteroDirectory() is deprecated -- use Zotero.DataDirectory.dir");
|
|
|
|
return Zotero.File.pathToFile(Zotero.DataDirectory.dir);
|
2006-07-27 08:45:48 +00:00
|
|
|
}
|
|
|
|
|
Data directory migration
This adds a new button to the Advanced prefs to migrate the data directory to
$HOME/Zotero. The button only appears if the data directory is set to the
default location within a profile directory (including the other program from
the one running, even though that's technically stored as a custom data
directory).
On Mac/Linux, directories within the data directory are moved with /bin/mv. On
Windows, or if that fails, they're copied recursively using OS.File.move()
(which annoyingly doesn't reliably support directory moving). The former should
be instantaneous on most systems (unless the data directory or 'storage' were
on a different filesystem from $HOME).
If the database fails to transfer, migration fails and the data directory
setting remains on the old directory. If the database transfers but other files
fail, the data directory setting is updated. In both cases, the user is
encouraged to migrate remaining files manually with a button that reveals the
directories and quits the program.
This isn't yet tested on Linux or Windows, and migration isn't yet suggested
automatically.
Adds Zotero.File.reveal(), Zotero.File.directoryIsEmpty(), and
Zotero.File.moveDirectory().
2016-11-12 14:20:34 -05:00
|
|
|
this.getZoteroDatabase = function (name, ext) {
|
2016-11-26 22:41:26 -05:00
|
|
|
Zotero.warn("Zotero.getZoteroDatabase() is deprecated -- use Zotero.DataDirectory.getDatabase()");
|
|
|
|
return Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(name, ext));
|
2006-08-01 23:10:31 +00:00
|
|
|
}
|
|
|
|
|
2018-08-02 13:02:28 +02:00
|
|
|
function getStorageDirectory() {
|
2018-08-02 17:56:57 +02:00
|
|
|
return Zotero.File.pathToFile(Zotero.DataDirectory.getSubdirectory('storage', true));
|
2008-11-14 13:43:01 +00:00
|
|
|
}
|
2018-08-02 13:02:28 +02:00
|
|
|
|
2008-09-11 17:20:09 +00:00
|
|
|
this.getStylesDirectory = function () {
|
2018-08-02 17:56:57 +02:00
|
|
|
return Zotero.File.pathToFile(Zotero.DataDirectory.getSubdirectory('styles', true));
|
2008-09-11 17:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.getTranslatorsDirectory = function () {
|
2018-08-02 17:56:57 +02:00
|
|
|
return Zotero.File.pathToFile(Zotero.DataDirectory.getSubdirectory('translators', true));
|
2018-08-02 13:02:28 +02:00
|
|
|
}
|
2023-07-20 05:34:09 -04:00
|
|
|
|
|
|
|
var _tmpDir;
|
2018-08-02 13:02:28 +02:00
|
|
|
this.getTempDirectory = function () {
|
2023-07-20 05:34:09 -04:00
|
|
|
if (_tmpDir) {
|
|
|
|
return Zotero.File.pathToFile(_tmpDir);
|
|
|
|
}
|
|
|
|
var dir;
|
|
|
|
try {
|
|
|
|
dir = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
|
|
|
let relDir;
|
|
|
|
if (Zotero.isWin) {
|
|
|
|
relDir = 'Zotero';
|
|
|
|
}
|
|
|
|
else if (Zotero.isMac) {
|
|
|
|
relDir = 'org.zotero.zotero';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
relDir = 'zotero';
|
|
|
|
}
|
|
|
|
dir.append(relDir);
|
|
|
|
Zotero.File.createDirectoryIfMissing(dir);
|
|
|
|
}
|
|
|
|
// If we can't use the system temp dir, fall back to 'tmp' in the data dir
|
|
|
|
catch (e) {
|
|
|
|
Zotero.warn(e);
|
|
|
|
dir = Zotero.File.pathToFile(Zotero.DataDirectory.getSubdirectory('tmp', true));
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncShutdown.profileBeforeChange.addBlocker(
|
|
|
|
"Zotero: Removing temp directory",
|
|
|
|
() => this.removeTempDirectory()
|
|
|
|
);
|
|
|
|
|
|
|
|
_tmpDir = dir.path;
|
|
|
|
return dir;
|
|
|
|
};
|
2018-08-02 13:02:28 +02:00
|
|
|
|
2023-07-20 05:34:09 -04:00
|
|
|
this.removeTempDirectory = async function () {
|
|
|
|
if (!_tmpDir) return;
|
|
|
|
try {
|
|
|
|
Zotero.debug("Removing " + _tmpDir);
|
|
|
|
return IOUtils.remove(_tmpDir, { recursive: true });
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
2008-09-11 17:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-12 02:49:36 +02:00
|
|
|
this.openMainWindow = function () {
|
2022-08-12 15:33:14 -04:00
|
|
|
var chromeURI = AppConstants.BROWSER_CHROME_URL;
|
2024-04-18 13:25:15 -04:00
|
|
|
var flags = "chrome,all,dialog=no,resizable=yes";
|
2017-08-12 02:49:36 +02:00
|
|
|
var ww = Components.classes['@mozilla.org/embedcomp/window-watcher;1']
|
|
|
|
.getService(Components.interfaces.nsIWindowWatcher);
|
2022-08-12 15:33:14 -04:00
|
|
|
ww.openWindow(null, chromeURI, '_blank', flags, null);
|
2017-08-12 02:49:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-04-02 06:42:35 -04:00
|
|
|
this.openCheckForUpdatesWindow = function ({ modal } = {}) {
|
|
|
|
let win = Services.wm.getMostRecentWindow('Update:Wizard');
|
|
|
|
if (win) {
|
|
|
|
win.focus();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let flags = 'chrome,centerscreen';
|
|
|
|
if (modal) {
|
|
|
|
flags += ',modal';
|
|
|
|
}
|
|
|
|
Services.ww.openWindow(null, 'chrome://zotero/content/update/updates.xhtml',
|
|
|
|
'updateChecker', flags, null);
|
|
|
|
}
|
2019-05-19 21:30:20 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-04-11 04:28:38 -04:00
|
|
|
/**
|
|
|
|
* Launch a file, the best way we can
|
|
|
|
*/
|
|
|
|
this.launchFile = function (file) {
|
2016-05-30 09:02:40 -04:00
|
|
|
file = Zotero.File.pathToFile(file);
|
2013-04-11 04:28:38 -04:00
|
|
|
try {
|
2017-06-08 14:10:41 -04:00
|
|
|
Zotero.debug("Launching " + file.path);
|
2013-04-11 04:28:38 -04:00
|
|
|
file.launch();
|
|
|
|
}
|
|
|
|
catch (e) {
|
2024-02-23 06:38:11 -05:00
|
|
|
// macOS only: if there's no associated application, launch() will throw, but
|
|
|
|
// the OS will show a dialog asking the user to choose an application. We don't
|
|
|
|
// want to show the Firefox dialog in that case.
|
|
|
|
if (Zotero.isMac && file.exists()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-07 18:29:32 -04:00
|
|
|
Zotero.debug(e, 2);
|
|
|
|
Zotero.debug("launch() not supported -- trying fallback executable", 2);
|
2013-04-11 04:28:38 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
if (Zotero.isWin) {
|
|
|
|
var pref = "fallbackLauncher.windows";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var pref = "fallbackLauncher.unix";
|
|
|
|
}
|
2018-03-05 19:29:03 -05:00
|
|
|
let launcher = Zotero.Prefs.get(pref);
|
|
|
|
this.launchFileWithApplication(file.path, launcher);
|
2013-04-11 04:28:38 -04:00
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.debug(e);
|
2024-02-23 06:38:11 -05:00
|
|
|
Zotero.debug("Launching via executable failed -- passing to loadURI()");
|
2013-04-11 04:28:38 -04:00
|
|
|
|
2019-08-25 06:51:11 -04:00
|
|
|
// If nsIFile.launch() isn't available and the fallback
|
2013-04-11 04:28:38 -04:00
|
|
|
// executable doesn't exist, we just let the Firefox external
|
|
|
|
// helper app window handle it
|
2024-02-23 06:38:11 -05:00
|
|
|
var uri = Services.io.newFileURI(file);
|
2013-04-11 04:28:38 -04:00
|
|
|
|
|
|
|
var nsIEPS = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"].
|
|
|
|
getService(Components.interfaces.nsIExternalProtocolService);
|
2024-02-23 06:38:11 -05:00
|
|
|
nsIEPS.loadURI(
|
|
|
|
uri,
|
|
|
|
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
|
|
);
|
2013-04-11 04:28:38 -04:00
|
|
|
}
|
|
|
|
}
|
2018-03-05 19:29:03 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Launch a file with the given application
|
|
|
|
*/
|
|
|
|
this.launchFileWithApplication = function (filePath, applicationPath) {
|
2018-08-19 01:36:23 -04:00
|
|
|
Zotero.debug(`Launching ${filePath} with ${applicationPath}`);
|
|
|
|
|
2018-03-05 19:29:03 -05:00
|
|
|
var exec = Zotero.File.pathToFile(applicationPath);
|
|
|
|
if (!exec.exists()) {
|
|
|
|
throw new Error("'" + applicationPath + "' does not exist");
|
|
|
|
}
|
|
|
|
|
2018-03-07 04:20:26 -05:00
|
|
|
var args;
|
|
|
|
// On macOS, if we only have an .app, launch it using 'open'
|
|
|
|
if (Zotero.isMac && applicationPath.endsWith('.app')) {
|
|
|
|
args = [filePath, '-a', applicationPath];
|
|
|
|
applicationPath = '/usr/bin/open';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
args = [filePath];
|
|
|
|
}
|
|
|
|
|
2018-03-05 19:29:03 -05:00
|
|
|
// Async, but we don't want to block
|
2018-03-07 04:20:26 -05:00
|
|
|
Zotero.Utilities.Internal.exec(applicationPath, args);
|
2018-03-05 19:29:03 -05:00
|
|
|
};
|
2013-04-11 04:28:38 -04:00
|
|
|
|
|
|
|
|
2017-08-10 04:49:42 +02:00
|
|
|
/**
|
2020-01-04 02:18:00 -05:00
|
|
|
* Launch a URL externally, the best way we can
|
2017-08-10 04:49:42 +02:00
|
|
|
*/
|
|
|
|
this.launchURL = function (url) {
|
2019-12-16 14:49:47 -05:00
|
|
|
if (!Zotero.Utilities.isHTTPURL(url)) {
|
|
|
|
if (Zotero.Utilities.isHTTPURL(url, true)) {
|
|
|
|
url = 'http://' + url;
|
|
|
|
}
|
2020-01-04 02:18:00 -05:00
|
|
|
// Launch non-HTTP URLs
|
2019-12-16 14:49:47 -05:00
|
|
|
else {
|
2020-01-06 15:41:36 -05:00
|
|
|
let schemeRE = /^([a-z][a-z0-9+.-]+):/;
|
|
|
|
let matches = url.match(schemeRE);
|
2020-01-04 02:18:00 -05:00
|
|
|
if (!matches) {
|
|
|
|
throw new Error(`Invalid URL '${url}'`);
|
|
|
|
}
|
|
|
|
let scheme = matches[1];
|
|
|
|
if (['javascript', 'data', 'chrome', 'resource'].includes(scheme)) {
|
|
|
|
throw new Error(`Invalid scheme '${scheme}'`);
|
|
|
|
}
|
|
|
|
let svc = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
|
|
|
|
.getService(Components.interfaces.nsIExternalProtocolService);
|
|
|
|
let found = {};
|
|
|
|
let handlerInfo = svc.getProtocolHandlerInfoFromOS(scheme, found);
|
|
|
|
if (!found.value) {
|
|
|
|
throw new Error(`Handler not found for '${scheme}' URLs`);
|
|
|
|
}
|
|
|
|
svc.loadURI(Services.io.newURI(url, null, null));
|
|
|
|
return;
|
2019-12-16 14:49:47 -05:00
|
|
|
}
|
2017-08-10 04:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2018-07-27 16:44:28 +02:00
|
|
|
var uri = Services.io.newURI(url, null, null);
|
2017-08-10 04:49:42 +02:00
|
|
|
var handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
|
|
|
|
.getService(Components.interfaces.nsIExternalProtocolService)
|
|
|
|
.getProtocolHandlerInfo('http');
|
|
|
|
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
|
|
|
|
handler.launchWithURI(uri, null);
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.debug("launchWithURI() not supported -- trying fallback executable");
|
|
|
|
|
|
|
|
if (Zotero.isWin) {
|
|
|
|
var pref = "fallbackLauncher.windows";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var pref = "fallbackLauncher.unix";
|
|
|
|
}
|
|
|
|
var path = Zotero.Prefs.get(pref);
|
|
|
|
|
2019-08-25 06:51:11 -04:00
|
|
|
let exec = Zotero.File.pathToFile(path);
|
2017-08-10 04:49:42 +02:00
|
|
|
if (!exec.exists()) {
|
2020-01-04 02:18:00 -05:00
|
|
|
throw new Error("Fallback executable not found -- "
|
|
|
|
+ "check extensions.zotero." + pref + " in about:config");
|
2017-08-10 04:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var proc = Components.classes["@mozilla.org/process/util;1"]
|
|
|
|
.createInstance(Components.interfaces.nsIProcess);
|
|
|
|
proc.init(exec);
|
|
|
|
|
|
|
|
var args = [url];
|
|
|
|
proc.runw(false, args, args.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-30 00:56:04 -05:00
|
|
|
/**
|
|
|
|
* Opens a URL in the basic viewer, and optionally run a callback on load
|
|
|
|
*
|
|
|
|
* @param {String} uri
|
2023-04-20 15:50:57 -04:00
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Function} [options.onLoad] - Function to run once URI is loaded; passed the loaded document
|
2023-12-27 11:43:50 +02:00
|
|
|
* @param {Object} [options.cookieSandbox] - Attach a cookie sandbox to the browser
|
2023-04-20 15:50:57 -04:00
|
|
|
* @param {Boolean} [options.allowJavaScript] - Set to false to disable JavaScript
|
2016-12-30 00:56:04 -05:00
|
|
|
*/
|
2023-04-20 15:50:57 -04:00
|
|
|
this.openInViewer = function (uri, options) {
|
|
|
|
if (options && !options.onLoad && typeof options === 'function') {
|
|
|
|
Zotero.debug("Zotero.openInViewer() now takes an 'options' object for its second parameter -- update your code");
|
|
|
|
options = { onLoad: options };
|
|
|
|
}
|
|
|
|
|
2023-04-12 16:53:28 -04:00
|
|
|
var viewerWins = Services.wm.getEnumerator("zotero:basicViewer");
|
|
|
|
for (let existingWin of viewerWins) {
|
|
|
|
if (existingWin.viewerOriginalURI === uri) {
|
|
|
|
existingWin.focus();
|
2023-12-27 11:43:50 +02:00
|
|
|
return existingWin;
|
2023-04-12 16:53:28 -04:00
|
|
|
}
|
2016-12-30 00:56:04 -05:00
|
|
|
}
|
2023-04-12 16:53:28 -04:00
|
|
|
let ww = Components.classes['@mozilla.org/embedcomp/window-watcher;1']
|
|
|
|
.getService(Components.interfaces.nsIWindowWatcher);
|
2023-04-20 15:50:57 -04:00
|
|
|
let arg = {
|
|
|
|
uri,
|
|
|
|
options: {
|
|
|
|
...options,
|
|
|
|
onLoad: undefined
|
|
|
|
}
|
|
|
|
};
|
|
|
|
arg.wrappedJSObject = arg;
|
2023-04-12 16:53:28 -04:00
|
|
|
let win = ww.openWindow(null, "chrome://zotero/content/standalone/basicViewer.xhtml",
|
|
|
|
null, "chrome,dialog=yes,resizable,centerscreen,menubar,scrollbars", arg);
|
2023-04-20 15:50:57 -04:00
|
|
|
if (options?.onLoad) {
|
2023-04-12 16:53:28 -04:00
|
|
|
let browser;
|
2016-12-30 00:56:04 -05:00
|
|
|
let func = function () {
|
|
|
|
win.removeEventListener("load", func);
|
2022-12-11 18:38:27 -07:00
|
|
|
// <browser> is created in basicViewer.js in a window load event, so we have to
|
|
|
|
// wait for that
|
|
|
|
setTimeout(() => {
|
|
|
|
browser = win.document.documentElement.getElementsByTagName('browser')[0];
|
|
|
|
browser.addEventListener("pageshow", innerFunc);
|
|
|
|
});
|
2016-12-30 00:56:04 -05:00
|
|
|
};
|
|
|
|
let innerFunc = function () {
|
|
|
|
browser.removeEventListener("pageshow", innerFunc);
|
2023-04-20 15:50:57 -04:00
|
|
|
options.onLoad(browser.contentDocument);
|
2016-12-30 00:56:04 -05:00
|
|
|
};
|
|
|
|
win.addEventListener("load", func);
|
|
|
|
}
|
2023-12-27 11:43:50 +02:00
|
|
|
return win;
|
2016-12-30 00:56:04 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-02-21 17:01:06 +00:00
|
|
|
/*
|
|
|
|
* Debug logging function
|
|
|
|
*
|
2007-10-23 07:11:59 +00:00
|
|
|
* Uses prefs e.z.debug.log and e.z.debug.level (restart required)
|
2006-02-21 17:01:06 +00:00
|
|
|
*
|
2014-08-12 00:17:20 -05:00
|
|
|
* @param {} message
|
|
|
|
* @param {Integer} [level=3]
|
2017-01-17 02:31:10 -05:00
|
|
|
* @param {Integer} [maxDepth]
|
2014-08-12 00:17:20 -05:00
|
|
|
* @param {Boolean|Integer} [stack] Whether to display the calling stack.
|
|
|
|
* If true, stack is displayed starting from the caller. If an integer,
|
|
|
|
* that many stack levels will be omitted starting from the caller.
|
2006-02-21 17:01:06 +00:00
|
|
|
*/
|
2017-01-17 02:31:10 -05:00
|
|
|
function debug(message, level, maxDepth, stack) {
|
2014-08-12 00:17:20 -05:00
|
|
|
// Account for this alias
|
|
|
|
if (stack === true) {
|
|
|
|
stack = 1;
|
|
|
|
} else if (stack >= 0) {
|
|
|
|
stack++;
|
|
|
|
}
|
|
|
|
|
2017-01-17 02:31:10 -05:00
|
|
|
Zotero.Debug.log(message, level, maxDepth, stack);
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Log a message to the Mozilla JS error console
|
|
|
|
*
|
|
|
|
* |type| is a string with one of the flag types in nsIScriptError:
|
|
|
|
* 'error', 'warning', 'exception', 'strict'
|
|
|
|
*/
|
2022-08-01 04:38:37 -04:00
|
|
|
this.log = function (message, type, sourceName, sourceLine, lineNumber, columnNumber) {
|
2007-10-23 07:11:59 +00:00
|
|
|
var scriptError = Components.classes["@mozilla.org/scripterror;1"]
|
|
|
|
.createInstance(Components.interfaces.nsIScriptError);
|
|
|
|
|
|
|
|
if (!type) {
|
|
|
|
type = 'warning';
|
2006-02-21 17:01:06 +00:00
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
var flags = scriptError[type + 'Flag'];
|
|
|
|
|
|
|
|
scriptError.init(
|
|
|
|
message,
|
|
|
|
sourceName ? sourceName : null,
|
|
|
|
sourceLine != undefined ? sourceLine : null,
|
|
|
|
lineNumber != undefined ? lineNumber : null,
|
|
|
|
columnNumber != undefined ? columnNumber : null,
|
|
|
|
flags,
|
2022-08-01 04:37:57 -04:00
|
|
|
'system javascript',
|
|
|
|
false,
|
|
|
|
true
|
2007-10-23 07:11:59 +00:00
|
|
|
);
|
2013-06-06 02:37:19 -04:00
|
|
|
Services.console.logMessage(scriptError);
|
2022-08-01 04:38:37 -04:00
|
|
|
};
|
2007-10-23 07:11:59 +00:00
|
|
|
|
2010-11-02 21:39:54 +00:00
|
|
|
/**
|
2015-03-16 15:17:45 -04:00
|
|
|
* Log a JS error to the Mozilla error console and debug output
|
2010-11-02 21:39:54 +00:00
|
|
|
* @param {Exception} err
|
|
|
|
*/
|
2022-08-01 04:38:37 -04:00
|
|
|
this.logError = function (err) {
|
2014-10-03 16:06:01 -04:00
|
|
|
Zotero.debug(err, 1);
|
2022-08-01 04:38:37 -04:00
|
|
|
this.log(err.message ? err.message : err.toString(), "error",
|
2011-08-11 04:06:45 +00:00
|
|
|
err.fileName ? err.fileName : (err.filename ? err.filename : null), null,
|
2016-04-22 21:09:18 -04:00
|
|
|
err.lineNumber ? err.lineNumber : null, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.warn = function (err) {
|
2019-05-19 21:32:05 -04:00
|
|
|
Zotero.debug(err + "\n\n" + Zotero.Utilities.Internal.filterStack(new Error().stack), 2);
|
2022-08-01 04:38:37 -04:00
|
|
|
this.log(err.message ? err.message : err.toString(), "warning",
|
2016-04-22 21:09:18 -04:00
|
|
|
err.fileName ? err.fileName : (err.filename ? err.filename : null), null,
|
2010-11-02 21:39:54 +00:00
|
|
|
err.lineNumber ? err.lineNumber : null, null);
|
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
|
2015-04-14 16:09:55 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Display an alert in a given window
|
|
|
|
*
|
|
|
|
* @param {Window}
|
|
|
|
* @param {String} title
|
|
|
|
* @param {String} msg
|
|
|
|
*/
|
|
|
|
this.alert = function (window, title, msg) {
|
2017-05-31 00:43:19 -04:00
|
|
|
this.debug(`Alert:\n\n${msg}`);
|
2023-12-04 05:19:57 -05:00
|
|
|
Services.prompt.alert(window, title, msg);
|
2015-04-14 16:09:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-13 17:17:02 -04:00
|
|
|
/**
|
|
|
|
* Display an error message saying that an error has occurred and Zotero needs to be restarted.
|
|
|
|
*
|
|
|
|
* If |popup| is TRUE, display in popup progress window; otherwise, display as items pane message
|
|
|
|
*/
|
|
|
|
this.crash = function (popup) {
|
|
|
|
this.crashed = true;
|
|
|
|
|
2020-11-17 01:26:23 -05:00
|
|
|
// Check the database after restart
|
|
|
|
Zotero.Schema.setIntegrityCheckRequired(true).catch(e => this.logError(e));
|
|
|
|
|
2020-03-13 17:17:02 -04:00
|
|
|
var reportErrorsStr = Zotero.getString('errorReport.reportErrors');
|
|
|
|
var reportInstructions = Zotero.getString('errorReport.reportInstructions', reportErrorsStr);
|
|
|
|
|
|
|
|
var msg;
|
|
|
|
if (popup) {
|
|
|
|
msg = Zotero.getString('general.pleaseRestart', Zotero.appName) + ' '
|
|
|
|
+ reportInstructions;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
msg = Zotero.getString('general.errorHasOccurred') + ' '
|
|
|
|
+ Zotero.getString('general.pleaseRestart', Zotero.appName) + '\n\n'
|
|
|
|
+ reportInstructions;
|
|
|
|
}
|
|
|
|
Zotero.logError(msg);
|
|
|
|
Zotero.logError(new Error().stack);
|
|
|
|
|
|
|
|
this.startupError = msg;
|
|
|
|
this.startupErrorHandler = null;
|
|
|
|
|
|
|
|
var enumerator = Services.wm.getEnumerator("navigator:browser");
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
let win = enumerator.getNext();
|
|
|
|
if (!win.ZoteroPane) continue;
|
|
|
|
|
|
|
|
// Display as popup progress window
|
|
|
|
if (popup) {
|
|
|
|
var pw = new Zotero.ProgressWindow();
|
|
|
|
pw.changeHeadline(Zotero.getString('general.errorHasOccurred'));
|
|
|
|
pw.addDescription(msg);
|
|
|
|
pw.show();
|
|
|
|
pw.startCloseTimer(8000);
|
|
|
|
}
|
|
|
|
// Display as items pane message
|
|
|
|
else {
|
|
|
|
win.ZoteroPane.setItemsPaneMessage(msg, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-08-16 00:38:15 +02:00
|
|
|
this.getErrors = function (asStrings) {
|
2007-10-23 07:11:59 +00:00
|
|
|
var errors = [];
|
|
|
|
|
2016-10-18 13:20:01 +01:00
|
|
|
for (let msg of _startupErrors.concat(_recentErrors)) {
|
2017-08-16 00:38:15 +02:00
|
|
|
let altMessage;
|
|
|
|
// Remove password in malformed XML errors
|
2008-09-15 09:48:27 +00:00
|
|
|
if (msg.category == 'malformed-xml') {
|
|
|
|
try {
|
|
|
|
// msg.message is read-only, so store separately
|
2017-08-16 00:38:15 +02:00
|
|
|
altMessage = msg.message.replace(/(https?:\/\/[^:]+:)([^@]+)(@[^"]+)/, "$1****$3");
|
2008-09-15 09:48:27 +00:00
|
|
|
}
|
|
|
|
catch (e) {}
|
|
|
|
}
|
|
|
|
|
2007-10-23 07:11:59 +00:00
|
|
|
if (asStrings) {
|
2017-08-16 00:38:15 +02:00
|
|
|
errors.push(altMessage || msg.message)
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
errors.push(msg);
|
|
|
|
}
|
2006-02-21 17:01:06 +00:00
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-02 16:07:50 -04:00
|
|
|
/**
|
|
|
|
* Get versions, platform, etc.
|
|
|
|
*/
|
2023-05-23 01:53:41 -04:00
|
|
|
this.getSystemInfo = async function () {
|
2011-11-02 16:07:50 -04:00
|
|
|
var info = {
|
2013-06-06 02:37:19 -04:00
|
|
|
appName: Services.appinfo.name,
|
2023-05-23 01:53:41 -04:00
|
|
|
version: Zotero.version
|
|
|
|
+ (!Zotero.isMac && !Services.appinfo.is64Bit ? ' (32-bit)' : ''),
|
|
|
|
os: await this.getOSVersion(),
|
|
|
|
locale: Zotero.locale,
|
2011-11-02 16:07:50 -04:00
|
|
|
};
|
|
|
|
|
2023-05-23 01:53:41 -04:00
|
|
|
var extensions = await Zotero.getInstalledExtensions();
|
2016-10-08 20:24:39 -04:00
|
|
|
info.extensions = extensions.join(', ');
|
2011-11-02 16:07:50 -04:00
|
|
|
|
|
|
|
var str = '';
|
|
|
|
for (var key in info) {
|
|
|
|
str += key + ' => ' + info[key] + ', ';
|
|
|
|
}
|
|
|
|
str = str.substr(0, str.length - 2);
|
|
|
|
return str;
|
2023-05-23 01:53:41 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return OS and OS version
|
|
|
|
*
|
|
|
|
* "macOS 13.3.1"
|
|
|
|
* "Windows 10.0 22000"
|
|
|
|
* "Linux 5.4.0-148-generic #165-Ubuntu SMP Tue Apr 18 08:53:12 UTC 2023"
|
|
|
|
*
|
|
|
|
* @return {String}
|
|
|
|
*/
|
|
|
|
this.getOSVersion = async function () {
|
|
|
|
if (Zotero.isMac) {
|
|
|
|
try {
|
|
|
|
return "macOS "
|
2024-03-31 05:10:28 -04:00
|
|
|
+ (await Zotero.Utilities.Internal.subprocess('/usr/bin/sw_vers', ['-productVersion'])).trim();
|
2023-05-23 01:53:41 -04:00
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.logError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (Zotero.isWin ? "Windows" : Services.sysinfo.getProperty("name"))
|
|
|
|
+ " " + Services.sysinfo.getProperty("version")
|
|
|
|
+ " " + Services.sysinfo.getProperty("build");
|
|
|
|
};
|
Renamed DB to scholar.sqlite, since that seems to be the current fashion
Added some new core functions:
- Scholar.varDump(), after PHP's var_dump()
- Scholar.flattenArguments(), to flatten mixed array/literal argument lists into a single array
- Scholar.join() -- a version of join() that operates externally, for use on, for example, the arguments object (safer than extending Object)
- Scholar.Hash, a slightly smarter associative array -- not perfect, but brings a proper length property and a few convenience methods (and allows for other additions) -- should probably be limited to places where the length property or other additional additions are needed, since its use is a little non-standard (e.g. you have to remember to do _for (i in arr.items)_ rather than just _for (i in arr)_, to use set(), etc.)
2006-03-14 11:45:19 +00:00
|
|
|
|
|
|
|
|
Merged revisions 3080-3081,3084,3087-3088,3090,3092,3099-3103,3113-3114,3132,3134-3143,3145,3148-3151,3154-3159,3165,3174,3194,3234-3235,3239-3240,3244,3246-3254,3258-3262,3268,3270,3274,3279,3286-3288,3294-3295 from 1.0 branch via svnmerge
2008-09-01 01:54:00 +00:00
|
|
|
/**
|
2016-10-08 20:24:39 -04:00
|
|
|
* @return {Promise<String[]>} - Promise for an array of extension names and versions
|
Merged revisions 3080-3081,3084,3087-3088,3090,3092,3099-3103,3113-3114,3132,3134-3143,3145,3148-3151,3154-3159,3165,3174,3194,3234-3235,3239-3240,3244,3246-3254,3258-3262,3268,3270,3274,3279,3286-3288,3294-3295 from 1.0 branch via svnmerge
2008-09-01 01:54:00 +00:00
|
|
|
*/
|
2020-08-03 12:20:58 -04:00
|
|
|
this.getInstalledExtensions = async function () {
|
|
|
|
var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
|
|
|
|
var installed = await AddonManager.getAllAddons();
|
|
|
|
|
|
|
|
installed.sort(function(a, b) {
|
|
|
|
return ((a.appDisabled || a.userDisabled) ? 1 : 0) -
|
|
|
|
((b.appDisabled || b.userDisabled) ? 1 : 0);
|
|
|
|
});
|
|
|
|
var addons = [];
|
|
|
|
for (let addon of installed) {
|
2022-09-28 06:53:48 -04:00
|
|
|
if (addon.type == "theme") {
|
|
|
|
continue;
|
2010-11-05 05:05:36 +00:00
|
|
|
}
|
2020-08-03 12:20:58 -04:00
|
|
|
|
|
|
|
addons.push(addon.name + " (" + addon.version
|
|
|
|
+ (addon.type != 2 ? ", " + addon.type : "")
|
|
|
|
+ ((addon.appDisabled || addon.userDisabled) ? ", disabled" : "")
|
|
|
|
+ ")");
|
2011-09-20 21:39:48 +00:00
|
|
|
}
|
|
|
|
|
2020-08-03 12:20:58 -04:00
|
|
|
return addons;
|
|
|
|
};
|
Merged revisions 3080-3081,3084,3087-3088,3090,3092,3099-3103,3113-3114,3132,3134-3143,3145,3148-3151,3154-3159,3165,3174,3194,3234-3235,3239-3240,3244,3246-3254,3258-3262,3268,3270,3274,3279,3286-3288,3294-3295 from 1.0 branch via svnmerge
2008-09-01 01:54:00 +00:00
|
|
|
|
2016-12-10 17:07:37 -05:00
|
|
|
this.getString = function (name, params, num) {
|
2018-07-30 10:00:53 +02:00
|
|
|
return Zotero.Intl.getString(...arguments);
|
2014-11-14 02:06:22 -06:00
|
|
|
}
|
|
|
|
|
2018-08-01 16:29:20 +03:00
|
|
|
this.defineProperty = (...args) => Zotero.Utilities.Internal.defineProperty(...args);
|
2018-08-02 11:59:58 +02:00
|
|
|
|
2018-08-01 16:29:20 +03:00
|
|
|
this.extendClass = (...args) => Zotero.Utilities.Internal.extendClass(...args);
|
2018-08-02 11:59:58 +02:00
|
|
|
|
2014-06-25 12:14:28 -04:00
|
|
|
this.getLocaleCollation = function () {
|
2018-08-02 11:59:58 +02:00
|
|
|
return Zotero.Intl.collation;
|
|
|
|
}
|
2018-07-30 12:35:11 +02:00
|
|
|
|
2018-07-30 13:22:20 +02:00
|
|
|
this.localeCompare = function (...args) {
|
|
|
|
return Zotero.Intl.compare(...args);
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function setFontSize(rootElement) {
|
2018-08-06 12:24:17 +03:00
|
|
|
return Zotero.Utilities.Internal.setFontSize(rootElement);
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
2006-03-20 21:47:22 +00:00
|
|
|
function flattenArguments(args){
|
2018-08-02 11:59:58 +02:00
|
|
|
return Zotero.Utilities.Internal.flattenArguments(args);
|
2006-03-20 21:47:22 +00:00
|
|
|
}
|
Renamed DB to scholar.sqlite, since that seems to be the current fashion
Added some new core functions:
- Scholar.varDump(), after PHP's var_dump()
- Scholar.flattenArguments(), to flatten mixed array/literal argument lists into a single array
- Scholar.join() -- a version of join() that operates externally, for use on, for example, the arguments object (safer than extending Object)
- Scholar.Hash, a slightly smarter associative array -- not perfect, but brings a proper length property and a few convenience methods (and allows for other additions) -- should probably be limited to places where the length property or other additional additions are needed, since its use is a little non-standard (e.g. you have to remember to do _for (i in arr.items)_ rather than just _for (i in arr)_, to use set(), etc.)
2006-03-14 11:45:19 +00:00
|
|
|
|
Closes #259, auto-complete of tags
Addresses #260, Add auto-complete to search window
- New XPCOM autocomplete component for Zotero data -- can be used by setting the autocompletesearch attribute of a textbox to 'zotero' and passing a search scope with the autocompletesearchparam attribute. Additional parameters can be passed by appending them to the autocompletesearchparam value with a '/', e.g. 'tag/2732' (to exclude tags that show up in item 2732)
- Tag entry now uses more or less the same interface as metadata -- no more popup window -- note that tab isn't working properly yet, and there's no way to quickly enter multiple tags (though it's now considerably quicker than it was before)
- Autocomplete for tags, excluding any tags already set for the current item
- Standalone note windows now register with the Notifier (since tags needed item modification notifications to work properly), which will help with #282, "Notes opened in separate windows need item notification"
- Tags are now retrieved in alphabetical order
- Scholar.Item.replaceTag(oldTagID, newTag), with a single notify
- Scholar.getAncestorByTagName(elem, tagName) -- walk up the DOM tree from an element until an element with the specified tag name is found (also checks with 'xul:' prefix, for use in XBL), or false if not found -- probably shouldn't be used too widely, since it's doing string comparisons, but better than specifying, say, nine '.parentNode' properties, and makes for more resilient code
A few notes:
- Autocomplete in Minefield seems to self-destruct after using it in the same field a few times, taking down saving of the field with it -- this may or may not be my fault, but it makes Zotero more or less unusable in 3.0 at the moment. Sorry. (I use 3.0 myself for development, so I'll work on it.)
- This would have been much, much easier if having an autocomplete textbox (which uses an XBL-generated popup for the suggestions) within a popup (as it is in the independent note edit panes) didn't introduce all sorts of crazy bugs that had to be defeated with annoying hackery -- one side effect of this is that at the moment you can't close the tags popup with the Escape key
- Independent note windows now need to pull in itemPane.js to function properly, which is a bit messy and not ideal, but less messy and more ideal than duplicating all the dual-state editor and tabindex logic would be
- Hitting tab in a tag field not only doesn't work but also breaks things until the next window refresh.
- There are undoubtedly other bugs.
2006-09-07 08:07:48 +00:00
|
|
|
function getAncestorByTagName(elem, tagName){
|
2018-08-02 11:59:58 +02:00
|
|
|
return Zotero.Utilities.Internal.getAncestorByTagName(elem, tagName);
|
Closes #259, auto-complete of tags
Addresses #260, Add auto-complete to search window
- New XPCOM autocomplete component for Zotero data -- can be used by setting the autocompletesearch attribute of a textbox to 'zotero' and passing a search scope with the autocompletesearchparam attribute. Additional parameters can be passed by appending them to the autocompletesearchparam value with a '/', e.g. 'tag/2732' (to exclude tags that show up in item 2732)
- Tag entry now uses more or less the same interface as metadata -- no more popup window -- note that tab isn't working properly yet, and there's no way to quickly enter multiple tags (though it's now considerably quicker than it was before)
- Autocomplete for tags, excluding any tags already set for the current item
- Standalone note windows now register with the Notifier (since tags needed item modification notifications to work properly), which will help with #282, "Notes opened in separate windows need item notification"
- Tags are now retrieved in alphabetical order
- Scholar.Item.replaceTag(oldTagID, newTag), with a single notify
- Scholar.getAncestorByTagName(elem, tagName) -- walk up the DOM tree from an element until an element with the specified tag name is found (also checks with 'xul:' prefix, for use in XBL), or false if not found -- probably shouldn't be used too widely, since it's doing string comparisons, but better than specifying, say, nine '.parentNode' properties, and makes for more resilient code
A few notes:
- Autocomplete in Minefield seems to self-destruct after using it in the same field a few times, taking down saving of the field with it -- this may or may not be my fault, but it makes Zotero more or less unusable in 3.0 at the moment. Sorry. (I use 3.0 myself for development, so I'll work on it.)
- This would have been much, much easier if having an autocomplete textbox (which uses an XBL-generated popup for the suggestions) within a popup (as it is in the independent note edit panes) didn't introduce all sorts of crazy bugs that had to be defeated with annoying hackery -- one side effect of this is that at the moment you can't close the tags popup with the Escape key
- Independent note windows now need to pull in itemPane.js to function properly, which is a bit messy and not ideal, but less messy and more ideal than duplicating all the dual-state editor and tabindex logic would be
- Hitting tab in a tag field not only doesn't work but also breaks things until the next window refresh.
- There are undoubtedly other bugs.
2006-09-07 08:07:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-06 12:24:17 +03:00
|
|
|
this.randomString = function(len, chars) {
|
2011-07-04 09:08:49 +00:00
|
|
|
return Zotero.Utilities.randomString(len, chars);
|
2006-06-01 19:46:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-17 18:08:03 -04:00
|
|
|
this.moveToUnique = function (file, newFile) {
|
|
|
|
Zotero.debug("Zotero.moveToUnique() is deprecated -- use Zotero.File.moveToUnique()", 2);
|
2016-10-16 02:03:40 +01:00
|
|
|
newFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o644);
|
2006-08-01 23:10:31 +00:00
|
|
|
var newName = newFile.leafName;
|
|
|
|
newFile.remove(null);
|
|
|
|
|
|
|
|
// Move file to unique name
|
|
|
|
file.moveTo(newFile.parent, newName);
|
|
|
|
return file;
|
|
|
|
}
|
Initial Zotero 1.5 Megacommit
Apologies for the massive (and, due to data_access.js splitting, difficult-to-follow) commit. Please note that external code that accesses the data layer may need to be tweaked for compatibility. Here's a comprehensive-as-possible changelog:
- Added server sync functionality (incomplete)
- Overhaul of data layer
- Split data_access.js into separate files (item.js, items.js, creator.js, etc.)
- Made creators and collections first-class objects, similar to items
- Constructors now take id as first parameter, e.g. new Zotero.Item(1234, 'book'), to allow explicit id setting and id changing
- Made various data layer operations (including attachment fields) require a save() rather than making direct DB changes
- Better handling of unsaved objects
- Item.setCreator() now takes creator objects instead of creator ids, and Item.save() will auto-save unsaved creators
- clone() now works on unsaved objects
- Newly created object instances are now disabled after save() to force refetch of globally accessible instance using Zotero.(Items|Creators|etc.).get()
- Added secondary lookup key to data objects
- Deprecated getID() and getItemType() methods in favor of .id and .itemTypeID properties
- toArray() deprecated in favor of serialize(), which has a somewhat modified format
- Added support for multiple creators with identical data -- currently unimplemented in interface and most of data layer
- Added Item.diff() for comparing item metadata
- Database changes
- Added SQLite triggers to enforce foreign key constraints
- Added Zotero.DB.transactionVacuum flag to run a VACUUM after a transaction
- Added Zotero.DB.transactionDate, .transactionDateTime, and transactionTimestamp to retrieve consistent timestamps for entire transaction
- Properly store 64-bit integers
- Set PRAGMA locking_mode=EXCLUSIVE on database
- Set SQLite page size to 4096 on new databases
- Set SQLite page cache to 8MB
- Do some database cleanup and integrity checking on migration from 1.0 branch
- Removed IF NOT EXISTS from userdata.sql CREATE statements -- userdata.sql is now processed only on DB initialization
- Removed itemNoteTitles table and moved titles into itemNotes
- Abstracted metadata edit box and note box into flexible XBL bindings with various modes, including read-only states
- Massive speed-up of item tree view
- Several fixes from 1.0 branch for Fx3 compatibility
- Added Notifier observer to log delete events for syncing
- Zotero.Utilities changes
- New methods getSQLDataType() and md5()
- Removed onError from Zotero.Utilities.HTTP.doGet()
- Don't display more than 1024 characters in doPost() debug output
- Don't display passwords in doPost() debug output
- Added Zotero.Notifier.untrigger() -- currently unused
- Added Zotero.reloadDataObjects() to reset all in-memory objects
- Added |chars| parameter to Zotero.randomString(len, chars)
- Added Zotero.Date.getUnixTimestamp() and Date.toUnixTimestamp(JSDate)
- Adjusted zotero-service.js to simplify file inclusion
Various things (such as tags) are temporarily broken.
2008-05-04 08:32:48 +00:00
|
|
|
|
2013-08-14 21:36:50 -04:00
|
|
|
this.lazy = function(fn) {
|
2018-08-02 17:20:09 +02:00
|
|
|
return Zotero.Utilities.Internal.lazy(fn);
|
|
|
|
}
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
|
|
|
|
this.serial = function (fn) {
|
2018-08-02 17:20:09 +02:00
|
|
|
return Zotero.Utilities.Internal.serial(fn);
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
this.spawn = function (generator, thisObject) {
|
2018-08-02 17:20:09 +02:00
|
|
|
return Zotero.Utilities.Internal.spawn(generator, thisObject);
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
}
|
|
|
|
|
2009-08-08 07:38:34 +00:00
|
|
|
/**
|
|
|
|
* Show Zotero pane overlay and progress bar in all windows
|
|
|
|
*
|
2016-12-06 05:09:08 -05:00
|
|
|
* @param {String} msg
|
|
|
|
* @param {Boolean} [determinate=false]
|
|
|
|
* @param {Boolean} [modalOnly=false] - Don't use popup if Zotero pane isn't showing
|
2009-08-09 19:39:32 +00:00
|
|
|
* @return void
|
2009-08-08 07:38:34 +00:00
|
|
|
*/
|
2016-12-06 05:09:08 -05:00
|
|
|
this.showZoteroPaneProgressMeter = function (msg, determinate, icon, modalOnly) {
|
2016-11-16 01:05:22 -05:00
|
|
|
// If msg is undefined, keep any existing message. If false/null/"", clear.
|
|
|
|
// The message is also cleared when the meters are hidden.
|
|
|
|
_progressMessage = msg = (msg === undefined ? _progressMessage : msg) || "";
|
2013-06-06 02:37:19 -04:00
|
|
|
var currentWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
|
|
|
var enumerator = Services.wm.getEnumerator("navigator:browser");
|
2009-08-08 07:38:34 +00:00
|
|
|
var progressMeters = [];
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
var win = enumerator.getNext();
|
2011-02-03 07:04:14 +00:00
|
|
|
if(!win.ZoteroPane) continue;
|
2009-08-08 07:38:34 +00:00
|
|
|
|
2013-08-11 20:51:16 -04:00
|
|
|
var label = win.ZoteroPane.document.getElementById('zotero-pane-progress-label');
|
2017-03-02 15:29:59 -05:00
|
|
|
if (!label) {
|
|
|
|
Components.utils.reportError("label not found in " + win.document.location.href);
|
|
|
|
}
|
2013-08-11 20:51:16 -04:00
|
|
|
if (msg) {
|
|
|
|
label.hidden = false;
|
|
|
|
label.value = msg;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
label.hidden = true;
|
|
|
|
}
|
2018-03-06 04:19:01 -05:00
|
|
|
// This is the craziest thing. In Firefox 52.6.0, the very presence of this line
|
|
|
|
// causes Zotero on Linux to burn 5% CPU at idle, even if everything below it in
|
|
|
|
// the block is commented out. Same if the progressmeter itself is hidden="true".
|
|
|
|
// For some reason it also doesn't seem to work to set the progressmeter to
|
|
|
|
// 'determined' when hiding, which we're doing in lookup.js. So instead, create a new
|
|
|
|
// progressmeter each time and delete it in _hideWindowZoteroPaneOverlay().
|
|
|
|
//
|
|
|
|
//let progressMeter = win.ZoteroPane.document.getElementById('zotero-pane-progressmeter');
|
|
|
|
let doc = win.ZoteroPane.document;
|
|
|
|
let container = doc.getElementById('zotero-pane-progressmeter-container');
|
2019-04-20 03:28:47 -04:00
|
|
|
let id = 'zotero-pane-progressmeter';
|
|
|
|
let progressMeter = doc.getElementById(id);
|
|
|
|
if (!progressMeter) {
|
2022-06-29 10:16:49 -04:00
|
|
|
progressMeter = doc.createElement('progress');
|
2019-04-20 03:28:47 -04:00
|
|
|
progressMeter.id = id;
|
|
|
|
}
|
2009-08-08 07:38:34 +00:00
|
|
|
if (determinate) {
|
2021-01-17 01:52:22 -05:00
|
|
|
progressMeter.setAttribute('value', 0);
|
2009-08-09 19:39:32 +00:00
|
|
|
progressMeter.max = 1000;
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-01-17 01:52:22 -05:00
|
|
|
progressMeter.removeAttribute('value');
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
2018-03-06 04:19:01 -05:00
|
|
|
container.appendChild(progressMeter);
|
2009-08-08 07:38:34 +00:00
|
|
|
|
2011-02-03 07:04:14 +00:00
|
|
|
_showWindowZoteroPaneOverlay(win.ZoteroPane.document);
|
|
|
|
win.ZoteroPane.document.getElementById('zotero-pane-overlay-deck').selectedIndex = 0;
|
2009-08-08 07:38:34 +00:00
|
|
|
|
|
|
|
progressMeters.push(progressMeter);
|
|
|
|
}
|
2013-08-11 20:51:16 -04:00
|
|
|
this.locked = true;
|
2009-08-09 19:39:32 +00:00
|
|
|
_progressMeters = progressMeters;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {Number} percentage Percentage complete as integer or float
|
|
|
|
*/
|
|
|
|
this.updateZoteroPaneProgressMeter = function (percentage) {
|
2011-02-03 07:04:14 +00:00
|
|
|
if(percentage !== null) {
|
|
|
|
if (percentage < 0 || percentage > 100) {
|
|
|
|
Zotero.debug("Invalid percentage value '" + percentage + "' in Zotero.updateZoteroPaneProgressMeter()");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
percentage = Math.round(percentage * 10);
|
2009-08-09 19:39:32 +00:00
|
|
|
}
|
2011-02-03 07:04:14 +00:00
|
|
|
if (percentage === _lastPercentage) {
|
2009-08-09 19:39:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-05-03 23:51:12 +02:00
|
|
|
for (let pm of _progressMeters) {
|
2011-02-03 07:04:14 +00:00
|
|
|
if (percentage !== null) {
|
2021-01-17 01:52:22 -05:00
|
|
|
if (!pm.hasAttribute('value')) {
|
2011-02-03 07:04:14 +00:00
|
|
|
pm.max = 1000;
|
|
|
|
}
|
2021-01-17 01:52:22 -05:00
|
|
|
pm.setAttribute('value', percentage);
|
|
|
|
}
|
|
|
|
else if (pm.hasAttribute('value')) {
|
|
|
|
pm.removeAttribute('value');
|
2009-08-09 19:39:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_lastPercentage = percentage;
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hide Zotero pane overlay in all windows
|
|
|
|
*/
|
2013-08-11 20:51:16 -04:00
|
|
|
this.hideZoteroPaneOverlays = function () {
|
|
|
|
this.locked = false;
|
2009-09-14 06:13:04 +00:00
|
|
|
|
2013-06-06 02:37:19 -04:00
|
|
|
var enumerator = Services.wm.getEnumerator("navigator:browser");
|
2009-08-08 07:38:34 +00:00
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
var win = enumerator.getNext();
|
2011-02-03 07:04:14 +00:00
|
|
|
if(win.ZoteroPane && win.ZoteroPane.document) {
|
|
|
|
_hideWindowZoteroPaneOverlay(win.ZoteroPane.document);
|
|
|
|
}
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
2011-09-21 23:00:20 +00:00
|
|
|
|
|
|
|
if (_progressPopup) {
|
|
|
|
_progressPopup.close();
|
|
|
|
}
|
|
|
|
|
2016-11-16 01:05:22 -05:00
|
|
|
_progressMessage = null;
|
2009-08-09 19:39:32 +00:00
|
|
|
_progressMeters = [];
|
2011-09-21 23:00:20 +00:00
|
|
|
_progressPopup = null;
|
2009-08-09 19:39:32 +00:00
|
|
|
_lastPercentage = null;
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
/**
|
|
|
|
* Adds a listener to be called when Zotero shuts down (even if Firefox is not shut down)
|
|
|
|
*/
|
|
|
|
this.addShutdownListener = function(listener) {
|
|
|
|
_shutdownListeners.push(listener);
|
|
|
|
}
|
2009-09-14 06:13:04 +00:00
|
|
|
|
2011-02-03 07:04:14 +00:00
|
|
|
function _showWindowZoteroPaneOverlay(doc) {
|
|
|
|
doc.getElementById('zotero-collections-tree').disabled = true;
|
|
|
|
doc.getElementById('zotero-items-tree').disabled = true;
|
|
|
|
doc.getElementById('zotero-pane-overlay').hidden = false;
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-03 07:04:14 +00:00
|
|
|
function _hideWindowZoteroPaneOverlay(doc) {
|
|
|
|
doc.getElementById('zotero-collections-tree').disabled = false;
|
|
|
|
doc.getElementById('zotero-items-tree').disabled = false;
|
|
|
|
doc.getElementById('zotero-pane-overlay').hidden = true;
|
2018-03-06 04:19:01 -05:00
|
|
|
|
|
|
|
// See note in showZoteroPaneProgressMeter()
|
|
|
|
let pm = doc.getElementById('zotero-pane-progressmeter');
|
|
|
|
if (pm) {
|
|
|
|
pm.parentNode.removeChild(pm);
|
|
|
|
}
|
2009-08-08 07:38:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-04-26 17:43:00 +00:00
|
|
|
this.updateQuickSearchBox = function (document) {
|
2011-10-03 02:27:42 +00:00
|
|
|
var searchBox = document.getElementById('zotero-tb-search');
|
2022-07-06 23:42:23 -04:00
|
|
|
if (searchBox) {
|
|
|
|
searchBox.updateMode();
|
2011-04-26 17:43:00 +00:00
|
|
|
}
|
2022-07-06 23:42:23 -04:00
|
|
|
};
|
2011-04-26 17:43:00 +00:00
|
|
|
|
|
|
|
|
2009-02-18 03:09:39 +00:00
|
|
|
/*
|
|
|
|
* Clear entries that no longer exist from various tables
|
|
|
|
*/
|
2016-04-10 18:58:01 -04:00
|
|
|
this.purgeDataObjects = Zotero.Promise.coroutine(function* () {
|
2017-09-01 17:46:20 -04:00
|
|
|
var d = new Date();
|
|
|
|
|
2020-07-05 06:20:01 -04:00
|
|
|
yield Zotero.DB.executeTransaction(async function () {
|
2015-05-22 13:38:50 -04:00
|
|
|
return Zotero.Creators.purge();
|
|
|
|
});
|
2020-07-05 06:20:01 -04:00
|
|
|
yield Zotero.DB.executeTransaction(async function () {
|
2015-05-22 13:38:50 -04:00
|
|
|
return Zotero.Tags.purge();
|
|
|
|
});
|
2017-04-21 02:59:24 -04:00
|
|
|
yield Zotero.Fulltext.purgeUnusedWords();
|
2020-07-05 06:20:01 -04:00
|
|
|
yield Zotero.DB.executeTransaction(async function () {
|
2015-05-22 15:50:27 -04:00
|
|
|
return Zotero.Items.purge();
|
2015-05-22 13:38:50 -04:00
|
|
|
});
|
2010-04-27 08:03:08 +00:00
|
|
|
// DEBUG: this might not need to be permanent
|
2020-07-05 06:20:01 -04:00
|
|
|
//yield Zotero.DB.executeTransaction(async function () {
|
2016-04-10 18:58:01 -04:00
|
|
|
// return Zotero.Relations.purge();
|
|
|
|
//});
|
2017-09-01 17:46:20 -04:00
|
|
|
|
|
|
|
Zotero.debug("Purged data tables in " + (new Date() - d) + " ms");
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
});
|
2009-02-18 03:09:39 +00:00
|
|
|
|
|
|
|
|
2.0b3 megacommit
- Support for group libraries
- General support for multiple libraries of different types
- Streamlined sync support
- Using solely libraryID and key rather than itemID, and removed all itemID-changing code
- Combined two requests for increased performance and decreased server load
- Added warning on user account change
- Provide explicit error message on SSL failure
- Removed snapshot and link toolbar buttons and changed browser context menu options and drags to create parent items + snapshots
- Closes #786, Add numPages field
- Fixes #1063, Duplicate item with tags broken in Sync Preview
- Added better purging of deleted tags
- Added local user key before first sync
- Add clientDateModified to all objects for more flexibility in syncing
- Added new triples-based Relation object type, currently used to store links between items copied between local and group libraries
- Updated zotero.org translator for groups
- Additional trigger-based consistency checks
- Fixed broken URL drag in Firefox 3.5
- Disabled zeroconf menu option (no longer functional)
Developer-specific changes:
- Overhauled data layer
- Data object constructors no longer take arguments (return to 1.0-like API)
- Existing objects can be retrieved by setting id or library/key properties
- id/library/key must be set for new objects before other fields
- New methods:
- ZoteroPane.getSelectedLibraryID()
- ZoteroPane.getSelectedGroup(asID)
- ZoteroPane.addItemFromDocument(doc, itemType, saveSnapshot)
- ZoteroPane.addItemFromURL(url, itemType)
- ZoteroPane.canEdit()
- Zotero.CollectionTreeView.selectLibrary(libraryID)
- New Zotero.URI methods
- Changed methods
- Many data object methods now take a libraryID
- ZoteroPane.addAttachmentFromPage(link, itemID)
- Removed saveItem and saveAttachments parameters from Zotero.Translate constructor
- translate() now takes a libraryID, null for local library, or false to not save items (previously on constructor)
- saveAttachments is now a translate() parameter
- Zotero.flattenArguments() better handles passed objects
- Zotero.File.getFileHash() (not currently used)
2009-05-14 18:23:40 +00:00
|
|
|
this.reloadDataObjects = function () {
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
return Zotero.Promise.all([
|
|
|
|
Zotero.Collections.reloadAll(),
|
|
|
|
Zotero.Creators.reloadAll(),
|
|
|
|
Zotero.Items.reloadAll()
|
|
|
|
]);
|
Initial Zotero 1.5 Megacommit
Apologies for the massive (and, due to data_access.js splitting, difficult-to-follow) commit. Please note that external code that accesses the data layer may need to be tweaked for compatibility. Here's a comprehensive-as-possible changelog:
- Added server sync functionality (incomplete)
- Overhaul of data layer
- Split data_access.js into separate files (item.js, items.js, creator.js, etc.)
- Made creators and collections first-class objects, similar to items
- Constructors now take id as first parameter, e.g. new Zotero.Item(1234, 'book'), to allow explicit id setting and id changing
- Made various data layer operations (including attachment fields) require a save() rather than making direct DB changes
- Better handling of unsaved objects
- Item.setCreator() now takes creator objects instead of creator ids, and Item.save() will auto-save unsaved creators
- clone() now works on unsaved objects
- Newly created object instances are now disabled after save() to force refetch of globally accessible instance using Zotero.(Items|Creators|etc.).get()
- Added secondary lookup key to data objects
- Deprecated getID() and getItemType() methods in favor of .id and .itemTypeID properties
- toArray() deprecated in favor of serialize(), which has a somewhat modified format
- Added support for multiple creators with identical data -- currently unimplemented in interface and most of data layer
- Added Item.diff() for comparing item metadata
- Database changes
- Added SQLite triggers to enforce foreign key constraints
- Added Zotero.DB.transactionVacuum flag to run a VACUUM after a transaction
- Added Zotero.DB.transactionDate, .transactionDateTime, and transactionTimestamp to retrieve consistent timestamps for entire transaction
- Properly store 64-bit integers
- Set PRAGMA locking_mode=EXCLUSIVE on database
- Set SQLite page size to 4096 on new databases
- Set SQLite page cache to 8MB
- Do some database cleanup and integrity checking on migration from 1.0 branch
- Removed IF NOT EXISTS from userdata.sql CREATE statements -- userdata.sql is now processed only on DB initialization
- Removed itemNoteTitles table and moved titles into itemNotes
- Abstracted metadata edit box and note box into flexible XBL bindings with various modes, including read-only states
- Massive speed-up of item tree view
- Several fixes from 1.0 branch for Fx3 compatibility
- Added Notifier observer to log delete events for syncing
- Zotero.Utilities changes
- New methods getSQLDataType() and md5()
- Removed onError from Zotero.Utilities.HTTP.doGet()
- Don't display more than 1024 characters in doPost() debug output
- Don't display passwords in doPost() debug output
- Added Zotero.Notifier.untrigger() -- currently unused
- Added Zotero.reloadDataObjects() to reset all in-memory objects
- Added |chars| parameter to Zotero.randomString(len, chars)
- Added Zotero.Date.getUnixTimestamp() and Date.toUnixTimestamp(JSDate)
- Adjusted zotero-service.js to simplify file inclusion
Various things (such as tags) are temporarily broken.
2008-05-04 08:32:48 +00:00
|
|
|
}
|
2011-06-14 00:36:21 +00:00
|
|
|
|
2013-04-11 04:28:38 -04:00
|
|
|
|
2011-06-14 00:36:21 +00:00
|
|
|
/**
|
|
|
|
* Brings Zotero Standalone to the foreground
|
|
|
|
*/
|
|
|
|
this.activateStandalone = function() {
|
2013-06-06 02:37:19 -04:00
|
|
|
var uri = Services.io.newURI('zotero://select', null, null);
|
2011-06-14 00:36:21 +00:00
|
|
|
var handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
|
|
|
|
.getService(Components.interfaces.nsIExternalProtocolService)
|
|
|
|
.getProtocolHandlerInfo('zotero');
|
|
|
|
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
|
|
|
|
handler.launchWithURI(uri, null);
|
|
|
|
}
|
2012-02-13 20:42:32 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines whether to keep an error message so that it can (potentially) be reported later
|
|
|
|
*/
|
|
|
|
function _shouldKeepError(msg) {
|
|
|
|
const skip = ['CSS Parser', 'content javascript'];
|
|
|
|
|
|
|
|
//Zotero.debug(msg);
|
|
|
|
try {
|
|
|
|
msg.QueryInterface(Components.interfaces.nsIScriptError);
|
|
|
|
//Zotero.debug(msg);
|
|
|
|
if (skip.indexOf(msg.category) != -1 || msg.flags & msg.warningFlag) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e) { }
|
|
|
|
|
|
|
|
const blacklist = [
|
|
|
|
"No chrome package registered for chrome://communicator",
|
|
|
|
'[JavaScript Error: "Components is not defined" {file: "chrome://nightly/content/talkback/talkback.js',
|
|
|
|
'[JavaScript Error: "document.getElementById("sanitizeItem")',
|
|
|
|
'No chrome package registered for chrome://piggy-bank',
|
|
|
|
'[JavaScript Error: "[Exception... "\'Component is not available\' when calling method: [nsIHandlerService::getTypeFromExtension',
|
|
|
|
'[JavaScript Error: "this._uiElement is null',
|
|
|
|
'Error: a._updateVisibleText is not a function',
|
|
|
|
'[JavaScript Error: "Warning: unrecognized command line flag ',
|
|
|
|
'LibX:',
|
|
|
|
'function skype_',
|
|
|
|
'[JavaScript Error: "uncaught exception: Permission denied to call method Location.toString"]',
|
|
|
|
'CVE-2009-3555',
|
2014-03-14 17:11:26 -04:00
|
|
|
'OpenGL',
|
2013-07-30 15:48:53 -04:00
|
|
|
'trying to re-register CID',
|
2013-11-05 12:39:06 -05:00
|
|
|
'Services.HealthReport',
|
|
|
|
'[JavaScript Error: "this.docShell is null"',
|
|
|
|
'[JavaScript Error: "downloadable font:',
|
2013-11-13 12:57:49 -05:00
|
|
|
'[JavaScript Error: "Image corrupt or truncated:',
|
2014-05-26 20:09:27 -04:00
|
|
|
'[JavaScript Error: "The character encoding of the',
|
|
|
|
'nsLivemarkService.js',
|
|
|
|
'Sync.Engine.Tabs',
|
|
|
|
'content-sessionStore.js',
|
2015-03-29 15:22:07 -04:00
|
|
|
'org.mozilla.appSessions',
|
2018-01-08 00:01:46 -05:00
|
|
|
'bad script XDR magic number',
|
|
|
|
'did not contain an updates property',
|
2012-02-13 20:42:32 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
for (var i=0; i<blacklist.length; i++) {
|
|
|
|
if (msg.message.indexOf(blacklist[i]) != -1) {
|
|
|
|
//Zotero.debug("Skipping blacklisted error: " + msg.message);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-08-07 18:32:40 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Warn if Zotero Standalone is running as root and clobber the cache directory if it is
|
|
|
|
*/
|
|
|
|
function _checkRoot() {
|
|
|
|
var env = Components.classes["@mozilla.org/process/environment;1"].
|
|
|
|
getService(Components.interfaces.nsIEnvironment);
|
|
|
|
var user = env.get("USER") || env.get("USERNAME");
|
|
|
|
if(user === "root") {
|
|
|
|
// Show warning
|
|
|
|
if(Services.prompt.confirmEx(null, "", Zotero.getString("standalone.rootWarning"),
|
|
|
|
Services.prompt.BUTTON_POS_0*Services.prompt.BUTTON_TITLE_IS_STRING |
|
|
|
|
Services.prompt.BUTTON_POS_1*Services.prompt.BUTTON_TITLE_IS_STRING,
|
|
|
|
Zotero.getString("standalone.rootWarning.exit"),
|
|
|
|
Zotero.getString("standalone.rootWarning.continue"),
|
|
|
|
null, null, {}) == 0) {
|
|
|
|
Components.utils.import("resource://gre/modules/ctypes.jsm");
|
|
|
|
var exit = Zotero.IPC.getLibc().declare("exit", ctypes.default_abi,
|
|
|
|
ctypes.void_t, ctypes.int);
|
|
|
|
// Zap cache files
|
|
|
|
try {
|
|
|
|
Services.dirsvc.get("ProfLD", Components.interfaces.nsIFile).remove(true);
|
|
|
|
} catch(e) {}
|
|
|
|
// Exit Zotero without giving XULRunner the opportunity to figure out the
|
|
|
|
// cache is missing. Otherwise XULRunner will zap the prefs
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-13 20:42:32 -05:00
|
|
|
|
2018-09-06 00:53:50 -04:00
|
|
|
function _checkExecutableLocation() {
|
|
|
|
// Make sure Zotero wasn't started from a Mac disk image, which can cause bundled extensions
|
|
|
|
// not to load and possibly other problems
|
|
|
|
if (Zotero.isMac && OS.Constants.Path.libDir.includes('AppTranslocation')) {
|
|
|
|
let ps = Services.prompt;
|
2019-02-18 14:36:53 -05:00
|
|
|
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING;
|
2020-03-21 00:53:14 -04:00
|
|
|
ps.confirmEx(
|
2018-09-06 00:53:50 -04:00
|
|
|
null,
|
2019-02-18 14:36:53 -05:00
|
|
|
Zotero.getString('general.error'),
|
2018-09-06 00:53:50 -04:00
|
|
|
Zotero.getString('startupError.startedFromDiskImage1', Zotero.clientName)
|
|
|
|
+ '\n\n'
|
|
|
|
+ Zotero.getString('startupError.startedFromDiskImage2', Zotero.clientName),
|
|
|
|
buttonFlags,
|
|
|
|
Zotero.getString('general.quitApp', Zotero.clientName),
|
2019-02-18 14:36:53 -05:00
|
|
|
null, null, null, {}
|
2018-09-06 00:53:50 -04:00
|
|
|
);
|
2019-02-18 14:36:53 -05:00
|
|
|
Zotero.Utilities.Internal.quit();
|
|
|
|
return false;
|
2018-09-06 00:53:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-02-13 20:42:32 -05:00
|
|
|
/**
|
|
|
|
* Observer for console messages
|
|
|
|
* @namespace
|
|
|
|
*/
|
|
|
|
var ConsoleListener = {
|
2020-06-30 02:08:08 -04:00
|
|
|
"QueryInterface":ChromeUtils.generateQI([Components.interfaces.nsIConsoleMessage,
|
2012-02-13 20:42:32 -05:00
|
|
|
Components.interfaces.nsISupports]),
|
|
|
|
"observe":function(msg) {
|
|
|
|
if(!_shouldKeepError(msg)) return;
|
2012-02-14 00:40:17 -05:00
|
|
|
if(_recentErrors.length === ERROR_BUFFER_SIZE) _recentErrors.shift();
|
2012-02-13 20:42:32 -05:00
|
|
|
_recentErrors.push(msg);
|
|
|
|
}
|
|
|
|
};
|
2011-06-14 00:36:21 +00:00
|
|
|
}).call(Zotero);
|
2006-06-13 15:14:22 +00:00
|
|
|
|
2006-06-25 07:31:01 +00:00
|
|
|
|
2007-10-23 07:11:59 +00:00
|
|
|
/*
|
|
|
|
* Handles keyboard shortcut initialization from preferences, optionally
|
|
|
|
* overriding existing global shortcuts
|
|
|
|
*
|
|
|
|
* Actions are configured in ZoteroPane.handleKeyPress()
|
|
|
|
*/
|
|
|
|
Zotero.Keys = new function() {
|
|
|
|
this.init = init;
|
|
|
|
this.windowInit = windowInit;
|
|
|
|
this.getCommand = getCommand;
|
|
|
|
|
Merged revisions 2640-2647,2651,2653-2654,2656-2658,2660-2667,2670-2672,2674-2677,2680,2683-2684,2687-2704,2707 to trunk via svnmerge from 1.0 branch
2008-05-16 09:14:11 +00:00
|
|
|
var _keys = {};
|
2007-10-23 07:11:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by Zotero.init()
|
|
|
|
*/
|
|
|
|
function init() {
|
2019-09-20 03:50:42 -04:00
|
|
|
var cmds = Zotero.Prefs.rootBranch.getChildList(ZOTERO_CONFIG.PREF_BRANCH + 'keys', {}, {});
|
2007-10-23 07:11:59 +00:00
|
|
|
|
|
|
|
// Get the key=>command mappings from the prefs
|
2016-10-18 13:20:01 +01:00
|
|
|
for (let cmd of cmds) {
|
2019-10-25 15:18:28 -04:00
|
|
|
cmd = cmd.replace(/^extensions\.zotero\.keys\./, '');
|
2013-07-31 23:19:19 -04:00
|
|
|
// Remove old pref
|
2014-04-30 15:06:35 -04:00
|
|
|
if (cmd == 'overrideGlobal') {
|
2013-01-22 18:38:00 -05:00
|
|
|
Zotero.Prefs.clear('keys.overrideGlobal');
|
2007-10-23 07:11:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-04-30 15:06:35 -04:00
|
|
|
_keys[this.getKeyForCommand(cmd)] = cmd;
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by ZoteroPane.onLoad()
|
|
|
|
*/
|
|
|
|
function windowInit(document) {
|
2013-07-31 23:19:19 -04:00
|
|
|
var globalKeys = [
|
|
|
|
{
|
|
|
|
name: 'saveToZotero',
|
|
|
|
defaultKey: 'S'
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2013-07-31 23:19:19 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
globalKeys.forEach(function (x) {
|
|
|
|
let keyElem = document.getElementById('key_' + x.name);
|
|
|
|
if (keyElem) {
|
2014-04-30 15:06:35 -04:00
|
|
|
let prefKey = this.getKeyForCommand(x.name);
|
2013-07-31 23:19:19 -04:00
|
|
|
// Only override the default with the pref if the <key> hasn't
|
|
|
|
// been manually changed and the pref has been
|
|
|
|
if (keyElem.getAttribute('key') == x.defaultKey
|
|
|
|
&& keyElem.getAttribute('modifiers') == 'accel shift'
|
|
|
|
&& prefKey != x.defaultKey) {
|
|
|
|
keyElem.setAttribute('key', prefKey);
|
2011-02-01 06:24:52 +00:00
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2014-04-30 15:06:35 -04:00
|
|
|
}.bind(this));
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getCommand(key) {
|
2013-07-26 15:34:39 -04:00
|
|
|
key = key.toUpperCase();
|
2007-10-23 07:11:59 +00:00
|
|
|
return _keys[key] ? _keys[key] : false;
|
|
|
|
}
|
2014-04-30 15:06:35 -04:00
|
|
|
|
|
|
|
|
|
|
|
this.getKeyForCommand = function (cmd) {
|
|
|
|
try {
|
|
|
|
var key = Zotero.Prefs.get('keys.' + cmd);
|
|
|
|
}
|
|
|
|
catch (e) {}
|
|
|
|
return key !== undefined ? key.toUpperCase() : false;
|
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
2006-06-13 15:14:22 +00:00
|
|
|
|
2008-09-16 20:11:27 +00:00
|
|
|
/**
|
2021-12-27 15:54:12 -05:00
|
|
|
* Identify client when connecting to first-party domains
|
2008-09-16 20:11:27 +00:00
|
|
|
*
|
|
|
|
* @namespace
|
|
|
|
*/
|
|
|
|
Zotero.VersionHeader = {
|
|
|
|
init: function () {
|
2018-08-17 02:18:35 -04:00
|
|
|
this.register();
|
2012-02-20 02:26:41 -05:00
|
|
|
Zotero.addShutdownListener(this.unregister);
|
2008-09-16 20:11:27 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
register: function () {
|
2013-06-05 17:54:40 -04:00
|
|
|
Services.obs.addObserver(this, "http-on-modify-request", false);
|
2008-09-16 20:11:27 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
observe: function (subject, topic, data) {
|
|
|
|
try {
|
2018-04-27 01:37:38 -04:00
|
|
|
let channel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
|
2018-05-15 20:18:41 -04:00
|
|
|
let domain = channel.URI.host;
|
2021-12-27 15:54:12 -05:00
|
|
|
// Add X-Zotero-Version header to HTTP requests to zotero.org
|
|
|
|
let isPrimaryDomain = domain == ZOTERO_CONFIG.DOMAIN_NAME
|
|
|
|
|| domain.endsWith('.' + ZOTERO_CONFIG.DOMAIN_NAME);
|
|
|
|
if (isPrimaryDomain) {
|
2008-09-16 20:11:27 +00:00
|
|
|
channel.setRequestHeader("X-Zotero-Version", Zotero.version, false);
|
|
|
|
}
|
2018-04-27 01:37:38 -04:00
|
|
|
else {
|
2022-01-03 01:22:14 -05:00
|
|
|
// Use "Firefox/[version]" in user agent if not a proxy check or file sync request
|
2022-01-03 01:29:35 -05:00
|
|
|
let s3RE = /(zoteroproxycheck|zoterofilestorage(test)?)\.s3\.(us-east-1\.)?amazonaws\.com/;
|
2022-01-03 01:22:14 -05:00
|
|
|
let isAppNameDomain = s3RE.test(domain);
|
2021-12-27 15:54:12 -05:00
|
|
|
if (!isAppNameDomain) {
|
|
|
|
let ua = channel.getRequestHeader('User-Agent');
|
|
|
|
ua = this.update(ua);
|
|
|
|
channel.setRequestHeader('User-Agent', ua, false);
|
|
|
|
}
|
2018-04-27 01:37:38 -04:00
|
|
|
}
|
2008-09-16 20:11:27 +00:00
|
|
|
}
|
|
|
|
catch (e) {
|
2018-04-27 01:37:38 -04:00
|
|
|
Zotero.debug(e, 1);
|
2008-09-16 20:11:27 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-05-15 20:18:41 -04:00
|
|
|
/**
|
2018-08-15 22:06:51 -04:00
|
|
|
* Replace Zotero/[version] with Firefox/[version] in the default user agent
|
2018-05-15 20:18:41 -04:00
|
|
|
*
|
|
|
|
* @param {String} ua - User Agent
|
|
|
|
* @param {String} [testAppName] - App name to look for (necessary in tests, which are
|
|
|
|
* currently run in Firefox)
|
|
|
|
*/
|
2021-12-27 15:54:12 -05:00
|
|
|
update: function (ua, testAppName) {
|
2018-05-15 20:18:41 -04:00
|
|
|
var info = Services.appinfo;
|
|
|
|
var appName = testAppName || info.name;
|
|
|
|
|
|
|
|
var pos = ua.indexOf(appName + '/');
|
2018-08-15 22:06:51 -04:00
|
|
|
var appPart = ua.substr(pos);
|
2018-05-15 20:18:41 -04:00
|
|
|
|
2018-08-15 22:06:51 -04:00
|
|
|
// Default UA (not a faked UA from the connector
|
2018-05-15 20:18:41 -04:00
|
|
|
if (pos != -1) {
|
2024-04-12 10:34:39 +03:00
|
|
|
ua = ua.slice(0, pos) + `Firefox/${info.platformVersion.match(/^\d+/)[0]}.0`;
|
|
|
|
// To fix cloudflare bot detection. rv frozen to avoid some websites
|
|
|
|
// detecting us as IE11. See https://bugzilla.mozilla.org/show_bug.cgi?id=1806690
|
|
|
|
// For some reason we are note getting this from the Firefox base.
|
|
|
|
ua = ua.replace('rv:115', 'rv:109');
|
2018-05-15 20:18:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return ua;
|
|
|
|
},
|
|
|
|
|
2008-09-16 20:11:27 +00:00
|
|
|
unregister: function () {
|
2013-06-05 17:54:40 -04:00
|
|
|
Services.obs.removeObserver(Zotero.VersionHeader, "http-on-modify-request");
|
2008-09-16 20:11:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-30 20:18:48 +00:00
|
|
|
Zotero.DragDrop = {
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
currentEvent: null,
|
2013-09-05 13:37:09 -04:00
|
|
|
currentOrientation: 0,
|
2008-11-30 20:18:48 +00:00
|
|
|
|
2013-09-05 13:37:09 -04:00
|
|
|
getDataFromDataTransfer: function (dataTransfer, firstOnly) {
|
|
|
|
var dt = dataTransfer;
|
2010-07-09 20:10:43 +00:00
|
|
|
|
2013-08-03 18:08:09 -04:00
|
|
|
var dragData = {
|
|
|
|
dataType: '',
|
|
|
|
data: [],
|
|
|
|
dropEffect: dt.dropEffect
|
|
|
|
};
|
|
|
|
|
2010-07-09 20:10:43 +00:00
|
|
|
var len = firstOnly ? 1 : dt.mozItemCount;
|
|
|
|
|
|
|
|
if (dt.types.contains('zotero/collection')) {
|
|
|
|
dragData.dataType = 'zotero/collection';
|
2017-05-23 02:10:26 -04:00
|
|
|
let ids = dt.getData('zotero/collection').split(",").map(id => parseInt(id));
|
2010-07-09 20:10:43 +00:00
|
|
|
dragData.data = ids;
|
|
|
|
}
|
|
|
|
else if (dt.types.contains('zotero/item')) {
|
|
|
|
dragData.dataType = 'zotero/item';
|
2017-05-23 02:10:26 -04:00
|
|
|
let ids = dt.getData('zotero/item').split(",").map(id => parseInt(id));
|
2010-07-09 20:10:43 +00:00
|
|
|
dragData.data = ids;
|
|
|
|
}
|
2017-04-05 14:24:34 -04:00
|
|
|
else {
|
|
|
|
if (dt.types.contains('application/x-moz-file')) {
|
|
|
|
dragData.dataType = 'application/x-moz-file';
|
|
|
|
var files = [];
|
|
|
|
for (var i=0; i<len; i++) {
|
|
|
|
var file = dt.mozGetDataAt("application/x-moz-file", i);
|
|
|
|
if (!file) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
file.QueryInterface(Components.interfaces.nsIFile);
|
|
|
|
// Don't allow folder drag
|
|
|
|
if (file.isDirectory()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
files.push(file);
|
2016-02-24 04:03:05 -05:00
|
|
|
}
|
2017-04-05 14:24:34 -04:00
|
|
|
dragData.data = files;
|
|
|
|
}
|
|
|
|
// This isn't an else because on Linux a link drag contains an empty application/x-moz-file too
|
|
|
|
if (!dragData.data || !dragData.data.length) {
|
|
|
|
if (dt.types.contains('text/x-moz-url')) {
|
|
|
|
dragData.dataType = 'text/x-moz-url';
|
|
|
|
var urls = [];
|
|
|
|
for (var i=0; i<len; i++) {
|
|
|
|
var url = dt.getData("text/x-moz-url").split("\n")[0];
|
|
|
|
urls.push(url);
|
|
|
|
}
|
|
|
|
dragData.data = urls;
|
2008-11-30 20:18:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-07-09 20:10:43 +00:00
|
|
|
|
2008-11-30 20:18:48 +00:00
|
|
|
return dragData;
|
2013-09-05 13:37:09 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
XUL -> JS tree megacommit
- Just a single huge commit. This has been developed over too long a
time, required many tiny changes across too many files and has seen too
many iterations to be separated into separate commits.
The original branch with all the messy commits will be kept around for
posterity
https://github.com/zotero/zotero/compare/bb220ad0f2d6bf0eca6df6d225d3d358cb50a27b...adomasven:feature/react-item-tree
- Replaces XUL <tree> element across the whole zotero client codebase
with a custom supermegafast virtualized-table inspired by
react-virtualized yet mimicking old XUL treeview API. The
virtualized-table sits on top on a raw-to-the-metal,
interpreted-at-runtime JS based windowing solution inspired by
react-window. React-based solutions could not be used because they were
slow and Zotero UI needs to be responsive and be able to
display thousands of rows in a treeview without any slowdowns.
- Attempts were made at making this screen-reader friendly, but yet to
be tested with something like JAWS
- RTL-friendly
- Styling and behaviour across all platforms was copied as closely as
possible to the original XUL tree
- Instead of row-based scroll snapping this has smooth-scrolling. If
you're using arrow keys to browse through the tree then it effectively
snap-scrolls. Current CSS snap scroll attributes do not seem to work in
the way we would require even on up-to-date browsers, yet alone the ESR
version of FX that Zotero is on. JS solutions are either terrible for
performance or produce inexcusable jitter.
- When dragging-and-dropping items the initial drag freezes the UI for
a fairly jarring amount of time. Does not seem to be fixable due to
the synchronous code that needs to be run in the dragstart handler.
Used to be possible to run that code async with the XUL tree.
- Item tree column picker no longer has a dedicated button. Just
right-click the columns. The column preferences (width, order, etc) are
no longer handled by XUL, which required a custom serialization and
storage solution that throws warnings in the developer console due to
the amount of data being stored. Might cause temporary freezing on HDDs
upon column resize/reorder/visibility toggling.
- Context menu handling code basically unchanged, but any UI changes
that plugins may have wanted to do (including adding new columns) will
have to be redone by them. No serious thought has gone into how plugin
developers would achieve that yet.
- Opens up the possibility for awesome alternative ways to render the
tree items, including things like multiple-row view for the item tree,
which has been requested for a long while especially by users switching
from other referencing software
2020-06-03 10:29:46 +03:00
|
|
|
getDragSource: function () {
|
|
|
|
return this.currentDragSource;
|
2013-09-05 13:37:09 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
Async DB megacommit
Promise-based rewrite of most of the codebase, with asynchronous database and file access -- see https://github.com/zotero/zotero/issues/518 for details.
WARNING: This includes backwards-incompatible schema changes.
An incomplete list of other changes:
- Schema overhaul
- Replace main tables with new versions with updated schema
- Enable real foreign key support and remove previous triggers
- Don't use NULLs for local libraryID, which broke the UNIQUE index
preventing object key duplication. All code (Zotero and third-party)
using NULL for the local library will need to be updated to use 0
instead (already done for Zotero code)
- Add 'compatibility' DB version that can be incremented manually to break DB
compatibility with previous versions. 'userdata' upgrades will no longer
automatically break compatibility.
- Demote creators and tags from first-class objects to item properties
- New API syncing properties
- 'synced'/'version' properties to data objects
- 'etag' to groups
- 'version' to libraries
- Create Zotero.DataObject that other objects inherit from
- Consolidate data object loading into Zotero.DataObjects
- Change object reloading so that only the loaded and changed parts of objects are reloaded, instead of reloading all data from the database (with some exceptions, including item primary data)
- Items and collections now have .parentItem and .parentKey properties, replacing item.getSource() and item.getSourceKey()
- New function Zotero.serial(fn), to wrap an async function such that all calls are run serially
- New function Zotero.Utilities.Internal.forEachChunkAsync(arr, chunkSize, func)
- Add tag selector loading message
- Various API and name changes, since everything was breaking anyway
Known broken things:
- Syncing (will be completely rewritten for API syncing)
- Translation architecture (needs promise-based rewrite)
- Duplicates view
- DB integrity check (from schema changes)
- Dragging (may be difficult to fix)
Lots of other big and little things are certainly broken, particularly with the UI, which can be affected by async code in all sorts of subtle ways.
2014-08-06 17:38:05 -04:00
|
|
|
getDragTarget: function (event) {
|
2013-09-05 13:37:09 -04:00
|
|
|
var target = event.target;
|
|
|
|
if (target.tagName == 'treechildren') {
|
|
|
|
var tree = target.parentNode;
|
|
|
|
if (tree.id == 'zotero-collections-tree') {
|
2020-07-05 17:31:03 -04:00
|
|
|
let { row } = tree.getCellAt(event.clientX, event.clientY);
|
2013-09-05 13:37:09 -04:00
|
|
|
let win = tree.ownerDocument.defaultView;
|
2020-07-05 17:31:03 -04:00
|
|
|
return win.ZoteroPane.collectionsView.getRow(row);
|
2013-09-05 13:37:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2008-11-30 20:18:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-23 07:11:59 +00:00
|
|
|
/*
|
|
|
|
* Implements nsIWebProgressListener
|
|
|
|
*/
|
|
|
|
Zotero.WebProgressFinishListener = function(onFinish) {
|
2017-07-09 23:05:02 -04:00
|
|
|
var _request;
|
2018-08-16 00:50:36 -04:00
|
|
|
var _finished = false;
|
2017-07-09 23:05:02 -04:00
|
|
|
|
|
|
|
this.getRequest = function () {
|
|
|
|
return _request;
|
|
|
|
};
|
|
|
|
|
2007-10-23 07:11:59 +00:00
|
|
|
this.onStateChange = function(wp, req, stateFlags, status) {
|
2018-08-16 00:50:36 -04:00
|
|
|
//Zotero.debug('onStateChange: ' + stateFlags);
|
2015-11-28 19:07:11 -05:00
|
|
|
if (stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP
|
2023-05-01 02:30:37 -04:00
|
|
|
&& stateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_NETWORK
|
|
|
|
&& !(stateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_REQUEST)) {
|
2018-08-16 00:50:36 -04:00
|
|
|
if (_finished) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status code and content ype
|
|
|
|
let status = null;
|
|
|
|
let contentType = null;
|
|
|
|
try {
|
|
|
|
let r = _request || req;
|
2018-12-12 10:59:58 +02:00
|
|
|
if (!r) {
|
|
|
|
Zotero.debug("WebProgressFinishListener: finished without a valid request")
|
|
|
|
} else {
|
|
|
|
r.QueryInterface(Components.interfaces.nsIHttpChannel);
|
|
|
|
status = r.responseStatus;
|
|
|
|
contentType = r.contentType;
|
|
|
|
}
|
2018-08-16 00:50:36 -04:00
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
Zotero.debug(e, 2);
|
|
|
|
}
|
|
|
|
|
2017-07-09 23:05:02 -04:00
|
|
|
_request = null;
|
2018-08-16 00:50:36 -04:00
|
|
|
onFinish({ status, contentType });
|
|
|
|
_finished = true;
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2017-07-09 23:05:02 -04:00
|
|
|
else {
|
|
|
|
_request = req;
|
|
|
|
}
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.onProgressChange = function(wp, req, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
|
|
|
|
//Zotero.debug('onProgressChange');
|
|
|
|
//Zotero.debug('Current: ' + curTotalProgress);
|
|
|
|
//Zotero.debug('Max: ' + maxTotalProgress);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.onLocationChange = function(wp, req, location) {}
|
|
|
|
this.onSecurityChange = function(wp, req, stateFlags, status) {}
|
|
|
|
this.onStatusChange = function(wp, req, status, msg) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-05 17:49:04 +00:00
|
|
|
* Saves or loads JSON objects.
|
2007-10-23 07:11:59 +00:00
|
|
|
*/
|
|
|
|
Zotero.JSON = new function() {
|
2010-06-05 17:49:04 +00:00
|
|
|
this.serialize = function(arg) {
|
2011-07-11 22:37:37 +00:00
|
|
|
Zotero.debug("WARNING: Zotero.JSON.serialize() is deprecated; use JSON.stringify()");
|
|
|
|
return JSON.stringify(arg);
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
|
|
|
|
2010-06-05 17:49:04 +00:00
|
|
|
this.unserialize = function(arg) {
|
2011-07-11 22:37:37 +00:00
|
|
|
Zotero.debug("WARNING: Zotero.JSON.unserialize() is deprecated; use JSON.parse()");
|
|
|
|
return JSON.parse(arg);
|
2007-10-23 07:11:59 +00:00
|
|
|
}
|
2010-09-25 16:09:55 +00:00
|
|
|
}
|