You are here

Page.php in Page Manager 8

Same filename and directory in other branches
  1. 8.4 src/Entity/Page.php

File

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

/**
 * @file
 * Contains \Drupal\page_manager\Entity\Page.
 */
namespace Drupal\page_manager\Entity;

use Drupal\Component\Plugin\Context\ContextInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\page_manager\Event\PageManagerContextEvent;
use Drupal\page_manager\Event\PageManagerEvents;
use Drupal\page_manager\PageInterface;
use Drupal\Core\Condition\ConditionPluginCollection;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\page_manager\PageVariantInterface;

/**
 * Defines a Page entity class.
 *
 * @ConfigEntityType(
 *   id = "page",
 *   label = @Translation("Page"),
 *   handlers = {
 *     "access" = "Drupal\page_manager\Entity\PageAccess",
 *   },
 *   admin_permission = "administer pages",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *     "status" = "status"
 *   },
 *   config_export = {
 *     "id",
 *     "label",
 *     "description",
 *     "use_admin_theme",
 *     "path",
 *     "access_logic",
 *     "access_conditions",
 *     "parameters",
 *   },
 * )
 */
class Page extends ConfigEntityBase implements PageInterface {

  /**
   * The ID of the page entity.
   *
   * @var string
   */
  protected $id;

  /**
   * The label of the page entity.
   *
   * @var string
   */
  protected $label;

  /**
   * The description of the page entity.
   *
   * @var string
   */
  protected $description;

  /**
   * The path of the page entity.
   *
   * @var string
   */
  protected $path;

  /**
   * The page variant entities.
   *
   * @var \Drupal\page_manager\PageVariantInterface[].
   */
  protected $variants;

  /**
   * An array of collected contexts.
   *
   * @var \Drupal\Component\Plugin\Context\ContextInterface[]
   */
  protected $contexts = [];

  /**
   * The configuration of access conditions.
   *
   * @var array
   */
  protected $access_conditions = [];

  /**
   * Tracks the logic used to compute access, either 'and' or 'or'.
   *
   * @var string
   */
  protected $access_logic = 'and';

  /**
   * The plugin collection that holds the access conditions.
   *
   * @var \Drupal\Component\Plugin\LazyPluginCollection
   */
  protected $accessConditionCollection;

  /**
   * Indicates if this page should be displayed in the admin theme.
   *
   * @var bool
   */
  protected $use_admin_theme;

  /**
   * Parameter context configuration.
   *
   * An associative array keyed by parameter name, which contains associative
   * arrays with the following keys:
   * - machine_name: Machine-readable context name.
   * - label: Human-readable context name.
   * - type: Context type.
   *
   * @var array[]
   */
  protected $parameters = [];

  /**
   * {@inheritdoc}
   */
  public function getDescription() {
    return $this->description;
  }

  /**
   * {@inheritdoc}
   */
  public function getPath() {
    return $this->path;
  }

  /**
   * {@inheritdoc}
   */
  public function usesAdminTheme() {
    return isset($this->use_admin_theme) ? $this->use_admin_theme : strpos($this
      ->getPath(), '/admin/') === 0;
  }

