diff --git a/pages/OrcidHandler.inc.php b/OrcidProfileHandler.php similarity index 62% rename from pages/OrcidHandler.inc.php rename to OrcidProfileHandler.php index 90383c73..3dc1ab11 100644 --- a/pages/OrcidHandler.inc.php +++ b/OrcidProfileHandler.php @@ -1,33 +1,54 @@ getRequest(); + $context = $request->getContext(); + $contextId = $context == null ? \PKP\core\PKPApplication::CONTEXT_ID_NONE : $context->getId(); + $this->plugin = PluginRegistry::getPlugin('generic', self::ORCIDPROFILEPLUGIN); + $this->isSandBox = $this->plugin->getSetting($contextId, 'orcidProfileAPIPath') == ORCID_API_URL_MEMBER_SANDBOX || + $this->plugin->getSetting($contextId, 'orcidProfileAPIPath') == ORCID_API_URL_PUBLIC_SANDBOX; + } /** * @copydoc PKPHandler::authorize() @@ -45,7 +66,6 @@ public function authorize($request, &$args, $roleAssignments) $targetOp = $request->getUserVar('targetOp'); if ($op === 'orcidAuthorize' && in_array($targetOp, ['profile', 'submit'])) { // ... but user must be logged in for orcidAuthorize with profile or submit - import('lib.pkp.classes.security.authorization.UserRequiredPolicy'); $this->addPolicy(new UserRequiredPolicy($request)); } @@ -68,20 +88,19 @@ public function orcidAuthorize($args, $request) { $context = $request->getContext(); $op = $request->getRequestedOp(); - $plugin = PluginRegistry::getPlugin('generic', 'orcidprofileplugin'); $contextId = ($context == null) ? \PKP\core\PKPApplication::CONTEXT_ID_NONE : $context->getId(); $httpClient = Application::get()->getHttpClient(); // API request: Get an OAuth token and ORCID. $response = $httpClient->request( 'POST', - $url = $plugin->getSetting($contextId, 'orcidProfileAPIPath') . OAUTH_TOKEN_URL, + $url = $this->plugin->getSetting($contextId, 'orcidProfileAPIPath') . OAUTH_TOKEN_URL, [ 'form_params' => [ 'code' => $request->getUserVar('code'), 'grant_type' => 'authorization_code', - 'client_id' => $plugin->getSetting($contextId, 'orcidClientId'), - 'client_secret' => $plugin->getSetting($contextId, 'orcidClientSecret') + 'client_id' => $this->plugin->getSetting($contextId, 'orcidClientId'), + 'client_secret' => $this->plugin->getSetting($contextId, 'orcidClientSecret') ], 'headers' => ['Accept' => 'application/json'], ] @@ -93,7 +112,7 @@ public function orcidAuthorize($args, $request) $response = json_decode($response->getBody(), true); $orcid = $response['orcid']; $accessToken = $response['access_token']; - $orcidUri = ($plugin->getSetting($contextId, 'isSandBox') == true ? ORCID_URL_SANDBOX : ORCID_URL) . $orcid; + $orcidUri = ($this->isSandBox == true ? ORCID_URL_SANDBOX : ORCID_URL) . $orcid; } switch ($request->getUserVar('targetOp')) { @@ -101,7 +120,7 @@ public function orcidAuthorize($args, $request) // API request: get user profile (for names; email; etc) $response = $httpClient->request( 'GET', - $url = $plugin->getSetting($contextId, 'orcidProfileAPIPath') . ORCID_API_VERSION_URL . urlencode($orcid) . '/' . ORCID_PROFILE_URL, + $url = $this->plugin->getSetting($contextId, 'orcidProfileAPIPath') . ORCID_API_VERSION_URL . urlencode($orcid) . '/' . ORCID_PROFILE_URL, [ 'headers' => [ 'Accept' => 'application/json', @@ -119,7 +138,7 @@ public function orcidAuthorize($args, $request) // API request: get employments (for affiliation field) $httpClient->request( 'GET', - $url = $plugin->getSetting($contextId, 'orcidProfileAPIPath') . ORCID_API_VERSION_URL . urlencode($orcid) . '/' . ORCID_EMPLOYMENTS_URL, + $url = $this->plugin->getSetting($contextId, 'orcidProfileAPIPath') . ORCID_API_VERSION_URL . urlencode($orcid) . '/' . ORCID_EMPLOYMENTS_URL, [ 'headers' => [ 'Accept' => 'application/json', @@ -128,7 +147,7 @@ public function orcidAuthorize($args, $request) ] ); if ($response->getStatusCode() != 200) { - error_log('ORCID employments URL error: ' . $response->getStatusCode() . ' (' . __FILE__ . ' line ' . __LINE__ . ', URL ' . $url . ')'); + error_log('ORCID deployments URL error: ' . $response->getStatusCode() . ' (' . __FILE__ . ' line ' . __LINE__ . ', URL ' . $url . ')'); $employmentJson = null; } else { $employmentJson = json_decode($response->getBody(), true); @@ -149,6 +168,7 @@ public function orcidAuthorize($args, $request) '; break; case 'profile': + $user = $request->getUser(); // Store the access token and other data for the user $this->_setOrcidData($user, $orcidUri, $response); @@ -162,7 +182,8 @@ public function orcidAuthorize($args, $request) '; break; - default: assert(false); + default: + assert(false); } } @@ -178,11 +199,10 @@ public function orcidVerify($args, $request) $context = $request->getContext(); $contextId = $context == null ? \PKP\core\PKPApplication::CONTEXT_ID_NONE : $context->getId(); - $plugin = PluginRegistry::getPlugin('generic', 'orcidprofileplugin'); - $templatePath = $plugin->getTemplateResource(self::TEMPLATE); + $templatePath = $this->plugin->getTemplateResource(self::TEMPLATE); - $publicationId = $request->getUserVar('publicationId'); + $publicationId = $request->getUserVar('state'); $authors = Repo::author() ->getCollector() ->filterByPublicationIds([$publicationId]) @@ -213,7 +233,7 @@ public function orcidVerify($args, $request) if ($authorToVerify == null) { // no Author exists in the database with the supplied orcidEmailToken - $plugin->logError('OrcidHandler::orcidverify - No author found with supplied token'); + $this->plugin->logError('OrcidProfileHandler::orcidverify - No author found with supplied token'); $templateMgr->assign('verifySuccess', false); $templateMgr->display($templatePath); return; @@ -230,101 +250,103 @@ public function orcidVerify($args, $request) $authorToVerify->setData('orcidAccessExpiresOn', null); $authorToVerify->setData('orcidEmailToken', null); Repo::author()->dao->update($authorToVerify); - $plugin->logError('OrcidHandler::orcidverify - ORCID access denied. Error description: ' . $request->getUserVar('error_description')); + $this->plugin->logError('OrcidProfileHandler::orcidverify - ORCID access denied. Error description: ' . $request->getUserVar('error_description')); $templateMgr->assign('denied', true); $templateMgr->display($templatePath); return; } // fetch the access token - $url = $plugin->getSetting($contextId, 'orcidProfileAPIPath') . OAUTH_TOKEN_URL; + $url = $this->plugin->getSetting($contextId, 'orcidProfileAPIPath') . OAUTH_TOKEN_URL; $httpClient = Application::get()->getHttpClient(); $header = ['Accept' => 'application/json']; $postData = [ 'code' => $request->getUserVar('code'), 'grant_type' => 'authorization_code', - 'client_id' => $plugin->getSetting($contextId, 'orcidClientId'), - 'client_secret' => $plugin->getSetting($contextId, 'orcidClientSecret') + 'client_id' => $this->plugin->getSetting($contextId, 'orcidClientId'), + 'client_secret' => $this->plugin->getSetting($contextId, 'orcidClientSecret') ]; - $plugin->logInfo('POST ' . $url); - $plugin->logInfo('Request header: ' . var_export($header, true)); - $plugin->logInfo('Request body: ' . http_build_query($postData)); + $this->plugin->logInfo('POST ' . $url); + $this->plugin->logInfo('Request header: ' . var_export($header, true)); + $this->plugin->logInfo('Request body: ' . http_build_query($postData)); + try { + $response = $httpClient->request( + 'POST', + $url, + [ + 'headers' => $header, + 'form_params' => $postData, + ] + ); + if ($response->getStatusCode() != 200) { + $this->plugin->logError('OrcidProfileHandler::orcidverify - unexpected response: ' . $response->getStatusCode()); + $templateMgr->assign('authFailure', true); - $response = $httpClient->request( - 'POST', - $url, - [ - 'headers' => $header, - 'form_params' => $postData, - ] - ); - if ($response->getStatusCode() != 200) { - $plugin->logError('OrcidHandler::orcidverify - unexpected response: ' . $response->getStatusCode()); - $templateMgr->assign('authFailure', true); - $templateMgr->display($templatePath); - return; - } - $response = json_decode($response->getBody(), true); + } + $response = json_decode($response->getBody(), true); - $plugin->logInfo('Response body: ' . print_r($response, true)); - if (($response['error'] ?? null) === 'invalid_grant') { - $plugin->logError('Authorization code invalid, maybe already used'); - $templateMgr->assign('authFailure', true); - $templateMgr->display($templatePath); - return; - } - if (isset($response['error'])) { - $plugin->logError("Invalid ORCID response: " . $response['error']); - $templateMgr->assign('authFailure', true); - $templateMgr->display($templatePath); - } - // Set the orcid id using the full https uri - $orcidUri = ($plugin->getSetting($contextId, 'isSandBox') == true ? ORCID_URL_SANDBOX : ORCID_URL) . $response['orcid']; - if (!empty($authorToVerify->getOrcid()) && $orcidUri != $authorToVerify->getOrcid()) { - // another ORCID id is stored for the author - $templateMgr->assign('duplicateOrcid', true); - $templateMgr->display($templatePath); - return; - } - $authorToVerify->setOrcid($orcidUri); - if (in_array($plugin->getSetting($contextId, 'orcidProfileAPIPath'), [ORCID_API_URL_MEMBER_SANDBOX, ORCID_API_URL_PUBLIC_SANDBOX])) { - // Set a flag to mark that the stored orcid id and access token came form the sandbox api - $authorToVerify->setData('orcidSandbox', true); - $templateMgr->assign('orcid', ORCID_URL_SANDBOX . $response['orcid']); - } else { - $templateMgr->assign('orcid', $orcidUri); - } + $this->plugin->logInfo('Response body: ' . print_r($response, true)); + if (($response['error'] ?? null) === 'invalid_grant') { + $this->plugin->logError('Authorization code invalid, maybe already used'); + $templateMgr->assign('authFailure', true); - // remove the email token - $authorToVerify->setData('orcidEmailToken', null); - $this->_setOrcidData($authorToVerify, $orcidUri, $response); - Repo::author()->dao->update($authorToVerify); - if ($plugin->isMemberApiEnabled($contextId)) { - if ($publication->getData('status') == PKPSubmission::STATUS_PUBLISHED) { - $templateMgr->assign('sendSubmission', true); - $sendResult = $plugin->sendSubmissionToOrcid($publication, $request); - if ($sendResult === true || (is_array($sendResult) && $sendResult[$response['orcid']])) { - $templateMgr->assign('sendSubmissionSuccess', true); + } + if (isset($response['error'])) { + $this->plugin->logError("Invalid ORCID response: " . $response['error']); + $templateMgr->assign('authFailure', true); } + // Set the orcid id using the full https uri + $orcidUri = ($this->isSandBox ? ORCID_URL_SANDBOX : ORCID_URL) . $response['orcid']; + if (!empty($authorToVerify->getOrcid()) && $orcidUri != $authorToVerify->getOrcid()) { + // another ORCID id is stored for the author + $templateMgr->assign('duplicateOrcid', true); + } + $authorToVerify->setOrcid($orcidUri); + if (in_array($this->plugin->getSetting($contextId, 'orcidProfileAPIPath'), [ORCID_API_URL_MEMBER_SANDBOX, ORCID_API_URL_PUBLIC_SANDBOX])) { + // Set a flag to mark that the stored orcid id and access token came form the sandbox api + $authorToVerify->setData('orcidSandbox', true); + $templateMgr->assign('orcid', ORCID_URL_SANDBOX . $response['orcid']); } else { - $templateMgr->assign('submissionNotPublished', true); + $templateMgr->assign('orcid', $orcidUri); } - } - $templateMgr->assign([ - 'verifySuccess' => true, - 'orcidIcon' => $plugin->getIcon() - ]); + // remove the email token + $authorToVerify->setData('orcidEmailToken', null); + $this->_setOrcidData($authorToVerify, $orcidUri, $response); + Repo::author()->dao->update($authorToVerify); + if ($this->plugin->isMemberApiEnabled($contextId)) { + if ($publication->getData('status') == PKPSubmission::STATUS_PUBLISHED) { + $templateMgr->assign('sendSubmission', true); + $sendResult = $this->plugin->sendSubmissionToOrcid($publication, $request); + if ($sendResult === true || (is_array($sendResult) && $sendResult[$response['orcid']])) { + $templateMgr->assign('sendSubmissionSuccess', true); + } + } else { + $templateMgr->assign('submissionNotPublished', true); + } + } + + $templateMgr->assign([ + 'verifySuccess' => true, + 'orcidIcon' => $this->plugin->getIcon() + ]); + + } catch (\GuzzleHttp\Exception\ClientException $exception) { + $reason = $exception->getResponse()->getBody(false); + $this->plugin->logInfo("Publication fail: ${reason}"); + + } + $templateMgr->assign('authFailure', true); $templateMgr->display($templatePath); } public function _setOrcidData($userOrAuthor, $orcidUri, $orcidResponse) { // Save the access token - $orcidAccessExpiresOn = Carbon\Carbon::now(); + $orcidAccessExpiresOn = Carbon::now(); // expires_in field from the response contains the lifetime in seconds of the token // See https://members.orcid.org/api/get-oauthtoken $orcidAccessExpiresOn->addSeconds($orcidResponse['expires_in']); @@ -335,6 +357,7 @@ public function _setOrcidData($userOrAuthor, $orcidUri, $orcidResponse) $userOrAuthor->setData('orcidAccessScope', $orcidResponse['scope']); $userOrAuthor->setData('orcidRefreshToken', $orcidResponse['refresh_token']); $userOrAuthor->setData('orcidAccessExpiresOn', $orcidAccessExpiresOn->toDateTimeString()); + return $userOrAuthor; } /** @@ -348,9 +371,10 @@ public function about($args, $request) $context = $request->getContext(); $contextId = $context == null ? \PKP\core\PKPApplication::CONTEXT_ID_NONE : $context->getId(); $templateMgr = TemplateManager::getManager($request); - $plugin = PluginRegistry::getPlugin('generic', 'orcidprofileplugin'); - $templateMgr->assign('orcidIcon', $plugin->getIcon()); - $templateMgr->assign('isMemberApi', $plugin->isMemberApiEnabled($contextId)); - $templateMgr->display($plugin->getTemplateResource('orcidAbout.tpl')); + $templateMgr->assign('orcidIcon', $this->plugin->getIcon()); + $templateMgr->assign('isMemberApi', $this->plugin->isMemberApiEnabled($contextId)); + $templateMgr->display($this->plugin->getTemplateResource('orcidAbout.tpl')); } + + } diff --git a/OrcidProfilePlugin.inc.php b/OrcidProfilePlugin.php similarity index 73% rename from OrcidProfilePlugin.inc.php rename to OrcidProfilePlugin.php index 5875e37f..73c59e68 100755 --- a/OrcidProfilePlugin.inc.php +++ b/OrcidProfilePlugin.php @@ -1,7 +1,7 @@ 'doi', 'other::urn' => 'urn']; - public const USER_GROUP_TO_ORCID_ROLE = ['Author' => 'AUTHOR', 'Translator' => 'CHAIR_OR_TRANSLATOR','Journal manager' => 'AUTHOR']; + public const USER_GROUP_TO_ORCID_ROLE = ['Author' => 'AUTHOR', 'Translator' => 'CHAIR_OR_TRANSLATOR', 'Journal manager' => 'AUTHOR']; private $submissionIdToBePublished; private $currentContextId; @@ -71,7 +93,7 @@ public function register($category, $path, $mainContextId = null) Hook::add('ArticleHandler::view', [&$this, 'submissionView']); Hook::add('PreprintHandler::view', [&$this, 'submissionView']); - // Insert the OrcidHandler to handle ORCID redirects + // Insert the OrcidProfileHandler to handle ORCID redirects Hook::add('LoadHandler', [$this, 'setupCallbackHandler']); // Register callback for Smarty filters; add CSS @@ -92,10 +114,24 @@ public function register($category, $path, $mainContextId = null) // Send emails to authors without ORCID id upon submission Hook::add('submissionsubmitstep3form::execute', [$this, 'handleSubmissionSubmitStep3FormExecute']); - // Add more ORCiD fields to user Schema - Hook::add('Schema::get::user', function ($hookName, $args) { - $schema = $args[0]; + // Send emails to authors without authorised ORCID access on promoting a submission to copy editing. Not included in OPS. + if ($this->getSetting($contextId, 'sendMailToAuthorsOnPublication')) { + Hook::add('EditorAction::recordDecision', [$this, 'handleEditorAction']); + } + + Hook::add('Publication::publish', [$this, 'handlePublicationStatusChange']); + + Hook::add('ThankReviewerForm::thankReviewer', [$this, 'handleThankReviewer']); + + // Add more ORCiD fields to author Schema + Hook::add('Schema::get::author', function ($hookName, $args) { + $schema = &$args[0]; + $schema->properties->orcidSandbox = (object)[ + 'type' => 'string', + 'apiSummary' => true, + 'validation' => ['nullable'] + ]; $schema->properties->orcidAccessToken = (object)[ 'type' => 'string', 'apiSummary' => true, @@ -121,24 +157,23 @@ public function register($category, $path, $mainContextId = null) 'apiSummary' => true, 'validation' => ['nullable'] ]; - }); - - // Send emails to authors without authorised ORCID access on promoting a submission to copy editing. Not included in OPS. - if ($this->getSetting($contextId, 'sendMailToAuthorsOnPublication')) { - Hook::add('EditorAction::recordDecision', [$this, 'handleEditorAction']); - } - - Hook::add('Publication::publish', [$this, 'handlePublicationStatusChange']); - - // Add more ORCiD fields to author Schema - Hook::add('Schema::get::author', function ($hookName, $args) { - $schema = $args[0]; - - $schema->properties->orcidSandbox = (object)[ + $schema->properties->orcidEmailToken = (object)[ 'type' => 'string', 'apiSummary' => true, 'validation' => ['nullable'] ]; + $schema->properties->orcidWorkPutCode = (object)[ + 'type' => 'string', + 'apiSummary' => true, + 'validation' => ['nullable'] + ]; + + }); + + // Add more ORCiD fields to user Schema + Hook::add('Schema::get::user', function ($hookName, $args) { + $schema = &$args[0]; + $schema->properties->orcidAccessToken = (object)[ 'type' => 'string', 'apiSummary' => true, @@ -164,49 +199,23 @@ public function register($category, $path, $mainContextId = null) 'apiSummary' => true, 'validation' => ['nullable'] ]; - $schema->properties->orcidEmailToken = (object)[ - 'type' => 'string', - 'apiSummary' => true, - 'validation' => ['nullable'] - ]; - $schema->properties->orcidWorkPutCode = (object)[ + $schema->properties->orcidReviewPutCode = (object)[ 'type' => 'string', 'apiSummary' => true, 'validation' => ['nullable'] ]; }); + Services::get('schema')->get(PKPSchemaService::SCHEMA_USER, true); Hook::add('Mailer::Mailables', [$this, 'addMailable']); - } - return $success; - } + Hook::add('Author::edit', [$this, 'handleAuthorFormExecute']); - /** - * Get page handler path for this plugin. - * - * @return string Path to plugin's page handler - */ - public function getHandlerPath() - { - return "{$this->getPluginPath()}/pages"; - } + Hook::add('Form::config::before', [$this, 'addOrcidFormFields']); - /** - * Hook callback: register pages for each sushi-lite method - * This URL is of the form: orcidapi/{$orcidrequest} - * - * @see PKPPageRouter::route() - */ - public function setupCallbackHandler($hookName, $params) - { - $page = $params[0]; - if ($this->getEnabled() && $page == 'orcidapi') { - $this->import('pages/OrcidHandler'); - define('HANDLER_CLASS', 'OrcidHandler'); - return true; } - return false; + + return $success; } /** @@ -230,6 +239,12 @@ public function getSetting($contextId, $name) case 'orcidClientSecret': $config_value = Config::getVar('orcid', 'client_secret'); break; + case 'country': + $config_value = Config::getVar('orcid', 'country'); + break; + case 'city': + $config_value = Config::getVar('orcid', 'city'); + break; default: return parent::getSetting($contextId, $name); } @@ -238,11 +253,298 @@ public function getSetting($contextId, $name) } /** - * Check if there exist a valid orcid configuration section in the global config.inc.php of OJS. + * adds orcid form fields. + * @param $hookName + * @param $form + * @return bool + */ + function addOrcidFormFields($hookName, $form): bool + + { + + if (!$form instanceof ContributorForm) return Hook::CONTINUE; + + $form->removeField('orcid'); + $form->addField(new FieldText('orcid', [ + 'label' => __('user.orcid'), + 'optIntoEdit' => true, + 'optIntoEditLabel' => __('common.override'), + 'tooltip' => __('plugins.generic.orcidProfile.about.orcidExplanation'), + + ]), [FIELD_POSITION_AFTER, 'url']); + + $form->addField(new FieldOptions('requestOrcidAuthorization', [ + 'label' => __('plugins.generic.orcidProfile.verify.title'), + 'options' => [ + [ + 'label' => __('plugins.generic.orcidProfile.author.requestAuthorization'), + 'value' > false, + ] + ] + ]), [FIELD_POSITION_AFTER, 'orcid']); + + $form->addField(new FieldOptions('deleteORCID', [ + 'label' => __('plugins.generic.orcidProfile.displayName'), + 'options' => [ + [ + 'label' => __('plugins.generic.orcidProfile.author.deleteORCID'), + 'value' > false, + ] + ], + 'showWhen' => 'orcid', + ]) + , [FIELD_POSITION_AFTER, 'orcid'] + ); + + return Hook::CONTINUE; + } + + /** + * @param $hookName + * @param $args + */ + function handleThankReviewer($hookName, $args) + { + $request = PKPApplication::get()->getRequest(); + $context = $request->getContext(); + $newPublication =& $args[0]; + if ($this->isMemberApiEnabled($this->currentContextId)) { + if ($this->getSetting($context->getId(), 'country') && $this->getSetting($context->getId(), 'city')) { + $this->publishReviewerWorkToOrcid($newPublication, $request); + } + } + } + + /** + * @return bool True if the ORCID Member API has been selected in this context. + */ + public function isMemberApiEnabled($contextId) + { + $apiUrl = $this->getSetting($contextId, 'orcidProfileAPIPath'); + if ($apiUrl === ORCID_API_URL_MEMBER || $apiUrl === ORCID_API_URL_MEMBER_SANDBOX) { + return true; + } else { + return false; + } + } + + /** + * @param Submission $submission + * @param Request $request + * @return false|void + */ + public function publishReviewerWorkToOrcid(Submission $submission, Request $request) + { + $context = $request->getContext(); + $requestVars = $request->getUserVars(); + + $reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); + /* @var $reviewAssignmentDao ReviewAssignmentDAO */ + $reviewAssignmentId = $requestVars['reviewAssignmentId']; + if (isset($reviewAssignmentId)) { + $review = $reviewAssignmentDao->getById($reviewAssignmentId); + $reviewer = Repo::user()->get($review->getData('reviewerId')); + + if ($reviewer->getOrcid() && $reviewer->getData('orcidAccessToken')) { + $orcidAccessExpiresOn = Carbon::parse($reviewer->getData('orcidAccessExpiresOn')); + if ($orcidAccessExpiresOn->isFuture()) { + # Extract only the ORCID from the stored ORCID uri + $orcid = basename(parse_url($reviewer->getOrcid(), PHP_URL_PATH)); + $orcidReview = $this->buildOrcidReview($submission, $review, $request); + $uri = $this->getSetting($context->getId(), 'orcidProfileAPIPath') . ORCID_API_VERSION_URL . $orcid . '/' . ORCID_REVIEW_URL; + $method = "POST"; + if ($putCode = $reviewer->getData('orcidReviewPutCode')) { + $uri .= '/' . $putCode; + $method = "PUT"; + $orcidReview['put-code'] = $putCode; + } + $headers = [ + 'Content-Type' => ' application/vnd.orcid+json; qs=4', + 'Accept' => 'application/json', + 'Authorization' => 'Bearer ' . $reviewer->getData("orcidAccessToken") + ]; + $httpClient = Application::get()->getHttpClient(); + $requestsSuccess = []; + + try { + $response = $httpClient->request( + $method, + $uri, + [ + 'headers' => $headers, + 'json' => $orcidReview, + ] + ); + } catch (ClientException $exception) { + $reason = $exception->getResponse()->getBody(false); + $this->logInfo("Publication fail: $reason"); + return new JSONMessage(false); + } + $httpStatus = $response->getStatusCode(); + $this->logInfo("Response status: $httpStatus"); + $responseHeaders = $response->getHeaders(); + switch ($httpStatus) { + case 200: + $this->logInfo("Review updated in profile, putCode: $putCode"); + break; + case 201: + $location = $responseHeaders['Location'][0]; + // Extract the ORCID work put code for updates/deletion. + $putCode = basename(parse_url($location, PHP_URL_PATH)); + $reviewer->setData('orcidReviewPutCode', $putCode); + Repo::user()->edit($reviewer, ['orcidReviewPutCode']); + $this->logInfo("Review added to profile, putCode: $putCode"); + break; + default: + $this->logError("Unexpected status $httpStatus response, body: $responseHeaders"); + } + } + + } + + } + } + + public function buildOrcidReview($submission, $review, $request, $issue = null) + { + $publicationUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_PAGE, null, 'article', 'view', $submission->getId()); + $context = $request->getContext(); + $publicationLocale = ($submission->getData('locale')) ? $submission->getData('locale') : 'en_US'; + $pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $context->getId()); // DO not remove + $supportedSubmissionLocales = $context->getSupportedSubmissionLocales(); + + if (!empty($review->getData('dateCompleted')) && $context->getData('onlineIssn')) { + $publicationPublishDate = Carbon::parse($submission->getData('datePublished')); + $reviewCompletionDate = Carbon::parse($review->getData('dateCompleted')); + + + $orcidReview = [ + 'reviewer-role' => 'reviewer', + 'review-type' => 'review', + "review-completion-date" => [ + "year" => [ + "value" => $reviewCompletionDate->format("Y") + ], + 'month' => [ + 'value' => $reviewCompletionDate->format("m") + ], + 'day' => [ + 'value' => $reviewCompletionDate->format("d") + ] + ], + 'review-group-id' => "issn:" . $context->getData('onlineIssn'), + + 'convening-organization' => [ + 'name' => $context->getData('publisherInstitution'), + 'address' => [ + 'city' => $this->getSetting($context->getId(), 'city'), + 'country' => $this->getSetting($context->getId(), 'country') + + ] + ], + 'review-identifiers' => ['external-id' => [ + [ + 'external-id-type' => 'source-work-id', + 'external-id-value' => $review->getData('reviewRoundId'), + 'external-id-relationship' => 'part-of'] + ]] + ]; + if ($review->getReviewMethod() == ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN) { + $orcidReview['subject-url'] = ['value' => $publicationUrl]; + $orcidReview['review-url'] = ['value' => $publicationUrl]; + $orcidReview['subject-type'] = 'journal-article'; + $orcidReview['subject-name'] = [ + 'title' => ['value' => $submission->getCurrentPublication()->getLocalizedData('title') ?? ''] + ]; + + + if (!empty($submission->getData('pub-id::doi'))) { + $externalIds = [ + + 'external-id-type' => 'doi', + 'external-id-value' => $submission->getData('pub-id::doi'), + 'external-id-url' => [ + 'value' => 'https://doi.org/' . $submission->getData('pub-id::doi') + ], + 'external-id-relationship' => 'self' + + ]; + $orcidReview['subject-external-identifier'] = $externalIds; + } + + } + + $translatedTitleAvailable = false; + foreach ($supportedSubmissionLocales as $defaultLanguage) { + if ($defaultLanguage !== $publicationLocale) { + $iso2LanguageCode = substr($defaultLanguage, 0, 2); + $defaultTitle = $submission->getLocalizedData($iso2LanguageCode); + if (strlen($defaultTitle) > 0 && !$translatedTitleAvailable) { + $orcidReview['subject-name']['translated-title'] = ['value' => $defaultTitle, 'language-code' => $iso2LanguageCode]; + $translatedTitleAvailable = true; + } + } + + + } + return $orcidReview; + } + } + + /** + * Write info message to log. * - * @return bool True, if the config file has api_url, client_id and client_secret set in an [orcid] section + * @param string $message Message to write */ - public function isGloballyConfigured() + public function logInfo($message) + { + if ($this->getSetting($this->currentContextId, 'logLevel') === 'ERROR') { + return; + } + self::writeLog($message, 'INFO'); + } + + /** + * Write a message with specified level to log + * + * @param string $message Message to write + * @param string $level Error level to add to message + */ + private static function writeLog($message, $level) + { + $fineStamp = date('Y-m-d H:i:s') . substr(microtime(), 1, 4); + error_log("${fineStamp} ${level} ${message}\n", 3, self::logFilePath()); + } + + /** + * @return string Path to a custom ORCID log file. + */ + public static function logFilePath() + { + return Config::getVar('files', 'files_dir') . '/orcid.log'; + } + + /** + * Hook callback: register pages for each sushi-lite method + * This URL is of the form: orcidapi/{$orcidrequest} + * + * @see PKPPageRouter::route() + */ + public function setupCallbackHandler($hookName, $params) + { + $page = $params[0]; + if ($this->getEnabled() && $page == 'orcidapi') { + define('HANDLER_CLASS', 'APP\plugins\generic\orcidProfile\OrcidProfileHandler'); + return true; + } + return false; + } + + /** + * Check if there exist a valid orcid configuration section in the global config.inc.php of OJS. + * @return boolean True, if the config file has api_url, client_id and client_secret set in an [orcid] section + */ + function isGloballyConfigured() { $apiUrl = Config::getVar('orcid', 'api_url'); $clientId = Config::getVar('orcid', 'client_id'); @@ -265,11 +567,12 @@ public function isGloballyConfigured() */ public function handleFormDisplay($hookName, $args) { + //TODO $request = Application::get()->getRequest(); $templateMgr = TemplateManager::getManager($request); switch ($hookName) { case 'authorform::display': - $authorForm = & $args[0]; + $authorForm = &$args[0]; $author = $authorForm->getAuthor(); if ($author) { $authenticated = !empty($author->getData('orcidAccessToken')); @@ -303,8 +606,9 @@ public function handleFormDisplay($hookName, $args) */ public function handleTemplateDisplay($hookName, $args) { - $templateMgr = & $args[0]; - $template = & $args[1]; + //TODO orcid + $templateMgr = &$args[0]; + $template = &$args[1]; $request = Application::get()->getRequest(); // Assign our private stylesheet, for front and back ends. @@ -325,13 +629,54 @@ public function handleTemplateDisplay($hookName, $args) } /** - * Return the OAUTH path (prod or sandbox) based on the current API configuration + * Return the location of the plugin's CSS file * * @return string */ - public function getOauthPath() + function getStyleSheet() { - return $this->getOrcidUrl() . 'oauth/'; + return $this->getPluginPath() . '/css/orcidProfile.css'; + } + + public function isSandbox() + { + + $apiUrl = $this->getSetting($this->getCurrentContextId(), 'orcidProfileAPIPath'); + return ($apiUrl == ORCID_API_URL_MEMBER_SANDBOX); + + } + + /** + * Output filter adds ORCiD interaction to registration form. + * + * @param string $output + * @param TemplateManager $templateMgr + * + * @return string + */ + public function registrationFilter($output, $templateMgr) + { + if (preg_match('/]+id="register"[^>]+>/', $output, $matches, PREG_OFFSET_CAPTURE)) { + $match = $matches[0][0]; + $offset = $matches[0][1]; + $request = Application::get()->getRequest(); + $context = $request->getContext(); + $contextId = ($context == null) ? 0 : $context->getId(); + $targetOp = 'register'; + $templateMgr->assign([ + 'targetOp' => $targetOp, + 'orcidUrl' => $this->getOrcidUrl(), + 'orcidOAuthUrl' => $this->buildOAuthUrl('orcidAuthorize', ['targetOp' => $targetOp]), + 'orcidIcon' => $this->getIcon(), + ]); + + $newOutput = substr($output, 0, $offset + strlen($match)); + $newOutput .= $templateMgr->fetch($this->getTemplateResource('orcidProfile.tpl')); + $newOutput .= substr($output, $offset + strlen($match)); + $output = $newOutput; + $templateMgr->unregisterFilter('output', [$this, 'registrationFilter']); + } + return $output; } /** @@ -352,7 +697,7 @@ public function getOrcidUrl() /** * Return an ORCID OAuth authorization link with * - * @param string $handlerMethod containting a valid method of the OrcidHandler + * @param string $handlerMethod containting a valid method of the OrcidProfileHandler * @param array $redirectParams associative array with additional request parameters for the redirect URL */ public function buildOAuthUrl($handlerMethod, $redirectParams) @@ -381,45 +726,33 @@ public function buildOAuthUrl($handlerMethod, $redirectParams) ); return $this->getOauthPath() . 'authorize?' . http_build_query( - [ - 'client_id' => $this->getSetting($contextId, 'orcidClientId'), - 'response_type' => 'code', - 'scope' => $scope, - 'redirect_uri' => $redirectUrl] - ); + [ + 'client_id' => $this->getSetting($contextId, 'orcidClientId'), + 'response_type' => 'code', + 'scope' => $scope, + 'redirect_uri' => $redirectUrl] + ); } /** - * Output filter adds ORCiD interaction to registration form. - * - * @param string $output - * @param TemplateManager $templateMgr + * Return the OAUTH path (prod or sandbox) based on the current API configuration * * @return string */ - public function registrationFilter($output, $templateMgr) + public function getOauthPath() { - if (preg_match('/]+id="register"[^>]+>/', $output, $matches, PREG_OFFSET_CAPTURE)) { - $match = $matches[0][0]; - $offset = $matches[0][1]; - $request = Application::get()->getRequest(); - $context = $request->getContext(); - $contextId = ($context == null) ? 0 : $context->getId(); - $targetOp = 'register'; - $templateMgr->assign([ - 'targetOp' => $targetOp, - 'orcidUrl' => $this->getOrcidUrl(), - 'orcidOAuthUrl' => $this->buildOAuthUrl('orcidAuthorize', ['targetOp' => $targetOp]), - 'orcidIcon' => $this->getIcon(), - ]); + return $this->getOrcidUrl() . 'oauth/'; + } - $newOutput = substr($output, 0, $offset + strlen($match)); - $newOutput .= $templateMgr->fetch($this->getTemplateResource('orcidProfile.tpl')); - $newOutput .= substr($output, $offset + strlen($match)); - $output = $newOutput; - $templateMgr->unregisterFilter('output', [$this, 'registrationFilter']); - } - return $output; + /** + * Return a string of the ORCiD SVG icon + * + * @return string + */ + function getIcon() + { + $path = Core::getBaseDir() . '/' . $this->getPluginPath() . '/templates/images/orcid.svg'; + return file_exists($path) ? file_get_contents($path) : ''; } /** @@ -435,11 +768,13 @@ public function registrationFilter($output, $templateMgr) */ public function handleUserPublicProfileDisplay($hookName, $params) { - $templateMgr = & $params[1]; - $output = & $params[2]; + + $templateMgr = &$params[1]; + $output = &$params[2]; $request = Application::get()->getRequest(); $context = $request->getContext(); - $user = $request->getUser(); + $userId = $request->getUser()->getId(); + $user = Repo::user()->get($userId); $contextId = ($context == null) ? 0 : $context->getId(); $targetOp = 'profile'; $templateMgr->assign( @@ -458,53 +793,93 @@ public function handleUserPublicProfileDisplay($hookName, $params) } /** - * Output filter adds ORCiD interaction to contributors metadata add/edit form. + * handleAuthorFormexecute sends an e-mail to the author if a specific checkbox was ticked in the author form. * - * @param string $output - * @param TemplateManager $templateMgr + * @param string $hookname + * @param AuthorForm[] $args + * + * @see AuthorForm::execute() The function calling the hook. * - * @return string */ - public function authorFormFilter($output, $templateMgr) + public function handleAuthorFormExecute($hookname, $args) { - if (preg_match('/]+name="submissionId"[^>]*>/', $output, $matches, PREG_OFFSET_CAPTURE)) { - $match = $matches[0][0]; - $offset = $matches[0][1]; - $templateMgr->assign('orcidIcon', $this->getIcon()); - $newOutput = substr($output, 0, $offset + strlen($match)); - $newOutput .= $templateMgr->fetch($this->getTemplateResource('authorFormOrcid.tpl')); - $newOutput .= substr($output, $offset + strlen($match)); - $output = $newOutput; - $templateMgr->unregisterFilter('output', [$this, 'authorFormFilter']); + if (count($args) == 3) { + $author = &$args[0]; + $values = $args[2]; + + if ($author && $values['requestOrcidAuthorization']) { + $this->sendAuthorMail($author); + } + + if ($author && $values['deleteORCID']) { + $author->setOrcid(null); + $this->removeOrcidAccessToken($author, false); + } } - return $output; } /** - * handleAuthorFormexecute sends an e-mail to the author if a specific checkbox was ticked in the author form. - * - * @param string $hookname - * @param AuthorForm[] $args - * - * @see AuthorForm::execute() The function calling the hook. + * Send mail with ORCID authorization link to the e-mail address of the supplied Author object. * + * @param Author $author + * @param bool $updateAuthor If true update the author fields in the database. + * Use this only if not called from a function, which does this anyway. */ - public function handleAuthorFormExecute($hookname, $args) + public function sendAuthorMail($author, $updateAuthor = false) { - $form = & $args[0]; - $form->readUserVars(['requestOrcidAuthorization', 'deleteOrcid']); + $request = Application::get()->getRequest(); + $context = $request->getContext(); + + // This should only ever happen within a context, never site-wide. + if ($context != null) { + $contextId = $context->getId(); + $publicationId = $author->getData('publicationId'); + $publication = Repo::publication()->get($publicationId); + $submission = Repo::submission()->get($publication->getData('submissionId')); + + $emailToken = md5(microtime() . $author->getEmail()); + $author->setData('orcidEmailToken', $emailToken); + $oauthUrl = $this->buildOAuthUrl('orcidVerify', ['token' => $emailToken, 'state' => $publicationId]); - $requestAuthorization = $form->getData('requestOrcidAuthorization'); - $deleteOrcid = $form->getData('deleteOrcid'); - $author = $form->getAuthor(); + if ($this->isMemberApiEnabled($contextId)) { + $mailable = new OrcidRequestAuthorAuthorization($context, $submission, $oauthUrl); + } else { + $mailable = new OrcidCollectAuthorId($context, $submission, $oauthUrl); + } + + // Set From to primary journal contact + $mailable->from($context->getData('contactEmail'), $context->getData('contactName')); + + // Send to author + $mailable->recipients([$author]); + $emailTemplateKey = $mailable::getEmailTemplateKey(); + $emailTemplate = Repo::emailTemplate()->getByKey($contextId, $emailTemplateKey); + $mailable->body($emailTemplate->getLocalizedData('body')) + ->subject($emailTemplate->getLocalizedData('subject')); + Mail::send($mailable); - if ($author && $requestAuthorization) { - $this->sendAuthorMail($author); + if ($updateAuthor) { + Repo::author()->dao->update($author); + } } + } - if ($author && $deleteOrcid) { - $author->setOrcid(null); - $this->removeOrcidAccessToken($author, false); + /** + * Remove all data fields, which belong to an ORCID access token from the + * given Author object. Also updates fields in the db. + * + * @param Author $author object with ORCID access token + */ + public function removeOrcidAccessToken($author, $saveAuthor = true) + { + $author->setData('orcidAccessToken', null); + $author->setData('orcidAccessScope', null); + $author->setData('orcidRefreshToken', null); + $author->setData('orcidAccessExpiresOn', null); + $author->setData('orcidSandbox', null); + + if ($saveAuthor) { + Repo::author()->dao->update($author); } } @@ -570,7 +945,7 @@ public function handleSubmissionSubmitStep3FormExecute($hookName, $params) */ public function handleAdditionalFieldNames($hookName, $params) { - $fields = & $params[1]; + $fields = &$params[1]; $fields[] = 'orcidSandbox'; $fields[] = 'orcidAccessToken'; $fields[] = 'orcidAccessScope'; @@ -578,15 +953,7 @@ public function handleAdditionalFieldNames($hookName, $params) $fields[] = 'orcidAccessExpiresOn'; $fields[] = 'orcidAccessDenied'; - return false; - } - - /** - * @copydoc Plugin::getDisplayName() - */ - public function getDisplayName() - { - return __('plugins.generic.orcidProfile.displayName'); + return false; } /** @@ -661,6 +1028,12 @@ public function getActions($request, $actionArgs) __('manager.plugins.settings'), null ), + new LinkAction( + 'status', + new AjaxModal($router->url($request, null, null, 'manage', null, array('verb' => 'status', 'plugin' => $this->getName(), 'category' => 'generic')), $this->getDisplayName()), + __('common.status'), + null + ) ] : [], parent::getActions($request, $actionArgs) ); @@ -669,27 +1042,66 @@ public function getActions($request, $actionArgs) /** * @see Plugin::manage() */ + function getDisplayName() + { + return __('plugins.generic.orcidProfile.displayName'); + } + + function setEnabled($enabled) + { + $contextId = $this->getCurrentContextId(); + $request = Application::get()->getRequest(); + $validator = new OrcidValidator($this); + + if ($this->isSitePlugin()) { + $contextId = 0; + } + if ($request->getUserVar('save') == 1) { + $clientId = $request->getUserVar('orcidClientId'); + $clientSecret = $request->getUserVar('orcidClientSecret'); + } else { + $clientId = $this->getSetting($contextId, 'orcidClientId'); + $clientSecret = $this->getSetting($contextId, 'orcidClientSecret'); + + } + + if (!$validator->validateClientSecret($clientSecret) or !$validator->validateClientId($clientId)) { + $enabled = false; + + } + $this->updateSetting($contextId, 'enabled', $enabled, 'bool'); + } + public function manage($args, $request) { + $context = $request->getContext(); + $contextId = ($context == null) ? 0 : $context->getId(); + switch ($request->getUserVar('verb')) { case 'settings': - $context = $request->getContext(); - $contextId = ($context == null) ? 0 : $context->getId(); $templateMgr = TemplateManager::getManager(); $templateMgr->registerPlugin('function', 'plugin_url', [$this, 'smartyPluginUrl']); - $apiOptions = [ + + $templateMgr->assign('orcidApiUrls', [ ORCID_API_URL_PUBLIC => 'plugins.generic.orcidProfile.manager.settings.orcidProfileAPIPath.public', ORCID_API_URL_PUBLIC_SANDBOX => 'plugins.generic.orcidProfile.manager.settings.orcidProfileAPIPath.publicSandbox', ORCID_API_URL_MEMBER => 'plugins.generic.orcidProfile.manager.settings.orcidProfileAPIPath.member', ORCID_API_URL_MEMBER_SANDBOX => 'plugins.generic.orcidProfile.manager.settings.orcidProfileAPIPath.memberSandbox' - ]; - $templateMgr->assign('orcidApiUrls', $apiOptions); + ]); + + $isoCodes = new IsoCodesFactory(); + $countries = array(); + foreach ($isoCodes->getCountries() as $country) { + $countries[$country->getAlpha2()] = $country->getLocalName(); + } + asort($countries); + $templateMgr->assign('countries', $countries); $templateMgr->assign('logLevelOptions', [ 'ERROR' => 'plugins.generic.orcidProfile.manager.settings.logLevel.error', 'ALL' => 'plugins.generic.orcidProfile.manager.settings.logLevel.all' ]); - $this->import('OrcidProfileSettingsForm'); + $form = new OrcidProfileSettingsForm($this, $contextId); if ($request->getUserVar('save')) { $form->readInputData(); @@ -701,73 +1113,14 @@ public function manage($args, $request) $form->initData(); } return new JSONMessage(true, $form->fetch($request)); + case 'status': + $form = new OrcidProfileStatusForm($this, $contextId); + $form->initData(); + return new JSONMessage(true, $form->fetch($request)); } return parent::manage($args, $request); } - /** - * Return the location of the plugin's CSS file - * - * @return string - */ - public function getStyleSheet() - { - return $this->getPluginPath() . '/css/orcidProfile.css'; - } - - /** - * Return a string of the ORCiD SVG icon - * - * @return string - */ - public function getIcon() - { - $path = Core::getBaseDir() . '/' . $this->getPluginPath() . '/templates/images/orcid.svg'; - return file_exists($path) ? file_get_contents($path) : ''; - } - - /** - * Send mail with ORCID authorization link to the e-mail address of the supplied Author object. - * - * @param Author $author - * @param bool $updateAuthor If true update the author fields in the database. - * Use this only if not called from a function, which does this anyway. - */ - public function sendAuthorMail($author, $updateAuthor = false) - { - $request = Application::get()->getRequest(); - $context = $request->getContext(); - - // This should only ever happen within a context, never site-wide. - if ($context != null) { - $contextId = $context->getId(); - $publicationId = $author->getData('publicationId'); - $publication = Repo::publication()->get($publicationId); - $submission = Repo::submission()->get($publication->getData('submissionId')); - - $emailToken = md5(microtime() . $author->getEmail()); - $author->setData('orcidEmailToken', $emailToken); - $oauthUrl = $this->buildOAuthUrl('orcidVerify', ['token' => $emailToken, 'publicationId' => $publicationId]); - - if ($this->isMemberApiEnabled($contextId)) { - $mailable = new OrcidRequestAuthorAuthorization($context, $submission, $oauthUrl); - } else { - $mailable = new OrcidCollectAuthorId($context, $submission, $oauthUrl); - } - - // Set From to primary journal contact - $mailable->from($context->getData('contactEmail'), $context->getData('contactName')); - - // Send to author - $mailable->recipients([$author]); - Mail::send($mailable); - - if ($updateAuthor) { - Repo::author()->dao->update($author); - } - } - } - /** * handlePublishIssue sends all submissions for which the authors hava an ORCID and access token * to ORCID. This hook will be called on publication of a new issue. @@ -780,11 +1133,11 @@ public function sendAuthorMail($author, $updateAuthor = false) */ public function handlePublicationStatusChange($hookName, $args) { - $newPublication = & $args[0]; + $newPublication = &$args[0]; /** @var Publication $newPublication */ - $publication = & $args[1]; + $publication = &$args[1]; /** @var Publication $publication */ - $submission = & $args[2]; + $submission = &$args[2]; /** @var Submission $submission */ $request = Application::get()->getRequest(); @@ -799,38 +1152,6 @@ public function handlePublicationStatusChange($hookName, $args) } } - /** - * handleEditorAction handles promoting a submission to copyediting. - * - * @param string $hookName Name the hook was registered with - * @param array $args Hook arguments, &$submission, &$editorDecision, &$result, &$recommendation. - * - * @see EditorAction::recordDecision() The function calling the hook. - */ - public function handleEditorAction($hookName, $args) - { - $submission = $args[0]; - /** @var Submission $submission */ - $decision = $args[1]; - - if ($decision['decision'] == Decision::ACCEPT) { - $publication = $submission->getCurrentPublication(); - - if (isset($publication)) { - $authors = Repo::author()->getCollector() - ->filterByPublicationIds([$submission->getCurrentPublication()->getId()]) - ->getMany(); - - foreach ($authors as $author) { - $orcidAccessExpiresOn = Carbon\Carbon::parse($author->getData('orcidAccessExpiresOn')); - if ($author->getData('orcidAccessToken') == null || $orcidAccessExpiresOn->isPast()) { - $this->sendAuthorMail($author, true); - } - } - } - } - } - /** * sendSubmissionToOrcid posts JSON consisting of submission, journal and issue meta data * to ORCID profiles of submission authors. @@ -869,7 +1190,7 @@ public function sendSubmissionToOrcid($publication, $request) $authorsWithOrcid = []; foreach ($authors as $author) { if ($author->getOrcid() && $author->getData('orcidAccessToken')) { - $orcidAccessExpiresOn = Carbon\Carbon::parse($author->getData('orcidAccessExpiresOn')); + $orcidAccessExpiresOn = Carbon::parse($author->getData('orcidAccessExpiresOn')); if ($orcidAccessExpiresOn->isFuture()) { # Extract only the ORCID from the stored ORCID uri $orcid = basename(parse_url($author->getOrcid(), PHP_URL_PATH)); @@ -904,7 +1225,6 @@ public function sendSubmissionToOrcid($publication, $request) unset($orcidWork['put-code']); } - $headers = [ 'Content-type: application/vnd.orcid+json', 'Accept' => 'application/json', @@ -924,7 +1244,7 @@ public function sendSubmissionToOrcid($publication, $request) 'json' => $orcidWork, ] ); - } catch (\GuzzleHttp\Exception\ClientException $exception) { + } catch (ClientException $exception) { $reason = $exception->getResponse()->getBody(false); $this->logInfo("Publication fail: ${reason}"); return new JSONMessage(false); @@ -1014,17 +1334,17 @@ public function buildOrcidWork($publication, $context, $authors, $request, $issu $orcidWork = [ 'title' => [ 'title' => [ - 'value' => $publication->getLocalizedTitle($publicationLocale) ?? '' + 'value' => trim(strip_tags($publication->getLocalizedTitle($publicationLocale))) ?? '' ], 'subtitle' => [ - 'value' => $publication->getLocalizedSubTitle($publicationLocale) ?? '' + 'value' => trim(strip_tags($publication->getLocalizedData('subtitle', $publicationLocale))) ?? '' ] ], 'journal-title' => [ 'value' => $context->getName($publicationLocale) ?? '' ], 'short-description' => trim(strip_tags($publication->getLocalizedData('abstract', $publicationLocale))) ?? '', - 'type' => 'annotation', + 'external-ids' => [ 'external-id' => $this->buildOrcidExternalIds($submission, $publication, $context, $issue, $publicationUrl) ], @@ -1043,11 +1363,13 @@ public function buildOrcidWork($publication, $context, $authors, $request, $issu $bibtexCitation = trim(strip_tags($citationPlugin->getCitation($request, $submission, 'bibtex', $issue, $publication))); $orcidWork['citation'] = [ 'citation-type' => 'bibtex', - 'citation-value' => $bibtexCitation + 'citation-value' => $bibtexCitation, ]; + $orcidWork['type'] = 'journal-article'; + } elseif ($applicationName == 'ops') { + $orcidWork['type'] = 'preprint'; } - $translatedTitleAvailable = false; foreach ($supportedSubmissionLocales as $defaultLanguage) { if ($defaultLanguage !== $publicationLocale) { @@ -1063,26 +1385,6 @@ public function buildOrcidWork($publication, $context, $authors, $request, $issu return $orcidWork; } - - /** - * Parse issue year and publication date and use the older on of the two as - * the publication date of the ORCID work. - * - * @param null|mixed $issue - * - * @return array Associative array with year, month and day or only year - */ - private function buildOrcidPublicationDate($publication, $issue = null) - { - $publicationPublishDate = Carbon\Carbon::parse($publication->getData('datePublished')); - - return [ - 'year' => ['value' => $publicationPublishDate->format('Y')], - 'month' => ['value' => $publicationPublishDate->format('m')], - 'day' => ['value' => $publicationPublishDate->format('d')] - ]; - } - /** * Build the external identifiers ORCID JSON structure from article, journal and issue meta data. * @@ -1114,7 +1416,7 @@ private function buildOrcidExternalIds($submission, $publication, $context, $iss $pubIdType = $plugin->getPubIdType(); # Add article ids - $pubId = $publication->getData($pubIdType); + $pubId = $publication->getStoredPubId($pubIdType); if ($pubId) { $externalIds[] = [ @@ -1164,17 +1466,18 @@ private function buildOrcidExternalIds($submission, $publication, $context, $iss } # Add issue ids if they exist - $pubId = $issue->getStoredPubId('doi'); - $doiObject = $issue->getData('doiObject'); - if ($doiObject) { - $externalIds[] = [ - 'external-id-type' => self::PUBID_TO_ORCID_EXT_ID['doi'], - 'external-id-value' => $doiObject->getData('doi'), - 'external-id-url' => [ - 'value' => $doiObject->getResolvingUrl() - ], - 'external-id-relationship' => 'part-of' - ]; + if ($issue) { + $doiObject = $issue->getData('doiObject'); + if ($doiObject) { + $externalIds[] = [ + 'external-id-type' => self::PUBID_TO_ORCID_EXT_ID['doi'], + 'external-id-value' => $doiObject->getData('doi'), + 'external-id-url' => [ + 'value' => $doiObject->getResolvingUrl() + ], + 'external-id-relationship' => 'part-of' + ]; + } } } } else { @@ -1204,6 +1507,25 @@ private function buildOrcidExternalIds($submission, $publication, $context, $iss return $externalIds; } + /** + * Parse issue year and publication date and use the older on of the two as + * the publication date of the ORCID work. + * + * @param null|mixed $issue + * + * @return array Associative array with year, month and day or only year + */ + private function buildOrcidPublicationDate($publication, $issue = null) + { + $publicationPublishDate = Carbon::parse($publication->getData('datePublished')); + + return [ + 'year' => ['value' => $publicationPublishDate->format('Y')], + 'month' => ['value' => $publicationPublishDate->format('m')], + 'day' => ['value' => $publicationPublishDate->format('d')] + ]; + } + /** * Build associative array fitting for ORCID contributor mentions in an * ORCID work from the supplied Authors array. @@ -1266,65 +1588,35 @@ private function buildOrcidContributors($authors, $context, $publication) } /** - * Remove all data fields, which belong to an ORCID access token from the - * given Author object. Also updates fields in the db. + * handleEditorAction handles promoting a submission to copyediting. * - * @param Author $author object with ORCID access token + * @param string $hookName Name the hook was registered with + * @param array $args Hook arguments, &$submission, &$editorDecision, &$result, &$recommendation. + * + * @see EditorAction::recordDecision() The function calling the hook. */ - public function removeOrcidAccessToken($author, $saveAuthor = true) + public function handleEditorAction($hookName, $args) { - $author->setData('orcidAccessToken', null); - $author->setData('orcidAccessScope', null); - $author->setData('orcidRefreshToken', null); - $author->setData('orcidAccessExpiresOn', null); - $author->setData('orcidSandbox', null); - - if ($saveAuthor) { - Repo::author()->dao->update($author); - } - } + $submission = $args[0]; + /** @var Submission $submission */ + $decision = $args[1]; - /** - * @return string Path to a custom ORCID log file. - */ - public static function logFilePath() - { - return Config::getVar('files', 'files_dir') . '/orcid.log'; - } + if ($decision['decision'] == Decision::ACCEPT) { + $publication = $submission->getCurrentPublication(); - /** - * Write error message to log. - * - * @param string $message Message to write - */ - public function logError($message) - { - self::writeLog($message, 'ERROR'); - } + if (isset($publication)) { + $authors = Repo::author()->getCollector() + ->filterByPublicationIds([$submission->getCurrentPublication()->getId()]) + ->getMany(); - /** - * Write info message to log. - * - * @param string $message Message to write - */ - public function logInfo($message) - { - if ($this->getSetting($this->currentContextId, 'logLevel') === 'ERROR') { - return; + foreach ($authors as $author) { + $orcidAccessExpiresOn = Carbon::parse($author->getData('orcidAccessExpiresOn')); + if ($author->getData('orcidAccessToken') == null || $orcidAccessExpiresOn->isPast()) { + $this->sendAuthorMail($author, true); + } + } + } } - self::writeLog($message, 'INFO'); - } - - /** - * Write a message with specified level to log - * - * @param string $message Message to write - * @param string $level Error level to add to message - */ - private static function writeLog($message, $level) - { - $fineStamp = date('Y-m-d H:i:s') . substr(microtime(), 1, 4); - error_log("${fineStamp} ${level} ${message}\n", 3, self::logFilePath()); } /** @@ -1337,20 +1629,13 @@ public function setCurrentContextId($contextId) $this->currentContextId = $contextId; } - /** - * @return bool True if the ORCID Member API has been selected in this context. - */ - public function isMemberApiEnabled($contextId) - { - $apiUrl = $this->getSetting($contextId, 'orcidProfileAPIPath'); - return in_array($apiUrl, [ORCID_API_URL_MEMBER, ORCID_API_URL_MEMBER_SANDBOX]); - } - /** * Add mailable to the list of mailables in the application */ public function addMailable(string $hookName, array $args): void { - $args[0] = array_merge($args[0], [OrcidCollectAuthorId::class, OrcidRequestAuthorAuthorization::class]); + foreach ([OrcidCollectAuthorId::class, OrcidRequestAuthorAuthorization::class] as $mailable) { + $args[0]->push($mailable); + } } } diff --git a/OrcidProfileSettingsForm.inc.php b/OrcidProfileSettingsForm.inc.php deleted file mode 100644 index b3a8a920..00000000 --- a/OrcidProfileSettingsForm.inc.php +++ /dev/null @@ -1,130 +0,0 @@ - 'string', - 'orcidClientId' => 'string', - 'orcidClientSecret' => 'string', - 'sendMailToAuthorsOnPublication' => 'bool', - 'logLevel' => 'string', - 'isSandBox' => 'bool' - ]; - /** @var int $contextId */ - public $contextId; - - /** @var object $plugin */ - public $plugin; - - /** - * Constructor - * - * @param object $plugin - * @param int $contextId - */ - public function __construct(&$plugin, $contextId) - { - $this->contextId = $contextId; - $this->plugin = & $plugin; - - parent::__construct($plugin->getTemplateResource('settingsForm.tpl')); - - if (!$this->plugin->isGloballyConfigured()) { - $this->addCheck(new \PKP\form\validation\FormValidator( - $this, - 'orcidProfileAPIPath', - 'required', - 'plugins.generic.orcidProfile.manager.settings.orcidAPIPathRequired' - )); - } - $this->addCheck(new \PKP\form\validation\FormValidatorPost($this)); - $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); - $this->addCheck(new \PKP\form\validation\FormValidatorCustom($this, 'orcidClientId', 'required', 'plugins.generic.orcidProfile.manager.settings.orcidClientId.error', function ($clientId) { - if (preg_match('/^APP-[\da-zA-Z]{16}|(\d{4}-){3,}\d{3}[\dX]/', $clientId) == 1) { - $this->plugin->setEnabled(true); - return true; - } - $this->plugin->setEnabled(false); - })); - $this->addCheck(new \PKP\form\validation\FormValidatorCustom($this, 'orcidClientSecret', 'required', 'plugins.generic.orcidProfile.manager.settings.orcidClientSecret.error', function ($clientSecret) { - if (preg_match('/^(\d|-|[a-f]){36,64}/', $clientSecret) == 1) { - $this->plugin->setEnabled(true); - return true; - } - $this->plugin->setEnabled(false); - })); - } - - /** - * Initialize form data. - */ - public function initData() - { - $contextId = $this->contextId; - $plugin = & $this->plugin; - $this->_data = []; - foreach (self::CONFIG_VARS as $configVar => $type) { - $this->_data[$configVar] = $plugin->getSetting($contextId, $configVar); - } - } - - /** - * Assign form data to user-submitted data. - */ - public function readInputData() - { - $this->readUserVars(array_keys(self::CONFIG_VARS)); - } - - /** - * Fetch the form. - * - * @copydoc Form::fetch() - * - * @param null|mixed $template - */ - public function fetch($request, $template = null, $display = false) - { - $templateMgr = TemplateManager::getManager($request); - $templateMgr->assign('globallyConfigured', $this->plugin->isGloballyConfigured()); - $templateMgr->assign('pluginName', $this->plugin->getName()); - return parent::fetch($request, $template, $display); - } - - /** - * @copydoc Form::execute() - */ - public function execute(...$functionArgs) - { - $plugin = & $this->plugin; - $contextId = $this->contextId; - foreach (self::CONFIG_VARS as $configVar => $type) { - if ($configVar === 'orcidProfileAPIPath') { - $plugin->updateSetting($contextId, $configVar, trim($this->getData($configVar), "\"\';"), $type); - } else { - $plugin->updateSetting($contextId, $configVar, $this->getData($configVar), $type); - } - } - if (strpos($this->getData('orcidProfileAPIPath'), 'sandbox.orcid.org') == true) { - $plugin->updateSetting($contextId, 'isSandBox', true, 'bool'); - } - - parent::execute(...$functionArgs); - } -} diff --git a/classes/OrcidValidator.php b/classes/OrcidValidator.php new file mode 100644 index 00000000..b4c28f45 --- /dev/null +++ b/classes/OrcidValidator.php @@ -0,0 +1,39 @@ +plugin =& $plugin; + } + + /** + * @param $str + * @return bool + */ + public function validateClientId($str) { + $valid = false; + if (preg_match('/^APP-[\da-zA-Z]{16}|(\d{4}-){3,}\d{3}[\dX]/', $str) == 1) { + $valid = true; + } + return $valid; + } + + /** + * @param $str + * @return bool + */ + public function validateClientSecret($str) { + $valid = false; + if (preg_match('/^(\d|-|[a-f]){36,64}/', $str) == 1) { + $valid = true; + } + return $valid; + } + +} diff --git a/classes/form/OrcidProfileSettingsForm.php b/classes/form/OrcidProfileSettingsForm.php new file mode 100644 index 00000000..94a22d7d --- /dev/null +++ b/classes/form/OrcidProfileSettingsForm.php @@ -0,0 +1,142 @@ + 'string', + 'orcidClientId' => 'string', + 'orcidClientSecret' => 'string', + 'sendMailToAuthorsOnPublication' => 'bool', + 'logLevel' => 'string', + 'isSandBox' => 'bool', + 'country' => 'string', + 'city' => 'string' + + ); + /** @var $contextId int */ + var $contextId; + + /** @var $plugin object */ + var $plugin; + + var $validator; + + /** + * Constructor + * @param $plugin object + * @param $contextId int + */ + function __construct($plugin, $contextId) { + $this->contextId = $contextId; + $this->plugin = $plugin; + $orcidValidator = new OrcidValidator($plugin); + $this->validator = $orcidValidator; + parent::__construct($plugin->getTemplateResource('settingsForm.tpl')); + $this->addCheck(new \PKP\form\validation\FormValidatorPost($this)); + $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); + + if (!$this->plugin->isGloballyConfigured()) { + $this->addCheck(new \PKP\form\validation\FormValidator($this, 'orcidProfileAPIPath', 'required', 'plugins.generic.orcidProfile.manager.settings.orcidAPIPathRequired')); + $this->addCheck(new \PKP\form\validation\FormValidatorCustom($this, 'orcidClientId', 'required', 'plugins.generic.orcidProfile.manager.settings.orcidClientId.error', function ($clientId) { + return $this->validator->validateClientId($clientId); + })); + $this->addCheck(new \PKP\form\validation\FormValidatorCustom($this, 'orcidClientSecret', 'required', 'plugins.generic.orcidProfile.manager.settings.orcidClientSecret.error', function ($clientSecret) { + return $this->validator->validateClientSecret($clientSecret); + })); + } + + } + + /** + * Initialize form data. + */ + function initData() { + $contextId = $this->contextId; + $plugin =& $this->plugin; + $this->_data = array(); + foreach (self::CONFIG_VARS as $configVar => $type) { + $this->_data[$configVar] = $plugin->getSetting($contextId, $configVar); + } + } + + /** + * Assign form data to user-submitted data. + */ + function readInputData() { + $this->readUserVars(array_keys(self::CONFIG_VARS)); + } + + /** + * Fetch the form. + * @copydoc Form::fetch() + */ + function fetch($request, $template = null, $display = false) { + $templateMgr = TemplateManager::getManager($request); + $templateMgr->assign('globallyConfigured', $this->plugin->isGloballyConfigured()); + $templateMgr->assign('pluginName', $this->plugin->getName()); + $templateMgr->assign('applicationName', Application::get()->getName()); + return parent::fetch($request, $template, $display); + } + + /** + * @copydoc Form::execute() + */ + function execute(...$functionArgs) { + $plugin =& $this->plugin; + $contextId = $this->contextId; + foreach (self::CONFIG_VARS as $configVar => $type) { + if ($configVar === 'orcidProfileAPIPath') { + $plugin->updateSetting($contextId, $configVar, trim($this->getData($configVar), "\"\';"), $type); + } else { + $plugin->updateSetting($contextId, $configVar, $this->getData($configVar), $type); + } + } + if (strpos($this->getData("orcidProfileAPIPath"), "sandbox.orcid.org") == true) { + $plugin->updateSetting($contextId, "isSandBox", true, "bool"); + } + + parent::execute(...$functionArgs); + } + + public function _checkPrerequisites() { + $messages = array(); + + $clientId = $this->getData('orcidClientId'); + if (!$this->validator->validateClientId($clientId)) { + $messages[] = __('plugins.generic.orcidProfile.manager.settings.orcidClientId.error'); + } + $clientSecret = $this->getData('orcidClientSecret'); + if (!$this->validator->validateClientSecret($clientSecret)) { + $messages[] = __('plugins.generic.orcidProfile.manager.settings.orcidClientSecret.error'); + } + if (strlen($clientId) == 0 or strlen($clientSecret) == 0) { + $this->plugin->setEnabled(false); + } + return $messages; + } + + +} + diff --git a/classes/form/OrcidProfileStatusForm.php b/classes/form/OrcidProfileStatusForm.php new file mode 100644 index 00000000..e356ae4b --- /dev/null +++ b/classes/form/OrcidProfileStatusForm.php @@ -0,0 +1,101 @@ + 'string', + 'orcidClientId' => 'string', + 'orcidClientSecret' => 'string', + 'sendMailToAuthorsOnPublication' => 'bool', + 'logLevel' => 'string', + 'isSandBox' => 'bool' + ); + + /** @var $contextId int */ + var $contextId; + + /** @var $plugin object */ + var $plugin; + + /** @var OrcidValidator */ + var $validator; + + /** + * Constructor + * @param $plugin object + * @param $contextId int + */ + function __construct($plugin, $contextId) { + $this->contextId = $contextId; + $this->plugin = $plugin; + $orcidValidator = new OrcidValidator($plugin); + $this->validator = $orcidValidator; + parent::__construct($plugin->getTemplateResource('statusForm.tpl')); + + if (!$this->plugin->isGloballyConfigured()) { + + } + + } + + /** + * Initialize form data. + */ + function initData() { + $contextId = $this->contextId; + $plugin =& $this->plugin; + $this->_data = array(); + foreach (self::CONFIG_VARS as $configVar => $type) { + $this->_data[$configVar] = $plugin->getSetting($contextId, $configVar); + } + } + + + /** + * Fetch the form. + * @copydoc Form::fetch() + */ + function fetch($request, $template = null, $display = false) { + $contextId = $request->getContext()->getId(); + $clientId = $this->plugin->getSetting($contextId, 'orcidClientId'); + $clientSecret = $this->plugin->getSetting($contextId, 'orcidClientSecret'); + + $templateMgr = TemplateManager::getManager($request); + $aboutUrl = $request->getDispatcher()->url($request, ROUTE_PAGE, null, 'orcidapi', 'about', null); + $templateMgr->assign(array( + 'globallyConfigured' => $this->plugin->isGloballyConfigured(), + 'orcidAboutUrl' => $aboutUrl, + 'pluginEnabled' => $this->plugin->getEnabled($contextId), + 'clientIdValid' => $this->validator->validateClientId($clientId), + 'clientSecretValid' => $this->validator->validateClientSecret($clientSecret), + + + )); + return parent::fetch($request, $template, $display); + } + + +} + diff --git a/emailTemplates.xml b/emailTemplates.xml index d0a5c0e3..980bf98e 100644 --- a/emailTemplates.xml +++ b/emailTemplates.xml @@ -3,14 +3,14 @@ - - + + diff --git a/index.php b/index.php index 454047ec..f31ac0de 100644 --- a/index.php +++ b/index.php @@ -8,8 +8,8 @@ * @file index.php * * Copyright (c) 2015-2019 University of Pittsburgh - * Copyright (c) 2014-2020 Simon Fraser University - * Copyright (c) 2003-2020 John Willinsky + * Copyright (c) 2014-2021 Simon Fraser University + * Copyright (c) 2003-2021 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @ingroup plugins_generic_orcidProfile @@ -17,6 +17,4 @@ * */ -require_once('OrcidProfilePlugin.inc.php'); - -return new OrcidProfilePlugin(); +return new \APP\plugins\generic\orcidProfile\OrcidProfilePlugin(); diff --git a/locale/en_US/emails.po b/locale/en_US/emails.po index 7fb3d2ad..69788658 100644 --- a/locale/en_US/emails.po +++ b/locale/en_US/emails.po @@ -81,3 +81,9 @@ msgstr "ORCID OAuth authorization link" msgid "emailTemplate.variable.orcidAboutUrl" msgstr "URL to the page about ORCID" + +msgid "plugins.generic.orcidProfile.orcidRequestAuthorAuthorization.name" +msgstr "orcidRequestAuthorAuthorization" + +msgid "plugins.generic.orcidProfile.orcidCollectAuthorId.name" +msgstr "orcidCollectAuthorId" diff --git a/locale/en_US/locale.po b/locale/en_US/locale.po index eaf3ea15..ba3f7f3f 100644 --- a/locale/en_US/locale.po +++ b/locale/en_US/locale.po @@ -41,6 +41,9 @@ msgstr "Please configure the ORCID API access for use in pulling ORCID profile i msgid "plugins.generic.orcidProfile.manager.settings.description.globallyconfigured" msgstr "The ORCID API was configured globally by the host. The following credentials have been saved." +msgid "plugins.generic.orcidProfile.manager.settings.pluginDisabled" +msgstr "Due to validation errors, plugin was disabled." + msgid "plugins.generic.orcidProfile.manager.settings.orcidProfileAPIPath" msgstr "ORCID API" @@ -68,6 +71,9 @@ msgstr "Profile Access Scope" msgid "plugins.generic.orcidProfile.manager.settings.mailSectionTitle" msgstr "E-Mail Settings" +msgid "plugins.generic.orcidProfile.manager.settings.saved" +msgstr "Settings saved" + msgid "plugins.generic.orcidProfile.manager.settings.sendMailToAuthorsOnPublication" msgstr "Send e-mail to request ORCID authorization from authors when an article is accepted ie. sent to copy editing" @@ -83,6 +89,18 @@ msgstr "Errors" msgid "plugins.generic.orcidProfile.manager.settings.logLevel.all" msgstr "All" +msgid "plugins.generic.orcidProfile.manager.settings.city" +msgstr "City" + +msgid "plugins.generic.orcidProfile.manager.settings.country" +msgstr "Country" + +msgid "plugins.generic.orcidProfile.manager.settings.review" +msgstr "Review Location settings" + +msgid "plugins.generic.orcidProfile.manager.settings.review.help" +msgstr "Select City, Country for enabling sending reviewer information to ORCID" + msgid "plugins.generic.orcidProfile.author.accessDenied" msgstr "ORCID access was denied at" @@ -128,6 +146,9 @@ msgstr "You denied access to your ORCID record." msgid "plugins.generic.orcidProfile.authFailure" msgstr "The ORCID authorization link has already been used or is invalid." +msgid "plugins.generic.orcidProfile.invalidClient" +msgstr "Invalid client credentials" + msgid "plugins.generic.orcidProfile.failure.contact" msgstr "Please contact the journal manager with your name, ORCID iD, and details of your submission." @@ -209,3 +230,31 @@ msgstr "Invalid client ID" msgid "plugins.generic.orcidProfile.manager.settings.orcidClientSecret.error" msgstr "Invalid client secret" + +msgid "plugins.generic.orcidProfile.manager.status.description" +msgstr "OrcidProfile Plugin Status" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.enabled" +msgstr "OrcidProfile Plugin enabled" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.disabled" +msgstr "OrcidProfile Plugin disabled" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.global" +msgstr "Globally configured in config.inc.php" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.journal" +msgstr "Journal-wise configured" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.clientIdValid" +msgstr "Client Id is valid" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.clientIdInvalid" +msgstr "Client Id is invalid valid, please check your input" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.clientSecretValid" +msgstr "Client Secret is valid" + +msgid "plugins.generic.orcidProfile.manager.status.configuration.clientSecretInvalid" +msgstr "Client Secret is invalid valid, please check your credentials" + diff --git a/mailables/OrcidRequestAuthorAuthorization.php b/mailables/OrcidRequestAuthorAuthorization.php index e2c73df5..d4f217b7 100644 --- a/mailables/OrcidRequestAuthorAuthorization.php +++ b/mailables/OrcidRequestAuthorAuthorization.php @@ -17,6 +17,7 @@ use APP\journal\Journal; use APP\plugins\generic\orcidProfile\mailables\traits\OrcidVariables; +use APP\server\Server; use APP\submission\Submission; use PKP\mail\Mailable; use PKP\mail\traits\Configurable; @@ -34,7 +35,7 @@ class OrcidRequestAuthorAuthorization extends Mailable protected static ?string $emailTemplateKey = 'ORCID_REQUEST_AUTHOR_AUTHORIZATION'; protected static array $toRoleIds = [Role::ROLE_ID_AUTHOR]; - public function __construct(Journal $context, Submission $submission, string $oauthUrl) + public function __construct(Journal|Server $context, Submission $submission, string $oauthUrl) { parent::__construct([$context, $submission]); $this->setupOrcidVariables($oauthUrl); diff --git a/templates/orcidAbout.tpl b/templates/orcidAbout.tpl index 4876f39e..30b4ca40 100644 --- a/templates/orcidAbout.tpl +++ b/templates/orcidAbout.tpl @@ -6,7 +6,7 @@ * Copyright (c) 2018-2019 University Library Heidelberg * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * - * Page template to display from the OrcidHandler to show ORCID verification success or failure. + * Page template to display from the OrcidProfileHandler to show ORCID verification success or failure. *} {include file="frontend/components/header.tpl"} diff --git a/templates/orcidProfile.tpl b/templates/orcidProfile.tpl index 9b563262..a21f8b4d 100644 --- a/templates/orcidProfile.tpl +++ b/templates/orcidProfile.tpl @@ -12,9 +12,9 @@ {capture name=orcidButton assign=orcidButton}