From e266eea435a594aa2b1f6a9964666d468d704671 Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Tue, 10 Feb 2026 19:25:45 +0100 Subject: [PATCH 01/23] User can now select customlabels to reserve new certificate identifier #89, #90 --- api/v1/CodecheckApiHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/v1/CodecheckApiHandler.php b/api/v1/CodecheckApiHandler.php index afedb097..49c36391 100644 --- a/api/v1/CodecheckApiHandler.php +++ b/api/v1/CodecheckApiHandler.php @@ -28,6 +28,8 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; +use function Clue\StreamFilter\append; + class CodecheckApiHandler { private JsonResponse $response; From c20f2f5165534fe49c85bda6ebbea75fdd7eef6f Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Fri, 1 May 2026 21:16:29 +0200 Subject: [PATCH 02/23] Added Hook to validate the success of a codecheck at publication #32 --- CodecheckPlugin.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CodecheckPlugin.php b/CodecheckPlugin.php index a1b03106..71744cd3 100644 --- a/CodecheckPlugin.php +++ b/CodecheckPlugin.php @@ -71,10 +71,18 @@ public function register($category, $path, $mainContextId = null): bool Hook::add('Template::SubmissionWizard::Section::Review', function($hookName, $params) use ($codecheckWizard) { return $codecheckWizard->addToSubmissionWizardReviewTemplate($hookName, $params); }); + + // Test if we can hook into the publication to block it if codecheck failed + Hook::add('Publication::publish', $this->interceptPublication(...)); } return $success; } + + public function interceptPublication(string $hookName, array $args): void + { + error_log("[CODECHECK Plugin] Successfully intercepted publication!"); + } /** * Setup the `CodecheckApiHandler` From ef34f78329dfd1f158c8e4f46dd770cc5b807ba9 Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Sun, 3 May 2026 16:54:49 +0200 Subject: [PATCH 03/23] Added feature to block publication, when CODECHECK status is failed #32 --- CodecheckPlugin.php | 20 +++++++++++++++++--- locale/en/locale.po | 4 ++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CodecheckPlugin.php b/CodecheckPlugin.php index 71744cd3..64eca938 100644 --- a/CodecheckPlugin.php +++ b/CodecheckPlugin.php @@ -73,15 +73,29 @@ public function register($category, $path, $mainContextId = null): bool }); // Test if we can hook into the publication to block it if codecheck failed - Hook::add('Publication::publish', $this->interceptPublication(...)); + Hook::add('Publication::validatePublish', $this->validateCodecheckStatus(...)); } return $success; } - public function interceptPublication(string $hookName, array $args): void + public function validateCodecheckStatus(string $hookName, array $args): bool { - error_log("[CODECHECK Plugin] Successfully intercepted publication!"); + $errors = &$args[0]; + $publication = $args[1]; // sometimes passed by reference depending on version + + error_log(print_r($publication, true)); + error_log(print_r($errors, true)); + + error_log("[CODECHECK Plugin] Validating CODECHECK before publication!"); + + $codecheckStatus = false; + + if (!$codecheckStatus) { + $errors[] = __('plugins.generic.codecheck.status.validation.failed'); + } + + return $codecheckStatus; } /** diff --git a/locale/en/locale.po b/locale/en/locale.po index cb727fd8..32e49dfd 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -358,6 +358,10 @@ msgstr "Status" msgid "plugins.generic.codecheck.status.complete" msgstr "Complete" + +msgid "plugins.generic.codecheck.status.validation.failed" +msgstr "The mandatory CODECHECK for this submission failed. Please get in touch with the codechecker(s) to resolve all errors, before this submission can be published!" + msgid "plugins.generic.codecheck.dataAvailability.placeholder" msgstr "Describe how your data and code are available..." From d8595e538c35248328942f6c7fa816996630e5c8 Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Tue, 5 May 2026 18:20:56 +0200 Subject: [PATCH 04/23] Added schema for codecheck_submission_status table #119 --- classes/migration/CodecheckSchemaMigration.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/classes/migration/CodecheckSchemaMigration.php b/classes/migration/CodecheckSchemaMigration.php index bfd40141..c4a17612 100644 --- a/classes/migration/CodecheckSchemaMigration.php +++ b/classes/migration/CodecheckSchemaMigration.php @@ -44,6 +44,22 @@ public function issueLabelsUp(): void }); } } + + public function codecheckStatusUp(): void + { + if (!Schema::hasTable('codecheck_status')) { + Schema::create('codecheck_status', function (Blueprint $table) { + $table->bigInteger('status_id')->autoIncrement()->primary(); + $table->bigInteger('submission_id'); + $table->foreign('submission_id', 'codecheck_status_metadata')->references('submission_id')->on('codecheck_metadata')->onDelete('cascade'); + $table->string('status', 100); + $table->timestamp('timestamp')->nullable(); + $table->string('user', 100); + $table->timestamps(); + $table->index('status_id'); + }); + } + } private function createCodecheckGenres(): void { From a3053257a76b253648e67a9c5d2b60783763dc08 Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Tue, 5 May 2026 19:02:33 +0200 Subject: [PATCH 05/23] Basic funtionaility for setting fr publication blocking #121 --- CodecheckPlugin.php | 7 +++--- classes/Constants.php | 2 ++ classes/Settings/SettingsForm.php | 27 ++++++++++++++++++++ locale/en/locale.po | 42 ++++++++++++++++++++++++++++--- templates/settings.tpl | 19 ++++++++++++++ 5 files changed, 91 insertions(+), 6 deletions(-) diff --git a/CodecheckPlugin.php b/CodecheckPlugin.php index 64eca938..83873f73 100644 --- a/CodecheckPlugin.php +++ b/CodecheckPlugin.php @@ -89,13 +89,14 @@ public function validateCodecheckStatus(string $hookName, array $args): bool error_log("[CODECHECK Plugin] Validating CODECHECK before publication!"); - $codecheckStatus = false; + $codecheckStatus = $this->getSetting($context->getId(), Constants::CODECHECK_STATUS); - if (!$codecheckStatus) { + if ($codecheckStatus != 'no-block') { $errors[] = __('plugins.generic.codecheck.status.validation.failed'); + return false; } - return $codecheckStatus; + return true; } /** diff --git a/classes/Constants.php b/classes/Constants.php index 997b9c8a..bd26e3b2 100644 --- a/classes/Constants.php +++ b/classes/Constants.php @@ -40,4 +40,6 @@ class Constants public const CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_FIELDS = 'codecheckGithubUpdateFields'; public const CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_TITLE = 'updateTitle'; public const CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_BODY = 'updateBody'; + # Codecheck Status + public const CODECHECK_STATUS = 'codecheckStatus'; } \ No newline at end of file diff --git a/classes/Settings/SettingsForm.php b/classes/Settings/SettingsForm.php index a35d47da..6466fecc 100644 --- a/classes/Settings/SettingsForm.php +++ b/classes/Settings/SettingsForm.php @@ -125,6 +125,14 @@ public function initData(): void ) ?? [] ); + $this->setData( + Constants::CODECHECK_STATUS, + $this->plugin->getSetting( + $context->getId(), + Constants::CODECHECK_STATUS + ) + ); + // Default to true — show the dashboard column unless explicitly disabled $showDashboardColumn = $this->plugin->getSetting( $context->getId(), @@ -166,6 +174,7 @@ public function readInputData(): void Constants::CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_TITLE, Constants::CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_BODY, Constants::CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_FIELDS, + Constants::CODECHECK_STATUS, ]); parent::readInputData(); @@ -195,6 +204,18 @@ public function fetch($request, $template = null, $display = false): ?string 'showDashboardColumn', $this->getData(Constants::CODECHECK_SHOW_DASHBOARD_COLUMN) ); + $templateMgr->assign('codecheckStatuses', [ + 'no-block' => __('plugins.generic.codecheck.settings.status.noBlock'), + 'needs-codechecker' => __('plugins.generic.codecheck.status.needsCodechecker'), + 'assigned-codechecker' => __('plugins.generic.codecheck.status.assignedCodechecker'), + 'stalled-author' => __('plugins.generic.codecheck.status.stalled.author'), + 'stalled-codechecker' => __('plugins.generic.codecheck.status.stalled.codechecker'), + 'completed-unsuccessful' => __('plugins.generic.codecheck.status.completed.unsuccessful'), + 'completed-partial-reproduction' => __('plugins.generic.codecheck.status.completed.partialReproduction'), + 'completed-full-reproduction' => __('plugins.generic.codecheck.status.completed.fullReproduction'), + 'published-certificate-partialReproduction' => __('plugins.generic.codecheck.status.publishedCertificate.partialReproduction'), + 'published-certificate-fullReproduction' => __('plugins.generic.codecheck.status.publishedCertificate.fullReproduction'), + ]); return parent::fetch($request, $template, $display); } @@ -265,6 +286,12 @@ public function execute(...$functionArgs): mixed fn ($label) => !empty($label) )) ); + + $this->plugin->updateSetting( + $context->getId(), + Constants::CODECHECK_STATUS, + $this->getData(Constants::CODECHECK_STATUS) + ); $this->plugin->updateSetting( $context->getId(), diff --git a/locale/en/locale.po b/locale/en/locale.po index 32e49dfd..af49f7e7 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -110,6 +110,15 @@ msgstr "Update the Issue Title (containing the article's authors)" msgid "plugins.generic.codecheck.settings.updateIssue.body" msgstr "Update the Issue Description (containing the article, codechecker and repository information)" +msgid "plugins.generic.codecheck.settings.status" +msgstr "Block Publication, if CODECHECK hasn't reached the desired status" + +msgid "plugins.generic.codecheck.settings.status.description" +msgstr "Please select the minimum required status a CODECHECK should have, before a submission is allowed to be published" + +msgid "plugins.generic.codecheck.settings.status.noBlock" +msgstr "Ignore the CODECHECK status and never block Publication" + msgid "plugins.generic.codecheck.title" msgstr "CODECHECK" @@ -362,15 +371,42 @@ msgstr "Complete" msgid "plugins.generic.codecheck.status.validation.failed" msgstr "The mandatory CODECHECK for this submission failed. Please get in touch with the codechecker(s) to resolve all errors, before this submission can be published!" +msgid "plugins.generic.codecheck.status.pending" +msgstr "Pending" + +msgid "plugins.generic.codecheck.status.needsCodechecker" +msgstr "needs codechecker" + +msgid "plugins.generic.codecheck.status.assignedCodechecker" +msgstr "codechecker assigned" + +msgid "plugins.generic.codecheck.status.stalled.author" +msgstr "stalled (author)" + +msgid "plugins.generic.codecheck.status.stalled.codechecker" +msgstr "stalled (codechecker)" + +msgid "plugins.generic.codecheck.status.completed.unsuccessful" +msgstr "completed: unsuccessful" + +msgid "plugins.generic.codecheck.status.completed.partialReproduction" +msgstr "completed: partial reproduction" + +msgid "plugins.generic.codecheck.status.completed.fullReproduction" +msgstr "completed: full reproduction" + +msgid "plugins.generic.codecheck.status.publishedCertificate.partialReproduction" +msgstr "published certificate: partial reproduction" + +msgid "plugins.generic.codecheck.status.publishedCertificate.fullReproduction" +msgstr "published certificate: full reproduction" + msgid "plugins.generic.codecheck.dataAvailability.placeholder" msgstr "Describe how your data and code are available..." msgid "plugins.generic.codecheck.review.title" msgstr "CODECHECK Information" -msgid "plugins.generic.codecheck.status.pending" -msgstr "Pending" - msgid "plugins.generic.codecheck.notYetAssigned" msgstr "Not yet assigned" diff --git a/templates/settings.tpl b/templates/settings.tpl index eb847e8f..16911216 100644 --- a/templates/settings.tpl +++ b/templates/settings.tpl @@ -265,6 +265,25 @@ label="plugins.generic.codecheck.settings.updateIssue.body" } {/fbvFormSection} + + {* Block Publication, when CODECHECK has specific status *} + {fbvFormSection + list=true + } +
+ +
+ + {fbvElement + type="select" + id="codecheckStatus" + class="codecheck-form-select" + from=$codecheckStatuses + selected=$codecheckStatus + translate=false + } + {/fbvFormSection} + {* TODO: Add more settings in future development *} {* - ORCID integration settings *} {* - Email template settings *} From 08e6cecb4ade51b520b7a4d5f4fa15901aa9f6ee Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Fri, 8 May 2026 19:03:52 +0200 Subject: [PATCH 06/23] Continued work on the select statuses setting #121 --- CodecheckPlugin.php | 14 +++-- api/v1/CodecheckApiHandler.php | 3 + classes/Constants.php | 17 +++++ classes/Settings/SettingsForm.php | 35 +++++------ css/codecheck.css | 101 ++++++++++++++++++++++++++++++ locale/en/locale.po | 9 ++- templates/settings.tpl | 43 ++++++++++--- 7 files changed, 186 insertions(+), 36 deletions(-) diff --git a/CodecheckPlugin.php b/CodecheckPlugin.php index 83873f73..17502f04 100644 --- a/CodecheckPlugin.php +++ b/CodecheckPlugin.php @@ -83,16 +83,18 @@ public function validateCodecheckStatus(string $hookName, array $args): bool { $errors = &$args[0]; $publication = $args[1]; // sometimes passed by reference depending on version - - error_log(print_r($publication, true)); - error_log(print_r($errors, true)); + $request = Application::get()->getRequest(); + $context = $request->getContext(); error_log("[CODECHECK Plugin] Validating CODECHECK before publication!"); - $codecheckStatus = $this->getSetting($context->getId(), Constants::CODECHECK_STATUS); + $codecheckStatus = 'plugins.generic.codecheck.status.completed.partialReproduction'; + $codecheckStatusKeysSelected = $this->getSetting($context->getId(), Constants::CODECHECK_STATUS_KEYS_SELECTED); - if ($codecheckStatus != 'no-block') { - $errors[] = __('plugins.generic.codecheck.status.validation.failed'); + if (!in_array($codecheckStatus, $codecheckStatusKeysSelected)) { + $errors[] = __('plugins.generic.codecheck.status.validation.failed', [ + 'codecheckStatus' => __($codecheckStatus) + ]); return false; } diff --git a/api/v1/CodecheckApiHandler.php b/api/v1/CodecheckApiHandler.php index 49c36391..1c5da939 100644 --- a/api/v1/CodecheckApiHandler.php +++ b/api/v1/CodecheckApiHandler.php @@ -261,6 +261,9 @@ private function getCodecheckIssueLabels(): void $githubCustomLabels = $this->plugin->getSetting($context->getId(), Constants::CODECHECK_GITHUB_CUSTOM_LABELS); $codecheckIssueLabels->addLabelArray($githubCustomLabels); + $codecheckStatuses = $this->plugin->getSetting($context->getId(), Constants::CODECHECK_STATUS_KEYS_SELECTED); + error_log(print_r($codecheckStatuses, true)); + // Serve the getCodecheckIssueLabels API route JsonResponse::staticResponse([ 'success' => true, diff --git a/classes/Constants.php b/classes/Constants.php index bd26e3b2..8296c441 100644 --- a/classes/Constants.php +++ b/classes/Constants.php @@ -22,6 +22,21 @@ class Constants * Basic plugin setting */ public const SETTING_ENABLE_CODECHECK = 'enableCodecheck'; + + /** + * The possible Codecheck Statuses + */ + public const CODECHECK_STATUSES = [ + 'plugins.generic.codecheck.status.needsCodechecker', + 'plugins.generic.codecheck.status.assignedCodechecker', + 'plugins.generic.codecheck.status.stalled.author', + 'plugins.generic.codecheck.status.stalled.codechecker', + 'plugins.generic.codecheck.status.completed.unsuccessful', + 'plugins.generic.codecheck.status.completed.partialReproduction', + 'plugins.generic.codecheck.status.completed.fullReproduction', + 'plugins.generic.codecheck.status.publishedCertificate.partialReproduction', + 'plugins.generic.codecheck.status.publishedCertificate.fullReproduction', + ]; /** * Plugin settings keys - NEW ADDITIONS @@ -42,4 +57,6 @@ class Constants public const CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_BODY = 'updateBody'; # Codecheck Status public const CODECHECK_STATUS = 'codecheckStatus'; + public const CODECHECK_STATUSES_SELECTED = 'codecheckStatusesSelected'; + public const CODECHECK_STATUS_KEYS_SELECTED = 'codecheckStatusKeysSelected'; } \ No newline at end of file diff --git a/classes/Settings/SettingsForm.php b/classes/Settings/SettingsForm.php index 6466fecc..5a7e86bf 100644 --- a/classes/Settings/SettingsForm.php +++ b/classes/Settings/SettingsForm.php @@ -126,11 +126,11 @@ public function initData(): void ); $this->setData( - Constants::CODECHECK_STATUS, + Constants::CODECHECK_STATUS_KEYS_SELECTED, $this->plugin->getSetting( $context->getId(), - Constants::CODECHECK_STATUS - ) + Constants::CODECHECK_STATUS_KEYS_SELECTED + ) ?? [] ); // Default to true — show the dashboard column unless explicitly disabled @@ -175,6 +175,8 @@ public function readInputData(): void Constants::CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_BODY, Constants::CODECHECK_GITHUB_REGISTER_ISSUE_UPDATE_FIELDS, Constants::CODECHECK_STATUS, + Constants::CODECHECK_STATUSES_SELECTED, + Constants::CODECHECK_STATUS_KEYS_SELECTED, ]); parent::readInputData(); @@ -192,7 +194,7 @@ public function fetch($request, $template = null, $display = false): ?string $templateMgr = TemplateManager::getManager($request); $templateMgr->assign('pluginName', $this->plugin->getName()); $templateMgr->assign( - 'githubCustomLabels', + Constants::CODECHECK_GITHUB_CUSTOM_LABELS, $this->getData(Constants::CODECHECK_GITHUB_CUSTOM_LABELS) ?? [] ); $templateMgr->assign('codecheckModes', [ @@ -200,22 +202,17 @@ public function fetch($request, $template = null, $display = false): ?string 'opt-out' => __('plugins.generic.codecheck.settings.mode.opt.out'), 'mandatory' => __('plugins.generic.codecheck.settings.mode.mandatory'), ]); + $templateMgr->assign( 'showDashboardColumn', $this->getData(Constants::CODECHECK_SHOW_DASHBOARD_COLUMN) ); - $templateMgr->assign('codecheckStatuses', [ - 'no-block' => __('plugins.generic.codecheck.settings.status.noBlock'), - 'needs-codechecker' => __('plugins.generic.codecheck.status.needsCodechecker'), - 'assigned-codechecker' => __('plugins.generic.codecheck.status.assignedCodechecker'), - 'stalled-author' => __('plugins.generic.codecheck.status.stalled.author'), - 'stalled-codechecker' => __('plugins.generic.codecheck.status.stalled.codechecker'), - 'completed-unsuccessful' => __('plugins.generic.codecheck.status.completed.unsuccessful'), - 'completed-partial-reproduction' => __('plugins.generic.codecheck.status.completed.partialReproduction'), - 'completed-full-reproduction' => __('plugins.generic.codecheck.status.completed.fullReproduction'), - 'published-certificate-partialReproduction' => __('plugins.generic.codecheck.status.publishedCertificate.partialReproduction'), - 'published-certificate-fullReproduction' => __('plugins.generic.codecheck.status.publishedCertificate.fullReproduction'), - ]); + + $templateMgr->assign( + Constants::CODECHECK_STATUSES_SELECTED, + (array) $this->getData(Constants::CODECHECK_STATUSES_SELECTED) ?? [] + ); + $templateMgr->assign('codecheckStatuses', Constants::CODECHECK_STATUSES); return parent::fetch($request, $template, $display); } @@ -286,11 +283,11 @@ public function execute(...$functionArgs): mixed fn ($label) => !empty($label) )) ); - + $this->plugin->updateSetting( $context->getId(), - Constants::CODECHECK_STATUS, - $this->getData(Constants::CODECHECK_STATUS) + Constants::CODECHECK_STATUS_KEYS_SELECTED, + (array) $this->getData(Constants::CODECHECK_STATUS_KEYS_SELECTED) ); $this->plugin->updateSetting( diff --git a/css/codecheck.css b/css/codecheck.css index 9267dd7a..a86adef7 100644 --- a/css/codecheck.css +++ b/css/codecheck.css @@ -315,4 +315,105 @@ textarea[name="dataAvailabilityStatement"] { border-radius: 3px !important; height: 2.5rem !important; background: #fff; +} + +/* Dropdown with Checkbox selects */ +.settings-droptown { + font-size: 14px; + padding: 6px; + border: 1px solid #ccc; + border-radius: 3px; + height: 2.5rem; + width: 100%; + background: #fff; +} + +.settings-droptown.dropdown { + overflow: visible; + color: inherit; + padding: 0 !important; + position: relative; +} + +fieldset:disabled .settings-droptown.dropdown .dropbtn { + /* Centeres the Text in the select */ + text-align: center; + text-align-last: center; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-image: none !important; /* removes arrow background */ + background-color: #868686; + color: #ffffff; + cursor: not-allowed; + pointer-events: none; + opacity: 0.6; + font-weight: 600; +} + +.settings-droptown.dropdown .dropbtn { + font-size: 14px; + padding-left: 6px; + padding-right: 6px; + padding-bottom: 0; + padding-top: 0; + height: 100%; + border: none; + outline: none; + background-color: inherit; + font-family: inherit; + margin: 0; + width: inherit; + text-align: left; +} + +.settings-droptown.dropdown:hover .dropbtn { + background-color: #eee; +} + +.settings-droptown .dropdown-content { + display: none; + position: absolute; + background-color: #fff; + border-radius: 8px; + border: 1px solid #ccc; + min-width: 160px; + z-index: 1; + padding: 10px; + top: 100%; + bottom: auto; +} + +.settings-droptown.dropdown-up .dropdown-content { + top: auto; + bottom: 100%; +} + +fieldset:disabled .settings-droptown .dropdown-content { + background-color: #868686; + border: 3px solid #ccc; + font-weight: 600; + color: #fff; +} + +.settings-droptown .dropdown-content .dropdown-checkbox-input { + float: none; + text-decoration: none; + display: block; + text-align: left; + outline: none; + border: none; + width: 100%; + padding: 0; +} + +.settings-droptown.dropdown:hover .dropdown-content { + display: block; +} + +.settings-droptown.dropdown .dropdown-content label { + font-style: italic; + margin-left: 5px; + color: inherit; + display: inline-block !important; } \ No newline at end of file diff --git a/locale/en/locale.po b/locale/en/locale.po index af49f7e7..bc1ed9c1 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -111,10 +111,13 @@ msgid "plugins.generic.codecheck.settings.updateIssue.body" msgstr "Update the Issue Description (containing the article, codechecker and repository information)" msgid "plugins.generic.codecheck.settings.status" -msgstr "Block Publication, if CODECHECK hasn't reached the desired status" +msgstr "What status must a check have for a paper to proceed with the publication?" msgid "plugins.generic.codecheck.settings.status.description" -msgstr "Please select the minimum required status a CODECHECK should have, before a submission is allowed to be published" +msgstr "Please select the minimum statuses required for a CODECHECK, before a submission is allowed to be published" + +msgid "plugins.generic.codecheck.settings.status.selectStatuses" +msgstr "Select Status(es)" msgid "plugins.generic.codecheck.settings.status.noBlock" msgstr "Ignore the CODECHECK status and never block Publication" @@ -369,7 +372,7 @@ msgid "plugins.generic.codecheck.status.complete" msgstr "Complete" msgid "plugins.generic.codecheck.status.validation.failed" -msgstr "The mandatory CODECHECK for this submission failed. Please get in touch with the codechecker(s) to resolve all errors, before this submission can be published!" +msgstr "The mandatory CODECHECK for this submission has a status ({$codecheckStatus}), which is not allowed for publication by the journal. Please get in touch with the codechecker(s) to resolve all errors, before this submission can be published!" msgid "plugins.generic.codecheck.status.pending" msgstr "Pending" diff --git a/templates/settings.tpl b/templates/settings.tpl index 16911216..d4d83fe9 100644 --- a/templates/settings.tpl +++ b/templates/settings.tpl @@ -70,6 +70,20 @@ ); }); }); + + $('.settings-droptown.dropdown').on('mouseenter', function() { + const $dropdown = $(this); + const $content = $dropdown.find('.dropdown-content'); + const rect = this.getBoundingClientRect(); + const contentHeight = $content.outerHeight() || 200; + const spaceBelow = window.innerHeight - rect.bottom; + + if (spaceBelow < contentHeight) { + $dropdown.addClass('dropdown-up'); + } else { + $dropdown.removeClass('dropdown-up'); + } + }); {/literal} @@ -274,14 +288,27 @@ - {fbvElement - type="select" - id="codecheckStatus" - class="codecheck-form-select" - from=$codecheckStatuses - selected=$codecheckStatus - translate=false - } +
+ +
{/fbvFormSection} {* TODO: Add more settings in future development *} From 6f4f594638a22287ace15d968e502f071ccc0ce6 Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Mon, 11 May 2026 12:07:52 +0200 Subject: [PATCH 07/23] Status should no be retrievable from the DB #61, #119 --- CodecheckPlugin.php | 7 +- api/v1/CodecheckApiHandler.php | 28 +++++++ classes/Workflow/CodecheckStatusHandler.php | 33 ++++++++ .../migration/CodecheckSchemaMigration.php | 6 +- registry/uiLocaleKeysBackend.json | 3 - .../js/Components/CodecheckReviewDisplay.vue | 81 ++++++++++--------- 6 files changed, 109 insertions(+), 49 deletions(-) create mode 100644 classes/Workflow/CodecheckStatusHandler.php diff --git a/CodecheckPlugin.php b/CodecheckPlugin.php index 17502f04..587ff746 100644 --- a/CodecheckPlugin.php +++ b/CodecheckPlugin.php @@ -438,9 +438,10 @@ public function setEnabled($enabled, $contextId = null) $result = parent::setEnabled($enabled, $contextId); if ($enabled) { - $migration = new CodecheckSchemaMigration(); - $migration->up(); - $migration->issueLabelsUp(); + $this->migration = new CodecheckSchemaMigration(); + $this->migration->up(); + $this->migration->issueLabelsUp(); + $this->migration->codecheckStatusUp(); } return $result; diff --git a/api/v1/CodecheckApiHandler.php b/api/v1/CodecheckApiHandler.php index 1c5da939..dc520c0e 100644 --- a/api/v1/CodecheckApiHandler.php +++ b/api/v1/CodecheckApiHandler.php @@ -29,6 +29,10 @@ use Illuminate\Support\Facades\Schema; use function Clue\StreamFilter\append; +use APP\plugins\generic\codecheck\classes\Exceptions\CurlExceptions\CurlInitException; +use APP\plugins\generic\codecheck\classes\Exceptions\CurlExceptions\CurlReadException; +use APP\plugins\generic\codecheck\classes\Workflow\CodecheckStatusHandler; +use Illuminate\Support\Facades\DB; class CodecheckApiHandler { @@ -87,6 +91,11 @@ public function __construct(CodecheckPlugin $plugin, Request $request, Codecheck 'handler' => [$this, 'getGithubRegisterRepositoryUrl'], 'roles' => $roles->readMetadata(), ], + [ + 'route' => 'status', + 'handler' => [$this, 'getCurrentStatus'], + 'roles' => $this->roles, + ] ], 'POST' => [ [ @@ -934,4 +943,23 @@ public function validateYamlStructure(): void 'success' => true, ], 200); } + + public function getCurrentStatus(): void + { + $submissionId = (int) $this->codecheckMetadataHandler->getSubmissionId(); + + $statusRecord = CodecheckStatusHandler::getCurrentStatusData($submissionId); + + if($statusRecord == null) { + JsonResponse::staticResponse([ + 'success' => false, + 'statusRecord' => $statusRecord, + ], 500); + } + + JsonResponse::staticResponse([ + 'success' => true, + 'statusRecord' => $statusRecord, + ], 200); + } } \ No newline at end of file diff --git a/classes/Workflow/CodecheckStatusHandler.php b/classes/Workflow/CodecheckStatusHandler.php new file mode 100644 index 00000000..8efd176a --- /dev/null +++ b/classes/Workflow/CodecheckStatusHandler.php @@ -0,0 +1,33 @@ +where('submission_id', $submissionId) + ->orderBy('timestamp', 'desc') + ->orderBy('status_id', 'desc') + ->first(); + } + + public static function getStatusDataHistory(int $submissionId) { + return DB::table('codecheck_status') + ->where('submission_id', $submissionId) + ->orderByRaw('timestamp DESC NULLS LAST') + ->orderBy('status_id', 'desc'); + } + + public static function updateStatus(int $submissionId, string $status, string $user) { + $newRecord = [ + 'submission_id' => $submissionId, + 'status' => $status, + 'timestamp' => now(), + 'user' => $user + ]; + + DB::table('codecheck_status')->insert($newRecord); + } +} \ No newline at end of file diff --git a/classes/migration/CodecheckSchemaMigration.php b/classes/migration/CodecheckSchemaMigration.php index c4a17612..8f1093f5 100644 --- a/classes/migration/CodecheckSchemaMigration.php +++ b/classes/migration/CodecheckSchemaMigration.php @@ -52,9 +52,9 @@ public function codecheckStatusUp(): void $table->bigInteger('status_id')->autoIncrement()->primary(); $table->bigInteger('submission_id'); $table->foreign('submission_id', 'codecheck_status_metadata')->references('submission_id')->on('codecheck_metadata')->onDelete('cascade'); - $table->string('status', 100); - $table->timestamp('timestamp')->nullable(); - $table->string('user', 100); + $table->string('status', 300); + $table->timestamp('timestamp'); + $table->string('user', 300); $table->timestamps(); $table->index('status_id'); }); diff --git a/registry/uiLocaleKeysBackend.json b/registry/uiLocaleKeysBackend.json index a5783fe2..cf1459eb 100644 --- a/registry/uiLocaleKeysBackend.json +++ b/registry/uiLocaleKeysBackend.json @@ -129,11 +129,8 @@ "plugins.generic.codecheck.source.label", "plugins.generic.codecheck.source.placeholder", "plugins.generic.codecheck.status", - "plugins.generic.codecheck.status.complete", - "plugins.generic.codecheck.status.inProgress", "plugins.generic.codecheck.status.marked", "plugins.generic.codecheck.status.notMarked", - "plugins.generic.codecheck.status.pending", "plugins.generic.codecheck.validation.certificateRequired", "plugins.generic.codecheck.validation.codecheckersRequired", "plugins.generic.codecheck.validation.githubIssueLinkRequired", diff --git a/resources/js/Components/CodecheckReviewDisplay.vue b/resources/js/Components/CodecheckReviewDisplay.vue index 0250dd9b..748146f3 100644 --- a/resources/js/Components/CodecheckReviewDisplay.vue +++ b/resources/js/Components/CodecheckReviewDisplay.vue @@ -3,11 +3,11 @@

