diff --git a/core/modules/block_content/block_content.module b/core/modules/block_content/block_content.module index 71e1ee44d6..44a69579cd 100644 --- a/core/modules/block_content/block_content.module +++ b/core/modules/block_content/block_content.module @@ -116,21 +116,21 @@ function block_content_add_body_field($block_type_id, $label = 'Body') { * 'block_content' and the query has the tag 'block_content_access'. * * These queries should only return reusable blocks unless a condition on - * reusable is explicitly set. + * 'reusable' is explicitly set. * - * Since block_content entities can be set to be non-reusable they should by - * default not be selectable as entity reference values. A module can still - * create a instance of + * Block_content entities that are reusable should by default not be selectable + * as entity reference values. A module can still + * create an instance of * \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface * that will will allow selection of non-reusable blocks by explicitly setting - * a condition on the reusable field. + * a condition on either the 'reusable' field. * * @see \Drupal\block_content\BlockContentAccessControlHandler */ function block_content_query_entity_reference_alter(AlterableInterface $query) { if ($query instanceof SelectInterface && $query->getMetaData('entity_type') === 'block_content' && $query->hasTag('block_content_access')) { $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); - if (array_key_exists($data_table, $query->getTables()) && !_block_content_has_reusable_condition($query->conditions())) { + if (array_key_exists($data_table, $query->getTables()) && !_block_content_has_reusable_condition($query->conditions(), $query->getTables())) { // If no reusable condition create a condition set to TRUE. $query->condition("$data_table.reusable", TRUE); } @@ -142,16 +142,20 @@ function block_content_query_entity_reference_alter(AlterableInterface $query) { * * @param array $condition * The condition or condition group to check. + * @param array $tables + * The tables from the related select query. + * + * @see \Drupal\Core\Database\Query\SelectInterface::getTables * * @return bool * Whether the conditions contain any condition using the reusable field. */ -function _block_content_has_reusable_condition(array $condition) { +function _block_content_has_reusable_condition(array $condition, array $tables) { // If this is a condition group call this function recursively for each nested // condition until a condition is found that return TRUE. if (isset($condition['#conjunction'])) { foreach (array_filter($condition, 'is_array') as $nested_condition) { - if (_block_content_has_reusable_condition($nested_condition)) { + if (_block_content_has_reusable_condition($nested_condition, $tables)) { return TRUE; } } @@ -160,13 +164,16 @@ function _block_content_has_reusable_condition(array $condition) { if (isset($condition['field'])) { $field = $condition['field']; if (is_object($field) && $field instanceof ConditionInterface) { - return _block_content_has_reusable_condition($field->conditions()); + return _block_content_has_reusable_condition($field->conditions(), $tables); } $field_parts = explode('.', $field); - $data_table = $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); - // With nested conditions the data table may have a suffix at the end like - // 'block_content_field_data_2'. - return strpos($field_parts[0], $data_table) === 0 && $field_parts[1] === 'reusable'; + $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); + foreach ($tables as $table) { + if ($table['table'] === $data_table && $field_parts[0] === $table['alias'] && $field_parts[1] === 'reusable') { + return TRUE; + } + } + } return FALSE; } diff --git a/core/modules/block_content/block_content.post_update.php b/core/modules/block_content/block_content.post_update.php index 927e588068..6b323f99f8 100644 --- a/core/modules/block_content/block_content.post_update.php +++ b/core/modules/block_content/block_content.post_update.php @@ -5,35 +5,42 @@ * Post update functions for Custom Block. */ +use Drupal\Core\Config\Entity\ConfigEntityUpdater; + /** - * Adds 'reusable filter to a Custom Block views. + * Adds 'reusable filter to Custom Block views. */ -function block_content_post_update_add_views_reusable_filter() { - $config_factory = \Drupal::configFactory(); +function block_content_post_update_add_views_reusable_filter(&$sandbox = NULL) { $data_table = \Drupal::entityTypeManager() ->getDefinition('block_content') ->getDataTable(); - foreach ($config_factory->listAll('views.view.') as $view_config_name) { - $view = $config_factory->getEditable($view_config_name); + \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function ($view) use ($data_table) { + /** @var \Drupal\views\ViewEntityInterface $view */ if ($view->get('base_table') != $data_table) { - continue; + return FALSE; } - foreach ($view->get('display') as $display_name => $display) { + $save_view = FALSE; + $displays = $view->get('display'); + foreach ($displays as $display_name => &$display) { // Update the default display and displays that have overridden filters. if (!isset($display['display_options']['filters']['reusable']) && ($display_name === 'default' || isset($display['display_options']['filters']))) { - // Save off the base part of the config path we are updating. - $base = "display.$display_name.display_options.filters.reusable"; - $view->set("$base.id", 'reusable') - ->set("$base.plugin_id", 'boolean') - ->set("$base.table", $data_table) - ->set("$base.field", "reusable") - ->set("$base.value", "1") - ->set("$base.entity_type", "block_content") - ->set("$base.entity_field", "reusable"); + $display['display_options']['filters']['reusable'] = [ + 'id' => 'reusable', + 'plugin_id' => 'boolean', + 'table' => $data_table, + 'field' => 'reusable', + 'value' => '1', + 'entity_type' => 'block_content', + 'entity_field' => 'reusable', + ]; + $save_view = TRUE; } } - $view->save(); - } + if ($save_view) { + $view->set('display', $displays); + } + return $save_view; + }); } diff --git a/core/modules/block_content/src/BlockContentAccessControlHandler.php b/core/modules/block_content/src/BlockContentAccessControlHandler.php index 2ad950f4d0..55bebc03b5 100644 --- a/core/modules/block_content/src/BlockContentAccessControlHandler.php +++ b/core/modules/block_content/src/BlockContentAccessControlHandler.php @@ -35,7 +35,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter if (empty($dependency)) { return AccessResult::forbidden("Non-reusable blocks must set an access dependency for access control."); } - $access->andIf($dependency->access($operation, $account, TRUE))->addCacheableDependency($access); + $access->andIf($dependency->access($operation, $account, TRUE)); } return $access; } diff --git a/core/modules/block_content/src/BlockContentListBuilder.php b/core/modules/block_content/src/BlockContentListBuilder.php index 88545e09b4..f254a764dc 100644 --- a/core/modules/block_content/src/BlockContentListBuilder.php +++ b/core/modules/block_content/src/BlockContentListBuilder.php @@ -34,7 +34,6 @@ public function buildRow(EntityInterface $entity) { protected function getEntityIds() { $query = $this->getStorage()->getQuery() ->sort($this->entityType->getKey('id')); - $query->condition('reusable', TRUE); // Only add the pager if a limit is specified. diff --git a/core/modules/block_content/tests/modules/block_content_test/src/Plugin/EntityReferenceSelection/TestSelection.php b/core/modules/block_content/tests/modules/block_content_test/src/Plugin/EntityReferenceSelection/TestSelection.php index 82b8aa90dc..1a46c486c2 100644 --- a/core/modules/block_content/tests/modules/block_content_test/src/Plugin/EntityReferenceSelection/TestSelection.php +++ b/core/modules/block_content/tests/modules/block_content_test/src/Plugin/EntityReferenceSelection/TestSelection.php @@ -5,25 +5,35 @@ use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection; /** - * Test EntityReferenceSelection class that adds various 'reusable' conditions. + * Test EntityReferenceSelection with conditions on the 'reusable' field. */ class TestSelection extends DefaultSelection { /** - * The test mode. + * The condition type. * * @var string */ - protected $testMode; + protected $conditionType; + + /** + * Whether to set the condition for reusable or non-reusable blocks. + * + * @var bool + */ + protected $isReusable; /** * Sets the test mode. * - * @param string $testMode - * The test mode. + * @param string $condition_type + * The condition type. + * @param bool $is_reusable + * Whether to set the condition for reusable or non-reusable blocks. */ - public function setTestMode($testMode) { - $this->testMode = $testMode; + public function setTestMode($condition_type = NULL, $is_reusable = NULL) { + $this->conditionType = $condition_type; + $this->isReusable = $is_reusable; } /** @@ -31,50 +41,38 @@ public function setTestMode($testMode) { */ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') { $query = parent::buildEntityQuery($match, $match_operator); - switch ($this->testMode) { - case 'reusable_condition_false': - $query->condition("reusable", 0); - break; - - case 'reusable_condition_exists': - $query->exists('reusable'); - break; - - case 'reusable_condition_group_false': - $group = $query->andConditionGroup() - ->condition("reusable", 0) - ->exists('type'); - $query->condition($group); - break; - - case 'reusable_condition_group_true': - $group = $query->andConditionGroup() - ->condition("reusable", 1) - ->exists('type'); - $query->condition($group); - break; + if ($this->conditionType) { + /** @var \Drupal\Core\Database\Query\ConditionInterface $add_condition */ + $add_condition = NULL; + switch ($this->conditionType) { + case 'base': + $add_condition = $query; + break; - case 'reusable_condition_nested_group_false': - $query->exists('type'); - $sub_group = $query->andConditionGroup() - ->condition("reusable", 0) - ->exists('type'); - $group = $query->andConditionGroup() - ->exists('type') - ->condition($sub_group); - $query->condition($group); - break; + case 'group': + $group = $query->andConditionGroup() + ->exists('type'); + $add_condition = $group; + $query->condition($group); + break; - case 'reusable_condition_nested_group_true': - $query->exists('type'); - $sub_group = $query->andConditionGroup() - ->condition("reusable", 1) - ->exists('type'); - $group = $query->andConditionGroup() - ->exists('type') - ->condition($sub_group); - $query->condition($group); - break; + case "nested_group": + $query->exists('type'); + $sub_group = $query->andConditionGroup() + ->exists('type'); + $add_condition = $sub_group; + $group = $query->andConditionGroup() + ->exists('type') + ->condition($sub_group); + $query->condition($group); + break; + } + if ($this->isReusable) { + $add_condition->condition('reusable', 1); + } + else { + $add_condition->condition('reusable', 0); + } } return $query; } diff --git a/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml b/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml index a3cc406077..1c95f9f854 100644 --- a/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml +++ b/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml @@ -4,8 +4,6 @@ dependencies: module: - block_content - user -_core: - default_config_hash: gkRJCqHr3uSO8ALHLatX-7YKfX0lWEgkC5qMBtCf_Sg id: block_content label: 'Custom block library' module: views diff --git a/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php b/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php index 7803f40813..5d82f6f904 100644 --- a/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php +++ b/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php @@ -4,7 +4,6 @@ use Drupal\block_content\Entity\BlockContent; use Drupal\FunctionalTests\Update\UpdatePathTestBase; -use Drupal\views\Entity\View; /** * Tests 'reusable' field related update functions for the Block Content module. @@ -23,7 +22,7 @@ protected function setDatabaseDumpFiles() { } /** - * Tests adding a reusable field to the block content entity type. + * Tests adding 'reusable' entity base field to the block content entity type. * * @see block_content_update_8600 * @see block_content_post_update_add_views_reusable_filter @@ -33,17 +32,31 @@ public function testReusableFieldAddition() { $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); // Delete custom block library view. - View::load('block_content')->delete(); + $this->config('views.view.block_content')->delete(); // Install the test module with the 'block_content' view with an extra // display with overridden filters. This extra display should also have a // filter added for 'reusable' field so that it does not expose non-reusable - // fields. This display also a filter only show blocks that contain - // 'block2' in the 'info' field. + // fields. This display also has a filter that only shows blocks that + // contain 'block2' in the 'info' field. $this->container->get('module_installer')->install(['block_content_view_override']); + // Ensure that 'reusable' field is not present before updates. + $this->assertEmpty($entity_definition_update_manager->getFieldStorageDefinition('reusable', 'block_content')); + + // Ensure that 'reusable' filter is not present before updates. + $view_config = \Drupal::configFactory()->get('views.view.block_content'); + $this->assertFalse($view_config->isNew()); + $this->assertEmpty($view_config->get('display.default.display_options.filters.reusable')); + $this->assertEmpty($view_config->get('display.page_2.display_options.filters.reusable')); // Run updates. $this->runUpdates(); + // Ensure that 'reusable' filter is present after updates. + \Drupal::configFactory()->clearStaticCache(); + $view_config = \Drupal::configFactory()->get('views.view.block_content'); + $this->assertNotEmpty($view_config->get('display.default.display_options.filters.reusable')); + $this->assertNotEmpty($view_config->get('display.page_2.display_options.filters.reusable')); + // Check that the field exists and is configured correctly. $reusable_field = $entity_definition_update_manager->getFieldStorageDefinition('reusable', 'block_content'); $this->assertEquals('Reusable', $reusable_field->getLabel()); @@ -64,59 +77,50 @@ public function testReusableFieldAddition() { ]); $after_block2->save(); - $this->assertEquals(TRUE, $after_block1->isReusable()); - $this->assertEquals(TRUE, $after_block2->isReusable()); + $this->assertTrue($after_block1->isReusable()); + $this->assertTrue($after_block2->isReusable()); + + $admin_user = $this->drupalCreateUser(['administer blocks']); + $this->drupalLogin($admin_user); - $non_reusable_block = BlockContent::create([ - 'info' => 'non-reusable block1', + $block_non_reusable = BlockContent::create([ + 'info' => 'block1 non reusable', 'type' => 'basic_block', 'reusable' => FALSE, ]); - $non_reusable_block->save(); - // Add second block that will be would shown with the 'info' filter on the - // additional view display if the 'reusable filter was not added. - $non_reusable_block2 = BlockContent::create([ - 'info' => 'non-reusable block2', + $block_non_reusable->save(); + // Add second block that would be shown with the 'info' filter on the + // additional view display if the 'reusable' filter was not added. + $block2_non_reusable = BlockContent::create([ + 'info' => 'block2 non reusable', 'type' => 'basic_block', 'reusable' => FALSE, ]); - $non_reusable_block2->save(); - $this->assertEquals(FALSE, $non_reusable_block->isReusable()); - $this->assertEquals(FALSE, $non_reusable_block2->isReusable()); - - $admin_user = $this->drupalCreateUser(['administer blocks']); - $this->drupalLogin($admin_user); + $block2_non_reusable->save(); + $this->assertFalse($block_non_reusable->isReusable()); + $this->assertFalse($block2_non_reusable->isReusable()); - // Ensure the Custom Block view shows the reusable blocks but not - // the non-reusable block. + // Ensure the Custom Block view shows the reusable blocks only. $this->drupalGet('admin/structure/block/block-content'); $assert_session->statusCodeEquals('200'); $assert_session->responseContains('view-id-block_content'); $assert_session->pageTextContains($after_block1->label()); $assert_session->pageTextContains($after_block2->label()); - $assert_session->pageTextNotContains($non_reusable_block->label()); - $assert_session->pageTextNotContains($non_reusable_block2->label()); + $assert_session->pageTextNotContains($block_non_reusable->label()); + $assert_session->pageTextNotContains($block2_non_reusable->label()); - // Ensure the views other display also filters out non-reusable blocks and - // still filters on the 'info' field. + // Ensure the view's other display also only shows reusable blocks and still + // filters on the 'info' field. $this->drupalGet('extra-view-display'); $assert_session->statusCodeEquals('200'); $assert_session->responseContains('view-id-block_content'); $assert_session->pageTextNotContains($after_block1->label()); $assert_session->pageTextContains($after_block2->label()); - $assert_session->pageTextNotContains($non_reusable_block->label()); - $assert_session->pageTextNotContains($non_reusable_block2->label()); - - $this->drupalGet('block/' . $after_block1->id()); - $assert_session->statusCodeEquals('200'); - - // Ensure that non-reusable blocks edit form edit route is not accessible. - $this->drupalGet('block/' . $non_reusable_block->id()); - $assert_session->statusCodeEquals('403'); + $assert_session->pageTextNotContains($block_non_reusable->label()); + $assert_session->pageTextNotContains($block2_non_reusable->label()); - // Ensure the Custom Block listing without Views installed shows the - // reusable blocks but not the non-reusable blocks. - // the non-reusable block. + // Ensure the Custom Block listing without Views installed shows the only + // reusable blocks. $this->drupalGet('admin/structure/block/block-content'); $this->container->get('module_installer')->uninstall(['views_ui', 'views']); $this->drupalGet('admin/structure/block/block-content'); @@ -124,8 +128,27 @@ public function testReusableFieldAddition() { $assert_session->responseNotContains('view-id-block_content'); $assert_session->pageTextContains($after_block1->label()); $assert_session->pageTextContains($after_block2->label()); - $assert_session->pageTextNotContains($non_reusable_block->label()); - $assert_session->pageTextNotContains($non_reusable_block2->label()); + $assert_session->pageTextNotContains($block_non_reusable->label()); + $assert_session->pageTextNotContains($block2_non_reusable->label()); + + $this->drupalGet('block/' . $after_block1->id()); + $assert_session->statusCodeEquals('200'); + + // Ensure the non-reusable block is not accessible in the form. + $this->drupalGet('block/' . $block_non_reusable->id()); + $assert_session->statusCodeEquals('403'); + + $this->drupalLogout(); + + $this->drupalLogin($this->createUser([ + 'access user profiles', + 'administer blocks', + ])); + $this->drupalGet('block/' . $after_block1->id()); + $assert_session->statusCodeEquals('200'); + + $this->drupalGet('block/' . $block_non_reusable->id()); + $assert_session->statusCodeEquals('403'); } } diff --git a/core/modules/block_content/tests/src/Kernel/BlockContentEntityReferenceSelectionTest.php b/core/modules/block_content/tests/src/Kernel/BlockContentEntityReferenceSelectionTest.php index 882e653eaf..725abf2603 100644 --- a/core/modules/block_content/tests/src/Kernel/BlockContentEntityReferenceSelectionTest.php +++ b/core/modules/block_content/tests/src/Kernel/BlockContentEntityReferenceSelectionTest.php @@ -10,7 +10,7 @@ /** * Tests EntityReference selection handlers don't return non-reusable blocks. * - * @see block_content_query_block_content_access_alter() + * @see block_content_query_entity_reference_alter() * * @group block_content */ @@ -34,12 +34,41 @@ class BlockContentEntityReferenceSelectionTest extends KernelTestBase { */ protected $entityTypeManager; + /** + * Test reusable block. + * + * @var \Drupal\block_content\BlockContentInterface + */ + protected $blockReusable; + + /** + * Test non-reusable block. + * + * @var \Drupal\block_content\BlockContentInterface + */ + protected $blockNonReusable; + + /** + * Test selection handler. + * + * @var \Drupal\block_content_test\Plugin\EntityReferenceSelection\TestSelection + */ + protected $selectionHandler; + + /** + * Test block expectations. + * + * @var array + */ + protected $expectations; + /** * {@inheritdoc} */ public function setUp() { parent::setUp(); $this->installSchema('system', ['sequence']); + $this->installSchema('system', ['sequences']); $this->installEntitySchema('user'); $this->installEntitySchema('block_content'); @@ -51,116 +80,111 @@ public function setUp() { ]); $block_content_type->save(); $this->entityTypeManager = $this->container->get('entity_type.manager'); - } - /** - * Tests that non-reusable blocks are not referenceable entities. - * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - * @throws \Exception - */ - public function testReferenceableEntities() { - // And reusable and non-reusable block content entities. - $block_content_reusable = BlockContent::create([ + // And reusable block content entities. + $this->blockReusable = BlockContent::create([ 'info' => 'Reusable Block', 'type' => 'spiffy', - 'reusable' => TRUE, ]); - $block_content_reusable->save(); - $block_content_nonreusable = BlockContent::create([ + $this->blockReusable->save(); + $this->blockNonReusable = BlockContent::create([ 'info' => 'Non-reusable Block', 'type' => 'spiffy', 'reusable' => FALSE, ]); - $block_content_nonreusable->save(); - - // Ensure that queries without all the tags are not altered. - $query = $this->entityTypeManager->getStorage('block_content')->getQuery(); - $this->assertCount(2, $query->execute()); + $this->blockNonReusable->setReusable(FALSE); + $this->blockNonReusable->save(); - $query = $this->entityTypeManager->getStorage('block_content')->getQuery(); - $query->addTag('block_content_access'); - $this->assertCount(2, $query->execute()); - - $query = $this->entityTypeManager->getStorage('block_content')->getQuery(); - $query->addTag('entity_query_block_content'); - $this->assertCount(2, $query->execute()); - - // Use \Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection - // class to test that getReferenceableEntities() does not get the - // non-reusable entity. $configuration = [ 'target_type' => 'block_content', 'target_bundles' => ['spiffy' => 'spiffy'], 'sort' => ['field' => '_none'], ]; - $selection_handler = new TestSelection($configuration, '', '', $this->container->get('entity.manager'), $this->container->get('module_handler'), \Drupal::currentUser()); + $this->selectionHandler = new TestSelection($configuration, '', '', $this->container->get('entity.manager'), $this->container->get('module_handler'), \Drupal::currentUser()); + // Setup the 3 expectation cases. - $both_blocks = [ - 'spiffy' => [ - $block_content_reusable->id() => $block_content_reusable->label(), - $block_content_nonreusable->id() => $block_content_nonreusable->label(), + $this->expectations = [ + 'both_blocks' => [ + 'spiffy' => [ + $this->blockReusable->id() => $this->blockReusable->label(), + $this->blockNonReusable->id() => $this->blockNonReusable->label(), + ], ], + 'block_reusable' => ['spiffy' => [$this->blockReusable->id() => $this->blockReusable->label()]], + 'block_non_reusable' => ['spiffy' => [$this->blockNonReusable->id() => $this->blockNonReusable->label()]], ]; - $reusable_block = ['spiffy' => [$block_content_reusable->id() => $block_content_reusable->label()]]; - $non_reusable_block = ['spiffy' => [$block_content_nonreusable->id() => $block_content_nonreusable->label()]]; + } - $this->assertEquals( - $reusable_block, - $selection_handler->getReferenceableEntities() - ); + /** + * Tests to make sure queries without the expected tags are not altered. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + public function testQueriesNotAltered() { + // Ensure that queries without all the tags are not altered. + $query = $this->entityTypeManager->getStorage('block_content')->getQuery(); + $this->assertCount(2, $query->execute()); - // Test various ways in which an EntityReferenceSelection plugin could set - // the 'reusable' condition. If the plugin has set a condition on 'reusable' - // at all then 'block_content_query_entity_reference_alter()' will not set - // a reusable condition. - $selection_handler->setTestMode('reusable_condition_false'); - $this->assertEquals( - $non_reusable_block, - $selection_handler->getReferenceableEntities() - ); + $query = $this->entityTypeManager->getStorage('block_content')->getQuery(); + $query->addTag('block_content_access'); + $this->assertCount(2, $query->execute()); - $selection_handler->setTestMode('reusable_condition_exists'); - $this->assertEquals( - $both_blocks, - $selection_handler->getReferenceableEntities() - ); + $query = $this->entityTypeManager->getStorage('block_content')->getQuery(); + $query->addTag('entity_query_block_content'); + $this->assertCount(2, $query->execute()); + } - $selection_handler->setTestMode('reusable_condition_group_false'); + /** + * Test with no conditions set. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ + public function testNoConditions() { $this->assertEquals( - $non_reusable_block, - $selection_handler->getReferenceableEntities() + $this->expectations['block_reusable'], + $this->selectionHandler->getReferenceableEntities() ); - $selection_handler->setTestMode('reusable_condition_group_true'); - $this->assertEquals( - $reusable_block, - $selection_handler->getReferenceableEntities() - ); + $this->blockNonReusable->setReusable(TRUE); + $this->blockNonReusable->save(); - $selection_handler->setTestMode('reusable_condition_nested_group_false'); + // Ensure that the block is now returned as a referenceable entity. $this->assertEquals( - $non_reusable_block, - $selection_handler->getReferenceableEntities() + $this->expectations['both_blocks'], + $this->selectionHandler->getReferenceableEntities() ); + } - $selection_handler->setTestMode('reusable_condition_nested_group_true'); + /** + * Tests setting 'reusable' condition on different levels. + * + * @dataProvider fieldConditionProvider + * + * @throws \Exception + */ + public function testFieldConditions($condition_type, $is_reusable) { + $this->selectionHandler->setTestMode($condition_type, $is_reusable); $this->assertEquals( - $reusable_block, - $selection_handler->getReferenceableEntities() + $is_reusable ? $this->expectations['block_reusable'] : $this->expectations['block_non_reusable'], + $this->selectionHandler->getReferenceableEntities() ); + } - // Change the block to reusable. - $block_content_nonreusable->setReusable(TRUE); - $block_content_nonreusable->save(); - // Don't use any conditions. - $selection_handler->setTestMode(NULL); - // Ensure that the block is now returned as a referenceable entity. - $this->assertEquals( - $both_blocks, - $selection_handler->getReferenceableEntities() - ); + /** + * Provides possible fields and condition types. + */ + public function fieldConditionProvider() { + $cases = []; + foreach (['base', 'group', 'nested_group'] as $condition_type) { + foreach ([TRUE, FALSE] as $reusable) { + $cases["$condition_type:" . ($reusable ? 'reusable' : 'non-reusable')] = [ + $condition_type, + $reusable, + ]; + } + } + return $cases; } }