io-8.x-1.x-dev/src/IoManager.php

src/IoManager.php
<?php

namespace Drupal\io;

use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\blazy\BlazyManagerInterface;
use Drupal\block\BlockForm;
use Drupal\block\BlockInterface;
use Drupal\io\Plugin\views\pager\IoPager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides IoManager service.
 */
class IoManager implements IoManagerInterface {

  use StringTranslationTrait;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * The current route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Drupal\Core\Block\BlockManagerInterface.
   *
   * @var \Drupal\Core\Block\BlockManagerInterface
   */
  protected $blockManager;

  /**
   * The blazy manager service.
   *
   * @var \Drupal\blazy\BlazyManagerInterface
   */
  protected $blazyManager;

  /**
   * Checks if IO block is disabled.
   *
   * @var bool
   */
  protected $isIoBlockDisabled;

  /**
   * Constructs a BlazyManager object.
   */
  public function __construct(AccountInterface $current_user, RouteMatchInterface $route_match, BlockManagerInterface $block_manager, BlazyManagerInterface $blazy_manager) {
    $this->currentUser = $current_user;
    $this->routeMatch = $route_match;
    $this->blockManager = $block_manager;
    $this->blazyManager = $blazy_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('current_user'),
      $container->get('current_route_match'),
      $container->get('plugin.manager.block'),
      $container->get('blazy.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function currentUser() {
    return $this->currentUser;
  }

  /**
   * {@inheritdoc}
   */
  public function routeMatch() {
    return $this->routeMatch;
  }

  /**
   * {@inheritdoc}
   */
  public function blockManager() {
    return $this->blockManager;
  }

  /**
   * {@inheritdoc}
   */
  public function blazyManager() {
    return $this->blazyManager;
  }

  /**
   * {@inheritdoc}
   */
  public function loadEntityByUuid($uuid, $entity_type = 'block'): ?object {
    return $this->blazyManager->loadByUuid($uuid, $entity_type);
  }

  /**
   * {@inheritdoc}
   */
  public function isBlockApplicable(BlockInterface $block): bool {
    if ($this->isIoBlockDisabled()) {
      return FALSE;
    }

    // Excludes from Ultimenu which can ajaxify the entire region instead.
    $region = $block->getRegion();
    $settings = $block->get('settings');

    // Excludes crucial blocks, or those not worth being ajaxified.
    if (in_array($block->getPluginId(), $this->excludedBlockPluginIds())
      || (isset($settings['provider']) && in_array($settings['provider'], $this->excludedBlockProviders()))
      || ($region && strpos($region, 'ultimenu_') !== FALSE)) {
      return FALSE;
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function isAllowedBlock(BlockInterface $block): bool {
    $this->checkVisibilityConfig($block);
    $access = $block->access('view', $this->currentUser, TRUE);
    return $access->isAllowed();
  }

  /**
   * {@inheritdoc}
   */
  public function getIoPager($view): ?object {
    if ($view && $view->ajaxEnabled() && $view->getDisplay()->isPagerEnabled()) {
      $pager = $view->getPager();
      if ($pager && $pager instanceof IoPager) {
        return $pager;
      }
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getIoSettings($type = 'block'): array {
    $is_block = $type == 'block';
    return [
      // Place loader animation inside link element, suitable for replaceWith
      // method. If using html method, disable inside so the loader will be
      // placed after the triggering element.
      'inside'       => TRUE,
      'addNow'       => TRUE,
      'selector'     => $is_block ? '[data-io-block-trigger]' : '[data-io-pager-trigger]',
      'errorClass'   => $is_block ? 'io__error' : 'pager__error',
      'successClass' => $is_block ? 'io__loaded' : 'pager__loaded',
    ] + (array) $this->blazyManager->getIoSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function blockFormAlter(array &$form, FormStateInterface $form_state, $form_id): void {
    // See https://www.drupal.org/node/2897557.
    if ($this->isIoBlockDisabled() || !isset($form_state->getBuildInfo()['callback_object'])) {
      return;
    }

    $object = $form_state->getFormObject();
    if (!($object instanceof BlockForm)) {
      return;
    }

    /** @var \Drupal\block\BlockInterface $block */
    $block = $object->getEntity();
    if (!($block instanceof BlockInterface)
      || !$this->isBlockApplicable($block)) {
      return;
    }

    // This will automatically be saved in the third party settings.
    $form['third_party_settings']['#tree'] = TRUE;
    $form['third_party_settings']['io']['lazyload'] = [
      '#type'          => 'checkbox',
      '#title'         => $this->t('Lazyload using Intersection Observer'),
      '#description'   => $this->t("Reasonable for non-essential or peripheral blocks below the fold, or at sidebars. Widgets like Facebook, Twitter, Google maps, or other third party, statistics, heavy-logic etc. are good candidates. Do not enable this for <a href=':url'>Ultimenu 2.x</a> regions as it is capable of ajaxifying the entire region instead. Check out more excluded blocks at <b>/admin/help/io</b>.", [':url' => 'https://drupal.org/project/ultimenu']),
      '#default_value' => $block->getThirdPartySetting('io', 'lazyload'),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function blazySettingsFormAlter(array &$form): void {
    $settings = $this->blazyManager->config();

    // Hooks into Blazy UI to support Blazy Filter.
    if (isset($settings['admin_css'])) {
      $form['extras']['#access'] = TRUE;
      $form['extras']['io_fallback'] = [
        '#type'          => 'textfield',
        '#title'         => $this->t('IO fallback'),
        '#default_value' => $settings['extras']['io_fallback'] ?? '',
        '#description'   => $this->t('Text to display when an AJAX block fails loading its content. Default to: <b>Loading... Click here if it takes longer.</b>'),
      ];

      $form['extras']['io_block_disabled'] = [
        '#type'          => 'checkbox',
        '#title'         => $this->t('Disable IO block'),
        '#default_value' => $settings['extras']['io_block_disabled'] ?? '',
        '#description'   => $this->t('Check here to disable lazyloading blocks sitewide. Only infinite IO pager will be available at Views UI.'),
      ];

      // Adds relevant IO AJAX description to existing Blazy IO options.
      $description = $form['io']['disconnect']['#description'];
      $form['io']['disconnect']['#description'] = $description . ' ' . $this->t('The same applies to IO ajaxified block and pager observers. The IO must stand-by and be able to watch the next/ subsequent AJAX results. No expensive methods executed on being stand-by. Each item will be unobserved once loaded, instead.');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function preprocessBlock(array &$variables): void {
    if ($this->isIoBlockDisabled()) {
      return;
    }

    // Replace the block content with a fallback, so that we can lazy load it.
    // In case the AJAX fails, the user has a link to load/ click it manually.
    $uuid = $variables['elements']['#io'];
    $variables['content'] = [];
    $variables['attributes']['class'][] = 'block--io io';

    // Cannot use regular `use-ajax` class as we need to work out errors.
    $classes = ['io__lazy'];
    if (function_exists('ajaxin')) {
      $classes[] = 'io__loading';
    }
    else {
      $classes[] = 'is-b-loading';
      $variables['#attached']['library'][] = 'blazy/loading';
    }

    $variables['content']['io'] = [
      '#type' => 'link',
      '#title' => [
        '#markup' => '<span class="io__text">' . $this->getFallbackText() . '</span>',
        '#allowed_tags' => ['small', 'span', 'strong'],
      ],
      '#attributes' => [
        'class' => $classes,
        'data-io-block-trigger' => TRUE,
        'rel' => 'nofollow',
      ],
      '#url' => $this->getBlockUrl($uuid),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getBlockUrl($uuid): object {
    return Url::fromRoute('io.block', ['ioid' => $uuid]);
  }

  /**
   * {@inheritdoc}
   */
  public function isIoBlockDisabled(): bool {
    if (!isset($this->isIoBlockDisabled)) {
      $this->isIoBlockDisabled = $this->blazyManager->config('extras.io_block_disabled') ?: FALSE;
    }
    return $this->isIoBlockDisabled;
  }

  /**
   * {@inheritdoc}
   */
  public function getFallbackText(): object {
    return $this->t('@text', [
      '@text' => $this->blazyManager->config('extras.io_fallback')
        ?: 'Loading... Click here if it takes longer.',
    ]);
  }

  /**
   * {@inheritdoc}
   */
  public function preprocessIoPager(array &$variables): void {
    /** @var \Drupal\Core\Pager\PagerManagerInterface $pager_manager */
    $pager_manager = $this->blazyManager->service('pager.manager');
    if (!$pager_manager) {
      return;
    }

    $element = $variables['element'];
    $parameters = $variables['parameters'];

    // Nothing to do if there is only one page.
    $pager = $pager_manager->getPager($element);
    if (!$pager) {
      return;
    }

    $current = $pager->getCurrentPage();
    $total = $pager->getTotalPages();

    // Current is the page we are currently paged to.
    $variables['items']['current'] = $current + 1;

    // Calculate various markers within this pager piece:
    if ($current < ($total - 1)) {
      $options = [
        'query' => $pager_manager->getUpdatedParameters($parameters, $element, $current + 1),
      ];
      $variables['items']['next']['href'] = Url::fromRoute('<current>', [], $options)->toString();
      $variables['items']['next']['attributes'] = new Attribute();
    }

    // This is based on the entire current query string. We need to ensure
    // cacheability is affected accordingly.
    $variables['#cache']['contexts'][] = 'url.query_args';

    if (!$this->blazyManager->moduleExists('ajaxin')
      && !empty($variables['options']['autoload'])) {
      $variables['content_attributes']['class'][] = 'is-b-loading';
    }
  }

  /**
   * Checks IO block visibility config, and includes IO block route.
   */
  protected function checkVisibilityConfig(BlockInterface &$block): void {
    if ($block && $visibility_config = $block->getVisibility()) {
      if (isset($visibility_config['request_path'])
        && $request_path = $visibility_config['request_path']) {
        // Include our path into visibility unless sitewide or negated.
        if (!empty($request_path['pages']) && empty($request_path['negate'])) {
          $pages = "/io/block\r\n";
          $pages .= $request_path['pages'];
          $request_path['pages'] = $pages;
          $block->setVisibilityConfig('request_path', $request_path);
        }
      }
    }
  }

  /**
   * Excludes crucial blocks, or those not worth being ajaxified.
   */
  private function excludedBlockPluginIds(): array {
    $excludes = [
      'help_block',
      'local_tasks_block',
      'node_syndicate_block',
      'page_title_block',
      'search_form_block',
      'system_branding_block',
      'system_breadcrumb_block',
      'system_main_block',
      'system_messages_block',
      'user_login_block',
    ];

    $this->blazyManager->moduleHandler()->alter('io_excluded_block_plugin_ids', $excludes);
    return array_unique($excludes);
  }

  /**
   * Excludes blocks by modules.
   */
  private function excludedBlockProviders(): array {
    $excludes = [
      'ultimenu',
      'jumper',
    ];

    $this->blazyManager->moduleHandler()->alter('io_excluded_block_providers', $excludes);
    return array_unique($excludes);
  }

}

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

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