You are here

bricks.module in Bricks​ 8

Same filename and directory in other branches
  1. 7.5 bricks.module
  2. 7.4 bricks.module
  3. 2.x bricks.module

File

bricks.module
View source
<?php

use Drupal\Core\Render\Element;
use Drupal\Component\Utility\Html;

/**
 * Prepares variables for `field.html.twig`.
 */
function bricks_preprocess_field(&$variables) {
  $element = $variables['element'];
  if (in_array($element['#formatter'], [
    'bricks_nested',
    'bricks_nested_dynamic',
  ])) {
    $variables['items'] = [
      [
        'content' => _bricks_nest_items($element, $variables['items']),
      ],
    ];
  }
}

/**
 * Helper function: converts element's items to a tree structure.
 */
function _bricks_nest_items($element, $items) {

  // Prepare items:
  $parents = [
    -1,
  ];
  $prev_depth = 0;
  foreach ($items as $delta => $item) {
    if (!$element['#items'][$delta]->entity) {
      unset($element['#items'][$delta]);
      unset($items[$delta]);
      continue;
    }
    $depth = $element['#items'][$delta]->depth;
    if ($depth > $prev_depth) {
      array_unshift($parents, $delta - 1);
    }
    elseif ($depth < $prev_depth) {
      array_splice($parents, 0, $prev_depth - $depth);
    }
    $prev_depth = $depth;
    $items[$delta] = $items[$delta]['content'];
    $items[$delta]['#label'] = $element['#items'][$delta]->entity
      ->label();
    $items[$delta]['#delta'] = $delta;
    $items[$delta]['#parent_delta'] = $parents[0];
    $items[$delta]['#attributes']['class'][] = 'brick';
    $items[$delta]['#attributes']['class'][] = 'brick--type--' . Html::cleanCssIdentifier($element['#items'][$delta]->entity
      ->bundle());
    $items[$delta]['#attributes']['class'][] = 'brick--id--' . $element['#items'][$delta]->entity
      ->id();
    $items[$delta]['childs'] = [];
    if (!empty($element['#items'][$delta]->options['view_mode'])) {
      $items[$delta]['#view_mode'] = $element['#items'][$delta]->options['view_mode'];
    }
    if (!empty($element['#items'][$delta]->options['layout'])) {
      $items[$delta]['#layout'] = $element['#items'][$delta]->options['layout'];
    }
    if (!empty($element['#items'][$delta]->options['css_class'])) {
      $items[$delta]['#attributes']['class'][] = $element['#items'][$delta]->options['css_class'];
    }

    // Disable entity render cache, rely on field cache:
    $items[$delta]['#cache'] = [
      'disabled' => TRUE,
    ];
  }

  // Process items in reverse order (without recursion):
  $rdeltas = array_reverse(array_keys($items));
  foreach ($rdeltas as $delta) {
    $parent_delta = $items[$delta]['#parent_delta'];
    if (!empty($items[$delta]['#layout']) && \Drupal::service('module_handler')
      ->moduleExists('layout_discovery')) {
      $items[$delta] = array_intersect_key($items[$delta], array_flip([
        '#label',
        '#attributes',
      ])) + _bricks_build_layout_from_items($items[$delta]['#layout'], $items[$delta]['childs']);
    }
    if ($parent_delta != -1) {
      array_unshift($items[$parent_delta]['childs'], $items[$delta]);
      unset($items[$delta]);
    }
  }
  return $items;
}

/**
 * Helper function for layout handling in _bricks_nest_items().
 */
function _bricks_build_layout_from_items($layout, $items) {
  $layoutPluginManager = \Drupal::service('plugin.manager.core.layout');
  if (!$layoutPluginManager
    ->hasDefinition($layout)) {
    drupal_set_message(t('Layout `%layout_id` is unknown.', [
      '%layout_id' => $layout,
    ]), 'warning');
    return;
  }

  // Provide any configuration to the layout plugin if necessary.
  $layoutInstance = $layoutPluginManager
    ->createInstance($layout);
  $regionNames = $layoutInstance
    ->getPluginDefinition()
    ->getRegionNames();

  // Adjust the lengths:
  $count = min(count($regionNames), count($items));
  $regionNames = array_slice($regionNames, 0, $count);
  $items = array_slice($items, 0, $count);

  // Build the content for your regions.
  $regions = array_combine($regionNames, $items);

  // This builds the render array.
  return $layoutInstance
    ->build($regions);
}

