www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 5787b230f7d81abbae6ca1956fb0c2c31c18b959
parent 2b4de89aaf13329ef4c7eb78c7328b91ca745cec
Author: Dan Stillman <dstillman@zotero.org>
Date:   Sun, 11 Aug 2013 20:51:16 -0400

Use asynchronous DB and file access for schema checks/updates

This change should improve Firefox startup time. If the Zotero pane is
opened before Zotero has initialized, the pane will be blocked with a
progress bar until it's done. Standalone currently does the same, but it
should be changed to just delay opening the window if possible.

The upgrade wizard has been disabled for schema upgrades, in the hope
that upgrades can be done in a way that won't break compatibility with
earlier versions (or that can at least be done in a way such that we can
put out point releases of the last major version that provide
compatibility during beta/post-upgrade periods).

This patch likely breaks many things, and definitely breaks connector
mode.

This change also removes upgrade steps for databases from Zotero 2.1b2
and earlier. Users with such databases will need to upgrade via Zotero
4.0.x first or delete their data directories and start anew. This should
only affect users who haven't opened Zotero since Nov. 2010.

Diffstat:
Mchrome/content/zotero/browser.js | 2+-
Mchrome/content/zotero/fileInterface.js | 2+-
Mchrome/content/zotero/itemPane.js | 9++++-----
Mchrome/content/zotero/overlay.js | 176+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mchrome/content/zotero/overlay.xul | 1-
Mchrome/content/zotero/standalone/standalone.js | 58+++++++++++++++++++++++++++++++++++-----------------------
Mchrome/content/zotero/xpcom/attachments.js | 9++++-----
Mchrome/content/zotero/xpcom/schema.js | 2415++++++++++++++-----------------------------------------------------------------
Mchrome/content/zotero/xpcom/sync.js | 4++--
Mchrome/content/zotero/xpcom/zotero.js | 291++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mchrome/content/zotero/zoteroPane.js | 253+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mcomponents/zotero-service.js | 32+++++++++++++++++++++-----------
Mresource/schema/userdata.sql | 2+-
13 files changed, 860 insertions(+), 2394 deletions(-)

