layout_builder_paragraphs-1.0.x-dev/layout_builder_paragraphs.module

layout_builder_paragraphs.module
<?php

/**
 * @file
 * Layout Builder Paragraphs module.
 */

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\layout_builder_perms\LayoutBuilderElement;
use Drupal\Core\Language\LanguageInterface;

/**
 * Default paragraph field.
 *
 * @var string
 */
const PARAGRAPH_FIELD = 'field_contents';

/**
 * Implements hook_plugin_filter_TYPE__CONSUMER_alter().
 *
 * No longer used; kept for backwards compatibility with modules still implementing
 * hook_layout_builder_paragraphs_allowed_plugins() or
 * hook_layout_builder_paragraphs_disallowed_plugins().
 */
function layout_builder_paragraphs_plugin_filter_block__layout_builder_alter(array &$definitions) {

  // Default allow all, disallow none.
  $allowed_plugin_ids = array_keys($definitions);
  $disallowed_plugin_ids = [];
  // Allow other modules to alter the array.
  \Drupal::moduleHandler()->alter('layout_builder_paragraphs_allowed_plugins', $allowed_plugin_ids);
  // Allow other modules to alter the array.
  \Drupal::moduleHandler()->alter('layout_builder_paragraphs_disallowed_plugins', $disallowed_plugin_ids);

  // First, filter the allowed blocks in layout builder.
  $definitions = array_filter($definitions, function (string $plugin_id) use (&$allowed_plugin_ids): bool {
    foreach ($allowed_plugin_ids as $allowed_plugin_id) {
      if (preg_match("~{$allowed_plugin_id}~", $plugin_id)) {
        return TRUE;
      }
    }
    return FALSE;
  }, ARRAY_FILTER_USE_KEY);

  // Next, filter out the disallowed blocks.
  $definitions = array_filter($definitions, function (string $plugin_id) use (&$disallowed_plugin_ids): bool {
    foreach ($disallowed_plugin_ids as $disallowed_plugin_id) {
      if (preg_match("~{$disallowed_plugin_id}~", $plugin_id)) {
        return FALSE;
      }
    }
    return TRUE;
  }, ARRAY_FILTER_USE_KEY);

}

/**
 * Implements hook_form_alter().
 */
function layout_builder_paragraphs_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Only alter the node edit form when the content language is default. This is
  // related to the fact that paragraph field is symmetrically translated.
  // @TODO: consider nulling this logic IF paragraphs_asymmetric_translation_widgets
  // module is enabled.
  if (!\Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->isDefault()) return;

  $route = \Drupal::routeMatch()->getRouteName();
  // Add the Save and edit layout button for node edit form with layout builder.
  if ($route === 'node.add' || $route === 'entity.node.edit_form') {
    if (!empty($form_state->getStorage()['form_display'])) {
      // Check if this bundle allows custom layout builder per node.
      $bundle = $form_state->getStorage()['form_display']->getTargetBundle();
      $fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', $bundle);
      if (isset($fields['layout_builder__layout'])) {
        $is_edit_form = $form_state->getFormObject()->getOperation() === 'edit';

        // Clone submit button to a new button and set it as primary. Add new
        // submit handler on top of those coming from the default submit button.
        $form['actions']['save_and_edit_layout'] = $form['actions']['submit'];
        $form['actions']['save_and_edit_layout']['#value'] = t('Save and edit layout');
        $form['actions']['save_and_edit_layout']['#submit'][] = 'layout_builder_paragraphs_save_and_edit_layout';

        // If it's an edit entity form, put the new button after the default
        // Save button, remove primary class from the new button.
        if ($is_edit_form) {
          $form['actions']['save_and_edit_layout']['#weight'] = 10;
          unset($form['actions']['save_and_edit_layout']['#button_type']);
        }
        // Otherwise it should be a create entity form. Put the new button as
        // the first and primary button. Remove the primary class for the
        // default Save button.
        else {
          $form['actions']['save_and_edit_layout']['#weight'] = -50;
          if (!empty($form['actions']['submit']['#button_type']))
            unset($form['actions']['submit']['#button_type']);
        }
      }
    }
  }
  // When it is a quick node clone interface and there is PARAGRAPH_FIELD,
  // disable PARAGRAPH_FIELD and provide explanation.
  elseif ($route === 'quick_node_clone.node.quick_clone' && isset($form[PARAGRAPH_FIELD])) {
    $form[PARAGRAPH_FIELD]['#disabled'] = TRUE;
    $form[PARAGRAPH_FIELD]['quick_node_clone_warning'] = [
      '#type' => 'markup',
      '#weight' => -100,
      '#markup' => t('Please proceed to save the cloned node first before editing this field.'),
    ];
    $form['#attached']['library'][] = 'layout_builder_paragraphs/layout-builder-paragraphs-quick-node-clone';
  }
}

/**
 * Custom submit form to redirect node add and edit form to layout builder form.
 */
