diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js index 4194d325f..963eebe36 100644 --- a/chrome/content/zotero/xpcom/data/collection.js +++ b/chrome/content/zotero/xpcom/data/collection.js @@ -44,6 +44,8 @@ Zotero.Collection.prototype._init = function () { this._hasChildItems = false; this._childItems = []; this._childItemsLoaded = false; + + this._dateModifiedLocked = false; } @@ -266,6 +268,21 @@ Zotero.Collection.prototype.getChildItems = function (asIDs) { } +/** + * Prevent dateModified from being updated when removing an item + * + * Used for a tricky sync case + */ +Zotero.Collection.prototype.lockDateModified = function () { + this._dateModifiedLocked = true; +} + + +Zotero.Collection.prototype.unlockDateModified = function () { + this._dateModifiedLocked = false; +} + + Zotero.Collection.prototype.save = function () { if (!this.name) { throw ('Collection name is empty in Zotero.Collection.save()'); @@ -594,8 +611,10 @@ Zotero.Collection.prototype.removeItem = function(itemID) { var sql = "DELETE FROM collectionItems WHERE collectionID=? AND itemID=?"; Zotero.DB.query(sql, [this.id, itemID]); - sql = "UPDATE collections SET dateModified=? WHERE collectionID=?"; - Zotero.DB.query(sql, [Zotero.DB.transactionDateTime, this.id]) + if (!this._dateModifiedLocked) { + sql = "UPDATE collections SET dateModified=? WHERE collectionID=?"; + Zotero.DB.query(sql, [Zotero.DB.transactionDateTime, this.id]) + } Zotero.DB.commitTransaction(); diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js index 8ac12a0d6..ca76655c0 100644 --- a/chrome/content/zotero/xpcom/sync.js +++ b/chrome/content/zotero/xpcom/sync.js @@ -1484,20 +1484,38 @@ Zotero.Sync.Server.Data = new function() { } + /** + * Pull out collections from delete queue in XML + * + * @param {XML} xml + * @return {Integer[]} Array of collection ids + */ + function _getDeletedCollections(xml) { + var ids = []; + if (xml.deleted && xml.deleted.collections) { + for each(var xmlNode in xml.deleted.collections.collection) { + ids.push(parseInt(xmlNode.@id)); + } + } + return ids; + } + + function processUpdatedXML(xml, lastLocalSyncDate, syncSession) { if (xml.children().length() == 0) { Zotero.debug('No changes received from server'); return Zotero.Sync.Server.Data.buildUploadXML(syncSession); } + Zotero.DB.beginTransaction(); + xml = _preprocessUpdatedXML(xml); + var deletedCollections = _getDeletedCollections(xml); var remoteCreatorStore = {}; var relatedItemsStore = {}; var itemStorageModTimes = {}; - Zotero.DB.beginTransaction(); - for each(var syncObject in Zotero.Sync.syncObjects) { var Type = syncObject.singular; // 'Item' var Types = syncObject.plural; // 'Items' @@ -2019,6 +2037,17 @@ Zotero.Sync.Server.Data = new function() { parents.push(item.id); } } + + // Lock dateModified in local versions of remotely deleted + // collections so that any deleted items within them don't + // update them, which would trigger erroneous conflicts + var collections = []; + for each(var colID in deletedCollections) { + var col = Zotero.Collections.get(colID); + col.lockDateModified(); + collections.push(col); + } + if (children.length) { Zotero.Sync.EventListener.ignoreDeletions('item', children); Zotero.Items.erase(children); @@ -2029,6 +2058,12 @@ Zotero.Sync.Server.Data = new function() { Zotero.Items.erase(parents); Zotero.Sync.EventListener.unignoreDeletions('item', parents); } + + // Unlock dateModified for deleted collections + for each(var col in collections) { + col.unlockDateModified(); + } + collection = null; } else { Zotero.Sync.EventListener.ignoreDeletions(type, toDelete);