toolbar_plus-1.0.x-dev/src/ToolbarPlusUi.php

src/ToolbarPlusUi.php
<?php

declare(strict_types=1);

namespace Drupal\toolbar_plus;

use Drupal\Core\Render\Element;
use Drupal\Core\Routing\AdminContext;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Extension\ExtensionPathResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\toolbar_plus\Event\ShouldNotEditModeEvent;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
 * Toolbar+ UI.
 */
final class ToolbarPlusUi {

  use StringTranslationTrait;

  public function __construct(
    private readonly RequestStack $requestStack,
    private readonly RouteMatchInterface $routeMatch,
    private readonly AdminContext $routerAdminContext,
    private readonly ToolPluginManager $toolbarManager,
    private readonly AccountProxyInterface $currentUser,
    private readonly EventDispatcherInterface  $eventDispatcher,
    private readonly ExtensionPathResolver $extensionPathResolver,
    private readonly EntityDisplayRepositoryInterface $entityDisplayRepository,
  ) {}

  /**
   * Add toolbar.
   *
   * Adds an Edit mode toolbar to the navigation module's left sidebar.
   *
   * @param array $variables
   *   The navigation sidebar.
   *
   * @return void
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  public function buildToolbar(&$variables) {
    if ($this->shouldNotEditMode()) {
      return;
    }

    // Determine what the entity is being edited. This is okay since
    // shouldNotEditMode ensures that we only have one entity parameter.
    $parameters = $this->routeMatch->getParameters()->all();
    foreach ($parameters as $entity) {
      if ($entity instanceof EntityInterface) {
        break;
      }
    }

    // Hide the navigation items and reveal the Edit mode toolbar based on the
    // the state.
    $state = $this->getEditModeState();
    $variables['#cache']['contexts'][] = 'cookies:editMode';
    $variables['#cache']['contexts'][] = 'user.permissions';
    $toolbar_classes = ['toolbar-block'];
    if ($state === 'enabled') {
      foreach ($variables['content']['content'] as &$navigation_item) {
        $navigation_item['#attributes']['class'][] = 'toolbar-plus-hidden';
      }
    } else {
      $toolbar_classes[] = 'toolbar-plus-hidden';
    }

    // Add a button to toggle into editing mode.
    $variables['#attached']['library'][] = 'toolbar_plus/toolbar_plus';
    $children = Element::children($variables['content']['footer']);
    $last_key = end($children);
    $variables['content']['footer'][$last_key]['content']['toolbar_plus_toggle_edit_mode'] = [
      '#type' => 'inline_template',
      '#template' => "<a id='toggle-edit-mode' data-drupal-tooltip='{{edit_mode}}' data-drupal-tooltip-class='admin-toolbar__tooltip' class='toolbar-button toolbar-button--collapsible{{toolbar_state}}' data-index-text='0' data-icon-text='Ed' href='javascript:void(0)'><span class='toolbar-button__label'>{{label}}</span></a>",
      '#context' => [
        'label' => t('Edit'),
        'edit_mode' => t('Edit mode'),
        'toolbar_state' => $state === 'enabled' ? ' active' : '',
      ],
    ];

    // Add tool buttons.
    foreach ($this->getToolPlugins($entity) as $id => $tool) {
      $tool->addAttachments($variables['#attached']);

      // Add styles for the plugin buttons and cursors.
      $icons = $tool->getIconsPath();
      $style = "<style>";
      if (!empty($icons['mouse_icon'])) {
        $style .= ".$id,\n.$id a {\n  cursor: url('{$icons['mouse_icon']}') 0 2, auto;\n}\n";
      }
      if (!empty($icons['toolbar_button_icons'])) {
        foreach ($icons['toolbar_button_icons'] as $name => $path) {
          $style .= ".toolbar-button--icon--$name {\n  --icon: url('$path');\n}\n";
        }
      }
      $style .= "</style>";

      // Add a button for the tool.
      $tools[$id] = [
        '#wrapper_attributes' => [
          'class' => ['toolbar-block__list-item'],
        ],
        '#type' => 'inline_template',
        '#template' => "<a href='javascript:void(0)' data-tool='{{id}}' class='toolbar-button toolbar-button--collapsible toolbar-plus-button toolbar-button--icon--{{id}}'><span class='toolbar-button__label'>{{label}}</span></a>$style",
        '#context' => [
          'id' => $id,
          'label' => $tool->label(),
        ],
      ];
    }

    // Add the toolbar.
    $variables['content']['content']['toolbar_plus'] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'toolbar-plus',
        'class' => $toolbar_classes,
      ],
      // Match the navigation module markup.
      'admin_toolbar_item' => [
        '#type' => 'container',
        '#attributes' => [
          'class' => ['admin-toolbar__item'],
        ],
        'title' => [
          '#type' => 'html_tag',
          '#tag' => 'h4',
          '#value' => t('Editing Toolbar'),
          '#attributes' => [
            'class' => ['visually-hidden', 'focusable'],
          ],
        ],
        'tools' => [
          '#theme' => 'item_list',
          '#attributes' => [
            'class' => ['toolbar-block__list'],
          ],
          '#items' => $tools,
        ],
      ],
      '#weight' => 99,
    ];

    // Set the initial mode and active tool when visiting a page for the first time.
    $entity_type = \Drupal::entityTypeManager()->getStorage($entity->getEntityType()->getBundleEntityType())->load($entity->bundle());
    $initial_mode = $entity_type->getThirdPartySetting('toolbar_plus', 'initial_mode', NULL);
    $variables['content']['content']['toolbar_plus']['#attached']['drupalSettings']['toolbarPlus']['initialMode'] = $initial_mode;
    if ($initial_mode === 'edit_mode') {
      $default_tool_default = \Drupal::moduleHandler()->moduleExists('edit_plus') ? 'edit_plus' : NULL;
      $default_tool = $entity_type->getThirdPartySetting('toolbar_plus', 'default_tool', $default_tool_default);
      if ($default_tool) {
        $variables['content']['content']['toolbar_plus']['#attached']['drupalSettings']['toolbarPlus']['defaultTool'] = $default_tool;
      }
    }
  }

  /**
   * Add a top bar.
   *
   * @param array $page_top
   *   The page top render array.
   *
   * @return void
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  public function buildTopBar(&$page_top) {
    if ($this->shouldNotEditMode()) {
      return;
    }

    $tool_top_bars = [];

    // Add tool specific buttons.
    foreach ($this->getToolPlugins() as $id => $tool) {
      $top_bar = $tool->buildTopBar();
      if (!empty($top_bar)) {
        $tool_top_bars[$id] = [
          '#type' => 'container',
          '#attributes' => [
            'id' => "$id-top-bar",
            'class' => ['top-bar__content', 'top-bar__left'],
          ],
          'top_bar' => $top_bar,
        ];
        if ($this->getActiveTool() !== $id) {
          $tool_top_bars[$id]['#attributes']['class'][] = 'toolbar-plus-hidden';
        }
      }
    }

    // Add some global buttons that work with all tools.
    $tool_top_bars['toolbar_plus'] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'toolbar_plus-top-bar',
        'class' => ['top-bar__content', 'top-bar__right'],
        ],
      'refresh' => [
        '#type' => 'container',
        '#attributes' => [
          'id' => 'toolbar-plus-refresh',
          'title' => t('Refresh the editing UI'),
          'class' => ['toolbar-button', 'toolbar-button--collapsible', 'toolbar-button--icon--refresh'],
        ],
        'label' => [
          '#type' => 'html_tag',
          '#tag' => 'span',
          '#value' => $this->t('Refresh'),
          '#attributes' => [
            'class' => ['toolbar-button__label'],
          ],
        ],
      ],
      'save' => [
        '#type' => 'container',
        '#attributes' => [
          'id' => 'toolbar-plus-save',
          'title' => t('Commit the tempstore changes'),
          'class' => ['toolbar-button', 'toolbar-button--collapsible', 'toolbar-button--icon--save'],
        ],
        'label' => [
          '#type' => 'html_tag',
          '#tag' => 'span',
          '#value' => $this->t('Save'),
          '#attributes' => [
            'class' => ['toolbar-button__label'],
          ],
        ],
      ],
      'discard-changes' => [
        '#type' => 'container',
        '#attributes' => [
          'id' => 'toolbar-plus-discard-changes',
          'title' => t('Discard the tempstore changes'),
          'class' => ['toolbar-button', 'toolbar-button--collapsible', 'toolbar-button--icon--discard-changes'],
        ],
        'label' => [
          '#type' => 'html_tag',
          '#tag' => 'span',
          '#value' => $this->t('Discard changes'),
          '#attributes' => [
            'class' => ['toolbar-button__label'],
          ],
        ],
      ],
    ];

    // Add the top bar.
    $page_top['toolbar_plus_top_bar'] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'toolbar-plus-top-bar',
        'class' => ['top-bar'],
        'data-drupal-admin-styles' => '',
      ],
      '#cache' => ['contexts' => ['user.permissions', 'cookies:editMode']],
      'tools' => $tool_top_bars,
    ];

    // Hide the top bar when not in Editing mode.
    if ($this->getEditModeState() === 'disabled') {
      $page_top['toolbar_plus_top_bar']['#attributes']['class'][] = 'toolbar-plus-hidden';
    }
  }

  /**
   * Build side bars.
   *
   * @param array $page_top
   *   The page_top render array.
   *
   * @return void
   */
  public function buildSideBars(&$page_top) {
    if ($this->shouldNotEditMode()) {
      return;
    }

    // Collect tool sidebars.
    $tool_left_sidebars = [];
    $tool_right_sidebars = [];
    foreach ($this->getToolPlugins() as $id => $tool) {

      $left_side_bar = $tool->buildLeftSideBar();
      if (!empty($left_side_bar)) {
        $tool_left_sidebars[$id] = [
          '#type' => 'container',
          '#attributes' => [
            'id' => "$id-left-sidebar",
          ],
          'left_side_bar' => $left_side_bar,
        ];
        if ($this->getActiveTool() !== $id) {
          $tool_left_sidebars[$id]['#attributes']['class'][] = 'toolbar-plus-hidden';
        }
      }

      $right_side_bar = $tool->buildRightSideBar();
      if (!empty($right_side_bar)) {
        $tool_right_sidebars[$id] = [
          '#type' => 'container',
          '#attributes' => [
            'id' => "$id-right-sidebar",
          ],
          'right_side_bar' => $right_side_bar,
        ];
        if ($this->getActiveTool() !== $id) {
          $tool_right_sidebars[$id]['#attributes']['class'][] = 'toolbar-plus-hidden';
        }
      }

    }

    // Add the sidebars wrapper.
    $page_top['toolbar_plus_left_sidebar'] = [
      '#type' => 'html_tag',
      '#tag' => 'aside',
      '#attributes' => [
        'id' => 'toolbar-plus-left-sidebar',
        'class' => ['toolbar-plus-sidebar-wrapper'],
      ],
      '#cache' => ['contexts' => ['user.permissions', 'cookies:editMode']],
      'left_sidebars' => $tool_left_sidebars,
    ];
    $page_top['toolbar_plus_right_sidebar'] = [
      '#type' => 'html_tag',
      '#tag' => 'aside',
      '#attributes' => [
        'id' => 'toolbar-plus-right-sidebar',
        'class' => ['toolbar-plus-sidebar-wrapper'],
      ],
      '#cache' => ['contexts' => ['user.permissions', 'cookies:editMode']],
      'right_sidebars' => $tool_right_sidebars,
    ];

    // Hide the sidebar when not in Editing mode.
    if ($this->getEditModeState() === 'disabled') {
      $page_top['toolbar_plus_sidebar']['#attributes']['class'][] = 'toolbar-plus-hidden';
    }
  }