function layout_builder_paragraphs_save_and_edit_layout(array &$form, FormStateInterface $form_state) {
  // Clear the way for the redirection to the layout builder page, by
  // saving and unsetting the 'destination', if there is any.
  $request = \Drupal::request();
  if ($request->query->has('destination')) {
    $request->query->get('destination');
    $request->query->remove('destination');
  }
  // Set the redirect to layout builder.
  $nid = $form_state->getValues()['nid'];
  $layout_builder_url = Url::fromRoute('layout_builder.overrides.node.view', ['node' => $nid]);
  $form_state->setRedirectUrl($layout_builder_url);
}

/**
 * Implements hook_field_widget_WIDGET_ID_form_alter().
 * @TODO: check if this is invalid. The code is taken from
 * https://github.com/nathandentzau/layout-builder-examples but the hook may
 * not be valid. The hook somehow works though.
 */
function layout_builder_paragraphs_field_widget_layout_builder_widget_form_alter(array &$element) {
  $element['#type'] = 'modal_layout_builder';
}

/**
 * Implements hook_contextual_links_alter().
 */
function layout_builder_paragraphs_contextual_links_alter(array &$links, string $group, array $route_parameters) {
  if ($group !== 'layout_builder_block') {
    return;
  }

  foreach ($links as &$link) {
    $link['localized_options']['attributes']['data-dialog-type'] = 'modal';
    unset($link['localized_options']['attributes']['data-dialog-renderer']);
  }
}

/**
 * Implements hook_element_info_alter().
 */
function layout_builder_paragraphs_element_info_alter(array &$element) {
  // Apply the layout_builder_perms preRender method when the relevant two
  // modules are enabled.
  if (!\Drupal::moduleHandler()->moduleExists('layout_builder_suite') ||
    !\Drupal::moduleHandler()->moduleExists('layout_builder_perms')) return;
  $element['modal_layout_builder']['#pre_render'][] = [LayoutBuilderElement::class, 'preRender'];
}

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter().
 *
 * Used for D8, D9.
 */
function layout_builder_paragraphs_field_widget_paragraphs_form_alter(&$element, FormStateInterface $form_state, $context) {
  _layout_builder_paragraphs_process_paragraphs_field_widget($element, $form_state, $context);
}

/**
 * Implements hook_field_widget_single_element_WIDGET_TYPE_form_alter().
 */
function layout_builder_paragraphs_field_widget_single_element_paragraphs_form_alter(&$element, FormStateInterface $form_state, $context) {
  _layout_builder_paragraphs_process_paragraphs_field_widget($element, $form_state, $context);
}

/**
 * Identify unused paragraph deltas in the layout.
 */
function _layout_builder_paragraphs_process_paragraphs_field_widget(&$element, FormStateInterface $form_state, $context) {
  // Only proceed when field name matches PARAGRAPH_FIELD.
  if ($context['items']->getFieldDefinition()->getName() != PARAGRAPH_FIELD) return;
  // Only proceed when delta has not been processed.
  $id = $context['items']->getFieldDefinition()->getName() . '_' . $context['delta'] . '_processed';
  if ($form_state->get($id)) return;
  // First, extract all the paragraph deltas used in the layout.
  // Check if paragraph_deltas_used_in_layout is already set in form_state.
  if ($deltas = $form_state->get('paragraph_deltas_used_in_layout')) {
    $paragraph_deltas_used_in_layout = $deltas;
  }
  else {
    $paragraph_deltas_used_in_layout = [];
    $paragraph = $context['items']->get($context['delta']);
    $node = $paragraph->getEntity();
    // Only proceed if node has layout.
    if (empty($node->layout_builder__layout)) return;
    // Iterate through each section and its components.
    foreach ($node->layout_builder__layout->getSections() as $section) {
      foreach ($section->getComponents() as $component) {
        // If the component is a paragraph_blocks plugin, extract the delta.
        $prefix = 'paragraph_field:node:' . PARAGRAPH_FIELD . ':';
        $plugin_id = $component->getPluginId();
        if (strpos($plugin_id, $prefix) === 0) {
          $paragraph_deltas_used_in_layout[substr($plugin_id, strlen($prefix), 1)] = TRUE;
        }
      }
    }
    $form_state->set('paragraph_deltas_used_in_layout', $paragraph_deltas_used_in_layout);
  }
  // Next, if the delta is not used in the layout, add special class.
  if (!isset($paragraph_deltas_used_in_layout[$context['delta']])) {
    $element['#attributes']['class'][] = 'missing-delta-in-layout';
    $element['#attached']['library'][] = 'layout_builder_paragraphs/layout-builder-paragraphs-missing-delta';
    $description = t('This paragraph is not used in the layout.');
    $element['#suffix'] = '<div class="missing-delta-description">' . $description . '</div>' . $element['#suffix'];
    // Indicate in form state that this delta has been processed.
    $form_state->set($id, TRUE);
  }
}

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

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