From 57183cf559fb56621c28e02faad9cdc358635bf5 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Sat, 3 May 2025 18:34:05 +0300 Subject: [PATCH] Dispatch and Hydrate --- .../resources/locales/en_US/orcid_source.po | 7 + .../OrcidSourceCollectorsController.php | 38 +- .../src/Controller/OrcidSourcesController.php | 2 +- .../src/Model/Entity/PetitionOrcid.php | 49 +++ .../Table/OrcidSourceCollectorsTable.php | 387 +++++++----------- .../src/Model/Table/OrcidSourcesTable.php | 6 +- .../src/Model/Table/OrcidTokensTable.php | 8 +- .../src/Model/Table/PetitionOrcidsTable.php | 156 +++++++ .../OrcidSource/src/config/plugin.json | 13 + .../OrcidSourceCollectors/dispatch.inc | 35 +- .../templates/element/authenticate.php | 51 +++ .../OrcidSource/templates/element/preview.php | 50 +++ .../OrcidSource/webroot/css/orcid-source.css | 3 - 13 files changed, 521 insertions(+), 284 deletions(-) create mode 100644 app/plugins/OrcidSource/src/Model/Entity/PetitionOrcid.php create mode 100644 app/plugins/OrcidSource/src/Model/Table/PetitionOrcidsTable.php create mode 100644 app/plugins/OrcidSource/templates/element/authenticate.php create mode 100644 app/plugins/OrcidSource/templates/element/preview.php diff --git a/app/plugins/OrcidSource/resources/locales/en_US/orcid_source.po b/app/plugins/OrcidSource/resources/locales/en_US/orcid_source.po index a19d45509..5d0340228 100644 --- a/app/plugins/OrcidSource/resources/locales/en_US/orcid_source.po +++ b/app/plugins/OrcidSource/resources/locales/en_US/orcid_source.po @@ -55,6 +55,9 @@ msgstr "Access token not configured (try resaving configuration)" msgid "error.param.notfound" msgstr "{0} was not found" +msgid "error.orcid_source.no_orcid" +msgstr "ORCID identifier missing from response." + msgid "field.OrcidSources.api_type" msgstr "API Type" @@ -79,4 +82,8 @@ msgstr "Authenticate with ORCID" msgid "information.OrcidSourceCollectors.sign_in" msgstr "Sign in with your ORCID account to securely verify your ORCID iD." +msgid "result.OrcidSourceCollector.collected" +msgstr "Obtained ORCID Identifier {0}" +msgid "result.orcid.saved" +msgstr "ORCID Token recorded" diff --git a/app/plugins/OrcidSource/src/Controller/OrcidSourceCollectorsController.php b/app/plugins/OrcidSource/src/Controller/OrcidSourceCollectorsController.php index 880e01872..6fcf1339c 100644 --- a/app/plugins/OrcidSource/src/Controller/OrcidSourceCollectorsController.php +++ b/app/plugins/OrcidSource/src/Controller/OrcidSourceCollectorsController.php @@ -30,17 +30,16 @@ namespace OrcidSource\Controller; use App\Controller\StandardEnrollerController; -use App\Lib\Enum\PetitionActionEnum; use Cake\Datasource\EntityInterface; use Cake\Event\EventInterface; use Cake\Http\Response; use Cake\ORM\TableRegistry; -use Cake\Routing\RouteBuilder; use Cake\Routing\Router; use OrcidSource\Lib\Enum\OrcidSourceScopeEnum; class OrcidSourceCollectorsController extends StandardEnrollerController { protected $OrcidSources; + protected $PetitionOrcids; public $paginate = [ 'order' => [ @@ -57,6 +56,7 @@ class OrcidSourceCollectorsController extends StandardEnrollerController { public function beforeFilter(\Cake\Event\EventInterface $event) { $this->OrcidSources = TableRegistry::getTableLocator()->get('OrcidSource.OrcidSources'); + $this->PetitionOrcids = TableRegistry::getTableLocator()->get('OrcidSource.PetitionOrcids'); return parent::beforeFilter($event); } @@ -92,7 +92,6 @@ public function beforeRender(EventInterface $event) { public function dispatch(string $id) { $request = $this->getRequest(); $session = $request->getSession(); - $username = $session->read('Auth.external.user'); $op = $this->requestParam('op'); $code = $this->getRequest()->getQuery('code') ?? null; @@ -124,7 +123,7 @@ public function dispatch(string $id) { // Let's authenticate first if ($op == 'authenticate') { $this->authenticate($id, $PluginServerEntity); - } else if (!empty($code)) { + } else if (!empty($code) && $op !== 'savetoken') { $response = $PluginServersTable->exchangeCode( $id, $code, @@ -134,21 +133,36 @@ public function dispatch(string $id) { '?' => ['petition_id' => $petition->id], ] ), + false ); // Use the response and save the data to petitions table + if(empty($response->orcid)) { + throw new \RuntimeException(__d('orcid_source', 'error.orcid_source.no_orcid')); + } + $this->set('vv_orcid', $response->orcid); + $this->set('vv_token', $response); + } if (!empty($code) && $op === 'savetoken') { + $orcid_token = $this->requestParam('orcid_token'); + $this->PetitionOrcids->record( + petitionId: $petition->id, + enrollmentFlowStepId: $oricdSourceEntity->enrollment_flow_step_id, + orcidToken: $orcid_token, + orcidSourceCollectorId: (int)$id, + ); + // On success, indicate the step is completed and generate a redirect + // to the next step + + return $this->finishStep( + enrollmentFlowStepId: $oricdSourceEntity->enrollment_flow_step_id, + petitionId: $petition->id, + comment: __d('orcid_source', 'result.orcid.saved') + ); } else { // Fall Through. Let the view render } } - catch(\OverflowException $e) { - // The requested Source Key is already attached to an External Identity, so we throw - // an error now rather than wait until finalization - - // Flag the Petition as a duplicate - $Petitions = TableRegistry::getTableLocator()->get("Petitions"); - } catch(\Exception $e) { $this->Flash->error($e->getMessage()); } @@ -165,8 +179,6 @@ public function dispatch(string $id) { * @param string|int $id ID of the collector * @param EntityInterface $serverCfg ORCID Server configuration * @return void - * @throws \Exception If authentication fails - * @throws \OverflowException If source key is already attached * @since COmanage Registry v5.2.0 */ protected function authenticate(string|int $id, EntityInterface $serverCfg): void diff --git a/app/plugins/OrcidSource/src/Controller/OrcidSourcesController.php b/app/plugins/OrcidSource/src/Controller/OrcidSourcesController.php index e114844da..6d838a3db 100644 --- a/app/plugins/OrcidSource/src/Controller/OrcidSourcesController.php +++ b/app/plugins/OrcidSource/src/Controller/OrcidSourcesController.php @@ -1,6 +1,6 @@ + */ + protected $_accessible = [ + '*' => true, + 'id' => false, + 'slug' => false, + ]; +} diff --git a/app/plugins/OrcidSource/src/Model/Table/OrcidSourceCollectorsTable.php b/app/plugins/OrcidSource/src/Model/Table/OrcidSourceCollectorsTable.php index da9578c40..3919feb02 100644 --- a/app/plugins/OrcidSource/src/Model/Table/OrcidSourceCollectorsTable.php +++ b/app/plugins/OrcidSource/src/Model/Table/OrcidSourceCollectorsTable.php @@ -29,256 +29,171 @@ namespace OrcidSource\Model\Table; -use Cake\Datasource\ConnectionManager; -use Cake\Datasource\EntityInterface; -use Cake\ORM\Query; -use Cake\ORM\RulesChecker; +use App\Model\Entity\Petition; use Cake\ORM\Table; use Cake\ORM\TableRegistry; use Cake\Validation\Validator; use App\Lib\Enum\PetitionActionEnum; class OrcidSourceCollectorsTable extends Table { - use \App\Lib\Traits\AutoViewVarsTrait; - use \App\Lib\Traits\CoLinkTrait; - use \App\Lib\Traits\LayoutTrait; - use \App\Lib\Traits\LabeledLogTrait; - use \App\Lib\Traits\PermissionsTrait; - use \App\Lib\Traits\PrimaryLinkTrait; - use \App\Lib\Traits\TableMetaTrait; - use \App\Lib\Traits\ValidationTrait; - use \App\Lib\Traits\TabTrait; - - /** - * Perform Cake Model initialization. - * - * @since COmanage Registry v5.2.0 - * @param array $config Configuration options passed to constructor - */ - - public function initialize(array $config): void { - parent::initialize($config); - - $this->addBehavior('Changelog'); - $this->addBehavior('Log'); - $this->addBehavior('Timestamp'); - - $this->setTableType(\App\Lib\Enum\TableTypeEnum::Configuration); - - // Define associations - - $this->belongsTo('EnrollmentFlowSteps'); - $this->belongsTo('ExternalIdentitySources'); - - $this->setDisplayField('id'); - - $this->setPrimaryLink('enrollment_flow_step_id'); - $this->setRequiresCO(true); - $this->setAllowLookupPrimaryLink(['dispatch', 'display']); - - // All the tabs share the same configuration in the ModelTable file - $this->setTabsConfig( - [ - // Ordered list of Tabs - 'tabs' => ['EnrollmentFlowSteps', 'OrcidSource.OrcidSourceCollectors'], - // What actions will include the subnavigation header - 'action' => [ - // If a model renders in a subnavigation mode in edit/view mode, it cannot - // render in index mode for the same use case/context - // XXX edit should go first. - 'EnrollmentFlowSteps' => ['edit', 'view'], - 'OrcidSource.OrcidSourceCollectors' => ['edit'], - ] - ] - ); - - $this->setAutoViewVars([ - 'externalIdentitySources' => [ - 'type' => 'select', - 'model' => 'ExternalIdentitySources', - 'where' => ['plugin' => 'OrcidSource.OrcidSources'] - ] - ]); - - $this->setPermissions([ - // Actions that operate over an entity (ie: require an $id) - 'entity' => [ - 'delete' => false, // Delete the pluggable object instead - 'dispatch' => true, - 'display' => true, - 'edit' => ['platformAdmin', 'coAdmin'], - 'view' => ['platformAdmin', 'coAdmin'] - ], - // Actions that operate over a table (ie: do not require an $id) - 'table' => [ - 'add' => false, // This is added by the parent model - 'index' => ['platformAdmin', 'coAdmin'] - ] - ]); - } - - /** - * Perform steps necessary to hydrate the Person record as part of Petition finalization. - * - * @since COmanage Registry v5.2.0 - * @param int $id Env Source Collector ID - * @param Petition $petition Petition - * @return bool true on success - * @throws OverflowException - * @throws RuntimeException - */ - - public function hydrate(int $id, \App\Model\Entity\Petition $petition) { - $cfg = $this->get($id); - - $PetitionHistoryRecords = TableRegistry::getTableLocator()->get('PetitionHistoryRecords'); - - // At this point there is a Person record allocated and stored in the Petition. - // We need to sync the OrcidSource Identity (which is cached in env_source_identities) - // to the Enrollee Person. - - // We need the Source Key to sync, which is available via the OrcidSourceIdentity. - - $pei = $this->PetitionEnvIdentities->find() - ->where(['petition_id' => $petition->id]) - ->contain(['OrcidSourceIdentities']) - ->first(); - - if(!empty($pei->env_source_identity->source_key)) { - $ExtIdentitySources = TableRegistry::getTableLocator()->get('ExternalIdentitySources'); - - // If this is a duplicate enrollment for this External Identity (ie: there is - // already an External Identity associated with this Petition Env Identity) we - // need to check for that and throw a duplicate error here. If we allow it to run, - // the Pipeline Sync code will find the existing External Identity and merge this - // request to that record. (In a way, this is technically OK since it's the same - // external record, but this will appear unintuitively as a successful enrollment - // when most deployments will want to treat it as a duplicate.) - - // This will throw OverflowException on duplicate - $this->checkDuplicate($cfg->external_identity_source_id, $pei->env_source_identity->source_key); - - try { - // Continue on to process the sync - $status = $ExtIdentitySources->sync( - id: $cfg->external_identity_source_id, - sourceKey: $pei->env_source_identity->source_key, - personId: $petition->enrollee_person_id, - syncOnly: true - ); - - $PetitionHistoryRecords->record( - petitionId: $petition->id, - enrollmentFlowStepId: $cfg->enrollment_flow_step_id, - action: PetitionActionEnum::Finalized, - comment: __d('env_source', 'result.pipeline.status', [$status]) + use \App\Lib\Traits\AutoViewVarsTrait; + use \App\Lib\Traits\CoLinkTrait; + use \App\Lib\Traits\LayoutTrait; + use \App\Lib\Traits\LabeledLogTrait; + use \App\Lib\Traits\PermissionsTrait; + use \App\Lib\Traits\PrimaryLinkTrait; + use \App\Lib\Traits\TableMetaTrait; + use \App\Lib\Traits\ValidationTrait; + use \App\Lib\Traits\TabTrait; + + /** + * Perform Cake Model initialization. + * + * @since COmanage Registry v5.2.0 + * @param array $config Configuration options passed to constructor + */ + + public function initialize(array $config): void { + parent::initialize($config); + + $this->addBehavior('Changelog'); + $this->addBehavior('Log'); + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Configuration); + + // Define associations + + $this->belongsTo('EnrollmentFlowSteps'); + $this->belongsTo('ExternalIdentitySources'); + + $this->setDisplayField('id'); + + $this->setPrimaryLink('enrollment_flow_step_id'); + $this->setRequiresCO(true); + $this->setAllowLookupPrimaryLink(['dispatch', 'display']); + + // All the tabs share the same configuration in the ModelTable file + $this->setTabsConfig( + [ + // Ordered list of Tabs + 'tabs' => ['EnrollmentFlowSteps', 'OrcidSource.OrcidSourceCollectors'], + // What actions will include the subnavigation header + 'action' => [ + // If a model renders in a subnavigation mode in edit/view mode, it cannot + // render in index mode for the same use case/context + // XXX edit should go first. + 'EnrollmentFlowSteps' => ['edit', 'view'], + 'OrcidSource.OrcidSourceCollectors' => ['edit'], + ] + ] ); - } - catch(\Exception $e) { - // We allow an error in the sync process (probably a duplicate record) to interrupt - // finalization since it could result in an inconsistent state (multiple Person - // records for the same External Identity). We don't bother recording Petition History - // here though since we're about to rollback. - - $this->llog('error', 'Sync failure during hydration of Petition ' . $petition->id . ': ' . $e->getMessage()); - - throw new \RuntimeException($e->getMessage()); - } - } else { - // If there's no source key (which is unlikely) we record an error but don't try - // to abort finalization. - - $this->llog('error', 'No source key found during hydration of Petition ' . $petition->id); - - $PetitionHistoryRecords->record( - petitionId: $petition->id, - enrollmentFlowStepId: $cfg->enrollment_flow_step_id, - action: PetitionActionEnum::Finalized, - comment: __d('env_source', 'error.source_key') - ); - } - - return true; - } - /** - * Parse the environment values as per the configuration. - * - * @since COmanag Registry v5.2.0 - * @param OrcidSource $envSource OrcidSource configuration entity - * @return array Array of env variables and their parsed values - * @throws InvalidArgumentException - */ - - public function parse(\OrcidSource\Model\Entity\OrcidSource $envSource): array { - // The filtered set of variables to return - $ret = []; - - // XXX getenv() does not return all the environmental variables. We need to check - // one by one. - if(!empty($envSource->lookaside_file)) { - // The look aside file is for debugging purposes. If the file is specified but not found, - // we throw an error to prevent unintended configurations. - - return $this->loadFromLookasideFile($envSource->lookaside_file, $envSource); + $this->setAutoViewVars([ + 'externalIdentitySources' => [ + 'type' => 'select', + 'model' => 'ExternalIdentitySources', + 'where' => ['plugin' => 'OrcidSource.OrcidSources'] + ] + ]); + + $this->setPermissions([ + // Actions that operate over an entity (ie: require an $id) + 'entity' => [ + 'delete' => false, // Delete the pluggable object instead + 'dispatch' => true, + 'display' => true, + 'edit' => ['platformAdmin', 'coAdmin'], + 'view' => ['platformAdmin', 'coAdmin'] + ], + // Actions that operate over a table (ie: do not require an $id) + 'table' => [ + 'add' => false, // This is added by the parent model + 'index' => ['platformAdmin', 'coAdmin'] + ] + ]); } - // We walk through our configuration and only copy the variables that were configured - foreach($envSource->getVisible() as $field) { - // We only want the fields starting env_ (except env_source_id, which is changelog metadata) - - if(strncmp($field, "env_", 4)==0 && $field != "env_source_id" - && !empty($envSource->$field) // This field is configured with an env var name - && getenv($envSource->$field) // This env var is populated - ) { - // Note we're using the OrcidSource field name (eg: env_name_given) as the key - // and not the configured variable name (which might be something like SHIB_FIRST_NAME) - $ret[$field] = getenv($envSource->$field); - } + /** + * Perform steps necessary to hydrate the Person record as part of Petition finalization. + * + * @param int $id Env Source Collector ID + * @param Petition $petition Petition + * @return bool true on success + * @since COmanage Registry v5.2.0 + */ + + public function hydrate(int $id, \App\Model\Entity\Petition $petition) { + $orcidSourceCollectorsEntity = $this->get($id); + + $PetitionHistoryRecords = TableRegistry::getTableLocator()->get('PetitionHistoryRecords'); + $PetitionOrcids = TableRegistry::getTableLocator()->get('OrcidSource.PetitionOrcids'); + $OrcidSources = TableRegistry::getTableLocator()->get('OrcidSource.OrcidSources'); + $orcid_source = $OrcidSources->find() + ->where(['external_identity_source_id' => $orcidSourceCollectorsEntity->external_identity_source_id]) + ->first(); + + $pOricd = $PetitionOrcids + ->find() + ->where( + ['petition_id' => $petition->id, 'orcid_source_collector_id' => $id]) + ->first(); + + if(!empty($pOricd->orcid_token)) { + // Copy the Identifier to the Person record in accordance with the configuration + $token = unserialize($pOricd->orcid_token); + $data = [ + 'orcid_identifier' => $token->orcid, + 'access_token' => $token->access_token, + 'refresh_token' => $token->refresh_token ?? '', + 'id_token' => $token->id_token ?? '', + 'orcid_source_id' => $orcid_source->id + ]; + + + $OrcidTokens = TableRegistry::getTableLocator()->get('OrcidSource.OrcidTokens'); + + $OrcidTokens->upsertOrFail( + data: $data, + whereClause: [ + 'orcid_source_id' => $orcid_source->id, + 'orcid_identifier' => $token->orcid + ], + ); + + // Record Petition History + $PetitionHistoryRecords->record( + petitionId: $petition->id, + enrollmentFlowStepId: $orcidSourceCollectorsEntity->enrollment_flow_step_id, + action: PetitionActionEnum::AttributesUpdated, + comment: __d('orcid_source', 'result.orcid.saved') + ); + } + + return 'provision'; } - return $ret; - } - - /** - * Insert or update a Petition Env Identity. - * - * @since COmanage Registry v5.2.0 - * @param int $id Env Source Collector ID - * @param int $petitionId Petition ID - * @param array $attributes Env Sounce Attributes - * @return bool true on success - * @throws InvalidArgumentException - * @throws OverflowException - * @throws PersistenceFailedException - */ + /** + * Set validation rules. + * + * @since COmanage Registry v5.2.0 + * @param Validator $validator Validator + * @return Validator Validator + */ - public function upsert(int $id, int $petitionId, array $attributes) { + public function validationDefault(Validator $validator): Validator { + $schema = $this->getSchema(); - return true; - } + $validator->add('enrollment_flow_step_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('enrollment_flow_step_id'); - /** - * Set validation rules. - * - * @since COmanage Registry v5.2.0 - * @param Validator $validator Validator - * @return Validator Validator - */ + $validator->add('external_identity_source_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('external_identity_source_id'); - public function validationDefault(Validator $validator): Validator { - $schema = $this->getSchema(); - - $validator->add('enrollment_flow_step_id', [ - 'content' => ['rule' => 'isInteger'] - ]); - $validator->notEmptyString('enrollment_flow_step_id'); - - $validator->add('external_identity_source_id', [ - 'content' => ['rule' => 'isInteger'] - ]); - $validator->notEmptyString('external_identity_source_id'); - - return $validator; - } + return $validator; + } } \ No newline at end of file diff --git a/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php b/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php index 7070ca925..cab870687 100644 --- a/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php +++ b/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php @@ -73,9 +73,9 @@ public function initialize(array $config): void { $this->belongsTo('ExternalIdentitySources'); $this->belongsTo('Servers'); -// $this->hasMany('OrcidSource.OrcidTokens') -// ->setDependent(true) -// ->setCascadeCallbacks(true); + $this->hasMany('OrcidSource.OrcidTokens') + ->setDependent(true) + ->setCascadeCallbacks(true); $this->setDisplayField('id'); diff --git a/app/plugins/OrcidSource/src/Model/Table/OrcidTokensTable.php b/app/plugins/OrcidSource/src/Model/Table/OrcidTokensTable.php index 3249b2378..9a2519a1c 100644 --- a/app/plugins/OrcidSource/src/Model/Table/OrcidTokensTable.php +++ b/app/plugins/OrcidSource/src/Model/Table/OrcidTokensTable.php @@ -32,14 +32,16 @@ use Cake\ORM\Table; use Cake\Utility\Security; use Cake\Validation\Validator; -use OrcidSource\Model\Entity\EventInterface; +use Cake\Event\EventInterface; class OrcidTokensTable extends Table { use \App\Lib\Traits\ChangelogBehaviorTrait; use \App\Lib\Traits\LabeledLogTrait; use \App\Lib\Traits\PermissionsTrait; + use \App\Lib\Traits\PrimaryLinkTrait; use \App\Lib\Traits\QueryModificationTrait; use \App\Lib\Traits\TableMetaTrait; + use \App\Lib\Traits\UpsertTrait; use \App\Lib\Traits\ValidationTrait; // Cache of Table Models @@ -62,7 +64,9 @@ public function initialize(array $config): void { $this->addBehavior('Timestamp'); // Define associations - $this->belongsTo('OrcidSources'); + $this->belongsTo('OrcidSource.OrcidSources'); + $this->setDisplayField('orcid_identifier'); + $this->setPrimaryLink('orcid_source_id'); $this->setPermissions([ // Actions that operate over an entity (ie: require an $id) diff --git a/app/plugins/OrcidSource/src/Model/Table/PetitionOrcidsTable.php b/app/plugins/OrcidSource/src/Model/Table/PetitionOrcidsTable.php new file mode 100644 index 000000000..5d61ab95b --- /dev/null +++ b/app/plugins/OrcidSource/src/Model/Table/PetitionOrcidsTable.php @@ -0,0 +1,156 @@ +addBehavior('Changelog'); + $this->addBehavior('Log'); + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Artifact); + + // Define associations + $this->belongsTo('Petitions'); + $this->belongsTo('OrcidSource.OrcidSourceCollectors'); + + $this->setDisplayField('orcid_token'); + + $this->setPrimaryLink('petition_id'); + $this->setRequiresCO(true); + + $this->setPermissions([ + // Actions that operate over an entity (ie: require an $id) + 'entity' => [ + 'delete' => false, + 'edit' => false, + 'view' => ['platformAdmin', 'coAdmin'] + ], + // Actions that operate over a table (ie: do not require an $id) + 'table' => [ + 'add' => false, + 'index' => false + ] + ]); + } + + /** + * Record an ORCID Token. + * + * @param int $petitionId Petition ID + * @param int $enrollmentFlowStepId Enrollment Flow Step ID + * @param int $orcidSourceCollectorId ORCID Source Collector ID + * @param string $orcid_token ORCID Token Response serialized + * @return void + * @since COmanage Registry v5.2.0 + */ + + public function record( + int $petitionId, + int $enrollmentFlowStepId, + int $orcidSourceCollectorId, + string $orcidToken, + ): void { + // Record the Identifier. We use upsert since at least initially we only support + // one Identifier per Petition. + + $orcid = unserialize($orcidToken); + + $orcidData = [ + 'petition_id' => $petitionId, + 'orcid_token' => $orcidToken, + 'orcid_identifier' => $orcid->orcid, + 'orcid_source_collector_id' => $orcidSourceCollectorId, + ]; + + $this->upsertOrFail( + data: $orcidData, + whereClause: ['petition_id' => $petitionId, 'orcid_identifier' => $orcid->orcid, 'orcid_source_collector_id' => $orcidSourceCollectorId], + ); + + // Record PetitionHistory + $PetitionHistoryRecords = TableRegistry::getTableLocator()->get('PetitionHistoryRecords'); + $PetitionHistoryRecords->record( + petitionId: $petitionId, + enrollmentFlowStepId: $enrollmentFlowStepId, + action: PetitionActionEnum::AttributesUpdated, + comment: __d('orcid_source', 'result.OrcidSourceCollector.collected', [$orcid->orcid]) +// We don't have $actorPersonId yet... +// ?int $actorPersonId=null + ); + } + + /** + * Set validation rules. + * + * @since COmanage Registry v5.2.0 + * @param Validator $validator Validator + * @return Validator Validator + */ + + public function validationDefault(Validator $validator): Validator { + $schema = $this->getSchema(); + + $validator->add('orcid_source_collector_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('orcid_source_collector_id'); + + $validator->add('petition_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('petition_id'); + + $this->registerStringValidation($validator, $schema, 'orcid_token', true); + + return $validator; + } +} diff --git a/app/plugins/OrcidSource/src/config/plugin.json b/app/plugins/OrcidSource/src/config/plugin.json index 79ddee3b6..7d9d9aac9 100644 --- a/app/plugins/OrcidSource/src/config/plugin.json +++ b/app/plugins/OrcidSource/src/config/plugin.json @@ -49,6 +49,19 @@ }, "orcid_source_collectors_i2": { "columns": [ "orcid_identifier" ] } } + }, + "petition_orcids": { + "columns": { + "id": {}, + "petition_id": {}, + "orcid_source_collector_id": { "type": "integer", "foreignkey": { "table": "orcid_source_collectors", "column": "id" }, "notnull": true }, + "orcid_identifier": { "type": "string", "size": "128" }, + "orcid_token": { "type": "text" } + }, + "indexes": { + "petition_orcids_i1": { "columns": [ "petition_id" ] }, + "petition_orcids_i2": { "columns": [ "orcid_source_collector_id" ] } + } } } } diff --git a/app/plugins/OrcidSource/templates/OrcidSourceCollectors/dispatch.inc b/app/plugins/OrcidSource/templates/OrcidSourceCollectors/dispatch.inc index ac5f61145..c941ef0dd 100644 --- a/app/plugins/OrcidSource/templates/OrcidSourceCollectors/dispatch.inc +++ b/app/plugins/OrcidSource/templates/OrcidSourceCollectors/dispatch.inc @@ -1,6 +1,6 @@ Html->css('OrcidSource/orcid-source', ['block' => true]); -// Make the Form fields editable -$this->Field->enableFormEditMode(); - -// The first time we will suppress default submit -$suppress_submit = true; -$btnAuthenticateLabel = '' - . __d('orcid_source', 'information.OrcidSourceCollectors.authenticate'); +// Authenticate and fetch the token +if (empty($vv_orcid) || empty($vv_token)) { + print $this->element('OrcidSource.authenticate'); + return; +} -print $this->Form->hidden('op', ['value' => 'authenticate']); +// Make the Form fields editable and the form submittable +$this->Field->enableFormEditMode(); +print $this->element('OrcidSource.preview'); -// Render the parsed variables -?> -
- Html->image('OrcidSource.orcid_128x128.png', ['alt' => 'Logo', 'class' => 'mb-3']) ?> -

-

- Form->button( - $btnAuthenticateLabel, - [ - 'escapeTitle' => false, - 'type' => 'submit', - 'class' => 'spin submit-button btn btn-primary d-flex mx-auto', - ] - ) - ?> -
-
diff --git a/app/plugins/OrcidSource/templates/element/authenticate.php b/app/plugins/OrcidSource/templates/element/authenticate.php new file mode 100644 index 000000000..6d58c1dec --- /dev/null +++ b/app/plugins/OrcidSource/templates/element/authenticate.php @@ -0,0 +1,51 @@ +login' + . __d('orcid_source', 'information.OrcidSourceCollectors.authenticate'); + +print $this->Form->hidden('op', ['value' => 'authenticate']); + +?> + +
+ Html->image('OrcidSource.orcid_128x128.png', ['alt' => 'Logo', 'class' => 'mb-3']) ?> +

+

+ Form->button( + $btnAuthenticateLabel, + [ + 'id' => 'orcid-auth-btn', + 'escapeTitle' => false, + 'type' => 'submit', + 'class' => 'spin submit-button btn btn-primary d-flex mx-auto', + ] + ) + ?> +
diff --git a/app/plugins/OrcidSource/templates/element/preview.php b/app/plugins/OrcidSource/templates/element/preview.php new file mode 100644 index 000000000..c5ad8ab0b --- /dev/null +++ b/app/plugins/OrcidSource/templates/element/preview.php @@ -0,0 +1,50 @@ +Form->hidden('op', ['value' => 'savetoken']); +print $this->Form->hidden('orcid_token', ['value' => serialize($vv_token)]); + +?> + + + + + + + + + + + + + + + +
+ diff --git a/app/plugins/OrcidSource/webroot/css/orcid-source.css b/app/plugins/OrcidSource/webroot/css/orcid-source.css index 6b4e72ce3..4b8fc336c 100644 --- a/app/plugins/OrcidSource/webroot/css/orcid-source.css +++ b/app/plugins/OrcidSource/webroot/css/orcid-source.css @@ -16,6 +16,3 @@ vertical-align: middle; } -#orcid-result { - color: #198754; -}