439 lines
13 KiB
PHP
439 lines
13 KiB
PHP
<?php
|
||
|
||
/**
|
||
* @file classes/publication/PKPPublication.php
|
||
*
|
||
* Copyright (c) 2016-2021 Simon Fraser University
|
||
* Copyright (c) 2003-2021 John Willinsky
|
||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||
*
|
||
* @class PKPPublication
|
||
*
|
||
* @ingroup publication
|
||
*
|
||
* @see DAO
|
||
*
|
||
* @brief Base class for Publication.
|
||
*/
|
||
|
||
namespace PKP\publication;
|
||
|
||
use APP\author\Author;
|
||
use APP\facades\Repo;
|
||
use PKP\core\Core;
|
||
use PKP\core\PKPString;
|
||
use PKP\facades\Locale;
|
||
use PKP\userGroup\UserGroup;
|
||
|
||
class PKPPublication extends \PKP\core\DataObject
|
||
{
|
||
/**
|
||
* Get the default/fall back locale the values should exist for
|
||
*/
|
||
public function getDefaultLocale(): ?string
|
||
{
|
||
return $this->getData('locale');
|
||
}
|
||
|
||
/**
|
||
* Combine the localized title, prefix and subtitle
|
||
*
|
||
* @param string $preferredLocale Override the publication's default locale and return the title in a specified locale.
|
||
* @param string $format Define the return data format as text or html
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getLocalizedFullTitle($preferredLocale = null, string $format = 'text')
|
||
{
|
||
$fullTitle = $this->getLocalizedTitle($preferredLocale, $format);
|
||
$subtitle = $this->getLocalizedSubTitle($preferredLocale, $format);
|
||
|
||
if ($subtitle) {
|
||
return PKPString::concatTitleFields([$fullTitle, $subtitle]);
|
||
}
|
||
|
||
return $fullTitle;
|
||
}
|
||
|
||
/**
|
||
* Return the combined prefix, title and subtitle for all locales
|
||
*
|
||
* @param string $format Define the return data format as text or html
|
||
*
|
||
* @return array
|
||
*/
|
||
public function getFullTitles(string $format = 'text')
|
||
{
|
||
$allTitles = (array) $this->getData('title');
|
||
$return = [];
|
||
foreach ($allTitles as $locale => $title) {
|
||
if (!$title) {
|
||
continue;
|
||
}
|
||
$return[$locale] = $this->getLocalizedFullTitle($locale, $format);
|
||
}
|
||
return $return;
|
||
}
|
||
|
||
/**
|
||
* Combine the localized title and prefix
|
||
*
|
||
* @param string $preferredLocale Override the publication's default locale and return the title in a specified locale.
|
||
* @param string $format Define the return data format as text or html
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getLocalizedTitle($preferredLocale = null, string $format = 'text')
|
||
{
|
||
$usedLocale = null;
|
||
$title = $this->getLocalizedData('title', $preferredLocale, $usedLocale);
|
||
$prefix = $this->getData('prefix', $usedLocale);
|
||
|
||
switch (strtolower($format)) {
|
||
case 'html':
|
||
// Title is already in HTML, prefix is in text. Convert prefix.
|
||
if ($prefix) {
|
||
$prefix = htmlspecialchars($prefix);
|
||
}
|
||
break;
|
||
case 'text':
|
||
// Title is in HTML, prefix is already in text. Convert title.
|
||
$title = strip_tags($title);
|
||
break;
|
||
default: throw new \Exception('Invalid format!');
|
||
}
|
||
|
||
if ($prefix) {
|
||
$title = $prefix . ' ' . $title;
|
||
}
|
||
|
||
return $title;
|
||
}
|
||
|
||
/**
|
||
* Get the localized sub title
|
||
*
|
||
* @param string $preferredLocale Override the publication's default locale and return the title in a specified locale.
|
||
* @param string $format Define the return data format as text or html
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getLocalizedSubTitle($preferredLocale = null, string $format = 'text')
|
||
{
|
||
$subTitle = $this->getLocalizedData('subtitle', $preferredLocale);
|
||
|
||
if ($subTitle) {
|
||
return strtolower($format) === 'text' ? strip_tags($subTitle) : $subTitle;
|
||
}
|
||
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* Return the combined title and prefix for all locales
|
||
*
|
||
* @param string $format Define the return data format as text or html
|
||
*
|
||
* @return array
|
||
*/
|
||
public function getTitles(string $format = 'text')
|
||
{
|
||
$allTitles = $this->getData('title');
|
||
$return = [];
|
||
foreach ($allTitles as $locale => $title) {
|
||
if (!$title) {
|
||
continue;
|
||
}
|
||
$return[$locale] = $this->getLocalizedTitle($locale, $format);
|
||
}
|
||
return $return;
|
||
}
|
||
|
||
/**
|
||
* Return all the sub titles
|
||
*
|
||
* @param string $format Define the return data format as text or html
|
||
*
|
||
* @return array
|
||
*/
|
||
public function getSubTitles(string $format = 'text')
|
||
{
|
||
$allSubTitles = $this->getData('subtitle');
|
||
$return = [];
|
||
foreach ($allSubTitles ?? [] as $locale => $subTitle) {
|
||
if (!$subTitle) {
|
||
continue;
|
||
}
|
||
$return[$locale] = $this->getLocalizedSubTitle($locale, $format);
|
||
}
|
||
return $return;
|
||
}
|
||
|
||
/**
|
||
* Combine author names and roles into a string
|
||
*
|
||
* Eg - Daniel Barnes, Carlo Corino (Author); Alan Mwandenga (Translator)
|
||
*
|
||
* @param \Traversable<UserGroup> $userGroups List of UserGroup objects
|
||
* @param bool $includeInBrowseOnly true if only the includeInBrowse Authors will be contained
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getAuthorString(\Traversable $userGroups, $includeInBrowseOnly = false)
|
||
{
|
||
$authors = $this->getData('authors');
|
||
|
||
if (empty($authors)) {
|
||
return '';
|
||
}
|
||
|
||
if ($includeInBrowseOnly) {
|
||
$authors = $authors->filter(function ($author, $key) {
|
||
return $author->getData('includeInBrowse');
|
||
});
|
||
}
|
||
|
||
$str = '';
|
||
$lastUserGroupId = null;
|
||
foreach ($authors as $author) {
|
||
if (!empty($str)) {
|
||
if ($lastUserGroupId != $author->getData('userGroupId')) {
|
||
foreach ($userGroups as $userGroup) {
|
||
if ($lastUserGroupId === $userGroup->getId()) {
|
||
if ($userGroup->getData('showTitle')) {
|
||
$str .= ' (' . $userGroup->getLocalizedData('name') . ')';
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
$str .= __('common.semicolonListSeparator');
|
||
} else {
|
||
$str .= __('common.commaListSeparator');
|
||
}
|
||
}
|
||
$str .= $author->getFullName();
|
||
$lastUserGroupId = $author->getUserGroupId();
|
||
}
|
||
|
||
// If there needs to be a trailing user group title, add it
|
||
if (isset($author)) {
|
||
foreach ($userGroups as $userGroup) {
|
||
if ($author->getData('userGroupId') === $userGroup->getId()) {
|
||
if ($userGroup->getData('showTitle')) {
|
||
$str .= ' (' . $userGroup->getLocalizedData('name') . ')';
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return $str;
|
||
}
|
||
|
||
/**
|
||
* Combine the author names into a shortened string
|
||
*
|
||
* Eg - Barnes, et al.
|
||
*
|
||
* @param string|null $defaultLocale
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getShortAuthorString($defaultLocale = null)
|
||
{
|
||
$authors = $this->getData('authors');
|
||
|
||
if (!$authors->count()) {
|
||
return '';
|
||
}
|
||
|
||
$firstAuthor = $authors->first();
|
||
|
||
$str = $firstAuthor->getLocalizedData('familyName', $defaultLocale);
|
||
if (!$str) {
|
||
$str = $firstAuthor->getLocalizedData('givenName', $defaultLocale);
|
||
}
|
||
|
||
if ($authors->count() > 1) {
|
||
return __('submission.shortAuthor', ['author' => $str], $defaultLocale);
|
||
}
|
||
|
||
return $str;
|
||
}
|
||
|
||
/**
|
||
* Get the primary contact
|
||
*
|
||
* @return Author|null
|
||
*/
|
||
public function getPrimaryAuthor()
|
||
{
|
||
if (empty($this->getData('authors'))) {
|
||
return null;
|
||
}
|
||
foreach ($this->getData('authors') as $author) {
|
||
if ($author->getId() === $this->getData('primaryContactId')) {
|
||
return $author;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Stamp the date of the last modification to the current time.
|
||
*/
|
||
public function stampModified()
|
||
{
|
||
return $this->setData('lastModified', Core::getCurrentDate());
|
||
}
|
||
|
||
/**
|
||
* Get the starting page of this publication
|
||
*
|
||
* Note the return type of string - this is not to be used for
|
||
* page counting.
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getStartingPage()
|
||
{
|
||
$ranges = $this->getPageArray();
|
||
$firstRange = array_shift($ranges);
|
||
if (is_array($firstRange)) {
|
||
return array_shift($firstRange);
|
||
}
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* Get ending page of a this publication
|
||
*
|
||
* Note the return type of string - this is not to be used for
|
||
* page counting.
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getEndingPage()
|
||
{
|
||
$ranges = $this->getPageArray();
|
||
$lastRange = array_pop($ranges);
|
||
$lastPage = is_array($lastRange) ? array_pop($lastRange) : '';
|
||
return $lastPage ?? '';
|
||
}
|
||
|
||
/**
|
||
* Get pages converted to a nested array of page ranges
|
||
*
|
||
* For example, pages of "pp. ii-ix, 9,15-18,a2,b2-b6" will return:
|
||
*
|
||
* [
|
||
* ['ii', 'ix'],
|
||
* ['9'],
|
||
* ['15', '18'],
|
||
* ['a2'],
|
||
* ['b2', 'b6'],
|
||
* ]
|
||
*
|
||
* @return array
|
||
*/
|
||
public function getPageArray()
|
||
{
|
||
$pages = $this->getData('pages') ?? '';
|
||
// Strip any leading word
|
||
if (preg_match('/^[[:alpha:]]+\W/', $pages)) {
|
||
// but don't strip a leading roman numeral
|
||
if (!preg_match('/^[MDCLXVUI]+\W/i', $pages)) {
|
||
// strip the word or abbreviation, including the period or colon
|
||
$pages = preg_replace('/^[[:alpha:]]+[:.]?/', '', $pages);
|
||
}
|
||
}
|
||
// strip leading and trailing space
|
||
$pages = trim($pages);
|
||
// shortcut the explode/foreach if the remainder is an empty value
|
||
if ($pages === '') {
|
||
return [];
|
||
}
|
||
// commas indicate distinct ranges
|
||
$ranges = explode(',', $pages);
|
||
$pageArray = [];
|
||
foreach ($ranges as $range) {
|
||
// hyphens (or double-hyphens) indicate range spans
|
||
$pageArray[] = array_map('trim', explode('-', str_replace(['--', '–'], '-', $range), 2));
|
||
}
|
||
return $pageArray;
|
||
}
|
||
|
||
/**
|
||
* Is the license for copyright on this publication a Creative Commons license?
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function isCCLicense()
|
||
{
|
||
return preg_match('/creativecommons\.org/i', $this->getData('licenseUrl'));
|
||
}
|
||
|
||
/**
|
||
* Helper method to fetch current DOI
|
||
*
|
||
*/
|
||
public function getDoi(): ?string
|
||
{
|
||
$doiObject = $this->getData('doiObject');
|
||
|
||
if (empty($doiObject)) {
|
||
return null;
|
||
} else {
|
||
return $doiObject->getData('doi');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get stored public ID of the publication
|
||
*
|
||
* This helper function is required by PKPPubIdPlugins.
|
||
* NB: To maintain backwards compatability, getDoi() is called from here
|
||
*
|
||
* @see Submission::getStoredPubId()
|
||
*/
|
||
public function getStoredPubId($pubIdType)
|
||
{
|
||
if ($pubIdType === 'doi') {
|
||
return $this->getDoi();
|
||
} else {
|
||
return $this->getData('pub-id::' . $pubIdType);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Set stored public issue id.
|
||
*
|
||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||
* 'other::something' if not part of the official NLM list
|
||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||
* @param string $pubId
|
||
*/
|
||
public function setStoredPubId($pubIdType, $pubId)
|
||
{
|
||
if ($pubIdType == 'doi') {
|
||
if ($doiObject = $this->getData('doiObject')) {
|
||
Repo::doi()->edit($doiObject, ['doi' => $pubId]);
|
||
} else {
|
||
$newDoiObject = Repo::doi()->newDataObject(
|
||
[
|
||
'doi' => $pubId,
|
||
'contextId' => Repo::submission()->get($this->getData('submissionId'))->getData('contextId')
|
||
]
|
||
);
|
||
$doiId = Repo::doi()->add($newDoiObject);
|
||
|
||
$this->setData('doiId', $doiId);
|
||
}
|
||
} else {
|
||
$this->setData('pub-id::' . $pubIdType, $pubId);
|
||
}
|
||
}
|
||
}
|
||
if (!PKP_STRICT_MODE) {
|
||
class_alias('\PKP\publication\PKPPublication', '\PKPPublication');
|
||
}
|