From 08655f2ae4b22a4027e92f771bab581705af019e Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Wed, 17 Jan 2024 08:47:02 -0500 Subject: [PATCH 01/11] pkp/pkp-lib#9453 Let reviewers view their recommendations for previous rounds --- classes/log/SubmissionEmailLogDAO.php | 29 ++++ .../review/ReviewRoundModalHandler.php | 145 ++++++++++++++++++ .../linkAction/ReviewRoundModalLinkAction.php | 87 +++++++++++ locale/en/reviewer.po | 18 +++ locale/fr_CA/reviewer.po | 18 +++ pages/reviewer/PKPReviewerHandler.php | 63 ++++++++ .../modals/reviewRound/reviewRound.tpl | 89 +++++++++++ templates/reviewer/review/reviewRoundTab.tpl | 21 +++ .../reviewer/review/reviewStepHeader.tpl | 4 +- 9 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 controllers/review/ReviewRoundModalHandler.php create mode 100644 controllers/review/linkAction/ReviewRoundModalLinkAction.php create mode 100644 templates/controllers/modals/reviewRound/reviewRound.tpl create mode 100644 templates/reviewer/review/reviewRoundTab.tpl diff --git a/classes/log/SubmissionEmailLogDAO.php b/classes/log/SubmissionEmailLogDAO.php index 88cb18c9f88..5a00269e49e 100644 --- a/classes/log/SubmissionEmailLogDAO.php +++ b/classes/log/SubmissionEmailLogDAO.php @@ -66,6 +66,35 @@ public function getBySubmissionId($submissionId) return $this->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId); } + /** + * Get submission email log entries by submission ID, event type and sender ID + * + * @param int $submissionId + * @param int $eventType SubmissionEmailLogEntry::SUBMISSION_EMAIL_* + * @param int $senderId Return only emails sent by this user. + * + * @return DAOResultFactory + */ + function getBySenderId($submissionId, $eventType, $senderId) { + $result = $this->retrieveRange( + 'SELECT e.* + FROM email_log e + WHERE + e.assoc_type = ? AND + e.assoc_id = ? AND + e.event_type = ? AND + e.sender_id = ?', + [ + Application::ASSOC_TYPE_SUBMISSION, + (int) $submissionId, + (int) $eventType, + (int) $senderId + ] + ); + + return new DAOResultFactory($result, $this, 'build'); + } + /** * Create a log entry from data in a Mailable class * diff --git a/controllers/review/ReviewRoundModalHandler.php b/controllers/review/ReviewRoundModalHandler.php new file mode 100644 index 00000000000..dfdd4ff4c3a --- /dev/null +++ b/controllers/review/ReviewRoundModalHandler.php @@ -0,0 +1,145 @@ +addRoleAssignment( + [Role::ROLE_ID_REVIEWER], + ['viewRoundInfo', 'closeModal'] + ); + } + + // + // Implement template methods from PKPHandler. + // + + /** + * @copydoc PKPHandler::authorize() + */ + function authorize($request, &$args, $roleAssignments): bool + { + $this->addPolicy(new RoleBasedHandlerOperationPolicy( + $request, + [Role::ROLE_ID_REVIEWER], + ['viewRoundInfo', 'close'] + )); + + return parent::authorize($request, $args, $roleAssignments); + } + + // + // Public operations + // + + /** + * Display the review round info modal. + * + * @param array $args + * @param PKPRequest $request + * + * @return JSONMessage JSON object + * @throws Exception + */ + function viewRoundInfo($args, $request) + { + $this->setupTemplate($request); + + $submission = Repo::submission()->get($args['submissionId']); + $submissionId = $submission->getId(); + $reviewerId = $request->getUser()->getId(); + + $reviewAssignments = Repo::reviewAssignment()->getCollector() + ->filterByReviewerIds([$reviewerId]) + ->getMany(); + $declinedReviewAssignments = array(); + foreach ($reviewAssignments as $submissionReviewAssignment) { + if ($submissionReviewAssignment->getDeclined() and $submissionId == $submissionReviewAssignment->getSubmissionId()) { + $declinedReviewAssignments[] = $submissionReviewAssignment; + } + } + + $reviewAssignment = Repo::reviewAssignment()->getCollector() + ->filterByReviewRoundIds([$args['reviewRoundId']]) + ->filterByReviewerIds([$reviewerId]) + ->filterByContextIds([$request->getContext()->getId()]) + ->getMany() + ->first(); + $submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO'); + $reviewComments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignment->getId()); + + $reviewRoundNumber = $args['reviewRoundNumber']; + $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); + $emailLogs = $submissionEmailLogDao + ->getBySenderId($submissionId, SubmissionEmailLogEntry::SUBMISSION_EMAIL_REVIEW_DECLINE, $reviewerId) + ->toArray(); + $declineEmail = null; + $i = 0; + foreach ($declinedReviewAssignments as $declinedReviewAssignment) { + if (isset($emailLogs[$i]) && $reviewRoundNumber == $declinedReviewAssignment->getRound()) { + $declineEmail = $emailLogs[$i]; + } + $i++; + } + + $displayFilesGrid = true; + $lastReviewAssignment = Repo::reviewAssignment()->getCollector() + ->filterBySubmissionIds([$submissionId]) + ->filterByReviewerIds([$reviewerId]) + ->filterByLastReviewRound(true) + ->getMany() + ->first(); + if($lastReviewAssignment->getDeclined() == 1) { + $displayFilesGrid = false; + } + + $templateMgr = TemplateManager::getManager($request); + $templateMgr->assign([ + 'submission' => $submission, + 'reviewAssignment' => $reviewAssignment, + 'reviewRoundNumber' => $reviewRoundNumber, + 'reviewRoundId' => $args['reviewRoundId'], + 'reviewComments' => $reviewComments, + 'declineEmail' => $declineEmail, + 'displayFilesGrid' => $displayFilesGrid + ]); + + return $templateMgr->fetchJson('controllers/modals/reviewRound/reviewRound.tpl'); + } +} diff --git a/controllers/review/linkAction/ReviewRoundModalLinkAction.php b/controllers/review/linkAction/ReviewRoundModalLinkAction.php new file mode 100644 index 00000000000..a956b6adc5e --- /dev/null +++ b/controllers/review/linkAction/ReviewRoundModalLinkAction.php @@ -0,0 +1,87 @@ +_round = $reviewRoundNumber; + + $submission = Repo::submission()->get($submissionId); + $submissionTitle = $submission->getCurrentPublication()->getLocalizedTitle(); + $router = $request->getRouter(); + $actionArgs = [ + 'submissionId' => $submissionId, + 'reviewRoundId' => $reviewRoundId, + 'reviewRoundNumber' => $reviewRoundNumber + ]; + + $ajaxModal = new AjaxModal( + $router->getDispatcher()->url( + $request, + PKPApplication::ROUTE_COMPONENT, + null, + 'review.ReviewRoundModalHandler', + 'viewRoundInfo', + null, + $actionArgs + ), + __( + 'reviewer.submission.reviewRound.info.modal.title', + [ + 'reviewRoundNumber' => $reviewRoundNumber, + 'submissionTitle' => $submissionTitle + ] + ), + 'modal_information' + ); + + // Configure the link action. + parent::__construct('viewRoundInfo', $ajaxModal); + } + + /** + * Get the review round number. + * + * @return int + */ + function getRound(): int + { + return $this->_round; + } +} diff --git a/locale/en/reviewer.po b/locale/en/reviewer.po index c177bf17c8e..af12f341a96 100644 --- a/locale/en/reviewer.po +++ b/locale/en/reviewer.po @@ -94,6 +94,24 @@ msgstr "" "Upload files you would like the editor and/or author to consult, including " "revised versions of the original review file(s)." +msgid "reviewer.submission.reviewRound.info" +msgstr "Read my former reviews: " + +msgid "reviewer.submission.reviewRound.info.modal.title" +msgstr "Evaluation cycle {$reviewRoundNumber} : {$submissionTitle}" + +msgid "reviewer.submission.reviewRound.info.history" +msgstr "Review history" + +msgid "reviewer.submission.comments.authorAndEditor" +msgstr "Author and editor" + +msgid "reviewer.submission.comments.editorOnly" +msgstr "Pour la rédaction seulement" + +msgid "reviewer.submission.comments.review" +msgstr "Review" + msgid "reviewer.complete" msgstr "Review Submitted" diff --git a/locale/fr_CA/reviewer.po b/locale/fr_CA/reviewer.po index fab1b5f7561..a3d335b7355 100644 --- a/locale/fr_CA/reviewer.po +++ b/locale/fr_CA/reviewer.po @@ -107,6 +107,24 @@ msgstr "" "consulter, y compris les versions révisées des fichiers d'évaluation " "originaux." +msgid "reviewer.submission.reviewRound.info" +msgstr "Consulter mes évaluations précédentes : " + +msgid "reviewer.submission.reviewRound.info.modal.title" +msgstr "Évaluation cycle {$reviewRoundNumber} : {$submissionTitle}" + +msgid "reviewer.submission.reviewRound.info.history" +msgstr "Historique d'évaluation" + +msgid "reviewer.submission.comments.authorAndEditor" +msgstr "Pour l'auteur et la rédaction" + +msgid "reviewer.submission.comments.editorOnly" +msgstr "Editor only" + +msgid "reviewer.submission.comments.review" +msgstr "Évaluation" + msgid "reviewer.complete" msgstr "Évaluation envoyée" diff --git a/pages/reviewer/PKPReviewerHandler.php b/pages/reviewer/PKPReviewerHandler.php index c2aa2cd1ba8..3e5cc1e7bfa 100644 --- a/pages/reviewer/PKPReviewerHandler.php +++ b/pages/reviewer/PKPReviewerHandler.php @@ -24,11 +24,14 @@ use Exception; use Illuminate\Support\Facades\Mail; use PKP\config\Config; +use PKP\controllers\review\linkAction\ReviewRoundModalLinkAction; use PKP\core\JSONMessage; use PKP\core\PKPApplication; use PKP\core\PKPRequest; +use PKP\db\DAORegistry; use PKP\facades\Locale; use PKP\notification\PKPNotification; +use PKP\security\Role; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewer\form\PKPReviewerReviewStep3Form; use PKP\submission\reviewer\form\ReviewerReviewForm; @@ -41,12 +44,15 @@ class PKPReviewerHandler extends Handler /** * Display the submission review page. + * @throws Exception */ public function submission(array $args, PKPRequest $request): void { $reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */ $reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId()); + $reviewSubmissionId = $reviewSubmission->getId(); + $this->insertNewStageAssignmentIfEmpty($request, $reviewSubmissionId); $this->setupTemplate($request); $templateMgr = TemplateManager::getManager($request); @@ -59,11 +65,39 @@ public function submission(array $args, PKPRequest $request): void if ($step < 1 || $step > 4) { throw new Exception('Invalid step!'); } + + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); + $reviewRounds = $reviewRoundDao->getBySubmissionId($reviewSubmissionId)->toArray(); + $reviewerId = $reviewAssignment->getReviewerId(); + $reviewRoundsWhereReviewerAssigned = []; + foreach ($reviewRounds as $reviewRound) { + $reviewAssignment = Repo::reviewAssignment()->getCollector() + ->filterByReviewRoundIds([$reviewRound->getId()]) + ->filterByReviewerIds([$reviewerId]) + ->filterByContextIds([$request->getContext()->getId()]) + ->getMany() + ->first(); + if (!is_null($reviewAssignment)) { + $reviewRoundsWhereReviewerAssigned[$reviewRound->getRound()] = $reviewRound; + } + } + + $lastReviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($reviewSubmissionId); + $lastReviewRoundNumber = $lastReviewRound->getRound(); + $reviewRoundHistories = []; + foreach ($reviewRoundsWhereReviewerAssigned as $reviewRound) { + $round = $reviewRound->getRound(); + if ($round != $lastReviewRoundNumber) { + $reviewRoundHistories[$round - 1] = new ReviewRoundModalLinkAction($request, $reviewSubmissionId, $reviewRound->getId(), $round); + } + } + $templateMgr->assign([ 'pageTitle' => __('semicolon', ['label' => __('submission.review')]) . $reviewSubmission->getCurrentPublication()->getLocalizedTitle(), 'reviewStep' => $reviewStep, 'selected' => $step - 1, 'submission' => $reviewSubmission, + 'reviewRoundHistories' => $reviewRoundHistories, ]); $templateMgr->setState([ @@ -91,6 +125,7 @@ public function submission(array $args, PKPRequest $request): void /** * Display a step tab contents in the submission review page. + * @throws Exception */ public function step(array $args, PKPRequest $request): JSONMessage { @@ -99,7 +134,9 @@ public function step(array $args, PKPRequest $request): JSONMessage assert(!empty($reviewId)); $reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId()); + $reviewSubmissionId = $reviewSubmission->getId(); + $this->insertNewStageAssignmentIfEmpty($request, $reviewSubmissionId); $this->setupTemplate($request); $reviewStep = max($reviewAssignment->getStep(), 1); // Get the current saved step from the DB @@ -246,4 +283,30 @@ public function _retrieveStep(): int assert(!empty($reviewId)); return $reviewId; } + + /** + * Insert a new stage assignment object if it doesn't already exist. + * + * @param PKPRequest $request + * @param int $reviewSubmissionId + * + * @throws Exception + */ + private function insertNewStageAssignmentIfEmpty(PKPRequest $request, int $reviewSubmissionId): void + { + $reviewerUserGroups = Repo::userGroup() + ->getByRoleIds([Role::ROLE_ID_REVIEWER], $request->getContext()->getId(), true) + ->first(); + $reviewerUserGroupsId = $reviewerUserGroups->getId(); + $userId = $request->getUser()->getId(); + $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); + $result = $stageAssignmentDao->getBySubmissionAndStageId($reviewSubmissionId, null, $reviewerUserGroupsId, $userId); + if (count($result->toArray()) === 0) { + $stageAssignment = $stageAssignmentDao->newDataObject(); + $stageAssignment->setSubmissionId($reviewSubmissionId); + $stageAssignment->setUserId($userId); + $stageAssignment->setUserGroupId($reviewerUserGroupsId); + $stageAssignmentDao->insertObject($stageAssignment); + } + } } diff --git a/templates/controllers/modals/reviewRound/reviewRound.tpl b/templates/controllers/modals/reviewRound/reviewRound.tpl new file mode 100644 index 00000000000..2d884c39bb7 --- /dev/null +++ b/templates/controllers/modals/reviewRound/reviewRound.tpl @@ -0,0 +1,89 @@ +{** + * templates/controllers/modals/reviewRound/reviewRound.tpl + * + * Copyright (c) 2014-2021 Simon Fraser University + * Copyright (c) 2003-2021 John Willinsky + * Copyright (c) 2021 Université Laval + * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. + * + * Display reviewer review round info modal. + *} + + + +
+ +
+ {if $reviewAssignment->getDeclined() == false} +

