From a5040a8064e51904e9908d7a7c674a665e609e3b Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Sat, 4 May 2024 09:02:57 +0300 Subject: [PATCH 1/7] Display full name on active button --- app/src/Model/Table/GroupsTable.php | 6 ++++ app/src/View/Helper/FieldHelper.php | 33 +++++++++++++++++++++ app/src/View/Helper/FilterHelper.php | 15 ++++++++++ app/templates/element/filter/topButtons.php | 6 +++- app/templates/element/form/listItem.php | 24 +-------------- 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/app/src/Model/Table/GroupsTable.php b/app/src/Model/Table/GroupsTable.php index a91ea9e38..acca78290 100644 --- a/app/src/Model/Table/GroupsTable.php +++ b/app/src/Model/Table/GroupsTable.php @@ -154,6 +154,12 @@ public function initialize(array $config): void { 'model' => 'Identifiers', 'active' => true, 'order' => 4 + ], + 'person_id' => [ + 'type' => 'integer', + 'model' => 'GroupMembers', + 'active' => true, + 'order' => 10 ] ]); diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php index 958d17005..5b9e5d85a 100644 --- a/app/src/View/Helper/FieldHelper.php +++ b/app/src/View/Helper/FieldHelper.php @@ -168,6 +168,39 @@ public function calculateLabelAndDescription(string $fieldName): array return [$label, $desc]; } + /** + * Calculate the list of classes for the li element + * + * @param string $fieldName + * + * @return string + */ + public function calculateLiClasses(string $fieldName): string + { + // Class calculation by field Type + $classes = match ($this->getFieldType($fieldName)) { + 'date', + 'datetime', + 'timestamp' => 'fields-datepicker ', + default => '' + }; + + // Class calculation by field name + $classes .= match ($fieldName) { + 'source_record' => 'source-record ', + 'retry_interval', + 'login' => 'subfield ', + default => '' + }; + + // Class calculation by type of Info Div + if(isset($arguments['autocomplete'])) { + $classes .= 'fields-people-autocomplete '; + } + + return $classes; + } + /** * Emit a date/time form control. * This is a wrapper function for $this->control() diff --git a/app/src/View/Helper/FilterHelper.php b/app/src/View/Helper/FilterHelper.php index 1cd59b602..cd9baddc3 100644 --- a/app/src/View/Helper/FilterHelper.php +++ b/app/src/View/Helper/FilterHelper.php @@ -30,6 +30,7 @@ namespace App\View\Helper; use Cake\Collection\Collection; +use Cake\ORM\TableRegistry; use Cake\Utility\{Inflector, Hash}; use Cake\View\Helper; @@ -157,4 +158,18 @@ public function getHiddenFields(): array ->filter(fn($value, $key) => !\in_array($key, $searchable_parameters, true) && $key != 'page') ->toArray(); } + + /** + * Construct Full Name from Person ID + * + * @param int $personId + * + * @return string + */ + public function getFullName(int $personId): string + { + $ModelTable = TableRegistry::getTableLocator()->get('Names'); + $person = $ModelTable->primaryName($personId); + return "{$person->given} {$person->family}"; + } } \ No newline at end of file diff --git a/app/templates/element/filter/topButtons.php b/app/templates/element/filter/topButtons.php index 93baf9a05..ee318888b 100644 --- a/app/templates/element/filter/topButtons.php +++ b/app/templates/element/filter/topButtons.php @@ -41,10 +41,12 @@ $populated_vvar = lcfirst(Inflector::pluralize(Inflector::camelize($key))); $button_label = 'Range'; if(isset($$populated_vvar) && isset($$populated_vvar[$params])) { + // Get label name from AutoViewPopulated vars $button_label = $$populated_vvar[$params]; } elseif(!is_array($params)) { $button_label = $params; - if(isset($vv_searchable_attributes_extras)) { + // Extras use case + if(!empty($vv_searchable_attributes_extras)) { $flattenedSearchableAttributesExtras = Hash::flatten($vv_searchable_attributes_extras); $filteredFlattenedSearchableAttributesExtras = array_filter( $flattenedSearchableAttributesExtras, @@ -54,6 +56,8 @@ if(!empty($filteredFlattenedSearchableAttributesExtras)) { $button_label = array_pop($filteredFlattenedSearchableAttributesExtras); } + } elseif ($key == 'person_id') { + $button_label = $this->Filter->getFullName((int)$params); } } diff --git a/app/templates/element/form/listItem.php b/app/templates/element/form/listItem.php index 2774f259a..4438c9ac0 100644 --- a/app/templates/element/form/listItem.php +++ b/app/templates/element/form/listItem.php @@ -38,28 +38,6 @@ $fieldName = $arguments['fieldName']; $this->set('vv_field_arguments', $arguments); -// Class calculation by field Type -$classes = match ($this->Field->getFieldType($fieldName)) { - 'date', - 'datetime', - 'timestamp' => 'fields-datepicker ', - default => '' -}; - -// Class calculation by field name -$classes .= match ($fieldName) { - 'source_record' => 'source-record ', - 'retry_interval', - 'login' => 'subfield ', - default => '' -}; - -// Class calculation by type of Info Div -if(isset($arguments['autocomplete'])) { - $classes .= 'fields-people-autocomplete '; -} - - // If an attribute is frozen, inject a special link to unfreeze it, since // the attribute is read-only and the admin can't simply uncheck the setting if($fieldName == 'frozen' && $this->Field->getEntity()->frozen) { @@ -101,6 +79,6 @@ ?> -
  • +
  • element('form/fieldDiv')?>
  • From 8948cb8dd67f6e39ab5cd119f754acb28ab6577b Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Sat, 4 May 2024 11:05:29 +0300 Subject: [PATCH 2/7] Render peoplePicker with hardcoded configuration --- app/src/Lib/Traits/IndexQueryTrait.php | 6 +- app/src/Lib/Traits/SearchFilterTrait.php | 21 ++++++- app/src/Model/Table/GroupsTable.php | 20 +++++- app/templates/element/filter/filter.php | 8 +++ app/templates/element/filter/peoplePicker.php | 62 +++++++++++++++++++ 5 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 app/templates/element/filter/peoplePicker.php diff --git a/app/src/Lib/Traits/IndexQueryTrait.php b/app/src/Lib/Traits/IndexQueryTrait.php index b62d65c11..8a6eb8f64 100644 --- a/app/src/Lib/Traits/IndexQueryTrait.php +++ b/app/src/Lib/Traits/IndexQueryTrait.php @@ -159,7 +159,11 @@ public function getIndexQuery(bool $pickerMode = false, array $requestParams = [ $this->viewBuilder() ->getVar('vv_tz')); - // Pass any additional field filter confiration in the view + // Extra View Variables + foreach ($table->getViewVars() as $key => $variable) { + $this->set($key, $variable); + } + // Pass any additional field filter configuration in the view $this->set('vv_searchable_attributes_extras', $table->getSearchFiltersExtras()); if(!empty($searchableAttributes)) { $this->set('vv_searchable_attributes', $searchableAttributes); diff --git a/app/src/Lib/Traits/SearchFilterTrait.php b/app/src/Lib/Traits/SearchFilterTrait.php index 7827e6593..335afb2d2 100644 --- a/app/src/Lib/Traits/SearchFilterTrait.php +++ b/app/src/Lib/Traits/SearchFilterTrait.php @@ -52,6 +52,12 @@ trait SearchFilterTrait { */ private array $searchFiltersExtras = []; + /** + * List of view Vars + * @var array + */ + private array $viewVars = []; + /** * Optional filter configuration that dictates display state and allows for related models * @@ -135,7 +141,7 @@ public function addJoins(Query $query, string $attribute, ServerRequest $request * @since COmanage Registry v5.0.0 */ public function constructDateComparisonClause(QueryExpression $exp, string $attributeWithModelPrefix, array $dates): QueryExpression { - // Both are empty, just return + // Both are empty, return if (empty($dates[0]) && empty($dates[1])) { return $exp; } @@ -243,9 +249,22 @@ public function getSearchableAttributes(string $controller, string $vv_tz=null): } $filterType = $f['type'] ?? 'string'; + // Custom boolean use cases if(\in_array($f['type'], ['isNull', 'isNotNull'])) { $filterType = 'boolean'; } + // Picker configuration + if(isset($f['picker'])) { + $autocompleteArgs = [ + 'type' => 'default', + 'fieldName' => $field, + 'personType' => $f['picker']['type'], + 'htmlId' => $field, // This is the input ID + 'viewConfigParameters' => $f['picker']['configuration'] + ]; + $this->viewVars['vv_autocomplete_arguments'] = $autocompleteArgs; + } + $this->searchFilters[$field] = [ 'type' => $filterType, 'label' => $f['label'] ?? StringUtilities::columnKey($fieldName, $field, $vv_tz, true), diff --git a/app/src/Model/Table/GroupsTable.php b/app/src/Model/Table/GroupsTable.php index acca78290..06c15da24 100644 --- a/app/src/Model/Table/GroupsTable.php +++ b/app/src/Model/Table/GroupsTable.php @@ -145,6 +145,16 @@ public function initialize(array $config): void { 'groupTypes' => [ 'type' => 'enum', 'class' => 'GroupTypeEnum' + ], + // Required for peoplePicker + 'cosettings' => [ + 'type' => 'auxiliary', + 'model' => 'CoSettings' + ], + // Required for peoplePicker + 'types' => [ + 'type' => 'auxiliary', + 'model' => 'Types' ] ]); @@ -159,7 +169,15 @@ public function initialize(array $config): void { 'type' => 'integer', 'model' => 'GroupMembers', 'active' => true, - 'order' => 10 + 'order' => 5, + 'picker' => [ + 'type' => 'person', + 'configuration' => [ + 'for' => 'GroupMembers', + 'action' => 'GET', + 'groupId' => 581 + ] + ] ] ]); diff --git a/app/templates/element/filter/filter.php b/app/templates/element/filter/filter.php index aba4149e8..7341eaede 100644 --- a/app/templates/element/filter/filter.php +++ b/app/templates/element/filter/filter.php @@ -68,8 +68,16 @@ $options) { $elementArguments = compact('options', 'key'); + if($vv_autocomplete_arguments['fieldName'] === $key) { + // Picker is a custom type. + // This is why we calculate it here, and we + // only use it to pick the correct element + $options['type'] = 'picker'; + } + print match($options['type']) { 'date' => $this->element('filter/dateSingle', $elementArguments), + 'picker' => $this->element('filter/peoplePicker', $elementArguments), default => $this->element('filter/default', $elementArguments), }; } diff --git a/app/templates/element/filter/peoplePicker.php b/app/templates/element/filter/peoplePicker.php new file mode 100644 index 000000000..6d480a5dc --- /dev/null +++ b/app/templates/element/filter/peoplePicker.php @@ -0,0 +1,62 @@ +Filter->calculateFieldParams($key, $label); +$vv_autocomplete_arguments['label'] = $label; + +?> + +
    + element('peopleAutocomplete', $vv_autocomplete_arguments) ?> +
    \ No newline at end of file From a09b547c99e8d54079c6e3dcda53144e131c3139 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Sat, 4 May 2024 11:24:31 +0300 Subject: [PATCH 3/7] add correct picker configuration --- app/src/Lib/Traits/SearchFilterTrait.php | 11 ++++++++++- app/src/Model/Table/GroupsTable.php | 6 ++++-- app/templates/element/filter/peoplePicker.php | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/Lib/Traits/SearchFilterTrait.php b/app/src/Lib/Traits/SearchFilterTrait.php index 335afb2d2..6b266a0b8 100644 --- a/app/src/Lib/Traits/SearchFilterTrait.php +++ b/app/src/Lib/Traits/SearchFilterTrait.php @@ -275,7 +275,7 @@ public function getSearchableAttributes(string $controller, string $vv_tz=null): } foreach ($this->filterMetadataFields() as $column => $type) { - // If the column is an array then we are accessing the Metadata fields. Skip + // If the column is an array, then we are accessing the Metadata fields. Skip if(is_array($type)) { continue; } @@ -342,4 +342,13 @@ public function getSearchFiltersExtras(): array return $this->searchFiltersExtras; } + /** + * Get View Vars + * + * @since COmanage Registry v5.0.0 + */ + public function getViewVars(): array + { + return $this->viewVars; + } } diff --git a/app/src/Model/Table/GroupsTable.php b/app/src/Model/Table/GroupsTable.php index 06c15da24..4326cea6f 100644 --- a/app/src/Model/Table/GroupsTable.php +++ b/app/src/Model/Table/GroupsTable.php @@ -173,9 +173,11 @@ public function initialize(array $config): void { 'picker' => [ 'type' => 'person', 'configuration' => [ - 'for' => 'GroupMembers', + // For the Groups Filtering block we want to + // pick/GET from the entire CO pool of people 'action' => 'GET', - 'groupId' => 581 + // The co configuration will fall throught the default configuration + 'for' => 'co' ] ] ] diff --git a/app/templates/element/filter/peoplePicker.php b/app/templates/element/filter/peoplePicker.php index 6d480a5dc..92537ad9c 100644 --- a/app/templates/element/filter/peoplePicker.php +++ b/app/templates/element/filter/peoplePicker.php @@ -1,6 +1,6 @@ Date: Sat, 4 May 2024 21:20:38 +0300 Subject: [PATCH 4/7] filter with selected person. Update the picker if a value alreday exists. --- app/src/View/Helper/FilterHelper.php | 3 +++ app/templates/element/filter/filter.php | 18 +++++++++++++++++- app/templates/element/filter/peoplePicker.php | 5 ++++- app/templates/element/javascript.php | 2 +- app/templates/element/peopleAutocomplete.php | 10 ++++++++-- .../autocomplete/cm-autocomplete-people.js | 16 +++++++++++++--- 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/app/src/View/Helper/FilterHelper.php b/app/src/View/Helper/FilterHelper.php index cd9baddc3..5c7b169ae 100644 --- a/app/src/View/Helper/FilterHelper.php +++ b/app/src/View/Helper/FilterHelper.php @@ -168,6 +168,9 @@ public function getHiddenFields(): array */ public function getFullName(int $personId): string { + if(empty($personId)) { + return ''; + } $ModelTable = TableRegistry::getTableLocator()->get('Names'); $person = $ModelTable->primaryName($personId); return "{$person->given} {$person->family}"; diff --git a/app/templates/element/filter/filter.php b/app/templates/element/filter/filter.php index 7341eaede..ba5b6e6b6 100644 --- a/app/templates/element/filter/filter.php +++ b/app/templates/element/filter/filter.php @@ -27,8 +27,24 @@ declare(strict_types = 1); -use Cake\Utility\Inflector; +?> + + +name = Models $modelsName = $this->name; diff --git a/app/templates/element/filter/peoplePicker.php b/app/templates/element/filter/peoplePicker.php index 92537ad9c..5d6fc6f2f 100644 --- a/app/templates/element/filter/peoplePicker.php +++ b/app/templates/element/filter/peoplePicker.php @@ -53,7 +53,10 @@ // Get the Field configuration $formParams = $this->Filter->calculateFieldParams($key, $label); -$vv_autocomplete_arguments['label'] = $label; +if(!empty($formParams['value']) && $key == 'person_id') { + $formParams['fullName'] = $this->Filter->getFullName((int)$formParams['value']); +} +$vv_autocomplete_arguments['formParams'] = $formParams; ?> diff --git a/app/templates/element/javascript.php b/app/templates/element/javascript.php index 7bbf30b89..23c171e21 100644 --- a/app/templates/element/javascript.php +++ b/app/templates/element/javascript.php @@ -175,7 +175,7 @@ $('#top-filters-submit').addClass("tss-rebalance"); } else { $('#top-filters-submit').removeClass("tss-rebalance"); - } + } } }); diff --git a/app/templates/element/peopleAutocomplete.php b/app/templates/element/peopleAutocomplete.php index bf7fbe1f4..3c3d1eb6c 100644 --- a/app/templates/element/peopleAutocomplete.php +++ b/app/templates/element/peopleAutocomplete.php @@ -27,7 +27,7 @@ // Get parameters $type = $type ?? 'stand-alone'; // autocomplete person picker type: 'stand-alone' or 'field', defaults to 'stand-alone'. - $label = $label ?? __d('operation','autocomplete.people.label'); + $label = $label ?? $formParams['label'] ?? __d('operation','autocomplete.people.label'); $fieldName = $fieldName ?? 'person_id'; $personType = $personType ?? 'coperson'; $htmlId = $htmlId ?? 'cmPersonPickerId'; @@ -38,6 +38,7 @@ $token = $this->request->getAttribute('csrfToken'); // Load my helper functions $vueHelper = $this->loadHelper('Vue'); + $inputValue = $inputValue ?? $formParams['value'] ?? ''; // If we have the $actionUrl array, construct the URL $constructedActionUrl = ''; @@ -80,7 +81,12 @@ personType: '', minLength: 2, // XXX probably should be set by config and default to 3 htmlId: '', - actionUrl: '' + actionUrl: '', + inputValue: '', + inputProps: { + name: '' + }, + formParams: , }, error: '' } diff --git a/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js b/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js index 6aabd382c..9b621ed39 100644 --- a/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js +++ b/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js @@ -159,10 +159,12 @@ export default { }) }, setPerson() { - if(this.options.type == 'field') { + if(this.options.type == 'default') { + // Do nothing + } else if(this.options.type == 'field') { // The picker is part of a standard form field const field = document.getElementById(this.options.fieldName); - field.value = this.person.value; + field.value = this.person.value; } else { // The picker is stand-alone, and should render the configured page in a modal on @item-select const urlForModal = this.options.actionUrl + '&person_id=' + this.person.value; @@ -180,6 +182,13 @@ export default { return str } }, + mounted() { + if(this.options.inputValue != undefined + && this.options.inputValue != '' + && this.options.htmlId == 'person_id') { + this.options.inputProps.value = `${this.options.formParams?.fullName} (ID: ${this.options.inputValue})` + } + }, computed: { hasMorePages: function() { if(this.rawData.responseMeta !== undefined) { @@ -209,7 +218,8 @@ export default { @complete="searchPeople" inputClass="cm-autocomplete" panelClass="cm-autocomplete-panel" - :inputId="this.options.htmlId" + :inputId="this.options.htmlId" + :inputProps="this.options.inputProps" :suggestions="this.people" optionLabel="label" :minLength="this.options.minLength" From 614a40146fa5e65f8515bcd14e5b4f6b4c6c5826 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Sat, 4 May 2024 21:26:42 +0300 Subject: [PATCH 5/7] handle undefined formsData array --- app/templates/element/peopleAutocomplete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/element/peopleAutocomplete.php b/app/templates/element/peopleAutocomplete.php index 3c3d1eb6c..5692f044d 100644 --- a/app/templates/element/peopleAutocomplete.php +++ b/app/templates/element/peopleAutocomplete.php @@ -86,7 +86,7 @@ inputProps: { name: '' }, - formParams: , + formParams: , }, error: '' } From 28cf51c5153cb27ff34ce0ce8b1d71fa5bcfdf0b Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 6 May 2024 09:24:23 +0300 Subject: [PATCH 6/7] move method parameters in the method --- app/src/View/Helper/FieldHelper.php | 9 +++++---- app/templates/element/form/listItem.php | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php index 5b9e5d85a..544fbb7cd 100644 --- a/app/src/View/Helper/FieldHelper.php +++ b/app/src/View/Helper/FieldHelper.php @@ -171,12 +171,13 @@ public function calculateLabelAndDescription(string $fieldName): array /** * Calculate the list of classes for the li element * - * @param string $fieldName - * * @return string */ - public function calculateLiClasses(string $fieldName): string + public function calculateLiClasses(): string { + $fieldName = $this->getView()->get('fieldName'); + $vv_field_arguments = $this->getView()->get('vv_field_arguments'); + // Class calculation by field Type $classes = match ($this->getFieldType($fieldName)) { 'date', @@ -194,7 +195,7 @@ public function calculateLiClasses(string $fieldName): string }; // Class calculation by type of Info Div - if(isset($arguments['autocomplete'])) { + if(isset($vv_field_arguments['autocomplete'])) { $classes .= 'fields-people-autocomplete '; } diff --git a/app/templates/element/form/listItem.php b/app/templates/element/form/listItem.php index 4438c9ac0..050321233 100644 --- a/app/templates/element/form/listItem.php +++ b/app/templates/element/form/listItem.php @@ -79,6 +79,6 @@ ?> -
  • +
  • element('form/fieldDiv')?>
  • From 9e72d0b998c7238ffc8945d1cf5aef182a5a78dd Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 6 May 2024 10:08:43 +0300 Subject: [PATCH 7/7] use data attribute to pass person id --- app/templates/element/filter/filter.php | 4 ++-- app/templates/element/peopleAutocomplete.php | 4 +++- .../components/autocomplete/cm-autocomplete-people.js | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/templates/element/filter/filter.php b/app/templates/element/filter/filter.php index ba5b6e6b6..cff0d99d7 100644 --- a/app/templates/element/filter/filter.php +++ b/app/templates/element/filter/filter.php @@ -35,8 +35,7 @@ const filterForm = document.getElementById("top-filters-form"); filterForm.addEventListener('formdata', (event) => { if(event.formData.has('person_id')) { - const regex = new RegExp('[() ]', 'gm') - const personId = event.formData.get('person_id').replace(regex, '').split(':')[1] + const personId = $(filterForm).find('#person_id')[0].getAttribute('datapersonid') event.formData.set('person_id', personId) } }); @@ -84,6 +83,7 @@ $options) { $elementArguments = compact('options', 'key'); + // Set in SearchfilterTrait.php if($vv_autocomplete_arguments['fieldName'] === $key) { // Picker is a custom type. // This is why we calculate it here, and we diff --git a/app/templates/element/peopleAutocomplete.php b/app/templates/element/peopleAutocomplete.php index 5692f044d..73e690f97 100644 --- a/app/templates/element/peopleAutocomplete.php +++ b/app/templates/element/peopleAutocomplete.php @@ -84,7 +84,9 @@ actionUrl: '', inputValue: '', inputProps: { - name: '' + name: '', + // This is not translated to data-personid but to datapersonid. + dataPersonid: '' }, formParams: , }, diff --git a/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js b/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js index 9b621ed39..e5bb29cab 100644 --- a/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js +++ b/app/webroot/js/comanage/components/autocomplete/cm-autocomplete-people.js @@ -160,7 +160,7 @@ export default { }, setPerson() { if(this.options.type == 'default') { - // Do nothing + this.options.inputProps.dataPersonid = this.person.value } else if(this.options.type == 'field') { // The picker is part of a standard form field const field = document.getElementById(this.options.fieldName); @@ -215,18 +215,18 @@ export default {