diff --git a/ScieloModerationStagesPlugin.inc.php b/ScieloModerationStagesPlugin.inc.php index 47126c1..aa1f01b 100644 --- a/ScieloModerationStagesPlugin.inc.php +++ b/ScieloModerationStagesPlugin.inc.php @@ -97,15 +97,22 @@ public function getActions($request, $actionArgs) import('lib.pkp.classes.linkAction.request.AjaxModal'); return array_merge( [ + new LinkAction( + 'sendModerationReminder', + new AjaxModal( + $router->url($request, null, null, 'manage', null, ['verb' => 'sendModerationReminder', 'plugin' => $this->getName(), 'category' => 'generic']), + __('plugins.generic.scieloModerationStages.sendModerationReminder') + ), + __('plugins.generic.scieloModerationStages.sendModerationReminder') + ), new LinkAction( 'settings', new AjaxModal( $router->url($request, null, null, 'manage', null, ['verb' => 'settings', 'plugin' => $this->getName(), 'category' => 'generic']), __('plugins.generic.scieloModerationStages.settings.title') ), - __('manager.plugins.settings'), - null - ), + __('manager.plugins.settings') + ) ], parent::getActions($request, $actionArgs) ); @@ -118,22 +125,29 @@ public function manage($args, $request) switch ($request->getUserVar('verb')) { case 'settings': - $this->import('ScieloModerationStagesSettingsForm'); - $form = new ScieloModerationStagesSettingsForm($this, $contextId); - if ($request->getUserVar('save')) { - $form->readInputData(); - if ($form->validate()) { - $form->execute(); - return new JSONMessage(true); - } - } else { - $form->initData(); - } - return new JSONMessage(true, $form->fetch($request)); + return $this->handlePluginForm($request, $contextId, 'ScieloModerationStagesSettingsForm'); + case 'sendModerationReminder': + return $this->handlePluginForm($request, $contextId, 'SendModerationReminderForm'); } return parent::manage($args, $request); } + private function handlePluginForm($request, $contextId, $formClass) + { + $this->import('form.'.$formClass); + $form = new $formClass($this, $contextId); + if ($request->getUserVar('save')) { + $form->readInputData(); + if ($form->validate()) { + $form->execute(); + return new JSONMessage(true); + } + } else { + $form->initData(); + } + return new JSONMessage(true, $form->fetch($request)); + } + public function setupScieloModerationStagesHandler($hookName, $params) { $component = &$params[0]; diff --git a/classes/ModerationReminderHelper.inc.php b/classes/ModerationReminderHelper.inc.php new file mode 100644 index 0000000..70e6d5f --- /dev/null +++ b/classes/ModerationReminderHelper.inc.php @@ -0,0 +1,103 @@ +moderationStageDao = new ModerationStageDAO(); + } + + public function setModerationStageDao($moderationStageDao) + { + $this->moderationStageDao = $moderationStageDao; + } + + public function getResponsiblesUserGroup(int $contextId) + { + $userGroupDao = DAORegistry::getDAO('UserGroupDAO'); + $contextUserGroups = $userGroupDao->getByContextId($contextId)->toArray(); + + foreach ($contextUserGroups as $userGroup) { + $userGroupAbbrev = strtolower($userGroupDao->getSetting($userGroup->getId(), 'abbrev', 'en_US')); + + if ($userGroupAbbrev === 'resp') { + $responsiblesUserGroup = $userGroup; + break; + } + } + + return $responsiblesUserGroup; + } + + public function getResponsibleAssignments($responsiblesUserGroup, $contextId): array + { + if (!$responsiblesUserGroup) { + return []; + } + + $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); + $responsiblesAssignments = $stageAssignmentDao->getByUserGroupId($responsiblesUserGroup->getId(), $contextId); + + return $responsiblesAssignments->toArray(); + } + + public function filterAssignmentsOfSubmissionsOnPreModeration(array $assignments): array + { + $preModerationAssignments = []; + + foreach ($assignments as $assignment) { + $submissionId = $assignment->getData('submissionId'); + $submissionModerationStage = $this->moderationStageDao->getSubmissionModerationStage($submissionId); + + if ($submissionModerationStage === SCIELO_MODERATION_STAGE_CONTENT) { + $preModerationAssignments[] = $assignment; + } + } + + return $preModerationAssignments; + } + + public function getUsersFromAssignments(array $assignments): array + { + $users = []; + $userDao = DAORegistry::getDAO('UserDAO'); + + foreach ($assignments as $assignment) { + $user = $userDao->getById($assignment->getUserId()); + + if ($user and !isset($users[$user->getId()])) { + $users[$user->getId()] = $user; + } + } + + return $users; + } + + public function mapUsersAndSubmissions($users, $assignments) + { + $usersMap = []; + $submissionDao = DAORegistry::getDAO('SubmissionDAO'); + + foreach ($users as $userId) { + foreach ($assignments as $assignment) { + if ($userId != $assignment->getData('userId')) { + continue; + } + + $submission = $submissionDao->getById($assignment->getData('submissionId')); + + if (isset($usersMap[$userId])) { + $usersMap[$userId] = array_merge($usersMap[$userId], [$submission]); + } else { + $usersMap[$userId] = [$submission]; + } + } + } + + return $usersMap; + } +} diff --git a/classes/tasks/SendModerationReminders.inc.php b/classes/tasks/SendModerationReminders.inc.php index b4101c5..7a79fdd 100644 --- a/classes/tasks/SendModerationReminders.inc.php +++ b/classes/tasks/SendModerationReminders.inc.php @@ -4,6 +4,7 @@ import('plugins.generic.scieloModerationStages.classes.ModerationStage'); import('plugins.generic.scieloModerationStages.classes.ModerationStageDAO'); import('plugins.generic.scieloModerationStages.classes.ModerationReminderEmailBuilder'); +import('plugins.generic.scieloModerationStages.classes.ModerationReminderHelper'); class SendModerationReminders extends ScheduledTask { @@ -15,15 +16,17 @@ public function executeActions() $this->plugin = PluginRegistry::getPlugin('generic', 'scielomoderationstagesplugin'); $context = Application::get()->getRequest()->getContext(); - $responsiblesAssignments = $this->getResponsiblesAssignments($context->getId()); - $preModerationAssignments = $this->filterPreModerationAssignments($responsiblesAssignments); + $moderationReminderHelper = new ModerationReminderHelper(); + $responsiblesUserGroup = $moderationReminderHelper->getResponsiblesUserGroup($context->getId()); + $responsibleAssignments = $moderationReminderHelper->getResponsibleAssignments($responsiblesUserGroup, $context->getId()); + $preModerationAssignments = $moderationReminderHelper->filterAssignmentsOfSubmissionsOnPreModeration($responsibleAssignments); if (empty($preModerationAssignments)) { return true; } $usersWithOverduePreModeration = $this->getUsersWithOverduePreModeration($context->getId(), $preModerationAssignments); - $mapModeratorsAndOverdueSubmissions = $this->mapModeratorsAndOverdueSubmissions($usersWithOverduePreModeration, $preModerationAssignments); + $mapModeratorsAndOverdueSubmissions = $moderationReminderHelper->mapUsersAndSubmissions($usersWithOverduePreModeration, $preModerationAssignments); foreach ($mapModeratorsAndOverdueSubmissions as $userId => $submissions) { $moderator = DAORegistry::getDAO('UserDAO')->getById($userId); @@ -36,47 +39,6 @@ public function executeActions() return true; } - private function getResponsiblesAssignments(int $contextId) - { - $userGroupDao = DAORegistry::getDAO('UserGroupDAO'); - $contextUserGroups = $userGroupDao->getByContextId($contextId)->toArray(); - - foreach ($contextUserGroups as $userGroup) { - $userGroupAbbrev = strtolower($userGroupDao->getSetting($userGroup->getId(), 'abbrev', 'en_US')); - - if ($userGroupAbbrev === 'resp') { - $responsiblesUserGroup = $userGroup; - break; - } - } - - if (!$responsiblesUserGroup) { - return []; - } - - $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); - $responsiblesAssignments = $stageAssignmentDao->getByUserGroupId($responsiblesUserGroup->getId(), $contextId); - - return $responsiblesAssignments->toArray(); - } - - private function filterPreModerationAssignments($responsiblesAssignments): array - { - $moderationStageDao = new ModerationStageDAO(); - $preModerationAssignments = []; - - foreach ($responsiblesAssignments as $assignment) { - $submissionId = $assignment->getData('submissionId'); - $submissionModerationStage = $moderationStageDao->getSubmissionModerationStage($submissionId); - - if ($submissionModerationStage === SCIELO_MODERATION_STAGE_CONTENT) { - $preModerationAssignments[] = $assignment; - } - } - - return $preModerationAssignments; - } - private function getUsersWithOverduePreModeration($contextId, $preModerationAssignments): array { $usersIds = []; @@ -94,28 +56,4 @@ private function getUsersWithOverduePreModeration($contextId, $preModerationAssi return $usersIds; } - - private function mapModeratorsAndOverdueSubmissions($moderators, $preModerationAssignments) - { - $moderatorsMap = []; - $submissionDao = DAORegistry::getDAO('SubmissionDAO'); - - foreach ($moderators as $moderatorId) { - foreach ($preModerationAssignments as $assignment) { - if ($moderatorId != $assignment->getData('userId')) { - continue; - } - - $submission = $submissionDao->getById($assignment->getData('submissionId')); - - if (isset($moderatorsMap[$moderatorId])) { - $moderatorsMap[$moderatorId] = array_merge($moderatorsMap[$moderatorId], [$submission]); - } else { - $moderatorsMap[$moderatorId] = [$submission]; - } - } - } - - return $moderatorsMap; - } } diff --git a/controllers/ScieloModerationStagesHandler.inc.php b/controllers/ScieloModerationStagesHandler.inc.php index 6a27a4f..527fc94 100644 --- a/controllers/ScieloModerationStagesHandler.inc.php +++ b/controllers/ScieloModerationStagesHandler.inc.php @@ -5,12 +5,39 @@ import('classes.handler.Handler'); import('classes.workflow.EditorDecisionActionsManager'); import('plugins.generic.scieloModerationStages.classes.ModerationStageDAO'); +import('plugins.generic.scieloModerationStages.classes.ModerationReminderHelper'); +import('plugins.generic.scieloModerationStages.classes.ModerationReminderEmailBuilder'); class ScieloModerationStagesHandler extends Handler { private const SUBMISSION_STAGE_ID = 5; private const THRESHOLD_TIME_EXHIBITORS = 2; + public function getReminderBody($args, $request) + { + $responsible = DAORegistry::getDAO('UserDAO')->getById((int) $args['responsible']); + + $moderationReminderHelper = new ModerationReminderHelper(); + $context = $request->getContext(); + $responsiblesUserGroup = $moderationReminderHelper->getResponsiblesUserGroup($context->getId()); + $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); + $assignments = $stageAssignmentDao->_getByIds(null, null, $responsiblesUserGroup->getId(), $responsible->getId())->toArray(); + + $submissions = []; + foreach ($assignments as $assignment) { + $submission = DAORegistry::getDAO('SubmissionDAO')->getById($assignment->getSubmissionId()); + + if ($submission) { + $submissions[] = $submission; + } + } + + $moderationReminderEmailBuilder = new ModerationReminderEmailBuilder($context, $responsible, $submissions); + $reminderEmail = $moderationReminderEmailBuilder->buildEmail(); + + return json_encode(['reminderBody' => $reminderEmail->getBody()]); + } + public function updateSubmissionStageData($args, $request) { $submissionDao = DAORegistry::getDAO('SubmissionDAO'); diff --git a/ScieloModerationStagesSettingsForm.inc.php b/form/ScieloModerationStagesSettingsForm.inc.php similarity index 100% rename from ScieloModerationStagesSettingsForm.inc.php rename to form/ScieloModerationStagesSettingsForm.inc.php diff --git a/form/SendModerationReminderForm.inc.php b/form/SendModerationReminderForm.inc.php new file mode 100644 index 0000000..6e945d3 --- /dev/null +++ b/form/SendModerationReminderForm.inc.php @@ -0,0 +1,76 @@ +contextId = $contextId; + $this->plugin = $plugin; + parent::__construct($plugin->getTemplateResource('sendModerationReminderForm.tpl')); + } + + private function getResponsibles(int $contextId): array + { + $moderationReminderHelper = new ModerationReminderHelper(); + $responsiblesUserGroup = $moderationReminderHelper->getResponsiblesUserGroup($contextId); + $responsibleAssignments = $moderationReminderHelper->getResponsibleAssignments($responsiblesUserGroup, $contextId); + + if (empty($responsibleAssignments)) { + return []; + } + + $filteredAssignments = $moderationReminderHelper->filterAssignmentsOfSubmissionsOnPreModeration($responsibleAssignments); + $usersFromAssignments = $moderationReminderHelper->getUsersFromAssignments($filteredAssignments); + + $mappedUsers = [null => null]; + foreach ($usersFromAssignments as $userId => $user) { + $fullName = $user->getFullName(); + $mappedUsers[$userId] = $fullName; + } + + asort($mappedUsers, SORT_STRING); + + return $mappedUsers; + } + + public function fetch($request, $template = null, $display = false) + { + $templateMgr = TemplateManager::getManager($request); + $contextId = $request->getContext()->getId(); + + $templateMgr->assign([ + 'responsibles' => $this->getResponsibles($contextId), + 'pluginName' => $this->plugin->getName(), + 'applicationName' => Application::get()->getName() + ]); + + return parent::fetch($request, $template, $display); + } + + public function readInputData() + { + $this->readUserVars(['responsible', 'reminderBody']); + } + + public function execute(...$functionArgs) + { + $responsibleUserId = $this->getData('responsible'); + $reminderBody = $this->getData('reminderBody'); + + $responsible = DAORegistry::getDAO('UserDAO')->getById($responsibleUserId); + $context = Application::get()->getRequest()->getContext(); + + $moderationReminderEmailBuilder = new ModerationReminderEmailBuilder($context, $responsible, []); + $reminderEmail = $moderationReminderEmailBuilder->buildEmail(); + $reminderEmail->setBody($reminderBody); + + $reminderEmail->send(); + } +} diff --git a/locale/en_US/locale.po b/locale/en_US/locale.po index e5c26bf..995396a 100644 --- a/locale/en_US/locale.po +++ b/locale/en_US/locale.po @@ -136,6 +136,24 @@ msgid "plugins.generic.scieloModerationStages.settings.remindersDayOfWeek" msgstr "Note: Moderation reminders will be sent every Monday." +msgid "plugins.generic.scieloModerationStages.sendModerationReminder" +msgstr "Send moderation reminder" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.responsible.title" +msgstr "Responsible" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.responsible.description" +msgstr "Select to which responsible person the moderation reminder should be sent" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.title" +msgstr "Reminder body" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.description" +msgstr "Body of the reminder message to be sent" + +msgid "plugins.generic.scieloModerationStages.send" +msgstr "Send" + msgid "plugins.generic.scieloModerationStages.submissionMade.lessThanADayAgo" msgstr "Submission made less than a day ago" diff --git a/locale/es_ES/locale.po b/locale/es_ES/locale.po index a07c441..7d781b5 100644 --- a/locale/es_ES/locale.po +++ b/locale/es_ES/locale.po @@ -136,6 +136,24 @@ msgid "plugins.generic.scieloModerationStages.settings.remindersDayOfWeek" msgstr "Nota: Los recordatorios de moderación se enviarán todos los lunes." +msgid "plugins.generic.scieloModerationStages.sendModerationReminder" +msgstr "Enviar recordatorio de moderación" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.responsible.title" +msgstr "Responsable" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.responsible.description" +msgstr "Seleccione a quién debe enviarse el recordatorio de moderación" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.title" +msgstr "Cuerpo del recordatorio" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.description" +msgstr "Cuerpo del mensaje de recordatorio que se enviará" + +msgid "plugins.generic.scieloModerationStages.send" +msgstr "Enviar" + msgid "plugins.generic.scieloModerationStages.submissionMade.lessThanADayAgo" msgstr "Envío realizado hace menos de un día" diff --git a/locale/pt_BR/locale.po b/locale/pt_BR/locale.po index d0f57fd..12419b7 100644 --- a/locale/pt_BR/locale.po +++ b/locale/pt_BR/locale.po @@ -136,6 +136,24 @@ msgid "plugins.generic.scieloModerationStages.settings.remindersDayOfWeek" msgstr "Obs.: Os lembretes de moderação serão enviados todas as segundas-feiras." +msgid "plugins.generic.scieloModerationStages.sendModerationReminder" +msgstr "Enviar lembrete de moderação" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.responsible.title" +msgstr "Responsável" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.responsible.description" +msgstr "Selecione para qual responsável deve ser enviado o lembrete de moderação" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.title" +msgstr "Corpo do lembrete" + +msgid "plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.description" +msgstr "Corpo da mensagem de lembrete a ser enviada" + +msgid "plugins.generic.scieloModerationStages.send" +msgstr "Enviar" + msgid "plugins.generic.scieloModerationStages.submissionMade.lessThanADayAgo" msgstr "Submissão realizada há menos de um dia" diff --git a/templates/sendModerationReminderForm.tpl b/templates/sendModerationReminderForm.tpl new file mode 100644 index 0000000..7a3ca6b --- /dev/null +++ b/templates/sendModerationReminderForm.tpl @@ -0,0 +1,47 @@ + + +
+
+ {csrf} + {include file="controllers/notification/inPlaceNotification.tpl" notificationId="sendModerationReminderFormNotification"} + + {fbvFormSection id="responsibleSection" label="plugins.generic.scieloModerationStages.sendModerationReminder.responsible.title"} + {fbvElement type="select" id="responsible" name="responsible" from=$responsibles required="true" label="plugins.generic.scieloModerationStages.sendModerationReminder.responsible.description" translate=false size=$fbvStyles.size.MEDIUM} + {/fbvFormSection} + + {fbvFormSection id="reminderBodySection" label="plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.title"} + {fbvElement type="textarea" id="reminderBody" name="reminderBody" label="plugins.generic.scieloModerationStages.sendModerationReminder.reminderBody.description" rich=true value=$reminderBody} + {/fbvFormSection} + + {fbvFormButtons submitText="plugins.generic.scieloModerationStages.send"} +

{translate key="common.requiredField"}

+
+
+ +{capture assign=getReminderBodyUrl}{url router=$smarty.const.ROUTE_COMPONENT component="plugins.generic.scieloModerationStages.controllers.ScieloModerationStagesHandler" op="getReminderBody" escape=false}{/capture} + \ No newline at end of file diff --git a/tests/ModerationReminderHelperTest.php b/tests/ModerationReminderHelperTest.php new file mode 100644 index 0000000..2ab7359 --- /dev/null +++ b/tests/ModerationReminderHelperTest.php @@ -0,0 +1,113 @@ +moderationReminderHelper = new ModerationReminderHelper(); + $this->moderatorUsers = $this->createTestModeratorUsers(); + $this->assignments = $this->createTestAssignments(); + } + + protected function getMockedDAOs() + { + return ['UserDAO']; + } + + private function createTestModeratorUsers(): array + { + $firstModerator = new User(); + $firstModerator->setData('id', 312); + $firstModerator->setGivenName('Edgar', $this->locale); + $firstModerator->setFamilyName('Linton', $this->locale); + + $secondModerator = new User(); + $secondModerator->setData('id', 313); + $secondModerator->setGivenName('Catherine', $this->locale); + $secondModerator->setFamilyName('Earnshaw', $this->locale); + + return [$firstModerator, $secondModerator]; + } + + private function createTestAssignments(): array + { + $firstAssignment = new StageAssignment(); + $firstAssignment->setData('submissionId', 256); + $firstAssignment->setData('stageId', WORKFLOW_STAGE_ID_SUBMISSION); + $firstAssignment->setData('userId', $this->moderatorUsers[0]->getId()); + + $secondAssignment = new StageAssignment(); + $secondAssignment->setData('submissionId', 257); + $secondAssignment->setData('stageId', WORKFLOW_STAGE_ID_SUBMISSION); + $secondAssignment->setData('userId', $this->moderatorUsers[0]->getId()); + + $thirdAssignment = new StageAssignment(); + $thirdAssignment->setData('submissionId', 258); + $thirdAssignment->setData('stageId', WORKFLOW_STAGE_ID_SUBMISSION); + $thirdAssignment->setData('userId', $this->moderatorUsers[1]->getId()); + + return [$firstAssignment, $secondAssignment, $thirdAssignment]; + } + + private function createModerationStageDaoMock() + { + $mockedDAO = $this->createMock(ModerationStageDAO::class); + $mockedDAO->method('getSubmissionModerationStage')->willReturnMap([ + [256, SCIELO_MODERATION_STAGE_CONTENT], + [257, SCIELO_MODERATION_STAGE_FORMAT], + [258, SCIELO_MODERATION_STAGE_CONTENT] + ]); + + return $mockedDAO; + } + + private function registerUserDaoMock() + { + $mockedUserDao = $this->getMockBuilder(UserDAO::class) + ->setMethods(['getById']) + ->getMock(); + $mockedUserDao->expects($this->any()) + ->method('getById') + ->will($this->onConsecutiveCalls($this->moderatorUsers[0], $this->moderatorUsers[1])); + + DAORegistry::registerDAO('UserDAO', $mockedUserDao); + } + + public function testFilterAssignmentsOfSubmissionsOnPreModeration(): void + { + $mockedModerationStageDao = $this->createModerationStageDaoMock(); + $this->moderationReminderHelper->setModerationStageDao($mockedModerationStageDao); + + $expectedFilteredAssignments = [$this->assignments[0], $this->assignments[2]]; + $filteredAssignments = $this->moderationReminderHelper->filterAssignmentsOfSubmissionsOnPreModeration($this->assignments); + + $this->assertEquals($expectedFilteredAssignments, $filteredAssignments); + } + + public function testGetUsersFromAssignments(): void + { + $this->registerUserDaoMock(); + + $expectedAssignedUsers = [ + $this->moderatorUsers[1]->getId() => $this->moderatorUsers[1], + $this->moderatorUsers[0]->getId() => $this->moderatorUsers[0], + ]; + $usersFromAssignments = $this->moderationReminderHelper->getUsersFromAssignments($this->assignments); + + $this->assertEquals($expectedAssignedUsers, $usersFromAssignments); + } +} diff --git a/version.xml b/version.xml index 8729e65..168bb60 100644 --- a/version.xml +++ b/version.xml @@ -13,7 +13,7 @@ scieloModerationStages plugins.generic 0 - 1.6.0.0 - 2024-08-26 + 1.6.1.0 + 2024-10-04 ScieloModerationStagesPlugin