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
 | 
			
		||||
 *   instantiated. Services should implement \Drupal\Core\DestructableInterface
 | 
			
		||||
 *   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
 | 
			
		||||
 * can be discovered or queried in a compiler pass when the container is built,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -266,6 +266,9 @@ services:
 | 
			
		|||
  context.handler:
 | 
			
		||||
    class: Drupal\Core\Plugin\Context\ContextHandler
 | 
			
		||||
    arguments: ['@typed_data_manager']
 | 
			
		||||
  context.repository:
 | 
			
		||||
    class: Drupal\Core\Plugin\Context\LazyContextRepository
 | 
			
		||||
    arguments: ['@service_container']
 | 
			
		||||
  cron:
 | 
			
		||||
    class: Drupal\Core\Cron
 | 
			
		||||
    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\ListCacheBinsPass;
 | 
			
		||||
use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass;
 | 
			
		||||
use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
 | 
			
		||||
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
 | 
			
		||||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
 | 
			
		||||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +89,7 @@ class CoreServiceProvider implements ServiceProviderInterface  {
 | 
			
		|||
    // Add the compiler pass that will process the tagged services.
 | 
			
		||||
    $container->addCompilerPass(new ListCacheBinsPass());
 | 
			
		||||
    $container->addCompilerPass(new CacheContextsPass());
 | 
			
		||||
    $container->addCompilerPass(new ContextProvidersPass());
 | 
			
		||||
 | 
			
		||||
    // Register plugin managers.
 | 
			
		||||
    $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
 | 
			
		||||
    tags:
 | 
			
		||||
      - { 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:
 | 
			
		||||
    class: Drupal\block\BlockRepository
 | 
			
		||||
    arguments: ['@entity.manager', '@theme.manager', '@context.handler']
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ use Drupal\Core\Entity\EntityInterface;
 | 
			
		|||
use Drupal\Core\Entity\EntityTypeInterface;
 | 
			
		||||
use Drupal\Core\Executable\ExecutableManagerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
 | 
			
		||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
 | 
			
		||||
use Drupal\Core\Session\AccountInterface;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +46,13 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
 | 
			
		|||
   */
 | 
			
		||||
  protected $contextHandler;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The context manager service.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $contextRepository;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +60,8 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
 | 
			
		|||
    return new static(
 | 
			
		||||
      $entity_type,
 | 
			
		||||
      $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.
 | 
			
		||||
   * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
 | 
			
		||||
   *   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);
 | 
			
		||||
    $this->manager = $manager;
 | 
			
		||||
    $this->contextHandler = $context_handler;
 | 
			
		||||
    $this->contextRepository = $context_repository;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -87,12 +99,12 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
 | 
			
		|||
      return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $contexts = $entity->getContexts();
 | 
			
		||||
      $conditions = [];
 | 
			
		||||
      $missing_context = FALSE;
 | 
			
		||||
      foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
 | 
			
		||||
        if ($condition instanceof ContextAwarePluginInterface) {
 | 
			
		||||
          try {
 | 
			
		||||
            $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
 | 
			
		||||
            $this->contextHandler->applyContextMapping($condition, $contexts);
 | 
			
		||||
          }
 | 
			
		||||
          catch (ContextException $e) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,6 @@
 | 
			
		|||
 | 
			
		||||
namespace Drupal\block;
 | 
			
		||||
 | 
			
		||||
use Drupal\block\Event\BlockContextEvent;
 | 
			
		||||
use Drupal\block\Event\BlockEvents;
 | 
			
		||||
use Drupal\Component\Utility\Html;
 | 
			
		||||
use Drupal\Core\Entity\EntityForm;
 | 
			
		||||
use Drupal\Core\Entity\EntityManagerInterface;
 | 
			
		||||
| 
						 | 
				
			
			@ -18,8 +16,8 @@ use Drupal\Core\Form\FormState;
 | 
			
		|||
use Drupal\Core\Form\FormStateInterface;
 | 
			
		||||
use Drupal\Core\Language\LanguageManagerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provides form for block instance forms.
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +66,13 @@ class BlockForm extends EntityForm {
 | 
			
		|||
   */
 | 
			
		||||
  protected $themeHandler;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The context repository service.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $contextRepository;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Constructs a BlockForm object.
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -75,17 +80,17 @@ class BlockForm extends EntityForm {
 | 
			
		|||
   *   The entity manager.
 | 
			
		||||
   * @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
 | 
			
		||||
   *   The ConditionManager for building the visibility UI.
 | 
			
		||||
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
 | 
			
		||||
   *   The EventDispatcher for gathering administrative contexts.
 | 
			
		||||
   * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
 | 
			
		||||
   *   The lazy context repository service.
 | 
			
		||||
   * @param \Drupal\Core\Language\LanguageManagerInterface $language
 | 
			
		||||
   *   The language manager.
 | 
			
		||||
   * @param \Drupal\Core\Extension\ThemeHandlerInterface $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->manager = $manager;
 | 
			
		||||
    $this->dispatcher = $dispatcher;
 | 
			
		||||
    $this->contextRepository = $context_repository;
 | 
			
		||||
    $this->language = $language;
 | 
			
		||||
    $this->themeHandler = $theme_handler;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +102,7 @@ class BlockForm extends EntityForm {
 | 
			
		|||
    return new static(
 | 
			
		||||
      $container->get('entity.manager'),
 | 
			
		||||
      $container->get('plugin.manager.condition'),
 | 
			
		||||
      $container->get('event_dispatcher'),
 | 
			
		||||
      $container->get('context.repository'),
 | 
			
		||||
      $container->get('language_manager'),
 | 
			
		||||
      $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
 | 
			
		||||
    // 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['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,24 +95,6 @@ interface BlockInterface extends ConfigEntityInterface {
 | 
			
		|||
   */
 | 
			
		||||
  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).
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ class BlockRepository implements BlockRepositoryInterface {
 | 
			
		|||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function getVisibleBlocksPerRegion(array $contexts, array &$cacheable_metadata = []) {
 | 
			
		||||
  public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
 | 
			
		||||
    $active_theme = $this->themeManager->getActiveTheme();
 | 
			
		||||
    // Build an array of the region names in the right order.
 | 
			
		||||
    $empty = array_fill_keys($active_theme->getRegions(), array());
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,6 @@ class BlockRepository implements BlockRepositoryInterface {
 | 
			
		|||
    $full = array();
 | 
			
		||||
    foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) {
 | 
			
		||||
      /** @var \Drupal\block\BlockInterface $block */
 | 
			
		||||
      $block->setContexts($contexts);
 | 
			
		||||
      $access = $block->access('view', NULL, TRUE);
 | 
			
		||||
      $region = $block->getRegion();
 | 
			
		||||
      if (!isset($cacheable_metadata[$region])) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,6 @@ interface BlockRepositoryInterface {
 | 
			
		|||
  /**
 | 
			
		||||
   * 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
 | 
			
		||||
   *   (optional) List of CacheableMetadata objects, keyed by region. This is
 | 
			
		||||
   *   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
 | 
			
		||||
   *   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}
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
use Drupal\block\BlockRepositoryInterface;
 | 
			
		||||
use Drupal\block\Event\BlockContextEvent;
 | 
			
		||||
use Drupal\block\Event\BlockEvents;
 | 
			
		||||
use Drupal\Core\Block\MainContentBlockPluginInterface;
 | 
			
		||||
use Drupal\Core\Block\MessagesBlockPluginInterface;
 | 
			
		||||
use Drupal\Core\Cache\CacheableMetadata;
 | 
			
		||||
use Drupal\Core\Display\PageVariantInterface;
 | 
			
		||||
use Drupal\Core\Entity\EntityTypeInterface;
 | 
			
		||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
 | 
			
		||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 | 
			
		||||
use Drupal\Core\Display\VariantBase;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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.
 | 
			
		||||
   * @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
 | 
			
		||||
   *   The block view builder.
 | 
			
		||||
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
 | 
			
		||||
   *   The event dispatcher.
 | 
			
		||||
   * @param string[] $block_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);
 | 
			
		||||
    $this->blockRepository = $block_repository;
 | 
			
		||||
    $this->blockViewBuilder = $block_view_builder;
 | 
			
		||||
    $this->dispatcher = $dispatcher;
 | 
			
		||||
    $this->blockListCacheTags = $block_list_cache_tags;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +96,6 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
 | 
			
		|||
      $plugin_definition,
 | 
			
		||||
      $container->get('block.repository'),
 | 
			
		||||
      $container->get('entity.manager')->getViewBuilder('block'),
 | 
			
		||||
      $container->get('event_dispatcher'),
 | 
			
		||||
      $container->get('entity.manager')->getDefinition('block')->getListCacheTags()
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -129,10 +121,9 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
 | 
			
		|||
        'tags' => $this->blockListCacheTags,
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
    $contexts = $this->getActiveBlockContexts();
 | 
			
		||||
    // Load all region content assigned via blocks.
 | 
			
		||||
    $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[] */
 | 
			
		||||
      foreach ($blocks as $key => $block) {
 | 
			
		||||
        $block_plugin = $block->getPlugin();
 | 
			
		||||
| 
						 | 
				
			
			@ -188,14 +179,4 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
 | 
			
		|||
    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(
 | 
			
		||||
            '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.
 | 
			
		||||
    $block_id = strtolower($this->randomMachineName(8));
 | 
			
		||||
    $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,
 | 
			
		||||
      'id' => $block_id,
 | 
			
		||||
      'region' => 'sidebar_first',
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +168,7 @@ class BlockLanguageTest extends WebTestBase {
 | 
			
		|||
 | 
			
		||||
    // Change visibility to now depend on content language for this block.
 | 
			
		||||
    $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'));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,13 +30,6 @@ class BlockFormTest extends UnitTestCase {
 | 
			
		|||
   */
 | 
			
		||||
  protected $storage;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The event dispatcher service.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
 | 
			
		||||
   */
 | 
			
		||||
  protected $dispatcher;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The language manager service.
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +52,13 @@ class BlockFormTest extends UnitTestCase {
 | 
			
		|||
   */
 | 
			
		||||
  protected $entityManager;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The mocked context repository.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
 | 
			
		||||
   */
 | 
			
		||||
  protected $contextRepository;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ class BlockFormTest extends UnitTestCase {
 | 
			
		|||
 | 
			
		||||
    $this->conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
 | 
			
		||||
    $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->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +104,7 @@ class BlockFormTest extends UnitTestCase {
 | 
			
		|||
      ->method('getQuery')
 | 
			
		||||
      ->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
 | 
			
		||||
    // name suggestion.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ class BlockRepositoryTest extends UnitTestCase {
 | 
			
		|||
      ]);
 | 
			
		||||
 | 
			
		||||
    $theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
 | 
			
		||||
    $theme_manager->expects($this->once())
 | 
			
		||||
    $theme_manager->expects($this->atLeastOnce())
 | 
			
		||||
      ->method('getActiveTheme')
 | 
			
		||||
      ->will($this->returnValue($active_theme));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,9 +85,6 @@ class BlockRepositoryTest extends UnitTestCase {
 | 
			
		|||
    $blocks = [];
 | 
			
		||||
    foreach ($blocks_config as $block_id => $block_config) {
 | 
			
		||||
      $block = $this->getMock('Drupal\block\BlockInterface');
 | 
			
		||||
      $block->expects($this->once())
 | 
			
		||||
        ->method('setContexts')
 | 
			
		||||
        ->willReturnSelf();
 | 
			
		||||
      $block->expects($this->once())
 | 
			
		||||
        ->method('access')
 | 
			
		||||
        ->will($this->returnValue($block_config[0]));
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +99,8 @@ class BlockRepositoryTest extends UnitTestCase {
 | 
			
		|||
      ->with(['theme' => $this->theme])
 | 
			
		||||
      ->willReturn($blocks);
 | 
			
		||||
    $result = [];
 | 
			
		||||
    foreach ($this->blockRepository->getVisibleBlocksPerRegion([]) as $region => $resulting_blocks) {
 | 
			
		||||
    $cacheable_metadata = [];
 | 
			
		||||
    foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
 | 
			
		||||
      $result[$region] = [];
 | 
			
		||||
      foreach ($resulting_blocks as $plugin_id => $block) {
 | 
			
		||||
        $result[$region][] = $plugin_id;
 | 
			
		||||
| 
						 | 
				
			
			@ -147,9 +145,6 @@ class BlockRepositoryTest extends UnitTestCase {
 | 
			
		|||
   */
 | 
			
		||||
  public function testGetVisibleBlocksPerRegionWithContext() {
 | 
			
		||||
    $block = $this->getMock('Drupal\block\BlockInterface');
 | 
			
		||||
    $block->expects($this->once())
 | 
			
		||||
      ->method('setContexts')
 | 
			
		||||
      ->willReturnSelf();
 | 
			
		||||
    $block->expects($this->once())
 | 
			
		||||
      ->method('access')
 | 
			
		||||
      ->willReturn(AccessResult::allowed()->addCacheTags(['config:block.block.block_id']));
 | 
			
		||||
| 
						 | 
				
			
			@ -158,14 +153,13 @@ class BlockRepositoryTest extends UnitTestCase {
 | 
			
		|||
      ->willReturn('top');
 | 
			
		||||
    $blocks['block_id'] = $block;
 | 
			
		||||
 | 
			
		||||
    $contexts = [];
 | 
			
		||||
    $this->blockStorage->expects($this->once())
 | 
			
		||||
      ->method('loadByProperties')
 | 
			
		||||
      ->with(['theme' => $this->theme])
 | 
			
		||||
      ->willReturn($blocks);
 | 
			
		||||
    $result = [];
 | 
			
		||||
    $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] = [];
 | 
			
		||||
      foreach ($resulting_blocks as $plugin_id => $block) {
 | 
			
		||||
        $result[$region][] = $plugin_id;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,13 +31,6 @@ class BlockPageVariantTest extends UnitTestCase {
 | 
			
		|||
   */
 | 
			
		||||
  protected $blockViewBuilder;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The event dispatcher.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
 | 
			
		||||
   */
 | 
			
		||||
  protected $dispatcher;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The plugin context handler.
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -71,12 +64,9 @@ class BlockPageVariantTest extends UnitTestCase {
 | 
			
		|||
 | 
			
		||||
    $this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
 | 
			
		||||
    $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')
 | 
			
		||||
      ->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'))
 | 
			
		||||
      ->getMock();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -217,23 +207,26 @@ class BlockPageVariantTest extends UnitTestCase {
 | 
			
		|||
    $messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
 | 
			
		||||
    foreach ($blocks_config as $block_id => $block_config) {
 | 
			
		||||
      $block = $this->getMock('Drupal\block\BlockInterface');
 | 
			
		||||
      $block->expects($this->any())
 | 
			
		||||
        ->method('getContexts')
 | 
			
		||||
        ->willReturn([]);
 | 
			
		||||
      $block->expects($this->atLeastOnce())
 | 
			
		||||
        ->method('getPlugin')
 | 
			
		||||
        ->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin));
 | 
			
		||||
      $blocks[$block_config[0]][$block_id] = $block;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $this->blockViewBuilder->expects($this->exactly($visible_block_count))
 | 
			
		||||
      ->method('view')
 | 
			
		||||
      ->will($this->returnValue(array()));
 | 
			
		||||
    $this->blockRepository->expects($this->once())
 | 
			
		||||
      ->method('getVisibleBlocksPerRegion')
 | 
			
		||||
      ->willReturnCallback(function ($contexts, &$cacheable_metadata) use ($blocks) {
 | 
			
		||||
      ->willReturnCallback(function (&$cacheable_metadata) use ($blocks) {
 | 
			
		||||
        $cacheable_metadata['top'] = (new CacheableMetadata())->addCacheTags(['route']);
 | 
			
		||||
        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')) {
 | 
			
		||||
    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.
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,3 +24,8 @@ services:
 | 
			
		|||
    tags:
 | 
			
		||||
      - { name: paramconverter }
 | 
			
		||||
    lazy: true
 | 
			
		||||
  language.current_language_context:
 | 
			
		||||
    class: Drupal\language\ContextProvider\CurrentLanguageContext
 | 
			
		||||
    arguments: ['@language_manager']
 | 
			
		||||
    tags:
 | 
			
		||||
      - { name: 'context_provider' }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,22 +2,22 @@
 | 
			
		|||
 | 
			
		||||
/**
 | 
			
		||||
 * @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\Language\LanguageManagerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\Context\Context;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextDefinition;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextProviderInterface;
 | 
			
		||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the current language as a context.
 | 
			
		||||
 */
 | 
			
		||||
class CurrentLanguageContext extends BlockContextSubscriberBase {
 | 
			
		||||
class CurrentLanguageContext implements ContextProviderInterface {
 | 
			
		||||
 | 
			
		||||
  use StringTranslationTrait;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,10 +41,20 @@ class CurrentLanguageContext extends BlockContextSubscriberBase {
 | 
			
		|||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function onBlockActiveContext(BlockContextEvent $event) {
 | 
			
		||||
  public function getRuntimeContexts(array $unqualified_context_ids) {
 | 
			
		||||
    // Add a context for each language type.
 | 
			
		||||
    $language_types = $this->languageManager->getLanguageTypes();
 | 
			
		||||
    $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) {
 | 
			
		||||
      if (isset($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]);
 | 
			
		||||
        $context->addCacheableDependency($cacheability);
 | 
			
		||||
 | 
			
		||||
        $event->setContext('language.' . $type_key, $context);
 | 
			
		||||
        $result[$type_key] = $context;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function onBlockAdministrativeContext(BlockContextEvent $event) {
 | 
			
		||||
    $this->onBlockActiveContext($event);
 | 
			
		||||
  public function getAvailableContexts() {
 | 
			
		||||
    return $this->getRuntimeContexts([]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,3 +45,8 @@ services:
 | 
			
		|||
    arguments: ['@current_user']
 | 
			
		||||
    tags:
 | 
			
		||||
      - { 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
 | 
			
		||||
 * 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\Plugin\Context\Context;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextDefinition;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextProviderInterface;
 | 
			
		||||
use Drupal\Core\Routing\RouteMatchInterface;
 | 
			
		||||
use Drupal\node\Entity\Node;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the current node as a context on node routes.
 | 
			
		||||
 */
 | 
			
		||||
class NodeRouteContext extends BlockContextSubscriberBase {
 | 
			
		||||
class NodeRouteContext implements ContextProviderInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The route match object.
 | 
			
		||||
| 
						 | 
				
			
			@ -39,13 +39,13 @@ class NodeRouteContext extends BlockContextSubscriberBase {
 | 
			
		|||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function onBlockActiveContext(BlockContextEvent $event) {
 | 
			
		||||
  public function getRuntimeContexts(array $unqualified_context_ids) {
 | 
			
		||||
    $result = [];
 | 
			
		||||
    $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 ($node = $this->routeMatch->getParameter('node')) {
 | 
			
		||||
        $context->setContextValue($node);
 | 
			
		||||
      }
 | 
			
		||||
      $event->setContext('node.node', $context);
 | 
			
		||||
    }
 | 
			
		||||
    elseif ($this->routeMatch->getRouteName() == 'node.add') {
 | 
			
		||||
      $node_type = $this->routeMatch->getParameter('node_type');
 | 
			
		||||
| 
						 | 
				
			
			@ -54,15 +54,17 @@ class NodeRouteContext extends BlockContextSubscriberBase {
 | 
			
		|||
    $cacheability = new CacheableMetadata();
 | 
			
		||||
    $cacheability->setCacheContexts(['route']);
 | 
			
		||||
    $context->addCacheableDependency($cacheability);
 | 
			
		||||
    $event->setContext('node.node', $context);
 | 
			
		||||
    $result['node'] = $context;
 | 
			
		||||
 | 
			
		||||
    return $result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function onBlockAdministrativeContext(BlockContextEvent $event) {
 | 
			
		||||
  public function getAvailableContexts() {
 | 
			
		||||
    $context = new Context(new ContextDefinition('entity:node'));
 | 
			
		||||
    $event->setContext('node.node', $context);
 | 
			
		||||
    return ['node' => $context];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,23 +2,23 @@
 | 
			
		|||
 | 
			
		||||
/**
 | 
			
		||||
 * @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\Entity\EntityManagerInterface;
 | 
			
		||||
use Drupal\Core\Plugin\Context\Context;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextDefinition;
 | 
			
		||||
use Drupal\Core\Plugin\Context\ContextProviderInterface;
 | 
			
		||||
use Drupal\Core\Session\AccountInterface;
 | 
			
		||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the current user as a context.
 | 
			
		||||
 */
 | 
			
		||||
class CurrentUserContext extends BlockContextSubscriberBase {
 | 
			
		||||
class CurrentUserContext implements ContextProviderInterface {
 | 
			
		||||
 | 
			
		||||
  use StringTranslationTrait;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ class CurrentUserContext extends BlockContextSubscriberBase {
 | 
			
		|||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function onBlockActiveContext(BlockContextEvent $event) {
 | 
			
		||||
  public function getRuntimeContexts(array $unqualified_context_ids) {
 | 
			
		||||
    $current_user = $this->userStorage->load($this->account->id());
 | 
			
		||||
 | 
			
		||||
    $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')));
 | 
			
		||||
| 
						 | 
				
			
			@ -60,14 +60,19 @@ class CurrentUserContext extends BlockContextSubscriberBase {
 | 
			
		|||
    $cacheability = new CacheableMetadata();
 | 
			
		||||
    $cacheability->setCacheContexts(['user']);
 | 
			
		||||
    $context->addCacheableDependency($cacheability);
 | 
			
		||||
    $event->setContext('user.current_user', $context);
 | 
			
		||||
 | 
			
		||||
    $result = [
 | 
			
		||||
      'current_user' => $context,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return $result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function onBlockAdministrativeContext(BlockContextEvent $event) {
 | 
			
		||||
    $this->onBlockActiveContext($event);
 | 
			
		||||
  public function getAvailableContexts() {
 | 
			
		||||
    return $this->getRuntimeContexts([]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +61,11 @@ services:
 | 
			
		|||
  user.permissions:
 | 
			
		||||
    class: Drupal\user\PermissionHandler
 | 
			
		||||
    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:
 | 
			
		||||
  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