You are here

GridStack.php in GridStack 8.2

Same filename and directory in other branches
  1. 8 src/Entity/GridStack.php

File

src/Entity/GridStack.php
View source
<?php

namespace Drupal\gridstack\Entity;

use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Unicode;
use Drupal\gridstack\GridStackDefault;

/**
 * Defines the GridStack configuration entity.
 *
 * @ConfigEntityType(
 *   id = "gridstack",
 *   label = @Translation("GridStack optionset"),
 *   list_path = "admin/structure/gridstack",
 *   config_prefix = "optionset",
 *   entity_keys = {
 *     "id" = "name",
 *     "label" = "label",
 *     "status" = "status",
 *     "weight" = "weight",
 *   },
 *   config_export = {
 *     "id",
 *     "name",
 *     "label",
 *     "description",
 *     "status",
 *     "weight",
 *     "options",
 *     "json",
 *   }
 * )
 */
class GridStack extends GridStackBase implements GridStackInterface {

  /**
   * The file system service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  private $fileSystem;

  /**
   * The converted $breakpoints.
   *
   * @var array
   */
  protected $breakpoints;

  /**
   * The $data suitable for attributes.
   *
   * @var string
   */
  protected $data;

  /**
   * The supported $breakpoints.
   *
   * @var array
   */
  private static $activeBreakpoints = [
    'xs',
    'sm',
    'md',
    'lg',
    'xl',
  ];

  /**
   * Returns the supported breakpoints.
   *
   * @todo deprecated and removed for GridStackDefault::breakpoints().
   */
  public static function getConstantBreakpoints() {
    return self::$activeBreakpoints;
  }

  /**
   * Returns TRUE if Use CSS framework is enabled, else FALSE.
   */
  public function isFramework() {
    return $this
      ->getOption('use_framework');
  }

  /**
   * {@inheritdoc}
   */
  public function getSettings($merged = TRUE) {
    $default = self::load('default');
    $options = $merged ? array_merge($default->options, $this->options) : $this->options;
    return isset($options['settings']) ? $options['settings'] : [];
  }

