diff --git a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php index 356967b..168d5d7 100644 --- a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php +++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php @@ -41,7 +41,7 @@ public function __construct(DiscoveryInterface $discovery) { * Implements Drupal\Component\Plugin\Factory\FactoryInterface::createInstance(). */ public function createInstance($plugin_id, array $configuration) { - $plugin_class = $this->getPluginClass($plugin_id); + $plugin_class = static::getPluginClass($plugin_id, $this->discovery); return new $plugin_class($configuration, $plugin_id, $this->discovery); } @@ -50,12 +50,14 @@ public function createInstance($plugin_id, array $configuration) { * * @param array $plugin_id * The id of a plugin. + * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery + * The discovery object. * * @return string * The appropriate class name. */ - protected function getPluginClass($plugin_id) { - $plugin_definition = $this->discovery->getDefinition($plugin_id); + public static function getPluginClass($plugin_id, DiscoveryInterface $discovery) { + $plugin_definition = $discovery->getDefinition($plugin_id); if (empty($plugin_definition['class'])) { throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id)); } diff --git a/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php b/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php index a81b868..50b3868 100644 --- a/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php +++ b/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php @@ -20,7 +20,7 @@ class ReflectionFactory extends DefaultFactory { * Implements Drupal\Component\Plugin\Factory\FactoryInterface::createInstance(). */ public function createInstance($plugin_id, array $configuration) { - $plugin_class = $this->getPluginClass($plugin_id); + $plugin_class = static::getPluginClass($plugin_id, $this->discovery); // Lets figure out of there's a constructor for this class and pull // arguments from the $options array if so to populate it. diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index 35cdc19..2fd5929 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -371,11 +371,9 @@ function aggregator_save_category($edit) { ->condition('cid', $edit['cid']) ->execute(); // Make sure there is no active block for this category. - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - if ($config->get('id') == 'aggregator_category_block:' . $edit['cid']) { - $config->delete(); + if (module_exists('block')) { + foreach (entity_load_multiple_by_properties('block', array('plugin' => 'aggregator_category_block:' . $edit['cid'])) as $block) { + $block->delete(); } } $edit['title'] = ''; @@ -439,11 +437,9 @@ function aggregator_save_feed($edit) { ->condition('fid', $edit['fid']) ->execute(); // Make sure there is no active block for this feed. - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - if ($config->get('id') == 'aggregator_feed_block:' . $edit['fid']) { - $config->delete(); + if (module_exists('block')) { + foreach (entity_load_multiple_by_properties('block', array('plugin' => 'aggregator_feed_block:' . $edit['fid'])) as $block) { + $block->delete(); } } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php index 1f13232..e1f8fcd 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php @@ -39,8 +39,7 @@ public function testBlockLinks() { $this->updateFeedItems($feed, $this->getDefaultFeedItemCount()); // Clear the block cache to load the new block definitions. - $manager = $this->container->get('plugin.manager.block'); - $manager->clearCachedDefinitions(); + $this->container->get('plugin.manager.block')->clearCachedDefinitions(); // Need admin user to be able to access block admin. $admin_user = $this->drupalCreateUser(array( @@ -51,15 +50,11 @@ public function testBlockLinks() { )); $this->drupalLogin($admin_user); - $block = array( - 'title' => 'feed-' . $feed->title, - 'block_count' => 2, - ); - $this->drupalPlaceBlock("aggregator_feed_block:{$feed->fid}", $block); + $block = $this->drupalPlaceBlock("aggregator_feed_block:{$feed->fid}", array('label' => 'feed-' . $feed->title), array('block_count' => 2)); // Confirm that the block is now being displayed on pages. $this->drupalGet('node'); - $this->assertText(t($block['title']), 'Feed block is displayed on the page.'); + $this->assertText($block->label(), 'Feed block is displayed on the page.'); // Find the expected read_more link. $href = 'aggregator/sources/' . $feed->fid; @@ -77,7 +72,7 @@ public function testBlockLinks() { aggregator_save_feed((array) $feed); // Check that the block is no longer displayed. $this->drupalGet('node'); - $this->assertNoText(t($block['title']), 'Feed block is not displayed on the page when number of items is set to 0.'); + $this->assertNoText($block->label(), 'Feed block is not displayed on the page when number of items is set to 0.'); } /** diff --git a/core/modules/block/block.admin.inc b/core/modules/block/block.admin.inc index 13f17ad..c3955fc 100644 --- a/core/modules/block/block.admin.inc +++ b/core/modules/block/block.admin.inc @@ -5,6 +5,8 @@ * Admin page callbacks for the block module. */ +use Drupal\block\Plugin\Core\Entity\Block; + /** * Page callback: Attaches CSS for the block region demo. * @@ -18,310 +20,80 @@ function block_admin_demo($theme = NULL) { /** * Page callback: Shows the block administration page. * - * @param $theme - * The theme to display the administration page for. If not provided, defaults - * to the currently used theme. - * - * @see block_menu() - */ -function block_admin_display($theme = NULL) { - global $theme_key; - - drupal_theme_initialize(); - - if (!isset($theme)) { - // If theme is not specifically set, rehash for the current theme. - $theme = $theme_key; - } - - // Fetch and sort blocks. - $blocks = block_admin_display_prepare_blocks($theme); - - return drupal_get_form('block_admin_display_form', $blocks, $theme); -} - -/** - * Prepares a list of blocks for display on the blocks administration page. - * - * @param $theme - * The machine-readable name of the theme whose blocks should be returned. - * - * @return - * An array of blocks, as returned by _block_rehash(), sorted by region in - * preparation for display on the blocks administration page. - * - * @see block_admin_display_form() - */ -function block_admin_display_prepare_blocks($theme) { - $blocks = _block_rehash($theme); - $compare_theme = &drupal_static('_block_compare:theme'); - $compare_theme = $theme; - usort($blocks, '_block_compare'); - return $blocks; -} - -/** - * Form constructor for the main block administration form. - * - * @param $blocks - * An array of blocks, as returned by block_admin_display_prepare_blocks(). - * @param $theme - * A string representing the name of the theme to edit blocks for. - * @param $block_regions - * (optional) An array of regions in which the blocks will be allowed to be - * placed. Defaults to all visible regions for the theme whose blocks are - * being configured. In all cases, a dummy region for disabled blocks will - * also be displayed. - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see block_admin_display_form_submit() - */ -function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_regions = NULL) { - $path = drupal_get_path('module', 'block'); - $form['#attached']['css'][] = $path . '/block.admin.css'; - $form['#attached']['library'][] = array('system', 'drupal.tableheader'); - $form['#attached']['library'][] = array('block', 'drupal.block'); - - // Get a list of block regions if one was not provided. - if (!isset($block_regions)) { - $block_regions = system_region_list($theme, REGIONS_VISIBLE); - } - // Add a last region for disabled blocks. - $block_regions_with_disabled = $block_regions + array(BLOCK_REGION_NONE => BLOCK_REGION_NONE); - - foreach ($block_regions_with_disabled as $region => $title) { - $form['#attached']['drupal_add_tabledrag'][] = array('blocks', 'match', 'sibling', 'block-region-select', 'block-region-' . $region, NULL, FALSE); - $form['#attached']['drupal_add_tabledrag'][] = array('blocks', 'order', 'sibling', 'block-weight', 'block-weight-' . $region); - } - - // Weights range from -delta to +delta, so delta should be at least half - // of the amount of blocks present. This makes sure all blocks in the same - // region get an unique weight. - $weight_delta = round(count($blocks) / 2); - - // Build the form tree. - $form['edited_theme'] = array( - '#type' => 'value', - '#value' => $theme, - ); - $form['block_regions'] = array( - '#type' => 'value', - '#value' => $block_regions_with_disabled, - ); - $form['blocks'] = array(); - $form['#tree'] = TRUE; - - foreach ($blocks as $key => $instance) { - $block = $instance->getConfig(); - $form['blocks'][$key]['config_id'] = array( - '#type' => 'value', - '#value' => $block['config_id'], - ); - $info = $instance->getDefinition(); - $form['blocks'][$key]['info'] = array( - '#markup' => check_plain($info['subject']), - ); - $form['blocks'][$key]['theme'] = array( - '#type' => 'hidden', - '#value' => $theme, - ); - $form['blocks'][$key]['weight'] = array( - '#type' => 'weight', - '#default_value' => $block['weight'], - '#delta' => $weight_delta, - '#title_display' => 'invisible', - '#title' => t('Weight for @block block', array('@block' => $info['subject'])), - ); - $form['blocks'][$key]['region'] = array( - '#type' => 'select', - '#default_value' => $block['region'] != BLOCK_REGION_NONE ? $block['region'] : NULL, - '#empty_value' => BLOCK_REGION_NONE, - '#title_display' => 'invisible', - '#title' => t('Region for @block block', array('@block' => $info['subject'])), - '#options' => $block_regions, - ); - $links['configure'] = array( - 'title' => t('configure'), - 'href' => 'admin/structure/block/manage/' . $block['config_id'] . '/' . $theme . '/configure', - ); - $links['delete'] = array( - 'title' => t('delete'), - 'href' => 'admin/structure/block/manage/' . $block['config_id'] . '/' . $theme . '/delete', - ); - $form['blocks'][$key]['operations'] = array( - '#type' => 'operations', - '#links' => $links, - ); - } - // Do not allow disabling the main system content block when it is present. - if (isset($form['blocks']['system_main']['region'])) { - $form['blocks']['system_main']['region']['#required'] = TRUE; - } - - $form['actions'] = array( - '#tree' => FALSE, - '#type' => 'actions', - ); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save blocks'), - '#button_type' => 'primary', - ); - - return $form; -} - -/** - * Form submission handler for block_admin_display_form(). + * @param string $theme + * The theme to display the administration page for. * - * @see block_admin_display_form() - */ -function block_admin_display_form_submit($form, &$form_state) { - foreach ($form_state['values']['blocks'] as $block) { - $config = config($block['config_id']); - $config->set('weight', $block['weight']); - $config->set('region', $block['region']); - $config->save(); - } - drupal_set_message(t('The block settings have been updated.')); - cache_invalidate_tags(array('content' => TRUE)); -} - -/** - * Sorts active blocks by region, then by weight; sorts inactive blocks by name. + * @return array + * A render array for a page containing a list of blocks. * - * Callback for usort() in block_admin_display_prepare_blocks(). + * @see block_menu() */ -function _block_compare($ainstance, $binstance) { - global $theme_key; - $a = $ainstance->getConfig(); - $b = $binstance->getConfig(); - - // Theme should be set before calling this function, or the current theme - // is being used. - $theme = &drupal_static(__FUNCTION__ . ':theme'); - if (!isset($theme)) { - $theme = $theme_key; - } - - $regions = &drupal_static(__FUNCTION__ . ':regions'); - // We need the region list to correctly order by region. - if (!isset($regions)) { - $regions = array_flip(array_keys(system_region_list($theme))); - $regions[BLOCK_REGION_NONE] = count($regions); - } - - // Separate enabled from disabled. - $status = $b['status'] - $a['status']; - if ($status) { - return $status; - } - // Sort by region (in the order defined by theme .info file). - if ((!empty($a['region']) && !empty($b['region'])) && ($place = ($regions[$a['region']] - $regions[$b['region']]))) { - return $place; - } - // Sort by weight, unless disabled. - if ($a['region'] != BLOCK_REGION_NONE) { - $weight = $a['weight'] - $b['weight']; - if ($weight) { - return $weight; - } - } - // Sort by title. - $ainfo = $ainstance->getDefinition(); - $binfo = $binstance->getDefinition(); - return strcmp($ainfo['subject'], $binfo['subject']); +function block_admin_display($theme) { + return entity_list_controller('block')->render($theme); } /** - * Form constructor for the block instance configuration form. + * Page callback: Build the block instance add form. * * @param string $plugin_id * The plugin ID for the block instance. * @param string $theme - * (optional) The name of the theme for the block instance. If no theme is - * specified, the default theme will be used. - * - * @see block_menu() - * @see custom_block_menu() - * @see block_admin_configure_validate() - * @see block_admin_configure_submit() + * The name of the theme for the block instance. * - * @ingroup forms + * @return array + * The block instance edit form. */ -function block_admin_configure($form, &$form_state, $plugin_id, $theme = NULL) { - $instance = block_load($plugin_id); - $form['#instance'] = $instance; - $config = $instance->getConfig(); - if (!isset($config['config_id']) && !$theme) { - $theme = variable_get('theme_default', 'stark'); - } - elseif (!$theme && isset($config['config_id'])) { - list(, , , $theme) = explode('.', $config['config_id']); - } - $form['theme'] = array( - '#type' => 'value', - '#value' => $theme, - ); - $form += $instance->form($form, $form_state); - return $form; +function block_admin_add($plugin_id, $theme) { + $entity = entity_create('block', array( + 'plugin' => $plugin_id, + 'theme' => $theme, + )); + return entity_get_form($entity); } /** - * Form validation handler for block_admin_configure(). + * Page callback: Build the block instance edit form. * - * @see block_admin_configure() - * @see block_admin_configure_submit() - */ -function block_admin_configure_validate($form, &$form_state) { - $form['#instance']->validate($form, $form_state); -} - -/** - * Form submission handler for block_admin_configure(). + * @param \Drupal\block\Plugin\Core\Entity\Block $entity + * The block instance. * - * @see block_admin_configure() - * @see block_admin_configure_validate() + * @return array + * The block instance edit form. */ -function block_admin_configure_submit($form, &$form_state) { - $form['#instance']->submit($form, $form_state); - $config_values = $form['#instance']->getConfig(); - $machine_name = 'plugin.core.block.' . $form_state['values']['theme'] . '.' . $form_state['values']['machine_name']; - $config = config($machine_name); - $config->set('id', $form['#instance']->getPluginId()); - foreach ($config_values as $key => $value) { - $config->set($key, $value); +function block_admin_edit(Block $entity) { + // Get the theme for the page title. + $admin_theme = config('system.theme')->get('admin'); + $themes = list_themes(); + $theme_key = $entity->get('theme'); + $theme = $themes[$theme_key]; + // Use meaningful titles for the main site and administrative themes. + $theme_title = $theme->info['name']; + if ($theme_key == variable_get('theme_default', 'stark')) { + $theme_title = t('!theme (default theme)', array('!theme' => $theme_title)); + } + elseif ($admin_theme && $theme_key == $admin_theme) { + $theme_title = t('!theme (administration theme)', array('!theme' => $theme_title)); } - $config->save(); - drupal_set_message(t('The block configuration has been saved.')); - cache_invalidate_tags(array('content' => TRUE)); - $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $form_state['values']['theme']; + + // Get the block subject for the page title. + drupal_set_title(t("%label block in %theme", array('%label' => $entity->label(), '%theme' => $theme_title)), PASS_THROUGH); + + return entity_get_form($entity); } /** * Form constructor for the block instance deletion form. * - * @param string $plugin_id - * The plugin ID for the block instance. - * @param string $theme - * The name of the theme for the block instance. + * @param \Drupal\block\Plugin\Core\Entity\Block $entity + * The block instance. * * @see block_menu() * @see block_admin_block_delete_submit() */ -function block_admin_block_delete($form, &$form_state, $plugin_id, $theme) { - $block = block_load($plugin_id); - $form['id'] = array('#type' => 'value', '#value' => $plugin_id); - $form['theme'] = array('#type' => 'value', '#value' => $theme); - $definition = $block->getDefinition(); - $config = $block->getConfig(); - $subject = empty($config['subject']) ? $definition['subject'] : $config['subject']; - $form['subject'] = array('#type' => 'value', '#value' => $subject); +function block_admin_block_delete(array $form, array &$form_state, Block $entity) { + $form['id'] = array('#type' => 'value', '#value' => $entity->id()); - return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $subject)), 'admin/structure/block', '', t('Delete'), t('Cancel')); + return confirm_form($form, t('Are you sure you want to delete the block %name?', array('%name' => $entity->label())), 'admin/structure/block', '', t('Delete'), t('Cancel')); } /** @@ -330,10 +102,10 @@ function block_admin_block_delete($form, &$form_state, $plugin_id, $theme) { * @see block_admin_block_delete() */ function block_admin_block_delete_submit($form, &$form_state) { - $config = config($form_state['values']['id']); - $config->delete(); - drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['subject']))); - $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $form_state['values']['theme']; + $entity = entity_load('block', $form_state['values']['id']); + drupal_set_message(t('The block %name has been removed.', array('%name' => $entity->label()))); + $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $entity->get('theme'); + $entity->delete(); } /** diff --git a/core/modules/block/block.api.php b/core/modules/block/block.api.php index defde15..c889e68 100644 --- a/core/modules/block/block.api.php +++ b/core/modules/block/block.api.php @@ -29,7 +29,7 @@ * @see hook_block_view_ID_alter() * @see hook_block_view_NAME_alter() */ -function hook_block_view_alter(array &$build, \Drupal\block\BlockInterface $block) { +function hook_block_view_alter(array &$build, \Drupal\block\Plugin\Core\Entity\Block $block) { // Remove the contextual links on all blocks that provide them. if (is_array($build) && isset($build['#contextual_links'])) { unset($build['#contextual_links']); diff --git a/core/modules/block/block.module b/core/modules/block/block.module index d9155c3..b6a5381 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -121,22 +121,29 @@ function block_menu() { 'access arguments' => array('administer blocks'), 'file' => 'block.admin.inc', ); - $items['admin/structure/block/manage/%/%'] = array( + $items['admin/structure/block/add/%/%'] = array( 'title' => 'Configure block', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_admin_configure', 4, 5), + 'page callback' => 'block_admin_add', + 'page arguments' => array(4, 5), + 'access arguments' => array('administer blocks'), + 'file' => 'block.admin.inc', + ); + $items['admin/structure/block/manage/%block'] = array( + 'title' => 'Configure block', + 'page callback' => 'block_admin_edit', + 'page arguments' => array(4), 'access arguments' => array('administer blocks'), 'file' => 'block.admin.inc', ); - $items['admin/structure/block/manage/%/%/configure'] = array( + $items['admin/structure/block/manage/%block/configure'] = array( 'title' => 'Configure block', 'type' => MENU_DEFAULT_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, ); - $items['admin/structure/block/manage/%/%/delete'] = array( + $items['admin/structure/block/manage/%block/delete'] = array( 'title' => 'Delete block', 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_admin_block_delete', 4, 5), + 'page arguments' => array('block_admin_block_delete', 4), 'access arguments' => array('administer blocks'), 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_NONE, @@ -306,27 +313,24 @@ function _block_get_renderable_region($list = array()) { !in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')); foreach ($list as $key => $block) { - $config = $block->getConfig(); - $definition = $block->getDefinition(); - $build[$key] = array( - '#block' => $block, - '#weight' => (int) $config['weight'], - '#theme_wrappers' => array('block'), - ); - - if ($not_cacheable || in_array($config['cache'], array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) { - // Non-cached blocks get built immediately. Provides more content - // that can be easily manipulated during hook_page_alter(). - $build[$key] = _block_get_renderable_block($build[$key]); + $settings = $block->get('settings'); + if ($not_cacheable || in_array($settings['cache'], array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) { + // Non-cached blocks get built immediately. + if ($block->access()) { + $build[$key] = entity_view($block, 'block'); + } } else { $key_components = explode('.', $key); $id = array_pop($key_components); - $build[$key] += array( + $build[$key] = array( + '#block' => $block, + '#weight' => $block->get('weight'), + '#theme_wrappers' => array('block'), '#pre_render' => array('_block_get_renderable_block'), '#cache' => array( - 'keys' => array($id, $config['module']), - 'granularity' => $config['cache'], + 'keys' => array($id, $block->get('module')), + 'granularity' => $settings['cache'], 'bin' => 'block', 'tags' => array('content' => TRUE), ), @@ -338,9 +342,8 @@ function _block_get_renderable_region($list = array()) { // skip the help block, since we assume that most users do not need or want // to perform contextual actions on the help block, and the links needlessly // draw attention on it. - if ($definition['class'] != 'Drupal\\system\\Plugin\\block\\block\\SystemHelpBlock' && $definition['class'] != 'Drupal\\system\\Plugin\\block\\block\\SystemMainBlock') { - global $theme; - $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($key, $theme)); + if (!in_array($block->get('plugin'), array('system_help_block', 'system_main_block'))) { + $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($key)); } } return $build; @@ -357,33 +360,25 @@ function _block_get_renderable_region($list = array()) { * Blocks currently exported by modules. */ function _block_rehash($theme = NULL) { - $blocks = array(); - $instances = array(); $theme = $theme ? $theme : variable_get('theme_default', 'stark'); - $block_configs = config_get_storage_names_with_prefix('plugin.core.block.' . $theme); $regions = system_region_list($theme); - foreach ($block_configs as $config) { - // Only list valid block instances. - if (!$block = block_load($config)) { - continue; - } - $blocks[$config] = $block; - $config = config($config); - $region = $config->get('region'); - $status = $config->get('status'); + $blocks = entity_load_multiple_by_properties('block', array('theme' => $theme)); + foreach ($blocks as $block_id => $block) { + $region = $block->get('region'); + $status = $block->get('status'); // Disable blocks in invalid regions. if (!empty($region) && $region != BLOCK_REGION_NONE && !isset($regions[$region]) && $status == 1) { - drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $config->get('id'), '%region' => $region)), 'warning'); + drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block_id, '%region' => $region)), 'warning'); // Disabled modules are moved into the BLOCK_REGION_NONE later so no // need to move the block to another region. - $config->set('status', 0); - $config->save(); + $block->set('status', 0); + $block->save(); } // Set region to none if not enabled and make sure status is set. if (empty($status)) { - $config->set('region', BLOCK_REGION_NONE); - $config->set('status', 0); - $config->save(); + $block->set('region', BLOCK_REGION_NONE); + $block->set('status', 0); + $block->save(); } } return $blocks; @@ -414,28 +409,22 @@ function block_themes_enabled($theme_list) { */ function block_theme_initialize($theme) { // Initialize theme's blocks if none already registered. - $has_blocks = config_get_storage_names_with_prefix('plugin.core.block.' . $theme); + $has_blocks = entity_load_multiple_by_properties('block', array('theme' => $theme)); if (!$has_blocks) { $default_theme = variable_get('theme_default', 'stark'); // Apply only to new theme's visible regions. $regions = system_region_list($theme, REGIONS_VISIBLE); - $default_theme_blocks = config_get_storage_names_with_prefix('plugin.core.block.' . $default_theme); - foreach ($default_theme_blocks as $config_id) { - $block_config = config($config_id)->get(); - $machine_name = explode('.', $config_id); - $machine_name = array_pop($machine_name); - $new_config_id = 'plugin.core.block.' . $theme . '.' . $machine_name; - $new_block = config($new_config_id); + $default_theme_blocks = entity_load_multiple_by_properties('block', array('theme' => $default_theme)); + foreach ($default_theme_blocks as $default_theme_block_id => $default_theme_block) { + list(, $machine_name) = explode('.', $default_theme_block_id); + $block = $default_theme_block->createDuplicate(); + $block->set('id', $theme . '.' . $machine_name); // If the region isn't supported by the theme, assign the block to the // theme's default region. - if (!isset($regions[$block_config['region']])) { - $new_block->set('region', system_default_region($theme)); - unset($block_config['region']); - } - foreach ($block_config as $key => $value) { - $new_block->set($key, $value); + if (!isset($regions[$block->get('region')])) { + $block->set('region', system_default_region($theme)); } - $new_block->save(); + $block->save(); } } } @@ -456,7 +445,11 @@ function block_list($region) { $blocks = &drupal_static(__FUNCTION__); if (!isset($blocks)) { - $blocks = _block_load_blocks(); + global $theme; + $blocks = array(); + foreach (entity_load_multiple_by_properties('block', array('theme' => $theme)) as $block_id => $block) { + $blocks[$block->get('region')][$block_id] = $block; + } } // Create an empty array if there are no entries. @@ -470,57 +463,16 @@ function block_list($region) { /** * Loads a block instance. * - * @param string $plugin_id - * The plugin ID to load. - * @param array $conf - * An optional configuration array for creating a block instance manually - * rather than retrieving it from the configuration system. + * This should only be used when entity_load() cannot be used directly. * - * @return - * A block object. + * @param string $entity_id + * The block ID. * - * @todo Add block_load_multiple() and make this function a single-value wrapper. + * @return \Drupal\block\Plugin\Core\Entity\Block + * The loaded block object. */ -function block_load($plugin_id, array $conf = array()) { - $manager = drupal_container()->get('plugin.manager.block'); - if (!$block = $manager->getInstance(array('config' => $plugin_id))) { - // If the block instance does not exist, try to create it. - try { - $block = $manager->createInstance($plugin_id, $conf); - } - catch (PluginException $e) { - $config = config($plugin_id)->get(); - // Ignore blocks belonging to disabled modules, but re-throw valid - // exceptions when the module is enabled and the plugin is misconfigured. - if (empty($config['module']) || module_exists($config['module'])) { - throw $e; - } - return FALSE; - } - } - return $block; -} - -/** - * Loads blocks' information from the configuration management system. - * - * @return - * An array of blocks grouped by region. - * - * @todo Remove this function, and replace it with a block_load_multiple(). - */ -function _block_load_blocks() { - global $theme; - $blocks = array(); - $instances = config_get_storage_names_with_prefix('plugin.core.block.' . $theme); - $manager = drupal_container()->get('plugin.manager.block'); - foreach ($instances as $plugin_id) { - if ($block = block_load($plugin_id)) { - $config = $block->getConfig(); - $blocks[$config['region']]["$plugin_id"] = $block; - } - } - return $blocks; +function block_load($entity_id) { + return entity_load('block', $entity_id); } /** @@ -538,17 +490,7 @@ function _block_get_renderable_block($element) { $block = $element['#block']; // Don't bother to build blocks that aren't accessible. if ($element['#access'] = $block->access()) { - $build = $block->build(); - if ($build) { - if (isset($build['#title'])) { - $element['#title'] = $build['#title']; - } - $element += $build; - } - else { - // @todo Add an inline comment explaining why this line is necessary. - $element = array(); - } + $element += entity_view($block, 'block'); } return $element; } @@ -590,10 +532,9 @@ function block_rebuild() { */ function template_preprocess_block(&$variables) { $block_counter = &drupal_static(__FUNCTION__, array()); - $variables['block'] = (object) array_merge($variables['elements']['#block']->getDefinition(), $variables['elements']['#block']->getConfig()); - if (!empty($variables['elements']['#title']) && empty($variables['block']->subject)) { - $variables['block']->subject = $variables['elements']['#title']; - } + + $variables['block'] = (object) $variables['elements']['#block_config']; + // All blocks get an independent counter for each region. if (!isset($block_counter[$variables['block']->region])) { $block_counter[$variables['block']->region] = 1; @@ -623,14 +564,14 @@ function template_preprocess_block(&$variables) { // We can safely explode on : because we know the Block plugin type manager // enforces that delimiter for all derivatives. - $parts = explode(':', $variables['elements']['#block']->getPluginId()); + $parts = explode(':', $variables['elements']['#block']->get('plugin')); $suggestion = 'block'; while ($part = array_shift($parts)) { $variables['theme_hook_suggestions'][] = $suggestion .= '__' . strtr($part, '-', '_'); } // Create a valid HTML ID and make sure it is unique. - if (!empty($variables['block']->config_id)) { - $config_id = explode('.', $variables['block']->config_id); + if ($id = $variables['elements']['#block']->id()) { + $config_id = explode('.', $id); $machine_name = array_pop($config_id); $variables['block_html_id'] = drupal_html_id('block-' . $machine_name); $variables['theme_hook_suggestions'][] = 'block__' . $machine_name; @@ -643,14 +584,12 @@ function template_preprocess_block(&$variables) { * Removes deleted role from blocks that use it. */ function block_user_role_delete($role) { - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - $roles = $config->get('visibility.role.roles'); - if (isset($roles[$role->rid])) { - unset($roles[$role->rid]); - $config->set('visibility.role.roles', $roles); - $config->save(); + foreach (entity_load_multiple('block') as $block_id => $block) { + $visibility = $block->get('visibility'); + if (isset($visibility['roles']['roles'][$role->rid])) { + unset($visibility['roles']['roles'][$role->rid]); + $block->set('visibility', $visibility); + $block->save(); } } } @@ -659,11 +598,9 @@ function block_user_role_delete($role) { * Implements hook_menu_delete(). */ function block_menu_delete($menu) { - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - if ($config->get('id') == 'menu_menu_block:' . $menu->id()) { - $config->delete(); + foreach (entity_load_multiple('block') as $block_id => $block) { + if ($block->get('plugin') == 'menu_menu_block:' . $menu->id()) { + $block->delete(); } } } @@ -681,35 +618,18 @@ function block_admin_paths() { } /** - * Implements hook_modules_uninstalled(). - * - * Cleans up any block configuration for uninstalled modules. - */ -function block_modules_uninstalled($modules) { - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - if (in_array($config->get('module'), $modules)) { - $config->delete(); - } - } -} - -/** * Implements hook_language_delete(). * * Delete the potential block visibility settings of the deleted language. */ function block_language_delete($language) { // Remove the block visibility settings for the deleted language. - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - $languages = $config->get('visibility.language.langcodes'); - if (isset($languages[$language->langcode])) { - unset($languages[$language->langcode]); - $config->set('visibility.language.langcodes', $languages); - $config->save(); + foreach (entity_load_multiple('block') as $block_id => $block) { + $visibility = $block->get('visibility'); + if (isset($visibility['language']['langcodes'][$language->langcode])) { + unset($visibility['language']['langcodes'][$language->langcode]); + $block->set('visibility', $visibility); + $block->save(); } } } diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module index d3e1588..8c7fdea 100644 --- a/core/modules/block/custom_block/custom_block.module +++ b/core/modules/block/custom_block/custom_block.module @@ -19,8 +19,8 @@ function custom_block_menu() { $items['admin/structure/block/list/' . $plugin_id . '/add/custom_blocks'] = array( 'title' => 'Add custom block', 'description' => 'Create a block with custom content and settings.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('block_admin_configure', 'custom_block:custom_block', $theme), + 'page callback' => 'block_admin_add', + 'page arguments' => array('custom_block:custom_block', $theme), 'access callback' => TRUE, 'type' => MENU_LOCAL_ACTION, 'file' => 'block.admin.inc', diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php index 8b142ef..6f7be2e 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/block/block/CustomBlock.php @@ -29,17 +29,6 @@ class CustomBlock extends BlockBase { /** - * Overrides \Drupal\block\BlockBase::blockSettings(). - */ - public function blockSettings() { - // By default, use a blank block title rather than the block description - // (which is "Custom Block"). - return array( - 'subject' => '', - ); - } - - /** * Overrides \Drupal\block\BlockBase::getConfig(). */ public function getConfig() { @@ -60,7 +49,7 @@ public function getConfig() { public function blockForm($form, &$form_state) { // @todo Disable this field when editing an existing block and provide a // separate interface for administering custom blocks. - $form['custom_block']['info'] = array( + $form['info'] = array( '#type' => 'textfield', '#title' => t('Block description'), '#required' => TRUE, @@ -69,7 +58,7 @@ public function blockForm($form, &$form_state) { ); // @todo Disable this field when editing an existing block and provide a // separate interface for administering custom blocks. - $form['custom_block']['body'] = array( + $form['body'] = array( '#type' => 'text_format', '#title' => t('Block body'), '#default_value' => $this->configuration['body'], @@ -78,11 +67,25 @@ public function blockForm($form, &$form_state) { '#rows' => 15, '#required' => TRUE, ); - $form['custom_block']['title']['#description'] = t('The title of the block as shown to the user.'); + $form['title']['#description'] = t('The title of the block as shown to the user.'); return $form; } /** + * Overrides \Drupal\block\BlockBase::blockValidate(). + */ + public function blockValidate($form, &$form_state) { + list(, $bid) = explode(':', $form_state['entity']->get('plugin')); + $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array( + ':bid' => $bid, + ':info' => $form_state['values']['info'], + ))->fetchField(); + if (empty($form_state['values']['info']) || $custom_block_exists) { + form_set_error('info', t('Ensure that each block description is unique.')); + } + } + + /** * Overrides \Drupal\block\BlockBase::blockSubmit(). */ public function blockSubmit($form, &$form_state) { @@ -94,7 +97,7 @@ public function blockSubmit($form, &$form_state) { 'bid' => is_numeric($bid) ? $bid : NULL, ); drupal_write_record('block_custom', $block, !is_null($block['bid']) ? array('bid') : array()); - $this->configuration['id'] = 'custom_block:' . $block['bid']; + $form_state['entity']->set('plugin', 'custom_block:' . $block['bid']); // Invalidate the block cache to update custom block-based derivatives. if (module_exists('block')) { drupal_container()->get('plugin.manager.block')->clearCachedDefinitions(); diff --git a/core/modules/block/lib/Drupal/block/BlockAccessController.php b/core/modules/block/lib/Drupal/block/BlockAccessController.php new file mode 100644 index 0000000..4bf4478 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockAccessController.php @@ -0,0 +1,26 @@ +getPlugin()->access($entity); + } + +} diff --git a/core/modules/block/lib/Drupal/block/BlockBase.php b/core/modules/block/lib/Drupal/block/BlockBase.php index 7d1356b..1b1fd9e 100644 --- a/core/modules/block/lib/Drupal/block/BlockBase.php +++ b/core/modules/block/lib/Drupal/block/BlockBase.php @@ -8,6 +8,8 @@ namespace Drupal\block; use Drupal\Component\Plugin\PluginBase; +use Drupal\block\Plugin\Core\Entity\Block; +use Drupal\Component\Plugin\Discovery\DiscoveryInterface; /** * Defines a base block implementation that most blocks plugins will extend. @@ -19,22 +21,19 @@ abstract class BlockBase extends PluginBase implements BlockInterface { /** - * Implements \Drupal\block\BlockInterface::settings(). + * The entity using this plugin. * - * Most block plugins should not override this method. To add additional - * settings or change the default values for setting, override - * BlockBase::blockSettings(). - * - * @see \Drupal\block\BlockBase::blockSettings() + * @var \Drupal\block\Plugin\Core\Entity\Block */ - public function settings() { - $settings = $this->blockSettings(); - // By default, blocks are enabled and not cached. - $settings += array( - 'status' => TRUE, - 'cache' => DRUPAL_NO_CACHE, - ); - return $settings; + protected $entity; + + /** + * Overrides \Drupal\Component\Plugin\PluginBase::__construct(). + */ + public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery, Block $entity) { + parent::__construct($configuration, $plugin_id, $discovery); + + $this->entity = $entity; } /** @@ -69,7 +68,7 @@ public function getConfig() { if (empty($this->configuration)) { // If the plugin configuration is not already set, initialize it with the // default settings for the block plugin. - $this->configuration = $this->settings(); + $this->configuration = $this->blockSettings(); // @todo This loads the default subject. Is this the right place to do so? $definition = $this->getDefinition(); @@ -77,6 +76,8 @@ public function getConfig() { $this->configuration += array('subject' => $definition['subject']); } } + // Ensure that the default cache mode is set. + $this->configuration += array('cache' => DRUPAL_NO_CACHE); return $this->configuration; } @@ -142,7 +143,7 @@ public function access() { global $user; // Deny access to disabled blocks. - if (empty($this->configuration['status'])) { + if (!$this->entity->get('status')) { return FALSE; } @@ -150,26 +151,27 @@ public function access() { // If a block has no roles associated, it is displayed for every role. // For blocks with roles associated, if none of the user's roles matches // the settings from this block, access is denied. - if (!empty($this->configuration['visibility']['role']['roles']) && !array_intersect(array_filter($this->configuration['visibility']['role']['roles']), array_keys($user->roles))) { + $visibility = $this->entity->get('visibility'); + if (!empty($visibility['role']['roles']) && !array_intersect(array_filter($visibility['role']['roles']), array_keys($user->roles))) { // No match. return FALSE; } // Page path handling. // Limited visibility blocks must list at least one page. - if (!empty($this->configuration['visibility']['path']['visibility']) && $this->configuration['visibility']['path']['visibility'] == BLOCK_VISIBILITY_LISTED && empty($this->configuration['visibility']['path']['pages'])) { + if (!empty($visibility['path']['visibility']) && $visibility['path']['visibility'] == BLOCK_VISIBILITY_LISTED && empty($visibility['path']['pages'])) { return FALSE; } // Match path if necessary. - if (!empty($this->configuration['visibility']['path']['pages'])) { + if (!empty($visibility['path']['pages'])) { // Assume there are no matches until one is found. $page_match = FALSE; // Convert path to lowercase. This allows comparison of the same path // with different case. Ex: /Page, /page, /PAGE. - $pages = drupal_strtolower($this->configuration['visibility']['path']['pages']); - if ($this->configuration['visibility']['path']['visibility'] < BLOCK_VISIBILITY_PHP) { + $pages = drupal_strtolower($visibility['path']['pages']); + if ($visibility['path']['visibility'] < BLOCK_VISIBILITY_PHP) { // Compare the lowercase path alias (if any) and internal path. $path = current_path(); $path_alias = drupal_strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path)); @@ -179,10 +181,10 @@ public function access() { // except those listed in $block->pages. When set to 1 // (BLOCK_VISIBILITY_LISTED), it is displayed only on those pages // listed in $block->pages. - $page_match = !($this->configuration['visibility']['path']['visibility'] xor $page_match); + $page_match = !($visibility['path']['visibility'] xor $page_match); } elseif (module_exists('php')) { - $page_match = php_eval($this->configuration['visibility']['path']['pages']); + $page_match = php_eval($visibility['path']['pages']); } // If there are page visibility restrictions and this page does not @@ -193,15 +195,15 @@ public function access() { } // Language visibility settings. - if (!empty($this->configuration['visibility']['language']['langcodes']) && array_filter($this->configuration['visibility']['language']['langcodes'])) { - if (empty($this->configuration['visibility']['language']['langcodes'][language($this->configuration['visibility']['language']['language_type'])->langcode])) { + if (!empty($visibility['language']['langcodes']) && array_filter($visibility['language']['langcodes'])) { + if (empty($visibility['language']['langcodes'][language($visibility['language']['language_type'])->langcode])) { return FALSE; } } // Check other modules for block access rules. foreach (module_implements('block_access') as $module) { - if (module_invoke($module, 'block_access', $this) === FALSE) { + if (module_invoke($module, 'block_access', $this->entity) === FALSE) { return FALSE; } } @@ -221,71 +223,43 @@ public function access() { * @see \Drupal\block\BlockBase::blockForm() */ public function form($form, &$form_state) { + $entity = $form_state['entity']; $definition = $this->getDefinition(); - $config = $this->getConfig(); $form['id'] = array( '#type' => 'value', - '#value' => $definition['id'], + '#value' => $entity->id(), ); $form['module'] = array( '#type' => 'value', '#value' => $definition['module'], ); - // Get the block subject for the page title. - $subject = isset($config['subject']) ? $config['subject'] : ''; - - // Get the theme for the page title. - $theme_default = variable_get('theme_default', 'stark'); - $admin_theme = config('system.theme')->get('admin'); - $themes = list_themes(); - $theme_key = $form['theme']['#value']; - $theme = $themes[$theme_key]; - // Use meaningful titles for the main site and administrative themes. - $theme_title = $theme->info['name']; - if ($theme_key == $theme_default) { - $theme_title = t('!theme (default theme)', array('!theme' => $theme_title)); - } - elseif ($admin_theme && $theme_key == $admin_theme) { - $theme_title = t('!theme (administration theme)', array('!theme' => $theme_title)); - } - - if ($subject) { - drupal_set_title(t("%subject block in %theme", array('%subject' => $subject, '%theme' => $theme_title)), PASS_THROUGH); - } - - $form['settings'] = array( - '#weight' => -5, - ); - $form['settings']['title'] = array( + $form['label'] = array( '#type' => 'textfield', '#title' => t('Block title'), '#maxlength' => 255, - '#default_value' => isset($subject) ? $subject : '', + '#default_value' => !$entity->isNew() ? $entity->label() : $definition['subject'], ); - $form['settings']['machine_name'] = array( + $form['machine_name'] = array( '#type' => 'textfield', '#title' => t('Block machine name'), '#maxlength' => 64, '#description' => t('A unique name to save this block configuration. Must be alpha-numeric and be underscore separated.'), - '#default_value' => isset($config['config_id']) ? $config['config_id'] : '', + '#default_value' => $entity->id(), '#required' => TRUE, + '#disabled' => !$entity->isNew(), ); - if (isset($config['config_id'])) { - $form['settings']['machine_name']['#disabled'] = TRUE; - } // Region settings. $form['region'] = array( '#type' => 'select', '#title' => t('Region'), '#description' => t('Select the region where this block should be displayed.'), - '#default_value' => !empty($config['region']) && $config['region'] != -1 ? $config['region'] : NULL, + '#default_value' => $entity->get('region'), '#empty_value' => BLOCK_REGION_NONE, - '#options' => system_region_list($theme_key, REGIONS_VISIBLE), + '#options' => system_region_list($entity->get('theme'), REGIONS_VISIBLE), ); - // Visibility settings. $form['visibility'] = array( '#type' => 'vertical_tabs', @@ -309,15 +283,16 @@ public function form($form, &$form_state) { // @todo remove this access check and inject it in some other way. In fact // this entire visibility settings section probably needs a separate user // interface in the near future. + $visibility = $entity->get('visibility'); $access = user_access('use PHP for settings'); - if (!empty($config['visibility']['path']['visibility']) && $config['visibility']['path']['visibility'] == BLOCK_VISIBILITY_PHP && !$access) { + if (!empty($visibility['path']['visibility']) && $visibility['path']['visibility'] == BLOCK_VISIBILITY_PHP && !$access) { $form['visibility']['path']['visibility'] = array( '#type' => 'value', '#value' => BLOCK_VISIBILITY_PHP, ); $form['visibility']['path']['pages'] = array( '#type' => 'value', - '#value' => !empty($config['visibility']['path']['pages']) ? $config['visibility']['path']['pages'] : '', + '#value' => !empty($visibility['path']['pages']) ? $visibility['path']['pages'] : '', ); } else { @@ -339,12 +314,12 @@ public function form($form, &$form_state) { '#type' => 'radios', '#title' => t('Show block on specific pages'), '#options' => $options, - '#default_value' => !empty($this->configuration['visibility']['path']['visibility']) ? $this->configuration['visibility']['path']['visibility'] : BLOCK_VISIBILITY_NOTLISTED, + '#default_value' => !empty($visibility['path']['visibility']) ? $visibility['path']['visibility'] : BLOCK_VISIBILITY_NOTLISTED, ); $form['visibility']['path']['pages'] = array( '#type' => 'textarea', '#title' => '' . $title . '', - '#default_value' => !empty($this->configuration['visibility']['path']['pages']) ? $this->configuration['visibility']['path']['pages'] : '', + '#default_value' => !empty($visibility['path']['pages']) ? $visibility['path']['pages'] : '', '#description' => $description, ); } @@ -379,13 +354,13 @@ public function form($form, &$form_state) { '#type' => 'radios', '#title' => t('Language type'), '#options' => $language_type_options, - '#default_value' => !empty($this->configuration['visibility']['language']['language_type']) ? $this->configuration['visibility']['language']['language_type'] : $configurable_language_types[0], + '#default_value' => !empty($visibility['language']['language_type']) ? $visibility['language']['language_type'] : $configurable_language_types[0], '#access' => count($language_type_options) > 1, ); $form['visibility']['language']['langcodes'] = array( '#type' => 'checkboxes', '#title' => t('Show this block only for specific languages'), - '#default_value' => !empty($this->configuration['visibility']['language']['langcodes']) ? $this->configuration['visibility']['language']['langcodes'] : array(), + '#default_value' => !empty($visibility['language']['langcodes']) ? $visibility['language']['langcodes'] : array(), '#options' => $langcodes_options, '#description' => t('Show this block only for the selected language(s). If you select no languages, the block will be visibile in all languages.'), ); @@ -403,20 +378,13 @@ public function form($form, &$form_state) { $form['visibility']['role']['roles'] = array( '#type' => 'checkboxes', '#title' => t('Show block for specific roles'), - '#default_value' => !empty($this->configuration['visibility']['role']['roles']) ? $this->configuration['visibility']['role']['roles'] : array(), + '#default_value' => !empty($visibility['role']['roles']) ? $visibility['role']['roles'] : array(), '#options' => $role_options, '#description' => t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'), ); - // Add specific configuration for this block type. - $form += $this->blockForm($form, $form_state); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save block'), - ); - + // Add plugin-specific settings for this block type. + $form['settings'] = $this->blockForm(array(), $form_state); return $form; } @@ -451,30 +419,19 @@ public function blockForm($form, &$form_state) { * @see \Drupal\block\BlockBase::blockValidate() */ public function validate($form, &$form_state) { - if (empty($form['settings']['machine_name']['#disabled'])) { + if (empty($form['machine_name']['#disabled'])) { if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['machine_name'])) { form_set_error('machine_name', t('Block name must be alphanumeric or underscores only.')); } - if (in_array('plugin.core.block.' . $form_state['values']['machine_name'], config_get_storage_names_with_prefix('plugin.core.block'))) { - form_set_error('machine_name', t('Block name must be unique.')); - } } else { $config_id = explode('.', $form_state['values']['machine_name']); $form_state['values']['machine_name'] = array_pop($config_id); } - if ($form_state['values']['module'] == 'block') { - $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE bid <> :bid AND info = :info', 0, 1, array( - ':bid' => $form_state['values']['delta'], - ':info' => $form_state['values']['info'], - ))->fetchField(); - if (empty($form_state['values']['info']) || $custom_block_exists) { - form_set_error('info', t('Ensure that each block description is unique.')); - } - } $form_state['values']['visibility']['role']['roles'] = array_filter($form_state['values']['visibility']['role']['roles']); - - // Perform block type-specific validation. + if ($form_state['entity']->isNew()) { + form_set_value($form['id'], $form_state['entity']->get('theme') . '.' . $form_state['values']['machine_name'], $form_state); + } $this->blockValidate($form, $form_state); } @@ -508,32 +465,11 @@ public function blockValidate($form, &$form_state) {} */ public function submit($form, &$form_state) { if (!form_get_errors()) { - $transaction = db_transaction(); - try { - $keys = array( - 'visibility' => 'visibility', - 'pages' => 'pages', - 'title' => 'subject', - 'module' => 'module', - 'region' => 'region', - ); - foreach ($keys as $key => $new_key) { - if (isset($form_state['values'][$key])) { - $this->configuration[$new_key] = $form_state['values'][$key]; - } - } - } - catch (Exception $e) { - $transaction->rollback(); - watchdog_exception('block', $e); - throw $e; - } - if (empty($this->configuration['weight'])) { - $this->configuration['weight'] = 0; - } - - // Perform block type-specific validation. $this->blockSubmit($form, $form_state); + + drupal_set_message(t('The block configuration has been saved.')); + cache_invalidate_tags(array('content' => TRUE)); + $form_state['redirect'] = 'admin/structure/block/list/block_plugin_ui:' . $form_state['entity']->get('theme'); } } @@ -555,46 +491,4 @@ public function submit($form, &$form_state) { */ public function blockSubmit($form, &$form_state) {} - /** - * Implements \Drupal\block\BlockInterface::build(). - * - * Allows blocks to be altered after they are built. - * - * Most block plugins should not override this method. To define how a - * particular block is rendered, implement the abstract method - * BlockBase::blockBuild(). - * - * @return array $build - * A renderable array of data. - * - #title: The default localized title of the block. - * - * @todo Add specific examples of $id and $name below. - * - * @see \Drupal\block\BlockBase::blockBuild() - */ - public function build() { - // Allow modules to modify the block before it is viewed, via either - // hook_block_view_alter(), hook_block_view_ID_alter(), or - // hook_block_view_NAME_alter(). - $id = str_replace(':', '__', $this->getPluginId()); - - $config = $this->getConfig(); - $config_id = explode('.', $config['config_id']); - $name = array_pop($config_id); - - $build = $this->blockBuild(); - drupal_alter(array('block_view', "block_view_$id", "block_view_$name"), $build, $this); - return $build; - } - - /** - * Builds the renderable array for a specific block type. - * - * @return array - * A renderable array representing the output of the block. - * - * @see \Drupal\block\BlockBase::build() - */ - abstract public function blockBuild(); - } diff --git a/core/modules/block/lib/Drupal/block/BlockFormController.php b/core/modules/block/lib/Drupal/block/BlockFormController.php new file mode 100644 index 0000000..0ad4d4a --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockFormController.php @@ -0,0 +1,59 @@ +getPlugin()->form($form, $form_state, $entity); + } + + /** + * Overrides \Drupal\Core\Entity\EntityFormController::actions(). + */ + protected function actions(array $form, array &$form_state) { + $actions = parent::actions($form, $form_state); + $actions['submit']['#value'] = t('Save block'); + return $actions; + } + + /** + * Overrides \Drupal\Core\Entity\EntityFormController::validate(). + */ + public function validate(array $form, array &$form_state) { + parent::validate($form, $form_state); + + $entity = $this->getEntity($form_state); + $entity->getPlugin()->validate($form, $form_state); + } + + /** + * Overrides \Drupal\Core\Entity\EntityFormController::submit(). + */ + public function submit(array $form, array &$form_state) { + parent::submit($form, $form_state); + + $entity = $this->getEntity($form_state); + // Call the plugin submit handler. + $entity->getPlugin()->submit($form, $form_state); + + // Save the settings of the plugin. + $entity->set('settings', $entity->getPlugin()->getConfig()); + $entity->save(); + } + +} diff --git a/core/modules/block/lib/Drupal/block/BlockInterface.php b/core/modules/block/lib/Drupal/block/BlockInterface.php index b419b27..ec3c145 100644 --- a/core/modules/block/lib/Drupal/block/BlockInterface.php +++ b/core/modules/block/lib/Drupal/block/BlockInterface.php @@ -1,6 +1,7 @@ theme) { + $this->theme = $GLOBALS['theme']; + } + + // Store the region list. + $this->regions = system_region_list($this->theme, REGIONS_VISIBLE); + + // Load only blocks for this theme, and sort them. + // @todo Move the functionality of _block_rehash() out of the listing page. + $entities = _block_rehash($this->theme); + uasort($entities, 'static::sort'); + return $entities; + } + + /** + * Overrides \Drupal\Core\Entity\EntityListController::render(). + */ + public function render($theme = NULL) { + // If no theme was specified, use the current theme. + $this->theme = $theme ?: $GLOBALS['theme_key']; + + $form_state = array(); + $form_state['build_info']['args'] = array(); + $form_state['build_info']['callback'] = array($this, 'form'); + return drupal_build_form('block_admin_display_form', $form_state); + } + + /** + * Sorts active blocks by region then weight; sorts inactive blocks by name. + */ + protected function sort(Block $a, Block $b) { + static $regions; + // We need the region list to correctly order by region. + if (!isset($regions)) { + $regions = array_flip(array_keys($this->regions)); + $regions[BLOCK_REGION_NONE] = count($regions); + } + + // Separate enabled from disabled. + $status = $b->get('status') - $a->get('status'); + if ($status) { + return $status; + } + // Sort by region (in the order defined by theme .info file). + $aregion = $a->get('region'); + $bregion = $b->get('region'); + if ((!empty($aregion) && !empty($bregion)) && ($place = ($regions[$aregion] - $regions[$bregion]))) { + return $place; + } + // Sort by weight, unless disabled. + if ($a->get('region') != BLOCK_REGION_NONE) { + $weight = $a->get('weight') - $b->get('weight'); + if ($weight) { + return $weight; + } + } + // Sort by label. + return strcmp($a->label(), $b->label()); + } + + /** + * Form constructor for the main block administration form. + */ + public function form($form, &$form_state) { + $entities = $this->load(); + $form['#attached']['css'][] = drupal_get_path('module', 'block') . '/block.admin.css'; + $form['#attached']['library'][] = array('system', 'drupal.tableheader'); + $form['#attached']['library'][] = array('block', 'drupal.block'); + + // Add a last region for disabled blocks. + $block_regions_with_disabled = $this->regions + array(BLOCK_REGION_NONE => BLOCK_REGION_NONE); + + foreach ($block_regions_with_disabled as $region => $title) { + $form['#attached']['drupal_add_tabledrag'][] = array('blocks', 'match', 'sibling', 'block-region-select', 'block-region-' . $region, NULL, FALSE); + $form['#attached']['drupal_add_tabledrag'][] = array('blocks', 'order', 'sibling', 'block-weight', 'block-weight-' . $region); + } + $form['block_regions'] = array( + '#type' => 'value', + '#value' => $block_regions_with_disabled, + ); + + // Weights range from -delta to +delta, so delta should be at least half + // of the amount of blocks present. This makes sure all blocks in the same + // region get an unique weight. + $weight_delta = round(count($entities) / 2); + + // Build the form tree. + $form['edited_theme'] = array( + '#type' => 'value', + '#value' => $this->theme, + ); + $form['blocks'] = array(); + $form['#tree'] = TRUE; + + foreach ($entities as $entity_id => $entity) { + $info = $entity->getPlugin()->getDefinition(); + $form['blocks'][$entity_id]['info'] = array( + '#markup' => check_plain($info['subject']), + ); + $form['blocks'][$entity_id]['theme'] = array( + '#type' => 'hidden', + '#value' => $this->theme, + ); + $form['blocks'][$entity_id]['weight'] = array( + '#type' => 'weight', + '#default_value' => $entity->get('weight'), + '#delta' => $weight_delta, + '#title_display' => 'invisible', + '#title' => t('Weight for @block block', array('@block' => $info['subject'])), + ); + $form['blocks'][$entity_id]['region'] = array( + '#type' => 'select', + '#default_value' => $entity->get('region') != BLOCK_REGION_NONE ? $entity->get('region') : NULL, + '#empty_value' => BLOCK_REGION_NONE, + '#title_display' => 'invisible', + '#title' => t('Region for @block block', array('@block' => $info['subject'])), + '#options' => $this->regions, + ); + $links['configure'] = array( + 'title' => t('configure'), + 'href' => 'admin/structure/block/manage/' . $entity_id . '/configure', + ); + $links['delete'] = array( + 'title' => t('delete'), + 'href' => 'admin/structure/block/manage/' . $entity_id . '/delete', + ); + $form['blocks'][$entity_id]['operations'] = array( + '#type' => 'operations', + '#links' => $links, + ); + } + // Do not allow disabling the main system content block when it is present. + if (isset($form['blocks']['system_main']['region'])) { + $form['blocks']['system_main']['region']['#required'] = TRUE; + } + + $form['actions'] = array( + '#tree' => FALSE, + '#type' => 'actions', + ); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save blocks'), + '#button_type' => 'primary', + '#submit' => array(array($this, 'submit')), + ); + return $form; + } + + /** + * Form submission handler for the main block administration form. + */ + public function submit($form, &$form_state) { + $entities = entity_load_multiple('block', array_keys($form_state['values']['blocks'])); + foreach ($entities as $entity_id => $entity) { + $entity->set('weight', $form_state['values']['blocks'][$entity_id]['weight']); + $entity->set('region', $form_state['values']['blocks'][$entity_id]['region']); + $entity->save(); + } + drupal_set_message(t('The block settings have been updated.')); + cache_invalidate_tags(array('content' => TRUE)); + } + +} diff --git a/core/modules/block/lib/Drupal/block/BlockRenderController.php b/core/modules/block/lib/Drupal/block/BlockRenderController.php new file mode 100644 index 0000000..416c52d --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockRenderController.php @@ -0,0 +1,90 @@ + $entity, + '#weight' => $entity->get('weight'), + '#theme_wrappers' => array('block'), + '#block_config' => array( + 'id' => $entity->get('plugin'), + 'region' => $entity->get('region'), + 'module' => $entity->get('module'), + 'subject' => $entity->label(), + ), + ); + } + + /** + * Implements Drupal\Core\Entity\EntityRenderControllerInterface::view(). + */ + public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { + $build = $this->viewMultiple(array($entity), $view_mode, $langcode); + return reset($build); + } + + /** + * Implements Drupal\Core\Entity\EntityRenderControllerInterface::viewMultiple(). + */ + public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { + $build = array(); + foreach ($entities as $entity_id => $entity) { + $build[$entity_id] = $entity->getPlugin()->blockBuild($entity); + // Allow blocks to be empty, do not add in the defaults. + if (!empty($build[$entity_id])) { + $build[$entity_id] = $this->getBuildDefaults($entity, $view_mode, $langcode) + $build[$entity_id]; + } + + // All blocks, even when empty, should be available for altering. + $id = str_replace(':', '__', $entity->get('plugin')); + list(, $name) = $entity->id(); + drupal_alter(array('block_view', "block_view_$id", "block_view_$name"), $build[$entity_id], $entity); + + } + return $build; + } + +} diff --git a/core/modules/block/lib/Drupal/block/BlockStorageController.php b/core/modules/block/lib/Drupal/block/BlockStorageController.php new file mode 100644 index 0000000..a873c5d --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockStorageController.php @@ -0,0 +1,56 @@ +get('module')) { + $definition = $entity->getPlugin()->getDefinition(); + $entity->set('module', $definition['module']); + } + + return $entity; + } + + /** + * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::load(). + */ + public function load(array $ids = NULL) { + $entities = parent::load($ids); + // Only blocks with a valid plugin should be loaded. + return array_filter($entities, function ($entity) { + return $entity->getPlugin(); + }); + } + + /** + * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::loadByProperties(). + */ + public function loadByProperties(array $values = array()) { + $blocks = $this->load(); + foreach ($values as $key => $value) { + $blocks = array_filter($blocks, function($block) use ($key, $value) { + return $value === $block->get($key); + }); + } + return $blocks; + } + +} diff --git a/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php b/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php new file mode 100644 index 0000000..1c78697 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php @@ -0,0 +1,194 @@ +instance) { + // Throw an exception if no plugin string was provided. + if (!$this->plugin) { + throw new PluginException(format_string("The block '@block' did not specify a plugin.", array('@block' => $this->id()))); + } + + // Create a plugin instance and store its configuration as settings. + try { + $this->instance = drupal_container()->get('plugin.manager.block')->createInstance($this->plugin, $this->settings, $this); + $this->settings += $this->instance->getConfig(); + } + catch (PluginException $e) { + // Ignore blocks belonging to disabled modules, but re-throw valid + // exceptions when the module is enabled and the plugin is misconfigured. + if (empty($this->module) || module_exists($this->module)) { + throw $e; + } + } + } + return $this->instance; + } + + /** + * Overrides \Drupal\Core\Entity\Entity::uri(); + */ + public function uri() { + return array( + 'path' => 'admin/structure/block/manage/' . $this->id(), + 'options' => array( + 'entity_type' => $this->entityType, + 'entity' => $this, + ), + ); + } + + /** + * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::get(); + */ + public function get($property_name, $langcode = NULL) { + // The theme is stored in the entity ID. + $value = parent::get($property_name, $langcode); + if ($property_name == 'theme' && !$value) { + list($value) = explode('.', $this->id()); + } + return $value; + } + + /** + * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::getExportProperties(); + */ + public function getExportProperties() { + $names = array( + 'id', + 'label', + 'uuid', + 'region', + 'weight', + 'module', + 'status', + 'visibility', + 'plugin', + 'settings', + ); + $properties = array(); + foreach ($names as $name) { + $properties[$name] = $this->get($name); + } + return $properties; + } + +} diff --git a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php index b7186b0..c977756 100644 --- a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php +++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php @@ -12,7 +12,7 @@ use Drupal\Core\Plugin\Discovery\AlterDecorator; use Drupal\Core\Plugin\Discovery\CacheDecorator; use Drupal\Component\Plugin\Factory\DefaultFactory; -use Drupal\Core\Plugin\Mapper\ConfigMapper; +use Drupal\block\Plugin\Core\Entity\Block; /** * Manages discovery and instantiation of block plugins. @@ -31,8 +31,14 @@ public function __construct() { $this->discovery = new DerivativeDiscoveryDecorator($this->discovery); $this->discovery = new AlterDecorator($this->discovery, 'block'); $this->discovery = new CacheDecorator($this->discovery, 'block_plugins:' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'cache_block'); - $this->factory = new DefaultFactory($this); - $this->mapper = new ConfigMapper($this); + } + + /** + * Overrides \Drupal\Component\Plugin\PluginManagerBase::createInstance(). + */ + public function createInstance($plugin_id, array $configuration = array(), Block $entity = NULl) { + $plugin_class = DefaultFactory::getPluginClass($plugin_id, $this->discovery); + return new $plugin_class($configuration, $plugin_id, $this->discovery, $entity); } } diff --git a/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php b/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php index 27256a2..baecaa6 100644 --- a/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php +++ b/core/modules/block/lib/Drupal/block/Plugin/system/plugin_ui/BlockPluginUI.php @@ -17,7 +17,7 @@ * id = "block_plugin_ui", * module = "block", * all_plugins = @Translation("All Blocks"), - * config_path = "admin/structure/block/manage", + * config_path = "admin/structure/block/add", * default_task = TRUE, * derivative = "Drupal\block\Plugin\Derivative\BlockPluginUI", * facets = { @@ -116,7 +116,7 @@ public function formValidate($form, &$form_state) { * Overrides \Drupal\system\Plugin\PluginUIBase::formSubmit(). */ public function formSubmit($form, &$form_state) { - $form_state['redirect'] = 'admin/structure/block/manage/' . $form_state['values']['block'] . '/' . $form_state['values']['theme']; + $form_state['redirect'] = 'admin/structure/block/add/' . $form_state['values']['block'] . '/' . $form_state['values']['theme']; } /** diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php index f40d4fa..96f9343 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php @@ -24,6 +24,7 @@ class BlockCacheTest extends WebTestBase { protected $admin_user; protected $normal_user; protected $normal_user_alt; + protected $block; public static function getInfo() { return array( @@ -49,8 +50,7 @@ function setUp() { $this->normal_user_alt->save(); // Enable our test block. - $block = $this->drupalPlaceBlock('test_cache'); - $this->block_config_id = $block['config_id']; + $this->block = $this->drupalPlaceBlock('test_cache'); } /** @@ -192,15 +192,10 @@ function testCachePerPage() { * Private helper method to set the test block's cache mode. */ private function setCacheMode($cache_mode) { - $block_config = config($this->block_config_id); - $block_config->set('cache', $cache_mode); - $block_config->save(); - - $instance = block_load($this->block_config_id); - $config = $instance->getConfig(); - if ($config['cache'] != $cache_mode) { - $this->fail(t('Unable to set cache mode to %mode. Current mode: %current_mode', array('%mode' => $cache_mode, '%current_mode' => $config['cache']))); - } - $this->assertEqual($config['cache'], $cache_mode, t("Test block's database entry updated to DRUPAL_NO_CACHE.")); + $settings = $this->block->get('settings'); + $settings['cache'] = $cache_mode; + $this->block->set('settings', $settings); + $this->block->save(); } + } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockInvalidRegionTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockInvalidRegionTest.php index ec41b39..54417f8 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockInvalidRegionTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockInvalidRegionTest.php @@ -46,11 +46,10 @@ function setUp() { function testBlockInInvalidRegion() { // Enable a test block and place it in an invalid region. $block = $this->drupalPlaceBlock('test_html_id'); - $config = config($block['config_id']); - $config->set('region', 'invalid_region'); - $config->save(); + $block->set('region', 'invalid_region'); + $block->save(); - $warning_message = t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $config->get('id'), '%region' => 'invalid_region')); + $warning_message = t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block->id(), '%region' => 'invalid_region')); // Clearing the cache should disable the test block placed in the invalid region. $this->drupalPost('admin/config/development/performance', array(), 'Clear all caches'); @@ -61,8 +60,9 @@ function testBlockInInvalidRegion() { $this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.'); // Place disabled test block in the invalid region of the default theme. - $config->set('region', 'invalid_region'); - $config->save(); + $block = entity_load('block', $block->id()); + $block->set('region', 'invalid_region'); + $block->save(); // Clear the cache to check if the warning message is not triggered. $this->drupalPost('admin/config/development/performance', array(), 'Clear all caches'); diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php index 2fa1d40..b9ac714 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php @@ -55,7 +55,7 @@ function setUp() { public function testLanguageBlockVisibility() { // Check if the visibility setting is available. $default_theme = variable_get('theme_default', 'stark'); - $this->drupalGet('admin/structure/block/manage/system_powered_by_block' . '/' . $default_theme); + $this->drupalGet('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme); $this->assertField('visibility[language][langcodes][en]', 'Language visibility field is visible.'); @@ -65,7 +65,7 @@ public function testLanguageBlockVisibility() { 'machine_name' => $this->randomName(8), 'region' => 'sidebar_first', ); - $this->drupalPost('admin/structure/block/manage/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block')); + $this->drupalPost('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block')); // Change the default language. $edit = array( @@ -89,19 +89,23 @@ public function testLanguageBlockVisibility() { * Tests if the visibility settings are removed if the language is deleted. */ public function testLanguageBlockVisibilityLanguageDelete() { - - $default_theme = variable_get('theme_default', 'stark'); // Enable a standard block and set the visibility setting for one language. $edit = array( - 'visibility[language][langcodes][fr]' => TRUE, + 'visibility' => array( + 'language' => array( + 'language_type' => 'language_interface', + 'langcodes' => array( + 'fr' => 'fr', + ), + ), + ), 'machine_name' => 'language_block_test', - 'region' => 'sidebar_first', ); - $this->drupalPost('admin/structure/block/manage/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block')); + $block = $this->drupalPlaceBlock('system_powered_by_block', $edit); // Check that we have the language in config after saving the setting. - $config = config('plugin.core.block.' . $default_theme . '.language_block_test'); - $setting = $config->get('visibility.language.langcodes.fr'); + $visibility = $block->get('visibility'); + $setting = $visibility['language']['langcodes']['fr']; $this->assertTrue('fr' === $setting, 'Language is set in the block configuration.'); // Delete the language. @@ -109,9 +113,9 @@ public function testLanguageBlockVisibilityLanguageDelete() { // Check that the language is no longer stored in the configuration after // it is deleted. - $config = config('plugin.core.block.' . $default_theme . '.language_block_test'); - $setting = $config->get('visibility.language.langcodes.fr'); - $this->assertTrue(empty($setting), 'Language is no longer not set in the block configuration after deleting the block.'); + $block = entity_load('block', $block->id()); + $visibility = $block->get('visibility'); + $this->assertTrue(empty($visibility['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.'); } } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php index dc93755..22f47e8 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockLibrarySearchTest.php @@ -54,6 +54,7 @@ function testBlockLibrarySearch() { // Check that the block search form redirects to the correct block form. $this->drupalPost('admin/structure/block/list/block_plugin_ui:stark/add', array('block' => 'system_main_block'), t('Next')); - $this->assertUrl('admin/structure/block/manage/system_main_block/stark'); + $this->assertUrl('admin/structure/block/add/system_main_block/stark'); } + } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php new file mode 100644 index 0000000..6695681 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php @@ -0,0 +1,206 @@ + 'Block storage', + 'description' => 'Tests the storage of blocks.', + 'group' => 'Block' + ); + } + + protected function setUp() { + parent::setUp(); + + $this->enableModules(array('block')); + $this->controller = entity_get_controller('block'); + } + + /** + * Tests CRUD operations. + */ + public function testBlockCRUD() { + $this->assertTrue($this->controller instanceof BlockStorageController, 'The block storage controller is loaded.'); + + // Run each test method in the same installation. + $this->createTests(); + $this->loadTests(); + $this->renderTests(); + $this->deleteTests(); + } + + /** + * Tests the creation of blocks. + */ + protected function createTests() { + // Attempt to create a block without a plugin. + try { + $entity = $this->controller->create(array()); + $this->fail('A block without a plugin was created with no exception thrown.'); + } + catch (PluginException $e) { + $this->assertEqual('The block \'\' did not specify a plugin.', $e->getMessage(), 'An exception was thrown when a block was created without a plugin.'); + } + + // Create a block with only required values. + $entity = $this->controller->create(array( + 'id' => 'stark.test_block', + 'plugin' => 'test_html_id', + )); + $entity->save(); + + $this->assertTrue($entity instanceof Block, 'The newly created entity is a Block.'); + + // Verify all of the block properties. + $actual_properties = config('plugin.core.block.stark.test_block')->get(); + $this->assertTrue(!empty($actual_properties['uuid']), 'The block UUID is set.'); + unset($actual_properties['uuid']); + + // Ensure that default values are filled in. + $expected_properties = array( + 'id' => 'stark.test_block', + 'label' => '', + 'region' => '-1', + 'weight' => '', + 'module' => 'block_test', + 'status' => '1', + 'visibility' => array(), + 'plugin' => 'test_html_id', + 'settings' => array( + 'cache' => '1', + 'subject' => t('Test block html id'), + ), + ); + $this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.'); + + $this->assertTrue($entity->getPlugin() instanceof TestHtmlIdBlock, 'The entity has an instance of the correct block plugin.'); + } + + /** + * Tests the rendering of blocks. + */ + protected function loadTests() { + $entities = $this->controller->load(array('stark.test_block')); + $entity = reset($entities); + + $this->assertTrue($entity instanceof Block, 'The loaded entity is a Block.'); + + // Verify several properties of the block. + $this->assertEqual($entity->get('region'), '-1'); + $this->assertTrue($entity->get('status')); + $this->assertEqual($entity->get('theme'), 'stark'); + $this->assertEqual($entity->get('module'), 'block_test'); + $this->assertTrue($entity->uuid()); + } + + /** + * Tests the rendering of blocks. + */ + protected function renderTests() { + $entity = $this->controller->create(array( + 'id' => 'stark.test_block', + 'plugin' => 'test_html_id', + )); + + // Test the rendering of a block. + $output = entity_view($entity, 'block'); + $expected = array(); + $expected[] = '
' . t('With multiple languages enabled, registered users can select their preferred language and authors can assign a specific language to content.') . '
'; } diff --git a/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php index 74fb83a..a698695 100644 --- a/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php +++ b/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php @@ -34,12 +34,13 @@ function blockAccess() { * Implements \Drupal\block\BlockBase::blockBuild(). */ public function blockBuild() { + $build = array(); $path = drupal_is_front_page() ? '