From 6bacd76693a4c937352f5712696d5eff7d2c9f1c Mon Sep 17 00:00:00 2001 From: upchuk Date: Mon, 7 Dec 2020 12:01:09 +0200 Subject: [PATCH] EWPP-593: Ensuring only entity type/bundle combos that are indexed are for list pages. --- src/Form/ListPageConfigurationSubForm.php | 11 +- src/ListSourceFactory.php | 56 +++++++- src/ListSourceFactoryInterface.php | 11 ++ .../config/install/search_api.index.node.yml | 6 +- .../install/search_api.index.taxonomy.yml | 33 +++++ .../taxonomy.vocabulary.vocabulary_one.yml | 7 + .../taxonomy.vocabulary.vocabulary_two.yml | 7 + .../oe_list_pages_filters_test.info.yml | 1 + .../ListPagesExposedFiltersTest.php | 6 +- .../ListPagesPluginTest.php | 131 ++++++------------ 10 files changed, 167 insertions(+), 102 deletions(-) create mode 100644 tests/modules/oe_list_pages_filters_test/config/install/search_api.index.taxonomy.yml create mode 100644 tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_one.yml create mode 100644 tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_two.yml diff --git a/src/Form/ListPageConfigurationSubForm.php b/src/Form/ListPageConfigurationSubForm.php index 9f1761cf..1851c4ad 100644 --- a/src/Form/ListPageConfigurationSubForm.php +++ b/src/Form/ListPageConfigurationSubForm.php @@ -444,11 +444,11 @@ protected function areExposedFiltersOverridden(ListSourceInterface $list_source) protected function getEntityTypeOptions(): array { $entity_type_options = []; $entity_types = $this->entityTypeManager->getDefinitions(); - foreach ($entity_types as $entity_type_key => $entity_type) { - if (!$entity_type instanceof ContentEntityTypeInterface) { + foreach ($entity_types as $entity_type_id => $entity_type) { + if (!$entity_type instanceof ContentEntityTypeInterface || !$this->listSourceFactory->isEntityTypeSourced($entity_type_id)) { continue; } - $entity_type_options[$entity_type_key] = $entity_type->getLabel(); + $entity_type_options[$entity_type_id] = $entity_type->getLabel(); } $event = new ListPageSourceAlterEvent(array_keys($entity_type_options)); @@ -469,6 +469,11 @@ protected function getBundleOptions(string $selected_entity_type): array { $bundle_options = []; $bundles = $this->entityTypeBundleInfo->getBundleInfo($selected_entity_type); foreach ($bundles as $bundle_key => $bundle) { + $list_source = $this->listSourceFactory->get($selected_entity_type, $bundle_key); + if (!$list_source instanceof ListSourceInterface) { + continue; + } + $bundle_options[$bundle_key] = $bundle['label']; } diff --git a/src/ListSourceFactory.php b/src/ListSourceFactory.php index 7dad0ec7..50236c59 100644 --- a/src/ListSourceFactory.php +++ b/src/ListSourceFactory.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\facets\FacetManager\DefaultFacetManager; +use Drupal\search_api\Datasource\DatasourceInterface; use Drupal\search_api\IndexInterface; /** @@ -31,7 +32,7 @@ class ListSourceFactory implements ListSourceFactoryInterface { /** * The list sources. * - * @var array + * @var \Drupal\oe_list_pages\ListSourceInterface[] */ protected $listsSources; @@ -52,7 +53,6 @@ public function __construct(DefaultFacetManager $facetsManager, EntityTypeManage * {@inheritdoc} */ public function get(string $entity_type, string $bundle): ?ListSourceInterface { - if (empty($this->listsSources)) { $this->instantiateLists(); } @@ -61,6 +61,23 @@ public function get(string $entity_type, string $bundle): ?ListSourceInterface { return !empty($this->listsSources[$id]) ? $this->listsSources[$id] : NULL; } + /** + * {@inheritdoc} + */ + public function isEntityTypeSourced(string $entity_type): bool { + if (empty($this->listsSources)) { + $this->instantiateLists(); + } + + foreach ($this->listsSources as $lists_source) { + if ($lists_source->getEntityType() === $entity_type) { + return TRUE; + } + } + + return FALSE; + } + /** * {@inheritdoc} */ @@ -72,7 +89,6 @@ public static function generateFacetSourcePluginId(string $entity_type, string $ * Instantiate the list sources from the indexed content bundles. */ protected function instantiateLists(): void { - if (!empty($this->listsSources)) { return; } @@ -93,7 +109,7 @@ protected function instantiateLists(): void { $bundles = $datasource->getBundles(); foreach ($bundles as $bundle => $label) { // In case not all bundles are indexed. - if (!empty($datasource->getConfiguration()['bundles']['selected']) && !in_array($bundle, $datasource->getConfiguration()['bundles']['selected'])) { + if (!$this->isBundleIndexed($datasource, $bundle)) { continue; } @@ -138,4 +154,36 @@ protected function create(string $entity_type, string $bundle, IndexInterface $i return new ListSource($id, $entity_type, $bundle, $bundle_field_id, $index, $filters); } + /** + * Checks if a given bundle is indexed on a data source. + * + * @param \Drupal\search_api\Datasource\DatasourceInterface $datasource + * The datasource. + * @param string $bundle + * The bundle. + * + * @return bool + * Whether the bundle is indexed. + */ + protected function isBundleIndexed(DatasourceInterface $datasource, string $bundle): bool { + $configuration = $datasource->getConfiguration(); + $selected = $configuration['bundles']['selected']; + if ($configuration['bundles']['default'] === TRUE && empty($selected)) { + // All bundles are indexed. + return TRUE; + } + + if ($configuration['bundles']['default'] === TRUE && !empty($selected) && !in_array($bundle, $selected)) { + // All bundles are indexed, except a few that are selected. + return TRUE; + } + + if ($configuration['bundles']['default'] === FALSE && in_array($bundle, $selected)) { + // Only specific bundles are indexed. + return TRUE; + } + + return FALSE; + } + } diff --git a/src/ListSourceFactoryInterface.php b/src/ListSourceFactoryInterface.php index e560ff39..418c2876 100644 --- a/src/ListSourceFactoryInterface.php +++ b/src/ListSourceFactoryInterface.php @@ -35,4 +35,15 @@ public static function generateFacetSourcePluginId(string $entity_type, string $ */ public function get(string $entity_type, string $bundle): ?ListSourceInterface; + /** + * Checks whether a given entity type has a list source. + * + * @param string $entity_type + * The entity type ID. + * + * @return bool + * Whether the entity type is used in any list sources. + */ + public function isEntityTypeSourced(string $entity_type): bool; + } diff --git a/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.node.yml b/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.node.yml index 6a751bec..45879850 100644 --- a/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.node.yml +++ b/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.node.yml @@ -100,8 +100,10 @@ field_settings: datasource_settings: 'entity:node': bundles: - default: true - selected: { } + default: false + selected: + - content_type_one + - content_type_two languages: default: true selected: { } diff --git a/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.taxonomy.yml b/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.taxonomy.yml new file mode 100644 index 00000000..b02b43a6 --- /dev/null +++ b/tests/modules/oe_list_pages_filters_test/config/install/search_api.index.taxonomy.yml @@ -0,0 +1,33 @@ +langcode: en +status: true +dependencies: + config: + - search_api.server.database_server + module: + - search_api + - taxonomy +id: taxonomy +name: Taxonomy +description: '' +read_only: false +field_settings: { } +datasource_settings: + 'entity:taxonomy_term': + bundles: + default: true + selected: { } + languages: + default: true + selected: { } +processor_settings: + add_url: { } + aggregated_field: { } + language_with_fallback: { } + rendered_item: { } +tracker_settings: + default: + indexing_order: fifo +options: + index_directly: true + cron_limit: 50 +server: database_server diff --git a/tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_one.yml b/tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_one.yml new file mode 100644 index 00000000..ee89a1f2 --- /dev/null +++ b/tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_one.yml @@ -0,0 +1,7 @@ +langcode: en +status: true +dependencies: { } +name: 'Vocabulary one' +vid: vocabulary_one +description: '' +weight: 0 diff --git a/tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_two.yml b/tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_two.yml new file mode 100644 index 00000000..a4f9e5b1 --- /dev/null +++ b/tests/modules/oe_list_pages_filters_test/config/install/taxonomy.vocabulary.vocabulary_two.yml @@ -0,0 +1,7 @@ +langcode: en +status: true +dependencies: { } +name: 'Vocabulary two' +vid: vocabulary_two +description: '' +weight: 0 diff --git a/tests/modules/oe_list_pages_filters_test/oe_list_pages_filters_test.info.yml b/tests/modules/oe_list_pages_filters_test/oe_list_pages_filters_test.info.yml index 9ae3f772..e39d0d06 100755 --- a/tests/modules/oe_list_pages_filters_test/oe_list_pages_filters_test.info.yml +++ b/tests/modules/oe_list_pages_filters_test/oe_list_pages_filters_test.info.yml @@ -11,6 +11,7 @@ dependencies: - drupal:options - drupal:datetime - drupal:datetime_range + - drupal:taxonomy - extra_field:extra_field - oe_list_pages:oe_list_pages - oe_list_pages:oe_list_page_content_type diff --git a/tests/src/FunctionalJavascript/ListPagesExposedFiltersTest.php b/tests/src/FunctionalJavascript/ListPagesExposedFiltersTest.php index 26267ee3..1cc0705f 100755 --- a/tests/src/FunctionalJavascript/ListPagesExposedFiltersTest.php +++ b/tests/src/FunctionalJavascript/ListPagesExposedFiltersTest.php @@ -51,12 +51,8 @@ public function testListPagePluginFiltersFormConfiguration(): void { $actual_entity_types = $this->getSelectOptions('Source entity type'); $expected_entity_types = [ '' => '- Select -', - 'entity_meta_relation' => 'Entity Meta Relation', - 'entity_meta' => 'Entity meta', 'node' => 'Content', - 'path_alias' => 'URL alias', - 'search_api_task' => 'Search task', - 'user' => 'User', + 'taxonomy_term' => 'Taxonomy term', ]; $this->assertEquals($expected_entity_types, $actual_entity_types); // By default, Node is selected if there are no stored values. diff --git a/tests/src/FunctionalJavascript/ListPagesPluginTest.php b/tests/src/FunctionalJavascript/ListPagesPluginTest.php index 01038954..32e40441 100755 --- a/tests/src/FunctionalJavascript/ListPagesPluginTest.php +++ b/tests/src/FunctionalJavascript/ListPagesPluginTest.php @@ -6,7 +6,6 @@ use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\node\Entity\Node; -use Drupal\taxonomy\Entity\Vocabulary; use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait; /** @@ -22,60 +21,28 @@ class ListPagesPluginTest extends WebDriverTestBase { * {@inheritdoc} */ protected static $modules = [ - 'taxonomy', + 'options', + 'facets', + 'entity_reference_revisions', + 'oe_list_pages', + 'oe_list_pages_filters_test', + 'oe_list_page_content_type', 'node', + 'emr', + 'emr_node', + 'search_api', + 'search_api_db', 'oe_list_pages_event_subscriber_test', + 'oe_list_pages_filters_test', ]; - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->drupalCreateContentType([ - 'type' => 'node_type_one', - 'name' => 'Node Type 1', - ]); - - $this->drupalCreateContentType([ - 'type' => 'node_type_two', - 'name' => 'Node Type 2', - ]); - - $this->drupalCreateContentType([ - 'type' => 'node_type_three', - 'name' => 'Node Type 3', - ]); - - Vocabulary::create([ - 'vid' => 'vocab_one', - 'name' => 'Vocabulary 1', - ])->save(); - - Vocabulary::create([ - 'vid' => 'vocab_two', - 'name' => 'Vocabulary 2', - ])->save(); - - Vocabulary::create([ - 'vid' => 'vocab_three', - 'name' => 'Vocabulary 3', - ])->save(); - - /** @var \Drupal\emr\EntityMetaRelationInstaller $installer */ - $installer = \Drupal::service('emr.installer'); - $installer->installEntityMetaTypeOnContentEntityType('oe_list_page', 'node', [ - 'node_type_one', - ]); - } - /** * Test List Page entity meta plugin and available entity types/bundles. */ public function testListPagePluginForm(): void { - $this->drupalLogin($this->rootUser); - $this->drupalGet('node/add/node_type_one'); + $admin = $this->createUser([], NULL, TRUE); + $this->drupalLogin($admin); + $this->drupalGet('node/add/oe_list_page'); // Open the list page details element. $this->clickLink('List Page'); @@ -84,12 +51,7 @@ public function testListPagePluginForm(): void { $expected_entity_types = [ '' => '- Select -', - 'entity_meta_relation' => 'Entity Meta Relation', - 'entity_meta' => 'Entity meta', 'node' => 'Content', - 'path_alias' => 'URL alias', - 'search_api_task' => 'Search task', - 'user' => 'User', 'taxonomy_term' => 'Taxonomy term', ]; $this->assertEquals($expected_entity_types, $actual_entity_types); @@ -98,9 +60,8 @@ public function testListPagePluginForm(): void { $actual_bundles = $this->getSelectOptions('Source bundle'); $expected_bundles = [ - 'node_type_one' => 'Node Type 1', - 'node_type_two' => 'Node Type 2', - 'node_type_three' => 'Node Type 3', + 'content_type_one' => 'Content type one', + 'content_type_two' => 'Content type two', '' => '- Select -', ]; $this->assertEquals($expected_bundles, $actual_bundles); @@ -110,51 +71,46 @@ public function testListPagePluginForm(): void { $this->assertSession()->assertWaitOnAjaxRequest(); $actual_bundles = $this->getSelectOptions('Source bundle'); $expected_bundles = [ - 'vocab_one' => 'Vocabulary 1', - 'vocab_two' => 'Vocabulary 2', - 'vocab_three' => 'Vocabulary 3', + 'vocabulary_one' => 'Vocabulary one', + 'vocabulary_two' => 'Vocabulary two', '' => '- Select -', ]; $this->assertEquals($expected_bundles, $actual_bundles); - // Switch to a bundle-less entity type and assert we have only one bundle - // selection available. - $this->getSession()->getPage()->selectFieldOption('Source entity type', 'user'); + // Select a bundle, then change back to Node. Wait for all the Ajax + // requests to complete to ensure the callbacks work work. + $this->getSession()->getPage()->selectFieldOption('Source bundle', 'vocabulary_one'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->selectFieldOption('Source entity type', 'node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->selectFieldOption('Source bundle', 'content_type_one'); $this->assertSession()->assertWaitOnAjaxRequest(); - $actual_bundles = $this->getSelectOptions('Source bundle'); - $expected_bundles = [ - 'user' => 'User', - '' => '- Select -', - ]; - $this->assertEquals($expected_bundles, $actual_bundles); // Set state values to trigger the test event subscriber and make some // limitations. $allowed = [ 'node' => [ - 'node_type_two', - 'node_type_three', + 'content_type_one', ], 'taxonomy_term' => [ - 'vocab_one', - 'vocab_three', + 'vocabulary_one', ], ]; \Drupal::state()->set('oe_list_pages_test.allowed_entity_types_bundles', $allowed); - $this->drupalGet('node/add/node_type_one'); + $this->drupalGet('node/add/oe_list_page'); $this->clickLink('List Page'); $actual_entity_types = $this->getSelectOptions('Source entity type'); - $this->assertEquals([ + $expected_entity_types = [ + '' => '- Select -', 'node' => 'Content', 'taxonomy_term' => 'Taxonomy term', - '' => '- Select -', - ], $actual_entity_types); + ]; + $this->assertEquals($expected_entity_types, $actual_entity_types); $this->assertOptionSelected('Source entity type', 'Content'); $actual_bundles = $this->getSelectOptions('Source bundle'); $expected_bundles = [ - 'node_type_two' => 'Node Type 2', - 'node_type_three' => 'Node Type 3', + 'content_type_one' => 'Content type one', '' => '- Select -', ]; $this->assertEquals($expected_bundles, $actual_bundles); @@ -162,12 +118,11 @@ public function testListPagePluginForm(): void { $this->assertSession()->assertWaitOnAjaxRequest(); $actual_bundles = $this->getSelectOptions('Source bundle'); $expected_bundles = [ - 'vocab_one' => 'Vocabulary 1', - 'vocab_three' => 'Vocabulary 3', + 'vocabulary_one' => 'Vocabulary one', '' => '- Select -', ]; $this->assertEquals($expected_bundles, $actual_bundles); - $this->getSession()->getPage()->selectFieldOption('Source bundle', 'vocab_three'); + $this->getSession()->getPage()->selectFieldOption('Source bundle', 'vocabulary_one'); $this->assertSession()->assertWaitOnAjaxRequest(); // Select a bundle and save the node. @@ -185,21 +140,21 @@ public function testListPagePluginForm(): void { /** @var \Drupal\oe_list_pages\ListPageWrapper $entity_meta_wrapper */ $entity_meta_wrapper = $entity_meta->getWrapper(); - $this->assertEquals('taxonomy_term:vocab_three', $entity_meta_wrapper->getSource()); + $this->assertEquals('taxonomy_term:vocabulary_one', $entity_meta_wrapper->getSource()); $this->assertEquals('taxonomy_term', $entity_meta_wrapper->getSourceEntityType()); - $this->assertEquals('vocab_three', $entity_meta_wrapper->getSourceEntityBundle()); + $this->assertEquals('vocabulary_one', $entity_meta_wrapper->getSourceEntityBundle()); // Edit the node and assert that we show correct values in the form. $this->drupalGet($node->toUrl('edit-form')); $this->clickLink('List Page'); $this->assertOptionSelected('Source entity type', 'Taxonomy term'); - $this->assertOptionSelected('Source bundle', 'Vocabulary 3'); + $this->assertOptionSelected('Source bundle', 'Vocabulary one'); // Change the source to a Node type. $this->getSession()->getPage()->fillField('Title', 'Node title 2'); $this->getSession()->getPage()->selectFieldOption('Source entity type', 'node'); $this->assertSession()->assertWaitOnAjaxRequest(); - $this->getSession()->getPage()->selectFieldOption('Source bundle', 'node_type_two'); + $this->getSession()->getPage()->selectFieldOption('Source bundle', 'content_type_one'); $this->assertSession()->assertWaitOnAjaxRequest(); $this->getSession()->getPage()->pressButton('Save'); @@ -211,9 +166,9 @@ public function testListPagePluginForm(): void { $entity_meta_list = $node->get('emr_entity_metas'); $entity_meta = $entity_meta_list->getEntityMeta('oe_list_page'); $entity_meta_wrapper = $entity_meta->getWrapper(); - $this->assertEquals('node:node_type_two', $entity_meta_wrapper->getSource()); + $this->assertEquals('node:content_type_one', $entity_meta_wrapper->getSource()); $this->assertEquals('node', $entity_meta_wrapper->getSourceEntityType()); - $this->assertEquals('node_type_two', $entity_meta_wrapper->getSourceEntityBundle()); + $this->assertEquals('content_type_one', $entity_meta_wrapper->getSourceEntityBundle()); // Assert the previous entity meta revision kept the old value. $first_revision = \Drupal::entityTypeManager()->getStorage('node')->loadRevision(1); @@ -225,9 +180,9 @@ public function testListPagePluginForm(): void { /** @var \Drupal\oe_list_pages\ListPageWrapper $entity_meta_wrapper */ $entity_meta_wrapper = $entity_meta->getWrapper(); - $this->assertEquals('taxonomy_term:vocab_three', $entity_meta_wrapper->getSource()); + $this->assertEquals('taxonomy_term:vocabulary_one', $entity_meta_wrapper->getSource()); $this->assertEquals('taxonomy_term', $entity_meta_wrapper->getSourceEntityType()); - $this->assertEquals('vocab_three', $entity_meta_wrapper->getSourceEntityBundle()); + $this->assertEquals('vocabulary_one', $entity_meta_wrapper->getSourceEntityBundle()); } /**