block_editor-1.0.x-dev/src/Routing/BlockEditorRouteSubscriber.php

src/Routing/BlockEditorRouteSubscriber.php
<?php

namespace Drupal\block_editor\Routing;

use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\block_editor\Controller\BlockEditorController;
use Drupal\block_editor\Service\EntityManager;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

/**
 * Subscribes to entity edit routes to provide Block Editor forms.
 */
class BlockEditorRouteSubscriber extends RouteSubscriberBase {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The entity manager.
   *
   * @var \Drupal\block_editor\Service\EntityManager
   */
  protected $entityManager;

  /**
   * Constructs a new BlockEditorRouteSubscriber.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\block_editor\Service\EntityManager $entity_manager
   *   The entity manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityManager $entity_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->entityManager = $entity_manager;
  }

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    // Add Block Editor settings routes for all supported bundle entity types.
    $this->addBlockEditorSettingsRoutes($collection);

    /*
     * Instead of altering edit form route, we are adding a new route
     * for Block Editor edit form and add an access check to default edit route.
     * This is to avoid potential changes from other modules and/or themes.
     */
    $entity_types = $this->entityTypeManager->getDefinitions();

    // This will handle all entity types that follow the "standard" pattern.
    // For example, node.
    foreach ($entity_types as $entity_type_id => $entity_type) {

      if ($entity_type instanceof ContentEntityType) {
        // We need to check if the entity type is supported by Block Editor.
        // If not, we skip it.
        $config_entity_type_id = $entity_type->getBundleEntityType();
        if (empty($config_entity_type_id)) {
          continue;
        }

        $config_entity_type = $this->entityTypeManager
          ->getDefinition($config_entity_type_id);

        if (!$this->entityManager->entityTypeSupportsBlockEditor($config_entity_type)) {
          continue;
        }

        // Always provide dedicated Block Editor routes for supported entity
        // types.
        $this->ensureBlockEditorAddRoute($collection, $entity_type_id, $config_entity_type_id);
        $this->ensureBlockEditorEditRoute($collection, $entity_type_id);

        // For entity types where canonical IS the edit form
        // (like block_content), alter the canonical route to point to Block
        // Editor when enabled.
        $this->alterCanonicalRouteIfNeeded($collection, $entity_type);
      }
    }

