345 lines
12 KiB
PHP
345 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* @file classes/doi/Repository.php
|
|
*
|
|
* Copyright (c) 2014-2022 Simon Fraser University
|
|
* Copyright (c) 2000-2022 John Willinsky
|
|
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
|
*
|
|
* @class Repository
|
|
*
|
|
* @brief A repository to find and manage DOIs.
|
|
*/
|
|
|
|
namespace APP\doi;
|
|
|
|
use APP\core\Request;
|
|
use APP\facades\Repo;
|
|
use APP\issue\Issue;
|
|
use APP\jobs\doi\DepositIssue;
|
|
use APP\journal\Journal;
|
|
use APP\journal\JournalDAO;
|
|
use APP\plugins\PubIdPlugin;
|
|
use APP\publication\Publication;
|
|
use APP\submission\Submission;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\App;
|
|
use PKP\context\Context;
|
|
use PKP\core\DataObject;
|
|
use PKP\db\DAORegistry;
|
|
use PKP\doi\Collector;
|
|
use PKP\doi\exceptions\DoiException;
|
|
use PKP\galley\Galley;
|
|
use PKP\services\PKPSchemaService;
|
|
use PKP\submission\Representation;
|
|
|
|
class Repository extends \PKP\doi\Repository
|
|
{
|
|
public const TYPE_ISSUE = 'issue';
|
|
|
|
public const CUSTOM_ISSUE_PATTERN = 'doiIssueSuffixPattern';
|
|
|
|
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
|
|
{
|
|
parent::__construct($dao, $request, $schemaService);
|
|
}
|
|
|
|
public function getCollector(): Collector
|
|
{
|
|
return App::makeWith(Collector::class, ['dao' => $this->dao]);
|
|
}
|
|
|
|
/**
|
|
* Create a DOI for the given publication.
|
|
*
|
|
* @throws DoiException
|
|
*/
|
|
public function mintPublicationDoi(Publication $publication, Submission $submission, Context $context): int
|
|
{
|
|
// Default suffix does not rely on any other metadata
|
|
if ($context->getData(Context::SETTING_DOI_SUFFIX_TYPE) === Repo::doi()::SUFFIX_DEFAULT) {
|
|
return $this->mintAndStoreDoi($context, $this->generateDefaultSuffix());
|
|
}
|
|
|
|
// If not using default suffix, additional checks are required
|
|
$issueId = $publication->getData('issueId');
|
|
if ($issueId === null) {
|
|
throw new DoiException(
|
|
DoiException::PUBLICATION_MISSING_ISSUE,
|
|
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
|
$publication->getLocalizedFullTitle()
|
|
);
|
|
}
|
|
|
|
$issue = Repo::issue()->get($publication->getData('issueId'));
|
|
if ($issue === null) {
|
|
throw new DoiException(
|
|
DoiException::PUBLICATION_MISSING_ISSUE,
|
|
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
|
$publication->getLocalizedFullTitle()
|
|
);
|
|
} elseif ($issue && $context->getId() != $issue->getJournalId()) {
|
|
throw new DoiException(
|
|
DoiException::PUBLICATION_MISSING_ISSUE,
|
|
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
|
$publication->getLocalizedFullTitle()
|
|
);
|
|
}
|
|
|
|
$doiSuffix = $this->generateSuffixPattern($publication, $context, $context->getData(Context::SETTING_DOI_SUFFIX_TYPE), $issue, $submission);
|
|
|
|
return $this->mintAndStoreDoi($context, $doiSuffix);
|
|
}
|
|
|
|
/**
|
|
* Create a DOI for the given galley
|
|
*
|
|
* @throws DoiException
|
|
*/
|
|
public function mintGalleyDoi(Galley $galley, Publication $publication, Submission $submission, Context $context): int
|
|
{
|
|
// Default suffix does not rely on any other metadata
|
|
if ($context->getData(Context::SETTING_DOI_SUFFIX_TYPE) === Repo::doi()::SUFFIX_DEFAULT) {
|
|
return $this->mintAndStoreDoi($context, $this->generateDefaultSuffix());
|
|
}
|
|
|
|
// If not using default suffix, additional checks are required
|
|
$issue = Repo::issue()->getBySubmissionId($submission->getId());
|
|
|
|
if ($issue === null) {
|
|
throw new DoiException(
|
|
DoiException::REPRESENTATION_MISSING_ISSUE,
|
|
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
|
$galley->getLabel()
|
|
);
|
|
} elseif ($issue && $context->getId() != $issue->getJournalId()) {
|
|
throw new DoiException(
|
|
DoiException::REPRESENTATION_MISSING_ISSUE,
|
|
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
|
$galley->getLabel()
|
|
);
|
|
}
|
|
|
|
$doiSuffix = $this->generateSuffixPattern($galley, $context, $context->getData(Context::SETTING_DOI_SUFFIX_TYPE), $issue, $submission, $galley);
|
|
|
|
return $this->mintAndStoreDoi($context, $doiSuffix);
|
|
}
|
|
|
|
/**
|
|
* Create a DOI for the given Issue
|
|
*
|
|
* @throws DoiException
|
|
*/
|
|
public function mintIssueDoi(Issue $issue, Context $context): int
|
|
{
|
|
if ($context->getId() != $issue->getJournalId()) {
|
|
throw new DoiException(
|
|
DoiException::INCORRECT_ISSUE_CONTEXT,
|
|
$issue->getLocalizedTitle(),
|
|
$issue->getLocalizedTitle()
|
|
);
|
|
}
|
|
|
|
// Default suffix does not rely on any other metadata
|
|
if ($context->getData(Context::SETTING_DOI_SUFFIX_TYPE) === Repo::doi()::SUFFIX_DEFAULT) {
|
|
return $this->mintAndStoreDoi($context, $this->generateDefaultSuffix());
|
|
}
|
|
|
|
// If not using default suffix, use pattern generator
|
|
$doiSuffix = $this->generateSuffixPattern($issue, $context, $context->getData(Context::SETTING_DOI_SUFFIX_TYPE), $issue);
|
|
|
|
return $this->mintAndStoreDoi($context, $doiSuffix);
|
|
}
|
|
|
|
/**
|
|
* Handles updating issue DOI status when metadata changes
|
|
*
|
|
*/
|
|
public function issueUpdated(Issue $issue)
|
|
{
|
|
$doiIds = Repo::doi()->getDoisForIssue($issue->getId());
|
|
$this->dao->markStale($doiIds);
|
|
}
|
|
|
|
/**
|
|
* Generate a suffix using a provided pattern type
|
|
*
|
|
* @param string $patternType Repo::doi()::CUSTOM_SUFFIX_* constants
|
|
*
|
|
*/
|
|
protected function generateSuffixPattern(
|
|
DataObject $object,
|
|
Context $context,
|
|
string $patternType,
|
|
?Issue $issue = null,
|
|
?Submission $submission = null,
|
|
?Representation $representation = null
|
|
): string {
|
|
$doiSuffix = '';
|
|
switch ($patternType) {
|
|
case self::SUFFIX_CUSTOM_PATTERN:
|
|
$pubIdSuffixPattern = $this->getPubIdSuffixPattern($object, $context);
|
|
$doiSuffix = PubIdPlugin::generateCustomPattern($context, $pubIdSuffixPattern, $object, $issue, $submission, $representation);
|
|
break;
|
|
case self::SUFFIX_MANUAL:
|
|
break;
|
|
}
|
|
|
|
return $doiSuffix;
|
|
}
|
|
|
|
/**
|
|
* Gets all DOI IDs related to a submission
|
|
*
|
|
* @return array<int> DOI IDs
|
|
*/
|
|
public function getDoisForSubmission(int $submissionId): array
|
|
{
|
|
$doiIds = Collection::make();
|
|
|
|
$submission = Repo::submission()->get($submissionId);
|
|
/** @var Publication[] $publications */
|
|
$publications = $submission->getData('publications');
|
|
|
|
|
|
/** @var JournalDAO $contextDao */
|
|
$contextDao = DAORegistry::getDAO('JournalDAO');
|
|
/** @var Journal $context */
|
|
$context = $contextDao->getById($submission->getData('contextId'));
|
|
|
|
foreach ($publications as $publication) {
|
|
$publicationDoiId = $publication->getData('doiId');
|
|
if (!empty($publicationDoiId) && $context->isDoiTypeEnabled(self::TYPE_PUBLICATION)) {
|
|
$doiIds->add($publicationDoiId);
|
|
}
|
|
|
|
// Galleys
|
|
$galleys = Repo::galley()->getCollector()
|
|
->filterByPublicationIds(['publicationIds' => $publication->getId()])
|
|
->getMany();
|
|
|
|
foreach ($galleys as $galley) {
|
|
$galleyDoiId = $galley->getData('doiId');
|
|
if (!empty($galleyDoiId) && $context->isDoiTypeEnabled(self::TYPE_REPRESENTATION)) {
|
|
$doiIds->add($galleyDoiId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $doiIds->unique()->toArray();
|
|
}
|
|
|
|
/**
|
|
* Gets all DOIs associated with an issue
|
|
* NB: Assumes only enabled DOI types are allowed
|
|
*
|
|
* @param bool $enabledDoiTypesOnly
|
|
*
|
|
* @throws \Exception
|
|
*
|
|
* @return array<int> DOI IDs
|
|
*
|
|
*/
|
|
public function getDoisForIssue(int $issueId, $enabledDoiTypesOnly = false): array
|
|
{
|
|
$doiIds = [];
|
|
|
|
$issue = Repo::issue()->get($issueId);
|
|
$issueDoiId = $issue->getData('doiId');
|
|
|
|
/** @var JournalDAO $contextDao */
|
|
$contextDao = DAORegistry::getDAO('JournalDAO');
|
|
/** @var Journal $context */
|
|
$context = $contextDao->getById($issue->getData('journalId'));
|
|
|
|
if (!empty($issueDoiId)) {
|
|
if ($enabledDoiTypesOnly == false || ($enabledDoiTypesOnly && $context->isDoiTypeEnabled(self::TYPE_ISSUE))) {
|
|
$doiIds[] = $issueDoiId;
|
|
}
|
|
}
|
|
return $doiIds;
|
|
}
|
|
|
|
/**
|
|
* Schedules DOI deposits with the active registration agency for all valid and
|
|
* unregistered/stale publication items. Items are added as a queued job to be
|
|
* completed asynchronously.
|
|
*
|
|
*
|
|
*/
|
|
public function depositAll(Context $context)
|
|
{
|
|
parent::depositAll($context);
|
|
if (in_array(Repo::doi()::TYPE_ISSUE, $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ?? [])) {
|
|
// If there is no configured registration agency, nothing can be deposited.
|
|
$agency = $context->getConfiguredDoiAgency();
|
|
if (!$agency) {
|
|
return;
|
|
}
|
|
/** @var DAO */
|
|
$dao = $this->dao;
|
|
$issuesCollection = $dao->getAllDepositableIssueIds($context);
|
|
$issueData = $issuesCollection->reduce(function ($carry, $item) {
|
|
$carry['issueIds'][] = $item->issue_id;
|
|
$carry['doiIds'][] = $item->doi_id;
|
|
return $carry;
|
|
}, ['issueIds' => [], 'doiIds' => []]);
|
|
|
|
// Schedule/queue jobs for issues
|
|
foreach ($issueData['issueIds'] as $issueId) {
|
|
dispatch(new DepositIssue($issueId, $context, $agency));
|
|
}
|
|
|
|
// Mark issue DOIs as submitted
|
|
Repo::doi()->markSubmitted($issueData['doiIds']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether a DOI object is referenced by ID on any pub objects for a given pub object type.
|
|
*
|
|
* @param string $pubObjectType One of Repo::doi()::TYPE_* constants
|
|
*/
|
|
public function isAssigned(int $doiId, string $pubObjectType): bool
|
|
{
|
|
$isAssigned = match ($pubObjectType) {
|
|
Repo::doi()::TYPE_REPRESENTATION => Repo::galley()
|
|
->getCollector()
|
|
->filterByDoiIds([$doiId])
|
|
->getIds()
|
|
->count(),
|
|
default => false,
|
|
};
|
|
|
|
return $isAssigned || parent::isAssigned($doiId, $pubObjectType);
|
|
}
|
|
|
|
/**
|
|
* Get app-specific DOI type constants to check when scheduling deposit for submissions
|
|
*/
|
|
protected function getValidSubmissionDoiTypes(): array
|
|
{
|
|
return [
|
|
self::TYPE_PUBLICATION,
|
|
self::TYPE_REPRESENTATION,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Gets legacy, user-generated suffix pattern associated with object type and context
|
|
*
|
|
*
|
|
* @return mixed|null
|
|
*/
|
|
private function getPubIdSuffixPattern(DataObject $object, Context $context)
|
|
{
|
|
if ($object instanceof Issue) {
|
|
return $context->getData(Repo::doi()::CUSTOM_ISSUE_PATTERN);
|
|
} elseif ($object instanceof Representation) {
|
|
return $context->getData(Repo::doi()::CUSTOM_REPRESENTATION_PATTERN);
|
|
} else {
|
|
return $context->getData(Repo::doi()::CUSTOM_PUBLICATION_PATTERN);
|
|
}
|
|
}
|
|
}
|