Open Journal Systems  3.3.0
CitationStyleLanguagePlugin.inc.php
1 <?php
2 
16 import('lib.pkp.classes.plugins.GenericPlugin');
17 require_once(__DIR__ . '/lib/vendor/autoload.php');
19 
22  public $_citationStyles = array();
23 
25  public $_citationDownloads = array();
26 
30  public function getDisplayName() {
31  return __('plugins.generic.citationStyleLanguage.displayName');
32  }
33 
37  public function getDescription() {
38  return __('plugins.generic.citationStyleLanguage.description');
39  }
40 
44  public function register($category, $path, $mainContextId = null) {
45  $success = parent::register($category, $path, $mainContextId);
46  if (!Config::getVar('general', 'installed') || defined('RUNNING_UPGRADE')) return $success;
47  if ($success && $this->getEnabled($mainContextId)) {
48  HookRegistry::register('ArticleHandler::view', array($this, 'getArticleTemplateData'));
49  HookRegistry::register('LoadHandler', array($this, 'setPageHandler'));
50  }
51  return $success;
52  }
53 
59  public function getCitationStyles() {
60 
61  if (!empty($this->_citationStyles)) {
63  }
64 
65  $defaults = array(
66  array(
67  'id' => 'acm-sig-proceedings',
68  'title' => __('plugins.generic.citationStyleLanguage.style.acm-sig-proceedings'),
69  'isEnabled' => true,
70  ),
71  array(
72  'id' => 'acs-nano',
73  'title' => __('plugins.generic.citationStyleLanguage.style.acs-nano'),
74  'isEnabled' => true,
75  ),
76  array(
77  'id' => 'apa',
78  'title' => __('plugins.generic.citationStyleLanguage.style.apa'),
79  'isEnabled' => true,
80  'isPrimary' => true,
81  ),
82  array(
83  'id' => 'associacao-brasileira-de-normas-tecnicas',
84  'title' => __('plugins.generic.citationStyleLanguage.style.associacao-brasileira-de-normas-tecnicas'),
85  'isEnabled' => true,
86  ),
87  array(
88  'id' => 'chicago-author-date',
89  'title' => __('plugins.generic.citationStyleLanguage.style.chicago-author-date'),
90  'isEnabled' => true,
91  ),
92  array(
93  'id' => 'harvard-cite-them-right',
94  'title' => __('plugins.generic.citationStyleLanguage.style.harvard-cite-them-right'),
95  'isEnabled' => true,
96  ),
97  array(
98  'id' => 'ieee',
99  'title' => __('plugins.generic.citationStyleLanguage.style.ieee'),
100  'isEnabled' => true,
101  ),
102  array(
103  'id' => 'modern-language-association',
104  'title' => __('plugins.generic.citationStyleLanguage.style.modern-language-association'),
105  'isEnabled' => true,
106  ),
107  array(
108  'id' => 'turabian-fullnote-bibliography',
109  'title' => __('plugins.generic.citationStyleLanguage.style.turabian-fullnote-bibliography'),
110  'isEnabled' => true,
111  ),
112  array(
113  'id' => 'vancouver',
114  'title' => __('plugins.generic.citationStyleLanguage.style.vancouver'),
115  'isEnabled' => true,
116  ),
117  );
118 
119  // If hooking in to add a custom .csl file, add a `useCsl` key to your
120  // style definition with the path to the file.
121  HookRegistry::call('CitationStyleLanguage::citationStyleDefaults', array(&$defaults, $this));
122  $this->_citationStyles = $defaults;
123 
124  return $this->_citationStyles;
125  }
126 
133  public function getPrimaryStyleName($contextId = 0) {
134 
135  $primaryStyleName = $this->getSetting($contextId, 'primaryCitationStyle');
136  if ($primaryStyleName) {
137  return $primaryStyleName;
138  }
139 
140  $styles = $this->getCitationStyles();
141  $primaryStyles = array_filter($styles, function($style) {
142  return !empty($style['isPrimary']);
143  });
144 
145  $primaryStyle = count($primaryStyles) ? array_shift($primaryStyles) : array_shift($styles);
146 
147  return $primaryStyle['id'];
148  }
149 
156  public function getEnabledCitationStyles($contextId = 0) {
157  $styles = $this->getCitationStyles();
158  $enabled = $this->getSetting($contextId, 'enabledCitationStyles');
159  if (!is_array($enabled)) {
160  return array_filter($styles, function($style) {
161  return !empty($style['isEnabled']);
162  });
163  } else {
164  return array_filter($styles, function($style) use ($enabled) {
165  return in_array($style['id'], $enabled);
166  });
167  }
168  }
169 
175  public function getCitationDownloads() {
176 
177  if (!empty($this->_citationDownloads)) {
179  }
180 
181  $defaults = array(
182  array(
183  'id' => 'ris',
184  'title' => __('plugins.generic.citationStyleLanguage.download.ris'),
185  'isEnabled' => true,
186  'useTemplate' => $this->getTemplateResource('citation-styles/ris.tpl'),
187  'fileExtension' => 'ris',
188  'contentType' => 'application/x-Research-Info-Systems',
189  ),
190  array(
191  'id' => 'bibtex',
192  'title' => __('plugins.generic.citationStyleLanguage.download.bibtex'),
193  'isEnabled' => true,
194  'fileExtension' => 'bib',
195  'contentType' => 'application/x-bibtex',
196  ),
197  );
198 
199  // If hooking in to add a custom .csl file, add a `useCsl` key to your
200  // style definition with the path to the file.
201  HookRegistry::call('CitationStyleLanguage::citationDownloadDefaults', array(&$defaults, $this));
202  $this->_citationDownloads = $defaults;
203 
205  }
206 
213  public function getEnabledCitationDownloads($contextId = 0) {
214  $downloads = $this->getCitationDownloads();
215  $enabled = $this->getSetting($contextId, 'enabledCitationDownloads');
216  if (!is_array($enabled)) {
217  return array_filter($downloads, function($style) {
218  return !empty($style['isEnabled']);
219  });
220  } else {
221  return array_filter($downloads, function($style) use ($enabled) {
222  return in_array($style['id'], $enabled);
223  });
224  }
225  }
226 
233  public function mapCitationIds($citations) {
234  return array_values(array_map(function($citation) { return $citation['id']; }, $citations));
235  }
236 
243  public function getCitationStyleConfig($styleId) {
244  $styleConfigs = array_merge($this->getCitationStyles(), $this->getCitationDownloads());
245  $styleConfig = array_filter($styleConfigs, function($styleConfig) use ($styleId) {
246  return $styleConfig['id'] === $styleId;
247  });
248  return array_shift($styleConfig);
249  }
250 
260  public function getArticleTemplateData($hookName, $args) {
261  $request = $args[0];
262  $issue = $args[1];
263  $article = $args[2];
264  $publication = $args[3];
265  $context = $request->getContext();
266  $contextId = $context ? $context->getId() : 0;
267  $templateMgr = TemplateManager::getManager();
268 
269  $citationArgs = array(
270  'submissionId' => $article->getId(),
271  'publicationId' => $publication->getId(),
272  );
273  $citationArgsJson = $citationArgs;
274  $citationArgsJson['return'] = 'json';
275 
276  $templateMgr->assign(array(
277  'citation' => $this->getCitation($request, $article, $this->getPrimaryStyleName($contextId), $issue, $publication),
278  'citationArgs' => $citationArgs,
279  'citationArgsJson' => $citationArgsJson,
280  'citationStyles' => $this->getEnabledCitationStyles($contextId),
281  'citationDownloads' => $this->getEnabledCitationDownloads($contextId),
282  ));
283 
284  $templateMgr->addJavaScript(
285  'citationStyleLanguage',
286  $request->getBaseUrl() . '/' . $this->getPluginPath() . '/js/articleCitation.js'
287  );
288 
289  return false;
290  }
291 
308  public function getCitation($request, $article, $citationStyle = 'apa', $issue = null, $publication = null) {
309  $publication = $publication ?? $article->getCurrentPublication();
310  $issueDao = DAORegistry::getDAO('IssueDAO'); /* @var $issueDao IssueDAO */
311  $issue = $issue ?? $issueDao->getById($publication->getData('issueId'));
312  $context = $request->getContext();
313 
314  import('lib.pkp.classes.core.PKPString');
315 
316  $citationData = new stdClass();
317  $citationData->type = 'article-journal';
318  $citationData->id = $article->getId();
319  $citationData->title = htmlspecialchars($publication->getLocalizedFullTitle());
320  $citationData->{'container-title'} = htmlspecialchars($context->getLocalizedName());
321  $citationData->{'publisher-place'} = $this->getSetting($context->getId(), 'publisherLocation');
322  $citationData->abstract = htmlspecialchars($publication->getLocalizedData('abstract'));
323 
324  $abbreviation = $context->getData('abbreviation', $context->getPrimaryLocale()) ?? $context->getData('acronym', $context->getPrimaryLocale());
325  if ($abbreviation) $citationData->{'container-title-short'} = htmlspecialchars($abbreviation);
326 
327  $citationData->volume = htmlspecialchars($issue->getData('volume'));
328  // Zotero prefers issue and Mendeley uses `number` to store revisions
329  $citationData->issue = htmlspecialchars($issue->getData('number'));
330 
331  $sectionDao = DAORegistry::getDAO('SectionDAO');
332  if ($sectionId = $publication->getData('sectionId')) {
333  $section = $sectionDao->getById($sectionId);
334  if ($section && !$section->getHideTitle()) $citationData->section = htmlspecialchars($section->getTitle($context->getPrimaryLocale()));
335  }
336 
337  $citationData->URL = $request->getDispatcher()->url(
338  $request,
339  ROUTE_PAGE,
340  null,
341  'article',
342  'view',
343  $article->getBestId()
344  );
345  $citationData->accessed = new stdClass();
346  $citationData->accessed->raw = date('Y-m-d');
347 
348  $authors = $publication->getData('authors');
349  if (count($authors)) {
350  $citationData->author = array();
351  foreach ($authors as $author) {
352  $currentAuthor = new stdClass();
353  if (empty($author->getLocalizedFamilyName())) {
354  $currentAuthor->family = htmlspecialchars($author->getLocalizedGivenName());
355  } else {
356  $currentAuthor->family = htmlspecialchars($author->getLocalizedFamilyName());
357  $currentAuthor->given = htmlspecialchars($author->getLocalizedGivenName());
358  }
359  $citationData->author[] = $currentAuthor;
360  }
361  }
362 
363  if ($publication->getData('datePublished')) {
364  $citationData->issued = new stdClass();
365  $citationData->issued->raw = htmlspecialchars($publication->getData('datePublished'));
366  $publishedPublications = $article->getPublishedPublications();
367  if (count($publishedPublications) > 1) {
368  $originalPublication = array_reduce($publishedPublications, function($a, $b) {
369  return $a && $a->getId() < $b->getId() ? $a : $b;
370  });
371  $originalDate = $originalPublication->getData('datePublished');
372  if ($originalDate && $originalDate !== $publication->getData('datePublished')) {
373  $citationData->{'original-date'} = new stdClass();
374  $citationData->{'original-date'}->raw = htmlspecialchars($originalPublication->getData('datePublished'));
375  }
376  }
377  } elseif ($issue->getPublished()) {
378  $citationData->issued = new stdClass();
379  $citationData->issued->raw = htmlspecialchars($issue->getDatePublished());
380  }
381 
382  if ($publication->getData('pages')) {
383  $citationData->page = htmlspecialchars($publication->getData('pages'));
384  }
385 
386  HookRegistry::call('CitationStyleLanguage::citation', array(&$citationData, &$citationStyle, $article, $issue, $context, $publication));
387 
388  $citation = '';
389 
390  // Determine whether to use citeproc-php or a custom template to render
391  // the citation
392  $styleConfig = $this->getCitationStyleConfig($citationStyle);
393  if (!empty($styleConfig)) {
394  if (!empty($styleConfig['useTemplate'])) {
395  $templateMgr = TemplateManager::getManager($request);
396  $templateMgr->assign(array(
397  'citationData' => $citationData,
398  'citationStyle' => $citationStyle,
399  'article' => $article,
400  'publication' => $publication,
401  'issue' => $issue,
402  'journal' => $context,
403  ));
404  $citation = $templateMgr->fetch($styleConfig['useTemplate']);
405  } else {
406  $style = $this->loadStyle($styleConfig);
407  if ($style) {
408  // Determine what locale to use. Try in order:
409  // - xx_YY
410  // - xx
411  // Fall back English if none found.
412  $tryLocale = null;
413  foreach (array(
414  str_replace('_', '-', substr(AppLocale::getLocale(), 0, 5)),
415  substr(AppLocale::getLocale(), 0, 2),
416  'en-US'
417  ) as $tryLocale) {
418  if (file_exists(dirname(__FILE__) . '/lib/vendor/citation-style-language/locales/locales-' . $tryLocale . '.xml')) break;
419  }
420  $citeProc = new CiteProc($style, $tryLocale);
421  $citation = $citeProc->render(array($citationData), 'bibliography');
422  }
423  }
424  }
425 
426  return $citation;
427  }
428 
434  public function loadStyle($styleConfig) {
435  if (!empty($styleConfig['useCsl'])) {
436  return file_get_contents($styleConfig['useCsl']);
437  } else {
438  return file_get_contents($this->getPluginPath() . '/citation-styles/' . $styleConfig['id'] . '.csl');
439  }
440  }
441 
454  public function downloadCitation($request, $article, $citationStyle = 'ris', $issue = null) {
455  $journal = $request->getContext();
456 
457  if (empty($issue)) {
458  $issueDao = DAORegistry::getDAO('IssueDAO'); /* @var $issueDao IssueDAO */
459  $issue = $issueDao->getById($article->getCurrentPublication()->getData('issueId'));
460  }
461 
462  $styleConfig = $this->getCitationStyleConfig($citationStyle);
463  if (empty($styleConfig)) {
464  return false;
465  }
466 
467  $citation = trim(strip_tags($this->getCitation($request, $article, $citationStyle, $issue)));
468  // TODO this is likely going to cause an error in a citation some day,
469  // but is necessary to get the .ris downloadable format working. The
470  // CSL language doesn't seem to offer a way to indicate a line break.
471  // See: https://github.com/citation-style-language/styles/issues/2831
472  $citation = str_replace('\n', "\n", $citation);
473 
474  $filename = substr(preg_replace('/[^a-zA-Z0-9_.-]/', '', str_replace(' ', '-', $article->getLocalizedTitle())), 0, 60);
475 
476  header('Content-Disposition: attachment; filename="' . $filename . '.' . $styleConfig['fileExtension'] . '"');
477  header('Content-Type: ' . $styleConfig['contentType']);
478  echo $citation;
479  exit;
480  }
481 
485  public function getActions($request, $actionArgs) {
486 
487  $actions = parent::getActions($request, $actionArgs);
488 
489  if (!$this->getEnabled()) {
490  return $actions;
491  }
492 
493  $router = $request->getRouter();
494  import('lib.pkp.classes.linkAction.request.AjaxModal');
495  $linkAction = new LinkAction(
496  'settings',
497  new AjaxModal(
498  $router->url(
499  $request,
500  null,
501  null,
502  'manage',
503  null,
504  array(
505  'verb' => 'settings',
506  'plugin' => $this->getName(),
507  'category' => 'generic'
508  )
509  ),
510  $this->getDisplayName()
511  ),
512  __('manager.plugins.settings'),
513  null
514  );
515 
516  array_unshift($actions, $linkAction);
517 
518  return $actions;
519  }
520 
524  public function manage($args, $request) {
525  switch ($request->getUserVar('verb')) {
526  case 'settings':
527  $this->import('CitationStyleLanguageSettingsForm');
528  $form = new CitationStyleLanguageSettingsForm($this);
529 
530  if ($request->getUserVar('save')) {
531  $form->readInputData();
532  if ($form->validate()) {
533  $form->execute();
534  return new JSONMessage(true);
535  }
536  }
537 
538  $form->initData();
539  return new JSONMessage(true, $form->fetch($request));
540  }
541  return parent::manage($args, $request);
542  }
543 
551  public function setPageHandler($hookName, $params) {
552  $page = $params[0];
553  if ($this->getEnabled() && $page === 'citationstylelanguage') {
554  $this->import('pages/CitationStyleLanguageHandler');
555  define('HANDLER_CLASS', 'CitationStyleLanguageHandler');
556  return true;
557  }
558  return false;
559  }
560 }
CitationStyleLanguagePlugin\downloadCitation
downloadCitation($request, $article, $citationStyle='ris', $issue=null)
Definition: CitationStyleLanguagePlugin.inc.php:460
CitationStyleLanguageSettingsForm
Definition: CitationStyleLanguageSettingsForm.inc.php:18
CitationStyleLanguagePlugin\getCitation
getCitation($request, $article, $citationStyle='apa', $issue=null, $publication=null)
Definition: CitationStyleLanguagePlugin.inc.php:314
CitationStyleLanguagePlugin\mapCitationIds
mapCitationIds($citations)
Definition: CitationStyleLanguagePlugin.inc.php:239
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
CitationStyleLanguagePlugin\manage
manage($args, $request)
Definition: CitationStyleLanguagePlugin.inc.php:530
CitationStyleLanguagePlugin\$_citationStyles
$_citationStyles
Definition: CitationStyleLanguagePlugin.inc.php:25
CitationStyleLanguagePlugin\loadStyle
loadStyle($styleConfig)
Definition: CitationStyleLanguagePlugin.inc.php:440
CitationStyleLanguagePlugin\getArticleTemplateData
getArticleTemplateData($hookName, $args)
Definition: CitationStyleLanguagePlugin.inc.php:266
CitationStyleLanguagePlugin\getCitationStyleConfig
getCitationStyleConfig($styleId)
Definition: CitationStyleLanguagePlugin.inc.php:249
CitationStyleLanguagePlugin\getDisplayName
getDisplayName()
Definition: CitationStyleLanguagePlugin.inc.php:36
CitationStyleLanguagePlugin\setPageHandler
setPageHandler($hookName, $params)
Definition: CitationStyleLanguagePlugin.inc.php:557
CitationStyleLanguagePlugin\getDescription
getDescription()
Definition: CitationStyleLanguagePlugin.inc.php:43
Plugin\getEnabled
getEnabled()
Definition: Plugin.inc.php:868
Seboettg\CiteProc\CiteProc
Definition: CiteProc.php:32
JSONMessage
Class to represent a JSON (Javascript Object Notation) message.
Definition: JSONMessage.inc.php:18
CitationStyleLanguagePlugin\getCitationStyles
getCitationStyles()
Definition: CitationStyleLanguagePlugin.inc.php:65
Plugin\getSetting
getSetting($contextId, $name)
Definition: Plugin.inc.php:473
Config\getVar
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
AjaxModal
A modal that retrieves its content from via AJAX.
Definition: AjaxModal.inc.php:18
LinkAction
Base class defining an action that can be performed by the user in the user interface.
Definition: LinkAction.inc.php:22
Seboettg\Collection\count
count()
Definition: ArrayListTrait.php:253
CitationStyleLanguagePlugin
Citation Style Language plugin class.
Definition: CitationStyleLanguagePlugin.inc.php:20
CitationStyleLanguagePlugin\getEnabledCitationDownloads
getEnabledCitationDownloads($contextId=0)
Definition: CitationStyleLanguagePlugin.inc.php:219
PKPTemplateManager\getManager
static & getManager($request=null)
Definition: PKPTemplateManager.inc.php:1239
CitationStyleLanguagePlugin\getCitationDownloads
getCitationDownloads()
Definition: CitationStyleLanguagePlugin.inc.php:181
CitationStyleLanguagePlugin\getActions
getActions($request, $actionArgs)
Definition: CitationStyleLanguagePlugin.inc.php:491
Plugin\getTemplateResource
getTemplateResource($template=null, $inCore=false)
Definition: Plugin.inc.php:349
CitationStyleLanguagePlugin\$_citationDownloads
$_citationDownloads
Definition: CitationStyleLanguagePlugin.inc.php:31
CitationStyleLanguagePlugin\getPrimaryStyleName
getPrimaryStyleName($contextId=0)
Definition: CitationStyleLanguagePlugin.inc.php:139
Plugin\$request
$request
Definition: Plugin.inc.php:68
HookRegistry\register
static register($hookName, $callback, $hookSequence=HOOK_SEQUENCE_NORMAL)
Definition: HookRegistry.inc.php:70
CitationStyleLanguagePlugin\getEnabledCitationStyles
getEnabledCitationStyles($contextId=0)
Definition: CitationStyleLanguagePlugin.inc.php:162
HookRegistry\call
static call($hookName, $args=null)
Definition: HookRegistry.inc.php:86
AppLocale\getLocale
static getLocale()
Definition: env1/MockAppLocale.inc.php:40
GenericPlugin
Abstract class for generic plugins.
Definition: GenericPlugin.inc.php:18