{translate key="reviewer.article.recommendation"}:

+
+

{$reviewAssignment->getLocalizedRecommendation()}

+
+ + {if !$reviewComments->wasEmpty()} +

{translate key="reviewer.submission.comments.review"}:

+ {iterate from=reviewComments item=reviewComment} +
+ {if $reviewComment->getViewable() == 1} + {translate key="reviewer.submission.comments.authorAndEditor"}: + {else} + {translate key="reviewer.submission.comments.editorOnly"}: + {/if} + {$reviewComment->getComments()} +
+ {/iterate} + {/if} + + {if $displayFilesGrid} + {capture assign="reviewAttachmentsModalUrl"}{url router=$smarty.const.ROUTE_COMPONENT component="grid.files.attachment.ReviewerReviewAttachmentsGridHandler" op="fetchGrid" assocType=$smarty.const.ASSOC_TYPE_REVIEW_ASSIGNMENT assocId=$reviewAssignment->getId() submissionId=$submission->getId() stageId=$reviewAssignment->getStageId() reviewIsClosed=true escape=false}{/capture} + {load_url_in_div id="reviewAttachmentsModal" url=$reviewAttachmentsModalUrl} + {/if} + +
+

{translate key="reviewer.submission.reviewRequestDate"}: {$reviewAssignment->getDateNotified()|date_format:$dateFormatShort}

+

{translate key="reviewer.submission.responseDueDate"}: {$reviewAssignment->getDateResponseDue()|date_format:$dateFormatShort}

+

{translate key="reviewer.submission.reviewDueDate"}: {$reviewAssignment->getDateDue()|date_format:$dateFormatShort}

