From 78777f3e4501508afb0abdd5c9c2f1f7137f21cf Mon Sep 17 00:00:00 2001 From: colemanw Date: Tue, 27 Jun 2023 00:19:23 -0400 Subject: [PATCH] Switch menubar quicksearch to use APIv4 autocomplete --- CRM/Core/BAO/CustomField.php | 2 +- CRM/Core/SelectValues.php | 38 ++++++++------- CRM/Upgrade/Incremental/php/FiveSixtySix.php | 47 +++++++++++++++++++ .../ContactAutocompleteProvider.php | 43 +++++++++++++++++ js/crm.menubar.js | 18 ++++--- settings/Search.setting.php | 14 +++++- 6 files changed, 138 insertions(+), 24 deletions(-) diff --git a/CRM/Core/BAO/CustomField.php b/CRM/Core/BAO/CustomField.php index 8c7f3d0aa9..43645d3dc4 100644 --- a/CRM/Core/BAO/CustomField.php +++ b/CRM/Core/BAO/CustomField.php @@ -2782,7 +2782,7 @@ WHERE cf.id = %1 AND cg.is_multiple = 1"; return TRUE; } // Do this before the "Select" string search because date fields have a "Select Date" html_type - // and contactRef fields have an "Autocomplete-Select" html_type - contacts are an FK not an option list. + // and entityRef fields have an "Autocomplete-Select" html_type - references are an FK not an option list. if (in_array($field['data_type'], ['EntityReference', 'ContactReference', 'Date'])) { return FALSE; } diff --git a/CRM/Core/SelectValues.php b/CRM/Core/SelectValues.php index 87fa71eba7..0573b1a8a1 100644 --- a/CRM/Core/SelectValues.php +++ b/CRM/Core/SelectValues.php @@ -1118,28 +1118,34 @@ class CRM_Core_SelectValues { $includeEmail = civicrm_api3('setting', 'getvalue', ['name' => 'includeEmailInName', 'group' => 'Search Preferences']); $options = [ 'sort_name' => $includeEmail ? ts('Name/Email') : ts('Name'), - 'contact_id' => ts('Contact ID'), + 'id' => ts('Contact ID'), 'external_identifier' => ts('External ID'), 'first_name' => ts('First Name'), 'last_name' => ts('Last Name'), - 'email' => ts('Email'), - 'phone_numeric' => ts('Phone'), - 'street_address' => ts('Street Address'), - 'city' => ts('City'), - 'postal_code' => ts('Postal Code'), + 'email_primary.email' => ts('Email'), + 'phone_primary.phone_numeric' => ts('Phone'), + 'address_primary.street_address' => ts('Street Address'), + 'address_primary.city' => ts('City'), + 'address_primary.postal_code' => ts('Postal Code'), 'job_title' => ts('Job Title'), ]; - $custom = civicrm_api3('CustomField', 'get', [ - 'return' => ['name', 'label', 'custom_group_id.title'], - 'custom_group_id.extends' => ['IN' => array_merge(['Contact'], CRM_Contact_BAO_ContactType::basicTypes())], - 'data_type' => ['NOT IN' => ['ContactReference', 'Date', 'File']], - 'custom_group_id.is_active' => 1, - 'is_active' => 1, - 'is_searchable' => 1, - 'options' => ['sort' => ['custom_group_id.weight', 'weight'], 'limit' => 0], + $custom = civicrm_api4('CustomField', 'get', [ + 'checkPermissions' => FALSE, + 'select' => ['name', 'label', 'custom_group_id.name', 'custom_group_id.title', 'option_group_id'], + 'where' => [ + ['custom_group_id.extends', 'IN', array_merge(['Contact'], CRM_Contact_BAO_ContactType::basicTypes())], + ['data_type', 'NOT IN', ['ContactReference', 'Date', 'File']], + ['custom_group_id.is_active', '=', TRUE], + ['is_active', '=', TRUE], + ['is_searchable', '=', TRUE], + ], + 'orderBy' => [ + 'custom_group_id.weight' => 'ASC', + 'weight' => 'ASC', + ], ]); - foreach ($custom['values'] as $field) { - $options['custom_' . $field['name']] = $field['custom_group_id.title'] . ': ' . $field['label']; + foreach ($custom as $field) { + $options[$field['custom_group_id.name'] . '.' . $field['name'] . ($field['option_group_id'] ? ':label' : '')] = $field['custom_group_id.title'] . ': ' . $field['label']; } return $options; } diff --git a/CRM/Upgrade/Incremental/php/FiveSixtySix.php b/CRM/Upgrade/Incremental/php/FiveSixtySix.php index e6035da708..0229285830 100644 --- a/CRM/Upgrade/Incremental/php/FiveSixtySix.php +++ b/CRM/Upgrade/Incremental/php/FiveSixtySix.php @@ -36,6 +36,7 @@ class CRM_Upgrade_Incremental_php_FiveSixtySix extends CRM_Upgrade_Incremental_B $this->addTask('Add fields to civicrm_mail_settings to allow more flexibility for email to activity', 'addMailSettingsFields'); $this->addTask('Update afform tab names', 'updateAfformTabs'); $this->addTask('Add in Client Removed Activity Type', 'addCaseClientRemovedActivity'); + $this->addTask('Update quicksearch options to v4 format', 'updateQuicksearchOptions'); } /** @@ -143,4 +144,50 @@ class CRM_Upgrade_Incremental_php_FiveSixtySix extends CRM_Upgrade_Incremental_B return TRUE; } + public static function updateQuicksearchOptions($ctx): bool { + $map = [ + 'sort_name' => 'sort_name', + 'contact_id' => 'id', + 'external_identifier' => 'external_identifier', + 'first_name' => 'first_name', + 'last_name' => 'last_name', + 'email' => 'email_primary.email', + 'phone_numeric' => 'phone_primary.phone_numeric', + 'street_address' => 'address_primary.street_address', + 'city' => 'address_primary.city', + 'postal_code' => 'address_primary.postal_code', + 'job_title' => 'job_title', + ]; + $custom = civicrm_api4('CustomField', 'get', [ + 'checkPermissions' => FALSE, + 'select' => ['name', 'label', 'custom_group_id.name', 'custom_group_id.title', 'option_group_id'], + 'where' => [ + ['custom_group_id.extends', 'IN', array_merge(['Contact'], CRM_Contact_BAO_ContactType::basicTypes())], + ['data_type', 'NOT IN', ['ContactReference', 'Date', 'File']], + ['custom_group_id.is_active', '=', TRUE], + ['is_active', '=', TRUE], + ['is_searchable', '=', TRUE], + ], + ]); + foreach ($custom as $field) { + $map['custom_' . $field['name']] = $field['custom_group_id.name'] . '.' . $field['name'] . ($field['option_group_id'] ? ':label' : ''); + } + + $oldOpts = Civi::settings()->get('quicksearch_options'); + if ($oldOpts) { + $newOpts = []; + foreach ($oldOpts as $oldOpt) { + // In case the upgrade has run already, check new and old options + if (in_array($oldOpt, $map) || array_key_exists($oldOpt, $map)) { + $newOpts[] = $map[$oldOpt] ?? $oldOpt; + } + } + Civi::settings()->set('quicksearch_options', $newOpts); + } + else { + Civi::settings()->revert('quicksearch_options'); + } + return TRUE; + } + } diff --git a/Civi/Api4/Service/Autocomplete/ContactAutocompleteProvider.php b/Civi/Api4/Service/Autocomplete/ContactAutocompleteProvider.php index 0d292aa0db..b676baa692 100644 --- a/Civi/Api4/Service/Autocomplete/ContactAutocompleteProvider.php +++ b/Civi/Api4/Service/Autocomplete/ContactAutocompleteProvider.php @@ -12,6 +12,7 @@ namespace Civi\Api4\Service\Autocomplete; +use Civi\API\Event\PrepareEvent; use Civi\Core\Event\GenericHookEvent; use Civi\Core\HookInterface; @@ -21,6 +22,27 @@ use Civi\Core\HookInterface; */ class ContactAutocompleteProvider extends \Civi\Core\Service\AutoService implements HookInterface { + /** + * Set filters for the menubar quicksearch. + * + * @param \Civi\API\Event\PrepareEvent $event + */ + public static function on_civi_api_prepare(PrepareEvent $event) { + $apiRequest = $event->getApiRequest(); + if (is_object($apiRequest) && + is_a($apiRequest, 'Civi\Api4\Generic\AutocompleteAction') && + $apiRequest->getFormName() === 'crmMenubar' && + $apiRequest->getFieldName() === 'crm-qsearch-input' + ) { + $allowedFilters = \Civi::settings()->get('quicksearch_options'); + foreach ($apiRequest->getFilters() as $fieldName => $val) { + if (in_array($fieldName, $allowedFilters)) { + $apiRequest->addFilter($fieldName, $val); + } + } + } + } + /** * Provide default SearchDisplay for Contact autocompletes * @@ -51,6 +73,27 @@ class ContactAutocompleteProvider extends \Civi\Core\Service\AutoService impleme ], ], ]; + // Adjust display for quicksearch input - the display only needs one column + // as the menubar autocomplete does not support descriptions + if (($e->context['formName'] ?? NULL) === 'crmMenubar' && ($e->context['fieldName'] ?? NULL) === 'crm-qsearch-input') { + $column = ['type' => 'field']; + // If doing a search by a field other than the default + if (!empty($e->context['filters'])) { + $filterField = array_keys($e->context['filters'])[0]; + } + elseif (\Civi::settings()->get('includeEmailInName')) { + $filterField = 'email_primary.email'; + } + if ($filterField) { + $column['key'] = $filterField; + $column['rewrite'] = "[sort_name] :: [$filterField]"; + $column['empty_value'] = '[sort_name]'; + } + else { + $column['key'] = 'sort_name'; + } + $e->display['settings']['columns'] = [$column]; + } } } diff --git a/js/crm.menubar.js b/js/crm.menubar.js index 65fe533bf3..5a4946e204 100644 --- a/js/crm.menubar.js +++ b/js/crm.menubar.js @@ -288,15 +288,21 @@ var option = $('input[name=quickSearchField]:checked'), params = { - name: request.term, - field_name: option.val() + formName: 'crmMenubar', + fieldName: 'crm-qsearch-input', + filters: {}, }; - CRM.api3('contact', 'getquick', params).done(function(result) { + if (option.val() === 'sort_name') { + params.input = request.term; + } else { + params.filters[option.val()] = request.term; + } + CRM.api4('Contact', 'autocomplete', params).then(function(result) { var ret = []; - if (result.values.length > 0) { + if (result.length > 0) { $('#crm-qsearch-input').autocomplete('widget').menu('option', 'disabled', false); - $.each(result.values, function(k, v) { - ret.push({value: v.id, label: v.data}); + $.each(result, function(key, item) { + ret.push({value: item.id, label: item.label}); }); } else { $('#crm-qsearch-input').autocomplete('widget').menu('option', 'disabled', true); diff --git a/settings/Search.setting.php b/settings/Search.setting.php index cb5988fd35..dfdbb46de9 100644 --- a/settings/Search.setting.php +++ b/settings/Search.setting.php @@ -184,7 +184,19 @@ return [ 'pseudoconstant' => [ 'callback' => 'CRM_Core_SelectValues::quicksearchOptions', ], - 'default' => ['sort_name', 'contact_id', 'external_identifier', 'first_name', 'last_name', 'email', 'phone_numeric', 'street_address', 'city', 'postal_code', 'job_title'], + 'default' => [ + 'sort_name', + 'id', + 'external_identifier', + 'first_name', + 'last_name', + 'email_primary.email', + 'phone_primary.phone_numeric', + 'address_primary.street_address', + 'address_primary.city', + 'address_primary.postal_code', + 'job_title', + ], 'add' => '5.8', 'title' => ts('Quicksearch options'), 'is_domain' => '1', -- 2.25.1