[ Mini Kiebo ]
Server: Windows NT DESKTOP-5B8S0D4 6.2 build 9200 (Windows 8 Professional Edition) i586
Path:
D:
/
Backup
/
05122024
/
htdocs
/
jurnal-kesmas
/
v1
/
lib
/
pkp
/
classes
/
doi
/
[
Home
]
File: Repository.php
<?php /** * @file classes/doi/Repository.php * * Copyright (c) 2014-2020 Simon Fraser University * Copyright (c) 2000-2020 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 PKP\doi; use APP\core\Request; use APP\core\Services; use APP\facades\Repo; use Exception; use Illuminate\Support\Facades\App; use PKP\context\Context; use PKP\core\PKPString; use PKP\doi\exceptions\DoiException; use PKP\jobs\doi\DepositSubmission; use PKP\plugins\Hook; use PKP\services\PKPSchemaService; use PKP\validation\ValidatorFactory; abstract class Repository { public const TYPE_PUBLICATION = 'publication'; public const TYPE_REPRESENTATION = 'representation'; public const SUFFIX_DEFAULT = 'default'; public const SUFFIX_CUSTOM_PATTERN = 'customPattern'; public const SUFFIX_MANUAL = 'customId'; public const CUSTOM_PUBLICATION_PATTERN = 'doiPublicationSuffixPattern'; public const CUSTOM_REPRESENTATION_PATTERN = 'doiRepresentationSuffixPattern'; public const CREATION_TIME_COPYEDIT = 'copyEditCreationTime'; public const CREATION_TIME_PUBLICATION = 'publicationCreationTime'; public const CREATION_TIME_NEVER = 'neverCreationTime'; /** @var DAO $dao */ public $dao; /** @var string $schemaMap The name of the class to map this entity to its schema */ public $schemaMap = maps\Schema::class; /** @var Request $request */ protected $request; /** @var PKPSchemaService<Doi> $schemaService */ protected $schemaService; public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService) { $this->dao = $dao; $this->request = $request; $this->schemaService = $schemaService; } /** @copydoc DAO::newDataObject() */ public function newDataObject(array $params = []): Doi { $doi = $this->dao->newDataObject(); if (!empty($params)) { $doi->setAllData($params); } return $doi; } /** @copydoc DAO::get() */ public function get(int $id, int $contextId = null): ?Doi { return $this->dao->get($id, $contextId); } /** @copydoc DAO::exists() */ public function exists(int $id, int $contextId = null): bool { return $this->dao->exists($id, $contextId); } /** @copydoc DAO::getCollector */ public function getCollector(): Collector { return App::make(Collector::class); } /** * Get an instance of the map class for mapping * DOIs to their schema */ public function getSchemaMap(): maps\Schema { return app('maps')->withExtensions($this->schemaMap); } /** * Check if duplicate of this DOI has already been recorded across all contexts. */ public function isDuplicate(string $doi, ?int $excludeDoiId = null): bool { $collector = $this->getCollector()->filterByIdentifier($doi); $ids = $collector->getIds(); if ($ids->count() == 0) { return false; } if ($excludeDoiId === null && $ids->count() > 0) { return true; } if ($ids->has($excludeDoiId) && $ids->count() < 2) { return false; } return true; } /** * Validate properties for a Doi * * Perform validation checks on data used to add or edit a Doi * * @param array $props A key/value array with the new data to validate * * @return array A key/value array with validation errors. Empty if no errors */ public function validate(?Doi $object, array $props): array { $errors = []; $validator = ValidatorFactory::make( $props, $this->schemaService->getValidationRules($this->dao->schema, []), ); // Check required fields ValidatorFactory::required( $validator, $object, $this->schemaService->getRequiredProps($this->dao->schema), $this->schemaService->getMultilingualProps($this->dao->schema), [], '' ); // The contextId must match an existing context $validator->after(function ($validator) use ($object, $props) { if (isset($props['contextId']) && !$validator->errors()->get('contextId')) { if (!Services::get('context')->exists($props['contextId'])) { $validator->errors()->add('contextId', __('api.contexts.404.contextNotFound')); } } // Check for duplicates across all contexts $doiId = $object ? $object->getData('id') : null; $doi = $props['doi'] ?? null; if ($doi !== null && $this->isDuplicate($doi, $doiId)) { $validator->errors()->add('doi', __('doi.editor.doiSuffixCustomIdentifierNotUnique')); } }); $validator->after(function ($validator) use ($object, $props) { $doi = $props['doi'] ?? null; if ($doi !== null && !$validator->errors()->get('doi')) { $validRegexPattern = '/[^-._;()\/A-Za-z0-9]/'; Hook::call('Doi::suffixValidation', [&$validRegexPattern]); $hasInvalidCharacters = PKPString::regexp_match($validRegexPattern, $doi); if ($hasInvalidCharacters) { $validator->errors()->add('doi', __('doi.editor.doiSuffixInvalidCharacters')); } } }); if ($validator->fails()) { $errors = $this->schemaService->formatValidationErrors($validator->errors()); } Hook::call('Doi::validate', [&$errors, $object, $props]); return $errors; } /** @copydoc DAO::insert() */ public function add(Doi $doi): int { $id = $this->dao->insert($doi); Hook::call('Doi::add', [$doi]); return $id; } /** @copydoc DAO:update() */ public function edit(Doi $doi, array $params) { $newDoi = clone $doi; $newDoi->setAllData(array_merge($newDoi->_data, $params)); Hook::call('Doi::edit', [$newDoi, $doi, $params]); $this->dao->update($newDoi); } /** @copydoc DAO::delete() */ public function delete(Doi $doi) { Hook::call('Doi::delete::before', [$doi]); $this->dao->delete($doi); Hook::call('Doi::delete', [$doi]); } /** * Delete a collection of DOIs */ public function deleteMany(Collector $collector) { $dois = $collector->getMany(); foreach ($dois as $doi) { $this->delete($doi); } } /** * Set DOIs status to Doi::STATUS_STALE, indicating the metadata has change and needs * to be updated with the registration agency. */ public function markStale(array $doiIds) { $this->dao->markStale($doiIds); } /** * Sets DOI status to Doi::STATUS_SUBMITTED, indicating the DOI has been queued to be * deposited with a registration agency, but the actual deposit has not yet been made. */ public function markSubmitted(array $doiIds) { $this->dao->markSubmitted($doiIds); } /** * Manually sets DOI status to Doi::STATUS_REGISTERED. This is used in cases where the * DOI registration process has been complete elsewhere and needs to be recorded as * registered locally. */ public function markRegistered(int $doiId) { $doi = $this->get($doiId); $editParams = [ 'status' => Doi::STATUS_REGISTERED, 'registrationAgency' => null ]; Hook::call('Doi::markRegistered', [&$editParams]); $this->edit($doi, $editParams); } /** * Manually sets DOI status to Doi::STATUS_UNREGISTERED. */ public function markUnregistered(int $doiId) { $doi = $this->get($doiId); $editParams = [ 'status' => Doi::STATUS_UNREGISTERED, ]; $this->edit($doi, $editParams); } /** * 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) { $enabledDoiTypes = $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ?? []; if ($this->_checkIfSubmissionValidForDeposit($enabledDoiTypes)) { // If there is no configured registration agency, nothing can be deposited. $agency = $context->getConfiguredDoiAgency(); if (!$agency) { return; } $submissionsCollection = $this->dao->getAllDepositableSubmissionIds($context); $submissionData = $submissionsCollection->reduce(function ($carry, $item) { if ($item->submission_id) { $carry['submissionIds'][] = $item->submission_id; } $carry['doiIds'][] = $item->doi_id; return $carry; }, ['submissionIds' => [], 'doiIds' => []]); // Schedule/queue jobs for submissions foreach ($submissionData['submissionIds'] as $submissionId) { dispatch(new DepositSubmission($submissionId, $context, $agency)); } // Mark submission DOIs as submitted Repo::doi()->markSubmitted($submissionData['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 { return match ($pubObjectType) { Repo::doi()::TYPE_PUBLICATION => Repo::publication() ->getCollector() ->filterByDoiIds([$doiId]) ->getIds() ->count(), default => false, }; } /** * Creates an eight character DOI suffix * */ protected function generateDefaultSuffix(): string { return DoiGenerator::encodeSuffix(); } /** * Loops over valid submission DOI types to see if any are enabled */ private function _checkIfSubmissionValidForDeposit(array $enabledDoiTypes): bool { foreach ($this->getValidSubmissionDoiTypes() as $validSubmissionDoiType) { if (in_array($validSubmissionDoiType, $enabledDoiTypes)) { return true; } } return false; } /** * Get app-specific DOI type constants to check when scheduling deposit for submissions */ abstract protected function getValidSubmissionDoiTypes(): array; /** * Gets all DOI IDs related to a submission * * @return array<int> DOI IDs */ abstract public function getDoisForSubmission(int $submissionId): array; /** * Compose final DOI and save to database * * @throws Exception */ protected function mintAndStoreDoi(Context $context, string $doiSuffix): int { $doiPrefix = $context->getData(Context::SETTING_DOI_PREFIX); if (empty($doiPrefix)) { throw new DoiException('doi.exceptions.missingPrefix'); } $completedDoi = $doiPrefix . '/' . $doiSuffix; $doiDataParams = [ 'doi' => $completedDoi, 'contextId' => $context->getId() ]; $doi = $this->newDataObject($doiDataParams); return $this->add($doi); } }