Merge pull request #2397 from AbeJellinek/scaffold-sanitize-for-display

This commit is contained in:
Abe Jellinek 2022-03-25 11:59:05 -07:00 committed by GitHub
commit a3322a9afb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1056,9 +1056,17 @@ var Scaffold = new function () {
* logs item output
*/
function _myItemDone(obj, item) {
item = _sanitizeItem(item);
_logOutput("Returned item:\n" + Zotero_TranslatorTester._generateDiff(item, Zotero_TranslatorTester._sanitizeItem(item, true)));
if (Array.isArray(item.attachments)) {
for (let attachment of item.attachments) {
if (attachment.document) {
attachment.document = '[object Document]';
attachment.mimeType = 'text/html';
}
delete attachment.url;
delete attachment.complete;
}
}
_logOutput("Returned item:\n" + Zotero_TranslatorTester._generateDiff(item, _sanitizeItemForDisplay(item)));
}
/*
@ -1266,20 +1274,163 @@ var Scaffold = new function () {
_writeToEditor(_editors.tests, _stringifyTests(tests));
}
/* turns an item into a test-safe item
* does not check if all fields are valid
/**
* Mimics most of the behavior of Zotero.Item#fromJSON. Most importantly,
* extracts valid fields from item.extra and inserts invalid fields into
* item.extra.
*
* For example,
* { itemType: 'journalArticle', extra: 'DOI: foo' }
* becomes
* { itemType: 'journalArticle', DOI: 'foo' }
* and
* { itemType: 'book', DOI: 'foo' }
* becomes
* { itemType: 'book', extra: 'DOI: foo' }
*
* @param {any} item
* @return {any}
*/
function _sanitizeItem(item) {
// Clear attachment document objects
if (item && item.attachments && item.attachments.length) {
for (var i = 0; i < item.attachments.length; i++) {
if (item.attachments[i].document) item.attachments[i].document = "[object]";
function _sanitizeItemForDisplay(item) {
// try to convert to JSON and back to get rid of undesirable undeletable elements; this may fail
try {
item = JSON.parse(JSON.stringify(item));
}
catch (e) {}
let itemTypeID = Zotero.ItemTypes.getID(item.itemType);
var setFields = new Set();
var { itemType, fields: extraFields, /* creators: extraCreators, */ extra }
= Zotero.Utilities.Internal.extractExtraFields(
item.extra || '',
null,
Object.keys(item)
// TEMP until we move creator lines to real creators
.concat('creators')
);
// If a different item type was parsed out of Extra, use that instead
if (itemType && item.itemType != itemType) {
item.itemType = itemType;
itemTypeID = Zotero.ItemTypes.getID(itemType);
}
for (let [field, value] of extraFields) {
item[field] = value;
setFields.add(field);
extraFields.delete(field);
}
for (let [field, val] of Object.entries(item)) {
switch (field) {
case 'itemType':
case 'accessDate':
case 'creators':
case 'attachments':
case 'notes':
case 'seeAlso':
break;
case 'extra':
// We set this later
delete item[field];
break;
case 'tags':
item[field] = Zotero.Translate.Base.prototype._cleanTags(val);
break;
// Item fields
default: {
let fieldID = Zotero.ItemFields.getID(field);
if (!fieldID) {
if (typeof val == 'string') {
extraFields.set(field, val);
break;
}
delete item[field];
continue;
}
// Convert to base-mapped field if necessary, so that setFields has the base-mapped field
// when it's checked for values from getUsedFields() below
let origFieldID = fieldID;
let origField = field;
fieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(itemTypeID, fieldID) || fieldID;
if (origFieldID != fieldID) {
field = Zotero.ItemFields.getName(fieldID);
}
if (!Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
extraFields.set(field, val);
delete item[field];
continue;
}
if (origFieldID != fieldID) {
item[field] = item[origField];
delete item[origField];
}
setFields.add(field);
}
}
}
if (item && item.tags) {
item.tags = Zotero.Utilities.arrayUnique(item.tags).sort();
if (extraFields.size) {
for (let field of setFields.keys()) {
let baseField;
if (Zotero.ItemFields.isBaseField(field)) {
baseField = field;
}
else if (Zotero.ItemFields.isValidForType(Zotero.ItemFields.getID(field), itemTypeID)) {
let baseFieldID = Zotero.ItemFields.getBaseIDFromTypeAndField(itemTypeID, field);
if (baseFieldID) {
baseField = baseFieldID;
}
}
if (baseField) {
let mappedFieldNames = Zotero.ItemFields.getTypeFieldsFromBase(baseField, true);
for (let mappedField of mappedFieldNames) {
if (extraFields.has(mappedField)) {
extraFields.delete(mappedField);
}
}
}
}
//
// Deduplicate remaining Extra fields
//
// For each invalid-for-type base field, remove any mapped fields with the same value
let baseFields = [];
for (let field of extraFields.keys()) {
if (Zotero.ItemFields.getID(field) && Zotero.ItemFields.isBaseField(field)) {
baseFields.push(field);
}
}
for (let baseField of baseFields) {
let value = extraFields.get(baseField);
let mappedFieldNames = Zotero.ItemFields.getTypeFieldsFromBase(baseField, true);
for (let mappedField of mappedFieldNames) {
if (extraFields.has(mappedField) && extraFields.get(mappedField) === value) {
extraFields.delete(mappedField);
}
}
}
// Remove Type-mapped fields from Extra, since 'Type' is mapped to Item Type by citeproc-js
// and Type values mostly aren't going to be useful for item types without a Type-mapped field.
let typeFieldNames = Zotero.ItemFields.getTypeFieldsFromBase('type', true)
.concat('audioFileType');
for (let typeFieldName of typeFieldNames) {
if (extraFields.has(typeFieldName)) {
extraFields.delete(typeFieldName);
}
}
}
if (extra || extraFields.size) {
item.extra = Zotero.Utilities.Internal.combineExtraFields(extra, extraFields);
}
return item;
}