io-8.x-1.x-dev/modules/io_browser/src/IoBrowserWidget.php

modules/io_browser/src/IoBrowserWidget.php
<?php

namespace Drupal\io_browser;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\entity_browser\Entity\EntityBrowser;

/**
 * Provides IoBrowserWidget service.
 */
class IoBrowserWidget extends IoBrowserAlter implements IoBrowserWidgetInterface {

  /**
   * {@inheritdoc}
   */
  public function fieldWidgetFormAlter(
    array &$element,
    FormStateInterface $form_state,
    $context,
  ): void {
    $settings = [];
    $plugin_id = $context['widget']->getPluginId();

    // Always assumes no "Display style" of IO widgets is enabled.
    if ($plugin_id == 'entity_browser_entity_reference') {
      $settings = $this->entityBrowserEntityReferenceFormAlter($element, $context);
    }
    elseif ($plugin_id == 'entity_browser_file') {
      $settings = $this->entityBrowserFileFormAlter($element, $context);
    }
    elseif ($plugin_id == 'media_library_widget') {
      $settings = $this->mediaLibraryWidgetFormAlter($element, $context);
    }

    // Ony proceed if we are conciously allowed via "Display style" option.
    // This settings may contain configurable third party settings.
    if (empty($settings)) {
      return;
    }

    // We are here because we are allowed to.
    // Build common settings to all supported plugins.
    // The non-image field type has 'display_field', 'description'.
    $this->widgetSettings($element, $settings, $context);
    $blazies = $settings['blazies'];

    // EB only respects field_widget_display with cardinality -1. If not, we
    // may need to override its display. Only concerns about File entity.
    // The _processed flag means IO plugin output is already processed at plugin
    // level, no further work with its display output is needed from here on.
    $file = $blazies->get('field.target_type') == 'file';
    $unlimited = $blazies->get('field.cardinality') == -1;
    $processed = $file && !$unlimited ? FALSE : TRUE;
    $blazies->set('is.ib.processed', $processed)
      ->set('is.unlazy', TRUE);

    // Don't bother if using label.
    if (!$blazies->use('label')) {
      // Yet only modify for EB, not core Media Library.
      if ($blazies->is('eb')) {
        $this->entityBrowserDisplay($element, $settings, $context);
      }
    }

    // Specific for EB, it might add own theme to style entity browser elements.
    $this->widgetElement($element, $settings, $context);
    $this->widgetAttach($element, $settings);
  }

  /**
   * Modifies the available widget settings.
   */
  private function widgetSettings(array &$element, array &$settings, $context) {
    $formatter   = $this->ioBrowser->formatter();
    $plugin      = $context['widget'];
    $items       = $context['items'];
    $field       = $items->getFieldDefinition();
    $entity      = $items->getEntity();
    $widgetsets  = $plugin->getSettings();
    $fieldsets   = $field->getSettings();
    $plugin_id   = $plugin->getPluginId();
    $cardinality = $field->getFieldStorageDefinition()->getCardinality();
    $grids       = ['column', 'grid', 'flex', 'nativegrid'];
    $ib          = $settings['iobrowsers'] = $formatter->settings();

    // @todo remove.
    $fieldsets['name'] = $field->getName();
    $fieldsets['type'] = $field->getType();
    $formatter_id = $settings['plugin_id_widget_display'] ?? '';

    $blazies = $formatter->verifySafely($settings);

    $ib->set('widget.settings', $widgetsets)
      ->set('widget.settings.plugin_id', $plugin_id)
      ->set('widget.plugin_id', $plugin_id);

    $blazies->set('field', $fieldsets, TRUE)
      ->set('field.cardinality', $cardinality)
      ->set('field.plugin_id', $formatter_id)
      ->set('field.settings', $fieldsets);

    foreach (['alt_field', 'title_field', 'target_type'] as $key) {
      $settings[$key] = $value = $fieldsets[$key] ?? FALSE;
      $blazies->set('field.' . $key, $value);
    }

    $settings['media_switch'] = 'media';
    $settings['ratio'] = 'fluid';
    $thumbnail_style = $settings['thumbnail_style'] ?? NULL;
    $settings['thumbnail_style'] = $thumbnail_style ?: 'io_browser_thumbnail';

    // @todo remove these post Blazy:2.10.
    $settings['bundle'] = $entity->bundle();
    $settings['entity_type_id'] = $entity->getEntityTypeId();
    $settings['plugin_id_widget'] = $plugin_id;

    $data = [
      'cardinality' => $cardinality,
      'field_name' => $context['items']->getName(),
      'field_type' => $field->getType(),
      'target_type' => $fieldsets['target_type'] ?? NULL,
      'use_label' => $formatter_id == 'io_browser_label',
      'use_slick' => ($settings['style'] ?? NULL) == 'slick',
      'use_autosubmit' => $settings['entity_type_id'] == 'media',
    ];

    foreach ($data as $key => $value) {
      $settings[$key] = $value;
    }

    $id_grid = !empty($settings['style'])
      && !empty($settings['grid'])
      && in_array($settings['style'], $grids);

    // Defaults.
    foreach ($data as $key => $value) {
      $k = $key;
      if (strpos($key, 'use_') !== FALSE) {
        $k = 'use.' . str_replace('use_', '', $key);
      }
      elseif (strpos($key, 'field_') !== FALSE) {
        $k = 'field.' . str_replace('field_', '', $key);
      }
      $blazies->set($k, $value);
    }

    $blazies->set('is.eb', FALSE)
      ->set('is.media_library', FALSE)
      ->set('is.grid', $id_grid)
      ->set('is.detached', !empty($settings['style']))
      ->set('use.tabs', FALSE)
      ->set('use.modal', FALSE)
      ->set('is.unlazy', TRUE);

    $data = [
      '#entity' => $entity,
      '#settings' => $settings,
    ];

    $this->ioBrowser->blazyEntity()->prepare($data);
    $subsets = &$data['#settings'];

    // Entity Browser integration.
    $plugins = ['entity_browser_entity_reference', 'entity_browser_file'];
    if (in_array($plugin_id, $plugins)) {
      $this->entityBrowserSettings($element, $subsets, $context);
    }

    ksort($subsets);
    return $subsets;
  }

