lb_plus-1.0.x-dev/src/Controller/DropZones.php

src/Controller/DropZones.php
<?php

namespace Drupal\lb_plus\Controller;

use Drupal\Core\Url;
use Drupal\Core\Database\Database;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\lb_plus\LbPlusRebuildTrait;
use Drupal\lb_plus\LbPlusSettingsTrait;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\lb_plus\SectionStorageHandler;
use Drupal\Core\Controller\ControllerBase;
use Drupal\layout_builder\SectionComponent;
use Drupal\Core\Layout\LayoutPluginManager;
use Drupal\Core\Block\BlockManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\lb_plus\Dropzones as DropzonesService;
use Drupal\layout_builder\SectionStorageInterface;
use Drupal\layout_builder\Plugin\Block\FieldBlock;
use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Returns responses for Layout Builder + routes.
 */
class DropZones extends ControllerBase {

  use ContextAwarePluginAssignmentTrait;
  use LayoutBuilderContextTrait;
  use LbPlusSettingsTrait;
  use LbPlusRebuildTrait;


  public function __construct(
    protected LayoutTempstoreRepositoryInterface $layoutTempstoreRepository,
    protected SectionStorageHandler $sectionStorageHandler,
    protected LayoutPluginManager $layoutManager,
    protected BlockManagerInterface $blockManager,
    protected DropzonesService $dropzones,
    protected EventDispatcherInterface $eventDispatcher,
    protected UuidInterface $uuid,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('layout_builder.tempstore_repository'),
      $container->get('lb_plus.section_storage_handler'),
      $container->get('plugin.manager.core.layout'),
      $container->get('plugin.manager.block'),
      $container->get('lb_plus.dropzones'),
      $container->get('event_dispatcher'),
      $container->get('uuid'),
    );
  }

  /**
   * Move section.
   *
   * Stores the updated position after a section has been moved and reordered.
   */
  public function moveSection(Request $request, SectionStorageInterface $section_storage) {
    try {
      $transaction = Database::getConnection()->startTransaction();
      // Get the section info from drop-zones.js.
      $from_section_delta = $request->get('from_section_delta');
      $preceding_section_delta = $request->get('preceding_section_delta');
      $nested_storage_path_to = $request->get('nested_storage_path_to');
      $nested_storage_path_from = $request->get('nested_storage_path_from');

      // Get the section storage where the section will be placed.
      $section_storage_to = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path_to);

      // Remove the section from the "from" section storage.
      if ($nested_storage_path_to !== $nested_storage_path_from) {
        $from_section_storage = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path_from);
        $section = $from_section_storage->getSection($from_section_delta);
        $from_section_storage->removeSection($from_section_delta);
        // Update the from section storage.
        $section_storage = $this->sectionStorageHandler->updateSectionStorage($section_storage, $nested_storage_path_from, $from_section_storage);
        $section_storage_to = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path_to);
      } else {
        $section = $section_storage_to->getSection($from_section_delta);
        $section_storage_to->removeSection($from_section_delta);
      }

      // If the section was moved from higher to lower on the page we need to
      // account for the delta's changing after it was removed.
      $new_delta = $from_section_delta < $preceding_section_delta ? $preceding_section_delta - 1 : $preceding_section_delta;
      $section_storage_to->insertSection($new_delta, $section);

      $section_storage = $this->sectionStorageHandler->updateSectionStorage($section_storage, $nested_storage_path_to, $section_storage_to);

      // Prepare an updated Layout Builder UI response.
      $response = new AjaxResponse();
      $response->addCommand(new RemoveCommand('[data-drupal-messages]'));
      $layout = [
        '#type' => 'layout_builder_plus',
        '#section_storage' => $section_storage,
        '#nested_storage_path' => $nested_storage_path_to,
      ];
      $selector = '#layout-builder';
      if (!empty($nested_storage_path_to)) {
        // Replace the nested layout.
        $pieces = SectionStorageHandler::decodeNestedStoragePath($nested_storage_path_to);
        $storage_uuid = end($pieces);
        $selector = "[data-nested-storage-uuid='$storage_uuid']";
      }
      $response->addCommand(new ReplaceCommand($selector, $layout));

      return $response;
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        $transaction->rollBack();
      }
      throw $e;
    }
  }

  /**
   * Move section.
   *
   * Stores the updated position after a section has been moved and reordered.
   */
  public function addEmptySection(Request $request, SectionStorageInterface $section_storage, string $nested_storage_path = NULL) {
    $current_section_storage = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path);
    // Get the new next_section from drop-zones.js.
    $next_section = $request->get('preceding_section');

    // Place the a blank section.
    [$blank_section, $delta] = $this->dropzones->getSection([
      'type' => 'section',
      'section' => $next_section,
    ], $current_section_storage);

    $section_storage = $this->sectionStorageHandler->updateSectionStorage($section_storage, $nested_storage_path, $current_section_storage);
    $response = $this->rebuildLayout($section_storage, $nested_storage_path);

    $uuid = $blank_section->getThirdPartySetting('lb_plus', 'uuid');
    $response->addCommand(new InvokeCommand('', 'LBPlusChangeLayout', [$uuid]));
    return $response;
  }

  /**
   * Place block.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
   *   The section storage.
   * @param string|null $nested_storage_path
   *   The nested storage path.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The layout builder UI with the updated block placement.
   *
   * @throws \Drupal\Component\Plugin\Exception\ContextException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function placeBlock(Request $request, SectionStorageInterface $section_storage, string $nested_storage_path = NULL) {
    $current_section_storage = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path);

    // Get the block information from block.js.
    $block_info = $request->get('place_block');
    if (empty($block_info)) {
      throw new \InvalidArgumentException('Missing block placement info.');
    }

    // Ensure we have a valid destination type.
    if (!in_array($block_info['destination']['type'], ['region', 'section'])) {
      throw new \InvalidArgumentException('Invalid block_plugin_id');
    }

    [$section, $section_delta] = $this->dropzones->getSection($block_info['destination'], $current_section_storage);
    if ($block_info['destination']['type'] !== 'region') {
      $block_info['destination']['region'] = $section->getDefaultRegion();
    }

    $block_plugin = $this->dropzones->createBlockPlugin($block_info['plugin_id'], $current_section_storage);
    $this->dropzones->createBlockContent($block_plugin);
    $component = $this->dropzones->insertBlock($block_info['destination'], $block_plugin, $section);
    $section_storage = $this->sectionStorageHandler->updateSectionStorage($section_storage, $nested_storage_path, $current_section_storage);

    $response = $this->rebuildLayout($section_storage, $nested_storage_path);

    if ($this->sectionStorageHandler->isLayoutBlock($block_plugin)) {
      // We are placing a layout block, so lets edit the layout block.
      $layout_block_storage_path = $this->sectionStorageHandler->encodeNestedStoragePath([$section_delta, $component->getUuid()]);
      $edit_layout_block_url = Url::fromRoute('lb_plus.contextual_link.layout_block.edit', [
        'section_storage_type' => $current_section_storage->getStorageType(),
        'section_storage' => $current_section_storage->getStorageId(),
        'nested_storage_path' => ($nested_storage_path ? "$nested_storage_path&" : '') . $layout_block_storage_path,
      ])->toString();
      $response->addCommand(new InvokeCommand(NULL, 'LBPlusEditLayout', [$edit_layout_block_url]));
    }

    return $response;
  }

  /**
   * Move block.
   *
   * Moves an existing block from one place on the page to another.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
   *   The section storage.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The updated layout builder.
   *
   * @throws \Drupal\Component\Plugin\Exception\ContextException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function moveBlock(Request $request, SectionStorageInterface $section_storage) {
    try {
      $transaction = Database::getConnection()->startTransaction();
      // Get the block information from block.js.
      $block_info = $request->get('place_block');
      if (empty($block_info)) {
        throw new \InvalidArgumentException('Missing block placement info.');
      }

      // Ensure we have a valid destination type.
      if (!in_array($block_info['destination']['type'], ['region', 'section'])) {
        throw new \InvalidArgumentException('Invalid block destination type');
      }

      $delta_from = $block_info['destination']['delta_from'] ?? NULL;
      $delta_to = $block_info['destination']['delta_to'] ?? NULL;
      $region_to = $block_info['destination']['region_to'] ?? NULL;
      $block_uuid = $block_info['destination']['block_uuid'] ?? NULL;
      $preceding_block_uuid = $block_info['destination']['preceding_block_uuid'] ?? NULL;
      $nested_storage_path_from = $block_info['destination']['nested_storage_path_from'] ?? NULL;
      $nested_storage_path_to = $block_info['destination']['nested_storage_path_to'] ?? NULL;
      $changing_section_storage = $nested_storage_path_to !== $nested_storage_path_from;

      // Get the section storage where the block will be placed.
      $section_storage_to = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path_to);

      // Remove the component from the section storage.
      if ($changing_section_storage) {
        $from_section_storage = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path_from);
        $section = $from_section_storage->getSection($delta_from);
      } else {
        $section = $section_storage_to->getSection($delta_from);
      }
      $component = $section->getComponent($block_uuid);
      $section->removeComponent($block_uuid);

      // If the block is moving to a new section storage update the from section
      // storage before placing the block.
      if ($changing_section_storage) {
        $section_storage = $this->sectionStorageHandler->updateSectionStorage($section_storage, $nested_storage_path_from, $from_section_storage);
        $section_storage_to = $this->sectionStorageHandler->getCurrentSectionStorage($section_storage, $nested_storage_path_to);
      }

      // Get the destination section.
      if ($block_info['destination']['type'] === 'section') {
        [$section, $section_delta] = $this->dropzones->getSection($block_info['destination'], $section_storage_to);
        $component->setRegion($section->getDefaultRegion());
        if (is_null($section->getLayoutId())) {
          throw new \InvalidArgumentException('Please configure a default layout for this section.');
        }
      } else {
        $section = $section_storage_to->getSection($delta_to);
        $component->setRegion($region_to);
      }

      $block = $component->getPlugin();
      $configuration = $block->getConfiguration();
      // Are we moving a field block to or from a nested entity?
      if ($block instanceof FieldBlock && $changing_section_storage && !empty($configuration['context_mapping']['entity'])) {
        if (empty($nested_storage_path_to)) {
          $configuration['context_mapping']['entity'] = $this->sectionStorageHandler->mapContextBackToLbEntity($section_storage_to, $configuration['context_mapping']['entity']);
        }
        else {
          $configuration['context_mapping']['entity'] = $this->sectionStorageHandler->mapContextToParentEntity($from_section_storage, $configuration['context_mapping']['entity']);
        }
        $component = new SectionComponent($component->getUuid(), $component->getRegion(), $configuration);
      }

      // Place the block in it's new location.
      if (!empty($preceding_block_uuid) && $preceding_block_uuid !== 'null') {
        $section->insertAfterComponent($preceding_block_uuid, $component);
      }
      else {
        $section->insertComponent(0, $component);
      }
      // Save the changes.
      $section_storage = $this->sectionStorageHandler->updateSectionStorage($section_storage, $nested_storage_path_to, $section_storage_to);

      // Prepare an updated Layout Builder UI response.
      $response = new AjaxResponse();
      $response->addCommand(new RemoveCommand('[data-drupal-messages]'));
      $layout = [
        '#type' => 'layout_builder_plus',
        '#section_storage' => $section_storage,
        '#nested_storage_path' => $nested_storage_path_to,
      ];
      $selector = '#layout-builder';
      if (!empty($nested_storage_path_to)) {
        // Replace the nested layout.
        $pieces = SectionStorageHandler::decodeNestedStoragePath($nested_storage_path_to);
        $storage_uuid = end($pieces);
        $selector = "[data-nested-storage-uuid='$storage_uuid']";
        if ($changing_section_storage) {
          // Remove the moved block if it was moved from the page to a nested layout.
          $response->addCommand(new RemoveCommand("[data-block-uuid='$block_uuid']"));
        }
      }
      $response->addCommand(new ReplaceCommand($selector, $layout));
      return $response;
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        $transaction->rollBack();
      }
      throw $e;
    }
  }


}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc