diff --git a/app/plugins/CoreEnroller/src/Controller/AttributeCollectorsController.php b/app/plugins/CoreEnroller/src/Controller/AttributeCollectorsController.php
index 27faa2749..7665edb2d 100644
--- a/app/plugins/CoreEnroller/src/Controller/AttributeCollectorsController.php
+++ b/app/plugins/CoreEnroller/src/Controller/AttributeCollectorsController.php
@@ -30,9 +30,9 @@
namespace CoreEnroller\Controller;
use App\Controller\StandardEnrollerController;
-use App\Lib\Enum\PetitionStatusEnum;
use Cake\Event\EventInterface;
use Cake\Http\Response;
+use Cake\ORM\TableRegistry;
class AttributeCollectorsController extends StandardEnrollerController {
public $paginate = [
@@ -125,12 +125,19 @@ public function dispatch(string $id) {
/**
* Display information about this Step.
*
- * @since COmanage Registry v5.0.0
+ * @since COmanage Registry v5.1.0
* @param string $id Attribute Collector ID
*/
- public function display(string $id) {
- debug("display something for this petition");
- debug($this->getPetition());
+ public function display(string $id): void {
+ $petition = $this->getPetition();
+
+ $this->set('vv_petition_attributes',
+ $this->AttributeCollectors
+ ->EnrollmentAttributes
+ ->PetitionAttributes
+ ->find()
+ ->where(['petition_id' => $petition->id])
+ ->all());
}
}
diff --git a/app/plugins/CoreEnroller/src/Model/Table/AttributeCollectorsTable.php b/app/plugins/CoreEnroller/src/Model/Table/AttributeCollectorsTable.php
index 18e3c22e9..bd5b1d27a 100644
--- a/app/plugins/CoreEnroller/src/Model/Table/AttributeCollectorsTable.php
+++ b/app/plugins/CoreEnroller/src/Model/Table/AttributeCollectorsTable.php
@@ -166,15 +166,33 @@ public function upsert(int $id, int $petitionId, array $attributes) {
foreach($attributes as $enrollmentAttributeLabel => $value) {
// Remove field- prefix from the form field name
- $enrollmentAttributeId = (int)substr($enrollmentAttributeLabel, 6);
+ $fieldToParts = explode('-', $enrollmentAttributeLabel);
+ // MVAs have multiple columns. For example a name has:
+ // given, surname, prefix, ...
+ $columnName = null;
+ if (count($fieldToParts) > 2) {
+ // There is a type ID in the middle
+ $columnName = $fieldToParts[1];
+ }
+ // The enrollment Attribute ID is the last part
+ $enrollmentAttributeId = (int)array_pop($fieldToParts);
+
+ // The people picker will send a complex string and not just the id. We need to extract it ourselves.
+ $re = '/^.*\(ID: (\d+)\)$/m';
+ preg_match_all($re, $value, $matches, PREG_SET_ORDER, 0);
+ if(!empty($matches)) {
+ $value = $matches[0][1];
+ }
$newAttribute = [
'petition_id' => $petitionId,
'enrollment_attribute_id' => $enrollmentAttributeId,
- 'value' => $value
+ 'value' => $value,
+ // This is the column name for the attributes that consist of multiple fields, like the name or the address
+ 'column_name' => $columnName,
];
- if(array_key_exists($enrollmentAttributeId, $currentAttributes)) {
+ if(\array_key_exists($enrollmentAttributeId, $currentAttributes)) {
// This is an update of an existing attribute
$newAttribute['id'] = $currentAttributes[$enrollmentAttributeId];
diff --git a/app/plugins/CoreEnroller/src/Model/Table/PetitionAttributesTable.php b/app/plugins/CoreEnroller/src/Model/Table/PetitionAttributesTable.php
index 161d873eb..d26329d5f 100644
--- a/app/plugins/CoreEnroller/src/Model/Table/PetitionAttributesTable.php
+++ b/app/plugins/CoreEnroller/src/Model/Table/PetitionAttributesTable.php
@@ -103,6 +103,7 @@ public function validationDefault(Validator $validator): Validator {
$validator->notEmptyString('enrollment_attribute_id');
$this->registerStringValidation($validator, $schema, 'value', false);
+ $this->registerStringValidation($validator, $schema, 'column', false);
return $validator;
}
diff --git a/app/plugins/CoreEnroller/src/config/plugin.json b/app/plugins/CoreEnroller/src/config/plugin.json
index dbb666b8a..bb58ddb56 100644
--- a/app/plugins/CoreEnroller/src/config/plugin.json
+++ b/app/plugins/CoreEnroller/src/config/plugin.json
@@ -111,7 +111,8 @@
"id": {},
"petition_id": {},
"enrollment_attribute_id": { "type": "integer", "foreignkey": { "table": "enrollment_attributes", "column": "id" } },
- "value": { "type": "string", "size": 160 }
+ "value": { "type": "string", "size": 160 },
+ "column_name": { "notnull": false, "type": "string", "size": 64 }
},
"indexes": {
"petition_attributes_i1": { "columns": [ "petition_id" ] },
diff --git a/app/plugins/CoreEnroller/templates/AttributeCollectors/display.php b/app/plugins/CoreEnroller/templates/AttributeCollectors/display.php
new file mode 100644
index 000000000..f25efb457
--- /dev/null
+++ b/app/plugins/CoreEnroller/templates/AttributeCollectors/display.php
@@ -0,0 +1,8 @@
+toArray();
+?>
+
+
+ - ID: = $attribute->id?>, Value: = $attribute->value?>, Column Name: = $attribute->column_name ?>
+
+
diff --git a/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc b/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc
index 0ea015f21..09a3a971d 100644
--- a/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc
+++ b/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc
@@ -32,8 +32,7 @@ use \Cake\Utility\Inflector;
$attribute_type = null;
if($vv_action == 'add') {
$attribute_type = $this->request->getQuery('attribute_type');
-} else if($vv_action == 'edit' && !empty($vv_obj)
-) {
+} else if($vv_action == 'edit' && !empty($vv_obj)) {
$attribute_type = $vv_obj->attribute;
}
diff --git a/app/plugins/CoreEnroller/templates/element/field.php b/app/plugins/CoreEnroller/templates/element/field.php
index b5cd48d5a..80a3b8cd4 100644
--- a/app/plugins/CoreEnroller/templates/element/field.php
+++ b/app/plugins/CoreEnroller/templates/element/field.php
@@ -119,5 +119,5 @@
'formArguments' => $formArguments
]),
// Default use case
- default => $this->element('form/listItem', ['arguments' => $formArguments])
+ default => $this->Field->getElementsForDisabledInput('form/listItem', $formArguments)
};
diff --git a/app/plugins/CoreEnroller/templates/element/mveas/fieldset-field.php b/app/plugins/CoreEnroller/templates/element/mveas/fieldset-field.php
index 58c89298c..4a1624761 100644
--- a/app/plugins/CoreEnroller/templates/element/mveas/fieldset-field.php
+++ b/app/plugins/CoreEnroller/templates/element/mveas/fieldset-field.php
@@ -38,12 +38,39 @@
$isRequiredFromValidationRule = false;
$supportedAttributes = $this->Petition->getSupportedEnrollmentAttribute($attr->attribute);
+// Field Options array
+$options = [];
+
if(isset($supportedAttributes['mveaModel'])) {
$supportedAttributes = $this->Petition->getSupportedEnrollmentAttribute($attr->attribute);
$modelTable = $this->Petition->getTable($supportedAttributes['mveaModel']);
$isRequiredFromValidationRule = !$modelTable->getValidator()->field($field)->isEmptyAllowed();
}
+// Do we have a default value configured?
+// Either a value or an Environmental Variable,
+// Each default value is mutually exclusive to the rest. We do not have to worry about a conflict.
+$options['default'] = match(true) {
+ isset($attr->default_value) => $attr->default_value,
+ isset($attr->default_value_env_name)
+ && getenv($attr->default_value_env_name) !== false => getenv($attr->default_value_env_name),
+ isset($attr->default_value_datetime) => $attr->default_value_datetime,
+ default => ''
+};
+
+// If we are re-rendering the Petition, override the default value with whatever
+// was previously saved
+if(!empty($vv_petition_attributes)) {
+ $curEntity = $vv_petition_attributes->firstMatch([
+ 'enrollment_attribute_id' => $attr->id,
+ 'column_name' => $field
+ ]);
+
+ if(!empty($curEntity->value)) {
+ $options['default'] = $curEntity->value;
+ }
+}
+
// Construct the field arguments
$formArguments = [
// We prefix the attribute ID with a string because Cake seems to sometimes have
@@ -53,6 +80,9 @@
'fieldType' => $modelTable->getSchema()->getColumn($field)['type'],
'fieldNameAlias' => $attr->attribute // the field name to its enrollment attribute field name
];
+
+// Set the final fieldOptions
+$formArguments['fieldOptions'] = $options;
?>
">
diff --git a/app/src/Model/Table/PetitionsTable.php b/app/src/Model/Table/PetitionsTable.php
index fd760387e..e999b7d80 100644
--- a/app/src/Model/Table/PetitionsTable.php
+++ b/app/src/Model/Table/PetitionsTable.php
@@ -90,6 +90,9 @@ public function initialize(array $config): void {
$this->hasMany('PetitionStepResults')
->setDependent(true)
->setCascadeCallbacks(true);
+ $this->hasMany('PetitionAttributes')
+ ->setDependent(true)
+ ->setCascadeCallbacks(true);
$this->hasOne('Verifications')
->setDependent(true)
@@ -116,7 +119,8 @@ public function initialize(array $config): void {
'EnrolleePeople' => ['PrimaryName' => ['foreignKey' => 'person_id']],
'PetitionerPeople' => ['PrimaryName' => ['foreignKey' => 'person_id']],
'PetitionHistoryRecords',
- 'PetitionStepResults'
+ 'PetitionStepResults',
+ 'PetitionAttributes',
]);
$this->setAutoViewVars([
diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php
index 4a221150c..34ca57ce3 100644
--- a/app/src/View/Helper/FieldHelper.php
+++ b/app/src/View/Helper/FieldHelper.php
@@ -304,7 +304,7 @@ public function dateField(string $fieldName,
isset($fieldArgs['default']) && is_string($fieldArgs['default']) => FrozenTime::parse($fieldArgs['default']),
// Petition View/ Value saved a FronzenTime
isset($fieldArgs['default'])
- && is_a($fieldArgs['default'], 'Cake\I18n\FrozenTime') => $fieldArgs['default'],
+ && is_a($fieldArgs['default'], 'Cake\I18n\FrozenTime') => $fieldArgs['default'],
// Table record/ Retrieve it from the Entity object
default => $this->getEntity()?->$fieldName,
};
diff --git a/app/src/View/Helper/PetitionHelper.php b/app/src/View/Helper/PetitionHelper.php
index 0f1ddec59..a23edf2e0 100644
--- a/app/src/View/Helper/PetitionHelper.php
+++ b/app/src/View/Helper/PetitionHelper.php
@@ -35,6 +35,7 @@
use Cake\Utility\Inflector;
use Cake\View\Helper;
use CoreEnroller\Model\Table\EnrollmentAttributesTable;
+use DOMDocument;
class PetitionHelper extends Helper
{
diff --git a/app/templates/Petitions/fields.inc b/app/templates/Petitions/fields.inc
index 1665c1682..a0a0cb007 100644
--- a/app/templates/Petitions/fields.inc
+++ b/app/templates/Petitions/fields.inc
@@ -230,35 +230,37 @@ if (!empty($vv_obj?->petitioner_person?->id)) {
-
enrollment_flow->enrollment_flow_steps as $step): ?>
- 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
- ]
- ];
- }
- ?>
-
+ petition_step_results, "{n}[enrollment_flow_step_id=$step->id]"); ?>
- = $step->description ?>
+ = $step->description ?>
- = !empty($result) ? $this->Html->link($result[0]->comment, $resultLink) : "" ?>
+ = !empty($result) ? $result[0]->comment : '' ?>
+
+
+ arrow_drop_down
+
+
+
+
+ = __d('menu', 'co.attributes') ?>
+
+ = $this->element('petition/enrollmentFlowStep', ['vv_step' => $step])?>
+
diff --git a/app/templates/element/petition/enrollmentFlowStep.php b/app/templates/element/petition/enrollmentFlowStep.php
new file mode 100644
index 000000000..afc898b70
--- /dev/null
+++ b/app/templates/element/petition/enrollmentFlowStep.php
@@ -0,0 +1,40 @@
+plugin);
+$modelName = array_pop($parsePluginName);
+$elementName = lcfirst($modelName) . 'Step';
+
+// The convention is that the element name for each step is the name of the EnrollmentFlow Step model followed by
+// the word Step
+print $this->element("petition/enrollmentFlowSteps/$elementName", ['vv_step' => $vv_step]);
diff --git a/app/templates/element/petition/enrollmentFlowSteps/attributeCollectorsStep.php b/app/templates/element/petition/enrollmentFlowSteps/attributeCollectorsStep.php
new file mode 100644
index 000000000..9ff02c4e3
--- /dev/null
+++ b/app/templates/element/petition/enrollmentFlowSteps/attributeCollectorsStep.php
@@ -0,0 +1,91 @@
+petition_attributes, '{n}.enrollment_attribute_id');
+$vv_enrollment_atttributes_ids = array_unique($vv_enrollment_atttributes_ids);
+
+$enrollmentAttributesTable = $this->Petition->getTable('EnrollmentAttributes');
+$vv_enrollment_attributes = $enrollmentAttributesTable->find()
+ ->where(fn(QueryExpression $exp, Query $q) => $exp->in('id', $vv_enrollment_atttributes_ids))
+ ->toArray();
+
+?>
+
+
+
+ petition_attributes)->filter(fn($attr) => $attr->enrollment_attribute_id === $attribute->id) ?>
+ count() === 1): ?>
+ first();
+ $value = $attr->value ?? 'N/A';
+ if(!empty($value) && str_ends_with($attribute->attribute, 'person_id')) {
+ // We need to load the associated model data
+ $associatedModel = $this->Petition->getRecordForId('person_id', $attr->value, ['PrimaryName']);
+ $value = $associatedModel[0]->primary_name->given . ' '
+ . $associatedModel[0]->primary_name->family . ' '
+ . '(ID: ' . $associatedModel[0]->id . ')';
+ $value = $this->Html->link($value,
+ ['controller' => 'people', 'action' => 'edit', $attr->value]);
+ } elseif(!empty($value) && str_ends_with($attribute->attribute, '_id')) {
+ // We need to load the associated model data
+ $associatedModel = $this->Petition->getRecordForId($attribute->attribute, $attr->value);
+ $value = $associatedModel['name'] ?? $associatedModel['value'] ?? $associatedModel['description'] ?? 'N/A';
+ $value = $this->Html->link($value,
+ ['controller' => \App\Lib\Util\StringUtilities::foreignKeyToController($attribute->attribute), 'action' => 'edit', $attr->value]);
+ }
+ ?>
+ -
+
= $attribute->label ?>
+ = $value ?>
+
+
+
+ -
+
= $attribute->label ?>
+
+
+ -
+
= $attr->column_name ?>
+ = $attr->value ?>
+
+
+
+
+
+
+
diff --git a/app/webroot/css/co-base.css b/app/webroot/css/co-base.css
index 92584f03f..799fa3b08 100644
--- a/app/webroot/css/co-base.css
+++ b/app/webroot/css/co-base.css
@@ -1001,6 +1001,21 @@ ul.form-list li.alert-banner .co-alert {
white-space: nowrap;
margin-right: 0.5em;
}
+/* PETITION PREVIEW */
+a.cm-toggle {
+ float: right;
+ border: none;
+ background-color: transparent;
+}
+a.cm-toggle .material-symbols,
+a.cm-toggle .material-symbols-outlined{
+ color: var(--cmg-color-headings);
+}
+a.cm-toggle .material-symbols,
+a.cm-toggle .material-symbols-outlined {
+ font-size: 34px;
+ line-height: 17px;
+}
/* PLATFORM NOTICE (for COmanage CO) */
#platform-notice {
padding: 0.5em;
diff --git a/app/webroot/js/comanage/comanage.js b/app/webroot/js/comanage/comanage.js
index dd8b88aba..a15bea941 100644
--- a/app/webroot/js/comanage/comanage.js
+++ b/app/webroot/js/comanage/comanage.js
@@ -423,4 +423,31 @@ function setApplicationState(value, elem, reload= false) {
generateFlash("" + errorThrown + " (" + jqXHR.status + ")", 'error')
}
});
+}
+
+function stepCollapseTongle(event, item) {
+ /**
+ * Toggles the expanded or collapsed state of the step element.
+ *
+ * This function prevents the default action and stops the propagation of the event,
+ * then toggles the "collapsed" class for the given element. It also updates
+ * the `aria-expanded` attribute and changes the icon displayed within the element.
+ *
+ * - If the element is collapsed, it sets `aria-expanded` to "false" and updates
+ * the icon to "arrow_drop_down."
+ * - If the element is expanded, it sets `aria-expanded` to "true" and updates
+ * the icon to "arrow_drop_up."
+ *
+ * @param {Event} event - The event triggered, usually by a click action.
+ * @param {HTMLElement} item - The DOM element being toggled that represents the step.
+ */
+ event.preventDefault();
+ event.stopPropagation();
+ if ($(item).hasClass("collapsed")) {
+ $(item).attr("aria-expanded","false")
+ $(item).find('em').text("arrow_drop_down");
+ } else {
+ $(item).attr("aria-expanded","true")
+ $(item).find('em').text("arrow_drop_up");
+ }
}
\ No newline at end of file