You are here

layout_paragraphs.module in Layout Paragraphs 2.0.x

Same filename and directory in other branches
  1. 1.0.x layout_paragraphs.module

File

layout_paragraphs.module
View source
<?php

/**
 * @file
 * Contains layout_paragraphs.module.
 */
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\Core\Routing\RouteMatchInterface;

/**
 * Implements hook_help().
 */
function layout_paragraphs_help($route_name, RouteMatchInterface $route_match) {
  $output = '';
  switch ($route_name) {

    // Main module help for the layout_paragraphs module.
    case 'help.page.layout_paragraphs':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Provide layout integration for paragraph fields.') . '</p>';
      break;
  }
  return $output;
}

/**
 * Implements hook_theme().
 */
function layout_paragraphs_theme() {
  return [
    'layout_paragraphs' => [
      'variables' => [
        'elements' => '',
        'content' => '',
      ],
    ],
    'layout_paragraphs_builder' => [
      'variables' => [
        'attributes' => [],
        'id' => '',
        'root_components' => [],
        'is_empty' => FALSE,
        'empty_message' => '',
        'insert_button' => '',
        'translation_warning' => '',
      ],
    ],
    'layout_paragraphs_builder_formatter' => [
      'variables' => [
        'link_url' => NULL,
        'link_text' => NULL,
        'field_label' => NULL,
        'is_empty' => FALSE,
        'root_components' => [],
      ],
    ],
    'layout_paragraphs_builder_controls' => [
      'variables' => [
        'attributes' => [],
        'label' => NULL,
        'edit_attributes' => [],
        'delete_attributes' => [],
      ],
    ],
    'layout_paragraphs_builder_component_menu' => [
      'variables' => [
        'attributes' => [],
        'types' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_theme_suggestions().
 */
function layout_paragraphs_theme_suggestions_layout_paragraphs(array $variables) {
  $suggestions = [];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  $suggestions[] = 'layout_paragraphs__' . $sanitized_view_mode;
  $suggestions[] = 'layout_paragraphs__' . $variables['elements']['field_name'];
  $suggestions[] = 'layout_paragraphs__' . $variables['elements']['field_name'] . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_module_implements_alter().
 *
 * If "content_translation", move the form_alter implementation by the
 * layout_paragraphs at the end of the list, so that it might be
 * called after the content_translation one.
 * Otherwise the $form['translatable'] won't be defined in
 * layout_paragraphs_form_field_config_edit_form_alter.
 *
 * @see: https://www.hashbangcode.com/article/drupal-8-altering-hook-weights.
 */
function layout_paragraphs_module_implements_alter(&$implementations, $hook) {

  // Move our hook_entity_type_alter() implementation to the end of the list.
  if ($hook == 'form_alter' && isset($implementations['layout_paragraphs']) && isset($implementations['content_translation'])) {
    $hook_init = $implementations['layout_paragraphs'];
    unset($implementations['layout_paragraphs']);
    $implementations['layout_paragraphs'] = $hook_init;
  }
}

/**
 * Implements hook_prepreprocess_radios().
 *
 * Add wrapper class for layout selection.
 */
function layout_paragraphs_preprocess_radios(&$variables) {
  if (isset($variables['element']['#wrapper_attributes'])) {
    $variables['attributes'] += $variables['element']['#wrapper_attributes'];
  }
}

/**
 * Implements hook_ENTITY_presave().
 *
 * Updates references to parent layout section uuids in cases
 * where paragraphs are duplicated, for example when using the
 * Replicate module.
 *
 * @todo Consider changing approach if this issue is addressed in core:
 * https://www.drupal.org/project/drupal/issues/3040556
 * (It is not possible to react to an entity being duplicated)
 */
function layout_paragraphs_paragraph_presave(Paragraph $paragraph) {
  $behavior_settings = $paragraph
    ->getAllBehaviorSettings();
  $parent_uuid = $behavior_settings['layout_paragraphs']['parent_uuid'] ?? NULL;
  if (empty($parent_uuid)) {
    return;
  }

  // If there is no host, the parent hasn't been saved and we are not cloning.
  $host = $paragraph
    ->getParentEntity();
  if (empty($host)) {
    return;
  }

  // If the parent component cannot be loaded, we are not cloning.
  $parent_component = \Drupal::service('entity.repository')
    ->loadEntityByUuid('paragraph', $parent_uuid);
  if (empty($parent_component)) {
    return;
  }

  // If the parent component does not have a parent entity, we are not cloning.
  $parent_host = $parent_component
    ->getParentEntity();
  if (empty($parent_host)) {
    return;
  }

  // If the current paragraph's host's id does not match
  // the paragraph's parent's host's id, we ARE cloning.
  if ($host
    ->id() !== $parent_host
    ->id()) {

    // Map the UUIDs to deltas from the clone source.
    $items = _layout_paragraphs_get_paragraphs($parent_component);
    foreach ($items as $delta => $item) {
      $uuid_map[$item
        ->uuid()] = $delta;
    }

    // Map the deltas to UUIds from the clone destination.
    $items = _layout_paragraphs_get_paragraphs($paragraph);
    $delta_map = [];
    foreach ($items as $delta => $item) {
      $delta_map[$delta] = $item
        ->uuid();
    }

    // Assign the correct uuid.
    $parent_delta = $uuid_map[$parent_uuid];
    $correct_uuid = $delta_map[$parent_delta];

    // Since paragraph::preSave() has already been called,
    // we have to set the serialized behavior settings directly
    // rather than using setBehaviorSettings().
    $behavior_settings['layout_paragraphs']['parent_uuid'] = $correct_uuid;
    $paragraph
      ->set('behavior_settings', serialize($behavior_settings));
  }
}

/**
 * Returns a list of all sibling paragraphs given a single paragraph.
 *
 * The returned list contains all of the referenced entities,
 * including the passed $paragraph.
 *
 * @param \Drupal\paragraphs\Entity\Paragraph $paragraph
 *   The paragraph to use for finding the complete list of siblings.
 *
 * @return \Drupal\Core\Field\EntityReferenceFieldItemList
 *   The list of paragraphs.
 */
function _layout_paragraphs_get_paragraphs(Paragraph $paragraph) {
  $host = $paragraph
    ->getParentEntity();
  $parent_field = $paragraph
    ->get('parent_field_name');
  $field_name = $parent_field
    ->first()
    ->getString();

  /** @var \Drupal\Core\Field\EntityReferenceFieldItemList $item_list */
  $item_list = $host
    ->get($field_name);
  return $item_list
    ->referencedEntities();
}

Functions

Namesort descending Description
layout_paragraphs_help Implements hook_help().
layout_paragraphs_module_implements_alter Implements hook_module_implements_alter().
layout_paragraphs_paragraph_presave Implements hook_ENTITY_presave().
layout_paragraphs_preprocess_radios Implements hook_prepreprocess_radios().
layout_paragraphs_theme Implements hook_theme().
layout_paragraphs_theme_suggestions_layout_paragraphs Implements hook_theme_suggestions().
_layout_paragraphs_get_paragraphs Returns a list of all sibling paragraphs given a single paragraph.