Newer
Older
Angie Byron
committed
<?php
/**
* @file
* Contains \Drupal\block\BlockListBuilder.
Angie Byron
committed
*/
namespace Drupal\block;
Dries Buytaert
committed
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Utility\Json;
Angie Byron
committed
use Drupal\Component\Utility\String;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
Alex Pott
committed
use Drupal\Core\Entity\EntityInterface;
Dries Buytaert
committed
use Drupal\Core\Entity\EntityStorageControllerInterface;
Alex Pott
committed
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormInterface;
Dries Buytaert
committed
use Symfony\Component\DependencyInjection\ContainerInterface;
Dries Buytaert
committed
use Symfony\Component\HttpFoundation\Request;
Angie Byron
committed
/**
* Defines a class to build a listing of block entities.
*
* @see \Drupal\block\Entity\Block
Angie Byron
committed
*/
class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface {
Angie Byron
committed
/**
* The regions containing the blocks.
*
* @var array
*/
protected $regions;
/**
* The theme containing the blocks.
*
* @var string
*/
protected $theme;
Dries Buytaert
committed
/**
* The current request.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
Dries Buytaert
committed
/**
* The block manager.
*
* @var \Drupal\Component\Plugin\PluginManagerInterface
*/
protected $blockManager;
/**
* Constructs a new BlockListBuilder object.
Dries Buytaert
committed
*
Alex Pott
committed
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
Dries Buytaert
committed
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
* The entity storage controller class.
* @param \Drupal\Component\Plugin\PluginManagerInterface $block_manager
* The block manager.
*/
Alex Pott
committed
public function __construct(EntityTypeInterface $entity_type, EntityStorageControllerInterface $storage, PluginManagerInterface $block_manager) {
parent::__construct($entity_type, $storage);
Dries Buytaert
committed
$this->blockManager = $block_manager;
}
/**
* {@inheritdoc}
*/
Alex Pott
committed
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
Dries Buytaert
committed
return new static(
Alex Pott
committed
$entity_type,
$container->get('entity.manager')->getStorageController($entity_type->id()),
Dries Buytaert
committed
$container->get('plugin.manager.block')
);
}
Angie Byron
committed
/**
* {@inheritdoc}
Angie Byron
committed
*/
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);
Alex Pott
committed
Jennifer Hodgdon
committed
// Sort the blocks using \Drupal\block\Entity\Block::sort().
uasort($entities, array($this->entityType->getClass(), 'sort'));
Angie Byron
committed
return $entities;
}
/**
* {@inheritdoc}
Dries Buytaert
committed
*
* @param string|null $theme
* (optional) The theme to display the blocks for. If NULL, the current
* theme will be used.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return array
* The block list as a renderable array.
Angie Byron
committed
*/
Dries Buytaert
committed
public function render($theme = NULL, Request $request = NULL) {
$this->request = $request;
Angie Byron
committed
// If no theme was specified, use the current theme.
$this->theme = $theme ?: $GLOBALS['theme_key'];
return drupal_get_form($this);
Angie Byron
committed
}
/**
Alex Pott
committed
* {@inheritdoc}
Alex Pott
committed
public function getFormId() {
return 'block_admin_display_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
Angie Byron
committed
* Form constructor for the main block administration form.
*/
public function buildForm(array $form, array &$form_state) {
Dries Buytaert
committed
$placement = FALSE;
if ($this->request->query->has('block-placement')) {
$placement = $this->request->query->get('block-placement');
$form['#attached']['js'][] = array(
'type' => 'setting',
'data' => array('blockPlacement' => $placement),
);
}
Angie Byron
committed
$entities = $this->load();
Dries Buytaert
committed
$form['#theme'] = array('block_list');
$form['#attached']['library'][] = 'core/drupal.tableheader';
$form['#attached']['library'][] = 'block/drupal.block';
$form['#attached']['library'][] = 'block/drupal.block.admin';
Dries Buytaert
committed
$form['#attributes']['class'][] = 'clearfix';
Angie Byron
committed
// Add a last region for disabled blocks.
Alex Pott
committed
$block_regions_with_disabled = $this->regions + array(BlockInterface::BLOCK_REGION_NONE => BlockInterface::BLOCK_REGION_NONE);
Dries Buytaert
committed
$form['block_regions'] = array(
Angie Byron
committed
'#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.
Dries Buytaert
committed
$form['edited_theme'] = array(
Angie Byron
committed
'#type' => 'value',
'#value' => $this->theme,
);
Dries Buytaert
committed
$form['blocks'] = array(
Alex Pott
committed
'#type' => 'table',
'#header' => array(
t('Block'),
Angie Byron
committed
t('Category'),
Alex Pott
committed
t('Region'),
t('Weight'),
t('Operations'),
),
'#attributes' => array(
'id' => 'blocks',
),
);
Angie Byron
committed
Alex Pott
committed
// Build blocks first for each region.
Angie Byron
committed
foreach ($entities as $entity_id => $entity) {
Alex Pott
committed
$definition = $entity->getPlugin()->getPluginDefinition();
$blocks[$entity->get('region')][$entity_id] = array(
Angie Byron
committed
'label' => $entity->label(),
'entity_id' => $entity_id,
'weight' => $entity->get('weight'),
Alex Pott
committed
'entity' => $entity,
Angie Byron
committed
'category' => $definition['category'],
);
Alex Pott
committed
}
// Loop over each region and build blocks.
foreach ($block_regions_with_disabled as $region => $title) {
Dries Buytaert
committed
$form['blocks']['#tabledrag'][] = array(
'action' => 'match',
'relationship' => 'sibling',
'group' => 'block-region-select',
'subgroup' => 'block-region-' . $region,
'hidden' => FALSE,
Angie Byron
committed
);
Dries Buytaert
committed
$form['blocks']['#tabledrag'][] = array(
'action' => 'order',
'relationship' => 'sibling',
'group' => 'block-weight',
'subgroup' => 'block-weight-' . $region,
Angie Byron
committed
);
Alex Pott
committed
Dries Buytaert
committed
$form['blocks'][$region] = array(
Alex Pott
committed
'#attributes' => array(
'class' => array('region-title', 'region-title-' . $region),
Alex Pott
committed
'no_striping' => TRUE,
),
Angie Byron
committed
);
Dries Buytaert
committed
$form['blocks'][$region]['title'] = array(
Alex Pott
committed
'#markup' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : t('Disabled'),
Alex Pott
committed
'#wrapper_attributes' => array(
'colspan' => 5,
),
Angie Byron
committed
);
Alex Pott
committed
Dries Buytaert
committed
$form['blocks'][$region . '-message'] = array(
Alex Pott
committed
'#attributes' => array(
'class' => array(
'region-message',
'region-' . $region . '-message',
empty($blocks[$region]) ? 'region-empty' : 'region-populated',
),
),
Angie Byron
committed
);
Dries Buytaert
committed
$form['blocks'][$region . '-message']['message'] = array(
Alex Pott
committed
'#markup' => '<em>' . t('No blocks in this region') . '</em>',
'#wrapper_attributes' => array(
'colspan' => 5,
),
Angie Byron
committed
);
Alex Pott
committed
if (isset($blocks[$region])) {
foreach ($blocks[$region] as $info) {
$entity_id = $info['entity_id'];
Dries Buytaert
committed
$form['blocks'][$entity_id] = array(
Alex Pott
committed
'#attributes' => array(
'class' => array('draggable'),
),
);
Dries Buytaert
committed
if ($placement && $placement == drupal_html_class($entity_id)) {
$form['blocks'][$entity_id]['#attributes']['id'] = 'block-placed';
}
Alex Pott
committed
Dries Buytaert
committed
$form['blocks'][$entity_id]['info'] = array(
Angie Byron
committed
'#markup' => String::checkPlain($info['label']),
Alex Pott
committed
'#wrapper_attributes' => array(
'class' => array('block'),
),
);
Angie Byron
committed
$form['blocks'][$entity_id]['type'] = array(
'#markup' => $info['category'],
);
Dries Buytaert
committed
$form['blocks'][$entity_id]['region-theme']['region'] = array(
Alex Pott
committed
'#type' => 'select',
'#default_value' => $region,
Alex Pott
committed
'#empty_value' => BlockInterface::BLOCK_REGION_NONE,
Angie Byron
committed
'#title' => t('Region for @block block', array('@block' => $info['label'])),
Angie Byron
committed
'#title_display' => 'invisible',
Alex Pott
committed
'#options' => $this->regions,
'#attributes' => array(
'class' => array('block-region-select', 'block-region-' . $region),
),
'#parents' => array('blocks', $entity_id, 'region'),
);
Dries Buytaert
committed
$form['blocks'][$entity_id]['region-theme']['theme'] = array(
Alex Pott
committed
'#type' => 'hidden',
'#value' => $this->theme,
'#parents' => array('blocks', $entity_id, 'theme'),
);
Dries Buytaert
committed
$form['blocks'][$entity_id]['weight'] = array(
Alex Pott
committed
'#type' => 'weight',
'#default_value' => $info['weight'],
Alex Pott
committed
'#delta' => $weight_delta,
Angie Byron
committed
'#title' => t('Weight for @block block', array('@block' => $info['label'])),
Angie Byron
committed
'#title_display' => 'invisible',
Alex Pott
committed
'#attributes' => array(
'class' => array('block-weight', 'block-weight-' . $region),
),
);
Dries Buytaert
committed
$form['blocks'][$entity_id]['operations'] = $this->buildOperations($info['entity']);
Alex Pott
committed
}
}
Angie Byron
committed
}
Alex Pott
committed
Angie Byron
committed
// Do not allow disabling the main system content block when it is present.
Dries Buytaert
committed
if (isset($form['blocks']['system_main']['region'])) {
$form['blocks']['system_main']['region']['#required'] = TRUE;
Angie Byron
committed
}
Dries Buytaert
committed
$form['actions'] = array(
Angie Byron
committed
'#tree' => FALSE,
'#type' => 'actions',
);
Dries Buytaert
committed
$form['actions']['submit'] = array(
Angie Byron
committed
'#type' => 'submit',
'#value' => t('Save blocks'),
'#button_type' => 'primary',
);
Dries Buytaert
committed
Dries Buytaert
committed
$form['place_blocks']['title'] = array(
Dries Buytaert
committed
'#type' => 'container',
'#children' => '<h3>' . t('Place blocks') . '</h3>',
'#attributes' => array(
'class' => array(
'entity-meta-header',
),
),
);
Dries Buytaert
committed
$form['place_blocks']['filter'] = array(
Dries Buytaert
committed
'#type' => 'search',
'#title' => t('Filter'),
'#title_display' => 'invisible',
'#size' => 30,
'#placeholder' => t('Filter by block name'),
'#attributes' => array(
'class' => array('block-filter-text'),
'data-element' => '.entity-meta',
'title' => t('Enter a part of the block name to filter by.'),
),
);
Dries Buytaert
committed
$form['place_blocks']['list']['#type'] = 'container';
$form['place_blocks']['list']['#attributes']['class'][] = 'entity-meta';
Dries Buytaert
committed
// Sort the plugins first by category, then by label.
$plugins = $this->blockManager->getDefinitions();
uasort($plugins, function ($a, $b) {
if ($a['category'] != $b['category']) {
return strnatcasecmp($a['category'], $b['category']);
}
return strnatcasecmp($a['admin_label'], $b['admin_label']);
});
foreach ($plugins as $plugin_id => $plugin_definition) {
Angie Byron
committed
$category = String::checkPlain($plugin_definition['category']);
$category_key = 'category-' . $category;
if (!isset($form['place_blocks']['list'][$category_key])) {
$form['place_blocks']['list'][$category_key] = array(
Dries Buytaert
committed
'#type' => 'details',
'#title' => $category,
Angie Byron
committed
'#open' => TRUE,
Dries Buytaert
committed
'content' => array(
'#theme' => 'links',
'#links' => array(),
'#attributes' => array(
'class' => array(
'block-list',
),
),
),
);
}
$form['place_blocks']['list'][$category_key]['content']['#links'][$plugin_id] = array(
Dries Buytaert
committed
'title' => $plugin_definition['admin_label'],
'href' => 'admin/structure/block/add/' . $plugin_id . '/' . $this->theme,
'attributes' => array(
'class' => array('use-ajax', 'block-filter-text-source'),
'data-accepts' => 'application/vnd.drupal-modal',
'data-dialog-options' => Json::encode(array(
'width' => 700,
)),
),
);
}
Angie Byron
committed
return $form;
}
Alex Pott
committed
/**
* {@inheritdoc}
*/
public function getOperations(EntityInterface $entity) {
$operations = parent::getOperations($entity);
if (isset($operations['edit'])) {
$operations['edit']['title'] = t('Configure');
}
Alex Pott
committed
return $operations;
}
Angie Byron
committed
/**
* Implements \Drupal\Core\Form\FormInterface::validateForm().
public function validateForm(array &$form, array &$form_state) {
// No validation.
}
/**
* Implements \Drupal\Core\Form\FormInterface::submitForm().
Angie Byron
committed
* Form submission handler for the main block administration form.
*/
public function submitForm(array &$form, array &$form_state) {
Angie Byron
committed
$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']);
Alex Pott
committed
if ($entity->get('region') == BlockInterface::BLOCK_REGION_NONE) {
$entity->disable();
}
else {
$entity->enable();
}
Angie Byron
committed
$entity->save();
}
drupal_set_message(t('The block settings have been updated.'));
Cache::invalidateTags(array('content' => TRUE));