www

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

commit e8736178902146edb6f840cce7deb100d92008a2
parent a80f1309974e5e36e9d7bd96c5e54414e0961282
Author: Dan Stillman <dstillman@zotero.org>
Date:   Sun, 17 Jan 2016 16:55:34 -0500

Fix trashing of descendant items when deleting a collection

Also allows 'collections' property to be passed to
createDataObject()/createUnsavedDataObject() in tests.

Diffstat:
Mchrome/content/zotero/itemPane.js | 2+-
Mchrome/content/zotero/xpcom/collectionTreeView.js | 4+++-
Mchrome/content/zotero/xpcom/data/dataObject.js | 12+++++++-----
Mchrome/content/zotero/xpcom/data/item.js | 1+
Mchrome/content/zotero/xpcom/data/items.js | 53++++++++++++++++++++++++++++++-----------------------
Mchrome/content/zotero/xpcom/itemTreeView.js | 2+-
Mtest/content/support.js | 3+++
Mtest/tests/collectionTest.js | 25+++++++++++++++++++++++++
Mtest/tests/dataObjectUtilitiesTest.js | 2+-
9 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js @@ -179,7 +179,7 @@ var ZoteroItemPane = new function() { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); if (ps.confirm(null, '', Zotero.getString('pane.item.notes.delete.confirm'))) { - Zotero.Items.trash(id); + Zotero.Items.trashTx(id); } } diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -1037,7 +1037,9 @@ Zotero.CollectionTreeView.prototype.deleteSelection = Zotero.Promise.coroutine(f //erase collection from DB: var treeRow = this.getRow(rows[i]-i); if (treeRow.isCollection()) { - yield treeRow.ref.erase(deleteItems); + yield treeRow.ref.eraseTx({ + deleteItems: true + }); } else if (treeRow.isSearch()) { yield Zotero.Searches.erase(treeRow.ref.id); diff --git a/chrome/content/zotero/xpcom/data/dataObject.js b/chrome/content/zotero/xpcom/data/dataObject.js @@ -1136,13 +1136,15 @@ Zotero.DataObject.prototype.updateSynced = Zotero.Promise.coroutine(function* (s * Delete object from database * * @param {Object} [options] + * @param {Boolean} [options.deleteItems] - Move descendant items to trash (Collection only) * @param {Boolean} [options.skipDeleteLog] - Don't add to sync delete log */ -Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* (options) { - options = options || {}; - var env = { - options: options - }; +Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* (options = {}) { + if (!options || typeof options != 'object') { + throw new Error("'options' must be an object"); + } + + var env = { options }; if (!env.options.tx && !Zotero.DB.inTransaction()) { Zotero.logError("erase() called on Zotero." + this._ObjectType + " without a wrapping " diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js @@ -148,6 +148,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'parentItemKey', { set: function(val) this.parentKey = val }); + Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', { get: function() this._firstCreator }); diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js @@ -492,32 +492,39 @@ Zotero.Items = function() { }; - this.trash = function (ids) { + this.trash = Zotero.Promise.coroutine(function* (ids) { + Zotero.DB.requireTransaction(); + ids = Zotero.flattenArguments(ids); - return Zotero.DB.executeTransaction(function* () { - for (let i=0; i<ids.length; i++) { - let id = ids[i]; - let item = yield this.getAsync(id); - if (!item) { - Zotero.debug('Item ' + id + ' does not exist in Items.trash()!', 1); - Zotero.Notifier.queue('delete', 'item', id); - continue; - } - - if (!item.isEditable()) { - throw new Error(item._ObjectType + " " + item.libraryKey + " is not editable"); - } - - if (!Zotero.Libraries.hasTrash(item.libraryID)) { - throw new Error(Zotero.Libraries.getName(item.libraryID) + " does not have Trash"); - } - - item.deleted = true; - yield item.save({ - skipDateModifiedUpdate: true - }); + for (let i=0; i<ids.length; i++) { + let id = ids[i]; + let item = yield this.getAsync(id); + if (!item) { + Zotero.debug('Item ' + id + ' does not exist in Items.trash()!', 1); + Zotero.Notifier.queue('delete', 'item', id); + continue; } + + if (!item.isEditable()) { + throw new Error(item._ObjectType + " " + item.libraryKey + " is not editable"); + } + + if (!Zotero.Libraries.hasTrash(item.libraryID)) { + throw new Error(Zotero.Libraries.getName(item.libraryID) + " does not have Trash"); + } + + item.deleted = true; + yield item.save({ + skipDateModifiedUpdate: true + }); + } + }); + + + this.trashTx = function (ids) { + return Zotero.DB.executeTransaction(function* () { + return this.trash(ids); }.bind(this)); } diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js @@ -1797,7 +1797,7 @@ Zotero.ItemTreeView.prototype.deleteSelection = Zotero.Promise.coroutine(functio Zotero.Items.erase(ids); } else if (collectionTreeRow.isLibrary(true) || force) { - Zotero.Items.trash(ids); + Zotero.Items.trashTx(ids); } else if (collectionTreeRow.isCollection()) { collectionTreeRow.ref.removeItems(ids); diff --git a/test/content/support.js b/test/content/support.js @@ -319,6 +319,9 @@ function createUnsavedDataObject(objectType, params = {}) { if (params.title !== undefined || params.setTitle) { obj.setField('title', params.title !== undefined ? params.title : Zotero.Utilities.randomString()); } + if (params.collections !== undefined) { + obj.setCollections(params.collections); + } break; case 'collection': diff --git a/test/tests/collectionTest.js b/test/tests/collectionTest.js @@ -13,6 +13,31 @@ describe("Zotero.Collection", function() { }); }) + describe("#erase()", function () { + it("should delete a collection but not its descendant item by default", function* () { + var collection = yield createDataObject('collection'); + var item = yield createDataObject('item', { collections: [collection.id] }); + assert.isTrue(collection.hasItem(item.id)); + + yield collection.eraseTx(); + + assert.isFalse((yield Zotero.Items.getAsync(item.id)).deleted); + }) + + it("should delete a collection and trash its descendant items with deleteItems: true", function* () { + var collection = yield createDataObject('collection'); + var item1 = yield createDataObject('item', { collections: [collection.id] }); + var item2 = yield createDataObject('item', { collections: [collection.id] }); + assert.isTrue(collection.hasItem(item1.id)); + assert.isTrue(collection.hasItem(item2.id)); + + yield collection.eraseTx({ deleteItems: true }); + + assert.isTrue((yield Zotero.Items.getAsync(item1.id)).deleted); + assert.isTrue((yield Zotero.Items.getAsync(item2.id)).deleted); + }) + }) + describe("#version", function () { it("should set object version", function* () { var version = 100; diff --git a/test/tests/dataObjectUtilitiesTest.js b/test/tests/dataObjectUtilitiesTest.js @@ -35,7 +35,7 @@ describe("Zotero.DataObjectUtilities", function() { var changes = Zotero.DataObjectUtilities.diff(json1, json2); assert.lengthOf(changes, 0); - yield Zotero.Items.erase(id1, id2); + yield Zotero.Items.erase([id1, id2]); }) it("should not show empty strings as different", function () {