Commit 34453e5e authored by webchick's avatar webchick

Issue #1871696 by tim.plunkett, xjm, sun, msonnabaum, EclipseGc: Convert block...

Issue #1871696 by tim.plunkett, xjm, sun, msonnabaum, EclipseGc: Convert block instances to configuration entities to resolve architectural issues.
parent 342ace17
......@@ -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();
}
}
}
......
......@@ -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.');
}
/**
......
This diff is collapsed.
......@@ -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']);
......
This diff is collapsed.
......@@ -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',
......
......@@ -28,17 +28,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().
*/
......@@ -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,10 +67,24 @@ 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().
*/
......@@ -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();
......
<?php
/**
* @file
* Contains \Drupal\block\BlockAccessController.
*/
namespace Drupal\block;
use Drupal\Core\Entity\EntityAccessController;
use Drupal\Core\Entity\EntityInterface;
use Drupal\user\Plugin\Core\Entity\User;
/**
* Provides a Block access controller.
*/
class BlockAccessController extends EntityAccessController {
/**
* Overrides \Drupal\Core\Entity\EntityAccessController::viewAccess().
*/
public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return $entity->getPlugin()->access();
}
}
<?php
/**
* @file
* Contains \Drupal\block\BlockFormController.
*/
namespace Drupal\block;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityFormController;
/**
* Provides form controller for block instance forms.
*/
class BlockFormController extends EntityFormController {
/**
* Overrides \Drupal\Core\Entity\EntityFormController::form().
*/
public function form(array $form, array &$form_state, EntityInterface $entity) {
return $entity->getPlugin()->form($form, $form_state);
}
/**
* 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();
}
}
<?php
/**
* @file
* Contains \Drupal\block\BlockInterface.
*/
......@@ -28,7 +29,7 @@ interface BlockInterface {
* @todo Consider merging this with the general plugin configuration member
* variable and its getter/setter in http://drupal.org/node/1764380.
*/
public function settings();
public function blockSettings();
/**
* Indicates whether the block should be shown.
......@@ -38,6 +39,8 @@ public function settings();
*
* @return bool
* TRUE if the block should be shown, or FALSE otherwise.
*
* @see \Drupal\block\BlockAccessController
*/
public function access();
......@@ -55,6 +58,7 @@ public function access();
* @return array $form
* The renderable form array representing the entire configuration form.
*
* @see \Drupal\block\BlockFormController::form()
* @see \Drupal\block\BlockInterace::validate()
* @see \Drupal\block\BlockInterace::submit()
*/
......@@ -68,6 +72,7 @@ public function form($form, &$form_state);
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @see \Drupal\block\BlockFormController::validate()
* @see \Drupal\block\BlockInterace::form()
* @see \Drupal\block\BlockInterace::submit()
*/
......@@ -81,17 +86,20 @@ public function validate($form, &$form_state);
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @see \Drupal\block\BlockFormController::submit()
* @see \Drupal\block\BlockInterace::form()
* @see \Drupal\block\BlockInterace::validate()
*/
public function submit($form, &$form_state);
/**
* Builds and returns the renderable array for this block.
* Builds and returns the renderable array for this block plugin.
*
* @return array
* A renderable array representing the output of the block.
* A renderable array representing the content of the block.
*
* @see \Drupal\block\BlockRenderController
*/
public function build();
public function blockBuild();
}
<?php
/**
* @file
* Contains \Drupal\block\BlockListController.
*/
namespace Drupal\block;
use Drupal\Core\Config\Entity\ConfigEntityListController;
use Drupal\block\Plugin\Core\Entity\Block;
/**
* Defines the block list controller.
*/
class BlockListController extends ConfigEntityListController {
/**
* The regions containing the blocks.
*
* @var array
*/
protected $regions;
/**
* The theme containing the blocks.
*
* @var string
*/
protected $theme;
/**
* Overrides \Drupal\Core\Config\Entity\ConfigEntityListController::load().
*/
public function load() {
// If no theme was specified, use the current theme.
if (!$this->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));
}
}
<?php
/**
* @file
* Contains \Drupal\block\BlockRenderController.
*/
namespace Drupal\block;
use Drupal\Core\Entity\EntityRenderControllerInterface;
use Drupal\Core\Entity\EntityInterface;
/**
* Provides a Block render controller.
*/
class BlockRenderController implements EntityRenderControllerInterface {
/**
* Implements \Drupal\Core\Entity\EntityRenderControllerInterface::buildContent().
*/
public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) {
return array();
}
/**
* Provides entity-specific defaults to the build process.
*
* @param Drupal\Core\Entity\EntityInterface $entity
* The entity for which the defaults should be provided.
* @param string $view_mode
* The view mode that should be used.
* @param string $langcode
* (optional) For which language the entity should be prepared, defaults to
* the current content language.
*
* @return array
* An array of defaults to add into the entity render array.
*/
protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
// @todo \Drupal\block\Tests\BlockTest::testCustomBlock() assuemes that a
// block can be rendered without any of its wrappers. To do so, it uses a
// custom view mode, and we choose to only add the wrappers on the default
// view mode, 'block'.
if ($view_mode != 'block') {
return array();
}
return array(
'#block' => $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();
// 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;
}
}
<?php
/**
* @file
* Contains \Drupal\block\BlockStorageController.
*/
namespace Drupal\block;
use Drupal\Core\Config\Entity\ConfigStorageController;
use Drupal\Core\Entity\EntityInterface;
/**
* Defines the storage controller class for Block entities.
*/
class BlockStorageController extends ConfigStorageController {
/**
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::create().
*/
public function create(array $values) {
$entity = parent::create($values);
if (!$entity->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;
}
}
<?php
/**
* @file
* Contains \Drupal\block\Plugin\Core\Entity\Block.
*/
namespace Drupal\block\Plugin\Core\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
use Drupal\Component\Plugin\Exception\PluginException;