You are here

GridStack.php in GridStack 8

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

File

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

namespace Drupal\gridstack\Entity;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Config\Entity\ConfigEntityBase;

/**
 * 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",
 *     "status",
 *     "weight",
 *     "options",
 *     "json",
 *   }
 * )
 */
class GridStack extends ConfigEntityBase implements GridStackInterface {

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

  /**
   * The GridStack HTML ID.
   *
   * @var int
   */
  private static $gridstackId;

  /**
   * The legacy CTools ID for the configurable optionset.
   *
   * @var string
   */
  protected $name;

  /**
   * The human-readable name for the optionset.
   *
   * @var string
   */
  protected $label;

  /**
   * The weight to re-arrange the order of gridstack optionsets.
   *
   * @var int
   */
  protected $weight = 0;

  /**
   * The plugin instance json to reduce frontend logic.
   *
   * @var string
   */
  protected $json = '';

  /**
   * The plugin instance options.
   *
   * @var array
   */
  protected $options = [];

  /**
   * Returns the supported breakpoints.
   */
  public static function getConstantBreakpoints() {
    return self::$activeBreakpoints;
  }

  /**
   * Overrides Drupal\Core\Entity\Entity::id().
   */
  public function id() {
    return $this->name;
  }

  /**
   * {@inheritdoc}
   */
  public function getOptions($group = NULL, $property = NULL) {
    $default = self::load('default');
    $options = $this->options ? array_merge($default->options, $this->options) : $default->options;
    if ($group) {
      if (is_array($group)) {
        return NestedArray::getValue($options, $group);
      }
      elseif (isset($property) && isset($options[$group])) {
        return isset($options[$group][$property]) ? $options[$group][$property] : NULL;
      }
      return isset($options[$group]) ? $options[$group] : $options;
    }
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function getOption($group) {
    return isset($this->options[$group]) ? $this->options[$group] : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setOption($group, $value) {
    $value = $group == 'settings' ? array_merge($this->options[$group], $value) : $value;
    $this->options[$group] = $value;
    return $this;
  }

  /**
   * {@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) {
    return NULL !== $this
      ->getOptions('settings', $name) ? $this
      ->getOptions('settings', $name) : NULL;
  }

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

  /**
   * {@inheritdoc}
   */
  public function getJson($group = '') {
    $default = self::load('default');
    $defaults = isset($default->json[$group]) ? $default->json[$group] : '';
    return $group && isset($this->json[$group]) ? $this->json[$group] : $defaults;
  }

  /**
   * Returns options.breakpoints.[lg|xl].[grids|nested].
   *
   * @param string $current
   *   The name of specific property holding grids: grids, or nested.
   *
   * @return mixed|array
   *   Available grids by the given $current parameter, else empty.
   */
  public function getEndBreakpointGrids($current = 'grids') {
    $build = [];
    foreach ($this
      ->getBreakpoints() as $key => $breakpoint) {
      if (empty($breakpoint['grids'])) {
        continue;
      }
      $build[$key] = $breakpoint;
    }
    $keys = array_keys($build);
    $end = end($keys);
    return $this
      ->getBreakpointGrids($end, $current);
  }

  /**
   * Returns the current nested grids with preserved indices even if empty.
   *
   * Only cares for the last breakpoint, others inherit its structure.
   * The reason is all breakpoints may have different DOM positionings, heights
   * and widths each, but they must have the same grid structure.
   *
   * @param int $delta
   *   The current delta.
   *
   * @return mixed|array
   *   Available grids by the given $delta parameter, else empty.
   */
  public function getNestedGridsByDelta($delta = 0) {
    $grids = $this
      ->getEndBreakpointGrids('nested');
    $nested = isset($grids[$delta]) ? $grids[$delta] : [];
    $check = array_filter($nested);
    return empty($check) ? [] : $nested;
  }

  /**
   * Returns options.breakpoints.[xs|sm|md|lg|xl], or all, else empty.
   *
   * If available, data may contain: column, image_style, width, grids, nested.
   *
   * @param string $breakpoint
   *   The current breakpoint: xs, sm, md, lg, xl.
   *
   * @return array
   *   Available data by the given $breakpoint parameter, else empty.
   */
  public function getBreakpoints($breakpoint = NULL) {
    $breakpoints = $this
      ->getOption('breakpoints') ?: [];
    if ($breakpoint && isset($breakpoints[$breakpoint])) {
      return $breakpoints[$breakpoint];
    }
    return $breakpoints;
  }

  /**
   * Returns options.breakpoints.sm.[width, column, image_style, grids, nested].
   */
  public function getBreakpointGrids($breakpoint = 'lg', $current = 'grids') {
    $grids = !empty($this
      ->getBreakpoints($breakpoint)) && isset($this
      ->getBreakpoints($breakpoint)[$current]) ? $this
      ->getBreakpoints($breakpoint)[$current] : '';
    $grids = Json::decode($grids);
    if ($grids) {
      return $current == 'grids' ? array_filter($grids) : $grids;
    }
    return [];
  }

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

  /**
   * Converts gridstack breakpoint grids from stored JSON into array.
   */
  public function gridsJsonToArray(array &$settings = []) {
    $settings['breakpoints'] = array_filter($this
      ->getBreakpoints());
    if (!empty($settings['breakpoints'])) {
      foreach ($settings['breakpoints'] as $key => $breakpoint) {
        if (!empty($breakpoint['grids']) && is_string($breakpoint['grids'])) {
          $settings['breakpoints'][$key]['grids'] = Json::decode($breakpoint['grids']);
        }
        if (!empty($breakpoint['nested']) && is_string($breakpoint['nested'])) {
          $settings['breakpoints'][$key]['nested'] = Json::decode($breakpoint['nested']);
        }
      }
    }
  }

  /**
   * Optimize grid widths to remove similar widths.
   *
   * This is premature optimization, postpone.
   *
   * @todo: Optimize more to be executed once.
   * @todo: Correct logic to only remove one level down with similarity.
   */
  public static function optimizeGridWidths(array $settings = [], $current = 'grids', $optimize = FALSE) {
    $breakpoints = isset($settings['breakpoints']) ? $settings['breakpoints'] : [];
    $delta = isset($settings['delta']) ? $settings['delta'] : 0;
    $nested_delta = isset($settings['nested_delta']) ? $settings['nested_delta'] : NULL;
    $unique = [];
    foreach (self::getConstantBreakpoints() as $id) {
      $item = isset($breakpoints[$id]) && isset($breakpoints[$id][$current][$delta]) ? $breakpoints[$id][$current][$delta] : '';
      if (empty($item)) {
        continue;
      }
      if ($current == 'grids') {
        if (!empty($item['width'])) {
          $unique[$id] = (int) $item['width'];
        }
      }
      elseif ($current == 'nested') {
        if (isset($item[$nested_delta]) && !empty($item[$nested_delta]['width'])) {
          $unique[$id] = (int) $item[$nested_delta]['width'];
        }
      }
    }
    return $optimize ? array_unique($unique) : $unique;
  }

  /**
   * Returns JSON for options.breakpoints.
   */
  public function getJsonSummaryBreakpoints($breakpoint = 'lg', $exclude_image_style = FALSE, $no_keys = TRUE) {
    $grids = $this
      ->getBreakpointGrids($breakpoint);
    if ($grids && $no_keys) {
      $values = [];
      foreach ($grids as &$grid) {
        if (empty($grid)) {
          continue;
        }
        if ($exclude_image_style && isset($grid['image_style'])) {
          array_pop($grid);
        }
        $values[] = array_values($grid);
      }

      // Simplify and remove keys:
      // Original: [{"x":1,"y":0,"width":2,"height":8}.
      // Now: [[1,0,2,8].
      $grids = $values;
    }
    return $grids ? Json::encode($grids) : '';
  }

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

    // The icon was updated, and stored at public://gridstack/ directory.
    if (is_file($uri)) {
      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 = '';
      $handler = \Drupal::service('gridstack.manager')
        ->getModuleHandler();
      if ($module && $handler
        ->moduleExists($module)) {
        $icon_path = drupal_get_path('module', $module) . '/images/' . $id . '.png';
        if (is_file(DRUPAL_ROOT . '/' . $icon_path)) {
          $uri = $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;
  }

  /**
   * Parses the given string attribute.
   */
  public static function parseAttributes($string = '') {
    $attributes = [];
    $layout_attributes = explode(',', $string);
    foreach ($layout_attributes as $attribute) {
      $replaced_attribute = $attribute;

      // @todo: Token support.
      if (strpos($attribute, '|') !== FALSE) {
        list($key, $value) = array_pad(array_map('trim', explode('|', $replaced_attribute, 2)), 2, NULL);
        $attributes[$key] = strip_tags($value);
      }
    }
    return $attributes;
  }

  /**
   * Returns the grids with/without node property.
   *
   * @deprecated: Remove for self::getEndBreakpointGrids().
   */
  public function getGrids($grid_only = TRUE, $group = 'node') {
    return [];
  }

  /**
   * Returns the trusted HTML ID of a single gridstack instance.
   */
  public static function getHtmlId($string = 'gridstack', $id = '') {
    if (!isset(static::$gridstackId)) {
      static::$gridstackId = 0;
    }

    // Do not use dynamic Html::getUniqueId, otherwise broken AJAX.
    return empty($id) ? Html::getId($string . '-' . ++static::$gridstackId) : str_replace('_', '-', strip_tags($id));
  }

  /**
   * Returns HTML or layout related settings, none of JS to shutup notices.
   */
  public static function htmlSettings() {
    return [
      '_admin' => FALSE,
      'background' => TRUE,
      'id' => '',
      'lightbox' => '',
      'media_switch' => '',
      'nested' => FALSE,
      'optionset' => 'default',
      'root' => TRUE,
      'use_framework' => FALSE,
      'skin' => '',
    ];
  }

}

Classes

Namesort descending Description
GridStack Defines the GridStack configuration entity.