Pathauto and dependencies

This commit is contained in:
Rob Davies 2017-05-22 15:12:47 +01:00
parent 4b1a293d57
commit 24ffcb956b
257 changed files with 29510 additions and 0 deletions

View file

@ -0,0 +1,105 @@
<?php
namespace Drupal\ctools\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a block to view a specific entity.
*
* @Block(
* id = "entity_view",
* deriver = "Drupal\ctools\Plugin\Deriver\EntityViewDeriver",
* )
*/
class EntityView extends BlockBase implements ContextAwarePluginInterface, ContainerFactoryPluginInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs a new EntityView.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'view_mode' => 'default',
];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form['view_mode'] = [
'#type' => 'select',
'#options' => $this->entityManager->getViewModeOptions($this->getDerivativeId()),
'#title' => $this->t('View mode'),
'#default_value' => $this->configuration['view_mode'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['view_mode'] = $form_state->getValue('view_mode');
}
/**
* {@inheritdoc}
*/
public function build() {
/** @var $entity \Drupal\Core\Entity\EntityInterface */
$entity = $this->getContextValue('entity');
$view_builder = $this->entityManager->getViewBuilder($entity->getEntityTypeId());
$build = $view_builder->view($entity, $this->configuration['view_mode']);
CacheableMetadata::createFromObject($this->getContext('entity'))
->applyTo($build);
return $build;
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
/**
* Provides a collection of block plugins.
*/
class BlockPluginCollection extends DefaultLazyPluginCollection {
/**
* {@inheritdoc}
*
* @return \Drupal\Core\Block\BlockPluginInterface
*/
public function &get($instance_id) {
return parent::get($instance_id);
}
/**
* Returns all blocks keyed by their region.
*
* @return array
* An associative array keyed by region, containing an associative array of
* block plugins.
*/
public function getAllByRegion() {
$region_assignments = [];
foreach ($this as $block_id => $block) {
$configuration = $block->getConfiguration();
$region = isset($configuration['region']) ? $configuration['region'] : NULL;
$region_assignments[$region][$block_id] = $block;
}
foreach ($region_assignments as $region => $region_assignment) {
// @todo Determine the reason this needs error suppression.
@uasort($region_assignment, function (BlockPluginInterface $a, BlockPluginInterface $b) {
$a_config = $a->getConfiguration();
$a_weight = isset($a_config['weight']) ? $a_config['weight'] : 0;
$b_config = $b->getConfiguration();
$b_weight = isset($b_config['weight']) ? $b_config['weight'] : 0;
if ($a_weight == $b_weight) {
return strcmp($a->label(), $b->label());
}
return $a_weight > $b_weight ? 1 : -1;
});
$region_assignments[$region] = $region_assignment;
}
return $region_assignments;
}
}

View file

@ -0,0 +1,96 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Core\Display\VariantInterface;
/**
* Provides an interface for variant plugins that use block plugins.
*/
interface BlockVariantInterface extends VariantInterface {
/**
* Returns the human-readable list of regions keyed by machine name.
*
* @return array
* An array of human-readable region names keyed by machine name.
*/
public function getRegionNames();
/**
* Returns the human-readable name of a specific region.
*
* @param string $region
* The machine name of a region.
*
* @return string
* The human-readable name of a region.
*/
public function getRegionName($region);
/**
* Adds a block to this display variant.
*
* @param array $configuration
* An array of block configuration.
*
* @return string
* The block ID.
*/
public function addBlock(array $configuration);
/**
* Returns the region a specific block is assigned to.
*
* @param string $block_id
* The block ID.
*
* @return string
* The machine name of the region this block is assigned to.
*/
public function getRegionAssignment($block_id);
/**
* Returns an array of regions and their block plugins.
*
* @return array
* The array is first keyed by region machine name, with the values
* containing an array keyed by block ID, with block plugin instances as the
* values.
*/
public function getRegionAssignments();
/**
* Returns a specific block plugin.
*
* @param string $block_id
* The block ID.
*
* @return \Drupal\Core\Block\BlockPluginInterface
* The block plugin.
*/
public function getBlock($block_id);
/**
* Updates the configuration of a specific block plugin.
*
* @param string $block_id
* The block ID.
* @param array $configuration
* The array of configuration to set.
*
* @return $this
*/
public function updateBlock($block_id, array $configuration);
/**
* Removes a specific block from this display variant.
*
* @param string $block_id
* The block ID.
*
* @return $this
*/
public function removeBlock($block_id);
}

View file

@ -0,0 +1,130 @@
<?php
namespace Drupal\ctools\Plugin;
/**
* Provides methods for \Drupal\ctools\Plugin\BlockVariantInterface.
*/
trait BlockVariantTrait {
/**
* The block manager.
*
* @var \Drupal\Core\Block\BlockManager
*/
protected $blockManager;
/**
* The plugin collection that holds the block plugins.
*
* @var \Drupal\ctools\Plugin\BlockPluginCollection
*/
protected $blockPluginCollection;
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionNames()
*/
abstract public function getRegionNames();
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::getBlock()
*/
public function getBlock($block_id) {
return $this->getBlockCollection()->get($block_id);
}
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::addBlock()
*/
public function addBlock(array $configuration) {
$configuration['uuid'] = $this->uuidGenerator()->generate();
$this->getBlockCollection()->addInstanceId($configuration['uuid'], $configuration);
return $configuration['uuid'];
}
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::removeBlock()
*/
public function removeBlock($block_id) {
$this->getBlockCollection()->removeInstanceId($block_id);
return $this;
}
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::updateBlock()
*/
public function updateBlock($block_id, array $configuration) {
$existing_configuration = $this->getBlock($block_id)->getConfiguration();
$this->getBlockCollection()->setInstanceConfiguration($block_id, $configuration + $existing_configuration);
return $this;
}
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionAssignment()
*/
public function getRegionAssignment($block_id) {
$configuration = $this->getBlock($block_id)->getConfiguration();
return isset($configuration['region']) ? $configuration['region'] : NULL;
}
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionAssignments()
*/
public function getRegionAssignments() {
// Build an array of the region names in the right order.
$empty = array_fill_keys(array_keys($this->getRegionNames()), []);
$full = $this->getBlockCollection()->getAllByRegion();
// Merge it with the actual values to maintain the ordering.
return array_intersect_key(array_merge($empty, $full), $empty);
}
/**
* @see \Drupal\ctools\Plugin\BlockVariantInterface::getRegionName()
*/
public function getRegionName($region) {
$regions = $this->getRegionNames();
return isset($regions[$region]) ? $regions[$region] : '';
}
/**
* Gets the block plugin manager.
*
* @return \Drupal\Core\Block\BlockManager
* The block plugin manager.
*/
protected function getBlockManager() {
if (!$this->blockManager) {
$this->blockManager = \Drupal::service('plugin.manager.block');
}
return $this->blockManager;
}
/**
* Returns the block plugins used for this display variant.
*
* @return \Drupal\Core\Block\BlockPluginInterface[]|\Drupal\ctools\Plugin\BlockPluginCollection
* An array or collection of configured block plugins.
*/
protected function getBlockCollection() {
if (!$this->blockPluginCollection) {
$this->blockPluginCollection = new BlockPluginCollection($this->getBlockManager(), $this->getBlockConfig());
}
return $this->blockPluginCollection;
}
/**
* Returns the UUID generator.
*
* @return \Drupal\Component\Uuid\UuidInterface
*/
abstract protected function uuidGenerator();
/**
* Returns the configuration for stored blocks.
*
* @return array
* An array of block configuration, keyed by the unique block ID.
*/
abstract protected function getBlockConfig();
}

View file

@ -0,0 +1,160 @@
<?php
namespace Drupal\ctools\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\ctools\ConstraintConditionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a 'Entity Bundle' condition.
*
* @Condition(
* id = "entity_bundle",
* deriver = "\Drupal\ctools\Plugin\Deriver\EntityBundle"
* )
*
*/
class EntityBundle extends ConditionPluginBase implements ConstraintConditionInterface, ContainerFactoryPluginInterface {
/**
* The entity type bundle info service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* @var \Drupal\Core\Entity\EntityTypeInterface|null
*/
protected $bundleOf;
/**
* Creates a new EntityBundle instance.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info service.
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->bundleOf = $entity_type_manager->getDefinition($this->getDerivativeId());
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info'),
$configuration,
$plugin_id,
$plugin_definition
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$options = array();
$bundles = $this->entityTypeBundleInfo->getBundleInfo($this->bundleOf->id());
foreach ($bundles as $id => $info) {
$options[$id] = $info['label'];
}
$form['bundles'] = array(
'#title' => $this->pluginDefinition['label'],
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => $this->configuration['bundles'],
);
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['bundles'] = array_filter($form_state->getValue('bundles'));
parent::submitConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function evaluate() {
if (empty($this->configuration['bundles']) && !$this->isNegated()) {
return TRUE;
}
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->getContextValue($this->bundleOf->id());
return !empty($this->configuration['bundles'][$entity->bundle()]);
}
/**
* {@inheritdoc}
*/
public function summary() {
if (count($this->configuration['bundles']) > 1) {
$bundles = $this->configuration['bundles'];
$last = array_pop($bundles);
$bundles = implode(', ', $bundles);
return $this->t('@bundle_type is @bundles or @last', array('@bundle_type' => $this->bundleOf->getBundleLabel(), '@bundles' => $bundles, '@last' => $last));
}
$bundle = reset($this->configuration['bundles']);
return $this->t('@bundle_type is @bundle', array('@bundle_type' => $this->bundleOf->getBundleLabel(), '@bundle' => $bundle));
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array('bundles' => array()) + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
*/
public function applyConstraints(array $contexts = array()) {
// Nullify any bundle constraints on contexts we care about.
$this->removeConstraints($contexts);
$bundle = array_values($this->configuration['bundles']);
// There's only one expected context for this plugint type.
foreach ($this->getContextMapping() as $definition_id => $context_id) {
$contexts[$context_id]->getContextDefinition()->addConstraint('Bundle', ['value' => $bundle]);
}
}
/**
* {@inheritdoc}
*
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
*/
public function removeConstraints(array $contexts = array()) {
// Reset the bundle constraint for any context we've mapped.
foreach ($this->getContextMapping() as $definition_id => $context_id) {
$constraints = $contexts[$context_id]->getContextDefinition()->getConstraints();
unset($constraints['Bundle']);
$contexts[$context_id]->getContextDefinition()->setConstraints($constraints);
}
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Drupal\ctools\Plugin\Condition;
use Drupal\node\Plugin\Condition\NodeType as CoreNodeType;
use Drupal\ctools\ConstraintConditionInterface;
class NodeType extends CoreNodeType implements ConstraintConditionInterface {
/**
* {@inheritdoc}
*
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
*/
public function applyConstraints(array $contexts = array()) {
// Nullify any bundle constraints on contexts we care about.
$this->removeConstraints($contexts);
// If a single bundle is configured, we can set a proper constraint.
if (count($this->configuration['bundles']) == 1) {
$bundle = array_values($this->configuration['bundles']);
foreach ($this->getContextMapping() as $definition_id => $context_id) {
$contexts[$context_id]->getContextDefinition()->addConstraint('Bundle', ['value' => $bundle[0]]);
}
}
}
/**
* {@inheritdoc}
*
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
*/
public function removeConstraints(array $contexts = array()) {
// Reset the bundle constraint for any context we've mapped.
foreach ($this->getContextMapping() as $definition_id => $context_id) {
$constraints = $contexts[$context_id]->getContextDefinition()->getConstraints();
unset($constraints['Bundle']);
$contexts[$context_id]->getContextDefinition()->setConstraints($constraints);
}
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\Plugin\Context\ContextDefinition;
/**
* Deriver that creates a condition for each entity type with bundles.
*/
class EntityBundle extends EntityDeriverBase {
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->hasKey('bundle')) {
$this->derivatives[$entity_type_id] = $base_plugin_definition;
$this->derivatives[$entity_type_id]['label'] = $this->getEntityBundleLabel($entity_type);
$this->derivatives[$entity_type_id]['context'] = [
"$entity_type_id" => new ContextDefinition('entity:' . $entity_type_id),
];
}
}
return $this->derivatives;
}
/**
* Provides the bundle label with a fallback when not defined.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type we are looking the bundle label for.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* The entity bundle label or a fallback label.
*/
protected function getEntityBundleLabel($entity_type) {
if ($label = $entity_type->getBundleLabel()) {
return $this->t('@label', ['@label' => $label]);
}
$fallback = $entity_type->getLabel();
if ($bundle_entity_type = $entity_type->getBundleEntityType()) {
// This is a better fallback.
$fallback = $this->entityManager->getDefinition($bundle_entity_type)->getLabel();
}
return $this->t('@label bundle', ['@label' => $fallback]);
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* An abstract base class that sets up the needs of entity specific derivers.
*/
abstract class EntityDeriverBase extends DeriverBase implements ContainerDeriverInterface {
use StringTranslationTrait;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs new EntityViewDeriver.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
$this->entityManager = $entity_manager;
$this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('entity.manager'),
$container->get('string_translation')
);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\Plugin\Context\ContextDefinition;
/**
* Provides entity view block definitions for each entity type.
*/
class EntityViewDeriver extends EntityDeriverBase {
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->hasViewBuilderClass()) {
$this->derivatives[$entity_type_id] = $base_plugin_definition;
$this->derivatives[$entity_type_id]['admin_label'] = $this->t('Entity view (@label)', ['@label' => $entity_type->getLabel()]);
$this->derivatives[$entity_type_id]['context'] = [
'entity' => new ContextDefinition('entity:' . $entity_type_id),
];
}
}
return $this->derivatives;
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\TypedData\DataDefinitionInterface;
class TypedDataEntityRelationshipDeriver extends TypedDataRelationshipDeriver {
/**
* {@inheritdoc}
*/
protected $label = '@property Entity from @base';
/**
* {@inheritdoc}
*/
protected function generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, DataDefinitionInterface $base_definition, $property_name, DataDefinitionInterface $property_definition) {
if (method_exists($property_definition, 'getType') && $property_definition->getType() == 'entity_reference') {
parent::generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, $base_definition, $property_name, $property_definition);
}
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\TypedData\DataDefinitionInterface;
class TypedDataLanguageRelationshipDeriver extends TypedDataRelationshipDeriver {
/**
* {@inheritdoc}
*
* @todo this results in awful labels like "Language Language from Content"
* Fix it.
*/
protected $label = '@property Language from @base';
/**
* {@inheritdoc}
*/
protected function generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, DataDefinitionInterface $base_definition, $property_name, DataDefinitionInterface $property_definition) {
if (method_exists($property_definition, 'getType') && $property_definition->getType() == 'language') {
parent::generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, $base_definition, $property_name, $property_definition);
}
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
parent::getDerivativeDefinitions($base_plugin_definition);
// The data types will all be set to string since language extends string
// and the parent class finds the related primitive.
foreach ($this->derivatives as $plugin_id => $derivative) {
$this->derivatives[$plugin_id]['data_type'] = 'language';
}
return $this->derivatives;
}
}

View file

@ -0,0 +1,113 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
use Drupal\Core\TypedData\ListDataDefinitionInterface;
use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\field\Entity\FieldConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class TypedDataPropertyDeriverBase extends DeriverBase implements ContainerDeriverInterface {
use StringTranslationTrait;
/**
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
*/
protected $typedDataManager;
/**
* The label string for use with derivative labels.
*
* @var string
*/
protected $label = '@property from @base';
/**
* TypedDataPropertyDeriverBase constructor.
*
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
* The typed data manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(TypedDataManagerInterface $typed_data_manager, TranslationInterface $string_translation) {
$this->typedDataManager = $typed_data_manager;
$this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('typed_data_manager'),
$container->get('string_translation')
);
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->typedDataManager->getDefinitions() as $data_type_id => $data_type_definition) {
if (is_subclass_of($data_type_definition['class'], ComplexDataInterface::class, TRUE)) {
/** @var \Drupal\Core\TypedData\ComplexDataDefinitionInterface $base_definition */
$base_definition = $this->typedDataManager->createDataDefinition($data_type_id);
foreach ($base_definition->getPropertyDefinitions() as $property_name => $property_definition) {
if ($property_definition instanceof BaseFieldDefinition || $property_definition instanceof FieldConfig) {
$this->generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, $base_definition, $property_name, $property_definition);
}
}
}
}
return $this->derivatives;
}
/**
* @param $property_definition
*
* @return mixed
*/
protected function getDataType($property_definition) {
if ($property_definition instanceof DataReferenceDefinitionInterface) {
return $property_definition->getTargetDefinition()->getDataType();
}
if ($property_definition instanceof ListDataDefinitionInterface) {
return $property_definition->getItemDefinition()->getDataType();
}
return $property_definition->getDataType();
}
/**
* Generates and maintains a derivative definition.
*
* This method should directly manipulate $this->derivatives and not return
* values. This allows implementations control over the derivative names.
*
* @param $base_plugin_definition
* The base plugin definition.
* @param string $data_type_id
* The plugin id of the data type.
* @param mixed $data_type_definition
* The plugin definition of the data type.
* @param \Drupal\Core\TypedData\DataDefinitionInterface $base_definition
* The data type definition of a complex data object.
* @param string $property_name
* The name of the property
* @param \Drupal\Core\TypedData\DataDefinitionInterface $property_definition
* The property definition.
*
*/
abstract protected function generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, DataDefinitionInterface $base_definition, $property_name, DataDefinitionInterface $property_definition);
}

