diff --git a/core/modules/block/block.install b/core/modules/block/block.install new file mode 100644 index 0000000..2a38bd5 --- /dev/null +++ b/core/modules/block/block.install @@ -0,0 +1,89 @@ + 'language.current_language_context', + 'node' => 'node.node_route_context', + 'user' => 'user.current_user_context', + ]; + + $condition_plugin_id_ui_label_map = [ + 'node_type' => \Drupal::translation()->translate('Content types'), + 'request_path' => \Drupal::translation()->translate('Pages'), + 'user_role' => \Drupal::translation()->translate('Roles'), + ]; + + $config_factory = \Drupal::configFactory(); + $message = NULL; + $disabled_blocks = $problematic_visibility_plugins = $backup_values = []; + // We deal with config objects directly in order to avoid invoking Entity API + // hooks. They can be problematic when two modules implement a specific hook, + // their code is updated at the same time, yet the update functions run + // sequentially so the second hook implementation can not rely on the data not + // being altered by the first hook. + foreach ($config_factory->listAll('block.block.') as $block_config_name) { + $block = $config_factory->getEditable($block_config_name); + if ($visibility = $block->get('visibility')) { + foreach ($visibility as $condition_plugin_id => &$condition) { + foreach ($condition['context_mapping'] as $key => $context) { + if (!isset($context_service_id_map[$key])) { + // Remove the visibility condition for unknown context mapping + // entries, so the update process itself runs through and users can + // fix their block placements manually OR alternatively contrib + // modules can run their own update functions to update mappings + // that they provide. + $disabled_blocks[$block->get('id')] = $block->get('settings.label'); + $problematic_visibility_plugins[$block->get('id')][] = $condition_plugin_id_ui_label_map[$condition_plugin_id]; + + $backup_values[] = $context; + unset($visibility[$condition_plugin_id]); + continue; + } + $new_context_id = explode('.', $context, 2); + $condition['context_mapping'][$key] = '@' . $context_service_id_map[$key] . ':' . $new_context_id[1]; + } + } + if (isset($disabled_blocks[$block->get('id')])) { + // This block will have an invalid context mapping service and must be + // disabled to avoid the 'You must provide the context IDs in the + // @{service_id}:{unqualified_context_id} format' exception. + $block->set('status', FALSE); + } + $block->set('visibility', $visibility); + + if ($backup_values) { + $block->set('visibility_2528178', $backup_values); + } + } + + $block->save(); + } + + if ($disabled_blocks) { + $message = \Drupal::translation()->translate('Encountered an unexpected context mapping key, one or more mappings could not be updated. Please manually review your visibility settings for the following blocks:'); + $message .= ''; + } + + return $message; +} + +/** + * @} End of "addtogroup updates-8.0.0-beta". + */ diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml index 16d79bc..c2dcfd8 100644 --- a/core/modules/block/config/schema/block.schema.yml +++ b/core/modules/block/config/schema/block.schema.yml @@ -30,6 +30,12 @@ block.block.*: sequence: type: condition.plugin.[id] label: 'Visibility Condition' + visibility_2528178: + type: sequence + label: 'Missing conversions of 2528178' + sequence: + type: string + label: 'Old visiblity settings' block.settings.*: type: block_settings diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php index d2b3031..471aac7 100644 --- a/core/modules/block/src/Entity/Block.php +++ b/core/modules/block/src/Entity/Block.php @@ -48,6 +48,7 @@ * "plugin", * "settings", * "visibility", + * "visibility_2528178", * }, * lookup_keys = { * "theme" @@ -99,6 +100,13 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin protected $visibility = []; /** + * Missing visibility contexts from the 2528178 update. + * + * @var array + */ + protected $visibility_2528178 = []; + + /** * The plugin collection that holds the block plugin for this entity. * * @var \Drupal\block\BlockPluginCollection diff --git a/core/modules/block/src/Tests/Update/BlockContextMappingUpdateTest.php b/core/modules/block/src/Tests/Update/BlockContextMappingUpdateTest.php new file mode 100644 index 0000000..3b3bdf7 --- /dev/null +++ b/core/modules/block/src/Tests/Update/BlockContextMappingUpdateTest.php @@ -0,0 +1,94 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php', + ]; + parent::setUp(); + } + + /** + * Tests that block context mapping are updated properly. + */ + public function testUpdateHookN() { + $this->runUpdates(); + $this->assertRaw('Encountered an unexpected context mapping key, one or more mappings could not be updated. Please manually review your visibility settings for the following blocks:'); + + // Disable maintenance mode. + \Drupal::state()->set('system.maintenance_mode', FALSE); + + // The block that we are testing has the following visibility rules: + // - only visible on node pages + // - only visible to authenticated users. + $block_title = 'Test for 2354889'; + + // Create two nodes, a page and an article. + $page = Node::create([ + 'type' => 'page', + 'title' => 'Page node', + ]); + $page->save(); + + $article = Node::create([ + 'type' => 'article', + 'title' => 'Article node', + ]); + $article->save(); + + // Check that the block appears only on Page nodes for authenticated users. + $this->drupalGet('node/' . $page->id()); + $this->assertRaw($block_title, 'Test block is visible on a Page node as an authenticated user.'); + + $this->drupalGet('node/' . $article->id()); + $this->assertNoRaw($block_title, 'Test block is not visible on a Article node as an authenticated user.'); + + $this->drupalLogout(); + + // Check that the block does not appear on any page for anonymous users. + $this->drupalGet('node/' . $page->id()); + $this->assertNoRaw($block_title, 'Test block is not visible on a Page node as an anonymous user.'); + + $this->drupalGet('node/' . $article->id()); + $this->assertNoRaw($block_title, 'Test block is not visible on a Article node as an anonymous user.'); + + // Check that a block with invalid context is being disabled and that it can + // still be edited afterward. + $disabled_block = Block::load('thirdtestfor2354889'); + $this->assertFalse($disabled_block->status(), 'Block with invalid context is disabled'); + $this->assertEqual(['baloney.spam'], $disabled_block->get('visibility_2528178')); + + $disabled_block_visibility = $disabled_block->get('visibility'); + $this->assertTrue(!isset($disabled_block_visibility['node_type']), 'The problematic visibility condition has been removed.'); + + $admin_user = $this->drupalCreateUser(['administer blocks']); + $this->drupalLogin($admin_user); + + $this->drupalGet('admin/structure/block/manage/thirdtestfor2354889'); + $this->assertResponse('200'); + } + +} diff --git a/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php b/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php new file mode 100644 index 0000000..0ad77c5 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php @@ -0,0 +1,158 @@ + '9d204071-a923-4707-8200-c298a540fb0c', + 'langcode' => 'en', + 'status' => TRUE, + 'dependencies' => [ + 'content' => [ + 'block_content:basic:c1895145-893e-460b-a24e-78cd2cefbb1f', + ], + 'module' => [ + 'block_content', + 'node', + 'user', + ], + 'theme' => [ + 'bartik', + ], + ], + 'id' => 'testfor2354889', + 'theme' => 'bartik', + 'region' => 'content', + 'weight' => -6, + 'provider' => NULL, + 'plugin' => 'block_content:c1895145-893e-460b-a24e-78cd2cefbb1f', + 'settings' => [ + 'id' => 'block_content:c1895145-893e-460b-a24e-78cd2cefbb1f', + 'label' => 'Test for 2354889', + 'provider' => 'block_content', + 'label_display' => 'visible', + 'cache' => [ + 'max_age' => -1, + ], + 'status' => TRUE, + 'info' => '', + 'view_mode' => 'full', + ], + 'visibility' => [ + 'node_type' => [ + 'id' => 'node_type', + 'bundles' => [ + 'page' => 'page', + ], + 'negate' => FALSE, + // Context that will be tested. + 'context_mapping' => [ + 'node' => 'node.node', + ], + ], + 'user_role' => [ + 'id' => 'user_role', + 'roles' => [ + 'authenticated' => 'authenticated', + ], + 'negate' => FALSE, + // Context that will be tested. + 'context_mapping' => [ + 'user' => 'user.current_user', + ], + ], + ], +],[ + 'uuid' => 'C499B41D-035E-432E-9462-36410C43C49F', + 'langcode' => 'en', + 'status' => TRUE, + 'dependencies' => [ + 'module' => [ + 'search', + ], + 'theme' => [ + 'bartik', + ], + ], + 'id' => 'secondtestfor2354889', + 'theme' => 'bartik', + 'region' => 'sidebar_first', + 'weight' => -6, + 'provider' => NULL, + 'plugin' => 'search_form_block', + 'settings' => [ + 'id' => 'search_form_block', + 'label' => 'Search', + 'provider' => 'search', + 'label_display' => 'visible', + 'cache' => [ + 'max_age' => -1, + ], + 'status' => TRUE, + ], + 'visibility' => [], +],[ + 'uuid' => '4558907D-2918-48FE-B56F-8A007B5FBDD5', + 'langcode' => 'en', + 'status' => TRUE, + 'dependencies' => [ + 'module' => [ + 'user', + ], + 'theme' => [ + 'bartik', + ], + ], + 'id' => 'thirdtestfor2354889', + 'theme' => 'bartik', + 'region' => 'sidebar_first', + 'weight' => -6, + 'provider' => NULL, + 'plugin' => 'user_login_block', + 'settings' => [ + 'id' => 'user_login_block', + 'label' => 'User login', + 'provider' => 'user', + 'label_display' => 'visible', + 'cache' => [ + 'max_age' => -1, + ], + 'status' => TRUE, + ], + 'visibility' => [ + 'node_type' => [ + 'id' => 'node_type', + 'bundles' => [ + 'page' => 'page', + ], + 'negate' => FALSE, + // Non-existent mapping that will be tested. + 'context_mapping' => [ + 'baloney' => 'baloney.spam', + ], + ], + ], +]]; + +foreach ($block_configs as $block_config) { + $connection->insert('config') + ->fields(array( + 'collection', + 'name', + 'data', + )) + ->values(array( + 'collection' => '', + 'name' => 'block.block.' . $block_config['id'], + 'data' => serialize($block_config), + )) + ->execute(); +}