diff --git a/app/src/Model/Table/ExternalIdentityRolesTable.php b/app/src/Model/Table/ExternalIdentityRolesTable.php index b24377067..35a267ba1 100644 --- a/app/src/Model/Table/ExternalIdentityRolesTable.php +++ b/app/src/Model/Table/ExternalIdentityRolesTable.php @@ -32,6 +32,7 @@ use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; +use Cake\Utility\Inflector; use Cake\Validation\Validator; use \App\Lib\Enum\ActionEnum; use \App\Lib\Enum\ExternalIdentityStatusEnum; @@ -146,10 +147,20 @@ public function initialize(array $config): void { */ public function beforeDelete(\Cake\Event\Event $event, $entity, \ArrayObject $options) { + // AR-ExternalIdentityRole-1 When an External Identity Role is deleted via a Pipeline + // action, any associated Person Role will be set to the status as configured in the + // associated Pipeline, and any associated MVEAs will be deleted from the Person Role. + + // Note the above does _not_ currently apply to manual deletions. It could, and + // we could walk EIR -> EI -> EISR -> EIS -> Pipeline to get the appropriate + // configuration, but for now at least we let manual operations require further + // manual work. + // Is there a Person Role associated with this EI Role? if(!empty($entity->id)) { $prole = $this->PersonRoles->find() ->where(['PersonRoles.source_external_identity_role_id' => $entity->id]) + ->contain(['AdHocAttributes', 'Addresses', 'TelephoneNumbers']) ->first(); if(!empty($prole)) { @@ -157,8 +168,38 @@ public function beforeDelete(\Cake\Event\Event $event, $entity, \ArrayObject $op // deletes or otherwise mess things up. $this->llog('trace', "Removing link from PersonRole " . $prole->id . " to source ExternalIdentityRole " . $entity->id); - $prole->source_external_identity_role_id = null; + + if(!empty($options['sync_status_on_delete'])) { + // Update the associated Person Role, unless it is Frozen + + if(isset($prole->frozen) && $prole->frozen) { + $this->llog('trace', "Refusing to update frozen Person Role " . $prole->id . " from deleted External Identity Role " . $entity->id); + } else { + // Update the status in accordance with the Pipeline configuration + $this->llog('rule', "AR-ExternalIdentityRole-1 Updating status on PersonRole " . $prole->id . " to " . $options['sync_status_on_delete'] . " following deletion of source ExternalIdentityRole " . $entity->id); + + $prole->status = $options['sync_status_on_delete']; + + // Delete the MVEAs associated with this Person Role. We do this here + // rather than in syncPerson since we're doing all the other work here. + foreach([ + 'Addresses', + 'AdHocAttributes', + 'TelephoneNumbers' + ] as $eirmodel) { + $aeirmodel = Inflector::underscore($eirmodel); + + if(!empty($prole->$aeirmodel)) { + foreach($prole->$aeirmodel as $aeirentity) { + $this->llog('rule', "AR-ExternalIdentityRole-1 Deleted $aeirmodel " . $aeirentity->id . " for Person Role " . $prole->id); + $this->PersonRoles->$eirmodel->deleteOrFail($aeirentity); + } + } + } + } + } + $this->PersonRoles->saveOrFail($prole); } } diff --git a/app/src/Model/Table/PipelinesTable.php b/app/src/Model/Table/PipelinesTable.php index e636ea420..c6a44a41f 100644 --- a/app/src/Model/Table/PipelinesTable.php +++ b/app/src/Model/Table/PipelinesTable.php @@ -1147,10 +1147,16 @@ protected function syncExternalIdentity( // deletes (and to trigger ExternalIdentitiesTable callbacks) rather than // do it model by model, below. + // This delete will cascade to ExternalIdentityRoles, where the beforeDelete + // callback will handle updating any associated Person Roles. + $this->llog('trace', "Deleting removed External Identity " . $eisRecord->external_identity_id . " for Person " . $person->id . " from EIS " . $eis->description . " (" . $eis->id . ")"); $entity = $this->Cos->People->ExternalIdentities->get($eisRecord->external_identity_id); - $this->Cos->People->ExternalIdentities->deleteOrFail($entity); + $this->Cos->People->ExternalIdentities->deleteOrFail( + $entity, + ['sync_status_on_delete' => $pipeline->sync_status_on_delete] + ); return null; } else { @@ -1398,57 +1404,14 @@ protected function syncExternalIdentity( } if(!$found) { - if($model == 'ExternalIdentityRoles') { - // We have to handle the link to PersonRoles a bit carefully. - // First, we'll set the status of the Role in accordance with the - // Pipeline configuration. We do this here because - // ExternalIdentityRolesTable::beforeDelete() will set the Person - // Role foreign key to null to avoid problems with cascading deletes, - // but then when we sync the Person record later we won't see this - // PersonRole since the foreign key was nulled out. - - // We don't set the foreign key to null here because we want it - // to be cleared regardless of how the ExternalIdentity was deleted. - // eg: If an admin deletes it, the delete should complete but there - // is no Pipeline context so the PersonRole status won't be updated. - - $prole = $this->Cos->People->PersonRoles->find() - ->where(['PersonRoles.source_external_identity_role_id' => $aentity->id]) - ->contain(['AdHocAttributes', 'Addresses', 'TelephoneNumbers']) - ->first(); - - if(!empty($prole)) { - if(isset($prole->frozen) && $prole->frozen) { - $this->llog('trace', "Refusing to update frozen Person Role " . $prole->id . " from deleted External Identity Role " . $aentity->id); - } else { - // Update the status in accordance with the Pipeline configuration - $this->llog('trace', "Updating status on PersonRole " . $prole->id . " to " . $pipeline->sync_status_on_delete . " following deletion of source ExternalIdentityRole " . $aentity->id); - - $prole->status = $pipeline->sync_status_on_delete; - $this->Cos->People->PersonRoles->saveOrFail($prole); - - // Delete the MVEAs associated with this Person Role. We do this here - // rather than in syncPerson since we're doing all the other work here. - foreach([ - 'Addresses', - 'AdHocAttributes', - 'TelephoneNumbers' - ] as $eirmodel) { - $aeirmodel = Inflector::underscore($eirmodel); - - if(!empty($prole->$aeirmodel)) { - foreach($prole->$aeirmodel as $aeirentity) { - $this->llog('trace', "Deleted $aeirmodel " . $aeirentity->id . " for Person Role " . $prole->id); - $this->Cos->People->PersonRoles->$eirmodel->deleteOrFail($aeirentity); - } - } - } - } - } - } + // Note there is logic in ExternalIdentityRolesTable::beforeDelete() + // to handle AR-ExternalIdentityRole-1. $this->llog('trace', "Deleted $model " . $aentity->id . " for External Identity " . $externalIdentityEntity->id); - $this->Cos->People->ExternalIdentities->$model->deleteOrFail($aentity); + $this->Cos->People->ExternalIdentities->$model->deleteOrFail( + $aentity, + ['sync_status_on_delete' => $pipeline->sync_status_on_delete] + ); // Note deleted related models remain on the ExternalIdentity in case they // are needed later in the Pipeline. }