You are here

class LayoutBuilderCopyWidget in Layout Builder Asymmetric Translation 8.2

A widget to display the copy widget form.

@FieldWidget( id = "layout_builder_at_copy", label = @Translation("Layout Builder Asymmetric Translation"), description = @Translation("A field widget for Layout Builder. This exposes a checkbox on the entity form to copy the blocks on translation."), field_types = { "layout_section", }, multiple_values = FALSE, )

@internal Plugin classes are internal.

Hierarchy

Expanded class hierarchy of LayoutBuilderCopyWidget

File

src/Plugin/Field/FieldWidget/LayoutBuilderCopyWidget.php, line 32

Namespace

Drupal\layout_builder_at\Plugin\Field\FieldWidget
View source
class LayoutBuilderCopyWidget extends WidgetBase {

  /**
   * Options for appearance.
   *
   * @return array
   */
  protected function options() {
    return [
      'unchecked' => $this
        ->t('Unchecked'),
      'checked' => $this
        ->t('Checked'),
      'checked_hidden' => $this
        ->t('Checked and hidden'),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return [
      'appearance' => 'unchecked',
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $element['appearance'] = [
      '#type' => 'select',
      '#title' => t('Checkbox appearance'),
      '#options' => $this
        ->options(),
      '#default_value' => $this
        ->getSetting('appearance'),
    ];
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    $summary = [];
    $summary[] = t('Appearance: @checked', [
      '@checked' => $this
        ->options()[$this
        ->getSetting('appearance')],
    ]);
    return $summary;
  }

  /**
   * {@inheritdoc}
   */
  public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
    $field_name = $this->fieldDefinition
      ->getName();
    $parents = $form['#parents'];

    // Store field information in $form_state.
    if (!static::getWidgetState($parents, $field_name, $form_state)) {
      $field_state = [
        'items_count' => count($items),
        'array_parents' => [],
      ];
      static::setWidgetState($parents, $field_name, $form_state, $field_state);
    }

    // Collect widget elements.
    $elements = [];
    $delta = 0;
    $element = [
      '#title' => $this->fieldDefinition
        ->getLabel(),
      '#description' => FieldFilteredMarkup::create(\Drupal::token()
        ->replace($this->fieldDefinition
        ->getDescription())),
    ];
    $element = $this
      ->formSingleElement($items, $delta, $element, $form, $form_state);
    if ($element) {
      if (isset($get_delta)) {

        // If we are processing a specific delta value for a field where the
        // field module handles multiples, set the delta in the result.
        $elements[$delta] = $element;
      }
      else {

        // For fields that handle their own processing, we cannot make
        // assumptions about how the field is structured, just merge in the
        // returned element.
        $elements = $element;
      }
    }

    // Populate the 'array_parents' information in $form_state->get('field')
    // after the form is built, so that we catch changes in the form structure
    // performed in alter() hooks.
    $elements['#after_build'][] = [
      get_class($this),
      'afterBuild',
    ];
    $elements['#field_name'] = $field_name;
    $elements['#field_parents'] = $parents;

    // Enforce the structure of submitted values.
    $elements['#parents'] = array_merge($parents, [
      $field_name,
    ]);

    // Most widgets need their internal structure preserved in submitted values.
    $elements += [
      '#tree' => TRUE,
    ];
    $return = [
      // Aid in theming of widgets by rendering a classified container.
      '#type' => 'container',
      // Assign a different parent, to keep the main id for the widget itself.
      '#parents' => array_merge($parents, [
        $field_name . '_wrapper',
      ]),
      '#attributes' => [
        'class' => [
          'field--type-' . Html::getClass($this->fieldDefinition
            ->getType()),
          'field--name-' . Html::getClass($field_name),
          'field--widget-' . Html::getClass($this
            ->getPluginId()),
        ],
      ],
      'widget' => $elements,
    ];
    return $return;
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $entity = $items
      ->getEntity();
    $access = FALSE;
    if ($entity instanceof ContentEntityInterface) {
      $access = $entity
        ->isNewTranslation() && !$entity
        ->isDefaultTranslation();
    }
    $element['#layout_builder_at_access'] = $access;
    $checked = FALSE;
    $v = $this
      ->getSetting('appearance');
    if ($v == 'checked' || $v == 'checked_hidden') {
      $checked = TRUE;
    }
    $element['value'] = $element + [
      '#access' => TRUE,
      '#type' => 'checkbox',
      '#default_value' => $checked,
      '#title' => $this
        ->t('Copy blocks into translation'),
    ];
    if ($v == 'checked_hidden') {
      $element['value']['#access'] = FALSE;
    }
    return $element;
  }

  /**
   * Extract form values.
   *
   * @param \Drupal\Core\Field\FieldItemListInterface $items
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \Drupal\Core\TypedData\Exception\ReadOnlyException
   */
  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {

    // @todo This isn't resilient to being set twice, during validation and
    //   save https://www.drupal.org/project/drupal/issues/2833682.
    if (!$form_state
      ->isValidationComplete()) {
      return;
    }
    $field_name = $this->fieldDefinition
      ->getName();

    // We can only copy if the field is set and access is TRUE.
    if (isset($form[$field_name]['widget']['#layout_builder_at_access']) && !$form[$field_name]['widget']['#layout_builder_at_access']) {
      return;
    }

    // Extract the values from $form_state->getValues().
    $path = array_merge($form['#parents'], [
      $field_name,
    ]);
    $key_exists = NULL;
    $values = NestedArray::getValue($form_state
      ->getValues(), $path, $key_exists);
    $values = $this
      ->massageFormValues($values, $form, $form_state);
    if (isset($values['value']) && $values['value']) {

      // Replicate.

      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */

      /** @var \Drupal\Core\Entity\ContentEntityInterface $default_entity */
      $entity = $items
        ->getEntity();
      $sourceLanguage = NULL;
      if ($form_state
        ->hasValue('source_langcode')) {
        $sourceLanguageArray = $form_state
          ->getValue('source_langcode');
        if (isset($sourceLanguageArray['source'])) {
          $sourceLanguage = $sourceLanguageArray['source'];
        }
      }
      $default_entity = is_null($sourceLanguage) ? $entity
        ->getUntranslated() : $entity
        ->getTranslation($sourceLanguage);

      /** @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface $layout */
      $layout = $default_entity
        ->get(OverridesSectionStorage::FIELD_NAME);
      $uuid = \Drupal::service('uuid');

      /** @var \Drupal\layout_builder\Section[] $sections */
      $sections = $layout
        ->getSections();
      $new_sections = [];
      foreach ($sections as $delta => $section) {
        $cloned_section = clone $section;

        // Remove components from the cloned section.
        foreach ($cloned_section
          ->getComponents() as $c) {
          $cloned_section
            ->removeComponent($c
            ->getUuid());
        }

        // Sort the components by weight.
        $components = $section
          ->getComponents();
        uasort($components, function (SectionComponent $a, SectionComponent $b) {
          return $a
            ->getWeight() > $b
            ->getWeight() ? 1 : -1;
        });
        foreach ($components as $component) {
          $add_component = TRUE;
          $cloned_component = clone $component;
          $configuration = $component
            ->get('configuration');

          // Replicate inline block content.
          if ($this
            ->isInlineBlock($configuration['id'])) {

            /** @var \Drupal\block_content\BlockContentInterface $block */

            /** @var \Drupal\block_content\BlockContentInterface $replicated_block */
            $block = \Drupal::service('entity_type.manager')
              ->getStorage('block_content')
              ->loadRevision($configuration['block_revision_id']);
            $replicated_block = $this
              ->cloneEntity('block_content', $block
              ->id());
            if ($replicated_block) {
              $replicated_block
                ->set('langcode', $entity
                ->language()
                ->getId());
              $replicated_block
                ->save();
              $configuration = $this
                ->updateComponentConfiguration($configuration, $replicated_block);
              $cloned_component
                ->setConfiguration($configuration);

              // Store usage.
              \Drupal::service('inline_block.usage')
                ->addUsage($replicated_block
                ->id(), $entity);
            }
            else {
              $add_component = FALSE;
              $this
                ->messenger()
                ->addMessage($this
                ->t('The inline block "@label" was not duplicated.', [
                '@label' => $block
                  ->label(),
              ]));
            }
          }

          // Add component.
          if ($add_component) {
            $cloned_component
              ->set('uuid', $uuid
              ->generate());
            $cloned_section
              ->appendComponent($cloned_component);
          }
        }
        $new_sections[] = $cloned_section;
      }
      $items
        ->setValue($new_sections);
    }
    else {
      $items
        ->setValue(NULL);
    }
  }

  /**
   * Replicate an entity.
   *
   * @param $entity_type_id
   * @param $entity_id
   *
   * @return \Drupal\Core\Entity\EntityInterface|NULL
   */
  protected function cloneEntity($entity_type_id, $entity_id) {
    $clone = NULL;
    try {

      /** @var \Drupal\Core\Entity\EntityInterface $entity */

      /** @var \Drupal\Core\Entity\EntityInterface $clone */
      $entity = \Drupal::service('entity_type.manager')
        ->getStorage($entity_type_id)
        ->load($entity_id);
      $clone = $entity
        ->createDuplicate();

      /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions */
      $field_definitions = \Drupal::service('entity_field.manager')
        ->getFieldDefinitions($entity
        ->getEntityTypeId(), $entity
        ->bundle());
      foreach ($field_definitions as $definition) {

        // Support for Entity reference revisions.
        if ($definition
          ->getFieldStorageDefinition()
          ->getType() == 'entity_reference_revisions') {
          $new_values = [];
          $target_type = $definition
            ->getFieldStorageDefinition()
            ->getSetting('target_type');
          $values = $clone
            ->get($definition
            ->getName())
            ->getValue();
          if (!empty($values)) {
            foreach ($values as $value) {

              /** @var \Drupal\Core\Entity\EntityInterface $reference */

              /** @var \Drupal\Core\Entity\EntityInterface $reference_clone */
              $reference = \Drupal::service('entity_type.manager')
                ->getStorage($target_type)
                ->load($value['target_id']);
              $reference_clone = $reference
                ->createDuplicate();
              $reference_clone
                ->save();
              $new_values[] = [
                'target_id' => $reference_clone
                  ->id(),
                'target_revision_id' => $reference_clone
                  ->getRevisionId(),
              ];
            }
            if (!empty($new_values)) {
              $clone
                ->set($definition
                ->getName(), $new_values);
            }
          }
        }
      }
    } catch (\Exception $e) {
      \Drupal::logger('layout_builder_at')
        ->error('Error cloning entity: @message', [
        '@message' => $e
          ->getMessage(),
      ]);
    }
    return $clone;
  }

  /**
   * Does the block id represent an inline block.
   *
   * @param $block_id
   *   The block id.
   * @return bool
   *   True if this is an inline block else false.
   */
  protected function isInlineBlock($block_id) {
    return substr($block_id, 0, 13) === 'inline_block:';
  }

  /**
   * Modify the supplied component configuration based on modified block.
   *
   * @param array $configuration
   *   The Layout Builder component configuration array.
   * @param BlockContentInterface $replicated_block
   *   The cloned block.
   * @return array
   *   A modified configuration array.
   */
  protected function updateComponentConfiguration(array $configuration, BlockContentInterface $replicated_block) {
    $configuration["block_revision_id"] = $replicated_block
      ->getRevisionId();
    return $configuration;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AllowedTagsXssTrait::allowedTags public function Returns a list of tags allowed by AllowedTagsXssTrait::fieldFilterXss().
AllowedTagsXssTrait::displayAllowedTags public function Returns a human-readable list of allowed tags for display in help texts.
AllowedTagsXssTrait::fieldFilterXss public function Filters an HTML string to prevent XSS vulnerabilities.
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
LayoutBuilderCopyWidget::cloneEntity protected function Replicate an entity.
LayoutBuilderCopyWidget::defaultSettings public static function Defines the default settings for this plugin. Overrides PluginSettingsBase::defaultSettings
LayoutBuilderCopyWidget::extractFormValues public function Extract form values. Overrides WidgetBase::extractFormValues
LayoutBuilderCopyWidget::form public function Creates a form element for a field. Overrides WidgetBase::form
LayoutBuilderCopyWidget::formElement public function Returns the form for a single field widget. Overrides WidgetInterface::formElement
LayoutBuilderCopyWidget::isInlineBlock protected function Does the block id represent an inline block.
LayoutBuilderCopyWidget::options protected function Options for appearance.
LayoutBuilderCopyWidget::settingsForm public function Returns a form to configure settings for the widget. Overrides WidgetBase::settingsForm
LayoutBuilderCopyWidget::settingsSummary public function Returns a short summary for the current widget settings. Overrides WidgetBase::settingsSummary
LayoutBuilderCopyWidget::updateComponentConfiguration protected function Modify the supplied component configuration based on modified block.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginSettingsBase::$defaultSettingsMerged protected property Whether default settings have been merged into the current $settings.
PluginSettingsBase::$thirdPartySettings protected property The plugin settings injected by third party modules.
PluginSettingsBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies 6
PluginSettingsBase::getSetting public function Returns the value of a setting, or its default value if absent. Overrides PluginSettingsInterface::getSetting
PluginSettingsBase::getSettings public function Returns the array of settings, including defaults for missing settings. Overrides PluginSettingsInterface::getSettings
PluginSettingsBase::getThirdPartyProviders public function Gets the list of third parties that store information. Overrides ThirdPartySettingsInterface::getThirdPartyProviders
PluginSettingsBase::getThirdPartySetting public function Gets the value of a third-party setting. Overrides ThirdPartySettingsInterface::getThirdPartySetting
PluginSettingsBase::getThirdPartySettings public function Gets all third-party settings of a given module. Overrides ThirdPartySettingsInterface::getThirdPartySettings
PluginSettingsBase::mergeDefaults protected function Merges default settings values into $settings.
PluginSettingsBase::onDependencyRemoval public function Informs the plugin that some configuration it depends on will be deleted. Overrides PluginSettingsInterface::onDependencyRemoval 3
PluginSettingsBase::setSetting public function Sets the value of a setting for the plugin. Overrides PluginSettingsInterface::setSetting
PluginSettingsBase::setSettings public function Sets the settings for the plugin. Overrides PluginSettingsInterface::setSettings
PluginSettingsBase::setThirdPartySetting public function Sets the value of a third-party setting. Overrides ThirdPartySettingsInterface::setThirdPartySetting
PluginSettingsBase::unsetThirdPartySetting public function Unsets a third-party setting. Overrides ThirdPartySettingsInterface::unsetThirdPartySetting
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
WidgetBase::$fieldDefinition protected property The field definition.
WidgetBase::$settings protected property The widget settings. Overrides PluginSettingsBase::$settings
WidgetBase::addMoreAjax public static function Ajax callback for the "Add another item" button.
WidgetBase::addMoreSubmit public static function Submission handler for the "Add another item" button.
WidgetBase::afterBuild public static function After-build handler for field elements in a form.
WidgetBase::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create 5
WidgetBase::errorElement public function Assigns a field-level validation error to the right widget sub-element. Overrides WidgetInterface::errorElement 8
WidgetBase::flagErrors public function Reports field-level validation errors against actual form elements. Overrides WidgetBaseInterface::flagErrors 2
WidgetBase::formMultipleElements protected function Special handling to create form elements for multiple values. 1
WidgetBase::formSingleElement protected function Generates the form element for a single copy of the widget.
WidgetBase::getFieldSetting protected function Returns the value of a field setting.
WidgetBase::getFieldSettings protected function Returns the array of field settings.
WidgetBase::getFilteredDescription protected function Returns the filtered field description.
WidgetBase::getWidgetState public static function Retrieves processing information about the widget from $form_state. Overrides WidgetBaseInterface::getWidgetState
WidgetBase::getWidgetStateParents protected static function Returns the location of processing information within $form_state.
WidgetBase::handlesMultipleValues protected function Returns whether the widget handles multiple values.
WidgetBase::isApplicable public static function Returns if the widget can be used for the provided field. Overrides WidgetInterface::isApplicable 4
WidgetBase::isDefaultValueWidget protected function Returns whether the widget used for default value form.
WidgetBase::massageFormValues public function Massages the form values into the format expected for field values. Overrides WidgetInterface::massageFormValues 7
WidgetBase::setWidgetState public static function Stores processing information about the widget in $form_state. Overrides WidgetBaseInterface::setWidgetState
WidgetBase::__construct public function Constructs a WidgetBase object. Overrides PluginBase::__construct 5