Issue #2354889 by larowlan, dawehner, lauriii, Berdir, catch, martin107, pfrenssen, EclipseGc, Fabianx, Wim Leers, dsnopek, jibran, tim.plunkett, andypost: Make block context faster by removing onBlock event and replace it with loading from a ContextManager
parent
46e6f72399
commit
794be16300
|
|
@ -2186,6 +2186,9 @@ function hook_validation_constraint_alter(array &$definitions) {
|
||||||
* at the end of a request to finalize operations, if this service was
|
* at the end of a request to finalize operations, if this service was
|
||||||
* instantiated. Services should implement \Drupal\Core\DestructableInterface
|
* instantiated. Services should implement \Drupal\Core\DestructableInterface
|
||||||
* in this case.
|
* in this case.
|
||||||
|
* - context_provider: Indicates a block context provider, used for example
|
||||||
|
* by block conditions. It has to implement
|
||||||
|
* \Drupal\Core\Plugin\Context\ContextProviderInterface.
|
||||||
*
|
*
|
||||||
* Creating a tag for a service does not do anything on its own, but tags
|
* Creating a tag for a service does not do anything on its own, but tags
|
||||||
* can be discovered or queried in a compiler pass when the container is built,
|
* can be discovered or queried in a compiler pass when the container is built,
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,9 @@ services:
|
||||||
context.handler:
|
context.handler:
|
||||||
class: Drupal\Core\Plugin\Context\ContextHandler
|
class: Drupal\Core\Plugin\Context\ContextHandler
|
||||||
arguments: ['@typed_data_manager']
|
arguments: ['@typed_data_manager']
|
||||||
|
context.repository:
|
||||||
|
class: Drupal\Core\Plugin\Context\LazyContextRepository
|
||||||
|
arguments: ['@service_container']
|
||||||
cron:
|
cron:
|
||||||
class: Drupal\Core\Cron
|
class: Drupal\Core\Cron
|
||||||
arguments: ['@module_handler', '@lock', '@queue', '@state', '@account_switcher', '@logger.channel.cron', '@plugin.manager.queue_worker']
|
arguments: ['@module_handler', '@lock', '@queue', '@state', '@account_switcher', '@logger.channel.cron', '@plugin.manager.queue_worker']
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace Drupal\Core;
|
||||||
use Drupal\Core\Cache\Context\CacheContextsPass;
|
use Drupal\Core\Cache\Context\CacheContextsPass;
|
||||||
use Drupal\Core\Cache\ListCacheBinsPass;
|
use Drupal\Core\Cache\ListCacheBinsPass;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass;
|
use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass;
|
||||||
|
use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
|
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
|
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
|
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
|
||||||
|
|
@ -88,6 +89,7 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
||||||
// Add the compiler pass that will process the tagged services.
|
// Add the compiler pass that will process the tagged services.
|
||||||
$container->addCompilerPass(new ListCacheBinsPass());
|
$container->addCompilerPass(new ListCacheBinsPass());
|
||||||
$container->addCompilerPass(new CacheContextsPass());
|
$container->addCompilerPass(new CacheContextsPass());
|
||||||
|
$container->addCompilerPass(new ContextProvidersPass());
|
||||||
|
|
||||||
// Register plugin managers.
|
// Register plugin managers.
|
||||||
$container->addCompilerPass(new PluginManagerPass());
|
$container->addCompilerPass(new PluginManagerPass());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the context provider service IDs to the context manager.
|
||||||
|
*/
|
||||||
|
class ContextProvidersPass implements CompilerPassInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* Passes the service IDs of all context providers to the context repository.
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container) {
|
||||||
|
$context_providers = [];
|
||||||
|
foreach (array_keys($container->findTaggedServiceIds('context_provider')) as $id) {
|
||||||
|
$context_providers[] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$definition = $container->getDefinition('context.repository');
|
||||||
|
$definition->addArgument($context_providers);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Core\Plugin\Context\ContextProviderInterface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Plugin\Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines an interface for providing plugin contexts.
|
||||||
|
*
|
||||||
|
* Implementations only need to deal with unqualified context IDs so they only
|
||||||
|
* need to be unique in the context of a given service provider.
|
||||||
|
*
|
||||||
|
* The fully qualified context ID then includes the service ID:
|
||||||
|
* @{service_id}:{unqualified_context_id}.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Plugin\Context\ContextRepositoryInterface
|
||||||
|
*/
|
||||||
|
interface ContextProviderInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets runtime context values for the given context IDs.
|
||||||
|
*
|
||||||
|
* For context-aware plugins to function correctly, all of the contexts that
|
||||||
|
* they require must be populated with values. So this method should set a
|
||||||
|
* value for each context that it adds. For example:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* // Determine a specific node to pass as context to a block.
|
||||||
|
* $node = ...
|
||||||
|
*
|
||||||
|
* // Set that specific node as the value of the 'node' context.
|
||||||
|
* $context = new Context(new ContextDefinition('entity:node'));
|
||||||
|
* $context->setContextValue($node);
|
||||||
|
* return ['node' => $context];
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* On the other hand, there are cases, on which providers no longer are
|
||||||
|
* possible to provide context objects, even without the value, so the caller
|
||||||
|
* should not expect it.
|
||||||
|
*
|
||||||
|
* @param string[] $unqualified_context_ids
|
||||||
|
* The requested context IDs. The context provider must only return contexts
|
||||||
|
* for those IDs.
|
||||||
|
*
|
||||||
|
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
|
||||||
|
* The determined available contexts, keyed by the unqualified context_id.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Plugin\Context\ContextProviderInterface:getAvailableContexts()
|
||||||
|
*/
|
||||||
|
public function getRuntimeContexts(array $unqualified_context_ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all available contexts for the purposes of configuration.
|
||||||
|
*
|
||||||
|
* When a context aware plugin is being configured, the configuration UI must
|
||||||
|
* know which named contexts are potentially available, but does not care
|
||||||
|
* about the value, since the value can be different for each request, and
|
||||||
|
* might not be available at all during the configuration UI's request.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* @code
|
||||||
|
* // During configuration, there is no specific node to pass as context.
|
||||||
|
* // However, inform the system that a context named 'node' is
|
||||||
|
* // available, and provide its definition, so that context aware plugins
|
||||||
|
* // can be configured to use it. When the plugin, for example a block,
|
||||||
|
* // needs to evaluate the context, the value of this context will be
|
||||||
|
* // supplied by getRuntimeContexts().
|
||||||
|
* $context = new Context(new ContextDefinition('entity:node'));
|
||||||
|
* return ['node' => $context];
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
|
||||||
|
* All available contexts keyed by the unqualified context ID.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Plugin\Context\ContextProviderInterface::getRuntimeContext()
|
||||||
|
*/
|
||||||
|
public function getAvailableContexts();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Core\Plugin\Context\ContextRepositoryInterface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Plugin\Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offers a global context repository.
|
||||||
|
*
|
||||||
|
* Provides a list of all available contexts, which is mostly useful for
|
||||||
|
* configuration on forms, as well as a method to get the concrete contexts with
|
||||||
|
* their values, given a list of fully qualified context IDs.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Plugin\Context\ContextProviderInterface
|
||||||
|
*/
|
||||||
|
interface ContextRepositoryInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets runtime context values for the given context IDs.
|
||||||
|
*
|
||||||
|
* Given that context providers might not return contexts for the given
|
||||||
|
* context IDs, it is also not guaranteed that the context repository returns
|
||||||
|
* contexts for all specified IDs.
|
||||||
|
*
|
||||||
|
* @param string[] $context_ids
|
||||||
|
* Fully qualified context IDs, which looks like
|
||||||
|
* @{service_id}:{unqualified_context_id}, so for example
|
||||||
|
* node.node_route_context:node.
|
||||||
|
*
|
||||||
|
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
|
||||||
|
* The determined contexts, keyed by the fully qualified context ID.
|
||||||
|
*/
|
||||||
|
public function getRuntimeContexts(array $context_ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all available contexts for the purposes of configuration.
|
||||||
|
*
|
||||||
|
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
|
||||||
|
* All available contexts.
|
||||||
|
*/
|
||||||
|
public function getAvailableContexts();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Core\Plugin\Context\LazyContextRepository.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Plugin\Context;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a context repository which uses context provider services.
|
||||||
|
*/
|
||||||
|
class LazyContextRepository implements ContextRepositoryInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of available context providers service IDs.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
* Context provider service IDs.
|
||||||
|
*/
|
||||||
|
protected $contextProviderServiceIDs = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service container.
|
||||||
|
*
|
||||||
|
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||||
|
*/
|
||||||
|
protected $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The statically cached contexts.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Plugin\Context\ContextInterface[]
|
||||||
|
*/
|
||||||
|
protected $contexts = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a LazyContextRepository object.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||||
|
* The current service container.
|
||||||
|
* @param string[] $context_provider_service_ids
|
||||||
|
* The set of the available context provider service IDs.
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container, array $context_provider_service_ids) {
|
||||||
|
$this->container = $container;
|
||||||
|
$this->contextProviderServiceIDs = $context_provider_service_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getRuntimeContexts(array $context_ids) {
|
||||||
|
$contexts = [];
|
||||||
|
|
||||||
|
// Create a map of context providers (service IDs) to unqualified context
|
||||||
|
// IDs.
|
||||||
|
$context_ids_by_service = [];
|
||||||
|
foreach ($context_ids as $id) {
|
||||||
|
if (isset($this->contexts[$id])) {
|
||||||
|
$contexts[$id] = $this->contexts[$id];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The IDs have been passed in @{service_id}:{unqualified_context_id}
|
||||||
|
// format.
|
||||||
|
// @todo Convert to an assert once https://www.drupal.org/node/2408013 is
|
||||||
|
// in.
|
||||||
|
if ($id[0] === '@' && strpos($id, ':') !== FALSE) {
|
||||||
|
list($service_id, $unqualified_context_id) = explode(':', $id, 2);
|
||||||
|
// Remove the leading '@'.
|
||||||
|
$service_id = substr($service_id, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new \InvalidArgumentException('You must provide the context IDs in the @{service_id}:{unqualified_context_id} format.');
|
||||||
|
}
|
||||||
|
$context_ids_by_service[$service_id][] = $unqualified_context_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all missing context providers (services), gather the
|
||||||
|
// runtime contexts and assign them as requested.
|
||||||
|
foreach ($context_ids_by_service as $service_id => $unqualified_context_ids) {
|
||||||
|
$contexts_by_service = $this->container->get($service_id)->getRuntimeContexts($unqualified_context_ids);
|
||||||
|
|
||||||
|
$wanted_contexts = array_intersect_key($contexts_by_service, array_flip($unqualified_context_ids));
|
||||||
|
foreach ($wanted_contexts as $unqualified_context_id => $context) {
|
||||||
|
$context_id = '@' . $service_id . ':' . $unqualified_context_id;
|
||||||
|
$this->contexts[$context_id] = $contexts[$context_id] = $context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAvailableContexts() {
|
||||||
|
$contexts = [];
|
||||||
|
foreach ($this->contextProviderServiceIDs as $service_id) {
|
||||||
|
$contexts_by_service = $this->container->get($service_id)->getAvailableContexts();
|
||||||
|
foreach ($contexts_by_service as $unqualified_context_id => $context) {
|
||||||
|
$context_id = '@' . $service_id . ':' . $unqualified_context_id;
|
||||||
|
$contexts[$context_id] = $context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -7,21 +7,6 @@ services:
|
||||||
class: Drupal\block\EventSubscriber\BlockPageDisplayVariantSubscriber
|
class: Drupal\block\EventSubscriber\BlockPageDisplayVariantSubscriber
|
||||||
tags:
|
tags:
|
||||||
- { name: event_subscriber }
|
- { name: event_subscriber }
|
||||||
block.current_user_context:
|
|
||||||
class: Drupal\block\EventSubscriber\CurrentUserContext
|
|
||||||
arguments: ['@current_user', '@entity.manager']
|
|
||||||
tags:
|
|
||||||
- { name: 'event_subscriber' }
|
|
||||||
block.current_language_context:
|
|
||||||
class: Drupal\block\EventSubscriber\CurrentLanguageContext
|
|
||||||
arguments: ['@language_manager']
|
|
||||||
tags:
|
|
||||||
- { name: 'event_subscriber' }
|
|
||||||
block.node_route_context:
|
|
||||||
class: Drupal\block\EventSubscriber\NodeRouteContext
|
|
||||||
arguments: ['@current_route_match']
|
|
||||||
tags:
|
|
||||||
- { name: 'event_subscriber' }
|
|
||||||
block.repository:
|
block.repository:
|
||||||
class: Drupal\block\BlockRepository
|
class: Drupal\block\BlockRepository
|
||||||
arguments: ['@entity.manager', '@theme.manager', '@context.handler']
|
arguments: ['@entity.manager', '@theme.manager', '@context.handler']
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use Drupal\Core\Entity\EntityInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
use Drupal\Core\Entity\EntityTypeInterface;
|
||||||
use Drupal\Core\Executable\ExecutableManagerInterface;
|
use Drupal\Core\Executable\ExecutableManagerInterface;
|
||||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||||
|
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
|
||||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
@ -45,6 +46,13 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
|
||||||
*/
|
*/
|
||||||
protected $contextHandler;
|
protected $contextHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context manager service.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $contextRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -52,7 +60,8 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
|
||||||
return new static(
|
return new static(
|
||||||
$entity_type,
|
$entity_type,
|
||||||
$container->get('plugin.manager.condition'),
|
$container->get('plugin.manager.condition'),
|
||||||
$container->get('context.handler')
|
$container->get('context.handler'),
|
||||||
|
$container->get('context.repository')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,11 +74,14 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
|
||||||
* The ConditionManager for checking visibility of blocks.
|
* The ConditionManager for checking visibility of blocks.
|
||||||
* @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
|
* @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
|
||||||
* The ContextHandler for applying contexts to conditions properly.
|
* The ContextHandler for applying contexts to conditions properly.
|
||||||
|
* @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
|
||||||
|
* The lazy context repository service.
|
||||||
*/
|
*/
|
||||||
public function __construct(EntityTypeInterface $entity_type, ExecutableManagerInterface $manager, ContextHandlerInterface $context_handler) {
|
public function __construct(EntityTypeInterface $entity_type, ExecutableManagerInterface $manager, ContextHandlerInterface $context_handler, ContextRepositoryInterface $context_repository ) {
|
||||||
parent::__construct($entity_type);
|
parent::__construct($entity_type);
|
||||||
$this->manager = $manager;
|
$this->manager = $manager;
|
||||||
$this->contextHandler = $context_handler;
|
$this->contextHandler = $context_handler;
|
||||||
|
$this->contextRepository = $context_repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -87,12 +99,12 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
|
||||||
return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
|
return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$contexts = $entity->getContexts();
|
|
||||||
$conditions = [];
|
$conditions = [];
|
||||||
$missing_context = FALSE;
|
$missing_context = FALSE;
|
||||||
foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
|
foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
|
||||||
if ($condition instanceof ContextAwarePluginInterface) {
|
if ($condition instanceof ContextAwarePluginInterface) {
|
||||||
try {
|
try {
|
||||||
|
$contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
|
||||||
$this->contextHandler->applyContextMapping($condition, $contexts);
|
$this->contextHandler->applyContextMapping($condition, $contexts);
|
||||||
}
|
}
|
||||||
catch (ContextException $e) {
|
catch (ContextException $e) {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
namespace Drupal\block;
|
namespace Drupal\block;
|
||||||
|
|
||||||
use Drupal\block\Event\BlockContextEvent;
|
|
||||||
use Drupal\block\Event\BlockEvents;
|
|
||||||
use Drupal\Component\Utility\Html;
|
use Drupal\Component\Utility\Html;
|
||||||
use Drupal\Core\Entity\EntityForm;
|
use Drupal\Core\Entity\EntityForm;
|
||||||
use Drupal\Core\Entity\EntityManagerInterface;
|
use Drupal\Core\Entity\EntityManagerInterface;
|
||||||
|
|
@ -18,8 +16,8 @@ use Drupal\Core\Form\FormState;
|
||||||
use Drupal\Core\Form\FormStateInterface;
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
use Drupal\Core\Language\LanguageManagerInterface;
|
use Drupal\Core\Language\LanguageManagerInterface;
|
||||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||||
|
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides form for block instance forms.
|
* Provides form for block instance forms.
|
||||||
|
|
@ -68,6 +66,13 @@ class BlockForm extends EntityForm {
|
||||||
*/
|
*/
|
||||||
protected $themeHandler;
|
protected $themeHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context repository service.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $contextRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a BlockForm object.
|
* Constructs a BlockForm object.
|
||||||
*
|
*
|
||||||
|
|
@ -75,17 +80,17 @@ class BlockForm extends EntityForm {
|
||||||
* The entity manager.
|
* The entity manager.
|
||||||
* @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
|
* @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
|
||||||
* The ConditionManager for building the visibility UI.
|
* The ConditionManager for building the visibility UI.
|
||||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
* @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
|
||||||
* The EventDispatcher for gathering administrative contexts.
|
* The lazy context repository service.
|
||||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language
|
* @param \Drupal\Core\Language\LanguageManagerInterface $language
|
||||||
* The language manager.
|
* The language manager.
|
||||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||||
* The theme handler.
|
* The theme handler.
|
||||||
*/
|
*/
|
||||||
public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, EventDispatcherInterface $dispatcher, LanguageManagerInterface $language, ThemeHandlerInterface $theme_handler) {
|
public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, ContextRepositoryInterface $context_repository, LanguageManagerInterface $language, ThemeHandlerInterface $theme_handler) {
|
||||||
$this->storage = $entity_manager->getStorage('block');
|
$this->storage = $entity_manager->getStorage('block');
|
||||||
$this->manager = $manager;
|
$this->manager = $manager;
|
||||||
$this->dispatcher = $dispatcher;
|
$this->contextRepository = $context_repository;
|
||||||
$this->language = $language;
|
$this->language = $language;
|
||||||
$this->themeHandler = $theme_handler;
|
$this->themeHandler = $theme_handler;
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +102,7 @@ class BlockForm extends EntityForm {
|
||||||
return new static(
|
return new static(
|
||||||
$container->get('entity.manager'),
|
$container->get('entity.manager'),
|
||||||
$container->get('plugin.manager.condition'),
|
$container->get('plugin.manager.condition'),
|
||||||
$container->get('event_dispatcher'),
|
$container->get('context.repository'),
|
||||||
$container->get('language_manager'),
|
$container->get('language_manager'),
|
||||||
$container->get('theme_handler')
|
$container->get('theme_handler')
|
||||||
);
|
);
|
||||||
|
|
@ -117,7 +122,7 @@ class BlockForm extends EntityForm {
|
||||||
|
|
||||||
// Store the gathered contexts in the form state for other objects to use
|
// Store the gathered contexts in the form state for other objects to use
|
||||||
// during form building.
|
// during form building.
|
||||||
$form_state->setTemporaryValue('gathered_contexts', $this->dispatcher->dispatch(BlockEvents::ADMINISTRATIVE_CONTEXT, new BlockContextEvent())->getContexts());
|
$form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts());
|
||||||
|
|
||||||
$form['#tree'] = TRUE;
|
$form['#tree'] = TRUE;
|
||||||
$form['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
|
$form['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
|
||||||
|
|
|
||||||
|
|
@ -95,24 +95,6 @@ interface BlockInterface extends ConfigEntityInterface {
|
||||||
*/
|
*/
|
||||||
public function setVisibilityConfig($instance_id, array $configuration);
|
public function setVisibilityConfig($instance_id, array $configuration);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all available contexts.
|
|
||||||
*
|
|
||||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
|
||||||
* An array of set contexts, keyed by context name.
|
|
||||||
*/
|
|
||||||
public function getContexts();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the contexts that are available for use within the block entity.
|
|
||||||
*
|
|
||||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
|
||||||
* An array of contexts to set on the block.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setContexts(array $contexts);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the weight of this block (used for sorting).
|
* Returns the weight of this block (used for sorting).
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ class BlockRepository implements BlockRepositoryInterface {
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getVisibleBlocksPerRegion(array $contexts, array &$cacheable_metadata = []) {
|
public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
|
||||||
$active_theme = $this->themeManager->getActiveTheme();
|
$active_theme = $this->themeManager->getActiveTheme();
|
||||||
// Build an array of the region names in the right order.
|
// Build an array of the region names in the right order.
|
||||||
$empty = array_fill_keys($active_theme->getRegions(), array());
|
$empty = array_fill_keys($active_theme->getRegions(), array());
|
||||||
|
|
@ -58,7 +58,6 @@ class BlockRepository implements BlockRepositoryInterface {
|
||||||
$full = array();
|
$full = array();
|
||||||
foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) {
|
foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) {
|
||||||
/** @var \Drupal\block\BlockInterface $block */
|
/** @var \Drupal\block\BlockInterface $block */
|
||||||
$block->setContexts($contexts);
|
|
||||||
$access = $block->access('view', NULL, TRUE);
|
$access = $block->access('view', NULL, TRUE);
|
||||||
$region = $block->getRegion();
|
$region = $block->getRegion();
|
||||||
if (!isset($cacheable_metadata[$region])) {
|
if (!isset($cacheable_metadata[$region])) {
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ interface BlockRepositoryInterface {
|
||||||
/**
|
/**
|
||||||
* Returns an array of regions and their block entities.
|
* Returns an array of regions and their block entities.
|
||||||
*
|
*
|
||||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
|
||||||
* An array of contexts to set on the blocks.
|
|
||||||
* @param \Drupal\Core\Cache\CacheableMetadata[] $cacheable_metadata
|
* @param \Drupal\Core\Cache\CacheableMetadata[] $cacheable_metadata
|
||||||
* (optional) List of CacheableMetadata objects, keyed by region. This is
|
* (optional) List of CacheableMetadata objects, keyed by region. This is
|
||||||
* by reference and is used to pass this information back to the caller.
|
* by reference and is used to pass this information back to the caller.
|
||||||
|
|
@ -22,6 +20,6 @@ interface BlockRepositoryInterface {
|
||||||
* The array is first keyed by region machine name, with the values
|
* The array is first keyed by region machine name, with the values
|
||||||
* containing an array keyed by block ID, with block entities as the values.
|
* containing an array keyed by block ID, with block entities as the values.
|
||||||
*/
|
*/
|
||||||
public function getVisibleBlocksPerRegion(array $contexts, array &$cacheable_metadata = []);
|
public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -250,21 +250,6 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function setContexts(array $contexts) {
|
|
||||||
$this->contexts = $contexts;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getContexts() {
|
|
||||||
return $this->contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Contains \Drupal\block\Event\BlockContextEvent.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Drupal\block\Event;
|
|
||||||
|
|
||||||
use Drupal\Core\Plugin\Context\ContextInterface;
|
|
||||||
use Symfony\Component\EventDispatcher\Event;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event subscribers can add context to be used by the block and its conditions.
|
|
||||||
*
|
|
||||||
* @see \Drupal\block\Event\BlockEvents::ACTIVE_CONTEXT
|
|
||||||
* @see \Drupal\block\Event\BlockEvents::ADMINISTRATIVE_CONTEXT
|
|
||||||
*/
|
|
||||||
class BlockContextEvent extends Event {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The array of available contexts for blocks.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $contexts = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the context object for a given name.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* The name to store the context object under.
|
|
||||||
* @param \Drupal\Core\Plugin\Context\ContextInterface $context
|
|
||||||
* The context object to set.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setContext($name, ContextInterface $context) {
|
|
||||||
$this->contexts[$name] = $context;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the context objects.
|
|
||||||
*
|
|
||||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
|
||||||
* An array of contexts that have been provided.
|
|
||||||
*/
|
|
||||||
public function getContexts() {
|
|
||||||
return $this->contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Contains \Drupal\block\Event\BlockEvents.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Drupal\block\Event;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines events for the Block module.
|
|
||||||
*/
|
|
||||||
final class BlockEvents {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the event when gathering condition context for a block plugin.
|
|
||||||
*
|
|
||||||
* This event allows you to provide additional context that can be used by
|
|
||||||
* a condition plugin in order to determine the visibility of a block. The
|
|
||||||
* event listener method receives a \Drupal\block\Event\BlockContextEvent
|
|
||||||
* instance. Generally any new context is paired with a new condition plugin
|
|
||||||
* that interprets the provided context and allows the block system to
|
|
||||||
* determine whether or not the block should be displayed.
|
|
||||||
*
|
|
||||||
* @Event
|
|
||||||
*
|
|
||||||
* @see \Drupal\Core\Block\BlockBase::getConditionContexts()
|
|
||||||
* @see \Drupal\block\Event\BlockContextEvent
|
|
||||||
* @see \Drupal\block\EventSubscriber\NodeRouteContext::onBlockActiveContext()
|
|
||||||
* @see \Drupal\Core\Condition\ConditionInterface
|
|
||||||
*/
|
|
||||||
const ACTIVE_CONTEXT = 'block.active_context';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the event when gathering contexts for plugin configuration.
|
|
||||||
*
|
|
||||||
* This event allows you to provide information about your context to the
|
|
||||||
* administration UI without having to provide a value for the context. For
|
|
||||||
* example, during configuration there is no specific node to pass as context.
|
|
||||||
* However, we still need to inform the system that a context named 'node' is
|
|
||||||
* available and provide a definition so that blocks can be configured to use
|
|
||||||
* it.
|
|
||||||
*
|
|
||||||
* The event listener method receives a \Drupal\block\Event\BlockContextEvent
|
|
||||||
* instance.
|
|
||||||
*
|
|
||||||
* @Event
|
|
||||||
*
|
|
||||||
* @see \Drupal\block\BlockForm::form()
|
|
||||||
* @see \Drupal\block\Event\BlockContextEvent
|
|
||||||
* @see \Drupal\block\EventSubscriber\NodeRouteContext::onBlockAdministrativeContext()
|
|
||||||
*/
|
|
||||||
const ADMINISTRATIVE_CONTEXT = 'block.administrative_context';
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Contains \Drupal\block\EventSubscriber\BlockContextSubscriberBase.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Drupal\block\EventSubscriber;
|
|
||||||
|
|
||||||
use Drupal\block\Event\BlockContextEvent;
|
|
||||||
use Drupal\block\Event\BlockEvents;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a base class for block context subscribers.
|
|
||||||
*/
|
|
||||||
abstract class BlockContextSubscriberBase implements EventSubscriberInterface {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public static function getSubscribedEvents() {
|
|
||||||
$events[BlockEvents::ACTIVE_CONTEXT][] = 'onBlockActiveContext';
|
|
||||||
$events[BlockEvents::ADMINISTRATIVE_CONTEXT][] = 'onBlockAdministrativeContext';
|
|
||||||
return $events;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the available run-time contexts.
|
|
||||||
*
|
|
||||||
* For blocks to render correctly, all of the contexts that they require
|
|
||||||
* must be populated with values. So this method must set a value for each
|
|
||||||
* context that it adds. For example:
|
|
||||||
* @code
|
|
||||||
* // Determine a specific node to pass as context to blocks.
|
|
||||||
* $node = ...
|
|
||||||
*
|
|
||||||
* // Set that specific node as the value of the 'node' context.
|
|
||||||
* $context = new Context(new ContextDefinition('entity:node'));
|
|
||||||
* $context->setContextValue($node);
|
|
||||||
* $event->setContext('node.node', $context);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* @param \Drupal\block\Event\BlockContextEvent $event
|
|
||||||
* The Event to which to register available contexts.
|
|
||||||
*/
|
|
||||||
abstract public function onBlockActiveContext(BlockContextEvent $event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the available configuration-time contexts.
|
|
||||||
*
|
|
||||||
* When a block is being configured, the configuration UI must know which
|
|
||||||
* named contexts are potentially available, but does not care about the
|
|
||||||
* value, since the value can be different for each request, and might not
|
|
||||||
* be available at all during the configuration UI's request.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
* @code
|
|
||||||
* // During configuration, there is no specific node to pass as context.
|
|
||||||
* // However, inform the system that a context named 'node.node' is
|
|
||||||
* // available, and provide its definition, so that blocks can be
|
|
||||||
* // configured to use it. When the block is rendered, the value of this
|
|
||||||
* // context will be supplied by onBlockActiveContext().
|
|
||||||
* $context = new Context(new ContextDefinition('entity:node'));
|
|
||||||
* $event->setContext('node.node', $context);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* @param \Drupal\block\Event\BlockContextEvent $event
|
|
||||||
* The Event to which to register available contexts.
|
|
||||||
*
|
|
||||||
* @see static::onBlockActiveContext()
|
|
||||||
*/
|
|
||||||
abstract public function onBlockAdministrativeContext(BlockContextEvent $event);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -8,18 +8,14 @@
|
||||||
namespace Drupal\block\Plugin\DisplayVariant;
|
namespace Drupal\block\Plugin\DisplayVariant;
|
||||||
|
|
||||||
use Drupal\block\BlockRepositoryInterface;
|
use Drupal\block\BlockRepositoryInterface;
|
||||||
use Drupal\block\Event\BlockContextEvent;
|
|
||||||
use Drupal\block\Event\BlockEvents;
|
|
||||||
use Drupal\Core\Block\MainContentBlockPluginInterface;
|
use Drupal\Core\Block\MainContentBlockPluginInterface;
|
||||||
use Drupal\Core\Block\MessagesBlockPluginInterface;
|
use Drupal\Core\Block\MessagesBlockPluginInterface;
|
||||||
use Drupal\Core\Cache\CacheableMetadata;
|
use Drupal\Core\Cache\CacheableMetadata;
|
||||||
use Drupal\Core\Display\PageVariantInterface;
|
use Drupal\Core\Display\PageVariantInterface;
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
|
||||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||||
use Drupal\Core\Display\VariantBase;
|
use Drupal\Core\Display\VariantBase;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a page display variant that decorates the main content with blocks.
|
* Provides a page display variant that decorates the main content with blocks.
|
||||||
|
|
@ -80,16 +76,13 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
|
||||||
* The block repository.
|
* The block repository.
|
||||||
* @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
|
* @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
|
||||||
* The block view builder.
|
* The block view builder.
|
||||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
|
||||||
* The event dispatcher.
|
|
||||||
* @param string[] $block_list_cache_tags
|
* @param string[] $block_list_cache_tags
|
||||||
* The Block entity type list cache tags.
|
* The Block entity type list cache tags.
|
||||||
*/
|
*/
|
||||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, EventDispatcherInterface $dispatcher, array $block_list_cache_tags) {
|
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, array $block_list_cache_tags) {
|
||||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||||
$this->blockRepository = $block_repository;
|
$this->blockRepository = $block_repository;
|
||||||
$this->blockViewBuilder = $block_view_builder;
|
$this->blockViewBuilder = $block_view_builder;
|
||||||
$this->dispatcher = $dispatcher;
|
|
||||||
$this->blockListCacheTags = $block_list_cache_tags;
|
$this->blockListCacheTags = $block_list_cache_tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +96,6 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
|
||||||
$plugin_definition,
|
$plugin_definition,
|
||||||
$container->get('block.repository'),
|
$container->get('block.repository'),
|
||||||
$container->get('entity.manager')->getViewBuilder('block'),
|
$container->get('entity.manager')->getViewBuilder('block'),
|
||||||
$container->get('event_dispatcher'),
|
|
||||||
$container->get('entity.manager')->getDefinition('block')->getListCacheTags()
|
$container->get('entity.manager')->getDefinition('block')->getListCacheTags()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -129,10 +121,9 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
|
||||||
'tags' => $this->blockListCacheTags,
|
'tags' => $this->blockListCacheTags,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$contexts = $this->getActiveBlockContexts();
|
|
||||||
// Load all region content assigned via blocks.
|
// Load all region content assigned via blocks.
|
||||||
$cacheable_metadata_list = [];
|
$cacheable_metadata_list = [];
|
||||||
foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts, $cacheable_metadata_list) as $region => $blocks) {
|
foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata_list) as $region => $blocks) {
|
||||||
/** @var $blocks \Drupal\block\BlockInterface[] */
|
/** @var $blocks \Drupal\block\BlockInterface[] */
|
||||||
foreach ($blocks as $key => $block) {
|
foreach ($blocks as $key => $block) {
|
||||||
$block_plugin = $block->getPlugin();
|
$block_plugin = $block->getPlugin();
|
||||||
|
|
@ -188,14 +179,4 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
|
||||||
return $build;
|
return $build;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of context objects to set on the blocks.
|
|
||||||
*
|
|
||||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
|
||||||
* An array of contexts to set on the blocks.
|
|
||||||
*/
|
|
||||||
protected function getActiveBlockContexts() {
|
|
||||||
return $this->dispatcher->dispatch(BlockEvents::ACTIVE_CONTEXT, new BlockContextEvent())->getContexts();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ class BlockLanguageTest extends WebTestBase {
|
||||||
'langcodes' => array(
|
'langcodes' => array(
|
||||||
'fr' => 'fr',
|
'fr' => 'fr',
|
||||||
),
|
),
|
||||||
'context_mapping' => ['language' => 'language.language_interface'],
|
'context_mapping' => ['language' => '@language.current_language_context:language_interface'],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -142,7 +142,7 @@ class BlockLanguageTest extends WebTestBase {
|
||||||
// Enable a standard block and set visibility to French only.
|
// Enable a standard block and set visibility to French only.
|
||||||
$block_id = strtolower($this->randomMachineName(8));
|
$block_id = strtolower($this->randomMachineName(8));
|
||||||
$edit = [
|
$edit = [
|
||||||
'visibility[language][context_mapping][language]' => 'language.language_interface',
|
'visibility[language][context_mapping][language]' => '@language.current_language_context:language_interface',
|
||||||
'visibility[language][langcodes][fr]' => TRUE,
|
'visibility[language][langcodes][fr]' => TRUE,
|
||||||
'id' => $block_id,
|
'id' => $block_id,
|
||||||
'region' => 'sidebar_first',
|
'region' => 'sidebar_first',
|
||||||
|
|
@ -168,7 +168,7 @@ class BlockLanguageTest extends WebTestBase {
|
||||||
|
|
||||||
// Change visibility to now depend on content language for this block.
|
// Change visibility to now depend on content language for this block.
|
||||||
$edit = [
|
$edit = [
|
||||||
'visibility[language][context_mapping][language]' => 'language.language_content'
|
'visibility[language][context_mapping][language]' => '@language.current_language_context:language_content'
|
||||||
];
|
];
|
||||||
$this->drupalPostForm('admin/structure/block/manage/' . $block_id, $edit, t('Save block'));
|
$this->drupalPostForm('admin/structure/block/manage/' . $block_id, $edit, t('Save block'));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,6 @@ class BlockFormTest extends UnitTestCase {
|
||||||
*/
|
*/
|
||||||
protected $storage;
|
protected $storage;
|
||||||
|
|
||||||
/**
|
|
||||||
* The event dispatcher service.
|
|
||||||
*
|
|
||||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
|
|
||||||
*/
|
|
||||||
protected $dispatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The language manager service.
|
* The language manager service.
|
||||||
*
|
*
|
||||||
|
|
@ -59,6 +52,13 @@ class BlockFormTest extends UnitTestCase {
|
||||||
*/
|
*/
|
||||||
protected $entityManager;
|
protected $entityManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mocked context repository.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
protected $contextRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -67,7 +67,7 @@ class BlockFormTest extends UnitTestCase {
|
||||||
|
|
||||||
$this->conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
|
$this->conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
|
||||||
$this->language = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
|
$this->language = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
|
||||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
$this->contextRepository = $this->getMock('Drupal\Core\Plugin\Context\ContextRepositoryInterface');
|
||||||
|
|
||||||
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
|
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
|
||||||
$this->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
|
$this->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
|
||||||
|
|
@ -104,7 +104,7 @@ class BlockFormTest extends UnitTestCase {
|
||||||
->method('getQuery')
|
->method('getQuery')
|
||||||
->will($this->returnValue($query));
|
->will($this->returnValue($query));
|
||||||
|
|
||||||
$block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->dispatcher, $this->language, $this->themeHandler);
|
$block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->contextRepository, $this->language, $this->themeHandler);
|
||||||
|
|
||||||
// Ensure that the block with just one other instance gets the next available
|
// Ensure that the block with just one other instance gets the next available
|
||||||
// name suggestion.
|
// name suggestion.
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class BlockRepositoryTest extends UnitTestCase {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
|
$theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
|
||||||
$theme_manager->expects($this->once())
|
$theme_manager->expects($this->atLeastOnce())
|
||||||
->method('getActiveTheme')
|
->method('getActiveTheme')
|
||||||
->will($this->returnValue($active_theme));
|
->will($this->returnValue($active_theme));
|
||||||
|
|
||||||
|
|
@ -85,9 +85,6 @@ class BlockRepositoryTest extends UnitTestCase {
|
||||||
$blocks = [];
|
$blocks = [];
|
||||||
foreach ($blocks_config as $block_id => $block_config) {
|
foreach ($blocks_config as $block_id => $block_config) {
|
||||||
$block = $this->getMock('Drupal\block\BlockInterface');
|
$block = $this->getMock('Drupal\block\BlockInterface');
|
||||||
$block->expects($this->once())
|
|
||||||
->method('setContexts')
|
|
||||||
->willReturnSelf();
|
|
||||||
$block->expects($this->once())
|
$block->expects($this->once())
|
||||||
->method('access')
|
->method('access')
|
||||||
->will($this->returnValue($block_config[0]));
|
->will($this->returnValue($block_config[0]));
|
||||||
|
|
@ -102,7 +99,8 @@ class BlockRepositoryTest extends UnitTestCase {
|
||||||
->with(['theme' => $this->theme])
|
->with(['theme' => $this->theme])
|
||||||
->willReturn($blocks);
|
->willReturn($blocks);
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($this->blockRepository->getVisibleBlocksPerRegion([]) as $region => $resulting_blocks) {
|
$cacheable_metadata = [];
|
||||||
|
foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
|
||||||
$result[$region] = [];
|
$result[$region] = [];
|
||||||
foreach ($resulting_blocks as $plugin_id => $block) {
|
foreach ($resulting_blocks as $plugin_id => $block) {
|
||||||
$result[$region][] = $plugin_id;
|
$result[$region][] = $plugin_id;
|
||||||
|
|
@ -147,9 +145,6 @@ class BlockRepositoryTest extends UnitTestCase {
|
||||||
*/
|
*/
|
||||||
public function testGetVisibleBlocksPerRegionWithContext() {
|
public function testGetVisibleBlocksPerRegionWithContext() {
|
||||||
$block = $this->getMock('Drupal\block\BlockInterface');
|
$block = $this->getMock('Drupal\block\BlockInterface');
|
||||||
$block->expects($this->once())
|
|
||||||
->method('setContexts')
|
|
||||||
->willReturnSelf();
|
|
||||||
$block->expects($this->once())
|
$block->expects($this->once())
|
||||||
->method('access')
|
->method('access')
|
||||||
->willReturn(AccessResult::allowed()->addCacheTags(['config:block.block.block_id']));
|
->willReturn(AccessResult::allowed()->addCacheTags(['config:block.block.block_id']));
|
||||||
|
|
@ -158,14 +153,13 @@ class BlockRepositoryTest extends UnitTestCase {
|
||||||
->willReturn('top');
|
->willReturn('top');
|
||||||
$blocks['block_id'] = $block;
|
$blocks['block_id'] = $block;
|
||||||
|
|
||||||
$contexts = [];
|
|
||||||
$this->blockStorage->expects($this->once())
|
$this->blockStorage->expects($this->once())
|
||||||
->method('loadByProperties')
|
->method('loadByProperties')
|
||||||
->with(['theme' => $this->theme])
|
->with(['theme' => $this->theme])
|
||||||
->willReturn($blocks);
|
->willReturn($blocks);
|
||||||
$result = [];
|
$result = [];
|
||||||
$cacheable_metadata = [];
|
$cacheable_metadata = [];
|
||||||
foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts, $cacheable_metadata) as $region => $resulting_blocks) {
|
foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
|
||||||
$result[$region] = [];
|
$result[$region] = [];
|
||||||
foreach ($resulting_blocks as $plugin_id => $block) {
|
foreach ($resulting_blocks as $plugin_id => $block) {
|
||||||
$result[$region][] = $plugin_id;
|
$result[$region][] = $plugin_id;
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,6 @@ class BlockPageVariantTest extends UnitTestCase {
|
||||||
*/
|
*/
|
||||||
protected $blockViewBuilder;
|
protected $blockViewBuilder;
|
||||||
|
|
||||||
/**
|
|
||||||
* The event dispatcher.
|
|
||||||
*
|
|
||||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
|
|
||||||
*/
|
|
||||||
protected $dispatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugin context handler.
|
* The plugin context handler.
|
||||||
*
|
*
|
||||||
|
|
@ -71,12 +64,9 @@ class BlockPageVariantTest extends UnitTestCase {
|
||||||
|
|
||||||
$this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
|
$this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
|
||||||
$this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
|
$this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
|
||||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
|
||||||
$this->dispatcher->expects($this->any())
|
|
||||||
->method('dispatch')
|
|
||||||
->willReturnArgument(1);
|
|
||||||
return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\BlockPageVariant')
|
return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\BlockPageVariant')
|
||||||
->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, $this->dispatcher, ['config:block_list']))
|
->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, ['config:block_list']))
|
||||||
->setMethods(array('getRegionNames'))
|
->setMethods(array('getRegionNames'))
|
||||||
->getMock();
|
->getMock();
|
||||||
}
|
}
|
||||||
|
|
@ -217,23 +207,26 @@ class BlockPageVariantTest extends UnitTestCase {
|
||||||
$messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
|
$messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
|
||||||
foreach ($blocks_config as $block_id => $block_config) {
|
foreach ($blocks_config as $block_id => $block_config) {
|
||||||
$block = $this->getMock('Drupal\block\BlockInterface');
|
$block = $this->getMock('Drupal\block\BlockInterface');
|
||||||
|
$block->expects($this->any())
|
||||||
|
->method('getContexts')
|
||||||
|
->willReturn([]);
|
||||||
$block->expects($this->atLeastOnce())
|
$block->expects($this->atLeastOnce())
|
||||||
->method('getPlugin')
|
->method('getPlugin')
|
||||||
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin));
|
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin));
|
||||||
$blocks[$block_config[0]][$block_id] = $block;
|
$blocks[$block_config[0]][$block_id] = $block;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->blockViewBuilder->expects($this->exactly($visible_block_count))
|
$this->blockViewBuilder->expects($this->exactly($visible_block_count))
|
||||||
->method('view')
|
->method('view')
|
||||||
->will($this->returnValue(array()));
|
->will($this->returnValue(array()));
|
||||||
$this->blockRepository->expects($this->once())
|
$this->blockRepository->expects($this->once())
|
||||||
->method('getVisibleBlocksPerRegion')
|
->method('getVisibleBlocksPerRegion')
|
||||||
->willReturnCallback(function ($contexts, &$cacheable_metadata) use ($blocks) {
|
->willReturnCallback(function (&$cacheable_metadata) use ($blocks) {
|
||||||
$cacheable_metadata['top'] = (new CacheableMetadata())->addCacheTags(['route']);
|
$cacheable_metadata['top'] = (new CacheableMetadata())->addCacheTags(['route']);
|
||||||
return $blocks;
|
return $blocks;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->assertSame($expected_render_array, $display_variant->build());
|
$value = $display_variant->build();
|
||||||
|
$this->assertSame($expected_render_array, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -345,7 +345,8 @@ function comment_entity_storage_load($entities, $entity_type) {
|
||||||
if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\FieldableEntityInterface')) {
|
if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\FieldableEntityInterface')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!\Drupal::service('comment.manager')->getFields($entity_type)) {
|
// @todo Investigate in https://www.drupal.org/node/2527866 why we need that.
|
||||||
|
if (!\Drupal::hasService('comment.manager') || !\Drupal::service('comment.manager')->getFields($entity_type)) {
|
||||||
// Do not query database when entity has no comment fields.
|
// Do not query database when entity has no comment fields.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,3 +24,8 @@ services:
|
||||||
tags:
|
tags:
|
||||||
- { name: paramconverter }
|
- { name: paramconverter }
|
||||||
lazy: true
|
lazy: true
|
||||||
|
language.current_language_context:
|
||||||
|
class: Drupal\language\ContextProvider\CurrentLanguageContext
|
||||||
|
arguments: ['@language_manager']
|
||||||
|
tags:
|
||||||
|
- { name: 'context_provider' }
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,22 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Contains \Drupal\block\EventSubscriber\CurrentLanguageContext.
|
* Contains \Drupal\language\ContextProvider\CurrentLanguageContext.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Drupal\block\EventSubscriber;
|
namespace Drupal\language\ContextProvider;
|
||||||
|
|
||||||
use Drupal\block\Event\BlockContextEvent;
|
|
||||||
use Drupal\Core\Cache\CacheableMetadata;
|
use Drupal\Core\Cache\CacheableMetadata;
|
||||||
use Drupal\Core\Language\LanguageManagerInterface;
|
use Drupal\Core\Language\LanguageManagerInterface;
|
||||||
use Drupal\Core\Plugin\Context\Context;
|
use Drupal\Core\Plugin\Context\Context;
|
||||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||||
|
use Drupal\Core\Plugin\Context\ContextProviderInterface;
|
||||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current language as a context.
|
* Sets the current language as a context.
|
||||||
*/
|
*/
|
||||||
class CurrentLanguageContext extends BlockContextSubscriberBase {
|
class CurrentLanguageContext implements ContextProviderInterface {
|
||||||
|
|
||||||
use StringTranslationTrait;
|
use StringTranslationTrait;
|
||||||
|
|
||||||
|
|
@ -41,10 +41,20 @@ class CurrentLanguageContext extends BlockContextSubscriberBase {
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onBlockActiveContext(BlockContextEvent $event) {
|
public function getRuntimeContexts(array $unqualified_context_ids) {
|
||||||
// Add a context for each language type.
|
// Add a context for each language type.
|
||||||
$language_types = $this->languageManager->getLanguageTypes();
|
$language_types = $this->languageManager->getLanguageTypes();
|
||||||
$info = $this->languageManager->getDefinedLanguageTypesInfo();
|
$info = $this->languageManager->getDefinedLanguageTypesInfo();
|
||||||
|
|
||||||
|
if ($unqualified_context_ids) {
|
||||||
|
foreach ($unqualified_context_ids as $unqualified_context_id) {
|
||||||
|
if (array_search($unqualified_context_id, $language_types) === FALSE) {
|
||||||
|
unset($language_types[$unqualified_context_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [];
|
||||||
foreach ($language_types as $type_key) {
|
foreach ($language_types as $type_key) {
|
||||||
if (isset($info[$type_key]['name'])) {
|
if (isset($info[$type_key]['name'])) {
|
||||||
$context = new Context(new ContextDefinition('language', $info[$type_key]['name']));
|
$context = new Context(new ContextDefinition('language', $info[$type_key]['name']));
|
||||||
|
|
@ -54,16 +64,18 @@ class CurrentLanguageContext extends BlockContextSubscriberBase {
|
||||||
$cacheability->setCacheContexts(['languages:' . $type_key]);
|
$cacheability->setCacheContexts(['languages:' . $type_key]);
|
||||||
$context->addCacheableDependency($cacheability);
|
$context->addCacheableDependency($cacheability);
|
||||||
|
|
||||||
$event->setContext('language.' . $type_key, $context);
|
$result[$type_key] = $context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onBlockAdministrativeContext(BlockContextEvent $event) {
|
public function getAvailableContexts() {
|
||||||
$this->onBlockActiveContext($event);
|
return $this->getRuntimeContexts([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -45,3 +45,8 @@ services:
|
||||||
arguments: ['@current_user']
|
arguments: ['@current_user']
|
||||||
tags:
|
tags:
|
||||||
- { name: cache.context }
|
- { name: cache.context }
|
||||||
|
node.node_route_context:
|
||||||
|
class: Drupal\node\ContextProvider\NodeRouteContext
|
||||||
|
arguments: ['@current_route_match']
|
||||||
|
tags:
|
||||||
|
- { name: 'context_provider' }
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,22 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Contains \Drupal\block\EventSubscriber\NodeRouteContext.
|
* Contains \Drupal\node\ContextProvider\NodeRouteContext.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Drupal\block\EventSubscriber;
|
namespace Drupal\node\ContextProvider;
|
||||||
|
|
||||||
use Drupal\block\Event\BlockContextEvent;
|
|
||||||
use Drupal\Core\Cache\CacheableMetadata;
|
use Drupal\Core\Cache\CacheableMetadata;
|
||||||
use Drupal\Core\Plugin\Context\Context;
|
use Drupal\Core\Plugin\Context\Context;
|
||||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||||
|
use Drupal\Core\Plugin\Context\ContextProviderInterface;
|
||||||
use Drupal\Core\Routing\RouteMatchInterface;
|
use Drupal\Core\Routing\RouteMatchInterface;
|
||||||
use Drupal\node\Entity\Node;
|
use Drupal\node\Entity\Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current node as a context on node routes.
|
* Sets the current node as a context on node routes.
|
||||||
*/
|
*/
|
||||||
class NodeRouteContext extends BlockContextSubscriberBase {
|
class NodeRouteContext implements ContextProviderInterface {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The route match object.
|
* The route match object.
|
||||||
|
|
@ -39,13 +39,13 @@ class NodeRouteContext extends BlockContextSubscriberBase {
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onBlockActiveContext(BlockContextEvent $event) {
|
public function getRuntimeContexts(array $unqualified_context_ids) {
|
||||||
|
$result = [];
|
||||||
$context = new Context(new ContextDefinition('entity:node', NULL, FALSE));
|
$context = new Context(new ContextDefinition('entity:node', NULL, FALSE));
|
||||||
if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
|
if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
|
||||||
if ($node = $this->routeMatch->getParameter('node')) {
|
if ($node = $this->routeMatch->getParameter('node')) {
|
||||||
$context->setContextValue($node);
|
$context->setContextValue($node);
|
||||||
}
|
}
|
||||||
$event->setContext('node.node', $context);
|
|
||||||
}
|
}
|
||||||
elseif ($this->routeMatch->getRouteName() == 'node.add') {
|
elseif ($this->routeMatch->getRouteName() == 'node.add') {
|
||||||
$node_type = $this->routeMatch->getParameter('node_type');
|
$node_type = $this->routeMatch->getParameter('node_type');
|
||||||
|
|
@ -54,15 +54,17 @@ class NodeRouteContext extends BlockContextSubscriberBase {
|
||||||
$cacheability = new CacheableMetadata();
|
$cacheability = new CacheableMetadata();
|
||||||
$cacheability->setCacheContexts(['route']);
|
$cacheability->setCacheContexts(['route']);
|
||||||
$context->addCacheableDependency($cacheability);
|
$context->addCacheableDependency($cacheability);
|
||||||
$event->setContext('node.node', $context);
|
$result['node'] = $context;
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onBlockAdministrativeContext(BlockContextEvent $event) {
|
public function getAvailableContexts() {
|
||||||
$context = new Context(new ContextDefinition('entity:node'));
|
$context = new Context(new ContextDefinition('entity:node'));
|
||||||
$event->setContext('node.node', $context);
|
return ['node' => $context];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,23 +2,23 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Contains \Drupal\block\EventSubscriber\CurrentUserContext.
|
* Contains \Drupal\user\ContextProvider\CurrentUserContext.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Drupal\block\EventSubscriber;
|
namespace Drupal\user\ContextProvider;
|
||||||
|
|
||||||
use Drupal\block\Event\BlockContextEvent;
|
|
||||||
use Drupal\Core\Cache\CacheableMetadata;
|
use Drupal\Core\Cache\CacheableMetadata;
|
||||||
use Drupal\Core\Entity\EntityManagerInterface;
|
use Drupal\Core\Entity\EntityManagerInterface;
|
||||||
use Drupal\Core\Plugin\Context\Context;
|
use Drupal\Core\Plugin\Context\Context;
|
||||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||||
|
use Drupal\Core\Plugin\Context\ContextProviderInterface;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current user as a context.
|
* Sets the current user as a context.
|
||||||
*/
|
*/
|
||||||
class CurrentUserContext extends BlockContextSubscriberBase {
|
class CurrentUserContext implements ContextProviderInterface {
|
||||||
|
|
||||||
use StringTranslationTrait;
|
use StringTranslationTrait;
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ class CurrentUserContext extends BlockContextSubscriberBase {
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onBlockActiveContext(BlockContextEvent $event) {
|
public function getRuntimeContexts(array $unqualified_context_ids) {
|
||||||
$current_user = $this->userStorage->load($this->account->id());
|
$current_user = $this->userStorage->load($this->account->id());
|
||||||
|
|
||||||
$context = new Context(new ContextDefinition('entity:user', $this->t('Current user')));
|
$context = new Context(new ContextDefinition('entity:user', $this->t('Current user')));
|
||||||
|
|
@ -60,14 +60,19 @@ class CurrentUserContext extends BlockContextSubscriberBase {
|
||||||
$cacheability = new CacheableMetadata();
|
$cacheability = new CacheableMetadata();
|
||||||
$cacheability->setCacheContexts(['user']);
|
$cacheability->setCacheContexts(['user']);
|
||||||
$context->addCacheableDependency($cacheability);
|
$context->addCacheableDependency($cacheability);
|
||||||
$event->setContext('user.current_user', $context);
|
|
||||||
|
$result = [
|
||||||
|
'current_user' => $context,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function onBlockAdministrativeContext(BlockContextEvent $event) {
|
public function getAvailableContexts() {
|
||||||
$this->onBlockActiveContext($event);
|
return $this->getRuntimeContexts([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +61,11 @@ services:
|
||||||
user.permissions:
|
user.permissions:
|
||||||
class: Drupal\user\PermissionHandler
|
class: Drupal\user\PermissionHandler
|
||||||
arguments: ['@module_handler', '@string_translation', '@controller_resolver']
|
arguments: ['@module_handler', '@string_translation', '@controller_resolver']
|
||||||
|
user.current_user_context:
|
||||||
|
class: Drupal\user\ContextProvider\CurrentUserContext
|
||||||
|
arguments: ['@current_user', '@entity.manager']
|
||||||
|
tags:
|
||||||
|
- { name: 'context_provider' }
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
user.tempstore.expire: 604800
|
user.tempstore.expire: 604800
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Tests\Core\Plugin\Context\LazyContextRepositoryTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Tests\Core\Plugin\Context;
|
||||||
|
|
||||||
|
use Drupal\Core\Plugin\Context\Context;
|
||||||
|
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||||
|
use Drupal\Core\Plugin\Context\LazyContextRepository;
|
||||||
|
use Drupal\Tests\UnitTestCase;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @coversDefaultClass \Drupal\Core\Plugin\Context\LazyContextRepository
|
||||||
|
* @group context
|
||||||
|
*/
|
||||||
|
class LazyContextRepositoryTest extends UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container.
|
||||||
|
*
|
||||||
|
* @var \Symfony\Component\DependencyInjection\ContainerBuilder
|
||||||
|
*/
|
||||||
|
protected $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->container = new ContainerBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getRuntimeContexts
|
||||||
|
*/
|
||||||
|
public function testGetRuntimeContextsSingle() {
|
||||||
|
$contexts = $this->setupContextAndProvider('test_provider', ['test_context']);
|
||||||
|
|
||||||
|
$lazy_context_repository = new LazyContextRepository($this->container, ['test_provider']);
|
||||||
|
$run_time_contexts = $lazy_context_repository->getRuntimeContexts(['@test_provider:test_context']);
|
||||||
|
$this->assertEquals(['@test_provider:test_context' => $contexts[0]], $run_time_contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getRuntimeContexts
|
||||||
|
*/
|
||||||
|
public function testGetRuntimeMultipleContextsPerService() {
|
||||||
|
$contexts = $this->setupContextAndProvider('test_provider', ['test_context0', 'test_context1']);
|
||||||
|
|
||||||
|
$lazy_context_repository = new LazyContextRepository($this->container, ['test_provider']);
|
||||||
|
$run_time_contexts = $lazy_context_repository->getRuntimeContexts(['@test_provider:test_context0', '@test_provider:test_context1']);
|
||||||
|
$this->assertEquals(['@test_provider:test_context0' => $contexts[0], '@test_provider:test_context1' => $contexts[1]], $run_time_contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getRuntimeContexts
|
||||||
|
*/
|
||||||
|
public function testGetRuntimeMultipleContextProviders() {
|
||||||
|
$contexts0 = $this->setupContextAndProvider('test_provider', ['test_context0', 'test_context1'], ['test_context0']);
|
||||||
|
$contexts1 = $this->setupContextAndProvider('test_provider2', ['test1_context0', 'test1_context1'], ['test1_context0']);
|
||||||
|
|
||||||
|
$lazy_context_repository = new LazyContextRepository($this->container, ['test_provider']);
|
||||||
|
$run_time_contexts = $lazy_context_repository->getRuntimeContexts(['@test_provider:test_context0', '@test_provider2:test1_context0']);
|
||||||
|
$this->assertEquals(['@test_provider:test_context0' => $contexts0[0], '@test_provider2:test1_context0' => $contexts1[1]], $run_time_contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getRuntimeContexts
|
||||||
|
* @expectedException \InvalidArgumentException
|
||||||
|
* @expectedExceptionMessage You must provide the context IDs in the @{service_id}:{unqualified_context_id} format.
|
||||||
|
*/
|
||||||
|
public function testInvalidContextId() {
|
||||||
|
$lazy_context_repository = new LazyContextRepository($this->container, ['test_provider']);
|
||||||
|
$lazy_context_repository->getRuntimeContexts(['test_context', '@test_provider:test_context1']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getRuntimeContexts
|
||||||
|
*/
|
||||||
|
public function testGetRuntimeStaticCache() {
|
||||||
|
$context0 = new Context(new ContextDefinition('example'));
|
||||||
|
$context1 = new Context(new ContextDefinition('example'));
|
||||||
|
|
||||||
|
$context_provider = $this->prophesize('\Drupal\Core\Plugin\Context\ContextProviderInterface');
|
||||||
|
$context_provider->getRuntimeContexts(['test_context0', 'test_context1'])
|
||||||
|
->shouldBeCalledTimes(1)
|
||||||
|
->willReturn(['test_context0' => $context0, 'test_context1' => $context1]);
|
||||||
|
$context_provider = $context_provider->reveal();
|
||||||
|
$this->container->set('test_provider', $context_provider);
|
||||||
|
|
||||||
|
$lazy_context_repository = new LazyContextRepository($this->container, ['test_provider']);
|
||||||
|
$lazy_context_repository->getRuntimeContexts(['@test_provider:test_context0', '@test_provider:test_context1']);
|
||||||
|
$lazy_context_repository->getRuntimeContexts(['@test_provider:test_context0', '@test_provider:test_context1']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getAvailableContexts
|
||||||
|
*/
|
||||||
|
public function testGetAvailableContexts() {
|
||||||
|
$contexts0 = $this->setupContextAndProvider('test_provider0', ['test0_context0', 'test0_context1']);
|
||||||
|
$contexts1 = $this->setupContextAndProvider('test_provider1', ['test1_context0', 'test1_context1']);
|
||||||
|
|
||||||
|
$lazy_context_repository = new LazyContextRepository($this->container, ['test_provider0', 'test_provider1']);
|
||||||
|
$contexts = $lazy_context_repository->getAvailableContexts();
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'@test_provider0:test0_context0' => $contexts0[0],
|
||||||
|
'@test_provider0:test0_context1' => $contexts0[1],
|
||||||
|
'@test_provider1:test1_context0' => $contexts1[0],
|
||||||
|
'@test_provider1:test1_context1' => $contexts1[1],
|
||||||
|
], $contexts);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up contexts and context providers.
|
||||||
|
*
|
||||||
|
* @param string $service_id
|
||||||
|
* The service ID of the service provider.
|
||||||
|
* @param string[] $unqualified_context_ids
|
||||||
|
* An array of context slot names.
|
||||||
|
* @param string[] $expected_unqualified_context_ids
|
||||||
|
* The expected unqualified context IDs passed to getRuntimeContexts.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of set up contexts.
|
||||||
|
*/
|
||||||
|
protected function setupContextAndProvider($service_id, array $unqualified_context_ids, array $expected_unqualified_context_ids = []) {
|
||||||
|
$contexts = [];
|
||||||
|
for ($i = 0; $i < count($unqualified_context_ids); $i++) {
|
||||||
|
$contexts[] = new Context(new ContextDefinition('example'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$expected_unqualified_context_ids = $expected_unqualified_context_ids ?: $unqualified_context_ids;
|
||||||
|
|
||||||
|
$context_provider = $this->prophesize('\Drupal\Core\Plugin\Context\ContextProviderInterface');
|
||||||
|
$context_provider->getRuntimeContexts($expected_unqualified_context_ids)
|
||||||
|
->willReturn(array_combine($unqualified_context_ids, $contexts));
|
||||||
|
$context_provider->getAvailableContexts()
|
||||||
|
->willReturn(array_combine($unqualified_context_ids, $contexts));
|
||||||
|
$context_provider = $context_provider->reveal();
|
||||||
|
$this->container->set($service_id, $context_provider);
|
||||||
|
|
||||||
|
return $contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue