From b709ef13e8de6198a75c430a8be309db378bccdf Mon Sep 17 00:00:00 2001 From: Benn Oshrin Date: Thu, 3 Oct 2024 12:30:48 -0400 Subject: [PATCH] Initial implementation of BasicAttributeCollector (CFM-415) --- .../resources/locales/en_US/core_enroller.po | 29 +- .../BasicAttributeCollectorsController.php | 107 ++++++ .../Model/Entity/BasicAttributeCollector.php | 49 +++ .../Entity/BasicPetitionAttributeSet.php | 49 +++ .../Table/BasicAttributeCollectorsTable.php | 320 ++++++++++++++++++ .../Table/BasicPetitionAttributeSetsTable.php | 125 +++++++ .../CoreEnroller/src/config/plugin.json | 37 +- .../BasicAttributeCollectors/dispatch.inc | 48 +++ .../BasicAttributeCollectors/display.php | 8 + .../BasicAttributeCollectors/fields.inc | 45 +++ app/resources/locales/en_US/error.po | 3 + app/src/Model/Table/PetitionsTable.php | 4 +- app/templates/Petitions/fields.inc | 26 +- app/templates/Standard/dispatch.php | 3 - 14 files changed, 841 insertions(+), 12 deletions(-) create mode 100644 app/plugins/CoreEnroller/src/Controller/BasicAttributeCollectorsController.php create mode 100644 app/plugins/CoreEnroller/src/Model/Entity/BasicAttributeCollector.php create mode 100644 app/plugins/CoreEnroller/src/Model/Entity/BasicPetitionAttributeSet.php create mode 100644 app/plugins/CoreEnroller/src/Model/Table/BasicAttributeCollectorsTable.php create mode 100644 app/plugins/CoreEnroller/src/Model/Table/BasicPetitionAttributeSetsTable.php create mode 100644 app/plugins/CoreEnroller/templates/BasicAttributeCollectors/dispatch.inc create mode 100644 app/plugins/CoreEnroller/templates/BasicAttributeCollectors/display.php create mode 100644 app/plugins/CoreEnroller/templates/BasicAttributeCollectors/fields.inc diff --git a/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po b/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po index 9fd6389a2..7341b0a35 100644 --- a/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po +++ b/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po @@ -25,6 +25,9 @@ msgid "controller.AttributeCollectors" msgstr "{0,plural,=1{Attribute Collector} other{Attribute Collectors}}" +msgid "controller.BasicAttributeCollectors" +msgstr "{0,plural,=1{Basic Attribute Collector} other{Basic Attribute Collectors}}" + msgid "controller.EnrollmentAttributes" msgstr "{0,plural,=1{Enrollment Attribute} other{Enrollment Attributes}}" @@ -32,6 +35,27 @@ msgstr "{0,plural,=1{Enrollment Attribute} other{Enrollment Attributes}}" msgid "enumeration.DefaultValueValidityType.after" msgstr "Days After Finalization" +msgid "field.BasicAttributeCollectors.affiliation_type_id" +msgstr "Affiliation Type" + +msgid "field.BasicAttributeCollectors.affiliation_type_id.desc" +msgstr "Affiliation assigned to Person Roles created by this Step" + +msgid "field.BasicAttributeCollectors.cou_id.desc" +msgstr "If set, Person Roles created by this Step will be placed in this COU" + +msgid "field.BasicAttributeCollectors.email_address_type_id" +msgstr "Email Address Type" + +msgid "field.BasicAttributeCollectors.email_address_type_id.desc" +msgstr "Type assigned to the Email Address collected by this Step" + +msgid "field.BasicAttributeCollectors.name_type_id" +msgstr "Name Type" + +msgid "field.BasicAttributeCollectors.name_type_id.desc" +msgstr "Type assigned to the Name collected by this Step" + msgid "field.EnrollmentAttributes.address_required_fields" msgstr "Required Address Fields" @@ -108,4 +132,7 @@ msgid "field.EnrollmentAttributes.url_type_id" msgstr "URL Type" msgid "result.attr.saved" -msgstr "Petition Attributes recorded" \ No newline at end of file +msgstr "Petition Attributes recorded" + +msgid "result.basicattr.finalized" +msgstr "Name, Email Address, and Person Role created during finalization" diff --git a/app/plugins/CoreEnroller/src/Controller/BasicAttributeCollectorsController.php b/app/plugins/CoreEnroller/src/Controller/BasicAttributeCollectorsController.php new file mode 100644 index 000000000..56c9af772 --- /dev/null +++ b/app/plugins/CoreEnroller/src/Controller/BasicAttributeCollectorsController.php @@ -0,0 +1,107 @@ + [ + 'BasicAttributeCollectors.id' => 'asc' + ] + ]; + + /** + * Dispatch an Enrollment Flow Step. + * + * @since COmanage Registry v5.1.0 + * @param string $id Attribute Collector ID + */ + + public function dispatch(string $id) { + $petition = $this->getPetition(); + + $CoSettings = TableRegistry::getTableLocator()->get('CoSettings'); + + $settings = $CoSettings->find()->where(['co_id' => $this->getCOID()])->firstOrFail(); + + $this->set('vv_permitted_name_fields', $settings->name_permitted_fields_array()); + $this->set('vv_required_name_fields', $settings->name_required_fields_array()); + + if($this->request->is(['post', 'put'])) { + + try { + $this->BasicAttributeCollectors->upsert( + id: (int)$id, + petitionId: $petition->id, + // Remove form metadata from the set of attributes we pass to upsert + attributes: array_diff_key($this->request->getData(), ['petition_id' => true, 'token' => true]) + ); + + // On success, indicate the step is completed and generate a redirect + // to the next step + + $link = $this->getPrimaryLink(true); + + return $this->finishStep( + enrollmentFlowStepId: $link->value, + petitionId: $petition->id, + comment: __d('core_enroller', 'result.attr.saved') + ); + } + catch(\Exception $e) { + $this->Flash->error($e->getMessage()); + } + } + + // Fall through and let the form render + + $this->render('/Standard/dispatch'); + } + + /** + * Display information about this Step. + * + * @since COmanage Registry v5.1.0 + * @param string $id Attribute Collector ID + */ + + public function display(string $id) { + $petition = $this->getPetition(); + + $this->set('vv_basic_petition_attribute_set', $this->BasicAttributeCollectors + ->BasicPetitionAttributeSets + ->find() + ->where(['BasicPetitionAttributeSets.petition_id' => $petition->id]) + ->firstOrFail()); + } +} diff --git a/app/plugins/CoreEnroller/src/Model/Entity/BasicAttributeCollector.php b/app/plugins/CoreEnroller/src/Model/Entity/BasicAttributeCollector.php new file mode 100644 index 000000000..01f468029 --- /dev/null +++ b/app/plugins/CoreEnroller/src/Model/Entity/BasicAttributeCollector.php @@ -0,0 +1,49 @@ + + */ + protected $_accessible = [ + '*' => true, + 'id' => false, + 'slug' => false, + ]; +} diff --git a/app/plugins/CoreEnroller/src/Model/Entity/BasicPetitionAttributeSet.php b/app/plugins/CoreEnroller/src/Model/Entity/BasicPetitionAttributeSet.php new file mode 100644 index 000000000..b810bd47d --- /dev/null +++ b/app/plugins/CoreEnroller/src/Model/Entity/BasicPetitionAttributeSet.php @@ -0,0 +1,49 @@ + + */ + protected $_accessible = [ + '*' => true, + 'id' => false, + 'slug' => false, + ]; +} diff --git a/app/plugins/CoreEnroller/src/Model/Table/BasicAttributeCollectorsTable.php b/app/plugins/CoreEnroller/src/Model/Table/BasicAttributeCollectorsTable.php new file mode 100644 index 000000000..bc550daba --- /dev/null +++ b/app/plugins/CoreEnroller/src/Model/Table/BasicAttributeCollectorsTable.php @@ -0,0 +1,320 @@ +addBehavior('Changelog'); + $this->addBehavior('Log'); + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Configuration); + + // Define associations + $this->belongsTo('EnrollmentFlowSteps'); + $this->belongsTo('EmailAddressTypes') + ->setClassName('Types') + ->setForeignKey('email_address_type_id') + ->setProperty('email_address_type'); + $this->belongsTo('NameTypes') + ->setClassName('Types') + ->setForeignKey('name_type_id') + ->setProperty('name_type'); + $this->belongsTo('AffiliationTypes') + ->setClassName('Types') + ->setForeignKey('affiliation_type_id') + ->setProperty('affiliation_type'); + $this->belongsTo('Cous'); + + $this->hasMany('CoreEnroller.BasicPetitionAttributeSets') + ->setDependent(true) + ->setCascadeCallbacks(true); + + $this->setDisplayField('id'); + + $this->setPrimaryLink('enrollment_flow_step_id'); + $this->setRequiresCO(true); + $this->setAllowLookupPrimaryLink(['dispatch', 'display']); + + $this->setAutoViewVars([ + 'affiliationTypes' => [ + 'type' => 'type', + 'attribute' => 'PersonRoles.affiliation_type' + ], + 'cous' => [ + 'type' => 'select', + 'model' => 'Cous' + ], + 'emailAddressTypes' => [ + 'type' => 'type', + 'attribute' => 'EmailAddresses.type' + ], + 'nameTypes' => [ + 'type' => 'type', + 'attribute' => 'Names.type' + ] + ]); + + $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'] + ], + 'related' => [ + 'table' => [ + 'CoreEnroller.EnrollmentAttributes' + ] + ] + ]); + } + + /** + * Perform steps necessary to finalize the Petition. + * + * @since COmanage Registry v5.1.0 + * @param int $id Attribute Collector ID + * @param Petition $petition Petition + * @return bool true on success + */ + + public function finalize(int $id, \App\Model\Entity\Petition $petition) { + $cfg = $this->get($id); + + // At this point there is a Person record allocated and stored in the Petition, + // but it doesn't have any attributes on it, including a Primary Name. + // We assume we're the only attribute collector, so we'll force a Primary Name + // based on the Basic Attribtues, and create a skeletal role. + + if(empty($petition->enrollee_person_id)) { + throw new \InvalidArgumentException(__d('error', 'Petitions.enrollee.notfound', [$petition->id])); + } + + $People = TableRegistry::getTableLocator()->get('People'); + + $person = $People->get($petition->enrollee_person_id); + + $attributes = $this->BasicPetitionAttributeSets + ->find() + ->where([ + 'petition_id' => $petition->id, + // Strictly speaking we only support one instance per Flow, + // but we'll filter on the $id anyway since we have it + 'basic_attribute_collector_id' => $id + ]) + ->firstOrFail(); + + // Since we're not modifying $person, it's a bit clearer if we save each entity + // individually than try to save related + + $Names = TableRegistry::getTableLocator()->get('Names'); + +// XXX enforce CoSettings required/permitted fields here? + $name = [ + 'person_id' => $person->id, + 'primary_name' => true, + 'type_id' => $cfg->name_type_id + ]; + + foreach(['honorific', 'given', 'middle', 'family', 'suffix'] as $n) { + if(!empty($attributes->$n)) { + $name[$n] = $attributes->$n; + } + } + + $Names->saveOrFail($Names->newEntity($name)); + + $EmailAddresses = TableRegistry::getTableLocator()->get('EmailAddresses'); + + $email = [ + 'person_id' => $person->id, + 'mail' => $attributes->mail, + 'type_id' => $cfg->email_address_type_id + ]; + + $EmailAddresses->saveOrFail($EmailAddresses->newEntity($email)); + + $PersonRoles = TableRegistry::getTableLocator()->get('PersonRoles'); + + $personRole = [ + 'person_id' => $person->id, + 'affiliation_type_id' => $cfg->affiliation_type_id, + 'cou_id' => $cfg->cou_id, + 'status' => StatusEnum::Active + ]; + + $PersonRoles->saveOrFail($PersonRoles->newEntity($personRole)); + + $PetitionHistoryRecords = TableRegistry::getTableLocator()->get('PetitionHistoryRecords'); + + $PetitionHistoryRecords->record( + petitionId: $petition->id, + enrollmentFlowStepId: $cfg->enrollment_flow_step_id, + action: PetitionActionEnum::Finalized, + comment: __d('core_enroller', 'result.basicattr.finalized') +// We don't have $actorPersonId yet... +// ?int $actorPersonId=null + ); + + return true; + } + + /** + * Insert or update a Basic Petition Attribute Set. + * + * @since COmanage Registry v5.1.0 + * @param int $id Basic Attribute Collector ID + * @param int $petitionId Petition ID + * @param array $attributes Petition Attributes + * @return bool true on success + * @throws PersistenceFailedException + */ + + public function upsert(int $id, int $petitionId, array $attributes) { + $basicAttributeCollector = $this->get($id); + + // Do we have existing attributes for this petition? Note this will pull + // _all_ attributes for the Petition, not just those associated with this + // particular Attribute Collector; however we'll only look at the attributes + // we need below. + $entity = $this->BasicPetitionAttributeSets + ->find() + ->where([ + 'petition_id' => $petitionId, + // Strictly speaking we only support one instance per Flow, + // but we'll filter on the $id anyway since we have it + 'basic_attribute_collector_id' => $id + ]) + ->first(); + + if(!$entity) { + // insert, not update + + $entity = $this->BasicPetitionAttributeSets->newEntity([ + 'basic_attribute_collector_id' => $id, + 'petition_id' => $petitionId + ]); + } + + foreach(['honorific', 'given', 'middle', 'family', 'suffix', 'mail'] as $f) { +// XXX we should probably check CoSettings for name settings + $entity->$f = $attributes[$f] ?? null; + } + + $this->BasicPetitionAttributeSets->saveOrFail($entity); + + // Record Petition History + + $PetitionHistoryRecords = TableRegistry::getTableLocator()->get('PetitionHistoryRecords'); + + $PetitionHistoryRecords->record( + petitionId: $petitionId, + enrollmentFlowStepId: $basicAttributeCollector->enrollment_flow_step_id, + action: PetitionActionEnum::AttributesUpdated, + comment: __d('core_enroller', 'result.attr.saved') +// We don't have $actorPersonId yet... +// ?int $actorPersonId=null + ); + + return true; + } + + /** + * Set validation rules. + * + * @since COmanage Registry v5.1.0 + * @param Validator $validator Validator + * @return Validator Validator + */ + + 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('name_type_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('name_type_id'); + + $validator->add('email_address_type_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('email_address_type_id'); + + $validator->add('affiliation_type_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('affiliation_type_id'); + + $validator->add('cou_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->allowEmptyString('cou_id'); + + return $validator; + } +} diff --git a/app/plugins/CoreEnroller/src/Model/Table/BasicPetitionAttributeSetsTable.php b/app/plugins/CoreEnroller/src/Model/Table/BasicPetitionAttributeSetsTable.php new file mode 100644 index 000000000..d0f7bb5bb --- /dev/null +++ b/app/plugins/CoreEnroller/src/Model/Table/BasicPetitionAttributeSetsTable.php @@ -0,0 +1,125 @@ +addBehavior('Changelog'); + $this->addBehavior('Log'); + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Artifact); + + // Define associations + $this->belongsTo('CoreEnroller.BasicAttributeCollectors'); + $this->belongsTo('Petition'); + + $this->setDisplayField('mail'); + + $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' => ['platformAdmin', 'coAdmin'] + ] + ]); + } + + /** + * Set validation rules. + * + * @since COmanage Registry v5.1.0 + * @param Validator $validator Validator + * @return Validator Validator + */ + + public function validationDefault(Validator $validator): Validator { + $schema = $this->getSchema(); + + $validator->add('petition_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('petition_id'); + + $validator->add('basic_attribute_collector_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('basic_attribute_collector_id'); + + // For now we allow any attribute to be empty and rely on upsert and the UI to + // enforce requirements. + foreach(['honorific', 'given', 'middle', 'family', 'suffix'] as $f) { + $validator->add($f, [ + 'size' => ['rule' => ['validateMaxLength', ['column' => $schema->getColumn($f)]], + 'provider' => 'table'], + 'filter' => ['rule' => ['validateInput'], + 'provider' => 'table'] + ]); + $validator->allowEmptyString($f); + } + + $this->registerStringValidation($validator, $schema, 'mail', false); + $validator->add('mail', [ + 'content' => ['rule' => ['email'], + 'message' => __d('error', 'input.invalid.email')] + ]); + + return $validator; + } +} diff --git a/app/plugins/CoreEnroller/src/config/plugin.json b/app/plugins/CoreEnroller/src/config/plugin.json index 9a13fabbe..908d4b12a 100644 --- a/app/plugins/CoreEnroller/src/config/plugin.json +++ b/app/plugins/CoreEnroller/src/config/plugin.json @@ -1,7 +1,8 @@ { "types": { "enroller": [ - "AttributeCollectors" + "AttributeCollectors", + "BasicAttributeCollectors" ] }, "schema": { @@ -16,6 +17,40 @@ "attribute_collectors_i1": { "columns": [ "enrollment_flow_step_id" ] } } }, + "basic_attribute_collectors": { + "columns": { + "id": {}, + "enrollment_flow_step_id": {}, + "name_type_id": { "type": "integer", "foreignkey": { "table": "types", "column": "id" } }, + "email_address_type_id": { "type": "integer", "foreignkey": { "table": "types", "column": "id" } }, + "affiliation_type_id": { "type": "integer", "foreignkey": { "table": "types", "column": "id" } }, + "cou_id": { "type": "integer", "foreignkey": { "table": "cous", "column": "id" } } + }, + "indexes": { + "basic_attribute_collectors_i1": { "columns": [ "enrollment_flow_step_id" ] }, + "basic_attribute_collectors_i2": { "needed": false, "columns": [ "name_type_id" ] }, + "basic_attribute_collectors_i3": { "needed": false, "columns": [ "email_address_type_id" ] }, + "basic_attribute_collectors_i4": { "needed": false, "columns": [ "affiliation_type_id" ] }, + "basic_attribute_collectors_i5": { "needed": false, "columns": [ "cou_id" ] } + } + }, + "basic_petition_attribute_sets": { + "columns": { + "id": {}, + "petition_id": {}, + "basic_attribute_collector_id": { "type": "integer", "foreignkey": { "table": "basic_attribute_collectors", "column": "id" } }, + "honorific": { "type": "string", "size": 32 }, + "given": { "type": "string", "size": 128 }, + "middle": { "type": "string", "size": 128 }, + "family": { "type": "string", "size": 128 }, + "suffix": { "type": "string", "size": 32 }, + "mail": { "type": "string", "size": 256 } + }, + "indexes": { + "basic_petition_attribute_sets_i1": { "columns": [ "petition_id" ] }, + "basic_petition_attribute_sets_i2": { "columns": [ "basic_attribute_collector_id" ] } + } + }, "enrollment_attributes": { "columns": { "id": {}, diff --git a/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/dispatch.inc b/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/dispatch.inc new file mode 100644 index 000000000..a89f5620a --- /dev/null +++ b/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/dispatch.inc @@ -0,0 +1,48 @@ +Field->enableFormEditMode(); + + foreach(['honorific', 'given', 'middle', 'family', 'suffix'] as $n) { + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => $n, + 'fieldLabel' => __d('field', $n) + ]]); + } + + print $this->element('form/listItem', [ + 'arguments' => [ + 'fieldName' => 'mail', + 'fieldLabel' => __d('field', 'mail') + ]]); +} \ No newline at end of file diff --git a/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/display.php b/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/display.php new file mode 100644 index 000000000..01b697ec7 --- /dev/null +++ b/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/display.php @@ -0,0 +1,8 @@ + diff --git a/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/fields.inc b/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/fields.inc new file mode 100644 index 000000000..b1dd114b8 --- /dev/null +++ b/app/plugins/CoreEnroller/templates/BasicAttributeCollectors/fields.inc @@ -0,0 +1,45 @@ +element('form/listItem', [ + 'arguments' => ['fieldName' => 'name_type_id'] + ]); + + print $this->element('form/listItem', [ + 'arguments' => ['fieldName' => 'email_address_type_id'] + ]); + + print $this->element('form/listItem', [ + 'arguments' => ['fieldName' => 'affiliation_type_id'] + ]); + + print $this->element('form/listItem', [ + 'arguments' => ['fieldName' => 'cou_id'] + ]); +} diff --git a/app/resources/locales/en_US/error.po b/app/resources/locales/en_US/error.po index 59e60bb0b..f236ba617 100644 --- a/app/resources/locales/en_US/error.po +++ b/app/resources/locales/en_US/error.po @@ -286,6 +286,9 @@ msgstr "Valid From date must be earlier than Valid Through date" msgid "Petitions.completed" msgstr "Petition {0} has been completed and cannot be changed" +msgid "Petitions.enrollee.notfound" +msgstr "Enrollee Person not found in Petition {0}" + msgid "Petitions.enrollee_email" msgstr "An Email Address for the Enrollee is required by this Enrollment Flow" diff --git a/app/src/Model/Table/PetitionsTable.php b/app/src/Model/Table/PetitionsTable.php index 5fd8554ab..4b76121ef 100644 --- a/app/src/Model/Table/PetitionsTable.php +++ b/app/src/Model/Table/PetitionsTable.php @@ -157,6 +157,8 @@ public function initialize(array $config): void { 'resume' => ['platformAdmin', 'coAdmin'], 'view' => ['platformAdmin', 'coAdmin'] ], + // Actions that are permitted on readonly entities (besides view) + 'readOnly' => ['result'], // Actions that operate over a table (ie: do not require an $id) 'table' => [ 'add' => false, @@ -267,7 +269,7 @@ public function finalize(int $id) { // We have "CoreEnroller.AttributeCollectors" but we want "attribute_collector" $pmodel = Inflector::underscore(Inflector::singularize(StringUtilities::pluginModel($step->plugin))); - $Plugin->finalize($step->$pmodel->id, $petition->id); + $Plugin->finalize($step->$pmodel->id, $petition); } } catch(\Exception $e) { diff --git a/app/templates/Petitions/fields.inc b/app/templates/Petitions/fields.inc index 6d5df1c3b..dba8a6aa7 100644 --- a/app/templates/Petitions/fields.inc +++ b/app/templates/Petitions/fields.inc @@ -82,14 +82,28 @@ if (!empty($vv_obj?->petitioner_person?->id)) { ?> +
    enrollment_flow->enrollment_flow_steps as $step): ?> -
  1. description ?> - petition_step_results, "{n}[enrollment_flow_step_id=$step->id]"); ?> - -
     ,Result: comment ?>
    - (View) - + petition_step_results, "{n}[enrollment_flow_step_id=$step->id]"); + + if(!empty($result)) { + $resultLink = [ + 'controller' => 'petitions', + 'action' => 'result', + $vv_obj->id, + '?' => [ + 'enrollment_flow_step_id' => $step->id + ] + ]; + } + ?> + +
  2. description ?>
  3. +
diff --git a/app/templates/Standard/dispatch.php b/app/templates/Standard/dispatch.php index cc1550548..4fa9d546a 100644 --- a/app/templates/Standard/dispatch.php +++ b/app/templates/Standard/dispatch.php @@ -53,9 +53,6 @@

-