diff --git a/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php b/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php
index d9ffeea2..cee5f79a 100644
--- a/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php
+++ b/app/plugins/CoreEnroller/src/Model/Table/EnrollmentAttributesTable.php
@@ -211,54 +211,64 @@ public function supportedAttributes(string $format='full'): array {
// Single valued Person attributes
$attrs['date_of_birth'] = [
'label' => __d('field', 'date_of_birth'),
- 'model' => 'Person'
+ 'model' => 'Person',
+ 'fieldType' => 'date'
];
// Single valued Person Role attributes
$attrs['affiliation_type_id'] = [
'label' => __d('field', 'affiliation'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'string'
];
$attrs['cou_id'] = [
'label' => __d('controller', 'Cous', [1]),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'integer'
];
$attrs['department'] = [
'label' => __d('field', 'department'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'string'
];
$attrs['manager_person_id'] = [
'label' => __d('field', 'manager'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'integer'
];
$attrs['organization'] = [
'label' => __d('field', 'organization'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'string'
];
$attrs['sponsor_person_id'] = [
'label' => __d('field', 'sponsor'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'integer'
];
$attrs['title'] = [
'label' => __d('field', 'title'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'string'
];
$attrs['valid_from'] = [
'label' => __d('field', 'valid_from'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'datetime'
];
$attrs['valid_through'] = [
'label' => __d('field', 'valid_through'),
- 'model' => 'PersonRole'
+ 'model' => 'PersonRole',
+ 'fieldType' => 'datetime'
];
// MVEAs, which might attach to the Person or Person Role or both
@@ -266,49 +276,58 @@ public function supportedAttributes(string $format='full'): array {
$attrs['address'] = [
'label' => __d('controller', 'Addresses', [1]),
'mveaModel' => 'Addresses',
- 'mveaParents' => ['Person', 'PersonRole']
+ 'mveaParents' => ['Person', 'PersonRole'],
+ 'fieldType' => 'string',
+ 'autoViewVar' => 'addressTypes',
];
$attrs['adHocAttribute'] = [
'label' => __d('controller', 'AdHocAttributes', [1]),
'mveaModel' => 'AdHocAttributes',
- 'mveaParents' => ['Person', 'PersonRole']
+ 'mveaParents' => ['Person', 'PersonRole'],
+ 'fieldType' => 'string'
];
$attrs['emailAddress'] = [
'label' => __d('controller', 'EmailAddresses', [1]),
'mveaModel' => 'EmailAddresses',
- 'mveaParents' => ['Person']
+ 'mveaParents' => ['Person'],
+ 'fieldType' => 'string'
];
$attrs['identifier'] = [
'label' => __d('controller', 'Identifiers', [1]),
'mveaModel' => 'Identifiers',
- 'mveaParents' => ['Person']
+ 'mveaParents' => ['Person'],
+ 'fieldType' => 'string'
];
$attrs['name'] = [
'label' => __d('controller', 'Names', [1]),
'mveaModel' => 'Names',
- 'mveaParents' => ['Person']
+ 'mveaParents' => ['Person'],
+ 'fieldType' => 'string'
];
$attrs['pronoun'] = [
'label' => __d('controller', 'Pronouns', [1]),
'mveaModel' => 'Pronouns',
- 'mveaParents' => ['Person']
+ 'mveaParents' => ['Person'],
+ 'fieldType' => 'string'
];
$attrs['telephoneNumber'] = [
'label' => __d('controller', 'TelephoneNumbers', [1]),
'mveaModel' => 'TelephoneNumbers',
- 'mveaParents' => ['Person', 'PersonRole']
+ 'mveaParents' => ['Person', 'PersonRole'],
+ 'fieldType' => 'string'
];
$attrs['url'] = [
'label' => __d('controller', 'Urls', [1]),
'mveaModel' => 'Urls',
- 'mveaParents' => ['Person']
+ 'mveaParents' => ['Person'],
+ 'fieldType' => 'string'
];
// Group memberships, as reflected by the Group ID for the membership to be created in
@@ -318,19 +337,22 @@ public function supportedAttributes(string $format='full'): array {
// to attach the membership to, but the label says Group Member because that'll be
// more obvious
'label' => __d('controller', 'GroupMembers', [1]),
- 'model' => 'Group'
+ 'model' => 'Group',
+ 'fieldType' => 'integer'
];
// Attributes that are only stored in the Petition
$attrs['petition_text'] = [
'label' => __d('core_enroller', 'field.EnrollmentAttributes.petition_text'),
- 'model' => 'Petition'
+ 'model' => 'Petition',
+ 'fieldType' => 'string'
];
$attrs['petition_textarea'] = [
'label' => __d('core_enroller', 'field.EnrollmentAttributes.petition_textarea'),
- 'model' => 'Petition'
+ 'model' => 'Petition',
+ 'fieldType' => 'text'
];
switch($format) {
diff --git a/app/plugins/CoreEnroller/templates/AttributeCollectors/dispatch.inc b/app/plugins/CoreEnroller/templates/AttributeCollectors/dispatch.inc
index f9c4ee9f..a90b4322 100644
--- a/app/plugins/CoreEnroller/templates/AttributeCollectors/dispatch.inc
+++ b/app/plugins/CoreEnroller/templates/AttributeCollectors/dispatch.inc
@@ -27,6 +27,9 @@
declare(strict_types = 1);
+use App\Lib\Enum\StatusEnum;
+use \Cake\Utility\Inflector;
+
// This view is intended to work with dispatch
if($vv_action !== 'dispatch') {
return;
@@ -34,10 +37,43 @@ if($vv_action !== 'dispatch') {
// Make the Form fields editable
$this->Field->enableFormEditMode();
+// Populate the AutoViewVars. These are the same we do for the EnrollmentAttributes configuration view
+$this->Petition->populateAutoViewVars();
+// We just populated the AutoViewVars. Add them to the current context
+extract($this->viewVars);
+
+// Sort according to configuration
+$vv_enrollment_attributes_sorted = $vv_enrollment_attributes->sortBy(function ($attribute) {
+ return $attribute->ordr;
+}, SORT_ASC)->toList();
+
+// Iterate over the attributes and render
+foreach($vv_enrollment_attributes_sorted as $attr) {
+ // Do not render if this is not active
+ if ($attr->status !== StatusEnum::Active) {
+ continue;
+ }
+
+ // Get the static configuration of my attribute
+ $supportedAttributes = $this->Petition->getSupportedEnrollmentAttribute($attr->attribute);
-foreach($vv_enrollment_attributes as $attr) {
+ // Field Options array
$options = [];
+
+ // 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 rerendering 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]);
@@ -46,13 +82,55 @@ foreach($vv_enrollment_attributes as $attr) {
}
}
+ // Construct the field arguments
+ $formArguments = [
+ // We prefix the attribute ID with a string because Cake seems to sometimes have
+ // problems with field names that are purely integers (even if cast to strings)
+ 'fieldName' => 'field-' . $attr->id,
+ 'fieldLabel' => $attr->label, // fieldLabel is only applicable to checkboxes
+ 'fieldType' => $supportedAttributes['fieldType'],
+ 'fieldDescription' => $attr->description,
+ 'fieldNameAlias' => $attr->attribute // the field name to its enrollment attribute field name
+ ];
+
+
+ /*
+ * Get the values for the attributes ending with _id
+ * Supported for attributes: group_id, cou_id, affiliation_type_id
+ */
+ if(str_ends_with($attr->attribute, '_id')) {
+ $suffix = substr($attr->attribute, 0, -3);
+ $suffix = Inflector::pluralize(Inflector::camelize($suffix)) ;
+ $defaultValuesPopulated = 'defaultValue' . $suffix;
+ if ($this->get($defaultValuesPopulated) !== null) {
+ $formArguments['fieldType'] = 'select';
+ $formArguments['fieldSelectOptions'] = $this->get($defaultValuesPopulated);
+ }
+ }
+
+ // READ-ONLY
+ if (isset($attr->modifiable) && !$attr->modifiable) {
+ $options['readonly'] = true;
+ }
+
+ // REQUIRED
+ if (isset($attr->required) && $attr->required) {
+ $options['required'] = true;
+ }
+
+ // Set the final fieldOptions
+ $formArguments['fieldOptions'] = $options;
+
+ // HIDDEN Field
+ // We print directly, we do not delegate to the element for further processing
+ if ($attr->hidden) {
+ // In case this is a hidden field, we need to get only the value
+ print $this->Form->hidden($formArguments['fieldName'], ['value' => $options['default']]);
+ continue;
+ }
+
+ // Print the element
print $this->element('form/listItem', [
- 'arguments' => [
- // We prefix the attribute ID with a string because Cake seems to sometimes have
- // problems with field names that are purely integers (even if cast to strings)
- 'fieldName' => 'field-' . $attr->id,
- 'fieldOptions' => $options,
- 'fieldLabel' => $attr->label
- ]
+ 'arguments' => $formArguments
]);
}
\ No newline at end of file
diff --git a/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc b/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc
index ae8aac2c..dc30198b 100644
--- a/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc
+++ b/app/plugins/CoreEnroller/templates/EnrollmentAttributes/fields.inc
@@ -53,7 +53,7 @@ $hidden = [
* Include a "cancel" button next to the "save" button
*/
-$this->set('vv_include_cancel',true);
+$this->set('vv_include_cancel', true);
/*
* Common Fields for all attributes
diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php
index e2ad7dae..02340d66 100644
--- a/app/src/Controller/StandardController.php
+++ b/app/src/Controller/StandardController.php
@@ -616,150 +616,13 @@ protected function populateAutoViewVars(object $obj=null) {
$modelsName = $this->name;
// $table = the actual table object
$table = $this->$modelsName;
-
- // Populate certain view vars (eg: selects) automatically.
-
- // AutoViewVarsTrait
- if(method_exists($table, "getAutoViewVars")
- && $table->getAutoViewVars()) {
- foreach($table->getAutoViewVars() as $vvar => $avv) {
- switch($avv['type']) {
- case 'array':
- // Use the provided array of values. By default, we use the values
- // for the keys as well, to generate HTML along the lines of
- // . (See also 'hash'.)
- $this->set($vvar, array_combine($avv['array'], $avv['array']));
- break;
- case 'enum':
- // We just want the localized text strings for the defined constants.
- $class = '\\App\\Lib\\Enum\\'.$avv['class'];
- // We support plugin notation for plugin defined enumerations.
- if(strstr($avv['class'], ".")) {
- $bits = explode('.', $avv['class'], 2);
- $class = '\\'.$bits[0].'\\Lib\\Enum\\'.$bits[1];
- }
- $this->set($vvar, $class::getLocalizedConsts());
- break;
- case 'hash':
- // Like 'array' but we assume we are passed key/value pairs
- $this->set($vvar, $avv['hash']);
- break;
- // "auxiliary" and "select" do basically the same thing, but the former
- // returns the full object and the latter just returns a hash suitable
- // for a select. "type" is a shorthand for "select" for type_id.
- case 'type':
- // Inject configuration. Since we're only ever looking at the types
- // table, inject the current CO along with the requested attribute
- $avv['model'] = 'Types';
- if(is_array($avv['attribute'])) {
- $avv['where'] = [
- 'attribute IN' => $avv['attribute'],
- 'status' => SuspendableStatusEnum::Active
- ];
- } else {
- $avv['where'] = [
- 'attribute' => $avv['attribute'],
- 'status' => SuspendableStatusEnum::Active
- ];
- }
- // fall through
- case 'auxiliary':
-// XXX add list as in match?
- case 'select':
- $avvmodel = $avv['model'];
- $this->$avvmodel = TableRegistry::getTableLocator()->get($avvmodel);
- // XXX We should probably move to a more generic approach.
- // Models can have various types of parent keys (and sometimes multiple concurrently),
- // so it’s better to use PrimaryLinkTrait to handle this.
- // if(method_exists($this->$avvmodel, "calculateCoForRecord")) {
- // $avv['where']['co_id'] = $this->$avvmodel->calculateCoForRecord($obj)
- // }
- if($this->$avvmodel->getSchema()->hasColumn('co_id')) {
- $avv['where']['co_id'] = $this->getCOID();
- }
-
- $query = $this->$avvmodel->find($avv['type'] == 'auxiliary' ? 'all' : 'list');
-
- if(!empty($avv['find'])) {
- if($avv['find'] == 'filterPrimaryLink') {
- // We're filtering the requested model, not our current model.
- // See if the requested key is available, and if so run the find.
-
- $linkFilter = $table->getPrimaryLink();
-
- if($linkFilter) {
- // Try to find the $linkFilter value
- $v = null;
-
- // We might have been passed an object with the current value
- if($obj && !empty($obj->$linkFilter)) {
- $v = $obj->$linkFilter;
- } elseif(!empty($this->request->getQuery($linkFilter))) {
- $v = $this->request->getQuery($linkFilter);
- }
-// XXX also need to check getData()?
-// XXX shouldn't this use $this->getPrimaryLink() instead? Or maybe move $this->primaryLink
-// to PrimaryLinkTrait and call it there?
-
- if($v) {
- $avv['where'][$table->getAlias().'.'.$linkFilter] = $v;
- //$query = $query->where([$table->getAlias().'.'.$linkFilter => $v]);
- }
- }
- } else {
- // Use the specified finder, if configured
- $query = $query->find($avv['find']);
- }
- } elseif($table->getSchema()->hasColumn('co_id')) {
- // XXX is this the best logic? maybe some relation to filterPrimaryLink?
- // By default, filter everything on CO ID
- $avv['where']['co_id'] = $this->getCOID();
- //$query = $query->where([$table->getAlias().'.co_id' => $this->getCOID()]);
- }
- // Where Rule. The rule will be transfered as is
- if(!empty($avv['where'])) {
- // Filter on the specified clause (of the form [column=>value])
- $query = $query->where($avv['where']);
- }
-
- // Where rule that will be evaluated. We use the custom whereEvan key to
- // distinguish from the plain where. Also it might contain more than one conditions
- if(!empty($avv['whereEval'])) {
- foreach ($avv['whereEval'] as $whereClauseColumn => $chainedMethodDescription) {
- $calculatedValue = FunctionUtilities::dynamicChainedFunction(
- $this,
- $chainedMethodDescription
- );
- $query = $query->where([$whereClauseColumn => $calculatedValue]);
- }
- }
-
- // Sort the list by display field
- if(!empty($avv['model']) && method_exists($this->$avvmodel, "getDisplayField")) {
- $query->order([$this->$avvmodel->getDisplayField() => 'ASC']);
- } elseif(method_exists($table, "getDisplayField")) {
- $query->order([$table->getDisplayField() => 'ASC']);
- }
-
- $this->set($vvar, $query->toArray());
- break;
- case 'parent':
- $modelsName = $this->name;
- // $table = the actual table object
- $table = $this->$modelsName;
- $this->set($vvar, $table->getParents($this->getCOID()));
- break;
- case 'plugin':
- $PluginTable = $this->getTableLocator()->get('Plugins');
- $this->set($vvar, $PluginTable->getActivePluginModels($avv['pluginType']));
- break;
- default:
-// XXX I18n? and in match?
- throw new \LogicException(__d('error', 'auto.viewvar.type.unknown', [$avv['type']]));
- }
+ // AutoViewVarsTrait
+ if(method_exists($table, 'getAutoViewVars') && $table->getAutoViewVars()) {
+ foreach ($table->calculateAutoViewVars($this->getCOID(), $obj) as $vvar => $value) {
+ $this->set($vvar, $value);
}
- }
+ }
}
/**
diff --git a/app/src/Lib/Traits/AutoViewVarsTrait.php b/app/src/Lib/Traits/AutoViewVarsTrait.php
index afb1862a..98bf1241 100644
--- a/app/src/Lib/Traits/AutoViewVarsTrait.php
+++ b/app/src/Lib/Traits/AutoViewVarsTrait.php
@@ -29,6 +29,10 @@
namespace App\Lib\Traits;
+use App\Lib\Enum\SuspendableStatusEnum;
+use App\Lib\Util\FunctionUtilities;
+use \Cake\ORM\TableRegistry;
+
trait AutoViewVarsTrait {
// Array (and configuration) of view variables to automatically populate
private $autoViewVars = null;
@@ -54,4 +58,159 @@ public function getAutoViewVars() {
public function setAutoViewVars($vars) {
$this->autoViewVars = $vars;
}
+
+ /**
+ * Calculate the AutoView Vars
+ *
+ * @param int $coId
+ * @param Object|null $obj Current object (eg: from edit), if set
+ *
+ * @return \Generator
+ * @since COmanage Registry v5.0.0
+ */
+ public function calculateAutoViewVars(int $coId, Object $obj = null): \Generator
+ {
+ // $table = the actual table object
+ $table = $this;
+
+ foreach($table->getAutoViewVars() as $vvar => $avv) {
+ $generatedValue = null;
+
+ switch($avv['type']) {
+ case 'array':
+ // Use the provided array of values. By default, we use the values
+ // for the keys as well, to generate HTML along the lines of
+ // . (See also 'hash'.)
+ $generatedValue = array_combine($avv['array'], $avv['array']);
+ break;
+ case 'enum':
+ // We just want the localized text strings for the defined constants.
+ $class = '\\App\\Lib\\Enum\\'.$avv['class'];
+ // We support plugin notation for plugin defined enumerations.
+ if(strstr($avv['class'], ".")) {
+ $bits = explode('.', $avv['class'], 2);
+ $class = '\\'.$bits[0].'\\Lib\\Enum\\'.$bits[1];
+ }
+
+ $generatedValue = $class::getLocalizedConsts();
+ break;
+ case 'hash':
+ // Like 'array' but we assume we are passed key/value pairs
+ $generatedValue = $avv['hash'];
+ break;
+ // "auxiliary" and "select" do basically the same thing, but the former
+ // returns the full object and the latter just returns a hash suitable
+ // for a select. "type" is a shorthand for "select" for type_id.
+ case 'type':
+ // Inject configuration. Since we're only ever looking at the types
+ // table, inject the current CO along with the requested attribute
+ $avv['model'] = 'Types';
+ if(\is_array($avv['attribute'])) {
+ $avv['where'] = [
+ 'attribute IN' => $avv['attribute'],
+ 'status' => SuspendableStatusEnum::Active
+ ];
+ } else {
+ $avv['where'] = [
+ 'attribute' => $avv['attribute'],
+ 'status' => SuspendableStatusEnum::Active
+ ];
+ }
+ // fall through
+ case 'auxiliary':
+// XXX add list as in match?
+ case 'select':
+ $avvmodel = $avv['model'];
+ $this->$avvmodel = TableRegistry::getTableLocator()->get($avvmodel);
+ // XXX We should probably move to a more generic approach.
+ // Models can have various types of parent keys (and sometimes multiple concurrently),
+ // so it’s better to use PrimaryLinkTrait to handle this.
+ // if(method_exists($this->$avvmodel, "calculateCoForRecord")) {
+ // $avv['where']['co_id'] = $this->$avvmodel->calculateCoForRecord($obj)
+ // }
+ if($this->$avvmodel->getSchema()->hasColumn('co_id')) {
+ $avv['where']['co_id'] = $coId;
+ }
+
+ $query = $this->$avvmodel->find($avv['type'] == 'auxiliary' ? 'all' : 'list');
+
+ if(!empty($avv['find'])) {
+ if($avv['find'] == 'filterPrimaryLink') {
+ // We're filtering the requested model, not our current model.
+ // See if the requested key is available, and if so run the find.
+
+ $linkFilter = $table->getPrimaryLink();
+
+ if($linkFilter) {
+ // Try to find the $linkFilter value
+ $v = null;
+
+ // We might have been passed an object with the current value
+ if($obj && !empty($obj->$linkFilter)) {
+ $v = $obj->$linkFilter;
+ } elseif(!empty($this->request->getQuery($linkFilter))) {
+ $v = $this->request->getQuery($linkFilter);
+ }
+// XXX also need to check getData()?
+// XXX shouldn't this use $this->getPrimaryLink() instead? Or maybe move $this->primaryLink
+// to PrimaryLinkTrait and call it there?
+
+ if($v) {
+ $avv['where'][$table->getAlias().'.'.$linkFilter] = $v;
+ //$query = $query->where([$table->getAlias().'.'.$linkFilter => $v]);
+ }
+ }
+ } else {
+ // Use the specified finder, if configured
+ $query = $query->find($avv['find']);
+ }
+ } elseif($table->getSchema()->hasColumn('co_id')) {
+ // XXX is this the best logic? maybe some relation to filterPrimaryLink?
+ // By default, filter everything on CO ID
+ $avv['where']['co_id'] = $coId;
+ //$query = $query->where([$table->getAlias().'.co_id' => $coId]);
+ }
+
+ // Where Rule. The rule will be transfered as is
+ if(!empty($avv['where'])) {
+ // Filter on the specified clause (of the form [column=>value])
+ $query = $query->where($avv['where']);
+ }
+
+ // Where rule that will be evaluated. We use the custom whereEvan key to
+ // distinguish from the plain where. Also it might contain more than one conditions
+ if(!empty($avv['whereEval'])) {
+ foreach ($avv['whereEval'] as $whereClauseColumn => $chainedMethodDescription) {
+ $calculatedValue = FunctionUtilities::dynamicChainedFunction(
+ $this,
+ $chainedMethodDescription
+ );
+ $query = $query->where([$whereClauseColumn => $calculatedValue]);
+ }
+ }
+
+ // Sort the list by display field
+ if(!empty($avv['model']) && method_exists($this->$avvmodel, "getDisplayField")) {
+ $query->order([$this->$avvmodel->getDisplayField() => 'ASC']);
+ } elseif(method_exists($table, "getDisplayField")) {
+ $query->order([$table->getDisplayField() => 'ASC']);
+ }
+
+ $generatedValue = $query->toArray();
+ break;
+ case 'parent':
+ $generatedValue = $table->getParents($coId);
+ break;
+ case 'plugin':
+ $PluginTable = TableRegistry::getTableLocator()->get('Plugins');
+ $generatedValue = $PluginTable->getActivePluginModels($avv['pluginType']);
+ break;
+ default:
+// XXX I18n? and in match?
+ throw new \LogicException(__d('error', 'auto.viewvar.type.unknown', [$avv['type']]));
+ }
+
+ yield $vvar => $generatedValue;
+ }
+ }
}
diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php
index 4172aa8f..03749912 100644
--- a/app/src/View/Helper/FieldHelper.php
+++ b/app/src/View/Helper/FieldHelper.php
@@ -186,8 +186,13 @@ public function calculateLiClasses(): string
$fieldName = $this->getView()->get('fieldName');
$vv_field_arguments = $this->getView()->get('vv_field_arguments');
+ // Get the fieldtype directly from the configuration or calculate it
+ // The latter will always work for simple model forms. The first one is used
+ // for more complex use cases
+ $fieldType = $vv_field_arguments['fieldType'] ?? $this->getFieldType($fieldName);
+
// Class calculation by field Type
- $classes = match ($this->getFieldType($fieldName)) {
+ $classes = match ($fieldType) {
'date',
'datetime',
'timestamp' => 'fields-datepicker ',
@@ -216,15 +221,15 @@ public function calculateLiClasses(): string
*
* @param string $fieldName Form field
* @param string $dateType Standard, DateOnly, FromTime, ThroughTime
- * @param string|null $label
+ * @param array|null $fieldArgs
*
* @return string HTML element
* @since COmanage Registry v5.0.0
*/
public function dateField(string $fieldName,
- string $dateType=DateTypeEnum::Standard,
- string $label=null): string
+ string $dateType = DateTypeEnum::Standard,
+ array $fieldArgs = null): string
{
// Initialize
$dateFormat = $dateType === DateTypeEnum::DateOnly ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss';
@@ -235,6 +240,10 @@ public function dateField(string $fieldName,
? FrozenTime::parse($queryParams[$fieldName])
: $this->getEntity()?->$fieldName;
+ // Petition Attribute Collection use case
+ if($date_object === null && !empty($fieldArgs['default'])) {
+ $date_object = $fieldArgs['default'];
+ }
// Create the options array for the (text input) form control
$coptions = [];
@@ -242,8 +251,12 @@ public function dateField(string $fieldName,
// that will interact with the field value. Allowing direct access to the input field is for
// accessibility purposes.
- // ACTION VIEW
- if($this->action == 'view') {
+ // ACTION VIEW or Readonly Field
+ // The latter applies for the attribute collection view
+ if($this->action == 'view'
+ ||
+ (isset($fieldArgs['readonly']) && $fieldArgs['readonly'])
+ ) {
// return the date as plaintext
$element = $this->getView()->element('form/notSetDiv', [], [
'cache' => '_html_elements',
@@ -259,7 +272,8 @@ public function dateField(string $fieldName,
// Special-case the very common "valid_from" and "valid_through" fields, so we won't need
// to specify their types in fields.inc.
- $pickerType = match ($fieldName) {
+ $pickerTypeName = $fieldArgs['fieldNameAlias'] ?? $fieldName;
+ $pickerType = match ($pickerTypeName) {
'valid_from' => DateTypeEnum::FromTime,
'valid_through' => DateTypeEnum::ThroughTime,
default => $dateType
@@ -268,9 +282,6 @@ public function dateField(string $fieldName,
// Append the timezone to the label
$coptions['class'] = 'form-control datepicker';
$coptions['placeholder'] = $dateFormat;
- if(!empty($label)) {
- $coptions['label'] = $label;
- }
$coptions['pattern'] = $datePattern;
$coptions['title'] = __d('field', $dateTitle);
@@ -300,8 +311,14 @@ public function dateField(string $fieldName,
'pickerFloor' => $pickerFloor,
];
+ $fieldLabel = '';
+ if(!empty($fieldArgs['label'])) {
+ $fieldLabel = $this->Form->label($fieldName, $fieldArgs['label']);
+ }
// Create a text field to hold our value and call the datePicker
- return $this->Form->text($fieldName, $coptions) . $this->getView()->element('datePicker', $date_picker_args);
+ return $fieldLabel // label
+ . $this->Form->text($fieldName, $coptions) // hidden input
+ . $this->getView()->element('datePicker', $date_picker_args); // datepicker field
}
/**
@@ -309,11 +326,13 @@ public function dateField(string $fieldName,
*
* @param string $fieldName Form field
* @param array|null $fieldOptions The second parameter of the Form->control helper. List of element options
- * @param string|null $fieldLabel Custom label thext
+ * @param string|null $fieldLabel Custom label text. Applicable to checkboxes ONLY
* @param string $fieldPrefix If the field has a specil prefix provide the value
* @param string|null $fieldType Field type to override the one calculated from the schema
* @param array|null $fieldSelectOptions Options array to override the one calculated options from the AutoPopulate property
* fieldType has to be 'select'
+ * @param string|null $fieldNameAlias Used for the Petition Attribute Collection form. The form uses generic field name.
+ * The variable is used to map the generic field name to the actual enrollment attribute name
*
* @return string HTML element
* @since COmanage Registry v5.0.0
@@ -323,7 +342,8 @@ public function formField(string $fieldName,
string $fieldLabel = null,
string $fieldPrefix = '',
string $fieldType = null,
- array $fieldSelectOptions = null): string
+ array $fieldSelectOptions = null,
+ string $fieldNameAlias = null): string
{
$fieldArgs = $fieldOptions ?? [];
$fieldArgs['label'] = $fieldOptions['label'] ?? false;
@@ -365,7 +385,7 @@ public function formField(string $fieldName,
$this->getView()->set($optionName, $optionValues);
}
- // Is this a multiple select
+ // Is this multiple select?
$fieldArgs['multiple'] = !empty($fieldOptions['multiple']);
// Manipulate the vv_object for the hasPrefix use case
@@ -389,9 +409,9 @@ public function formField(string $fieldName,
'class' => 'form-check-input',
]),
'select' => $this->Form->select($fieldName, $fieldSelectOptions, $fieldArgs),
- 'date' => $this->dateField($fieldName, DateTypeEnum::DateOnly),
+ 'date' => $this->dateField(fieldName: $fieldName, dateType: DateTypeEnum::DateOnly, fieldArgs: $fieldArgs),
'datetime',
- 'timestamp' => $this->dateField($fieldName),
+ 'timestamp' => $this->dateField(fieldName: $fieldName, fieldArgs: $fieldArgs),
default => $this->Form->control($fieldName, $fieldArgs)
};
}
diff --git a/app/src/View/Helper/PetitionHelper.php b/app/src/View/Helper/PetitionHelper.php
new file mode 100644
index 00000000..3b4253b8
--- /dev/null
+++ b/app/src/View/Helper/PetitionHelper.php
@@ -0,0 +1,73 @@
+entity = $this->getView()->get('vv_obj');
+ $this->enrollmentAttributesTable = new EnrollmentAttributesTable();
+ }
+
+ /**
+ * Get the
+ *
+ * @param string $attribute
+ *
+ * @return array
+ * @since COmanage Registry v5.0.0
+ */
+ public function getSupportedEnrollmentAttribute(string $attribute): array
+ {
+ return $this->enrollmentAttributesTable->supportedAttributes()[$attribute];
+ }
+
+ public function populateAutoViewVars(): void
+ {
+ foreach ($this->enrollmentAttributesTable->calculateAutoViewVars(2, $this->entity) as $vvar => $value) {
+ $this->getView()->set($vvar, $value);
+ }
+ }
+}
\ No newline at end of file