From 6abc696704d82243d2c9ef1f24f91921e6d74da1 Mon Sep 17 00:00:00 2001 From: Christian Marsilius Date: Mon, 17 Jun 2024 08:53:54 +0200 Subject: [PATCH] Add submission file DOI. --- DataciteExportPlugin.php | 18 ++++- DatacitePlugin.php | 60 ++++++++++++++- README.md | 2 +- classes/DOIPubIdExportPlugin.php | 23 ++++++ classes/DataciteSettings.php | 12 +++ classes/PubObjectCache.php | 8 ++ classes/PubObjectsExportPlugin.php | 55 ++++++++++++-- filter/DataciteXmlFilter.php | 118 ++++++++++++++++++++++++----- filter/filterConfig.xml | 12 +++ locale/en/locale.po | 13 ++-- 10 files changed, 285 insertions(+), 36 deletions(-) diff --git a/DataciteExportPlugin.php b/DataciteExportPlugin.php index c7e3a25..9e99d61 100644 --- a/DataciteExportPlugin.php +++ b/DataciteExportPlugin.php @@ -35,6 +35,7 @@ class DataciteExportPlugin extends DOIPubIdExportPlugin { //DataCite API public const DATACITE_API_URL = 'https://mds.datacite.org/'; + public const DATACITE_API_URL_TEST = 'https://mds.test.datacite.org/'; #endregion @@ -86,6 +87,14 @@ public function getRepresentationFilter(): string return 'publicationFormat=>datacite-xml'; } + /** + * @copydoc PubObjectsExportPlugin::getRepresentationFilter() + */ + public function getSubmissionFileFilter(): string + { + return 'submissionFile=>datacite-xml'; + } + public function getPluginSettingsPrefix(): string { return 'datacite'; @@ -201,7 +210,7 @@ public function exportAsDownload(Context $context, array $objects, ?bool $noVali $this->getExportPath(), $objectFileNamePart, $context, - '.xml' + '.'. count($objects) .'.xml' ); $fileManager->writeFile($exportFileName, $exportXml); $exportedFiles[] = $exportFileName; @@ -424,6 +433,9 @@ public function _getObjectUrl(Request$request, Context $context, DataObject $obj $url = $dispatcher->url($request, PKPApplication::ROUTE_PAGE, $context->getPath(), 'catalog', 'book', [$publication->getData('submissionId')], null, null, true); } break; + case $object instanceof SubmissionFile: + $url = $dispatcher->url($request, PKPApplication::ROUTE_PAGE, $context->getPath(), 'catalog', 'view', [$object->getData('submissionId'), $object->getData('assocId'), $object->getId()], null, null, true); + break; } if ($this->isTestMode($context)) { @@ -446,6 +458,8 @@ private function _getFilterFromObject(DataObject $object): string return $this->getChapterFilter(); } elseif ($object instanceof PublicationFormat) { return $this->getRepresentationFilter(); + } elseif ($object instanceof SubmissionFile) { + return $this->getSubmissionFileFilter(); } else { return ''; } @@ -459,6 +473,8 @@ private function _getObjectFileNamePart(DataObject $object): string return 'chapter-' . $object->getSourceChapterId(); } elseif ($object instanceof PublicationFormat) { return 'publicationFormat-' . $object->getId(); + } elseif ($object instanceof SubmissionFile) { + return 'submissionFile-' . $object->getId(); } else { return ''; } diff --git a/DatacitePlugin.php b/DatacitePlugin.php index 267bd3c..fbf26ba 100644 --- a/DatacitePlugin.php +++ b/DatacitePlugin.php @@ -32,6 +32,7 @@ use PKP\plugins\Hook; use PKP\plugins\PluginRegistry; use PKP\services\PKPSchemaService; +use PKP\submissionFile\SubmissionFile; class DatacitePlugin extends GenericPlugin implements IDoiRegistrationAgency { @@ -115,12 +116,18 @@ public function exportSubmissions(array $submissions, Context $context): array if (in_array(Repo::doi()::TYPE_CHAPTER, $context->getEnabledDoiTypes())) { $chapterDAO = new ChapterDAO(); $chapters = $chapterDAO->getByPublicationId($currentPublicationId)->toAssociativeArray(); + $onlyWithLandingPage = $this->getSetting($context->getId(), DataciteSettings::KEY_ONLY_WITH_LANDINGPAGE); /** @var Chapter $chapter */ foreach ($chapters as $chapter) { if ($chapter->getDoi()) { - if ($chapter->isPageEnabled() === 1) { //TODO: Remove this control structure, if omp core only assigns DOIs to chapters with own landing page. + if ($onlyWithLandingPage) { //TODO: Remove this control structure, if omp core only assigns DOIs to chapters with own landing page. + if ($chapter->isPageEnabled() === 1) { + $items[] = $chapter; + } + } else { $items[] = $chapter; } + } } } @@ -128,7 +135,7 @@ public function exportSubmissions(array $submissions, Context $context): array //publication formats if (in_array(Repo::doi()::TYPE_REPRESENTATION, $context->getEnabledDoiTypes())) { $publicationFormatDAO = new PublicationFormatDAO(); - $publicationFormats = $publicationFormatDAO->getByPublicationId($currentPublicationId)->toAssociativeArray(); + $publicationFormats = $publicationFormatDAO->getByPublicationId($currentPublicationId); /** @var PublicationFormat $publicationFormat */ foreach ($publicationFormats as $publicationFormat) { if ($publicationFormat->getDoi()) { @@ -136,6 +143,26 @@ public function exportSubmissions(array $submissions, Context $context): array } } } + + //submission files + if (in_array(Repo::doi()::TYPE_SUBMISSION_FILE, $context->getEnabledDoiTypes())) { + $submissionFiles = Repo::submissionFile() + ->getCollector() + ->filterBySubmissionIds([$submission->getId()]) + ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF]) + ->filterByAssoc( + Application::ASSOC_TYPE_PUBLICATION_FORMAT + ) + ->getMany() + ->toArray(); + + /** @var SubmissionFile $submissionFile */ + foreach ($submissionFiles as $submissionFile) { + if ($submissionFile->getDoi()) { + $items[] = $submissionFile; + } + } + } } $temporaryFileId = $exportPlugin->exportAsDownload($context, $items, null, $xmlErrors); @@ -164,12 +191,18 @@ public function depositSubmissions(array $submissions, Context $context): array if (in_array(Repo::doi()::TYPE_CHAPTER, $context->getEnabledDoiTypes())) { $chapterDAO = new ChapterDAO(); $chapters = $chapterDAO->getByPublicationId($currentPublicationId)->toAssociativeArray(); + $onlyWithLandingPage = $this->getSetting($context->getId(), DataciteSettings::KEY_ONLY_WITH_LANDINGPAGE); /** @var Chapter $chapter */ foreach ($chapters as $chapter) { if ($chapter->getDoi()) { - if ($chapter->isPageEnabled() === 1) { //TODO: Remove this control structure, if omp core only assigns DOIs to chapters with own landing page. + if ($onlyWithLandingPage) { //TODO: Remove this control structure, if omp core only assigns DOIs to chapters with own landing page. + if ($chapter->isPageEnabled() === 1) { + $items[] = $chapter; + } + } else { $items[] = $chapter; } + } } } @@ -185,6 +218,26 @@ public function depositSubmissions(array $submissions, Context $context): array } } } + + //submission files + if (in_array(Repo::doi()::TYPE_SUBMISSION_FILE, $context->getEnabledDoiTypes())) { + $submissionFiles = Repo::submissionFile() + ->getCollector() + ->filterBySubmissionIds([$submission->getId()]) + ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF]) + ->filterByAssoc( + Application::ASSOC_TYPE_PUBLICATION_FORMAT + ) + ->getMany() + ->toArray(); + + /** @var SubmissionFile $submissionFile */ + foreach ($submissionFiles as $submissionFile) { + if ($submissionFile->getDoi()) { + $items[] = $submissionFile; + } + } + } } $status = $exportPlugin->exportAndDeposit($context, $items, $responseMessage); @@ -356,6 +409,7 @@ public function getAllowedDoiTypes(): array Repo::doi()::TYPE_PUBLICATION, //book Repo::doi()::TYPE_CHAPTER, //chapter Repo::doi()::TYPE_REPRESENTATION, //publication format + Repo::doi()::TYPE_SUBMISSION_FILE, //submission file ]; } } diff --git a/README.md b/README.md index 6fe6b51..07b0e72 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ### OMPDatacite Plugin #### Introduction -This plugin registers DOIS for monographs, chapters and publication formats for DOI provider [Datacite.org](https://datacite.org). +This plugin registers DOIS for monographs, chapters, publication formats and files for DOI provider [Datacite.org](https://datacite.org). Current Schema version is [4.4](https://support.datacite.org/docs/datacite-metadata-schema-44) diff --git a/classes/DOIPubIdExportPlugin.php b/classes/DOIPubIdExportPlugin.php index a9eac37..ff61a02 100644 --- a/classes/DOIPubIdExportPlugin.php +++ b/classes/DOIPubIdExportPlugin.php @@ -23,6 +23,7 @@ use APP\template\TemplateManager; use PKP\context\Context; use PKP\core\PKPString; +use PKP\submissionFile\SubmissionFile; abstract class DOIPubIdExportPlugin extends PubObjectsExportPlugin @@ -187,6 +188,28 @@ public function getPublishedPublicationFormats(array $publicationFormatIds, Cont return $validPublishedPublicationFormatsWithDoi; } + /** + * Get publication formats from publication format IDs. + * + * @param array $submissionFileIds + * @param Context $context + * + * @return array + */ + public function getPublishedSubmissionFiles(array $submissionFileIds, Context $context): array + { + $validPublishedSubmissionFiles = parent::getPublishedSubmissionFiles($submissionFileIds, $context); + $validPublishedSubmissionFilesWithDoi = []; + /** @var SubmissionFile $submissionFile */ + foreach ($validPublishedSubmissionFiles as $submissionFile) { + if ($submissionFile->getDoi() !== null) { + $validPublishedSubmissionFilesWithDoi[] = $submissionFile; + } + } + + return $validPublishedSubmissionFilesWithDoi; + } + /** * @copydoc ImportExportPlugin::executeCLI() diff --git a/classes/DataciteSettings.php b/classes/DataciteSettings.php index c4464ed..fc2bd30 100644 --- a/classes/DataciteSettings.php +++ b/classes/DataciteSettings.php @@ -32,6 +32,7 @@ class DataciteSettings extends RegistrationAgencySettings public const KEY_TEST_USERNAMER = 'testUsername'; public const KEY_TEST_PASSWORD = 'testPassword'; public const KEY_TEST_DOI_PREFIX = 'testDOIPrefix'; + public const KEY_ONLY_WITH_LANDINGPAGE = 'onlyWithLandingPage'; public function getSchema(): stdClass { return (object) [ @@ -65,6 +66,10 @@ public function getSchema(): stdClass 'type' => 'string', 'validation' => ['nullable', 'max:50'] ], + self::KEY_ONLY_WITH_LANDINGPAGE => (object) [ + 'type' => 'boolean', + 'validation' => ['nullable'] + ], ], ]; } @@ -89,6 +94,13 @@ public function getFields(Context $context): array 'inputType' => 'password', 'value' => $this->agencyPlugin->getSetting($context->getId(), self::KEY_PASSWORD), ]), + new FieldOptions(self::KEY_ONLY_WITH_LANDINGPAGE, [ + 'label' => __('plugins.importexport.datacite.settings.form.onlyWithLandingPage.label'), + 'options' => [ + ['value' => true, 'label' => __('plugins.importexport.datacite.settings.form.onlyWithLandingPage.description')], + ], + 'value' => $this->agencyPlugin->getSetting($context->getId(), self::KEY_ONLY_WITH_LANDINGPAGE), + ]), new FieldOptions(self::KEY_TEST_MODE, [ 'label' => __('plugins.importexport.common.settings.form.testMode.label'), 'options' => [ diff --git a/classes/PubObjectCache.php b/classes/PubObjectCache.php index 70eabf4..ca44f83 100644 --- a/classes/PubObjectCache.php +++ b/classes/PubObjectCache.php @@ -20,6 +20,7 @@ use APP\publication\Publication; use APP\publicationFormat\PublicationFormat; use DataObject; +use PKP\submissionFile\SubmissionFile; class PubObjectCache { @@ -55,6 +56,13 @@ public function add(DataObject $object, ?Publication $parent = null): void $this->_insertInternally($object, 'publicationFormatsByPublication', $parent->getId(), $object->getId()); } } + if ($object instanceof SubmissionFile) { + assert($parent instanceof Publication); + $this->_insertInternally($object, 'submissionFiles', $object->getId()); + if ($parent) { + $this->_insertInternally($object, 'submissionFilesByPublication', $parent->getId(), $object->getId()); + } + } } /** diff --git a/classes/PubObjectsExportPlugin.php b/classes/PubObjectsExportPlugin.php index dbb9a94..629ba84 100644 --- a/classes/PubObjectsExportPlugin.php +++ b/classes/PubObjectsExportPlugin.php @@ -185,6 +185,7 @@ public function display($args, $request): void case 'exportPublications': case 'exportChapters': case 'exportRepresentations': + case 'exportSubmissionFiles': $this->prepareAndExportPubObjects($request, $context); } } @@ -201,6 +202,7 @@ public function prepareAndExportPubObjects(PKPRequest $request, Context $context $selectedPublications = (array) $request->getUserVar('selectedPublications'); $selectedChapters = (array) $request->getUserVar('selectedChapters'); $selectedRepresentations = (array) $request->getUserVar('selectedRepresentations'); + $selectedSubmissionFiles = (array) $request->getUserVar('selectedSubmissionFiles'); $tab = (string) $request->getUserVar('tab'); $noValidation = !$request->getUserVar('validation'); $objects = []; @@ -213,7 +215,7 @@ public function prepareAndExportPubObjects(PKPRequest $request, Context $context if (!empty($args['chapterIds'])) { $selectedChapters = (array) $args['chapterIds']; } - if (empty($selectedPublications) && empty($selectedChapters) && empty($selectedRepresentations)) { + if (empty($selectedPublications) && empty($selectedChapters) && empty($selectedRepresentations) && empty($selectedSubmissionFiles)) { fatalError(__('plugins.importexport.common.error.noObjectsSelected')); } if (!empty($selectedPublications)) { @@ -228,6 +230,10 @@ public function prepareAndExportPubObjects(PKPRequest $request, Context $context $objects = $this->getPublishedPublicationFormats($selectedRepresentations, $context); $filter = $this->getRepresentationFilter(); $objectsFileNamePart = 'publicationFormats'; + } elseif (!empty($selectedSubmissionFiles)) { + $objects = $this->getPublishedSubmissionFiles($selectedSubmissionFiles, $context); + $filter = $this->getSubmissionFileFilter(); + $objectsFileNamePart = 'submissionFiles'; } // Execute export action @@ -290,7 +296,7 @@ public function executeExportAction(PKPRequest $request, array $objects, string // Get the XML $exportXml = $this->exportXML($objects, $filter, $context, $noValidation); // Write the XML to a file. - // export file name example: crossref-20160723-160036-articles-1.xml + // export file name example: datacite-20160723-160036-articles-1.xml $fileManager = new FileManager(); $exportFileName = $this->getExportFileName($this->getExportPath(), $objectsFileNamePart, $context, '.xml'); $fileManager->writeFile($exportFileName, $exportXml); @@ -398,6 +404,16 @@ public function getRepresentationFilter(): ?string return null; } + /** + * Get the submission file filter. + * + * @return string|null + */ + public function getSubmissionFileFilter(): ?string + { + return null; + } + /** * Get status names for the filter search option. * @@ -732,6 +748,11 @@ public function executeCLI($scriptName, &$args): void $filter = $this->getRepresentationFilter(); $objectsFileNamePart = 'publicationFormats'; break; + case 'submissionFiles': + $objects = $this->getPublishedSubmissionFiles($args, $context); + $filter = $this->getSubmissionFileFilter(); + $objectsFileNamePart = 'submissionFiles'; + break; default: $this->usage($scriptName); return; @@ -812,7 +833,7 @@ public function getPublishedPublications(array $publicationIds, Context $context $validPublishedPublications = []; foreach ($validPublicationIds as $publicationId) { $publication = Repo::publication()->get($publicationId); - if( $publication->getData('status') === PKPSubmission::STATUS_PUBLISHED ) { + if ($publication->getData('status') === PKPSubmission::STATUS_PUBLISHED) { $validPublishedPublications[$publicationId] = $publication; } } @@ -835,7 +856,7 @@ public function getPublishedChapters(array $chapterIds, Context $context): array foreach ($chapterIds as $chapterId) { $chapter = $chapterDao->getChapter( $chapterId ); $publication = Repo::publication()->get($chapter->getData('publicationId')); - if( $publication->getData('status') === PKPSubmission::STATUS_PUBLISHED ) { + if ($publication->getData('status') === PKPSubmission::STATUS_PUBLISHED) { $chapters[$chapterId] = $chapter; } } @@ -859,7 +880,7 @@ public function getPublishedPublicationFormats(array $publicationFormatIds, Cont foreach ($publicationFormatIds as $publicationFormatId) { $publicationFormat = $publicationFormatDao->getById($publicationFormatId); $publication = Repo::publication()->get($publicationFormat->getData('publicationId')); - if( $publication->getData('status') === PKPSubmission::STATUS_PUBLISHED ) { + if ($publication->getData('status') === PKPSubmission::STATUS_PUBLISHED) { $publicationFormats[$publicationFormatId] = $publicationFormat; } } @@ -867,6 +888,30 @@ public function getPublishedPublicationFormats(array $publicationFormatIds, Cont return $publicationFormats; } + + /** + * Get submission files from submission file IDs. + * + * @param array $submissionFileIds + * @param Context $context + * + * @return array + */ + public function getPublishedSubmissionFiles(array $submissionFileIds, Context $context): array + { + $submissionFiles = []; + foreach ($submissionFileIds as $submissionFileId) { + $submissionFile = Repo::submissionFile()->get($submissionFileId); + $submission = Repo::submission()->get($submissionFile->getData('submissionId')); + $publication = $submission->getCurrentPublication(); + if ($publication->getData('status') === PKPSubmission::STATUS_PUBLISHED) { + $submissionFiles[$submissionFileId] = $submissionFile; + } + } + + return $submissionFiles; + } + /** * Add a notification. * diff --git a/filter/DataciteXmlFilter.php b/filter/DataciteXmlFilter.php index 99a150d..c3916a4 100644 --- a/filter/DataciteXmlFilter.php +++ b/filter/DataciteXmlFilter.php @@ -29,6 +29,7 @@ use PKP\filter\FilterGroup; use PKP\i18n\LocaleConversion; use PKP\plugins\importexport\native\filter\NativeExportFilter; +use PKP\submissionFile\SubmissionFile; class DataciteXmlFilter extends NativeExportFilter { @@ -81,7 +82,7 @@ public function &process(mixed &$input): DOMDocument $cache = $plugin->getCache(); // Get all objects - $publication = $chapter = $publicationFormat = null; + $publication = $chapter = $publicationFormat = $submissionFile = null; if ($input instanceof Publication) { $publication = $input; if (!$cache->isCached('publication', $publication->getId())) { @@ -105,10 +106,20 @@ public function &process(mixed &$input): DOMDocument if (!$cache->isCached('publicationFormats', $publicationFormat->getId())) { $cache->add($publicationFormat, null); } + } elseif ($input instanceof SubmissionFile) { + $submissionFile = $input; + $submission = Repo::submission()->get($submissionFile->getData('submissionId')); + $publication = $submission->getCurrentPublication(); + if (!$cache->isCached('publication', $publication->getId())) { + $cache->add($publication, null); + } + if (!$cache->isCached('submissionFile', $submissionFile->getId())) { + $cache->add($submissionFile, null); + } } // Identify the object locale. - $objectLocalePrecedence = $this->getObjectLocalePrecedence($context, $publication, $chapter, $publicationFormat); + $objectLocalePrecedence = $this->getObjectLocalePrecedence($context, $publication); // The publisher is required. // Use the press name as DataCite recommends for now. $publisher = $context->getData('publisher'); @@ -135,9 +146,9 @@ public function &process(mixed &$input): DOMDocument $rootNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'identifier', htmlspecialchars($doi, ENT_COMPAT, 'UTF-8'))); $node->setAttribute('identifierType', self::DATACITE_IDTYPE_DOI); // Creators (mandatory) - $rootNode->appendChild($this->createCreatorsNode($doc, $publication, $chapter, $publicationFormat, $publisher, $objectLocalePrecedence)); + $rootNode->appendChild($this->createCreatorsNode($doc, $publication, $chapter, $publisher, $objectLocalePrecedence)); // Title (mandatory) - $rootNode->appendChild($this->createTitlesNode($doc, $publication, $chapter, $publicationFormat, $objectLocalePrecedence)); + $rootNode->appendChild($this->createTitlesNode($doc, $publication, $chapter, $publicationFormat, $submissionFile, $objectLocalePrecedence)); // Publisher (mandatory) $rootNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'publisher', htmlspecialchars($publisher, ENT_COMPAT, 'UTF-8'))); // Publication Year (mandatory) @@ -160,10 +171,10 @@ public function &process(mixed &$input): DOMDocument // Language $rootNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'language', LocaleConversion::getIso1FromLocale($objectLocalePrecedence[0]))); // Resource Type - $resourceTypeNode = $this->createResourceTypeNode($doc, $publication, $chapter, $publicationFormat); + $resourceTypeNode = $this->createResourceTypeNode($doc, $publication, $chapter, $publicationFormat, $submissionFile); $rootNode->appendChild($resourceTypeNode); // Related Identifiers - $relatedIdentifiersNode = $this->createRelatedIdentifiersNode($doc, $publication, $chapter, $publicationFormat); + $relatedIdentifiersNode = $this->createRelatedIdentifiersNode($doc, $publication, $chapter, $publicationFormat, $submissionFile); if ($relatedIdentifiersNode) { $rootNode->appendChild($relatedIdentifiersNode); } @@ -179,12 +190,12 @@ public function &process(mixed &$input): DOMDocument $rootNode->appendChild($rightsNode); } // Descriptions - $descriptionsNode = $this->createDescriptionsNode($doc, $publication, $chapter, $publicationFormat, $objectLocalePrecedence); + $descriptionsNode = $this->createDescriptionsNode($doc, $publication, $chapter, $publicationFormat, $submissionFile, $objectLocalePrecedence); if ($descriptionsNode) { $rootNode->appendChild($descriptionsNode); } // relatedItems - $relatedItemsNode = $this->createRelatedItemsNode($doc, $publication, $chapter, $publicationFormat, $publisher, $objectLocalePrecedence); + $relatedItemsNode = $this->createRelatedItemsNode($doc, $publication, $chapter, $publicationFormat, $submissionFile, $publisher, $objectLocalePrecedence); if ($relatedItemsNode) { $rootNode->appendChild($relatedItemsNode); } @@ -218,7 +229,6 @@ public function createRootNode(DOMDocument $doc): DOMElement * @param DOMDocument $doc * @param Publication $publication * @param Chapter|null $chapter - * @param PublicationFormat|null $publicationFormat * @param string $publisher * @param array $objectLocalePrecedence * @@ -229,7 +239,6 @@ public function createCreatorsNode( DOMDocument $doc, Publication $publication, ?Chapter $chapter, - ?PublicationFormat $publicationFormat, string $publisher, array $objectLocalePrecedence ): DOMElement { @@ -296,6 +305,7 @@ public function createCreatorsNode( * @param Publication $publication * @param Chapter|null $chapter * @param PublicationFormat|null $publicationFormat + * @param SubmissionFile|null $submissionFile * @param array $objectLocalePrecedence * * @return DOMElement @@ -306,12 +316,24 @@ public function createTitlesNode( Publication $publication, ?Chapter $chapter, ?PublicationFormat $publicationFormat, + ?SubmissionFile $submissionFile, array $objectLocalePrecedence ): DOMElement { /** @var DataciteExportDeployment $deployment*/ $deployment = $this->getDeployment(); $plugin = $deployment->getPlugin(); // Get an array of localized titles. + + $fileTitles = []; + if (null !== $submissionFile) { + $fileTitles = $submissionFile->getData('name'); + if (is_array($fileTitles)) { + $fileTitles = $this->getTranslationsByPrecedence($fileTitles, $objectLocalePrecedence); + } else { + $fileTitle = $fileTitles; + $fileTitles = [$fileTitle]; + } + } $publicationFormatNames = []; if (null !== $publicationFormat) { $publicationFormatNames = $publicationFormat->getName(); @@ -328,13 +350,14 @@ public function createTitlesNode( $publicationTitles = $this->getTranslationsByPrecedence($publicationTitles, $objectLocalePrecedence); // We expect at least one title. - $counter = count($publicationFormatNames) + count($chapterTitles) + count($publicationTitles); + $counter = count($fileTitles) + count($publicationFormatNames) + count($chapterTitles) + count($publicationTitles); assert($counter >= 1); $titlesNode = $doc->createElementNS($deployment->getNamespace(), 'titles'); // Start with the primary object locale. $primaryPublicationTitle = array_shift($publicationTitles); $primaryChapterTitle = array_shift($chapterTitles); $primaryPublicationFormatName = array_shift($publicationFormatNames); + $primaryFileTitle = array_shift($fileTitles); if ($primaryPublicationFormatName) { $titlesNode->appendChild( @@ -381,6 +404,23 @@ public function createTitlesNode( $titlesNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'title', htmlspecialchars(PKPString::html2text($title), ENT_COMPAT, 'UTF-8'))); $node->setAttribute('titleType', self::DATACITE_TITLETYPE_TRANSLATED); } + } elseif ($primaryFileTitle) { + $titlesNode->appendChild( + $node = $doc->createElementNS( + $deployment->getNamespace(), + 'title', + htmlspecialchars( + PKPString::html2text($primaryFileTitle), + ENT_COMPAT, + 'UTF-8' + ) + ) + ); + // Then let the translated titles follow. + foreach ($fileTitles as $locale => $title) { + $titlesNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'title', htmlspecialchars(PKPString::html2text($title), ENT_COMPAT, 'UTF-8'))); + $node->setAttribute('titleType', self::DATACITE_TITLETYPE_TRANSLATED); + } } else { $titlesNode->appendChild( $node = $doc->createElementNS( @@ -410,6 +450,7 @@ public function createTitlesNode( * @param Publication $publication * @param Chapter|null $chapter * @param PublicationFormat|null $publicationFormat + * @param SubmissionFile|null $submissionFile * * @return DOMElement * @throws DOMException @@ -418,11 +459,15 @@ public function createResourceTypeNode( DOMDocument $doc, Publication $publication, ?Chapter $chapter, - ?PublicationFormat $publicationFormat + ?PublicationFormat $publicationFormat, + ?SubmissionFile $submissionFile ): DOMElement { /** @var DataciteExportDeployment $deployment */ $deployment = $this->getDeployment(); switch (true) { + case null !== $submissionFile: + $resourceType = 'File'; + break; case null !== $chapter: $resourceType = 'Chapter'; break; @@ -433,6 +478,10 @@ public function createResourceTypeNode( // Create the resourceType element for Chapters. $resourceTypeNode = $doc->createElementNS($deployment->getNamespace(), 'resourceType', $resourceType); $resourceTypeNode->setAttribute('resourceTypeGeneral', 'BookChapter'); + } elseif ($resourceType === 'File') { + // Create the resourceType element for Files. + $resourceTypeNode = $doc->createElementNS($deployment->getNamespace(), 'resourceType'); + $resourceTypeNode->setAttribute('resourceTypeGeneral', 'Dataset'); } else { $resourceTypeNode = $doc->createElementNS($deployment->getNamespace(), 'resourceType', $resourceType); $resourceTypeNode->setAttribute('resourceTypeGeneral', 'Book'); @@ -447,6 +496,7 @@ public function createResourceTypeNode( * @param Publication $publication * @param Chapter|null $chapter * @param PublicationFormat|null $publicationFormat + * @param SubmissionFile|null $submissionFile * * @return ?DOMElement * @throws DOMException @@ -455,11 +505,13 @@ public function createRelatedIdentifiersNode( DOMDocument $doc, Publication $publication, ?Chapter $chapter, - ?PublicationFormat $publicationFormat + ?PublicationFormat $publicationFormat, + ?SubmissionFile $submissionFile ): ?DOMElement { $deployment = $this->getDeployment(); $relatedIdentifiersNode = $doc->createElementNS($deployment->getNamespace(), 'relatedIdentifiers'); switch (true) { + case null !== $submissionFile: case null !== $publicationFormat: case null !== $chapter: assert(isset($publication)); @@ -485,6 +537,7 @@ public function createRelatedIdentifiersNode( // Publication formats $publicationFormats = $publication->getData('publicationFormats'); + /** @var PublicationFormat $pubFormat */ foreach ($publicationFormats as $pubFormat) { $doi = $pubFormat->getDoi(); if (!empty($doi)) { @@ -492,8 +545,31 @@ public function createRelatedIdentifiersNode( $node->setAttribute('relatedIdentifierType', self::DATACITE_IDTYPE_DOI); $node->setAttribute('relationType', self::DATACITE_RELTYPE_HASPART); } + // Submission files + $submissionFiles = Repo::submissionFile() + ->getCollector() + ->filterBySubmissionIds([$publication->getData('submissionId')]) + ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF]) + ->filterByAssoc( + Application::ASSOC_TYPE_PUBLICATION_FORMAT, + [$pubFormat->getId()] + ) + ->getMany() + ->toArray(); + + /** @var SubmissionFile $file */ + foreach ($submissionFiles as $file) { + $fileDoi = $file->getDoi(); + if (!empty($fileDoi)) { + $relatedIdentifiersNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'relatedIdentifier', htmlspecialchars($fileDoi, ENT_COMPAT, 'UTF-8'))); + $node->setAttribute('relatedIdentifierType', self::DATACITE_IDTYPE_DOI); + $node->setAttribute('relationType', self::DATACITE_RELTYPE_HASPART); + } + unset($file, $fileDoi); + } unset($pubFormat, $doi); } + break; } if ($relatedIdentifiersNode->hasChildNodes()) { @@ -510,9 +586,10 @@ public function createRelatedIdentifiersNode( * @param Publication $publication * @param Chapter|null $chapter * @param PublicationFormat|null $publicationFormat + * @param SubmissionFile|null $submissionFile * @param array $objectLocalePrecedence * - * @return ?DOMElement Can be null if a size cannot be identified for the given object. + * @return ?DOMElement Can be null. * @throws DOMException */ public function createDescriptionsNode( @@ -520,6 +597,7 @@ public function createDescriptionsNode( Publication $publication, ?Chapter $chapter, ?PublicationFormat $publicationFormat, + ?SubmissionFile $submissionFile, array $objectLocalePrecedence ): ?DOMElement { /** @var DataciteExportDeployment $deployment */ @@ -549,10 +627,11 @@ public function createDescriptionsNode( * @param Publication $publication * @param Chapter|null $chapter * @param PublicationFormat|null $publicationFormat + * @param SubmissionFile|null $submissionFile * @param string $publisher * @param array $objectLocalePrecedence * - * @return ?DOMElement Can be null if a size cannot be identified for the given object. + * @return ?DOMElement Can be null * @throws DOMException */ public function createRelatedItemsNode( @@ -560,6 +639,7 @@ public function createRelatedItemsNode( Publication $publication, ?Chapter $chapter, ?PublicationFormat $publicationFormat, + ?SubmissionFile $submissionFile, string $publisher, array $objectLocalePrecedence ): ?DOMElement { @@ -570,7 +650,7 @@ public function createRelatedItemsNode( $request = Application::get()->getRequest(); $relatedItemsNode = null; - if (null !== $chapter) { + if (null !== $chapter || null !== $publicationFormat || null !== $submissionFile) { $relatedItemsNode = $doc->createElementNS($deployment->getNamespace(), 'relatedItems'); $relatedItemNode = $doc->createElementNS($deployment->getNamespace(), 'relatedItem'); @@ -628,17 +708,13 @@ public function createRelatedItemsNode( * * @param Context $context * @param Publication|null $publication - * @param Chapter|null $chapter - * @param PublicationFormat|null $publicationFormat * * @return array A list of valid PKP locales in descending * order of priority. */ public function getObjectLocalePrecedence( Context $context, - ?Publication $publication, - ?Chapter $chapter, - ?PublicationFormat $publicationFormat + ?Publication $publication ): array { $locales = []; if ($publication instanceof Publication) { diff --git a/filter/filterConfig.xml b/filter/filterConfig.xml index 914fcf6..966d4a7 100644 --- a/filter/filterConfig.xml +++ b/filter/filterConfig.xml @@ -33,6 +33,13 @@ description="plugins.importexport.datacite.description" inputType="class::classes.publicationFormat.PublicationFormat" outputType="xml::schema(http://schema.datacite.org/meta/kernel-4/metadata.xsd)" /> + + @@ -50,5 +57,10 @@ inGroup="publicationFormat=>datacite-xml" class="APP\plugins\generic\datacite\filter\DataciteXmlFilter" isTemplate="0" /> + + diff --git a/locale/en/locale.po b/locale/en/locale.po index 5b3695e..6a36607 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -45,7 +45,7 @@ msgid "plugins.importexport.datacite.settings.description" msgstr "Description" msgid "plugins.importexport.datacite.intro" -msgstr "This plugin registers DOIs for monographs, chapters and publication formats for DOI provider Datacite.org." +msgstr "This plugin registers DOIs for monographs, chapters, publication formats and files for DOI provider Datacite.org." msgid "plugins.importexport.datacite.settings.label" msgstr "Settings" @@ -92,9 +92,6 @@ msgstr "Test URL" msgid "plugins.importexport.datacite.senderTask.name" msgstr "DataCite automatic registration task" -msgid "plugins.importexport.datacite.cliUsage" -msgstr "Usage: {$scriptName} {$pluginName} export [outputFileName] [journal_path] {issues|articles|galleys} objectId1 [objectId2] ... {$scriptName} {$pluginName} register [journal_path] {issues|articles|galleys} objectId1 [objectId2] ..." - msgid "plugins.importexport.datacite.tab.monographs" msgstr "Monographs / Chapters" @@ -174,4 +171,10 @@ msgid "plugins.generic.datacite.displayName" msgstr "Datacite Manager Plugin" msgid "plugins.generic.datacite.description" -msgstr "Handles depositing and exporting Datacite metadata" \ No newline at end of file +msgstr "Handles depositing and exporting Datacite metadata" + +msgid "plugins.importexport.datacite.settings.form.onlyWithLandingPage.label" +msgstr "Chapters without own page" + +msgid "plugins.importexport.datacite.settings.form.onlyWithLandingPage.description" +msgstr "Deposit only chapters with own page" \ No newline at end of file