/* BRICKS EDITING */

/**
 * Implements hook_field_widget_info_alter().
 */
function bricks_field_widget_info_alter(array &$info) {

  // Let Bricks to re-use ANY Entity Reference -compatible widgets:
  foreach ($info as $widget_id => &$widget_info) {
    if (in_array('entity_reference', $widget_info['field_types'])) {
      $widget_info['field_types'][] = 'bricks';
    }
    if (in_array('entity_reference_revisions', $widget_info['field_types'])) {
      $widget_info['field_types'][] = 'bricks_revisioned';
    }
  }
}

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter() for `entity_reference_autocomplete`.
 */
function bricks_field_widget_entity_reference_autocomplete_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  $field_type = $context['items']
    ->getFieldDefinition()
    ->getType();

  // @TODO: Replace by 'Nested bricks' widget setting.
  if (in_array($field_type, [
    'bricks',
  ])) {

    // @TODO: Find a better way to be used in _bricks_preprocess_tabledrag_form().
    $element['#widget'] = 'entity_reference_autocomplete';

    // #default_value is en Entity or NULL.
    _bricks_form_element_alter($element, $context['items'][$context['delta']], $element['target_id']['#default_value']);
    hide($element['depth']);
  }
}

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter() for `bricks_tree_autocomplete`.
 */
function bricks_field_widget_bricks_tree_autocomplete_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  bricks_field_widget_entity_reference_autocomplete_form_alter($element, $form_state, $context);
}

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter() for `paragraphs`.
 */
function bricks_field_widget_paragraphs_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  $field_type = $context['items']
    ->getFieldDefinition()
    ->getType();

  // @TODO: Replace by 'Nested bricks' widget setting.
  if (in_array($field_type, [
    'bricks_revisioned',
  ])) {

    // @TODO: Find a better way to be used in _bricks_preprocess_tabledrag_form().
    $element['#widget'] = 'paragraphs';
    $item = $context['items'][$context['delta']];
    $entity = $item->target_id ? entity_load('paragraph', $item->target_id) : entity_create('paragraph', array(
      'type' => $element['subform']['#process'][0][0]
        ->getTargetBundle(),
    ));
    _bricks_form_element_alter($element, $item, $entity);
    hide($element['depth']);
  }
}

/**
 * Prepares variables for `field-multiple-value-form.html.twig`.
 */
function bricks_preprocess_field_multiple_value_form(&$variables) {
  _bricks_preprocess_tabledrag_form($variables, 'element', 'entity_reference_autocomplete', $variables['element']['#field_name'] . '-delta-order');
  _bricks_preprocess_tabledrag_form($variables, 'element', 'paragraphs', $variables['element']['#field_name'] . '-delta-order');
}

/**
 * Helper function for hook_preprocess_field_multiple_value_form().
 */
function _bricks_preprocess_tabledrag_form(&$variables, $element_key, $widget, $order_class, $render_options = FALSE) {
  $element = $variables[$element_key];

  // @TODO: Replace by 'Nested bricks' widget setting.
  if (isset($element['#widget']) && $element['#widget'] == $widget || isset($element[0]['#widget']) && $element[0]['#widget'] == $widget) {

    // @TODO: Tmp hack for the proper indent width calculation.
    $variables['table']['#header'][0]['style'] = 'min-width: 150px';
    $variables['table']['#header'][] = [
      'data' => t('Depth'),
      'class' => [
        'bricks-depth-header',
      ],
    ];
    $row = 0;
    foreach (Element::children($element) as $i => $key) {
      if ($key !== 'add_more' && $key !== 'header_actions') {
        $depth = $element[$key]['depth']['#value'];
        $indentation = [];
        if ($depth > 0) {
          $indentation = [
            '#theme' => 'indentation',
            '#size' => $depth,
          ];
        }
        $drag_cell =& $variables['table']['#rows'][$row]['data'][0];
        $drag_cell['data'] = !empty($indentation) ? drupal_render($indentation) : '' . $drag_cell['data'];

        // @TODO
        $drag_cell['style'] = 'width: auto; min-width: 150px';
        show($element[$key]['depth']);
        $variables['table']['#rows'][$row]['data'][] = \Drupal::service('renderer')
          ->render($element[$key]['depth']);

        // @TODO: Get rid of $render_options hack.
        if ($render_options) {
          $element[$key]['options']['#prefix'] = $variables['table']['#rows'][$row]['data'][2]['data'];
          $variables['table']['#rows'][$row]['data'][2]['data'] = \Drupal::service('renderer')
            ->render($element[$key]['options']);
        }
      }
      if ($key !== 'add_more') {
        $row++;
      }
    }
    $tabledrag_options =& $variables['table']['#tabledrag'];
    $tabledrag_options[0]['relationship'] = 'all';
    $tabledrag_options[] = [
      'action' => 'depth',
      'relationship' => 'group',
      'group' => 'bricks-depth',
    ];

    // Fake option to enable indentation:
    $tabledrag_options[] = [
      'action' => 'match',
      'relationship' => 'parent',
      'group' => $order_class,
    ];
    $variables['table']['#attached']['library'][] = 'bricks/tabledrag.relationship-all';
  }
}

