layout_paragraphs-1.0.x-dev/src/Plugin/Field/FieldWidget/LayoutParagraphsWidget.php

src/Plugin/Field/FieldWidget/LayoutParagraphsWidget.php
<?php

namespace Drupal\layout_paragraphs\Plugin\Field\FieldWidget;

use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Layout\LayoutPluginManagerInterface;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\layout_paragraphs\LayoutParagraphsLayout;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\layout_paragraphs\LayoutParagraphsLayoutTempstoreRepository;

/**
 * Layout paragraphs widget.
 *
 * @FieldWidget(
 *   id = "layout_paragraphs",
 *   label = @Translation("Layout Paragraphs"),
 *   description = @Translation("Layout builder for paragraphs."),
 *   multiple_values = TRUE,
 *   field_types = {
 *     "entity_reference_revisions"
 *   },
 * )
 */
class LayoutParagraphsWidget extends WidgetBase implements ContainerFactoryPluginInterface {

  /**
   * The tempstore.
   *
   * @var \Drupal\layout_paragraphs\LayoutParagraphsLayoutTempstoreRepository
   */
  protected $tempstore;

  /**
   * The Entity Type Manager service property.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The Layouts Manager.
   *
   * @var \Drupal\Core\Layout\LayoutPluginManagerInterface
   */
  protected $layoutPluginManager;

  /**
   * The layout paragraphs layout.
   *
   * @var \Drupal\layout_paragraphs\LayoutParagraphsLayout
   */
  protected $layoutParagraphsLayout;

  /**
   * The layout paragraphs layout tempstore storage key.
   *
   * @var string
   */
  protected $storageKey;

  /**
   * The form builder service.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * The entity display repository.
   *
   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
   */
  protected $entityDisplayRepository;

  /**
   * The entity repository service.
   *
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * The module configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The source translation language id.
   *
   * @var string
   */
  protected $sourceLangcode;

  /**
   * The language code.
   *
   * @var string
   */
  protected $langcode;

  /**
   * Indicates whether the element is in translation mode.
   *
   * @var bool
   */
  protected $isTranslating;