    return $collection;
  }

  /**
   * Adds a dedicated Block Editor edit route for supported entity types.
   *
   * @param \Symfony\Component\Routing\RouteCollection $collection
   *   The route collection.
   * @param string $entity_type_id
   *   The content entity type ID (e.g. node).
   */
  protected function ensureBlockEditorEditRoute(RouteCollection $collection, string $entity_type_id): void {
    $default_route_name = 'entity.' . $entity_type_id . '.edit_form';
    $default_route = $collection->get($default_route_name);

    if (!$default_route) {
      return;
    }

    $block_editor_route_name = 'block_editor.entity.' . $entity_type_id . '.edit_form';

    if ($collection->get($block_editor_route_name)) {
      return;
    }

    $block_editor_route = new Route(
          $this->deriveBlockEditorPath($default_route->getPath()),
          $default_route->getDefaults(),
          $default_route->getRequirements(),
          $default_route->getOptions(),
          $default_route->getHost(),
          $default_route->getSchemes(),
          $default_route->getMethods()
      );

    $defaults = $default_route->getDefaults();
    unset($defaults['_controller']);
    $defaults['_entity_form'] = $entity_type_id . '.block_editor';
    if (!isset($defaults['_title_callback'])) {
      $defaults['_title_callback'] = BlockEditorController::class . '::editTitle';
    }
    $block_editor_route->setDefaults($defaults);

    $options = $block_editor_route->getOptions();
    $options['_admin_route'] = TRUE;
    if ($default_route->hasOption('parameters')) {
      $options['parameters'] = $default_route->getOption('parameters');
    }
    $block_editor_route->setOptions($options);

    $block_editor_route->setRequirement('_block_editor_form_access', 'TRUE');

    $collection->add($block_editor_route_name, $block_editor_route);
  }

  /**
   * Derives the Block Editor path from the default edit path.
   *
   * @param string $default_path
   *   The default edit path.
   *
   * @return string
   *   The derived Block Editor path.
   */
  protected function deriveBlockEditorPath(string $default_path): string {
    if (\str_ends_with($default_path, '/edit')) {
      return substr($default_path, 0, -5) . '/block-editor';
    }

    return rtrim($default_path, '/') . '/block-editor';
  }

  /**
   * Adds a dedicated Block Editor add route for supported entity types.
   */
  protected function ensureBlockEditorAddRoute(RouteCollection $collection, string $entity_type_id, string $config_entity_type_id): void {
    $candidate_routes = [
      'entity.' . $entity_type_id . '.add_form',
      $entity_type_id . '.add',
      $entity_type_id . '.add_form',
    ];

    $default_route = NULL;
    foreach ($candidate_routes as $candidate) {
      if ($route = $collection->get($candidate)) {
        $default_route = $route;
        break;
      }
    }

    if (!$default_route) {
      return;
    }

    $block_editor_route_name = 'block_editor.entity.' . $entity_type_id . '.add_form';
    if ($collection->get($block_editor_route_name)) {
      return;
    }

    $block_editor_route = new Route(
          $this->deriveBlockEditorAddPath($default_route->getPath()),
          $default_route->getDefaults(),
          $default_route->getRequirements(),
          $default_route->getOptions(),
          $default_route->getHost(),
          $default_route->getSchemes(),
          $default_route->getMethods()
      );

    $defaults = $default_route->getDefaults();
    unset($defaults['_entity_form']);
    unset($defaults['_controller']);
    unset($defaults['_title']);
    $defaults['_controller'] = BlockEditorController::class . '::addForm';
    $defaults['_title_callback'] = BlockEditorController::class . '::addTitle';
    $defaults['_block_editor_route'] = TRUE;
    $block_editor_route->setDefaults($defaults);

    $options = $block_editor_route->getOptions();
    $options['_admin_route'] = TRUE;
    if ($default_route->hasOption('parameters')) {
      $options['parameters'] = $default_route->getOption('parameters');
    }
    else {
      $options['parameters'][$config_entity_type_id] = [
        'type' => "entity:{$config_entity_type_id}",
      ];
    }
    $block_editor_route->setOptions($options);

    $block_editor_route->setRequirement('_block_editor_add_form_access', 'TRUE');

    $collection->add($block_editor_route_name, $block_editor_route);
  }

  /**
   * Derives the Block Editor path for add forms.
   */
  protected function deriveBlockEditorAddPath(string $default_path): string {
    return rtrim($default_path, '/') . '/block-editor';
  }

  /**
   * Alters canonical routes for entity types where canonical equals edit-form.
   *
   * For entity types like block_content where the canonical route IS the
   * edit form, we need to add an access check that redirects to Block Editor
   * when it's enabled.
   *
   * @param \Symfony\Component\Routing\RouteCollection $collection
   *   The route collection.
   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
   *   The content entity type.
   */
  protected function alterCanonicalRouteIfNeeded(RouteCollection $collection, ContentEntityType $entity_type): void {
    $entity_type_id = $entity_type->id();

    // Check if canonical and edit-form link templates are the same.
    $canonical_link = $entity_type->getLinkTemplate('canonical');
    $edit_form_link = $entity_type->getLinkTemplate('edit-form');

    if ($canonical_link !== $edit_form_link) {
      return;
    }

    // Alter both the canonical and edit_form routes to add Block Editor
    // access check.
    $canonical_route_name = 'entity.' . $entity_type_id . '.canonical';
    $edit_form_route_name = 'entity.' . $entity_type_id . '.edit_form';

    foreach ([$canonical_route_name, $edit_form_route_name] as $route_name) {
      $route = $collection->get($route_name);
      if (!$route) {
        continue;
      }

      // Add our access check to these routes.
      // This will redirect to Block Editor if it's enabled.
      $route->setRequirement('_block_editor_canonical_access', 'TRUE');
    }
  }

  /**
   * Dynamically adds Block Editor settings routes for all supported types.
   *
   * This creates routes like block_editor.{bundle_entity_type}.settings
   * by deriving the path from the bundle entity type's edit form route.
   *
   * @param \Symfony\Component\Routing\RouteCollection $collection
   *   The route collection.
   */
  protected function addBlockEditorSettingsRoutes(RouteCollection $collection): void {
    // Get all entity types that support Block Editor.
    $supported_mappings = $this->entityManager->getSupportedEntityTypeMappings();

    foreach ($supported_mappings as $bundle_entity_type_id => $content_entity_type_id) {
      $route_name = 'block_editor.' . $bundle_entity_type_id . '.settings';

      // Skip if route already exists (defined manually in routing.yml).
      if ($collection->get($route_name)) {
        continue;
      }

      // Verify the bundle entity type exists.
      try {
        $this->entityTypeManager->getDefinition($bundle_entity_type_id);
      }
      catch (\Exception $e) {
        continue;
      }

      // Get the edit form route for this bundle entity type.
      $edit_route_name = 'entity.' . $bundle_entity_type_id . '.edit_form';
      $edit_route = $collection->get($edit_route_name);

      if (!$edit_route) {
        continue;
      }

      // Create the Block Editor settings route based on the edit route.
      $settings_route = clone $edit_route;

      // Modify the path by appending /block-editor-settings.
      $edit_path = $edit_route->getPath();
      $settings_path = rtrim($edit_path, '/') . '/block-editor-settings';
      $settings_route->setPath($settings_path);

      // Set the form controller to EntityTypeManageForm.
      // Get existing defaults from the edit route and modify them.
      $defaults = $edit_route->getDefaults();
      $defaults['_form'] = '\Drupal\block_editor\Form\EntityTypeManageForm';
      $defaults['_title_callback'] = '\Drupal\block_editor\Controller\BlockEditorController::settingsTitle';
      // Remove any controller that might conflict.
      unset($defaults['_controller']);
      unset($defaults['_entity_form']);
      unset($defaults['_title']);
      $settings_route->setDefaults($defaults);

      // Set requirements - only use the Block Editor access check.
      // Don't copy all requirements from edit route as they may conflict.
      $requirements = [];

      // Keep entity parameter type validation if it exists.
      $edit_requirements = $edit_route->getRequirements();
      foreach ($edit_requirements as $key => $value) {
        // Only copy entity type validation requirements.
        if ($key === $bundle_entity_type_id) {
          $requirements[$key] = $value;
        }
      }

      // Add Block Editor settings access check.
      $requirements['_block_editor_settings_access'] = 'TRUE';
      $settings_route->setRequirements($requirements);

      // Keep the same options (including parameter definitions).
      $options = $edit_route->getOptions();

      // Ensure parameter upcasting is configured.
      if (!isset($options['parameters'][$bundle_entity_type_id])) {
        $options['parameters'][$bundle_entity_type_id] = [
          'type' => 'entity:' . $bundle_entity_type_id,
        ];
      }

      $settings_route->setOptions($options);

      // Add the route to the collection.
      $collection->add($route_name, $settings_route);
    }
  }

}

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

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