first commit
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user