You are here

GridStackLayout.php in GridStack 8

File

src/Layout/GridStackLayout.php
View source
<?php

namespace Drupal\gridstack\Layout;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformStateInterface;
use Drupal\Core\Layout\LayoutDefault;
use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Layout\LayoutDefinition;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Serialization\Json;
use Drupal\gridstack\GridStackManagerInterface;
use Drupal\gridstack\Entity\GridStack;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a GridStack class for Layout plugins.
 */
class GridStackLayout extends LayoutDefault implements ContainerFactoryPluginInterface, LayoutInterface, PluginFormInterface {
  use GridStackLayoutTrait;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountInterface $current_user, GridStackManagerInterface $manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->currentUser = $current_user;
    $this->manager = $manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container
      ->get('current_user'), $container
      ->get('gridstack.manager'));
  }

  /**
   * {@inheritdoc}
   */
  public function build(array $regions) {
    $config = $this
      ->getConfiguration();
    $definition = $this
      ->getPluginDefinition();
    $id = $definition
      ->id();
    $item_id = 'box';
    $name = $definition
      ->get('optionset');
    $optionset = GridStack::load($name);
    $grids = $optionset
      ->getEndBreakpointGrids();
    $ipe_exists = $this->manager
      ->getModuleHandler()
      ->moduleExists('panels_ipe');
    $is_builder = $this->manager
      ->getModuleHandler()
      ->moduleExists('layout_builder');

    // Only check that Panels IPE is granted. Further access check is not here.
    // @see \Drupal\gridstack\GridStackManager::preRenderGridStack()
    $config['_ipe'] = $config['_layout_builder'] = FALSE;
    if ($ipe_exists && $this->currentUser
      ->hasPermission('access panels in-place editing')) {
      $config['_ipe'] = TRUE;
    }

    /** @var \Drupal\Core\Routing\AdminContext $admin_context */

    // @todo $admin_context = \Drupal::service('router.admin_context');
    // @todo $admin_context->isAdminRoute()
    if ($is_builder && $this->currentUser
      ->hasPermission('configure any layout')) {
      $config['_layout_builder'] = TRUE;
    }

    // Converts string to array.
    $config['extras'] = empty($config['extras']) ? [] : Json::decode($config['extras']);

    // Defines settings.
    $settings = [
      'id' => $id,
      'item_id' => $item_id,
      'namespace' => 'gridstack',
      'optionset' => $name,
    ] + $config;

    // Converts gridstack breakpoint grids from stored JSON into array.
    $optionset
      ->gridsJsonToArray($settings);
    $items = [];
    foreach ($grids as $delta => $grid) {
      $region = 'gridstack_' . $delta;
      $box = [];
      $box_settings = $settings;
      unset($box_settings['attributes'], $box_settings['wrapper'], $box_settings['wrapper_classes']);
      $box['settings'] = $box_settings;
      $nested_grids = $optionset
        ->getNestedGridsByDelta($delta);
      $is_nested = array_filter($nested_grids);
      if (!empty($is_nested)) {
        foreach ($nested_grids as $key => $nested_grid) {
          $nested_id = $delta . '_' . $key;
          $region = 'gridstack_' . $nested_id;

          // Preserves indices even if empty.
          $box[$item_id][$key][$item_id] = isset($regions[$region]) && !Element::isEmpty($regions[$region]) ? $regions[$region] : [];
        }
      }
      else {

        // Preserves indices even if empty.
        $box[$item_id] = isset($regions[$region]) && !Element::isEmpty($regions[$region]) ? $regions[$region] : [];
      }
      $items[] = $box;
      unset($box);
    }
    $build = [
      'items' => $items,
      'optionset' => $optionset,
      'settings' => $settings,
      'layout' => $definition,
    ];
    return empty($items) ? [] : $this->manager
      ->build($build);
  }

  /**
   * Implements hook_layout_alter().
   */
  public static function layoutAlter(&$definitions) {
    $manager = \Drupal::service('gridstack.manager');
    $optionsets = $manager
      ->entityLoadMultiple('gridstack');
    $framework = $manager
      ->configLoad('framework', 'gridstack.settings');
    $path = drupal_get_path('module', 'gridstack');
    foreach ($optionsets as $key => $optionset) {
      if ($key == 'default') {
        continue;
      }
      $static = !empty($framework) && $optionset
        ->getOption('use_framework');
      $id = $optionset
        ->id();
      $layout_id = 'gridstack_' . $id;
      $regions = self::prepareRegions($optionset);
      $default = isset($regions['gridstack_0']) ? 'gridstack_0' : 'gridstack_0_0';
      $additional = [
        'optionset' => $id,
      ];

      // Defines the layout.
      $definition = [
        'label' => strip_tags($optionset
          ->label()),
        'category' => $static ? 'GridStack ' . ucwords($framework) : 'GridStack JS',
        'class' => '\\Drupal\\gridstack\\Layout\\GridStackLayout',
        'default_region' => $default,
        'icon' => $optionset
          ->getIconUrl(),
        'id' => $layout_id,
        'provider' => 'gridstack',
        'additional' => $additional,
        'regions' => $regions,
        'theme_hook' => 'gridstack',
        'path' => $path,
        'library' => 'gridstack/layout',
        'config_dependencies' => [
          'config' => [
            'gridstack.optionset.' . $id,
          ],
          'module' => [
            'gridstack',
          ],
        ],
      ];
      $definitions[$layout_id] = new LayoutDefinition($definition);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {

    // This form may be loaded as a subform by Field Layout, Panels, etc.
    // @see https://www.drupal.org/node/2536646
    // @see https://www.drupal.org/node/2798261
    // @see https://www.drupal.org/node/2774077
    // @todo: Remove when no more issues with it.
    if ($form_state instanceof SubformStateInterface) {
      $form_state = $form_state
        ->getCompleteFormState();
    }
    $access = $this->currentUser
      ->hasPermission('administer gridstack');
    $config = $this
      ->getConfiguration();
    $definition = $this
      ->getPluginDefinition();
    $regions = $definition
      ->getRegions();
    $name = $definition
      ->get('optionset');
    $optionset = GridStack::load($name);
    $regions_all = self::prepareRegions($optionset, FALSE);
    $extras = [];

    /** @var \Drupal\field_ui\Form\EntityViewDisplayEditForm $entity_form */
    $entity_form = $form_state
      ->getFormObject();

    /* @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */
    if (method_exists($entity_form, 'getEntity') && ($display = $entity_form
      ->getEntity())) {
      $extras = [
        'bundle' => $display
          ->getTargetBundle(),
        'entity_type' => $display
          ->getTargetEntityTypeId(),
        'view_mode' => $display
          ->getMode(),
      ];
    }
    $settings = [];
    foreach ([
      'attributes',
      'wrapper_classes',
      'skin',
      'wrapper',
    ] as $key) {
      $default = $key == 'wrapper' ? 'div' : '';
      $default = isset($config[$key]) ? $config[$key] : $default;
      $settings[$key] = $form_state
        ->getValue([
        'settings',
        $key,
      ], $default);
    }
    $prefix = '<h3>';
    if ($this->manager
      ->getModuleHandler()
      ->moduleExists('gridstack_ui') && $access) {
      $prefix .= $this
        ->t('Outer wrapper settings [<small><a href=":url">Edit @id</a></small>]', [
        ':url' => $optionset
          ->toUrl('edit-form')
          ->toString(),
        '@id' => strip_tags($optionset
          ->label()),
      ]);
    }
    else {
      $prefix .= $this
        ->t('Outer wrapper settings');
    }
    $prefix .= '</h3>';
    $form['settings'] = [
      '#type' => 'container',
      '#tree' => TRUE,
      '#weight' => 20,
      '#prefix' => $prefix,
    ];
    $form['settings']['skin'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Skin'),
      '#options' => $this->manager
        ->getSkinOptions(),
      '#empty_option' => $this
        ->t('- None -'),
      '#description' => $this
        ->t('Choose a skin to load for this layout. Check out, or clone, gridstack_example.module to register skins here. Leave empty to disable.'),
      '#default_value' => $settings['skin'],
      '#prefix' => '<div class="form-wrapper form-wrapper--inline form-wrapper--left">',
    ];
    $form['settings']['extras'] = [
      '#type' => 'hidden',
      '#value' => empty($extras) ? '' : Json::encode($extras),
    ];
    $form['settings'] += $this
      ->buildFormElements($settings);
    $closing = '</div><div class="form-wrapper form-wrapper--inline form-wrapper--icon form-wrapper--right">';
    if ($uri = $optionset
      ->getIconUri()) {
      $image = [
        '#theme' => 'image',
        '#uri' => $uri,
        '#alt' => $this
          ->t('Thumbnail'),
      ];
      $closing .= $this->manager
        ->getRenderer()
        ->render($image);
    }
    $closing .= '</div>';
    $form['settings']['attributes']['#suffix'] = $closing;
    $form['regions'] = [
      '#type' => 'container',
      '#tree' => TRUE,
      '#prefix' => '<p>' . $this
        ->t('A region has direct contents. A container contains a single, or multiple regions, grouped by their container index.') . '</p>',
    ];
    $settings = [];
    foreach ($regions_all as $region => $info) {
      foreach ([
        'attributes',
        'wrapper_classes',
        'wrapper',
      ] as $key) {
        $default = $key == 'wrapper' ? 'div' : '';
        $default = isset($config['regions'][$region][$key]) ? $config['regions'][$region][$key] : $default;
        $default = $form_state
          ->getValue([
          'regions',
          $region,
          $key,
        ], $default);
        $settings['regions'][$region][$key] = $default;
      }
      $prefix = !array_key_exists($region, $regions) ? 'Container' : 'Region';
      $form['regions'][$region] = [
        '#type' => 'details',
        '#title' => $this
          ->t('@prefix: <em>@label</em>', [
          '@prefix' => $prefix,
          '@label' => $info['label'],
        ]),
        '#open' => TRUE,
        '#tree' => TRUE,
        '#attributes' => [
          'class' => [
            'form-wrapper',
          ],
        ],
      ];
      $form['regions'][$region] += $this
        ->buildFormElements($settings['regions'][$region]);
    }
    $form['#attached']['library'][] = 'gridstack/admin_base';
    return $form;
  }

}

Classes

Namesort descending Description
GridStackLayout Provides a GridStack class for Layout plugins.