diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx index c2beef211f..70ab141024 100644 --- a/chrome/content/zotero/collectionTree.jsx +++ b/chrome/content/zotero/collectionTree.jsx @@ -1426,11 +1426,12 @@ var CollectionTree = class CollectionTree extends LibraryTree { event.dataTransfer.effectAllowed = 'copyMove'; } - if (!treeRow.isCollection()) { + if (!treeRow.isCollection() && !treeRow.isSearch()) { return; } - event.dataTransfer.setData("zotero/collection", treeRow.ref.id); - Zotero.debug("Dragging collection " + treeRow.id); + let type = treeRow.isCollection() ? "zotero/collection" : "zotero/search"; + event.dataTransfer.setData(type, treeRow.ref.id); + Zotero.debug(`Dragging ${type} ` + treeRow.id); } onDragOver(event, index) { @@ -1567,6 +1568,21 @@ var CollectionTree = class CollectionTree extends LibraryTree { // Directly on a row if (orient === 0) { if (!treeRow.editable) { + // Can drop collections, searches, and items into trash of their own library + if (treeRow.isTrash()) { + let objects = []; + if (dataType === 'zotero/item') { + objects = Zotero.Items.get(data); + } + else if (dataType === 'zotero/collection') { + objects = Zotero.Collections.get(data); + } + else if (dataType === 'zotero/search') { + objects = Zotero.Searches.get(data); + } + let allInSameLibrary = objects.every(object => object.libraryID === treeRow.ref.libraryID); + return allInSameLibrary; + } // Zotero.debug("Drop target not editable"); return false; } @@ -2096,6 +2112,27 @@ var CollectionTree = class CollectionTree extends LibraryTree { childFileAttachments: Zotero.Prefs.get('groups.copyChildFileAttachments'), annotations: Zotero.Prefs.get('groups.copyAnnotations'), }; + + // Dropping items, collections, or searches into trash + if (targetTreeRow.isTrash()) { + let objects = []; + if (dataType == 'zotero/collection') { + objects = await Zotero.Collections.getAsync(data); + } + else if (dataType == 'zotero/search') { + objects = await Zotero.Searches.getAsync(data); + } + else if (dataType == 'zotero/item') { + objects = await Zotero.Items.getAsync(data); + } + await Zotero.DB.executeTransaction(async function () { + for (let obj of objects) { + obj.deleted = true; + await obj.save(); + } + }); + return; + } var targetLibraryID = targetTreeRow.ref.libraryID; var targetCollectionID = targetTreeRow.isCollection() ? targetTreeRow.ref.id : false; diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index 7cfcc8b8df..b311b59c9e 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -2082,6 +2082,11 @@ Zotero.DragDrop = { let ids = dt.getData('zotero/item').split(",").map(id => parseInt(id)); dragData.data = ids; } + else if (dt.types.includes('zotero/search')) { + dragData.dataType = 'zotero/search'; + let ids = dt.getData('zotero/search').split(",").map(id => parseInt(id)); + dragData.data = ids; + } else { if (dt.types.includes('application/x-moz-file')) { dragData.dataType = 'application/x-moz-file'; diff --git a/test/tests/collectionTreeTest.js b/test/tests/collectionTreeTest.js index 77e6a323fa..2cb87527c3 100644 --- a/test/tests/collectionTreeTest.js +++ b/test/tests/collectionTreeTest.js @@ -1118,6 +1118,14 @@ describe("Zotero.CollectionTree", function() { assert.isTrue(droppedItem.deleted); }) + + it("can delete an item by dropping it into the trash", async function () { + var item = await createDataObject('item', false, { skipSelect: true }); + var deferred = Zotero.Promise.defer(); + deferred.resolve(); + await onDrop('item', 'T' + userLibraryID, [item.id], deferred.promise); + assert.isTrue(item.deleted); + }); }) @@ -1373,6 +1381,14 @@ describe("Zotero.CollectionTree", function() { await group1.eraseTx(); await group2.eraseTx(); }); + + it("can delete a collection by dropping it into the trash", async function () { + var collection = await createDataObject('collection'); + var deferred = Zotero.Promise.defer(); + deferred.resolve(); + await onDrop('collection', 'T' + userLibraryID, [collection.id], deferred.promise); + assert.isTrue(collection.deleted); + }) }) @@ -1430,6 +1446,16 @@ describe("Zotero.CollectionTree", function() { assert.equal(treeRow.ref.id, item.id); }) }) + + describe("with searches", function () { + it("can delete a saved search by dropping it into the trash", async function () { + var search = await createDataObject('search'); + var deferred = Zotero.Promise.defer(); + deferred.resolve(); + await onDrop('search', 'T' + userLibraryID, [search.id], deferred.promise); + assert.isTrue(search.deleted); + }) + }); }) describe("Feeds pseudo-library", function () {