diff --git a/chrome/content/zotero/errorReport.xul b/chrome/content/zotero/errorReport.xul new file mode 100644 index 000000000..668d82841 --- /dev/null +++ b/chrome/content/zotero/errorReport.xul @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.errorReport.submissionInProgress; + + + + &zotero.errorReport.submitted; + + &zotero.errorReport.includeReportID; + + diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js index 1086eaaba..b05282c70 100644 --- a/chrome/content/zotero/overlay.js +++ b/chrome/content/zotero/overlay.js @@ -81,6 +81,7 @@ var ZoteroPane = new function() this.showSelectedAttachmentInFilesystem = showSelectedAttachmentInFilesystem; this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog; this.relinkAttachment = relinkAttachment; + this.reportErrors = reportErrors; var self = this; @@ -582,12 +583,16 @@ var ZoteroPane = new function() itemgroup.setSearch(''); itemgroup.setTags(getTagSelection()); - Zotero.UnresponsiveScriptIndicator.disable(); - this.itemsView = new Zotero.ItemTreeView(itemgroup); - this.itemsView.addCallback(_setTagScope); - document.getElementById('zotero-items-tree').view = this.itemsView; - this.itemsView.selection.clearSelection(); - Zotero.UnresponsiveScriptIndicator.enable(); + try { + Zotero.UnresponsiveScriptIndicator.disable(); + this.itemsView = new Zotero.ItemTreeView(itemgroup); + this.itemsView.addCallback(_setTagScope); + document.getElementById('zotero-items-tree').view = this.itemsView; + this.itemsView.selection.clearSelection(); + } + finally { + Zotero.UnresponsiveScriptIndicator.enable(); + } } else { @@ -1707,6 +1712,21 @@ var ZoteroPane = new function() item.relinkAttachmentFile(file); } } + + + function reportErrors() { + var errors = Zotero.getErrors(true); + var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Components.interfaces.nsIWindowWatcher); + var data = { + msg: Zotero.getString('errorReport.followingErrors'), + e: errors.join('\n\n'), + askForSteps: true + }; + var io = { wrappedJSObject: { Zotero: Zotero, data: data } }; + var win = ww.openWindow(null, "chrome://zotero/content/errorReport.xul", + "zotero-error-report", "chrome,centerscreen,modal", io); + } } window.addEventListener("load", function(e) { ZoteroPane.onLoad(e); }, false); diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul index 69a2e630b..ee68fa9f0 100644 --- a/chrome/content/zotero/overlay.xul +++ b/chrome/content/zotero/overlay.xul @@ -37,7 +37,9 @@ + + - + @@ -348,6 +351,12 @@ icon.setAttribute('compact', true); break; } + + // Used for loading the changelog after upgrades + if (Zotero.initialURL) { + gBrowser.selectedTab = gBrowser.addTab(Zotero.initialURL); + Zotero.initialURL = null; + } } else { if (Zotero) { diff --git a/chrome/content/zotero/upgrade.xul b/chrome/content/zotero/upgrade.xul new file mode 100644 index 000000000..183b69e94 --- /dev/null +++ b/chrome/content/zotero/upgrade.xul @@ -0,0 +1,93 @@ + + + + + + + + + + + + + &zotero.upgrade.newVersionInstalled; + &zotero.upgrade.upgradeRequired; &zotero.upgrade.autoBackup; + + + + + &zotero.upgrade.upgradeInProgress; + + + + + &zotero.upgrade.upgradeSucceeded; + + &zotero.upgrade.changeLogBeforeLink; + + + diff --git a/chrome/content/zotero/xpcom/db.js b/chrome/content/zotero/xpcom/db.js index 4b85fc2ae..bed08da47 100644 --- a/chrome/content/zotero/xpcom/db.js +++ b/chrome/content/zotero/xpcom/db.js @@ -25,6 +25,8 @@ Zotero.DBConnection = function(dbName) { throw ('DB name not provided in Zotero.DBConnection()'); } + this.skipBackup = false; + // Private members this._dbName = dbName; this._shutdown = false; @@ -32,7 +34,7 @@ Zotero.DBConnection = function(dbName) { this._transactionRollback = null; this._transactionNestingLevel = 0; this._callbacks = { begin: [], commit: [], rollback: [] }; - this._skipBackup = false; + this._dbIsCorrupt = null this._self = this; } @@ -543,7 +545,7 @@ Zotero.DBConnection.prototype.observe = function(subject, topic, data) { Zotero.DBConnection.prototype.checkException = function (e) { - if (e.name == 'NS_ERROR_FILE_CORRUPTED') { + if (e.name && e.name == 'NS_ERROR_FILE_CORRUPTED') { var file = Zotero.getZoteroDatabase(this._dbName, 'is.corrupt'); var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance(Components.interfaces.nsIFileOutputStream); @@ -551,7 +553,7 @@ Zotero.DBConnection.prototype.checkException = function (e) { foStream.write('', 0); foStream.close(); - this._skipBackup = true; + this._dbIsCorrupt = true; var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); @@ -589,7 +591,11 @@ Zotero.DBConnection.prototype.backupDatabase = function (suffix) { var corruptMarker = Zotero.getZoteroDatabase(this._dbName, 'is.corrupt').exists(); - if (this._skipBackup || corruptMarker) { + if (this.skipBackup) { + this._debug("Skipping backup of database '" + this._dbName + "'", 1); + return false; + } + else if (this._dbIsCorrupt || corruptMarker) { this._debug("Database '" + this._dbName + "' is marked as corrupt--skipping backup", 1); return false; } diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js index a2833e666..5c9a9a3d3 100644 --- a/chrome/content/zotero/xpcom/schema.js +++ b/chrome/content/zotero/xpcom/schema.js @@ -21,14 +21,56 @@ */ Zotero.Schema = new function(){ + this.userDataUpgradeRequired = userDataUpgradeRequired; + this.showUpgradeWizard = showUpgradeWizard; + this.updateSchema = updateSchema; + this.updateScrapersRemote = updateScrapersRemote; + this.stopRepositoryTimer = stopRepositoryTimer; + + this.upgradeFinished = false; + this.goToChangeLog = false; + var _dbVersions = []; var _schemaVersions = []; var _repositoryTimer; var _remoteUpdateInProgress = false; - this.updateSchema = updateSchema; - this.updateScrapersRemote = updateScrapersRemote; - this.stopRepositoryTimer = stopRepositoryTimer; + + function userDataUpgradeRequired() { + var dbVersion = _getDBVersion('userdata'); + var schemaVersion = _getSchemaSQLVersion('userdata'); + + return dbVersion && (dbVersion < schemaVersion); + } + + + function showUpgradeWizard() { + var dbVersion = _getDBVersion('userdata'); + var schemaVersion = _getSchemaSQLVersion('userdata'); + + var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Components.interfaces.nsIWindowWatcher); + var obj = { Zotero: Zotero, data: { success: false } }; + 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) { + 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); + } + + return obj.data.success; + } + /* * Checks if the DB schema exists and is up-to-date, updating if necessary @@ -46,75 +88,77 @@ Zotero.Schema = new function(){ var schemaVersion = _getSchemaSQLVersion('userdata'); - Zotero.UnresponsiveScriptIndicator.disable(); - - // If upgrading userdata, make backup of database first - if (dbVersion < schemaVersion){ - Zotero.DB.backupDatabase(dbVersion); - } - - Zotero.DB.beginTransaction(); - try { - // Old schema system - if (!dbVersion){ - // Check for pre-1.0b2 'user' table - var user = _getDBVersion('user'); - if (user) - { - dbVersion = user; - var sql = "UPDATE version SET schema=? WHERE schema=?"; - Zotero.DB.query(sql, ['userdata', 'user']); - } - else - { - dbVersion = 0; - } + Zotero.UnresponsiveScriptIndicator.disable(); + + // If upgrading userdata, make backup of database first + if (dbVersion < schemaVersion){ + Zotero.DB.backupDatabase(dbVersion); } - var up1 = _migrateUserDataSchema(dbVersion); - var up2 = _updateSchema('system'); - var up3 = _updateSchema('scrapers'); + Zotero.DB.beginTransaction(); - // Rebuild fulltext cache if necessary - if (Zotero.Fulltext.cacheIsOutdated()){ - Zotero.Fulltext.rebuildCache(); - } - Zotero.DB.commitTransaction(); - } - catch(e){ - Zotero.debug(e); - Zotero.DB.rollbackTransaction(); - Zotero.UnresponsiveScriptIndicator.enable(); - throw(e); - } - - if (up1) { - // Upgrade seems to have been a success -- delete any previous backups - var maxPrevious = dbVersion - 1; - var file = Zotero.getZoteroDirectory(); - // directoryEntries.hasMoreElements() throws an error (possibly - // because of the temporary SQLite journal file?), so we just look - // for all versions - for (var i=maxPrevious; i>=29; i--) { - var fileName = 'zotero.sqlite.' + i + '.bak'; - file.append(fileName); - if (file.exists()) { - Zotero.debug('Removing previous backup file ' + fileName); - file.remove(null); + try { + // Old schema system + if (!dbVersion){ + // Check for pre-1.0b2 'user' table + var user = _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 = _migrateUserDataSchema(dbVersion); + var up2 = _updateSchema('system'); + var up3 = _updateSchema('scrapers'); + + // Rebuild fulltext cache if necessary + if (Zotero.Fulltext.cacheIsOutdated()){ + Zotero.Fulltext.rebuildCache(); + } + Zotero.DB.commitTransaction(); + } + catch(e){ + Zotero.debug(e); + Zotero.DB.rollbackTransaction(); + throw(e); + } + + if (up1) { + // Upgrade seems to have been a success -- delete any previous backups + var maxPrevious = dbVersion - 1; + var file = Zotero.getZoteroDirectory(); + // directoryEntries.hasMoreElements() throws an error (possibly + // because of the temporary SQLite journal file?), so we just look + // for all versions + for (var i=maxPrevious; i>=29; i--) { + var fileName = 'zotero.sqlite.' + i + '.bak'; + file.append(fileName); + if (file.exists()) { + Zotero.debug('Removing previous backup file ' + fileName); + file.remove(null); + } + file = file.parent; + } + } + + if (up2 || up3) { + // Run a manual scraper update if upgraded and pref set + if (Zotero.Prefs.get('automaticScraperUpdates')){ + this.updateScrapersRemote(2); } - file = file.parent; } } - - if (up2 || up3) { - // Run a manual scraper update if upgraded and pref set - if (Zotero.Prefs.get('automaticScraperUpdates')){ - this.updateScrapersRemote(2); - } + finally { + Zotero.UnresponsiveScriptIndicator.enable(); } - - Zotero.UnresponsiveScriptIndicator.enable(); return; } @@ -399,6 +443,7 @@ Zotero.Schema = new function(){ } catch(e){ Zotero.debug(e, 1); + Components.utils.reportError(e); Zotero.DB.rollbackTransaction(); alert('Error initializing Zotero database'); // TODO: localize throw(e); @@ -433,7 +478,6 @@ Zotero.Schema = new function(){ catch (e){ Zotero.debug(e, 1); Zotero.DB.rollbackTransaction(); - alert('Error updating Zotero database'); // TODO: localize throw(e); } return true; @@ -640,7 +684,7 @@ Zotero.Schema = new function(){ * Migrate user data schema from an older version, preserving data */ function _migrateUserDataSchema(fromVersion){ - toVersion = _getSchemaSQLVersion('userdata'); + var toVersion = _getSchemaSQLVersion('userdata'); if (fromVersion==toVersion){ return false; @@ -761,6 +805,58 @@ Zotero.Schema = new function(){ // 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) { 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-1 && ln<80) { t = t.substring(0, ln); } return t; } - for each(var note in notes) { - Zotero.DB.query("INSERT INTO itemNoteTitles VALUES (?,?)", [note['itemID'], f(note['note'])]); + for (var j=0; j ' + info[key] + ', '; + } + str = str.substr(0, str.length - 2); + return str; + } + + /** * PHP var_dump equivalent for JS * diff --git a/chrome/locale/en-US/zotero/zotero.dtd b/chrome/locale/en-US/zotero/zotero.dtd index 5e5c5143c..832ed1609 100644 --- a/chrome/locale/en-US/zotero/zotero.dtd +++ b/chrome/locale/en-US/zotero/zotero.dtd @@ -1,3 +1,22 @@ + + + + + + + + + + + + + + + + + + + @@ -44,6 +63,7 @@ + diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties index 07ba1a18c..dcbb6f0bc 100644 --- a/chrome/locale/en-US/zotero/zotero.properties +++ b/chrome/locale/en-US/zotero/zotero.properties @@ -7,6 +7,15 @@ general.restartFirefox.plural = Firefox must be restarted for the changes to ta general.restartNow = Restart now general.restartLater = Restart later +upgrade.failed = Upgrading of the Zotero database failed: +upgrade.advanceMessage = Press %S to upgrade now. + +errorReport.followingErrors = The following errors have occurred: +errorReport.advanceMessage = Press %S to send an error report to the Zotero developers. +errorReport.stepsToReproduce = Steps to Reproduce: +errorReport.expectedResult = Expected result: +errorReport.actualResult = Actual result: + dataDir.notFound = The Zotero data directory could not be found. dataDir.previousDir = Previous directory: dataDir.useProfileDir = Use Firefox profile directory diff --git a/chrome/skin/default/zotero/errorReport.css b/chrome/skin/default/zotero/errorReport.css new file mode 100644 index 000000000..1a42fddeb --- /dev/null +++ b/chrome/skin/default/zotero/errorReport.css @@ -0,0 +1,36 @@ +description { + margin-bottom: 1.5em; +} + +/* Intro pane */ +#zotero-error-message { + font-style: italic; +} + +#zotero-advance-message { + margin: 1.5em 0 .5em; +} + +/* Additional Info pane */ +#zotero-email-address-box { + margin-bottom: 1.5em; +} + +#zotero-email-address-box { + -moz-box-align: center; +} + +#zotero-email-address-box label { + margin-left: 0; +} + +#zotero-error-steps-box description { + margin-bottom: .25em; +} + +/* Submitted pane */ + +#zotero-report-id { + color: red; + font-weight: bold; +} diff --git a/chrome/skin/default/zotero/upgrade.css b/chrome/skin/default/zotero/upgrade.css new file mode 100644 index 000000000..8ea22a6cf --- /dev/null +++ b/chrome/skin/default/zotero/upgrade.css @@ -0,0 +1,7 @@ +description { + margin-bottom: 1.5em; +} + +#zotero-change-log-link { + margin: 0; +} diff --git a/userdata.sql b/userdata.sql index 7067d2f6d..ac61e730c 100644 --- a/userdata.sql +++ b/userdata.sql @@ -1,4 +1,4 @@ --- 29 +-- 30 -- This file creates tables containing user-specific data -- any changes -- to existing tables made here must be mirrored in transition steps in