diff --git a/app/availableplugins/PipelineToolkit/resources/locales/en_US/pipeline_toolkit.po b/app/availableplugins/PipelineToolkit/resources/locales/en_US/pipeline_toolkit.po index 36de5007a..0297e2df4 100644 --- a/app/availableplugins/PipelineToolkit/resources/locales/en_US/pipeline_toolkit.po +++ b/app/availableplugins/PipelineToolkit/resources/locales/en_US/pipeline_toolkit.po @@ -22,12 +22,21 @@ # @since COmanage Registry v5.0.0 # @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +msgid "controller.IdentifierMappers" +msgstr "{0,plural,=1{Identifier Mapper} other{Identifier Mappers}}" + +msgid "controller.LoginIdentifierTypes" +msgstr "{0,plural,=1{Login Identifier Type} other{Login Identifier Types}}" + msgid "controller.PersonRoleMappers" msgstr "{0,plural,=1{Person Role Mapper} other{Person Role Mappers}}" msgid "controller.PersonRoleMappings" msgstr "{0,plural,=1{Person Role Mapping} other{Person Role Mappings}}" +msgid "error.LoginIdentifierType.already" +msgstr "Type {0} is already configured (ID {1}) for this Identifier Mapper" + msgid "field.PersonRoleMappings.ad_hoc_tag" msgstr "Ad Hoc Attribute Tag" diff --git a/app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-links.inc b/app/availableplugins/PipelineToolkit/src/Controller/IdentifierMappersController.php similarity index 69% rename from app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-links.inc rename to app/availableplugins/PipelineToolkit/src/Controller/IdentifierMappersController.php index d8c2ada5e..619e72252 100644 --- a/app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-links.inc +++ b/app/availableplugins/PipelineToolkit/src/Controller/IdentifierMappersController.php @@ -1,6 +1,6 @@ 'transform', - 'order' => 'Default', - 'label' => __d('pipeline_toolkit', 'controller.PersonRoleMappings', [99]), - 'link' => [ - 'plugin' => 'PipelineToolkit', - 'controller' => 'PersonRoleMappings', - 'action' => 'index', - 'person_role_mapper_id' => $vv_obj->id - ], - 'class' => '' -]; +declare(strict_types=1); + +namespace PipelineToolkit\Controller; + +use App\Controller\StandardPluginController; + +class IdentifierMappersController extends StandardPluginController { + public $paginate = [ + 'order' => [ + 'IdentifierMappers.id' => 'asc' + ] + ]; +} diff --git a/app/availableplugins/PipelineToolkit/src/Controller/LoginIdentifierTypesController.php b/app/availableplugins/PipelineToolkit/src/Controller/LoginIdentifierTypesController.php new file mode 100644 index 000000000..43f7d0950 --- /dev/null +++ b/app/availableplugins/PipelineToolkit/src/Controller/LoginIdentifierTypesController.php @@ -0,0 +1,40 @@ + [ + 'LoginIdentifierTypes.id' => 'asc' + ] + ]; +} diff --git a/app/availableplugins/PipelineToolkit/src/Model/Entity/IdentifierMapper.php b/app/availableplugins/PipelineToolkit/src/Model/Entity/IdentifierMapper.php new file mode 100644 index 000000000..da62bb347 --- /dev/null +++ b/app/availableplugins/PipelineToolkit/src/Model/Entity/IdentifierMapper.php @@ -0,0 +1,49 @@ + + */ + protected $_accessible = [ + '*' => true, + 'id' => false, + 'slug' => false, + ]; +} diff --git a/app/availableplugins/PipelineToolkit/src/Model/Entity/LoginIdentifierType.php b/app/availableplugins/PipelineToolkit/src/Model/Entity/LoginIdentifierType.php new file mode 100644 index 000000000..5e24a0756 --- /dev/null +++ b/app/availableplugins/PipelineToolkit/src/Model/Entity/LoginIdentifierType.php @@ -0,0 +1,51 @@ + + */ + protected $_accessible = [ + '*' => true, + 'id' => false, + 'slug' => false, + ]; +} diff --git a/app/availableplugins/PipelineToolkit/src/Model/Table/IdentifierMappersTable.php b/app/availableplugins/PipelineToolkit/src/Model/Table/IdentifierMappersTable.php new file mode 100644 index 000000000..5cb331933 --- /dev/null +++ b/app/availableplugins/PipelineToolkit/src/Model/Table/IdentifierMappersTable.php @@ -0,0 +1,158 @@ +addBehavior('Changelog'); + $this->addBehavior('Log'); + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Configuration); + + // Define associations + $this->belongsTo('Flanges'); + + $this->hasMany('PipelineToolkit.LoginIdentifierTypes') + ->setDependent(true) + ->setCascadeCallbacks(true); + + $this->setDisplayField('id'); + + $this->setPrimaryLink(['flange_id']); + $this->setRequiresCO(true); + + $this->setPermissions([ + // Actions that operate over an entity (ie: require an $id) + 'entity' => [ + 'delete' => false, // Delete the pluggable object instead + 'edit' => ['platformAdmin', 'coAdmin'], + 'view' => ['platformAdmin', 'coAdmin'] + ], + // Actions that operate over a table (ie: do not require an $id) + 'table' => [ + 'add' => false, + 'index' => ['platformAdmin', 'coAdmin'] + ], + // Related models whose permissions we'll need, typically for table views + 'related' => [ + 'table' => [ + 'PipelineToolkit.LoginIdentifierTypes' + ] + ] + ]); + } + + /** + * Apply mappings for vuildRelatedAttributes. + * + * @since COmanage Registry v5.1.0 + * @param Flange $flange Flange, including top level plugin configuration + * @param array $newdata Array of new Entity data + * @param string $model Model name (eg: "EmailAddresses") + * @param Entity $entity Original Entity data + * @return array Updated array of new Entity data + */ + + public function buildRelatedAttributes( + \App\Model\Entity\Flange $flange, + array $newdata, + string $model, + ?\Cake\ORM\Entity $entity=null + ): array { + // We only operate over Identifiers + if($model != 'Identifiers') { + return $newdata; + } + + $retdata = $newdata; + + // Pull our type configuration + $typeSettings = $this->LoginIdentifierTypes->find() + ->where(['identifier_mapper_id' => $flange->identifier_mapper->id]) + ->all(); + + // Note we don't (and can't) preserve login flags if the configuration is changed and + // the Pipeline is rerun, but that's probably the correct behavior. Specifically, if + // an Identifier is flagged as login, then the configuration is changed so that type + // is no longer flagged as login, then the next time the Pipeline runs the Identifier + // will be updated to be not login. + + foreach($typeSettings as $ts) { + if($ts->type_id == $retdata['type_id'] && $ts->login) { + $this->llog('trace', $flange->description . " setting Identifier " . $ts->identifier + . " to be flagged as login=true"); + $retdata['login'] = true; + } + } + + return $retdata; + } + + /** + * Set validation rules. + * + * @since COmanage Registry v5.1.0 + * @param Validator $validator Validator + * @return Validator Validator + * @throws InvalidArgumentException + * @throws RecordNotFoundException + */ + + public function validationDefault(Validator $validator): Validator { + $schema = $this->getSchema(); + + $validator->add('flange_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('flange_id'); + + return $validator; + } +} \ No newline at end of file diff --git a/app/availableplugins/PipelineToolkit/src/Model/Table/LoginIdentifierTypesTable.php b/app/availableplugins/PipelineToolkit/src/Model/Table/LoginIdentifierTypesTable.php new file mode 100644 index 000000000..a32eded1d --- /dev/null +++ b/app/availableplugins/PipelineToolkit/src/Model/Table/LoginIdentifierTypesTable.php @@ -0,0 +1,174 @@ +addBehavior('Changelog'); + $this->addBehavior('Log'); + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Configuration); + + // Define associations + $this->belongsTo('PipelineToolkit.IdentifierMappers'); + $this->belongsTo('Types'); + + $this->setDisplayField('id'); + + $this->setPrimaryLink(['PipelineToolkit.identifier_mapper_id']); + $this->setRequiresCO(true); + $this->setRedirectGoal('index'); + + $this->setAutoViewVars([ + 'types' => [ + 'type' => 'type', + 'attribute' => 'Identifiers.type' + ] + ]); + + $this->setPermissions([ + // Actions that operate over an entity (ie: require an $id) + 'entity' => [ + 'delete' => ['platformAdmin', 'coAdmin'], + 'edit' => ['platformAdmin', 'coAdmin'], + 'view' => ['platformAdmin', 'coAdmin'] + ], + // Actions that operate over a table (ie: do not require an $id) + 'table' => [ + 'add' => ['platformAdmin', 'coAdmin'], + 'index' => ['platformAdmin', 'coAdmin'] + ] + ]); + } + + /** + * Define business rules. + * + * @since COmanage Registry v5.1.0 + * @param RulesChecker $rules RulesChecker object + * @return RulesChecker + */ + + public function buildRules(RulesChecker $rules): RulesChecker { + // We only allow an Identifier Type to be configured once per Mapper. + // This isn't strictly speaking an Application Rule, it is mostly intended + // to avoid inconsistent configurations. + $rules->add([$this, 'ruleUniqueType'], + 'uniqueType', + ['errorField' => 'type_id']); + + return $rules; + } + + /** + * Application Rule to determine if a specific Type is already configured. + * + * @since COmanage Registry v5.1.0 + * @param Entity $entity Entity to be validated + * @param array $options Application rule options + * @return boolean true if the Rule check passes, false otherwise + */ + + public function ruleUniqueType($entity, $options) { + // debug($entity); + + if($entity->isNew() || $entity->isDirty('type_id')) { + $whereClause = [ + 'LoginIdentifierTypes.type_id' => $entity->type_id, + 'identifier_mapper_id' => $entity->identifier_mapper_id + ]; + + if(!$entity->isNew()) { + $whereClause['id IS NOT'] = $entity->id; + } + + $existing = $this->find()->where($whereClause)->contain(['Types'])->first(); + + if(!empty($existing)) { + throw new \InvalidArgumentException(__d('pipeline_toolkit', "error.LoginIdentifierType.already", [$existing->type->display_name, $existing->id])); + } + } + + return true; + } + + /** + * Set validation rules. + * + * @since COmanage Registry v5.1.0 + * @param Validator $validator Validator + * @return Validator Validator + * @throws InvalidArgumentException + * @throws RecordNotFoundException + */ + + public function validationDefault(Validator $validator): Validator { + $schema = $this->getSchema(); + + $validator->add('identifier_mapper_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('identifier_mapper_id'); + + $validator->add('type_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('type_id'); + + $validator->add('login', [ + 'content' => ['rule' => ['boolean']] + ]); + $validator->allowEmptyString('login'); + + return $validator; + } +} \ No newline at end of file diff --git a/app/availableplugins/PipelineToolkit/src/config/plugin.json b/app/availableplugins/PipelineToolkit/src/config/plugin.json index 5ef8ae600..3bd7608d9 100644 --- a/app/availableplugins/PipelineToolkit/src/config/plugin.json +++ b/app/availableplugins/PipelineToolkit/src/config/plugin.json @@ -1,11 +1,35 @@ { "types": { "pipeline": [ + "IdentifierMappers", "PersonRoleMappers" ] }, "schema": { "tables": { + "identifier_mappers": { + "columns": { + "id": {}, + "flange_id": { "type": "integer", "foreignkey": { "table": "flanges", "column": "id" } } + }, + "indexes": { + "identifier_mappers_i1": { "columns": [ "flange_id" ] } + } + }, + + "login_identifier_types": { + "columns": { + "id": {}, + "identifier_mapper_id": { "type": "integer", "foreignkey": { "table": "identifier_mappers", "column": "id" } }, + "type_id": {}, + "login": { "type": "boolean" } + }, + "indexes": { + "login_identifier_types_i1": { "columns": [ "identifier_mapper_id"] }, + "login_identifier_types_i2": { "needed": false, "columns": [ "type_id"] } + } + }, + "person_role_mappers": { "columns": { "id": {}, diff --git a/app/availableplugins/PipelineToolkit/templates/IdentifierMappers/fields-nav.inc b/app/availableplugins/PipelineToolkit/templates/IdentifierMappers/fields-nav.inc new file mode 100644 index 000000000..e5db5ae5c --- /dev/null +++ b/app/availableplugins/PipelineToolkit/templates/IdentifierMappers/fields-nav.inc @@ -0,0 +1,44 @@ + 'plugin', + 'active' => 'plugin' +]; + +$topLinks[] = [ + 'icon' => 'transform', + 'order' => 'Default', + 'label' => __d('pipeline_toolkit', 'controller.LoginIdentifierTypes', [99]), + 'link' => [ + 'plugin' => 'PipelineToolkit', + 'controller' => 'LoginIdentifierTypes', + 'action' => 'index', + 'identifier_mapper_id' => $vv_obj->id + ], + 'class' => '' +]; diff --git a/app/availableplugins/PipelineToolkit/templates/IdentifierMappers/fields.inc b/app/availableplugins/PipelineToolkit/templates/IdentifierMappers/fields.inc new file mode 100644 index 000000000..ad18b384e --- /dev/null +++ b/app/availableplugins/PipelineToolkit/templates/IdentifierMappers/fields.inc @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/app/availableplugins/PipelineToolkit/templates/LoginIdentifierTypes/columns.inc b/app/availableplugins/PipelineToolkit/templates/LoginIdentifierTypes/columns.inc new file mode 100644 index 000000000..f287f169c --- /dev/null +++ b/app/availableplugins/PipelineToolkit/templates/LoginIdentifierTypes/columns.inc @@ -0,0 +1,48 @@ + [ + 'type' => 'link' + ], + 'type_id' => [ + 'type' => 'fk' + ], + 'login' => [ + 'type' => 'boolean', + 'class' => 'YesBooleanEnum' + ] +]; + +/* +// When the $bulkActions variable exists in a columns.inc config, the "Bulk edit" switch will appear in the index. +$bulkActions = [ + // TODO: develop bulk actions. For now, use a placeholder. + 'delete' => true +]; +*/ diff --git a/app/availableplugins/PipelineToolkit/templates/LoginIdentifierTypes/fields.inc b/app/availableplugins/PipelineToolkit/templates/LoginIdentifierTypes/fields.inc new file mode 100644 index 000000000..d1866d0a1 --- /dev/null +++ b/app/availableplugins/PipelineToolkit/templates/LoginIdentifierTypes/fields.inc @@ -0,0 +1,38 @@ +element('form/listItem', [ + 'arguments' => [ + 'fieldName' => $field + ]]); + } +} diff --git a/app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-nav.inc b/app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-nav.inc index fc4530862..2f2ed3258 100644 --- a/app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-nav.inc +++ b/app/availableplugins/PipelineToolkit/templates/PersonRoleMappers/fields-nav.inc @@ -24,8 +24,21 @@ * @since COmanage Registry v5.0.0 * @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) */ - - $subnav = [ - 'name' => 'plugin', - 'active' => 'plugin' - ]; + +$subnav = [ + 'name' => 'plugin', + 'active' => 'plugin' +]; + +$topLinks[] = [ + 'icon' => 'transform', + 'order' => 'Default', + 'label' => __d('pipeline_toolkit', 'controller.PersonRoleMappings', [99]), + 'link' => [ + 'plugin' => 'PipelineToolkit', + 'controller' => 'PersonRoleMappings', + 'action' => 'index', + 'person_role_mapper_id' => $vv_obj->id + ], + 'class' => '' +]; diff --git a/app/plugins/EnvSource/src/Model/Table/EnvSourceCollectorsTable.php b/app/plugins/EnvSource/src/Model/Table/EnvSourceCollectorsTable.php index 873bc9bde..5a865dbfe 100644 --- a/app/plugins/EnvSource/src/Model/Table/EnvSourceCollectorsTable.php +++ b/app/plugins/EnvSource/src/Model/Table/EnvSourceCollectorsTable.php @@ -359,7 +359,7 @@ public function upsert(int $id, int $petitionId, array $attributes) { $EnvSourceIdentities = TableRegistry::getTableLocator()->get('EnvSource.EnvSourceIdentities'); - $esi = $EnvSourceIdentities->upsert( + $esi = $EnvSourceIdentities->upsertOrFail( data: [ 'env_source_id' => $envSource->id, 'source_key' => $sourceKey, @@ -390,7 +390,7 @@ public function upsert(int $id, int $petitionId, array $attributes) { // v4 Query mode becomes, and so should maybe be more general (like // AR-ExternalIdentitySourceRecord-1). - $pei = $this->PetitionEnvIdentities->upsert( + $pei = $this->PetitionEnvIdentities->upsertOrFail( data: [ 'petition_id' => $petitionId, 'env_source_collector_id' => $id, diff --git a/app/plugins/EnvSource/src/Model/Table/EnvSourceIdentitiesTable.php b/app/plugins/EnvSource/src/Model/Table/EnvSourceIdentitiesTable.php index 13b9d8114..35f08f708 100644 --- a/app/plugins/EnvSource/src/Model/Table/EnvSourceIdentitiesTable.php +++ b/app/plugins/EnvSource/src/Model/Table/EnvSourceIdentitiesTable.php @@ -104,7 +104,10 @@ public function validationDefault(Validator $validator): Validator { $this->registerStringValidation($validator, $schema, 'source_key', true); - $this->registerStringValidation($validator, $schema, 'env_attributes', false); + // We don't have any meaningful validation rules for env_attributes because it is + // a text field (not a varchar with a max length) and because the value comes from + // an external system. + $validator->allowEmptyString('env_attributes'); return $validator; } diff --git a/app/resources/locales/en_US/enumeration.po b/app/resources/locales/en_US/enumeration.po index f58d0af2b..9d504dd80 100644 --- a/app/resources/locales/en_US/enumeration.po +++ b/app/resources/locales/en_US/enumeration.po @@ -156,6 +156,9 @@ msgstr "Deleted" msgid "FlangeModeEnum.EI" msgstr "Retrieve External Identity" +msgid "FlangeModeEnum.MV" +msgstr "Build Related Attributes" + msgid "FlangeModeEnum.PR" msgstr "Build Person Role" diff --git a/app/src/Controller/DashboardsController.php b/app/src/Controller/DashboardsController.php index 9a133edde..e28248549 100644 --- a/app/src/Controller/DashboardsController.php +++ b/app/src/Controller/DashboardsController.php @@ -123,7 +123,7 @@ public function configuration() { 'action' => 'index' ], __d('controller', 'Pipelines', [99]) => [ - 'icon' => 'cable', + 'icon' => 'valve', 'controller' => 'pipelines', 'action' => 'index' ], diff --git a/app/src/Lib/Enum/FlangeModeEnum.php b/app/src/Lib/Enum/FlangeModeEnum.php index 69ef4b36c..d4489149e 100644 --- a/app/src/Lib/Enum/FlangeModeEnum.php +++ b/app/src/Lib/Enum/FlangeModeEnum.php @@ -32,5 +32,6 @@ class FlangeModeEnum extends StandardEnum { const Disabled = 'X'; const BuildPersonRole = 'PR'; + const BuildRelatedAttributes = 'MV'; const RetrieveExternalIdentity = 'EI'; } \ No newline at end of file diff --git a/app/src/Lib/Traits/UpsertTrait.php b/app/src/Lib/Traits/UpsertTrait.php index f258dcee2..4c83f3f9e 100644 --- a/app/src/Lib/Traits/UpsertTrait.php +++ b/app/src/Lib/Traits/UpsertTrait.php @@ -30,20 +30,22 @@ namespace App\Lib\Traits; trait UpsertTrait { - use \App\Lib\Traits\LabeledLogTrait; - /** * Perform an upsert. * * @since COmanage Registry v5.1.0 * @param array $data Data to persist * @param array $whereClause Conditions to search for current entity + * @param bool $orFail If true, use saveOrFail() instead of save() * @return Cake\Datasource\EntityInterface|false Persisted entity, or false on failure + * @throws Cake\ORM\Exception\PersistenceFailedException * @throws Cake\ORM\Exception\RolledbackTransactionException */ + public function upsert( array $data, - array $whereClause + array $whereClause, + bool $orFail=false ): \Cake\Datasource\EntityInterface|false { // First check if we have an entity matching $whereClause $entity = $this->find() @@ -61,12 +63,7 @@ public function upsert( $entity = $this->newEntity($data); } - if (!empty($entity->getErrors())) { - $this->llog('error', "Save failed for {$this->getAlias()}: " . print_r($entity->getErrors(), true)); - throw new \RuntimeException(__d('error', 'save', [$this->getAlias()])); - } - - return $this->save($entity); + return $orFail ? $this->saveOrFail($entity) : $this->save($entity); } /** @@ -83,12 +80,6 @@ public function upsertOrFail( array $data, array $whereClause ): \Cake\Datasource\EntityInterface { - $entity = $this->upsert($data, $whereClause); - - if($entity === false) { - throw new Cake\ORM\Exception\PersistenceFailedException($entity, ['upsert']); - } - - return $entity; + return $this->upsert($data, $whereClause, true); } } diff --git a/app/src/Model/Table/PipelinesTable.php b/app/src/Model/Table/PipelinesTable.php index a62e13c30..f6fd1ffef 100644 --- a/app/src/Model/Table/PipelinesTable.php +++ b/app/src/Model/Table/PipelinesTable.php @@ -388,6 +388,76 @@ protected function createPersonFromEIS( return $entity; } + /** + * Dispatch Petition Plugins (Flanges). + * + * @since COmanage Registry v5.1.0 + * @param Pipeline $pipeline Pipeline configuration + * @param string $flangeMode Flange Mode Enum + * @param string $modelName Model Name (eg: TelephoneNumbers) + * @param array $newdata Array of data to pass to the plugin + * @param Entity $entity Original Entity data (unmodified from the External Identity) + * @return array Updated array as modified by any relevant Flanges + */ + + protected function dispatchFlanges( + \App\Model\Entity\Pipeline $pipeline, + string $flangeMode, + string $modelName, + array $newdata, + ?\Cake\ORM\Entity $entity=null + ): array { + $ret = $newdata; + + if(!empty($pipeline->flanges)) { + // These should already be ordered by ordr. Note that ordr operates a bit + // unexpectedly here... the later flanges to get called can override the + // values returned by earlier flanges, since we always call all active + // flanges, so the later flanges take precedence over the earlier ones. + + // We support slightly different interfaces per context for clarify + $fn = "unknown"; + + switch($flangeMode) { + case FlangeModeEnum::BuildPersonRole: + $fn = 'buildPersonRole'; + break; + case FlangeModeEnum::BuildRelatedAttributes: + $fn = 'buildRelatedAttributes'; + break; + case FlangeModeEnum::RetrieveExternalIdentity: + default: + throw new \RuntimeException('NOT IMPLEMENTED'); + } + + foreach($pipeline->flanges as $flange) { + if($flange->status == $flangeMode) { + $this->llog('trace', 'Running Flange ' . $flange->description . ' for Flange Mode ' . $flangeMode); + + $PluginTable = TableRegistry::getTableLocator()->get($flange->plugin); + + if(!method_exists($PluginTable, $fn)) { + throw new \RuntimeException(__d('Pipelines.plugin.notimpl', [$fn])); + } + + // The plugin should modify $newdata (via our local copy $ret) as needed, then return it + switch($fn) { + case 'buildPersonRole': + $ret = $PluginTable->buildPersonRole($flange, $ret, $entity); + break; + case 'buildRelatedAttributes': + $ret = $PluginTable->buildRelatedAttributes($flange, $ret, $modelName, $entity); + break; + default: + throw new \RuntimeException('NOT IMPLEMENTED'); + } + } + } + } + + return $ret; + } + /** * Copy the data from an entity and filter metadata, returning an array * suitable for creating a new entity. Related models are also removed. @@ -1593,7 +1663,15 @@ protected function syncPerson( // Do we need to create a Verification record for this entity // (if it is an email address)? $createVerification = false; - + + $newdata = $this->dispatchFlanges( + $pipeline, + FlangeModeEnum::BuildRelatedAttributes, + $model, + $newdata, + $eientity + ); + if($found) { // There is an existing record, update it (if it changed) _unless_ // the attribute record is frozen. @@ -1663,6 +1741,13 @@ protected function syncPerson( } } + $newdata = $this->dispatchFlanges( + $pipeline, + FlangeModeEnum::BuildRelatedAttributes, + $model, + $newdata + ); + $newentity = $this->Cos->People->$model->newEntity($newdata); $this->Cos->People->$model->saveOrFail($newentity); @@ -1814,26 +1899,13 @@ protected function syncPerson( $newdata['status'] = StatusEnum::Active; } - if(!empty($pipeline->flanges)) { - // These should already be order by ordr. Note that ordr operates a bit - // unexpectedly here... the later flanges to get called can override the - // values returned by earlier flanges, since we always call all active - // flanges, so the later flanges take precedence over the earlier ones. - foreach($pipeline->flanges as $flange) { - if($flange->status == FlangeModeEnum::BuildPersonRole) { - $this->llog('trace', 'Running Flange ' . $flange->description . ' for BuildPersonRole'); - - $PluginTable = TableRegistry::getTableLocator()->get($flange->plugin); - - if(!method_exists($PluginTable, 'buildPersonRole')) { - throw new \RuntimeException(__d('Pipelines.plugin.notimpl', ['buildPersonRole'])); - } - - // The plugin should modify $newdata as needed, then return it - $newdata = $PluginTable->buildPersonRole($flange, $newdata, $eirentity); - } - } - } + $newdata = $this->dispatchFlanges( + $pipeline, + FlangeModeEnum::BuildPersonRole, + 'PersonRole', + $newdata, + $eirentity + ); // Do we have a corresponding record on the Person? $found = $curentities->firstMatch([$sourcefk => $eirentity->id]); @@ -1881,6 +1953,14 @@ protected function syncPerson( $newdata[$rsourcefk] = $relatedEntity->id; $newdata['person_role_id'] = $found->id ?? $newentity->id; + $newdata = $this->dispatchFlanges( + $pipeline, + FlangeModeEnum::BuildRelatedAttributes, + $model, + $newdata, + $eientity + ); + // See if we have a correponding Person Role entity, but only if // we're working with an existing Person Role