Fix collections in trash showing up in menus

New Collection menu and Add to Collection menu

https://forums.zotero.org/discussion/115447/zotero-7-beta-deleted-collections-still-appear-in-the-ui
This commit is contained in:
Dan Stillman 2024-06-22 16:33:11 -04:00
parent b9f0d26cee
commit a612c1227e
4 changed files with 60 additions and 18 deletions

View file

@ -194,18 +194,24 @@ Zotero.Collection.prototype.hasChildItems = function() {
* Returns subcollections of this collection * Returns subcollections of this collection
* *
* @param {Boolean} [asIDs=false] Return as collectionIDs * @param {Boolean} [asIDs=false] Return as collectionIDs
* @param {Boolean} [includeTrashed=false] - Include collections in the trash
* @return {Zotero.Collection[]|Integer[]} * @return {Zotero.Collection[]|Integer[]}
*/ */
Zotero.Collection.prototype.getChildCollections = function (asIDs) { Zotero.Collection.prototype.getChildCollections = function (asIDs, includeTrashed) {
this._requireData('childCollections'); this._requireData('childCollections');
var collections = [...this._childCollections].map(id => this.ObjectsClass.get(id));
if (!includeTrashed) {
collections = collections.filter(c => !c.deleted);
}
// Return collectionIDs // Return collectionIDs
if (asIDs) { if (asIDs) {
return [...this._childCollections.values()]; return collections.map(c => c.id);
} }
// Return Zotero.Collection objects // Return Zotero.Collection objects
return Array.from(this._childCollections).map(id => this.ObjectsClass.get(id)); return collections;
} }
@ -216,7 +222,7 @@ Zotero.Collection.prototype.getChildCollections = function (asIDs) {
* @param {Boolean} includeDeleted Include items in Trash * @param {Boolean} includeDeleted Include items in Trash
* @return {Zotero.Item[]|Integer[]} - Array of Zotero.Item instances or itemIDs * @return {Zotero.Item[]|Integer[]} - Array of Zotero.Item instances or itemIDs
*/ */
Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) { Zotero.Collection.prototype.getChildItems = function (asIDs, includeTrashed) {
this._requireData('childItems'); this._requireData('childItems');
if (this._childItems.size == 0) { if (this._childItems.size == 0) {
@ -227,7 +233,7 @@ Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
var childItems = []; var childItems = [];
for (let itemID of this._childItems) { for (let itemID of this._childItems) {
let item = this.ChildObjects.get(itemID); let item = this.ChildObjects.get(itemID);
if (includeDeleted || !item.deleted) { if (includeTrashed || !item.deleted) {
childItems.push(item); childItems.push(item);
} }
} }
@ -840,11 +846,11 @@ Zotero.Collection.prototype.toJSON = function (options = {}) {
* @param {Boolean} [nested=false] Return multidimensional array with 'children' * @param {Boolean} [nested=false] Return multidimensional array with 'children'
* nodes instead of flat array * nodes instead of flat array
* @param {String} [type] 'item', 'collection', or NULL for both * @param {String} [type] 'item', 'collection', or NULL for both
* @param {Boolean} [includeDeletedItems=false] Include items in Trash * @param {Boolean} [includeTrashed=false] Include collections and items in Trash
* @return {Object[]} - An array of objects with 'id', 'key', 'type' ('item' or 'collection'), * @return {Object[]} - An array of objects with 'id', 'key', 'type' ('item' or 'collection'),
* 'parent', and, if collection, 'name' and the nesting 'level' * 'parent', and, if collection, 'name' and the nesting 'level'
*/ */
Zotero.Collection.prototype.getDescendents = function (nested, type, includeDeletedItems, level) { Zotero.Collection.prototype.getDescendents = function (nested, type, includeTrashed, level) {
if (!this.id) { if (!this.id) {
throw new Error('Cannot be called on an unsaved item'); throw new Error('Cannot be called on an unsaved item');
} }
@ -863,7 +869,7 @@ Zotero.Collection.prototype.getDescendents = function (nested, type, includeDele
} }
} }
var collections = Zotero.Collections.getByParent(this.id); var collections = Zotero.Collections.getByParent(this.id, false, includeTrashed);
var children = collections.map(c => ({ var children = collections.map(c => ({
id: c.id, id: c.id,
name: c.name, name: c.name,
@ -871,7 +877,7 @@ Zotero.Collection.prototype.getDescendents = function (nested, type, includeDele
key: c.key key: c.key
})); }));
if (!type || type == 'item') { if (!type || type == 'item') {
let items = this.getChildItems(false, includeDeletedItems); let items = this.getChildItems(false, includeTrashed);
children = children.concat(items.map(i => ({ children = children.concat(items.map(i => ({
id: i.id, id: i.id,
name: null, name: null,
@ -902,7 +908,7 @@ Zotero.Collection.prototype.getDescendents = function (nested, type, includeDele
let child = this.ObjectsClass.get(children[i].id); let child = this.ObjectsClass.get(children[i].id);
let descendents = child.getDescendents( let descendents = child.getDescendents(
nested, type, includeDeletedItems, level + 1 nested, type, includeTrashed, level + 1
); );
if (nested) { if (nested) {

View file

@ -65,10 +65,11 @@ Zotero.Collections = function() {
* *
* @param {Integer} libraryID * @param {Integer} libraryID
* @param {Boolean} [recursive=false] * @param {Boolean} [recursive=false]
* @param {Boolean} [includeTrashed=false]
* @return {Zotero.Collection[]} * @return {Zotero.Collection[]}
*/ */
this.getByLibrary = function (libraryID, recursive) { this.getByLibrary = function (libraryID, recursive, includeTrashed) {
return _getByContainer(libraryID, null, recursive); return _getByContainer(libraryID, null, recursive, includeTrashed);
} }
@ -77,23 +78,24 @@ Zotero.Collections = function() {
* *
* @param {Integer} parentCollectionID * @param {Integer} parentCollectionID
* @param {Boolean} [recursive=false] * @param {Boolean} [recursive=false]
* @param {Boolean} [includeTrashed=false]
* @return {Zotero.Collection[]} * @return {Zotero.Collection[]}
*/ */
this.getByParent = function (parentCollectionID, recursive) { this.getByParent = function (parentCollectionID, recursive, includeTrashed) {
return _getByContainer(null, parentCollectionID, recursive); return _getByContainer(null, parentCollectionID, recursive, includeTrashed);
} }
var _getByContainer = function (libraryID, parentID, recursive) { var _getByContainer = function (libraryID, parentID, recursive, includeTrashed) {
let children = []; let children = [];
if (parentID) { if (parentID) {
let parent = Zotero.Collections.get(parentID); let parent = Zotero.Collections.get(parentID);
children = parent.getChildCollections(); children = parent.getChildCollections(false, includeTrashed);
} else if (libraryID) { } else if (libraryID) {
for (let id in this._objectCache) { for (let id in this._objectCache) {
let c = this._objectCache[id]; let c = this._objectCache[id];
if (c.libraryID == libraryID && !c.parentKey) { if (c.libraryID == libraryID && !c.parentKey && (includeTrashed || !c.deleted)) {
c.level = 0; c.level = 0;
children.push(c); children.push(c);
} }
@ -116,7 +118,7 @@ Zotero.Collections = function() {
var obj = children[i]; var obj = children[i];
toReturn.push(obj); toReturn.push(obj);
var descendants = obj.getDescendents(false, 'collection'); var descendants = obj.getDescendents(false, 'collection', includeTrashed);
for (let d of descendants) { for (let d of descendants) {
var obj2 = this.get(d.id); var obj2 = this.get(d.id);
if (!obj2) { if (!obj2) {

View file

@ -285,6 +285,22 @@ describe("Zotero.Collection", function() {
assert.lengthOf(childCollections, 0); assert.lengthOf(childCollections, 0);
}) })
it("should not include collections in trash by default", async function () {
var collection1 = await createDataObject('collection');
var collection2 = await createDataObject('collection', { parentID: collection1.id, deleted: true });
var childCollections = collection1.getChildCollections();
assert.lengthOf(childCollections, 0);
});
it("should include collections in trash if includeTrashed=true", async function () {
var collection1 = await createDataObject('collection');
var collection2 = await createDataObject('collection', { parentID: collection1.id, deleted: true });
var childCollections = collection1.getChildCollections(false, true);
assert.lengthOf(childCollections, 1);
});
it("should not include collections that have been deleted", function* () { it("should not include collections that have been deleted", function* () {
var collection1 = yield createDataObject('collection'); var collection1 = yield createDataObject('collection');
var collection2 = yield createDataObject('collection', { parentID: collection1.id }); var collection2 = yield createDataObject('collection', { parentID: collection1.id });

View file

@ -48,6 +48,24 @@ describe("Zotero.Collections", function () {
assert.equal(cols[5].level, 1); assert.equal(cols[5].level, 1);
assert.equal(cols[6].level, 0); assert.equal(cols[6].level, 0);
}) })
it("should not include collections in trash", async function () {
var libraryID = Zotero.Libraries.userLibraryID;
var col = await createDataObject('collection', { deleted: true });
var cols = Zotero.Collections.getByLibrary(libraryID);
assert.notInclude(cols.map(c => c.id), col.id);
});
it("should not include collections in trash in recursive mode", async function () {
var libraryID = Zotero.Libraries.userLibraryID;
var col1 = await createDataObject('collection');
var col2 = await createDataObject('collection', { parentID: col1.id, deleted: true });
var col3 = await createDataObject('collection', { parentID: col2.id });
var col4 = await createDataObject('collection', { parentID: col1.id });
var col5 = await createDataObject('collection', { parentID: col4.id, deleted: true });
var cols = Zotero.Collections.getByLibrary(libraryID, true);
assert.notIncludeMembers(cols.map(c => c.id), [col2.id, col3.id, col5.id]);
});
}) })
describe("#getByParent()", function () { describe("#getByParent()", function () {