annotate core/modules/block/src/BlockListBuilder.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\block;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\Html;
Chris@0 6 use Drupal\Component\Serialization\Json;
Chris@0 7 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
Chris@0 8 use Drupal\Core\Entity\EntityInterface;
Chris@0 9 use Drupal\Core\Entity\EntityStorageInterface;
Chris@0 10 use Drupal\Core\Entity\EntityTypeInterface;
Chris@0 11 use Drupal\Core\Form\FormBuilderInterface;
Chris@0 12 use Drupal\Core\Form\FormInterface;
Chris@0 13 use Drupal\Core\Form\FormStateInterface;
Chris@17 14 use Drupal\Core\Messenger\MessengerInterface;
Chris@0 15 use Drupal\Core\Theme\ThemeManagerInterface;
Chris@0 16 use Drupal\Core\Url;
Chris@0 17 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@0 18 use Symfony\Component\HttpFoundation\Request;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * Defines a class to build a listing of block entities.
Chris@0 22 *
Chris@0 23 * @see \Drupal\block\Entity\Block
Chris@0 24 */
Chris@0 25 class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface {
Chris@0 26
Chris@0 27 /**
Chris@0 28 * The theme containing the blocks.
Chris@0 29 *
Chris@0 30 * @var string
Chris@0 31 */
Chris@0 32 protected $theme;
Chris@0 33
Chris@0 34 /**
Chris@0 35 * The current request.
Chris@0 36 *
Chris@0 37 * @var \Symfony\Component\HttpFoundation\Request
Chris@0 38 */
Chris@0 39 protected $request;
Chris@0 40
Chris@0 41 /**
Chris@0 42 * The theme manager.
Chris@0 43 *
Chris@0 44 * @var \Drupal\Core\Theme\ThemeManagerInterface
Chris@0 45 */
Chris@0 46 protected $themeManager;
Chris@0 47
Chris@0 48 /**
Chris@0 49 * The form builder.
Chris@0 50 *
Chris@0 51 * @var \Drupal\Core\Form\FormBuilderInterface
Chris@0 52 */
Chris@0 53 protected $formBuilder;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * {@inheritdoc}
Chris@0 57 */
Chris@0 58 protected $limit = FALSE;
Chris@0 59
Chris@0 60 /**
Chris@17 61 * The messenger.
Chris@17 62 *
Chris@17 63 * @var \Drupal\Core\Messenger\MessengerInterface
Chris@17 64 */
Chris@17 65 protected $messenger;
Chris@17 66
Chris@17 67 /**
Chris@0 68 * Constructs a new BlockListBuilder object.
Chris@0 69 *
Chris@0 70 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 71 * The entity type definition.
Chris@0 72 * @param \Drupal\Core\Entity\EntityStorageInterface $storage
Chris@0 73 * The entity storage class.
Chris@0 74 * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
Chris@0 75 * The theme manager.
Chris@0 76 * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
Chris@0 77 * The form builder.
Chris@0 78 */
Chris@17 79 public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, ThemeManagerInterface $theme_manager, FormBuilderInterface $form_builder, MessengerInterface $messenger) {
Chris@0 80 parent::__construct($entity_type, $storage);
Chris@0 81
Chris@0 82 $this->themeManager = $theme_manager;
Chris@0 83 $this->formBuilder = $form_builder;
Chris@17 84 $this->messenger = $messenger;
Chris@0 85 }
Chris@0 86
Chris@0 87 /**
Chris@0 88 * {@inheritdoc}
Chris@0 89 */
Chris@0 90 public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
Chris@0 91 return new static(
Chris@0 92 $entity_type,
Chris@18 93 $container->get('entity_type.manager')->getStorage($entity_type->id()),
Chris@0 94 $container->get('theme.manager'),
Chris@17 95 $container->get('form_builder'),
Chris@17 96 $container->get('messenger')
Chris@0 97 );
Chris@0 98 }
Chris@0 99
Chris@0 100 /**
Chris@0 101 * {@inheritdoc}
Chris@0 102 *
Chris@0 103 * @param string|null $theme
Chris@0 104 * (optional) The theme to display the blocks for. If NULL, the current
Chris@0 105 * theme will be used.
Chris@0 106 * @param \Symfony\Component\HttpFoundation\Request $request
Chris@0 107 * The current request.
Chris@0 108 *
Chris@0 109 * @return array
Chris@0 110 * The block list as a renderable array.
Chris@0 111 */
Chris@0 112 public function render($theme = NULL, Request $request = NULL) {
Chris@0 113 $this->request = $request;
Chris@0 114 $this->theme = $theme;
Chris@0 115
Chris@0 116 return $this->formBuilder->getForm($this);
Chris@0 117 }
Chris@0 118
Chris@0 119 /**
Chris@0 120 * {@inheritdoc}
Chris@0 121 */
Chris@0 122 public function getFormId() {
Chris@0 123 return 'block_admin_display_form';
Chris@0 124 }
Chris@0 125
Chris@0 126 /**
Chris@0 127 * {@inheritdoc}
Chris@0 128 */
Chris@0 129 public function buildForm(array $form, FormStateInterface $form_state) {
Chris@0 130 $form['#attached']['library'][] = 'core/drupal.tableheader';
Chris@0 131 $form['#attached']['library'][] = 'block/drupal.block';
Chris@0 132 $form['#attached']['library'][] = 'block/drupal.block.admin';
Chris@0 133 $form['#attributes']['class'][] = 'clearfix';
Chris@0 134
Chris@0 135 // Build the form tree.
Chris@0 136 $form['blocks'] = $this->buildBlocksForm();
Chris@0 137
Chris@0 138 $form['actions'] = [
Chris@0 139 '#tree' => FALSE,
Chris@0 140 '#type' => 'actions',
Chris@0 141 ];
Chris@0 142 $form['actions']['submit'] = [
Chris@0 143 '#type' => 'submit',
Chris@0 144 '#value' => $this->t('Save blocks'),
Chris@0 145 '#button_type' => 'primary',
Chris@0 146 ];
Chris@0 147
Chris@0 148 return $form;
Chris@0 149 }
Chris@0 150
Chris@0 151 /**
Chris@0 152 * Builds the main "Blocks" portion of the form.
Chris@0 153 *
Chris@0 154 * @return array
Chris@0 155 */
Chris@0 156 protected function buildBlocksForm() {
Chris@0 157 // Build blocks first for each region.
Chris@0 158 $blocks = [];
Chris@0 159 $entities = $this->load();
Chris@0 160 /** @var \Drupal\block\BlockInterface[] $entities */
Chris@0 161 foreach ($entities as $entity_id => $entity) {
Chris@0 162 $definition = $entity->getPlugin()->getPluginDefinition();
Chris@0 163 $blocks[$entity->getRegion()][$entity_id] = [
Chris@0 164 'label' => $entity->label(),
Chris@0 165 'entity_id' => $entity_id,
Chris@0 166 'weight' => $entity->getWeight(),
Chris@0 167 'entity' => $entity,
Chris@0 168 'category' => $definition['category'],
Chris@0 169 'status' => $entity->status(),
Chris@0 170 ];
Chris@0 171 }
Chris@0 172
Chris@0 173 $form = [
Chris@0 174 '#type' => 'table',
Chris@0 175 '#header' => [
Chris@0 176 $this->t('Block'),
Chris@0 177 $this->t('Category'),
Chris@0 178 $this->t('Region'),
Chris@0 179 $this->t('Weight'),
Chris@0 180 $this->t('Operations'),
Chris@0 181 ],
Chris@0 182 '#attributes' => [
Chris@0 183 'id' => 'blocks',
Chris@0 184 ],
Chris@0 185 ];
Chris@0 186
Chris@0 187 // Weights range from -delta to +delta, so delta should be at least half
Chris@0 188 // of the amount of blocks present. This makes sure all blocks in the same
Chris@0 189 // region get an unique weight.
Chris@0 190 $weight_delta = round(count($entities) / 2);
Chris@0 191
Chris@0 192 $placement = FALSE;
Chris@0 193 if ($this->request->query->has('block-placement')) {
Chris@0 194 $placement = $this->request->query->get('block-placement');
Chris@0 195 $form['#attached']['drupalSettings']['blockPlacement'] = $placement;
Chris@17 196 // Remove the block placement from the current request so that it is not
Chris@17 197 // passed on to any redirect destinations.
Chris@17 198 $this->request->query->remove('block-placement');
Chris@0 199 }
Chris@0 200
Chris@0 201 // Loop over each region and build blocks.
Chris@0 202 $regions = $this->systemRegionList($this->getThemeName(), REGIONS_VISIBLE);
Chris@0 203 foreach ($regions as $region => $title) {
Chris@0 204 $form['#tabledrag'][] = [
Chris@0 205 'action' => 'match',
Chris@0 206 'relationship' => 'sibling',
Chris@0 207 'group' => 'block-region-select',
Chris@0 208 'subgroup' => 'block-region-' . $region,
Chris@0 209 'hidden' => FALSE,
Chris@0 210 ];
Chris@0 211 $form['#tabledrag'][] = [
Chris@0 212 'action' => 'order',
Chris@0 213 'relationship' => 'sibling',
Chris@0 214 'group' => 'block-weight',
Chris@0 215 'subgroup' => 'block-weight-' . $region,
Chris@0 216 ];
Chris@0 217
Chris@0 218 $form['region-' . $region] = [
Chris@0 219 '#attributes' => [
Chris@0 220 'class' => ['region-title', 'region-title-' . $region],
Chris@0 221 'no_striping' => TRUE,
Chris@0 222 ],
Chris@0 223 ];
Chris@0 224 $form['region-' . $region]['title'] = [
Chris@0 225 '#theme_wrappers' => [
Chris@0 226 'container' => [
Chris@0 227 '#attributes' => ['class' => 'region-title__action'],
Chris@17 228 ],
Chris@0 229 ],
Chris@0 230 '#prefix' => $title,
Chris@0 231 '#type' => 'link',
Chris@0 232 '#title' => $this->t('Place block <span class="visually-hidden">in the %region region</span>', ['%region' => $title]),
Chris@0 233 '#url' => Url::fromRoute('block.admin_library', ['theme' => $this->getThemeName()], ['query' => ['region' => $region]]),
Chris@0 234 '#wrapper_attributes' => [
Chris@0 235 'colspan' => 5,
Chris@0 236 ],
Chris@0 237 '#attributes' => [
Chris@0 238 'class' => ['use-ajax', 'button', 'button--small'],
Chris@0 239 'data-dialog-type' => 'modal',
Chris@0 240 'data-dialog-options' => Json::encode([
Chris@0 241 'width' => 700,
Chris@0 242 ]),
Chris@0 243 ],
Chris@0 244 ];
Chris@0 245
Chris@0 246 $form['region-' . $region . '-message'] = [
Chris@0 247 '#attributes' => [
Chris@0 248 'class' => [
Chris@0 249 'region-message',
Chris@0 250 'region-' . $region . '-message',
Chris@0 251 empty($blocks[$region]) ? 'region-empty' : 'region-populated',
Chris@0 252 ],
Chris@0 253 ],
Chris@0 254 ];
Chris@0 255 $form['region-' . $region . '-message']['message'] = [
Chris@0 256 '#markup' => '<em>' . $this->t('No blocks in this region') . '</em>',
Chris@0 257 '#wrapper_attributes' => [
Chris@0 258 'colspan' => 5,
Chris@0 259 ],
Chris@0 260 ];
Chris@0 261
Chris@0 262 if (isset($blocks[$region])) {
Chris@0 263 foreach ($blocks[$region] as $info) {
Chris@0 264 $entity_id = $info['entity_id'];
Chris@0 265
Chris@0 266 $form[$entity_id] = [
Chris@0 267 '#attributes' => [
Chris@0 268 'class' => ['draggable'],
Chris@0 269 ],
Chris@0 270 ];
Chris@0 271 $form[$entity_id]['#attributes']['class'][] = $info['status'] ? 'block-enabled' : 'block-disabled';
Chris@0 272 if ($placement && $placement == Html::getClass($entity_id)) {
Chris@0 273 $form[$entity_id]['#attributes']['class'][] = 'color-success';
Chris@0 274 $form[$entity_id]['#attributes']['class'][] = 'js-block-placed';
Chris@0 275 }
Chris@0 276 $form[$entity_id]['info'] = [
Chris@0 277 '#plain_text' => $info['status'] ? $info['label'] : $this->t('@label (disabled)', ['@label' => $info['label']]),
Chris@0 278 '#wrapper_attributes' => [
Chris@0 279 'class' => ['block'],
Chris@0 280 ],
Chris@0 281 ];
Chris@0 282 $form[$entity_id]['type'] = [
Chris@0 283 '#markup' => $info['category'],
Chris@0 284 ];
Chris@0 285 $form[$entity_id]['region-theme']['region'] = [
Chris@0 286 '#type' => 'select',
Chris@0 287 '#default_value' => $region,
Chris@0 288 '#required' => TRUE,
Chris@0 289 '#title' => $this->t('Region for @block block', ['@block' => $info['label']]),
Chris@0 290 '#title_display' => 'invisible',
Chris@0 291 '#options' => $regions,
Chris@0 292 '#attributes' => [
Chris@0 293 'class' => ['block-region-select', 'block-region-' . $region],
Chris@0 294 ],
Chris@0 295 '#parents' => ['blocks', $entity_id, 'region'],
Chris@0 296 ];
Chris@0 297 $form[$entity_id]['region-theme']['theme'] = [
Chris@0 298 '#type' => 'hidden',
Chris@0 299 '#value' => $this->getThemeName(),
Chris@0 300 '#parents' => ['blocks', $entity_id, 'theme'],
Chris@0 301 ];
Chris@0 302 $form[$entity_id]['weight'] = [
Chris@0 303 '#type' => 'weight',
Chris@0 304 '#default_value' => $info['weight'],
Chris@0 305 '#delta' => $weight_delta,
Chris@0 306 '#title' => $this->t('Weight for @block block', ['@block' => $info['label']]),
Chris@0 307 '#title_display' => 'invisible',
Chris@0 308 '#attributes' => [
Chris@0 309 'class' => ['block-weight', 'block-weight-' . $region],
Chris@0 310 ],
Chris@0 311 ];
Chris@0 312 $form[$entity_id]['operations'] = $this->buildOperations($info['entity']);
Chris@0 313 }
Chris@0 314 }
Chris@0 315 }
Chris@0 316
Chris@0 317 // Do not allow disabling the main system content block when it is present.
Chris@0 318 if (isset($form['system_main']['region'])) {
Chris@0 319 $form['system_main']['region']['#required'] = TRUE;
Chris@0 320 }
Chris@0 321 return $form;
Chris@0 322 }
Chris@0 323
Chris@0 324 /**
Chris@0 325 * Gets the name of the theme used for this block listing.
Chris@0 326 *
Chris@0 327 * @return string
Chris@0 328 * The name of the theme.
Chris@0 329 */
Chris@0 330 protected function getThemeName() {
Chris@0 331 // If no theme was specified, use the current theme.
Chris@0 332 if (!$this->theme) {
Chris@0 333 $this->theme = $this->themeManager->getActiveTheme()->getName();
Chris@0 334 }
Chris@0 335 return $this->theme;
Chris@0 336 }
Chris@0 337
Chris@0 338 /**
Chris@0 339 * {@inheritdoc}
Chris@0 340 */
Chris@0 341 protected function getEntityIds() {
Chris@0 342 return $this->getStorage()->getQuery()
Chris@0 343 ->condition('theme', $this->getThemeName())
Chris@0 344 ->sort($this->entityType->getKey('id'))
Chris@0 345 ->execute();
Chris@0 346 }
Chris@0 347
Chris@0 348 /**
Chris@0 349 * {@inheritdoc}
Chris@0 350 */
Chris@0 351 public function getDefaultOperations(EntityInterface $entity) {
Chris@0 352 $operations = parent::getDefaultOperations($entity);
Chris@0 353
Chris@0 354 if (isset($operations['edit'])) {
Chris@0 355 $operations['edit']['title'] = $this->t('Configure');
Chris@0 356 }
Chris@0 357
Chris@0 358 if (isset($operations['delete'])) {
Chris@0 359 $operations['delete']['title'] = $this->t('Remove');
Chris@0 360 }
Chris@0 361 return $operations;
Chris@0 362 }
Chris@0 363
Chris@0 364 /**
Chris@0 365 * {@inheritdoc}
Chris@0 366 */
Chris@0 367 public function validateForm(array &$form, FormStateInterface $form_state) {
Chris@0 368 // No validation.
Chris@0 369 }
Chris@0 370
Chris@0 371 /**
Chris@0 372 * {@inheritdoc}
Chris@0 373 */
Chris@0 374 public function submitForm(array &$form, FormStateInterface $form_state) {
Chris@0 375 $entities = $this->storage->loadMultiple(array_keys($form_state->getValue('blocks')));
Chris@0 376 /** @var \Drupal\block\BlockInterface[] $entities */
Chris@0 377 foreach ($entities as $entity_id => $entity) {
Chris@0 378 $entity_values = $form_state->getValue(['blocks', $entity_id]);
Chris@0 379 $entity->setWeight($entity_values['weight']);
Chris@0 380 $entity->setRegion($entity_values['region']);
Chris@0 381 $entity->save();
Chris@0 382 }
Chris@17 383 $this->messenger->addStatus($this->t('The block settings have been updated.'));
Chris@0 384 }
Chris@0 385
Chris@0 386 /**
Chris@0 387 * Wraps system_region_list().
Chris@0 388 */
Chris@0 389 protected function systemRegionList($theme, $show = REGIONS_ALL) {
Chris@0 390 return system_region_list($theme, $show);
Chris@0 391 }
Chris@0 392
Chris@0 393 }