diff --git a/config/schema/search_api.views.schema.yml b/config/schema/search_api.views.schema.yml index 93a2cdf..0fb0f47 100644 --- a/config/schema/search_api.views.schema.yml +++ b/config/schema/search_api.views.schema.yml @@ -5,6 +5,17 @@ views.query.search_api_query: search_api_bypass_access: type: boolean label: If the underlying search index has access checks enabled, this option allows you to disable them for this view. + parse_mode: + type: string + label: Chooses how the search keys will be parsed. + +views.query.search_api_entity_query: + type: views_query + label: 'Search API entity query' + mapping: + search_api_bypass_access: + type: boolean + label: If the underlying search index has access checks enabled, this option allows you to disable them for this view. entity_access: type: boolean label: Execute an access check for all result entities. diff --git a/search_api.views.inc b/search_api.views.inc index fa4a810..6bf2f9a 100644 --- a/search_api.views.inc +++ b/search_api.views.inc @@ -10,15 +10,19 @@ use Drupal\search_api\Entity\Index; use Drupal\search_api\Item\FieldInterface; use Drupal\search_api\SearchApiException; use Drupal\search_api\Utility; +use Drupal\search_api\IndexInterface; /** * Implements hook_views_data(). */ function search_api_views_data() { $data = array(); - try { - /** @var \Drupal\search_api\IndexInterface $index */ - foreach (Index::loadMultiple() as $index) { + $entity_type_manager = \Drupal::entityTypeManager(); + $logger = \Drupal::logger('search_api'); + + /** @var \Drupal\search_api\IndexInterface $index */ + foreach (Index::loadMultiple() as $index) { + try { // Fill in base data. $key = 'search_api_index_' . $index->id(); $table = &$data[$key]; @@ -27,7 +31,7 @@ function search_api_views_data() { 'field' => 'search_api_id', 'index' => $index->id(), 'title' => t('Index @name', array('@name' => $index->label())), - 'help' => t('Use the %name search index for filtering and retrieving data.', array('%name' => $index->label())), + 'help' => t('Use the @name search index for filtering and retrieving data.', array('@name' => $index->label())), 'query_id' => 'search_api_query', ); @@ -53,53 +57,64 @@ function search_api_views_data() { } } - if (isset($table['search_api_language']['filter']['id'])) { - $table['search_api_language']['filter']['id'] = 'search_api_language'; - $table['search_api_language']['filter']['allow empty'] = FALSE; - } + // Add special fields. + _search_api_views_data_special_fields($table, $index); + + foreach ($index->getDatasources() as $datasource_id => $datasource) { + $entity_type_id = $datasource->getEntityTypeId(); + if (!$entity_type_id) { + // @todo Add our own base table for the datasource. + continue; + } - // Add handlers for special fields. - $table['search_api_id']['title'] = t('Entity ID'); - $table['search_api_id']['help'] = t("The entity's ID"); - $table['search_api_id']['field']['id'] = 'numeric'; - $table['search_api_id']['sort']['id'] = 'search_api'; - - $table['search_api_datasource']['title'] = t('Datasource'); - $table['search_api_datasource']['help'] = t("The data source ID"); - $table['search_api_datasource']['field']['id'] = 'standard'; - $table['search_api_datasource']['filter']['id'] = 'search_api_datasource'; - $table['search_api_datasource']['sort']['id'] = 'search_api'; - - $table['search_api_relevance']['group'] = t('Search'); - $table['search_api_relevance']['title'] = t('Relevance'); - $table['search_api_relevance']['help'] = t('The relevance of this search result with respect to the query'); - $table['search_api_relevance']['field']['type'] = 'decimal'; - $table['search_api_relevance']['field']['id'] = 'numeric'; - $table['search_api_relevance']['field']['click sortable'] = TRUE; - $table['search_api_relevance']['sort']['id'] = 'search_api'; - - $table['search_api_excerpt']['group'] = t('Search'); - $table['search_api_excerpt']['title'] = t('Excerpt'); - $table['search_api_excerpt']['help'] = t('The search result excerpted to show found search terms'); - $table['search_api_excerpt']['field']['id'] = 'search_api_excerpt'; - - $table['search_api_fulltext']['group'] = t('Search'); - $table['search_api_fulltext']['title'] = t('Fulltext search'); - $table['search_api_fulltext']['help'] = t('Search several or all fulltext fields at once.'); - $table['search_api_fulltext']['filter']['id'] = 'search_api_fulltext'; - $table['search_api_fulltext']['argument']['id'] = 'search_api_fulltext'; - - $table['search_api_more_like_this']['group'] = t('Search'); - $table['search_api_more_like_this']['title'] = t('More like this'); - $table['search_api_more_like_this']['help'] = t('Find similar content.'); - $table['search_api_more_like_this']['argument']['id'] = 'search_api_more_like_this'; - - // @todo Add an "All taxonomy terms" contextual filter (if applicable). + $entity_type = $entity_type_manager->getDefinition($entity_type_id); + if (!$entity_type || !$entity_type->getBaseTable()) { + $args = array( + '%index' => $index->label(), + '%datasource' => $datasource->label(), + '%entity_type' => $entity_type_id, + ); + $logger->warning("Datasource %datasource of index %index specifies an unknown entity type %entity_type. Could not add integration for the datasource's fields to Views.", $args); + continue; + } + // Collect the different tables for this entity type. + $entity_tables = array( + 'base' => $entity_type->getBaseTable() + ); + // Some entities may not have a data table. + $data_table = $entity_type->getDataTable(); + if ($data_table) { + $entity_tables['data'] = $data_table; + } + // Now add relationships for all tables of this entity type. + foreach ($entity_tables as $type => $entity_table) { + $args = array( + '@datasource' => $datasource->label(), + ); + if ($type == 'base') { + $title = t('@datasource entity base fields from search results', $args); + $label = t('@datasource base fields', $args); + } + else { + $title = t('@datasource entity data from search results', $args); + $label = t('@datasource data', $args); + } + $field_alias = _search_api_views_find_field_alias($datasource_id . '_' . $type, $table); + $table[$field_alias]['relationship'] = array( + 'title' => $title, + 'label' => $label, + 'id' => 'search_api_datasource', + 'base' => $entity_table, + 'datasource' => $datasource_id, + ); + } + } + } + catch (\Exception $e) { + watchdog_exception('search_api', $e); } } - catch (Exception $e) { - watchdog_exception('search_api', $e); - } + return $data; } @@ -208,6 +223,13 @@ function _search_api_views_get_handlers(FieldInterface $field) { * The handler definitions for the field, as a reference. */ function _search_api_views_handler_adjustments($type, FieldInterface $field, array &$definitions) { + // By default, all fields can be empty (or at least have to be treated that + // way by the Search API). + if (!isset($definitions['filter']['allow empty'])) { + $definitions['filter']['allow empty'] = TRUE; + } + + // For taxonomy term references, set the referenced vocabulary. $data_definition = $field->getDataDefinition(); if ($type == 'entity:taxonomy_term') { if (isset($data_definition->getSettings()['handler_settings']['target_bundles'])) { @@ -217,10 +239,11 @@ function _search_api_views_handler_adjustments($type, FieldInterface $field, arr } } } - // By default, all fields can be empty (or at least have to be treated that - // way by the Search API). - if (!isset($definitions['filter']['allow empty'])) { - $definitions['filter']['allow empty'] = TRUE; + + // Special case for our own language field. + if (!$field->getDatasourceId() && $field->getPropertyPath() === 'search_api_language') { + $definitions['filter']['id'] = 'search_api_language'; + $definitions['filter']['allow empty'] = FALSE; } } @@ -242,6 +265,9 @@ function _search_api_views_handler_mapping() { if (!isset($mapping)) { $mapping = array( 'boolean' => array( + 'field' => array( + 'id' => 'standard', + ), 'filter' => array( 'id' => 'search_api_boolean', ), @@ -250,6 +276,9 @@ function _search_api_views_handler_mapping() { ), ), 'date' => array( + 'field' => array( + 'id' => 'date', + ), 'filter' => array( 'id' => 'search_api_date', ), @@ -258,6 +287,10 @@ function _search_api_views_handler_mapping() { ), ), 'decimal' => array( + 'field' => array( + 'id' => 'numeric', + 'type' => 'integer', + ), 'filter' => array( 'id' => 'search_api_numeric', ), @@ -266,6 +299,10 @@ function _search_api_views_handler_mapping() { ), ), 'integer' => array( + 'field' => array( + 'id' => 'numeric', + 'type' => 'integer', + ), 'filter' => array( 'id' => 'search_api_numeric', ), @@ -274,6 +311,9 @@ function _search_api_views_handler_mapping() { ), ), 'string' => array( + 'field' => array( + 'id' => 'standard', + ), 'filter' => array( 'id' => 'search_api_string', ), @@ -282,6 +322,9 @@ function _search_api_views_handler_mapping() { ), ), 'text' => array( + 'field' => array( + 'id' => 'markup', + ), 'filter' => array( 'id' => 'search_api_text', ), @@ -290,6 +333,9 @@ function _search_api_views_handler_mapping() { ), ), 'options' => array( + 'field' => array( + 'id' => 'standard', + ), 'filter' => array( 'id' => 'search_api_options', ), @@ -298,6 +344,9 @@ function _search_api_views_handler_mapping() { ), ), 'entity:taxonomy_term' => array( + 'field' => array( + 'id' => 'standard', + ), 'filter' => array( 'id' => 'search_api_term', ), @@ -306,6 +355,9 @@ function _search_api_views_handler_mapping() { ), ), 'entity:user' => array( + 'field' => array( + 'id' => 'standard', + ), 'filter' => array( 'id' => 'search_api_user', ), @@ -321,3 +373,72 @@ function _search_api_views_handler_mapping() { return $mapping; } + +/** + * Adds definitions for our special fields to a Views data table definition. + * + * @param array $table + * The existing Views data table definition. + */ +function _search_api_views_data_special_fields(array &$table) { + $id_field = _search_api_views_find_field_alias('search_api_id', $table); + $table[$id_field]['title'] = t('Entity ID'); + $table[$id_field]['help'] = t("The entity's ID"); + $table[$id_field]['field']['id'] = 'numeric'; + $table[$id_field]['sort']['id'] = 'search_api'; + if ($id_field != 'search_api_id') { + $table[$id_field]['real field'] = 'search_api_id'; + } + + $datasource_field = _search_api_views_find_field_alias('search_api_datasource', $table); + $table[$datasource_field]['title'] = t('Datasource'); + $table[$datasource_field]['help'] = t("The data source ID"); + $table[$datasource_field]['field']['id'] = 'standard'; + $table[$datasource_field]['filter']['id'] = 'search_api_datasource'; + $table[$datasource_field]['sort']['id'] = 'search_api'; + if ($datasource_field != 'search_api_datasource') { + $table[$datasource_field]['real field'] = 'search_api_datasource'; + } + + $relevance_field = _search_api_views_find_field_alias('search_api_relevance', $table); + $table[$relevance_field]['group'] = t('Search'); + $table[$relevance_field]['title'] = t('Relevance'); + $table[$relevance_field]['help'] = t('The relevance of this search result with respect to the query'); + $table[$relevance_field]['field']['type'] = 'decimal'; + $table[$relevance_field]['field']['id'] = 'numeric'; + $table[$relevance_field]['field']['click sortable'] = TRUE; + $table[$relevance_field]['sort']['id'] = 'search_api'; + if ($relevance_field != 'search_api_relevance') { + $table[$relevance_field]['real field'] = 'search_api_relevance'; + } + + $excerpt_field = _search_api_views_find_field_alias('search_api_excerpt', $table); + $table[$excerpt_field]['group'] = t('Search'); + $table[$excerpt_field]['title'] = t('Excerpt'); + $table[$excerpt_field]['help'] = t('The search result excerpted to show found search terms'); + $table[$excerpt_field]['field']['id'] = 'search_api_excerpt'; + if ($excerpt_field != 'search_api_excerpt') { + $table[$excerpt_field]['real field'] = 'search_api_excerpt'; + } + + $fulltext_field = _search_api_views_find_field_alias('search_api_fulltext', $table); + $table[$fulltext_field]['group'] = t('Search'); + $table[$fulltext_field]['title'] = t('Fulltext search'); + $table[$fulltext_field]['help'] = t('Search several or all fulltext fields at once.'); + $table[$fulltext_field]['filter']['id'] = 'search_api_fulltext'; + $table[$fulltext_field]['argument']['id'] = 'search_api_fulltext'; + if ($fulltext_field != 'search_api_fulltext') { + $table[$fulltext_field]['real field'] = 'search_api_fulltext'; + } + + $mlt_field = _search_api_views_find_field_alias('search_api_more_like_this', $table); + $table[$mlt_field]['group'] = t('Search'); + $table[$mlt_field]['title'] = t('More like this'); + $table[$mlt_field]['help'] = t('Find similar content.'); + $table[$mlt_field]['argument']['id'] = 'search_api_more_like_this'; + if ($mlt_field != 'search_api_more_like_this') { + $table[$mlt_field]['real field'] = 'search_api_more_like_this'; + } + + // @todo Add an "All taxonomy terms" contextual filter (if applicable). +} diff --git a/search_api_db/search_api_db_defaults/config/optional/views.view.search_content.yml b/search_api_db/search_api_db_defaults/config/optional/views.view.search_content.yml index 9404bd0..14154b7 100644 --- a/search_api_db/search_api_db_defaults/config/optional/views.view.search_content.yml +++ b/search_api_db/search_api_db_defaults/config/optional/views.view.search_content.yml @@ -33,7 +33,6 @@ display: type: search_api_query options: search_api_bypass_access: false - entity_access: false parse_mode: terms exposed_form: type: basic diff --git a/src/Plugin/views/field/StandardList.php b/src/Plugin/views/field/StandardList.php new file mode 100644 index 0000000..c2e775e --- /dev/null +++ b/src/Plugin/views/field/StandardList.php @@ -0,0 +1,33 @@ +retrievedProperties = array(); - $this->index = self::getIndexFromTable($view->storage->get('base_table')); + $this->index = static::getIndexFromTable($view->storage->get('base_table')); if (!$this->index) { $this->abort(new FormattableMarkup('View %view is not based on Search API but tries to use its query plugin.', array('%view' => $view->storage->label()))); } @@ -167,12 +167,40 @@ class SearchApiQuery extends QueryPluginBase { * * @return $this */ - public function addField($combined_property_path) { + public function addRetrievedProperty($combined_property_path) { $this->retrievedProperties[$combined_property_path] = TRUE; return $this; } /** + * Adds a field to the table. + * + * This replicates the interface of Views' default SQL backend to simplify + * the Views integration of the Search API. If you are writing Search + * API-specific Views code, you should better use the addRetrievedProperty() + * method. + * + * @param string|null $table + * Ignored. + * @param string $field + * The combined property path of the property that should be retrieved. + * @param string $alias + * (optional) Ignored. + * @param array $params + * (optional) Ignored. + * + * @return string + * The name that this field can be referred to as (always $field). + * + * @see \Drupal\views\Plugin\views\query\Sql::addField() + * @see \Drupal\search_api\Plugin\views\query\SearchApiQuery::addField() + */ + public function addField($table, $field, $alias = '', $params = array()) { + $this->retrievedProperties[$field] = TRUE; + return $field; + } + + /** * {@inheritdoc} */ public function defineOptions() { @@ -441,22 +469,33 @@ class SearchApiQuery extends QueryPluginBase { * The executed view. */ protected function addResults(array $results, ViewExecutable $view) { + $rows = $this->retrieveItemResults($results); + $entity_information = $this->getEntityTableInfo(); + if (!empty($entity_information)) { + $this->loadResultEntities($rows, $view, $entity_information); + } + $view->result = array_values($rows); + } + + /** + * Retrieves items from the results. + * + * @param \Drupal\search_api\Item\ItemInterface[] $results + * The search results. + * + * @return \Drupal\views\ResultRow[] + * The results as Views result row objects. + */ + protected function retrieveItemResults(array $results) { /** @var \Drupal\views\ResultRow[] $rows */ $rows = array(); - $missing = array(); - - if (!empty($this->configuration['entity_access'])) { - $items = $this->index->loadItemsMultiple(array_keys($results)); - $results = array_intersect_key($results, $items); - /** @var \Drupal\Core\Entity\Plugin\DataType\EntityAdapter $item */ - foreach ($items as $item_id => $item) { - if (!$item->getValue()->access('view')) { - unset($results[$item_id]); - } - } - } - // First off, we try to gather as much property values as possible without + // Views \Drupal\views\Plugin\views\style\StylePluginBase::renderFields() + // uses a numeric results index to key the rendered results. + // The ResultRow::index property is the key then used to retrieve these. + $count = 0; + + // First off, we try to gather as many property values as possible without // loading any items. foreach ($results as $item_id => $result) { $datasource_id = $result->getDatasourceId(); @@ -473,57 +512,90 @@ class SearchApiQuery extends QueryPluginBase { $values['search_api_excerpt'] = $result->getExcerpt() ?: ''; // Gather any fields from the search results. - foreach ($result->getFields(FALSE) as $field) { + foreach ($result->getFields() as $field_id => $field) { if ($field->getValues()) { + // @todo so results from fields here are always arrays; even when single. + // What are the different handling in views when something _can_ be multiple, + // and when it should be single? $combined_id = Utility::createCombinedId($field->getDatasourceId(), $field->getPropertyPath()); - $values[$combined_id] = $field->getValues(); + $values[$combined_id] = $field->getValues()[0]; } } - // Check whether we need to extract any properties from the result item. - $missing_fields = array_diff_key($this->retrievedProperties, $values); - if ($missing_fields) { - $missing[$item_id] = array_keys($missing_fields); - if (!is_object($values['_item'])) { - $item_ids[] = $item_id; - } - } + $values['index'] = $count++; - // Save the row values for adding them to the Views result afterwards. // @todo Use a custom sub-class here to also pass the result item object, // or other information? $rows[$item_id] = new ResultRow($values); } - // Load items of those rows which haven't got all field values, yet. - if (!empty($item_ids)) { - foreach ($this->index->loadItemsMultiple($item_ids) as $item_id => $object) { - $results[$item_id]->setOriginalObject($object); - $rows[$item_id]->_item = $object; - } + return $rows; + } + + /** + * Loads the views results as entities. + * + * @param \Drupal\views\ResultRow[] $results + * An array of results. + * @param \Drupal\views\ViewExecutable $view + * The view where these results are shown. + * @param array $entity_information + * An array containing information about all the available tables in this + * view that contain entity data. + */ + protected function loadResultEntities(array &$results, ViewExecutable $view, $entity_information) { + // List entities types to load from the relationships on the view. + $entities_to_load = []; + foreach ($entity_information as $join) { + $entities_to_load[$join['entity_type']][] = $join; } - foreach ($missing as $item_id => $missing_fields) { - /** @var \Drupal\search_api\Item\FieldInterface[] $fields_to_extract */ - $fields_to_extract = array(); - foreach ($missing_fields as $combined_id) { - list($datasource_id, $property_path) = Utility::splitCombinedId($combined_id); - if ($datasource_id == $results[$item_id]->getDatasourceId()) { - $fields_to_extract[$property_path] = Utility::createField($this->index, $combined_id); - } - } - Utility::extractFields($results[$item_id]->getOriginalObject(), $fields_to_extract); - foreach ($fields_to_extract as $field) { - $combined_id = $field->getFieldIdentifier(); - $rows[$item_id]->$combined_id = $field->getValues(); + foreach ($results as $item_id => $result) { + // Load the entity if it is one of the relationship entities. + list($datasource_id) = Utility::splitCombinedId($item_id); + if (isset($entities_to_load[$this->index->getDatasource($datasource_id)->getEntityTypeId()])) { + $this->loadResultEntity($results, $result, $view->relationship, $entities_to_load); } } + } - // Finally, add all rows to the Views result set. - $view->result = array_values($rows); - array_walk($view->result, function (ResultRow $row, $index) { - $row->index = $index; - }); + /** + * Loads the views result as an entity. + * + * @param \Drupal\views\ResultRow[] $results + * An array of results. + * @param \Drupal\views\ResultRow $result + * One item out of the results set. + * @param \Drupal\views\Plugin\views\relationship\RelationshipPluginBase[] $relationships + * The relationships set for the current view. + * @param array $entities_to_load + * An array containing information about all the available tables in this + * view that contain entity data, keyed by entity type. + */ + private function loadResultEntity(array &$results, ResultRow $result, array $relationships, $entities_to_load) { + if (!isset($result->search_api_id)) { + return; + } + + $item_id = $result->search_api_id; + $item = $this->index->loadItem($item_id); + if (!$item) { + return; + } + + $entity = $item->getValue(); + // Check access to the entity if access check on. + foreach ($entities_to_load[$entity->getEntityTypeId()] as $join) { + if ($relationships[$join['base']]->options['entity_access'] && !$entity->access('view')) { + unset($results[$item_id]); + continue; + } + } + // Add entity to all relationships that use it. + // Multiple example: node and node_fields. + foreach ($entities_to_load[$entity->getEntityTypeId()] as $join) { + $results[$item_id]->_relationship_entities[$join['relationship_id']] = $entity; + } } /** @@ -1119,6 +1191,7 @@ class SearchApiQuery extends QueryPluginBase { * concept of "tables", this method implementation does nothing. If you are * writing Search API-specific Views code, there is therefore no reason at all * to call this method. + * See https://www.drupal.org/node/2484565 for more information. * * @return string * An empty string. diff --git a/src/Plugin/views/relationship/Datasource.php b/src/Plugin/views/relationship/Datasource.php new file mode 100644 index 0000000..fabb517 --- /dev/null +++ b/src/Plugin/views/relationship/Datasource.php @@ -0,0 +1,74 @@ + FALSE, + 'bool' => TRUE, + ); + + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['entity_access'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Check view access permissions'), + '#description' => $this->t("Enable to check user has permission to view the entity. This prevents users from seeing inappropriate content when the index contains stale data, or doesn't provide access checks. However, result counts, paging and other things won't work correctly if results are eliminated in this way, so only use this as a last resort (and in addition to other checks, if possible)."), + '#default_value' => !empty($this->options['required']), + ); + } + + /** + * {@inheritdoc} + */ + public function query() { + // If the relationship is required, add a filter for this datasource. + if (!empty($this->options['required'])) { + $this->query->addCondition('search_api_datasource', $this->definition['datasource']); + } + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + // @todo: Add the entity as a dependency if there is an entity. + } + +} diff --git a/src/Plugin/views/row/SearchApiRow.php b/src/Plugin/views/row/SearchApiRow.php index b0ba304..8d3ceba 100644 --- a/src/Plugin/views/row/SearchApiRow.php +++ b/src/Plugin/views/row/SearchApiRow.php @@ -24,7 +24,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * * @ViewsRow( * id = "search_api", - * title = @Translation("Rendered Search API item"), + * title = @Translation("Rendered entity"), * help = @Translation("Displays entity of the matching search API item"), * ) */ @@ -118,7 +118,6 @@ class SearchApiRow extends RowPluginBase { */ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) { parent::init($view, $display, $options); - $base_table = $view->storage->get('base_table'); $this->index = SearchApiQuery::getIndexFromTable($base_table, $this->getEntityTypeManager()); if (!$this->index) { @@ -248,7 +247,7 @@ class SearchApiRow extends RowPluginBase { public function query() { parent::query(); // @todo Find a better way to ensure that the item is loaded. - $this->view->query->addField('_magic'); + $this->view->query->addRetrievedProperty('_magic'); } } diff --git a/src/Tests/ViewsTest.php b/src/Tests/ViewsTest.php index e89c56c..40befe4 100644 --- a/src/Tests/ViewsTest.php +++ b/src/Tests/ViewsTest.php @@ -24,7 +24,7 @@ class ViewsTest extends WebTestBase { * * @var string[] */ - public static $modules = array('search_api_test_views'); + public static $modules = array('search_api_test_views', 'views_ui'); /** * A search index ID. @@ -182,4 +182,19 @@ class ViewsTest extends WebTestBase { } } + /** + * Test views admin. + */ + public function testViewsAdmin() { + $admin_user = $this->drupalCreateUser(array( + 'administer search_api', + 'access administration pages', + 'administer views', + )); + $this->drupalLogin($admin_user); + $this->insertExampleContent(); + + $this->drupalGet('admin/structure/views/view/search_api_test_views_fulltext'); + } + } diff --git a/tests/search_api_test_views/config/install/views.view.search_api_test_views_fulltext.yml b/tests/search_api_test_views/config/install/views.view.search_api_test_views_fulltext.yml index 0031593..a397f64 100644 --- a/tests/search_api_test_views/config/install/views.view.search_api_test_views_fulltext.yml +++ b/tests/search_api_test_views/config/install/views.view.search_api_test_views_fulltext.yml @@ -20,7 +20,6 @@ display: type: search_api_query options: search_api_bypass_access: false - entity_access: false parse_mode: terms exposed_form: type: basic