layout_builder_paragraphs-1.0.x-dev/src/Controller/ChooseParagraphController.php
src/Controller/ChooseParagraphController.php
<?php
namespace Drupal\layout_builder_paragraphs\Controller;
use Drupal\layout_builder\Controller\ChooseBlockController;
use Drupal\Core\Ajax\AjaxHelperTrait;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
use Drupal\layout_builder\LayoutBuilderHighlightTrait;
use Drupal\layout_builder\SectionStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Component\Serialization\Json;
/**
* Defines a controller to choose a new block.
*
* @internal
* Controller classes are internal.
*/
class ChooseParagraphController extends ChooseBlockController {
/**
* {@inheritdoc}
*/
public function build(SectionStorageInterface $section_storage, $delta, $region) {
// Start with the parent form.
$build = parent::build($section_storage, $delta, $region);
// First, as we need the entity context, we check if that exists. Otherwise
// return the original form.
$context_values = $section_storage->getContextValues();
if (empty($context_values['entity'])) return $build;
// We only progress when the entity has specific paragraph reference field.
// @TODO: make this more generic. E.g. check that that least there is
// paragraph reference field, regardless of the name.
$entity = $section_storage->getContextValue('entity');
if (!$entity->hasField(PARAGRAPH_FIELD)) return $build;
// Unset add block.
if (isset($build['add_block'])) unset($build['add_block']);
// Add choose paragraph link. It's assumed that we have multiple paragraph
// types.
$url = Url::fromRoute('layout_builder_paragraphs.choose_paragraph', [
'section_storage_type' => $section_storage->getStorageType(),
'section_storage' => $section_storage->getStorageId(),
'delta' => $delta,
'region' => $region,
]);
if (isset($url)) {
$build['choose_paragraph'] = [
'#type' => 'link',
'#url' => $url,
// Right now it's hardcoded to paragraph.
// @TODO: make this more generic.
'#title' => $this->t('Create @entity_type', [
'@entity_type' => $this->entityTypeManager->getDefinition('paragraph')->getSingularLabel(),
]),
'#attributes' => parent::getAjaxAttributes(), // Use dialog.
// As user has access to the layout builder tab, user must have access to the entity.
// @TODO: make this more generic. E.g. check that the user has access
// to the paragraph reference field.
'#access' => TRUE,
'#weight' => -100,
];
$build['choose_paragraph']['#attributes']['class'][] = 'inline-block-create-button';
}
// Start with collapsed block categories.
foreach ($build['block_categories'] as &$block_category) {
if (!empty($block_category['#type']) && $block_category['#type'] == 'details') {
$block_category['#open'] = FALSE;
}
}
return $build;
}
/**
* Provides the UI for choosing the type for the new paragraph.
*
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
* The section storage.
* @param int $delta
* The delta of the section to splice.
* @param string $region
* The region the block is going in.
*
* @return array
* A render array.
*/
public function paragraphList(SectionStorageInterface $section_storage, $delta, $region) {
// @TODO: check the access to various paragraph types, refer to paragraphs_type_permissions module.
$entity = $section_storage->getContextValue('entity');
$field_definition = $entity->getFieldDefinition(PARAGRAPH_FIELD);
// The following lines are adapted from ParagraphsWidget::getAllowedTypes().
$selection_manager = \Drupal::service('plugin.manager.entity_reference_selection');
$handler = $selection_manager->getSelectionHandler($field_definition);
$allowed_paragraph_types = $handler->getSortedAllowedTypes();
$build = [];
$links = [];
// Create paragraph_blocks equivalent plugin id. E.g.
// paragraph_field:node:field_contents:0:landing_page
$paragraph_plugin_id = [
'parent_type' => $entity->getEntityTypeId(),
'field' => PARAGRAPH_FIELD,
'delta' => $entity->{PARAGRAPH_FIELD}->count(),
'parent_bundle' => $entity->bundle(),
];
$paragraph_plugin_id = implode(':', $paragraph_plugin_id);
// Render links for all allowed paragraph types.
foreach ($allowed_paragraph_types as $paragraph_type => $paragraph) {
$paragraph_type_entity = \Drupal\paragraphs\Entity\ParagraphsType::load($paragraph_type);
$icon_url = $paragraph_type_entity->getIconUrl();
$paragraph_label = $paragraph_type_entity->label();
// if paragraph icon url is not empty
if (!empty($icon_url)) {
$title_with_icon = '<img src="' . $icon_url . '" alt="' . $paragraph_label . '" class="paragraph-icon">' . $paragraph_label;
$link_title = $this->t($title_with_icon);
}
// If paragraph icon url is empty, use default icons
else {
$module_handler = \Drupal::service('module_handler');
$module_path = $module_handler->getModule('layout_builder_paragraphs')->getPath();
$default_icon_path = '/' . $module_path . '/images/default-paragraph-icon.png';
$title_with_icon = '<img src="' . $default_icon_path . '" alt="Paragraph icon" class="paragraph-icon">' . $paragraph_label;
$link_title = $this->t($title_with_icon);
}
$attributes = [];
// @TODO: To use the paragraph form in modal, we need to solve the issue
// of modal media browser closing the underlying paragraph form.
// $attributes = $this->getAjaxAttributes(); // Use modal.
$attributes['class'][] = 'js-layout-builder-block-link';
$attributes['class'][] = \Drupal::moduleHandler()->moduleExists('gin_lb') ? 'create-paragraph-item' : 'inline-block-list__item';
$link = [
'title' => $link_title,
'url' => Url::fromRoute('layout_builder_paragraphs.add_paragraph',
[
'section_storage_type' => $section_storage->getStorageType(),
'section_storage' => $section_storage->getStorageId(),
'delta' => $delta,
'region' => $region,
'plugin_id' => $paragraph_plugin_id,
'paragraph_type' => $paragraph_type,
]
),
'attributes' => $attributes,
];
$links[] = $link;
}
// The remaining form setup mimics ChooseBlockController::inlineBlockList().
$build['links'] = [
'#theme' => 'links',
'#links' => $links,
'#attributes' => [
'class' => [
\Drupal::moduleHandler()->moduleExists('gin_lb') ? 'glb-list' : 'inline-block-list',
],
'data-layout-builder-target-highlight-id' => $this->blockAddHighlightId($delta, $region),
],
];
$build['back_button'] = [
'#type' => 'link',
'#url' => Url::fromRoute('layout_builder.choose_block',
[
'section_storage_type' => $section_storage->getStorageType(),
'section_storage' => $section_storage->getStorageId(),
'delta' => $delta,
'region' => $region,
]
),
'#title' => $this->t('Back'),
'#attributes' => parent::getAjaxAttributes(), // Use dialog.
];
return $build;
}
/**
* {@inheritdoc}
*/
protected function getAjaxAttributes($modal = NULL) {
if ($this->isAjax()) {
return [
'class' => ['use-ajax'],
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode(['width' => '80vw']),
];
}
return [];
}
}
