1<?php 2 3namespace Drupal\layout_builder\Controller; 4 5use Drupal\Core\Ajax\AjaxHelperTrait; 6use Drupal\Core\Block\BlockManagerInterface; 7use Drupal\Core\DependencyInjection\ContainerInjectionInterface; 8use Drupal\Core\Entity\EntityTypeManagerInterface; 9use Drupal\Core\Session\AccountInterface; 10use Drupal\Core\StringTranslation\StringTranslationTrait; 11use Drupal\Core\Url; 12use Drupal\layout_builder\Context\LayoutBuilderContextTrait; 13use Drupal\layout_builder\LayoutBuilderHighlightTrait; 14use Drupal\layout_builder\SectionStorageInterface; 15use Symfony\Component\DependencyInjection\ContainerInterface; 16 17/** 18 * Defines a controller to choose a new block. 19 * 20 * @internal 21 * Controller classes are internal. 22 */ 23class ChooseBlockController implements ContainerInjectionInterface { 24 25 use AjaxHelperTrait; 26 use LayoutBuilderContextTrait; 27 use LayoutBuilderHighlightTrait; 28 use StringTranslationTrait; 29 30 /** 31 * The block manager. 32 * 33 * @var \Drupal\Core\Block\BlockManagerInterface 34 */ 35 protected $blockManager; 36 37 /** 38 * The entity type manager. 39 * 40 * @var \Drupal\Core\Entity\EntityTypeManagerInterface 41 */ 42 protected $entityTypeManager; 43 44 /** 45 * The current user. 46 * 47 * @var \Drupal\Core\Session\AccountInterface 48 */ 49 protected $currentUser; 50 51 /** 52 * ChooseBlockController constructor. 53 * 54 * @param \Drupal\Core\Block\BlockManagerInterface $block_manager 55 * The block manager. 56 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager 57 * The entity type manager. 58 * @param \Drupal\Core\Session\AccountInterface $current_user 59 * The current user. 60 */ 61 public function __construct(BlockManagerInterface $block_manager, EntityTypeManagerInterface $entity_type_manager, AccountInterface $current_user) { 62 $this->blockManager = $block_manager; 63 $this->entityTypeManager = $entity_type_manager; 64 $this->currentUser = $current_user; 65 } 66 67 /** 68 * {@inheritdoc} 69 */ 70 public static function create(ContainerInterface $container) { 71 return new static( 72 $container->get('plugin.manager.block'), 73 $container->get('entity_type.manager'), 74 $container->get('current_user') 75 ); 76 } 77 78 /** 79 * Provides the UI for choosing a new block. 80 * 81 * @param \Drupal\layout_builder\SectionStorageInterface $section_storage 82 * The section storage. 83 * @param int $delta 84 * The delta of the section to splice. 85 * @param string $region 86 * The region the block is going in. 87 * 88 * @return array 89 * A render array. 90 */ 91 public function build(SectionStorageInterface $section_storage, int $delta, $region) { 92 if ($this->entityTypeManager->hasDefinition('block_content_type') && $types = $this->entityTypeManager->getStorage('block_content_type')->loadMultiple()) { 93 if (count($types) === 1) { 94 $type = reset($types); 95 $plugin_id = 'inline_block:' . $type->id(); 96 if ($this->blockManager->hasDefinition($plugin_id)) { 97 $url = Url::fromRoute('layout_builder.add_block', [ 98 'section_storage_type' => $section_storage->getStorageType(), 99 'section_storage' => $section_storage->getStorageId(), 100 'delta' => $delta, 101 'region' => $region, 102 'plugin_id' => $plugin_id, 103 ]); 104 } 105 } 106 else { 107 $url = Url::fromRoute('layout_builder.choose_inline_block', [ 108 'section_storage_type' => $section_storage->getStorageType(), 109 'section_storage' => $section_storage->getStorageId(), 110 'delta' => $delta, 111 'region' => $region, 112 ]); 113 } 114 if (isset($url)) { 115 $build['add_block'] = [ 116 '#type' => 'link', 117 '#url' => $url, 118 '#title' => $this->t('Create @entity_type', [ 119 '@entity_type' => $this->entityTypeManager->getDefinition('block_content')->getSingularLabel(), 120 ]), 121 '#attributes' => $this->getAjaxAttributes(), 122 '#access' => $this->currentUser->hasPermission('create and edit custom blocks'), 123 ]; 124 $build['add_block']['#attributes']['class'][] = 'inline-block-create-button'; 125 } 126 } 127 128 $build['filter'] = [ 129 '#type' => 'search', 130 '#title' => $this->t('Filter by block name'), 131 '#title_display' => 'invisible', 132 '#size' => 30, 133 '#placeholder' => $this->t('Filter by block name'), 134 '#attributes' => [ 135 'class' => ['js-layout-builder-filter'], 136 'title' => $this->t('Enter a part of the block name to filter by.'), 137 ], 138 ]; 139 140 $block_categories['#type'] = 'container'; 141 $block_categories['#attributes']['class'][] = 'block-categories'; 142 $block_categories['#attributes']['class'][] = 'js-layout-builder-categories'; 143 $block_categories['#attributes']['data-layout-builder-target-highlight-id'] = $this->blockAddHighlightId($delta, $region); 144 145 $definitions = $this->blockManager->getFilteredDefinitions('layout_builder', $this->getAvailableContexts($section_storage), [ 146 'section_storage' => $section_storage, 147 'delta' => $delta, 148 'region' => $region, 149 ]); 150 $grouped_definitions = $this->blockManager->getGroupedDefinitions($definitions); 151 foreach ($grouped_definitions as $category => $blocks) { 152 $block_categories[$category]['#type'] = 'details'; 153 $block_categories[$category]['#attributes']['class'][] = 'js-layout-builder-category'; 154 $block_categories[$category]['#open'] = TRUE; 155 $block_categories[$category]['#title'] = $category; 156 $block_categories[$category]['links'] = $this->getBlockLinks($section_storage, $delta, $region, $blocks); 157 } 158 $build['block_categories'] = $block_categories; 159 return $build; 160 } 161 162 /** 163 * Provides the UI for choosing a new inline block. 164 * 165 * @param \Drupal\layout_builder\SectionStorageInterface $section_storage 166 * The section storage. 167 * @param int $delta 168 * The delta of the section to splice. 169 * @param string $region 170 * The region the block is going in. 171 * 172 * @return array 173 * A render array. 174 */ 175 public function inlineBlockList(SectionStorageInterface $section_storage, int $delta, $region) { 176 $definitions = $this->blockManager->getFilteredDefinitions('layout_builder', $this->getAvailableContexts($section_storage), [ 177 'section_storage' => $section_storage, 178 'region' => $region, 179 'list' => 'inline_blocks', 180 ]); 181 $blocks = $this->blockManager->getGroupedDefinitions($definitions); 182 $build = []; 183 $inline_blocks_category = (string) $this->t('Inline blocks'); 184 if (isset($blocks[$inline_blocks_category])) { 185 $build['links'] = $this->getBlockLinks($section_storage, $delta, $region, $blocks[$inline_blocks_category]); 186 $build['links']['#attributes']['class'][] = 'inline-block-list'; 187 foreach ($build['links']['#links'] as &$link) { 188 $link['attributes']['class'][] = 'inline-block-list__item'; 189 } 190 $build['back_button'] = [ 191 '#type' => 'link', 192 '#url' => Url::fromRoute('layout_builder.choose_block', 193 [ 194 'section_storage_type' => $section_storage->getStorageType(), 195 'section_storage' => $section_storage->getStorageId(), 196 'delta' => $delta, 197 'region' => $region, 198 ] 199 ), 200 '#title' => $this->t('Back'), 201 '#attributes' => $this->getAjaxAttributes(), 202 ]; 203 } 204 $build['links']['#attributes']['data-layout-builder-target-highlight-id'] = $this->blockAddHighlightId($delta, $region); 205 return $build; 206 } 207 208 /** 209 * Gets a render array of block links. 210 * 211 * @param \Drupal\layout_builder\SectionStorageInterface $section_storage 212 * The section storage. 213 * @param int $delta 214 * The delta of the section to splice. 215 * @param string $region 216 * The region the block is going in. 217 * @param array $blocks 218 * The information for each block. 219 * 220 * @return array 221 * The block links render array. 222 */ 223 protected function getBlockLinks(SectionStorageInterface $section_storage, int $delta, $region, array $blocks) { 224 $links = []; 225 foreach ($blocks as $block_id => $block) { 226 $attributes = $this->getAjaxAttributes(); 227 $attributes['class'][] = 'js-layout-builder-block-link'; 228 $link = [ 229 'title' => $block['admin_label'], 230 'url' => Url::fromRoute('layout_builder.add_block', 231 [ 232 'section_storage_type' => $section_storage->getStorageType(), 233 'section_storage' => $section_storage->getStorageId(), 234 'delta' => $delta, 235 'region' => $region, 236 'plugin_id' => $block_id, 237 ] 238 ), 239 'attributes' => $attributes, 240 ]; 241 242 $links[] = $link; 243 } 244 return [ 245 '#theme' => 'links', 246 '#links' => $links, 247 ]; 248 } 249 250 /** 251 * Get dialog attributes if an ajax request. 252 * 253 * @return array 254 * The attributes array. 255 */ 256 protected function getAjaxAttributes() { 257 if ($this->isAjax()) { 258 return [ 259 'class' => ['use-ajax'], 260 'data-dialog-type' => 'dialog', 261 'data-dialog-renderer' => 'off_canvas', 262 ]; 263 } 264 return []; 265 } 266 267} 268