diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e6a606e..0a57280 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,5 +6,5 @@ include: ref: main file: - 'templates/groups/pkp_plugin.yml' - - 'templates/groups/ops_3_4_plugins_unit_tests_model.yml' - - 'templates/groups/ops_3_4_plugins_cypress_tests_model.yml' \ No newline at end of file + - 'templates/groups/ops/unit_tests.yml' + - 'templates/groups/ops/cypress_tests.yml' \ No newline at end of file diff --git a/PlauditPreEndorsementPlugin.php b/PlauditPreEndorsementPlugin.php index 444ee0d..9eb5234 100644 --- a/PlauditPreEndorsementPlugin.php +++ b/PlauditPreEndorsementPlugin.php @@ -23,17 +23,20 @@ use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\db\DAORegistry; use PKP\core\Core; -use APP\facades\Repo; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; use PKP\security\Role; use PKP\core\JSONMessage; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Mail; use APP\plugins\generic\plauditPreEndorsement\classes\OrcidClient; -use APP\plugins\generic\plauditPreEndorsement\classes\Endorsement; +use APP\plugins\generic\plauditPreEndorsement\classes\endorsement\Endorsement; use APP\plugins\generic\plauditPreEndorsement\classes\components\forms\EndorsementForm; use APP\plugins\generic\plauditPreEndorsement\PlauditPreEndorsementSettingsForm; use APP\plugins\generic\plauditPreEndorsement\classes\mail\mailables\OrcidRequestEndorserAuthorization; use APP\plugins\generic\plauditPreEndorsement\classes\observers\listeners\SendEmailToEndorser; +use APP\plugins\generic\plauditPreEndorsement\classes\SchemaBuilder; +use APP\plugins\generic\plauditPreEndorsement\classes\migration\addEndorsementsTable; +use Illuminate\Database\Migrations\Migration; class PlauditPreEndorsementPlugin extends GenericPlugin { @@ -50,14 +53,24 @@ public function register($category, $path, $mainContextId = null) if ($success && $this->getEnabled($mainContextId)) { Event::subscribe(new SendEmailToEndorser()); - Hook::add('TemplateManager::display', [$this, 'modifySubmissionSteps']); - Hook::add('Schema::get::publication', [$this, 'addOurFieldsToPublicationSchema']); - Hook::add('Submission::validateSubmit', [$this, 'validateEndorsement']); - Hook::add('Template::SubmissionWizard::Section::Review', [$this, 'modifyReviewSections']); + Hook::add('TemplateManager::display', [$this, 'addToSubmissionWizardSteps']); + Hook::add('Template::SubmissionWizard::Section', array($this, 'addToSubmissionWizardTemplate')); + Hook::add('Template::SubmissionWizard::Section::Review', [$this, 'addToReviewSubmissionWizardTemplate']); + Hook::add('Schema::get::endorsement', [$this, 'addEndorsementSchema']); - Hook::add('Template::Workflow::Publication', [$this, 'addEndorserFieldsToWorkflow']); + Hook::add('Template::Workflow::Publication', [$this, 'addEndorsementFieldsToWorkflow']); Hook::add('LoadHandler', [$this, 'setupPreEndorsementHandler']); Hook::add('AcronPlugin::parseCronTab', [$this, 'addEndorsementTasksToCrontab']); + Hook::add('LoadComponentHandler', [$this, 'setupGridHandler']); + + $templateMgr = TemplateManager::getManager(); + $request = Application::get()->getRequest(); + + $templateMgr->addJavaScript( + 'EndorsementGridHandler', + $request->getBaseUrl() . '/' . $this->getPluginPath() . '/js/EndorsementGridHandler.js', + array('contexts' => 'backend') + ); } return $success; @@ -110,19 +123,12 @@ public function writeOnActivityLog($submission, $message, $messageParams = array Repo::eventLog()->add($eventLog); } - public function inputIsEmail(string $input): bool - { - return filter_var($input, FILTER_VALIDATE_EMAIL); - } - - public function modifySubmissionSteps($hookName, $params) + public function addToSubmissionWizardSteps($hookName, $params) { $request = Application::get()->getRequest(); - $context = $request->getContext(); - $templateMgr = $params[0]; - if ($request->getRequestedPage() != 'submission' || $request->getRequestedOp() == 'saved') { - return false; + if ($request->getRequestedPage() !== 'submission' || $request->getRequestedOp() == 'saved') { + return; } $submission = $request @@ -131,96 +137,70 @@ public function modifySubmissionSteps($hookName, $params) ->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION); if (!$submission || !$submission->getData('submissionProgress')) { - return false; + return; } - $publication = $submission->getCurrentPublication(); - $publicationApiUrl = $request->getDispatcher()->url( - $request, - Application::ROUTE_API, - $request->getContext()->getPath(), - 'submissions/' . $submission->getId() . '/publications/' . $publication->getId() - ); - $endorsementForm = new EndorsementForm( - $publicationApiUrl, - $publication, - ); + $templateMgr = $params[0]; $steps = $templateMgr->getState('steps'); - $steps = array_map(function ($step) use ($endorsementForm) { - if ($step['id'] == 'details') { + $steps = array_map(function ($step) { + if ($step['id'] === 'details') { $step['sections'][] = [ - 'id' => 'endorsement', + 'id' => 'plauditPreEndorsement', 'name' => __('plugins.generic.plauditPreEndorsement.endorsement'), 'description' => __('plugins.generic.plauditPreEndorsement.endorsement.description'), - 'type' => SubmissionHandler::SECTION_TYPE_FORM, - 'form' => $endorsementForm->getConfig(), + 'type' => SubmissionHandler::SECTION_TYPE_TEMPLATE, ]; } return $step; }, $steps); - $templateMgr->setState(['steps' => $steps]); + $templateMgr->setState([ + 'steps' => $steps, + ]); return false; } - public function validateEndorsement($hookName, $params) + public function addToSubmissionWizardTemplate($hookName, $params) { - $errors = &$params[0]; - $submission = $params[1]; - $publication = $submission->getCurrentPublication(); - $endorserEmail = $publication->getData('endorserEmail'); + $smarty = $params[1]; + $output = & $params[2]; - if ($endorserEmail) { - if (!$this->inputIsEmail($endorserEmail)) { - $errors['endorsement'] = [__("plugins.generic.plauditPreEndorsement.endorsementEmailInvalid")]; - } else { - foreach ($publication->getData('authors') as $author) { - if($author->getData('email') == $endorserEmail) { - $errors['endorsement'] = [__("plugins.generic.plauditPreEndorsement.endorsementFromAuthor")]; - } - } - } - } + $output .= sprintf( + '', + $smarty->fetch($this->getTemplateResource('endorsementComponent.tpl')) + ); return false; } - public function modifyReviewSections($hookName, $params) + public function addToReviewSubmissionWizardTemplate($hookName, $params) { $step = $params[0]['step']; $templateMgr = $params[1]; $output = &$params[2]; if ($step == 'details') { - $output .= $templateMgr->fetch($this->getTemplateResource('reviewEndorsement.tpl')); + $output .= $templateMgr->fetch($this->getTemplateResource('endorsementComponent.tpl')); } + + return false; } - public function addOurFieldsToPublicationSchema($hookName, $params) + public function addEndorsementSchema(string $hookName, array $params): bool { $schema = &$params[0]; - $ourFields = [ - 'endorserName' => 'string', - 'endorserEmail' => 'string', - 'endorsementStatus' => 'integer', - 'endorserOrcid' => 'string', - 'endorserEmailToken' => 'string', - 'endorserEmailCount' => 'integer', - ]; - - foreach ($ourFields as $name => $type) { - $schema->properties->{$name} = (object) [ - 'type' => $type, - 'apiSummary' => true, - 'validation' => ['nullable'], - ]; - } + $schema = SchemaBuilder::get('endorsement'); + return true; + } - return false; + public function getInstallMigration(): Migration + { + return new addEndorsementsTable(); } + private function getEndorsementStatusSuffix($endorsementStatus): string { $mapStatusToSuffix = [ @@ -234,90 +214,78 @@ private function getEndorsementStatusSuffix($endorsementStatus): string return $mapStatusToSuffix[$endorsementStatus] ?? ""; } - public function addEndorserFieldsToWorkflow($hookName, $params) + public function addEndorsementFieldsToWorkflow($hookName, $params) { $smarty = &$params[1]; $output = &$params[2]; $submission = $smarty->getTemplateVars('submission'); $publication = $submission->getCurrentPublication(); - $request = Application::get()->getRequest(); - $handlerUrl = $request->getDispatcher()->url($request, Application::ROUTE_PAGE, null, self::HANDLER_PAGE); - - $endorsementStatus = $publication->getData('endorsementStatus'); - $endorsementStatusSuffix = $this->getEndorsementStatusSuffix($endorsementStatus); - $canEditEndorsement = (is_null($endorsementStatus) || $endorsementStatus == Endorsement::STATUS_NOT_CONFIRMED || $endorsementStatus == Endorsement::STATUS_DENIED); - $canSendEndorsementManually = $publication->getData('status') == STATUS_PUBLISHED - && !$this->userAccessingIsAuthor($submission) - && ($endorsementStatus == Endorsement::STATUS_CONFIRMED || $endorsementStatus == Endorsement::STATUS_COULDNT_COMPLETE); - $canRemoveEndorsement = !is_null($endorsementStatus) && !$this->userAccessingIsAuthor($submission); - - $smarty->assign([ - 'submissionId' => $submission->getId(), - 'endorserName' => $publication->getData('endorserName'), - 'endorserEmail' => $publication->getData('endorserEmail'), - 'endorserOrcid' => $publication->getData('endorserOrcid'), - 'endorserEmailCount' => $publication->getData('endorserEmailCount'), - 'endorsementStatus' => $endorsementStatus, - 'endorsementStatusSuffix' => $endorsementStatusSuffix, - 'canEditEndorsement' => $canEditEndorsement, - 'canRemoveEndorsement' => $canRemoveEndorsement, - 'canSendEndorsementManually' => $canSendEndorsementManually, - 'handlerUrl' => $handlerUrl, - ]); - $tabBadge = (is_null($endorsementStatus) ? 'badge="0"' : 'badge="1"'); + $countEndorsers = Repo::endorsement()->getCollector() + ->filterByContextIds([$request->getContext()->getId()]) + ->filterByPublicationIds([$publication->getId()]) + ->getCount(); + + $tabBadge = (empty($countEndorsers) ? 'badge="0"' : 'badge=' . $countEndorsers); $output .= sprintf( '%s', $tabBadge, __('plugins.generic.plauditPreEndorsement.preEndorsement'), - $smarty->fetch($this->getTemplateResource('endorserFieldWorkflow.tpl')) + $smarty->fetch($this->getTemplateResource('endorsementComponent.tpl')) ); } - public function sendEmailToEndorser($publication, $endorserChanged = false) + public function sendEmailToEndorser($publication, $endorsement, $endorsementChanged = false) { $request = Application::get()->getRequest(); $context = $request->getContext(); - $endorserName = $publication->getData('endorserName'); - $endorserEmail = $publication->getData('endorserEmail'); + $endorsementName = $endorsement->getName(); + $endorsementEmail = $endorsement->getEmail(); - if (!is_null($context) && !empty($endorserEmail)) { + if (!is_null($context) && !empty($endorsementEmail)) { $submission = Repo::submission()->get($publication->getData('submissionId')); $emailTemplate = Repo::emailTemplate()->getByKey( $context->getId(), 'ORCID_REQUEST_ENDORSER_AUTHORIZATION' ); - $endorserEmailToken = md5(microtime() . $endorserEmail); + $endorsementEmailToken = md5(microtime() . $endorsementEmail); $orcidClient = new OrcidClient($this, $context->getId()); - $oauthUrl = $orcidClient->buildOAuthUrl(['token' => $endorserEmailToken, 'state' => $publication->getId()]); + $oauthUrl = $orcidClient->buildOAuthUrl( + [ + 'token' => $endorsementEmailToken, + 'state' => $publication->getId(), + 'endorsementId' => $endorsement->getId() + ] + ); $emailParams = [ 'orcidOauthUrl' => $oauthUrl, - 'endorserName' => htmlspecialchars($endorserName), + 'endorserName' => htmlspecialchars($endorsementName), ]; $email = new OrcidRequestEndorserAuthorization($context, $submission, $emailParams); $email->from($context->getData('contactEmail'), $context->getData('contactName')); - $email->to([['name' => $endorserName, 'email' => $endorserEmail]]); + $email->to([['name' => $endorsementName, 'email' => $endorsementEmail]]); $email->subject($emailTemplate->getLocalizedData('subject')); $email->body($emailTemplate->getLocalizedData('body')); Mail::send($email); - if(is_null($publication->getData('endorserEmailCount')) || $endorserChanged) { - $endorserEmailCount = 0; + if (is_null($endorsement->getEmailCount()) || $endorsementChanged) { + $endorsementEmailCount = 0; } else { - $endorserEmailCount = $publication->getData('endorserEmailCount'); + $endorsementEmailCount = $endorsement->getEmailCount(); } - $publication->setData('endorserEmailToken', $endorserEmailToken); - $publication->setData('endorsementStatus', Endorsement::STATUS_NOT_CONFIRMED); - $publication->setData('endorserEmailCount', $endorserEmailCount + 1); - Repo::publication()->edit($publication, []); + $endorsement->setEmailToken($endorsementEmailToken); + $endorsement->setStatus(Endorsement::STATUS_NOT_CONFIRMED); + $endorsement->setEmailCount($endorsementEmailCount + 1); - $this->writeOnActivityLog($submission, 'plugins.generic.plauditPreEndorsement.log.sentEmailEndorser', ['endorserName' => $endorserName, 'endorserEmail' => $endorserEmail]); + Repo::endorsement()->edit($endorsement, []); + + $this->writeOnActivityLog($submission, 'plugins.generic.plauditPreEndorsement.log.sentEmailEndorser', ['endorserName' => $endorsementName, 'endorserEmail' => $endorsementEmail]); } } @@ -390,4 +358,14 @@ public function userAccessingIsAuthor($submission): bool return $currentUserAssignedRoles[0] == Role::ROLE_ID_AUTHOR; } + + public function setupGridHandler($hookName, $params) + { + $component = & $params[0]; + if ($component == 'plugins.generic.plauditPreEndorsement.controllers.grid.EndorsementGridHandler') { + define('PLAUDIT_PRE_ENDORSEMENT_PLUGIN_NAME', $this->getName()); + return true; + } + return false; + } } diff --git a/classes/Endorsement.php b/classes/Endorsement.php deleted file mode 100644 index bd4bb5a..0000000 --- a/classes/Endorsement.php +++ /dev/null @@ -1,13 +0,0 @@ -orcidClient = $orcidClient; } - public function sendEndorsement($publication, $needCheckMessageWasLoggedToday = false) + public function sendEndorsement($endorsement, $needCheckMessageWasLoggedToday = false) { + $publication = Repo::publication()->get($endorsement->getPublicationId()); $validationResult = $this->validateEndorsementSending($publication); - if($validationResult == 'ok') { - $this->sendEndorsementToPlaudit($publication); + if ($validationResult == 'ok') { + $this->sendEndorsementToPlaudit($endorsement, $publication); } else { $submissionId = $publication->getData('submissionId'); - if(!$needCheckMessageWasLoggedToday or !$this->messageWasAlreadyLoggedToday($submissionId, $validationResult)) { + if (!$needCheckMessageWasLoggedToday or !$this->messageWasAlreadyLoggedToday($submissionId, $validationResult)) { $submission = Repo::submission()->get($submissionId); $this->plugin->writeOnActivityLog($submission, $validationResult); } @@ -56,32 +57,39 @@ public function validateEndorsementSending($publication): string $doi = $publication->getDoi(); $secretKey = $this->plugin->getSetting($this->contextId, 'plauditAPISecret'); - if(empty($doi)) { + if (empty($doi)) { return 'plugins.generic.plauditPreEndorsement.log.failedEndorsementSending.emptyDoi'; } - if(!$this->crossrefClient->doiIsIndexed($doi)) { + if (!$this->crossrefClient->doiIsIndexed($doi)) { return 'plugins.generic.plauditPreEndorsement.log.failedEndorsementSending.doiNotIndexed'; } - if(empty($secretKey)) { + if (empty($secretKey)) { return 'plugins.generic.plauditPreEndorsement.log.failedEndorsementSending.secretKey'; } return 'ok'; } - public function sendEndorsementToPlaudit($publication) + public function sendEndorsementToPlaudit($endorsement, $publication) { $submission = Repo::submission()->get($publication->getData('submissionId')); - $this->plugin->writeOnActivityLog($submission, 'plugins.generic.plauditPreEndorsement.log.attemptSendingEndorsement', ['doi' => $publication->getDoi(), 'orcid' => $publication->getData('endorserOrcid')]); + $this->plugin->writeOnActivityLog( + $submission, + 'plugins.generic.plauditPreEndorsement.log.attemptSendingEndorsement', + [ + 'doi' => $publication->getDoi(), + 'orcid' => $endorsement->getOrcid() + ] + ); $plauditClient = new PlauditClient(); try { $secretKey = $this->plugin->getSetting($this->contextId, 'plauditAPISecret'); - $response = $plauditClient->requestEndorsementCreation($publication, $secretKey); - $newEndorsementStatus = $plauditClient->getEndorsementStatusByResponse($response, $publication); + $response = $plauditClient->requestEndorsementCreation($endorsement, $publication, $secretKey); + $newEndorsementStatus = $plauditClient->getEndorsementStatusByResponse($response, $publication, $endorsement); } catch (ClientException $exception) { $response = $exception->getResponse(); $responseCode = $response->getStatusCode(); @@ -91,22 +99,20 @@ public function sendEndorsementToPlaudit($publication) $newEndorsementStatus = Endorsement::STATUS_COULDNT_COMPLETE; } - Repo::publication()->edit($publication, [ - 'endorsementStatus' => $newEndorsementStatus - ]); + $endorsement->setStatus($newEndorsementStatus); + Repo::endorsement()->edit($endorsement, []); } - public function updateEndorserNameFromOrcid($publication, $orcid) + public function updateEndorsementNameFromOrcid($endorsement, $orcid) { $accessToken = $this->orcidClient->getReadPublicAccessToken(); $orcidRecord = $this->orcidClient->getOrcidRecord($orcid, $accessToken); $fullName = $this->orcidClient->getFullNameFromRecord($orcidRecord); - $publication->setData('endorserName', $fullName); - $publicationDao = Repo::publication()->dao; - $publicationDao->update($publication); + $endorsement->setName($fullName); - return $publication; + Repo::endorsement()->edit($endorsement, []); + return $endorsement; } public function messageWasAlreadyLoggedToday(int $submissionId, string $message): bool @@ -116,9 +122,9 @@ public function messageWasAlreadyLoggedToday(int $submissionId, string $message) ->getMany(); $today = Core::getCurrentDate(); - foreach($submissionLogEntries->toArray() as $logEntry) { + foreach ($submissionLogEntries->toArray() as $logEntry) { $entryWasLoggedToday = $this->datesAreOnSameDay($logEntry->getDateLogged(), $today); - if($entryWasLoggedToday and $logEntry->getMessage() == $message) { + if ($entryWasLoggedToday and $logEntry->getMessage() == $message) { return true; } } diff --git a/classes/PlauditClient.php b/classes/PlauditClient.php index a0b8f88..14a4b74 100644 --- a/classes/PlauditClient.php +++ b/classes/PlauditClient.php @@ -3,7 +3,7 @@ namespace APP\plugins\generic\plauditPreEndorsement\classes; use APP\core\Application; -use APP\plugins\generic\plauditPreEndorsement\classes\Endorsement; +use APP\plugins\generic\plauditPreEndorsement\classes\endorsement\Endorsement; class PlauditClient { @@ -15,12 +15,12 @@ public function filterOrcidNumbers(string $orcid): string return $matches[0]; } - public function requestEndorsementCreation($publication, $secretKey) + public function requestEndorsementCreation($endorsement, $publication, $secretKey) { $httpClient = Application::get()->getHttpClient(); $headers = ['Content-Type' => 'application/json']; - $orcid = $this->filterOrcidNumbers($publication->getData('endorserOrcid')); + $orcid = $this->filterOrcidNumbers($endorsement->getOrcid()); $postData = [ 'secret_key' => $secretKey, 'orcid' => $orcid, @@ -39,7 +39,7 @@ public function requestEndorsementCreation($publication, $secretKey) return $response; } - public function getEndorsementStatusByResponse($response, $publication) + public function getEndorsementStatusByResponse($response, $publication, $endorsement) { if ($response->getStatusCode() == 200) { $body = json_decode($response->getBody()->getContents(), true); @@ -48,9 +48,9 @@ public function getEndorsementStatusByResponse($response, $publication) $responseDoi = $endorsementData['doi']; $responseOrcid = $endorsementData['orcid']; $publicationDoi = strtolower($publication->getDoi()); - $publicationOrcid = $this->filterOrcidNumbers($publication->getData('endorserOrcid')); + $endorsementOrcid = $this->filterOrcidNumbers($endorsement->getOrcid()); - if ($responseDoi == $publicationDoi && $responseOrcid == $publicationOrcid) { + if ($responseDoi == $publicationDoi && $responseOrcid == $endorsementOrcid) { return Endorsement::STATUS_COMPLETED; } } diff --git a/classes/PlauditPreEndorsementDAO.php b/classes/PlauditPreEndorsementDAO.php deleted file mode 100644 index db47e31..0000000 --- a/classes/PlauditPreEndorsementDAO.php +++ /dev/null @@ -1,35 +0,0 @@ -leftJoin('publications as pub', 'sub.current_publication_id', '=', 'pub.publication_id') - ->leftJoin('publication_settings as ps', 'pub.publication_id', '=', 'ps.publication_id') - ->where('ps.setting_name', 'endorsementStatus') - ->where('ps.setting_value', Endorsement::STATUS_CONFIRMED) - ->where('sub.status', Submission::STATUS_PUBLISHED) - ->where('sub.context_id', $contextId) - ->select('pub.publication_id') - ->get(); - - $publications = []; - - foreach ($result as $row) { - $row = (array) $row; - $publications[] = Repo::publication()->get($row['publication_id']); - } - - return $publications; - - } -} diff --git a/classes/PlauditPreEndorsementHandler.php b/classes/PlauditPreEndorsementHandler.php index 8144fe3..c79dcb8 100644 --- a/classes/PlauditPreEndorsementHandler.php +++ b/classes/PlauditPreEndorsementHandler.php @@ -3,12 +3,12 @@ namespace APP\plugins\generic\plauditPreEndorsement\classes; use APP\handler\Handler; -use APP\facades\Repo; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; use APP\submission\Submission; use PKP\plugins\PluginRegistry; use APP\template\TemplateManager; use APP\plugins\generic\plauditPreEndorsement\PlauditPreEndorsementPlugin; -use APP\plugins\generic\plauditPreEndorsement\classes\Endorsement; +use APP\plugins\generic\plauditPreEndorsement\classes\endorsement\Endorsement; use APP\plugins\generic\plauditPreEndorsement\classes\EndorsementService; use APP\plugins\generic\plauditPreEndorsement\classes\OrcidClient; @@ -18,52 +18,12 @@ class PlauditPreEndorsementHandler extends Handler public const AUTH_INVALID_TOKEN = 'invalid_token'; public const AUTH_ACCESS_DENIED = 'access_denied'; - public function updateEndorser($args, $request) - { - $plugin = new PlauditPreEndorsementPlugin(); - $submissionId = $request->getUserVar('submissionId'); - $endorserName = $request->getUserVar('endorserName'); - $endorserEmail = $request->getUserVar('endorserEmail'); - - $submission = Repo::submission()->get($submissionId); - $publication = $submission->getCurrentPublication(); - - $endorsementIsConfirmed = $publication->getData('endorsementStatus') == Endorsement::STATUS_CONFIRMED; - if ($endorsementIsConfirmed) { - return http_response_code(400); - } - - if(!$plugin->inputIsEmail($endorserEmail)) { - http_response_code(400); - header('Content-Type: application/json'); - echo json_encode(['errorMessage' => __('plugins.generic.plauditPreEndorsement.endorsementEmailInvalid')]); - return; - } - - if($this->checkDataIsFromAnyAuthor($publication, 'email', $endorserEmail)) { - http_response_code(400); - header('Content-Type: application/json'); - echo json_encode(['errorMessage' => __('plugins.generic.plauditPreEndorsement.endorsementFromAuthor')]); - return; - } - - $endorserChanged = ($endorserEmail != $publication->getData('endorserEmail')); - - $publication->setData('endorserName', $endorserName); - $publication->setData('endorserEmail', $endorserEmail); - Repo::publication()->edit($publication, []); - - $plugin->sendEmailToEndorser($publication, $endorserChanged); - - return http_response_code(200); - } - private function checkDataIsFromAnyAuthor($publication, $dataName, $dataValue): bool { $authors = $publication->getData('authors'); - foreach($authors as $author) { - if($author->getData($dataName) == $dataValue) { + foreach ($authors as $author) { + if ($author->getData($dataName) == $dataValue) { return true; } } @@ -71,68 +31,21 @@ private function checkDataIsFromAnyAuthor($publication, $dataName, $dataValue): return false; } - public function removeEndorsement($args, $request) - { - $submissionId = $request->getUserVar('submissionId'); - $submission = Repo::submission()->get($submissionId); - $publication = $submission->getCurrentPublication(); - - $endorsementFields = [ - 'endorserName', - 'endorserEmail', - 'endorsementStatus', - 'endorserOrcid', - 'endorserEmailToken', - 'endorserEmailCount' - ]; - - foreach($endorsementFields as $field) { - $publication->unsetData($field); - } - - Repo::publication()->edit($publication, []); - - $plugin = PluginRegistry::getPlugin('generic', 'plauditpreendorsementplugin'); - $plugin->writeOnActivityLog($submission, 'plugins.generic.plauditPreEndorsement.log.endorsementRemoved'); - - return http_response_code(200); - } - - public function sendEndorsementManually($args, $request) - { - $submissionId = $request->getUserVar('submissionId'); - $submission = Repo::submission()->get($submissionId); - $publication = $submission->getCurrentPublication(); - $plugin = PluginRegistry::getPlugin('generic', 'plauditpreendorsementplugin'); - - $endorsementStatus = $publication->getData('endorsementStatus'); - $canSendEndorsementManually = $publication->getData('status') == Submission::STATUS_PUBLISHED - && !$plugin->userAccessingIsAuthor($submission) - && ($endorsementStatus == Endorsement::STATUS_CONFIRMED || $endorsementStatus == Endorsement::STATUS_COULDNT_COMPLETE); - - if($canSendEndorsementManually) { - $endorsementService = new EndorsementService($request->getContext()->getId(), $plugin); - $endorsementService->sendEndorsement($publication); - return http_response_code(200); - } - - return http_response_code(400); - } - public function orcidVerify($args, $request) { $publication = Repo::publication()->get($request->getUserVar('state')); $submission = Repo::submission()->get($publication->getData('submissionId')); + $endorsement = Repo::endorsement()->get($request->getUserVar('endorsementId')); $plugin = PluginRegistry::getPlugin('generic', 'plauditpreendorsementplugin'); $contextId = $request->getContext()->getId(); - $statusAuth = $this->getStatusAuthentication($publication, $request); + $statusAuth = $this->getStatusAuthentication($endorsement, $request); if ($statusAuth == self::AUTH_INVALID_TOKEN) { $this->logMessageAndDisplayTemplate($submission, $request, 'plugins.generic.plauditPreEndorsement.log.invalidToken', ['errorType' => 'invalidToken']); return; } elseif ($statusAuth == self::AUTH_ACCESS_DENIED) { - $this->setAccessDeniedEndorsement($publication); + $this->setAccessDeniedEndorsement($endorsement); $this->logMessageAndDisplayTemplate($submission, $request, 'plugins.generic.plauditPreEndorsement.log.orcidAccessDenied', ['errorType' => 'denied']); return; } @@ -151,18 +64,18 @@ public function orcidVerify($args, $request) $orcidUri = ($isSandBox ? OrcidClient::ORCID_URL_SANDBOX : OrcidClient::ORCID_URL) . $orcid; if (strlen($orcid) > 0) { - if($this->checkDataIsFromAnyAuthor($publication, 'orcid', $orcidUri)) { + if ($this->checkDataIsFromAnyAuthor($publication, 'orcid', $orcidUri)) { $this->logMessageAndDisplayTemplate($submission, $request, 'plugins.generic.plauditPreEndorsement.log.endorserOrcidFromAuthor', ['errorType' => 'orcidFromAuthor']); return; } $endorsementService = new EndorsementService($contextId, $plugin); - $endorsementService->updateEndorserNameFromOrcid($publication, $orcid); + $endorsementService->updateEndorsementNameFromOrcid($endorsement, $orcid); - $this->setConfirmedEndorsementPublication($publication, $orcidUri); + $this->setConfirmedEndorsementPublication($endorsement, $orcidUri); $this->logMessageAndDisplayTemplate($submission, $request, 'plugins.generic.plauditPreEndorsement.log.endorsementConfirmed', ['orcid' => $orcidUri]); - if($publication->getData('status') == Submission::STATUS_PUBLISHED) { + if ($publication->getData('status') == Submission::STATUS_PUBLISHED) { $endorsementService->sendEndorsement($publication); } } @@ -180,24 +93,24 @@ private function logMessageAndDisplayTemplate($submission, $request, string $mes $templateMgr->display($templatePath); } - private function setConfirmedEndorsementPublication($publication, $orcidUri) + private function setConfirmedEndorsementPublication($endorsement, $orcidUri) { - $publication->setData('endorserEmailToken', null); - $publication->setData('endorserOrcid', $orcidUri); - $publication->setData('endorsementStatus', Endorsement::STATUS_CONFIRMED); - Repo::publication()->edit($publication, []); + $endorsement->setEmailToken(null); + $endorsement->setOrcid($orcidUri); + $endorsement->setStatus(Endorsement::STATUS_CONFIRMED); + Repo::endorsement()->edit($endorsement, []); } - private function setAccessDeniedEndorsement($publication) + private function setAccessDeniedEndorsement($endorsement) { - $publication->setData('endorserEmailToken', null); - $publication->setData('endorsementStatus', Endorsement::STATUS_DENIED); - Repo::publication()->edit($publication, []); + $endorsement->setEmailToken(null); + $endorsement->setStatus(Endorsement::STATUS_DENIED); + Repo::endorsement()->edit($endorsement, []); } - public function getStatusAuthentication($publication, $request) + public function getStatusAuthentication($endorsement, $request) { - if ($request->getUserVar('token') != $publication->getData('endorserEmailToken')) { + if ($request->getUserVar('token') != $endorsement->getEmailToken()) { return self::AUTH_INVALID_TOKEN; } elseif ($request->getUserVar('error') == 'access_denied') { return self::AUTH_ACCESS_DENIED; diff --git a/classes/SchemaBuilder.php b/classes/SchemaBuilder.php new file mode 100644 index 0000000..8abd9db --- /dev/null +++ b/classes/SchemaBuilder.php @@ -0,0 +1,27 @@ +action = $action; - - $this->addField(new FieldText('endorserName', [ - 'label' => __('plugins.generic.plauditPreEndorsement.endorserName'), - 'value' => $publication->getData('endorserName'), - ])); - - $this->addField(new FieldText('endorserEmail', [ - 'label' => __('plugins.generic.plauditPreEndorsement.endorserEmail'), - 'value' => $publication->getData('endorserEmail'), - 'inputType' => 'email' - ])); - } -} diff --git a/classes/endorsement/Collector.php b/classes/endorsement/Collector.php new file mode 100644 index 0000000..431be7a --- /dev/null +++ b/classes/endorsement/Collector.php @@ -0,0 +1,74 @@ +dao = $dao; + } + + public function filterByContextIds(?array $contextIds): Collector + { + $this->contextIds = $contextIds; + return $this; + } + + public function filterByPublicationIds(?array $publicationIds): Collector + { + $this->publicationIds = $publicationIds; + return $this; + } + + public function filterByStatus(?array $status): Collector + { + $this->status = $status; + return $this; + } + + public function getQueryBuilder(): Builder + { + $queryBuilder = DB::table($this->dao->table . ' as endorsements') + ->select(['endorsements.*']); + + if (isset($this->contextIds)) { + $queryBuilder->whereIn('endorsements.context_id', $this->contextIds); + } + + if (isset($this->publicationIds)) { + $queryBuilder->whereIn('endorsements.publication_id', $this->publicationIds); + } + + if (isset($this->status)) { + $queryBuilder->whereIn('endorsements.status', $this->status); + } + + return $queryBuilder; + } + + public function getCount(): int + { + return $this->dao->getCount($this); + } + + public function getIds(): Collection + { + return $this->dao->getIds($this); + } + + public function getMany(): LazyCollection + { + return $this->dao->getMany($this); + } +} diff --git a/classes/endorsement/DAO.php b/classes/endorsement/DAO.php new file mode 100644 index 0000000..9479e6e --- /dev/null +++ b/classes/endorsement/DAO.php @@ -0,0 +1,89 @@ + 'endorsement_id', + 'contextId' => 'context_id', + 'publicationId' => 'publication_id', + 'name' => 'name', + 'email' => 'email', + 'status' => 'status', + 'orcid' => 'orcid', + 'emailToken' => 'email_token', + 'emailCount' => 'email_count' + ]; + + public function getParentColumn(): string + { + return 'context_id'; + } + + public function newDataObject(): Endorsement + { + return app(Endorsement::class); + } + + public function insert(Endorsement $endorsement): int + { + return parent::_insert($endorsement); + } + + public function delete(Endorsement $endorsement) + { + return parent::_delete($endorsement); + } + + public function update(Endorsement $endorsement) + { + return parent::_update($endorsement); + } + + public function getCount(Collector $query): int + { + return $query + ->getQueryBuilder() + ->count(); + } + + public function getByEmail(string $email, int $publicationId, int $contextId): ?Endorsement + { + $row = DB::table($this->table) + ->where('email', $email) + ->where('publication_id', $publicationId) + ->where('context_id', $contextId) + ->get('endorsement_id') + ->first(); + return $row ? $this->get($row->endorsement_id) : null; + } + + public function getMany(Collector $query): LazyCollection + { + $rows = $query + ->getQueryBuilder() + ->get(); + + return LazyCollection::make(function () use ($rows) { + foreach ($rows as $row) { + yield $row->endorsement_id => $this->fromRow($row); + } + }); + } + + public function fromRow(object $row): Endorsement + { + return parent::fromRow($row); + } +} diff --git a/classes/endorsement/Endorsement.php b/classes/endorsement/Endorsement.php new file mode 100644 index 0000000..86963e5 --- /dev/null +++ b/classes/endorsement/Endorsement.php @@ -0,0 +1,97 @@ +getData("id"); + } + + public function getContextId(): int + { + return $this->getData("contextId"); + } + + public function setContextId(int $contextId) + { + $this->setData("contextId", $contextId); + } + + public function getPublicationId(): int + { + return $this->getData("publicationId"); + } + + public function setPublicationId(int $publicationId) + { + $this->setData("publicationId", $publicationId); + } + + public function getName(): string + { + return $this->getData("name"); + } + + public function setName(string $name) + { + $this->setData("name", $name); + } + + public function getEmail(): string + { + return $this->getData("email"); + } + + public function setEmail(string $email) + { + $this->setData("email", $email); + } + + public function getStatus(): ?int + { + return $this->getData("status"); + } + + public function setStatus(int $status) + { + $this->setData("status", $status); + } + + public function getOrcid(): ?string + { + return $this->getData("orcid"); + } + + public function setOrcid(string $orcid) + { + $this->setData("orcid", $orcid); + } + + public function getEmailToken(): ?string + { + return $this->getData("emailToken"); + } + + public function setEmailToken(?string $emailToken) + { + $this->setData("emailToken", $emailToken); + } + + public function getEmailCount(): ?int + { + return $this->getData("emailCount"); + } + + public function setEmailCount(int $emailCount) + { + $this->setData("emailCount", $emailCount); + } +} diff --git a/classes/endorsement/Repository.php b/classes/endorsement/Repository.php new file mode 100644 index 0000000..194a819 --- /dev/null +++ b/classes/endorsement/Repository.php @@ -0,0 +1,63 @@ +dao = $dao; + } + + public function newDataObject(array $params = []): Endorsement + { + $object = $this->dao->newDataObject(); + if (!empty($params)) { + $object->setAllData($params); + } + return $object; + } + + public function get(int $id, int $contextId = null): ?Endorsement + { + return $this->dao->get($id, $contextId); + } + + public function add(Endorsement $endorsement): int + { + $id = $this->dao->insert($endorsement); + return $id; + } + + public function edit(Endorsement $endorsement, array $params) + { + $newEndorsement = clone $endorsement; + $newEndorsement->setAllData(array_merge($newEndorsement->_data, $params)); + + $this->dao->update($newEndorsement); + } + + public function delete(Endorsement $endorsement) + { + $this->dao->delete($endorsement); + } + + public function exists(int $id, int $contextId = null): bool + { + return $this->dao->exists($id, $contextId); + } + + public function getByEmail(string $email, int $publicationId, int $contextId): ?Endorsement + { + return $this->dao->getByEmail($email, $publicationId, $contextId); + } + + public function getCollector(): Collector + { + return app(Collector::class); + } +} diff --git a/classes/facades/Repo.php b/classes/facades/Repo.php new file mode 100644 index 0000000..9022479 --- /dev/null +++ b/classes/facades/Repo.php @@ -0,0 +1,13 @@ +bigInteger('endorsement_id')->autoIncrement(); + $table->bigInteger('context_id'); + $table->bigInteger('publication_id'); + $table->string('name'); + $table->string('email'); + $table->integer('status')->nullable(); + $table->string('orcid')->nullable(); + $table->string('email_token')->nullable(); + $table->integer('email_count')->default(0); + + $table->foreign('context_id') + ->references('server_id') + ->on('servers') + ->onDelete('cascade'); + $table->index(['context_id'], 'endorsers_context_id'); + + $table->foreign('publication_id') + ->references('publication_id') + ->on('publications') + ->onDelete('cascade'); + $table->index(['context_id'], 'endorsers_publication_id'); + + $table->unique(['context_id', 'publication_id', 'email'], 'endorsement_pkey'); + }); + } + + $upgradeMigration = new MoveLegacyEndorsementsToEndorsementsTable(); + $upgradeMigration->up(); + } + + public function down(): void + { + throw new DowngradeNotSupportedException(); + } +} diff --git a/classes/migration/upgrade/MoveLegacyEndorsementsToEndorsementsTable.php b/classes/migration/upgrade/MoveLegacyEndorsementsToEndorsementsTable.php new file mode 100644 index 0000000..53051aa --- /dev/null +++ b/classes/migration/upgrade/MoveLegacyEndorsementsToEndorsementsTable.php @@ -0,0 +1,82 @@ +getEndorsementsFromPublicationSettings(); + + if (!empty($endorsementSettings)) { + $legacyEndorsements = $this->getLegacyEndorsements($endorsementSettings); + $this->moveToEndorsementsTable($legacyEndorsements); + $this->deleteLegacyEndorsements(); + } + } + + public function down(): void + { + throw new DowngradeNotSupportedException(); + } + + private function getEndorsementsFromPublicationSettings() + { + return DB::table('publication_settings') + ->whereIn('setting_name', [ + 'endorserName', + 'endorserEmail', + 'endorsementStatus', + 'endorserOrcid', + 'endorserEmailToken', + 'endorserEmailCount' + ]) + ->get(); + } + + private function getLegacyEndorsements($endorsementSettings) + { + $legacyEndorsements = []; + foreach ($endorsementSettings as $endorsementSetting) { + $publicationId = $endorsementSetting->publication_id; + $legacyEndorsements[$publicationId][$endorsementSetting->setting_name] = $endorsementSetting->setting_value; + } + return $legacyEndorsements; + } + + private function moveToEndorsementsTable($legacyEndorsements) + { + foreach ($legacyEndorsements as $publicationId => $settings) { + $submissionId = DB::table('publications')->where('publication_id', $publicationId)->value('submission_id'); + $contextId = DB::table('submissions')->where('submission_id', $submissionId)->value('context_id'); + + DB::table('endorsements')->insert([ + 'context_id' => $contextId, + 'publication_id' => $publicationId, + 'name' => $settings['endorserName'] ?? '', + 'email' => $settings['endorserEmail'] ?? '', + 'status' => $settings['endorsementStatus'] ?? null, + 'orcid' => $settings['endorserOrcid'] ?? '', + 'email_token' => $settings['endorserEmailToken'] ?? '', + 'email_count' => $settings['endorserEmailCount'] ?? 0, + ]); + } + } + + private function deleteLegacyEndorsements() + { + DB::table('publication_settings') + ->whereIn('setting_name', [ + 'endorserName', + 'endorserEmail', + 'endorsementStatus', + 'endorserOrcid', + 'endorserEmailToken', + 'endorserEmailCount' + ])->delete(); + } +} diff --git a/classes/observers/listeners/SendEmailToEndorser.php b/classes/observers/listeners/SendEmailToEndorser.php index 6851e6a..2507c20 100644 --- a/classes/observers/listeners/SendEmailToEndorser.php +++ b/classes/observers/listeners/SendEmailToEndorser.php @@ -5,6 +5,7 @@ use Illuminate\Events\Dispatcher; use PKP\observers\events\SubmissionSubmitted; use PKP\plugins\PluginRegistry; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; class SendEmailToEndorser { @@ -19,10 +20,17 @@ public function subscribe(Dispatcher $events): void public function handle(SubmissionSubmitted $event): void { $publication = $event->submission->getCurrentPublication(); + $contextId = $event->context->getId(); + $endorsements = Repo::endorsement()->getCollector() + ->filterByContextIds([$contextId]) + ->filterByPublicationIds([$publication->getId()]) + ->getMany() + ->toArray(); - if (!empty($publication->getData('endorserEmail'))) { - $plugin = PluginRegistry::getPlugin('generic', 'plauditpreendorsementplugin'); - $plugin->sendEmailToEndorser($publication); + $plugin = PluginRegistry::getPlugin('generic', 'plauditpreendorsementplugin'); + + foreach ($endorsements as $endorsement) { + $plugin->sendEmailToEndorser($publication, $endorsement); } } } diff --git a/classes/tasks/SendReadyEndorsements.php b/classes/tasks/SendReadyEndorsements.php index 220f97d..cf4cfa5 100644 --- a/classes/tasks/SendReadyEndorsements.php +++ b/classes/tasks/SendReadyEndorsements.php @@ -5,8 +5,9 @@ use PKP\scheduledTask\ScheduledTask; use PKP\plugins\PluginRegistry; use APP\core\Application; -use APP\plugins\generic\plauditPreEndorsement\classes\PlauditPreEndorsementDAO; use APP\plugins\generic\plauditPreEndorsement\classes\EndorsementService; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; +use APP\plugins\generic\plauditPreEndorsement\classes\endorsement\Endorsement; class SendReadyEndorsements extends ScheduledTask { @@ -14,14 +15,16 @@ public function executeActions() { PluginRegistry::loadCategory('generic'); $plugin = PluginRegistry::getPlugin('generic', 'plauditpreendorsementplugin'); - $preEndorsementDao = new PlauditPreEndorsementDAO(); $context = Application::get()->getRequest()->getContext(); + $readyEndorsements = Repo::endorsement()->getCollector() + ->filterByContextIds([$context->getId()]) + ->filterByStatus([Endorsement::STATUS_CONFIRMED]) + ->getMany() + ->toArray(); - $readyPublications = $preEndorsementDao->getPublicationsWithEndorsementReadyToSend($context->getId()); - - foreach($readyPublications as $publication) { + foreach($readyEndorsements as $endorsement) { $endorsementService = new EndorsementService($context->getId(), $plugin); - $endorsementService->sendEndorsement($publication, true); + $endorsementService->sendEndorsement($endorsement, true); } return true; diff --git a/controllers/grid/EndorsementGridCellProvider.php b/controllers/grid/EndorsementGridCellProvider.php new file mode 100644 index 0000000..282865e --- /dev/null +++ b/controllers/grid/EndorsementGridCellProvider.php @@ -0,0 +1,51 @@ +getData(); + switch ($column->getId()) { + case 'endorserName': + return array('label' => $element->getName(), 'orcid' => $element->getOrcid()); + case 'endorserEmail': + return array('label' => $element->getEmail(), 'emailCount' => $element->getEmailCount()); + case 'endorsementStatus': + return array( + 'label' => $this->getEndorsementStatusSuffix($element->getStatus()), + 'badgeClass' => $this->getEndorsementStatusBadge($element->getStatus()) + ); + } + } + + private function getEndorsementStatusSuffix(?int $endorsementStatus): string + { + $mapStatusToSuffix = [ + Endorsement::STATUS_NOT_CONFIRMED => __('plugins.generic.plauditPreEndorsement.endorsementNotConfirmed'), + Endorsement::STATUS_CONFIRMED => __('plugins.generic.plauditPreEndorsement.endorsementConfirmed'), + Endorsement::STATUS_DENIED => __('plugins.generic.plauditPreEndorsement.endorsementDenied'), + Endorsement::STATUS_COMPLETED => __('plugins.generic.plauditPreEndorsement.endorsementCompleted'), + Endorsement::STATUS_COULDNT_COMPLETE => __('plugins.generic.plauditPreEndorsement.endorsementCouldntComplete') + ]; + + return $mapStatusToSuffix[$endorsementStatus] ?? ""; + } + + private function getEndorsementStatusBadge(?int $endorsementStatus): string + { + $mapStatusToSuffix = [ + Endorsement::STATUS_NOT_CONFIRMED => 'endorsementStatusCustomBadge', + Endorsement::STATUS_CONFIRMED => 'pkpBadge pkpBadge--isPrimary', + Endorsement::STATUS_DENIED => 'pkpBadge pkpBadge--isWarnable', + Endorsement::STATUS_COMPLETED => 'pkpBadge pkpBadge--isSuccess', + Endorsement::STATUS_COULDNT_COMPLETE => 'pkpBadge pkpBadge--isWarnable' + ]; + + return $mapStatusToSuffix[$endorsementStatus] ?? ""; + } +} diff --git a/controllers/grid/EndorsementGridHandler.php b/controllers/grid/EndorsementGridHandler.php new file mode 100644 index 0000000..9828f9e --- /dev/null +++ b/controllers/grid/EndorsementGridHandler.php @@ -0,0 +1,212 @@ +addRoleAssignment( + array(Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_ASSISTANT, Role::ROLE_ID_AUTHOR), + array('fetchGrid', 'fetchRow', 'addEndorsement', 'editEndorsement', 'updateEndorsement', 'deleteEndorsement', 'sendEndorsementManually') + ); + $this->plugin = PluginRegistry::getPlugin('generic', PLAUDIT_PRE_ENDORSEMENT_PLUGIN_NAME); + } + + public static function setPlugin($plugin) + { + $this->plugin = $plugin; + } + + public function getSubmission() + { + return $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION); + } + + public function authorize($request, &$args, $roleAssignments) + { + $this->addPolicy(new SubmissionAccessPolicy($request, $args, $roleAssignments)); + return parent::authorize($request, $args, $roleAssignments); + } + + public function initialize($request, $args = null) + { + parent::initialize($request, $args); + + $submission = $this->getSubmission(); + $submissionId = $submission->getId(); + $publication = $submission->getCurrentPublication(); + + $this->setTitle('plugins.generic.plauditPreEndorsement.endorsement'); + $this->setEmptyRowText('common.none'); + + $router = $request->getRouter(); + $this->addAction( + new LinkAction( + 'addEndorsement', + new AjaxModal( + $router->url($request, null, null, 'addEndorsement', null, ['submissionId' => $submissionId]), + __('common.add'), + 'modal_add_item' + ), + __('common.add'), + 'add_item' + ) + ); + + $cellProvider = new EndorsementGridCellProvider(); + $this->addColumn(new GridColumn( + 'endorserName', + 'plugins.generic.plauditPreEndorsement.endorserName', + null, + $this->plugin->getTemplateResource('gridCells/endorserName.tpl'), + $cellProvider, + ['maxLength' => 40] + )); + $this->addColumn(new GridColumn( + 'endorserEmail', + 'plugins.generic.plauditPreEndorsement.emailColumnName', + null, + $this->plugin->getTemplateResource('gridCells/endorserEmail.tpl'), + $cellProvider, + ['maxLength' => 40] + )); + + if (!$submission->getSubmissionProgress()) { + $this->addColumn(new GridColumn( + 'endorsementStatus', + 'plugins.generic.plauditPreEndorsement.endorsementStatus', + null, + $this->plugin->getTemplateResource('gridCells/endorsementStatus.tpl'), + $cellProvider + )); + } + } + + protected function loadData($request, $filter) + { + $submission = $this->getSubmission(); + $submissionId = $submission->getId(); + $publication = $submission->getCurrentPublication(); + $endorsements = Repo::endorsement()->getCollector() + ->filterByContextIds([$request->getContext()->getId()]) + ->filterByPublicationIds([$publication->getId()]) + ->getMany(); + return $endorsements->toArray(); + } + + public function addEndorsement($args, $request) + { + return $this->editEndorsement($args, $request); + } + + public function sendEndorsementManually($args, $request) + { + $contextId = $request->getContext()->getId(); + $rowId = $request->getUserVar('rowId'); + $endorsement = Repo::endorsement()->get((int)$rowId, $contextId); + $user = $request->getUser(); + + $endorsementService = new EndorsementService($contextId, $this->plugin); + $endorsementService->sendEndorsement($endorsement); + + $notificationManager = new NotificationManager(); + $notificationManager->createTrivialNotification( + $user->getId(), + NOTIFICATION_TYPE_SUCCESS, + array('contents' => __('plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditNotification')) + ); + + $json = new JSONMessage(true); + return $json->getString(); + } + + public function editEndorsement($args, $request) + { + $context = $request->getContext(); + $submission = $this->getSubmission(); + $submissionId = $submission->getId(); + $rowId = $args['rowId'] ?? null; + + $this->setupTemplate($request); + + $endorsementForm = new EndorsementForm($context->getId(), $submissionId, $request, $this->plugin, $rowId); + $endorsementForm->initData(); + $json = new JSONMessage(true, $endorsementForm->fetch($request)); + return $json->getString(); + } + + public function updateEndorsement($args, $request) + { + $context = $request->getContext(); + $submission = $this->getSubmission(); + $submissionId = $submission->getId(); + $rowId = $args['rowId'] ?? null; + + $this->setupTemplate($request); + + $endorsementForm = new EndorsementForm($context->getId(), $submissionId, $request, $this->plugin, $rowId); + $endorsementForm->readInputData(); + if ($endorsementForm->validate()) { + $endorsementForm->execute(); + $json = DAO::getDataChangedEvent($submissionId); + $json->setGlobalEvent('plugin:plauditPreEndorsement:endorsementAdded', ['submissionId' => $submissionId]); + return $json; + } else { + $json = new JSONMessage(true, $endorsementForm->fetch($request)); + return $json->getString(); + } + } + + public function deleteEndorsement($args, $request) + { + $context = $request->getContext(); + $submission = $this->getSubmission(); + $submissionId = $submission->getId(); + $rowId = $request->getUserVar('rowId'); + $endorsement = Repo::endorsement()->get((int)$rowId, $context->getId()); + Repo::endorsement()->delete($endorsement); + $this->plugin->writeOnActivityLog( + $submission, + 'plugins.generic.plauditPreEndorsement.log.endorsementRemoved', + ['endorserName' => $endorsement->getName(), 'endorserEmail' => $endorsement->getEmail()] + ); + $json = DAO::getDataChangedEvent($submissionId); + $json->setGlobalEvent('plugin:plauditPreEndorsement:endorsementRemoved', ['submissionId' => $submissionId]); + return $json; + } + + public function getJSHandler() + { + return '$.pkp.plugins.generic.plauditPreEndorsement.EndorsementGridHandler'; + } + + protected function getRowInstance() + { + return new EndorsementGridRow(); + } +} + +if (!PKP_STRICT_MODE) { + class_alias('\APP\plugins\generic\plauditPreEndorsement\controllers\grid\EndorsementGridHandler', '\EndorsementGridHandler'); +} diff --git a/controllers/grid/EndorsementGridRow.php b/controllers/grid/EndorsementGridRow.php new file mode 100644 index 0000000..ee2736a --- /dev/null +++ b/controllers/grid/EndorsementGridRow.php @@ -0,0 +1,113 @@ +plugin = PluginRegistry::getPlugin('generic', PLAUDIT_PRE_ENDORSEMENT_PLUGIN_NAME); + } + + public function initialize($request, $template = null) + { + parent::initialize($request, $template); + $submissionId = $request->getUserVar('submissionId'); + $submission = Repo::submission()->get($submissionId); + $publication = $submission->getCurrentPublication(); + + $router = $request->getRouter(); + + $element = & $this->getData(); + + $rowId = $this->getId(); + + $canSendEndorsementManually = $publication->getData('status') == Submission::STATUS_PUBLISHED + && !$this->plugin->userAccessingIsAuthor($submission) + && ( + $element->getStatus() == Endorsement::STATUS_CONFIRMED || + $element->getStatus() == Endorsement::STATUS_COULDNT_COMPLETE + ); + + if ($canSendEndorsementManually) { + $this->addAction( + new LinkAction( + 'sendEndorsementManually', + new RemoteActionConfirmationModal( + $request->getSession(), + __('plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditConfirmationMessage'), + __('plugins.generic.plauditPreEndorsement.sendEndorsementToPlaudit'), + $router->url( + $request, + null, + null, + 'sendEndorsementManually', + null, + array('submissionId' => $submissionId, 'rowId' => $rowId) + ), + 'modal_delete' + ), + __('plugins.generic.plauditPreEndorsement.sendEndorsementToPlaudit'), + 'sendToPlaudit' + ) + ); + } + + if ($element->getStatus() < Endorsement::STATUS_CONFIRMED) { + $this->addAction( + new LinkAction( + 'editEndorsementItem', + new AjaxModal( + $router->url( + $request, + null, + null, + 'editEndorsement', + null, + array('submissionId' => $submissionId, 'rowId' => $rowId) + ), + __('grid.action.edit'), + 'modal_edit', + true + ), + __('grid.action.edit'), + 'edit' + ) + ); + } + + $this->addAction( + new LinkAction( + 'delete', + new RemoteActionConfirmationModal( + $request->getSession(), + __('common.confirmDelete'), + __('grid.action.delete'), + $router->url( + $request, + null, + null, + 'deleteEndorsement', + null, + array('submissionId' => $submissionId, 'rowId' => $rowId) + ), + 'modal_delete' + ), + __('grid.action.delete'), + 'delete' + ) + ); + } +} diff --git a/controllers/grid/form/EndorsementForm.php b/controllers/grid/form/EndorsementForm.php new file mode 100644 index 0000000..46fa8a7 --- /dev/null +++ b/controllers/grid/form/EndorsementForm.php @@ -0,0 +1,88 @@ +contextId = $contextId; + $this->submissionId = $submissionId; + $this->request = $request ?? null; + $this->plugin = $plugin; + parent::__construct($plugin->getTemplateResource('addEndorsement.tpl')); + Validator::addValidations($this, $contextId, $submissionId, $rowId); + } + + public function initData() + { + $this->setData('submissionId', $this->submissionId); + } + + public function readInputData() + { + $this->readUserVars(array('endorserEmail', 'endorserName')); + } + + public function fetch($request, $template = null, $display = false) + { + $templateMgr = TemplateManager::getManager(); + $rowId = $this->request->getUserVar('rowId'); + if ($rowId) { + $endorsement = Repo::endorsement()->get($rowId, $this->contextId); + $templateMgr->assign('endorserName', $endorsement->getName()); + $templateMgr->assign('endorserEmail', $endorsement->getEmail()); + } + $templateMgr->assign('rowId', $rowId); + $templateMgr->assign('submissionId', $this->submissionId); + return parent::fetch($request); + } + + public function execute(...$functionArgs) + { + $rowId = $this->request->getUserVar('rowId'); + $submission = Repo::submission()->get($this->submissionId); + $publication = $submission->getCurrentPublication(); + + if ($rowId) { + $endorsement = Repo::endorsement()->get((int)$rowId, $this->contextId); + $params = [ + 'name' => $this->getData('endorserName'), + 'email' => $this->getData('endorserEmail') + ]; + + $endorserChanged = ($this->getData('endorserEmail') != $endorsement->getEmail()); + Repo::endorsement()->edit($endorsement, $params); + $newEndorsement = Repo::endorsement()->get((int)$rowId, $this->contextId); + if (!$submission->getSubmissionProgress()) { + $this->plugin->sendEmailToEndorser($publication, $newEndorsement, $endorserChanged); + } + } else { + $params = [ + 'contextId' => $this->contextId, + 'name' => $this->getData('endorserName'), + 'email' => $this->getData('endorserEmail'), + 'publicationId' => $publication->getId(), + ]; + $endorsement = Repo::endorsement()->newDataObject($params); + Repo::endorsement()->add($endorsement); + + if (!$submission->getSubmissionProgress()) { + $this->plugin->sendEmailToEndorser($publication, $endorsement); + } + } + } +} diff --git a/controllers/grid/form/Validator.php b/controllers/grid/form/Validator.php new file mode 100644 index 0000000..24ddb92 --- /dev/null +++ b/controllers/grid/form/Validator.php @@ -0,0 +1,46 @@ +addCheck(new FormValidatorPost($form)); + $form->addCheck(new FormValidatorCSRF($form)); + $form->addCheck(new \PKP\form\validation\FormValidator($form, 'endorserName', 'required', 'validator.required')); + $form->addCheck(new \PKP\form\validation\FormValidatorEmail($form, 'endorserEmail', 'required', 'plugins.generic.plauditPreEndorsement.endorsementEmailInvalid')); + $form->addCheck(new \PKP\form\validation\FormValidatorCustom($form, 'endorserEmail', 'required', 'user.register.form.emailExists', function ($endorserEmail) use ($contextId, $submissionId, $rowId) { + $submission = Repo::submission()->get($submissionId); + $publication = $submission->getCurrentPublication(); + $endorsement = Repo::endorsement()->getByEmail($endorserEmail, $publication->getId(), $contextId); + + if (is_null($endorsement)) { + return true; + } else { + if ($rowId) { + return $rowId == $endorsement->getId(); + } + return false; + } + })); + $form->addCheck(new \PKP\form\validation\FormValidatorCustom($form, 'endorserEmail', 'required', 'plugins.generic.plauditPreEndorsement.endorsementFromAuthor', function ($endorserEmail) use ($submissionId) { + $submission = Repo::submission()->get($submissionId); + $publication = $submission->getCurrentPublication(); + $authors = $publication->getData('authors'); + + foreach ($authors as $author) { + if ($author->getData('email') == $endorserEmail) { + return false; + } + } + return true; + })); + } +} diff --git a/cypress/tests/Test1_endorserFieldsSubmission.cy.js b/cypress/tests/Test1_endorserFieldsSubmission.cy.js index 17ddec2..0b77df0 100644 --- a/cypress/tests/Test1_endorserFieldsSubmission.cy.js +++ b/cypress/tests/Test1_endorserFieldsSubmission.cy.js @@ -20,12 +20,14 @@ describe("Plaudit Pre-Endorsement Plugin - Endorser fields in submission wizard" title: "Killers of the Flower Moon", abstract: 'A series of murders starts among native americans', }; - dummyPdf = { - 'file': 'dummy.pdf', - 'fileName': 'dummy.pdf', - 'mimeType': 'application/pdf', - 'genre': 'Preprint Text' - }; + dummyPdf = [ + { + 'file': 'dummy.pdf', + 'fileName': 'dummy.pdf', + 'mimeType': 'application/pdf', + 'genre': 'Preprint Text' + } + ]; endorsers = { invalidEmail: { name: 'John Wayne', @@ -35,10 +37,14 @@ describe("Plaudit Pre-Endorsement Plugin - Endorser fields in submission wizard" name: 'Catherine Kwantes', email: 'ckwantes@mailinator.com' }, - correct: { + firstEndorsement: { name: 'Bong Joon-ho', email: 'bong.joon-ho@email.kr' - } + }, + secondEndorsement: { + name: 'DummyEndorsement', + email: 'DummyEndorsement@mailinator.com' + }, }; }); @@ -47,44 +53,68 @@ describe("Plaudit Pre-Endorsement Plugin - Endorser fields in submission wizard" cy.get('div#myQueue a:contains("New Submission")').click(); beginSubmission(submissionData); - cy.setTinyMceContent('titleAbstract-abstract-control-en', submissionData.abstract); cy.contains('h2', 'Endorsement'); cy.contains('Do you have the endorsement of an experienced researcher in the field of knowledge of the manuscript?'); cy.contains('If yes, please provide the name and e-mail address of the endorsing researcher. Endorsements can significantly speed up the moderation process.'); cy.contains('The endorsement cannot be given by one of the authors of the manuscript.'); + cy.get('a[id^="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-addEndorsement-button-"]').contains("Add").click(); cy.get('input[name="endorserName"]').clear().type(endorsers.invalidEmail.name, {delay: 0}); cy.get('input[name="endorserEmail"]').clear().type(endorsers.invalidEmail.email, {delay: 0}); - cy.contains('button', 'Continue').click(); - - cy.addSubmissionGalleys([dummyPdf]); - cy.contains('button', 'Continue').click(); - cy.contains('button', 'Continue').click(); - cy.contains('button', 'Continue').click(); - + cy.get('form[id="endorsementForm"]').find('button[id^="submitFormButton-"]').click(); + cy.contains('Errors occurred processing this form'); cy.contains('Please enter a valid endorser e-mail'); }); + it("Validates endorser is not an author of the submission", function() { cy.login('ckwantes', null, 'publicknowledge'); cy.findSubmission('myQueue', submissionData.title); + cy.get('a[id^="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-addEndorsement-button-"]').contains("Add").click(); cy.get('input[name="endorserName"]').clear().type(endorsers.isAuthor.name, {delay: 0}); cy.get('input[name="endorserEmail"]').clear().type(endorsers.isAuthor.email, {delay: 0}); - cy.contains('button', 'Continue').click(); - cy.contains('button', 'Continue').click(); - cy.contains('button', 'Continue').click(); - cy.contains('button', 'Continue').click(); + cy.get('form[id="endorsementForm"]').find('button[id^="submitFormButton-"]').click(); cy.contains('The endorsement cannot be given by any of the authors of the manuscript'); }); - it("Finishes submission with correct endorsement", function() { + + it("Add correct endorsements", function() { + cy.login('ckwantes', null, 'publicknowledge'); + cy.findSubmission('myQueue', submissionData.title); + + cy.get('a[id^="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-addEndorsement-button-"]').contains("Add").click(); + cy.get('input[name="endorserName"]').clear().type(endorsers.firstEndorsement.name, {delay: 0}); + cy.get('input[name="endorserEmail"]').clear().type(endorsers.firstEndorsement.email, {delay: 0}); + cy.get('form[id="endorsementForm"]').find('button[id^="submitFormButton-"]').click(); + cy.get('a[id^="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-addEndorsement-button-"]').contains("Add").click(); + cy.get('input[name="endorserName"]').clear().type(endorsers.secondEndorsement.name, {delay: 0}); + cy.get('input[name="endorserEmail"]').clear().type(endorsers.secondEndorsement.email, {delay: 0}); + cy.get('form[id="endorsementForm"]').find('button[id^="submitFormButton-"]').click(); + }); + + it("Validates endorsement email exists", function() { + cy.login('ckwantes', null, 'publicknowledge'); + cy.findSubmission('myQueue', submissionData.title); + + cy.get('a[id^="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-addEndorsement-button-"]').contains("Add").click(); + cy.get('input[name="endorserName"]').clear().type(endorsers.firstEndorsement.name, {delay: 0}); + cy.get('input[name="endorserEmail"]').clear().type(endorsers.firstEndorsement.email, {delay: 0}); + cy.get('form[id="endorsementForm"]').find('button[id^="submitFormButton-"]').click(); + cy.contains('Errors occurred processing this form'); + cy.contains('The selected email address is already in use by another user.'); + }); + + it("Finishes submission with correct endorsements", function() { cy.login('ckwantes', null, 'publicknowledge'); cy.findSubmission('myQueue', submissionData.title); + cy.setTinyMceContent('titleAbstract-abstract-control-en', submissionData.abstract); - cy.get('input[name="endorserName"]').clear().type(endorsers.correct.name, {delay: 0}); - cy.get('input[name="endorserEmail"]').clear().type(endorsers.correct.email, {delay: 0}); cy.contains('button', 'Continue').click(); + cy.get('h2').contains('Upload Files'); + cy.get('h2').contains('Files'); + cy.addSubmissionGalleys(dummyPdf); + cy.contains('button', 'Continue').click(); cy.contains('button', 'Continue').click(); cy.contains('button', 'Continue').click(); @@ -96,4 +126,12 @@ describe("Plaudit Pre-Endorsement Plugin - Endorser fields in submission wizard" cy.waitJQuery(); cy.contains('h1', 'Submission complete'); }); + + it("Check endorsements emails", function() { + cy.visit('localhost:8025'); + cy.contains('Ramiro Vaca'); + cy.contains('DummyEndorsement@mailinator.com'); + cy.contains('bong.joon-ho@email.kr'); + cy.contains('Endorsement confirmation'); + }); }); \ No newline at end of file diff --git a/cypress/tests/Test2_workflowFeatures.cy.js b/cypress/tests/Test2_workflowFeatures.cy.js index 17bef10..9382580 100644 --- a/cypress/tests/Test2_workflowFeatures.cy.js +++ b/cypress/tests/Test2_workflowFeatures.cy.js @@ -9,56 +9,32 @@ describe("Plaudit Pre-Endorsement Plugin - Workflow features", function() { cy.get("#publication-button").click(); cy.contains("Pre-Endorsement").click(); - cy.get('input[name="endorserNameWorkflow"]').should('have.value', 'Bong Joon-ho'); - cy.get('input[name="endorserEmailWorkflow"]').should('have.value', 'bong.joon-ho@email.kr'); - cy.contains('The endorsement has not yet been confirmed by the endorser'); - cy.contains("1 endorsement confirmation e-mail has been sent to the endorser"); - cy.get('#plauditPreEndorsement-button .pkpBadge:contains("1")'); - }); - it("E-mail sendings counting in workflow tab", function() { - cy.login('ckwantes', null, 'publicknowledge'); - cy.findSubmission('myQueue', submissionTitle); - - cy.get("#publication-button").click(); - cy.contains("Pre-Endorsement").click(); - cy.contains("1 endorsement confirmation e-mail has been sent to the endorser"); - cy.get("#plauditPreEndorsement").contains("Save").click(); - - cy.reload(); - cy.contains("2 endorsement confirmation e-mails have been sent to the endorser"); - cy.get('input[name="endorserNameWorkflow"]').clear().type("Lady Diana", { delay: 0 }); - cy.get('input[name="endorserEmailWorkflow"]').clear().type("lady.diana@gmail.com", { delay: 0 }); - cy.get("#plauditPreEndorsement").contains("Save").click(); - - cy.reload(); - cy.contains("1 endorsement confirmation e-mail has been sent to the endorser"); + cy.contains('Bong Joon-ho'); + cy.contains('bong.joon-ho@email.kr'); + cy.contains('DummyEndorsement'); + cy.contains('DummyEndorsement@mailinator.com'); + cy.contains('Awaiting confirmation'); + cy.get('#plauditPreEndorsement-button .pkpBadge:contains("2")'); }); - it("Endorsement removal", function() { - cy.login('ckwantes', null, 'publicknowledge'); - cy.findSubmission('myQueue', submissionTitle); - - cy.get("#publication-button").click(); - cy.contains("Pre-Endorsement").click(); - cy.contains('button', 'Remove endorsement').should('not.exist'); - cy.logout(); + it("Endorsement removal", function() { cy.login('dbarnes', null, 'publicknowledge'); cy.findSubmission('active', submissionTitle); cy.get("#publication-button").click(); cy.contains("Pre-Endorsement").click(); - cy.contains('button', 'Remove endorsement').click(); - cy.on('window:confirm', () => true); + cy.get('[id*="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-row-"] > .first_column > .show_extras').first().click(); + cy.get('[id*="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-row-"][id*="-delete-button-"]').first().click(); + cy.get('.ok').click(); cy.reload(); - cy.get('input[name="endorserNameWorkflow"]').should('have.value', ''); - cy.get('input[name="endorserEmailWorkflow"]').should('have.value', ''); - cy.contains('The endorsement has not yet been confirmed by the endorser').should('not.exist'); - cy.contains("1 endorsement confirmation e-mail has been sent to the endorser").should('not.exist'); - cy.get('#plauditPreEndorsement-button .pkpBadge:contains("0")'); + cy.contains('Bong Joon-ho').should('not.exist'); + cy.contains("bong.joon-ho@email.kr").should('not.exist'); + cy.get('#plauditPreEndorsement-button .pkpBadge:contains("1")'); }); + it("Endorsement adding on workflow", function() { - let newEndorserName = 'Francis Ford Coppola'; - let newEndorserEmail = 'francis.coppola@hollywood.com'; + let newEndorsementName = 'Francis Ford Coppola'; + let newEndorsementEmail = 'francis.coppola@hollywood.com'; cy.login('ckwantes', null, 'publicknowledge'); cy.findSubmission('myQueue', submissionTitle); @@ -66,25 +42,33 @@ describe("Plaudit Pre-Endorsement Plugin - Workflow features", function() { cy.get("#publication-button").click(); cy.contains("Pre-Endorsement").click(); - cy.get('input[name="endorserNameWorkflow"]').clear().type(newEndorserName, { delay: 0 }); - cy.get('input[name="endorserEmailWorkflow"]').clear().type(newEndorserEmail, { delay: 0 }); - cy.get("#plauditPreEndorsement").contains("Save").click(); + cy.get('a[id^="component-plugins-generic-plauditpreendorsement-controllers-grid-endorsementgrid-addEndorsement-button-"]').contains("Add").click(); + cy.get('input[name="endorserName"]').clear().type(newEndorsementName, {delay: 0}); + cy.get('input[name="endorserEmail"]').clear().type(newEndorsementEmail, {delay: 0}); + cy.get('form[id="endorsementForm"]').find('button[id^="submitFormButton-"]').click(); cy.reload(); - cy.get('input[name="endorserNameWorkflow"]').should('have.value', newEndorserName); - cy.get('input[name="endorserEmailWorkflow"]').should('have.value', newEndorserEmail); - cy.contains('The endorsement has not yet been confirmed by the endorser'); - cy.contains("1 endorsement confirmation e-mail has been sent to the endorser"); - cy.get('#plauditPreEndorsement-button .pkpBadge:contains("1")'); + cy.contains(newEndorsementName); + cy.contains(newEndorsementEmail); + cy.get('#plauditPreEndorsement-button .pkpBadge:contains("2")'); }); + it("Endorsement actions are written in submission's Activity Log", function() { cy.login('dbarnes', null, 'publicknowledge'); cy.findSubmission('active', submissionTitle); cy.contains('button', 'Activity Log').click(); cy.contains('An endorsement confirmation e-mail has been sent to Bong Joon-ho (bong.joon-ho@email.kr)'); - cy.contains('An endorsement confirmation e-mail has been sent to Lady Diana (lady.diana@gmail.com)'); - cy.contains('The submission endorsement has been removed'); + cy.contains('A submission endorsement has been removed: Bong Joon-ho (bong.joon-ho@email.kr)'); cy.contains('An endorsement confirmation e-mail has been sent to Francis Ford Coppola (francis.coppola@hollywood.com)'); }); + + it("Check endorsements emails", function() { + cy.visit('localhost:8025'); + cy.contains('Ramiro Vaca'); + cy.contains('DummyEndorsement@mailinator.com'); + cy.contains('bong.joon-ho@email.kr'); + cy.contains('francis.coppola@hollywood.com'); + cy.contains('Endorsement confirmation'); + }); }); \ No newline at end of file diff --git a/js/EndorsementGridHandler.js b/js/EndorsementGridHandler.js new file mode 100644 index 0000000..87a9972 --- /dev/null +++ b/js/EndorsementGridHandler.js @@ -0,0 +1,59 @@ +(function($) { + + /** @type {Object} */ + $.pkp.plugins.generic.plauditPreEndorsement = + $.pkp.plugins.generic.plauditPreEndorsement || + { js: { } }; + + /** + * @constructor + * + * @extends $.pkp.controllers.grid.CategoryGridHandler + * + * @param {jQueryObject} $grid The grid this handler is + * attached to. + * @param {Object} options Grid handler configuration. + */ + $.pkp.plugins.generic.plauditPreEndorsement.EndorsementGridHandler = + function($grid, options) { + this.parent($grid, options); + }; + $.pkp.classes.Helper.inherits( + $.pkp.plugins.generic.plauditPreEndorsement.EndorsementGridHandler, + $.pkp.controllers.grid.GridHandler); + + // + // Public methods. + // + + /** + * Refresh the whole grid. + * + * @protected + * + * @param {HTMLElement} sourceElement The element that + * issued the event. + * @param {Event} event The triggering event. + * @param {number|Object=} opt_elementId The submissionId + * @param {Boolean=} opt_fetchedAlready Flag that subclasses can send + * telling that a fetch operation was already handled there. + */ + $.pkp.plugins.generic.plauditPreEndorsement.EndorsementGridHandler.prototype.refreshGridHandler = + function(sourceElement, event, opt_elementId, opt_fetchedAlready) { + var params; + + params = this.getFetchExtraParams(); + + // Check if subclasses already handled the fetch of new elements. + if (!opt_fetchedAlready) { + params.submissionId = opt_elementId; + $.get(this.fetchGridUrl_, params, + this.callbackWrapper(this.replaceGridResponseHandler_), 'json'); + } + + // Let the calling context (page?) know that the grids are being redrawn. + this.trigger('gridRefreshRequested'); + this.publishChangeEvents(); + }; + +}(jQuery)); \ No newline at end of file diff --git a/locale/en/locale.po b/locale/en/locale.po index f055c2a..9fb4980 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -32,20 +32,32 @@ msgstr "Endorser's email address" msgid "plugins.generic.plauditPreEndorsement.endorserOrcid" msgstr "Endorser's ORCID" +msgid "plugins.generic.plauditPreEndorsement.endorsementStatus" +msgstr "Status" + +msgid "plugins.generic.plauditPreEndorsement.emailColumnName" +msgstr "Endorser's email address / Emails count" + msgid "plugins.generic.plauditPreEndorsement.endorsementConfirmed" -msgstr "The endorsement has been confirmed by the endorser" +msgstr "Confirmed by endorser" msgid "plugins.generic.plauditPreEndorsement.endorsementNotConfirmed" -msgstr "The endorsement has not yet been confirmed by the endorser" +msgstr "Awaiting confirmation" msgid "plugins.generic.plauditPreEndorsement.endorsementDenied" -msgstr "The endorser denied access to his/her ORCID registry" +msgstr "Access to ORCID denied" msgid "plugins.generic.plauditPreEndorsement.endorsementCouldntComplete" -msgstr "The submission of the endorsement to Plaudit could not be completed" +msgstr "Error sending to Plaudit" msgid "plugins.generic.plauditPreEndorsement.endorsementCompleted" -msgstr "The endorsement was successfully sent to Plaudit" +msgstr "Sent to Plaudit" + +msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditNotification" +msgstr "Attempt to send endorsement to Plaudit completed. You can check the endorser's status or view the Activity Log for more details." + +msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditConfirmationMessage" +msgstr "Do you want to send the endorsement of this submission to Plaudit?" msgid "plugins.generic.plauditPreEndorsement.endorserEmailCount.one" msgstr "1 endorsement confirmation e-mail has been sent to the endorser" @@ -178,4 +190,4 @@ msgid "plugins.generic.plauditPreEndorsement.log.attemptSendingEndorsement" msgstr "Attempt to send endorsement to Plaudit with DOI {$doi} and ORCID {$orcid}" msgid "plugins.generic.plauditPreEndorsement.log.endorsementRemoved" -msgstr "The submission endorsement has been removed" \ No newline at end of file +msgstr "A submission endorsement has been removed: {$endorserName} ({$endorserEmail})" \ No newline at end of file diff --git a/locale/es/locale.po b/locale/es/locale.po index 5bd9f1a..334f360 100644 --- a/locale/es/locale.po +++ b/locale/es/locale.po @@ -32,20 +32,26 @@ msgstr "Dirección de correo electrónico del/a endosador/a" msgid "plugins.generic.plauditPreEndorsement.endorserOrcid" msgstr "ORCID del/a endosador/a" +msgid "plugins.generic.plauditPreEndorsement.endorsementStatus" +msgstr "Estado" + +msgid "plugins.generic.plauditPreEndorsement.emailColumnName" +msgstr "Dirección de correo electrónico del endosante / Conteo de correos" + msgid "plugins.generic.plauditPreEndorsement.endorsementConfirmed" -msgstr "El endoso fue confirmado por el/la endosador/a" +msgstr "Confirmado por el endosante" msgid "plugins.generic.plauditPreEndorsement.endorsementNotConfirmed" -msgstr "El endoso aún no fue confirmado por el/la endosador/a" +msgstr "Esperando confirmación" msgid "plugins.generic.plauditPreEndorsement.endorsementDenied" -msgstr "El/La endosador/a ha denegado el acceso a su registro ORCID" +msgstr "Acceso a ORCID denegado" msgid "plugins.generic.plauditPreEndorsement.endorsementCouldntComplete" -msgstr "No se pudo completar el envío del endoso a Plaudit" +msgstr "Error al enviar a Plaudit" msgid "plugins.generic.plauditPreEndorsement.endorsementCompleted" -msgstr "El endoso se ha enviado correctamente a Plaudit" +msgstr "Enviado a Plaudit" msgid "plugins.generic.plauditPreEndorsement.endorserEmailCount.one" msgstr "Se ha enviado 1 correo electrónico de confirmación de endoso al endosante" @@ -59,6 +65,12 @@ msgstr "Aprobación previa" msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlaudit" msgstr "Enviar endoso a Plaudit" +msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditNotification" +msgstr "Intento de envío del endoso a Plaudit completado. Puede verificar el estado del endosador o ver el Registro de actividad para más detalles." + +msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditConfirmationMessage" +msgstr "¿Desea enviar el endoso de este envío a Plaudit?" + msgid "plugins.generic.plauditPreEndorsement.removeEndorsement" msgstr "Eliminar endoso" @@ -143,7 +155,6 @@ msgstr "El ORCID proporcionado para el endosante no debe ser el mismo que el de msgid "plugins.generic.plauditPreEndorsement.failure.contact" msgstr "Comuníquese con el administrador del servidor con su nombre, ORCID ID y detalles de envío" - msgid "plugins.generic.plauditPreEndorsement.log.sentEmailEndorser" msgstr "Se ha enviado un correo electrónico de confirmación de endoso a {$endorserName} ({$endorserEmail})" @@ -178,4 +189,4 @@ msgid "plugins.generic.plauditPreEndorsement.log.attemptSendingEndorsement" msgstr "Intento de enviar endoso a Plaudit con DOI {$doi} y ORCID {$orcid}" msgid "plugins.generic.plauditPreEndorsement.log.endorsementRemoved" -msgstr "Se ha eliminado lo endoso del envío" +msgstr "Se ha eliminado un endoso del envío: {$endorserName} ({$endorserEmail})" \ No newline at end of file diff --git a/locale/pt_BR/locale.po b/locale/pt_BR/locale.po index 537fce2..3abe3cd 100644 --- a/locale/pt_BR/locale.po +++ b/locale/pt_BR/locale.po @@ -32,20 +32,26 @@ msgstr "Endereço de e-mail do(a) endossador(a)" msgid "plugins.generic.plauditPreEndorsement.endorserOrcid" msgstr "ORCID do(a) endossador(a)" +msgid "plugins.generic.plauditPreEndorsement.endorsementStatus" +msgstr "Situação" + +msgid "plugins.generic.plauditPreEndorsement.emailColumnName" +msgstr "Endereço de email do endossador / Contagem de emails" + msgid "plugins.generic.plauditPreEndorsement.endorsementConfirmed" -msgstr "O endosso foi confirmado pelo(a) endossador(a)" +msgstr "Confirmado pelo(a) endossador(a)" msgid "plugins.generic.plauditPreEndorsement.endorsementNotConfirmed" -msgstr "O endosso ainda não foi confirmado pelo(a) endossador(a)" +msgstr "Aguardando confirmação" msgid "plugins.generic.plauditPreEndorsement.endorsementDenied" -msgstr "O(A) endossador(a) negou acesso ao seu registro ORCID" +msgstr "Acesso ao ORCID negado" msgid "plugins.generic.plauditPreEndorsement.endorsementCouldntComplete" -msgstr "O envio do endosso à Plaudit não pôde ser completado" +msgstr "Erro no envio à Plaudit" msgid "plugins.generic.plauditPreEndorsement.endorsementCompleted" -msgstr "O endosso foi enviado com sucesso à Plaudit" +msgstr "Enviado à Plaudit" msgid "plugins.generic.plauditPreEndorsement.endorserEmailCount.one" msgstr "Foi enviado 1 e-mail de confirmação do endosso para o(a) endossador(a)" @@ -56,6 +62,12 @@ msgstr "Foram enviados {$numEmails} e-mails de confirmação do endosso para o(a msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlaudit" msgstr "Enviar endosso à Plaudit" +msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditConfirmationMessage" +msgstr "Deseja enviar o endosso desta submissão à Plaudit?" + +msgid "plugins.generic.plauditPreEndorsement.sendEndorsementToPlauditNotification" +msgstr "Tentativa de envio do endosso à Plaudit concluída. Você pode checar o status do endossador ou verificar o Histórico de Atividades para mais detalhes." + msgid "plugins.generic.plauditPreEndorsement.removeEndorsement" msgstr "Remover endosso" @@ -143,7 +155,6 @@ msgstr "O ORCID informado para o(a) endossador(a) não pode ser o mesmo de um do msgid "plugins.generic.plauditPreEndorsement.failure.contact" msgstr "Por favor, entre em contato com o administrador do servidor, informando seu nome, ORCID ID e detalhes da submissão" - msgid "plugins.generic.plauditPreEndorsement.log.sentEmailEndorser" msgstr "Foi enviado um e-mail de confirmação de endosso para {$endorserName} ({$endorserEmail})" @@ -178,4 +189,4 @@ msgid "plugins.generic.plauditPreEndorsement.log.attemptSendingEndorsement" msgstr "Tentativa de envio do endosso à Plaudit com DOI {$doi} e ORCID {$orcid}" msgid "plugins.generic.plauditPreEndorsement.log.endorsementRemoved" -msgstr "O endosso da submissão foi removido" \ No newline at end of file +msgstr "Um endosso de submissão foi removido: {$endorserName} ({$endorserEmail})" \ No newline at end of file diff --git a/schemas/endorsement.json b/schemas/endorsement.json new file mode 100644 index 0000000..06d3ae2 --- /dev/null +++ b/schemas/endorsement.json @@ -0,0 +1,34 @@ +{ + "title": "Endorsement", + "description": "An endorsement of a publication to be sent to Plaudit.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "contextId": { + "type": "integer" + }, + "publicationId": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "orcid": { + "type": "string" + }, + "emailToken": { + "type": "string" + }, + "emailCount": { + "type": "integer" + } + } +} diff --git a/styles/endorserWorkflowStyleSheet.css b/styles/endorserWorkflowStyleSheet.css index a68af07..cf85f3e 100644 --- a/styles/endorserWorkflowStyleSheet.css +++ b/styles/endorserWorkflowStyleSheet.css @@ -1,4 +1,4 @@ -#updateEndorserForm { +#updateEndorsementForm { padding: 0.875rem 1rem; margin: 0 -2rem; } @@ -43,4 +43,22 @@ .formButtons { text-align: right; margin-top: 2rem; +} + +.endorsementStatusCustomBadge { + display: inline-block; + padding: .25em 1em; + font-size: .75rem; + font-weight: 400; + line-height: 1.5em; + border: 1px solid #ddd; + border-radius: 1.2em; + color: #fff; + background-color: #e08914; +} + +#plauditPreEndorsement-button { + span { + text-align: center; + } } \ No newline at end of file diff --git a/templates/addEndorsement.tpl b/templates/addEndorsement.tpl new file mode 100644 index 0000000..2726059 --- /dev/null +++ b/templates/addEndorsement.tpl @@ -0,0 +1,27 @@ + + +
+ + {capture assign=actionUrl}{url router=$smarty.const.ROUTE_COMPONENT component="plugins.generic.plauditPreEndorsement.controllers.grid.EndorsementGridHandler" op="updateEndorsement" submissionId=$submissionId escape=false}{/capture} +
+ {csrf} + {include file="controllers/notification/inPlaceNotification.tpl" notificationId="OASwitchboardSettingsFormNotification"} + + {fbvFormArea id="endorsementForm"} + {fbvFormSection label="plugins.generic.plauditPreEndorsement.endorserName" required=true} + {fbvElement type="text" id="endorserName" value=$endorserName|escape size=$fbvStyles.size.MEDIUM} + {/fbvFormSection} + + {fbvFormSection label="plugins.generic.plauditPreEndorsement.endorserEmail" required=true} + {fbvElement type="text" id="endorserEmail" value=$endorserEmail|escape size=$fbvStyles.size.MEDIUM} + {/fbvFormSection} + + {fbvFormButtons submitText="common.save"} + + {/fbvFormArea} +
+
\ No newline at end of file diff --git a/templates/endorsementComponent.tpl b/templates/endorsementComponent.tpl new file mode 100644 index 0000000..eadc9fe --- /dev/null +++ b/templates/endorsementComponent.tpl @@ -0,0 +1,8 @@ + + + + +
+ {capture assign=endorsersGridUrl}{url router=$smarty.const.ROUTE_COMPONENT component="plugins.generic.plauditPreEndorsement.controllers.grid.EndorsementGridHandler" op="fetchGrid" submissionId=$submission->getId() escape=false}{/capture} + {load_url_in_div id="endorsersGridContainer"|uniqid url=$endorsersGridUrl inVueEl=true} +
diff --git a/templates/endorserFieldWorkflow.tpl b/templates/endorserFieldWorkflow.tpl deleted file mode 100644 index c588399..0000000 --- a/templates/endorserFieldWorkflow.tpl +++ /dev/null @@ -1,138 +0,0 @@ -{* - * Copyright (c) 2022 - 2024 SciELO - * Copyright (c) 2022 - 2024 Lepidus Tecnologia - * Distributed under the GNU GPL v3. For full terms see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt - * - *} - - - -
-
- - {if $canEditEndorsement} - {fbvElement type="text" name="endorserNameWorkflow" id="endorserNameWorkflow" value=$endorserName|escape maxlength="90" size=$fbvStyles.size.MEDIUM} - {else} - {$endorserName|escape} - {/if} -
- -
- - {if $canEditEndorsement} - {fbvElement type="email" name="endorserEmailWorkflow" id="endorserEmailWorkflow" value=$endorserEmail|escape maxlength="90" size=$fbvStyles.size.MEDIUM} - {else} - {$endorserEmail|escape} - {/if} -
- - {if $endorserOrcid} -
- - {$endorserOrcid|escape} -
- {/if} - - {if !is_null($endorsementStatus)} - -
{translate key="plugins.generic.plauditPreEndorsement.endorsement{$endorsementStatusSuffix}"}
-
- {/if} - - {if isset($endorserEmailCount)} - - {if $endorserEmailCount == 1} -
{translate key="plugins.generic.plauditPreEndorsement.endorserEmailCount.one"}
- {else} -
{translate key="plugins.generic.plauditPreEndorsement.endorserEmailCount.many" numEmails=$endorserEmailCount}
- {/if} -
- {/if} - -
- {if $canRemoveEndorsement} - - {/if} - - {if $canEditEndorsement} - - {/if} - - {if $canSendEndorsementManually} - - {/if} -
-
- -{if $canEditEndorsement} - -{/if} - -{if $canSendEndorsementManually} - -{/if} - -{if $canRemoveEndorsement} - -{/if} \ No newline at end of file diff --git a/templates/gridCells/endorsementStatus.tpl b/templates/gridCells/endorsementStatus.tpl new file mode 100644 index 0000000..81fab30 --- /dev/null +++ b/templates/gridCells/endorsementStatus.tpl @@ -0,0 +1,17 @@ +{** + * templates/controllers/grid/gridCell.tpl + * + * Copyright (c) 2014-2021 Simon Fraser University + * Copyright (c) 2000-2021 John Willinsky + * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. + * + * a regular grid cell (with or without actions) + *} +{if $id} + {assign var=cellId value="cell-"|concat:$id} +{else} + {assign var=cellId value=""} +{/if} + + {include file="controllers/grid/gridCellContents.tpl"} + diff --git a/templates/gridCells/endorserEmail.tpl b/templates/gridCells/endorserEmail.tpl new file mode 100644 index 0000000..c58755f --- /dev/null +++ b/templates/gridCells/endorserEmail.tpl @@ -0,0 +1,20 @@ +{** + * templates/controllers/grid/gridCell.tpl + * + * Copyright (c) 2014-2021 Simon Fraser University + * Copyright (c) 2000-2021 John Willinsky + * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. + * + * a regular grid cell (with or without actions) + *} + {if $id} + {assign var=cellId value="cell-"|concat:$id} +{else} + {assign var=cellId value=""} +{/if} + + {include file="controllers/grid/gridCellContents.tpl"} + + + {$emailCount} + diff --git a/templates/gridCells/endorserName.tpl b/templates/gridCells/endorserName.tpl new file mode 100644 index 0000000..23b45a7 --- /dev/null +++ b/templates/gridCells/endorserName.tpl @@ -0,0 +1,25 @@ +{** + * templates/controllers/grid/gridCell.tpl + * + * Copyright (c) 2014-2021 Simon Fraser University + * Copyright (c) 2000-2021 John Willinsky + * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. + * + * a regular grid cell (with or without actions) + *} +{if $id} + {assign var=cellId value="cell-"|concat:$id} +{else} + {assign var=cellId value=""} +{/if} + + {include file="controllers/grid/gridCellContents.tpl"} + + +{if $orcid} + + + ORCID iD + + +{/if} diff --git a/tests/EndorsementServiceTest.php b/tests/EndorsementServiceTest.php index d7b97ff..ad0e309 100644 --- a/tests/EndorsementServiceTest.php +++ b/tests/EndorsementServiceTest.php @@ -5,15 +5,18 @@ use APP\publication\Publication; use PKP\doi\Doi; use APP\core\Application; -use APP\facades\Repo; use PKP\core\Core; use APP\plugins\generic\plauditPreEndorsement\classes\CrossrefClient; use APP\plugins\generic\plauditPreEndorsement\classes\OrcidClient; use APP\plugins\generic\plauditPreEndorsement\classes\EndorsementService; use APP\plugins\generic\plauditPreEndorsement\PlauditPreEndorsementPlugin; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; +use APP\plugins\generic\plauditPreEndorsement\tests\helpers\TestHelperTrait; final class EndorsementServiceTest extends DatabaseTestCase { + use TestHelperTrait; + private $endorsementService; private $contextId = 1; private $submissionId; @@ -21,17 +24,20 @@ final class EndorsementServiceTest extends DatabaseTestCase private $plugin; private $doi = '10.1234/TestePublication.1234'; private $secretKey = 'a1b2c3d4-e5f6g7h8'; - private $endorserName = 'Caio Anjo'; - private $endorserOrcid = '0010-1010-1101-0001'; - private $endorserGivenNameOrcid = 'Caio'; - private $endorserFamilyNameOrcid = 'dos Anjos'; + private $endorsementName = 'Caio Anjo'; + private $endorsementOrcid = '0010-1010-1101-0001'; + private $endorsementGivenNameOrcid = 'Caio'; + private $endorsementFamilyNameOrcid = 'dos Anjos'; + private $endorsementId; public function setUp(): void { parent::setUp(); - $this->publication = $this->createEndorsedPublication(); + $this->addSchemaFile('endorsement'); + $this->publication = $this->createPublication(); $this->plugin = new PlauditPreEndorsementPlugin(); $this->endorsementService = new EndorsementService($this->contextId, $this->plugin); + $this->endorsementId = $this->createEndorsement(); } public function tearDown(): void @@ -46,10 +52,22 @@ public function tearDown(): void protected function getAffectedTables(): array { - return ['event_log', 'event_log_settings']; + return ['event_log', 'event_log_settings', 'endorsements']; + } + + private function createEndorsement() + { + $params = [ + 'publicationId' => $this->publication->getId(), + 'contextId' => $this->contextId, + 'name' => 'Dummy', + 'email' => 'dummy@mailinator.com.br' + ]; + $endorsement = Repo::endorsement()->newDataObject($params); + return Repo::endorsement()->add($endorsement); } - private function createEndorsedPublication(): Publication + private function createPublication(): Publication { $context = DAORegistry::getDAO('ServerDAO')->getById($this->contextId); @@ -57,7 +75,6 @@ private function createEndorsedPublication(): Publication $submission->setData('contextId', $this->contextId); $publication = new Publication(); - $publication->setData('endorserName', $this->endorserName); $this->submissionId = Repo::submission()->add($submission, $publication, $context); @@ -91,15 +108,15 @@ private function getMockOrcidClient() 'value' => 1666816304613 ], 'given-names' => [ - 'value' => $this->endorserGivenNameOrcid + 'value' => $this->endorsementGivenNameOrcid ], 'family-name' => [ - 'value' => $this->endorserFamilyNameOrcid + 'value' => $this->endorsementFamilyNameOrcid ], 'credit-name' => '', 'source' => '', 'visibility' => 'public', - 'path' => $this->endorserOrcid + 'path' => $this->endorsementOrcid ] ] ]; @@ -107,10 +124,10 @@ private function getMockOrcidClient() $mockOrcidClient = $this->createMock(OrcidClient::class); $mockOrcidClient->method('getReadPublicAccessToken')->willReturn($fictionalAccessToken); $mockOrcidClient->method('getOrcidRecord')->willReturnMap([ - [$this->endorserOrcid, $fictionalAccessToken, $testRecord] + [$this->endorsementOrcid, $fictionalAccessToken, $testRecord] ]); $mockOrcidClient->method('getFullNameFromRecord')->willReturnMap([ - [$testRecord, $this->endorserGivenNameOrcid . ' ' . $this->endorserFamilyNameOrcid] + [$testRecord, $this->endorsementGivenNameOrcid . ' ' . $this->endorsementFamilyNameOrcid] ]); return $mockOrcidClient; @@ -150,15 +167,16 @@ public function testValidateEndorsementSending(): void $this->assertEquals('ok', $validateResult); } - public function testUpdateEndorserName(): void + public function testUpdateEndorsementName(): void { + $endorsement = Repo::endorsement()->get($this->endorsementId); $mockOrcidClient = $this->getMockOrcidClient(); $this->endorsementService->setOrcidClient($mockOrcidClient); - $this->publication = $this->endorsementService->updateEndorserNameFromOrcid($this->publication, $this->endorserOrcid); - $expectedNewName = $this->endorserGivenNameOrcid . ' ' . $this->endorserFamilyNameOrcid; + $newEndorsement = $this->endorsementService->updateEndorsementNameFromOrcid($endorsement, $this->endorsementOrcid); + $expectedNewName = $this->endorsementGivenNameOrcid . ' ' . $this->endorsementFamilyNameOrcid; - $this->assertEquals($expectedNewName, $this->publication->getData('endorserName')); + $this->assertEquals($expectedNewName, $newEndorsement->getName()); } public function testMessageWasAlreadyLoggedToday(): void diff --git a/tests/PlauditClientTest.php b/tests/PlauditClientTest.php index 11ec831..f4ba40f 100644 --- a/tests/PlauditClientTest.php +++ b/tests/PlauditClientTest.php @@ -4,9 +4,10 @@ use APP\publication\Publication; use PKP\doi\Doi; -use APP\plugins\generic\plauditPreEndorsement\classes\Endorsement; +use APP\plugins\generic\plauditPreEndorsement\classes\endorsement\Endorsement; use APP\plugins\generic\plauditPreEndorsement\classes\PlauditClient; use APP\plugins\generic\plauditPreEndorsement\tests\TestResponse; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; use PHPUnit\Framework\TestCase; class PlauditClientTest extends TestCase @@ -16,6 +17,7 @@ class PlauditClientTest extends TestCase private $doi = '10.1590/LepidusPreprints.1535'; private $orcid = '0000-0001-5542-5100'; private $orcidX = '0000-0001-5542-510X'; + private $endorsement; public function setUp(): void { @@ -25,11 +27,22 @@ public function setUp(): void $doiObject->setData('doi', $this->doi); $this->publication = new Publication(); $this->publication->setData('doiObject', $doiObject); - $this->publication->setData('endorserOrcid', $this->orcid); + $this->endorsement = $this->createEndorsement(); $this->plauditClient = new PlauditClient(); } + private function createEndorsement() + { + $params = [ + 'name' => 'Dummy', + 'email' => 'dummy@mailinator.com.br', + 'orcid' => $this->orcid + ]; + $endorsement = Repo::endorsement()->newDataObject($params); + return $endorsement; + } + public function testFilterOrcidNumbers(): void { $orcidUrlPrefix = 'https://orcid.org/'; @@ -48,7 +61,7 @@ public function testEndorsementStatusWhenRequestSucceed(): void $bodyJson = "{\"endorsements\":[{\"doi\":\"$lowerCaseDoi\",\"orcid\":\"$this->orcid\",\"tags\":[]}]}"; $response = new TestResponse($statusOk, $bodyJson); - $this->assertEquals(Endorsement::STATUS_COMPLETED, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication)); + $this->assertEquals(Endorsement::STATUS_COMPLETED, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication, $this->endorsement)); } public function testEndorsementStatusWhenRequestSucceedButDataDiffs(): void @@ -57,13 +70,13 @@ public function testEndorsementStatusWhenRequestSucceedButDataDiffs(): void $bodyJson = "{\"endorsements\":[{\"doi\":\"10.1590/lepiduspreprints.2022\",\"orcid\":\"$this->orcid\",\"tags\":[]}]}"; $response = new TestResponse($statusOk, $bodyJson); - $this->assertEquals(Endorsement::STATUS_COULDNT_COMPLETE, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication)); + $this->assertEquals(Endorsement::STATUS_COULDNT_COMPLETE, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication, $this->endorsement)); $lowerCaseDoi = strtolower($this->doi); $bodyJson = "{\"endorsements\":[{\"doi\":\"$lowerCaseDoi\",\"orcid\":\"0000-0001-5542-1234\",\"tags\":[]}]}"; $response = new TestResponse($statusOk, $bodyJson); - $this->assertEquals(Endorsement::STATUS_COULDNT_COMPLETE, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication)); + $this->assertEquals(Endorsement::STATUS_COULDNT_COMPLETE, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication, $this->endorsement)); } public function testEndorsementStatusWhenRequestFails(): void @@ -72,6 +85,6 @@ public function testEndorsementStatusWhenRequestFails(): void $bodyJson = ""; $response = new TestResponse($statusBadRequest, $bodyJson); - $this->assertEquals(Endorsement::STATUS_COULDNT_COMPLETE, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication)); + $this->assertEquals(Endorsement::STATUS_COULDNT_COMPLETE, $this->plauditClient->getEndorsementStatusByResponse($response, $this->publication, $this->endorsement)); } } diff --git a/tests/PlauditPreEndorsementHandlerTest.php b/tests/PlauditPreEndorsementHandlerTest.php index e93e874..91fd025 100644 --- a/tests/PlauditPreEndorsementHandlerTest.php +++ b/tests/PlauditPreEndorsementHandlerTest.php @@ -2,42 +2,59 @@ use APP\publication\Publication; use APP\core\Request; -use APP\plugins\generic\plauditPreEndorsement\classes\Endorsement; +use APP\plugins\generic\plauditPreEndorsement\classes\endorsement\Endorsement; use APP\plugins\generic\plauditPreEndorsement\classes\PlauditPreEndorsementHandler; use PHPUnit\Framework\TestCase; +use APP\plugins\generic\plauditPreEndorsement\classes\facades\Repo; final class PlauditPreEndorsementHandlerTest extends TestCase { private $publication; - private $endorserEmail = 'endorser@email.com'; - private $endorserName = 'Endorser'; - private $endorserEmailToken; + private $firstEndorsement; + private $secondEndorsement; + private $endorsementEmailToken; public function setUp(): void { parent::setUp(); - $this->endorserEmailToken = md5(microtime() . $this->endorserEmail); + $this->endorsementEmailToken = md5(microtime() . 'dummy@mailinator.com.br'); $this->publication = $this->createPublication(); + [$this->firstEndorsement, $this->secondEndorsement] = $this->addEndorsements(); } private function createPublication(): Publication { $this->publication = new Publication(); - $this->publication->setData('id', 1); - $this->publication->setData('endorserEmail', $this->endorserEmail); - $this->publication->setData('endorserName', $this->endorserName); - $this->publication->setData('endorserEmailToken', $this->endorserEmailToken); - $this->publication->setData('endorsementStatus', Endorsement::STATUS_NOT_CONFIRMED); + $this->publication->setData('id', rand()); return $this->publication; } - private function verifyEndorserAuth($token, $error = null): string + private function addEndorsements(): array + { + $firstEndorsementParams = [ + 'name' => 'YvesDummy', + 'email' => 'dummy@mailinator.com.br', + 'emailToken' => $this->endorsementEmailToken + ]; + $secondEndorsementParams = [ + 'name' => 'JhonDummy', + 'email' => 'dummy2@mailinator.com.br', + 'emailToken' => md5(microtime() . 'dummy2@mailinator.com.br') + ]; + $firstEndorsement = Repo::endorsement()->newDataObject($firstEndorsementParams); + $secondEndorsement = Repo::endorsement()->newDataObject($secondEndorsementParams); + + return [$firstEndorsement, $secondEndorsement]; + } + + private function verifyEndorsementAuth($token, $endorsement, $error = null): string { $request = new Request(); $request->_requestVars = [ 'state' => $this->publication->getId(), - 'token' => $token + 'token' => $token, + 'endorsementId' => rand() ]; if ($error) { @@ -45,25 +62,24 @@ private function verifyEndorserAuth($token, $error = null): string } $handler = new PlauditPreEndorsementHandler(); - return $handler->getStatusAuthentication($this->publication, $request); + return $handler->getStatusAuthentication($endorsement, $request); } - public function testEndorserAuthenticatesCorrectly(): void + public function testEndorsementAuthenticatesCorrectly(): void { - $result = $this->verifyEndorserAuth($this->endorserEmailToken); + $result = $this->verifyEndorsementAuth($this->endorsementEmailToken, $this->firstEndorsement); $this->assertEquals(PlauditPreEndorsementHandler::AUTH_SUCCESS, $result); } - public function testEndorserTokenIsDifferent(): void + public function testEndorsementTokenIsDifferent(): void { - $diffToken = md5(microtime() . 'email@email.com'); - $result = $this->verifyEndorserAuth($diffToken); + $result = $this->verifyEndorsementAuth($this->endorsementEmailToken, $this->secondEndorsement); $this->assertEquals(PlauditPreEndorsementHandler::AUTH_INVALID_TOKEN, $result); } - public function testEndorserAutheticationHasAccessDenied(): void + public function testEndorsementAutheticationHasAccessDenied(): void { - $result = $this->verifyEndorserAuth($this->endorserEmailToken, 'access_denied'); + $result = $this->verifyEndorsementAuth($this->endorsementEmailToken, $this->firstEndorsement, 'access_denied'); $this->assertEquals(PlauditPreEndorsementHandler::AUTH_ACCESS_DENIED, $result); } } diff --git a/tests/endorsement/DAOTest.php b/tests/endorsement/DAOTest.php new file mode 100644 index 0000000..93d8d7f --- /dev/null +++ b/tests/endorsement/DAOTest.php @@ -0,0 +1,109 @@ +endorsementDAO = app(DAO::class); + $this->contextId = $this->createServerMock(); + $this->publicationId = $this->createPublicationMock(); + $this->addSchemaFile('endorsement'); + } + + public function testNewDataObjectIsInstanceOfEndorsement(): void + { + $endorsement = $this->endorsementDAO->newDataObject(); + self::assertInstanceOf(Endorsement::class, $endorsement); + } + + public function testCreateEndorsement(): void + { + $fetchedEndorsement = $this->retrieveEndorsement(); + + self::assertEquals([ + 'id' => $fetchedEndorsement->getId(), + 'contextId' => $this->contextId, + 'name' => 'DummyEndorsement', + 'email' => "DummyEndorsement@mailinator.com.br", + 'publicationId' => $this->publicationId, + 'status' => null, + 'orcid' => null, + 'emailToken' => null, + 'emailCount' => 0 + ], $fetchedEndorsement->_data); + } + + public function testDeleteEndorsement(): void + { + $fetchedEndorsement = $this->retrieveEndorsement(); + + $this->endorsementDAO->delete($fetchedEndorsement); + self::assertFalse($this->endorsementDAO->exists($fetchedEndorsement->getId(), $this->contextId)); + } + + public function testEditEndorsement(): void + { + $fetchedEndorsement = $this->retrieveEndorsement(); + + $updatedName = "Updated name"; + $fetchedEndorsement->setName($updatedName); + + $this->endorsementDAO->update($fetchedEndorsement); + + $fetchedEndorsement = $this->retrieveEndorsement($fetchedEndorsement->getId()); + + self::assertEquals($fetchedEndorsement->getName(), $updatedName); + } + + public function testGetEndorsementByEmail(): void + { + $endorsement = $this->retrieveEndorsement(); + + self::assertEquals( + $this->endorsementDAO->getByEmail( + $endorsement->getEmail(), + (int) $endorsement->getPublicationId(), + (int) $endorsement->getContextId() + ), + $endorsement + ); + } + + private function retrieveEndorsement($endorsementId = null) + { + $insertedEndorsementId = isset($endorsementId) ? $endorsementId : $this->createEndorsement(); + + return $this->endorsementDAO->get( + $insertedEndorsementId, + $this->contextId + ); + } + + private function createEndorsement() + { + $endorsementDataObject = $this->createEndorsementDataObject($this->contextId, $this->publicationId); + return $this->endorsementDAO->insert($endorsementDataObject); + } +} diff --git a/tests/endorsement/EndorsementTest.php b/tests/endorsement/EndorsementTest.php new file mode 100644 index 0000000..0e9614b --- /dev/null +++ b/tests/endorsement/EndorsementTest.php @@ -0,0 +1,70 @@ +endorsement = new Endorsement(); + } + + public function testEndorsementContextIdRetrieval() + { + $contextId = rand(); + $this->endorsement->setContextId($contextId); + $this->assertEquals($this->endorsement->getContextId(), $contextId); + } + + public function testEndorsementPublicationIdRetrieval() + { + $publicationId = rand(); + $this->endorsement->setPublicationId($publicationId); + $this->assertEquals($this->endorsement->getPublicationId(), $publicationId); + } + + public function testEndorsementNameRetrieval() + { + $this->endorsement->setName("DummyEndorsement"); + $this->assertEquals($this->endorsement->getName(), "DummyEndorsement"); + } + + public function testEndorsementEmailRetrieval() + { + $this->endorsement->setEmail("DummyEndorsement@mailinator.com.br"); + $this->assertEquals($this->endorsement->getEmail(), "DummyEndorsement@mailinator.com.br"); + } + + public function testEndorsementStatusRetrieval() + { + $this->endorsement->setStatus(Endorsement::STATUS_COMPLETED); + $this->assertEquals($this->endorsement->getStatus(), Endorsement::STATUS_COMPLETED); + } + + public function testEndorsementOrcidRetrieval() + { + $dummyOrcid = "0009-0009-190X-Y612"; + $this->endorsement->setOrcid($dummyOrcid); + $this->assertEquals($this->endorsement->getOrcid(), $dummyOrcid); + } + + public function testEndorsementEmailTokenRetrieval() + { + $dummyEmailToken = "066235YTVa78273grv76ha8%¨$#@aiusd"; + $this->endorsement->setEmailToken($dummyEmailToken); + $this->assertEquals($this->endorsement->getEmailToken(), $dummyEmailToken); + } + + public function testEndorsementEmailCountRetrieval() + { + $emailCount = 2; + $this->endorsement->setEmailCount($emailCount); + $this->assertEquals($this->endorsement->getEmailCount(), $emailCount); + } +} diff --git a/tests/endorsement/RepositoryTest.php b/tests/endorsement/RepositoryTest.php new file mode 100644 index 0000000..59c80b1 --- /dev/null +++ b/tests/endorsement/RepositoryTest.php @@ -0,0 +1,145 @@ +contextId = $this->createServerMock(); + $this->publicationId = $this->createPublicationMock(); + $this->params = [ + 'contextId' => $this->contextId, + 'name' => 'DummyEndorsement', + 'email' => "DummyEndorsement@mailinator.com.br", + 'publicationId' => $this->publicationId, + 'status' => null, + 'orcid' => null, + 'emailToken' => null, + 'emailCount' => 0 + ]; + $this->addSchemaFile('endorsement'); + } + + public function testGetNewEndorsementObject(): void + { + $repository = app(Repository::class); + $endorsement = $repository->newDataObject(); + self::assertInstanceOf(Endorsement::class, $endorsement); + $endorsement = $repository->newDataObject($this->params); + self::assertEquals($this->params, $endorsement->_data); + } + + public function testCrud(): void + { + $repository = app(Repository::class); + $endorsement = $repository->newDataObject($this->params); + $insertedEndorsementId = $repository->add($endorsement); + $this->params['id'] = $insertedEndorsementId; + + $fetchedEndorsement = $repository->get($insertedEndorsementId, $this->contextId); + self::assertEquals($this->params, $fetchedEndorsement->_data); + + $this->params['emailToken'] = 'iuqwidub78a9qbkjabiao'; + $this->params['emailCount'] += 1; + $repository->edit($endorsement, $this->params); + + $fetchedEndorsement = $repository->get($endorsement->getId(), $this->contextId); + self::assertEquals($this->params, $fetchedEndorsement->_data); + + $repository->delete($endorsement); + self::assertFalse($repository->exists($endorsement->getId())); + } + + public function testCollectorFilterByContextAndPublicationId(): void + { + $repository = app(Repository::class); + $endorsement = $repository->newDataObject($this->params); + + $repository->add($endorsement); + + $endorsements = $repository->getCollector() + ->filterByContextIds([$this->contextId]) + ->filterByPublicationIds([$this->publicationId]) + ->getMany(); + self::assertTrue(in_array($endorsement, $endorsements->all())); + } + + public function testEmptyCollectorFilterByContextAndPublicationId(): void + { + $repository = app(Repository::class); + $endorsement = $repository->newDataObject($this->params); + $newMockPublicationId = 2; + $newPublicationId = $this->createPublicationMock($newMockPublicationId); + $endorsement->setPublicationId($newPublicationId); + + $repository->add($endorsement); + + $endorsements = $repository->getCollector() + ->filterByContextIds([$this->contextId]) + ->filterByPublicationIds([$this->publicationId]) + ->getMany(); + self::assertFalse(in_array($endorsement, $endorsements->all())); + } + + public function testCollectorFilterByContextAndStatus(): void + { + $repository = app(Repository::class); + $this->params['status'] = Endorsement::STATUS_CONFIRMED; + $endorsement = $repository->newDataObject($this->params); + + $repository->add($endorsement); + + $endorsements = $repository->getCollector() + ->filterByContextIds([$this->contextId]) + ->filterByStatus([Endorsement::STATUS_CONFIRMED]) + ->getMany(); + self::assertTrue(in_array($endorsement, $endorsements->all())); + } + + public function testEmptyCollectorFilterByContextAndStatus(): void + { + $repository = app(Repository::class); + $this->params['status'] = Endorsement::STATUS_CONFIRMED; + $endorsement = $repository->newDataObject($this->params); + + $repository->add($endorsement); + + $endorsements = $repository->getCollector() + ->filterByContextIds([$this->contextId]) + ->filterByStatus([Endorsement::STATUS_NOT_CONFIRMED]) + ->getMany(); + self::assertFalse(in_array($endorsement, $endorsements->all())); + } + + public function testGetEndorsementByEmail(): void + { + $repository = app(Repository::class); + $endorsement = $repository->newDataObject($this->params); + $repository->add($endorsement); + + $fetchedEndorsement = $repository->getByEmail($this->params['email'], $endorsement->getPublicationId(), $endorsement->getContextId()); + self::assertEquals($endorsement, $fetchedEndorsement); + } +} diff --git a/tests/helpers/TestHelperTrait.php b/tests/helpers/TestHelperTrait.php new file mode 100644 index 0000000..e0fabbb --- /dev/null +++ b/tests/helpers/TestHelperTrait.php @@ -0,0 +1,83 @@ +getMockBuilder(Server::class) + ->onlyMethods(['getId']) + ->getMock(); + + $server->expects($this->any()) + ->method('getId') + ->will($this->returnValue(1)); + $server->setName('server-title', "en"); + $server->setData('publisherInstitution', 'server-publisher'); + $server->setPrimaryLocale("en"); + $server->setPath('server-path'); + $server->setId(1); + + return $server->getId(); + } + + private function createPublicationMock($mockPublicationId = null) + { + $publicationId = isset($mockPublicationId) ? $mockPublicationId : 1; + $publication = $this->getMockBuilder(Publication::class) + ->onlyMethods(['getId']) + ->getMock(); + + $publication->expects($this->any()) + ->method('getId') + ->will($this->returnValue($publicationId)); + + return $publication->getId(); + } + + private function addSchemaFile(string $schemaName): void + { + Hook::add( + 'Schema::get::' . $schemaName, + function (string $hookName, array $args) use ($schemaName) { + $schema = &$args[0]; + + $schemaFile = sprintf( + '%s/plugins/generic/plauditPreEndorsement/schemas/%s.json', + BASE_SYS_DIR, + $schemaName + ); + if (file_exists($schemaFile)) { + $schema = json_decode(file_get_contents($schemaFile)); + if (!$schema) { + throw new \Exception( + 'Schema failed to decode. This usually means it is invalid JSON. Requested: ' + . $schemaFile + . '. Last JSON error: ' + . json_last_error() + ); + } + } + return true; + } + ); + } + + private function createEndorsementDataObject($contextId, $publicationId) + { + $endorsement = $this->endorsementDAO->newDataObject(); + $endorsement->setContextId($contextId); + $endorsement->setPublicationId($publicationId); + $endorsement->setName("DummyEndorsement"); + $endorsement->setEmail("DummyEndorsement@mailinator.com.br"); + + return $endorsement; + } +}