Open Journal Systems  3.3.0
O4DOIXmlFilter.inc.php
1 <?php
2 
16 // Notification types
17 define('O4DOI_NOTIFICATION_TYPE_NEW', '06');
18 define('O4DOI_NOTIFICATION_TYPE_UPDATE', '07');
19 
20 // ID types
21 define('O4DOI_ID_TYPE_PROPRIETARY', '01');
22 define('O4DOI_ID_TYPE_DOI', '06');
23 define('O4DOI_ID_TYPE_ISSN', '07');
24 
25 // Text formats
26 define('O4DOI_TEXTFORMAT_ASCII', '00');
27 
28 // Title types
29 define('O4DOI_TITLE_TYPE_FULL', '01');
30 define('O4DOI_TITLE_TYPE_ISSUE', '07');
31 
32 // Publishing roles
33 define('O4DOI_PUBLISHING_ROLE_PUBLISHER', '01');
34 
35 // Product forms
36 define('O4DOI_PRODUCT_FORM_PRINT', 'JB');
37 define('O4DOI_PRODUCT_FORM_ELECTRONIC', 'JD');
38 
39 // ePublication formats
40 // S. ONIX List 11 (https://onix-codelists.io/codelist/11)
41 // We will consider only HTML and PDF
42 define('O4DOI_EPUB_FORMAT_HTML', '01');
43 define('O4DOI_EPUB_FORMAT_PDF', '02');
44 
45 // Date formats
46 define('O4DOI_DATE_FORMAT_YYYY', '06');
47 
48 // Extent types
49 define('O4DOI_EXTENT_TYPE_FILESIZE', '22');
50 
51 // Extent units
52 define('O4DOI_EXTENT_UNIT_BYTES', '17');
53 
54 // Contributor roles
55 define('O4DOI_CONTRIBUTOR_ROLE_ACTUAL_AUTHOR', 'A01');
56 
57 // Language roles
58 define('O4DOI_LANGUAGE_ROLE_LANGUAGE_OF_TEXT', '01');
59 
60 // Subject schemes
61 define('O4DOI_SUBJECT_SCHEME_PUBLISHER', '23');
62 define('O4DOI_SUBJECT_SCHEME_PROPRIETARY', '24');
63 
64 // Text type codes
65 define('O4DOI_TEXT_TYPE_MAIN_DESCRIPTION', '01');
66 
67 // Relation codes
68 define('O4DOI_RELATION_INCLUDES', '80');
69 define('O4DOI_RELATION_IS_PART_OF', '81');
70 define('O4DOI_RELATION_IS_A_NEW_VERSION_OF', '82');
71 define('O4DOI_RELATION_HAS_A_NEW_VERSION', '83');
72 define('O4DOI_RELATION_IS_A_DIFFERENT_FORM_OF', '84');
73 define('O4DOI_RELATION_IS_A_LANGUAGE_VERSION_OF', '85');
74 define('O4DOI_RELATION_IS_MANIFESTED_IN', '89');
75 define('O4DOI_RELATION_IS_A_MANIFESTATION_OF', '90');
76 
77 
78 import('lib.pkp.plugins.importexport.native.filter.NativeExportFilter');
79 
80 
86  function __construct($filterGroup) {
87  parent::__construct($filterGroup);
88  }
89 
96  function isWork($context, $plugin) {
97  return true;
98  }
99 
104  function getRootNodeName() {
105  assert(false);
106  }
107 
108  //
109  // Common filter functions
110  //
117  function createRootNode($doc, $rootNodeName) {
118  $deployment = $this->getDeployment();
119  $rootNode = $doc->createElementNS($deployment->getNamespace(), $rootNodeName);
120  $rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', $deployment->getXmlSchemaInstance());
121  $rootNode->setAttribute('xsi:schemaLocation', $deployment->getNamespace() . ' ' . $deployment->getSchemaFilename());
122  return $rootNode;
123  }
124 
130  function createHeadNode($doc) {
131  $deployment = $this->getDeployment();
132  $context = $deployment->getContext();
133  $plugin = $deployment->getPlugin();
134  $headNode = $doc->createElementNS($deployment->getNamespace(), 'Header');
135  $headNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'FromCompany', htmlspecialchars($plugin->getSetting($context->getId(), 'fromCompany'), ENT_COMPAT, 'UTF-8')));
136  $headNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'FromPerson', htmlspecialchars($plugin->getSetting($context->getId(), 'fromName'), ENT_COMPAT, 'UTF-8')));
137  $headNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'FromEmail', htmlspecialchars($plugin->getSetting($context->getId(), 'fromEmail'), ENT_COMPAT, 'UTF-8')));
138  $headNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'ToCompany', 'mEDRA'));
139  $headNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'SentDate', date('YmdHi')));
140  // Message note
141  $app = Application::get();
142  $name = $app->getName();
143  $version = $app->getCurrentVersion();
144  $versionString = $version->getVersionString();
145  $headNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'MessageNote', "This dataset was exported with $name, version $versionString."));
146  return $headNode;
147  }
148 
156  function createSerialPublicationNode($doc, $journalLocalePrecedence, $epubFormat = null) {
157  $deployment = $this->getDeployment();
158  $context = $deployment->getContext();
159  $plugin = $deployment->getPlugin();
160  $serialPublicationNode = $doc->createElementNS($deployment->getNamespace(), 'SerialPublication');
161  // Serial Work (mandatory)
162  $serialPublicationNode->appendChild($this->createSerialWorkNode($doc, $journalLocalePrecedence));
163  // Electronic Serial Version
164  $onlineIssn = $context->getData('onlineIssn');
165  $serialPublicationNode->appendChild($this->createSerialVersionNode($doc, $onlineIssn, O4DOI_PRODUCT_FORM_ELECTRONIC, $epubFormat));
166  // Print Serial Version
167  if (($printIssn = $context->getData('printIssn')) && $this->isWork($context, $plugin)) {
168  $serialPublicationNode->appendChild($this->createSerialVersionNode($doc, $printIssn, O4DOI_PRODUCT_FORM_PRINT, null));
169  }
170  return $serialPublicationNode;
171  }
172 
179  function createSerialWorkNode($doc, $journalLocalePrecedence) {
180  $deployment = $this->getDeployment();
181  $context = $deployment->getContext();
182  $plugin = $deployment->getPlugin();
183  $serialWorkNode = $doc->createElementNS($deployment->getNamespace(), 'SerialWork');
184  // Title (mandatory)
185  $journalTitles = $this->getTranslationsByPrecedence($context->getName(null), $journalLocalePrecedence);
186  assert(!empty($journalTitles));
187  foreach($journalTitles as $locale => $journalTitle) {
188  $serialWorkNode->appendChild($this->createTitleNode($doc, $locale, $journalTitle, O4DOI_TITLE_TYPE_FULL));
189  }
190  // Publisher
191  $serialWorkNode->appendChild($this->createPublisherNode($doc, $journalLocalePrecedence));
192  // Country of Publication (mandatory)
193  $serialWorkNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'CountryOfPublication', htmlspecialchars($plugin->getSetting($context->getId(), 'publicationCountry'), ENT_COMPAT, 'UTF-8')));
194  return $serialWorkNode;
195  }
196 
205  function createTitleNode($doc, $locale, $localizedTitle, $titleType) {
206  $deployment = $this->getDeployment();
207  $titleNode = $doc->createElementNS($deployment->getNamespace(), 'Title');
208  // Text format
209  $titleNode->setAttribute('textformat', O4DOI_TEXTFORMAT_ASCII);
210  // Language
211  $language = AppLocale::get3LetterIsoFromLocale($locale);
212  assert(!empty($language));
213  $titleNode->setAttribute('language', $language);
214  // Title type (mandatory)
215  $titleNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'TitleType', $titleType));
216  // Title text (mandatory)
217  $titleNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'TitleText', htmlspecialchars(PKPString::html2text($localizedTitle), ENT_COMPAT, 'UTF-8')));
218  return $titleNode;
219  }
220 
227  function createPublisherNode($doc, $journalLocalePrecedence) {
228  $deployment = $this->getDeployment();
229  $context = $deployment->getContext();
230  $publisherNode = $doc->createElementNS($deployment->getNamespace(), 'Publisher');
231  // Publishing role (mandatory)
232  $publisherNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'PublishingRole', O4DOI_PUBLISHING_ROLE_PUBLISHER));
233  // Publisher name (mandatory)
234  $publisher = $context->getData('publisherInstitution');
235  if (empty($publisher)) {
236  // Use the journal title if no publisher is set.
237  // This corresponds to the logic implemented for OAI interfaces, too.
238  $publisher = $this->getPrimaryTranslation($context->getName(null), $journalLocalePrecedence);
239  }
240  assert(!empty($publisher));
241  $publisherNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'PublisherName', htmlspecialchars($publisher, ENT_COMPAT, 'UTF-8')));
242  return $publisherNode;
243  }
244 
253  function createSerialVersionNode($doc, $issn, $productForm, $epubFormat = null) {
254  $deployment = $this->getDeployment();
255  $context = $deployment->getContext();
256  $serialVersionNode = $doc->createElementNS($deployment->getNamespace(), 'SerialVersion');
257  // Proprietary Journal Identifier
258  if ($productForm == O4DOI_PRODUCT_FORM_ELECTRONIC) {
259  $serialVersionNode->appendChild($this->createIdentifierNode($doc, 'Product', O4DOI_ID_TYPE_PROPRIETARY, $context->getId()));
260  }
261  // ISSN
262  if (!empty($issn)) {
263  $issn = PKPString::regexp_replace('/[^0-9]/', '', $issn);
264  $serialVersionNode->appendChild($this->createIdentifierNode($doc, 'Product', O4DOI_ID_TYPE_ISSN, $issn));
265  }
266  // Product Form
267  $serialVersionNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'ProductForm', $productForm));
268  if ($productForm == O4DOI_PRODUCT_FORM_ELECTRONIC) {
269  // ePublication Format
270  if ($epubFormat) $serialVersionNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'EpubFormat', $epubFormat));
271  // ePublication Format Description
272  $serialVersionNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'EpubFormatDescription', 'Open Journal Systems (OJS)'));
273  }
274  return $serialVersionNode;
275  }
276 
284  function createJournalIssueNode($doc, $issue, $journalLocalePrecedence) {
285  $deployment = $this->getDeployment();
286  $journalIssueNode = $doc->createElementNS($deployment->getNamespace(), 'JournalIssue');
287  // Volume
288  $volume = $issue->getVolume();
289  if (!empty($volume) && $issue->getShowVolume()) {
290  $journalIssueNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'JournalVolumeNumber', htmlspecialchars($volume, ENT_COMPAT, 'UTF-8')));
291  }
292  // Number
293  $number = $issue->getNumber();
294  if (!empty($number) && $issue->getShowNumber()) {
295  $journalIssueNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'JournalIssueNumber', htmlspecialchars($number, ENT_COMPAT, 'UTF-8')));
296  }
297  // Identification
298  $identification = $issue->getIssueIdentification();
299  if (!empty($identification)) {
300  $journalIssueNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'JournalIssueDesignation', htmlspecialchars($identification, ENT_COMPAT, 'UTF-8')));
301  }
302  assert(!(empty($number) && empty($identification)));
303  // Nominal Year
304  $year = (string) $issue->getYear();
305  $yearlen = strlen($year);
306  if ($issue->getShowYear() && !empty($year) && ($yearlen == 2 || $yearlen == 4)) {
307  $issueDateNode = $doc->createElementNS($deployment->getNamespace(), 'JournalIssueDate');
308  $issueDateNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'DateFormat', O4DOI_DATE_FORMAT_YYYY));
309  // Try to extend the year if necessary.
310  if ($yearlen == 2) {
311  // Assume that the issue date will never be
312  // more than one year in the future.
313  if ((int)$year <= (int)date('y')+1) {
314  $year = '20' . $year;
315  } else {
316  $year = '19' . $year;
317  }
318  }
319  $issueDateNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'Date', $year));
320  $journalIssueNode->appendChild($issueDateNode);
321  }
322  return $journalIssueNode;
323  }
324 
333  function createRelatedNode($doc, $workOrProduct, $relationCode, $ids) {
334  $deployment = $this->getDeployment();
335  $relatedNode = $doc->createElementNS($deployment->getNamespace(), "Related$workOrProduct");
336  // Relation code (mandatory)
337  $relatedNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'RelationCode', $relationCode));
338  // Work/Product ID (mandatory)
339  foreach($ids as $idType => $id) {
340  $relatedNode->appendChild($this->createIdentifierNode($doc, $workOrProduct, $idType, $id));
341  }
342  return $relatedNode;
343  }
344 
353  function createIdentifierNode($doc, $workOrProduct, $idType, $id) {
354  $deployment = $this->getDeployment();
355  $productIdentifierNode = $doc->createElementNS($deployment->getNamespace(), "${workOrProduct}Identifier");
356  // ID type (mandatory)
357  $productIdentifierNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), "${workOrProduct}IDType", $idType));
358  // ID (mandatory)
359  $productIdentifierNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'IDValue', $id));
360  return $productIdentifierNode;
361  }
362 
369  function createExtentNode($doc, $file) {
370  $deployment = $this->getDeployment();
371  $extentNode = $doc->createElementNS($deployment->getNamespace(), 'Extent');
372  // Extent type
373  $extentNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'ExtentType', O4DOI_EXTENT_TYPE_FILESIZE));
374  // Extent value
375  $extentNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'ExtentValue', $file->getFileSize()));
376  // Extent unit
377  $extentNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'ExtentUnit', O4DOI_EXTENT_UNIT_BYTES));
378  return $extentNode;
379  }
380 
388  function createOtherTextNode($doc, $locale, $description) {
389  $deployment = $this->getDeployment();
390  $otherTextNode = $doc->createElementNS($deployment->getNamespace(), 'OtherText');
391  // Text Type
392  $otherTextNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'TextTypeCode', O4DOI_TEXT_TYPE_MAIN_DESCRIPTION));
393  // Text
394  $language = AppLocale::get3LetterIsoFromLocale($locale);
395  assert(!empty($language));
396  $otherTextNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'Text', htmlspecialchars(PKPString::html2text($description), ENT_COMPAT, 'UTF-8')));
397  $node->setAttribute('textformat', O4DOI_TEXTFORMAT_ASCII);
398  $node->setAttribute('language', $language);
399  return $otherTextNode;
400  }
401 
402  //
403  // Helper functions
404  //
409  function getDOIStructuralType() {
410  $deployment = $this->getDeployment();
411  $context = $deployment->getContext();
412  $plugin = $deployment->getPlugin();
413  if ($this->isWork($context, $plugin)) {
414  return 'Abstraction';
415  } else {
416  return 'DigitalFixation';
417  }
418  }
419 
428  function getObjectLocalePrecedence($context, $article, $galley) {
429  $locales = array();
430  if (is_a($galley, 'ArticleGalley') && AppLocale::isLocaleValid($galley->getLocale())) {
431  $locales[] = $galley->getLocale();
432  }
433  if (is_a($article, 'Submission')) {
434  // First try to translate the article language into a locale.
435  $articleLocale = $this->translateLanguageToLocale($article->getLanguage());
436  if (!is_null($articleLocale)) {
437  $locales[] = $articleLocale;
438  }
439 
440  // Use the article locale as fallback only
441  // as this is the primary locale of article meta-data, not
442  // necessarily of the article itself.
443  if(AppLocale::isLocaleValid($article->getLocale())) {
444  $locales[] = $article->getLocale();
445  }
446  }
447 
448  // Use the journal locale as fallback.
449  $locales[] = $context->getPrimaryLocale();
450 
451  // Use form locales as fallback.
452  $formLocales = array_keys($context->getSupportedFormLocaleNames());
453  // Sort form locales alphabetically so that
454  // we get a well-defined order.
455  sort($formLocales);
456  foreach($formLocales as $formLocale) {
457  if (!in_array($formLocale, $locales)) $locales[] = $formLocale;
458  }
459 
460  assert(!empty($locales));
461  return $locales;
462  }
463 
470  function translateLanguageToLocale($language) {
471  $locale = null;
472  if (strlen($language) == 2) {
473  $language = AppLocale::get3LetterFrom2LetterIsoLanguage($language);
474  }
475  if (strlen($language) == 3) {
476  $language = AppLocale::getLocaleFrom3LetterIso($language);
477  }
478  if (AppLocale::isLocaleValid($language)) {
479  $locale = $language;
480  }
481  return $locale;
482  }
483 
494  function getPrimaryTranslation($localizedData, $localePrecedence) {
495  // Check whether we have localized data at all.
496  if (!is_array($localizedData) || empty($localizedData)) return null;
497 
498  // Try all locales from the precedence list first.
499  foreach($localePrecedence as $locale) {
500  if (isset($localizedData[$locale]) && !empty($localizedData[$locale])) {
501  return $localizedData[$locale];
502  }
503  }
504 
505  // As a fallback: use any translation by alphabetical
506  // order of locales.
507  ksort($localizedData);
508  foreach($localizedData as $locale => $value) {
509  if (!empty($value)) return $value;
510  }
511 
512  // If we found nothing (how that?) return null.
513  return null;
514  }
515 
524  function getTranslationsByPrecedence($localizedData, $localePrecedence) {
525  $reorderedLocalizedData = array();
526 
527  // Check whether we have localized data at all.
528  if (!is_array($localizedData) || empty($localizedData)) return $reorderedLocalizedData;
529 
530  // Order by explicit locale precedence first.
531  foreach($localePrecedence as $locale) {
532  if (isset($localizedData[$locale]) && !empty($localizedData[$locale])) {
533  $reorderedLocalizedData[$locale] = $localizedData[$locale];
534  }
535  unset($localizedData[$locale]);
536  }
537 
538  // Order any remaining values alphabetically by locale
539  // and amend the re-ordered array.
540  ksort($localizedData);
541  $reorderedLocalizedData = array_merge($reorderedLocalizedData, $localizedData);
542 
543  return $reorderedLocalizedData;
544  }
545 
546 }
547 
548 
O4DOIXmlFilter\createJournalIssueNode
createJournalIssueNode($doc, $issue, $journalLocalePrecedence)
Definition: O4DOIXmlFilter.inc.php:284
O4DOIXmlFilter\translateLanguageToLocale
translateLanguageToLocale($language)
Definition: O4DOIXmlFilter.inc.php:470
PKPString\regexp_replace
static regexp_replace($pattern, $replacement, $subject, $limit=-1)
Definition: PKPString.inc.php:279
O4DOIXmlFilter\getRootNodeName
getRootNodeName()
Definition: O4DOIXmlFilter.inc.php:104
O4DOIXmlFilter\createSerialVersionNode
createSerialVersionNode($doc, $issn, $productForm, $epubFormat=null)
Definition: O4DOIXmlFilter.inc.php:253
O4DOIXmlFilter\createExtentNode
createExtentNode($doc, $file)
Definition: O4DOIXmlFilter.inc.php:369
O4DOIXmlFilter\createOtherTextNode
createOtherTextNode($doc, $locale, $description)
Definition: O4DOIXmlFilter.inc.php:388
O4DOIXmlFilter\getTranslationsByPrecedence
getTranslationsByPrecedence($localizedData, $localePrecedence)
Definition: O4DOIXmlFilter.inc.php:524
O4DOIXmlFilter\getDOIStructuralType
getDOIStructuralType()
Definition: O4DOIXmlFilter.inc.php:409
PKPLocale\get3LetterIsoFromLocale
static get3LetterIsoFromLocale($locale)
Definition: PKPLocale.inc.php:651
O4DOIXmlFilter\createSerialWorkNode
createSerialWorkNode($doc, $journalLocalePrecedence)
Definition: O4DOIXmlFilter.inc.php:179
O4DOIXmlFilter\createHeadNode
createHeadNode($doc)
Definition: O4DOIXmlFilter.inc.php:130
O4DOIXmlFilter
Basis class for converting objects (issues, articles, galleys) to a O4DOI XML document.
Definition: O4DOIXmlFilter.inc.php:81
NativeImportExportFilter\getDeployment
getDeployment()
Definition: NativeImportExportFilter.inc.php:49
O4DOIXmlFilter\getPrimaryTranslation
getPrimaryTranslation($localizedData, $localePrecedence)
Definition: O4DOIXmlFilter.inc.php:494
O4DOIXmlFilter\createRootNode
createRootNode($doc, $rootNodeName)
Definition: O4DOIXmlFilter.inc.php:117
NativeExportFilter
Base class that converts a DataObject to a Native XML document.
Definition: NativeExportFilter.inc.php:18
PKPLocale\getLocaleFrom3LetterIso
static getLocaleFrom3LetterIso($iso3Letter)
Definition: PKPLocale.inc.php:671
PKPLocale\get3LetterFrom2LetterIsoLanguage
static get3LetterFrom2LetterIsoLanguage($iso2Letter)
Definition: PKPLocale.inc.php:614
PKPString\html2text
static html2text($html)
Definition: PKPString.inc.php:395
O4DOIXmlFilter\isWork
isWork($context, $plugin)
Definition: O4DOIXmlFilter.inc.php:96
O4DOIXmlFilter\createSerialPublicationNode
createSerialPublicationNode($doc, $journalLocalePrecedence, $epubFormat=null)
Definition: O4DOIXmlFilter.inc.php:156
O4DOIXmlFilter\getObjectLocalePrecedence
getObjectLocalePrecedence($context, $article, $galley)
Definition: O4DOIXmlFilter.inc.php:428
O4DOIXmlFilter\__construct
__construct($filterGroup)
Definition: O4DOIXmlFilter.inc.php:86
O4DOIXmlFilter\createTitleNode
createTitleNode($doc, $locale, $localizedTitle, $titleType)
Definition: O4DOIXmlFilter.inc.php:205
O4DOIXmlFilter\createIdentifierNode
createIdentifierNode($doc, $workOrProduct, $idType, $id)
Definition: O4DOIXmlFilter.inc.php:353
PKPApplication\get
static get()
Definition: PKPApplication.inc.php:235
PKPLocale\isLocaleValid
static isLocaleValid($locale)
Definition: PKPLocale.inc.php:505
O4DOIXmlFilter\createRelatedNode
createRelatedNode($doc, $workOrProduct, $relationCode, $ids)
Definition: O4DOIXmlFilter.inc.php:333
O4DOIXmlFilter\createPublisherNode
createPublisherNode($doc, $journalLocalePrecedence)
Definition: O4DOIXmlFilter.inc.php:227