commit 185c5a3a4d3111c5f25b31dcb30573b7ce44c2b5
parent b9c2ea507d54268d93b76a2a62cc07da632d21ef
Author: Dan Stillman <dstillman@zotero.org>
Date: Mon, 22 Jul 2013 20:41:37 -0400
Perform sync file modification checks off the main thread in Firefox 23+
OS.File doesn't seem to work reliably before Firefox 23, so older
versions will continue to do mod time checking on the main thread.
Zotero.Sync.Storage.checkForUpdatedFiles() now returns a promise.
Diffstat:
3 files changed, 517 insertions(+), 361 deletions(-)
diff --git a/chrome/content/zotero/xpcom/storage.js b/chrome/content/zotero/xpcom/storage.js
@@ -136,9 +136,9 @@ Zotero.Sync.Storage = new function () {
&& !Zotero.Sync.Storage.WebDAV.verified) {
Zotero.debug("WebDAV file sync is not active");
var promise = Zotero.Sync.Storage.checkServerPromise(Zotero.Sync.Storage.WebDAV)
- .then(function () {
- mode.cacheCredentials();
- });
+ .then(function () {
+ return mode.cacheCredentials();
+ });
}
else {
var promise = mode.cacheCredentials();
@@ -146,6 +146,7 @@ Zotero.Sync.Storage = new function () {
promises.push(Q.allResolved([mode, promise]));
}
}
+
return Q.all(promises)
// Get library last-sync times
.then(function (cacheCredentialsPromises) {
@@ -212,79 +213,82 @@ Zotero.Sync.Storage = new function () {
}
});
- // Queue files to download and upload from each library
- for (let libraryID in librarySyncTimes) {
- var lastSyncTime = librarySyncTimes[libraryID];
- libraryID = parseInt(libraryID);
-
- self.checkForUpdatedFiles(null, libraryID);
-
- var downloadAll = self.downloadOnSync(libraryID);
-
- // Forced downloads happen even in on-demand mode
- var sql = "SELECT COUNT(*) FROM items "
- + "JOIN itemAttachments USING (itemID) "
- + "WHERE libraryID=? AND syncState=?";
- var downloadForced = !!Zotero.DB.valueQuery(
- sql,
- [
- libraryID == 0 ? null : libraryID,
- Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD
- ]
- );
-
- // If we don't have any forced downloads, we can skip
- // downloads if the last sync time hasn't changed
- // or doesn't exist on the server (meaning there are no files)
- if (downloadAll && !downloadForced) {
- if (lastSyncTime) {
- var version = self.getStoredLastSyncTime(
- libraryModes[libraryID], libraryID
- );
- if (version == lastSyncTime) {
- Zotero.debug("Last " + libraryModes[libraryID].name
- + " sync id hasn't changed for library "
- + libraryID + " -- skipping file downloads");
+ // Check for updated files to upload in each library
+ return Q.all([self.checkForUpdatedFiles(null, parseInt(libraryID))
+ for (libraryID in librarySyncTimes)])
+ .then(function () {
+ // Queue files to download and upload from each library
+ for (let libraryID in librarySyncTimes) {
+ libraryID = parseInt(libraryID);
+
+ var downloadAll = self.downloadOnSync(libraryID);
+
+ // Forced downloads happen even in on-demand mode
+ var sql = "SELECT COUNT(*) FROM items "
+ + "JOIN itemAttachments USING (itemID) "
+ + "WHERE libraryID=? AND syncState=?";
+ var downloadForced = !!Zotero.DB.valueQuery(
+ sql,
+ [
+ libraryID == 0 ? null : libraryID,
+ Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD
+ ]
+ );
+
+ // If we don't have any forced downloads, we can skip
+ // downloads if the last sync time hasn't changed
+ // or doesn't exist on the server (meaning there are no files)
+ if (downloadAll && !downloadForced) {
+ let lastSyncTime = librarySyncTimes[libraryID];
+ if (lastSyncTime) {
+ var version = self.getStoredLastSyncTime(
+ libraryModes[libraryID], libraryID
+ );
+ if (version == lastSyncTime) {
+ Zotero.debug("Last " + libraryModes[libraryID].name
+ + " sync id hasn't changed for library "
+ + libraryID + " -- skipping file downloads");
+ downloadAll = false;
+ }
+ }
+ else {
+ Zotero.debug("No last " + libraryModes[libraryID].name
+ + " sync time for library " + libraryID
+ + " -- skipping file downloads");
downloadAll = false;
}
}
+
+ if (downloadAll || downloadForced) {
+ for each(var itemID in _getFilesToDownload(libraryID, !downloadAll)) {
+ var item = Zotero.Items.get(itemID);
+ self.queueItem(item);
+ }
+ }
+
+ // Get files to upload
+ if (Zotero.Libraries.isFilesEditable(libraryID)) {
+ for each(var itemID in _getFilesToUpload(libraryID)) {
+ var item = Zotero.Items.get(itemID);
+ self.queueItem(item);
+ }
+ }
else {
- Zotero.debug("No last " + libraryModes[libraryID].name
- + " sync time for library " + libraryID
- + " -- skipping file downloads");
- downloadAll = false;
+ Zotero.debug("No file editing access -- skipping file uploads for library " + libraryID);
}
}
- if (downloadAll || downloadForced) {
- for each(var itemID in _getFilesToDownload(libraryID, !downloadAll)) {
- var item = Zotero.Items.get(itemID);
- self.queueItem(item);
- }
+ // Start queues for each library
+ for (let libraryID in librarySyncTimes) {
+ libraryID = parseInt(libraryID);
+ libraryQueues.push(Q.allResolved(
+ [libraryID, Zotero.Sync.Storage.QueueManager.start(libraryID)]
+ ));
}
- // Get files to upload
- if (Zotero.Libraries.isFilesEditable(libraryID)) {
- for each(var itemID in _getFilesToUpload(libraryID)) {
- var item = Zotero.Items.get(itemID);
- self.queueItem(item);
- }
- }
- else {
- Zotero.debug("No file editing access -- skipping file uploads for library " + libraryID);
- }
- }
-
- // Start queues for each library
- for (let libraryID in librarySyncTimes) {
- libraryID = parseInt(libraryID);
- libraryQueues.push(Q.allResolved(
- [libraryID, Zotero.Sync.Storage.QueueManager.start(libraryID)]
- ));
- }
-
- // The promise is done when all libraries are done
- return Q.all(libraryQueues);
+ // The promise is done when all libraries are done
+ return Q.all(libraryQueues);
+ });
})
.then(function (promises) {
Zotero.debug('Queue manager is finished');
@@ -650,8 +654,8 @@ Zotero.Sync.Storage = new function () {
* be marked for download
* @param {Boolean} [includePersonalItems=false]
* @param {Boolean} [includeGroupItems=false]
- * @return {Boolean} TRUE if any items changed state,
- * FALSE otherwise
+ * @return {Promise} Promise resolving to TRUE if any items changed state,
+ * FALSE otherwise
*/
this.checkForUpdatedFiles = function (itemModTimes, libraryID) {
var msg = "Checking for locally changed attachment files";
@@ -669,7 +673,7 @@ Zotero.Sync.Storage = new function () {
}
else {
if (!itemModTimes) {
- return false;
+ return Q(false);
}
}
Zotero.debug(msg);
@@ -714,6 +718,8 @@ Zotero.Sync.Storage = new function () {
}
while (done < numIDs);
+ Zotero.DB.commitTransaction();
+
// If no files, or everything is already marked for download,
// we don't need to do anything
if (!rows.length) {
@@ -722,14 +728,13 @@ Zotero.Sync.Storage = new function () {
msg += " in library " + libraryID;
}
Zotero.debug(msg);
- Zotero.DB.commitTransaction();
- return changed;
+ return Q(changed);
}
// Index attachment data by item id
var itemIDs = [];
var attachmentData = {};
- for each(var row in rows) {
+ for each(let row in rows) {
var id = row.itemID;
itemIDs.push(id);
attachmentData[id] = {
@@ -742,115 +747,269 @@ Zotero.Sync.Storage = new function () {
}
rows = null;
- var updatedStates = {};
- var items = Zotero.Items.get(itemIDs);
- for each(var item in items) {
- Zotero.debug("Memory usage: " + memmgr.resident);
-
- var lk = libraryID + "/" + item.key;
- Zotero.debug("Checking attachment file for item " + lk);
- var file = item.getFile(attachmentData[item.id]);
- if (!file) {
- Zotero.debug("Marking attachment " + lk + " as missing");
- updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
- continue;
- }
-
- // If file is already marked for upload, skip check. Even if this
- // is download-marking mode (itemModTimes) and the file was
- // changed remotely, conflicts are checked at upload time, so we
- // don't need to worry about it here.
- if (attachmentData[item.id].state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
- continue;
- }
-
- var fmtime = item.attachmentModificationTime;
-
- //Zotero.debug("Stored mtime is " + attachmentData[item.id].mtime);
- //Zotero.debug("File mtime is " + fmtime);
-
- // Download-marking mode
- if (itemModTimes) {
- Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
+ // OS.File didn't work reliably before Firefox 23, so use the old code
+ if (Zotero.platformMajorVersion < 23) {
+ var updatedStates = {};
+ var items = Zotero.Items.get(itemIDs);
+ for each(var item in items) {
+ Zotero.debug("Memory usage: " + memmgr.resident);
+
+ let row = attachmentData[item.id];
+ let lk = item.libraryID + "/" + item.key;
+ Zotero.debug("Checking attachment file for item " + lk);
+
+ var file = item.getFile(row);
+ if (!file) {
+ Zotero.debug("Marking attachment " + lk + " as missing");
+ updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
+ continue;
+ }
+
+ // If file is already marked for upload, skip check. Even if this
+ // is download-marking mode (itemModTimes) and the file was
+ // changed remotely, conflicts are checked at upload time, so we
+ // don't need to worry about it here.
+ if (row.state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
+ continue;
+ }
+
+ var fmtime = item.attachmentModificationTime;
+
+ //Zotero.debug("Stored mtime is " + row.mtime);
+ //Zotero.debug("File mtime is " + fmtime);
+
+ // Download-marking mode
+ if (itemModTimes) {
+ Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
+
+ // Ignore attachments whose stored mod times haven't changed
+ if (row.storageModTime == itemModTimes[id]) {
+ Zotero.debug("Storage mod time (" + row.storageModTime + ") "
+ + "hasn't changed for item " + lk);
+ continue;
+ }
+
+ Zotero.debug("Marking attachment " + lk + " for download");
+ updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
+ }
+
+ var mtime = row.mtime;
+
+ // If stored time matches file, it hasn't changed locally
+ if (mtime == fmtime) {
+ continue;
+ }
+
+ // Allow floored timestamps for filesystems that don't support
+ // millisecond precision (e.g., HFS+)
+ if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
+ Zotero.debug("File mod times are within one-second precision "
+ + "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
+ + " for item " + lk + " -- ignoring");
+ continue;
+ }
+
+ // Allow timestamp to be exactly one hour off to get around
+ // time zone issues -- there may be a proper way to fix this
+ if (Math.abs(fmtime - mtime) == 3600000
+ // And check with one-second precision as well
+ || Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
+ || Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
+ Zotero.debug("File mod time (" + fmtime + ") is exactly one "
+ + "hour off remote file (" + mtime + ") for item " + lk
+ + "-- assuming time zone issue and skipping upload");
+ continue;
+ }
- // Ignore attachments whose stored mod times haven't changed
- if (row.storageModTime == itemModTimes[id]) {
- Zotero.debug("Storage mod time (" + row.storageModTime + ") "
- + "hasn't changed for item " + lk);
+ // If file hash matches stored hash, only the mod time changed, so skip
+ var f = item.getFile();
+ if (f) {
+ Zotero.debug(f.path);
+ }
+ else {
+ Zotero.debug("File for item " + lk + " missing before getting hash");
+ }
+ var fileHash = item.attachmentHash;
+ if (row.hash && row.hash == fileHash) {
+ Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
+ + "but hash did for " + file.leafName + " for item " + lk
+ + " -- updating file mod time");
+ try {
+ file.lastModifiedTime = row.mtime;
+ }
+ catch (e) {
+ Zotero.File.checkFileAccessError(e, file, 'update');
+ }
continue;
}
- Zotero.debug("Marking attachment " + lk + " for download");
- updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
+ // Mark file for upload
+ Zotero.debug("Marking attachment " + lk + " as changed "
+ + "(" + mtime + " != " + fmtime + ")");
+ updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD;
}
- var mtime = attachmentData[item.id].mtime;
-
- // If stored time matches file, it hasn't changed locally
- if (mtime == fmtime) {
- continue;
+ for (var itemID in updatedStates) {
+ Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
+ changed = true;
}
- // Allow floored timestamps for filesystems that don't support
- // millisecond precision (e.g., HFS+)
- if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
- Zotero.debug("File mod times are within one-second precision "
- + "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
- + " for item " + lk + " -- ignoring");
- continue;
+ if (!changed) {
+ Zotero.debug("No synced files have changed locally");
}
- // Allow timestamp to be exactly one hour off to get around
- // time zone issues -- there may be a proper way to fix this
- if (Math.abs(fmtime - mtime) == 3600000
- // And check with one-second precision as well
- || Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
- || Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
- Zotero.debug("File mod time (" + fmtime + ") is exactly one "
- + "hour off remote file (" + mtime + ") for item " + lk
- + "-- assuming time zone issue and skipping upload");
- continue;
- }
+ return Q(changed);
+ }
+
+ Components.utils.import("resource://gre/modules/osfile.jsm")
+
+ var updatedStates = {};
+ var items = Zotero.Items.get(itemIDs);
+
+ let checkItems = function () {
+ if (!items.length) return;
- // If file hash matches stored hash, only the mod time changed, so skip
- var f = item.getFile();
- if (f) {
- Zotero.debug(f.path);
- }
- else {
- Zotero.debug("File for item " + lk + " missing before getting hash");
- }
- var fileHash = item.attachmentHash;
- if (attachmentData[item.id].hash && attachmentData[item.id].hash == fileHash) {
- Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
- + "but hash did for " + file.leafName + " for item " + lk
- + " -- updating file mod time");
- try {
- file.lastModifiedTime = attachmentData[item.id].mtime;
+ Zotero.debug("Memory usage: " + memmgr.resident);
+
+ let item = items.shift();
+ let row = attachmentData[item.id];
+ let lk = item.libraryKey;
+ Zotero.debug("Checking attachment file for item " + lk);
+
+ let nsIFile = item.getFile(row, true);
+ let file = null;
+ return Q(OS.File.open(nsIFile.path))
+ .then(function (promisedFile) {
+ file = promisedFile;
+ return file.stat()
+ .then(function (info) {
+ Zotero.debug("Memory usage: " + memmgr.resident);
+
+ var fmtime = info.lastModificationDate.getTime();
+ Zotero.debug("File modification time for item " + lk + " is " + fmtime);
+
+ if (fmtime < 1) {
+ Zotero.debug("File mod time " + fmtime + " is less than 1 -- interpreting as 1", 2);
+ fmtime = 1;
+ }
+
+ // If file is already marked for upload, skip check. Even if this
+ // is download-marking mode (itemModTimes) and the file was
+ // changed remotely, conflicts are checked at upload time, so we
+ // don't need to worry about it here.
+ if (row.state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
+ return;
+ }
+
+ //Zotero.debug("Stored mtime is " + row.mtime);
+ //Zotero.debug("File mtime is " + fmtime);
+
+ // Download-marking mode
+ if (itemModTimes) {
+ Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
+
+ // Ignore attachments whose stored mod times haven't changed
+ if (row.storageModTime == itemModTimes[id]) {
+ Zotero.debug("Storage mod time (" + row.storageModTime + ") "
+ + "hasn't changed for item " + lk);
+ return;
+ }
+
+ Zotero.debug("Marking attachment " + lk + " for download");
+ updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
+ }
+
+ var mtime = row.mtime;
+
+ // If stored time matches file, it hasn't changed locally
+ if (mtime == fmtime) {
+ return;
+ }
+
+ // Allow floored timestamps for filesystems that don't support
+ // millisecond precision (e.g., HFS+)
+ if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
+ Zotero.debug("File mod times are within one-second precision "
+ + "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
+ + " for item " + lk + " -- ignoring");
+ return;
+ }
+
+ // Allow timestamp to be exactly one hour off to get around
+ // time zone issues -- there may be a proper way to fix this
+ if (Math.abs(fmtime - mtime) == 3600000
+ // And check with one-second precision as well
+ || Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
+ || Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
+ Zotero.debug("File mod time (" + fmtime + ") is exactly one "
+ + "hour off remote file (" + mtime + ") for item " + lk
+ + "-- assuming time zone issue and skipping upload");
+ return;
+ }
+
+ // If file hash matches stored hash, only the mod time changed, so skip
+ return Zotero.Utilities.Internal.md5Async(file)
+ .then(function (fileHash) {
+ if (row.hash && row.hash == fileHash) {
+ Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
+ + "but hash did for " + file.leafName + " for item " + lk
+ + " -- updating file mod time");
+ try {
+ nsIFile.lastModifiedTime = row.mtime;
+ }
+ catch (e) {
+ Zotero.File.checkFileAccessError(e, nsIFile, 'update');
+ }
+ return;
+ }
+
+ // Mark file for upload
+ Zotero.debug("Marking attachment " + lk + " as changed "
+ + "(" + mtime + " != " + fmtime + ")");
+ updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD;
+ });
+ });
+ })
+ .finally(function () {
+ if (file) {
+ Zotero.debug("Closing file for item " + lk);
+ file.close();
}
- catch (e) {
- Zotero.File.checkFileAccessError(e, file, 'update');
+ })
+ .catch(function (e) {
+ if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
+ Zotero.debug("Marking attachment " + lk + " as missing");
+ updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
+ return;
}
- continue;
+
+ if (e instanceof OS.File.Error && e.becauseClosed) {
+ Zotero.debug("File was closed", 2);
+ }
+ else {
+ Zotero.debug(e);
+ Zotero.debug(e.toString());
+ }
+ throw new Error("Error " + e.operation + " " + nsIFile.path);
+ })
+ .then(function () {
+ return checkItems();
+ });
+ };
+
+ return checkItems()
+ .then(function () {
+ for (let itemID in updatedStates) {
+ Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
+ changed = true;
}
- // Mark file for upload
- Zotero.debug("Marking attachment " + lk + " as changed "
- + "(" + mtime + " != " + fmtime + ")");
- updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD;
- }
-
- for (var itemID in updatedStates) {
- Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
- changed = true;
- }
-
- if (!changed) {
- Zotero.debug("No synced files have changed locally");
- }
-
- Zotero.DB.commitTransaction();
- return changed;
+ if (!changed) {
+ Zotero.debug("No synced files have changed locally");
+ }
+
+ return changed;
+ });
}
diff --git a/chrome/content/zotero/xpcom/storage/zfs.js b/chrome/content/zotero/xpcom/storage/zfs.js
@@ -86,7 +86,7 @@ Zotero.Sync.Storage.ZFS = (function () {
}
else {
var msg = "Unexpected status code " + e.xmlhttp.status
- + " getting storage file info";
+ + " getting storage file info for item " + item.libraryKey;
}
Zotero.debug(msg, 1);
Zotero.debug(e.xmlhttp.responseText);
@@ -1003,7 +1003,7 @@ Zotero.Sync.Storage.ZFS = (function () {
obj._cacheCredentials = function () {
if (_cachedCredentials) {
Zotero.debug("ZFS credentials are already cached");
- return;
+ return Q();
}
var uri = this.rootURI;
diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js
@@ -1561,219 +1561,216 @@ Zotero.Sync.Server = new function () {
_error(e);
}
- try {
- var gen = Zotero.Sync.Server.Data.processUpdatedXML(
- responseNode.getElementsByTagName('updated')[0],
- lastLocalSyncDate,
- syncSession,
- libraryID,
- function (xmlstr) {
- Zotero.UnresponsiveScriptIndicator.enable();
-
- if (Zotero.locked) {
- Zotero.hideZoteroPaneOverlay();
- }
- Zotero.suppressUIUpdates = false;
- _updatesInProgress = false;
-
- if (xmlstr === false) {
- Zotero.debug("Sync cancelled");
- Zotero.DB.rollbackTransaction();
- Zotero.reloadDataObjects();
- Zotero.Sync.EventListener.resetIgnored();
- _syncInProgress = false;
- _callbacks.onStop();
+ Components.utils.import("resource://gre/modules/Task.jsm");
+
+ Task.spawn(Zotero.Sync.Server.Data.processUpdatedXML(
+ responseNode.getElementsByTagName('updated')[0],
+ lastLocalSyncDate,
+ syncSession,
+ libraryID,
+ function (xmlstr) {
+ Zotero.UnresponsiveScriptIndicator.enable();
+
+ if (Zotero.locked) {
+ Zotero.hideZoteroPaneOverlay();
+ }
+ Zotero.suppressUIUpdates = false;
+ _updatesInProgress = false;
+
+ if (xmlstr === false) {
+ Zotero.debug("Sync cancelled");
+ Zotero.DB.rollbackTransaction();
+ Zotero.reloadDataObjects();
+ Zotero.Sync.EventListener.resetIgnored();
+ _syncInProgress = false;
+ _callbacks.onStop();
+ return;
+ }
+
+ if (xmlstr) {
+ Zotero.debug(xmlstr);
+ }
+
+ if (Zotero.Prefs.get('sync.debugBreak')) {
+ Zotero.debug('===============');
+ throw ("break");
+ }
+
+ if (!xmlstr) {
+ Zotero.debug("Nothing to upload to server");
+ Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
+ Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
+ Zotero.Sync.Server.nextLocalSyncDate = false;
+ Zotero.DB.commitTransaction();
+ _syncInProgress = false;
+ _callbacks.onSuccess();
+ return;
+ }
+
+ Zotero.DB.commitTransaction();
+
+ Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadingData'));
+
+ var url = _serverURL + 'upload';
+ var body = _apiVersionComponent
+ + '&' + Zotero.Sync.Server.sessionIDComponent
+ + '&updateKey=' + updateKey
+ + '&data=' + encodeURIComponent(xmlstr);
+
+ //var file = Zotero.getZoteroDirectory();
+ //file.append('lastupload.txt');
+ //Zotero.File.putContents(file, body);
+
+ var uploadCallback = function (xmlhttp) {
+ if (xmlhttp.status == 409) {
+ Zotero.debug("Upload key is no longer valid -- restarting sync");
+ setTimeout(function () {
+ Zotero.Sync.Server.sync(_callbacks, true, true);
+ }, 1);
return;
}
- if (xmlstr) {
- Zotero.debug(xmlstr);
- }
+ _checkResponse(xmlhttp);
+
+ Zotero.debug(xmlhttp.responseText);
+ var response = xmlhttp.responseXML.childNodes[0];
+
+ if (_checkServerLock(response, function (mode) {
+ switch (mode) {
+ // If the upload was queued, keep checking back
+ case 'queued':
+ Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadAccepted'));
+
+ var url = _serverURL + 'uploadstatus';
+ var body = _apiVersionComponent
+ + '&' + Zotero.Sync.Server.sessionIDComponent;
+ Zotero.HTTP.doPost(url, body, function (xmlhttp) {
+ uploadCallback(xmlhttp);
+ });
+ break;
+
+ // If affected libraries were locked, restart sync,
+ // since the upload key would be out of date anyway
+ case 'locked':
+ setTimeout(function () {
+ Zotero.Sync.Server.sync(_callbacks, true, true);
+ }, 1);
+ break;
+
+ default:
+ throw ("Unexpected server lock mode '" + mode + "' in Zotero.Sync.Server.upload()");
+ }
+ })) { return; }
- if (Zotero.Prefs.get('sync.debugBreak')) {
- Zotero.debug('===============');
- throw ("break");
+ if (response.firstChild.tagName == 'error') {
+ // handle error
+ _error(response.firstChild.firstChild.nodeValue);
}
- if (!xmlstr) {
- Zotero.debug("Nothing to upload to server");
- Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
- Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
- Zotero.Sync.Server.nextLocalSyncDate = false;
- Zotero.DB.commitTransaction();
- _syncInProgress = false;
- _callbacks.onSuccess();
- return;
+ if (response.firstChild.localName != 'uploaded') {
+ _error("Unexpected upload response '" + response.firstChild.localName
+ + "' in Zotero.Sync.Server.sync()");
}
- Zotero.DB.commitTransaction();
+ Zotero.DB.beginTransaction();
+ Zotero.Sync.purgeDeletedObjects(nextLocalSyncTime);
+ Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
+ Zotero.Sync.Server.nextLocalSyncDate = false;
+ Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
- Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadingData'));
+ var sql = "UPDATE syncedSettings SET synced=1";
+ Zotero.DB.query(sql);
- var url = _serverURL + 'upload';
- var body = _apiVersionComponent
- + '&' + Zotero.Sync.Server.sessionIDComponent
- + '&updateKey=' + updateKey
- + '&data=' + encodeURIComponent(xmlstr);
+ //throw('break2');
- //var file = Zotero.getZoteroDirectory();
- //file.append('lastupload.txt');
- //Zotero.File.putContents(file, body);
+ Zotero.DB.commitTransaction();
- var uploadCallback = function (xmlhttp) {
- if (xmlhttp.status == 409) {
- Zotero.debug("Upload key is no longer valid -- restarting sync");
- setTimeout(function () {
- Zotero.Sync.Server.sync(_callbacks, true, true);
- }, 1);
- return;
- }
-
- _checkResponse(xmlhttp);
-
- Zotero.debug(xmlhttp.responseText);
- var response = xmlhttp.responseXML.childNodes[0];
-
- if (_checkServerLock(response, function (mode) {
- switch (mode) {
- // If the upload was queued, keep checking back
- case 'queued':
- Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadAccepted'));
-
- var url = _serverURL + 'uploadstatus';
- var body = _apiVersionComponent
- + '&' + Zotero.Sync.Server.sessionIDComponent;
- Zotero.HTTP.doPost(url, body, function (xmlhttp) {
- uploadCallback(xmlhttp);
- });
- break;
-
- // If affected libraries were locked, restart sync,
- // since the upload key would be out of date anyway
- case 'locked':
- setTimeout(function () {
- Zotero.Sync.Server.sync(_callbacks, true, true);
- }, 1);
- break;
-
- default:
- throw ("Unexpected server lock mode '" + mode + "' in Zotero.Sync.Server.upload()");
- }
- })) { return; }
+ // Check if any items were modified during /upload,
+ // and restart the sync if so
+ if (Zotero.Items.getNewer(nextLocalSyncDate, true)) {
+ Zotero.debug("Items were modified during upload -- restarting sync");
+ Zotero.Sync.Server.sync(_callbacks, true, true);
+ return;
+ }
+
+ _syncInProgress = false;
+ _callbacks.onSuccess();
+ }
+
+ var compress = Zotero.Prefs.get('sync.server.compressData');
+ // Compress upload data
+ if (compress) {
+ // Callback when compressed data is available
+ var bufferUploader = function (data) {
+ var gzurl = url + '?gzip=1';
- if (response.firstChild.tagName == 'error') {
- // handle error
- _error(response.firstChild.firstChild.nodeValue);
- }
+ var oldLen = body.length;
+ var newLen = data.length;
+ var savings = Math.round(((oldLen - newLen) / oldLen) * 100)
+ Zotero.debug("HTTP POST " + newLen + " bytes to " + gzurl
+ + " (gzipped from " + oldLen + " bytes; "
+ + savings + "% savings)");
- if (response.firstChild.localName != 'uploaded') {
- _error("Unexpected upload response '" + response.firstChild.localName
- + "' in Zotero.Sync.Server.sync()");
+ if (Zotero.HTTP.browserIsOffline()) {
+ Zotero.debug('Browser is offline');
+ return false;
}
- Zotero.DB.beginTransaction();
- Zotero.Sync.purgeDeletedObjects(nextLocalSyncTime);
- Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
- Zotero.Sync.Server.nextLocalSyncDate = false;
- Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
-
- var sql = "UPDATE syncedSettings SET synced=1";
- Zotero.DB.query(sql);
-
- //throw('break2');
-
- Zotero.DB.commitTransaction();
-
- // Check if any items were modified during /upload,
- // and restart the sync if so
- if (Zotero.Items.getNewer(nextLocalSyncDate, true)) {
- Zotero.debug("Items were modified during upload -- restarting sync");
- Zotero.Sync.Server.sync(_callbacks, true, true);
- return;
- }
+ var req =
+ Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
+ createInstance();
+ req.open('POST', gzurl, true);
+ req.setRequestHeader('Content-Type', "application/octet-stream");
+ req.setRequestHeader('Content-Encoding', 'gzip');
- _syncInProgress = false;
- _callbacks.onSuccess();
- }
-
- var compress = Zotero.Prefs.get('sync.server.compressData');
- // Compress upload data
- if (compress) {
- // Callback when compressed data is available
- var bufferUploader = function (data) {
- var gzurl = url + '?gzip=1';
-
- var oldLen = body.length;
- var newLen = data.length;
- var savings = Math.round(((oldLen - newLen) / oldLen) * 100)
- Zotero.debug("HTTP POST " + newLen + " bytes to " + gzurl
- + " (gzipped from " + oldLen + " bytes; "
- + savings + "% savings)");
-
- if (Zotero.HTTP.browserIsOffline()) {
- Zotero.debug('Browser is offline');
- return false;
- }
-
- var req =
- Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
- createInstance();
- req.open('POST', gzurl, true);
- req.setRequestHeader('Content-Type', "application/octet-stream");
- req.setRequestHeader('Content-Encoding', 'gzip');
-
- req.onreadystatechange = function () {
- if (req.readyState == 4) {
- uploadCallback(req);
- }
- };
- try {
- req.sendAsBinary(data);
- }
- catch (e) {
- _error(e);
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ uploadCallback(req);
}
+ };
+ try {
+ req.sendAsBinary(data);
+ }
+ catch (e) {
+ _error(e);
}
-
- // Get input stream from POST data
- var unicodeConverter =
- Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
- unicodeConverter.charset = "UTF-8";
- var bodyStream = unicodeConverter.convertToInputStream(body);
-
- // Get listener for when compression is done
- var listener = new Zotero.BufferedInputListener(bufferUploader);
-
- // Initialize stream converter
- var converter =
- Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"]
- .createInstance(Components.interfaces.nsIStreamConverter);
- converter.asyncConvertData("uncompressed", "gzip", listener, null);
-
- // Send input stream to stream converter
- var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
- createInstance(Components.interfaces.nsIInputStreamPump);
- pump.init(bodyStream, -1, -1, 0, 0, true);
- pump.asyncRead(converter, null);
}
- // Don't compress upload data
- else {
- Zotero.HTTP.doPost(url, body, uploadCallback);
- }
+ // Get input stream from POST data
+ var unicodeConverter =
+ Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+ unicodeConverter.charset = "UTF-8";
+ var bodyStream = unicodeConverter.convertToInputStream(body);
+
+ // Get listener for when compression is done
+ var listener = new Zotero.BufferedInputListener(bufferUploader);
+
+ // Initialize stream converter
+ var converter =
+ Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"]
+ .createInstance(Components.interfaces.nsIStreamConverter);
+ converter.asyncConvertData("uncompressed", "gzip", listener, null);
+
+ // Send input stream to stream converter
+ var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
+ createInstance(Components.interfaces.nsIInputStreamPump);
+ pump.init(bodyStream, -1, -1, 0, 0, true);
+ pump.asyncRead(converter, null);
+ }
+
+ // Don't compress upload data
+ else {
+ Zotero.HTTP.doPost(url, body, uploadCallback);
}
- );
-
- try {
- gen.next();
}
- catch (e if e.toString() === "[object StopIteration]") {}
- Zotero.pumpGenerator(gen, false, errorHandler);
- }
- catch (e) {
- errorHandler(e, true);
- }
+ ))
+ .then(
+ null,
+ function (e) {
+ errorHandler(e);
+ }
+ );
}
catch (e) {
_error(e);
@@ -3433,14 +3430,14 @@ Zotero.Sync.Server.Data = new function() {
// Check mod times and hashes of updated items against stored values to see
// if they've been updated elsewhere and mark for download if so
if (type == 'item' && Object.keys(itemStorageModTimes).length) {
- Zotero.Sync.Storage.checkForUpdatedFiles(itemStorageModTimes);
+ yield Zotero.Sync.Storage.checkForUpdatedFiles(itemStorageModTimes);
}
}
if (_timeToYield()) yield true;
callback(Zotero.Sync.Server.Data.buildUploadXML(syncSession));
- }
+ };
/**