diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js index eae92306d..105a96d99 100644 --- a/chrome/content/zotero/xpcom/data/items.js +++ b/chrome/content/zotero/xpcom/data/items.js @@ -102,9 +102,8 @@ Zotero.Items = function() { /** * Return items marked as deleted * - * @param {Integer} libraryID - * @param {Boolean} asIDs Return itemIDs instead of - * Zotero.Item objects + * @param {Integer} libraryID - Library to search + * @param {Boolean} [asIDs] - Return itemIDs instead of Zotero.Item objects * @return {Zotero.Item[]|Integer[]} */ this.getDeleted = Zotero.Promise.coroutine(function* (libraryID, asIDs, days) { @@ -113,7 +112,6 @@ Zotero.Items = function() { if (days) { sql += " AND dateDeleted<=DATE('NOW', '-" + parseInt(days) + " DAYS')"; } - var ids = yield Zotero.DB.columnQueryAsync(sql, [libraryID]); if (!ids.length) { return []; @@ -500,24 +498,27 @@ Zotero.Items = function() { /** - * @param {Integer} days Only delete items deleted more than this many days ago + * @param {Integer} libraryID - Library to delete from + * @param {Integer} [days] - Only delete items deleted more than this many days ago + * @param {Integer} [limit] */ this.emptyTrash = Zotero.Promise.coroutine(function* (libraryID, days, limit) { + if (!libraryID) { + throw new Error("Library ID not provided"); + } + var t = new Date(); var deletedIDs = []; - yield Zotero.DB.executeTransaction(function* () { - deletedIDs = yield this.getDeleted(libraryID, true, days); - if (deletedIDs.length) { - if (limit) { - deletedIDs = deletedIDs.slice(0, limit - 1) - } - yield this.erase(deletedIDs); + deletedIDs = yield this.getDeleted(libraryID, true, days); + if (deletedIDs.length) { + yield Zotero.Utilities.Internal.forEachChunkAsync(deletedIDs, 50, function* (chunk) { + yield this.erase(chunk); Zotero.Notifier.trigger('refresh', 'trash', libraryID); - } - }.bind(this)); - + }.bind(this)); + } + if (deletedIDs.length) { Zotero.debug("Emptied " + deletedIDs.length + " item(s) from trash in " + (new Date() - t) + " ms"); } @@ -547,7 +548,7 @@ Zotero.Items = function() { // TODO: increase number after dealing with slow // tag.getLinkedItems() call during deletes var num = 10; - this.emptyTrash(null, days, num) + this.emptyTrash(Zotero.Libraries.userLibraryID, days, num) .then(deleted => { if (!deleted) { this._emptyTrashTimer = null; diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js index 89b166e31..d6d96bbab 100644 --- a/chrome/content/zotero/xpcom/itemTreeView.js +++ b/chrome/content/zotero/xpcom/itemTreeView.js @@ -603,15 +603,12 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio } if (rows.length > 0) { + // Child items might have been added more than once + rows = Zotero.Utilities.arrayUnique(rows); rows.sort(function(a,b) { return a-b }); - for(var i=0, len=rows.length; i= 0; i--) { + this._removeRow(rows[i]); } madeChanges = true; diff --git a/test/content/support.js b/test/content/support.js index ec50ec954..7187e51d3 100644 --- a/test/content/support.js +++ b/test/content/support.js @@ -76,6 +76,11 @@ function waitForWindow(uri) { return deferred.promise; } +var selectLibrary = Zotero.Promise.coroutine(function* (win) { + yield win.ZoteroPane.collectionsView.selectLibrary(Zotero.Libraries.userLibraryID); + yield waitForItemsLoad(win); +}); + var waitForItemsLoad = function (win, collectionRowToSelect) { var resolve; var promise = new Zotero.Promise(() => resolve = arguments[0]); @@ -151,7 +156,7 @@ function waitForCallback(cb, interval, timeout) { function createUnsavedDataObject(objectType, params) { params = params || {}; if (objectType == 'item') { - var param = 'book'; + var param = params.itemType || 'book'; } var obj = new Zotero[Zotero.Utilities.capitalize(objectType)](param); switch (objectType) { diff --git a/test/tests/collectionTreeViewTest.js b/test/tests/collectionTreeViewTest.js index dfed568d4..544a7c02f 100644 --- a/test/tests/collectionTreeViewTest.js +++ b/test/tests/collectionTreeViewTest.js @@ -3,21 +3,14 @@ describe("Zotero.CollectionTreeView", function() { var win, collectionsView; - // Select library - // TODO: Add a selectCollection() function and select a collection instead - var resetSelection = Zotero.Promise.coroutine(function* () { - yield collectionsView.selectLibrary(Zotero.Libraries.userLibraryID); - yield waitForItemsLoad(win); - assert.equal(collectionsView.getSelectedLibraryID(), Zotero.Libraries.userLibraryID); - }); - // Load Zotero pane and select library before(function* () { win = yield loadZoteroPane(); collectionsView = win.ZoteroPane.collectionsView; }); beforeEach(function () { - return resetSelection(); + // TODO: Add a selectCollection() function and select a collection instead? + return selectLibrary(win); }) after(function () { win.close(); @@ -108,7 +101,7 @@ describe("Zotero.CollectionTreeView", function() { collection.name = "No select on modify"; var id = yield collection.saveTx(); - yield resetSelection(); + yield selectLibrary(win); collection.name = "No select on modify 2"; yield collection.saveTx(); diff --git a/test/tests/itemsTest.js b/test/tests/itemsTest.js index 965a8fe1e..427112ab0 100644 --- a/test/tests/itemsTest.js +++ b/test/tests/itemsTest.js @@ -1,3 +1,42 @@ -describe("Zotero.Items", function() { - +describe("Zotero.Items", function () { + var win, collectionsView; + + before(function* () { + win = yield loadZoteroPane(); + collectionsView = win.ZoteroPane.collectionsView; + }) + beforeEach(function () { + return selectLibrary(win); + }) + after(function () { + win.close(); + }) + + describe("#emptyTrash()", function () { + it("should delete items in the trash", function* () { + var item1 = createUnsavedDataObject('item'); + item1.setField('title', 'a'); + item1.deleted = true; + var id1 = yield item1.saveTx(); + + var item2 = createUnsavedDataObject('item'); + item2.setField('title', 'b'); + item2.deleted = true; + var id2 = yield item2.saveTx(); + + var item3 = createUnsavedDataObject('item', { itemType: 'attachment', parentID: id2 }); + item3.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_URL; + item3.deleted = true; + var id3 = yield item3.saveTx(); + + yield collectionsView.selectTrash(Zotero.Libraries.userLibraryID); + + yield Zotero.Items.emptyTrash(Zotero.Libraries.userLibraryID); + + assert.isFalse(yield Zotero.Items.getAsync(id1)); + assert.isFalse(yield Zotero.Items.getAsync(id2)); + assert.isFalse(yield Zotero.Items.getAsync(id3)); + assert.equal(win.ZoteroPane.itemsView.rowCount, 0); + }) + }) });