  /**
   * {@inheritdoc}
   */
  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    parent::postSave($storage, $update);
    static::routeBuilder()
      ->setRebuildNeeded();
  }

  /**
   * {@inheritdoc}
   */
  public static function postDelete(EntityStorageInterface $storage, array $entities) {
    parent::postDelete($storage, $entities);
    static::routeBuilder()
      ->setRebuildNeeded();
  }

  /**
   * Wraps the route builder.
   *
   * @return \Drupal\Core\Routing\RouteBuilderInterface
   *   An object for state storage.
   */
  protected static function routeBuilder() {
    return \Drupal::service('router.builder');
  }

  /**
   * Wraps the entity storage for page variants.
   *
   * @return \Drupal\Core\Entity\EntityStorageInterface
   */
  protected function variantStorage() {
    return \Drupal::service('entity_type.manager')
      ->getStorage('page_variant');
  }

  /**
   * {@inheritdoc}
   */
  public function getPluginCollections() {
    return [
      'access_conditions' => $this
        ->getAccessConditions(),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getAccessConditions() {
    if (!$this->accessConditionCollection) {
      $this->accessConditionCollection = new ConditionPluginCollection(\Drupal::service('plugin.manager.condition'), $this
        ->get('access_conditions'));
    }
    return $this->accessConditionCollection;
  }

  /**
   * {@inheritdoc}
   */
  public function addAccessCondition(array $configuration) {
    $configuration['uuid'] = $this
      ->uuidGenerator()
      ->generate();
    $this
      ->getAccessConditions()
      ->addInstanceId($configuration['uuid'], $configuration);
    return $configuration['uuid'];
  }

  /**
   * {@inheritdoc}
   */
  public function getAccessCondition($condition_id) {
    return $this
      ->getAccessConditions()
      ->get($condition_id);
  }

  /**
   * {@inheritdoc}
   */
  public function removeAccessCondition($condition_id) {
    $this
      ->getAccessConditions()
      ->removeInstanceId($condition_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getAccessLogic() {
    return $this->access_logic;
  }

  /**
   * {@inheritdoc}
   */
  public function getParameters() {
    $names = $this
      ->getParameterNames();
    if ($names) {
      return array_intersect_key($this->parameters, array_flip($names));
    }
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function getParameter($name) {
    if ($this
      ->hasParameter($name)) {
      return $this->parameters[$name];
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function hasParameter($name) {
    return isset($this->parameters[$name]);
  }

  /**
   * {@inheritdoc}
   */
  public function setParameter($name, $type, $label = '') {
    $this->parameters[$name] = [
      'machine_name' => $name,
      'type' => $type,
      'label' => $label,
    ];

    // Reset contexts when a parameter is added or changed.
    $this->contexts = [];

    // Reset the contexts of every variant.
    foreach ($this
      ->getVariants() as $page_variant) {
      $page_variant
        ->resetCollectedContexts();
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function removeParameter($name) {
    unset($this->parameters[$name]);

    // Reset contexts when a parameter is removed.
    $this->contexts = [];

    // Reset the contexts of every variant.
    foreach ($this
      ->getVariants() as $page_variant) {
      $page_variant
        ->resetCollectedContexts();
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterNames() {
    if (preg_match_all('|\\{(\\w+)\\}|', $this
      ->getPath(), $matches)) {
      return $matches[1];
    }
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);
    $this
      ->filterParameters();
  }

  /**
   * Filters the parameters to remove any without a valid type.
   *
   * @return $this
   */
  protected function filterParameters() {
    $names = $this
      ->getParameterNames();
    foreach ($this
      ->get('parameters') as $name => $parameter) {

      // Remove parameters without any type, or which are no longer valid.
      if (empty($parameter['type']) || !in_array($name, $names)) {
        $this
          ->removeParameter($name);
      }
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function addContext($name, ContextInterface $value) {
    $this->contexts[$name] = $value;
  }

  /**
   * {@inheritdoc}
   */
  public function getContexts() {

    // @todo add the other global contexts here as they are added
    // @todo maybe come up with a non-hardcoded way of doing this?
    $global_contexts = [
      'current_user',
    ];
    if (!$this->contexts) {
      $this
        ->eventDispatcher()
        ->dispatch(PageManagerEvents::PAGE_CONTEXT, new PageManagerContextEvent($this));
      foreach ($this
        ->getParameters() as $machine_name => $configuration) {

        // Parameters can be updated in the UI, so unless it's a global context
        // we'll need to rely on the current settings in the tempstore instead
        // of the ones cached in the router.
        if (!isset($global_contexts[$machine_name])) {

          // First time through, parameters will not be defined by the route.
          if (!isset($this->contexts[$machine_name])) {
            $cacheability = new CacheableMetadata();
            $cacheability
              ->setCacheContexts([
              'route',
            ]);
            $context_definition = new ContextDefinition($configuration['type'], $configuration['label']);
            $context = new Context($context_definition);
            $context
              ->addCacheableDependency($cacheability);
            $this->contexts[$machine_name] = $context;
          }
          else {
            $this->contexts[$machine_name]
              ->getContextDefinition()
              ->setDataType($configuration['type']);
            if (!empty($configuration['label'])) {
              $this->contexts[$machine_name]
                ->getContextDefinition()
                ->setLabel($configuration['label']);
            }
          }
        }
      }
    }
    return $this->contexts;
  }

  /**
   * {@inheritdoc}
   */
  public function addVariant(PageVariantInterface $variant) {

    // If variants hasn't been initialized, we initialize it before adding the
    // new variant.
    if ($this->variants === NULL) {
      $this
        ->getVariants();
    }
    $this->variants[$variant
      ->id()] = $variant;
    $this
      ->sortVariants();
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getVariant($variant_id) {
    $variants = $this
      ->getVariants();
    if (!isset($variants[$variant_id])) {
      throw new \UnexpectedValueException('The requested variant does not exist or is not associated with this page');
    }
    return $variants[$variant_id];
  }

  /**
   * {@inheritdoc}
   */
  public function removeVariant($variant_id) {
    $this
      ->getVariant($variant_id)
      ->delete();
    unset($this->variants[$variant_id]);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getVariants() {
    if (!isset($this->variants)) {
      $this->variants = [];

      /** @var \Drupal\page_manager\PageVariantInterface $variant */
      foreach ($this
        ->variantStorage()
        ->loadByProperties([
        'page' => $this
          ->id(),
      ]) as $variant) {
        $this->variants[$variant
          ->id()] = $variant;
      }
      $this
        ->sortVariants();
    }
    return $this->variants;
  }

  /**
   * Sort variants.
   */
  protected function sortVariants() {
    if (isset($this->variants)) {

      // Suppress errors because of https://bugs.php.net/bug.php?id=50688.
      @uasort($this->variants, [
        $this,
        'variantSortHelper',
      ]);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function variantSortHelper($a, $b) {
    $a_weight = $a
      ->getWeight();
    $b_weight = $b
      ->getWeight();
    if ($a_weight == $b_weight) {
      return 0;
    }
    return $a_weight < $b_weight ? -1 : 1;
  }

  /**
   * Wraps the event dispatcher.
   *
   * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
   *   The event dispatcher.
   */
  protected function eventDispatcher() {
    return \Drupal::service('event_dispatcher');
  }

  /**
   * {@inheritdoc}
   */
  public function __sleep() {
    $vars = parent::__sleep();

    // Gathered contexts objects should not be serialized.
    if (($key = array_search('contexts', $vars)) !== FALSE) {
      unset($vars[$key]);
    }
    return $vars;
  }

  /**
   * {@inheritdoc}
   *
   * @todo: Remove this as part of https://www.drupal.org/node/2696683.
   */
  protected function urlRouteParameters($rel) {
    if ($rel == 'edit-form') {
      $uri_route_parameters = [];
      $uri_route_parameters['machine_name'] = $this
        ->id();
      $uri_route_parameters['step'] = 'general';
      return $uri_route_parameters;
    }
    return parent::urlRouteParameters($rel);
  }

}

Classes

Namesort descending Description
Page Defines a Page entity class.