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