diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php
index 60355b63c..958d17005 100644
--- a/app/src/View/Helper/FieldHelper.php
+++ b/app/src/View/Helper/FieldHelper.php
@@ -172,9 +172,8 @@ public function calculateLabelAndDescription(string $fieldName): array
* 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
+ * @param string $fieldName Form field
+ * @param string $dateType Standard, DateOnly, FromTime, ThroughTime
* @param string|null $label
*
* @return string HTML element
@@ -183,24 +182,16 @@ public function calculateLabelAndDescription(string $fieldName): array
public function dateField(string $fieldName,
string $dateType=DateTypeEnum::Standard,
- array $queryParams=null,
string $label=null): string
{
// 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;
- }
+ $queryParams = $this->getView()->getRequest()->getQueryParams();
+ $date_object = !empty($queryParams[$fieldName])
+ ? FrozenTime::parse($queryParams[$fieldName])
+ : $this->getEntity()?->$fieldName;
// Create the options array for the (text input) form control
$coptions = [];
diff --git a/app/src/View/Helper/FilterHelper.php b/app/src/View/Helper/FilterHelper.php
new file mode 100644
index 000000000..1cd59b602
--- /dev/null
+++ b/app/src/View/Helper/FilterHelper.php
@@ -0,0 +1,160 @@
+getView()->getRequest()->getQueryParams();
+ $searchableAttributesExtras = $this->getView()->get('vv_searchable_attributes_extras') ?? [];
+ $populatedVarData = $this->getView()->get(
+ // The populated variables are in plural while the column names are singular
+ // Convention: It is a prerequisite that the vvar should be the plural of the column name
+ lcfirst(Inflector::pluralize(Inflector::camelize($columnName)))
+ );
+
+ // Field options
+ $formParams = [
+ 'label' => $label,
+ 'type' => isset($populatedVarData) ? 'select' : 'text',
+ // Options will be ignored for non-select fields
+ 'options' => $populatedVarData,
+ 'value' => $queryParameters[$columnName] ?? '',
+ 'required' => false,
+ 'class' => 'form-control',
+ // Empty will be ignored for non-select fields
+ 'empty' => true
+ ];
+
+ // Custom/Additional option items defined in the ModelTable::initialize::setFilterConfig
+ // Example: CousTable
+ if(isset($searchableAttributesExtras[$columnName]['options'])) {
+ // Flatten the custom options
+ $customOptionsFlattened = Hash::flatten($searchableAttributesExtras[$columnName]['options']);
+ // Get the key of the placeholder string
+ $dataKey = array_search('@DATA@', $customOptionsFlattened, true);
+ if($dataKey !== false) {
+ $customOptionsFlattened[$dataKey] = $formParams['options'];
+ $formParams['options'] = Hash::expand($customOptionsFlattened);
+ }
+ }
+
+ return $formParams;
+ }
+
+ /**
+ *
+ * @return array[] [search_params, $field_booleans_columns, $field_datetime_columns, $field_generic_columns]
+ */
+ public function explodeFieldsByType(): array
+ {
+ // Get the query string and separate the search params from the non-search params
+ $queryParameters = $this->getView()->getRequest()->getQueryParams();
+ $searchableAttributes = $this->getView()->get('vv_searchable_attributes') ?? [];
+
+ // Filter the search params and take params with aliases into consideration
+ $search_params = [];
+ $field_booleans_columns = [];
+ $field_datetime_columns = [];
+ $field_generic_columns = [];
+ foreach ($searchableAttributes as $attr => $value) {
+ if($value['type'] == 'boolean') {
+ $field_booleans_columns[$attr] = $value;
+ } elseif ($value['type'] == 'timestamp') {
+ $field_datetime_columns[$attr] = $value;
+ } else {
+ $field_generic_columns[$attr] = $value;
+ }
+
+ if(isset($queryParameters[$attr])) {
+ $search_params[$attr] = $queryParameters[$attr];
+ continue;
+ }
+
+ if(isset($value['alias']) && is_array($value['alias'])) {
+ foreach ($value['alias'] as $alias_key) {
+ if(isset($queryParameters[$alias_key])) {
+ $search_params[$attr][$alias_key] = $queryParameters[$alias_key];
+ }
+ }
+ }
+ }
+
+ return [
+ $search_params,
+ $field_booleans_columns,
+ $field_datetime_columns,
+ $field_generic_columns,
+ ];
+ }
+
+ /**
+ * Return an array of the Form hidden fields and values
+ *
+ * @return array
+ */
+ public function getHiddenFields(): array
+ {
+ // Get the query string and separate the search params from the non-search params
+ $queryParameters = $this->getView()->getRequest()->getQueryParams();
+ $searchableAttributes = $this->getView()->get('vv_searchable_attributes') ?? [];
+
+ // Search attributes collection
+ $alias_params = (new Collection($searchableAttributes))
+ ->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($searchableAttributes),
+ ...$alias_params
+ ];
+
+ // 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).
+ return (new Collection($queryParameters))
+ ->filter(fn($value, $key) => !\in_array($key, $searchable_parameters, true) && $key != 'page')
+ ->toArray();
+ }
+}
\ No newline at end of file
diff --git a/app/templates/Standard/index.php b/app/templates/Standard/index.php
index a262d8d08..d29a64b71 100644
--- a/app/templates/Standard/index.php
+++ b/app/templates/Standard/index.php
@@ -50,14 +50,18 @@
$linkActions = ['edit', 'view'];
// $vv_template_path will be set for plugins
-$templatePath = $vv_template_path ?? ROOT . DS . "templates" . DS . $modelsName;
+$templatePath = $vv_template_path ?? ROOT . DS . 'templates' . DS . $modelsName;
// Read the index configuration ($indexColumns) and the associated actions for this model
-$incFile = $templatePath . DS . "columns.inc";
+$incFile = $templatePath . DS . 'columns.inc';
+
if(!is_readable($incFile)) {
throw new \InvalidArgumentException("$incFile is not readable");
}
include($incFile);
+if(isset($indexColumns)) {
+ $this->set('vv_indexColumns', $indexColumns);
+}
// $linkFilter is used for models that belong to a specific parent model (eg: co_id)
$linkFilter = [];
diff --git a/app/templates/element/filter/checkboxes.php b/app/templates/element/filter/checkboxes.php
new file mode 100644
index 000000000..d39d6f4a9
--- /dev/null
+++ b/app/templates/element/filter/checkboxes.php
@@ -0,0 +1,63 @@
+request->getQueryParams();
+
+?>
+
+
+
+
+
diff --git a/app/templates/element/filter/dateSingle.php b/app/templates/element/filter/dateSingle.php
new file mode 100644
index 000000000..dc1aa11bb
--- /dev/null
+++ b/app/templates/element/filter/dateSingle.php
@@ -0,0 +1,64 @@
+
+
+
+ = $this->Form->label($key, $label) ?>
+
+ = $this->Field->dateField($key, DateTypeEnum::DateOnly) ?>
+
+
diff --git a/app/templates/element/filter/dateTimeFilters.php b/app/templates/element/filter/datetimeGroup.php
similarity index 86%
rename from app/templates/element/filter/dateTimeFilters.php
rename to app/templates/element/filter/datetimeGroup.php
index fb38f0f50..29fcc95de 100644
--- a/app/templates/element/filter/dateTimeFilters.php
+++ b/app/templates/element/filter/datetimeGroup.php
@@ -25,12 +25,24 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
+/*
+ * Parameters:
+ * $field_datetime_columns : array, required
+ */
+
+declare(strict_types = 1);
+
+
use App\Lib\Enum\DateTypeEnum;
use Cake\Utility\Inflector;
+// $columns = the passed parameter $indexColumns as found in columns.inc;
+// provides overrides for labels and sorting.
+$columns = $vv_indexColumns;
+
?>
-
-
+
diff --git a/app/templates/element/filter/default.php b/app/templates/element/filter/default.php
new file mode 100644
index 000000000..40aa654df
--- /dev/null
+++ b/app/templates/element/filter/default.php
@@ -0,0 +1,61 @@
+Filter->calculateFieldParams($key, $label);
+
+?>
+
+
+ = $this->Form->control($key, $formParams) ?>
+
\ No newline at end of file
diff --git a/app/templates/element/filter/filter.php b/app/templates/element/filter/filter.php
index f0df46ea6..aba4149e8 100644
--- a/app/templates/element/filter/filter.php
+++ b/app/templates/element/filter/filter.php
@@ -24,51 +24,22 @@
* @since COmanage Registry v5.0.0
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
-use Cake\Collection\Collection;
-use Cake\Utility\{Inflector, Hash};
-use App\Lib\Enum\DateTypeEnum;
+
+declare(strict_types = 1);
+
+use Cake\Utility\Inflector;
// $this->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];
- }
- }
- }
-}
+// Group Fields by Type
+[ $search_params,
+ $field_booleans_columns,
+ $field_datetime_columns,
+ $field_generic_columns ] = $this->Filter->explodeFieldsByType();
// Begin the form
print $this->Form->create(null, [
@@ -76,171 +47,50 @@
'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";
- }
- }
+// Hidden Form fields
+foreach($this->Filter->getHiddenFields() as $param => $value) {
+ print $this->Form->hidden(
+ filter_var($param, FILTER_SANITIZE_SPECIAL_CHARS),
+ array('default' => filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS))) . PHP_EOL;
}
-// Boolean to distinguish between search filters and sort parameters
-$hasActiveFilters = false;
?>
-= $this->Form->end(); ?>
+= $this->Form->end() ?>
diff --git a/app/templates/element/filter/footerButtons.php b/app/templates/element/filter/footerButtons.php
new file mode 100644
index 000000000..168895bcb
--- /dev/null
+++ b/app/templates/element/filter/footerButtons.php
@@ -0,0 +1,79 @@
+filter(fn($col) => boolval($col['active']) === false)
+ ->count();
+if ((count($vv_searchable_attributes) - $inactiveFiltersCount) % 2 === 1
+ &&
+ empty($field_booleans_columns)
+) {
+ $classes .= ' class="tss-rebalance';
+}
+
+?>
+
+>
+
+ Form->submit(__d('operation', 'filter'), $args);
+
+ // clear button
+ $args = array();
+ $args['id'] = 'top-filters-clear';
+ $args['class'] = 'clear-button spin btn btn-default';
+ $args['aria-label'] = __d('operation', 'clear');
+ $args['onclick'] = 'clearTopSearch(this.form)';
+ print $this->Form->button(__d('operation', 'clear'), $args);
+
+ // Options dropdown list
+ if(!empty($vv_searchable_attributes)) {
+ print $this->element(
+ 'filter/options',
+ compact('field_booleans_columns')
+ );
+ }
+ ?>
+
\ No newline at end of file
diff --git a/app/templates/element/filter/topFiltersToggle.php b/app/templates/element/filter/legend.php
similarity index 90%
rename from app/templates/element/filter/topFiltersToggle.php
rename to app/templates/element/filter/legend.php
index 442b474e4..06a84d9c8 100644
--- a/app/templates/element/filter/topFiltersToggle.php
+++ b/app/templates/element/filter/legend.php
@@ -25,7 +25,14 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
-// $search_params passed as a parameter
+
+/*
+ * Parameters:
+ * $search_params : array, required
+ */
+
+declare(strict_types = 1);
+
$hasActiveFilters = false;
@@ -44,12 +51,12 @@
foreach($search_params as $key => $params) {
// We have active filters - not just a sort.
$hasActiveFilters = true;
- print $this->element('filter/activeTopButton', compact('key', 'params'));
+ print $this->element('filter/topButtons', compact('key', 'params'));
}
?>
diff --git a/app/templates/element/filter/filterOptions.php b/app/templates/element/filter/options.php
similarity index 99%
rename from app/templates/element/filter/filterOptions.php
rename to app/templates/element/filter/options.php
index 327f456db..0337d5f6b 100644
--- a/app/templates/element/filter/filterOptions.php
+++ b/app/templates/element/filter/options.php
@@ -25,6 +25,8 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
+declare(strict_types = 1);
+
?>
diff --git a/app/templates/element/filter/activeTopButton.php b/app/templates/element/filter/topButtons.php
similarity index 99%
rename from app/templates/element/filter/activeTopButton.php
rename to app/templates/element/filter/topButtons.php
index 2c3895d95..93baf9a05 100644
--- a/app/templates/element/filter/activeTopButton.php
+++ b/app/templates/element/filter/topButtons.php
@@ -25,6 +25,8 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
+declare(strict_types = 1);
+
use Cake\Utility\{Inflector, Hash};
// Construct aria-controls string