diff --git a/chrome/content/zotero/xpcom/storage.js b/chrome/content/zotero/xpcom/storage.js index 94b32bf04..f05039e3a 100644 --- a/chrome/content/zotero/xpcom/storage.js +++ b/chrome/content/zotero/xpcom/storage.js @@ -289,6 +289,7 @@ Zotero.Sync.Storage = new function () { ])); } else { + result = result.exception; Zotero.debug("File " + result.type + " sync failed " + "for library " + libraryID); finalPromises.push([libraryID, queuePromise]); @@ -378,7 +379,7 @@ Zotero.Sync.Storage = new function () { var request = new Zotero.Sync.Storage.Request( (item.libraryID ? item.libraryID : 0) + '/' + item.key, callbacks ); - if (queue == 'upload') { + if (queue.type == 'upload') { request.setMaxSize(Zotero.Attachments.getTotalFileSize(item)); } queue.addRequest(request, highPriority); diff --git a/chrome/content/zotero/xpcom/storage/queue.js b/chrome/content/zotero/xpcom/storage/queue.js index e2ab45190..2a12b23c4 100644 --- a/chrome/content/zotero/xpcom/storage/queue.js +++ b/chrome/content/zotero/xpcom/storage/queue.js @@ -49,6 +49,8 @@ Zotero.Sync.Storage.Queue = function (type, libraryID) { this._localChanges = false; this._remoteChanges = false; this._conflicts = []; + this._cachedPercentage; + this._cachedPercentageTime; } Zotero.Sync.Storage.Queue.prototype.__defineGetter__('name', function () { @@ -158,11 +160,18 @@ Zotero.Sync.Storage.Queue.prototype.__defineGetter__('percentage', function () { return 100; } + // Cache percentage for a second + if (this._cachedPercentage && (new Date() - this._cachedPercentageTime) < 1000) { + return this._cachedPercentage; + } + var completedRequests = 0; for each(var request in this._requests) { completedRequests += request.percentage / 100; } - return Math.round((completedRequests / this.totalRequests) * 100); + this._cachedPercentage = Math.round((completedRequests / this.totalRequests) * 100); + this._cachedPercentageTime = new Date(); + return this._cachedPercentage; }); @@ -288,7 +297,7 @@ Zotero.Sync.Storage.Queue.prototype.advance = function () { ); } }) - .fail(function (e) { + .catch(function (e) { self.error(e); }); @@ -330,7 +339,7 @@ Zotero.Sync.Storage.Queue.prototype.advance = function () { ); } }) - .fail(function (e) { + .catch(function (e) { self.error(e); }); @@ -362,7 +371,6 @@ Zotero.Sync.Storage.Queue.prototype.error = function (e) { this._error = e; } Zotero.debug(e, 1); - Components.utils.reportError(e.message ? e.message : e); this.stop(); } diff --git a/chrome/content/zotero/xpcom/storage/request.js b/chrome/content/zotero/xpcom/storage/request.js index 961c21535..080bd87e5 100644 --- a/chrome/content/zotero/xpcom/storage/request.js +++ b/chrome/content/zotero/xpcom/storage/request.js @@ -208,7 +208,6 @@ Zotero.Sync.Storage.Request.prototype.start = function () { }; }) .then(function (results) { - Zotero.debug('!!!!'); Zotero.debug(results); if (results.localChanges) { @@ -263,7 +262,7 @@ Zotero.Sync.Storage.Request.prototype.isFinished = function () { * (usually total bytes) */ Zotero.Sync.Storage.Request.prototype.onProgress = function (channel, progress, progressMax) { - Zotero.debug(progress + "/" + progressMax + " for request " + this.name); + //Zotero.debug(progress + "/" + progressMax + " for request " + this.name); if (!this._running) { Zotero.debug("Trying to update finished request " + this.name + " in " diff --git a/chrome/content/zotero/xpcom/storage/webdav.js b/chrome/content/zotero/xpcom/storage/webdav.js index 37901d328..4e56f330a 100644 --- a/chrome/content/zotero/xpcom/storage/webdav.js +++ b/chrome/content/zotero/xpcom/storage/webdav.js @@ -68,20 +68,22 @@ Zotero.Sync.Storage.WebDAV = (function () { return false; } - if (req.responseXML) { - // TODO: other stuff, but this makes us forward-compatible - try { - var mtime = req.responseXML.getElementsByTagName('mtime')[0] - } - catch (e) { - Zotero.debug(e); - var mtime = ""; - } - var seconds = false; + var seconds = false; + var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"] + .createInstance(Components.interfaces.nsIDOMParser); + try { + var xml = parser.parseFromString(req.responseText, "text/xml"); + var mtime = xml.getElementsByTagName('mtime')[0].textContent; } - else { + catch (e) { + Zotero.debug(e); + var mtime = false; + } + + // TEMP + if (!mtime) { mtime = req.responseText; - var seconds = true; + seconds = true; } var invalid = false; @@ -106,13 +108,15 @@ Zotero.Sync.Storage.WebDAV = (function () { + "' for item " + Zotero.Items.getLibraryKeyHash(item); Zotero.debug(msg, 1); Components.utils.reportError(msg); - deleteStorageFiles([item.key + ".prop"]); - throw _defaultError; + return deleteStorageFiles([item.key + ".prop"]) + .then(function (results) { + throw new Error(_defaultError); + }); } return new Date(parseInt(mtime)); }) - .fail(function (e) { + .catch(function (e) { if (e instanceof Zotero.HTTP.UnexpectedStatusException) { Zotero.debug(req.responseText); throw new Error("Unexpected status code " + e.status + " in " + funcName); @@ -138,8 +142,8 @@ Zotero.Sync.Storage.WebDAV = (function () { + '' + hash + '' + ''; - return Zotero.HTTP.promise("PUT", uri, prop, - { debug: true, successCodes: [200, 201, 204] }) + return Zotero.HTTP.promise("PUT", uri, + { body: prop, debug: true, successCodes: [200, 201, 204] }) .then(function (req) { return { mtime: mtime, hash: hash }; }) @@ -459,7 +463,7 @@ Zotero.Sync.Storage.WebDAV = (function () { * 'deleted', 'missing', and 'error', * each containing filenames */ - function deleteStorageFiles(files, callback) { + function deleteStorageFiles(files) { var results = { deleted: [], missing: [], @@ -467,114 +471,90 @@ Zotero.Sync.Storage.WebDAV = (function () { }; if (files.length == 0) { - if (callback) { - callback(results); - } - return; + return Q.resolve(results); } - for (var i=0; i i) { - delete files[propIndex]; - i--; - last = (i == files.length - 1); - } - - // Delete property file - Zotero.HTTP.WebDAV.doDelete(deletePropURI, function (req) { + let deleteURI = _rootURI.clone(); + // This should never happen, but let's be safe + if (!deleteURI.spec.match(/\/$/)) { + throw new Error( + "Root URI does not end in slash in " + + "Zotero.Sync.Storage.WebDAV.deleteStorageFiles()" + ); + } + + results = Q.resolve(results); + files.forEach(function (fileName) { + results = results.then(function (results) { + let deleteURI = _rootURI.clone(); + deleteURI.QueryInterface(Components.interfaces.nsIURL); + deleteURI.fileName = fileName; + deleteURI.QueryInterface(Components.interfaces.nsIURI); + return Zotero.HTTP.promise("DELETE", deleteURI, { successCodes: [200, 204, 404] }) + .then(function (req) { switch (req.status) { case 204: // IIS 5.1 and Sakai return 200 case 200: - results.deleted.push(fileName); + var fileDeleted = true; break; case 404: - if (fileDeleted) { - results.deleted.push(fileName); - } - else { - results.missing.push(fileName); - } + var fileDeleted = false; break; - - default: - var error = true; } - if (last && callback) { - callback(results); + // If an item file URI, get the property URI + var deletePropURI = getPropertyURIFromItemURI(deleteURI); + if (!deletePropURI) { + if (fileDeleted) { + results.deleted.push(fileName); + } + else { + results.missing.push(fileName); + } + return results; } - if (error) { - results.error.push(fileName); - var msg = "An error occurred attempting to delete " - + "'" + fileName - + "' (" + req.status + " " + req.statusText + ")."; - Zotero.Sync.Storage.EventManager.error(msg); + // If property file appears separately in delete queue, + // remove it, since we're taking care of it here + var propIndex = files.indexOf(deletePropURI.fileName); + if (propIndex > i) { + delete files[propIndex]; + i--; + last = (i == files.length - 1); } + + // Delete property file + return Zotero.HTTP.promise("DELETE", deletePropURI, { successCodes: [200, 204, 404] }) + .then(function (req) { + switch (req.status) { + case 204: + // IIS 5.1 and Sakai return 200 + case 200: + results.deleted.push(fileName); + break; + + case 404: + if (fileDeleted) { + results.deleted.push(fileName); + } + else { + results.missing.push(fileName); + } + break; + } + }); + }) + .catch(function (e) { + results.error.push(fileName); + var msg = "An error occurred attempting to delete " + + "'" + fileName + + "' (" + e.status + " " + e.xmlhttp.statusText + ")."; }); }); - } + }); + return results; } @@ -888,9 +868,10 @@ Zotero.Sync.Storage.WebDAV = (function () { Components.utils.reportError(msg); // Delete the orphaned prop file - deleteStorageFiles([item.key + ".prop"]); - - deferred.resolve(false); + deleteStorageFiles([item.key + ".prop"]) + .finally(function (results) { + deferred.resolve(false); + }); return; } else if (status != 200) { @@ -1474,27 +1455,23 @@ Zotero.Sync.Storage.WebDAV = (function () { * * @param {Function} callback Passed number of files deleted */ - obj._purgeDeletedStorageFiles = function (callback) { + obj._purgeDeletedStorageFiles = function () { if (!this._active) { - return false; + return Q(false); } Zotero.debug("Purging deleted storage files"); var files = Zotero.Sync.Storage.getDeletedFiles(); if (!files) { Zotero.debug("No files to delete remotely"); - if (callback) { - callback(); - } - return false; + return Q(false); } - // TODO: promisify - // Add .zip extension var files = files.map(function (file) file + ".zip"); - deleteStorageFiles(files, function (results) { + return deleteStorageFiles(files) + .then(function (results) { // Remove deleted and nonexistent files from storage delete log var toPurge = results.deleted.concat(results.missing); if (toPurge.length > 0) { @@ -1516,11 +1493,7 @@ Zotero.Sync.Storage.WebDAV = (function () { Zotero.DB.commitTransaction(); } - if (callback) { - callback(results.deleted.length); - } - - Zotero.Sync.Storage.EventManager.success(); + return results.deleted.length; }); }; @@ -1647,12 +1620,10 @@ Zotero.Sync.Storage.WebDAV = (function () { } } - deleteStorageFiles(deleteFiles, function (results) { + deleteStorageFiles(deleteFiles) + .then(function (results) { Zotero.Prefs.set("lastWebDAVOrphanPurge", Math.round(new Date().getTime() / 1000)) - if (callback) { - callback(results); - } - Zotero.Sync.Storage.EventManager.success(); + Zotero.debug(results); }); }, { Depth: 1 }); }; diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js index 90a2c9955..7ca6553ba 100644 --- a/chrome/content/zotero/xpcom/sync.js +++ b/chrome/content/zotero/xpcom/sync.js @@ -553,8 +553,11 @@ Zotero.Sync.Runner = new function () { Zotero.debug("File sync is finished"); if (results.errors.length) { + Zotero.debug(results.errors, 1); + for each(var e in results.errors) { + Components.utils.reportError(e); + } Zotero.Sync.Runner.setErrors(results.errors); - return; } @@ -716,9 +719,7 @@ Zotero.Sync.Runner = new function () { * library-specific sync error icons across all windows */ this.setErrors = function (errors) { - Zotero.debug(errors); errors = [this.parseSyncError(e) for each(e in errors)]; - Zotero.debug(errors); _errorsByLibrary = {}; var primaryError = this.getPrimaryError(errors);