Don't pass nested collections to translators as top-level collections

While we need to pass all items from descendant collections to
translators, only the immediate child collections of the selected
library or collection should be returned from Zotero.nextCollection().

I'm not sure how long we've been doing this wrong, but it resulted in
duplicated subcollections when round-tripping Zotero RDF. (TEI is the
only other translator that uses nextCollection(), so its exports were
probably similarly incorrect.)

https://forums.zotero.org/discussion/87135/export-import-creates-numerous-duplicate-subcollections
This commit is contained in:
Dan Stillman 2021-01-25 03:15:26 -05:00
parent 10ca05e22e
commit b3220e83b1
2 changed files with 71 additions and 6 deletions

View file

@ -1021,12 +1021,13 @@ Zotero.Translate.ItemGetter.prototype = {
var items = new Set(collection.getChildItems());
if(getChildCollections) {
// get child collections
this._collectionsLeft = Zotero.Collections.getByParent(collection.id, true);
// Get child collections
this._collectionsLeft = Zotero.Collections.getByParent(collection.id);
// get items in child collections
for (let collection of this._collectionsLeft) {
var childItems = collection.getChildItems();
// Get items in all descendant collections
let descendantCollections = Zotero.Collections.getByParent(collection.id, true);
for (let collection of descendantCollections) {
let childItems = collection.getChildItems();
childItems.forEach(item => items.add(item));
}
}
@ -1040,7 +1041,7 @@ Zotero.Translate.ItemGetter.prototype = {
this._itemsLeft = yield Zotero.Items.getAll(libraryID, true);
if(getChildCollections) {
this._collectionsLeft = Zotero.Collections.getByLibrary(libraryID, true);
this._collectionsLeft = Zotero.Collections.getByLibrary(libraryID);
}
this._itemsLeft.sort(function(a, b) { return a.id - b.id; });

View file

@ -1148,6 +1148,70 @@ describe("Zotero.Translate", function() {
var attachment = Zotero.Items.get(attachments[0]);
assert.isTrue(yield attachment.fileExists());
});
it("should round-trip collections via Zotero RDF", async function () {
this.timeout(60000);
await resetDB();
var c1 = await createDataObject('collection', { name: '1' });
var c2 = await createDataObject('collection', { name: '2', parentID: c1.id });
var c3 = await createDataObject('collection', { name: '3', parentID: c2.id });
var c4 = await createDataObject('collection', { name: '4', parentID: c3.id });
var c5 = await createDataObject('collection', { name: '5', parentID: c4.id });
var item = await createDataObject('item', { collections: [c5.id] });
var tmpDir = await getTempDirectory();
var libraryExportFile = OS.Path.join(tmpDir, 'export-library.rdf');
var collectionExportFile = OS.Path.join(tmpDir, 'export-collection.rdf');
// Export library
var translation = new Zotero.Translate.Export();
translation.setLocation(Zotero.File.pathToFile(libraryExportFile));
translation.setLibraryID(Zotero.Libraries.userLibraryID);
translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9'); // Zotero RDF
await translation.translate();
// Export top-most collection
translation = new Zotero.Translate.Export();
translation.setLocation(Zotero.File.pathToFile(collectionExportFile));
translation.setCollection(c1);
translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9'); // Zotero RDF
await translation.translate();
async function check(file, mode) {
var collectionNames = [c1.name, c2.name, c3.name, c4.name, c5.name];
var translation = new Zotero.Translate.Import();
translation.setLocation(Zotero.File.pathToFile(file));
var translators = await translation.getTranslators();
translation.setTranslator(translators[0]);
var importCollection = await createDataObject('collection');
await translation.translate({
libraryID: Zotero.Libraries.userLibraryID,
collections: [importCollection.id]
});
// When exporting a library, the top-most collection should be included. When
// exporting a collection, the selected collection isn't included, so remove it.
if (mode == 'collection') {
collectionNames.shift();
}
var collections = importCollection.getChildCollections();
assert.lengthOf(collections, 1, mode);
assert.equal(collections[0].name, collectionNames.shift(), mode)
var name;
while (name = collectionNames.shift()) {
collections = collections[0].getChildCollections();
assert.lengthOf(collections, 1, mode);
assert.equal(collections[0].name, name, mode)
}
}
await check(libraryExportFile, 'library');
await check(collectionExportFile, 'collection');
});
});