  /**
   * The optional content translation service.
   *
   * @var \Drupal\content_translation\ContentTranslationManagerInterface|null
   */
  protected $contentTranslationManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(
    $plugin_id,
    $plugin_definition,
    FieldDefinitionInterface $field_definition,
    array $settings,
    array $third_party_settings,
    LayoutParagraphsLayoutTempstoreRepository $tempstore,
    EntityTypeManagerInterface $entity_type_manager,
    LayoutPluginManagerInterface $layout_plugin_manager,
    FormBuilderInterface $form_builder,
    EntityDisplayRepositoryInterface $entity_display_repository,
    ConfigFactoryInterface $config_factory,
    EntityRepositoryInterface $entity_repository,
    $content_translation_manager,
  ) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);

    $this->tempstore = $tempstore;
    $this->entityTypeManager = $entity_type_manager;
    $this->layoutPluginManager = $layout_plugin_manager;
    $this->formBuilder = $form_builder;
    $this->entityDisplayRepository = $entity_display_repository;
    $this->entityRepository = $entity_repository;
    $this->config = $config_factory->get('layout_paragraphs.settings');
    $this->contentTranslationManager = $content_translation_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $plugin_id,
      $plugin_definition,
      $configuration['field_definition'],
      $configuration['settings'],
      $configuration['third_party_settings'],
      $container->get('layout_paragraphs.tempstore_repository'),
      $container->get('entity_type.manager'),
      $container->get('plugin.manager.core.layout'),
      $container->get('form_builder'),
      $container->get('entity_display.repository'),
      $container->get('config.factory'),
      $container->get('entity.repository'),
      $container->has('content_translation.manager') ? $container->get('content_translation.manager') : NULL
    );
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $input = $form_state->getUserInput();
    $parents = array_merge($form['#parents'], [
      $this->fieldDefinition->getName(),
      'layout_paragraphs_storage_key',
    ]);
    $layout_paragraphs_storage_key = NestedArray::getValue($input, $parents);
    // If the form is being rendered for the first time, create a new Layout
    // Paragraphs Layout instance, save it to tempstore, and store the key.
    if (empty($layout_paragraphs_storage_key)) {
      $settings = $this->getSettings() + [
        'button_labels' => $this->config->get('button_labels') ?? [],
      ];
      $this->layoutParagraphsLayout = new LayoutParagraphsLayout($items, $settings);
      $this->tempstore->set($this->layoutParagraphsLayout);
      $layout_paragraphs_storage_key = $this->tempstore->getStorageKey($this->layoutParagraphsLayout);
    }
    // On subsequent form renders, this loads the correct Layout Paragraphs
    // Layout from the tempstore using the storage key.
    else {
      $this->layoutParagraphsLayout = $this->tempstore->getWithStorageKey($layout_paragraphs_storage_key);
    }
    $this->initTranslations($form_state);
    $element += [
      '#type' => 'fieldset',
      '#title' => $this->fieldDefinition->getLabel(),
      'layout_paragraphs_builder' => [
        '#type' => 'layout_paragraphs_builder',
        '#layout_paragraphs_layout' => $this->layoutParagraphsLayout,
        '#is_translating' => $this->isTranslating($form_state),
      ],
      // Stores the Layout Paragraphs Layout storage key.
      'layout_paragraphs_storage_key' => [
        '#type' => 'hidden',
        '#default_value' => $layout_paragraphs_storage_key,
      ],
    ];
    if ($source = $form_state->get(['content_translation', 'source'])) {
      $element['layout_paragraphs_builder']['#source_langcode'] = $source->getId();
    }
    return $element;
  }

  /**
   * Determine if widget is in translation.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @see \Drupal\paragraphs\Plugin\Field\FieldWidget\ParagraphsWidget::initIsTranslating()
   */
  protected function isTranslating(FormStateInterface $form_state) {
    if ($this->isTranslating != NULL) {
      return $this->isTranslating;
    }
    /** @var \Drupal\Core\Entity\ContentEntityInterface $host */
    $host = $this->layoutParagraphsLayout->getEntity();
    $this->isTranslating = FALSE;
    if (!$host->isTranslatable()) {
      return $this->isTranslating;
    }
    if (!$host->getEntityType()->hasKey('default_langcode')) {
      return $this->isTranslating;
    }
    $default_langcode_key = $host->getEntityType()->getKey('default_langcode');
    if (!$host->hasField($default_langcode_key)) {
      return $this->isTranslating;
    }

    // Support for
    // \Drupal\content_translation\Controller\ContentTranslationController.
    if (!empty($form_state->get('content_translation'))) {
      // Adding a translation.
      $this->isTranslating = TRUE;
    }
    $langcode = $form_state->get('langcode');
    if (isset($langcode) && $host->hasTranslation($langcode) && $host->getTranslation($langcode)->get($default_langcode_key)->value == 0) {
      // Editing a translation.
      $this->isTranslating = TRUE;
    }
    return $this->isTranslating;
  }

  /**
   * Initialize translations for item list.
   *
   * Makes sure all components have a translation for the current
   * language and creates them if necessary.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function initTranslations(FormStateInterface $form_state) {
    if ($source = $form_state->get(['content_translation', 'source'])) {
      $this->sourceLangcode = $source->getId();
    }
    $this->langcode = $this->entityRepository
      ->getTranslationFromContext($this->layoutParagraphsLayout->getEntity())
      ->language()
      ->getId();
    $items = $this->layoutParagraphsLayout->getParagraphsReferenceField();
    /** @var \Drupal\entity_reference_revisions\Plugin\Field\FieldType\EntityReferenceRevisionsItem $item */
    foreach ($items as $delta => $item) {
      if (!empty($item->entity) && $item->entity instanceof ParagraphInterface) {
        // Now we're sure it's a paragraph:
        $paragraph = $item->entity;
        if (!$this->isTranslating($form_state)) {
          // Set the langcode if we are not translating.
          $langcode_key = $paragraph->getEntityType()->getKey('langcode');
          if ($paragraph->get($langcode_key)->value != $this->langcode) {
            // If a translation in the given language already exists,
            // switch to that. If there is none yet, update the language.
            if ($paragraph->hasTranslation($this->langcode)) {
              $paragraph = $paragraph->getTranslation($this->langcode);
            }
            else {
              $paragraph->set($langcode_key, $this->langcode);
            }
          }
        }
        else {
          // Add translation if missing for the target language,
          // if the paragraph is translatable at all:
          if ($paragraph->isTranslatable() && !$paragraph->hasTranslation($this->langcode)) {
            // Get the selected translation of the paragraph entity.
            $entity_langcode = $paragraph->language()->getId();
            $source_langcode = $this->sourceLangcode ?? $entity_langcode;
            // Make sure the source language version is used if available.
            // Fetching the translation without this check could lead valid
            // scenario to have no paragraphs items in the source version of
            // to an exception.
            if ($paragraph->hasTranslation($source_langcode)) {
              $paragraph = $paragraph->getTranslation($source_langcode);
            }
            // The paragraphs entity has no content translation source field
            // if no paragraph entity field is translatable,
            // even if the host is.
            if ($paragraph->hasField('content_translation_source')) {
              // Initialize the translation with source language values.
              $paragraph->addTranslation($this->langcode, $paragraph->toArray());
              $translation = $paragraph->getTranslation($this->langcode);
              $this->contentTranslationManager->getTranslationMetadata($translation)
                ->setSource($paragraph->language()->getId());
            }
          }
          // If any paragraphs type is translatable do not switch.
          if ($paragraph->isTranslatable() && $paragraph->hasField('content_translation_source')) {
            // Switch the paragraph to the translation.
            $paragraph = $paragraph->getTranslation($this->langcode);
          }
        }
        $items[$delta]->entity = $paragraph;
      }
    }
    $this->layoutParagraphsLayout->setParagraphsReferenceField($items);
    $this->tempstore->set($this->layoutParagraphsLayout);
  }

  /**
   * {@inheritdoc}
   */
  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
    $field_name = $this->fieldDefinition->getName();
    // Load the correct layout paragraphs layout instance using the value
    // passed in the layout_instance_id hidden field.
    $path = array_merge($form['#parents'], [$field_name]);
    $layout_paragraphs_storage_key = $form_state->getValue(array_merge($path, ['layout_paragraphs_storage_key']));
    if (!empty($layout_paragraphs_storage_key)) {
      $this->layoutParagraphsLayout = $this->tempstore->getWithStorageKey($layout_paragraphs_storage_key);
      $values = [];
      foreach ($this->layoutParagraphsLayout->getParagraphsReferenceField() as $item) {
        if ($item->entity) {
          $entity = $item->entity;
          // Set each paragraph langcode if we are not translating.
          if (!$this->isTranslating($form_state)) {
            $langcode_key = $entity->getEntityType()->getKey('langcode');
            $entity->set($langcode_key, $items->getLangcode());
          }
          $entity->setNeedsSave(TRUE);
          $values[] = [
            'entity' => $entity,
            'target_id' => $entity->id(),
            'target_revision_id' => $entity->getRevisionId(),
          ];
        }
      }
      $form_state->setValue($path, $values);
    }
    return parent::extractFormValues($items, $form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $entity_type_id = $this->getFieldSetting('target_type');
    $element = parent::settingsForm($form, $form_state);
    $element['view_mode'] = [
      '#type' => 'select',
      '#title' => $this->t('View mode'),
      '#default_value' => $this->getSetting('view_mode'),
      '#options' => $this->entityDisplayRepository->getViewModeOptions($entity_type_id),
      '#required' => TRUE,
    ];
    $element['preview_view_mode'] = [
      '#type' => 'select',
      '#title' => $this->t('Preview view mode'),
      '#default_value' => $this->getSetting('preview_view_mode'),
      '#options' => $this->entityDisplayRepository->getViewModeOptions($entity_type_id),
      '#description' => $this->t('View mode for the referenced entity preview on the edit form. Automatically falls back to "default" if it is not enabled in the referenced entity type displays.'),
    ];
    $element['form_display_mode'] = [
      '#type' => 'select',
      '#title' => $this->t('Form display mode'),
      '#default_value' => $this->getSetting('form_display_mode'),
      '#options' => $this->entityDisplayRepository->getFormModeOptions($entity_type_id),
      '#description' => $this->t('The form display mode to use when rendering the paragraph form. Automatically falls back to "default", if it is not enabled in the referenced entity type displays.'),
    ];
    $element['nesting_depth'] = [
      '#type' => 'select',
      '#title' => $this->t('Maximum nesting depth'),
      '#options' => range(0, 10),
      '#default_value' => $this->getSetting('nesting_depth'),
      '#description' => $this->t('Choosing 0 will prevent nesting layouts within other layouts.'),
    ];
    $element['require_layouts'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Require paragraphs to be added inside a layout'),
      '#default_value' => $this->getSetting('require_layouts'),
    ];
    $element['empty_message'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Placeholder message to display when field is empty'),
      '#default_value' => $this->getSetting('empty_message'),
    ];
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    $summary = parent::settingsSummary();
    $view_modes = $this->entityDisplayRepository->getViewModeOptions($this->getFieldSetting('target_type'));
    $view_mode = $this->getSetting('view_mode');
    $preview_view_mode = $this->getSetting('preview_view_mode');

    $summary[] = $this->t('Rendered as @mode', ['@mode' => $view_modes[$view_mode] ?? $view_mode]);
    $summary[] = $this->t('Preview view mode: @preview_mode', ['@preview_mode' => $view_modes[$preview_view_mode] ?? $preview_view_mode]);
    $summary[] = $this->t('Form display mode: @form_display_mode', ['@form_display_mode' => $this->getSetting('form_display_mode')]);
    $summary[] = $this->t('Maximum nesting depth: @max_depth', ['@max_depth' => $this->getSetting('nesting_depth')]);
    if ($this->getSetting('require_layouts')) {
      $summary[] = $this->t('Paragraphs <b>must be</b> added within layouts.');
    }
    else {
      $summary[] = $this->t('Layouts are optional.');
    }
    return $summary;
  }

  /**
   * Default settings for widget.
   *
   * @return array
   *   The default settings array.
   */
  public static function defaultSettings() {
    $defaults = parent::defaultSettings();
    $defaults += [
      'empty_message' => '',
      'view_mode' => 'default',
      'preview_view_mode' => 'default',
      'form_display_mode' => 'default',
      'nesting_depth' => 0,
      'require_layouts' => 0,
    ];
    return $defaults;
  }

  /**
   * {@inheritdoc}
   */
  public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
    $elements = parent::form($items, $form, $form_state, $get_delta);
    // Signal to content_translation that this field should be treated as
    // multilingual and not be hidden, see
    // \Drupal\content_translation\ContentTranslationHandler::entityFormSharedElements().
    $elements['#multilingual'] = TRUE;
    return $elements;
  }

}

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

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