diff --git a/app/plugins/HistoricPetitionViewer/HistoricPetitionViewerPlugin.php b/app/plugins/HistoricPetitionViewer/HistoricPetitionViewerPlugin.php index 714f1692..1355e0fd 100644 --- a/app/plugins/HistoricPetitionViewer/HistoricPetitionViewerPlugin.php +++ b/app/plugins/HistoricPetitionViewer/HistoricPetitionViewerPlugin.php @@ -40,12 +40,14 @@ public function bootstrap(PluginApplicationInterface $app): void public function routes(RouteBuilder $routes): void { $routes->plugin( - 'HistoryPetitionViewer', + 'HistoricPetitionViewer', ['path' => '/historic-petition-viewer'], function (RouteBuilder $builder) { - // Add custom routes here + // Your snippet goes here, using $builder instead of $routes + $builder->setRouteClass(DashedRoute::class); - $builder->fallbacks(); + // Add your plugin routes here if needed, then: + $builder->fallbacks(DashedRoute::class); } ); parent::routes($routes); diff --git a/app/plugins/HistoricPetitionViewer/config/plugin.json b/app/plugins/HistoricPetitionViewer/config/plugin.json index 3cd8921a..01fde4d0 100644 --- a/app/plugins/HistoricPetitionViewer/config/plugin.json +++ b/app/plugins/HistoricPetitionViewer/config/plugin.json @@ -10,11 +10,17 @@ "columns": { "id": { "type": "integer", "autoincrement": true, "primarykey": true }, "petition_id": { "type": "integer", "foreignkey": { "table": "petitions", "column": "id" } }, + "historic_petition_viewer_id": { + "type": "integer", + "foreignkey": { "table": "historic_petition_viewers", "column": "id" }, + "notnull": false + }, + "attribute": { "type": "string", "size": 128 }, "value": { "type": "text" }, "created": { "type": "datetime" }, "modified": { "type": "datetime" }, - "historic_petition_attribute_id": { "type": "integer", "foreignkey": { "table": "petition_hist_attrs", "column": "id" } }, + "petition_hist_attr_id": { "type": "integer", "foreignkey": { "table": "petition_hist_attrs", "column": "id" } }, "revision": { "type": "integer" }, "deleted": { "type": "boolean" }, "actor_identifier": { "type": "string", "size": 256 } @@ -22,7 +28,7 @@ "indexes": { "petition_hist_attrs_i1": { "columns": [ "petition_id" ] }, "petition_hist_attrs_i2": { "columns": [ "attribute" ] }, - "petition_hist_attrs_i3": { "columns": [ "historic_petition_attribute_id" ] } + "petition_hist_attrs_i3": { "columns": [ "petition_hist_attr_id" ] } }, "timestamps": false, "changelog": false @@ -32,6 +38,12 @@ "columns": { "id": { "type": "integer", "autoincrement": true, "primarykey": true }, "petition_id": { "type": "integer", "foreignkey": { "table": "petitions", "column": "id" } }, + "enrollment_flow_id": { "type": "integer", "foreignkey": { "table": "enrollment_flows", "column": "id" }}, + "historic_petition_viewer_id": { + "type": "integer", + "foreignkey": { "table": "historic_petition_viewers", "column": "id" }, + "notnull": false + }, "enrollee_org_identity_id": { "type": "integer" }, "archived_org_identity_id": { "type": "integer" }, @@ -60,17 +72,14 @@ "timestamps": false, "changelog": false }, - "historic_petition_step_links": { - "comment": "Associates a Petition’s historic data to a specific Enrollment Flow Step for read-only viewing", + "historic_petition_viewers": { + "comment": "Enrollment Flow Step for historic petition data", "columns": { "id": { "type": "integer", "autoincrement": true, "primarykey": true }, - "petition_id": { "type": "integer", "foreignkey": { "table": "petitions", "column": "id" } }, "enrollment_flow_step_id": { "type": "integer", "foreignkey": { "table": "enrollment_flow_steps", "column": "id" } } }, "indexes": { - "historic_petition_step_links_i1": { "columns": [ "petition_id" ] }, - "historic_petition_step_links_i2": { "columns": [ "enrollment_flow_step_id" ] }, - "historic_petition_step_links_u1": { "columns": [ "petition_id", "enrollment_flow_step_id" ] } + "historic_petition_step_links_i1": { "columns": [ "enrollment_flow_step_id" ] } } } } diff --git a/app/plugins/HistoricPetitionViewer/src/Controller/HistoricPetitionViewersController.php b/app/plugins/HistoricPetitionViewer/src/Controller/HistoricPetitionViewersController.php new file mode 100644 index 00000000..0f4b5a0c --- /dev/null +++ b/app/plugins/HistoricPetitionViewer/src/Controller/HistoricPetitionViewersController.php @@ -0,0 +1,40 @@ + [ + 'HistoricPetitionViewers.id' => 'asc' + ] + ]; +} diff --git a/app/plugins/HistoricPetitionViewer/src/Model/Entity/HistoricPetitionStepLink.php b/app/plugins/HistoricPetitionViewer/src/Model/Entity/HistoricPetitionViewer.php similarity index 93% rename from app/plugins/HistoricPetitionViewer/src/Model/Entity/HistoricPetitionStepLink.php rename to app/plugins/HistoricPetitionViewer/src/Model/Entity/HistoricPetitionViewer.php index 0788334e..8ada2824 100644 --- a/app/plugins/HistoricPetitionViewer/src/Model/Entity/HistoricPetitionStepLink.php +++ b/app/plugins/HistoricPetitionViewer/src/Model/Entity/HistoricPetitionViewer.php @@ -1,6 +1,6 @@ setTable('petition_hist_attrs'); + $this->setPrimaryKey('id'); + $this->setDisplayField('id'); $this->addBehavior('Changelog'); $this->addBehavior('Log'); @@ -56,12 +60,13 @@ public function initialize(array $config): void { // Define associations $this->belongsTo('Petitions'); + $this->belongsTo('HistoricPetitionViewers'); $this->setDisplayField('attribute'); $this->setPrimaryLink('petition_id'); $this->setRequiresCO(false); -// $this->setAllowLookupPrimaryLink(['dispatch', 'display']); + $this->setAllowLookupPrimaryLink(['display']); $this->setPermissions([ // Actions that operate over an entity (ie: require an $id) @@ -106,11 +111,11 @@ public function validationDefault(Validator $validator): Validator { // value (text, optional) $validator->allowEmptyString('value'); - // historic_petition_attribute_id (self-referential changelog FK, optional) - $validator->add('historic_petition_attribute_id', [ + // petition_hist_attr_id (self-referential changelog FK, optional) + $validator->add('petition_hist_attr_id', [ 'content' => ['rule' => 'isInteger'] ]); - $validator->allowEmptyString('historic_petition_attribute_id'); + $validator->allowEmptyString('petition_hist_attr_id'); // revision (optional integer) $validator->add('revision', [ diff --git a/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionMetadataRecordsTable.php b/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionMetadataRecordsTable.php index 89a6da23..99a63256 100644 --- a/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionMetadataRecordsTable.php +++ b/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionMetadataRecordsTable.php @@ -34,6 +34,7 @@ use Cake\Validation\Validator; class HistoricPetitionMetadataRecordsTable extends Table { + use \App\Lib\Traits\CoLinkTrait; use \App\Lib\Traits\LabeledLogTrait; use \App\Lib\Traits\LayoutTrait; use \App\Lib\Traits\PermissionsTrait; @@ -47,6 +48,8 @@ public function initialize(array $config): void { // Map model to shorter physical table name $this->setTable('petition_meta_hist_recs'); + $this->setPrimaryKey('id'); + $this->setDisplayField('id'); $this->addBehavior('Changelog'); $this->addBehavior('Log'); @@ -56,11 +59,13 @@ public function initialize(array $config): void { // Define associations $this->belongsTo('Petitions'); + $this->belongsTo('HistoricPetitionViewers'); + $this->belongsTo('EnrollmentFlows'); $this->setDisplayField('petition_id'); $this->setRequiresCO(false); - $this->setAllowLookupPrimaryLink(['dispatch', 'display']); + $this->setAllowLookupPrimaryLink(['display']); $this->setPermissions([ // Actions that operate over an entity (ie: require an $id) diff --git a/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionStepLinksTable.php b/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionViewersTable.php similarity index 93% rename from app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionStepLinksTable.php rename to app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionViewersTable.php index 1fda42bc..221f930f 100644 --- a/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionStepLinksTable.php +++ b/app/plugins/HistoricPetitionViewer/src/Model/Table/HistoricPetitionViewersTable.php @@ -33,7 +33,8 @@ use Cake\ORM\Table; use Cake\Validation\Validator; -class HistoricPetitionStepLinksTable extends Table { +class HistoricPetitionViewersTable extends Table { + use \App\Lib\Traits\CoLinkTrait; use \App\Lib\Traits\LabeledLogTrait; use \App\Lib\Traits\LayoutTrait; use \App\Lib\Traits\PermissionsTrait; @@ -59,20 +60,20 @@ public function initialize(array $config): void { $this->setPrimaryLink('enrollment_flow_step_id'); $this->setRequiresCO(false); - $this->setAllowLookupPrimaryLink(['dispatch', 'display']); + $this->setAllowLookupPrimaryLink(['display']); // All the tabs share the same configuration in the ModelTable file $this->setTabsConfig( [ // Ordered list of Tabs - 'tabs' => ['EnrollmentFlowSteps', 'HistoricPetitionViewer.HistoricPetitionStepLinks'], + 'tabs' => ['EnrollmentFlowSteps', 'HistoricPetitionViewer.HistoricPetitionViewers'], // 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' => ['view'], - 'HistoricPetitionViewer.HistoricPetitionStepLinks' => ['view'] + 'HistoricPetitionViewer.HistoricPetitionViewers' => ['view'] ] ] ); @@ -83,7 +84,7 @@ public function initialize(array $config): void { 'delete' => false, 'dispatch' => false, 'display' => true, - 'edit' => false, + 'edit' => true, 'view' => ['platformAdmin', 'coAdmin'] ], // Actions that operate over a table (ie: do not require an $id) diff --git a/app/plugins/HistoricPetitionViewer/src/View/Cell/HistoricPetitionStepLinksCell.php b/app/plugins/HistoricPetitionViewer/src/View/Cell/HistoricPetitionViewersCell.php similarity index 65% rename from app/plugins/HistoricPetitionViewer/src/View/Cell/HistoricPetitionStepLinksCell.php rename to app/plugins/HistoricPetitionViewer/src/View/Cell/HistoricPetitionViewersCell.php index e2345518..a5f29cec 100644 --- a/app/plugins/HistoricPetitionViewer/src/View/Cell/HistoricPetitionStepLinksCell.php +++ b/app/plugins/HistoricPetitionViewer/src/View/Cell/HistoricPetitionViewersCell.php @@ -31,14 +31,11 @@ declare(strict_types=1); -namespace CoreEnroller\View\Cell; +namespace HistoricPetitionViewer\View\Cell; use Cake\View\Cell; -/** - * BasicAttributeCollectors cell - */ -class HistoricPetitionStepLinksCell extends Cell +class HistoricPetitionViewersCell extends Cell { /** * @var mixed @@ -85,21 +82,38 @@ public function initialize(): void */ public function display(int $petitionId): void { - // Fetch historic metadata rows for this petition (could be zero or more) - $historicMetadataRecords = $this->fetchTable('HistoricPetitionViewer.HistoricPetitionMetadataRecords') - ->find() + // Latest (current) metadata record for this petition + $metaTable = $this->fetchTable('HistoricPetitionViewer.HistoricPetitionMetadataRecords'); + $latestMeta = $metaTable->find() + ->applyOptions(['archived' => false]) ->where(['petition_id' => $petitionId]) + ->orderBy(['id' => 'DESC']) + ->limit(1) ->all(); - // Fetch historic attributes rows for this petition (could be zero or more) - $historicAttributes = $this->fetchTable('HistoricPetitionViewer.HistoricPetitionAttributes') - ->find() + // Current attributes for this petition, deduplicated to latest row per attribute + $attrTable = $this->fetchTable('HistoricPetitionViewer.HistoricPetitionAttributes'); + + // Subquery: max(id) per attribute for the petition + $sub = $attrTable->find() + ->select([ + 'max_id' => $attrTable->find()->func()->max('id'), + ]) + ->applyOptions(['archived' => false]) ->where(['petition_id' => $petitionId]) + ->group('attribute'); + + $latestAttrs = $attrTable->find() + ->applyOptions(['archived' => false]) + ->where(function ($exp) use ($sub) { + // id IN (SELECT max(id) ...) for the petition, one per attribute + return $exp->in('HistoricPetitionAttributes.id', $sub); + }) ->orderBy(['attribute' => 'ASC', 'id' => 'ASC']) ->all(); - $this->set('vv_historic_petition_metadata_records', $historicMetadataRecords); - $this->set('vv_historic_petition_attributes', $historicAttributes); + $this->set('vv_historic_petition_metadata_records', $latestMeta); // array with at most 1 record + $this->set('vv_historic_petition_attributes', $latestAttrs); // deduped by attribute $this->set('vv_obj', $this->vv_obj); } } diff --git a/app/plugins/HistoricPetitionViewer/templates/HistoricPetitionViewers/fields.inc b/app/plugins/HistoricPetitionViewer/templates/HistoricPetitionViewers/fields.inc new file mode 100644 index 00000000..20843df6 --- /dev/null +++ b/app/plugins/HistoricPetitionViewer/templates/HistoricPetitionViewers/fields.inc @@ -0,0 +1,32 @@ +Field->disableFormEditMode(); + +// There are currently no configurable options for the SSH Key Authenticator +print $this->element('notify/banner', ['info' => __d('information', 'plugin.config.none')]); \ No newline at end of file diff --git a/app/plugins/HistoricPetitionViewer/templates/cell/HistoricPetitionStepLinks/display.php b/app/plugins/HistoricPetitionViewer/templates/cell/HistoricPetitionStepLinks/display.php deleted file mode 100644 index 4055cd21..00000000 --- a/app/plugins/HistoricPetitionViewer/templates/cell/HistoricPetitionStepLinks/display.php +++ /dev/null @@ -1,104 +0,0 @@ -id === null) { - echo __d('error', 'notfound', 'Historic Petition'); - return; -} - -// Normalize collections -$metaRows = $vv_historic_petition_metadata_records ?? []; -$attrRows = $vv_historic_petition_attributes ?? []; - -// Helper: render a KV list from an entity/array, excluding technical fields -$excludeMetaKeys = [ - 'id', 'petition_id', - 'historic_petition_metadata_id', 'revision', 'deleted', - 'actor_identifier', 'created', 'modified' -]; - -// Order token-like fields first for readability if present -$preferredMetaOrder = [ - 'approver_comment', - 'return_url', - 'token', 'petitioner_token', 'enrollee_token', - 'enrollee_org_identity_id', 'archived_org_identity_id', 'enrollee_person_role_id', - 'sponsor_person_id', 'approver_person_id', - 'co_invite_id', 'vetting_request_id', -]; - -function renderMetaKeyValue(string $label, mixed $value): void { - if ($value === null || $value === '') { - return; - } - ?> -
= h(__('No historic attributes recorded for this petition.')) ?>
- -= h(__('No historic metadata recorded for this petition.')) ?>
+ += h(__('No historic attributes recorded for this petition.')) ?>
+ +