  /**
   * Modifies the available entity browser widget settings.
   */
  private function entityBrowserSettings(array &$element, array &$settings, $context) {
    $widgetsets = $context['widget']->getSettings();
    $blazies = $settings['blazies'];
    $ib = $settings['iobrowsers'];
    $image_style = $settings['image_style'] ?? '';
    $settings['image_style'] = $image_style
      ?: ($widgetsets['preview_image_style'] ?? '');

    // Chances are IO browsers within iframes/modals, even if no IO widgets.
    // Or using any of IO field_widget_display.
    // Only EntityReferenceBrowserWidget has field_widget_display, not FBW.
    // $formatter_id = $blazies->get('field.plugin_id')
    // ?: $settings['plugin_id_widget_display'] ?? '';.
    $blazies->set('is.eb', TRUE)
      ->set('is.browser', TRUE);

    // Load relevant assets based on the chosen IO browsers plugins.
    if ($id = ($widgetsets['entity_browser'] ?? NULL)) {
      if ($eb = EntityBrowser::load($id)) {

        // Entity display plugins: io_browser_file, io_browser_media, etc.
        $internal['display'] = $eb->getDisplay()->getConfiguration();
        $internal['display']['plugin_id'] = $pid_widget_display = $eb->getDisplay()->getPluginId();
        $internal['selection_display'] = $eb->getSelectionDisplay()->getConfiguration();
        $internal['selection_display']['plugin_id'] = $eb->getSelectionDisplay()->getPluginId();
        $internal['selector'] = $eb->getWidgetSelector()->getConfiguration();
        $internal['selector']['plugin_id'] = $pid_widget_selector = $eb->getWidgetSelector()->getPluginId();

        // Selection displays: modal, iframe, form, etc.
        $use_modal = $pid_widget_display == 'modal';
        $use_tabs = $pid_widget_selector == 'io_browser_tabs';

        $blazies->set('use.modal', $use_modal)
          ->set('use.tabs', $use_tabs);

        $ib->set('widget', $internal, TRUE);
      }
    }

    // @todo move it upstream.
    if ($current = $element['current'] ?? []) {
      $children = Element::children($current);
      $count = count($children);
      if ($items = ($current['items'] ?? [])) {
        $count = count($items);
      }

      $settings['count'] = $count;
      $blazies->set('count', $count);
    }
  }

