Open Journal Systems  3.3.0
back.inc.php
1 <?php
2 
22  protected function _findJats($article, $galleys) {
23  import('lib.pkp.classes.submission.SubmissionFile'); // SUBMISSION_FILE_... constants
24  $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO');
25  $candidateFiles = array();
26 
27  // First, look for candidates in the galleys area (published content).
28  foreach ($galleys as $galley) {
29  $galleyFiles = $submissionFileDao->getLatestRevisionsByAssocId(ASSOC_TYPE_GALLEY, $galley->getId(), $galley->getSubmissionId(), SUBMISSION_FILE_PROOF);
30  foreach ($galleyFiles as $galleyFile) {
31  if ($this->_isCandidateFile($galleyFile)) $candidateFiles[] = $galleyFile;
32  }
33  }
34 
35  // If no candidates were found, look in the layout area (unpublished content).
36  if (empty($candidateFiles)) {
37  $layoutFiles = $submissionFileDao->getLatestRevisions($article->getId(), SUBMISSION_FILE_PRODUCTION_READY);
38  foreach ($layoutFiles as $layoutFile) {
39  if ($this->_isCandidateFile($layoutFile)) $candidateFiles[] = $layoutFile;
40  }
41  }
42 
43  $doc = null;
44  HookRegistry::call('OAIMetadataFormat_JATS::findJats', array(&$this, &$candidateFiles, &$doc));
45 
46  // If no candidate files were located, return the null XML.
47  if (!$doc && empty($candidateFiles)) {
48  return null;
49  }
50  if (count($candidateFiles) > 1) error_log('WARNING: More than one JATS XML candidate documents were located for submission ' . $article->getId() . '.');
51 
52  // Fetch the XML document
53  if (!$doc) {
54  $candidateFile = array_shift($candidateFiles);
55  $doc = new DOMDocument;
56  $doc->loadXML(file_get_contents($candidateFile->getFilePath()));
57  }
58 
59  return $doc;
60  }
61 
65  public function toXml($record, $format = null) {
66  $oaiDao = DAORegistry::getDAO('OAIDAO');
67  $journal = $record->getData('journal');
68  $article = $record->getData('article');
69  $galleys = $record->getData('galleys');
70  $section = $record->getData('section');
71  $issue = $record->getData('issue');
72 
73  // Check access
74  import('classes.issue.IssueAction');
75  $subscriptionRequired = IssueAction::subscriptionRequired($issue, $journal);
76  $isSubscribedDomain = IssueAction::subscribedDomain(Application::get()->getRequest(), $journal, $issue->getId(), $article->getId());
77  if ($subscriptionRequired && !$subscriptionRequired) {
78  $oaiDao->oai->error('cannotDisseminateFormat', 'Cannot disseminate format (JATS XML not available)');
79  }
80 
81  $doc = $this->_findJats($article, $galleys);
82  if (!$doc) {
83  $oaiDao->oai->error('cannotDisseminateFormat', 'Cannot disseminate format (JATS XML not available)');
84  }
85 
86  $this->_mungeMetadata($doc, $journal, $article, $section, $issue);
87 
88  return $doc->saveXml($doc->getElementsByTagName('article')->item(0));
89  }
90 
97  protected function _addChildInOrder($parentNode, $childNode) {
98  $permittedElementOrders = array(
99  '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'),
100  'journal-meta' => array('journal-id', 'journal-title-group', 'contrib-group', 'aff', 'aff-alternatives', 'issn', 'issn-l', 'isbn', 'publisher', 'notes', 'self-uri', 'custom-meta-group'),
101  );
102  assert(isset($permittedElementOrders[$parentNode->nodeName])); // We have an order list for the parent node
103  $order = $permittedElementOrders[$parentNode->nodeName];
104  $position = array_search($childNode->nodeName, $order);
105  assert($position !== false); // The child node appears in the order list
106 
107  $followingElements = array_slice($order, $position);
108  $followingElement = null;
109  foreach ($parentNode->childNodes as $node) {
110  if (in_array($node->nodeName, $followingElements)) {
111  $followingElement = $node;
112  break;
113  }
114  }
115 
116  return $parentNode->insertBefore($childNode, $followingElement);
117  }
118 
127  protected function _mungeMetadata($doc, $journal, $article, $section, $issue) {
128  $xpath = new DOMXPath($doc);
129  $articleMetaNode = $xpath->query('//article/front/article-meta')->item(0);
130  $journalMetaNode = $xpath->query('//article/front/journal-meta')->item(0);
131  if (!$journalMetaNode) {
132  $frontNode = $xpath->query('//article/front')->item(0);
133  $journalMetaNode = $this->_addChildInOrder($frontNode, $doc->createElement('journal-meta'));
134  }
135 
136  // Set the article language.
137  $xpath->query('//article')->item(0)->setAttribute('xml:lang', substr($article->getLocale(),0,2));
138 
139  // Set the publication date.
140  if ($datePublished = $article->getDatePublished()) {
141  $datePublished = strtotime($datePublished);
142  $match = $xpath->query("//article/front/article-meta/pub-date[@date-type='pub' and publication-format='epub']");
143  if ($match->length) {
144  // An existing pub-date was found; empty and re-use.
145  $dateNode = $match->item(0);
146  while ($dateNode->hasChildNodes()) $dateNode->removeChild($dateNode->firstChild);
147  } else {
148  // No pub-date was found; create a new one.
149  $dateNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('pub-date'));
150  $dateNode->setAttribute('date-type', 'pub');
151  $dateNode->setAttribute('publication-format', 'epub');
152  }
153 
154  $dateNode->setAttribute('iso-8601-date', strftime('%Y-%m-%d', $datePublished));
155  $dateNode->appendChild($doc->createElement('day'))->nodeValue = strftime('%d', $datePublished);
156  $dateNode->appendChild($doc->createElement('month'))->nodeValue = strftime('%m', $datePublished);
157  $dateNode->appendChild($doc->createElement('year'))->nodeValue = strftime('%Y', $datePublished);
158  }
159 
160  $issueYear = null;
161  if ($issue && $issue->getShowYear()) $issueYear = $issue->getYear();
162  if (!$issueYear && $datePublished) $issueYear = strftime('%Y', $datePublished);
163  if ($issueYear) {
164  $match = $xpath->query("//article/front/article-meta/pub-date[@date-type='issue' and publication-format='epub']");
165  if ($match->length) {
166  // An existing pub-date was found; empty and re-use.
167  $dateNode = $match->item(0);
168  while ($dateNode->hasChildNodes()) $dateNode->removeChild($dateNode->firstChild);
169  } else {
170  // No pub-date was found; create a new one.
171  $dateNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('pub-date'));
172  $dateNode->setAttribute('date-type', 'issue');
173  $dateNode->setAttribute('publication-format', 'epub');
174  }
175  $dateNode->setAttribute('iso-8601-date', strftime('%Y-%m-%d', $datePublished));
176  $dateNode->appendChild($doc->createElement('day'))->nodeValue = strftime('%d', $datePublished);
177  $dateNode->appendChild($doc->createElement('month'))->nodeValue = strftime('%m', $datePublished);
178  $dateNode->appendChild($doc->createElement('year'))->nodeValue = strftime('%Y', $datePublished);
179  }
180 
181  // Set the article title.
182  $titleGroupNode = $xpath->query('//article/front/article-meta/title-group')->item(0);
183  foreach ($titleGroupNode->getElementsByTagName('article-title') as $titleNode) $titleGroupNode->removeChild($titleNode);
184  foreach ($article->getTitle(null) as $locale => $title) {
185  $titleNode = $titleGroupNode->appendChild($doc->createElement('article-title'));
186  $titleNode->setAttribute('xml:lang', substr($locale,0,2));
187  $titleNode->nodeValue = $title;
188  }
189 
190  // Set the article abstract.
191  static $purifier;
192  if (!$purifier) {
193  $config = HTMLPurifier_Config::createDefault();
194  $config->set('HTML.Allowed', 'p');
195  $config->set('Cache.SerializerPath', 'cache');
196  $purifier = new HTMLPurifier($config);
197  }
198  foreach ($articleMetaNode->getElementsByTagName('abstract') as $abstractNode) $articleMetaNode->removeChild($abstractNode);
199  foreach ((array) $article->getAbstract(null) as $locale => $abstract) {
200  $abstractNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('abstract'));
201  $abstractNode->setAttribute('xml:lang', substr($locale,0,2));
202  $abstractNode->nodeValue = $purifier->purify($abstract);
203  }
204 
205  // Set the journal-id[publisher-id']
206  $match = $xpath->query("//article/front/journal-meta/journal-id[@journal-id-type='publisher-id']");
207  if ($match->length) $match->item(0)->nodeValue = $journal->getPath();
208  else {
209  $journalIdNode = $this->_addChildInOrder($journalMetaNode, $doc->createElement('journal-id'));
210  $journalIdNode->setAttribute('journal-id-type', 'publisher-id');
211  $journalIdNode->nodeValue = $journal->getPath();
212  }
213 
214  // Store the DOI
215  if ($doi = trim($article->getStoredPubId('doi'))) {
216  $match = $xpath->query("//article/front/article-meta/article-id[@pub-id-type='doi']");
217  if ($match->length) $match->item(0)->nodeValue = $doi;
218  else {
219  $articleIdNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('article-id'));
220  $articleIdNode->setAttribute('pub-id-type', 'doi');
221  $articleIdNode->nodeValue = $doi;
222  }
223  }
224 
225  // Override permissions, when not supplied in the document
226  $match = $xpath->query('//article/front/article-meta/permissions');
227  $copyrightHolder = $article->getLocalizedCopyrightHolder($article->getLocale());
228  $copyrightYear = $article->getCopyrightYear();
229  $licenseUrl = $article->getLicenseURL();
230  if (!$match->length && ($copyrightHolder || $copyrightYear || $licenseUrl)) {
231  $permissionsNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('permissions'));
232  if ($copyrightYear) $permissionsNode->appendChild($doc->createElement('copyright-year'))->nodeValue = $copyrightYear;
233  if ($copyrightHolder) $permissionsNode->appendChild($doc->createElement('copyright-holder'))->nodeValue = $copyrightHolder;
234  if ($licenseUrl) {
235  $licenseNode = $permissionsNode->appendChild($doc->createElement('license'));
236  $licenseNode->setAttribute('xlink:href', $licenseUrl);
237  }
238  }
239 
240  // Section information
241  $match = $xpath->query("//article/front/article-meta/article-categories");
242  if ($match->length) $articleCategoriesNode = $match->item(0);
243  else {
244  $articleCategoriesNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('article-categories'));
245  }
246  $match = $xpath->query('//article/front/article-meta/subj-group[@subj-group-type="heading"]');
247  if ($match->length) $subjGroupNode = $match->item(0);
248  else {
249  $subjGroupNode = $articleCategoriesNode->appendChild($doc->createElement('subj-group'));
250  $subjGroupNode->setAttribute('subj-group-type', 'heading');
251  }
252  $subjectNode = $subjGroupNode->appendChild($doc->createElement('subject'));
253  $subjectNode->nodeValue = $section->getTitle($journal->getPrimaryLocale());
254 
255  // Article sequence information
256  $publishedArticleDao = DAORegistry::getDAO('PublishedArticleDAO');
257  $articleIds = array_map(function($publishedArticle) {
258  return $publishedArticle->getId();
259  }, $publishedArticleDao->getPublishedArticles($issue->getId()));
260  foreach (array('volume', 'number') as $nodeName) {
261  $match = $xpath->query("//article/front/article-meta/$nodeName");
262  if ($match->length) $match->item(0)->setAttribute('seq', array_search($article->getId(), $articleIds));
263  }
264 
265  // Issue ID
266  $match = $xpath->query("//article/front/article-meta/issue-id");
267  if ($match->length) $match->item(0)->nodeValue = $issue->getId();
268  else {
269  $issueIdNode = $this->_addChildInOrder($articleMetaNode, $doc->createElement('issue-id'));
270  $issueIdNode->nodeValue = $issue->getId();
271  }
272 
273  // Article type
274  if ($articleType = trim($section->getLocalizedIdentifyType())) {
275  $articleNode = $xpath->query("//article")->item(0);
276  $articleNode->setAttribute('article-type', $articleType);
277  }
278 
279  // Editorial team
280  $userGroupDao = DAORegistry::getDAO('UserGroupDAO');
281  $userGroups = $userGroupDao->getByContextId($journal->getId());
282  $journalMetaNode = $xpath->query('//article/front/journal-meta')->item(0);
283  $contribGroupNode = $this->_addChildInOrder($journalMetaNode, $doc->createElement('contrib-group'));
284  $keyContribTypeMapping = array(
285  'default.groups.name.manager' => 'jmanager',
286  'default.groups.name.editor' => 'editor',
287  'default.groups.name.sectionEditor' => 'secteditor',
288  );
289  while ($userGroup = $userGroups->next()) {
290  if (!isset($keyContribTypeMapping[$userGroup->getData('nameLocaleKey')])) continue;
291 
292  $users = $userGroupDao->getUsersById($userGroup->getId());
293  while ($user = $users->next()) {
294  $contribNode = $contribGroupNode->appendChild($doc->createElement('contrib'));
295  $contribNode->setAttribute('contrib-type', $keyContribTypeMapping[$userGroup->getData('nameLocaleKey')]);
296  $nameNode = $contribNode->appendChild($doc->createElement('name'));
297  $surnameNode = $nameNode->appendChild($doc->createElement('surname'));
298  $surnameNode->nodeValue = method_exists($user, 'getLastName')?$user->getLastName():$user->getLocalizedFamilyName();
299  $givenNamesNode = $nameNode->appendChild($doc->createElement('given-names'));
300  $givenNamesNode->nodeValue = method_exists($user, 'getFirstName')?$user->getFirstName():$user->getLocalizedGivenName();
301  if (method_exists($user, 'getMiddleName') && $s = $user->getMiddleName()) $givenNamesNode->nodeValue .= " $s";
302  }
303  }
304 
305  }
306 
312  protected function _isCandidateFile($submissionFile) {
313  // The file type isn't XML.
314  if (!in_array($submissionFile->getFileType(), array('application/xml', 'text/xml'))) return false;
315 
316  static $genres = array();
317  $genreDao = DAORegistry::getDAO('GenreDAO');
318  $genreId = $submissionFile->getGenreId();
319  if (!isset($genres[$genreId])) $genres[$genreId] = $genreDao->getById($genreId);
320  assert($genres[$genreId]);
321  $genre = $genres[$genreId];
322 
323  // The genre doesn't look like a main submission document.
324  if ($genre->getCategory() != GENRE_CATEGORY_DOCUMENT) return false;
325  if ($genre->getDependent()) return false;
326  if ($genre->getSupplementary()) return false;
327 
328  return true;
329  }
330 }
OAIMetadataFormat_JATS\_addChildInOrder
_addChildInOrder($parentNode, $childNode)
Definition: back.inc.php:97
OAIMetadataFormat_JATS\_mungeMetadata
_mungeMetadata($doc, $journal, $article, $section, $issue)
Definition: back.inc.php:127
IssueAction\subscribedDomain
subscribedDomain($request, $journal, $issueId=null, $articleId=null)
Definition: IssueAction.inc.php:131
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
OAIMetadataFormat
Definition: OAIStruct.inc.php:183
OAIMetadataFormat_JATS\_findJats
_findJats($article, $galleys)
Definition: back.inc.php:22
OAIMetadataFormat_JATS
OAI metadata format class – JATS.
Definition: back.inc.php:21
IssueAction\subscriptionRequired
subscriptionRequired($issue, $journal)
Definition: IssueAction.inc.php:28
OAIMetadataFormat_JATS\toXml
toXml($record, $format=null)
Definition: back.inc.php:65
OAIMetadataFormat_JATS\_isCandidateFile
_isCandidateFile($submissionFile)
Definition: back.inc.php:312
PKPApplication\get
static get()
Definition: PKPApplication.inc.php:235
HookRegistry\call
static call($hookName, $args=null)
Definition: HookRegistry.inc.php:86