Switch menubar quicksearch to use APIv4 autocomplete
authorcolemanw <coleman@civicrm.org>
Tue, 27 Jun 2023 04:19:23 +0000 (00:19 -0400)
committercolemanw <coleman@civicrm.org>
Thu, 31 Aug 2023 23:11:28 +0000 (19:11 -0400)
CRM/Core/BAO/CustomField.php
CRM/Core/SelectValues.php
CRM/Upgrade/Incremental/php/FiveSixtySix.php
Civi/Api4/Service/Autocomplete/ContactAutocompleteProvider.php
js/crm.menubar.js
settings/Search.setting.php

index 8c7f3d0aa9ee2a74c5a3f88348140fc6d69ed4af..43645d3dc415b5b2c51d549bdcf6e46f11d00cdf 100644 (file)
@@ -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;
     }
index 87fa71eba7afa9c3810e3aa78c1de8a4193d3cfc..0573b1a8a1f161d16bbd1b56cc8f3fa2ef11f1a6 100644 (file)
@@ -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;
   }
index e6035da70853026dfb663f4c87e1d7b42886177b..0229285830ecf9a7daadab3b9b250a5eb0ec2e94 100644 (file)
@@ -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;
+  }
+
 }
index 0d292aa0db32793f59ae6fd1368860d7d7cf40ff..b676baa692b517355303565c8ac055f1a0cbdeb0 100644 (file)
@@ -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];
+    }
   }
 
 }
index 65fe533bf3a12628c1eccf47b91578b60e46e011..5a4946e204ffa0bd490fedf32cbe767f7ea86dd8 100644 (file)
             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);
index cb5988fd359514cfc58e940f400a441f29f18b2d..dfdbb46de98db257c8cdce6af0d02e64dab8b45f 100644 (file)
@@ -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',