  public function preprocessTopBar(&$variables) {
    if ($this->shouldNotEditMode()) {
      return;
    }
    // Hide the navigation top bar when in Editing mode.
    $variables['#cache']['contexts'][] = 'cookies:editMode';
    if ($this->getEditModeState() === 'enabled') {
      $variables['attributes']['class'][] = 'toolbar-plus-hidden';
    }
  }

  /**
   * Get tool plugins.
   *
   * @param \Drupal\Core\Entity\EntityInterface|NULL $entity
   *   If an entity is provided it will check if the tool plugin applies to this
   *   entity.
   *
   * @return array
   *   An array of tool plugins.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  public function getToolPlugins(EntityInterface $entity = NULL): array {
    $tool_definitions = $this->toolbarManager->getDefinitions();
    $tools = [];
    if (!empty($tool_definitions)) {
      foreach ($tool_definitions as $tool_definition) {
        $tool = $this->toolbarManager->createInstance($tool_definition['id']);
        if (is_null($entity) || ($entity && $tool->applies($entity))) {
          $tools[$tool_definition['id']] = $tool;
        }
      }
    }
    return $tools;
  }

  /**
   * Should not edit mode.
   *
   * @return bool
   *   Whether edit mode should be available on this page.
   */
  public function shouldNotEditMode(): bool {
    // @todo Only run once.
    return $this->eventDispatcher->dispatch(new ShouldNotEditModeEvent(), ShouldNotEditModeEvent::class)->shouldNotEdit();
  }