  /**
   * Modifies the widget form element.
   */
  private function widgetElement(array &$element, array &$settings, $context) {
    $blazies = $settings['blazies'];
    $ib = $settings['iobrowsers'];
    $attributes = &$element['#attributes'] ?? [];

    // Build the IO widgets, nothing to do with IO browsers here on.
    // This used to be "IO Widget", moved into "IO Browser".
    $classes = $attributes['class'] ?? [];
    $ib_classes = ['ib', 'ib--root', 'ib--launcher'];
    $attributes['class'] = array_merge($ib_classes, $classes);
    $attributes['class'][] = $blazies->use('modal') ? 'ib--root-inline' : 'ib--root-modal';

    if ($blazies->use('autosubmit')) {
      $attributes['class'][] = 'ib--autoselect';
    }

    // Media Library integration: plugin_id_widget = media_library_widget.
    if (isset($element['selection'])) {
      $this->mediaLibraryElement($element, $settings, $context);
    }
    // Entity Browser integration has property current.
    elseif (isset($element['current'])) {
      $this->entityBrowserElement($element, $settings, $context);
    }

    $attributes['data-ib-bundle'] = $blazies->get('entity.bundle');
    $attributes['data-ib-entity-type-id'] = $blazies->get('entity.type_id');
    $attributes['data-ib-field-type'] = $blazies->get('field.type');
    $attributes['data-ib-target-type'] = $blazies->get('field.target_type');
    $attributes['data-ib-cardinality'] = $blazies->get('field.cardinality') ?: 0;
    $attributes['data-ib-entity-browser'] = $blazies->get('field.plugin_id');
    $attributes['data-ib-plugin-id-widget'] = $ib->get('widget.plugin_id');
  }

  /**
   * Provides asset attachments.
   */
  private function widgetAttach(array &$element, array $settings) {
    $formatter = $this->ioBrowser->formatter();

    // Enforce Blazy to work with hidden element such as with EB selection.
    $load = $formatter->attach($settings);
    $load['drupalSettings']['blazy']['loadInvisible'] = TRUE;
    $load['library'][] = 'io_browser/widget';

    $blazies = $settings['blazies'];
    foreach (['autosubmit', 'modal', 'tabs'] as $key) {
      if ($blazies->use($key)) {
        $load['library'][] = 'io_browser/' . $key;
      }
    }

    if ($blazies->is('grid')) {
      $load['library'][] = 'io_browser/grid';
    }

    // Disable tabledrag, including FBW table CSS, for IO/ CSS grid.
    if ($settings['plugin_id_widget'] == 'entity_browser_file') {
      $attachments = $load;
    }
    else {
      $attachments = $formatter->merge($load, $element['current'] ?? [], '#attached');
    }

    $element['#attached'] = $formatter->merge($attachments, $element, '#attached');
  }

  /**
   * Implements hook_field_widget_WIDGET_TYPE_form_alter().
   */
  private function entityBrowserEntityReferenceFormAlter(array &$element, $context) {
    $plugin = $context['widget'];
    $widgetsets = $plugin->getSettings();
    $display = $widgetsets['field_widget_display'] ?? 'x';

    if (strpos($display, 'io_browser') === FALSE) {
      return [];
    }

    $defaults = IoBrowserDefault::widgetEntitySettings();
    $settings = IoBrowserUtil::thirdPartySettings(
      $plugin,
      'field_widget_display_settings',
      $defaults
    );

    $settings['plugin_id_widget_display'] = $widgetsets['field_widget_display'];

    return empty($settings['style']) ? [] : $settings;
  }

  /**
   * Implements hook_field_widget_WIDGET_TYPE_form_alter().
   */
  private function entityBrowserFileFormAlter(array &$element, $context) {
    $plugin     = $context['widget'];
    $widgetsets = $plugin->getSettings();
    $display    = $widgetsets['entity_browser'] ?? 'x';

    if (strpos($display, 'io_browser') === FALSE) {
      return [];
    }

    // Allows IO Browser to remove File Browser empty table.
    $settings = IoBrowserUtil::thirdPartySettings($plugin);

    return empty($settings['style']) ? [] : $settings;
  }

  /**
   * Implements hook_field_widget_WIDGET_TYPE_form_alter().
   */
  private function mediaLibraryWidgetFormAlter(array &$element, $context) {
    $plugin = $context['widget'];

    // Allows to provide configurable grids.
    $settings = IoBrowserUtil::thirdPartySettings($plugin);

    return empty($settings['style']) ? [] : $settings;
  }

