28 $article = $record->getData(
'article');
29 $galleys = $record->getData(
'galleys');
31 import(
'lib.pkp.classes.submission.SubmissionFile');
33 $candidateFiles = array();
36 foreach ($galleys as $galley) {
37 $galleyFiles = $submissionFileDao->getLatestRevisionsByAssocId(ASSOC_TYPE_GALLEY, $galley->getId(), $article->getId(), SUBMISSION_FILE_PROOF);
38 foreach ($galleyFiles as $galleyFile) {
44 if (empty($candidateFiles)) {
45 $layoutFiles = $submissionFileDao->getLatestRevisions($article->getId(), SUBMISSION_FILE_PRODUCTION_READY);
46 foreach ($layoutFiles as $layoutFile) {
52 HookRegistry::call(
'OAIMetadataFormat_JATS::findJats', array(&$this, &$record, &$candidateFiles, &$doc));
55 if (!$doc && empty($candidateFiles)) {
58 if (count($candidateFiles) > 1) error_log(
'WARNING: More than one JATS XML candidate documents were located for submission ' . $article->getId() .
'.');
62 $candidateFile = array_shift($candidateFiles);
63 $doc =
new DOMDocument;
64 $doc->loadXML(file_get_contents($candidateFile->getFilePath()));
73 function toXml($record, $format =
null) {
75 $journal = $record->getData(
'journal');
76 $article = $record->getData(
'article');
77 $section = $record->getData(
'section');
78 $issue = $record->getData(
'issue');
81 import(
'classes.issue.IssueAction');
83 $subscriptionRequired = $issueAction->subscriptionRequired($issue, $journal);
84 $isSubscribedDomain = $issueAction->subscribedDomain(
Application::get()->getRequest(), $journal, $issue->getId(), $article->getId());
85 if ($subscriptionRequired && !$isSubscribedDomain) {
86 $oaiDao->oai->error(
'cannotDisseminateFormat',
'Cannot disseminate format (JATS XML not available)');
92 $oaiDao->oai->error(
'cannotDisseminateFormat',
'Cannot disseminate format (JATS XML not available)');
97 return $doc->saveXml($doc->getElementsByTagName(
'article')->item(0));
107 $permittedElementOrders = array(
108 'front' => array(
'article-meta',
'journal-meta'),
109 'article-meta' => array(
'article-id',
'article-categories',
'title-group',
'contrib-group',
'aff',
'aff-alternatives',
'x',
'author-notes',
'pub-date',
'volume',
'volume-id',
'volume-series',
'issue',
'issue-id',
'issue-title',
'issue-sponsor',
'issue-part',
'isbn',
'supplement',
'fpage',
'lpage',
'page-range',
'elocation-id',
'email',
'ext-link',
'uri',
'product',
'supplementary-material',
'history',
'permissions',
'self-uri',
'related-article',
'related-object',
'abstract',
'trans-abstract',
'kwd-group',
'funding-group',
'conference',
'counts',
'custom-meta-group'),
110 'journal-meta' => array(
'journal-id',
'journal-title-group',
'contrib-group',
'aff',
'aff-alternatives',
'issn',
'issn-l',
'isbn',
'publisher',
'notes',
'self-uri',
'custom-meta-group'),
111 'counts' => array(
'count',
'fig-count',
'table-count',
'equation-count',
'ref-count',
'page-count',
'word-count'),
114 assert(isset($permittedElementOrders[$parentNode->nodeName]));
115 $order = $permittedElementOrders[$parentNode->nodeName];
116 $position = array_search($childNode->nodeName, $order);
117 assert($position !==
false);
119 $followingElements = array_slice($order, $position);
120 $followingElement =
null;
121 foreach ($parentNode->childNodes as $node) {
122 if (in_array($node->nodeName, $followingElements)) {
123 $followingElement = $node;
128 return $parentNode->insertBefore($childNode, $followingElement);
140 $xpath =
new DOMXPath($doc);
141 $articleMetaNode = $xpath->query(
'//article/front/article-meta')->item(0);
142 $journalMetaNode = $xpath->query(
'//article/front/journal-meta')->item(0);
143 if (!$journalMetaNode) {
144 $frontNode = $xpath->query(
'//article/front')->item(0);
145 $journalMetaNode = $this->
_addChildInOrder($frontNode, $doc->createElement(
'journal-meta'));
149 $publication = $article->getCurrentPublication();
151 $articleNode = $xpath->query(
'//article')->item(0);
152 $articleNode->setAttribute(
'xml:lang', substr($article->getLocale(),0,2));
153 $articleNode->setAttribute(
'dtd-version',
'1.1');
154 $articleNode->setAttribute(
'specific-use',
'eps-0.1');
155 $articleNode->setAttribute(
'xmlns',
'https://jats.nlm.nih.gov/publishing/1.1/');
158 if ($datePublished = $article->getDatePublished()) {
159 $datePublished = strtotime($datePublished);
160 $match = $xpath->query(
"//article/front/article-meta/pub-date[@date-type='pub' and @publication-format='epub']");
161 if ($match->length) {
163 $dateNode = $match->item(0);
164 while ($dateNode->hasChildNodes()) $dateNode->removeChild($dateNode->firstChild);
167 $dateNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'pub-date'));
168 $dateNode->setAttribute(
'date-type',
'pub');
169 $dateNode->setAttribute(
'publication-format',
'epub');
172 $dateNode->appendChild($doc->createElement(
'day'))->nodeValue = strftime(
'%d', $datePublished);
173 $dateNode->appendChild($doc->createElement(
'month'))->nodeValue = strftime(
'%m', $datePublished);
174 $dateNode->appendChild($doc->createElement(
'year'))->nodeValue = strftime(
'%Y', $datePublished);
179 if ($issue && $issue->getShowYear()) $issueYear = $issue->getYear();
180 if (!$issueYear && $issue && $issue->getDatePublished()) $issueYear = strftime(
'%Y', strtotime($issue->getDatePublished()));
181 if (!$issueYear && $datePublished) $issueYear = strftime(
'%Y', $datePublished);
183 $match = $xpath->query(
"//article/front/article-meta/pub-date[@date-type='collection']");
184 if ($match->length) {
186 $dateNode = $match->item(0);
187 while ($dateNode->hasChildNodes()) $dateNode->removeChild($dateNode->firstChild);
190 $dateNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'pub-date'));
191 $dateNode->setAttribute(
'date-type',
'collection');
193 $dateNode->appendChild($doc->createElement(
'year'))->nodeValue = $issueYear;
197 $match = $xpath->query(
'//article/front/article-meta/self-uri');
198 foreach ($match as $node) $articleMetaNode->removeChild($node);
201 $uriNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'self-uri'));
202 $uriNode->setAttribute(
'xlink:href', $request->url(
null,
'article',
'view', $article->getBestArticleId()));
205 foreach ($article->getGalleys() as $galley) {
206 $uriNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'self-uri'));
207 $uriNode->setAttribute(
'xlink:href', $request->url(
null,
'article',
'view', array($article->getBestArticleId(), $galley->getId())));
208 if (!$galley->getData(
'urlRemote')) $uriNode->setAttribute(
'content-type', $galley->getFileType());
212 if ($issue && $issue->getShowVolume()) {
213 $match = $xpath->query(
'//article/front/article-meta/volume');
214 if ($match->length) $volumeNode = $match->item(0);
215 else $volumeNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'volume'));
216 $volumeNode->nodeValue = $issue->getVolume();
220 if ($issue && $issue->getShowNumber()) {
221 $match = $xpath->query(
'//article/front/article-meta/issue');
222 if ($match->length) $numberNode = $match->item(0);
223 else $numberNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'issue'));
224 $numberNode->nodeValue = $issue->getNumber();
228 if ($issue && $issue->getShowTitle()) {
229 $match = $xpath->query(
'//article/front/article-meta/issue-title');
230 foreach ($match as $node) $articleMetaNode->removeChild($node);
231 foreach ($issue->getTitle(
null) as $locale => $title) {
232 if (empty($title))
continue;
233 $titleNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'issue-title'));
234 $titleText = $doc->createTextNode($title);
235 $titleNode->appendChild($titleText);
236 $titleNode->setAttribute(
'xml:lang', substr($locale,0,2));
242 $titleGroupNode = $xpath->query(
'//article/front/article-meta/title-group')->item(0);
243 while ($titleGroupNode->hasChildNodes()) $titleGroupNode->removeChild($titleGroupNode->firstChild);
244 $titleNode = $titleGroupNode->appendChild($doc->createElement(
'article-title'));
245 $titleNode->setAttribute(
'xml:lang', substr($article->getLocale(),0,2));
246 $articleTitleText = $doc->createTextNode($article->getTitle($article->getLocale()));
247 $titleNode->appendChild($articleTitleText);
248 if (!empty($subtitle = $article->getSubtitle($article->getLocale()))) {
249 $subtitleText = $doc->createTextNode($subtitle);
250 $subtitleNode = $titleGroupNode->appendChild($doc->createElement(
'subtitle'));
251 $subtitleNode->setAttribute(
'xml:lang', substr($article->getLocale(),0,2));
252 $subtitleNode->appendChild($subtitleText);
254 foreach ($article->getTitle(
null) as $locale => $title) {
255 if ($locale == $article->getLocale())
continue;
256 if (trim($title) ===
'')
continue;
257 $transTitleGroupNode = $titleGroupNode->appendChild($doc->createElement(
'trans-title-group'));
258 $transTitleGroupNode->setAttribute(
'xml:lang', substr($locale,0,2));
259 $titleNode = $transTitleGroupNode->appendChild($doc->createElement(
'trans-title'));
260 $titleText = $doc->createTextNode($title);
261 $titleNode->appendChild($titleText);
262 if (!empty($subtitle = $article->getSubtitle($locale))) {
263 $subtitleNode = $transTitleGroupNode->appendChild($doc->createElement(
'trans-subtitle'));
264 $subtitleText = $doc->createTextNode($subtitle);
265 $subtitleNode->appendChild($subtitleText);
270 $keywordGroupNode = $xpath->query(
'//article/front/article-meta/kwd-group')->item(0);
272 foreach ($articleMetaNode->getElementsByTagName(
'kwd-group') as $kwdGroupNode) $articleMetaNode->removeChild($kwdGroupNode);
273 foreach ($submissionKeywordDao->getKeywords($publication->getId(), $journal->getSupportedLocales()) as $locale => $keywords) {
274 if (empty($keywords))
continue;
279 $kwdGroupNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'kwd-group'));
280 $kwdGroupNode->setAttribute(
'xml:lang', substr($locale,0,2));
281 $kwdGroupNode->appendChild($doc->createElement(
'title'))->nodeValue = __(
'article.subject', array(), $locale);
282 foreach ($keywords as $keyword) $kwdGroupNode->appendChild($doc->createElement(
'kwd'))->nodeValue = $keyword;
288 $config = HTMLPurifier_Config::createDefault();
289 $config->set(
'HTML.Allowed',
'p');
290 $config->set(
'Cache.SerializerPath',
'cache');
291 $purifier =
new HTMLPurifier($config);
293 foreach ($articleMetaNode->getElementsByTagName(
'abstract') as $abstractNode) $articleMetaNode->removeChild($abstractNode);
294 foreach ((array) $publication->getData(
'abstract') as $locale => $abstract) {
295 if (empty($abstract))
continue;
296 $isPrimary = $locale == $article->getLocale();
297 $abstractDoc =
new DOMDocument;
298 if (strpos($abstract,
'<p>')===
null) $abstract =
"<p>$abstract</p>";
299 $abstractDoc->loadXML(($isPrimary?
'<abstract>':
'<trans-abstract>') . $purifier->purify($abstract) . ($isPrimary?
'</abstract>':
'</trans-abstract>'));
300 $abstractNode = $this->
_addChildInOrder($articleMetaNode, $doc->importNode($abstractDoc->documentElement,
true));
301 if (!$isPrimary) $abstractNode->setAttribute(
'xml:lang', substr($locale,0,2));
305 $match = $xpath->query(
"//article/front/journal-meta/journal-id[@journal-id-type='publisher']");
306 if ($match->length) $match->item(0)->nodeValue = $journal->getPath();
308 $journalIdNode = $this->
_addChildInOrder($journalMetaNode, $doc->createElement(
'journal-id'));
309 $journalIdNode->setAttribute(
'journal-id-type',
'publisher');
310 $journalIdNode->nodeValue = $journal->getPath();
314 if ($doi = trim($article->getStoredPubId(
'doi'))) {
315 $match = $xpath->query(
"//article/front/article-meta/article-id[@pub-id-type='doi']");
316 if ($match->length) $match->item(0)->nodeValue = $doi;
318 $articleIdNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'article-id'));
319 $articleIdNode->setAttribute(
'pub-id-type',
'doi');
320 $articleIdNode->nodeValue = $doi;
325 $match = $xpath->query(
'//article/front/article-meta/permissions');
326 $copyrightHolder = $article->getLocalizedCopyrightHolder($article->getLocale());
327 $copyrightYear = $article->getCopyrightYear();
328 $licenseUrl = $article->getLicenseURL();
329 if (!$match->length && ($copyrightHolder || $copyrightYear || $licenseUrl)) {
330 $permissionsNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'permissions'));
331 if ($copyrightYear || $copyrightHolder) $permissionsNode->appendChild($doc->createElement(
'copyright-statement'))->nodeValue = __(
'submission.copyrightStatement', array(
'copyrightYear' => $copyrightYear,
'copyrightHolder' => $copyrightHolder));
332 if ($copyrightYear) $permissionsNode->appendChild($doc->createElement(
'copyright-year'))->nodeValue = $copyrightYear;
333 if ($copyrightHolder) $permissionsNode->appendChild($doc->createElement(
'copyright-holder'))->nodeValue = $copyrightHolder;
335 $licenseNode = $permissionsNode->appendChild($doc->createElement(
'license'));
336 $licenseNode->setAttribute(
'xlink:href', $licenseUrl);
341 $match = $xpath->query(
"//article/front/article-meta/article-categories");
342 if ($match->length) $articleCategoriesNode = $match->item(0);
344 $articleCategoriesNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'article-categories'));
346 while ($articleCategoriesNode->hasChildNodes()) $articleCategoriesNode->removeChild($articleCategoriesNode->firstChild);
347 foreach ($section->getTitle(
null) as $locale => $title) {
348 if (empty($title))
continue;
349 $subjGroupNode = $articleCategoriesNode->appendChild($doc->createElement(
'subj-group'));
350 $subjGroupNode->setAttribute(
'subj-group-type',
'heading');
351 $subjGroupNode->setAttribute(
'xml:lang', substr($locale,0,2));
352 $subjectNode = $subjGroupNode->appendChild($doc->createElement(
'subject'));
353 $subjectNode->nodeValue = $title;
357 import(
'classes.submission.Submission');
358 $publishedArticles = iterator_to_array(
Services::get(
'submission')->getMany([
359 'contextId' => $journal->getId(),
360 'issueIds' => [$issue->getId()],
361 'status' => STATUS_PUBLISHED,
363 $articleIds = array_map(
function($publishedArticle) {
364 return $publishedArticle->getId();
365 }, $publishedArticles);
366 foreach (array(
'volume',
'issue') as $nodeName) {
367 $match = $xpath->query(
"//article/front/article-meta/$nodeName");
368 if ($match->length) {
369 $match->item(0)->setAttribute(
'seq', array_search($article->getId(), $articleIds)+1);
375 $match = $xpath->query(
"//article/front/article-meta/issue-id");
376 if ($match->length) $match->item(0)->nodeValue = $issue->getId();
378 $issueIdNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'issue-id'));
379 $issueIdNode->nodeValue = $issue->getId();
383 if ($coverUrl = $issue->getLocalizedCoverImageUrl()) {
384 $customMetaGroupNode = $this->
_addChildInOrder($articleMetaNode, $doc->createElement(
'custom-meta-group'));
385 $customMetaNode = $customMetaGroupNode->appendChild($doc->createElement(
'custom-meta'));
386 $metaNameNode = $customMetaNode->appendChild($doc->createElement(
'meta-name'));
387 $metaNameNode->nodeValue =
'issue-cover';
388 $metaValueNode = $customMetaNode->appendChild($doc->createElement(
'meta-value'));
389 $inlineGraphicNode = $metaValueNode->appendChild($doc->createElement(
'inline-graphic'));
390 $inlineGraphicNode->setAttribute(
'xmlns:xlink',
'http://www.w3.org/1999/xlink');
391 $inlineGraphicNode->setAttribute(
'xlink:href', $coverUrl);
395 if ($articleType = strtolower(trim($section->getLocalizedIdentifyType()))) {
396 $articleNode = $xpath->query(
"//article")->item(0);
397 $articleNode->setAttribute(
'article-type', $articleType);
402 $userGroups = $userGroupDao->getByContextId($journal->getId());
403 $journalMetaNode = $xpath->query(
'//article/front/journal-meta')->item(0);
404 $contribGroupNode = $this->
_addChildInOrder($journalMetaNode, $doc->createElement(
'contrib-group'));
405 $keyContribTypeMapping = array(
406 'default.groups.name.manager' =>
'jmanager',
407 'default.groups.name.editor' =>
'editor',
408 'default.groups.name.sectionEditor' =>
'secteditor',
410 while ($userGroup = $userGroups->next()) {
411 if (!isset($keyContribTypeMapping[$userGroup->getData(
'nameLocaleKey')]))
continue;
413 $users = $userGroupDao->getUsersById($userGroup->getId());
414 while ($user = $users->next()) {
415 $contribNode = $contribGroupNode->appendChild($doc->createElement(
'contrib'));
416 $contribNode->setAttribute(
'contrib-type', $keyContribTypeMapping[$userGroup->getData(
'nameLocaleKey')]);
417 $nameNode = $contribNode->appendChild($doc->createElement(
'name'));
418 $surname = method_exists($user,
'getLastName')?$user->getLastName():$user->getLocalizedFamilyName();
419 if ($surname !=
'') {
420 $surnameNode = $nameNode->appendChild($doc->createElement(
'surname'));
421 $surnameNode->nodeValue = $surname;
423 $givenNamesNode = $nameNode->appendChild($doc->createElement(
'given-names'));
424 $givenNamesNode->nodeValue = method_exists($user,
'getFirstName')?$user->getFirstName():$user->getLocalizedGivenName();
425 if (method_exists($user,
'getMiddleName') && $s = $user->getMiddleName()) $givenNamesNode->nodeValue .=
" $s";
438 if (!in_array($submissionFile->getFileType(), array(
'application/xml',
'text/xml')))
return false;
440 static $genres = array();
442 $genreId = $submissionFile->getGenreId();
443 if (!isset($genres[$genreId])) $genres[$genreId] = $genreDao->getById($genreId);
444 assert($genres[$genreId]);
445 $genre = $genres[$genreId];
448 if ($genre->getCategory() != GENRE_CATEGORY_DOCUMENT)
return false;
449 if ($genre->getDependent())
return false;
450 if ($genre->getSupplementary())
return false;