{{ t("plugins.generic.codecheck.reviewTitle") }}

-
-

{{ t("plugins.generic.codecheck.status") }}

- +
+

{{ t("plugins.generic.codecheck.status") }}

+

{{ getStatusText() }} - +

@@ -109,39 +109,44 @@ const hasMetadata = computed(() => { return Object.keys(metadata.value).length > 0; }); -function getStatus() { - if (metadata.value.certificate && metadata.value.checkTime) { - return 'complete'; - } else if (hasMetadata.value) { - return 'in-progress'; +async function getStatus() { + try { + if (!props.submission?.id) { + throw new Error('Invalid submission object'); + } + + const submissionId = props.submission.id; + let apiUrl = pkp.context.apiBaseUrl; + apiUrl += 'codecheck'; + apiUrl = `${apiUrl}/status?submissionId=${submissionId}`; + + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + 'X-Csrf-Token': pkp.currentUser.csrfToken + } + }); + + const data = await response.json(); + + console.log(data); + + return data.status; + + } catch (error) { + console.error('getStatus error:', error); + this.error = t('plugins.generic.codecheck.loadError') + ': ' + error.message; } - return 'pending'; } const statusClass = computed(() => { const status = getStatus(); - switch (status) { - case 'complete': - return 'status-complete'; - case 'in-progress': - return 'status-in-progress'; - case 'pending': - default: - return 'status-pending'; - } + return 'status-' + status; }); function getStatusText() { const status = getStatus(); - switch (status) { - case 'complete': - return t("plugins.generic.codecheck.status.complete"); - case 'in-progress': - return t("plugins.generic.codecheck.status.inProgress"); - case 'pending': - default: - return t("plugins.generic.codecheck.status.pending"); - } + return t(status); } function formatDate(dateString) { @@ -151,29 +156,25 @@ function formatDate(dateString) { } function viewFullMetadata() { - const workflowStore = pkp.registry.getPiniaStore("workflow"); - workflowStore.selectedMenuState = { - primaryMenuItem: 'workflow', - stageId: 999 - }; + // Sadly only works by bypassing the API, searching for the 'CODECHECK' Button and then pressing it by script + const allLinks = document.querySelectorAll('a, button, [role="button"]'); + const codecheckLink = Array.from(allLinks).find(el => + el.textContent.trim().includes(t("plugins.generic.codecheck.workflow.label")) + ); + console.log('codecheck link:', codecheckLink); + if (codecheckLink) codecheckLink.click(); } \ No newline at end of file diff --git a/resources/js/Components/CodecheckReviewDisplay.vue b/resources/js/Components/CodecheckReviewDisplay.vue index 748146f3..3b77db37 100644 --- a/resources/js/Components/CodecheckReviewDisplay.vue +++ b/resources/js/Components/CodecheckReviewDisplay.vue @@ -1,154 +1,153 @@ + + \ No newline at end of file diff --git a/resources/js/main.js b/resources/js/main.js index e42ae62d..c606103f 100644 --- a/resources/js/main.js +++ b/resources/js/main.js @@ -2,8 +2,9 @@ import { createApp, reactive } from 'vue'; import CodecheckManifestFiles from "./Components/CodecheckManifestFiles.vue"; import CodecheckRepositoryList from "./Components/CodecheckRepositoryList.vue"; import CodecheckReviewDisplay from "./Components/CodecheckReviewDisplay.vue"; -import CodecheckMetadataForm from "./Components/CodecheckMetadataForm.vue"; import CodecheckDataAndSoftwareAvailability from "./Components/CodecheckDataAndSoftwareAvailability.vue"; +import CodecheckMetadataForm from './Components/CodecheckMetadataForm.vue'; +import CodecheckStatusForm from './Components/CodecheckStatusForm.vue'; import CodecheckGithubIssueDisplay from "./Components/CodecheckGithubIssueDisplay.vue"; pkp.registry.registerComponent("CodecheckReviewDisplay", CodecheckReviewDisplay); @@ -11,6 +12,7 @@ pkp.registry.registerComponent("CodecheckMetadataForm", CodecheckMetadataForm); pkp.registry.registerComponent("CodecheckManifestFiles", CodecheckManifestFiles); pkp.registry.registerComponent("CodecheckRepositoryList", CodecheckRepositoryList); pkp.registry.registerComponent("CodecheckDataAndSoftwareAvailability", CodecheckDataAndSoftwareAvailability); +pkp.registry.registerComponent("CodecheckStatusForm", CodecheckStatusForm); pkp.registry.registerComponent("CodecheckGithubIssueDisplay", CodecheckGithubIssueDisplay); const { useLocalize } = pkp.modules.useLocalize; @@ -95,6 +97,7 @@ pkp.registry.storeExtend("workflow", (piniaContext) => { workflowStore.extender.extendFn("getSecondaryItems", (sidebarItems, args) => { const store = pkp.registry.stores?.workflow; + console.log(store?.extender); const submission = args?.submission; if ( @@ -102,6 +105,13 @@ pkp.registry.storeExtend("workflow", (piniaContext) => { args?.selectedMenuState?.stageId === 999 ) { return [ + { + component: "CodecheckStatusForm", + props: { + submission: submission, + canEdit: true + }, + }, { component: "CodecheckGithubIssueDisplay", props: { From 19b58eafa3c5ad6b62b548ce681a4999c45bdbf2 Mon Sep 17 00:00:00 2001 From: dxL1nus Date: Tue, 12 May 2026 15:49:06 +0200 Subject: [PATCH 09/23] 119, #61 Status can now be changed, Status History can be viewed --- CodecheckPlugin.php | 2 +- api/v1/CodecheckApiHandler.php | 76 ++++++- classes/Workflow/CodecheckStatusHandler.php | 21 +- .../migration/CodecheckSchemaMigration.php | 3 +- locale/en/locale.po | 27 +++ registry/uiLocaleKeysBackend.json | 8 + .../js/Components/CodecheckStatusForm.vue | 194 +++++++++++++++++- 7 files changed, 318 insertions(+), 13 deletions(-) diff --git a/CodecheckPlugin.php b/CodecheckPlugin.php index b022fe43..32081896 100644 --- a/CodecheckPlugin.php +++ b/CodecheckPlugin.php @@ -463,7 +463,7 @@ public function setEnabled($enabled, $contextId = null) $this->migration->issueLabelsUp(); $this->migration->codecheckStatusUp(); - CodecheckStatusHandler::updateStatus(25, "plugins.generic.codecheck.status.stalled.author", 'test user'); + CodecheckStatusHandler::updateStatus(25, "plugins.generic.codecheck.status.stalled.author", 1); } return $result; diff --git a/api/v1/CodecheckApiHandler.php b/api/v1/CodecheckApiHandler.php index dc520c0e..4b9b2e7a 100644 --- a/api/v1/CodecheckApiHandler.php +++ b/api/v1/CodecheckApiHandler.php @@ -95,7 +95,12 @@ public function __construct(CodecheckPlugin $plugin, Request $request, Codecheck 'route' => 'status', 'handler' => [$this, 'getCurrentStatus'], 'roles' => $this->roles, - ] + ], + [ + 'route' => 'status/history', + 'handler' => [$this, 'getStatusHistory'], + 'roles' => $this->roles, + ], ], 'POST' => [ [ @@ -128,6 +133,11 @@ public function __construct(CodecheckPlugin $plugin, Request $request, Codecheck 'handler' => [$this, 'validateYamlStructure'], 'roles' => $roles->readMetadata(), ], + [ + 'route' => 'status/update', + 'handler' => [$this, 'updateStatus'], + 'roles' => $this->roles, + ], ], ]; @@ -954,12 +964,74 @@ public function getCurrentStatus(): void JsonResponse::staticResponse([ 'success' => false, 'statusRecord' => $statusRecord, - ], 500); + 'allStatuses' => Constants::CODECHECK_STATUSES, + ], 400); } JsonResponse::staticResponse([ 'success' => true, 'statusRecord' => $statusRecord, + 'allStatuses' => Constants::CODECHECK_STATUSES, + ], 200); + } + + public function getStatusHistory(): void + { + $submissionId = (int) $this->codecheckMetadataHandler->getSubmissionId(); + + $statusHistory = CodecheckStatusHandler::getStatusDataHistory($submissionId); + + if($statusHistory == null) { + JsonResponse::staticResponse([ + 'success' => false, + 'statusHistory' => $statusHistory, + ], 400); + } + + JsonResponse::staticResponse([ + 'success' => true, + 'statusHistory' => $statusHistory, + ], 200); + } + + public function updateStatus(): void + { + $submissionId = (int) $this->codecheckMetadataHandler->getSubmissionId(); + + $postParams = json_decode(file_get_contents('php://input'), true); + $status = $postParams["status"]; + $userId = $postParams["userId"]; + + if(!is_string($status) || !is_int($userId)) { + JsonResponse::staticResponse([ + 'success' => false, + 'statusRecord' => [ + 'status' => $status, + 'userId' => $userId + ], + 'allStatuses' => Constants::CODECHECK_STATUSES, + 'error' => 'Bad Request: Please provide a Status form of string and a User ID in the form of int.' + ], 400); + } + + $statusUpdate = CodecheckStatusHandler::updateStatus($submissionId, $status, $userId); + + if($statusUpdate == false) { + JsonResponse::staticResponse([ + 'success' => true, + 'statusRecord' => [ + 'status' => $status, + 'userId' => $userId + ], + 'allStatuses' => Constants::CODECHECK_STATUSES, + 'error' => "Inserting into the CODECHECK Status Database went wrong." + ], 500); + } + + JsonResponse::staticResponse([ + 'success' => true, + 'statusRecord' => $statusUpdate, + 'allStatuses' => Constants::CODECHECK_STATUSES, ], 200); } } \ No newline at end of file diff --git a/classes/Workflow/CodecheckStatusHandler.php b/classes/Workflow/CodecheckStatusHandler.php index 8efd176a..17e4dd52 100644 --- a/classes/Workflow/CodecheckStatusHandler.php +++ b/classes/Workflow/CodecheckStatusHandler.php @@ -5,7 +5,7 @@ use Illuminate\Support\Facades\DB; class CodecheckStatusHandler { - public static function getCurrentStatusData(int $submissionId) { + public static function getCurrentStatusData(int $submissionId): object { return DB::table('codecheck_status') ->where('submission_id', $submissionId) ->orderBy('timestamp', 'desc') @@ -13,21 +13,28 @@ public static function getCurrentStatusData(int $submissionId) { ->first(); } - public static function getStatusDataHistory(int $submissionId) { + public static function getStatusDataHistory(int $submissionId): object { return DB::table('codecheck_status') ->where('submission_id', $submissionId) - ->orderByRaw('timestamp DESC NULLS LAST') - ->orderBy('status_id', 'desc'); + ->orderBy('timestamp') + ->orderBy('status_id', 'desc') + ->get(); } - public static function updateStatus(int $submissionId, string $status, string $user) { + public static function updateStatus(int $submissionId, string $status, int $userId): object|false { $newRecord = [ 'submission_id' => $submissionId, 'status' => $status, 'timestamp' => now(), - 'user' => $user + 'user_id' => $userId ]; - DB::table('codecheck_status')->insert($newRecord); + $insertWorked = DB::table('codecheck_status')->insert($newRecord); + + if(!$insertWorked) { + return false; + } + + return CodecheckStatusHandler::getCurrentStatusData($submissionId); } } \ No newline at end of file diff --git a/classes/migration/CodecheckSchemaMigration.php b/classes/migration/CodecheckSchemaMigration.php index 8f1093f5..78f5705a 100644 --- a/classes/migration/CodecheckSchemaMigration.php +++ b/classes/migration/CodecheckSchemaMigration.php @@ -54,7 +54,7 @@ public function codecheckStatusUp(): void $table->foreign('submission_id', 'codecheck_status_metadata')->references('submission_id')->on('codecheck_metadata')->onDelete('cascade'); $table->string('status', 300); $table->timestamp('timestamp'); - $table->string('user', 300); + $table->bigInteger('user_id'); $table->timestamps(); $table->index('status_id'); }); @@ -93,6 +93,7 @@ private function createCodecheckGenres(): void public function down(): void { + Schema::dropIfExists('codecheck_status'); Schema::dropIfExists('codecheck_metadata'); } diff --git a/locale/en/locale.po b/locale/en/locale.po index bc1ed9c1..136479c3 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -368,6 +368,30 @@ msgstr "CODECHECK Information" msgid "plugins.generic.codecheck.status" msgstr "Status" +msgid "plugins.generic.codecheck.status.history" +msgstr "CODECHECK Status History" + +msgid "plugins.generic.codecheck.status.history.timestamp" +msgstr "Timestamp" + +msgid "plugins.generic.codecheck.status.history.user" +msgstr "user" + +msgid "plugins.generic.codecheck.status.buttons.change" +msgstr "Change" + +msgid "plugins.generic.codecheck.status.buttons.history" +msgstr "History" + +msgid "plugins.generic.codecheck.status.modal.title" +msgstr "Change CODECHECK Status" + +msgid "plugins.generic.codecheck.status.modal.label" +msgstr "Select the new CODECHECK Status for this Submission" + +msgid "plugins.generic.codecheck.status.modal.change" +msgstr "Change" + msgid "plugins.generic.codecheck.status.complete" msgstr "Complete" @@ -566,6 +590,9 @@ msgstr "Cancel" msgid "plugins.generic.codecheck.modal.add" msgstr "Add" +msgid "plugins.generic.codecheck.modal.change" +msgstr "Change" + msgid "plugins.generic.codecheck.review.configVersion" msgstr "Config Version" diff --git a/registry/uiLocaleKeysBackend.json b/registry/uiLocaleKeysBackend.json index cf1459eb..d481ba68 100644 --- a/registry/uiLocaleKeysBackend.json +++ b/registry/uiLocaleKeysBackend.json @@ -86,6 +86,7 @@ "plugins.generic.codecheck.markAsOutputTitle", "plugins.generic.codecheck.modal.add", "plugins.generic.codecheck.modal.cancel", + "plugins.generic.codecheck.modal.change", "plugins.generic.codecheck.modal.close", "plugins.generic.codecheck.no", "plugins.generic.codecheck.noDataFound", @@ -129,7 +130,14 @@ "plugins.generic.codecheck.source.label", "plugins.generic.codecheck.source.placeholder", "plugins.generic.codecheck.status", + "plugins.generic.codecheck.status.buttons.change", + "plugins.generic.codecheck.status.buttons.history", + "plugins.generic.codecheck.status.history", + "plugins.generic.codecheck.status.history.timestamp", + "plugins.generic.codecheck.status.history.user", "plugins.generic.codecheck.status.marked", + "plugins.generic.codecheck.status.modal.label", + "plugins.generic.codecheck.status.modal.title", "plugins.generic.codecheck.status.notMarked", "plugins.generic.codecheck.validation.certificateRequired", "plugins.generic.codecheck.validation.codecheckersRequired", diff --git a/resources/js/Components/CodecheckStatusForm.vue b/resources/js/Components/CodecheckStatusForm.vue index 5c94a7ee..cc5f204f 100644 --- a/resources/js/Components/CodecheckStatusForm.vue +++ b/resources/js/Components/CodecheckStatusForm.vue @@ -23,8 +23,33 @@ " type="button" href="false" + @click="showHistoryModal" > - Change + {{ t('plugins.generic.codecheck.status.buttons.history') }} + +
@@ -75,6 +100,7 @@ export default { saveMessageType: '', hasUnsavedChanges: false, statusData: [], + allStatuses: [], } }, computed: { @@ -112,8 +138,10 @@ export default { }); const data = await response.json(); + console.log("Status data", data); this.statusData = data.statusRecord; - + this.allStatuses = data.allStatuses; + this.dataLoaded = true; } catch (error) { console.error('getStatus error:', error); @@ -123,6 +151,168 @@ export default { }, getStatusText() { return this.t(this.statusData.status); + }, + getStatusSelect() { + let statusSelectString = ''; + return statusSelectString; + }, + async showStatusModal() { + const { useModal } = pkp.modules.useModal; + const { openDialog } = useModal(); + + const modalHtml = '