* @method string getFormName()
* @method $this setFieldName(string $fieldName) Set fieldName.
* @method string getFieldName()
+ * @method $this setFilters(array $filters)
+ * @method array getFilters()
*/
class AutocompleteAction extends AbstractAction {
use Traits\SavedSearchInspectorTrait;
*/
protected $key;
+ /**
+ * Search conditions that will be automatically added to the WHERE or HAVING clauses
+ *
+ * Format: [fieldName => value][]
+ * @var array
+ */
+ public $filters = [];
+
/**
* Filters set programmatically by `civi.api.prepare` listener. Automatically trusted.
*
$this->addFilter($labelField, $this->input);
}
- // Ensure SELECT param includes all fields & filters
+ // Ensure SELECT param includes all fields & trusted filters
$select = [$idField];
foreach ($this->display['settings']['columns'] as $column) {
if ($column['type'] === 'field') {
$apiResult = \Civi\Api4\SearchDisplay::run(FALSE)
->setSavedSearch($this->savedSearch)
->setDisplay($this->display)
- ->setFilters($this->trustedFilters)
+ ->setFilters($this->filters)
->setReturn($return)
->execute();
* @param mixed $value
*/
public function addFilter(string $fieldName, $value) {
+ $this->filters[$fieldName] = $value;
$this->trustedFilters[$fieldName] = $value;
}
</li>
<li ng-if="$ctrl.fieldDefn.input_type === 'EntityRef'" title="{{:: ts('Use a saved search to filter the autocomplete results') }}">
<div href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
- <label>{{:: ts('Saved Search:') }}</label>
- <input class="form-control" crm-entityref="{entity: 'SavedSearch', api: {id_field: 'name', params: {api_entity: $ctrl.getEntity().name}}}" ng-model="getSet('saved_search')" ng-model-options="{getterSetter: true}">
+ <input placeholder="{{:: ts('Saved Search') }}" class="form-control" crm-autocomplete="'SavedSearch'" crm-autocomplete-params="{key: 'name', filters: {api_entity: $ctrl.fieldDefn.fk_entity}, formName: 'afformAdmin', fieldName: 'autocompleteSavedSearch'}" auto-open="true" ng-model="getSet('saved_search')" ng-model-options="{getterSetter: true}" ng-change="getSet('search_display')(null)">
+ </div>
+</li>
+<li ng-if="$ctrl.fieldDefn.input_type === 'EntityRef' && $ctrl.fieldDefn.saved_search" title="{{:: ts('Use a saved search to filter the autocomplete results') }}">
+ <div href ng-click="$event.stopPropagation()" class="af-gui-field-select-in-dropdown">
+ <input placeholder="{{:: ts('Default Display') }}" class="form-control" crm-autocomplete="'SearchDisplay'" crm-autocomplete-params="{key: 'name', filters: {'saved_search_id.name': $ctrl.fieldDefn.saved_search}, formName: 'afformAdmin', fieldName: 'autocompleteDisplay'}" auto-open="true" ng-model="getSet('search_display')" ng-model-options="{getterSetter: true}">
</div>
</li>
<li ng-if="$ctrl.fieldDefn.input_type === 'EntityRef'" title="{{:: ts('Should permissions be checked when autocompleting existing entities') }}">
<input crm-ui-select="{data: $ctrl.editor.securityModes}" ng-model="getSet('security')" ng-model-options="{getterSetter: true}" class="form-control">
</div>
</li>
+<li ng-if="$ctrl.fieldDefn.input_type === 'EntityRef'">
+ <a href ng-click="toggleAttr('input_attrs.autoOpen'); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Show autocomplete results without typing') }}">
+ <i class="crm-i fa-{{ getProp('input_attrs.autoOpen') ? 'check-' : '' }}square-o"></i>
+ {{:: ts('Auto Open') }}
+ </a>
+</li>
<li>
- <a href ng-click="toggleRequired(); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Require this field') }}">
+ <a href ng-click="toggleAttr('required'); $event.stopPropagation(); $event.target.blur();" title="{{:: ts('Require this field') }}">
<i class="crm-i fa-{{ getProp('required') ? 'check-' : '' }}square-o"></i>
{{:: ts('Required') }}
</a>
use Civi\Afform\FormDataModel;
use Civi\API\Events;
use Civi\Api4\Afform;
+use Civi\Api4\Generic\AutocompleteAction;
use Civi\Api4\Utils\CoreUtil;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
public function onApiPrepare(\Civi\API\Event\PrepareEvent $event) {
$apiRequest = $event->getApiRequest();
if (is_object($apiRequest) && is_a($apiRequest, 'Civi\Api4\Generic\AutocompleteAction')) {
- $formName = $apiRequest->getFormName();
- if (!str_starts_with((string) $formName, 'afform:') || !strpos((string) $apiRequest->getFieldName(), ':')) {
- return;
- }
- [$entityName, $fieldName] = explode(':', $apiRequest->getFieldName());
- // Load afform only if user has permission
- $afform = Afform::get()
- ->addWhere('name', '=', str_replace('afform:', '', $formName))
- ->addSelect('layout')
- ->execute()->first();
- if (!$afform) {
- return;
- }
- $formDataModel = new FormDataModel($afform['layout']);
- $entity = $formDataModel->getEntity($entityName);
- $isId = $fieldName === CoreUtil::getIdFieldName($entity['type']);
- $fieldSpec = civicrm_api4($entity['type'], 'getFields', [
- 'checkPermissions' => FALSE,
- 'where' => [['name', '=', $fieldName]],
- ])->single();
- $formField = $entity['fields'][$fieldName]['defn'] ?? [];
+ [$formType, $formName] = array_pad(explode(':', (string) $apiRequest->getFormName()), 2, '');
+ [$entityName, $fieldName] = array_pad(explode(':', (string) $apiRequest->getFieldName()), 2, '');
- // Auto-add filters defined in schema
- foreach ($fieldSpec['input_attrs']['filter'] ?? [] as $key => $value) {
- $apiRequest->addFilter($key, $value);
+ switch ($formType) {
+ case 'afform':
+ if ($formName && $entityName && $fieldName) {
+ $this->processAfformAutocomplete($formName, $entityName, $fieldName, $apiRequest);
+ }
+ return;
+
+ case 'afformAdmin':
+ $this->processAfformAdminAutocomplete($entityName, $apiRequest);
}
+ }
+ }
- // For the "Existing Entity" selector,
- // Look up the "type" fields (e.g. contact_type, activity_type_id, case_type_id, etc)
- // And apply it as a filter if specified on the form.
- if ($isId) {
- if ($entity['type'] === 'Contact') {
- $typeFields = ['contact_type', 'contact_sub_type'];
- }
- else {
- $extends = array_column(\CRM_Core_BAO_CustomGroup::getCustomGroupExtendsOptions(), 'grouping', 'id');
- $typeFields = (array) ($extends[$entity['type']] ?? NULL);
- }
- // If entity has a type set in the values, auto-apply that to filters
- foreach ($typeFields as $typeField) {
- if (!empty($entity['data'][$typeField])) {
- $apiRequest->addFilter($typeField, $entity['data'][$typeField]);
- }
+ /**
+ * Preprocess autocomplete fields for afforms
+ *
+ * @param string $formName
+ * @param string $entityName
+ * @param string $fieldName
+ * @param \Civi\Api4\Generic\AutocompleteAction $apiRequest
+ */
+ private function processAfformAutocomplete(string $formName, string $entityName, string $fieldName, AutocompleteAction $apiRequest):void {
+ // Load afform only if user has permission
+ $afform = Afform::get()
+ ->addWhere('name', '=', $formName)
+ ->addSelect('layout')
+ ->execute()->first();
+ if (!$afform) {
+ return;
+ }
+ $formDataModel = new FormDataModel($afform['layout']);
+ $entity = $formDataModel->getEntity($entityName);
+ $isId = $fieldName === CoreUtil::getIdFieldName($entity['type']);
+ $fieldSpec = civicrm_api4($entity['type'], 'getFields', [
+ 'checkPermissions' => FALSE,
+ 'where' => [['name', '=', $fieldName]],
+ ])->single();
+ $formField = $entity['fields'][$fieldName]['defn'] ?? [];
+
+ // Auto-add filters defined in schema
+ foreach ($fieldSpec['input_attrs']['filter'] ?? [] as $key => $value) {
+ $apiRequest->addFilter($key, $value);
+ }
+
+ // For the "Existing Entity" selector,
+ // Look up the "type" fields (e.g. contact_type, activity_type_id, case_type_id, etc)
+ // And apply it as a filter if specified on the form.
+ if ($isId) {
+ if ($entity['type'] === 'Contact') {
+ $typeFields = ['contact_type', 'contact_sub_type'];
+ }
+ else {
+ $extends = array_column(\CRM_Core_BAO_CustomGroup::getCustomGroupExtendsOptions(), 'grouping', 'id');
+ $typeFields = (array) ($extends[$entity['type']] ?? NULL);
+ }
+ // If entity has a type set in the values, auto-apply that to filters
+ foreach ($typeFields as $typeField) {
+ if (!empty($entity['data'][$typeField])) {
+ $apiRequest->addFilter($typeField, $entity['data'][$typeField]);
}
}
+ }
+
+ $apiRequest->setCheckPermissions(($formField['security'] ?? NULL) !== 'FBAC');
+ $apiRequest->setSavedSearch($formField['saved_search'] ?? NULL);
+ $apiRequest->setDisplay($formField['search_display'] ?? NULL);
+ }
- $apiRequest->setCheckPermissions($formField['security'] !== 'FBAC');
- $apiRequest->setSavedSearch($formField['saved_search'] ?? NULL);
+ /**
+ * Preprocess autocomplete fields on AfformAdmin screens
+ *
+ * @param string $fieldName
+ * @param \Civi\Api4\Generic\AutocompleteAction $apiRequest
+ */
+ private function processAfformAdminAutocomplete(string $fieldName, AutocompleteAction $apiRequest):void {
+ if (!\CRM_Core_Permission::check([['administer CiviCRM', 'administer afform']])) {
+ return;
+ }
+ switch ($fieldName) {
+ case 'autocompleteSavedSearch':
+ $apiRequest->addFilter('api_entity', $apiRequest->getFilters()['api_entity']);
+ return;
+
+ case 'autocompleteDisplay':
+ $apiRequest->addFilter('saved_search_id.name', $apiRequest->getFilters()['saved_search_id.name']);
+ $apiRequest->addFilter('type', 'autocomplete');
+ return;
}
}