Skip to content

Commit

Permalink
pkp#229 OrcidProfilePlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
withanage committed Feb 11, 2023
1 parent 8d66d49 commit b07a3d4
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 39 deletions.
102 changes: 69 additions & 33 deletions OrcidProfileHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use APP\template\TemplateManager;
use Carbon\Carbon;
use PKP\core\Core;
use PKP\plugins\Hook;
use PKP\plugins\PluginRegistry;
use PKP\security\authorization\PKPSiteAccessPolicy;
use PKP\security\authorization\UserRequiredPolicy;
Expand All @@ -34,10 +35,19 @@ class OrcidProfileHandler extends Handler
{
public const TEMPLATE = 'orcidVerify.tpl';
const ORCIDPROFILEPLUGIN = 'orcidprofileplugin';
private bool $isSandBox;
private ?\PKP\plugins\Plugin $plugin;



public function __construct()
{
$request = Application::get()->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;
}

/**
Expand Down Expand Up @@ -78,20 +88,19 @@ public function orcidAuthorize($args, $request)
{
$context = $request->getContext();
$op = $request->getRequestedOp();
$plugin = PluginRegistry::getPlugin('generic', self::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'],
]
Expand All @@ -103,15 +112,15 @@ 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')) {
case 'register':
// 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',
Expand All @@ -129,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',
Expand Down Expand Up @@ -159,6 +168,36 @@ public function orcidAuthorize($args, $request)
';
break;
case 'profile':

Hook::add('Schema::get::user', function ($hookName, $args) {
$schema = &$args[0];

$schema->properties->orcidAccessToken = (object)[
'type' => 'string',
'apiSummary' => true,
'validation' => ['nullable']
];
$schema->properties->orcidAccessScope = (object)[
'type' => 'string',
'apiSummary' => true,
'validation' => ['nullable']
];
$schema->properties->orcidRefreshToken = (object)[
'type' => 'string',
'apiSummary' => true,
'validation' => ['nullable']
];
$schema->properties->orcidAccessExpiresOn = (object)[
'type' => 'string',
'apiSummary' => true,
'validation' => ['nullable']
];
$schema->properties->orcidAccessDenied = (object)[
'type' => 'string',
'apiSummary' => true,
'validation' => ['nullable']
];
});
$user = $request->getUser();
// Store the access token and other data for the user
$this->_setOrcidData($user, $orcidUri, $response);
Expand Down Expand Up @@ -188,9 +227,8 @@ public function orcidVerify($args, $request)
$templateMgr = TemplateManager::getManager($request);
$context = $request->getContext();
$contextId = $context == null ? \PKP\core\PKPApplication::CONTEXT_ID_NONE : $context->getId();
$plugin = PluginRegistry::getPlugin('generic', self::ORCIDPROFILEPLUGIN);

$templatePath = $plugin->getTemplateResource(self::TEMPLATE);
$templatePath = $this->plugin->getTemplateResource(self::TEMPLATE);


$publicationId = $request->getUserVar('state');
Expand All @@ -199,8 +237,6 @@ public function orcidVerify($args, $request)
->filterByPublicationIds([$publicationId])
->getMany();

$isSandBox = $plugin->getSetting($contextId, 'orcidProfileAPIPath') == ORCID_API_URL_MEMBER_SANDBOX ||
$plugin->getSetting($contextId, 'orcidProfileAPIPath') == ORCID_API_URL_PUBLIC_SANDBOX;
$publication = Repo::publication()->get($publicationId);

$authorToVerify = null;
Expand All @@ -226,7 +262,7 @@ public function orcidVerify($args, $request)

if ($authorToVerify == null) {
// no Author exists in the database with the supplied orcidEmailToken
$plugin->logError('OrcidProfileHandler::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;
Expand All @@ -243,27 +279,27 @@ public function orcidVerify($args, $request)
$authorToVerify->setData('orcidAccessExpiresOn', null);
$authorToVerify->setData('orcidEmailToken', null);
Repo::author()->dao->update($authorToVerify);
$plugin->logError('OrcidProfileHandler::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',
Expand All @@ -274,30 +310,30 @@ public function orcidVerify($args, $request)
]
);
if ($response->getStatusCode() != 200) {
$plugin->logError('OrcidProfileHandler::orcidverify - unexpected response: ' . $response->getStatusCode());
$this->plugin->logError('OrcidProfileHandler::orcidverify - unexpected response: ' . $response->getStatusCode());
$templateMgr->assign('authFailure', true);

}
$response = json_decode($response->getBody(), true);

$plugin->logInfo('Response body: ' . print_r($response, true));
$this->plugin->logInfo('Response body: ' . print_r($response, true));
if (($response['error'] ?? null) === 'invalid_grant') {
$plugin->logError('Authorization code invalid, maybe already used');
$this->plugin->logError('Authorization code invalid, maybe already used');
$templateMgr->assign('authFailure', true);

}
if (isset($response['error'])) {
$plugin->logError("Invalid ORCID response: " . $response['error']);
$this->plugin->logError("Invalid ORCID response: " . $response['error']);
$templateMgr->assign('authFailure', true);
}
// Set the orcid id using the full https uri
$orcidUri = ($plugin->getSetting($contextId, 'isSandBox') == true ? ORCID_URL_SANDBOX : ORCID_URL) . $response['orcid'];
$orcidUri = ($this->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);
}
$authorToVerify->setOrcid($orcidUri);
if (in_array($plugin->getSetting($contextId, 'orcidProfileAPIPath'), [ORCID_API_URL_MEMBER_SANDBOX, ORCID_API_URL_PUBLIC_SANDBOX])) {
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']);
Expand All @@ -309,10 +345,10 @@ public function orcidVerify($args, $request)
$authorToVerify->setData('orcidEmailToken', null);
$this->_setOrcidData($authorToVerify, $orcidUri, $response);
Repo::author()->dao->update($authorToVerify);
if ($plugin->isMemberApiEnabled($contextId)) {
if ($this->plugin->isMemberApiEnabled($contextId)) {
if ($publication->getData('status') == PKPSubmission::STATUS_PUBLISHED) {
$templateMgr->assign('sendSubmission', true);
$sendResult = $plugin->sendSubmissionToOrcid($publication, $request);
$sendResult = $this->plugin->sendSubmissionToOrcid($publication, $request);
if ($sendResult === true || (is_array($sendResult) && $sendResult[$response['orcid']])) {
$templateMgr->assign('sendSubmissionSuccess', true);
}
Expand All @@ -323,13 +359,13 @@ public function orcidVerify($args, $request)

$templateMgr->assign([
'verifySuccess' => true,
'orcidIcon' => $plugin->getIcon()
'orcidIcon' => $this->plugin->getIcon()
]);


} catch (\GuzzleHttp\Exception\ClientException $exception) {
$reason = $exception->getResponse()->getBody(false);
$plugin->logInfo("Publication fail: ${reason}");
$this->plugin->logInfo("Publication fail: ${reason}");

}
$templateMgr->assign('authFailure', true);
Expand All @@ -350,6 +386,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;
}

/**
Expand All @@ -363,10 +400,9 @@ 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', self::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'));
}


Expand Down
14 changes: 8 additions & 6 deletions OrcidProfilePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
use APP\plugins\generic\orcidProfile\mailables\OrcidRequestAuthorAuthorization;
use APP\plugins\generic\orcidProfile\OrcidProfileHanlder;
use APP\template\TemplateManager;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use PKP\components\forms\FieldOptions;
use PKP\components\forms\publication\ContributorForm;
Expand Down Expand Up @@ -108,7 +109,7 @@ public function register($category, $path, $mainContextId = null)

// Add more ORCiD fields to user Schema
Hook::add('Schema::get::user', function ($hookName, $args) {
$schema = $args[0];
$schema = &$args[0];

$schema->properties->orcidAccessToken = (object)[
'type' => 'string',
Expand Down Expand Up @@ -148,7 +149,7 @@ public function register($category, $path, $mainContextId = null)

// Add more ORCiD fields to author Schema
Hook::add('Schema::get::author', function ($hookName, $args) {
$schema = $args[0];
$schema = &$args[0];

$schema->properties->orcidSandbox = (object)[
'type' => 'string',
Expand Down Expand Up @@ -194,7 +195,6 @@ public function register($category, $path, $mainContextId = null)

Hook::add('Mailer::Mailables', [$this, 'addMailable']);

#Hook::add('Form::config::before', [$this, 'addOrcidFormFields']);
Hook::add('Author::edit', [$this, 'handleAuthorFormExecute']);

Hook::add('Form::config::before', [$this, 'addOrcidFormFields']);
Expand All @@ -215,6 +215,7 @@ function addOrcidFormFields($hookName, $form): bool
{

if (!$form instanceof ContributorForm) return Hook::CONTINUE;
$publication = $form->submission->getCurrentPublication();
$form->addField(new FieldOptions('requestOrcidAuthorization', [
'label' => __('plugins.generic.orcidProfile.verify.title'),
'options' => [
Expand Down Expand Up @@ -549,6 +550,7 @@ public function handleUserPublicProfileDisplay($hookName, $params)
$request = Application::get()->getRequest();
$context = $request->getContext();
$user = $request->getUser();

$contextId = ($context == null) ? 0 : $context->getId();
$targetOp = 'profile';
$templateMgr->assign(
Expand Down Expand Up @@ -955,7 +957,7 @@ public function handleEditorAction($hookName, $args)
->getMany();

foreach ($authors as $author) {
$orcidAccessExpiresOn = Carbon\Carbon::parse($author->getData('orcidAccessExpiresOn'));
$orcidAccessExpiresOn = Carbon::parse($author->getData('orcidAccessExpiresOn'));
if ($author->getData('orcidAccessToken') == null || $orcidAccessExpiresOn->isPast()) {
$this->sendAuthorMail($author, true);
}
Expand Down Expand Up @@ -1002,7 +1004,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));
Expand Down Expand Up @@ -1209,7 +1211,7 @@ public function buildOrcidWork($publication, $context, $authors, $request, $issu
*/
private function buildOrcidPublicationDate($publication, $issue = null)
{
$publicationPublishDate = Carbon\Carbon::parse($publication->getData('datePublished'));
$publicationPublishDate = Carbon::parse($publication->getData('datePublished'));

return [
'year' => ['value' => $publicationPublishDate->format('Y')],
Expand Down

0 comments on commit b07a3d4

Please sign in to comment.