  /**
   * Modifies the entity browser widget identified by element current.
   */
  private function entityBrowserElement(array &$element, array &$settings, $context) {
    $current = &$element['current'];

    // Prevents collapsed details from breaking lazyload.
    if (empty($element['#open'])) {
      $element['#open'] = TRUE;
      $element['#attributes']['class'][] = 'ib--root-hidden';
    }

    $current['#settings']       = $settings;
    $current['#attributes']     = [];
    $current['#theme_wrappers'] = [];
    $current['#theme']          = 'io_browser';

    // Removes table markups for regular divities.
    if ($settings['plugin_id_widget'] == 'entity_browser_file') {
      unset(
        $current['#type'],
        $current['#header'],
        $current['#tabledrag']
      );
    }
  }

  /**
   * Prepare entity displays for entity browser.
   *
   * EB only respects field_widget_display with cardinality -1. If not, we
   * may need to override its display such as for single-value file/ media.
   */
  private function entityBrowserDisplay(array &$element, array &$settings, $context) {
    $browser = $this->ioBrowser;
    $blazies = $settings['blazies'];
    $target_type = $blazies->get('field.target_type') ?: ($settings['target_type'] ?? NULL);

    // The items property is for entity, not file image, except cardinality -1.
    // Cannot rely on $context['items'] for AJAX results. The entity_browser
    // property is only available for:
    // File, indexed by entity ID: cardinality -1 and > 1, but not 1,
    // Media, grouped by items property: cardinality -1, but not 1, nor > 1.
    $entities = $element['entity_browser']['#default_value'] ?? [];
    if (empty($entities) && isset($element['current'])) {

      if ($children = Element::children($element['current'])) {
        // Maybe empty, but items is always set.
        // File is never here.
        // Media, grouped by items property: cardinality 1, > 1, not -1.
        $items = $element['current']['items'] ?? [];
        if ($items && $subchildren = Element::children($items)) {
          foreach ($subchildren as $delta) {
            $sets = $settings;
            $sets['delta'] = $delta;

            if ($entity = ($items[$delta]['display']['#entity'] ?? NULL)) {
              $this->entityDisplay($element, $sets, $entity, $delta);
            }
          }
        }
        else {
          // Indexed by entity ID.
          // File, indexed by entity ID: cardinality 1.
          foreach ($children as $delta => $id) {
            $sets = $settings;
            $sets['delta'] = $delta;

            if ($entity = $browser->formatter()->load($id, $target_type)) {
              $this->entityDisplay($element, $sets, $entity, $delta);
            }
          }
        }
      }
    }
    else {
      // Non-AJAX here.
      // Hence we have entities, except single file image.
      // File, indexed by entity ID: cardinality -1 and > 1, but not 1,
      // Media, grouped by items property: cardinality -1, but not 1, nor > 1.
      foreach ($entities as $delta => $entity) {
        $sets = $settings;
        $sets['delta'] = $delta;

        $this->entityDisplay($element, $sets, $entity, $delta);
      }
    }
  }

  /**
   * Prepares entity item display, applicable to EB and Media Library.
   *
   * Can be File or Media entities.
   */
  private function entityDisplay(array &$element, array &$settings, $entity, $delta = 0) {
    $browser    = $this->ioBrowser;
    $blazies    = $settings['blazies'];
    $langcode   = $blazies->get('language.current') ?: 'en';
    $translated = $browser->formatter()->getTranslatedEntity($entity, $langcode);
    $label      = $translated->label();
    $display    = $content = [];

    // If not already processed, proceed. Processed means plugin is respected
    // which is currently not the case given different cardinality for File.
    if (!$blazies->is('ib.processed')) {
      /** @var \Drupal\file\Entity\File $entity */
      $data['#entity']   = $entity;
      $data['#delta']    = $delta;
      $data['#settings'] = $settings;
      $data['fallback']  = $label;

      /*
      // If media_library_widget:
      if (isset($element['selection'])) {
      $data['wrapper_attributes']['class'][] = 'grid__content';
      $data['media_attributes']['class'][] = 'ib__preview';
      $data['media_attributes']['class'][] = 'b-cover';
      $data['media_attributes']['class'][] = 'media-library-item__preview';
      $data['media_attributes']['class'][] = 'js-media-library-item-preview';
      $data['overlay']['ib_label']['#markup'] =
      '<div class="ib__label ib__label--ml">' . $label . '</div>';
      }
       */
      $display = $browser->blazyEntity()->build($data);

      $sets = $data['#settings'];
      $display_settings = $display['#settings'] ?? [];
      $display['#settings'] = $browser->formatter()->merge($display_settings, $sets);
    }

    // EB put them in items property for cardinality -1 + entity, not image.
    // Do not modify anything if already processed such as cardinality -1.
    if (isset($element['current'])) {
      // Media entity non-AJAX:
      if (isset($element['current']['items'])) {
        // Already theme_blazy() here for content.display:
        $content = &$element['current']['items'][$delta];
      }
      else {
        // Still theme_image_style().
        // @todo recheck if should use translated entity here.
        $content = &$element['current'][$entity->id()];
      }

      if ($display) {
        $content['display'] = $display;
      }
    }
    /*
    elseif (isset($element['selection'])) {
    $content = &$element['selection'][$delta];
    if ($display) {
    $content['rendered_entity'] = $display;
    }
    }
     */
  }