+

{translate key="common.dateCompleted"}: {$reviewAssignment->getDateCompleted()|date_format:$dateFormatShort}

+
+ + {if $displayFilesGrid} + {capture assign="reviewFilesModalUrl"}{url router=$smarty.const.ROUTE_COMPONENT component="grid.files.review.ReviewerReviewFilesGridHandler" op="fetchGrid" submissionId=$submission->getId() stageId=$reviewAssignment->getStageId() reviewRoundId=$reviewRoundId reviewAssignmentId=$reviewAssignment->getId() escape=false}{/capture} + {load_url_in_div id="reviewFilesModal" url=$reviewFilesModalUrl} + {/if} + {else} +

{translate key="reviewer.submission.reviewDeclineDate"}:

+
+

{$reviewAssignment->getDateConfirmed()|date_format:$dateFormatShort}

+
+

{translate key="reviewer.submission.emailLog"}:

+ {if isset($declineEmail)} +
+

{$declineEmail->getSubject()}
+ {$declineEmail->getBody()} +

+
+ {else} +

{translate key="reviewer.submission.emailLog.defaultMessage"}

+ {/if} + {/if} +
+
+ {fbvFormSection class="formButtons form_buttons"} + {assign var=cancelButtonId value="cancelFormButton"|concat:"-"|uniqid} + {translate key="common.ok"} + + {/fbvFormSection} +
+
diff --git a/templates/reviewer/review/reviewRoundTab.tpl b/templates/reviewer/review/reviewRoundTab.tpl new file mode 100644 index 00000000000..a5d3d08b26f --- /dev/null +++ b/templates/reviewer/review/reviewRoundTab.tpl @@ -0,0 +1,21 @@ +{** + * templates/reviewer/review/reviewRoundTab.tpl + * + * Copyright (c) 2014-2021 Simon Fraser University + * Copyright (c) 2003-2021 John Willinsky + * Copyright (c) 2021 Université Laval + * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. + * + * Build reviewer review round info buttons + *} + +{if $reviewRoundHistories|@count > 0} +
+ {translate key="reviewer.submission.reviewRound.info"}  + {foreach from=$reviewRoundHistories item=reviewRoundHistory key=key} + {assign var=resumeButtonId value="resumeButton"|concat:"-"|uniqid} + {include file="linkAction/buttonGenericLinkAction.tpl" buttonSelector="#"|concat:$resumeButtonId action=$reviewRoundHistory} + {translate key="submission.round" round=$reviewRoundHistory->getRound()} + {/foreach} +
+{/if} diff --git a/templates/reviewer/review/reviewStepHeader.tpl b/templates/reviewer/review/reviewStepHeader.tpl index 389f11850c2..bdb00c120bc 100644 --- a/templates/reviewer/review/reviewStepHeader.tpl +++ b/templates/reviewer/review/reviewStepHeader.tpl @@ -16,7 +16,7 @@ - + + {include file="reviewer/review/reviewRoundTab.tpl"} +
  • getId() step=1}">{translate key="reviewer.reviewSteps.request"}
  • From 165558bb6e2c00b0ecfc7895553d88cfa7433d9b Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Mon, 27 Nov 2023 09:33:58 -0500 Subject: [PATCH 02/11] pkp/pkp-lib#9453 Wrong english/french message --- locale/en/reviewer.po | 2 +- locale/fr_CA/reviewer.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locale/en/reviewer.po b/locale/en/reviewer.po index af12f341a96..5e357cf06b6 100644 --- a/locale/en/reviewer.po +++ b/locale/en/reviewer.po @@ -107,7 +107,7 @@ msgid "reviewer.submission.comments.authorAndEditor" msgstr "Author and editor" msgid "reviewer.submission.comments.editorOnly" -msgstr "Pour la rédaction seulement" +msgstr "Editor only" msgid "reviewer.submission.comments.review" msgstr "Review" diff --git a/locale/fr_CA/reviewer.po b/locale/fr_CA/reviewer.po index a3d335b7355..5dc3e82fdd3 100644 --- a/locale/fr_CA/reviewer.po +++ b/locale/fr_CA/reviewer.po @@ -120,7 +120,7 @@ msgid "reviewer.submission.comments.authorAndEditor" msgstr "Pour l'auteur et la rédaction" msgid "reviewer.submission.comments.editorOnly" -msgstr "Editor only" +msgstr "Pour la rédaction seulement" msgid "reviewer.submission.comments.review" msgstr "Évaluation" From f66486559c21d28438a44d9dcbe806cd22034427 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Wed, 17 Jan 2024 16:46:12 -0500 Subject: [PATCH 03/11] pkp/pkp-lib#9453 Use the new Vue.js model --- .../linkAction/ReviewRoundModalLinkAction.php | 87 ------------------ pages/reviewer/PKPReviewerHandler.php | 85 ++++-------------- .../modals/reviewRound/reviewRound.tpl | 89 ------------------- templates/reviewer/review/reviewRoundTab.tpl | 21 ----- .../reviewer/review/reviewStepHeader.tpl | 2 - 5 files changed, 17 insertions(+), 267 deletions(-) delete mode 100644 controllers/review/linkAction/ReviewRoundModalLinkAction.php delete mode 100644 templates/controllers/modals/reviewRound/reviewRound.tpl delete mode 100644 templates/reviewer/review/reviewRoundTab.tpl diff --git a/controllers/review/linkAction/ReviewRoundModalLinkAction.php b/controllers/review/linkAction/ReviewRoundModalLinkAction.php deleted file mode 100644 index a956b6adc5e..00000000000 --- a/controllers/review/linkAction/ReviewRoundModalLinkAction.php +++ /dev/null @@ -1,87 +0,0 @@ -_round = $reviewRoundNumber; - - $submission = Repo::submission()->get($submissionId); - $submissionTitle = $submission->getCurrentPublication()->getLocalizedTitle(); - $router = $request->getRouter(); - $actionArgs = [ - 'submissionId' => $submissionId, - 'reviewRoundId' => $reviewRoundId, - 'reviewRoundNumber' => $reviewRoundNumber - ]; - - $ajaxModal = new AjaxModal( - $router->getDispatcher()->url( - $request, - PKPApplication::ROUTE_COMPONENT, - null, - 'review.ReviewRoundModalHandler', - 'viewRoundInfo', - null, - $actionArgs - ), - __( - 'reviewer.submission.reviewRound.info.modal.title', - [ - 'reviewRoundNumber' => $reviewRoundNumber, - 'submissionTitle' => $submissionTitle - ] - ), - 'modal_information' - ); - - // Configure the link action. - parent::__construct('viewRoundInfo', $ajaxModal); - } - - /** - * Get the review round number. - * - * @return int - */ - function getRound(): int - { - return $this->_round; - } -} diff --git a/pages/reviewer/PKPReviewerHandler.php b/pages/reviewer/PKPReviewerHandler.php index 3e5cc1e7bfa..b5229893983 100644 --- a/pages/reviewer/PKPReviewerHandler.php +++ b/pages/reviewer/PKPReviewerHandler.php @@ -24,14 +24,12 @@ use Exception; use Illuminate\Support\Facades\Mail; use PKP\config\Config; -use PKP\controllers\review\linkAction\ReviewRoundModalLinkAction; use PKP\core\JSONMessage; use PKP\core\PKPApplication; use PKP\core\PKPRequest; use PKP\db\DAORegistry; use PKP\facades\Locale; use PKP\notification\PKPNotification; -use PKP\security\Role; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewer\form\PKPReviewerReviewStep3Form; use PKP\submission\reviewer\form\ReviewerReviewForm; @@ -50,9 +48,7 @@ public function submission(array $args, PKPRequest $request): void { $reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */ $reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId()); - $reviewSubmissionId = $reviewSubmission->getId(); - $this->insertNewStageAssignmentIfEmpty($request, $reviewSubmissionId); $this->setupTemplate($request); $templateMgr = TemplateManager::getManager($request); @@ -67,28 +63,23 @@ public function submission(array $args, PKPRequest $request): void } $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); - $reviewRounds = $reviewRoundDao->getBySubmissionId($reviewSubmissionId)->toArray(); - $reviewerId = $reviewAssignment->getReviewerId(); - $reviewRoundsWhereReviewerAssigned = []; - foreach ($reviewRounds as $reviewRound) { - $reviewAssignment = Repo::reviewAssignment()->getCollector() - ->filterByReviewRoundIds([$reviewRound->getId()]) - ->filterByReviewerIds([$reviewerId]) - ->filterByContextIds([$request->getContext()->getId()]) - ->getMany() - ->first(); - if (!is_null($reviewAssignment)) { - $reviewRoundsWhereReviewerAssigned[$reviewRound->getRound()] = $reviewRound; - } - } - - $lastReviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($reviewSubmissionId); - $lastReviewRoundNumber = $lastReviewRound->getRound(); + $submissionId = $reviewSubmission->getId(); + $lastRoundId = $reviewRoundDao->getLastReviewRoundBySubmissionId($submissionId)->getId(); + $reviewAssignments = Repo::reviewAssignment()->getCollector() + ->filterByContextIds([$request->getContext()->getId()]) + ->filterBySubmissionIds([$submissionId]) + ->filterByReviewerIds([$reviewAssignment->getReviewerId()]) + ->getMany() + ->toArray(); $reviewRoundHistories = []; - foreach ($reviewRoundsWhereReviewerAssigned as $reviewRound) { - $round = $reviewRound->getRound(); - if ($round != $lastReviewRoundNumber) { - $reviewRoundHistories[$round - 1] = new ReviewRoundModalLinkAction($request, $reviewSubmissionId, $reviewRound->getId(), $round); + foreach ($reviewAssignments as $reviewAssignment) { + $reviewRoundId = $reviewAssignment->getReviewRoundId(); + if ($reviewRoundId != $lastRoundId) { + $reviewRoundHistories[] = [ + 'submissionId' => $submissionId, + 'reviewRoundId' => $reviewRoundId, + 'reviewRoundNumber' => $reviewAssignment->getRound() + ]; } } @@ -97,26 +88,12 @@ public function submission(array $args, PKPRequest $request): void 'reviewStep' => $reviewStep, 'selected' => $step - 1, 'submission' => $reviewSubmission, - 'reviewRoundHistories' => $reviewRoundHistories, ]); $templateMgr->setState([ 'isReviewRoundHistoryEnabled' => Config::getVar('features', 'enable_review_round_history'), 'pageInitConfig' => [ - 'reviewRoundHistories' => [ - [ - 'submissionId' => $reviewSubmission->getId(), - // Just as example, not real data - 'reviewRoundId' => 0, - 'reviewRoundNumber' => 1 - ], - [ - 'submissionId' => $reviewSubmission->getId(), - // Just as example, not real data - 'reviewRoundId' => 1, - 'reviewRoundNumber' => 2 - ] - ] + 'reviewRoundHistories' => $reviewRoundHistories ] ]); @@ -134,9 +111,7 @@ public function step(array $args, PKPRequest $request): JSONMessage assert(!empty($reviewId)); $reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId()); - $reviewSubmissionId = $reviewSubmission->getId(); - $this->insertNewStageAssignmentIfEmpty($request, $reviewSubmissionId); $this->setupTemplate($request); $reviewStep = max($reviewAssignment->getStep(), 1); // Get the current saved step from the DB @@ -283,30 +258,4 @@ public function _retrieveStep(): int assert(!empty($reviewId)); return $reviewId; } - - /** - * Insert a new stage assignment object if it doesn't already exist. - * - * @param PKPRequest $request - * @param int $reviewSubmissionId - * - * @throws Exception - */ - private function insertNewStageAssignmentIfEmpty(PKPRequest $request, int $reviewSubmissionId): void - { - $reviewerUserGroups = Repo::userGroup() - ->getByRoleIds([Role::ROLE_ID_REVIEWER], $request->getContext()->getId(), true) - ->first(); - $reviewerUserGroupsId = $reviewerUserGroups->getId(); - $userId = $request->getUser()->getId(); - $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); - $result = $stageAssignmentDao->getBySubmissionAndStageId($reviewSubmissionId, null, $reviewerUserGroupsId, $userId); - if (count($result->toArray()) === 0) { - $stageAssignment = $stageAssignmentDao->newDataObject(); - $stageAssignment->setSubmissionId($reviewSubmissionId); - $stageAssignment->setUserId($userId); - $stageAssignment->setUserGroupId($reviewerUserGroupsId); - $stageAssignmentDao->insertObject($stageAssignment); - } - } } diff --git a/templates/controllers/modals/reviewRound/reviewRound.tpl b/templates/controllers/modals/reviewRound/reviewRound.tpl deleted file mode 100644 index 2d884c39bb7..00000000000 --- a/templates/controllers/modals/reviewRound/reviewRound.tpl +++ /dev/null @@ -1,89 +0,0 @@ -{** - * templates/controllers/modals/reviewRound/reviewRound.tpl - * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2003-2021 John Willinsky - * Copyright (c) 2021 Université Laval - * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. - * - * Display reviewer review round info modal. - *} - - - -
    - -
    - {if $reviewAssignment->getDeclined() == false} -

    {translate key="reviewer.article.recommendation"}:

    -
    -

    {$reviewAssignment->getLocalizedRecommendation()}

    -
    - - {if !$reviewComments->wasEmpty()} -

    {translate key="reviewer.submission.comments.review"}:

    - {iterate from=reviewComments item=reviewComment} -
    - {if $reviewComment->getViewable() == 1} - {translate key="reviewer.submission.comments.authorAndEditor"}: - {else} - {translate key="reviewer.submission.comments.editorOnly"}: - {/if} - {$reviewComment->getComments()} -
    - {/iterate} - {/if} - - {if $displayFilesGrid} - {capture assign="reviewAttachmentsModalUrl"}{url router=$smarty.const.ROUTE_COMPONENT component="grid.files.attachment.ReviewerReviewAttachmentsGridHandler" op="fetchGrid" assocType=$smarty.const.ASSOC_TYPE_REVIEW_ASSIGNMENT assocId=$reviewAssignment->getId() submissionId=$submission->getId() stageId=$reviewAssignment->getStageId() reviewIsClosed=true escape=false}{/capture} - {load_url_in_div id="reviewAttachmentsModal" url=$reviewAttachmentsModalUrl} - {/if} - -
    -

    {translate key="reviewer.submission.reviewRequestDate"}: {$reviewAssignment->getDateNotified()|date_format:$dateFormatShort}

    -

    {translate key="reviewer.submission.responseDueDate"}: {$reviewAssignment->getDateResponseDue()|date_format:$dateFormatShort}

    -

    {translate key="reviewer.submission.reviewDueDate"}: {$reviewAssignment->getDateDue()|date_format:$dateFormatShort}

    -

    {translate key="common.dateCompleted"}: {$reviewAssignment->getDateCompleted()|date_format:$dateFormatShort}

    -
    - - {if $displayFilesGrid} - {capture assign="reviewFilesModalUrl"}{url router=$smarty.const.ROUTE_COMPONENT component="grid.files.review.ReviewerReviewFilesGridHandler" op="fetchGrid" submissionId=$submission->getId() stageId=$reviewAssignment->getStageId() reviewRoundId=$reviewRoundId reviewAssignmentId=$reviewAssignment->getId() escape=false}{/capture} - {load_url_in_div id="reviewFilesModal" url=$reviewFilesModalUrl} - {/if} - {else} -

    {translate key="reviewer.submission.reviewDeclineDate"}:

    -
    -

    {$reviewAssignment->getDateConfirmed()|date_format:$dateFormatShort}

    -
    -

    {translate key="reviewer.submission.emailLog"}:

    - {if isset($declineEmail)} -
    -

    {$declineEmail->getSubject()}
    - {$declineEmail->getBody()} -

    -
    - {else} -

    {translate key="reviewer.submission.emailLog.defaultMessage"}

    - {/if} - {/if} -
    -
    - {fbvFormSection class="formButtons form_buttons"} - {assign var=cancelButtonId value="cancelFormButton"|concat:"-"|uniqid} - {translate key="common.ok"} - - {/fbvFormSection} -
    -
    diff --git a/templates/reviewer/review/reviewRoundTab.tpl b/templates/reviewer/review/reviewRoundTab.tpl deleted file mode 100644 index a5d3d08b26f..00000000000 --- a/templates/reviewer/review/reviewRoundTab.tpl +++ /dev/null @@ -1,21 +0,0 @@ -{** - * templates/reviewer/review/reviewRoundTab.tpl - * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2003-2021 John Willinsky - * Copyright (c) 2021 Université Laval - * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. - * - * Build reviewer review round info buttons - *} - -{if $reviewRoundHistories|@count > 0} -
    - {translate key="reviewer.submission.reviewRound.info"}  - {foreach from=$reviewRoundHistories item=reviewRoundHistory key=key} - {assign var=resumeButtonId value="resumeButton"|concat:"-"|uniqid} - {include file="linkAction/buttonGenericLinkAction.tpl" buttonSelector="#"|concat:$resumeButtonId action=$reviewRoundHistory} - {translate key="submission.round" round=$reviewRoundHistory->getRound()} - {/foreach} -
    -{/if} diff --git a/templates/reviewer/review/reviewStepHeader.tpl b/templates/reviewer/review/reviewStepHeader.tpl index bdb00c120bc..116b44858bf 100644 --- a/templates/reviewer/review/reviewStepHeader.tpl +++ b/templates/reviewer/review/reviewStepHeader.tpl @@ -30,8 +30,6 @@ {rdelim}); - {include file="reviewer/review/reviewRoundTab.tpl"} -
    • getId() step=1}">{translate key="reviewer.reviewSteps.request"}
    • From 6b2cbc10366aabbb2fef8a062a041b65994a0d7e Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Fri, 19 Jan 2024 10:22:35 -0500 Subject: [PATCH 04/11] pkp/pkp-lib#9453 Add a new API endpoint for the review round history --- api/v1/reviews/PKPReviewController.php | 165 ++++++++++++++++++ .../review/ReviewRoundModalHandler.php | 145 --------------- locale/en/reviewer.po | 15 +- locale/fr_CA/reviewer.po | 15 +- 4 files changed, 189 insertions(+), 151 deletions(-) create mode 100644 api/v1/reviews/PKPReviewController.php delete mode 100644 controllers/review/ReviewRoundModalHandler.php diff --git a/api/v1/reviews/PKPReviewController.php b/api/v1/reviews/PKPReviewController.php new file mode 100644 index 00000000000..1e491d2d5a0 --- /dev/null +++ b/api/v1/reviews/PKPReviewController.php @@ -0,0 +1,165 @@ +getHistory(...)) + ->name('review.get.submission.round.history') + ->whereNumber(['reviewRoundId', 'submissionId']); + } + + /** + * @copydoc \PKP\core\PKPBaseController::authorize() + */ + public function authorize(PKPRequest $request, array &$args, array $roleAssignments): bool + { + $illuminateRequest = $args[0]; /** @var \Illuminate\Http\Request $illuminateRequest */ + $actionName = static::getRouteActionName($illuminateRequest); + + $this->addPolicy(new UserRolesRequiredPolicy($request), true); + $this->addPolicy(new ContextAccessPolicy($request, $roleAssignments)); + + if ($actionName === 'getHistory') { + $this->addPolicy(new SubmissionAccessPolicy($request, $args, $roleAssignments, 'submissionId', true)); + } + + return parent::authorize($request, $args, $roleAssignments); + } + + /** + * Get reviewer's submission round history + * @throws \Exception + */ + public function getHistory(Request $illuminateRequest): JsonResponse + { + $request = $this->getRequest(); + $context = $request->getContext(); + $contextId = $context->getId(); + + // TODO: get the reviewer ID from the request or from the route? + $reviewerId = $request->getUser()->getId(); + //$reviewerId = $illuminateRequest->route('reviewerId'); + $submissionId = $illuminateRequest->route('submissionId'); + $reviewRoundId = $illuminateRequest->route('reviewRoundId'); + + $reviewAssignment = Repo::reviewAssignment()->getCollector() + ->filterByContextIds([$contextId]) + ->filterBySubmissionIds([$submissionId]) + ->filterByReviewerIds([$reviewerId]) + ->filterByReviewRoundIds([$reviewRoundId]) + ->getMany() + ->first(); + + if (!$reviewAssignment) { + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); + } + + $submission = Repo::submission()->get($submissionId, $contextId); + $publicationTitle = $submission->getCurrentPublication()->getLocalizedTitle(); + + $declineEmail = null; + if (!$reviewAssignment->getDeclined()) { + $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); + $emailLogs = $submissionEmailLogDao->getBySenderId($submissionId, SubmissionEmailLogEntry::SUBMISSION_EMAIL_REVIEW_DECLINE, $reviewerId)->toArray(); + // TODO: look for the right email log with the right round number. + if ($emailLogs) { + $emailLog = $emailLogs[0]; + $declineEmail = [ + 'subject' => $emailLog->getData('subject'), + 'body' => $emailLog->getData('body'), + ]; + } + } + + $reviewAssignmentProps = Repo::reviewAssignment()->getSchemaMap()->map($reviewAssignment); + + $reviewAssignmentId = $reviewAssignment->getId(); + $submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO'); + $comments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignmentId, true)->toArray(); + $privateComments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignmentId, false)->toArray(); + + // TODO: get the comments data... + + $lastReviewAssignment = Repo::reviewAssignment()->getCollector() + ->filterByContextIds([$contextId]) + ->filterBySubmissionIds([$submissionId]) + ->filterByReviewerIds([$reviewerId]) + ->filterByLastReviewRound(true) + ->getMany() + ->first(); + $displayFiles = $lastReviewAssignment->getDeclined() != 1; + + $reviewRoundHistoryProps = [ + 'publicationTitle' => $publicationTitle, + 'declineEmail' => $declineEmail, + 'reviewAssignment' => $reviewAssignmentProps, + 'comments' => $comments, + 'privateComments' => $privateComments, + 'displayFiles' => $displayFiles, + ]; + + return response()->json($reviewRoundHistoryProps, Response::HTTP_OK); + } +} diff --git a/controllers/review/ReviewRoundModalHandler.php b/controllers/review/ReviewRoundModalHandler.php deleted file mode 100644 index dfdd4ff4c3a..00000000000 --- a/controllers/review/ReviewRoundModalHandler.php +++ /dev/null @@ -1,145 +0,0 @@ -addRoleAssignment( - [Role::ROLE_ID_REVIEWER], - ['viewRoundInfo', 'closeModal'] - ); - } - - // - // Implement template methods from PKPHandler. - // - - /** - * @copydoc PKPHandler::authorize() - */ - function authorize($request, &$args, $roleAssignments): bool - { - $this->addPolicy(new RoleBasedHandlerOperationPolicy( - $request, - [Role::ROLE_ID_REVIEWER], - ['viewRoundInfo', 'close'] - )); - - return parent::authorize($request, $args, $roleAssignments); - } - - // - // Public operations - // - - /** - * Display the review round info modal. - * - * @param array $args - * @param PKPRequest $request - * - * @return JSONMessage JSON object - * @throws Exception - */ - function viewRoundInfo($args, $request) - { - $this->setupTemplate($request); - - $submission = Repo::submission()->get($args['submissionId']); - $submissionId = $submission->getId(); - $reviewerId = $request->getUser()->getId(); - - $reviewAssignments = Repo::reviewAssignment()->getCollector() - ->filterByReviewerIds([$reviewerId]) - ->getMany(); - $declinedReviewAssignments = array(); - foreach ($reviewAssignments as $submissionReviewAssignment) { - if ($submissionReviewAssignment->getDeclined() and $submissionId == $submissionReviewAssignment->getSubmissionId()) { - $declinedReviewAssignments[] = $submissionReviewAssignment; - } - } - - $reviewAssignment = Repo::reviewAssignment()->getCollector() - ->filterByReviewRoundIds([$args['reviewRoundId']]) - ->filterByReviewerIds([$reviewerId]) - ->filterByContextIds([$request->getContext()->getId()]) - ->getMany() - ->first(); - $submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO'); - $reviewComments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignment->getId()); - - $reviewRoundNumber = $args['reviewRoundNumber']; - $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); - $emailLogs = $submissionEmailLogDao - ->getBySenderId($submissionId, SubmissionEmailLogEntry::SUBMISSION_EMAIL_REVIEW_DECLINE, $reviewerId) - ->toArray(); - $declineEmail = null; - $i = 0; - foreach ($declinedReviewAssignments as $declinedReviewAssignment) { - if (isset($emailLogs[$i]) && $reviewRoundNumber == $declinedReviewAssignment->getRound()) { - $declineEmail = $emailLogs[$i]; - } - $i++; - } - - $displayFilesGrid = true; - $lastReviewAssignment = Repo::reviewAssignment()->getCollector() - ->filterBySubmissionIds([$submissionId]) - ->filterByReviewerIds([$reviewerId]) - ->filterByLastReviewRound(true) - ->getMany() - ->first(); - if($lastReviewAssignment->getDeclined() == 1) { - $displayFilesGrid = false; - } - - $templateMgr = TemplateManager::getManager($request); - $templateMgr->assign([ - 'submission' => $submission, - 'reviewAssignment' => $reviewAssignment, - 'reviewRoundNumber' => $reviewRoundNumber, - 'reviewRoundId' => $args['reviewRoundId'], - 'reviewComments' => $reviewComments, - 'declineEmail' => $declineEmail, - 'displayFilesGrid' => $displayFilesGrid - ]); - - return $templateMgr->fetchJson('controllers/modals/reviewRound/reviewRound.tpl'); - } -} diff --git a/locale/en/reviewer.po b/locale/en/reviewer.po index 5e357cf06b6..ccf4ea90350 100644 --- a/locale/en/reviewer.po +++ b/locale/en/reviewer.po @@ -103,15 +103,24 @@ msgstr "Evaluation cycle {$reviewRoundNumber} : {$submissionTitle}" msgid "reviewer.submission.reviewRound.info.history" msgstr "Review history" -msgid "reviewer.submission.comments.authorAndEditor" +msgid "reviewer.submission.reviewRound.comments.authorAndEditor" msgstr "Author and editor" -msgid "reviewer.submission.comments.editorOnly" +msgid "reviewer.submission.reviewRound.comments.editorOnly" msgstr "Editor only" -msgid "reviewer.submission.comments.review" +msgid "reviewer.submission.reviewRound.comments.review" msgstr "Review" +msgid "reviewer.submission.reviewRound.emailLog" +msgstr "Decline reason sent by email" + +msgid "reviewer.submission.reviewRound.emailLog.defaultMessage" +msgstr "No reason given to the decline of the review invitation." + +msgid "reviewer.submission.reviewRound.reviewDeclineDate" +msgstr "Declined Date" + msgid "reviewer.complete" msgstr "Review Submitted" diff --git a/locale/fr_CA/reviewer.po b/locale/fr_CA/reviewer.po index 5dc3e82fdd3..f678fa1a779 100644 --- a/locale/fr_CA/reviewer.po +++ b/locale/fr_CA/reviewer.po @@ -116,15 +116,24 @@ msgstr "Évaluation cycle {$reviewRoundNumber} : {$submissionTitle}" msgid "reviewer.submission.reviewRound.info.history" msgstr "Historique d'évaluation" -msgid "reviewer.submission.comments.authorAndEditor" +msgid "reviewer.submission.reviewRound.comments.authorAndEditor" msgstr "Pour l'auteur et la rédaction" -msgid "reviewer.submission.comments.editorOnly" +msgid "reviewer.submission.reviewRound.comments.editorOnly" msgstr "Pour la rédaction seulement" -msgid "reviewer.submission.comments.review" +msgid "reviewer.submission.reviewRound.comments.review" msgstr "Évaluation" +msgid "reviewer.submission.reviewRound.emailLog" +msgstr "Raison du refus envoyée par courriel" + +msgid "reviewer.submission.reviewRound.emailLog.defaultMessage" +msgstr "Aucune raison fournie pour le refus de l'invitation à évaluer." + +msgid "reviewer.submission.reviewRound.reviewDeclineDate" +msgstr "Date du refus" + msgid "reviewer.complete" msgstr "Évaluation envoyée" From ece4289b210c7685f275eab44d9921405c6a7c78 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Fri, 19 Jan 2024 11:15:02 -0500 Subject: [PATCH 05/11] pkp/pkp-lib#9453 Expose the right recommandation value --- api/v1/reviews/PKPReviewController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/v1/reviews/PKPReviewController.php b/api/v1/reviews/PKPReviewController.php index 1e491d2d5a0..2ceeb8facc9 100644 --- a/api/v1/reviews/PKPReviewController.php +++ b/api/v1/reviews/PKPReviewController.php @@ -134,6 +134,7 @@ public function getHistory(Request $illuminateRequest): JsonResponse } $reviewAssignmentProps = Repo::reviewAssignment()->getSchemaMap()->map($reviewAssignment); + $recommendation = $reviewAssignment->getLocalizedRecommendation(); $reviewAssignmentId = $reviewAssignment->getId(); $submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO'); @@ -155,6 +156,7 @@ public function getHistory(Request $illuminateRequest): JsonResponse 'publicationTitle' => $publicationTitle, 'declineEmail' => $declineEmail, 'reviewAssignment' => $reviewAssignmentProps, + 'recommendation' => $recommendation, 'comments' => $comments, 'privateComments' => $privateComments, 'displayFiles' => $displayFiles, From 674d7c6bd7a681d43013b7a4feb8620c8b81ab62 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Fri, 19 Jan 2024 14:56:07 -0500 Subject: [PATCH 06/11] pkp/pkp-lib#9453 Matching the correct decline email and get review comments data --- api/v1/reviews/PKPReviewController.php | 37 +++++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/api/v1/reviews/PKPReviewController.php b/api/v1/reviews/PKPReviewController.php index 2ceeb8facc9..2506dfdc266 100644 --- a/api/v1/reviews/PKPReviewController.php +++ b/api/v1/reviews/PKPReviewController.php @@ -120,16 +120,20 @@ public function getHistory(Request $illuminateRequest): JsonResponse $publicationTitle = $submission->getCurrentPublication()->getLocalizedTitle(); $declineEmail = null; - if (!$reviewAssignment->getDeclined()) { + if ($reviewAssignment->getDeclined()) { $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); $emailLogs = $submissionEmailLogDao->getBySenderId($submissionId, SubmissionEmailLogEntry::SUBMISSION_EMAIL_REVIEW_DECLINE, $reviewerId)->toArray(); - // TODO: look for the right email log with the right round number. - if ($emailLogs) { - $emailLog = $emailLogs[0]; - $declineEmail = [ - 'subject' => $emailLog->getData('subject'), - 'body' => $emailLog->getData('body'), - ]; + foreach ($emailLogs as $emailLog) { + $dateSent = substr($emailLog->getData('dateSent'), 0, 10); + $dateConfirmed = substr($reviewAssignment->getData('dateConfirmed'), 0, 10); + // Compare the dates to get the decline email associated to the current round. + if ($dateSent === $dateConfirmed) { + $declineEmail = [ + 'subject' => $emailLog->getData('subject'), + 'body' => $emailLog->getData('body'), + ]; + break; + } } } @@ -138,10 +142,17 @@ public function getHistory(Request $illuminateRequest): JsonResponse $reviewAssignmentId = $reviewAssignment->getId(); $submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO'); - $comments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignmentId, true)->toArray(); - $privateComments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignmentId, false)->toArray(); - - // TODO: get the comments data... + $allSubmissionComments = $submissionCommentDao->getReviewerCommentsByReviewerId($submissionId, $reviewerId, $reviewAssignmentId)->toArray(); + $viewableComments = []; + $privateComments = []; + foreach ($allSubmissionComments as $submissionComment) { + $comments = $submissionComment->getData('comments'); + if ($submissionComment->getData('viewable')) { + $viewableComments[] = $comments; + } else { + $privateComments[] = $comments; + } + } $lastReviewAssignment = Repo::reviewAssignment()->getCollector() ->filterByContextIds([$contextId]) @@ -157,7 +168,7 @@ public function getHistory(Request $illuminateRequest): JsonResponse 'declineEmail' => $declineEmail, 'reviewAssignment' => $reviewAssignmentProps, 'recommendation' => $recommendation, - 'comments' => $comments, + 'comments' => $viewableComments, 'privateComments' => $privateComments, 'displayFiles' => $displayFiles, ]; From 3cdcdff498a75c52cb3dba220ace9f6f663d0252 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Thu, 1 Feb 2024 15:36:18 -0500 Subject: [PATCH 07/11] pkp/pkp-lib#9453 Change the review API to fetch additional data for round history --- api/v1/reviews/PKPReviewController.php | 52 ++++++++++++++++++++++---- pages/reviewer/PKPReviewerHandler.php | 3 +- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/api/v1/reviews/PKPReviewController.php b/api/v1/reviews/PKPReviewController.php index 2506dfdc266..3e9649fac20 100644 --- a/api/v1/reviews/PKPReviewController.php +++ b/api/v1/reviews/PKPReviewController.php @@ -22,6 +22,7 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Route; +use PKP\core\PKPApplication; use PKP\core\PKPBaseController; use PKP\core\PKPRequest; use PKP\db\DAORegistry; @@ -30,6 +31,7 @@ use PKP\security\authorization\SubmissionAccessPolicy; use PKP\security\authorization\UserRolesRequiredPolicy; use PKP\security\Role; +use PKP\submissionFile\SubmissionFile; class PKPReviewController extends PKPBaseController { @@ -96,9 +98,7 @@ public function getHistory(Request $illuminateRequest): JsonResponse $context = $request->getContext(); $contextId = $context->getId(); - // TODO: get the reviewer ID from the request or from the route? $reviewerId = $request->getUser()->getId(); - //$reviewerId = $illuminateRequest->route('reviewerId'); $submissionId = $illuminateRequest->route('submissionId'); $reviewRoundId = $illuminateRequest->route('reviewRoundId'); @@ -117,7 +117,13 @@ public function getHistory(Request $illuminateRequest): JsonResponse } $submission = Repo::submission()->get($submissionId, $contextId); - $publicationTitle = $submission->getCurrentPublication()->getLocalizedTitle(); + $publication = $submission->getCurrentPublication(); + $publicationTitle = $publication->getLocalizedTitle(); + + $section = Repo::section()->get($submission->getSectionId()); + $publicationType = $section->getLocalizedData('title'); + $publicationAbstract = $publication->getLocalizedData('abstract'); + $publicationKeywords = implode(', ', $publication->getLocalizedData('keywords')); $declineEmail = null; if ($reviewAssignment->getDeclined()) { @@ -154,6 +160,21 @@ public function getHistory(Request $illuminateRequest): JsonResponse } } + $genreDao = DAORegistry::getDAO('GenreDAO'); + $fileGenres = $genreDao->getByContextId($contextId)->toArray(); + + $attachments = Repo::submissionFile()->getCollector() + ->filterBySubmissionIds([$submissionId]) + ->filterByReviewRoundIds([$reviewRoundId]) + ->filterByUploaderUserIds([$reviewerId]) + ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT]) + ->filterByAssoc(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT, [$reviewAssignmentId]) + ->getMany(); + $attachmentsProps = Repo::submissionFile() + ->getSchemaMap() + ->mapMany($attachments, $fileGenres) + ->toArray(); + $lastReviewAssignment = Repo::reviewAssignment()->getCollector() ->filterByContextIds([$contextId]) ->filterBySubmissionIds([$submissionId]) @@ -161,18 +182,35 @@ public function getHistory(Request $illuminateRequest): JsonResponse ->filterByLastReviewRound(true) ->getMany() ->first(); - $displayFiles = $lastReviewAssignment->getDeclined() != 1; - $reviewRoundHistoryProps = [ + $filesProps = []; + if ($lastReviewAssignment->getDeclined() != 1) { + $files = Repo::submissionFile()->getCollector() + ->filterBySubmissionIds([$submissionId]) + ->filterByReviewRoundIds([$reviewRoundId]) + ->filterByAssoc(PKPApplication::ASSOC_TYPE_REVIEW_ROUND, [$reviewAssignmentId]) + ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_REVIEW_FILE]) + ->getMany(); + $filesProps = Repo::submissionFile() + ->getSchemaMap() + ->mapMany($files, $fileGenres) + ->toArray(); + } + + $reviewRoundHistory = [ 'publicationTitle' => $publicationTitle, + 'publicationType' => $publicationType, + 'publicationAbstract' => $publicationAbstract, + 'publicationKeywords' => $publicationKeywords, 'declineEmail' => $declineEmail, 'reviewAssignment' => $reviewAssignmentProps, 'recommendation' => $recommendation, 'comments' => $viewableComments, 'privateComments' => $privateComments, - 'displayFiles' => $displayFiles, + 'attachments' => array_values($attachmentsProps), + 'files' => array_values($filesProps), ]; - return response()->json($reviewRoundHistoryProps, Response::HTTP_OK); + return response()->json($reviewRoundHistory, Response::HTTP_OK); } } diff --git a/pages/reviewer/PKPReviewerHandler.php b/pages/reviewer/PKPReviewerHandler.php index b5229893983..86885b5e6c8 100644 --- a/pages/reviewer/PKPReviewerHandler.php +++ b/pages/reviewer/PKPReviewerHandler.php @@ -78,7 +78,8 @@ public function submission(array $args, PKPRequest $request): void $reviewRoundHistories[] = [ 'submissionId' => $submissionId, 'reviewRoundId' => $reviewRoundId, - 'reviewRoundNumber' => $reviewAssignment->getRound() + 'reviewRoundNumber' => $reviewAssignment->getRound(), + 'submittedOn' => $reviewAssignment->getDateCompleted() ]; } } From 3c8ca3fa2c69ada12bac53cc0994c801b0412ce1 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Thu, 1 Feb 2024 15:37:34 -0500 Subject: [PATCH 08/11] pkp/pkp-lib#9453 Adjustments on the labels for the round history --- locale/en/common.po | 6 +++++ locale/en/reviewer.po | 49 +++++++++++++++++++++++++++++++++------- locale/fr_CA/common.po | 6 +++++ locale/fr_CA/reviewer.po | 47 ++++++++++++++++++++++++++++++++------ 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/locale/en/common.po b/locale/en/common.po index 5cf11ec8dab..e73df288185 100644 --- a/locale/en/common.po +++ b/locale/en/common.po @@ -1839,6 +1839,12 @@ msgstr "Response Due Date" msgid "reviewer.submission.reviewDueDate" msgstr "Review Due Date" +msgid "reviewer.submission.acceptedOn" +msgstr "Review Accepted On" + +msgid "reviewer.submission.submittedOn" +msgstr "Review Submitted On" + msgid "submission.task.responseDueDate" msgstr "Response Due Date" diff --git a/locale/en/reviewer.po b/locale/en/reviewer.po index ccf4ea90350..10f0daf028d 100644 --- a/locale/en/reviewer.po +++ b/locale/en/reviewer.po @@ -95,22 +95,28 @@ msgstr "" "revised versions of the original review file(s)." msgid "reviewer.submission.reviewRound.info" -msgstr "Read my former reviews: " +msgstr "Previous Reviews" + +msgid "reviewer.submission.reviewRound.info.submittedOn" +msgstr "Round {$round} Review Submitted on {$submittedOn}" + +msgid "reviewer.submission.reviewRound.info.read" +msgstr "Read Round {$round} Review" msgid "reviewer.submission.reviewRound.info.modal.title" -msgstr "Evaluation cycle {$reviewRoundNumber} : {$submissionTitle}" +msgstr "Round {$round} Review submitted by you for" -msgid "reviewer.submission.reviewRound.info.history" -msgstr "Review history" +msgid "reviewer.submission.reviewRound.comments" +msgstr "Reviewer Comments" msgid "reviewer.submission.reviewRound.comments.authorAndEditor" -msgstr "Author and editor" +msgstr "For editors and authors" msgid "reviewer.submission.reviewRound.comments.editorOnly" -msgstr "Editor only" +msgstr "For editors only" -msgid "reviewer.submission.reviewRound.comments.review" -msgstr "Review" +msgid "reviewer.submission.reviewRound.comments.prefix" +msgstr "Comment {$index}: " msgid "reviewer.submission.reviewRound.emailLog" msgstr "Decline reason sent by email" @@ -121,6 +127,33 @@ msgstr "No reason given to the decline of the review invitation." msgid "reviewer.submission.reviewRound.reviewDeclineDate" msgstr "Declined Date" +msgid "reviewer.submission.reviewRound.metadata" +msgstr "Article Metadata" + +msgid "reviewer.submission.reviewRound.metadata.type" +msgstr "Type" + +msgid "reviewer.submission.reviewRound.metadata.abstract" +msgstr "Abstract" + +msgid "reviewer.submission.reviewRound.metadata.keywords" +msgstr "Keywords" + +msgid "reviewer.submission.reviewRound.general" +msgstr "General Information" + +msgid "reviewer.submission.reviewRound.attachments" +msgstr "Attachments" + +msgid "reviewer.submission.reviewRound.attachments.description" +msgstr "These are files that you attached along with your review" + +msgid "reviewer.submission.reviewRound.files" +msgstr "Files For Review" + +msgid "reviewer.submission.reviewRound.files.description" +msgstr "These files were sent to you for review" + msgid "reviewer.complete" msgstr "Review Submitted" diff --git a/locale/fr_CA/common.po b/locale/fr_CA/common.po index 2505d3da52c..ef615c7f51f 100644 --- a/locale/fr_CA/common.po +++ b/locale/fr_CA/common.po @@ -1861,6 +1861,12 @@ msgstr "Date d'échéance de la réponse" msgid "reviewer.submission.reviewDueDate" msgstr "Date d'échéance de remise de l'évaluation" +msgid "reviewer.submission.acceptedOn" +msgstr "Date d'acceptation de l'évaluation" + +msgid "reviewer.submission.submittedOn" +msgstr "Date de la soumission de l'évaluation" + msgid "submission.task.responseDueDate" msgstr "Date d'échéance de la réponse" diff --git a/locale/fr_CA/reviewer.po b/locale/fr_CA/reviewer.po index f678fa1a779..45c5ba08989 100644 --- a/locale/fr_CA/reviewer.po +++ b/locale/fr_CA/reviewer.po @@ -108,22 +108,28 @@ msgstr "" "originaux." msgid "reviewer.submission.reviewRound.info" -msgstr "Consulter mes évaluations précédentes : " +msgstr "Évaluations précédentes" + +msgid "reviewer.submission.reviewRound.info.submittedOn" +msgstr "Cycle d'évaluation {$round} Évaluation soumise le {$submittedOn}" + +msgid "reviewer.submission.reviewRound.info.read" +msgstr "Lire l'évaluation du cycle {$round}" msgid "reviewer.submission.reviewRound.info.modal.title" -msgstr "Évaluation cycle {$reviewRoundNumber} : {$submissionTitle}" +msgstr "Cycle d'évaluation {$round} que vous avez soumis pour" -msgid "reviewer.submission.reviewRound.info.history" -msgstr "Historique d'évaluation" +msgid "reviewer.submission.reviewRound.comments" +msgstr "Commentaires de l'évaluateur" msgid "reviewer.submission.reviewRound.comments.authorAndEditor" -msgstr "Pour l'auteur et la rédaction" +msgstr "Pour la rédaction et les auteurs" msgid "reviewer.submission.reviewRound.comments.editorOnly" msgstr "Pour la rédaction seulement" -msgid "reviewer.submission.reviewRound.comments.review" -msgstr "Évaluation" +msgid "reviewer.submission.reviewRound.comments.prefix" +msgstr "Commentaire {$index}: " msgid "reviewer.submission.reviewRound.emailLog" msgstr "Raison du refus envoyée par courriel" @@ -134,6 +140,33 @@ msgstr "Aucune raison fournie pour le refus de l'invitation à évaluer." msgid "reviewer.submission.reviewRound.reviewDeclineDate" msgstr "Date du refus" +msgid "reviewer.submission.reviewRound.metadata" +msgstr "Métadonnées de l'article" + +msgid "reviewer.submission.reviewRound.metadata.type" +msgstr "Type" + +msgid "reviewer.submission.reviewRound.metadata.abstract" +msgstr "Résumé" + +msgid "reviewer.submission.reviewRound.metadata.keywords" +msgstr "Mots clés" + +msgid "reviewer.submission.reviewRound.general" +msgstr "Information générale" + +msgid "reviewer.submission.reviewRound.attachments" +msgstr "Pièces jointes" + +msgid "reviewer.submission.reviewRound.attachments.description" +msgstr "Il s'agit des fichiers que vous avez joints à votre évaluation" + +msgid "reviewer.submission.reviewRound.files" +msgstr "Fichiers pour l'évaluation" + +msgid "reviewer.submission.reviewRound.files.description" +msgstr "Il s'agit des fichiers qui vous ont été envoyés pour l'évaluation" + msgid "reviewer.complete" msgstr "Évaluation envoyée" From 2a12d513df1cc4d60f59b9871b9ce4f8827488c1 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Fri, 2 Feb 2024 09:27:32 -0500 Subject: [PATCH 09/11] pkp/pkp-lib#9453 Fix to get the ReviewAssignment and not the DAO in the policy --- .../internal/SubmissionFileAssignedReviewerAccessPolicy.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.php b/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.php index 12d9a2b4327..936a70c43cf 100644 --- a/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.php +++ b/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.php @@ -50,7 +50,9 @@ public function effect() } $context = $request->getContext(); - $reviewAssignments = Repo::reviewAssignment()->getCollector()->filterByReviewerIds([$user->getId()])->getMany(); + $reviewAssignments = Repo::reviewAssignment()->getCollector() + ->filterByReviewerIds([$user->getId()]) + ->getMany(); $reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /** @var ReviewFilesDAO $reviewFilesDao */ foreach ($reviewAssignments as $reviewAssignment) { if ($context->getData('restrictReviewerFileAccess') && !$reviewAssignment->getDateConfirmed()) { From c0683e1f6757a9522fa3fad09c952b89e8b13740 Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Fri, 16 Feb 2024 15:40:40 -0500 Subject: [PATCH 10/11] pkp/pkp-lib#9453 Fixed a null date when the reviewer has declined the review --- pages/reviewer/PKPReviewerHandler.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pages/reviewer/PKPReviewerHandler.php b/pages/reviewer/PKPReviewerHandler.php index 86885b5e6c8..24b86bf0f07 100644 --- a/pages/reviewer/PKPReviewerHandler.php +++ b/pages/reviewer/PKPReviewerHandler.php @@ -79,7 +79,9 @@ public function submission(array $args, PKPRequest $request): void 'submissionId' => $submissionId, 'reviewRoundId' => $reviewRoundId, 'reviewRoundNumber' => $reviewAssignment->getRound(), - 'submittedOn' => $reviewAssignment->getDateCompleted() + 'submittedOn' => $reviewAssignment->getDeclined() + ? $reviewAssignment->getDateConfirmed() + : $reviewAssignment->getDateCompleted(), ]; } } @@ -94,7 +96,7 @@ public function submission(array $args, PKPRequest $request): void $templateMgr->setState([ 'isReviewRoundHistoryEnabled' => Config::getVar('features', 'enable_review_round_history'), 'pageInitConfig' => [ - 'reviewRoundHistories' => $reviewRoundHistories + 'reviewRoundHistories' => $reviewRoundHistories, ] ]); From 8f88974c564d6b78a4f22f10906c9b4c215faa9f Mon Sep 17 00:00:00 2001 From: Nicolas Boulay Date: Fri, 16 Feb 2024 15:41:33 -0500 Subject: [PATCH 11/11] pkp/pkp-lib#9453 Localize the data on the front end --- api/v1/reviews/PKPReviewController.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/v1/reviews/PKPReviewController.php b/api/v1/reviews/PKPReviewController.php index 3e9649fac20..0b664545e6d 100644 --- a/api/v1/reviews/PKPReviewController.php +++ b/api/v1/reviews/PKPReviewController.php @@ -118,12 +118,13 @@ public function getHistory(Request $illuminateRequest): JsonResponse $submission = Repo::submission()->get($submissionId, $contextId); $publication = $submission->getCurrentPublication(); - $publicationTitle = $publication->getLocalizedTitle(); + //$publicationTitlePrefix = $publication->getData('prefix'); + $publicationTitle = $publication->getData('title'); $section = Repo::section()->get($submission->getSectionId()); - $publicationType = $section->getLocalizedData('title'); - $publicationAbstract = $publication->getLocalizedData('abstract'); - $publicationKeywords = implode(', ', $publication->getLocalizedData('keywords')); + $publicationType = $section->getData('title'); + $publicationAbstract = $publication->getData('abstract'); + $publicationKeywords = $publication->getData('keywords'); $declineEmail = null; if ($reviewAssignment->getDeclined()) { @@ -144,6 +145,7 @@ public function getHistory(Request $illuminateRequest): JsonResponse } $reviewAssignmentProps = Repo::reviewAssignment()->getSchemaMap()->map($reviewAssignment); + // It doesn't seem we can translate the recommendation inside the vue page as it's a dynamic label key. $recommendation = $reviewAssignment->getLocalizedRecommendation(); $reviewAssignmentId = $reviewAssignment->getId();