diff --git a/classes/components/listPanels/DoiListPanel.php b/classes/components/listPanels/DoiListPanel.php
index f2f82610f9d..4724fbd4f64 100644
--- a/classes/components/listPanels/DoiListPanel.php
+++ b/classes/components/listPanels/DoiListPanel.php
@@ -52,6 +52,15 @@ protected function setAppConfig(array &$config): void
]
]
];
-
+ $config['filters'][] = [
+ 'heading' => __('manager.dois.filters.workflow'),
+ 'filters' => [
+ [
+ 'title' => __('manager.dois.filters.inEditingOrPublished'),
+ 'param' => 'inEditingOrPublished',
+ 'value' => '1',
+ ],
+ ],
+ ];
}
}
diff --git a/classes/doi/DAO.php b/classes/doi/DAO.php
index 41f2e402489..5a27c891c63 100644
--- a/classes/doi/DAO.php
+++ b/classes/doi/DAO.php
@@ -18,6 +18,7 @@
namespace APP\doi;
+use APP\core\Application;
use APP\facades\Repo;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
@@ -38,62 +39,66 @@ class DAO extends \PKP\doi\DAO
public function getAllDepositableSubmissionIds(Context $context): Collection
{
$enabledDoiTypes = $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ?? [];
+ $doiVersioning = (bool) $context->getData(Context::SETTING_DOI_VERSIONING);
$q = DB::table($this->table, 'd')
+ // Minor versions share their major version's DOIs, so a DOI can match several publications/objects
->leftJoin('publications as p', 'd.doi_id', '=', 'p.doi_id')
- ->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
+ ->leftJoin('submission_chapters as cd', 'd.doi_id', '=', 'cd.doi_id')
+ ->leftJoin('publications as cp', 'cd.publication_id', '=', 'cp.publication_id')
+ ->leftJoin('publication_formats as pfd', 'd.doi_id', '=', 'pfd.doi_id')
+ ->leftJoin('publications as pfp', 'pfd.publication_id', '=', 'pfp.publication_id')
+ ->leftJoin('submission_files as sfd', 'd.doi_id', '=', 'sfd.doi_id')
->where('d.context_id', '=', $context->getId())
- ->where(function (Builder $q) use ($enabledDoiTypes) {
+ ->where(function (Builder $q) use ($enabledDoiTypes, $doiVersioning) {
// Publication DOIs
- $q->when(in_array(Repo::doi()::TYPE_PUBLICATION, $enabledDoiTypes), function (Builder $q) {
- $q->whereIn('d.doi_id', function (Builder $q) {
+ $q->when(in_array(Repo::doi()::TYPE_PUBLICATION, $enabledDoiTypes), function (Builder $q) use ($doiVersioning) {
+ $q->whereIn('d.doi_id', function (Builder $q) use ($doiVersioning) {
$q->select('p.doi_id')
->from('publications', 'p')
- ->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
- ->whereColumn('p.publication_id', '=', 's.current_publication_id')
->whereNotNull('p.doi_id')
->where('p.status', '=', PKPPublication::STATUS_PUBLISHED);
+ $this->whereDepositablePublication($q, $doiVersioning);
});
});
// Chapter DOIs
- $q->when(in_array(Repo::doi()::TYPE_CHAPTER, $enabledDoiTypes), function (Builder $q) {
- $q->orWhereIn('d.doi_id', function (Builder $q) {
+ $q->when(in_array(Repo::doi()::TYPE_CHAPTER, $enabledDoiTypes), function (Builder $q) use ($doiVersioning) {
+ $q->orWhereIn('d.doi_id', function (Builder $q) use ($doiVersioning) {
$q->select('spc.doi_id')
->from('submission_chapters', 'spc')
->join('publications as p', 'spc.publication_id', '=', 'p.publication_id')
- ->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
- ->whereColumn('p.publication_id', '=', 's.current_publication_id')
->whereNotNull('spc.doi_id')
->where('p.status', '=', PKPPublication::STATUS_PUBLISHED);
+ $this->whereDepositablePublication($q, $doiVersioning, false);
});
});
// Publication format DOIs
- $q->when(in_array(Repo::doi()::TYPE_REPRESENTATION, $enabledDoiTypes), function (Builder $q) {
- $q->orWhereIn('d.doi_id', function (Builder $q) {
+ $q->when(in_array(Repo::doi()::TYPE_REPRESENTATION, $enabledDoiTypes), function (Builder $q) use ($doiVersioning) {
+ $q->orWhereIn('d.doi_id', function (Builder $q) use ($doiVersioning) {
$q->select('pf.doi_id')
->from('publication_formats', 'pf')
->join('publications as p', 'pf.publication_id', '=', 'p.publication_id')
- ->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
- ->whereColumn('p.publication_id', '=', 's.current_publication_id')
->whereNotNull('pf.doi_id')
->where('p.status', '=', PKPPublication::STATUS_PUBLISHED);
+ $this->whereDepositablePublication($q, $doiVersioning, false);
});
});
- // Submission file DOIs
- $q->when(in_array(Repo::doi()::TYPE_SUBMISSION_FILE, $enabledDoiTypes), function (Builder $q) {
- $q->orWhereIn('d.doi_id', function (Builder $q) {
+ // Submission file DOIs, of the proof files of a publication format
+ $q->when(in_array(Repo::doi()::TYPE_SUBMISSION_FILE, $enabledDoiTypes), function (Builder $q) use ($doiVersioning) {
+ $q->orWhereIn('d.doi_id', function (Builder $q) use ($doiVersioning) {
$q->select('sf.doi_id')
->from('submission_files', 'sf')
- ->join('submissions as s', 's.submission_id', '=', 'sf.submission_id')
- ->leftJoin('publications as p', 's.current_publication_id', '=', 'p.publication_id')
- ->whereColumn('p.publication_id', '=', 's.current_publication_id')
+ ->join('publication_formats as pf', 'sf.assoc_id', '=', 'pf.publication_format_id')
+ ->join('publications as p', 'pf.publication_id', '=', 'p.publication_id')
+ ->where('sf.assoc_type', '=', Application::ASSOC_TYPE_PUBLICATION_FORMAT)
->where('sf.file_stage', '=', SubmissionFile::SUBMISSION_FILE_PROOF)
->whereNotNull('sf.doi_id')
->where('p.status', '=', PKPPublication::STATUS_PUBLISHED);
+ $this->whereDepositablePublication($q, $doiVersioning, false);
});
});
})
->whereIn('d.status', [Doi::STATUS_UNREGISTERED, Doi::STATUS_ERROR, Doi::STATUS_STALE]);
- return $q->get(['s.submission_id', 'd.doi_id']);
+ return $q->distinct()->get([DB::raw('COALESCE(p.submission_id, cp.submission_id, pfp.submission_id, sfd.submission_id) AS submission_id'), 'd.doi_id']);
}
}
diff --git a/classes/doi/Repository.php b/classes/doi/Repository.php
index b856233af3c..706a2bfb0e4 100644
--- a/classes/doi/Repository.php
+++ b/classes/doi/Repository.php
@@ -113,61 +113,12 @@ public function mintSubmissionFileDoi(SubmissionFile $submissionFile, Submission
public function getDoisForSubmission(int $submissionId): array
{
- $doiIds = Collection::make();
-
$submission = Repo::submission()->get($submissionId);
- /** @var Publication[] $publications */
- $publications = [$submission->getCurrentPublication()];
-
- /** @var PressDAO $contextDao */
- $contextDao = Application::getContextDAO();
- /** @var Press */
- $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);
- }
-
- // Chapters
- $chapters = $publication->getData('chapters');
- foreach ($chapters as $chapter) {
- $chapterDoiId = $chapter->getData('doiId');
- if (!empty($chapterDoiId) && $context->isDoiTypeEnabled(self::TYPE_CHAPTER)) {
- $doiIds->add($chapterDoiId);
- }
- }
-
- // Publication formats
- $publicationFormats = $publication->getData('publicationFormats');
- foreach ($publicationFormats as $publicationFormat) {
- $publicationFormatDoiId
- = $publicationFormat->getData('doiId');
- if (!empty($publicationFormatDoiId) && $context->isDoiTypeEnabled(self::TYPE_REPRESENTATION)) {
- $doiIds->add($publicationFormatDoiId);
- }
- }
-
- // Submission files
- if ($context->isDoiTypeEnabled(self::TYPE_SUBMISSION_FILE)) {
- $submissionFiles = Repo::submissionFile()
- ->getCollector()
- ->filterBySubmissionIds([$publication->getData('submissionId')])
- ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF])
- ->getMany();
-
- /** @var SubmissionFile $submissionFile */
- foreach ($submissionFiles as $submissionFile) {
- $submissionFileDoiId = $submissionFile->getData('doiId');
- if (!empty($submissionFileDoiId)) {
- $doiIds->add($submissionFileDoiId);
- }
- }
- }
- }
-
- return $doiIds->unique()->toArray();
+ return collect($submission->getData('publications'))
+ ->flatMap(fn (Publication $publication) => $this->getDoisForPublication($publication))
+ ->unique()
+ ->values()
+ ->all();
}
/**
@@ -212,10 +163,13 @@ public function getDoisForPublication(Publication $publication): array
// Submission files
if ($context->isDoiTypeEnabled(self::TYPE_SUBMISSION_FILE)) {
- $submissionFiles = Repo::submissionFile()
+ // Only the files of this version's publication formats
+ $publicationFormatIds = collect($publicationFormats)->map(fn (PublicationFormat $publicationFormat) => $publicationFormat->getId())->all();
+ $submissionFiles = empty($publicationFormatIds) ? [] : Repo::submissionFile()
->getCollector()
->filterBySubmissionIds([$publication->getData('submissionId')])
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF])
+ ->filterByAssoc(Application::ASSOC_TYPE_PUBLICATION_FORMAT, $publicationFormatIds)
->getMany();
/** @var SubmissionFile $submissionFile */
diff --git a/classes/publication/DAO.php b/classes/publication/DAO.php
index 41a272c0765..08dabe6fbb0 100644
--- a/classes/publication/DAO.php
+++ b/classes/publication/DAO.php
@@ -16,8 +16,11 @@
use APP\core\Application;
use APP\monograph\ChapterDAO;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Support\Facades\DB;
use PKP\core\interfaces\CollectorInterface;
use PKP\db\DAORegistry;
+use PKP\submissionFile\SubmissionFile;
class DAO extends \PKP\publication\DAO
{
@@ -55,4 +58,34 @@ public function fromRow(object $row, array $ids, object $cache, ?CollectorInterf
$publication->setData('chapters', $chapterDao->getByPublicationId($publication->getId())->toArray());
return $publication;
}
+
+ /**
+ * @copydoc \PKP\publication\DAO::whereHasDoi()
+ */
+ protected function whereHasDoi(Builder $q): Builder
+ {
+ return parent::whereHasDoi($q)
+ ->orWhereExists(
+ fn (Builder $q) => $q->select(DB::raw(1))
+ ->from('submission_chapters as spc')
+ ->whereColumn('spc.publication_id', '=', 'p.publication_id')
+ ->whereNotNull('spc.doi_id')
+ )
+ ->orWhereExists(
+ fn (Builder $q) => $q->select(DB::raw(1))
+ ->from('publication_formats as pf')
+ ->whereColumn('pf.publication_id', '=', 'p.publication_id')
+ ->where(
+ fn (Builder $q) => $q->whereNotNull('pf.doi_id')
+ ->orWhereExists(
+ fn (Builder $q) => $q->select(DB::raw(1))
+ ->from('submission_files as sf')
+ ->where('sf.assoc_type', '=', Application::ASSOC_TYPE_PUBLICATION_FORMAT)
+ ->whereColumn('sf.assoc_id', '=', 'pf.publication_format_id')
+ ->where('sf.file_stage', '=', SubmissionFile::SUBMISSION_FILE_PROOF)
+ ->whereNotNull('sf.doi_id')
+ )
+ )
+ );
+ }
}
diff --git a/classes/publication/Repository.php b/classes/publication/Repository.php
index dfc042d93fb..67e7671993c 100644
--- a/classes/publication/Repository.php
+++ b/classes/publication/Repository.php
@@ -38,6 +38,7 @@
use PKP\db\DAORegistry;
use PKP\doi\exceptions\DoiException;
use PKP\notification\Notification;
+use PKP\observers\events\PublicationVersioned;
use PKP\plugins\Hook;
use PKP\publication\Collector;
use PKP\publication\PKPPublication;
@@ -252,6 +253,8 @@ public function version(Publication $publication, ?VersionStage $versionStage =
}
}
+ event(new PublicationVersioned($this->get($newId), $publication, Repo::submission()->get($submissionId), $context));
+
return $newId;
}
@@ -483,6 +486,24 @@ public function makeThumbnail($filePath, $thumbFileName, $maxWidth, $maxHeight)
imagedestroy($thumb);
}
+ /**
+ * Assign DOIs to a publication's new chapters, formats or files, if DOIs are assigned on item creation
+ */
+ public function createDoisOnCreation(int $publicationId): void
+ {
+ $publication = $this->get($publicationId);
+ $submission = Repo::submission()->get($publication->getData('submissionId'));
+ // Objects added during the submission wizard get their DOIs on submit
+ if ($submission->getData('submissionProgress')) {
+ return;
+ }
+
+ $context = Application::getContextDAO()->getById($submission->getData('contextId'));
+ if (Repo::doi()->assignOnItemCreation($context)) {
+ $this->createDois($publication);
+ }
+ }
+
/** @copydoc \PKP\publication\Repository::createDois() */
public function createDois(Publication $publication): array
{
@@ -544,11 +565,13 @@ public function createDois(Publication $publication): array
// Submission files
if ($context->isDoiTypeEnabled(Repo::doi()::TYPE_SUBMISSION_FILE)) {
- // Get all submission files assigned to a publication format
- $submissionFiles = Repo::submissionFile()
+ // Only the files of this version's publication formats
+ $publicationFormatIds = collect($publicationFormats)->map(fn (PublicationFormat $publicationFormat) => $publicationFormat->getId())->all();
+ $submissionFiles = empty($publicationFormatIds) ? [] : Repo::submissionFile()
->getCollector()
->filterBySubmissionIds([$publication->getData('submissionId')])
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF])
+ ->filterByAssoc(Application::ASSOC_TYPE_PUBLICATION_FORMAT, $publicationFormatIds)
->getMany();
/** @var SubmissionFile $submissionFile */
diff --git a/classes/submission/Collector.php b/classes/submission/Collector.php
index 67962e32507..cb6cb5acbc3 100644
--- a/classes/submission/Collector.php
+++ b/classes/submission/Collector.php
@@ -131,12 +131,27 @@ protected function addHasDoisFilterToQuery(Builder $q)
/**
* APP-specific filtering for submissions that should be listed on the DOI management page.
- * Those are:
- * submissions in the workflow editing and production stage,
- * submissions that have a published publication, and
- * submissions whose sub objects have a DOI.
+ * Those are all submitted submissions that have not been declined, and all submissions in the
+ * copyediting or production stage, with a published publication or with a DOI. The latter
+ * can also be listed on their own (inEditingOrPublished).
*/
protected function addOnDoiPageFilterToQuery(Builder $q)
+ {
+ $q->where('s.submission_progress', '');
+ if ($this->inEditingOrPublished) {
+ $this->addInEditingPublishedOrWithDoisToQuery($q);
+ } else {
+ $q->where(function (Builder $q) {
+ $q->where('s.status', '!=', Submission::STATUS_DECLINED)
+ ->orWhere(fn (Builder $q) => $this->addInEditingPublishedOrWithDoisToQuery($q));
+ });
+ }
+ }
+
+ /**
+ * Submissions in the copyediting or production stage, with a published publication or with a DOI
+ */
+ protected function addInEditingPublishedOrWithDoisToQuery(Builder $q): void
{
$q->where(function (Builder $q) {
$q->whereIn('s.stage_id', [WORKFLOW_STAGE_ID_EDITING, WORKFLOW_STAGE_ID_PRODUCTION])
diff --git a/classes/submission/Repository.php b/classes/submission/Repository.php
index 8dc347743dd..b2860beaec0 100644
--- a/classes/submission/Repository.php
+++ b/classes/submission/Repository.php
@@ -116,11 +116,13 @@ public function createDois(Submission $submission): array
// Submission files
if ($context->isDoiTypeEnabled(Repo::doi()::TYPE_SUBMISSION_FILE)) {
- // Get all submission files assigned to a publication format
- $submissionFiles = Repo::submissionFile()
+ // Only the files of this version's publication formats
+ $publicationFormatIds = collect($publicationFormats)->map(fn (PublicationFormat $publicationFormat) => $publicationFormat->getId())->all();
+ $submissionFiles = empty($publicationFormatIds) ? [] : Repo::submissionFile()
->getCollector()
->filterBySubmissionIds([$publication->getData('submissionId')])
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF])
+ ->filterByAssoc(Application::ASSOC_TYPE_PUBLICATION_FORMAT, $publicationFormatIds)
->getMany();
/** @var SubmissionFile $submissionFile */
diff --git a/classes/submissionFile/Repository.php b/classes/submissionFile/Repository.php
index 3ecb201efde..a7a7e42013e 100644
--- a/classes/submissionFile/Repository.php
+++ b/classes/submissionFile/Repository.php
@@ -53,6 +53,28 @@ public function getCollector(): Collector
return App::makeWith(Collector::class, ['dao' => $this->dao]);
}
+ /** @copydoc SubmissionFileRepository::add() */
+ public function add(SubmissionFile $submissionFile): int
+ {
+ $submissionFileId = parent::add($submissionFile);
+
+ // Only proof files of a publication format get a DOI
+ if (
+ !$submissionFile->getData('doiId')
+ && $submissionFile->getData('fileStage') == SubmissionFile::SUBMISSION_FILE_PROOF
+ && $submissionFile->getData('assocType') == Application::ASSOC_TYPE_PUBLICATION_FORMAT
+ ) {
+ $publicationId = DB::table('publication_formats')
+ ->where('publication_format_id', '=', $submissionFile->getData('assocId'))
+ ->value('publication_id');
+ if ($publicationId) {
+ Repo::publication()->createDoisOnCreation($publicationId);
+ }
+ }
+
+ return $submissionFileId;
+ }
+
public function getFileStages(): array
{
$stages = [
diff --git a/controllers/grid/catalogEntry/form/PublicationFormatForm.php b/controllers/grid/catalogEntry/form/PublicationFormatForm.php
index a10006dbd49..cdc4115f1e8 100644
--- a/controllers/grid/catalogEntry/form/PublicationFormatForm.php
+++ b/controllers/grid/catalogEntry/form/PublicationFormatForm.php
@@ -284,6 +284,8 @@ public function execute(...$functionParams)
'publicationFormatName' => $publicationFormat->getData('name')
]);
Repo::eventLog()->add($logEntry);
+
+ Repo::publication()->createDoisOnCreation($this->getPublication()->getId());
}
return $representationId;
diff --git a/controllers/grid/users/chapter/form/ChapterForm.php b/controllers/grid/users/chapter/form/ChapterForm.php
index eea75f7b27d..67c72e7153b 100644
--- a/controllers/grid/users/chapter/form/ChapterForm.php
+++ b/controllers/grid/users/chapter/form/ChapterForm.php
@@ -347,6 +347,9 @@ public function execute(...$functionParams)
$this->getChapter()->getId()
);
+ // Also when its page is enabled later, as only chapters with a page get a DOI
+ Repo::publication()->createDoisOnCreation($this->getPublication()->getId());
+
return true;
}
}
diff --git a/cypress/tests/integration/Doi.cy.js b/cypress/tests/integration/Doi.cy.js
index 648240f00c1..329c5b2a22e 100644
--- a/cypress/tests/integration/Doi.cy.js
+++ b/cypress/tests/integration/Doi.cy.js
@@ -26,6 +26,14 @@ describe('DOI tests', function() {
cy.get('button#submission-doi-management-button').click();
};
+ // The submissions the DOI page listed before it showed all submitted submissions
+ const filterInEditingPublishedOrWithDois = () => {
+ cy.get(
+ '#submission-doi-management button:contains("In Copyediting, Production, Published or with DOIs")'
+ ).click();
+ cy.get('#submission-doi-management .pkpSpinner').should('not.exist');
+ };
+
const clearFilter = () => {
cy.get('#submission-doi-management button:contains("Clear filter")').each(
($el, index, $list) => {
@@ -79,9 +87,11 @@ describe('DOI tests', function() {
it('Check filters and mark registered', function() {
cy.log('Check Submission Filter Behaviour (pre-deposit)');
loginAndGoToDoiPage();
+ filterInEditingPublishedOrWithDois();
cy.checkDoiFilterResults('Needs DOI', 'Allan — Bomb Canada and Other Unkind Remarks in the American Media', 6);
cy.checkDoiFilterResults('DOI Assigned', 'Dawson et al. — From Bricks to Brains: The Embodied Cognitive Science of LEGO Robots', 1);
clearFilter();
+ filterInEditingPublishedOrWithDois();
cy.checkDoiFilterResults('Unregistered', 'Dawson et al. — From Bricks to Brains: The Embodied Cognitive Science of LEGO Robots', 1);
clearFilter();
diff --git a/dbscripts/xml/upgrade.xml b/dbscripts/xml/upgrade.xml
index 83f64b4cc47..0f0d4e39c70 100644
--- a/dbscripts/xml/upgrade.xml
+++ b/dbscripts/xml/upgrade.xml
@@ -235,6 +235,9 @@
+
+
+
diff --git a/lib/pkp b/lib/pkp
index 03b9a78d8f8..58c7f454ff8 160000
--- a/lib/pkp
+++ b/lib/pkp
@@ -1 +1 @@
-Subproject commit 03b9a78d8f83f054db1ffbb4d82b8870566edafc
+Subproject commit 58c7f454ff88baf7dc6df9cd0c9090fb97364b56
diff --git a/locale/en/manager.po b/locale/en/manager.po
index bb9b593c018..1de71833e32 100644
--- a/locale/en/manager.po
+++ b/locale/en/manager.po
@@ -412,6 +412,12 @@ msgstr "Chapters without a landing page cannot have a DOI."
msgid "manager.dois.formatIdentifier.file"
msgstr "Format / {$format}"
+msgid "manager.dois.filters.workflow"
+msgstr "Workflow"
+
+msgid "manager.dois.filters.inEditingOrPublished"
+msgstr "In Copyediting, Production, Published or with DOIs"
+
msgid "manager.setup.editorDecision"
msgstr "Editor Decision"