This commit is contained in:
Abe Jellinek 2026-10-15 15:42:09 -04:00 • committed by GitHub
commit aec8847965
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 156 additions and 8 deletions

View file

@ -1409,17 +1409,22 @@ Zotero.Items = function () {
return "";
}
// Groups of creator types that are shown together, in order of precedence
var validCreatorTypes = [
Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID),
Zotero.CreatorTypes.getID('editor'),
[
Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID),
Zotero.CreatorTypes.getID('composer'),
Zotero.CreatorTypes.getID('wordsBy')
],
[Zotero.CreatorTypes.getID('editor')],
// Director used to be the primary type for Video Recording before
// Creator (mapped to Author) was added
Zotero.CreatorTypes.getID('director'),
Zotero.CreatorTypes.getID('contributor')
[Zotero.CreatorTypes.getID('director')],
[Zotero.CreatorTypes.getID('contributor')],
];
for (let creatorTypeID of validCreatorTypes) {
let matches = creatorsData.filter(data => data.creatorTypeID == creatorTypeID)
for (let creatorTypeIDs of validCreatorTypes) {
let matches = creatorsData.filter(data => creatorTypeIDs.includes(data.creatorTypeID));
if (!matches.length) {
continue;
}
@ -1584,7 +1589,9 @@ Zotero.Items = function () {
let primaryJoin = "LEFT JOIN itemTypeCreatorTypes ITCT "
+ "ON (IC.creatorTypeID=ITCT.creatorTypeID AND ITCT.itemTypeID=O.itemTypeID) "
+ "WHERE itemID=O.itemID AND primaryField=1";
// composer and wordsBy are shown alongside primary creators (e.g., performers)
+ "WHERE itemID=O.itemID AND (primaryField=1 "
+ `OR IC.creatorTypeID IN (${Zotero.CreatorTypes.getID('composer')}, ${Zotero.CreatorTypes.getID('wordsBy')}))`;
function creatorTypeWhere(typeName) {
return `WHERE itemID=O.itemID AND creatorTypeID=${Zotero.CreatorTypes.getID(typeName)}`;
}
@ -1643,7 +1650,9 @@ Zotero.Items = function () {
let primaryJoin = "LEFT JOIN itemTypeCreatorTypes ITCT "
+ "ON (IC.creatorTypeID=ITCT.creatorTypeID AND ITCT.itemTypeID=O.itemTypeID) "
+ "WHERE itemID=O.itemID AND primaryField=1";
// composer and wordsBy are shown alongside primary creators (e.g., performers)
+ "WHERE itemID=O.itemID AND (primaryField=1 "
+ `OR IC.creatorTypeID IN (${Zotero.CreatorTypes.getID('composer')}, ${Zotero.CreatorTypes.getID('wordsBy')}))`;
function creatorTypeWhere(typeName) {
return `WHERE itemID=O.itemID AND creatorTypeID=${Zotero.CreatorTypes.getID(typeName)}`;
}

View file

@ -72,6 +72,145 @@ describe("Zotero.Item", function () {
assert.equal(item.getField('firstCreator'), "B");
});
it("should return composer as firstCreator for an audioRecording without a primary creator", async function () {
var item = createUnsavedDataObject('item', { itemType: 'audioRecording' });
item.setCreators([
{
firstName: "A",
lastName: "B",
creatorType: "translator"
},
{
firstName: "C",
lastName: "D",
creatorType: "composer"
}
]);
assert.equal(item.getField('firstCreator'), "D");
await item.saveTx();
assert.equal(item.getField('firstCreator'), "D");
assert.equal(item.sortCreator, "D C");
});
it("should return wordsBy as firstCreator for an audioRecording without a primary creator", async function () {
var item = createUnsavedDataObject('item', { itemType: 'audioRecording' });
item.setCreators([
{
firstName: "A",
lastName: "B",
creatorType: "translator"
},
{
firstName: "C",
lastName: "D",
creatorType: "wordsBy"
},
{
firstName: "E",
lastName: "F",
creatorType: "wordsBy"
}
]);
var expected = Zotero.getString('general.andJoiner', ['⁨D⁩', '⁨F⁩']);
assert.equal(item.getField('firstCreator'), expected);
await item.saveTx();
assert.equal(item.getField('firstCreator'), expected);
assert.equal(item.sortCreator, "D C F E");
});
it("should show composer and wordsBy alongside performer for an audioRecording", async function () {
var item = createUnsavedDataObject('item', { itemType: 'audioRecording' });
item.setCreators([
{
firstName: "A",
lastName: "B",
creatorType: "performer"
},
{
firstName: "C",
lastName: "D",
creatorType: "translator"
},
{
firstName: "E",
lastName: "F",
creatorType: "composer"
}
]);
var expected = Zotero.getString('general.andJoiner', ['⁨B⁩', '⁨F⁩']);
assert.equal(item.getField('firstCreator'), expected);
await item.saveTx();
assert.equal(item.getField('firstCreator'), expected);
assert.equal(item.sortCreator, "B A F E");
item.setCreators([
...item.getCreators(),
{
firstName: "G",
lastName: "H",
creatorType: "wordsBy"
}
]);
expected = "B " + Zotero.getString('general.etAl');
assert.equal(Zotero.Items.getFirstCreatorFromData(item.itemTypeID, item.getCreators()), expected);
await item.saveTx();
assert.equal(item.getField('firstCreator'), expected);
assert.equal(item.sortCreator, "B A F E H G");
});
it("should show wordsBy alongside performer for an audioRecording", async function () {
var item = createUnsavedDataObject('item', { itemType: 'audioRecording' });
item.setCreators([
{
firstName: "A",
lastName: "B",
creatorType: "wordsBy"
},
{
firstName: "C",
lastName: "D",
creatorType: "performer"
}
]);
var expected = Zotero.getString('general.andJoiner', ['⁨B⁩', '⁨D⁩']);
assert.equal(item.getField('firstCreator'), expected);
await item.saveTx();
assert.equal(item.getField('firstCreator'), expected);
assert.equal(item.sortCreator, "B A D C");
});
it("should prefer composer and wordsBy over contributor", async function () {
var item = createUnsavedDataObject('item', { itemType: 'audioRecording' });
item.setCreators([
{
firstName: "A",
lastName: "B",
creatorType: "wordsBy"
},
{
firstName: "C",
lastName: "D",
creatorType: "contributor"
},
{
firstName: "E",
lastName: "F",
creatorType: "composer"
}
]);
var expected = Zotero.getString('general.andJoiner', ['⁨B⁩', '⁨F⁩']);
assert.equal(item.getField('firstCreator'), expected);
await item.saveTx();
assert.equal(item.getField('firstCreator'), expected);
assert.equal(item.sortCreator, "B A F E");
});
it("should strip bidi isolates from firstCreator when unformatted = true", async function () {
var item = createUnsavedDataObject('item');
item.setCreators([