View file

@ -0,0 +1,75 @@
<?php
namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\field\FieldConfigInterface;
class TypedDataRelationshipDeriver extends TypedDataPropertyDeriverBase implements ContainerDeriverInterface {
/**
* {@inheritdoc}
*/
protected function generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, DataDefinitionInterface $base_definition, $property_name, DataDefinitionInterface $property_definition) {
$bundle_info = $base_definition->getConstraint('Bundle');
// Identify base definitions that appear on bundle-able entities.
if ($bundle_info && array_filter($bundle_info) && $base_definition->getConstraint('EntityType')) {
$base_data_type = 'entity:' . $base_definition->getConstraint('EntityType');
}
// Otherwise, just use the raw data type identifier.
else {
$base_data_type = $data_type_id;
}
// If we've not processed this thing before.
if (!isset($this->derivatives[$base_data_type . ':' . $property_name])) {
$derivative = $base_plugin_definition;
$derivative['label'] = $this->t($this->label, [
'@property' => $property_definition->getLabel(),
'@base' => $data_type_definition['label'],
]);
$derivative['data_type'] = $property_definition->getFieldStorageDefinition()->getPropertyDefinition($property_definition->getFieldStorageDefinition()->getMainPropertyName())->getDataType();
$derivative['property_name'] = $property_name;
$context_definition = new ContextDefinition($base_data_type, $this->typedDataManager->createDataDefinition($base_data_type));
// Add the constraints of the base definition to the context definition.
if ($base_definition->getConstraint('Bundle')) {
$context_definition->addConstraint('Bundle', $base_definition->getConstraint('Bundle'));
}
$derivative['context'] = [
'base' => $context_definition,
];
$derivative['property_name'] = $property_name;
$this->derivatives[$base_data_type . ':' . $property_name] = $derivative;
}
// Individual fields can be on multiple bundles.
elseif ($property_definition instanceof FieldConfigInterface) {
// We should only end up in here on entity bundles.
$derivative = $this->derivatives[$base_data_type . ':' . $property_name];
// Update label
/** @var \Drupal\Core\StringTranslation\TranslatableMarkup $label */
$label = $derivative['label'];
list(,, $argument_name) = explode(':', $data_type_id);
$arguments = $label->getArguments();
$arguments['@'. $argument_name] = $data_type_definition['label'];
$string_args = $arguments;
array_shift($string_args);
$last = array_slice($string_args, -1);
// The slice doesn't remove, so do that now.
array_pop($string_args);
$string = count($string_args) >= 2 ? '@property from '. implode(', ', array_keys($string_args)) .' and '. array_keys($last)[0] : '@property from @base and '. array_keys($last)[0];
$this->derivatives[$base_data_type . ':' . $property_name]['label'] = $this->t($string, $arguments);
if ($base_definition->getConstraint('Bundle')) {
// Add bundle constraints
$context_definition = $derivative['context']['base'];
$bundles = $context_definition->getConstraint('Bundle') ?: [];
$bundles = array_merge($bundles, $base_definition->getConstraint('Bundle'));
$context_definition->addConstraint('Bundle', $bundles);
}
}
}
}