  /**
   * {@inheritdoc}
   */
  public function setSettings(array $values, $merged = TRUE) {
    $settings = isset($this->options['settings']) ? $this->options['settings'] : [];
    $this->options['settings'] = $merged ? array_merge($settings, $values) : $values;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getSetting($name, $default = NULL) {
    return NULL !== $this
      ->getOptions('settings', $name) ? $this
      ->getOptions('settings', $name) : $default;
  }

  /**
   * {@inheritdoc}
   */
  public function setSetting($name, $value) {
    $this->options['settings'][$name] = $value;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getBreakpoints($breakpoint = NULL) {
    $breakpoints = $this
      ->getOption('breakpoints') ?: [];
    if ($breakpoint && isset($breakpoints[$breakpoint])) {
      return $breakpoints[$breakpoint];
    }
    return $breakpoints;
  }

  /**
   * Converts breakpoint items from stored JSON into array.
   */
  protected function breakpointToArray(array $breakpoint) {
    $build = [];
    foreach ([
      'column',
      'width',
      'grids',
      'nested',
    ] as $id) {
      if (isset($breakpoint[$id])) {
        $build[$id] = in_array($id, [
          'column',
          'width',
        ]) ? (int) $breakpoint[$id] : Json::decode($breakpoint[$id]);
      }
    }
    return $build;
  }

  /**
   * Converts the entire breakpoint items from stored JSON into array.
   */
  public function breakpointsToArray() {
    if (!isset($this->breakpoints)) {
      $breakpoints = [];
      if ($data = array_filter($this
        ->getBreakpoints())) {
        foreach ($data as $key => $datum) {
          $breakpoints[$key] = $this
            ->breakpointToArray($datum);
        }
      }
      $this->breakpoints = $breakpoints;
    }
    return $this->breakpoints;
  }

  /**
   * Returns the available breakpoint columns.
   */
  public function getColumns() {
    return Json::decode($this
      ->getJson('breakpoints'));
  }

  /**
   * Returns the last breakpoint column, or fallback to global setting.
   */
  public function getLastColumn() {
    return $this
      ->getLastBreakpoint('column') ?: $this
      ->getSetting('column', 12);
  }

  /**
   * Returns the last breakpoint key: Bootstrap3/Foundation lg, the rest xl.
   */
  public function getLastBreakpointKey() {
    $keys = array_keys($this
      ->getBreakpoints());
    return end($keys);
  }

  /**
   * {@inheritdoc}
   */
  public function getLastBreakpoint($type = 'grids') {
    return $this
      ->getBreakpointItems($this
      ->getLastBreakpointKey(), $type);
  }

  /**
   * Returns options.breakpoints.sm.[width, column, grids, nested].
   */
  public function getBreakpointItems($breakpoint = 'lg', $type = 'grids', $clean = TRUE) {
    $data = $this
      ->getBreakpoints($breakpoint);
    $data = isset($data[$type]) ? $data[$type] : '';
    if (in_array($type, [
      'grids',
      'nested',
    ])) {
      if ($data && ($output = Json::decode($data))) {

        // Do not cleanup nested grids as each needs to map to their parent.
        $grids = $clean ? array_filter($output) : $output;
        return $type == 'grids' ? $grids : $output;
      }
      return [];
    }
    return $data;
  }

  /**
   * Returns options.breakpoints.sm.[width, column, grids, nested].
   */
  public function getBreakpointItem($breakpoint = 'lg', $index = -1, $property = '', $type = 'grids') {
    $data = $this
      ->getBreakpointItems($breakpoint, $type);
    return isset($data[$index], $data[$index][$property]) ? $data[$index][$property] : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getGridsByDelta($delta = 0, $type = 'grids', $preserve = FALSE) {
    $grids = $this
      ->getLastBreakpoint($type);
    $grids = isset($grids[$delta]) ? $grids[$delta] : [];
    $check = array_filter($grids);
    return $preserve ? $grids : (empty($check) ? [] : $grids);
  }

  /**
   * {@inheritdoc}
   */
  public function getNestedGridsByDelta($delta = 0, $preserve = FALSE) {
    return $this
      ->getGridsByDelta($delta, 'nested', $preserve);
  }

  /**
   * Returns property by its key and delta.
   */
  public function getGridPropertyByDelta($key, $delta = 0, $type = 'grids', $preserve = FALSE) {
    $grids = $this
      ->getGridsByDelta($delta, $type, $preserve);
    return isset($grids[$key]) ? $grids[$key] : FALSE;
  }

  /**
   * Returns GridStack data as string for container attributes.
   */
  public function getData() {
    if (!isset($this->data)) {
      $data = [];
      if ($breakpoints = $this
        ->breakpointsToArray()) {
        foreach ($breakpoints as $breakpoint) {
          if (isset($breakpoint['width'], $breakpoint['grids'])) {
            $data[$breakpoint['width']] = $this
              ->massageGrids($breakpoint['grids']);
          }
        }
      }
      $this->data = $data ? Json::encode($data) : '';
    }
    return $this->data;
  }

  /**
   * Returns grids without regions if required.
   */
  protected function massageGrids(array $grids, $exclude_region = TRUE) {
    $values = [];
    foreach ($grids as $grid) {
      if (isset($grid[4]) && !isset($grid['image_style'])) {
        $grid[4] = str_replace([
          '-',
          ' ',
        ], '_', $grid[4]);
      }

      // Old grid has key image_style, needed pre gridstack_update_8211().
      // New grid has no keys, but values, re-purposed image_style for region.
      if (isset($grid['image_style']) || $exclude_region && (!empty($grid['region']) || isset($grid[4]))) {
        array_pop($grid);
      }
      $values[] = array_values($grid);
    }
    return $values;
  }

  /**
   * Returns JSON for options.breakpoints[xs|sm|md|lg|xl] keyed by indices.
   *
   * Simplify and remove keys:
   * Original: [{"x":1,"y":0,"width":2,"height":8}.
   * Now: [[1,0,2,8].
   */
  public function getJsonSummaryBreakpoints($breakpoint = 'lg', $grids = '', $exclude_region = TRUE) {
    $grids = $grids ?: $this
      ->getBreakpointItems($breakpoint);
    if ($grids) {
      $grids = is_string($grids) ? Json::decode($grids) : $grids;
      $values = $this
        ->massageGrids($grids, $exclude_region);
      return Json::encode($values);
    }
    return '';
  }

  /**
   * Returns JSON for options.breakpoints[xs|sm|md|lg|xl] keyed by indices.
   *
   * Simplify and remove keys:
   * Original: [{"x":1,"y":0,"width":2,"height":8}.
   * Now: [[1,0,2,8].
   */
  public function getJsonSummaryNestedBreakpoints($breakpoint = 'lg', $nested = '', $grids = '') {
    if ($nested) {
      $grids = $grids ?: $this
        ->getBreakpointItems($breakpoint, 'grids', FALSE);
      $grids = is_string($grids) ? Json::decode($grids) : $grids;
      $nested = is_string($nested) ? Json::decode($nested) : $nested;
      $values = [];
      foreach (array_keys($grids) as $id) {
        if (isset($nested[$id])) {
          if (empty($nested[$id])) {
            $values[] = [];
          }
          else {
            foreach ($nested[$id] as $grid) {
              $values[$id][] = array_values($grid);
            }
          }
        }
      }
      return Json::encode($values);
    }
    return '';
  }

  /**
   * Returns a node as required by admin storage, or frontend attributes.
   */
  public function getNode(array $grid, $exclude_region = TRUE) {
    $value = [];

    // Outlayer ridiculously allows a single WIDTHxHEIGHT to apply for N-grid.
    $grid = array_values($grid);
    if (isset($grid[3])) {
      foreach ([
        'x',
        'y',
        'width',
        'height',
      ] as $key => $node) {
        $value[$node] = $grid[$key];
      }
      $rid = isset($grid[4]) ? 4 : -1;
    }
    else {

      // Outlayer ungridstack (Custom Grid) doesn't provide x, y.
      $value = [
        'width' => $grid[0],
        'height' => $grid[1],
      ];
      $rid = isset($grid[2]) ? 2 : -1;
    }

    // Post gridstack_update_8211.
    if (!$exclude_region && !empty($grid[$rid])) {
      $value['region'] = $grid[$rid];
    }
    return $value;
  }

  /**
   * Returns the icon URI.
   */
  public function getIconUri() {
    $id = $this
      ->id();

    // The icon was updated, and stored at public://gridstack/ directory.
    if ($uri = $this
      ->getIconFileUri()) {
      return $uri;
    }

    // The icon may be empty, or not, yet not always exists at public directory.
    $uri = $this
      ->getOption('icon');
    $dependencies = $this
      ->getDependencies();
    $module = isset($dependencies['module'][0]) && !empty($dependencies['module'][0]) ? $dependencies['module'][0] : '';

    // Support static icons at MODULE_NAME/images/OPTIONSET_ID.png as long as
    // the module dependencies are declared explicitly for the stored optionset.
    if (empty($uri) || !is_file($uri)) {

      // Reset to empty first.
      $uri = '';
      if ($module && \gridstack()
        ->getModuleHandler()
        ->moduleExists($module)) {
        $icon_path = \drupal_get_path('module', $module) . '/images/' . $id . '.png';
        if (is_file(\Drupal::root() . '/' . $icon_path)) {
          $uri = \base_path() . $icon_path;
        }
      }
    }
    return $uri;
  }

  /**
   * Returns the icon URL.
   */
  public function getIconUrl($absolute = FALSE) {
    $url = '';
    if ($uri = $this
      ->getIconUri()) {
      $url = \file_url_transform_relative(\file_create_url($uri));
      if (!$absolute) {
        $url = ltrim($url, '/');
      }
    }
    return $url;
  }

  /**
   * {@inheritdoc}
   */
  public function delete() {
    if (!$this
      ->isNew()) {
      if ($uri = $this
        ->getIconFileUri()) {
        $this
          ->getFileSystem()
          ->delete($uri);
      }
    }
    parent::delete();
  }

  /**
   * Gets the file system service.
   *
   * @return \Drupal\Core\File\FileSystemInterface
   *   The file system service.
   */
  private function getFileSystem() {
    if (!isset($this->fileSystem)) {
      $this->fileSystem = \Drupal::service('file_system');
    }
    return $this->fileSystem;
  }

  /**
   * {@inheritdoc}
   */
  public function prepareRegions($clean = TRUE) {
    $regions = [];
    foreach ($this
      ->getLastBreakpoint() as $delta => $data) {
      $data = array_values($data);
      $index = $delta + 1;
      $rid = $cid = GridStackDefault::regionId($delta);
      $id = empty($data[4]) ? '' : $data[4];
      $name = $id ? ' ' . str_replace('_', ' ', Unicode::ucfirst($id)) : '';
      $fw = isset($data[2]) && $data[2] == 12;
      $regions[$rid]['id'] = $id;
      $regions[$rid]['rid'] = $rid;
      $regions[$rid]['label'] = $index . $name;
      $regions[$rid]['delta'] = $delta;
      $regions[$rid]['name'] = $name;
      $regions[$rid]['node'] = $data;
      $regions[$rid]['type'] = GridStackDefault::REGION;
      $regions[$rid]['width'] = isset($data[2]) ? $data[2] : NULL;
      $regions[$rid]['_fw'] = $fw;
      $regions[$rid]['_level'] = GridStackDefault::LEVEL_ROOT_ITEM;
      $regions[$rid]['_context'] = $delta;
      $regions[$rid]['_root'] = 'grids';

      // With nested grids, its container doesn't contain contents, but grids.
      if ($grids = $this
        ->getNestedGridsByDelta($delta)) {
        $regions[$rid]['type'] = GridStackDefault::CONTAINER;
        $regions[$rid]['_level'] = GridStackDefault::LEVEL_NESTED;

        // Remove container since the actual contents are moved, if required.
        if ($clean) {
          unset($regions[$rid]);
        }
        foreach ($grids as $nid => $grid) {
          $grid = array_values($grid);
          $id = empty($grid[4]) ? '' : $grid[4];
          $name = $id ? ' ' . str_replace('_', ' ', Unicode::ucfirst($id)) : '';
          $rid = GridStackDefault::regionId($delta . '_' . $nid);
          $regions[$rid]['id'] = $id;
          $regions[$rid]['cid'] = $cid;
          $regions[$rid]['rid'] = $rid;
          $regions[$rid]['label'] = $index . ':' . ($nid + 1) . $name;
          $regions[$rid]['delta'] = $delta;
          $regions[$rid]['name'] = $name;
          $regions[$rid]['node'] = $grid;
          $regions[$rid]['nid'] = $nid;
          $regions[$rid]['type'] = GridStackDefault::REGION;
          $regions[$rid]['width'] = isset($grid[2]) ? $grid[2] : NULL;
          $regions[$rid]['_fw'] = $fw && isset($grid[2]) && $grid[2] == 12;
          $regions[$rid]['_level'] = GridStackDefault::LEVEL_NESTED_ITEM;
          $regions[$rid]['_context'] = $index . '-' . ($nid + 1);
          $regions[$rid]['_root'] = 'nested';
        }
      }
    }
    return $regions;
  }

}

Classes

Namesort descending Description
GridStack Defines the GridStack configuration entity.