/**
 * Helper function for widget's formElement().
 */
function _bricks_form_element_alter(&$element, $item, $entity) {
  $element['depth'] = [
    // @TODO: Other types break the correct indentations.
    '#type' => 'hidden',
    '#default_value' => !empty($item->depth) ? $item->depth : 0,
    '#weight' => 10,
    '#attributes' => [
      'class' => [
        'bricks-depth',
      ],
    ],
  ];
  $element['options'] = [
    '#type' => 'container',
    '#weight' => 100,
    '#attributes' => [
      'class' => [
        'container-inline',
      ],
    ],
  ];
  if ($entity) {
    if ($entity
      ->bundle() == 'layout' && \Drupal::service('module_handler')
      ->moduleExists('layout_discovery')) {
      $element['options']['layout'] = [
        '#type' => 'select',
        '#options' => \Drupal::service('plugin.manager.core.layout')
          ->getLayoutOptions(),
        '#default_value' => !empty($item->options['layout']) ? $item->options['layout'] : NULL,
      ];
    }
    if ($entity
      ->bundle() != 'layout') {
      $element['options']['view_mode'] = [
        '#type' => 'select',
        '#options' => \Drupal::service('entity_display.repository')
          ->getViewModeOptionsByBundle($entity
          ->getEntityTypeId(), $entity
          ->bundle()),
        '#default_value' => !empty($item->options['view_mode']) ? $item->options['view_mode'] : NULL,
      ];
    }
  }
  $element['options']['css_class'] = [
    '#type' => 'textfield',
    '#default_value' => !empty($item->options['css_class']) ? $item->options['css_class'] : '',
    '#size' => 10,
    '#attributes' => [
      'placeholder' => t('CSS class(-es)'),
    ],
  ];
}

/* MISC */

/**
 * Prepares variables for `block.html.twig` for `system_powered_by_block`.
 */
function bricks_preprocess_block__system_powered_by_block(&$variables) {
  $bricks_link = '<a href="https://uibricks.com">Bricks</a>';
  $variables['content']['#markup'] = str_replace('>Drupal</a>', '>Drupal</a> & ' . $bricks_link, $variables['content']['#markup']);
}

Functions

Namesort descending Description
bricks_field_widget_bricks_tree_autocomplete_form_alter Implements hook_field_widget_WIDGET_TYPE_form_alter() for `bricks_tree_autocomplete`.
bricks_field_widget_entity_reference_autocomplete_form_alter Implements hook_field_widget_WIDGET_TYPE_form_alter() for `entity_reference_autocomplete`.
bricks_field_widget_info_alter Implements hook_field_widget_info_alter().
bricks_field_widget_paragraphs_form_alter Implements hook_field_widget_WIDGET_TYPE_form_alter() for `paragraphs`.
bricks_preprocess_block__system_powered_by_block Prepares variables for `block.html.twig` for `system_powered_by_block`.
bricks_preprocess_field Prepares variables for `field.html.twig`.
bricks_preprocess_field_multiple_value_form Prepares variables for `field-multiple-value-form.html.twig`.
_bricks_build_layout_from_items Helper function for layout handling in _bricks_nest_items().
_bricks_form_element_alter Helper function for widget's formElement().
_bricks_nest_items Helper function: converts element's items to a tree structure.
_bricks_preprocess_tabledrag_form Helper function for hook_preprocess_field_multiple_value_form().