  /**
   * Modifies the available media library widget settings.
   *
   * Unlike EB with full override, this is all we do with Media Library for now.
   * Basically making the Media Library grid configurable, and blazy-enabled.
   */
  private function mediaLibraryElement(array &$element, array &$settings, $context) {
    $browser = $this->ioBrowser;
    $blazies = $settings['blazies'];

    $blazies->set('is.media_library', TRUE)
      ->set('is.grid', TRUE);

    $selection = &$element['selection'];
    $attributes = &$selection['#attributes'];

    $children = Element::children($selection);
    $settings['count'] = $count = count($children);

    $blazies->set('count', $count);

    // @todo use $this->ioBrowser->formatter()->initGrid(); post blazy:2.17.
    $browser->formatter()->gridAttributes($attributes, $settings);
    $selection['#settings'] = $settings;
    $browser->blazyEntity()->prepare($selection);

    // Respects anyone adding a suffix here.
    $zoom = '<div class="ib__zoom"></div>';
    if (isset($selection['#suffix'])) {
      $selection['#suffix'] .= $zoom;
    }
    else {
      $selection['#suffix'] = $zoom;
    }

    // Cannot use content_attributes, not all core themes have it at fieldset.
    $attributes['class'][] = 'ib__widget';

    if ($children) {
      foreach ($children as $delta) {
        $sets = $settings;
        $sets['delta'] = $delta;

        $this->mediaLibraryDisplay($selection[$delta], $sets, $context, $delta);
      }
    }
  }

  /**
   * Modifies the available media library widget settings.
   *
   * Unlike EB with full override, this is all we do with Media Library for now.
   * Basically making the Media Library grid configurable, and blazy-enabled.
   */
  private function mediaLibraryDisplay(array &$item, array &$settings, $context, $delta) {
    $browser    = $this->ioBrowser;
    $blazies    = $settings['blazies']->reset($settings);
    $style      = $settings['style'] ?? NULL;
    $item_attrs = &$item['#attributes'] ?? [];

    $item_attrs['class'][] = 'grid';
    $item_attrs['class'][] = 'grid--ib';
    $item_attrs['class'][] = 'grid--' . $delta;

    $blazies->set('delta', $delta);
    if ($style == 'nativegrid') {
      $attrs = [];
      $content_attrs = [];
      $browser->formatter()->gridItemAttributes($attrs, $content_attrs, $settings);

      if ($attrs) {
        $item_attrs = $browser->formatter()->merge($attrs, $item_attrs);
      }
    }

    $rendered = $item['rendered_entity'] ?? NULL;
    $entity = ($rendered['#media'] ?? NULL);
    if ($rendered && $entity) {
      $label = $entity->label();

      $data['#delta']    = $delta;
      $data['#entity']   = $entity;
      $data['#settings'] = $settings;
      $data['fallback']  = $label;

      $data['#wrapper_attributes']['class'][] = 'grid__content';
      $data['#media_attributes']['class'][] = 'ib__preview';
      $data['#media_attributes']['class'][] = 'b-cover';
      $data['#media_attributes']['class'][] = 'media-library-item__preview';
      $data['#media_attributes']['class'][] = 'js-media-library-item-preview';
      $data['overlay']['ib_label']['#markup'] = '<div class="ib__label ib__label--ml">' . $label . '</div>';

      // $this->entityDisplay($element, $settings, $entity, $delta);
      $item['rendered_entity'] = $browser->blazyEntity()->build($data);
      $item['rendered_entity']['#media'] = $entity;
    }
  }

}

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

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