layoutcomponents-8.x-1.14-beta2/src/Element/LcElement.php
src/Element/LcElement.php
<?php
namespace Drupal\layoutcomponents\Element;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\layout_builder\Element\LayoutBuilder;
use Drupal\Core\Config\ConfigFactory;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\SectionStorageInterface;
use Drupal\Core\Url;
use Drupal\Core\Render\Element;
use Drupal\layoutcomponents\Access\LcAccessHelperTrait;
use Drupal\layoutcomponents\LcDialogHelperTrait;
use Drupal\layoutcomponents\LcSectionManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\layoutcomponents\LcLayoutsManager;
use Drupal\layoutcomponents\LcDisplayHelperTrait;
use Drupal\Core\Session\AccountProxy;
use Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* {@inheritdoc}
*/
class LcElement extends LayoutBuilder {
use LcDisplayHelperTrait;
use LcDialogHelperTrait;
use LcAccessHelperTrait;
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* Config factory object.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $configFactory;
/**
* Drupal\Core\TempStore\PrivateTempStoreFactory definition.
*
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The Layout Tempstore.
*
* @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
*/
protected $layoutTempstore;
/**
* The LC manager.
*
* @var \Drupal\layoutcomponents\LcLayoutsManager
*/
protected $lcLayoutManager;
/**
* Drupal\Core\Session\AccountProxy definition.
*
* @var \Drupal\Core\Session\AccountProxy
*/
protected $currentUser;
/**
* Drupal\layoutcomponents\LcSectionManager definition.
*
* @var \Drupal\layoutcomponents\LcSectionManager
*/
protected $lcSectionManager;
/**
* Current Entity.
*
* @var \Drupal\node\Entity\Node
*/
protected $entity;
/**
* The event dispatcher.
*
* @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher , LayoutTempstoreRepositoryInterface $layout_tempstore_repository, MessengerInterface $messenger, ThemeHandlerInterface $theme_handler, ConfigFactory $config_factory, PrivateTempStoreFactory $temp_store, LcLayoutsManager $layout_manager, AccountProxy $current_user, LcSectionManager $lc_section_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher);
$this->themeHandler = $theme_handler;
$this->configFactory = $config_factory;
$this->tempStoreFactory = $temp_store;
$this->eventDispatcher = $event_dispatcher;
$this->layoutTempstore = $layout_tempstore_repository;
$this->lcLayoutManager = $layout_manager;
$this->currentUser = $current_user;
$this->lcSectionManager = $lc_section_manager;
$this->entity = $this->getCurrentEntity();
$this->messenger = $messenger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('event_dispatcher'),
$container->get('layout_builder.tempstore_repository'),
$container->get('messenger'),
$container->get('theme_handler'),
$container->get('config.factory'),
$container->get('tempstore.private'),
$container->get('plugin.manager.layoutcomponents_layouts'),
$container->get('current_user'),
$container->get('layoutcomponents.section')
);
}
/**
* {@inheritdoc}
*/
public function layout(SectionStorageInterface $section_storage) {
$output = parent::layout($section_storage);
$output['#attached']['library'][] = 'layoutcomponents/layoutcomponents.editform';
// Info.
$this->messenger->addWarning($this->t('To use LayoutComponents is completely necessary a Bootstrap4-5 theme.'));
// Storage settings.
$storage_type = $section_storage->getStorageType();
$storage_id = $section_storage->getStorageId();
// Allow remove clipboard.
/** @var \Drupal\Core\TempStore\PrivateTempStore $store */
$store = $this->tempStoreFactory->get('lc');
$data = $store->get('lc_element');
$clipboard_attr = (!empty($data)) ?: 'hidden';
// Remove the content of clipboard.
$output['clipboard'] = [
'#type' => 'link',
'#title' => 'Remove clipboard',
'#url' => Url::fromRoute('layoutcomponents.copy_remove',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__link',
'lc-clipboard',
$clipboard_attr,
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Remove the content of clipboard'),
],
]),
'#weight' => -1,
];
// Hide sub sections.
foreach ($output as $delta => $section) {
if (!is_numeric($delta)) {
continue;
}
if (!array_key_exists('layout-builder__section', $section)) {
continue;
}
if (array_key_exists('sub_section', $section['layout-builder__section']['#settings'])) {
unset($output[$delta]);
}
}
return $output;
}
/**
* {@inheritdoc}
*/
public function prepareLayout(SectionStorageInterface $section_storage) {
if (!$section_storage instanceof DefaultsSectionStorage) {
// Content sections.
$sections = $this->getOrderedSections($section_storage);
// Set the rest of defaults.
foreach ($sections as $delta => $section) {
if (!isset($section)) {
continue;
}
if ($section->getLayoutId() == 'layout_builder_blank') {
continue;
}
$section_storage->appendSection($section);
}
// Is neccesary register the changes in the temp store repository.
// instead of save object, if not the option "Discard changes" won't.
// works correctly.
$this->layoutTempstore->set($section_storage);
}
// Send the new structure to the default event.
parent::prepareLayout($section_storage);
}
/**
* {@inheritdoc}
*/
public function buildAddSectionLink(SectionStorageInterface $section_storage, $delta, $sub_section = []) {
$build = parent::buildAddSectionLink($section_storage, $delta);
$build['link']['#title'] = '';
// Alter Add Section button.
/** @var \Drupal\Core\Url $url */
$url = $build['link']['#url'];
// Set update_layout if is "Add section".
$url->setRouteParameter('update_layout', 0);
// Set this section as sub section.
$url->setRouteParameter('sub_section', $sub_section);
// Remove link--add class.
$options = $url->getOptions();
$options['attributes']['class'] = [
'use-ajax',
'link-rounded',
'lc_editor-link',
'layout-builder__link',
'layout-builder__link-add-section',
];
$options['attributes']['data-dialog-options'] = $this->dialogOptions();
$options['attributes']['title'] = $this->t('Add new section');
// Check if a section is ready to copy.
$store = $this->tempStoreFactory->get('lc');
$data = $store->get('lc_element');
$options['attributes']['class']['lc-copy'] = ((isset($data)) && ($data['type'] == 'section')) ? 'lc-copy' : '';
// Save new options.
$url->setOptions($options);
if (isset($this->entity) && !$this->getAccess($this->currentUser, 'create ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' sections')) {
unset($build['link']['#url']);
}
return $build;
}
/**
* {@inheritdoc}
*/
public function buildAdministrativeSection(SectionStorageInterface $section_storage, $delta, $region = 'first') {
$build = parent::buildAdministrativeSection($section_storage, $delta);
// Storage settings.
$storage_type = $section_storage->getStorageType();
$storage_id = $section_storage->getStorageId();
$section = $section_storage->getSection($delta);
$layout = $section->getLayout();
$layout_definition = $layout->getPluginDefinition();
$is_sub_section = $this->lcSectionManager->isSubSection($section_storage, $delta);
// Alter configure button.
$configure['configure'] = $build['configure'];
$configure['configure']['#url'] = $this->addTooltip($configure['configure']['#url'], 'Configure this section');
$configure['configure']['#title'] = '';
$configure['configure']['#attributes']['data-dialog-options'] = $this->dialogOptions();
$configure['configure']['#attributes']['class'] = [
'use-ajax',
'lc_editor-link',
'layout-builder__section_link',
'layout-builder__section_link-configure',
];
// Alter remove button.
$remove['remove'] = $build['remove'];
$remove['remove']['#url'] = $this->addTooltip($remove['remove']['#url'], 'Remove this section');
$remove['remove']['#title'] = '';
$remove['remove']['#attributes']['data-dialog-options'] = $this->dialogOptions();
$remove['remove']['#attributes']['class'] = [
'use-ajax',
'lc_editor-link',
'layout-builder__section_link',
'layout-builder__section_link-remove',
];
// Add change layout button.
$update = [
'move_layout' => [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromRoute('layout_builder.move_sections_form',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
'sub_section' => ($is_sub_section) ? ['delta' => $delta] : '',
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__section_link',
'layout-builder__section_link-move',
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Move section'),
],
]
),
'#weight' => -1,
],
'update_layout' => [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromRoute('layout_builder.choose_section',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
'delta' => $delta,
'update_layout' => 1,
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__section_link',
'layout-builder__section_link-update',
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Change layout'),
],
]
),
],
/*'copy' => [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromRoute('layoutcomponents.copy_section',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
'delta' => $delta,
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__section_link',
'layout-builder__section_link-copy',
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Copy to clipboard'),
],
]
),
],*/
];
// Section access control.
if (isset($this->entity)) {
if (!$this->getAccess($this->currentUser, 'configure all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' sections')) {
unset($configure['configure']);
}
if (!$this->getAccess($this->currentUser, 'remove all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' sections')) {
unset($remove['remove']);
}
if (!$this->getAccess($this->currentUser, 'move all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' sections')) {
unset($update['move_layout']);
}
if (!$this->getAccess($this->currentUser, 'change all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' layout sections')) {
unset($update['update_layout']);
}
if (!$this->getAccess($this->currentUser, 'copy all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' sections')) {
unset($update['copy']);
}
}
// Reorder section links.
$new_config = $remove + $configure + $update;
$new_config['#type'] = 'container';
$new_config['#attributes']['class'] = 'layout_builder__configure_section_items';
$new_config['#weight'] = -1;
// Remove old buttons.
unset($build['configure']);
unset($build['remove']);
$build['layout-builder__configure_section'] = $new_config;
foreach ($layout_definition->getRegions() as $region => $info) {
// Blocks.
$section = &$build['layout-builder__section'];
if (!empty($section[$region])) {
foreach (Element::children($section[$region]) as $uuid) {
if (array_key_exists('#contextual_links', $section[$region][$uuid])) {
// Implement buildAdministrativeBlock().
$section[$region][$uuid]['content']['layout_builder-configuration'] = $this->buildAdministrativeBlock($storage_type, $storage_id, $delta, $region, $uuid);
}
}
}
// Process Add Block button.
$addBlock = &$build['layout-builder__section'][$region]['layout_builder_add_block'];
$addBlock['link']['#title'] = '';
$addBlock['#weight'] = 999;
/** @var \Drupal\Core\Url $url */
$url = $addBlock['link']['#url'];
// Remove link--add class.
$options = $url->getOptions();
$options['attributes']['class'] = [
'use-ajax',
'lc_editor-link',
'link-rounded',
'layout-builder__column_link',
'layout-builder__column_link-add',
];
$options['attributes']['data-dialog-options'] = $this->dialogOptions();
$options['attributes']['title'] = $this->t('Add new block');
// Check if a block is ready to copy.
$store = $this->tempStoreFactory->get('lc');
$data = $store->get('lc_element');
$options['attributes']['class'][] = ((isset($data)) && ($data['type'] == 'block')) ? 'lc-copy' : '';
$url->setOptions($options);
// Include new Add section in columns.
$build['layout-builder__section'][$region]['layout_builder_add_section']['link'] = $this->buildAddSectionLink($section_storage, $delta, [
'parent_section' => $delta,
'parent_region' => $region,
]);
$build['layout-builder__section'][$region]['layout_builder_add_section']['#weight'] = 1000;
// Column.
$configureSection = [
'#type' => 'container',
'#attributes' => [
'class' => ['layout-builder__configure-column'],
],
'#weight' => 1,
];
// Check if a column is ready to copy.
$store = $this->tempStoreFactory->get('lc');
$data = $store->get('lc_element');
$copy = ((isset($data)) && ($data['type'] == 'column')) ? 'lc-copy' : '';
$configureSection['configure'] = [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromRoute('layoutcomponents.update_column',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
'delta' => $delta,
'region' => $region,
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__column_link',
'layout-builder__column_link-configure',
$copy,
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Configure column'),
],
]
),
];
// Column access control.
if (isset($this->entity)) {
if (!$this->getAccess($this->currentUser, 'add ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' blocks')) {
unset($build['layout-builder__section'][$region]['layout_builder_add_block']);
}
if (!$this->getAccess($this->currentUser, 'configure all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' columns')) {
unset($configureSection['configure']);
}
if (!$this->getAccess($this->currentUser, 'copy all ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' columns')) {
unset($configureSection['copy']);
}
}
// Reorder block links.
$build['layout-builder__section'][$region][] = $configureSection;
// Include sub sections.
$current_layout_settings = $section_storage->getSection($delta)->getLayoutSettings();
/** @var \Drupal\layout_builder\Section $dd_section */
foreach ($section_storage->getSections() as $dd => $dd_section) {
$dd_settings = $dd_section->getLayoutSettings();
if (empty($dd_settings['sub_section'])) {
continue;
}
if (!array_key_exists('lc_id', $dd_settings['sub_section'])) {
continue;
}
if (!array_key_exists('lc_id', $current_layout_settings)) {
$current_layout_settings['lc_id'] = \Drupal::service('uuid')->generate();
$section_storage->getSection($delta)->setLayoutSettings($current_layout_settings);
}
if ($dd_settings['sub_section']['lc_id'] == $current_layout_settings['lc_id'] && $dd_settings['sub_section']['parent_region'] == $region) {
$build['layout-builder__section'][$region]['sub_section'][] = $this->buildAdministrativeSection($section_storage, $dd, $region);
}
}
}
return $build;
}
/**
* Builds the render array for the layout block while editing.
*
* @param string $storage_type
* The section storage.
* @param string $storage_id
* The storage id.
* @param int $delta
* The delta of the section.
* @param string $region
* The region.
* @param string $uuid
* The uuid.
*
* @return array
* The render array for a given block.
*/
public function buildAdministrativeBlock($storage_type, $storage_id, $delta, $region, $uuid) {
$configureBlock = [
'#type' => 'container',
'#attributes' => [
'class' => ['layout-builder__configure-block'],
],
'#weight' => -2,
];
$configureBlock['move'] = [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromUserInput('#',
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__block_link',
'layout-builder__block_link-move',
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Move block'),
],
]
),
];
$configureBlock['remove'] = [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromRoute('layout_builder.remove_block',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
'delta' => $delta,
'region' => $region,
'uuid' => $uuid,
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__block_link',
'layout-builder__block_link-remove',
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Remove block'),
],
]
),
];
$configureBlock['configure'] = [
'#type' => 'link',
'#title' => '',
'#url' => Url::fromRoute('layout_builder.update_block',
[
'section_storage_type' => $storage_type,
'section_storage' => $storage_id,
'delta' => $delta,
'region' => $region,
'uuid' => $uuid,
],
[
'attributes' => [
'class' => [
'use-ajax',
'lc_editor-link',
'layout-builder__block_link',
'layout-builder__block_link-configure',
],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => $this->dialogOptions(),
'title' => $this->t('Configure block'),
'resizable' => TRUE,
],
]
),
];
// Block access control.
if (isset($this->entity)) {
if (!$this->getAccess($this->currentUser, 'move ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' blocks')) {
unset($configureBlock['move']);
}
if (!$this->getAccess($this->currentUser, 'remove ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' blocks')) {
unset($configureBlock['remove']);
}
if (!$this->getAccess($this->currentUser, 'configure ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' blocks')) {
unset($configureBlock['configure']);
}
if (!$this->getAccess($this->currentUser, 'copy ' . $this->entity->bundle() . ' ' . $this->entity->getEntityTypeId() . ' blocks')) {
unset($configureBlock['copy']);
}
}
return $configureBlock;
}
/**
* Provide tooltip for Url elements.
*
* @param \Drupal\Core\Url $url
* The section storage.
* @param string $text
* The text.
*
* @return \Drupal\Core\Url
* The url preprocessed.
*/
public function addTooltip(Url $url, $text) {
$options = $url->getOptions();
$options['attributes']['title'] = $text;
$url->setOptions($options);
return $url;
}
}
