Commit f0f61474 authored by Justin Toupin's avatar Justin Toupin
Browse files

Issue #3211196: Add "Promote to library" / Paragraphs Library compatibility

parent 2a8d4160
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -275,8 +275,6 @@ function layout_paragraphs_entity_extra_field_info() {

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Adjusts the field weight based on settings from "Manage form display" tab.
 */
function layout_paragraphs_form_layout_paragraphs_component_form_alter(&$form, FormStateInterface $form_state) {
  $display = $form['#display'];
@@ -285,6 +283,16 @@ function layout_paragraphs_form_layout_paragraphs_component_form_alter(&$form, F
  }
}

/**
 * Adjusts the field weight based on settings from "Manage form display" tab.
 */
function _layout_paragraphs_form_field_weights(array &$form, FormStateInterface $form_state) {
  $display = $form['#display'];
  if ($layout_paragraphs_fields = $display->getComponent('layout_paragraphs_fields')) {
    $form['layout_paragraphs']['#weight'] = $layout_paragraphs_fields['weight'];
  }
}

/**
 * Implements hook_ENTITY_presave().
 *
+8 −0
Original line number Diff line number Diff line
name: Layout Paragraphs Library
description: Provides integration between Layout Paragraphs and Paragraphs Library
core_version_requirement: ^8 || ^9
type: module
package: 'Paragraphs'
dependencies:
  - layout_paragraphs:layout_paragraphs
  - paragraphs:paragraphs_library
+155 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Contains layout paragraphs library module.
 */

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\Core\Form\FormStateInterface;
use Drupal\paragraphs_library\Entity\LibraryItem;

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the layout paragraphs component form to add 'Promote to library'.
 */
function layout_paragraphs_library_form_layout_paragraphs_component_form_alter(array &$form, FormStateInterface $form_state) {
  /** @var \Drupal\layout_paragraphs\Contracts\ComponentFormInterface $form_object */
  $form_object = $form_state->getFormObject();
  $paragraph = $form_object->getParagraph();
  $paragraph_type = $paragraph->getParagraphType();

  // Only applies to paragraph types that allow being promoted to library.
  // Section paragraphs cannot be used as library items.
  $allow_library_conversion =
    $paragraph_type->getThirdPartySetting('paragraphs_library', 'allow_library_conversion', FALSE)
    && !$paragraph_type->hasEnabledBehaviorPlugin('layout_paragraphs');
  if ($allow_library_conversion) {
    $form['actions']['promote_to_library'] = [
      '#type' => 'submit',
      '#value' => t('Promote to library'),
      '#submit' => ['layout_paragraphs_library_submit'],
      '#name' => 'promote-to-library',
      '#ajax' => [
        'callback' => 'layout_paragraphs_library_ajax',
      ],
      '#weight' => 110,
    ];
    // Fix inline_entity_form compabitility.
    // @see https://www.drupal.org/project/inline_entity_form/issues/2830136
    if ($form['actions']['submit']['#ief_submit_trigger']) {
      $form['actions']['promote_to_library']['#ief_submit_trigger'] = TRUE;
      $form['actions']['promote_to_library']['#ief_submit_trigger_all'] = TRUE;
      array_unshift($form['actions']['promote_to_library']['#submit'], $form['actions']['submit']['#submit'][0]);
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the paragraph types form.
 */
function layout_paragraphs_library_form_paragraphs_type_form_alter(array &$form, FormStateInterface $form_state) {
  /** @var \Drupal\paragraphs\Form\ParagraphsTypeForm $form_object */
  $form_object = $form_state->getFormObject();
  /** @var \Drupal\paragraphs\ParagraphsTypeInterface $paragraph_type */
  $paragraph_type = $form_object->getEntity();
  if ($paragraph_type->id() != 'from_library' && $paragraph_type->hasEnabledBehaviorPlugin('layout_paragraphs')) {
    $form['#process'][] = 'layout_paragraphs_library_disable_allow_library_conversion';
  }
}

/**
 * Form #process callback.
 *
 * Blocks access to 'allow_library_conversion' form field.
 */
function layout_paragraphs_library_disable_allow_library_conversion($form, FormStateInterface $form_state) {
  if (isset($form['allow_library_conversion'])) {
    $form['allow_library_conversion']['#access'] = FALSE;
  }
  return $form;
}

/**
 * Form submit callback for "Promote to library" button.
 */
function layout_paragraphs_library_submit(&$form, FormStateInterface $form_state) {

  $tempstore = \Drupal::service('layout_paragraphs.tempstore_repository');
  /** @var \Drupal\layout_paragraphs\Contracts\ComponentFormInterface $form_object */
  $form_object = $form_state->getFormObject();
  $paragraph = $form_object->buildParagraphComponent($form, $form_state);
  $layout_paragraphs_layout = $form_object->getLayoutParagraphsLayout();
  $component = $layout_paragraphs_layout->getComponent($paragraph);
  $component_settings = $component->getSettings();

  $form_state->set('original_paragraph', $paragraph);

  // Replacing element in the array.
  $library_item = LibraryItem::createFromParagraph($paragraph);
  $library_item->save();

  // Replace this paragraph with a library reference one.
  $library_paragraph = Paragraph::create([
    'type' => 'from_library',
    'field_reusable_paragraph' => $library_item,
  ]);
  $library_component = $layout_paragraphs_layout->getComponent($library_paragraph);
  $library_component->setSettings($component_settings);
  $form_object->setParagraph($library_component->getEntity());

  if (get_class($form_object) == 'Drupal\layout_paragraphs\Form\EditComponentForm') {
    $layout_paragraphs_layout->insertBeforeComponent($paragraph->uuid(), $library_component->getEntity());
    $layout_paragraphs_layout->deleteComponent($paragraph->uuid());
    $form_object->setLayoutParagraphsLayout($layout_paragraphs_layout);
    $tempstore->set($form_object->getLayoutParagraphsLayout());
  }
  elseif (get_class($form_object) == 'Drupal\layout_paragraphs\Form\InsertComponentForm') {
    $form_object->setParagraph($library_component->getEntity());
    /** @var \Drupal\layout_paragraphs\Form\InsertComponentForm $form_object */
    $form_object->insertComponent();
    $tempstore->set($form_object->getLayoutParagraphsLayout());
  }

}

/**
 * Ajax callback for "Promote to library" button.
 */
function layout_paragraphs_library_ajax(&$form, FormStateInterface $form_state) {

  // Check for errors.
  if ($form_state->hasAnyErrors()) {
    $form['status_messages'] = [
      '#type' => 'status_messages',
      '#weight' => -1000,
    ];
    $form['#sorted'] = FALSE;
    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand('[data-drupal-selector="' . $form['#attributes']['data-drupal-selector'] . '"]', $form));
    return $response;
  }

  $original_paragraph = $form_state->get('original_paragraph');
  /** @var \Drupal\layout_paragraphs\Contracts\ComponentFormInterface $form_object */
  $form_object = $form_state->getFormObject();
  if (get_class($form_object) == 'Drupal\layout_paragraphs\Form\EditComponentForm') {
    $response = $form_object->successfulAjaxSubmit($form, $form_state);
    $wrong_selector = '[data-uuid=' . $form_object->getParagraph()->uuid() . ']';
    $correct_selector = '[data-uuid=' . $original_paragraph->uuid() . ']';
    foreach ($response->getCommands() as &$command) {
      if ($command['command'] == 'insert' && $command['selector'] == $wrong_selector) {
        $command['selector'] = $correct_selector;
      }
    };
    return $response;
  }
  else {
    return $form_object->successfulAjaxSubmit($form, $form_state);
  }
}
+100 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\layout_paragraphs\Contracts;

use Drupal\Core\Form\FormInterface;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\Core\Form\FormStateInterface;
use Drupal\layout_paragraphs\LayoutParagraphsLayout;

/**
 * Defines an interface for Layout Paragraphs component forms.
 */
interface ComponentFormInterface extends FormInterface {

  /**
   * Gets the paragraph entity.
   */
  public function getParagraph();

  /**
   * Sets the paragraph entity.
   */
  public function setParagraph(Paragraph $paragraph);

  /**
   * Gets the Layout Paragraphs Layout object.
   */
  public function getLayoutParagraphsLayout();

  /**
   * Setter for layoutParagraphsLayout property.
   *
   * @param \Drupal\layout_paragraphs\LayoutParagraphsLayout $layout_paragraphs_layout
   *   The layout paragraphs layout object.
   *
   * @return $this
   */
  public function setLayoutParagraphsLayout(LayoutParagraphsLayout $layout_paragraphs_layout);

  /**
   * Builds the paragraph component using submitted form values.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state object.
   *
   * @return \Drupal\paragraphs\Entity\Paragraph
   *   The paragraph entity.
   */
  public function buildParagraphComponent(array $form, FormStateInterface $form_state);

  /**
   * Form #process callback.
   *
   * Renders the layout paragraphs behavior form for layout selection.
   *
   * @param array $element
   *   The form element.
   * @param Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $form
   *   The complete form array.
   *
   * @return array
   *   The processed element.
   */
  public function layoutParagraphsBehaviorForm(array $element, FormStateInterface $form_state, array &$form);

  /**
   * Form #process callback.
   *
   * Attaches the behavior plugin forms.
   *
   * @param array $element
   *   The form element.
   * @param Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $form
   *   The complete form array.
   *
   * @return array
   *   The processed element.
   */
  public function behaviorPluginsForm(array $element, FormStateInterface $form_state, array &$form);

  /**
   * Provides an Ajax reponse to inject the new / editing component.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state object.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The Ajax response.
   */
  public function successfulAjaxSubmit(array $form, FormStateInterface $form_state);

}
+36 −6
Original line number Diff line number Diff line
@@ -8,26 +8,28 @@ use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Access\AccessResult;
use Drupal\field_group\FormatterHelper;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxFormHelperTrait;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Layout\LayoutPluginManager;
use Drupal\layout_paragraphs\Utility\Dialog;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\layout_paragraphs\Contracts\ComponentFormInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\layout_paragraphs\LayoutParagraphsLayoutRefreshTrait;
use Drupal\layout_paragraphs\LayoutParagraphsLayoutTempstoreRepository;
use Drupal\layout_paragraphs\Utility\Dialog;

/**
 * Class LayoutParagraphsComponentFormBase.
 *
 * Base form for Layout Paragraphs component forms.
 */
abstract class ComponentFormBase extends FormBase {
abstract class ComponentFormBase extends FormBase implements ComponentFormInterface {

  use AjaxFormHelperTrait;
  use LayoutParagraphsLayoutRefreshTrait;
@@ -121,6 +123,27 @@ abstract class ComponentFormBase extends FormBase {
    return 'layout_paragraphs_component_form';
  }

  /**
   * {@inheritDoc}
   */
  public function getParagraph() {
    return $this->paragraph;
  }

  /**
   * {@inheritDoc}
   */
  public function setParagraph(Paragraph $paragraph) {
    $this->paragraph = $paragraph;
  }

  /**
   * {@inheritDoc}
   */
  public function getLayoutParagraphsLayout() {
    return $this->layoutParagraphsLayout;
  }

  /**
   * Builds a component (paragraph) edit form.
   *
@@ -152,6 +175,7 @@ abstract class ComponentFormBase extends FormBase {
        '#type' => 'actions',
        'submit' => [
          '#type' => 'submit',
          '#weight' => 100,
          '#value' => $this->t('Save'),
          '#ajax' => [
            'callback' => '::ajaxSubmit',
@@ -164,6 +188,7 @@ abstract class ComponentFormBase extends FormBase {
        ],
        'cancel' => [
          '#type' => 'button',
          '#weight' => 200,
          '#value' => $this->t('Cancel'),
          '#ajax' => [
            'callback' => '::cancel',
@@ -267,9 +292,14 @@ abstract class ComponentFormBase extends FormBase {
   *   The form state object.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->paragraph = $this->buildParagraphComponent($form, $form_state);
    $this->setParagraph($this->buildParagraphComponent($form, $form_state));
  }

  /**
   * {@inheritDoc}
   */
  abstract public function successfulAjaxSubmit(array $form, FormStateInterface $form_state);

  /**
   * Builds the paragraph component using submitted form values.
   *
@@ -281,7 +311,7 @@ abstract class ComponentFormBase extends FormBase {
   * @return \Drupal\paragraphs\Entity\Paragraph
   *   The paragraph entity.
   */
  protected function buildParagraphComponent(array $form, FormStateInterface $form_state) {
  public function buildParagraphComponent(array $form, FormStateInterface $form_state) {
    /** @var Drupal\Core\Entity\Entity\EntityFormDisplay $display */
    $display = $form['#display'];

@@ -343,7 +373,7 @@ abstract class ComponentFormBase extends FormBase {
   *
   * @param array $element
   *   The form element.
   * @param Drupal\Core\Form\FormStateInterface $form_state
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $form
   *   The complete form array.
Loading