diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index 060d1d2f14..920a2b5d3c 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -2186,6 +2186,10 @@ Zotero.Translate.Export.prototype._prepareTranslation = function() { // initialize ItemGetter this._itemGetter = new Zotero.Translate.ItemGetter(); + + // Toggle legacy mode for translators pre-4.0.27 + this._itemGetter.legacy = Services.vc.compare('4.0.27', this._translatorInfo.minVersion) > 0; + var configOptions = this._translatorInfo.configOptions || {}, getCollections = configOptions.getCollections || false; switch (this._export.type) { diff --git a/chrome/content/zotero/xpcom/translation/translate_item.js b/chrome/content/zotero/xpcom/translation/translate_item.js index 616040b870..68d0c18bb1 100644 --- a/chrome/content/zotero/xpcom/translation/translate_item.js +++ b/chrome/content/zotero/xpcom/translation/translate_item.js @@ -748,6 +748,7 @@ Zotero.Translate.ItemGetter = function() { this._itemsLeft = null; this._collectionsLeft = null; this._exportFileDirectory = null; + this.legacy = false; }; Zotero.Translate.ItemGetter.prototype = { @@ -828,13 +829,8 @@ Zotero.Translate.ItemGetter.prototype = { * Converts an attachment to array format and copies it to the export folder if desired */ "_attachmentToArray":function(attachment) { - var attachmentArray = this._itemToArray(attachment); + var attachmentArray = Zotero.Utilities.Internal.itemToExportFormat(attachment, this.legacy); var linkMode = attachment.attachmentLinkMode; - - // Get mime type - attachmentArray.mimeType = attachmentArray.uniqueFields.mimeType = attachment.attachmentMIMEType; - // Get charset - attachmentArray.charset = attachmentArray.uniqueFields.charset = attachment.attachmentCharset; if(linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { var attachFile = attachment.getFile(); attachmentArray.localPath = attachFile.path; @@ -845,7 +841,7 @@ Zotero.Translate.ItemGetter.prototype = { // Add path and filename if not an internet link var attachFile = attachment.getFile(); if(attachFile) { - attachmentArray.defaultPath = "files/" + attachmentArray.itemID + "/" + attachFile.leafName; + attachmentArray.defaultPath = "files/" + attachment.id + "/" + attachFile.leafName; attachmentArray.filename = attachFile.leafName; /** @@ -959,39 +955,8 @@ Zotero.Translate.ItemGetter.prototype = { } } - attachmentArray.itemType = "attachment"; - return attachmentArray; }, - - /** - * Converts an item to array format - */ - "_itemToArray":function(returnItem) { - // TODO use Zotero.Item#serialize() - var returnItemArray = returnItem.toArray(); - - // Remove SQL date from multipart dates - if (returnItemArray.date) { - returnItemArray.date = Zotero.Date.multipartToStr(returnItemArray.date); - } - - var returnItemArray = Zotero.Utilities.itemToExportFormat(returnItemArray); - - // TODO: Change tag.tag references in translators to tag.name - // once translators are 1.5-only - // TODO: Preserve tag type? - if (returnItemArray.tags) { - for (var i in returnItemArray.tags) { - returnItemArray.tags[i].tag = returnItemArray.tags[i].fields.name; - } - } - - // add URI - returnItemArray.uri = Zotero.URI.getItemURI(returnItem); - - return returnItemArray; - }, /** * Retrieves the next available item @@ -1004,10 +969,10 @@ Zotero.Translate.ItemGetter.prototype = { var returnItemArray = this._attachmentToArray(returnItem); if(returnItemArray) return returnItemArray; } else { - var returnItemArray = this._itemToArray(returnItem); + var returnItemArray = Zotero.Utilities.Internal.itemToExportFormat(returnItem, this.legacy); // get attachments, although only urls will be passed if exportFileData is off - returnItemArray.attachments = new Array(); + returnItemArray.attachments = []; var attachments = returnItem.getAttachments(); for each(var attachmentID in attachments) { var attachment = Zotero.Items.get(attachmentID); diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js index eec16d1bb1..8ee2cec4a7 100644 --- a/chrome/content/zotero/xpcom/utilities.js +++ b/chrome/content/zotero/xpcom/utilities.js @@ -61,7 +61,7 @@ const CSL_TEXT_MAPPINGS = { "number-of-volumes":["numberOfVolumes"], "number-of-pages":["numPages"], "edition":["edition"], - "version":["version"], + "version":["versionNumber"], "section":["section", "committee"], "genre":["type", "programmingLanguage"], "source":["libraryCatalog"], @@ -133,7 +133,10 @@ const CSL_TYPE_MAPPINGS = { 'tvBroadcast':"broadcast", 'radioBroadcast':"broadcast", 'podcast':"song", // ?? - 'computerProgram':"book" // ?? + 'computerProgram':"book", // ?? + 'document':"article", + 'note':"article", + 'attachment':"article" }; /** @@ -1345,49 +1348,6 @@ Zotero.Utilities = { return dumpedText; }, - /** - * Adds all fields to an item in toArray() format and adds a unique (base) fields to - * uniqueFields array - */ - "itemToExportFormat":function(item) { - const CREATE_ARRAYS = ['creators', 'notes', 'tags', 'seeAlso', 'attachments']; - for(var i=0; iISSN:1234\xA0-\t5679(print)\neISSN (electronic):0028-0836'), '1234-5679'); }); }); + describe("itemToCSLJSON", function() { + it("should accept Zotero.Item and Zotero export item format", function() { + let data = populateDBWithSampleData(loadSampleData('journalArticle')); + let item = Zotero.Items.get(data.journalArticle.id); + + let fromZoteroItem; + try { + fromZoteroItem = Zotero.Utilities.itemToCSLJSON(item); + } catch(e) { + assert.fail(e, null, 'accepts Zotero Item'); + } + assert.isObject(fromZoteroItem, 'converts Zotero Item to object'); + assert.isNotNull(fromZoteroItem, 'converts Zotero Item to non-null object'); + + + let fromExportItem; + try { + fromExportItem = Zotero.Utilities.itemToCSLJSON( + Zotero.Utilities.Internal.itemToExportFormat(item) + ); + } catch(e) { + assert.fail(e, null, 'accepts Zotero export item'); + } + assert.isObject(fromExportItem, 'converts Zotero export item to object'); + assert.isNotNull(fromExportItem, 'converts Zotero export item to non-null object'); + + assert.deepEqual(fromZoteroItem, fromExportItem, 'conversion from Zotero Item and from export item are the same'); + }); + it("should convert standalone notes to expected format", function() { + let note = new Zotero.Item('note'); + note.setNote('Some note longer than 50 characters, which will become the title.'); + note = Zotero.Items.get(note.save()); + + let cslJSONNote = Zotero.Utilities.itemToCSLJSON(note); + assert.equal(cslJSONNote.type, 'article', 'note is exported as "article"'); + }); + it("should convert standalone attachments to expected format", function() { + let file = getTestDataDirectory(); + file.append("empty.pdf"); + + let attachment = Zotero.Items.get(Zotero.Attachments.importFromFile(file)); + attachment.setField('title', 'Empty'); + attachment.setField('accessDate', '2001-02-03 12:13:14'); + attachment.setField('url', 'http://example.com'); + attachment.setNote('Note'); + + attachment.save(); + + cslJSONAttachment = Zotero.Utilities.itemToCSLJSON(attachment); + assert.equal(cslJSONAttachment.type, 'article', 'attachment is exported as "article"'); + assert.equal(cslJSONAttachment.title, 'Empty', 'attachment title is correct'); + assert.deepEqual(cslJSONAttachment.accessed, {"date-parts":[["2001",2,3]]}, 'attachment access date is mapped correctly'); + }); + it("should refuse to convert unexpected item types", function() { + let data = populateDBWithSampleData(loadSampleData('journalArticle')); + let item = Zotero.Items.get(data.journalArticle.id); + + let exportFormat = Zotero.Utilities.Internal.itemToExportFormat(item); + exportFormat.itemType = 'foo'; + + assert.throws(Zotero.Utilities.itemToCSLJSON.bind(Zotero.Utilities, exportFormat), /^Unexpected Zotero Item type ".*"$/, 'throws an error when trying to map invalid item types'); + }); + it("should map additional fields from Extra field", function() { + let item = new Zotero.Item('journalArticle'); + item.setField('extra', 'PMID: 12345\nPMCID:123456'); + item = Zotero.Items.get(item.save()); + + let cslJSON = Zotero.Utilities.itemToCSLJSON(item); + + assert.equal(cslJSON.PMID, '12345', 'PMID from Extra is mapped to PMID'); + assert.equal(cslJSON.PMCID, '123456', 'PMCID from Extra is mapped to PMCID'); + + item.setField('extra', 'PMID: 12345'); + item.save(); + cslJSON = Zotero.Utilities.itemToCSLJSON(item); + + assert.equal(cslJSON.PMID, '12345', 'single-line entry is extracted correctly'); + + item.setField('extra', 'some junk: note\nPMID: 12345\nstuff in-between\nPMCID: 123456\nlast bit of junk!'); + item.save(); + cslJSON = Zotero.Utilities.itemToCSLJSON(item); + + assert.equal(cslJSON.PMID, '12345', 'PMID from mixed Extra field is mapped to PMID'); + assert.equal(cslJSON.PMCID, '123456', 'PMCID from mixed Extra field is mapped to PMCID'); + + item.setField('extra', 'a\n PMID: 12345\nfoo PMCID: 123456'); + item.save(); + cslJSON = Zotero.Utilities.itemToCSLJSON(item); + + assert.isUndefined(cslJSON.PMCID, 'field label must not be preceded by other text'); + assert.isUndefined(cslJSON.PMID, 'field label must not be preceded by a space'); + assert.equal(cslJSON.note, 'a\n PMID: 12345\nfoo PMCID: 123456', 'note is left untouched if nothing is extracted'); + + item.setField('extra', 'something\npmid: 12345\n'); + item.save(); + cslJSON = Zotero.Utilities.itemToCSLJSON(item); + + assert.isUndefined(cslJSON.PMID, 'field labels are case-sensitive'); + }); + }); });