  /**
   * Get edit mode state.
   *
   * Many UI state items are stored in JS's Local and Session storage. Edit mode
   * is stored as a cookie so that the server can conditionally render the page
   * elements with attributes used for the editing UI. Rendering this server side
   * prevents a flashing that would occur if we waited till the JS was loaded
   * to enable the editing UI.
   *
   * @return string
   *   Whether edit mode is enabled (Whether the toolbar is open or closed).
   */
  public function getEditModeState(): string {
    // Cookies are for pages like /node/10
    return $this->requestStack->getCurrentRequest()->cookies->get('editMode') ??
      // Query parameters are for pages like /lb-plus/place-block/overrides/node.16
      $this->requestStack->getCurrentRequest()->get('editMode') ??
      'disabled';
  }

  /**
   * Get active tool.
   *
   * @return string
   *   The active tool.
   */
  public function getActiveTool(): string {
    return $this->requestStack->getCurrentRequest()->cookies->get('activeTool') ?? 'pointer';
  }

  /**
   * Is valid view mode.
   *
   * @return bool
   *   Whether the view_mode is a valid view mode.
   */
  public function isValidViewMode(EntityInterface $entity, string $view_mode): bool {
    $valid_view_modes = $this->entityDisplayRepository->getViewModes($entity->getEntityTypeId());
    $valid_view_modes['default'] = TRUE;
    return !empty($valid_view_modes[$view_mode]);
  }

}

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

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