diff --git a/chrome/content/zotero/browser.js b/chrome/content/zotero/browser.js @@ -101,7 +101,7 @@ var Zotero_Browser = new function() { * Initialize some variables and prepare event listeners for when chrome is done loading */ function init() { - if (!Zotero || !Zotero.initialized || !window.hasOwnProperty("gBrowser")) { + if (!Zotero || Zotero.skipLoading || !window.hasOwnProperty("gBrowser")) { return; } diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js @@ -669,6 +669,6 @@ Zotero_File_Interface.Progress = new function() { } function close() { - Zotero.hideZoteroPaneOverlay(); + Zotero.hideZoteroPaneOverlays(); } } diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js @@ -24,17 +24,16 @@ */ var ZoteroItemPane = new function() { - this.onLoad = onLoad; - var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox; - function onLoad() - { - if (!Zotero || !Zotero.initialized) { + this.onLoad = function () { + if (!Zotero) { return; } // Not in item pane, so skip the introductions + // + // DEBUG: remove? if (!document.getElementById('zotero-view-tabbox')) { return; } diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js @@ -29,56 +29,23 @@ var ZoteroOverlay = new function() { const DEFAULT_ZPANE_HEIGHT = 300; - var toolbarCollapseState, showInPref; + var toolbarCollapseState, showInPref; var zoteroPane, zoteroSplitter; var _stateBeforeReload = false; + var _initializationDeferred, _initializationPromise; this.isTab = false; this.onLoad = function() { - try { - zoteroPane = document.getElementById('zotero-pane-stack'); zoteroSplitter = document.getElementById('zotero-splitter'); - ZoteroPane_Overlay = ZoteroPane; - ZoteroPane.init(); - - // Open Zotero app tab, if in Fx 4 and requested by pref - showInPref = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService) - .getBranch('extensions.zotero.').getIntPref('showIn'); - this.isTab = showInPref !== 1; - - var observerService = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - var zoteroObserver = function(subject, topic, data) { - if(subject != window) return; - observerService.removeObserver(this, "browser-delayed-startup-finished"); - if(showInPref === 3) { - var tabbar = document.getElementById("TabsToolbar"); - if(tabbar && window.getComputedStyle(tabbar).display !== "none") { - // load Zotero as a tab, if it isn't loading by default - ZoteroOverlay.loadZoteroTab(true); - } - } else if(showInPref === 1) { - // close Zotero as a tab, in case it was pinned - var zoteroTab = ZoteroOverlay.findZoteroTab(); - if(zoteroTab) gBrowser.removeTab(zoteroTab); - } - }; - - observerService.addObserver(zoteroObserver, "browser-delayed-startup-finished", false); - // Make Zotero icon visible, if requested var prefBranch = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService) .getBranch('extensions.zotero.'); - var addonBar = document.getElementById('addon-bar'); - var iconPref = prefBranch.getIntPref('statusBarIcon'); - // If this is the first run, add icon to add-on bar if not // in the window already and not hidden by the Zotero prefs if (!document.getElementById("zotero-toolbar-button") && iconPref != 0) { @@ -91,10 +58,53 @@ var ZoteroOverlay = new function() var icon = document.getElementById('zotero-toolbar-button'); - // Add a listener for toolbar change events - window.addEventListener("customizationchange", onToolbarChange, false); + var self = this; - if (Zotero && Zotero.initialized){ + Q.fcall(function () { + if (!Zotero || Zotero.skipLoading) { + throw true; + } + return Zotero.unlockPromise; + }) + .then(function () { + Zotero.debug("Initializing overlay"); + + if (Zotero.skipLoading) { + throw true; + } + + ZoteroPane_Overlay = ZoteroPane; + ZoteroPane.init(); + + // Open Zotero app tab, if in Fx 4 and requested by pref + showInPref = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService) + .getBranch('extensions.zotero.').getIntPref('showIn'); + self.isTab = showInPref !== 1; + + var observerService = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + var zoteroObserver = function(subject, topic, data) { + if(subject != window) return; + observerService.removeObserver(this, "browser-delayed-startup-finished"); + if(showInPref === 3) { + var tabbar = document.getElementById("TabsToolbar"); + if(tabbar && window.getComputedStyle(tabbar).display !== "none") { + // load Zotero as a tab, if it isn't loading by default + ZoteroOverlay.loadZoteroTab(true); + } + } else if(showInPref === 1) { + // close Zotero as a tab, in case it was pinned + var zoteroTab = ZoteroOverlay.findZoteroTab(); + if(zoteroTab) gBrowser.removeTab(zoteroTab); + } + }; + + observerService.addObserver(zoteroObserver, "browser-delayed-startup-finished", false); + + // Add a listener for toolbar change events + window.addEventListener("customizationchange", onToolbarChange, false); + document.getElementById('appcontent').addEventListener('mousemove', Zotero.ProgressWindowSet.updateTimers, false); if (icon) { if (iconPref == 1) { @@ -111,58 +121,49 @@ var ZoteroOverlay = new function() } } } - } - else { - if (Zotero) { - var errMsg = Zotero.startupError; + + // Used for loading pages from upgrade wizard + if (Zotero.initialURL) { + setTimeout(function () { + gBrowser.selectedTab = gBrowser.addTab(Zotero.initialURL); + Zotero.initialURL = null; + }, 1); } + // Hide browser chrome on Zotero tab + XULBrowserWindow.inContentWhitelist.push("chrome://zotero/content/tab.xul"); + + // Close pane if connector is enabled + ZoteroPane_Local.addReloadListener(function() { + if(Zotero.isConnector) { + // save current state + _stateBeforeReload = !zoteroPane.hidden && !zoteroPane.collapsed; + // ensure pane is closed + if(!zoteroPane.collapsed) ZoteroOverlay.toggleDisplay(false, true); + } else { + // reopen pane if it was open before + ZoteroOverlay.toggleDisplay(_stateBeforeReload, true); + } + }); + }) + .catch(function (e) { + var errMsg = Zotero ? Zotero.startupError : null; // Use defaults if necessary if (!errMsg) { // Get the stringbundle manually var src = 'chrome://zotero/locale/zotero.properties'; - var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1']. - getService(Components.interfaces.nsILocaleService); + var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'] + .getService(Components.interfaces.nsILocaleService); var appLocale = localeService.getApplicationLocale(); var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] .getService(Components.interfaces.nsIStringBundleService); var stringBundle = stringBundleService.createBundle(src, appLocale); - var errMsg = stringBundle.GetStringFromName('startupError'); + errMsg = stringBundle.GetStringFromName('startupError'); } - icon.setAttribute('tooltiptext', errMsg); icon.setAttribute('error', 'true'); - } - - // Used for loading pages from upgrade wizard - if (Zotero && Zotero.initialURL) { - setTimeout(function () { - gBrowser.selectedTab = gBrowser.addTab(Zotero.initialURL); - Zotero.initialURL = null; - }, 1); - } - - // Hide browser chrome on Zotero tab - XULBrowserWindow.inContentWhitelist.push("chrome://zotero/content/tab.xul"); - - // Close pane if connector is enabled - ZoteroPane_Local.addReloadListener(function() { - if(Zotero.isConnector) { - // save current state - _stateBeforeReload = !zoteroPane.hidden && !zoteroPane.collapsed; - // ensure pane is closed - if(!zoteroPane.collapsed) ZoteroOverlay.toggleDisplay(false, true); - } else { - // reopen pane if it was open before - ZoteroOverlay.toggleDisplay(_stateBeforeReload, true); - } }); - - } - catch (e) { - Zotero.debug(e); - } } @@ -209,7 +210,7 @@ var ZoteroOverlay = new function() */ this.toggleDisplay = function(makeVisible, dontRefocus) { - if(!Zotero || !Zotero.initialized) { + if (!Zotero || Zotero.skipLoading) { ZoteroPane.displayStartupError(); return; } @@ -236,14 +237,6 @@ var ZoteroOverlay = new function() */ if(makeVisible) { - if (Zotero.locked) { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - var msg = Zotero.getString('general.operationInProgress') + '\n\n' + Zotero.getString('general.operationInProgress.waitUntilFinished'); - ps.alert(null, "", msg); - return false; - } - zoteroSplitter.setAttribute('hidden', false); zoteroPane.setAttribute('hidden', false); zoteroPane.setAttribute('collapsed', false); @@ -349,6 +342,19 @@ var ZoteroOverlay = new function() } } -window.addEventListener("load", function(e) { ZoteroOverlay.onLoad(e); }, false); +window.addEventListener("load", function(e) { + try { + ZoteroOverlay.onLoad(e); + } + catch (e) { + Components.utils.reportError(e); + if (Zotero) { + Zotero.debug(e, 1); + } + else { + dump(e + "\n\n"); + } + } +}, false); window.addEventListener("unload", function(e) { ZoteroOverlay.onUnload(e); }, false); window.addEventListener("beforeunload", function(e) { ZoteroOverlay.onBeforeUnload(e); }, false); diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul @@ -38,7 +38,6 @@ <overlay id="zotero" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <!-- Include the global XPCOM object --> <script src="overlay.js"/> <popup id="contentAreaContextMenu"/> diff --git a/chrome/content/zotero/standalone/standalone.js b/chrome/content/zotero/standalone/standalone.js @@ -33,32 +33,44 @@ const ZoteroStandalone = new function() { * Run when standalone window first opens */ this.onLoad = function() { - if(!Zotero || !Zotero.initialized) { + Q.fcall(function () { + if(!Zotero) { + throw true; + } + if(Zotero.initializationPromise.isPending()) { + Zotero.showZoteroPaneProgressMeter(); + } + return Zotero.initializationPromise; + }) + .then(function () { + Zotero.hideZoteroPaneOverlays(); + ZoteroPane.init(); + ZoteroPane.makeVisible(); + + // Don't ask before handing http and https URIs + var eps = Components.classes['@mozilla.org/uriloader/external-protocol-service;1'] + .getService(Components.interfaces.nsIExternalProtocolService); + var hs = Components.classes["@mozilla.org/uriloader/handler-service;1"] + .getService(Components.interfaces.nsIHandlerService); + for each(var scheme in ["http", "https"]) { + var handlerInfo = eps.getProtocolHandlerInfo(scheme); + handlerInfo.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault; + handlerInfo.alwaysAskBeforeHandling = false; + hs.store(handlerInfo); + } + + // Add add-on listeners (not yet hooked up) + Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); + Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); + Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); + Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); + Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false); + }) + .catch(function (e) { ZoteroPane.displayStartupError(); window.close(); return; - } - ZoteroPane.init(); - ZoteroPane.makeVisible(); - - // Don't ask before handing http and https URIs - var eps = Components.classes['@mozilla.org/uriloader/external-protocol-service;1'] - .getService(Components.interfaces.nsIExternalProtocolService); - var hs = Components.classes["@mozilla.org/uriloader/handler-service;1"] - .getService(Components.interfaces.nsIHandlerService); - for each(var scheme in ["http", "https"]) { - var handlerInfo = eps.getProtocolHandlerInfo(scheme); - handlerInfo.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault; - handlerInfo.alwaysAskBeforeHandling = false; - hs.store(handlerInfo); - } - - // Add add-on listeners (not yet hooked up) - Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); - Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); - Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); - Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); - Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false); + }); } /** diff --git a/chrome/content/zotero/xpcom/attachments.js b/chrome/content/zotero/xpcom/attachments.js @@ -1476,12 +1476,11 @@ Zotero.Attachments = new function(){ } // Since the callback can be called during an import process that uses - // Zotero.wait(), try to queue the callback to run at the end, - // or run now if not queued - var queued = Zotero.addUnlockCallback(writeCallback); - if (!queued) { + // Zotero.wait(), wait until we're unlocked + Zotero.unlockPromise + .then(function () { writeCallback(); - } + }); }; Zotero.File.addCharsetListener(browser, callback, itemID); diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js @@ -32,7 +32,6 @@ Zotero.Schema = new function(){ var _schemaVersions = []; var _repositoryTimer; var _remoteUpdateInProgress = false, _localUpdateInProgress = false; - var _renamedStylesByNew = null; var self = this; @@ -41,72 +40,20 @@ Zotero.Schema = new function(){ */ this.getDBVersion = function (schema) { if (_dbVersions[schema]){ - return _dbVersions[schema]; + return Q(_dbVersions[schema]); } - if (Zotero.DB.tableExists('version')){ - var dbVersion = Zotero.DB.valueQuery("SELECT version FROM " - + "version WHERE schema='" + schema + "'"); - _dbVersions[schema] = dbVersion; - return dbVersion; - } - return false; - } - - - this.userDataUpgradeRequired = function () { - var dbVersion = this.getDBVersion('userdata'); - var schemaVersion = _getSchemaSQLVersion('userdata'); - - return dbVersion && (dbVersion < schemaVersion); - } - - - this.showUpgradeWizard = function () { - var dbVersion = this.getDBVersion('userdata'); - var schemaVersion = _getSchemaSQLVersion('userdata'); - - // Upgrading from 1.0 or earlier - if (dbVersion <= 36) { - var integrityCheck = true; - var majorUpgrade = true; - } - else { - var integrityCheck = false; - var majorUpgrade = false; - } - - var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Components.interfaces.nsIWindowWatcher); - var data = { - success: false, - majorUpgrade: majorUpgrade, - integrityCheck: integrityCheck - }; - var obj = { Zotero: Zotero, data: data }; - var io = { wrappedJSObject: obj }; - var win = ww.openWindow(null, "chrome://zotero/content/upgrade.xul", - "zotero-schema-upgrade", "chrome,centerscreen,modal", io); - - if (obj.data.e) { - if (obj.data.e.name && obj.data.e.name == "NS_ERROR_FAILURE" && obj.data.e.message.match(/nsIFile\.moveTo/)) { - Zotero.logError(obj.data.e); - return false; - } - - var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Components.interfaces.nsIWindowWatcher); - var data = { - msg: obj.data.msg, - e: obj.data.e, - extraData: "Schema upgrade from " + dbVersion + " to " + schemaVersion - }; - var io = { wrappedJSObject: { Zotero: Zotero, data: data } }; - var win = ww.openWindow(null, "chrome://zotero/content/errorReport.xul", - "zotero-error-report", "chrome,centerscreen,modal", io); + if (!Zotero.DB.tableExists('version')) { + return Q(false) } - return obj.data.success; + var sql = "SELECT version FROM version WHERE schema='" + schema + "'"; + return Zotero.DB.valueQueryAsync(sql) + .then(function (dbVersion) { + dbVersion = parseInt(dbVersion); + _dbVersions[schema] = dbVersion; + return dbVersion; + }); } @@ -114,114 +61,97 @@ Zotero.Schema = new function(){ * Checks if the DB schema exists and is up-to-date, updating if necessary */ this.updateSchema = function () { - var dbVersion = this.getDBVersion('userdata'); - var dbVersion2 = this.getDBVersion('userdata2'); - - // 'schema' check is for old (<= 1.0b1) schema system, - // 'user' is for pre-1.0b2 'user' table - if (!dbVersion && !this.getDBVersion('schema') && !this.getDBVersion('user')){ - Zotero.debug('Database does not exist -- creating\n'); - _initializeSchema(); - return true; - } - - var schemaVersion = _getSchemaSQLVersion('userdata'); - - try { - Zotero.UnresponsiveScriptIndicator.disable(); - - // If upgrading userdata, make backup of database first - if (dbVersion < schemaVersion){ - Zotero.DB.backupDatabase(dbVersion); - Zotero.wait(1000); + return Q.all([this.getDBVersion('userdata'), this.getDBVersion('userdata3')]) + .spread(function (oldDBVersion, dbVersion) { + if (!oldDBVersion) { + Zotero.debug('Database does not exist -- creating\n'); + return _initializeSchema().thenResolve(true); } - Zotero.DB.beginTransaction(); - - try { - // Old schema system - if (!dbVersion){ - // Check for pre-1.0b2 'user' table - var user = this.getDBVersion('user'); - if (user) - { - dbVersion = user; - var sql = "UPDATE version SET schema=? WHERE schema=?"; - Zotero.DB.query(sql, ['userdata', 'user']); - } - else - { - dbVersion = 0; - } - } - - var up1 = _updateSchema('system'); - // Update custom tables if they exist so that changes are in place before user data migration - if (Zotero.DB.tableExists('customItemTypes')) { - this.updateCustomTables(up1); - } - if(up1) Zotero.wait(); - var up2 = _migrateUserDataSchema(dbVersion); - var up3 = _migrateUserDataSchemaSilent(dbVersion2); - var up4 = _updateSchema('triggers'); - if (up2) { - // Update custom tables again in case custom fields were changed during user data migration - this.updateCustomTables(); - Zotero.wait() - } - - Zotero.DB.commitTransaction(); - } - catch(e){ - Zotero.debug(e); - Zotero.DB.rollbackTransaction(); - throw(e); + if (dbVersion < 76) { + // TODO: localize + let msg = "Zotero found a pre–Zotero 2.1 database that cannot be upgraded to " + + "work with this version of Zotero. To continue, either upgrade your " + + "database using an earlier version of Zotero or delete your " + + "Zotero data directory to start with a new database." + throw new Error(msg); } - if (up2) { - // Upgrade seems to have been a success -- delete any previous backups - var maxPrevious = dbVersion - 1; - var file = Zotero.getZoteroDirectory(); - var toDelete = []; - try { - var files = file.directoryEntries; - while (files.hasMoreElements()) { - var file = files.getNext(); - file.QueryInterface(Components.interfaces.nsIFile); - if (file.isDirectory()) { - continue; - } - var matches = file.leafName.match(/zotero\.sqlite\.([0-9]{2,})\.bak/); - if (!matches) { - continue; - } - if (matches[1]>=28 && matches[1]<=maxPrevious) { - toDelete.push(file); - } - } - for each(var file in toDelete) { - Zotero.debug('Removing previous backup file ' + file.leafName); - file.remove(false); - } - } - catch (e) { - Zotero.debug(e); + return _getSchemaSQLVersion('userdata3') + // If upgrading userdata, make backup of database first + .then(function (schemaVersion) { + if (dbVersion < schemaVersion) { + return Zotero.DB.backupDatabase(dbVersion); } - } - } - finally { - Zotero.UnresponsiveScriptIndicator.enable(); - } - - // After a delay, start update of bundled files and repo updates - setTimeout(function () { - Zotero.UnresponsiveScriptIndicator.disable(); - Zotero.Schema.updateBundledFiles(null, false, true) - .finally(function () { - Zotero.UnresponsiveScriptIndicator.enable(); }) - .done(); - }, 5000); + .then(function () { + return Zotero.DB.executeTransaction(function (conn) { + var up1 = yield _updateSchema('system'); + + // Update custom tables if they exist so that changes are in + // place before user data migration + if (Zotero.DB.tableExists('customItemTypes')) { + yield Zotero.Schema.updateCustomTables(up1); + } + var up2 = yield _migrateUserDataSchema(dbVersion); + yield _updateSchema('triggers'); + + Zotero.DB.asyncResult(up2); + }) + .then(function (updated) { + // Populate combined tables for custom types and fields + // -- this is likely temporary + // + // We do this even if updated in case custom fields were + // changed during user data migration + return Zotero.Schema.updateCustomTables() + .then(function () { + if (updated) { + // Upgrade seems to have been a success -- delete any previous backups + var maxPrevious = dbVersion - 1; + var file = Zotero.getZoteroDirectory(); + var toDelete = []; + try { + var files = file.directoryEntries; + while (files.hasMoreElements()) { + var file = files.getNext(); + file.QueryInterface(Components.interfaces.nsIFile); + if (file.isDirectory()) { + continue; + } + var matches = file.leafName.match(/zotero\.sqlite\.([0-9]{2,})\.bak/); + if (!matches) { + continue; + } + if (matches[1]>=28 && matches[1]<=maxPrevious) { + toDelete.push(file); + } + } + for each(var file in toDelete) { + Zotero.debug('Removing previous backup file ' + file.leafName); + file.remove(false); + } + } + catch (e) { + Zotero.debug(e); + } + } + + // After a delay, start update of bundled files and repo updates + setTimeout(function () { + Zotero.UnresponsiveScriptIndicator.disable(); + Zotero.Schema.updateBundledFiles(null, false, true) + .finally(function () { + Zotero.UnresponsiveScriptIndicator.enable(); + }) + .done(); + }, 5000); + + return updated; + }); + }); + }); + }); } @@ -360,71 +290,76 @@ Zotero.Schema = new function(){ } function _reloadSchema() { - Zotero.Schema.updateCustomTables(); - Zotero.ItemTypes.reload(); - Zotero.ItemFields.reload(); - Zotero.SearchConditions.reload(); - - // Update item type menus in every open window - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - var enumerator = wm.getEnumerator("navigator:browser"); - while (enumerator.hasMoreElements()) { - var win = enumerator.getNext(); - win.ZoteroPane.buildItemTypeSubMenu(); - win.document.getElementById('zotero-editpane-item-box').buildItemTypeMenu(); - } + Zotero.Schema.updateCustomTables() + .then(function () { + Zotero.ItemTypes.reload(); + Zotero.ItemFields.reload(); + Zotero.SearchConditions.reload(); + + // Update item type menus in every open window + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var enumerator = wm.getEnumerator("navigator:browser"); + while (enumerator.hasMoreElements()) { + var win = enumerator.getNext(); + win.ZoteroPane.buildItemTypeSubMenu(); + win.document.getElementById('zotero-editpane-item-box').buildItemTypeMenu(); + } + }) + .done(); } this.updateCustomTables = function (skipDelete, skipSystem) { - Zotero.debug("Updating custom tables"); - - if (!skipDelete) { - Zotero.DB.query("DELETE FROM itemTypesCombined"); - Zotero.DB.query("DELETE FROM fieldsCombined"); - Zotero.DB.query("DELETE FROM itemTypeFieldsCombined"); - Zotero.DB.query("DELETE FROM baseFieldMappingsCombined"); - } - var offset = Zotero.ItemTypes.customIDOffset; - Zotero.DB.query( - "INSERT INTO itemTypesCombined " - + ( - skipSystem - ? "" - : "SELECT itemTypeID, typeName, display, 0 AS custom FROM itemTypes UNION " - ) - + "SELECT customItemTypeID + " + offset + " AS itemTypeID, typeName, display, 1 AS custom FROM customItemTypes" - ); - Zotero.DB.query( - "INSERT INTO fieldsCombined " - + ( - skipSystem - ? "" - : "SELECT fieldID, fieldName, NULL AS label, fieldFormatID, 0 AS custom FROM fields UNION " - ) - + "SELECT customFieldID + " + offset + " AS fieldID, fieldName, label, NULL, 1 AS custom FROM customFields" - ); - Zotero.DB.query( - "INSERT INTO itemTypeFieldsCombined " - + ( - skipSystem - ? "" - : "SELECT itemTypeID, fieldID, hide, orderIndex FROM itemTypeFields UNION " - ) - + "SELECT customItemTypeID + " + offset + " AS itemTypeID, " - + "COALESCE(fieldID, customFieldID + " + offset + ") AS fieldID, hide, orderIndex FROM customItemTypeFields" - ); - Zotero.DB.query( - "INSERT INTO baseFieldMappingsCombined " - + ( - skipSystem - ? "" - : "SELECT itemTypeID, baseFieldID, fieldID FROM baseFieldMappings UNION " - ) - + "SELECT customItemTypeID + " + offset + " AS itemTypeID, baseFieldID, " - + "customFieldID + " + offset + " AS fieldID FROM customBaseFieldMappings" - ); + return Zotero.DB.executeTransaction(function (conn) { + Zotero.debug("Updating custom tables"); + + if (!skipDelete) { + yield Zotero.DB.queryAsync("DELETE FROM itemTypesCombined"); + yield Zotero.DB.queryAsync("DELETE FROM fieldsCombined"); + yield Zotero.DB.queryAsync("DELETE FROM itemTypeFieldsCombined"); + yield Zotero.DB.queryAsync("DELETE FROM baseFieldMappingsCombined"); + } + var offset = Zotero.ItemTypes.customIDOffset; + yield Zotero.DB.queryAsync( + "INSERT INTO itemTypesCombined " + + ( + skipSystem + ? "" + : "SELECT itemTypeID, typeName, display, 0 AS custom FROM itemTypes UNION " + ) + + "SELECT customItemTypeID + " + offset + " AS itemTypeID, typeName, display, 1 AS custom FROM customItemTypes" + ); + yield Zotero.DB.queryAsync( + "INSERT INTO fieldsCombined " + + ( + skipSystem + ? "" + : "SELECT fieldID, fieldName, NULL AS label, fieldFormatID, 0 AS custom FROM fields UNION " + ) + + "SELECT customFieldID + " + offset + " AS fieldID, fieldName, label, NULL, 1 AS custom FROM customFields" + ); + yield Zotero.DB.queryAsync( + "INSERT INTO itemTypeFieldsCombined " + + ( + skipSystem + ? "" + : "SELECT itemTypeID, fieldID, hide, orderIndex FROM itemTypeFields UNION " + ) + + "SELECT customItemTypeID + " + offset + " AS itemTypeID, " + + "COALESCE(fieldID, customFieldID + " + offset + ") AS fieldID, hide, orderIndex FROM customItemTypeFields" + ); + yield Zotero.DB.queryAsync( + "INSERT INTO baseFieldMappingsCombined " + + ( + skipSystem + ? "" + : "SELECT itemTypeID, baseFieldID, fieldID FROM baseFieldMappings UNION " + ) + + "SELECT customItemTypeID + " + offset + " AS itemTypeID, baseFieldID, " + + "customFieldID + " + offset + " AS fieldID FROM customBaseFieldMappings" + ); + }); } @@ -962,102 +897,116 @@ Zotero.Schema = new function(){ * @param {Function} callback */ this.updateFromRepository = function (force, callback) { - if (!force){ + Q.fcall(function () { + if (force) return true; + if (_remoteUpdateInProgress) { Zotero.debug("A remote update is already in progress -- not checking repository"); return false; } // Check user preference for automatic updates - if (!Zotero.Prefs.get('automaticScraperUpdates')){ + if (!Zotero.Prefs.get('automaticScraperUpdates')) { Zotero.debug('Automatic repository updating disabled -- not checking repository', 4); return false; } // Determine the earliest local time that we'd query the repository again - var nextCheck = new Date(); - nextCheck.setTime((parseInt(this.getDBVersion('lastcheck')) - + ZOTERO_CONFIG['REPOSITORY_CHECK_INTERVAL']) * 1000); // JS uses ms - var now = new Date(); - - // If enough time hasn't passed, don't update - if (now < nextCheck){ - Zotero.debug('Not enough time since last update -- not checking repository', 4); - // Set the repository timer to the remaining time - _setRepositoryTimer(Math.round((nextCheck.getTime() - now.getTime()) / 1000)); - return false; - } - } - - if (_localUpdateInProgress) { - Zotero.debug('A local update is already in progress -- delaying repository check', 4); - _setRepositoryTimer(600); - return false; - } - - if (Zotero.locked) { - Zotero.debug('Zotero is locked -- delaying repository check', 4); - _setRepositoryTimer(600); - return false; - } - - // If transaction already in progress, delay by ten minutes - if (Zotero.DB.transactionInProgress()){ - Zotero.debug('Transaction in progress -- delaying repository check', 4) - _setRepositoryTimer(600); - return false; - } - - // Get the last timestamp we got from the server - var lastUpdated = this.getDBVersion('repository'); - - var url = ZOTERO_CONFIG['REPOSITORY_URL'] + '/updated?' - + (lastUpdated ? 'last=' + lastUpdated + '&' : '') - + 'version=' + Zotero.version; - - Zotero.debug('Checking repository for updates'); - - _remoteUpdateInProgress = true; - - if (force) { - if (force == 2) { - url += '&m=2'; - } - else { - url += '&m=1'; + return self.getDBVersion('lastcheck') + .then(function (lastCheck) { + var nextCheck = new Date(); + nextCheck.setTime((lastCheck + ZOTERO_CONFIG.REPOSITORY_CHECK_INTERVAL) * 1000); + + var now = new Date(); + + // If enough time hasn't passed, don't update + if (now < nextCheck) { + Zotero.debug('Not enough time since last update -- not checking repository', 4); + // Set the repository timer to the remaining time + _setRepositoryTimer(Math.round((nextCheck.getTime() - now.getTime()) / 1000)); + return false; + } + + return true; + }); + }) + .then(function (update) { + if (!update) return; + + if (_localUpdateInProgress) { + Zotero.debug('A local update is already in progress -- delaying repository check', 4); + _setRepositoryTimer(600); + return; } - } - - // Send list of installed styles - var styles = Zotero.Styles.getAll(); - var styleTimestamps = []; - for (var id in styles) { - var updated = Zotero.Date.sqlToDate(styles[id].updated); - updated = updated ? updated.getTime() / 1000 : 0; - var selfLink = styles[id].url; - var data = { - id: id, - updated: updated - }; - if (selfLink) { - data.url = selfLink; + + if (Zotero.locked) { + Zotero.debug('Zotero is locked -- delaying repository check', 4); + _setRepositoryTimer(600); + return; } - styleTimestamps.push(data); - } - var body = 'styles=' + encodeURIComponent(JSON.stringify(styleTimestamps)); - - var get = Zotero.HTTP.doPost(url, body, function (xmlhttp) { - var updated = _updateFromRepositoryCallback(xmlhttp, !!force); - if (callback) { - callback(xmlhttp, updated) + + // If transaction already in progress, delay by ten minutes + if (Zotero.DB.transactionInProgress()) { + Zotero.debug('Transaction in progress -- delaying repository check', 4) + _setRepositoryTimer(600); + return; } - }); - - // TODO: instead, add an observer to start and stop timer on online state change - if (!get){ - Zotero.debug('Browser is offline -- skipping check'); - _setRepositoryTimer(ZOTERO_CONFIG['REPOSITORY_RETRY_INTERVAL']); - } + + // Get the last timestamp we got from the server + return self.getDBVersion('repository') + .then(function (lastUpdated) { + var url = ZOTERO_CONFIG['REPOSITORY_URL'] + '/updated?' + + (lastUpdated ? 'last=' + lastUpdated + '&' : '') + + 'version=' + Zotero.version; + + Zotero.debug('Checking repository for updates'); + + _remoteUpdateInProgress = true; + + if (force) { + if (force == 2) { + url += '&m=2'; + } + else { + url += '&m=1'; + } + } + + // Send list of installed styles + var styles = Zotero.Styles.getAll(); + var styleTimestamps = []; + for (var id in styles) { + var updated = Zotero.Date.sqlToDate(styles[id].updated); + updated = updated ? updated.getTime() / 1000 : 0; + var selfLink = styles[id].url; + var data = { + id: id, + updated: updated + }; + if (selfLink) { + data.url = selfLink; + } + styleTimestamps.push(data); + } + var body = 'styles=' + encodeURIComponent(JSON.stringify(styleTimestamps)); + + var get = Zotero.HTTP.doPost(url, body, function (xmlhttp) { + _updateFromRepositoryCallback(xmlhttp, !!force) + .then(function (updated) { + if (callback) { + callback(xmlhttp, updated); + } + }); + }); + + // TODO: instead, add an observer to start and stop timer on online state change + if (!get) { + Zotero.debug('Browser is offline -- skipping check'); + _setRepositoryTimer(ZOTERO_CONFIG.REPOSITORY_RETRY_INTERVAL); + } + }); + }) + .done(); } @@ -1357,42 +1306,45 @@ Zotero.Schema = new function(){ // ///////////////////////////////////////////////////////////////// - /* + /** * Retrieve the version from the top line of the schema SQL file + * + * @return {Promise:String} A promise for the SQL file's version number */ function _getSchemaSQLVersion(schema){ // TEMP - if (schema == 'userdata2') { + if (schema == 'userdata3') { schema = 'userdata'; var newUserdata = true; } - var sql = _getSchemaSQL(schema); - - // Fetch the schema version from the first line of the file - var schemaVersion = parseInt(sql.match(/^-- ([0-9]+)/)[1]); - - // TEMP: For 'userdata', cap the version at 76 - // For 'userdata2', versions > 76 are allowed. - if (schema == 'userdata' && !newUserdata) { - schemaVersion = Math.min(76, schemaVersion); - } - - _schemaVersions[schema] = schemaVersion; - return schemaVersion; + return _getSchemaSQL(schema) + .then(function (sql) { + // Fetch the schema version from the first line of the file + var schemaVersion = parseInt(sql.match(/^-- ([0-9]+)/)[1]); + + // TEMP: For 'userdata', cap the version at 76 + // For 'userdata3', versions > 76 are allowed. + if (schema == 'userdata' && !newUserdata) { + schemaVersion = Math.min(76, schemaVersion); + } + + _schemaVersions[schema] = schemaVersion; + return schemaVersion; + }); } - /* + /** * Load in SQL schema * - * Returns the contents of an SQL file for feeding into query() + * @return {Promise:String} A promise for the contents of a schema SQL file */ function _getSchemaSQL(schema){ if (!schema){ throw ('Schema type not provided to _getSchemaSQL()'); } - return Zotero.File.getContentsFromURL("resource://zotero/schema/"+schema+".sql"); + return Zotero.File.getContentsFromURLAsync("resource://zotero/schema/" + schema + ".sql"); } @@ -1405,7 +1357,7 @@ Zotero.Schema = new function(){ * Returns the SQL statements as a string for feeding into query() */ function _getDropCommands(schema){ - var sql = _getSchemaSQL(schema); + var sql = _getSchemaSQL(schema); // FIXME: now a promise const re = /(?:[\r\n]|^)CREATE (TABLE|INDEX) IF NOT EXISTS ([^\s]+)/; var m, str=""; @@ -1421,71 +1373,83 @@ Zotero.Schema = new function(){ * Create new DB schema */ function _initializeSchema(){ - Zotero.DB.beginTransaction(); - try { + return Zotero.DB.executeTransaction(function (conn) { // Enable auto-vacuuming - Zotero.DB.query("PRAGMA page_size = 4096"); - Zotero.DB.query("PRAGMA encoding = 'UTF-8'"); - Zotero.DB.query("PRAGMA auto_vacuum = 1"); - - Zotero.DB.query(_getSchemaSQL('system')); - Zotero.DB.query(_getSchemaSQL('userdata')); - Zotero.DB.query(_getSchemaSQL('triggers')); - Zotero.Schema.updateCustomTables(true); - - _updateDBVersion('system', _getSchemaSQLVersion('system')); - _updateDBVersion('userdata', _getSchemaSQLVersion('userdata')); - _updateDBVersion('userdata2', _getSchemaSQLVersion('userdata2')); - _updateDBVersion('triggers', _getSchemaSQLVersion('triggers')); + yield Zotero.DB.queryAsync("PRAGMA page_size = 4096"); + yield Zotero.DB.queryAsync("PRAGMA encoding = 'UTF-8'"); + yield Zotero.DB.queryAsync("PRAGMA auto_vacuum = 1"); + + yield _getSchemaSQL('system').then(function (sql) { + return Zotero.DB.queryAsync(sql); + }); + yield _getSchemaSQL('userdata').then(function (sql) { + return Zotero.DB.queryAsync(sql); + }); + yield _getSchemaSQL('triggers').then(function (sql) { + return Zotero.DB.queryAsync(sql); + }); + yield Zotero.Schema.updateCustomTables(true); + + yield _getSchemaSQLVersion('system').then(function (sql) { + return _updateDBVersion('system', sql); + }); + yield _getSchemaSQLVersion('userdata').then(function (sql) { + return _updateDBVersion('userdata', sql); + }); + yield _getSchemaSQLVersion('userdata3').then(function (sql) { + return _updateDBVersion('userdata3', sql); + }); + yield _getSchemaSQLVersion('triggers').then(function (sql) { + return _updateDBVersion('triggers', sql); + }); if (!Zotero.Schema.skipDefaultData) { // Quick Start Guide web page item var sql = "INSERT INTO items VALUES(1, 13, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, 'ABCD2345')"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); var sql = "INSERT INTO itemDataValues VALUES (1, ?)"; - Zotero.DB.query(sql, Zotero.getString('install.quickStartGuide')); + yield Zotero.DB.queryAsync(sql, Zotero.getString('install.quickStartGuide')); var sql = "INSERT INTO itemData VALUES (1, 110, 1)"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); var sql = "INSERT INTO itemDataValues VALUES (2, 'http://zotero.org/support/quick_start_guide')"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); var sql = "INSERT INTO itemData VALUES (1, 1, 2)"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); // CHNM as creator var sql = "INSERT INTO creatorData VALUES (1, '', 'Center for History and New Media', '', 1, NULL)"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); var sql = "INSERT INTO creators VALUES (1, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, 'ABCD2345')"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); var sql = "INSERT INTO itemCreators VALUES (1, 1, 1, 0)"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); // Welcome note var sql = "INSERT INTO items VALUES(2, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, 'ABCD3456')"; - Zotero.DB.query(sql); + yield Zotero.DB.queryAsync(sql); var welcomeTitle = Zotero.getString('install.quickStartGuide.message.welcome'); var welcomeMsg = '<div class="zotero-note znv1"><p><strong>' + welcomeTitle + '</strong></p>' + '<p>' + Zotero.getString('install.quickStartGuide.message.view') + '</p>' + '<p>' + Zotero.getString('install.quickStartGuide.message.thanks') + '</p></div>'; var sql = "INSERT INTO itemNotes VALUES (2, 1, ?, ?)"; - Zotero.DB.query(sql, [welcomeMsg, welcomeTitle]); + yield Zotero.DB.queryAsync(sql, [welcomeMsg, welcomeTitle]); } - Zotero.DB.commitTransaction(); self.dbInitialized = true; - } - catch(e){ + }) + .catch(function (e) { Zotero.debug(e, 1); - Zotero.logError(e); - Zotero.DB.rollbackTransaction(); + Components.utils.reportError(e); alert('Error initializing Zotero database'); - throw(e); - } - - Zotero.Schema.updateBundledFiles(null, null, true) - .catch(function (e) { - Zotero.debug(e); - Zotero.logError(e); - alert('Error updating Zotero translators and styles'); + throw e; + }) + .then(function () { + return Zotero.Schema.updateBundledFiles(null, null, true) + .catch(function (e) { + Zotero.debug(e); + Zotero.logError(e); + alert('Error updating Zotero translators and styles'); + }); }); } @@ -1493,44 +1457,41 @@ Zotero.Schema = new function(){ /* * Update a DB schema version tag in an existing database */ - function _updateDBVersion(schema, version){ + function _updateDBVersion(schema, version) { _dbVersions[schema] = version; var sql = "REPLACE INTO version (schema,version) VALUES (?,?)"; - return Zotero.DB.query(sql, [{'string':schema},{'int':version}]); + return Zotero.DB.queryAsync(sql, [schema, version]); } function _updateSchema(schema){ - var dbVersion = Zotero.Schema.getDBVersion(schema); - var schemaVersion = _getSchemaSQLVersion(schema); - - if (dbVersion == schemaVersion){ - return false; - } - else if (dbVersion < schemaVersion){ - Zotero.DB.beginTransaction(); - try { - Zotero.DB.query(_getSchemaSQL(schema)); - _updateDBVersion(schema, schemaVersion); - Zotero.DB.commitTransaction(); + return Q.all([Zotero.Schema.getDBVersion(schema), _getSchemaSQLVersion(schema)]) + .spread(function (dbVersion, schemaVersion) { + if (dbVersion == schemaVersion) { + return false; } - catch (e){ - Zotero.debug(e, 1); - Zotero.DB.rollbackTransaction(); - throw(e); + else if (dbVersion < schemaVersion) { + return _getSchemaSQL(schema) + .then(function (sql) { + return Zotero.DB.queryAsync(sql); + }) + .then(function () { + return _updateDBVersion(schema, schemaVersion); + }); } - return true; - } - - throw ("Zotero '" + schema + "' DB version (" + dbVersion - + ") is newer than SQL file (" + schemaVersion + ")"); + + throw new Error("Zotero '" + schema + "' DB version (" + dbVersion + + ") is newer than SQL file (" + schemaVersion + ")"); + }); } /** - * Process the response from the repository - **/ - function _updateFromRepositoryCallback(xmlhttp, manual){ + * Process the response from the repository + * + * @return {Promise:Boolean} A promise for whether the update suceeded + **/ + function _updateFromRepositoryCallback(xmlhttp, manual) { if (!xmlhttp.responseXML){ try { if (xmlhttp.status>1000){ @@ -1549,35 +1510,33 @@ Zotero.Schema = new function(){ } _remoteUpdateInProgress = false; - return false; + return Q(false); } var currentTime = xmlhttp.responseXML. getElementsByTagName('currentTime')[0].firstChild.nodeValue; + var lastCheckTime = Math.round(new Date().getTime()/1000); var translatorUpdates = xmlhttp.responseXML.getElementsByTagName('translator'); var styleUpdates = xmlhttp.responseXML.getElementsByTagName('style'); - Zotero.DB.beginTransaction(); - - // TODO: clear DB version 'sync' from removed _updateDBVersion() - - // Store the timestamp provided by the server - _updateDBVersion('repository', currentTime); - - if (!manual){ - // And the local timestamp of the update time - var d = new Date(); - _updateDBVersion('lastcheck', Math.round(d.getTime()/1000)); // JS uses ms - } - if (!translatorUpdates.length && !styleUpdates.length){ - Zotero.debug('All translators and styles are up-to-date'); - Zotero.DB.commitTransaction(); - if (!manual){ - _setRepositoryTimer(ZOTERO_CONFIG['REPOSITORY_CHECK_INTERVAL']); - } - _remoteUpdateInProgress = false; - return -1; + return Zotero.DB.executeTransaction(function (conn) { + // Store the timestamp provided by the server + yield _updateDBVersion('repository', currentTime); + + if (!manual) { + // And the local timestamp of the update time + yield _updateDBVersion('lastcheck', lastCheckTime); + } + }) + .then(function () { + Zotero.debug('All translators and styles are up-to-date'); + if (!manual) { + _setRepositoryTimer(ZOTERO_CONFIG['REPOSITORY_CHECK_INTERVAL']); + } + _remoteUpdateInProgress = false; + return -1; + }); } try { @@ -1595,20 +1554,30 @@ Zotero.Schema = new function(){ } catch (e) { Zotero.debug(e, 1); - Zotero.DB.rollbackTransaction(); if (!manual){ _setRepositoryTimer(ZOTERO_CONFIG['REPOSITORY_RETRY_INTERVAL']); } _remoteUpdateInProgress = false; - return false; + return Q(false); } - Zotero.DB.commitTransaction(); - if (!manual){ - _setRepositoryTimer(ZOTERO_CONFIG['REPOSITORY_CHECK_INTERVAL']); - } - _remoteUpdateInProgress = false; - return true; + return Zotero.DB.executeTransaction(function (conn) { + // Store the timestamp provided by the server + yield _updateDBVersion('repository', currentTime); + + if (!manual) { + // And the local timestamp of the update time + yield _updateDBVersion('lastcheck', lastCheckTime); + } + }) + .then(function () { + if (!manual) { + _setRepositoryTimer(ZOTERO_CONFIG['REPOSITORY_CHECK_INTERVAL']); + } + _remoteUpdateInProgress = false; + + return true; + }); } @@ -1767,1579 +1736,47 @@ Zotero.Schema = new function(){ } - /* - * Migrate user data schema from an older version, preserving data - */ - function _migrateUserDataSchema(fromVersion){ - var toVersion = _getSchemaSQLVersion('userdata'); - - // Only upgrades through version 76 are handled here - toVersion = Math.min(76, toVersion); - - if (fromVersion==toVersion){ - return false; - } - - if (fromVersion > toVersion){ - throw("Zotero user data DB version is newer than SQL file"); - } - - Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion); - - Zotero.DB.beginTransaction(); - - try { - // Step through version changes until we reach the current version - // - // Each block performs the changes necessary to move from the - // previous revision to that one. - for (var i=fromVersion + 1; i<=toVersion; i++){ - if (i==1){ - Zotero.DB.query("DELETE FROM version WHERE schema='schema'"); - } - - if (i==5){ - Zotero.DB.query("REPLACE INTO itemData SELECT itemID, 1, originalPath FROM itemAttachments WHERE linkMode=1"); - Zotero.DB.query("REPLACE INTO itemData SELECT itemID, 1, path FROM itemAttachments WHERE linkMode=3"); - Zotero.DB.query("REPLACE INTO itemData SELECT itemID, 27, dateAdded FROM items NATURAL JOIN itemAttachments WHERE linkMode IN (1,3)"); - Zotero.DB.query("UPDATE itemAttachments SET originalPath=NULL WHERE linkMode=1"); - Zotero.DB.query("UPDATE itemAttachments SET path=NULL WHERE linkMode=3"); - try { Zotero.DB.query("DELETE FROM fulltextItems WHERE itemID IS NULL"); } catch(e){} - } - - if (i==6){ - Zotero.DB.query("CREATE TABLE creatorsTemp (creatorID INT, firstName INT, lastName INT, fieldMode INT)"); - Zotero.DB.query("INSERT INTO creatorsTemp SELECT * FROM creators"); - Zotero.DB.query("DROP TABLE creators"); - Zotero.DB.query("CREATE TABLE creators (\n creatorID INT,\n firstName INT,\n lastName INT,\n fieldMode INT,\n PRIMARY KEY (creatorID)\n);"); - Zotero.DB.query("INSERT INTO creators SELECT * FROM creatorsTemp"); - Zotero.DB.query("DROP TABLE creatorsTemp"); - } - - if (i==7){ - Zotero.DB.query("DELETE FROM itemData WHERE fieldID=17"); - Zotero.DB.query("UPDATE itemData SET fieldID=64 WHERE fieldID=20"); - Zotero.DB.query("UPDATE itemData SET fieldID=69 WHERE fieldID=24 AND itemID IN (SELECT itemID FROM items WHERE itemTypeID=7)"); - Zotero.DB.query("UPDATE itemData SET fieldID=65 WHERE fieldID=24 AND itemID IN (SELECT itemID FROM items WHERE itemTypeID=8)"); - Zotero.DB.query("UPDATE itemData SET fieldID=66 WHERE fieldID=24 AND itemID IN (SELECT itemID FROM items WHERE itemTypeID=9)"); - Zotero.DB.query("UPDATE itemData SET fieldID=59 WHERE fieldID=24 AND itemID IN (SELECT itemID FROM items WHERE itemTypeID=12)"); - } - - if (i==8){ - Zotero.DB.query("DROP TABLE IF EXISTS translators"); - Zotero.DB.query("DROP TABLE IF EXISTS csl"); - } - - // 1.0b2 (1.0.0b2.r1) - - if (i==9){ - var attachments = Zotero.DB.query("SELECT itemID, linkMode, path FROM itemAttachments"); - for each(var row in attachments){ - var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); - try { - var refDir = (row.linkMode==Zotero.Attachments.LINK_MODE_LINKED_FILE) ? Zotero.getZoteroDirectory() : Zotero.getStorageDirectory(); - file.setRelativeDescriptor(refDir, row.path); - Zotero.DB.query("UPDATE itemAttachments SET path=? WHERE itemID=?", [file.persistentDescriptor, row.itemID]); - } - catch (e){} - } - } - - // 1.0.0b2.r2 - - if (i==10){ - var dates = Zotero.DB.query("SELECT itemID, value FROM itemData WHERE fieldID=14"); - for each(var row in dates){ - if (!Zotero.Date.isMultipart(row.value)){ - Zotero.DB.query("UPDATE itemData SET value=? WHERE itemID=? AND fieldID=14", [Zotero.Date.strToMultipart(row.value), row.itemID]); - } + // TODO + // + // Replace customBaseFieldMappings to fix FK fields/customField -> customFields->customFieldID + // If libraryID set, make sure no relations still use a local user key, and then remove on-error code in sync.js + + function _migrateUserDataSchema(fromVersion) { + return _getSchemaSQLVersion('userdata3') + .then(function (toVersion) { + if (!fromVersion) { + fromVersion = 76; + } + + if (fromVersion >= toVersion) { + return false; + } + + Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion); + + return Zotero.DB.executeTransaction(function (conn) { + // Step through version changes until we reach the current version + // + // Each block performs the changes necessary to move from the + // previous revision to that one. + for (var i=fromVersion + 1; i<=toVersion; i++) { + if (i == 77) { + yield Zotero.DB.queryAsync("CREATE TABLE IF NOT EXISTS syncedSettings (\n setting TEXT NOT NULL,\n libraryID INT NOT NULL,\n value NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (setting, libraryID)\n)"); + yield Zotero.DB.queryAsync("INSERT OR IGNORE INTO syncObjectTypes VALUES (7, 'setting')"); } - } - - if (i==11){ - var attachments = Zotero.DB.query("SELECT itemID, linkMode, path FROM itemAttachments WHERE linkMode IN (0,1)"); - for each(var row in attachments){ - var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); - try { - file.persistentDescriptor = row.path; - var storageDir = Zotero.getStorageDirectory(); - storageDir.QueryInterface(Components.interfaces.nsILocalFile); - var path = file.getRelativeDescriptor(storageDir); - Zotero.DB.query("UPDATE itemAttachments SET path=? WHERE itemID=?", [path, row.itemID]); - } - catch (e){} + + if (i == 78) { + yield Zotero.DB.queryAsync("CREATE INDEX IF NOT EXISTS creatorData_name ON creatorData(lastName, firstName)"); } - } - - if (i==12){ - Zotero.DB.query("CREATE TABLE translatorsTemp (translatorID TEXT PRIMARY KEY, lastUpdated DATETIME, inRepository INT, priority INT, translatorType INT, label TEXT, creator TEXT, target TEXT, detectCode TEXT, code TEXT);"); - if (Zotero.DB.tableExists('translators')) { - Zotero.DB.query("INSERT INTO translatorsTemp SELECT * FROM translators"); - Zotero.DB.query("DROP TABLE translators"); + + if (i == 79) { + yield Zotero.DB.queryAsync("DELETE FROM version WHERE schema='userdata2'"); } - Zotero.DB.query("CREATE TABLE translators (\n translatorID TEXT PRIMARY KEY,\n minVersion TEXT,\n maxVersion TEXT,\n lastUpdated DATETIME,\n inRepository INT,\n priority INT,\n translatorType INT,\n label TEXT,\n creator TEXT,\n target TEXT,\n detectCode TEXT,\n code TEXT\n);"); - Zotero.DB.query("INSERT INTO translators SELECT translatorID, '', '', lastUpdated, inRepository, priority, translatorType, label, creator, target, detectCode, code FROM translatorsTemp"); - Zotero.DB.query("CREATE INDEX translators_type ON translators(translatorType)"); - Zotero.DB.query("DROP TABLE translatorsTemp"); } - if (i==13) { - Zotero.DB.query("CREATE TABLE itemNotesTemp (itemID INT, sourceItemID INT, note TEXT, PRIMARY KEY (itemID), FOREIGN KEY (itemID) REFERENCES items(itemID), FOREIGN KEY (sourceItemID) REFERENCES items(itemID))"); - Zotero.DB.query("INSERT INTO itemNotesTemp SELECT * FROM itemNotes"); - Zotero.DB.query("DROP TABLE itemNotes"); - Zotero.DB.query("CREATE TABLE itemNotes (\n itemID INT,\n sourceItemID INT,\n note TEXT,\n isAbstract INT DEFAULT NULL,\n PRIMARY KEY (itemID),\n FOREIGN KEY (itemID) REFERENCES items(itemID),\n FOREIGN KEY (sourceItemID) REFERENCES items(itemID)\n);"); - Zotero.DB.query("INSERT INTO itemNotes SELECT itemID, sourceItemID, note, NULL FROM itemNotesTemp"); - Zotero.DB.query("CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID)"); - Zotero.DB.query("DROP TABLE itemNotesTemp"); - } - - // 1.0.0b3.r1 - - // Repair for interrupted B4 upgrades - if (i==14) { - var hash = Zotero.DB.getColumnHash('itemNotes'); - if (!hash.isAbstract) { - // See if itemDataValues exists - if (!Zotero.DB.tableExists('itemDataValues')) { - // Copied from step 23 - var notes = Zotero.DB.query("SELECT itemID, note FROM itemNotes WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=1)"); - if (notes) { - var f = function(text) { text = text + ''; var t = text.substring(0, 80); var ln = t.indexOf("\n"); if (ln>-1 && ln<80) { t = t.substring(0, ln); } return t; } - for (var j=0; j<notes.length; j++) { - Zotero.DB.query("REPLACE INTO itemNoteTitles VALUES (?,?)", [notes[j]['itemID'], f(notes[j]['note'])]); - } - } - - Zotero.DB.query("CREATE TABLE itemDataValues (\n valueID INT,\n value,\n PRIMARY KEY (valueID)\n);"); - var values = Zotero.DB.columnQuery("SELECT DISTINCT value FROM itemData"); - if (values) { - for (var j=0; j<values.length; j++) { - var valueID = Zotero.ID.get('itemDataValues'); - Zotero.DB.query("INSERT INTO itemDataValues VALUES (?,?)", [valueID, values[j]]); - } - } - - Zotero.DB.query("CREATE TEMPORARY TABLE itemDataTemp AS SELECT itemID, fieldID, (SELECT valueID FROM itemDataValues WHERE value=ID.value) AS valueID FROM itemData ID"); - Zotero.DB.query("DROP TABLE itemData"); - Zotero.DB.query("CREATE TABLE itemData (\n itemID INT,\n fieldID INT,\n valueID INT,\n PRIMARY KEY (itemID, fieldID),\n FOREIGN KEY (itemID) REFERENCES items(itemID),\n FOREIGN KEY (fieldID) REFERENCES fields(fieldID)\n FOREIGN KEY (valueID) REFERENCES itemDataValues(valueID)\n);"); - Zotero.DB.query("INSERT INTO itemData SELECT * FROM itemDataTemp"); - Zotero.DB.query("DROP TABLE itemDataTemp"); - - i = 23; - continue; - } - - var rows = Zotero.DB.query("SELECT * FROM itemData WHERE valueID NOT IN (SELECT valueID FROM itemDataValues)"); - if (rows) { - for (var j=0; j<rows.length; j++) { - for (var j=0; j<values.length; j++) { - var valueID = Zotero.ID.get('itemDataValues'); - Zotero.DB.query("INSERT INTO itemDataValues VALUES (?,?)", [valueID, values[j]]); - Zotero.DB.query("UPDATE itemData SET valueID=? WHERE itemID=? AND fieldID=?", [valueID, rows[j]['itemID'], rows[j]['fieldID']]); - } - } - i = 23; - continue; - } - - i = 27; - continue; - } - } - - if (i==15) { - Zotero.DB.query("DROP TABLE IF EXISTS annotations"); - } - - if (i==16) { - Zotero.DB.query("CREATE TABLE tagsTemp (tagID INT, tag TEXT, PRIMARY KEY (tagID))"); - if (Zotero.DB.tableExists("tags")) { - Zotero.DB.query("INSERT INTO tagsTemp SELECT * FROM tags"); - Zotero.DB.query("DROP TABLE tags"); - } - Zotero.DB.query("CREATE TABLE tags (\n tagID INT,\n tag TEXT,\n tagType INT,\n PRIMARY KEY (tagID),\n UNIQUE (tag, tagType)\n);"); - Zotero.DB.query("INSERT INTO tags SELECT tagID, tag, 0 FROM tagsTemp"); - Zotero.DB.query("DROP TABLE tagsTemp"); - - // Compensate for csl table drop in step 8 for upgraders from early versions, - // in case we do something with it in a later step - Zotero.DB.query("CREATE TABLE IF NOT EXISTS csl (\n cslID TEXT PRIMARY KEY,\n updated DATETIME,\n title TEXT,\n csl TEXT\n);"); - } - - if (i==17) { - Zotero.DB.query("UPDATE itemData SET fieldID=89 WHERE fieldID=8 AND itemID IN (SELECT itemID FROM items WHERE itemTypeID=7)"); - } - - if (i==19) { - Zotero.DB.query("INSERT INTO itemData SELECT sourceItemID, 90, note FROM itemNotes WHERE isAbstract=1"); - Zotero.DB.query("DELETE FROM items WHERE itemID IN (SELECT itemID FROM itemNotes WHERE isAbstract=1)"); - Zotero.DB.query("DELETE FROM itemData WHERE itemID IN (SELECT itemID FROM itemNotes WHERE isAbstract=1)"); - Zotero.DB.query("CREATE TEMPORARY TABLE itemNotesTemp (itemID INT, sourceItemID INT, note TEXT)"); - Zotero.DB.query("INSERT INTO itemNotesTemp SELECT itemID, sourceItemID, note FROM itemNotes WHERE isAbstract IS NULL"); - Zotero.DB.query("DROP TABLE itemNotes"); - Zotero.DB.query("CREATE TABLE itemNotes (\n itemID INT,\n sourceItemID INT,\n note TEXT, \n PRIMARY KEY (itemID),\n FOREIGN KEY (itemID) REFERENCES items(itemID),\n FOREIGN KEY (sourceItemID) REFERENCES items(itemID)\n);"); - Zotero.DB.query("INSERT INTO itemNotes SELECT * FROM itemNotesTemp") - Zotero.DB.query("DROP TABLE itemNotesTemp"); - } - - if (i==20) { - Zotero.DB.query("UPDATE itemData SET fieldID=91 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=13) AND fieldID=12;"); - Zotero.DB.query("UPDATE itemData SET fieldID=92 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=15) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=93 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=16) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=94 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=16) AND fieldID=4;"); - Zotero.DB.query("UPDATE itemData SET fieldID=95 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=16) AND fieldID=10;"); - Zotero.DB.query("UPDATE itemData SET fieldID=96 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=17) AND fieldID=14;"); - Zotero.DB.query("UPDATE itemData SET fieldID=97 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=17) AND fieldID=4;"); - Zotero.DB.query("UPDATE itemData SET fieldID=98 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=17) AND fieldID=10;"); - Zotero.DB.query("UPDATE itemData SET fieldID=99 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=18) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=100 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=20) AND fieldID=14;"); - Zotero.DB.query("UPDATE itemData SET fieldID=101 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=20) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=102 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=19) AND fieldID=7;"); - Zotero.DB.query("UPDATE itemData SET fieldID=103 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=19) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=104 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=25) AND fieldID=12;"); - Zotero.DB.query("UPDATE itemData SET fieldID=105 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=29) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=105 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=30) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=105 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=31) AND fieldID=60;"); - Zotero.DB.query("UPDATE itemData SET fieldID=107 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=23) AND fieldID=12;"); - Zotero.DB.query("INSERT OR IGNORE INTO itemData SELECT itemID, 52, value FROM itemData WHERE fieldID IN (14, 52) AND itemID IN (SELECT itemID FROM items WHERE itemTypeID=19) LIMIT 1"); - Zotero.DB.query("DELETE FROM itemData WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=19) AND fieldID=14"); - } - - if (i==21) { - Zotero.DB.query("INSERT INTO itemData SELECT itemID, 110, title FROM items WHERE title IS NOT NULL AND itemTypeID NOT IN (1,17,20,21)"); - Zotero.DB.query("INSERT INTO itemData SELECT itemID, 111, title FROM items WHERE title IS NOT NULL AND itemTypeID = 17"); - Zotero.DB.query("INSERT INTO itemData SELECT itemID, 112, title FROM items WHERE title IS NOT NULL AND itemTypeID = 20"); - Zotero.DB.query("INSERT INTO itemData SELECT itemID, 113, title FROM items WHERE title IS NOT NULL AND itemTypeID = 21"); - Zotero.DB.query("CREATE TEMPORARY TABLE itemsTemp AS SELECT itemID, itemTypeID, dateAdded, dateModified FROM items"); - Zotero.DB.query("DROP TABLE items"); - Zotero.DB.query("CREATE TABLE IF NOT EXISTS items (\n itemID INTEGER PRIMARY KEY,\n itemTypeID INT,\n dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP,\n dateModified DATETIME DEFAULT CURRENT_TIMESTAMP\n);"); - Zotero.DB.query("INSERT INTO items SELECT * FROM itemsTemp"); - Zotero.DB.query("DROP TABLE itemsTemp"); - } - - if (i==22) { - if (Zotero.DB.valueQuery("SELECT COUNT(*) FROM items WHERE itemID=0")) { - var itemID = Zotero.ID.get('items', true); - Zotero.DB.query("UPDATE items SET itemID=? WHERE itemID=?", [itemID, 0]); - Zotero.DB.query("UPDATE itemData SET itemID=? WHERE itemID=?", [itemID, 0]); - Zotero.DB.query("UPDATE itemNotes SET itemID=? WHERE itemID=?", [itemID, 0]); - Zotero.DB.query("UPDATE itemAttachments SET itemID=? WHERE itemID=?", [itemID, 0]); - } - if (Zotero.DB.valueQuery("SELECT COUNT(*) FROM collections WHERE collectionID=0")) { - var collectionID = Zotero.ID.get('collections', true); - Zotero.DB.query("UPDATE collections SET collectionID=? WHERE collectionID=0", [collectionID]); - Zotero.DB.query("UPDATE collectionItems SET collectionID=? WHERE collectionID=0", [collectionID]); - } - Zotero.DB.query("DELETE FROM tags WHERE tagID=0"); - Zotero.DB.query("DELETE FROM itemTags WHERE tagID=0"); - Zotero.DB.query("DELETE FROM savedSearches WHERE savedSearchID=0"); - } - - if (i==23) { - Zotero.DB.query("CREATE TABLE IF NOT EXISTS itemNoteTitles (\n itemID INT,\n title TEXT,\n PRIMARY KEY (itemID),\n FOREIGN KEY (itemID) REFERENCES itemNotes(itemID)\n);"); - var notes = Zotero.DB.query("SELECT itemID, note FROM itemNotes WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=1)"); - if (notes) { - var f = function(text) { var t = text.substring(0, 80); var ln = t.indexOf("\n"); if (ln>-1 && ln<80) { t = t.substring(0, ln); } return t; } - for (var j=0; j<notes.length; j++) { - Zotero.DB.query("INSERT INTO itemNoteTitles VALUES (?,?)", [notes[j]['itemID'], f(notes[j]['note'])]); - } - } - - Zotero.DB.query("CREATE TABLE IF NOT EXISTS itemDataValues (\n valueID INT,\n value,\n PRIMARY KEY (valueID)\n);"); - var values = Zotero.DB.columnQuery("SELECT DISTINCT value FROM itemData"); - if (values) { - for (var j=0; j<values.length; j++) { - var valueID = Zotero.ID.get('itemDataValues'); - Zotero.DB.query("INSERT INTO itemDataValues VALUES (?,?)", [valueID, values[j]]); - } - } - - Zotero.DB.query("CREATE TEMPORARY TABLE itemDataTemp AS SELECT itemID, fieldID, (SELECT valueID FROM itemDataValues WHERE value=ID.value) AS valueID FROM itemData ID"); - Zotero.DB.query("DROP TABLE itemData"); - Zotero.DB.query("CREATE TABLE itemData (\n itemID INT,\n fieldID INT,\n valueID INT,\n PRIMARY KEY (itemID, fieldID),\n FOREIGN KEY (itemID) REFERENCES items(itemID),\n FOREIGN KEY (fieldID) REFERENCES fields(fieldID)\n FOREIGN KEY (valueID) REFERENCES itemDataValues(valueID)\n);"); - Zotero.DB.query("INSERT INTO itemData SELECT * FROM itemDataTemp"); - Zotero.DB.query("DROP TABLE itemDataTemp"); - } - - if (i==24) { - var rows = Zotero.DB.query("SELECT * FROM itemData NATURAL JOIN itemDataValues WHERE fieldID IN (52,96,100)"); - if (rows) { - for (var j=0; j<rows.length; j++) { - if (!Zotero.Date.isMultipart(rows[j]['value'])) { - var value = Zotero.Date.strToMultipart(rows[j]['value']); - var valueID = Zotero.DB.valueQuery("SELECT valueID FROM itemDataValues WHERE value=?", rows[j]['value']); - if (!valueID) { - var valueID = Zotero.ID.get('itemDataValues'); - Zotero.DB.query("INSERT INTO itemDataValues VALUES (?,?)", [valueID, value]); - } - Zotero.DB.query("UPDATE itemData SET valueID=? WHERE itemID=? AND fieldID=?", [valueID, rows[j]['itemID'], rows[j]['fieldID']]); - } - } - } - } - - if (i==25) { - Zotero.DB.query("UPDATE itemData SET fieldID=100 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=15) AND fieldID=14;") - } - - if (i==26) { - Zotero.DB.query("INSERT INTO itemData SELECT itemID, 114, valueID FROM itemData WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=33) AND fieldID=84"); - } - - if (i==27) { - Zotero.DB.query("UPDATE itemData SET fieldID=115 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=3) AND fieldID=12"); - } - - // 1.0.0b4.r1 - - if (i==28) { - var childNotes = Zotero.DB.query("SELECT * FROM itemNotes WHERE itemID IN (SELECT itemID FROM items) AND sourceItemID IS NOT NULL"); - if (!childNotes.length) { - continue; - } - Zotero.DB.query("CREATE TEMPORARY TABLE itemNotesTemp AS SELECT * FROM itemNotes WHERE note IN (SELECT itemID FROM items) AND sourceItemID IS NOT NULL"); - Zotero.DB.query("CREATE INDEX tmp_itemNotes_pk ON itemNotesTemp(note, sourceItemID);"); - var num = Zotero.DB.valueQuery("SELECT COUNT(*) FROM itemNotesTemp"); - if (!num) { - continue; - } - for (var j=0; j<childNotes.length; j++) { - var reversed = Zotero.DB.query("SELECT * FROM itemNotesTemp WHERE note=? AND sourceItemID=?", [childNotes[j].itemID, childNotes[j].sourceItemID]); - if (!reversed.length) { - continue; - } - var maxLength = 0; - for (var k=0; k<reversed.length; k++) { - if (reversed[k].itemID.length > maxLength) { - maxLength = reversed[k].itemID.length; - var maxLengthIndex = k; - } - } - if (maxLengthIndex) { - Zotero.DB.query("UPDATE itemNotes SET note=? WHERE itemID=?", [reversed[maxLengthIndex].itemID, childNotes[j].itemID]); - var f = function(text) { text = text + ''; var t = text.substring(0, 80); var ln = t.indexOf("\n"); if (ln>-1 && ln<80) { t = t.substring(0, ln); } return t; } - Zotero.DB.query("UPDATE itemNoteTitles SET title=? WHERE itemID=?", [f(reversed[maxLengthIndex].itemID), childNotes[j].itemID]); - } - Zotero.DB.query("DELETE FROM itemNotes WHERE note=? AND sourceItemID=?", [childNotes[j].itemID, childNotes[j].sourceItemID]); - } - } - - // 1.0.0b4.r2 - - if (i==29) { - Zotero.DB.query("CREATE TABLE IF NOT EXISTS settings (\n setting TEXT,\n key TEXT,\n value,\n PRIMARY KEY (setting, key)\n);"); - } - - if (i==31) { - Zotero.DB.query("UPDATE itemData SET fieldID=14 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=15) AND fieldID=100"); - } - - if (i==32) { - Zotero.DB.query("UPDATE itemData SET fieldID=100 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=20) AND fieldID=14;"); - } - - // 1.0.0b4.r3 - - if (i==33) { - var rows = Zotero.DB.query("SELECT * FROM itemNotes WHERE itemID NOT IN (SELECT itemID FROM items)"); - if (rows) { - var colID = Zotero.ID.get('collections'); - Zotero.DB.query("INSERT INTO collections VALUES (?,?,?)", [colID, "[Recovered Notes]", null]); - - for (var j=0; j<rows.length; j++) { - if (rows[j].sourceItemID) { - var count = Zotero.DB.valueQuery("SELECT COUNT(*) FROM items WHERE itemID=?", rows[j].sourceItemID); - if (count == 0) { - Zotero.DB.query("UPDATE itemNotes SET sourceItemID=NULL WHERE itemID=?", rows[j].sourceItemID); - } - } - var parsedID = parseInt(rows[j].itemID); - if ((parsedID + '').length != rows[j].itemID) { - if (parseInt(rows[j].note) != rows[j].note || - (parseInt(rows[j].note) + '').length != rows[j].note.length) { - Zotero.DB.query("DELETE FROM itemNotes WHERE itemID=?", rows[j].itemID); - continue; - } - var exists = Zotero.DB.valueQuery("SELECT COUNT(*) FROM itemNotes WHERE itemID=?", rows[j].note); - if (exists) { - var noteItemID = Zotero.ID.get('items', true); - } - else { - var noteItemID = rows[j].note; - } - Zotero.DB.query("UPDATE itemNotes SET itemID=?, sourceItemID=NULL, note=? WHERE itemID=? AND sourceItemID=?", [noteItemID, rows[j].itemID, rows[j].itemID, rows[j].sourceItemID]); - var f = function(text) { text = text + ''; var t = text.substring(0, 80); var ln = t.indexOf("\n"); if (ln>-1 && ln<80) { t = t.substring(0, ln); } return t; } - Zotero.DB.query("REPLACE INTO itemNoteTitles VALUES (?,?)", [noteItemID, f(rows[j].itemID)]); - Zotero.DB.query("INSERT OR IGNORE INTO items (itemID, itemTypeID) VALUES (?,?)", [noteItemID, 1]); - var max = Zotero.DB.valueQuery("SELECT COUNT(*) FROM collectionItems WHERE collectionID=?", colID); - Zotero.DB.query("INSERT OR IGNORE INTO collectionItems VALUES (?,?,?)", [colID, noteItemID, max]); - continue; - } - else if (parsedID != rows[j].itemID) { - Zotero.DB.query("DELETE FROM itemNotes WHERE itemID=?", rows[j].itemID); - continue; - } - Zotero.DB.query("INSERT INTO items (itemID, itemTypeID) VALUES (?,?)", [rows[j].itemID, 1]); - var max = Zotero.DB.valueQuery("SELECT COUNT(*) FROM collectionItems WHERE collectionID=?", colID); - Zotero.DB.query("INSERT INTO collectionItems VALUES (?,?,?)", [colID, rows[j].itemID, max]); - } - } - } - - // 1.0.0b4.r5 - - if (i==34) { - if (!Zotero.DB.tableExists('annotations')) { - Zotero.DB.query("CREATE TABLE annotations (\n annotationID INTEGER PRIMARY KEY,\n itemID INT,\n parent TEXT,\n textNode INT,\n offset INT,\n x INT,\n y INT,\n cols INT,\n rows INT,\n text TEXT,\n collapsed BOOL,\n dateModified DATE,\n FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID)\n)"); - Zotero.DB.query("CREATE INDEX annotations_itemID ON annotations(itemID)"); - } - else { - Zotero.DB.query("ALTER TABLE annotations ADD collapsed BOOL"); - Zotero.DB.query("ALTER TABLE annotations ADD dateModified DATETIME"); - } - if (!Zotero.DB.tableExists('highlights')) { - Zotero.DB.query("CREATE TABLE highlights (\n highlightID INTEGER PRIMARY KEY,\n itemID INTEGER,\n startParent TEXT,\n startTextNode INT,\n startOffset INT,\n endParent TEXT,\n endTextNode INT,\n endOffset INT,\n dateModified DATE,\n FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID)\n)"); - Zotero.DB.query("CREATE INDEX highlights_itemID ON highlights(itemID)"); - } - else { - Zotero.DB.query("ALTER TABLE highlights ADD dateModified DATETIME"); - } - Zotero.DB.query("UPDATE annotations SET dateModified = DATETIME('now')"); - Zotero.DB.query("UPDATE highlights SET dateModified = DATETIME('now')"); - } - - if (i==35) { - Zotero.DB.query("ALTER TABLE fulltextItems RENAME TO fulltextItemWords"); - Zotero.DB.query("CREATE TABLE fulltextItems (\n itemID INT,\n version INT,\n PRIMARY KEY (itemID),\n FOREIGN KEY (itemID) REFERENCES items(itemID)\n);"); - } - - if (i==36) { - Zotero.DB.query("ALTER TABLE fulltextItems ADD indexedPages INT"); - Zotero.DB.query("ALTER TABLE fulltextItems ADD totalPages INT"); - Zotero.DB.query("ALTER TABLE fulltextItems ADD indexedChars INT"); - Zotero.DB.query("ALTER TABLE fulltextItems ADD totalChars INT"); - Zotero.DB.query("DELETE FROM version WHERE schema='fulltext'"); - } - - // 1.5 Sync Preview 1 - if (i==37) { - // Some data cleanup from the pre-FK-trigger days - Zotero.DB.query("DELETE FROM annotations WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM collectionItems WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM fulltextItems WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM fulltextItemWords WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM highlights WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemAttachments WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemCreators WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemData WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemNotes WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemNoteTitles WHERE itemID NOT IN (SELECT itemID FROM itemNotes)"); - Zotero.DB.query("DELETE FROM itemSeeAlso WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemSeeAlso WHERE linkedItemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemTags WHERE itemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("DELETE FROM itemTags WHERE tagID NOT IN (SELECT tagID FROM tags)"); - Zotero.DB.query("DELETE FROM savedSearchConditions WHERE savedSearchID NOT IN (select savedSearchID FROM savedSearches)"); - Zotero.DB.query("DELETE FROM items WHERE itemTypeID IS NULL"); - - Zotero.DB.query("DELETE FROM itemData WHERE valueID NOT IN (SELECT valueID FROM itemDataValues)"); - Zotero.DB.query("DELETE FROM fulltextItemWords WHERE wordID NOT IN (SELECT wordID FROM fulltextWords)"); - Zotero.DB.query("DELETE FROM collectionItems WHERE collectionID NOT IN (SELECT collectionID FROM collections)"); - Zotero.DB.query("DELETE FROM itemCreators WHERE creatorID NOT IN (SELECT creatorID FROM creators)"); - Zotero.DB.query("DELETE FROM itemTags WHERE tagID NOT IN (SELECT tagID FROM tags)"); - Zotero.DB.query("DELETE FROM itemData WHERE fieldID NOT IN (SELECT fieldID FROM fields)"); - Zotero.DB.query("DELETE FROM itemData WHERE valueID NOT IN (SELECT valueID FROM itemDataValues)"); - - Zotero.DB.query("DROP TABLE IF EXISTS userFieldMask"); - Zotero.DB.query("DROP TABLE IF EXISTS userItemTypes"); - Zotero.DB.query("DROP TABLE IF EXISTS userItemTypeMask"); - Zotero.DB.query("DROP TABLE IF EXISTS userFields"); - Zotero.DB.query("DROP TABLE IF EXISTS userItemTypeFields"); - - Zotero.wait(); - - // Index corruption can allow duplicate values - var wordIDs = Zotero.DB.columnQuery("SELECT GROUP_CONCAT(wordID) AS wordIDs FROM fulltextWords GROUP BY word HAVING COUNT(*)>1"); - if (wordIDs.length) { - Zotero.DB.query("CREATE TEMPORARY TABLE deleteWordIDs (wordID INTEGER PRIMARY KEY)"); - for (var j=0, len=wordIDs.length; j<len; j++) { - var ids = wordIDs[j].split(','); - for (var k=1; k<ids.length; k++) { - Zotero.DB.query("INSERT INTO deleteWordIDs VALUES (?)", ids[k]); - } - } - Zotero.DB.query("DELETE FROM fulltextWords WHERE wordID IN (SELECT wordID FROM deleteWordIDs)"); - Zotero.DB.query("DROP TABLE deleteWordIDs"); - } - - Zotero.DB.query("DROP INDEX IF EXISTS fulltextWords_word"); - - Zotero.wait(); - - Zotero.DB.query("REINDEX"); - Zotero.DB.transactionVacuum = true; - - Zotero.wait(); - - // Orphaned child attachment - Zotero.DB.query("UPDATE itemAttachments SET sourceItemID=NULL WHERE sourceItemID NOT IN (SELECT itemID FROM items)"); - Zotero.DB.query("UPDATE itemNotes SET sourceItemID=NULL WHERE sourceItemID NOT IN (SELECT itemID FROM items)"); - - // Create sync delete log - Zotero.DB.query("CREATE TABLE syncDeleteLog (\n syncObjectTypeID INT NOT NULL,\n objectID INT NOT NULL,\n key TEXT NOT NULL,\n timestamp INT NOT NULL,\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID)\n);"); - Zotero.DB.query("CREATE INDEX syncDeleteLog_timestamp ON syncDeleteLog(timestamp);"); - - Zotero.wait(); - - // Note titles - Zotero.DB.query("ALTER TABLE itemNotes ADD COLUMN title TEXT"); - var notes = Zotero.DB.query("SELECT itemID, title FROM itemNoteTitles"); - if (notes) { - var statement = Zotero.DB.getStatement("UPDATE itemNotes SET title=? WHERE itemID=?"); - for (var j=0, len=notes.length; j<len; j++) { - statement.bindUTF8StringParameter(0, notes[j].title); - statement.bindInt32Parameter(1, notes[j].itemID); - try { - statement.execute(); - } - catch (e) { - throw (Zotero.DB.getLastErrorString()); - } - } - statement.reset(); - } - Zotero.DB.query("DROP TABLE itemNoteTitles"); - - Zotero.wait(); - - // Creator data - Zotero.DB.query("CREATE TABLE creatorData (\n creatorDataID INTEGER PRIMARY KEY,\n firstName TEXT,\n lastName TEXT,\n shortName TEXT,\n fieldMode INT,\n birthYear INT\n)"); - Zotero.DB.query("INSERT INTO creatorData SELECT DISTINCT NULL, firstName, lastName, NULL, fieldMode, NULL FROM creators WHERE creatorID IN (SELECT creatorID FROM itemCreators)"); - Zotero.DB.query("CREATE TEMPORARY TABLE itemCreatorsTemp AS SELECT * FROM itemCreators NATURAL JOIN creators"); - Zotero.DB.query("DROP TABLE creators"); - Zotero.DB.query("CREATE TABLE creators (\n creatorID INTEGER PRIMARY KEY,\n creatorDataID INT,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL,\n FOREIGN KEY (creatorDataID) REFERENCES creatorData(creatorDataID)\n);"); - - Zotero.wait(); - - var data = Zotero.DB.query("SELECT * FROM creatorData"); - if (data) { - Zotero.DB.query("CREATE INDEX itemCreatorsTemp_names ON itemCreatorsTemp(lastName, firstName)"); - Zotero.DB.query("DELETE FROM itemCreators"); - - // For each distinct data row, create a new creator - var insertStatement = Zotero.DB.getStatement("INSERT INTO creators (creatorID, creatorDataID, key) VALUES (?, ?, ?)"); - for (var j=0, len=data.length; j<len; j++) { - insertStatement.bindInt32Parameter(0, data[j].creatorDataID); - insertStatement.bindInt32Parameter(1, data[j].creatorDataID); - var key = Zotero.ID.getKey(); - insertStatement.bindStringParameter(2, key); - try { - insertStatement.execute(); - } - catch (e) { - throw (Zotero.DB.getLastErrorString()); - } - } - insertStatement.reset(); - - Zotero.DB.query("INSERT INTO itemCreators SELECT itemID, C.creatorID, creatorTypeID, orderIndex FROM itemCreatorsTemp ICT JOIN creatorData CD ON (ICT.firstName=CD.firstName AND ICT.lastName=CD.lastName AND ICT.fieldMode=CD.fieldMode) JOIN creators C ON (CD.creatorDataID=C.creatorDataID)"); - } - Zotero.DB.query("DROP TABLE itemCreatorsTemp"); - Zotero.DB.query("CREATE INDEX creators_creatorDataID ON creators(creatorDataID)"); - - Zotero.wait(); - - // Items - Zotero.DB.query("ALTER TABLE items ADD COLUMN key TEXT"); - - var items = Zotero.DB.query("SELECT itemID, itemTypeID, dateAdded FROM items"); - var titles = Zotero.DB.query("SELECT itemID, value FROM itemData NATURAL JOIN itemDataValues WHERE fieldID BETWEEN 110 AND 112"); - var statement = Zotero.DB.getStatement("UPDATE items SET key=? WHERE itemID=?"); - for (var j=0, len=items.length; j<len; j++) { - var key = Zotero.ID.getKey(); - if (key == 'AJ4PT6IT') { - j--; - continue; - } - else if (items[j].itemID == 123456789) { - key = 'AJ4PT6IT'; - } - statement.bindStringParameter(0, key); - statement.bindInt32Parameter(1, items[j].itemID); - try { - statement.execute(); - } - catch (e) { - throw (Zotero.DB.getLastErrorString()); - } - } - statement.reset(); - Zotero.DB.query("CREATE UNIQUE INDEX items_key ON items(key)"); - - Zotero.wait(); - - var rows = Zotero.DB.columnQuery("SELECT GROUP_CONCAT(valueID) FROM itemDataValues GROUP BY value HAVING COUNT(*) > 1"); - for each(var row in rows) { - var ids = row.split(','); - var id = parseInt(ids[0]); - var deleteIDs = []; - for (var j=1; j<ids.length; j++) { - deleteIDs.push(parseInt(ids[j])); - } - - var done = 0; - var max = 998; // compiled limit - var num = deleteIDs.length; - - do { - var chunk = deleteIDs.splice(0, max); - - Zotero.DB.query("UPDATE itemData SET valueID=? WHERE valueID IN (" + chunk.map(function () '?').join() + ")", [id].concat(chunk)); - Zotero.DB.query("DELETE FROM itemDataValues WHERE valueID IN (" + chunk.map(function () '?').join() + ")", chunk); - - done += chunk.length; - } - while (done < num); - } - Zotero.DB.query("CREATE UNIQUE INDEX itemDataValues_value ON itemDataValues(value)"); - - Zotero.wait(); - - // Collections - var collections = Zotero.DB.query("SELECT * FROM collections"); - Zotero.DB.query("DROP TABLE collections"); - Zotero.DB.query("CREATE TABLE collections (\n collectionID INTEGER PRIMARY KEY,\n collectionName TEXT,\n parentCollectionID INT,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID)\n);"); - var statement = Zotero.DB.getStatement("INSERT INTO collections (collectionID, collectionName, parentCollectionID, key) VALUES (?,?,?,?)"); - for (var j=0, len=collections.length; j<len; j++) { - statement.bindInt32Parameter(0, collections[j].collectionID); - statement.bindUTF8StringParameter(1, collections[j].collectionName); - if (collections[j].parentCollectionID) { - statement.bindInt32Parameter(2, collections[j].parentCollectionID); - } - else { - statement.bindNullParameter(2); - } - var key = Zotero.ID.getKey(); - statement.bindStringParameter(3, key); - - try { - statement.execute(); - } - catch (e) { - throw (Zotero.DB.getLastErrorString()); - } - } - statement.reset(); - - Zotero.wait(); - - // Saved searches - var searches = Zotero.DB.query("SELECT * FROM savedSearches"); - Zotero.DB.query("DROP TABLE savedSearches"); - Zotero.DB.query("CREATE TABLE savedSearches (\n savedSearchID INTEGER PRIMARY KEY,\n savedSearchName TEXT,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE\n);"); - var statement = Zotero.DB.getStatement("INSERT INTO savedSearches (savedSearchID, savedSearchName, key) VALUES (?,?,?)"); - for (var j=0, len=searches.length; j<len; j++) { - statement.bindInt32Parameter(0, searches[j].savedSearchID); - statement.bindUTF8StringParameter(1, searches[j].savedSearchName); - var key = Zotero.ID.getKey(); - statement.bindStringParameter(2, key); - - try { - statement.execute(); - } - catch (e) { - throw (Zotero.DB.getLastErrorString()); - } - } - statement.reset(); - - Zotero.wait(); - - // Tags - var tags = Zotero.DB.query("SELECT tagID, tag AS tag, tagType FROM tags"); - var newTags = []; - var cases = {}; - if (tags) { - // Find tags with multiple case forms - for each(var row in tags) { - var l = '_' + row.tag.toLowerCase(); - if (!cases[l]) { - cases[l] = []; - } - if (cases[l].indexOf(row.tag) == -1) { - cases[l].push(row.tag); - } - } - var done = {}; - for each(var row in tags) { - var l = row.tag.toLowerCase(); - var lk = '_' + l; - - if (done[lk]) { - continue; - } - done[lk] = true; - - // Only one tag -- use - if (cases[lk].length == 1) { - newTags.push(row); - continue; - } - - var counts = Zotero.DB.query("SELECT tag, COUNT(*) AS numItems FROM tags NATURAL JOIN itemTags WHERE tag LIKE ? ESCAPE '`' GROUP BY tag ORDER BY numItems DESC", l.replace('_', '`_').replace('%', '`%')); - // If not associated with any items, use all lowercase - if (!counts) { - var newTag = l; - } - // Use most frequent - else if (counts[0].numItems != counts[1].numItems) { - var newTag = counts[0].tag; - } - // Use earliest - else { - var newTag = Zotero.DB.valueQuery("SELECT tag FROM tags NATURAL JOIN itemTags WHERE tag IN (SELECT tag FROM tags NATURAL JOIN itemTags NATURAL JOIN items WHERE tag LIKE ? ESCAPE '`' ORDER BY dateAdded LIMIT 1) GROUP BY tag", l.replace('_', '`_').replace('%', '`%')); - } - - // Point old to new - var types = Zotero.DB.columnQuery("SELECT DISTINCT tagType FROM tags WHERE tag LIKE ? ESCAPE '`'", l.replace('_', '`_').replace('%', '`%')); - for each(var type in types) { - var newTagID = Zotero.DB.valueQuery("SELECT tagID FROM tags WHERE tag=? AND tagType=?", [newTag, type]); - var oldIDs = Zotero.DB.columnQuery("SELECT tagID FROM tags WHERE tag LIKE ? ESCAPE '`' AND tag != ? AND tagType=?", [l.replace('_', '`_').replace('%', '`%'), newTag, type]); - if (oldIDs) { - if (!newTagID) { - newTagID = oldIDs[0]; - } - Zotero.DB.query("UPDATE OR REPLACE itemTags SET tagID=? WHERE tagID IN (" + oldIDs.map(function () '?').join() + ")", [newTagID].concat(oldIDs)); - } - newTags.push({ tagID: newTagID, tag: newTag, tagType: type }); - } - } - } - - Zotero.wait(); - - Zotero.DB.query("DROP TABLE tags"); - Zotero.DB.query("CREATE TABLE tags (\n tagID INTEGER PRIMARY KEY,\n name TEXT COLLATE NOCASE,\n type INT,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n UNIQUE (name, type)\n)"); - var statement = Zotero.DB.getStatement("INSERT INTO tags (tagID, name, type, key) VALUES (?,?,?,?)"); - for (var j=0, len=newTags.length; j<len; j++) { - statement.bindInt32Parameter(0, newTags[j].tagID); - statement.bindUTF8StringParameter(1, newTags[j].tag); - statement.bindInt32Parameter(2, newTags[j].tagType); - var key = Zotero.ID.getKey(); - statement.bindStringParameter(3, key); - - try { - statement.execute(); - } - catch (e) { - throw (Zotero.DB.getLastErrorString()); - } - } - statement.reset(); - - Zotero.wait(); - - // Migrate attachment folders to secondary keys - Zotero.DB.query("UPDATE itemAttachments SET path=REPLACE(path, itemID || '/', 'storage:') WHERE path REGEXP '^[0-9]+/'"); - - if (Zotero.Prefs.get('useDataDir')) { - var dataDir = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); - dataDir.persistentDescriptor = Zotero.Prefs.get('dataDir'); - } - else { - var dataDir = Zotero.getProfileDirectory(); - dataDir.append('zotero'); - } - if (!dataDir.exists() || !dataDir.isDirectory()){ - var e = { name: "NS_ERROR_FILE_NOT_FOUND" }; - throw (e); - } - var movedFiles37 = {}; - var moveReport = ''; - var orphaned = dataDir.clone(); - var storage37 = dataDir.clone(); - var moveReportFile = dataDir.clone(); - orphaned.append('orphaned-files'); - storage37.append('storage'); - moveReportFile.append('zotero.moved-files.' + fromVersion + '.bak'); - var keys = {}; - var rows = Zotero.DB.query("SELECT itemID, key FROM items"); - for each(var row in rows) { - keys[row.itemID] = row.key; - } - if (storage37.exists()) { - var entries = storage37.directoryEntries; - entries.QueryInterface(Components.interfaces.nsIDirectoryEnumerator); - var file; - var renameQueue = []; - var orphanQueue = []; - while (file = entries.nextFile) { - var id = parseInt(file.leafName); - if (!file.isDirectory() || file.leafName != id) { - continue; - } - if (keys[id]) { - var renameTarget = storage37.clone(); - renameTarget.append(keys[id]); - if (renameTarget.exists()) { - orphanQueue.push({ - id: id, - file: renameTarget - }); - } - renameQueue.push({ - id: id, - file: file, - key: keys[id] - }); - } - else { - orphanQueue.push({ - id: id, - file: file - }); - } - } - entries.close(); - - Zotero.wait(); - - if (orphanQueue.length) { - if (!orphaned.exists()) { - orphaned.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755); - } - for each(var orphan in orphanQueue) { - var target = orphaned.clone(); - target.append(orphan.file.leafName); - var newName = null; - if (target.exists()) { - try { - target.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644); - newName = target.leafName; - } - catch (e) { - // DEBUG: Work around createUnique() brokenness on Windows - // as of Fx3.0.4 (https://bugzilla.mozilla.org/show_bug.cgi?id=452217) - // - // We just delete the conflicting file - if (Zotero.isWin && e.name == 'NS_ERROR_FILE_ACCESS_DENIED') { - target.remove(true); - } - else { - throw (e); - } - } - if (newName) { - target.remove(false); - } - } - try { - orphan.file.moveTo(orphaned, newName); - } - catch (e) { - if (e.name == 'NS_ERROR_FILE_ACCESS_DENIED') { - throw ("Zotero cannot move orphaned file '" + orphan.file.path + "'"); - } - else { - Components.utils.reportError("Error moving orphaned file '" + orphan.file.leafName + "'"); - throw (e); - } - } - movedFiles37[orphan.id] = orphan.file; - } - } - - Zotero.wait(); - - for each(var dir in renameQueue) { - Zotero.debug("Moving " + dir.file.leafName + " to " + dir.key); - dir.file.moveTo(null, dir.key); - moveReport += dir.key + ' ' + dir.id + "\n"; - movedFiles37[dir.id] = dir.file; - } - - if (moveReport) { - moveReport = 'The following directory names in storage were changed:\n' - + '------------------------------------------------------\n' - + moveReport; - Zotero.File.putContents(moveReportFile, moveReport); - } - } - - Zotero.wait(); - - // Migrate big integers - var itemIDs = Zotero.DB.columnQuery("SELECT itemID FROM items WHERE itemID>16777215"); - var smalls = Zotero.DB.columnQuery("SELECT itemID FROM items WHERE itemID<300000"); - var newID = smalls ? Math.max.apply(this, smalls) : 0; - for each(var oldID in itemIDs) { - do { - newID = newID + 1; - var exists = Zotero.DB.valueQuery("SELECT COUNT(*) FROM items WHERE itemID=?", newID); - } - while (exists); - var params = [newID, oldID]; - Zotero.DB.query("UPDATE items SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE annotations SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE collectionItems SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE highlights SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE itemCreators SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE itemAttachments SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE itemAttachments SET sourceItemID=? WHERE sourceItemID=?", params); - Zotero.DB.query("UPDATE itemData SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE itemNotes SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE itemNotes SET sourceItemID=? WHERE sourceItemID=?", params); - Zotero.DB.query("UPDATE itemSeeAlso SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE itemSeeAlso SET linkedItemID=? WHERE linkedItemID=?", params); - Zotero.DB.query("UPDATE itemTags SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE fulltextItemWords SET itemID=? WHERE itemID=?", params); - Zotero.DB.query("UPDATE fulltextItems SET itemID=? WHERE itemID=?", params); - } - } - - // 1.5 Sync Preview 2 - if (i==38) { - var ids = Zotero.DB.columnQuery("SELECT itemID FROM items WHERE itemTypeID=14 AND itemID NOT IN (SELECT itemID FROM itemAttachments)"); - for each(var id in ids) { - Zotero.DB.query("INSERT INTO itemAttachments (itemID, linkMode) VALUES (?, ?)", [id, 3]); - } - } - - if (i==39) { - Zotero.DB.query("CREATE TABLE proxies (\n proxyID INTEGER PRIMARY KEY,\n multiHost INT,\n autoAssociate INT,\n scheme TEXT\n)"); - Zotero.DB.query("CREATE TABLE proxyHosts (\n hostID INTEGER PRIMARY KEY,\n proxyID INTEGER,\n hostname TEXT,\n FOREIGN KEY (proxyID) REFERENCES proxies(proxyID)\n)"); - Zotero.DB.query("CREATE INDEX proxyHosts_proxyID ON proxyHosts(proxyID)"); - } - - if (i==40) { - Zotero.DB.query("ALTER TABLE itemAttachments ADD COLUMN syncState INT DEFAULT 0"); - Zotero.DB.query("ALTER TABLE itemAttachments ADD COLUMN storageModTime INT"); - Zotero.DB.query("CREATE INDEX itemAttachments_syncState ON itemAttachments(syncState)"); - Zotero.DB.query("CREATE TABLE storageDeleteLog (\n key TEXT PRIMARY KEY,\n timestamp INT NOT NULL\n)"); - Zotero.DB.query("CREATE INDEX storageDeleteLog_timestamp ON storageDeleteLog(timestamp)"); - } - - // 1.5 Sync Preview 2.2 - if (i==41) { - var translators = Zotero.DB.query("SELECT * FROM translators WHERE inRepository!=1"); - if (translators) { - var dir = Zotero.getTranslatorsDirectory(); - if (dir.exists()) { - dir.remove(true); - } - Zotero.getTranslatorsDirectory() - for each(var row in translators) { - var file = dir.clone(); - var fileName = Zotero.Translators.getFileNameFromLabel(row.label); - file.append(fileName); - var metadata = { translatorID: row.translatorID, translatorType: parseInt(row.translatorType), label: row.label, creator: row.creator, target: row.target ? row.target : null, minVersion: row.minVersion, maxVersion: row.maxVersion, priority: parseInt(row.priority), inRepository: row.inRepository == 1 ? true : false, lastUpdated: row.lastUpdated }; - var metadataJSON = JSON.stringify(metadata, null, "\t"); - var str = metadataJSON + "\n\n" + (row.detectCode ? row.detectCode + "\n\n" : "") + row.code; - Zotero.debug("Extracting translator '" + row.label + "' from database"); - Zotero.File.putContents(file, str); - Zotero.wait(); - } - } - var styles = Zotero.DB.query("SELECT * FROM csl"); - if (styles) { - var dir = Zotero.getStylesDirectory(); - if (dir.exists()) { - dir.remove(true); - } - Zotero.getStylesDirectory() - for each(var row in styles) { - var file = dir.clone(); - var matches = row.cslID.match(/([^\/]+)$/); - if (!matches) { - continue; - } - try { - Zotero.debug("Extracting styles '" + matches[1] + "' from database"); - file.append(matches[1]); - Zotero.File.putContents(file, row.csl); - Zotero.wait(); - } - catch (e) { - Zotero.debug(e); - Components.utils.reportError("Skipping style '" + matches[1] + "'"); - } - } - Zotero.Styles.init(); - } - Zotero.DB.query("DROP TABLE translators"); - Zotero.DB.query("DROP TABLE csl"); - } - - if (i==42) { - Zotero.DB.query("UPDATE itemAttachments SET syncState=0"); - } - - // 1.5 Sync Preview 2.3 - if (i==43) { - Zotero.DB.query("UPDATE itemNotes SET note='<div class=\"zotero-note znv1\">' || TEXT2HTML(note) || '</div>' WHERE note NOT LIKE '<div class=\"zotero-note %'"); - } - - // 1.5 Sync Preview 3 (i==44) - // 1.5 Sync Preview 3.1 - if (i==45) { - Zotero.DB.query("DELETE FROM itemData WHERE valueID IN (SELECT valueID FROM itemDataValues WHERE value REGEXP '^\\s*$')"); - Zotero.DB.query("DELETE FROM itemDataValues WHERE value REGEXP '^\\s*$'"); - var rows = Zotero.DB.query("SELECT * FROM itemDataValues WHERE value REGEXP '(^\\s+|\\s+$)'"); - if (rows) { - for each(var row in rows) { - var trimmed = Zotero.Utilities.trim(row.value); - var valueID = Zotero.DB.valueQuery("SELECT valueID FROM itemDataValues WHERE value=?", trimmed); - if (valueID) { - Zotero.DB.query("UPDATE OR REPLACE itemData SET valueID=? WHERE valueID=?", [valueID, row.valueID]); - Zotero.DB.query("DELETE FROM itemDataValues WHERE valueID=?", row.valueID); - } - else { - Zotero.DB.query("UPDATE itemDataValues SET value=? WHERE valueID=?", [trimmed, row.valueID]); - } - Zotero.wait(); - } - } - - Zotero.DB.query("UPDATE creatorData SET firstName=TRIM(firstName), lastName=TRIM(lastName)"); - var rows = Zotero.DB.query("SELECT * FROM creatorData ORDER BY lastName, firstName, creatorDataID"); - if (rows) { - for (var j=0; j<rows.length-1; j++) { - var k = j + 1; - while (k < rows.length && - rows[k].lastName == rows[j].lastName && - rows[k].firstName == rows[j].firstName && - rows[k].fieldMode == rows[j].fieldMode) { - Zotero.DB.query("UPDATE creators SET creatorDataID=? WHERE creatorDataID=?", [rows[j].creatorDataID, rows[k].creatorDataID]); - Zotero.DB.query("DELETE FROM creatorData WHERE creatorDataID=?", rows[k].creatorDataID); - k++; - } - } - } - - Zotero.wait(); - - Zotero.DB.query("DELETE FROM itemTags WHERE tagID IN (SELECT tagID FROM tags WHERE name REGEXP '^\\s*$')"); - Zotero.DB.query("DELETE FROM tags WHERE name REGEXP '^\\s*$'"); - var rows = Zotero.DB.query("SELECT * FROM tags WHERE name REGEXP '(^\\s+|\\s+$)'"); - if (rows) { - for each(var row in rows) { - var trimmed = Zotero.Utilities.trim(row.name); - var tagID = Zotero.DB.valueQuery("SELECT tagID FROM tags WHERE name=?", trimmed); - if (tagID) { - Zotero.DB.query("UPDATE OR REPLACE itemTags SET tagID=? WHERE tagID=?", [tagID, row.tagID]); - Zotero.DB.query("DELETE FROM tags WHERE tagID=?", row.tagID); - } - else { - Zotero.DB.query("UPDATE tags SET name=? WHERE tagID=?", [trimmed, row.tagID]); - } - Zotero.wait(); - } - } - - Zotero.DB.query("UPDATE itemNotes SET note=TRIM(note)"); - Zotero.DB.query("UPDATE collections SET collectionName=TRIM(collectionName)"); - Zotero.DB.query("UPDATE savedSearches SET savedSearchName=TRIM(savedSearchName)"); - } - - // 1.5 Sync Preview 3.2 - if (i==46) { - if (fromVersion < 37) { - continue; - } - - if (Zotero.Prefs.get('useDataDir')) { - var dataDir = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); - dataDir.persistentDescriptor = Zotero.Prefs.get('dataDir'); - } - else { - var dataDir = Zotero.getProfileDirectory(); - dataDir.append('zotero'); - } - if (!dataDir.exists() || !dataDir.isDirectory()){ - var e = { name: "NS_ERROR_FILE_NOT_FOUND" }; - throw (e); - } - var movedFiles46 = {}; - var orphaned = dataDir.clone(); - var storage46 = dataDir.clone(); - orphaned.append('orphaned-files'); - storage46.append('storage'); - var keys = {}; - var rows = Zotero.DB.query("SELECT itemID, key FROM items NATURAL JOIN itemAttachments"); - for each(var row in rows) { - keys[row.itemID] = row.key; - } - if (storage46.exists()) { - var entries = storage46.directoryEntries; - entries.QueryInterface(Components.interfaces.nsIDirectoryEnumerator); - var file; - var renameQueue = []; - var orphanQueue = []; - while (file = entries.nextFile) { - var id = parseInt(file.leafName); - if (!file.isDirectory() || file.leafName != id) { - continue; - } - if (keys[id]) { - var renameTarget = storage46.clone(); - renameTarget.append(keys[id]); - if (renameTarget.exists()) { - orphanQueue.push({ - id: id, - file: renameTarget - }); - } - renameQueue.push({ - id: id, - file: file, - key: keys[id] - }); - } - else { - orphanQueue.push({ - id: id, - file: file - }); - } - } - entries.close(); - - Zotero.wait(); - - if (orphanQueue.length) { - if (!orphaned.exists()) { - orphaned.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755); - } - for each(var orphan in orphanQueue) { - var target = orphaned.clone(); - target.append(orphan.file.leafName); - var newName = null; - if (target.exists()) { - try { - target.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644); - newName = target.leafName; - } - catch (e) { - // DEBUG: Work around createUnique() brokenness on Windows - // as of Fx3.0.4 (https://bugzilla.mozilla.org/show_bug.cgi?id=452217) - // - // We just delete the conflicting file - if (Zotero.isWin && e.name == 'NS_ERROR_FILE_ACCESS_DENIED') { - target.remove(true); - } - else { - throw (e); - } - } - if (newName) { - target.remove(false); - } - } - try { - orphan.file.moveTo(orphaned, newName); - } - catch (e) { - if (e.name == 'NS_ERROR_FILE_ACCESS_DENIED') { - throw ("Zotero cannot move orphaned file '" + orphan.file.path + "'"); - } - else { - Components.utils.reportError("Error moving orphaned file '" + orphan.file.leafName + "'"); - throw (e); - } - } - movedFiles46[orphan.id] = orphan.file; - } - } - - Zotero.wait(); - - for each(var dir in renameQueue) { - Zotero.debug("Moving " + dir.file.leafName + " to " + dir.key); - dir.file.moveTo(null, dir.key); - movedFiles46[dir.id] = dir.file; - } - } - } - - // 1.5 Sync Preview 3.6 - if (i==47) { - Zotero.DB.query("ALTER TABLE syncDeleteLog RENAME TO syncDeleteLogOld"); - Zotero.DB.query("DROP INDEX syncDeleteLog_timestamp"); - Zotero.DB.query("CREATE TABLE syncDeleteLog (\n syncObjectTypeID INT NOT NULL,\n key TEXT NOT NULL UNIQUE,\n timestamp INT NOT NULL,\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID)\n);"); - Zotero.DB.query("CREATE INDEX syncDeleteLog_timestamp ON syncDeleteLog(timestamp);"); - Zotero.DB.query("INSERT OR IGNORE INTO syncDeleteLog SELECT syncObjectTypeID, key, timestamp FROM syncDeleteLogOld ORDER BY timestamp DESC"); - Zotero.DB.query("DROP TABLE syncDeleteLogOld"); - } - - // 1.5 Sync Preview 3.7 - if (i==48) { - Zotero.DB.query("CREATE TABLE deletedItems (\n itemID INTEGER PRIMARY KEY,\n dateDeleted DEFAULT CURRENT_TIMESTAMP NOT NULL\n);"); - } - - if (i==49) { - Zotero.DB.query("ALTER TABLE collections RENAME TO collectionsOld"); - Zotero.DB.query("DROP INDEX creators_creatorDataID"); - Zotero.DB.query("ALTER TABLE creators RENAME TO creatorsOld"); - Zotero.DB.query("ALTER TABLE savedSearches RENAME TO savedSearchesOld"); - Zotero.DB.query("ALTER TABLE tags RENAME TO tagsOld"); - - Zotero.wait(); - - Zotero.DB.query("CREATE TABLE collections (\n collectionID INTEGER PRIMARY KEY,\n collectionName TEXT,\n parentCollectionID INT,\n dateAdded DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID)\n);"); - Zotero.DB.query("CREATE TABLE creators (\n creatorID INTEGER PRIMARY KEY,\n creatorDataID INT NOT NULL,\n dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n FOREIGN KEY (creatorDataID) REFERENCES creatorData(creatorDataID)\n);"); - Zotero.DB.query("CREATE TABLE savedSearches (\n savedSearchID INTEGER PRIMARY KEY,\n savedSearchName TEXT,\n dateAdded DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE\n);"); - Zotero.DB.query("CREATE TABLE tags (\n tagID INTEGER PRIMARY KEY,\n name TEXT COLLATE NOCASE,\n type INT NOT NULL,\n dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,\n dateModified DEFAULT CURRENT_TIMESTAMP NOT NULL,\n key TEXT NOT NULL UNIQUE,\n UNIQUE (name, type)\n);"); - - Zotero.wait(); - - Zotero.DB.query("INSERT INTO collections SELECT collectionID, collectionName, parentCollectionID, dateModified, dateModified, key FROM collectionsOld"); - Zotero.DB.query("INSERT INTO creators SELECT creatorID, creatorDataID, dateModified, dateModified, key FROM creatorsOld"); - Zotero.DB.query("INSERT INTO savedSearches SELECT savedSearchID, savedSearchName, dateModified, dateModified, key FROM savedSearchesOld"); - Zotero.DB.query("INSERT INTO tags SELECT tagID, name, type, dateModified, dateModified, key FROM tagsOld"); - - Zotero.wait(); - - Zotero.DB.query("CREATE INDEX creators_creatorDataID ON creators(creatorDataID);"); - - Zotero.DB.query("DROP TABLE collectionsOld"); - Zotero.DB.query("DROP TABLE creatorsOld"); - Zotero.DB.query("DROP TABLE savedSearchesOld"); - Zotero.DB.query("DROP TABLE tagsOld"); - } - - // 1.5 Beta 3 - if (i==50) { - Zotero.DB.query("DELETE FROM proxyHosts"); - Zotero.DB.query("DELETE FROM proxies"); - } - - if (i==51) { - Zotero.DB.query("ALTER TABLE collections RENAME TO collectionsOld"); - Zotero.DB.query("DROP INDEX creators_creatorDataID"); - Zotero.DB.query("ALTER TABLE creators RENAME TO creatorsOld"); - Zotero.DB.query("ALTER TABLE items RENAME TO itemsOld") - Zotero.DB.query("ALTER TABLE savedSearches RENAME TO savedSearchesOld"); - Zotero.DB.query("ALTER TABLE tags RENAME TO tagsOld"); - - Zotero.DB.query("CREATE TABLE collections (\n collectionID INTEGER PRIMARY KEY,\n collectionName TEXT NOT NULL,\n parentCollectionID INT DEFAULT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT,\n key TEXT NOT NULL,\n UNIQUE (libraryID, key),\n FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID)\n);"); - Zotero.DB.query("CREATE TABLE creators (\n creatorID INTEGER PRIMARY KEY,\n creatorDataID INT NOT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT,\n key TEXT NOT NULL,\n UNIQUE (libraryID, key),\n FOREIGN KEY (creatorDataID) REFERENCES creatorData(creatorDataID)\n);"); - Zotero.DB.query("CREATE TABLE items (\n itemID INTEGER PRIMARY KEY,\n itemTypeID INT NOT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT,\n key TEXT NOT NULL,\n UNIQUE (libraryID, key)\n);"); - Zotero.DB.query("CREATE TABLE savedSearches (\n savedSearchID INTEGER PRIMARY KEY,\n savedSearchName TEXT NOT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT,\n key TEXT NOT NULL,\n UNIQUE (libraryID, key)\n);"); - Zotero.DB.query("CREATE TABLE tags (\n tagID INTEGER PRIMARY KEY,\n name TEXT NOT NULL COLLATE NOCASE,\n type INT NOT NULL,\n dateAdded TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n dateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n libraryID INT,\n key TEXT NOT NULL,\n UNIQUE (libraryID, name, type),\n UNIQUE (libraryID, key)\n);\n"); - - Zotero.DB.query("INSERT INTO collections SELECT collectionID, collectionName, parentCollectionID, dateAdded, dateModified, dateModified, NULL, key FROM collectionsOld"); - Zotero.DB.query("INSERT INTO creators SELECT creatorID, creatorDataID, dateAdded, dateModified, dateModified, NULL, key FROM creatorsOld"); - Zotero.DB.query("INSERT INTO items SELECT itemID, itemTypeID, dateAdded, dateModified, dateModified, NULL, key FROM itemsOld"); - Zotero.DB.query("INSERT INTO savedSearches SELECT savedSearchID, savedSearchName, dateAdded, dateModified, dateModified, NULL, key FROM savedSearchesOld"); - Zotero.DB.query("INSERT INTO tags SELECT tagID, name, type, dateAdded, dateModified, dateModified, NULL, key FROM tagsOld"); - - Zotero.wait(); - - Zotero.DB.query("CREATE INDEX creators_creatorDataID ON creators(creatorDataID);"); - - Zotero.DB.query("DROP TABLE collectionsOld"); - Zotero.DB.query("DROP TABLE creatorsOld"); - Zotero.DB.query("DROP TABLE itemsOld"); - Zotero.DB.query("DROP TABLE savedSearchesOld"); - Zotero.DB.query("DROP TABLE tagsOld"); - - Zotero.DB.query("CREATE TABLE libraries (\n libraryID INTEGER PRIMARY KEY,\n libraryType TEXT NOT NULL\n);"); - Zotero.DB.query("CREATE TABLE users (\n userID INTEGER PRIMARY KEY,\n username TEXT NOT NULL\n);"); - Zotero.DB.query("CREATE TABLE groups (\n groupID INTEGER PRIMARY KEY,\n libraryID INT NOT NULL UNIQUE,\n name TEXT NOT NULL,\n description TEXT NOT NULL,\n editable INT NOT NULL,\n filesEditable INT NOT NULL,\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID)\n);"); - Zotero.DB.query("CREATE TABLE groupItems (\n itemID INTEGER PRIMARY KEY,\n createdByUserID INT NOT NULL,\n lastModifiedByUserID INT NOT NULL,\n FOREIGN KEY (createdByUserID) REFERENCES users(userID),\n FOREIGN KEY (lastModifiedByUserID) REFERENCES users(userID)\n);"); - - Zotero.DB.query("ALTER TABLE syncDeleteLog RENAME TO syncDeleteLogOld"); - Zotero.DB.query("DROP INDEX syncDeleteLog_timestamp"); - Zotero.DB.query("CREATE TABLE syncDeleteLog (\n syncObjectTypeID INT NOT NULL,\n libraryID INT,\n key TEXT NOT NULL,\n timestamp INT NOT NULL,\n UNIQUE (libraryID, key),\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID)\n);"); - Zotero.DB.query("INSERT INTO syncDeleteLog SELECT syncObjectTypeID, NULL, key, timestamp FROM syncDeleteLogOld"); - Zotero.DB.query("CREATE INDEX syncDeleteLog_timestamp ON syncDeleteLog(timestamp)"); - Zotero.DB.query("DROP TABLE syncDeleteLogOld"); - - Zotero.wait(); - - Zotero.DB.query("ALTER TABLE storageDeleteLog RENAME TO storageDeleteLogOld"); - Zotero.DB.query("DROP INDEX storageDeleteLog_timestamp"); - Zotero.DB.query("CREATE TABLE storageDeleteLog (\n libraryID INT,\n key TEXT NOT NULL,\n timestamp INT NOT NULL,\n PRIMARY KEY (libraryID, key)\n);"); - Zotero.DB.query("INSERT INTO storageDeleteLog SELECT NULL, key, timestamp FROM storageDeleteLogOld"); - Zotero.DB.query("CREATE INDEX storageDeleteLog_timestamp ON storageDeleteLog(timestamp)"); - Zotero.DB.query("DROP TABLE storageDeleteLogOld"); - - Zotero.DB.query("CREATE TEMPORARY TABLE tmpUpdatedItems (itemID INTEGER PRIMARY KEY)"); - Zotero.DB.query("INSERT INTO tmpUpdatedItems SELECT itemID FROM items NATURAL JOIN itemData WHERE fieldID=10 AND itemTypeID IN (2,9)"); - Zotero.DB.query("UPDATE itemData SET fieldID=118 WHERE fieldID=10 AND itemID IN (SELECT itemID FROM tmpUpdatedItems)"); - Zotero.DB.query("DROP TABLE tmpUpdatedItems"); - } - - if (i==52) { - Zotero.DB.query("CREATE TABLE relations (\n libraryID INT NOT NULL,\n subject TEXT NOT NULL,\n predicate TEXT NOT NULL,\n object TEXT NOT NULL,\n clientDateModified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (libraryID, subject, predicate, object)\n)"); - Zotero.DB.query("CREATE INDEX relations_object ON relations(libraryID, object)") - } - - if (i==53) { - Zotero.DB.query("DELETE FROM collectionItems WHERE itemID IN (SELECT itemID FROM items WHERE itemID IN (SELECT itemID FROM itemAttachments WHERE sourceItemID IS NOT NULL) OR itemID IN (SELECT itemID FROM itemNotes WHERE sourceItemID IS NOT NULL))"); - } - - if (i==54) { - Zotero.DB.query("UPDATE creatorData SET shortName='' WHERE shortName IS NULL"); - Zotero.DB.query("UPDATE creatorData SET birthYear=NULL WHERE birthYear=''"); - } - - if (i==55) { - Zotero.DB.query("CREATE TEMPORARY TABLE tmpEmptyCreators AS SELECT creatorID FROM creators WHERE creatorDataID IN (SELECT creatorDataID FROM creatorData WHERE firstName='' AND lastName='')"); - Zotero.DB.query("INSERT INTO syncDeleteLog SELECT 2, libraryID, key, CURRENT_TIMESTAMP FROM creators WHERE creatorID IN (SELECT creatorID FROM tmpEmptyCreators)"); - var rows = Zotero.DB.query("SELECT * FROM itemCreators WHERE creatorID IN (SELECT creatorID FROM tmpEmptyCreators) ORDER BY orderIndex DESC"); - for each(var row in rows) { - Zotero.DB.query("DELETE FROM itemCreators WHERE itemID=? AND creatorID=? AND orderIndex=?", [row.itemID, row.creatorID, row.orderIndex]); - Zotero.DB.query("UPDATE itemCreators SET orderIndex=orderIndex-1 WHERE itemID=? AND orderIndex>?", [row.itemID, row.orderIndex]); - } - Zotero.DB.query("DELETE FROM itemCreators WHERE creatorID IN (SELECT creatorID FROM tmpEmptyCreators)"); - Zotero.DB.query("DELETE FROM creators WHERE creatorDataID IN (SELECT creatorDataID FROM creatorData WHERE firstName='' AND lastName='')"); - Zotero.DB.query("DROP TABLE tmpEmptyCreators"); - Zotero.DB.query("DELETE FROM creatorData WHERE firstName='' AND lastName=''"); - } - - if (i==56) { - Zotero.DB.query("UPDATE itemAttachments SET mimeType=charsetID, charsetID=NULL WHERE charsetID REGEXP '[a-zA-Z0-9\-]+/[a-zA-Z0-9\-]'"); - } - - if (i==57) { - Zotero.DB.query("UPDATE itemAttachments SET linkMode=0, mimeType=NULL WHERE linkMode IS NULL AND mimeType=0"); - } - - if (i==58) { - if (!Zotero.DB.valueQuery("SELECT COUNT(*) FROM version WHERE schema='syncdeletelog'") && Zotero.DB.valueQuery("SELECT COUNT(*) FROM syncDeleteLog")) { - Zotero.DB.query("INSERT INTO version VALUES ('syncdeletelog', CURRENT_TIMESTAMP)"); - } - } - - if (i==59) { - var namestr = '[Missing Name]'; - var id = Zotero.DB.valueQuery("SELECT creatorDataID FROM creatorData WHERE firstName='' AND lastName=? AND fieldMode=1", namestr); - if (!id) { - id = Zotero.DB.query("INSERT INTO creatorData (firstName, lastName, fieldMode) VALUES ('', ?, 1)", namestr); - } - var creatorID = Zotero.DB.valueQuery("SELECT creatorID FROM creators WHERE creatorDataID=?", id); - if (!creatorID) { - var key = Zotero.ID.getKey(); - creatorID = Zotero.DB.query("INSERT INTO creators (creatorDataID, key) VALUES (?, ?)", [id, key]); - } - Zotero.DB.query("UPDATE itemCreators SET creatorID=? WHERE creatorID NOT IN (SELECT creatorID FROM creators)", creatorID); - } - - if (i==60) { - Zotero.DB.query("DROP TRIGGER IF EXISTS fki_itemAttachments_libraryID"); - Zotero.DB.query("DROP TRIGGER IF EXISTS fku_itemAttachments_libraryID"); - Zotero.DB.query("DROP TRIGGER IF EXISTS fki_itemNotes_libraryID"); - Zotero.DB.query("DROP TRIGGER IF EXISTS fku_itemNotes_libraryID"); - Zotero.DB.query("DELETE FROM collectionItems WHERE itemID IN (SELECT itemID FROM items NATURAL JOIN itemAttachments WHERE sourceItemID IS NOT NULL UNION SELECT itemID FROM items NATURAL JOIN itemNotes WHERE sourceItemID IS NOT NULL)"); - Zotero.DB.query("UPDATE itemAttachments SET sourceItemID=NULL WHERE sourceItemID=itemID"); - Zotero.DB.query("UPDATE itemNotes SET sourceItemID=NULL WHERE sourceItemID=itemID"); - } - - if (i==61) { - Zotero.DB.query("UPDATE itemAttachments SET storageModTime=NULL WHERE storageModTime<0"); - } - - if (i==62) { - Zotero.DB.query("CREATE INDEX IF NOT EXISTS itemData_fieldID ON itemData(fieldID)"); - } - - if (i==63) { - Zotero.DB.query("ALTER TABLE itemAttachments ADD COLUMN storageHash TEXT"); - - var url = Zotero.Prefs.get('sync.storage.url'); - if (url) { - var protocol = Zotero.Prefs.get('sync.storage.protocol'); - if (protocol == 'webdav') { - Zotero.Prefs.set('sync.storage.scheme', 'http'); - } - else { - Zotero.Prefs.set('sync.storage.protocol', 'webdav'); - Zotero.Prefs.set('sync.storage.scheme', 'https'); - } - } - else { - Zotero.Prefs.set('sync.storage.protocol', 'zotero'); - } - - Zotero.DB.query("UPDATE version SET schema='storage_webdav' WHERE schema='storage'"); - } - - if (i==64) { - Zotero.DB.query("ALTER TABLE syncDeleteLog RENAME TO syncDeleteLogOld"); - Zotero.DB.query("CREATE TABLE syncDeleteLog (\n syncObjectTypeID INT NOT NULL,\n libraryID INT NOT NULL,\n key TEXT NOT NULL,\n timestamp INT NOT NULL,\n UNIQUE (syncObjectTypeID, libraryID, key),\n FOREIGN KEY (syncObjectTypeID) REFERENCES syncObjectTypes(syncObjectTypeID)\n)"); - Zotero.DB.query("INSERT INTO syncDeleteLog SELECT syncObjectTypeID, IFNULL(libraryID, 0) AS libraryID, key, timestamp FROM syncDeleteLogOld"); - Zotero.DB.query("DROP TABLE syncDeleteLogOld"); - } - - if (i==65) { - Zotero.DB.query("UPDATE itemAttachments SET sourceItemID=NULL WHERE sourceItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (1,14))"); - Zotero.DB.query("UPDATE itemNotes SET sourceItemID=NULL WHERE sourceItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (1,14))"); - } - - if (i==66) { - Zotero.DB.query("DELETE FROM itemTags WHERE tagID IN (SELECT tagID FROM tags WHERE TRIM(name)='')"); - Zotero.DB.query("DELETE FROM tags WHERE TRIM(name)=''"); - } - - if (i==67) { - var rows = Zotero.DB.query("SELECT * FROM savedSearchConditions WHERE condition='collectionID'"); - for each(var row in rows) { - var key = Zotero.DB.valueQuery("SELECT key FROM collections WHERE collectionID=?", row.value); - var newVal = key ? '0_' + key : null; - Zotero.DB.query("UPDATE savedSearchConditions SET condition='collection', value=? WHERE savedSearchID=? AND searchConditionID=?", [newVal, row.savedSearchID, row.searchConditionID]); - } - var rows = Zotero.DB.query("SELECT * FROM savedSearchConditions WHERE condition='savedSearchID'"); - for each(var row in rows) { - var key = Zotero.DB.valueQuery("SELECT key FROM savedSearches WHERE savedSearchID=?", row.value); - var newVal = key ? '0_' + key : null; - Zotero.DB.query("UPDATE savedSearchConditions SET condition='savedSearch', value=? WHERE savedSearchID=? AND searchConditionID=?", [newVal, row.savedSearchID, row.searchConditionID]); - } - } - - if (i==68) { - Zotero.DB.query("DROP TRIGGER IF EXISTS fkd_itemData_fieldID_fields_fieldID"); - Zotero.DB.query("DROP TRIGGER IF EXISTS fku_fields_fieldID_itemData_fieldID"); - Zotero.DB.query("UPDATE savedSearchConditions SET condition='itemType', value=(SELECT typeName FROM itemTypes WHERE itemTypeID=value) WHERE condition='itemTypeID'"); - - Zotero.DB.query("CREATE TABLE customItemTypes (\n customItemTypeID INTEGER PRIMARY KEY,\n typeName TEXT,\n label TEXT,\n display INT DEFAULT 1,\n icon TEXT\n)"); - Zotero.DB.query("CREATE TABLE customFields (\n customFieldID INTEGER PRIMARY KEY,\n fieldName TEXT,\n label TEXT\n)"); - Zotero.DB.query("CREATE TABLE customItemTypeFields (\n customItemTypeID INT NOT NULL,\n fieldID INT,\n customFieldID INT,\n hide INT NOT NULL,\n orderIndex INT NOT NULL,\n PRIMARY KEY (customItemTypeID, orderIndex),\n FOREIGN KEY (customItemTypeID) REFERENCES customItemTypes(customItemTypeID),\n FOREIGN KEY (fieldID) REFERENCES fields(fieldID),\n FOREIGN KEY (customFieldID) REFERENCES customFields(customFieldID)\n)"); - Zotero.DB.query("CREATE INDEX customItemTypeFields_fieldID ON customItemTypeFields(fieldID)"); - Zotero.DB.query("CREATE INDEX customItemTypeFields_customFieldID ON customItemTypeFields(customFieldID)"); - Zotero.DB.query("CREATE TABLE customBaseFieldMappings (\n customItemTypeID INT,\n baseFieldID INT,\n customFieldID INT,\n PRIMARY KEY (customItemTypeID, baseFieldID, customFieldID),\n FOREIGN KEY (customItemTypeID) REFERENCES customItemTypes(customItemTypeID),\n FOREIGN KEY (baseFieldID) REFERENCES fields(fieldID),\n FOREIGN KEY (customFieldID) REFERENCES fields(customFieldID)\n);\nCREATE INDEX customBaseFieldMappings_baseFieldID ON customBaseFieldMappings(baseFieldID)"); - Zotero.DB.query("CREATE INDEX customBaseFieldMappings_customFieldID ON customBaseFieldMappings(customFieldID)"); - } - - if (i==69) { - Zotero.DB.query("DROP TRIGGER IF EXISTS fku_customFields_customFieldID_customFields_customFieldID"); - } - - if (i==70) { - Zotero.DB.query("UPDATE itemData SET fieldID=118 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=7) AND fieldID=10"); - Zotero.DB.query("UPDATE itemData SET fieldID=119 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=29) AND fieldID=28"); - Zotero.DB.query("UPDATE itemData SET fieldID=119 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=30) AND fieldID=28"); - Zotero.DB.query("UPDATE itemData SET fieldID=121 WHERE itemID IN (SELECT itemID FROM items WHERE itemTypeID=19) AND fieldID=14"); - } - - // 2.0rc3 - if (i==71) { - Zotero.DB.query("UPDATE itemAttachments SET storageModTime=storageModTime*1000 WHERE storageModTime<10000000000"); - } - - if (i==72) { - Zotero.DB.query("UPDATE itemData SET fieldID=123 WHERE fieldID=62 AND itemID IN (SELECT itemID FROM items WHERE itemTypeID IN (SELECT itemTypeID FROM itemTypeFields WHERE fieldID=19) AND itemTypeID NOT IN (2,3,4,5,6,7))"); - } - - if (i==73) { - Zotero.DB.query("UPDATE savedSearchConditions SET condition='libraryCatalog' WHERE condition='repository'"); - } - - // 2.1b1 - if (i==74) { - Zotero.DB.query("CREATE INDEX deletedItems_dateDeleted ON deletedItems(dateDeleted)"); - } - - // 2.1b2 - if (i==75) { - Zotero.DB.query("DROP TABLE IF EXISTS translatorCache"); - Zotero.DB.query("CREATE TABLE translatorCache (\n leafName TEXT PRIMARY KEY,\n translatorJSON TEXT,\n code TEXT,\n lastModifiedTime INT\n)"); - } - - if (i==76) { - Zotero.DB.query("DELETE FROM itemTags WHERE tagID IS NULL"); - } - - Zotero.wait(); - } - - // TODO - // - // Replace customBaseFieldMappings to fix FK fields/customField -> customFields->customFieldID - // If libraryID set, make sure no relations still use a local user key, and then remove on-error code in sync.js - - _updateDBVersion('userdata', toVersion); - - Zotero.DB.commitTransaction(); - } - catch (e) { - if (movedFiles37) { - for (var id in movedFiles37) { - try { - movedFiles37[id].moveTo(storage37, id); - } - catch (e2) { Zotero.debug(e2); } - } - } - if (movedFiles46) { - for (var id in movedFiles46) { - try { - movedFiles46[id].moveTo(storage46, id); - } - catch (e2) { Zotero.debug(e2); } - } - } - Zotero.DB.rollbackTransaction(); - - // Display more helpful message on errors due to open files - // - // Conditional should be same as in showUpgradeWizard() - if (e.name && e.name == "NS_ERROR_FAILURE" && e.message.match(/nsIFile\.moveTo/)) { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - var title = Zotero.getString('upgrade.failed.title'); - ps.alert(null, title, Zotero.getString('upgrade.couldNotMigrate', Zotero.appName) + "\n\n" + Zotero.getString('upgrade.couldNotMigrate.restart')); - } - - throw(e); - } - - return true; - } - - - // TEMP - // - // TODO: Make this asynchronous, and make it block other SQLite - function _migrateUserDataSchemaSilent(fromVersion) { - var toVersion = _getSchemaSQLVersion('userdata2'); - - if (!fromVersion) { - fromVersion = 76; - } - - if (fromVersion >= toVersion) { - return false; - } - - Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion); - - Zotero.DB.beginTransaction(); - - try { - // Step through version changes until we reach the current version - // - // Each block performs the changes necessary to move from the - // previous revision to that one. - for (var i=fromVersion + 1; i<=toVersion; i++) { - if (i == 77) { - Zotero.DB.query("CREATE TABLE IF NOT EXISTS syncedSettings (\n setting TEXT NOT NULL,\n libraryID INT NOT NULL,\n value NOT NULL,\n version INT NOT NULL DEFAULT 0,\n synced INT NOT NULL DEFAULT 0,\n PRIMARY KEY (setting, libraryID)\n)"); - Zotero.DB.query("INSERT OR IGNORE INTO syncObjectTypes VALUES (7, 'setting')"); - } - - if (i == 78) { - Zotero.DB.query("CREATE INDEX IF NOT EXISTS creatorData_name ON creatorData(lastName, firstName)"); - } - } - - _updateDBVersion('userdata2', toVersion); - - Zotero.DB.commitTransaction(); - } - catch (e) { - Zotero.DB.rollbackTransaction(); - throw(e); - } - - return true; + yield _updateDBVersion('userdata3', toVersion); + }) + .then(function () true); + }) } } diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js @@ -1563,7 +1563,7 @@ Zotero.Sync.Server = new function () { Zotero.UnresponsiveScriptIndicator.enable(); if (Zotero.locked) { - Zotero.hideZoteroPaneOverlay(); + Zotero.hideZoteroPaneOverlays(); } Zotero.suppressUIUpdates = false; _updatesInProgress = false; @@ -1585,7 +1585,7 @@ Zotero.Sync.Server = new function () { Zotero.UnresponsiveScriptIndicator.enable(); if (Zotero.locked) { - Zotero.hideZoteroPaneOverlay(); + Zotero.hideZoteroPaneOverlays(); } Zotero.suppressUIUpdates = false; _updatesInProgress = false; diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js @@ -156,6 +156,19 @@ Components.utils.import("resource://gre/modules/Services.jsm"); * with an overlay */ this.__defineGetter__('locked', function () _locked); + this.__defineSetter__('locked', function (lock) { + var wasLocked = _locked; + _locked = lock; + + if (!wasLocked && lock) { + this.unlockDeferred = Q.defer(); + this.unlockPromise = this.unlockDeferred.promise; + } + else if (wasLocked && !lock) { + Zotero.debug("Running unlock callbacks"); + this.unlockDeferred.resolve(); + } + }); /** * @property {Boolean} suppressUIUpdates Don't update UI on Notifier triggers @@ -167,14 +180,19 @@ Components.utils.import("resource://gre/modules/Services.jsm"); */ this.closing = false; + + this.initializationDeferred; + this.initializationPromise; + this.unlockDeferred; + this.unlockPromise; + var _startupErrorHandler; var _zoteroDirectory = false; var _localizedStringBundle; var _localUserKey; var _waiting = 0; - var _locked; - var _unlockCallbacks = []; + var _locked = false; var _shutdownListeners = []; var _progressMeters; var _progressPopup; @@ -208,12 +226,18 @@ Components.utils.import("resource://gre/modules/Services.jsm"); /** * Initialize the extension + * + * @return {Boolean|Promise:Boolean} */ function init() { if (this.initialized || this.skipLoading) { return false; } + this.initializationDeferred = Q.defer(); + this.initializationPromise = this.initializationDeferred.promise; + this.locked = true; + // Load in the preferences branch for the extension Zotero.Prefs.init(); Zotero.Debug.init(); @@ -456,9 +480,13 @@ Components.utils.import("resource://gre/modules/Services.jsm"); } } else { Zotero.debug("Loading in full mode"); - if(!_initFull()) return false; - if(Zotero.isStandalone) Zotero.Standalone.init(); - Zotero.initComplete(); + return Q.fcall(_initFull) + .then(function (success) { + if(!success) return false; + + if(Zotero.isStandalone) Zotero.Standalone.init(); + Zotero.initComplete(); + }); } return true; @@ -469,7 +497,10 @@ Components.utils.import("resource://gre/modules/Services.jsm"); */ this.initComplete = function() { if(Zotero.initialized) return; + + Zotero.debug("Running initialization callbacks"); this.initialized = true; + this.initializationDeferred.resolve(); if(Zotero.isConnector) { Zotero.Repo.init(); @@ -487,12 +518,14 @@ Components.utils.import("resource://gre/modules/Services.jsm"); /** * Initialization function to be called only if Zotero is in full mode + * + * @return {Promise:Boolean} */ function _initFull() { - var dataDir = Zotero.getZoteroDirectory(); Zotero.VersionHeader.init(); // Check for DB restore + var dataDir = Zotero.getZoteroDirectory(); var restoreFile = dataDir.clone(); restoreFile.append('restore-from-server'); if (restoreFile.exists()) { @@ -530,123 +563,109 @@ Components.utils.import("resource://gre/modules/Services.jsm"); Zotero.Fulltext.init(); - // Require >=2.1b3 database to ensure proper locking - if (Zotero.isStandalone && Zotero.Schema.getDBVersion('system') > 0 && Zotero.Schema.getDBVersion('system') < 31) { - var dir = Zotero.getProfileDirectory(); - dir.append('zotero'); - - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .createInstance(Components.interfaces.nsIPromptService); - 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.standaloneMigration.selectCustom'), - Zotero.getString('general.quit'), - null, - {} - ); - - var quit = false; - - // Default location - if (index == 0) { - Zotero.File.createDirectoryIfMissing(dir); + return Q.fcall(function () { + // Require >=2.1b3 database to ensure proper locking + if (!Zotero.isStandalone) { + return; + } + return Zotero.Schema.getDBVersion('system') + .then(function (dbSystemVersion) { + if (dbSystemVersion > 0 && dbSystemVersion < 31) { + var dir = Zotero.getProfileDirectory(); + dir.append('zotero'); + + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .createInstance(Components.interfaces.nsIPromptService); + 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.standaloneMigration.selectCustom'), + Zotero.getString('general.quit'), + null, + {} + ); + + var quit = false; + + // Default location + if (index == 0) { + Zotero.File.createDirectoryIfMissing(dir); + + Zotero.Prefs.set("useDataDir", false) + + Services.startup.quit( + Components.interfaces.nsIAppStartup.eAttemptQuit + | Components.interfaces.nsIAppStartup.eRestart + ); + } + // Select new data directory + else if (index == 1) { + var dir = Zotero.chooseZoteroDirectory(true); + if (!dir) { + quit = true; + } + } + else { + quit = true; + } + + if (quit) { + Services.startup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit); + } + + throw true; + } + }); + }) + .then(function () { + return Zotero.Schema.updateSchema() + .then(function (updated) { + Zotero.locked = false; - Zotero.Prefs.set("useDataDir", false) + // Initialize various services + Zotero.Integration.init(); - Services.startup.quit( - Components.interfaces.nsIAppStartup.eAttemptQuit - | Components.interfaces.nsIAppStartup.eRestart - ); - } - // Select new data directory - else if (index == 1) { - var dir = Zotero.chooseZoteroDirectory(true); - if (!dir) { - quit = true; + if(Zotero.Prefs.get("httpServer.enabled")) { + Zotero.Server.init(); } - } - else { - quit = true; - } - - if (quit) { - Services.startup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit); - } - + + Zotero.Notifier.registerObserver(Zotero.Tags, 'setting'); + + Zotero.Sync.init(); + Zotero.Sync.Runner.init(); + + Zotero.MIMETypeHandler.init(); + Zotero.Proxies.init(); + + // Initialize keyboard shortcuts + Zotero.Keys.init(); + + // Initialize Locate Manager + Zotero.LocateManager.init(); + + Zotero.Items.startEmptyTrashTimer(); + }) + .catch(function (e) { + Zotero.debug(e, 1); + Components.utils.reportError(e); // DEBUG: doesn't always work + Zotero.startupError = Zotero.getString('startupError.databaseUpgradeError') + "\n\n" + e; + throw true; + }); + }) + .then(function () { + return true; + }) + .catch(function (e) { Zotero.skipLoading = true; return false; - } - - // Trigger updating of schema and scrapers - if (Zotero.Schema.userDataUpgradeRequired()) { - var upgraded = Zotero.Schema.showUpgradeWizard(); - if (!upgraded) { - Zotero.skipLoading = true; - return false; - } - } - // If no userdata upgrade, still might need to process system - else { - try { - var updated = Zotero.Schema.updateSchema(); - } - catch (e) { - if (typeof e == 'string' && e.match('newer than SQL file')) { - var kbURL = "http://zotero.org/support/kb/newer_db_version"; - var msg = Zotero.localeJoin([ - Zotero.getString('startupError.zoteroVersionIsOlder'), - Zotero.getString('startupError.zoteroVersionIsOlder.upgrade') - ]) + "\n\n" - + Zotero.getString('startupError.zoteroVersionIsOlder.current', Zotero.version) + "\n\n" - + Zotero.getString('general.seeForMoreInformation', kbURL); - Zotero.startupError = msg; - } - else { - Zotero.startupError = Zotero.getString('startupError.databaseUpgradeError') + "\n\n" + e; - } - Zotero.skipLoading = true; - Components.utils.reportError(e); - return false; - } - } - - // Populate combined tables for custom types and fields -- this is likely temporary - if (!upgraded && !updated) { - Zotero.Schema.updateCustomTables(); - } - - // Initialize various services - Zotero.Integration.init(); - - if(Zotero.Prefs.get("httpServer.enabled")) { - Zotero.Server.init(); - } - - Zotero.Notifier.registerObserver(Zotero.Tags, 'setting'); - - Zotero.Sync.init(); - Zotero.Sync.Runner.init(); - - Zotero.MIMETypeHandler.init(); - Zotero.Proxies.init(); - - // Initialize keyboard shortcuts - Zotero.Keys.init(); - - // Initialize Locate Manager - Zotero.LocateManager.init(); - - Zotero.Items.startEmptyTrashTimer(); - - return true; + }); } /** @@ -1602,6 +1621,7 @@ Components.utils.import("resource://gre/modules/Services.jsm"); * @return void */ this.showZoteroPaneProgressMeter = function (msg, determinate, icon) { + if (!msg) msg = ""; var currentWindow = Services.wm.getMostRecentWindow("navigator:browser"); var enumerator = Services.wm.getEnumerator("navigator:browser"); var progressMeters = []; @@ -1626,7 +1646,14 @@ Components.utils.import("resource://gre/modules/Services.jsm"); continue; } - win.ZoteroPane.document.getElementById('zotero-pane-progress-label').value = msg; + var label = win.ZoteroPane.document.getElementById('zotero-pane-progress-label'); + if (msg) { + label.hidden = false; + label.value = msg; + } + else { + label.hidden = true; + } var progressMeter = win.ZoteroPane.document.getElementById('zotero-pane-progressmeter') if (determinate) { progressMeter.mode = 'determined'; @@ -1642,7 +1669,7 @@ Components.utils.import("resource://gre/modules/Services.jsm"); progressMeters.push(progressMeter); } - _locked = true; + this.locked = true; _progressMeters = progressMeters; } @@ -1679,14 +1706,8 @@ Components.utils.import("resource://gre/modules/Services.jsm"); /** * Hide Zotero pane overlay in all windows */ - this.hideZoteroPaneOverlay = function () { - // Run any queued callbacks - if (_unlockCallbacks.length) { - var func; - while (func = _unlockCallbacks.shift()) { - func(); - } - } + this.hideZoteroPaneOverlays = function () { + this.locked = false; var enumerator = Services.wm.getEnumerator("navigator:browser"); while (enumerator.hasMoreElements()) { @@ -1700,7 +1721,6 @@ Components.utils.import("resource://gre/modules/Services.jsm"); _progressPopup.close(); } - _locked = false; _progressMeters = []; _progressPopup = null; _lastPercentage = null; @@ -1708,19 +1728,6 @@ Components.utils.import("resource://gre/modules/Services.jsm"); /** - * Adds a callback to be called when the Zotero pane overlay closes - * - * @param {Boolean} TRUE if added, FALSE if not locked - */ - this.addUnlockCallback = function (callback) { - if (!_locked) { - return false; - } - _unlockCallbacks.push(callback); - return true; - } - - /** * Adds a listener to be called when Zotero shuts down (even if Firefox is not shut down) */ this.addShutdownListener = function(listener) { diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js @@ -97,6 +97,8 @@ var ZoteroPane = new function() * Called when the window containing Zotero pane is open */ function init() { + Zotero.debug("Initializing Zotero pane"); + // Set "Report Errors..." label via property rather than DTD entity, // since we need to reference it in script elsewhere document.getElementById('zotero-tb-actions-reportErrors').setAttribute('label', @@ -104,9 +106,6 @@ var ZoteroPane = new function() // Set key down handler document.getElementById('appcontent').addEventListener('keydown', ZoteroPane_Local.handleKeyDown, true); - if (Zotero.locked) { - return; - } _loaded = true; var zp = document.getElementById('zotero-pane'); @@ -139,8 +138,6 @@ var ZoteroPane = new function() * mode */ function _loadPane() { - if(!Zotero || !Zotero.initialized) return; - if(Zotero.isConnector) { ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('connector.standaloneOpen')); return; @@ -356,98 +353,100 @@ var ZoteroPane = new function() */ function makeVisible() { - // If pane not loaded, load it or display an error message - if (!ZoteroPane_Local.loaded) { - if (Zotero.locked) { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - var msg = Zotero.getString('general.operationInProgress') + '\n\n' + Zotero.getString('general.operationInProgress.waitUntilFinished'); - ps.alert(null, "", msg); + if (Zotero.locked) { + Zotero.showZoteroPaneProgressMeter(); + } + Zotero.unlockPromise + .then(function () { + Zotero.hideZoteroPaneOverlays(); + + // If pane not loaded, load it or display an error message + if (!ZoteroPane_Local.loaded) { + ZoteroPane_Local.init(); + } + + // If Zotero could not be initialized, display an error message and return + if (!Zotero || Zotero.skipLoading) { + this.displayStartupError(); return false; } - ZoteroPane_Local.init(); - } - - // If Zotero could not be initialized, display an error message and return - if(!Zotero || !Zotero.initialized) { - this.displayStartupError(); - return false; - } - - if(!_madeVisible) { - this.buildItemTypeSubMenu(); - } - _madeVisible = true; - - this.unserializePersist(); - this.updateToolbarPosition(); - this.updateTagSelectorSize(); - - // restore saved row selection (for tab switching) - var containerWindow = (window.ZoteroTab ? window.ZoteroTab.containerWindow : window); - if(containerWindow.zoteroSavedCollectionSelection) { - this.collectionsView.rememberSelection(containerWindow.zoteroSavedCollectionSelection); - delete containerWindow.zoteroSavedCollectionSelection; - } - - // restore saved item selection (for tab switching) - if(containerWindow.zoteroSavedItemSelection) { - var me = this; - // hack to restore saved selection after itemTreeView finishes loading - window.setTimeout(function() { - if(containerWindow.zoteroSavedItemSelection) { - me.itemsView.rememberSelection(containerWindow.zoteroSavedItemSelection); - delete containerWindow.zoteroSavedItemSelection; - } - }, 51); - } - - // Focus the quicksearch on pane open - var searchBar = document.getElementById('zotero-tb-search'); - setTimeout(function () { - searchBar.inputField.select(); - }, 1); - - var d = new Date(); - Zotero.purgeDataObjects(); - var d2 = new Date(); - Zotero.debug("Purged data tables in " + (d2 - d) + " ms"); - - // Auto-sync on pane open - if (Zotero.Prefs.get('sync.autoSync')) { - Zotero.proxyAuthComplete - .delay(1000) - .then(function () { - if (!Zotero.Sync.Server.enabled) { - Zotero.debug('Sync not enabled -- skipping auto-sync', 4); - return; - } - - if (Zotero.Sync.Server.syncInProgress || Zotero.Sync.Storage.syncInProgress) { - Zotero.debug('Sync already running -- skipping auto-sync', 4); - return; - } - - if (Zotero.Sync.Server.manualSyncRequired) { - Zotero.debug('Manual sync required -- skipping auto-sync', 4); - return; - } - - Zotero.Sync.Runner.sync({ - background: true - }); - }) - .done(); - } - - // Set sync icon to spinning or not - // - // We don't bother setting an error state at open - if (Zotero.Sync.Server.syncInProgress || Zotero.Sync.Storage.syncInProgress) { - Zotero.Sync.Runner.setSyncIcon('animate'); - } - - return true; + + if(!_madeVisible) { + this.buildItemTypeSubMenu(); + } + _madeVisible = true; + + this.unserializePersist(); + this.updateToolbarPosition(); + this.updateTagSelectorSize(); + + // restore saved row selection (for tab switching) + var containerWindow = (window.ZoteroTab ? window.ZoteroTab.containerWindow : window); + if(containerWindow.zoteroSavedCollectionSelection) { + this.collectionsView.rememberSelection(containerWindow.zoteroSavedCollectionSelection); + delete containerWindow.zoteroSavedCollectionSelection; + } + + // restore saved item selection (for tab switching) + if(containerWindow.zoteroSavedItemSelection) { + var me = this; + // hack to restore saved selection after itemTreeView finishes loading + window.setTimeout(function() { + if(containerWindow.zoteroSavedItemSelection) { + me.itemsView.rememberSelection(containerWindow.zoteroSavedItemSelection); + delete containerWindow.zoteroSavedItemSelection; + } + }, 51); + } + + // Focus the quicksearch on pane open + var searchBar = document.getElementById('zotero-tb-search'); + setTimeout(function () { + searchBar.inputField.select(); + }, 1); + + var d = new Date(); + Zotero.purgeDataObjects(); + var d2 = new Date(); + Zotero.debug("Purged data tables in " + (d2 - d) + " ms"); + + // Auto-sync on pane open + if (Zotero.Prefs.get('sync.autoSync')) { + Zotero.proxyAuthComplete + .delay(1000) + .then(function () { + if (!Zotero.Sync.Server.enabled) { + Zotero.debug('Sync not enabled -- skipping auto-sync', 4); + return; + } + + if (Zotero.Sync.Server.syncInProgress || Zotero.Sync.Storage.syncInProgress) { + Zotero.debug('Sync already running -- skipping auto-sync', 4); + return; + } + + if (Zotero.Sync.Server.manualSyncRequired) { + Zotero.debug('Manual sync required -- skipping auto-sync', 4); + return; + } + + Zotero.Sync.Runner.sync({ + background: true + }); + }) + .done(); + } + + // Set sync icon to spinning or not + // + // We don't bother setting an error state at open + if (Zotero.Sync.Server.syncInProgress || Zotero.Sync.Storage.syncInProgress) { + Zotero.Sync.Runner.setSyncIcon('animate'); + } + + return true; + }.bind(this)) + .done(); } /** @@ -3965,40 +3964,38 @@ var ZoteroPane = new function() } this.displayStartupError = function(asPaneMessage) { - if(!Zotero || !Zotero.initialized) { - if (Zotero) { - var errMsg = Zotero.startupError; - var errFunc = Zotero.startupErrorHandler; - } - - if (!errMsg) { - // Get the stringbundle manually - var src = 'chrome://zotero/locale/zotero.properties'; - var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1']. - getService(Components.interfaces.nsILocaleService); - var appLocale = localeService.getApplicationLocale(); - var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] - .getService(Components.interfaces.nsIStringBundleService); - var stringBundle = stringBundleService.createBundle(src, appLocale); - - var errMsg = stringBundle.GetStringFromName('startupError'); - } + if (Zotero) { + var errMsg = Zotero.startupError; + var errFunc = Zotero.startupErrorHandler; + } + + if (!errMsg) { + // Get the stringbundle manually + var src = 'chrome://zotero/locale/zotero.properties'; + var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1']. + getService(Components.interfaces.nsILocaleService); + var appLocale = localeService.getApplicationLocale(); + var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] + .getService(Components.interfaces.nsIStringBundleService); + var stringBundle = stringBundleService.createBundle(src, appLocale); - if (errFunc) { - errFunc(); - } - else { - // TODO: Add a better error page/window here with reporting - // instructions - // window.loadURI('chrome://zotero/content/error.xul'); - //if(asPaneMessage) { - // ZoteroPane_Local.setItemsPaneMessage(errMsg, true); - //} else { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - ps.alert(null, "", errMsg); - //} - } + var errMsg = stringBundle.GetStringFromName('startupError'); + } + + if (errFunc) { + errFunc(); + } + else { + // TODO: Add a better error page/window here with reporting + // instructions + // window.loadURI('chrome://zotero/content/error.xul'); + //if(asPaneMessage) { + // ZoteroPane_Local.setItemsPaneMessage(errMsg, true); + //} else { + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + ps.alert(null, "", errMsg); + //} } } diff --git a/components/zotero-service.js b/components/zotero-service.js @@ -167,7 +167,7 @@ ZoteroContext.prototype = { zContext.Zotero.shutdown().then(function() { // create a new zContext makeZoteroContext(isConnector); - zContext.Zotero.init(); + return zContext.Zotero.init(); }).done(); } @@ -289,20 +289,30 @@ function ZoteroService() { if(isFirstLoadThisSession) { makeZoteroContext(false); - try { - zContext.Zotero.init(); - } catch(e) { + Q.fcall(function () { + return zContext.Zotero.init(); + }) + .catch(function (e) { + dump(e + "\n\n"); + Components.utils.reportError(e); + // if Zotero should start as a connector, reload it - zContext.Zotero.shutdown().then(function() { + return zContext.Zotero.shutdown() + .then(function() { makeZoteroContext(true); - zContext.Zotero.init(); - }).done(); - } + return zContext.Zotero.init(); + }) + }) + .then(function () { + zContext.Zotero.debug("Initialized in "+(Date.now() - start)+" ms"); + }) + .done(); } - isFirstLoadThisSession = false; // no longer first load + else { + zContext.Zotero.debug("Already initialized"); + } + isFirstLoadThisSession = false; this.wrappedJSObject = zContext.Zotero; - - zContext.Zotero.debug("Initialized in "+(Date.now() - start)+" ms"); } catch(e) { var msg = typeof e == 'string' ? e : e.name; dump(e + "\n\n"); diff --git a/resource/schema/userdata.sql b/resource/schema/userdata.sql @@ -1,4 +1,4 @@ --- 78 +-- 79 -- Copyright (c) 2009 Center for History and New Media -- George Mason University, Fairfax, Virginia, USA