citationstylelanguage
An OJS 3 plugin to generate an article citation in any CSL citation style using citeproc-php.
Science Score: 44.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
✓CITATION.cff file
Found CITATION.cff file -
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (2.7%) to scientific vocabulary
Repository
An OJS 3 plugin to generate an article citation in any CSL citation style using citeproc-php.
Basic Info
- Host: GitHub
- Owner: pkp
- License: gpl-3.0
- Language: PHP
- Default Branch: main
- Size: 461 KB
Statistics
- Stars: 15
- Watchers: 12
- Forks: 56
- Open Issues: 6
- Releases: 0
Metadata Files
README.md
Citation Style Language plugin for OJS 3
An OJS 3 plugin to generate an article citation in any CSL citation style using citeproc-php.
License
This plugin is licensed under the GNU General Public License v3.
Consult the citeproc-php licensing information for that library's license details.
Each CSL file has it's own licensing information attached to the XML.
Compatibility
This plugin is compatible with OJS 3.1.x.
Owner
- Name: Public Knowledge Project
- Login: pkp
- Kind: organization
- Location: The Internet!
- Website: http://pkp.sfu.ca
- Twitter: pkp
- Repositories: 90
- Profile: https://github.com/pkp
This is the official Github account for PKP. We track most software milestones in the pkp-lib repository (pinned below).
Citation (CitationStyleLanguagePlugin.php)
<?php
/**
* @file plugins/generic/citationStyleLanguage/CitationStyleLanguagePlugin.php
*
* Copyright (c) 2017-2020 Simon Fraser University
* Copyright (c) 2017-2020 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class CitationStyleLanguagePlugin
*
* @ingroup plugins_generic_citationStyleLanguage
*
* @brief Citation Style Language plugin class.
*/
namespace APP\plugins\generic\citationStyleLanguage;
require_once(__DIR__ . '/lib/vendor/autoload.php');
use APP\author\Author;
use APP\core\Application;
use APP\facades\Repo;
use APP\issue\Issue;
use APP\monograph\Chapter;
use APP\monograph\ChapterDAO;
use APP\plugins\generic\citationStyleLanguage\pages\CitationStyleLanguageHandler;
use APP\publication\Publication;
use APP\publicationFormat\PublicationFormat;
use APP\submission\Submission;
use APP\template\TemplateManager;
use Exception;
use PKP\controlledVocab\ControlledVocab;
use PKP\core\JSONMessage;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\core\PKPString;
use PKP\db\DAORegistry;
use PKP\facades\Locale;
use PKP\linkAction\LinkAction;
use PKP\linkAction\request\AjaxModal;
use PKP\plugins\GenericPlugin;
use PKP\plugins\Hook;
use Seboettg\CiteProc\CiteProc;
use stdClass;
class CitationStyleLanguagePlugin extends GenericPlugin
{
/** @var array List of citation styles available */
public array $_citationStyles = [];
/** @var array List of citation download formats available */
public array $_citationDownloads = [];
/** @var string Name of the application */
public string $application;
protected bool $isBook = false;
protected bool $isChapter = false;
protected bool $isArticle = false;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->application = Application::get()->getName();
}
/**
* @copydoc Plugin::getDisplayName()
*/
public function getDisplayName()
{
return __('plugins.generic.citationStyleLanguage.displayName');
}
/**
* @copydoc Plugin::getDescription()
*/
public function getDescription()
{
return __('plugins.generic.citationStyleLanguage.description');
}
/**
* @copydoc Plugin::register()
*
* @param null|mixed $mainContextId
*/
public function register($category, $path, $mainContextId = null)
{
$success = parent::register($category, $path, $mainContextId);
if (Application::isUnderMaintenance()) {
return $success;
}
if ($success && $this->getEnabled($mainContextId)) {
Hook::add('PreprintHandler::view', $this->getTemplateData(...));
Hook::add('CatalogBookHandler::book', $this->getTemplateData(...));
Hook::add('ArticleHandler::view', $this->getTemplateData(...));
Hook::add('Templates::Article::Details', $this->addCitationMarkup(...));
Hook::add('Templates::Catalog::Book::Details', $this->addCitationMarkup(...));
Hook::add('Templates::Catalog::Chapter::Details', $this->addCitationMarkup(...));
Hook::add('LoadHandler', $this->setPageHandler(...));
}
return $success;
}
/**
* Get list of citation styles available
*
* @hook CitationStyleLanguage::citationStyleDefaults [[&$defaults, $this]]
*/
public function getCitationStyles(): array
{
if (!empty($this->_citationStyles)) {
return $this->_citationStyles;
}
$defaults = [
[
'id' => 'acm-sig-proceedings',
'title' => __('plugins.generic.citationStyleLanguage.style.acm-sig-proceedings'),
'isEnabled' => true,
],
[
'id' => 'acs-nano',
'title' => __('plugins.generic.citationStyleLanguage.style.acs-nano'),
'isEnabled' => true,
],
[
'id' => 'apa',
'title' => __('plugins.generic.citationStyleLanguage.style.apa'),
'isEnabled' => true,
'isPrimary' => true,
],
[
'id' => 'associacao-brasileira-de-normas-tecnicas',
'title' => __('plugins.generic.citationStyleLanguage.style.associacao-brasileira-de-normas-tecnicas'),
'isEnabled' => true,
],
[
'id' => 'chicago-author-date',
'title' => __('plugins.generic.citationStyleLanguage.style.chicago-author-date'),
'isEnabled' => true,
],
[
'id' => 'harvard-cite-them-right',
'title' => __('plugins.generic.citationStyleLanguage.style.harvard-cite-them-right'),
'isEnabled' => true,
],
[
'id' => 'ieee',
'title' => __('plugins.generic.citationStyleLanguage.style.ieee'),
'isEnabled' => true,
],
[
'id' => 'modern-language-association',
'title' => __('plugins.generic.citationStyleLanguage.style.modern-language-association'),
'isEnabled' => true,
],
[
'id' => 'turabian-fullnote-bibliography',
'title' => __('plugins.generic.citationStyleLanguage.style.turabian-fullnote-bibliography'),
'isEnabled' => true,
],
[
'id' => 'vancouver',
'title' => __('plugins.generic.citationStyleLanguage.style.vancouver'),
'isEnabled' => true,
],
[
'id' => 'ama',
'title' => __('plugins.generic.citationStyleLanguage.style.ama'),
'isEnabled' => true,
],
];
// If hooking in to add a custom .csl file, add a `useCsl` key to your
// style definition with the path to the file.
Hook::call('CitationStyleLanguage::citationStyleDefaults', [&$defaults, $this]);
$this->_citationStyles = $defaults;
return $this->_citationStyles;
}
/**
* Get the primary style name or default to the first available style
*/
public function getPrimaryStyleName(int $contextId): string
{
$primaryStyleName = $this->getSetting($contextId, 'primaryCitationStyle');
if ($primaryStyleName) {
return $primaryStyleName;
}
$styles = $this->getCitationStyles();
$primaryStyles = array_filter($styles, function ($style) {
return !empty($style['isPrimary']);
});
$primaryStyle = count($primaryStyles) ? array_shift($primaryStyles) : array_shift($styles);
return $primaryStyle['id'];
}
/**
* Get enabled citation styles
*/
public function getEnabledCitationStyles(int $contextId): array
{
$styles = $this->getCitationStyles();
$enabled = $this->getSetting($contextId, 'enabledCitationStyles');
if (!is_array($enabled)) {
return array_filter($styles, function ($style) {
return !empty($style['isEnabled']);
});
}
return array_filter($styles, function ($style) use ($enabled) {
return in_array($style['id'], $enabled);
});
}
/**
* Get list of citation download formats available
*
* @hook CitationStyleLanguage::citationDownloadDefaults [[&$defaults, $this]]
*/
public function getCitationDownloads(): array
{
if (!empty($this->_citationDownloads)) {
return $this->_citationDownloads;
}
$defaults = [
[
'id' => 'ris',
'title' => __('plugins.generic.citationStyleLanguage.download.ris'),
'isEnabled' => true,
'useTemplate' => $this->getTemplateResource('citation-styles/ris.tpl'),
'fileExtension' => 'ris',
'contentType' => 'application/x-Research-Info-Systems',
],
[
'id' => 'bibtex',
'title' => __('plugins.generic.citationStyleLanguage.download.bibtex'),
'isEnabled' => true,
'fileExtension' => 'bib',
'contentType' => 'application/x-bibtex',
],
];
// If hooking in to add a custom .csl file, add a `useCsl` key to your
// style definition with the path to the file.
Hook::call('CitationStyleLanguage::citationDownloadDefaults', [&$defaults, $this]);
$this->_citationDownloads = $defaults;
return $this->_citationDownloads;
}
/**
* Get enabled citation styles
*/
public function getEnabledCitationDownloads(int $contextId): array
{
$downloads = $this->getCitationDownloads();
$enabled = $this->getSetting($contextId, 'enabledCitationDownloads');
if (!is_array($enabled)) {
return array_filter($downloads, function ($style) {
return !empty($style['isEnabled']);
});
}
return array_filter($downloads, function ($style) use ($enabled) {
return in_array($style['id'], $enabled);
});
}
/**
* Pluck citation IDs from array of citations
*/
public function mapCitationIds(array $citations): array
{
return array_values(array_map(function ($citation) {
return $citation['id'];
}, $citations));
}
/**
* Get citation config for a citation ID (example: 'apa')
*/
public function getCitationStyleConfig(string $styleId): array
{
$styleConfigs = array_merge($this->getCitationStyles(), $this->getCitationDownloads());
$styleConfig = array_filter($styleConfigs, function ($styleConfig) use ($styleId) {
return $styleConfig['id'] === $styleId;
});
return array_shift($styleConfig);
}
protected function getPublicationTypeUrlPath(): string
{
return match ($this->application) {
'ojs2' => 'article',
'ops' => 'preprint',
'omp' => 'catalog',
default => ''
};
}
/**
* Retrieve citation information for the article details template. This
* method is hooked in before a template displays.
*
* @throws Exception
*/
public function getTemplateData(string $hookName, array $args): bool
{
$request = $args[0];
$templateMgr = TemplateManager::getManager($request);
$context = $request->getContext();
$contextId = $context->getId();
switch ($this->application) {
case 'ops':
$submission = $args[1];
$publication = $args[2];
$citation = $this->getCitation($request, $submission, $this->getPrimaryStyleName($contextId), null, $publication);
break;
case 'ojs2':
$issue = $args[1];
$submission = $args[2];
$publication = $args[3];
$chapter = null;
$citation = $this->getCitation($request, $submission, $this->getPrimaryStyleName($contextId), $issue, $publication);
break;
case 'omp':
/** @var Submission $submission */
$submission = &$args[1];
$publication = &$args[2];
$chapter = &$args[3];
$issue = null;
$citation = $this->getCitation($request, $submission, $this->getPrimaryStyleName($contextId), $issue, $publication);
break;
default:
throw new Exception('Unknown application!');
}
$citationArgs = [
'submissionId' => $submission->getId(),
'publicationId' => $publication->getId(),
];
if ($issue) {
$citationArgs['issueId'] = $issue->getId();
}
if ($chapter) {
$citationArgs['chapterId'] = $chapter->getId();
}
$citationArgsJson = $citationArgs;
$citationArgsJson['return'] = 'json';
$templateMgr->assign([
'citation' => $citation,
'citationArgs' => $citationArgs,
'citationArgsJson' => $citationArgsJson,
'citationStyles' => $this->getEnabledCitationStyles($contextId),
'citationDownloads' => $this->getEnabledCitationDownloads($contextId),
]);
$templateMgr->addStyleSheet(
'cslPluginStyles',
$request->getBaseUrl() . '/' . $this->getPluginPath() . '/css/citationStyleLanguagePlugin.css',
[
'priority' => TemplateManager::STYLE_SEQUENCE_LAST,
'contexts' => ['frontend'],
'inline' => false,
]
);
$templateMgr->addJavaScript(
'citationStyleLanguage',
$request->getBaseUrl() . '/' . $this->getPluginPath() . '/js/articleCitation.js'
);
return false;
}
public function addCitationMarkup(string $hookName, array $args): bool
{
$smarty = &$args[1];
$output = &$args[2];
$output .= $smarty->fetch($this->getTemplateResource('citationblock.tpl'));
return false;
}
/**
* Get a specified citation for a given article, book or chapter
*
* This citation format follows the csl-json schema and takes some direction
* from existing CSL mappings documented by Zotero and Mendeley.
*
* @see CSL-json schema https://github.com/citation-style-language/schema#csl-json-schema
* @see Zotero's mappings https://aurimasv.github.io/z2csl/typeMap.xml#map-journalArticle
* @see Mendeley's mappings http://support.mendeley.com/customer/portal/articles/364144-csl-type-mapping
*
* @param string $citationStyle Name of the citation style to use.
* @param ?Issue $issue Optional. Will fetch from db if not passed.
* @param ?Publication $publication Optional. A particular version
* @param ?Chapter $chapter Optional. OMP chapter pages only.
*
* @throws Exception
*
* @hook CitationStyleLanguage::citation [[&$citationData, &$citationStyle, $submission, $issue, $context, $publication]]
*/
public function getCitation(PKPRequest $request, Submission $submission, string $citationStyle = 'apa', ?Issue $issue = null, ?Publication $publication = null, ?Chapter $chapter = null): string
{
$publication ??= $submission->getCurrentPublication();
$context = $request->getContext();
if (!$chapter) {
$chapter = $this->application === 'omp' ? $this->getChapter($request, $publication) : null;
}
$this->setDocumentType($chapter);
$keywords = Repo::controlledVocab()->getBySymbolic(
ControlledVocab::CONTROLLED_VOCAB_SUBMISSION_KEYWORD,
Application::ASSOC_TYPE_PUBLICATION,
$publication->getId()
);
$citationData = new stdClass();
if ($this->isArticle) {
$citationData->type = ($this->application === 'ojs2' ? 'article-journal' : 'article');
$citationData->risType = 'JOUR';
$citationData->id = $submission->getId();
$citationData->title = $publication->getLocalizedFullTitle();
$citationData->{'container-title'} = $context->getLocalizedName();
$issueId = $publication->getData('issueId');
$issue ??= $issueId ? Repo::issue()->get($issueId) : null;
if ($issue) {
$citationData->volume = $issue->getData('volume');
// Zotero prefers issue and Mendeley uses `number` to store revisions
$citationData->issue = $issue->getData('number');
}
if ($sectionId = $publication->getData('sectionId')) {
$section = Repo::section()->get($sectionId);
if ($section && !$section->getHideTitle()) {
$citationData->section = htmlspecialchars($section->getTitle($context->getPrimaryLocale()));
}
}
$citationData->keywords = $keywords[Locale::getLocale()] ?? [];
if ($publication->getData('pages')) {
$citationData->page = htmlspecialchars($publication->getData('pages'));
}
$citationData->abstract = PKPString::html2text($publication->getLocalizedData('abstract'));
$citationData = $this->setArticleAuthors($citationData, $publication, $context);
$citationData->URL = $request->getDispatcher()->url($request, PKPApplication::ROUTE_PAGE, null, $this->getPublicationTypeUrlPath(), 'view', [$publication->getData('urlPath') ?? $submission->getId()], urlLocaleForPage: '');
if ($publication->getDoi()) {
$citationData->DOI = $publication->getDoi();
}
} elseif ($this->isBook) {
$citationData->type = 'book';
$citationData->risType = 'BOOK';
$citationData->id = $submission->getId();
$citationData->title = $publication->getLocalizedFullTitle();
$citationData = $this->addSeriesInformation($citationData, $publication);
$citationData->publisher = $context->getLocalizedName();
$citationData->keywords = $keywords[Locale::getLocale()] ?? [];
if ($publication->getData('pages')) {
$citationData->page = htmlspecialchars($publication->getData('pages'));
}
$citationData->abstract = PKPString::html2text($publication->getLocalizedData('abstract'));
$citationData->serialNumber = $this->getSerialNumber($publication);
$citationData = $this->setBookAuthors($citationData, $publication, $context);
$citationData->URL = $request->getDispatcher()->url($request, PKPApplication::ROUTE_PAGE, null, $this->getPublicationTypeUrlPath(), 'book', [$publication->getData('urlPath') ?? $submission->getId()], urlLocaleForPage: '');
if ($publication->getDoi()) {
$citationData->DOI = $publication->getDoi();
}
} elseif ($this->isChapter) {
$citationData->type = 'chapter';
$citationData->risType = 'CHAP';
$citationData->id = $chapter->getSourceChapterId();
$citationData->title = $chapter->getLocalizedFullTitle();
$citationData->{'container-title'} = $publication->getLocalizedFullTitle();
$citationData = $this->addSeriesInformation($citationData, $publication);
$citationData->publisher = $context->getLocalizedName();
if ($chapter->getPages()) {
$citationData->page = htmlspecialchars($chapter->getPages());
}
$citationData->abstract = htmlspecialchars(strip_tags($chapter->getLocalizedData('abstract')));
$citationData->serialNumber = $this->getSerialNumber($publication);
$citationData = $this->setBookChapterAuthors($citationData, $publication, $context, $chapter);
$citationData->URL = $request->getDispatcher()->url($request, PKPApplication::ROUTE_PAGE, null, $this->getPublicationTypeUrlPath(), 'book', [$publication->getData('urlPath') ?? $submission->getId(), 'chapter', $chapter->getSourceChapterId()], urlLocaleForPage: '');
if ($chapter->getDoi()) {
$citationData->DOI = $chapter->getDoi();
}
} else {
throw new Exception('Unknown submission content type!');
}
$citationData->languages = collect($publication->getData('galleys') ?? [])
->map(fn ($g) => $g->getData('locale'))
->push($submission->getData('locale'))
->filter()->unique()->sort()->values()->toArray();
$citationData->{'publisher-place'} = $this->getSetting($context->getId(), 'publisherLocation');
$abbreviation = $context->getData('abbreviation', $context->getPrimaryLocale()) ?? $context->getData('acronym', $context->getPrimaryLocale());
if ($abbreviation) {
$citationData->{'container-title-short'} = $abbreviation;
}
$citationData->accessed = new stdClass();
$citationData->accessed->raw = date('Y-m-d');
if ($publication->getData('datePublished')) {
$citationData->issued = new stdClass();
$citationData->issued->raw = htmlspecialchars($publication->getData('datePublished'));
$publishedPublications = $submission->getPublishedPublications();
if (count($publishedPublications) > 1) {
$originalPublication = array_reduce($publishedPublications, function ($a, $b) {
return $a && $a->getId() < $b->getId() ? $a : $b;
});
$originalDate = $originalPublication->getData('datePublished');
if ($originalDate && $originalDate !== $publication->getData('datePublished')) {
$citationData->{'original-date'} = new stdClass();
$citationData->{'original-date'}->raw = htmlspecialchars($originalPublication->getData('datePublished'));
}
}
} elseif ($this->isArticle && $issue?->getPublished()) {
$citationData->issued = new stdClass();
$citationData->issued->raw = htmlspecialchars($issue->getDatePublished());
}
Hook::call('CitationStyleLanguage::citation', [&$citationData, &$citationStyle, $submission, $issue, $context, $publication]);
$citation = '';
// Determine whether to use citeproc-php or a custom template to render
// the citation
$styleConfig = $this->getCitationStyleConfig($citationStyle);
if (!empty($styleConfig)) {
if (!empty($styleConfig['useTemplate'])) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'citationData' => $citationData,
'citationStyle' => $citationStyle,
'publication' => $publication,
]);
switch ($this->application) {
case 'ops':
case 'ojs2':
$templateMgr->assign([
'article' => $submission,
'issue' => $issue,
'journal' => $context,
]);
break;
case 'omp':
$templateMgr->assign([
'book' => $submission,
'press' => $context,
]);
break;
default: throw new Exception('Unknown application!');
}
$citation = $templateMgr->fetch($styleConfig['useTemplate']);
} else {
$style = $this->loadStyle($styleConfig);
if ($style) {
// Determine what locale to use. Fall back English if none found.
$tryLocale = $this->getCSLLocale(Locale::getLocale(), 'en-US');
// Clickable URL and DOI including affixes
$additionalMarkup = [
'DOI' => [
'function' => function ($item, $renderedValue) {
$doiWithUrl = 'https://doi.org/' . $item->DOI;
if (str_contains($renderedValue, $doiWithUrl)) {
$doiLink = '<a href="' . $doiWithUrl . '">' . $doiWithUrl . '</a>';
return str_replace($doiWithUrl, $doiLink, $renderedValue);
} else {
$doiLink = '<a href="' . $doiWithUrl . '">' . $item->DOI . '</a>';
return str_replace($item->DOI, $doiLink, $renderedValue);
}
},
'affixes' => true
],
'URL' => [
'function' => function ($item, $renderedValue) {
return '<a href="' . $item->URL . '">' . $renderedValue . '</a>';
},
'affixes' => false
],
];
$citeProc = new CiteProc($style, $tryLocale, $additionalMarkup);
$citation = $citeProc->render([$citationData], 'bibliography');
}
}
}
return $citation;
}
/**
* Load a CSL style and return the contents as a string
*/
public function loadStyle(array $styleConfig): false|string
{
$path = empty($styleConfig['useCsl'])
? $this->getPluginPath() . '/citation-styles/' . $styleConfig['id'] . '.csl'
: $styleConfig['useCsl'];
return file_get_contents($path);
}
/**
* Download a citation format
*
* Downloadable citation formats can be used to import into third-party
* software.
*
* @param string $citationStyle Name of the citation style to use.
* @param Issue $issue Optional. Will fetch from db if not passed.
* @param $publication Publication Optional.
* @param $chapter Chapter Optional. OMP chapter pages only.
*/
public function downloadCitation(PKPRequest $request, Submission $submission, string $citationStyle = 'ris', ?Issue $issue = null, ?Publication $publication = null, ?Chapter $chapter = null)
{
if ($this->isArticle) {
$issueId = $submission->getCurrentPublication()->getData('issueId');
$issue ??= $issueId ? Repo::issue()->get($issueId) : null;
}
$styleConfig = $this->getCitationStyleConfig($citationStyle);
if (empty($styleConfig)) {
return false;
}
$citation = trim(PKPString::html2text($this->getCitation($request, $submission, $citationStyle, $issue, $publication, $chapter)));
// TODO this is likely going to cause an error in a citation some day,
// but is necessary to get the .ris downloadable format working. The
// CSL language doesn't seem to offer a way to indicate a line break.
// See: https://github.com/citation-style-language/styles/issues/2831
$citation = str_replace('\n', "\n", $citation);
$encodedFilename = urlencode(substr(($publication ? $publication->getLocalizedTitle() : ''), 0, 60)) . '.' . $styleConfig['fileExtension'];
header("Content-Disposition: attachment; filename*=UTF-8''{$encodedFilename}");
header('Content-Type: ' . $styleConfig['contentType']);
echo $citation;
exit;
}
/**
* @see Plugin::getActions()
*/
public function getActions($request, $actionArgs)
{
$actions = parent::getActions($request, $actionArgs);
if (!$this->getEnabled()) {
return $actions;
}
$router = $request->getRouter();
$linkAction = new LinkAction(
'settings',
new AjaxModal(
$router->url(
$request,
null,
null,
'manage',
null,
[
'verb' => 'settings',
'plugin' => $this->getName(),
'category' => 'generic',
]
),
$this->getDisplayName()
),
__('manager.plugins.settings'),
null
);
array_unshift($actions, $linkAction);
return $actions;
}
/**
* @see Plugin::manage()
*/
public function manage($args, $request)
{
switch ($request->getUserVar('verb')) {
case 'settings':
$form = new CitationStyleLanguageSettingsForm($this);
if ($request->getUserVar('save')) {
$form->readInputData();
if ($form->validate()) {
$form->execute();
return new JSONMessage(true);
}
}
$form->initData();
return new JSONMessage(true, $form->fetch($request));
}
return parent::manage($args, $request);
}
/**
* Route requests for the citation styles to custom page handler
*/
public function setPageHandler(string $hookName, array $params): bool
{
$page = &$params[0];
$handler = &$params[3];
if ($this->getEnabled() && $page === 'citationstylelanguage') {
$handler = new CitationStyleLanguageHandler($this);
return true;
}
return false;
}
public function getEditorGroups(int $contextId): array
{
return $this->getSetting($contextId, 'groupEditor') ?? [];
}
public function getTranslatorGroups(int $contextId): array
{
return $this->getSetting($contextId, 'groupTranslator') ?? [];
}
public function getAuthorGroups(int $contextId): array
{
return $this->getSetting($contextId, 'groupAuthor') ?? [];
}
public function getChapterAuthorGroups(int $contextId): array
{
return $this->getSetting($contextId, 'groupChapterAuthor') ?? [];
}
public function getSerialNumber(Publication $publication): array
{
$serialNumber = [];
$publicationFormats = $publication->getData('publicationFormats');
/** @var PublicationFormat $publicationFormat */
foreach ($publicationFormats as $publicationFormat) {
if ($publicationFormat->getIsApproved()) {
$identificationCodes = $publicationFormat->getIdentificationCodes();
$identificationCodes = $identificationCodes->toArray();
foreach ($identificationCodes as $identificationCode) {
$serialNumber[] = htmlspecialchars($identificationCode->getValue());
}
}
}
return $serialNumber;
}
protected function addSeriesInformation(stdClass $citationData, Publication $publication): stdClass
{
$seriesId = $publication->getData('seriesId');
$series = $seriesId ? Repo::section()->get($seriesId) : null;
if (!$series) {
return $citationData;
}
$citationData->{'collection-title'} = trim($series->getLocalizedFullTitle());
$citationData->volume = $publication->getData('seriesPosition');
$citationData->{'collection-editor'} = htmlspecialchars($series->getEditorsString());
$onlineISSN = $series->getOnlineISSN();
if (!empty($onlineISSN)) {
$citationData->serialNumber[] = htmlspecialchars($onlineISSN);
}
$printISSN = $series->getPrintISSN();
if (!empty($printISSN)) {
$citationData->serialNumber[] = htmlspecialchars($printISSN);
}
return $citationData;
}
protected function getChapter(PKPRequest $request, Publication $publication): ?Chapter
{
$args = $request->getRequestedArgs();
if (isset($args[1], $args[2]) && $args[1] === 'chapter') {
$key = 2;
} elseif (isset($args[1], $args[3], $args[4]) && $args[1] === 'version' && $args[3] === 'chapter') {
$key = 4;
} else {
return null;
}
$chapterId = (int) $args[$key];
if ($chapterId > 0) {
/** @var ChapterDAO */
$chapterDao = DAORegistry::getDAO('ChapterDAO');
return $chapterDao->getBySourceChapterAndPublication($chapterId, $publication->getId());
}
return null;
}
/**
* @throws Exception
*/
protected function setDocumentType($chapter): void
{
switch ($this->application) {
case 'ops':
case 'ojs2':
$this->isArticle = true;
break;
case 'omp':
if ($chapter) {
$this->isChapter = true;
} else {
$this->isBook = true;
}
break;
default: throw new Exception('Unknown application!');
}
}
protected function setArticleAuthors($citationData, $publication, $context): stdClass
{
$authors = $publication->getData('authors');
$authorsGroups = $this->getAuthorGroups($context->getId());
$translatorsGroups = $this->getTranslatorGroups($context->getId());
if (count($authors)) {
/** @var Author $author */
foreach ($authors as $author) {
$currentAuthor = new stdClass();
if (empty($author->getLocalizedFamilyName())) {
$currentAuthor->family = htmlspecialchars($author->getLocalizedGivenName());
} else {
$currentAuthor->family = htmlspecialchars($author->getLocalizedFamilyName());
$currentAuthor->given = htmlspecialchars($author->getLocalizedGivenName());
}
$userGroupId = $author->getUserGroupId();
switch (true) {
case in_array($userGroupId, $translatorsGroups):
if (!isset($citationData->translator)) {
$citationData->translator = [];
}
$citationData->translator[] = $currentAuthor;
break;
case in_array($userGroupId, $authorsGroups):
if (!isset($citationData->author)) {
$citationData->author = [];
}
$citationData->author[] = $currentAuthor;
break;
default:
if (!isset($citationData->author)) {
$citationData->author = [];
}
break;
}
}
}
return $citationData;
}
protected function setBookAuthors($citationData, $publication, $context): stdClass
{
$authors = $publication->getData('authors');
$authorsGroups = $this->getAuthorGroups($context->getId());
$editorsGroups = $this->getEditorGroups($context->getId());
$translatorsGroups = $this->getTranslatorGroups($context->getId());
if (count($authors)) {
/** @var Author $author */
foreach ($authors as $author) {
$currentAuthor = new stdClass();
if (empty($author->getLocalizedFamilyName())) {
$currentAuthor->family = htmlspecialchars($author->getLocalizedGivenName());
} else {
$currentAuthor->family = htmlspecialchars($author->getLocalizedFamilyName());
$currentAuthor->given = htmlspecialchars($author->getLocalizedGivenName());
}
$userGroupId = $author->getUserGroupId();
switch (true) {
case in_array($userGroupId, $editorsGroups):
if (!isset($citationData->editor)) {
$citationData->editor = [];
}
$citationData->editor[] = $currentAuthor;
break;
case in_array($userGroupId, $translatorsGroups):
if (!isset($citationData->translator)) {
$citationData->translator = [];
}
$citationData->translator[] = $currentAuthor;
break;
case in_array($userGroupId, $authorsGroups):
if (!isset($citationData->author)) {
$citationData->author = [];
}
$citationData->author[] = $currentAuthor;
break;
default:
if (!isset($citationData->author)) {
$citationData->author = [];
}
break;
}
}
}
return $citationData;
}
protected function setBookChapterAuthors($citationData, $publication, $context, $chapter): stdClass
{
$chapterAuthorGroups = $this->getChapterAuthorGroups($context->getId());
$chapterAuthors = $chapter->getAuthors();
/** @var Author $chapterAuthor */
foreach ($chapterAuthors as $chapterAuthor) {
$currentAuthor = new stdClass();
if (empty($chapterAuthor->getLocalizedFamilyName())) {
$currentAuthor->family = htmlspecialchars($chapterAuthor->getLocalizedGivenName());
} else {
$currentAuthor->family = htmlspecialchars($chapterAuthor->getLocalizedFamilyName());
$currentAuthor->given = htmlspecialchars($chapterAuthor->getLocalizedGivenName());
}
$userGroupId = $chapterAuthor->getUserGroupId();
if (in_array($userGroupId, $chapterAuthorGroups)) {
$citationData->author[] = $currentAuthor;
}
}
$bookAuthors = $publication->getData('authors');
$authorsGroups = $this->getAuthorGroups($context->getId());
$editorsGroups = $this->getEditorGroups($context->getId());
$translatorsGroups = $this->getTranslatorGroups($context->getId());
if (count($bookAuthors)) {
/** @var Author $bookAuthor */
foreach ($bookAuthors as $bookAuthor) {
$currentAuthor = new stdClass();
if (empty($bookAuthor->getLocalizedFamilyName())) {
$currentAuthor->family = htmlspecialchars($bookAuthor->getLocalizedGivenName());
} else {
$currentAuthor->family = htmlspecialchars($bookAuthor->getLocalizedFamilyName());
$currentAuthor->given = htmlspecialchars($bookAuthor->getLocalizedGivenName());
}
$userGroupId = $bookAuthor->getUserGroupId();
switch (true) {
case in_array($userGroupId, $editorsGroups):
if (!isset($citationData->editor)) {
$citationData->editor = [];
}
$citationData->editor[] = $currentAuthor;
break;
case in_array($userGroupId, $translatorsGroups):
if (!isset($citationData->translator)) {
$citationData->translator = [];
}
$citationData->translator[] = $currentAuthor;
break;
case in_array($userGroupId, $authorsGroups):
if (!isset($citationData->{'container-author'})) {
$citationData->{'container-author'} = [];
}
$citationData->{'container-author'}[] = $currentAuthor;
break;
default:
if (!isset($citationData->author)) {
$citationData->author = [];
}
break;
}
}
}
if (isset($citationData->{'container-author'})) {
$diffChapterAuthorsAuthors = array_udiff($citationData->{'container-author'}, $citationData->author, $this->compareAuthors(...));
if (count($diffChapterAuthorsAuthors) === 0) {
$citationData->{'container-author'} = [];
}
}
return $citationData;
}
protected function compareAuthors($a, $b): int
{
return 0 === strcmp($a->family, $b->family) && 0 === strcmp($a->given, $b->given) ? 0 : 1;
}
/**
* Find the best match for a CSL locale.
*
* @param $locale Weblate locale.
* @param $defaultLocale A locale code to use as default. This should already be sanitized.
*
* @return string A language code that's available in the CSL library.
*/
public function getCSLLocale(string $locale, string $defaultLocale = 'en-US'): string
{
$prefix = $this->getPluginPath() . '/lib/vendor/citation-style-language/locales/locales-';
$suffix = '.xml';
$preferences = [
'de' => 'de-DE',
'en' => 'en-US',
'es' => 'es-ES',
'fr' => 'fr-FR',
'pt' => 'pt-PT',
];
// Determine the language and region we're looking for from $locale
$language = \Locale::getPrimaryLanguage($locale);
$region = \Locale::getRegion($locale) ?? null;
$localeAndRegion = $language . ($region ? "-{$region}" : '');
// Get a list of available options from the filesystem.
$availableLocaleFiles = glob("{$prefix}*{$suffix}");
// 1. Look for an exact match and return it.
if (in_array("{$prefix}{$locale}{$suffix}", $availableLocaleFiles)) {
return $locale;
}
// 2. Look in the preference list for a preferred fallback.
if ($preference = $preferences[$localeAndRegion] ?? false) {
return $preference;
}
// 3. Find the first match by language.
foreach ($availableLocaleFiles as $filename) {
if (strpos($filename, "{$prefix}{$language}-") === 0) {
return substr($filename, strlen($prefix), -strlen($suffix));
}
}
// 4. Use the supplied default.
return $defaultLocale;
}
}
GitHub Events
Total
- Issues event: 7
- Watch event: 2
- Issue comment event: 10
- Push event: 19
- Pull request review event: 15
- Pull request review comment event: 9
- Pull request event: 28
- Fork event: 2
- Create event: 5
Last Year
- Issues event: 7
- Watch event: 2
- Issue comment event: 10
- Push event: 19
- Pull request review event: 15
- Pull request review comment event: 9
- Pull request event: 28
- Fork event: 2
- Create event: 5
Issues and Pull Requests
Last synced: 10 months ago
All Time
- Total issues: 6
- Total pull requests: 24
- Average time to close issues: 6 months
- Average time to close pull requests: 11 days
- Total issue authors: 6
- Total pull request authors: 9
- Average comments per issue: 1.33
- Average comments per pull request: 0.04
- Merged pull requests: 16
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 4
- Pull requests: 16
- Average time to close issues: 1 day
- Average time to close pull requests: 4 days
- Issue authors: 4
- Pull request authors: 6
- Average comments per issue: 0.75
- Average comments per pull request: 0.0
- Merged pull requests: 9
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- mpbraendle (2)
- touhidurabir (1)
- viktorholzwert (1)
- kaitlinnewson (1)
- bozana (1)
- jmnobrega (1)
- Yemoon (1)
Pull Request Authors
- bozana (7)
- touhidurabir (7)
- kaitlinnewson (4)
- jyhein (1)
- Hafsa-Naeem (1)
- thuanhuynhh (1)
- ronste (1)
- Vitaliy-1 (1)
- asmecher (1)
- ajnyga (1)
- ewhanson (1)
Top Labels
Issue Labels
Pull Request Labels
Dependencies
- citation-style-language/locales @dev
- seboettg/citeproc-php ^2.2.0
- citation-style-language/locales 1.0.0
- myclabs/php-enum 1.7.7
- seboettg/citeproc-php v2.2.5
- seboettg/collection v2.1.1
- symfony/polyfill-mbstring v1.24.0