Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

The block visibility system leverages condition plugins. The use of this system was tightly coupled to the block plugin, but is now decoupled to allow for saner context injection and handling within core, and to allow contrib developer to implement their own block visibility approaches.

This affects Site Builders & Module Developers in the following ways.

Site Builders

Site builders will now have the opportunity to choose what context is used to satisfy their block visibility conditions when 2 or more contexts of the required type are available. In core this is only likely to be seen with the multiple language contexts.

Module Developers

While the BlockPluginInterface has actually changed, the methods that were moved off of it were unlikely to ever be changed by a module developer. This is especially true at this phase of contrib's development, so it is exceptionally unlikely to impact any BlockPlugin providers.

For BlockPlugin consumers, like Display Suite or Panels, this will remove the visibility coupling that previously existed and allow them to provide their own solution. This is especially important since Core's block visibility UI makes a number of assumptions that would not be safe for these modules.

Event Subscribers that subscribed to BlockEvents::CONDITION_CONTEXT will now want to subscribe to BlockEvents::ACTIVE_CONTEXT instead. In addition to this, they should provide an implementation of BlockEvents::ADMINISTRATIVE_CONTEXT in order to make administrative context definitions available during config time.

Before:

class NodeRouteContext extends BlockConditionContextSubscriberBase {

  /**
   * The route match object.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Constructs a new NodeRouteContext.
   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match object.
   */
  public function __construct(RouteMatchInterface $route_match) {
    $this->routeMatch = $route_match;
  }

  /**
   * {@inheritdoc}
   */
  protected function determineBlockContext() {
    if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
      $context = new Context(new ContextDefinition($route_contexts['node']['type']));
      if ($node = $this->routeMatch->getParameter('node')) {
        $context->setContextValue($node);
      }
      $this->addContext('node', $context);
    }
    elseif ($this->routeMatch->getRouteName() == 'node.add') {
      $node_type = $this->routeMatch->getParameter('node_type');
      $context = new Context(new ContextDefinition('entity:node'));
      $context->setContextValue(Node::create(array('type' => $node_type->id())));
      $this->addContext('node', $context);
    }
  }

}

After:

class NodeRouteContext extends BlockContextSubscriberBase {

  /**
   * The route match object.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Constructs a new NodeRouteContext.
   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match object.
   */
  public function __construct(RouteMatchInterface $route_match) {
    $this->routeMatch = $route_match;
  }

  /**
   * {@inheritdoc}
   */
  public function onBlockActiveContext(BlockContextEvent $event) {
    if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
      $context = new Context(new ContextDefinition($route_contexts['node']['type']));
      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');
      $context = new Context(new ContextDefinition('entity:node'));
      $context->setContextValue(Node::create(array('type' => $node_type->id())));
      $event->setContext('node.node', $context);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function onBlockAdministrativeContext(BlockContextEvent $event) {
    $context = new Context(new ContextDefinition('entity:node'));
    $event->setContext('node', $context);
  }

}
Impacts: 
Site builders, administrators, editors
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done