View file

@ -0,0 +1,226 @@
<?php
namespace Drupal\ctools\Plugin\DisplayVariant;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Block\BlockManager;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Condition\ConditionManager;
use Drupal\Core\Display\VariantBase;
use Drupal\Core\Display\ContextAwareVariantInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Utility\Token;
use Drupal\ctools\Form\AjaxFormTrait;
use Drupal\ctools\Plugin\BlockVariantInterface;
use Drupal\ctools\Plugin\BlockVariantTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a base class for a display variant that simply contains blocks.
*/
abstract class BlockDisplayVariant extends VariantBase implements ContextAwareVariantInterface, ContainerFactoryPluginInterface, BlockVariantInterface, RefinableCacheableDependencyInterface {
use AjaxFormTrait;
use BlockVariantTrait;
/**
* The context handler.
*
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
*/
protected $contextHandler;
/**
* The UUID generator.
*
* @var \Drupal\Component\Uuid\UuidInterface
*/
protected $uuidGenerator;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* The token service.
*
* @var \Drupal\Core\Utility\Token
*/
protected $token;
/**
* An array of collected contexts.
*
* This is only used on runtime, and is not stored.
*
* @var \Drupal\Component\Plugin\Context\ContextInterface[]
*/
protected $contexts = [];
/**
* Constructs a new BlockDisplayVariant.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
* The context handler.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Component\Uuid\UuidInterface $uuid_generator
* The UUID generator.
* @param \Drupal\Core\Utility\Token $token
* The token service.
* @param \Drupal\Core\Block\BlockManager $block_manager
* The block manager.
* @param \Drupal\Core\Condition\ConditionManager $condition_manager
* The condition manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ContextHandlerInterface $context_handler, AccountInterface $account, UuidInterface $uuid_generator, Token $token, BlockManager $block_manager, ConditionManager $condition_manager) {
// Inject dependencies as early as possible, so they can be used in
// configuration.
$this->contextHandler = $context_handler;
$this->account = $account;
$this->uuidGenerator = $uuid_generator;
$this->token = $token;
$this->blockManager = $block_manager;
$this->conditionManager = $condition_manager;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('context.handler'),
$container->get('current_user'),
$container->get('uuid'),
$container->get('token'),
$container->get('plugin.manager.block'),
$container->get('plugin.manager.condition')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'blocks' => []
];
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
foreach ($this->getBlockCollection() as $instance) {
$this->calculatePluginDependencies($instance);
}
return $this->dependencies;
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return [
'blocks' => $this->getBlockCollection()->getConfiguration(),
] + parent::getConfiguration();
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
// preserve the uuid.
if ($this->configuration && !empty($this->configuration['uuid'])) {
$configuration['uuid'] = $this->configuration['uuid'];
}
parent::setConfiguration($configuration);
$this->getBlockCollection()->setConfiguration($this->configuration['blocks']);
return $this;
}
/**
* Gets the contexts.
*
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
* An array of set contexts, keyed by context name.
*/
public function getContexts() {
return $this->contexts;
}
/**
* Sets the contexts.
*
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
* An array of contexts, keyed by context name.
*
* @return $this
*/
public function setContexts(array $contexts) {
$this->contexts = $contexts;
return $this;
}
/**
* {@inheritdoc}
*/
protected function contextHandler() {
return $this->contextHandler;
}
/**
* {@inheritdoc}
*/
protected function getBlockConfig() {
return $this->configuration['blocks'];
}
/**
* {@inheritdoc}
*/
protected function uuidGenerator() {
return $this->uuidGenerator;
}
/**
* {@inheritdoc}
*/
public function __sleep() {
$vars = parent::__sleep();
// Gathered contexts objects should not be serialized.
if (($key = array_search('contexts', $vars)) !== FALSE) {
unset($vars[$key]);
}
// The block plugin collection should also not be serialized, ensure that
// configuration is synced back.
if (($key = array_search('blockPluginCollection', $vars)) !== FALSE) {
if ($this->blockPluginCollection) {
$this->configuration['blocks'] = $this->blockPluginCollection->getConfiguration();
}
unset($vars[$key]);
}
return $vars;
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Drupal\ctools\Plugin;
/**
* Provides an interface for configuring a plugin via wizard steps.
*/
interface PluginWizardInterface {
/**
* Retrieve a list of FormInterface classes by their step key in the wizard.
*
* @param mixed $cached_values
* The cached values used in the wizard. The plugin we're editing will
* always be assigned to the 'plugin' key.
*
* @return array
* An associative array keyed on the step name with an array value with the
* following keys:
* - title (string): Human-readable title of the step.
* - form (string): Fully-qualified class name of the form for this step.
*/
public function getWizardOperations($cached_values);
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\ctools\Plugin\Relationship;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
/**
* @Relationship(
* id = "typed_data_entity_relationship",
* deriver = "\Drupal\ctools\Plugin\Deriver\TypedDataEntityRelationshipDeriver"
* )
*/
class TypedDataEntityRelationship extends TypedDataRelationship {
/**
* {@inheritdoc}
*/
public function getRelationship() {
$plugin_definition = $this->getPluginDefinition();
$entity_type = $this->getData($this->getContext('base'))->getDataDefinition()->getSetting('target_type');
$context_definition = new ContextDefinition("entity:$entity_type", $plugin_definition['label']);
$context_value = NULL;
// If the 'base' context has a value, then get the property value to put on
// the context (otherwise, mapping hasn't occurred yet and we just want to
// return the context with the right definition and no value).
if ($this->getContext('base')->hasContextValue()) {
$context_value = $this->getData($this->getContext('base'))->entity;
}
$context_definition->setDefaultValue($context_value);
return new Context($context_definition, $context_value);
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\ctools\Plugin\Relationship;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
/**
* @Relationship(
* id = "typed_data_language_relationship",
* deriver = "\Drupal\ctools\Plugin\Deriver\TypedDataLanguageRelationshipDeriver"
* )
*/
class TypedDataLanguageRelationship extends TypedDataRelationship {
/**
* {@inheritdoc}
*/
public function getRelationship() {
$plugin_definition = $this->getPluginDefinition();
$context_definition = new ContextDefinition("language", $plugin_definition['label']);
$context_value = NULL;
// If the 'base' context has a value, then get the property value to put on
// the context (otherwise, mapping hasn't occurred yet and we just want to
// return the context with the right definition and no value).
if ($this->getContext('base')->hasContextValue()) {
$context_value = $this->getData($this->getContext('base'))->language;
}
$context_definition->setDefaultValue($context_value);
return new Context($context_definition, $context_value);
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Drupal\ctools\Plugin\Relationship;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextInterface;
use Drupal\Core\TypedData\DataReferenceInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\ctools\Annotation\Relationship;
use Drupal\ctools\Plugin\RelationshipBase;
/**
* @Relationship(
* id = "typed_data_relationship",
* deriver = "\Drupal\ctools\Plugin\Deriver\TypedDataRelationshipDeriver"
* )
*/
class TypedDataRelationship extends RelationshipBase {
/**
* {@inheritdoc}
*/
public function getRelationship() {
$plugin_definition = $this->getPluginDefinition();
$data_type = $plugin_definition['data_type'];
$context_definition = new ContextDefinition($data_type, $plugin_definition['label']);
$context_value = NULL;
// If the 'base' context has a value, then get the property value to put on
// the context (otherwise, mapping hasn't occurred yet and we just want to
// return the context with the right definition and no value).
if ($this->getContext('base')->hasContextValue()) {
$data = $this->getData($this->getContext('base'));
$property = $this->getMainPropertyName($data);
$context_value = $data->get($property)->getValue();
}
$context_definition->setDefaultValue($context_value);
return new Context($context_definition, $context_value);
}
public function getName() {
return $this->getPluginDefinition()['property_name'];
}
protected function getData(ContextInterface $context) {
/** @var \Drupal\Core\TypedData\ComplexDataInterface $base */
$base = $context->getContextValue();
$name = $this->getPluginDefinition()['property_name'];
$data = $base->get($name);
// @todo add configuration to get N instead of first.
if ($data instanceof ListInterface) {
$data = $data->first();
}
if ($data instanceof DataReferenceInterface) {
$data = $data->getTarget();
}
return $data;
}
protected function getMainPropertyName(FieldItemInterface $data) {
return $data->getFieldDefinition()->getFieldStorageDefinition()->getMainPropertyName();
}
public function getRelationshipValue() {
$property = $this->getMainPropertyName();
/** @var \Drupal\Core\TypedData\ComplexDataInterface $data */
$data = $this->getRelationship()->getContextData();
$data->get($property)->getValue();
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Core\Plugin\ContextAwarePluginBase;
/**
* Base class for Relationship plugins.
*/
abstract class RelationshipBase extends ContextAwarePluginBase implements RelationshipInterface {}

View file

@ -0,0 +1,27 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Component\Plugin\DerivativeInspectionInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
/**
* Defines an interface for Relationship plugins.
*/
interface RelationshipInterface extends ContextAwarePluginInterface, DerivativeInspectionInterface {
/**
* Generates a context based on this plugin's configuration.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface
*/
public function getRelationship();
/**
* The name of the property used to get this relationship.
*
* @return string
*/
public function getName();
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Core\Plugin\Context\ContextAwarePluginManagerTrait;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
/**
* Provides the Relationship plugin manager.
*/
class RelationshipManager extends DefaultPluginManager implements RelationshipManagerInterface {
use ContextAwarePluginManagerTrait;
/**
* Constructor for RelationshipManager objects.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Relationship', $namespaces, $module_handler, 'Drupal\ctools\Plugin\RelationshipInterface', 'Drupal\ctools\Annotation\Relationship');
$this->alterInfo('ctools_relationship_info');
$this->setCacheBackend($cache_backend, 'ctools_relationship_plugins');
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
use Drupal\Core\Plugin\Context\ContextAwarePluginManagerInterface;
/**
* Provides the Relationship plugin manager.
*/
interface RelationshipManagerInterface extends ContextAwarePluginManagerInterface, CachedDiscoveryInterface {}

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\ctools\Plugin;
/**
* Provides an interface for objects that have variants e.g. Pages.
*/
interface VariantCollectionInterface {
/**
* Adds a new variant to the entity.
*
* @param array $configuration
* An array of configuration for the new variant.
*
* @return string
* The variant ID.
*/
public function addVariant(array $configuration);
/**
* Retrieves a specific variant.
*
* @param string $variant_id
* The variant ID.
*
* @return \Drupal\Core\Display\VariantInterface
* The variant object.
*/
public function getVariant($variant_id);
/**
* Removes a specific variant.
*
* @param string $variant_id
* The variant ID.
*
* @return $this
*/
public function removeVariant($variant_id);
/**
* Returns the variants available for the entity.
*
* @return \Drupal\Core\Display\VariantInterface[]
* An array of the variants.
*/
public function getVariants();
}

View file

@ -0,0 +1,67 @@
<?php
namespace Drupal\ctools\Plugin;
/**
* Provides methods for VariantCollectionInterface.
*/
trait VariantCollectionTrait {
/**
* The plugin collection that holds the variants.
*
* @var \Drupal\ctools\Plugin\VariantPluginCollection
*/
protected $variantCollection;
/**
* @see \Drupal\ctools\Plugin\VariantCollectionInterface::addVariant()
*/
public function addVariant(array $configuration) {
$configuration['uuid'] = $this->uuidGenerator()->generate();
$this->getVariants()->addInstanceId($configuration['uuid'], $configuration);
return $configuration['uuid'];
}
/**
* @see \Drupal\ctools\Plugin\VariantCollectionInterface::getVariant()
*/
public function getVariant($variant_id) {
return $this->getVariants()->get($variant_id);
}
/**
* @see \Drupal\ctools\Plugin\VariantCollectionInterface::removeVariant()
*/
public function removeVariant($variant_id) {
$this->getVariants()->removeInstanceId($variant_id);
return $this;
}
/**
* @see \Drupal\ctools\Plugin\VariantCollectionInterface::getVariants()
*/
public function getVariants() {
if (!$this->variantCollection) {
$this->variantCollection = new VariantPluginCollection(\Drupal::service('plugin.manager.display_variant'), $this->getVariantConfig());
$this->variantCollection->sort();
}
return $this->variantCollection;
}
/**
* Returns the configuration for stored variants.
*
* @return array
* An array of variant configuration, keyed by the unique variant ID.
*/
abstract protected function getVariantConfig();
/**
* Returns the UUID generator.
*
* @return \Drupal\Component\Uuid\UuidInterface
*/
abstract protected function uuidGenerator();
}

View file

@ -0,0 +1,43 @@
<?php
namespace Drupal\ctools\Plugin;
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
/**
* Provides a collection of variants plugins.
*/
class VariantPluginCollection extends DefaultLazyPluginCollection {
/**
* {@inheritdoc}
*
* @return \Drupal\Core\Display\VariantInterface
*/
public function &get($instance_id) {
return parent::get($instance_id);
}
/**
* {@inheritdoc}
*/
public function sort() {
// @todo Determine the reason this needs error suppression.
@uasort($this->instanceIDs, [$this, 'sortHelper']);
return $this;
}
/**
* {@inheritdoc}
*/
public function sortHelper($aID, $bID) {
$a_weight = $this->get($aID)->getWeight();
$b_weight = $this->get($bID)->getWeight();
if ($a_weight == $b_weight) {
return strcmp($aID, $bID);
}
return ($a_weight < $b_weight) ? -1 : 1;
}
}