diff --git a/app/resources/locales/en_US/field.po b/app/resources/locales/en_US/field.po
index 53a4f4a8d..d0a500df0 100644
--- a/app/resources/locales/en_US/field.po
+++ b/app/resources/locales/en_US/field.po
@@ -107,6 +107,9 @@ msgstr "Email"
msgid "ends_at"
msgstr "Ends at:"
+msgid "ends_at.tz"
+msgstr "Ends at ({0})"
+
msgid "extension"
msgstr "Extension"
@@ -243,6 +246,9 @@ msgstr "Sponsor"
msgid "starts_at"
msgstr "Starts at:"
+msgid "starts_at.tz"
+msgstr "Starts at ({0})"
+
msgid "state"
msgstr "State"
diff --git a/app/resources/locales/en_US/information.po b/app/resources/locales/en_US/information.po
index 6a05a1458..933cd91f0 100644
--- a/app/resources/locales/en_US/information.po
+++ b/app/resources/locales/en_US/information.po
@@ -106,4 +106,7 @@ msgid "plugin.active.only"
msgstr "Active, Cannot Be Disabled"
msgid "plugin.inactive"
-msgstr "Inactive"
\ No newline at end of file
+msgstr "Inactive"
+
+msgid "table.list"
+msgstr "{0} List"
\ No newline at end of file
diff --git a/app/resources/locales/en_US/operation.po b/app/resources/locales/en_US/operation.po
index 4d3f3bfb1..9127afaee 100644
--- a/app/resources/locales/en_US/operation.po
+++ b/app/resources/locales/en_US/operation.po
@@ -63,6 +63,9 @@ msgstr "Apply Database Schema"
msgid "assign"
msgstr "Assign"
+msgid "any"
+msgstr "Any"
+
msgid "cancel"
msgstr "Cancel"
@@ -144,6 +147,9 @@ msgstr "Logout"
msgid "next"
msgstr "Next"
+msgid "none"
+msgstr "None"
+
msgid "page.display"
msgstr "Display records"
diff --git a/app/src/Lib/Traits/IndexQueryTrait.php b/app/src/Lib/Traits/IndexQueryTrait.php
index e94394dd2..b62d65c11 100644
--- a/app/src/Lib/Traits/IndexQueryTrait.php
+++ b/app/src/Lib/Traits/IndexQueryTrait.php
@@ -136,7 +136,7 @@ public function getIndexQuery(bool $pickerMode = false, array $requestParams = [
$link = $this->getPrimaryLink(true);
// Initialize the Query Object
$query = $table->find();
- // Get a pointer to my expressions list
+ // Get a pointer to my expression list
$newexp = $query->newExpr();
// The searchable attributes can have an AND or an OR conjunction. The first one is used from the filtering block
// while the second one from the picker vue module.
@@ -159,6 +159,8 @@ public function getIndexQuery(bool $pickerMode = false, array $requestParams = [
$this->viewBuilder()
->getVar('vv_tz'));
+ // Pass any additional field filter confiration in the view
+ $this->set('vv_searchable_attributes_extras', $table->getSearchFiltersExtras());
if(!empty($searchableAttributes)) {
$this->set('vv_searchable_attributes', $searchableAttributes);
@@ -195,7 +197,7 @@ public function getIndexQuery(bool $pickerMode = false, array $requestParams = [
// Specific expressions per view
$query = match($requestParams['for'] ?? '') {
// GroupMembers Add view: We need to filter the active members
- 'GroupMembers' => $query->leftJoinWith('GroupMembers', fn($q) => $q->where(['GroupMembers.group_id' => (int)$requestParams['groupid'] ?? -1]))
+ 'GroupMembers' => $query->leftJoinWith('GroupMembers', fn($q) => $q->where(['GroupMembers.group_id' => (int)($requestParams['groupid'] ?? -1)]))
->where($this->getTableLocator()->get('GroupMembers')->checkValidity($query))
->where(fn(QueryExpression $exp, Query $query) => $exp->isNull('GroupMembers.' . StringUtilities::classNameToForeignKey($table->getAlias()))),
// Just return the query
diff --git a/app/src/Lib/Traits/SearchFilterTrait.php b/app/src/Lib/Traits/SearchFilterTrait.php
index e8d449e66..7827e6593 100644
--- a/app/src/Lib/Traits/SearchFilterTrait.php
+++ b/app/src/Lib/Traits/SearchFilterTrait.php
@@ -29,6 +29,7 @@
namespace App\Lib\Traits;
+use App\Lib\Util\StringUtilities;
use Bake\Utility\Model\AssociationFilter;
use Cake\Database\Expression\QueryExpression;
use Cake\Http\ServerRequest;
@@ -37,9 +38,25 @@
use Cake\I18n\FrozenTime;
trait SearchFilterTrait {
- // Array (and configuration) of permitted search filters
+ /**
+ * Array (and configuration) of permitted search filters
+ *
+ * @var array
+ */
private array $searchFilters = [];
- // Optional filter configuration that dictates display state and allows for related models
+
+ /**
+ * Extra Configurations for each filter
+ *
+ * @var array
+ */
+ private array $searchFiltersExtras = [];
+
+ /**
+ * Optional filter configuration that dictates display state and allows for related models
+ *
+ * @var array
+ */
private array $filterConfig = [];
/**
@@ -167,21 +184,30 @@ public function expressionsConstructor(Query $query, QueryExpression $exp, strin
$attributeWithModelPrefix = $modelPrefix . '.' . $attribute;
-
$search = $q;
- // Use the `lower` function to apply uniformity for the search
- $lower = $query->func()->lower([$attributeWithModelPrefix => 'identifier']);
+
+ // Handle special expression functions here
+ if(\in_array($search, ['isnull', 'isnotnull'])) {
+ return match($search) {
+ 'isnull' => $exp->isNull($attributeWithModelPrefix),
+ 'isnotnull' => $exp->isNotNull($attributeWithModelPrefix)
+ };
+ }
+
// XXX Strings and Enums are not treated the same. Enums require an exact match but strings
- // are partially/non-case sensitive matched
+ // are partially/non-case sensitive matched
return match ($this->searchFilters[$attribute]['type']) {
- 'string' => $exp->like($lower, strtolower('%' . $search . '%')),
+ // Use the `lower` function to apply uniformity for the search
+ 'string' => $exp->like($query->func()->lower([$attributeWithModelPrefix => 'identifier']),
+ strtolower('%' . $search . '%')),
'integer',
'boolean',
'parent' => $exp->add([$attributeWithModelPrefix => $search]),
'date' => $exp->add([$attributeWithModelPrefix => FrozenTime::parseDate($search, 'y-M-d')]),
'timestamp' => $this->constructDateComparisonClause($exp, $attributeWithModelPrefix, $search),
- default => $exp->eq($lower, strtolower($search))
+ default => $exp->eq($query->func()->lower([$attributeWithModelPrefix => 'identifier']),
+ strtolower($search))
};
}
@@ -209,16 +235,24 @@ public function getSearchableAttributes(string $controller, string $vv_tz=null):
// Gather up related models defined in the $filterConfig
// XXX For now, we'll list these first - but we should probably provide a better way to order these.
foreach ($filterConfig as $field => $f) {
- if($f['type'] == 'relatedModel') {
- $fieldName = Inflector::classify(Inflector::underscore($field));
- $this->searchFilters[$field] = [
- 'type' => 'string', // XXX for now - this needs to be looked up.
- 'label' => \App\Lib\Util\StringUtilities::columnKey($fieldName, $field, $vv_tz, true),
- 'active' => $f['active'] ?? true,
- 'model' => $f['model'],
- 'order' => $f['order']
- ];
+ $fieldName = Inflector::classify(Inflector::underscore($field));
+
+ if(isset($f['extras'])) {
+ $this->searchFiltersExtras[$field] = $f['extras'];
+ continue;
}
+
+ $filterType = $f['type'] ?? 'string';
+ if(\in_array($f['type'], ['isNull', 'isNotNull'])) {
+ $filterType = 'boolean';
+ }
+ $this->searchFilters[$field] = [
+ 'type' => $filterType,
+ 'label' => $f['label'] ?? StringUtilities::columnKey($fieldName, $field, $vv_tz, true),
+ 'active' => $f['active'] ?? true,
+ 'model' => $f['model'],
+ 'order' => $f['order']
+ ];
}
foreach ($this->filterMetadataFields() as $column => $type) {
@@ -239,7 +273,7 @@ public function getSearchableAttributes(string $controller, string $vv_tz=null):
$attribute = [
'type' => $type,
- 'label' => \App\Lib\Util\StringUtilities::columnKey($modelname, $column, $vv_tz, true),
+ 'label' => StringUtilities::columnKey($modelname, $column, $vv_tz, true),
'active' => $fieldIsActive,
'order' => 99 // this is the default
];
@@ -270,7 +304,7 @@ public function getSearchableAttributes(string $controller, string $vv_tz=null):
}
/**
- * Set explicilty defined filter configuration defined in the table class.
+ * Set explicit defined filter configuration defined in the table class.
*
* @since COmanage Registry v5.0.0
*/
@@ -279,4 +313,14 @@ public function setFilterConfig(array $filterConfig): void {
$this->filterConfig = $filterConfig;
}
+ /**
+ * Get field extra configurations calculated in getSearchableAttributes
+ *
+ * @since COmanage Registry v5.0.0
+ */
+ public function getSearchFiltersExtras(): array
+ {
+ return $this->searchFiltersExtras;
+ }
+
}
diff --git a/app/src/Lib/Util/FunctionUtilities.php b/app/src/Lib/Util/FunctionUtilities.php
index cb7728d8b..8ed32710d 100644
--- a/app/src/Lib/Util/FunctionUtilities.php
+++ b/app/src/Lib/Util/FunctionUtilities.php
@@ -43,7 +43,7 @@ class FunctionUtilities {
* // Chain of methods
* 'getRequest',
* 'getQuery' => [
- * // parameter name => parameter value, We are taking advantage the named parameters feature
+ * // parameter name => parameter value, We are taking advantage of the named parameters feature
* 'name' =>'group_id'
* ],
* ]
diff --git a/app/src/Model/Table/CousTable.php b/app/src/Model/Table/CousTable.php
index 78dada52d..ee4d59b21 100644
--- a/app/src/Model/Table/CousTable.php
+++ b/app/src/Model/Table/CousTable.php
@@ -113,6 +113,29 @@ public function initialize(array $config): void {
'index' => ['platformAdmin', 'coAdmin']
]
]);
+
+ $this->setFilterConfig([
+ 'identifier' => [
+ 'type' => 'string',
+ 'model' => 'Identifiers',
+ 'active' => true,
+ 'order' => 4
+ ],
+ 'parent_id' => [
+ // We want to keep the default column configuration and add extra functionality.
+ // Here the extra functionality is additional to select options since the parent_id
+ // is of type select
+ // XXX If the extras key is present, no other provided key will be evaluated. The rest
+ // of the configuration will be expected from the TableMetaTrait::filterMetadataFields()
+ 'extras' => [
+ 'options' => [
+ 'isnotnull' => __d('operation','any'),
+ 'isnull' => __d('operation','none'),
+ __d('information','table.list', 'COUs') => '@DATA@',
+ ]
+ ]
+ ]
+ ]);
}
/**
diff --git a/app/src/Model/Table/GroupMembersTable.php b/app/src/Model/Table/GroupMembersTable.php
index a35fdd5ef..3aaca7a71 100644
--- a/app/src/Model/Table/GroupMembersTable.php
+++ b/app/src/Model/Table/GroupMembersTable.php
@@ -131,13 +131,13 @@ public function initialize(array $config): void {
$this->setFilterConfig([
'family' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'People.Names',
'active' => true,
'order' => 2
],
'given' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'People.Names',
'active' => true,
'order' => 1
diff --git a/app/src/Model/Table/GroupNestingsTable.php b/app/src/Model/Table/GroupNestingsTable.php
index 26e76f323..0fdda7cb2 100644
--- a/app/src/Model/Table/GroupNestingsTable.php
+++ b/app/src/Model/Table/GroupNestingsTable.php
@@ -96,24 +96,26 @@ public function initialize(array $config): void {
]
]);
- $this->setAutoViewVars([
- 'groupMembers' => [
- 'type' => 'auxiliary',
- 'model' => 'GroupMembers',
- 'whereEval' => [
- // Where Clause column name
- 'GroupMembers.group_id' => [
- // Chain of methods that will construct the whereClause condition value
- // Method that accepts no parameters
- 'getRequest',
- // Method that accepts only one parameter
- // getQuery(name: 'group_id')
- 'getQuery' => [
- 'name' =>'group_id'
- ]
- ]
- ]
- ]]);
+ // XXX Keeping for functionality reference
+// $this->setAutoViewVars([
+// 'groupMembers' => [
+// 'type' => 'auxiliary',
+// 'model' => 'GroupMembers',
+// 'whereEval' => [
+// // Where Clause column name
+// 'GroupMembers.group_id' => [
+// // Chain of methods that will construct the whereClause condition value
+// // Method that accepts no parameters
+// 'getRequest',
+// // Method that accepts only one parameter
+// // getQuery(name: 'group_id')
+// 'getQuery' => [
+// 'name' =>'group_id'
+// ]
+// ]
+// ]
+// ]]);
+
}
/**
diff --git a/app/src/Model/Table/GroupsTable.php b/app/src/Model/Table/GroupsTable.php
index 339693d10..a91ea9e38 100644
--- a/app/src/Model/Table/GroupsTable.php
+++ b/app/src/Model/Table/GroupsTable.php
@@ -150,7 +150,7 @@ public function initialize(array $config): void {
$this->setFilterConfig([
'identifier' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'Identifiers',
'active' => true,
'order' => 4
diff --git a/app/src/Model/Table/IdentifiersTable.php b/app/src/Model/Table/IdentifiersTable.php
index 881bd1587..ecaa2f28d 100644
--- a/app/src/Model/Table/IdentifiersTable.php
+++ b/app/src/Model/Table/IdentifiersTable.php
@@ -30,6 +30,7 @@
namespace App\Model\Table;
use Cake\Event\EventInterface;
+use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use \App\Lib\Enum\SuspendableStatusEnum;
@@ -191,24 +192,43 @@ public function localAfterSave(\Cake\Event\EventInterface $event, \Cake\Datasour
/**
* Look up a Person ID from an identifier and identifier type ID.
* Only active Identifiers can be used for lookups.
- *
- * @since COmanage Registry v5.0.0
- * @param int $typeId Identifier Type ID
- * @param string $identifier Identifier
+ *
+ * @param int $typeId Identifier Type ID
+ * @param string $identifier Identifier
+ * @param int|null $coId CO Id
+ * @param bool $login The identifier is login enabled
+ *
* @return int Person ID
- * @throws Cake\Datasource\Exception\RecordNotFoundException
+ * @since COmanage Registry v5.0.0
*/
- public function lookupPerson(int $typeId, string $identifier): int {
- $id = $this->find()
- ->where([
- 'identifier' => $identifier,
- 'type_id' => $typeId,
- 'status' => SuspendableStatusEnum::Active,
- 'person_id IS NOT NULL'
- ])
- ->firstOrFail();
-
+ public function lookupPerson(int $typeId, string $identifier, ?int $coId, bool $login=false): int {
+ $whereClause = [
+ 'identifier' => $identifier,
+ 'status' => SuspendableStatusEnum::Active,
+ 'person_id IS NOT NULL'
+ ];
+
+ if($typeId) {
+ $whereClause['type_id'] = $typeId;
+ }
+
+ if($login) {
+ $whereClause['login'] = true;
+ }
+
+ $query = $this->find()
+ ->where($whereClause);
+
+ if($coId) {
+ $query->matching(
+ 'People',
+ fn(QueryExpression $exp, Query $query) => $query->where(['People.co_id' => $coId])
+ );
+ }
+
+ $id = $query->firstOrFail();
+
return $id->person_id;
}
diff --git a/app/src/Model/Table/PeopleTable.php b/app/src/Model/Table/PeopleTable.php
index e39220db6..39de20376 100644
--- a/app/src/Model/Table/PeopleTable.php
+++ b/app/src/Model/Table/PeopleTable.php
@@ -164,31 +164,32 @@ public function initialize(array $config): void {
// XXX expand/revise this as needed to work best with looking up the related models
$this->setFilterConfig([
'family' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'Names',
'active' => true,
'order' => 2
],
'given' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'Names',
'active' => true,
'order' => 1
],
'mail' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'EmailAddresses',
'active' => true,
'order' => 3
],
'identifier' => [
- 'type' => 'relatedModel',
+ 'type' => 'string',
'model' => 'Identifiers',
'active' => true,
'order' => 4
],
'timezone' => [
'type' => 'field',
+ 'model' => 'People',
'active' => false,
'order' => 99
]
diff --git a/app/src/View/Helper/CommonHelper.php b/app/src/View/Helper/CommonHelper.php
new file mode 100644
index 000000000..e7660bf88
--- /dev/null
+++ b/app/src/View/Helper/CommonHelper.php
@@ -0,0 +1,56 @@
+get($modelsName);
+ $count = $ModelTable->find()
+ ->where($whereClause)
+ ->count();
+
+ return $count;
+ }
+}
\ No newline at end of file
diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php
index 87bec56ae..1b26ac583 100644
--- a/app/src/View/Helper/FieldHelper.php
+++ b/app/src/View/Helper/FieldHelper.php
@@ -167,108 +167,137 @@ public function control(string $fieldName,
$this->formInfoDiv($controlCode, $beforeField, $afterField) )
. $this->endLine();
}
-
+
/**
* Emit a date/time form control.
* This is a wrapper function for $this->control()
*
- * @since COmanage Registry v5.0.0
- * @param string $fieldName Form field
- * @param string $dateType Standard, DateOnly, FromTime, ThroughTime
- *
+ * @param string $fieldName Form field
+ * @param string $dateType Standard, DateOnly, FromTime, ThroughTime
+ * @param array|null $queryParams Request Query parameters used by the filtering Blocks to get the date values
+ *
* @return string HTML for control
+ * @since COmanage Registry v5.0.0
*/
- public function dateControl(string $fieldName, string $dateType=DateTypeEnum::Standard): string {
+ public function dateControl(string $fieldName, string $dateType=DateTypeEnum::Standard, array $queryParams = null): string
+ {
+ $dateFieldConfig = $this->dateField($fieldName, $dateType, $queryParams);
+ return $this->control(fieldName: $fieldName,
+ options: $dateFieldConfig['coptions'],
+ ctrlCode: $dateFieldConfig['controlCode'],
+ cssClass: $dateFieldConfig['cssClass'],
+ labelIsTextOnly: $dateFieldConfig['labelIsTextOnly']);
+
+ }
+
+ /**
+ * Emit a date/time form control.
+ * This is a wrapper function for $this->control()
+ *
+ * @param string $fieldName Form field
+ * @param string $dateType Standard, DateOnly, FromTime, ThroughTime
+ * @param array|null $queryParams Request Query parameters used by the filtering Blocks to get the date values
+ *
+ * @return array HTML for control
+ * @since COmanage Registry v5.0.0
+ */
+
+ public function dateField(string $fieldName, string $dateType=DateTypeEnum::Standard, array $queryParams = null): array
+ {
+ // Initialize
+ $dateFormat = $dateType === DateTypeEnum::DateOnly ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss';
+ $dateTitle = $dateType === DateTypeEnum::DateOnly ? 'datepicker.enterDate' : 'datepicker.enterDateTime';
+ $datePattern = $dateType === DateTypeEnum::DateOnly ? '\d{4}-\d{2}-\d{2}' : '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}';
+ $date_object = null;
+
+ if(isset($queryParams)) {
+ if(!empty($queryParams[$fieldName])) {
+ $date_object = FrozenTime::parse($queryParams[$fieldName]);
+ }
+ } else {
+ // This is an entity view. We are getting the data from the object
+ $entity = $this->getView()->get('vv_obj');
+ $date_object = $entity->$fieldName;
+ }
+
+ // Create the options array for the (text input) form control
+ $coptions = [];
+
+ // A datetime field will be rendered as a plain text input with adjacent date and time pickers
+ // that will interact with the field value. Allowing direct access to the input field is for
+ // accessibility purposes.
+
+ // ACTION VIEW
if($this->action == 'view') {
// return the date as plaintext
- $coptions = [];
- $entity = $this->getView()->get('vv_obj');
- if (!empty($entity->$fieldName)) {
+ $controlCode = $this->notSetElement();
+ if ($date_object !== null) {
// Adjust the time back to the user's timezone
- if ($dateType == DateTypeEnum::DateOnly) {
- $controlCode = '';
- } else {
- $controlCode = '';
- }
- } else {
- $controlCode = '
' . __d('information', 'notset') . '
';
+ $controlCode = '';
}
+
// Return this to the generic control() function
+ return ['controlCode' => $controlCode,
+ 'coptions' => [],
+ 'cssClass' => '',
+ 'labelIsTextOnly' => true];
return $this->control($fieldName, $coptions, ctrlCode: $controlCode, labelIsTextOnly: true);
-
- } else {
- // A datetime field will be rendered as a plain text input with adjacent date and time pickers
- // that will interact with the field value. Allowing direct access to the input field is for
- // accessibility purposes.
-
- $pickerType = $dateType;
- // Special-case the very common "valid_from" and "valid_through" fields so we won't need
- // to specify their types in fields.inc.
- if ($fieldName == 'valid_from') {
- $pickerType = DateTypeEnum::FromTime;
- }
- if ($fieldName == 'valid_through') {
- $pickerType = DateTypeEnum::ThroughTime;
- }
-
- // Append the timezone to the label -- TODO: see that the timezone gets output to the display
- $label = __d('field', $fieldName . ".tz", [$this->_View->get('vv_tz')]);
-
- // Create the options array for the (text input) form control
- $coptions = [];
- $coptions['class'] = 'form-control datepicker';
-
- if ($pickerType == DateTypeEnum::DateOnly) {
- $coptions['placeholder'] = 'YYYY-MM-DD';
- $coptions['pattern'] = '\d{4}-\d{2}-\d{2}';
- $coptions['title'] = __d('field', 'datepicker.enterDate');
- } else {
- $coptions['placeholder'] = 'YYYY-MM-DD HH:MM:SS';
- $coptions['pattern'] = '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}';
- $coptions['title'] = __d('field', 'datepicker.enterDateTime');
- }
- $coptions['id'] = str_replace("_", "-", $fieldName);
-
- $entity = $this->getView()->get('vv_obj');
-
- // Default the picker date to today
- $now = FrozenTime::now();
- $pickerDate = $now->i18nFormat('yyyy-MM-dd');
-
- // Get the existing values, if present
- if(!empty($entity->$fieldName)) {
- // Adjust the time back to the user's timezone
- if($pickerType == DateTypeEnum::DateOnly) {
- $coptions['value'] = $entity->$fieldName->i18nFormat("yyyy-MM-dd", $this->getView()->get('vv_tz'));
- } else {
- $coptions['value'] = $entity->$fieldName->i18nFormat("yyyy-MM-dd HH:mm:ss", $this->getView()->get('vv_tz'));
- }
- $pickerDate = $entity->$fieldName->i18nFormat("yyyy-MM-dd", $this->getView()->get('vv_tz'));
- }
-
- // Set the date picker floor year value (-100 years)
- $pickerDateFT = new FrozenTime($pickerDate);
- $pickerDateFT = $pickerDateFT->subYears(100);
- $pickerFloor = $pickerDateFT->i18nFormat("yyyy-MM-dd");
-
- $date_picker_args = [
- 'fieldName' => $fieldName,
- 'pickerDate' => $pickerDate,
- 'pickerType' => $pickerType,
- 'pickerFloor' => $pickerFloor
- ];
-
- // Create a text field to hold our value and call the datePicker
- $controlCode = $this->Form->text($fieldName, $coptions)
- . $this->getView()->element('datePicker', $date_picker_args);
-
- // Specify a class on the
form control wrapper
- $liClass = "fields-datepicker";
-
- // Pass everything to the generic control() function
- return $this->control($fieldName, $coptions, ctrlCode: $controlCode, cssClass: $liClass);
}
+
+ // 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) {
+ 'valid_from' => DateTypeEnum::FromTime,
+ 'valid_through' => DateTypeEnum::ThroughTime,
+ default => $dateType
+ };
+
+ // 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);
+
+ $coptions['id'] = str_replace('_', '-', $fieldName);
+
+
+ // Default the picker date to today
+ $now = FrozenTime::now();
+ $pickerDate = $now->i18nFormat($dateFormat);
+
+ // Get the existing values, if present
+ if($date_object !== null) {
+ // Adjust the time back to the user's timezone
+ $coptions['value'] = $date_object->i18nFormat($dateFormat);
+ $pickerDate = $date_object->i18nFormat($dateFormat);
+ }
+
+ // Set the date picker floor year value (-100 years)()
+ $pickerDateFT = new FrozenTime($pickerDate);
+ $pickerDateFT = $pickerDateFT->subYears(100);
+ $pickerFloor = $pickerDateFT->i18nFormat($dateFormat);
+
+ $date_picker_args = [
+ 'fieldName' => $fieldName,
+ 'pickerDate' => $pickerDate,
+ 'pickerType' => $pickerType,
+ 'pickerFloor' => $pickerFloor,
+ ];
+
+ // Create a text field to hold our value and call the datePicker
+ $controlCode = $this->Form->text($fieldName, $coptions) . $this->getView()->element('datePicker', $date_picker_args);
+
+ // Specify a class on the
form control wrapper
+ $liClass = 'fields-datepicker';
+ // Pass everything to the generic control() function
+ return ['controlCode' => $controlCode,
+ 'coptions' => $coptions,
+ 'labelIsTextOnly' => false,
+ 'cssClass' => $liClass];
}
/**
@@ -667,11 +696,24 @@ public function peopleAutocompleteControl(string $fieldName, array $viewConfigPa
* @return string
* @since COmanage Registry v5.0.0
*/
- protected function requiredSpanElement() {
+ protected function requiredSpanElement(): string
+ {
return "*"
. "{__d('field', 'required')}";
}
+ /**
+ * Static Not Set Div Element
+ *
+ * @return string
+ * @since COmanage Registry v5.0.0
+ */
+
+ protected function notSetElement(): string
+ {
+ return '
' . __d('information', 'notset') . '
';
+ }
+
/**
* Emit a source control for an MVEA that has a source_foo_id field pointing
* to an External Identity attribute.
diff --git a/app/templates/Standard/index.php b/app/templates/Standard/index.php
index 783ffa70e..a262d8d08 100644
--- a/app/templates/Standard/index.php
+++ b/app/templates/Standard/index.php
@@ -180,7 +180,7 @@
$filterArgs['indexColumns'] = $indexColumns;
}
?>
- = $this->element('filter', $filterArgs); ?>
+ = $this->element('filter/filter', $filterArgs); ?>
diff --git a/app/templates/element/datePicker.php b/app/templates/element/datePicker.php
index 7d25f5d20..6eda46363 100644
--- a/app/templates/element/datePicker.php
+++ b/app/templates/element/datePicker.php
@@ -70,7 +70,19 @@
},
components: {
CmDateTimePicker
- }
+ },
+ template: `
+
+
+ `
});
// Add custom global directives available to all child components.
@@ -92,15 +104,4 @@
app.mount("#= $pickerId ?>-container");
-
-
-
-
+
diff --git a/app/templates/element/filter.php b/app/templates/element/filter.php
deleted file mode 100644
index d7a6226d9..000000000
--- a/app/templates/element/filter.php
+++ /dev/null
@@ -1,417 +0,0 @@
-name = Models
-$modelsName = $this->name;
-// $modelName = Model
-$modelName = Inflector::singularize($modelsName);
-// $columns = the passed parameter $indexColumns as found in columns.inc; provides overrides for labels and sorting.
-$columns = $indexColumns;
-
-// Get the query string and separate the search params from the non-search params
-$query = $this->request->getQueryParams();
-// Search attributes collection
-$search_attributes_collection = new Collection($vv_searchable_attributes);
-$alias_params = $search_attributes_collection->filter(fn ($val, $attr) => (is_array($val) && array_key_exists('alias', $val)) )
- ->extract('alias')
- ->unfold()
- ->toArray();
-// For the non search params we need to search the alias params as well
-$searchable_parameters = [
- ...array_keys($vv_searchable_attributes),
- ...$alias_params
- ];
-$non_search_params = (new Collection($query))->filter( fn($value, $key) => !in_array($key, $searchable_parameters) )
- ->toArray();
-
-// Filter the search params and take params with aliases into consideration
-$search_params = [];
-foreach ($vv_searchable_attributes as $attr => $value) {
- if(isset($query[$attr])) {
- $search_params[$attr] = $query[$attr];
- continue;
- }
-
- if(isset($value['alias'])
- && is_array($value['alias'])) {
- foreach ($value['alias'] as $alias_key) {
- if(isset($query[$alias_key])) {
- $search_params[$attr][$alias_key] = $query[$alias_key];
- }
- }
- }
-}
-
-// Begin the form
-print $this->Form->create(null, [
- 'id' => 'top-filters-form',
- 'type' => 'get'
-]);
-
-// Pass back the non-search params as hidden fields, but always exclude the page parameter
-// because we need to start new searches on page one (or we're likely to end up with a 404).
-if(!empty($non_search_params)) {
- foreach($non_search_params as $param => $value) {
- if($param != 'page') {
- print $this->Form->hidden(filter_var($param, FILTER_SANITIZE_SPECIAL_CHARS), array('default' => filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS))) . "\n";
- }
- }
-}
-
-// Boolean to distinguish between search filters and sort parameters
-$hasActiveFilters = false;
-?>
-
-
diff --git a/app/templates/element/filter/filter.php b/app/templates/element/filter/filter.php
new file mode 100644
index 000000000..ca534135b
--- /dev/null
+++ b/app/templates/element/filter/filter.php
@@ -0,0 +1,246 @@
+name = Models
+$modelsName = $this->name;
+// $modelName = Model
+$modelName = Inflector::singularize($modelsName);
+// $columns = the passed parameter $indexColumns as found in columns.inc; provides overrides for labels and sorting.
+$columns = $indexColumns;
+
+// Get the query string and separate the search params from the non-search params
+$query = $this->request->getQueryParams();
+// Search attributes collection
+$search_attributes_collection = new Collection($vv_searchable_attributes);
+$alias_params = $search_attributes_collection->filter(fn ($val, $attr) => (is_array($val) && array_key_exists('alias', $val)) )
+ ->extract('alias')
+ ->unfold()
+ ->toArray();
+// For the non search params we need to search the alias params as well
+$searchable_parameters = [
+ ...array_keys($vv_searchable_attributes),
+ ...$alias_params
+ ];
+$non_search_params = (new Collection($query))->filter( fn($value, $key) => !in_array($key, $searchable_parameters) )
+ ->toArray();
+
+// Filter the search params and take params with aliases into consideration
+$search_params = [];
+foreach ($vv_searchable_attributes as $attr => $value) {
+ if(isset($query[$attr])) {
+ $search_params[$attr] = $query[$attr];
+ continue;
+ }
+
+ if(isset($value['alias'])
+ && is_array($value['alias'])) {
+ foreach ($value['alias'] as $alias_key) {
+ if(isset($query[$alias_key])) {
+ $search_params[$attr][$alias_key] = $query[$alias_key];
+ }
+ }
+ }
+}
+
+// Begin the form
+print $this->Form->create(null, [
+ 'id' => 'top-filters-form',
+ 'type' => 'get'
+]);
+
+// Pass back the non-search params as hidden fields, but always exclude the page parameter
+// because we need to start new searches on page one (or we're likely to end up with a 404).
+if(!empty($non_search_params)) {
+ foreach($non_search_params as $param => $value) {
+ if($param != 'page') {
+ print $this->Form->hidden(filter_var($param, FILTER_SANITIZE_SPECIAL_CHARS), array('default' => filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS))) . "\n";
+ }
+ }
+}
+
+// Boolean to distinguish between search filters and sort parameters
+$hasActiveFilters = false;
+?>
+
+