From 1228438e65de97bfe38291a79799e1e42dd9a0e4 Mon Sep 17 00:00:00 2001 From: webchick <drupal@webchick.net> Date: Tue, 17 Jun 2014 10:53:40 -0700 Subject: [PATCH] Issue #2278541 by tim.plunkett, kim.pepper, Bojhan, EclipseGc: Refactor block visibility to use condition plugins. --- core/config/schema/core.data_types.schema.yml | 14 ++ .../ConditionAccessResolverTrait.php | 53 ++++++ .../Core/Condition/ConditionPluginBag.php | 76 ++++++++ .../Core/Plugin/Context/ContextHandler.php | 7 + core/modules/block/block.module | 30 +-- core/modules/block/block.services.yml | 15 ++ .../block/config/schema/block.schema.yml | 34 ---- core/modules/block/js/block.js | 6 +- .../block/src/BlockAccessController.php | 99 +--------- core/modules/block/src/BlockBase.php | 180 +++++++++++++++++- core/modules/block/src/BlockForm.php | 151 +-------------- core/modules/block/src/BlockInterface.php | 8 + .../block/src/BlockPluginInterface.php | 32 ++++ core/modules/block/src/Entity/Block.php | 14 +- .../src/Event/BlockConditionContextEvent.php | 39 ++++ core/modules/block/src/Event/BlockEvents.php | 23 +++ .../BlockConditionContextSubscriberBase.php | 61 ++++++ .../CurrentLanguageContext.php | 50 +++++ .../EventSubscriber/CurrentUserContext.php | 63 ++++++ .../src/EventSubscriber/NodeRouteContext.php | 57 ++++++ .../block/src/Tests/BlockInterfaceTest.php | 7 +- .../block/src/Tests/BlockLanguageTest.php | 8 +- .../block/src/Tests/BlockStorageUnitTest.php | 2 +- core/modules/block/src/Tests/BlockTest.php | 10 +- .../config/install/block.block.test_block.yml | 9 +- .../modules/block/tests/src/BlockBaseTest.php | 64 ++++++- .../src/Plugin/Condition/Language.php | 3 +- .../install/migrate.migration.d6_block.yml | 6 +- .../migrate/process/d6/BlockSettings.php | 3 +- .../src/Tests/d6/MigrateBlockTest.php | 67 +++---- core/modules/node/node.module | 65 ------- .../node/src/Plugin/Condition/NodeType.php | 3 +- .../src/Tests/NodeBlockFunctionalTest.php | 6 +- .../search/src/Tests/SearchBlockTest.php | 6 +- core/modules/simpletest/src/WebTestBase.php | 5 +- .../Cache/PageCacheTagsIntegrationTest.php | 3 +- .../user/src/Plugin/Condition/UserRole.php | 3 +- .../Tests/Plugin/BlockDependenciesTest.php | 5 +- .../tests/src/Plugin/Block/ViewsBlockTest.php | 16 ++ .../src/Tests/OverrideDisplaysTest.php | 4 +- .../install/block.block.stark_admin.yml | 9 +- .../install/block.block.stark_login.yml | 9 +- .../install/block.block.stark_tools.yml | 9 +- .../block.block.bartik_breadcrumbs.yml | 12 +- .../install/block.block.bartik_content.yml | 12 +- .../install/block.block.bartik_footer.yml | 12 +- .../install/block.block.bartik_help.yml | 12 +- .../install/block.block.bartik_login.yml | 12 +- .../install/block.block.bartik_powered.yml | 12 +- .../install/block.block.bartik_search.yml | 12 +- .../install/block.block.bartik_tools.yml | 12 +- .../install/block.block.seven_breadcrumbs.yml | 12 +- .../install/block.block.seven_content.yml | 12 +- .../config/install/block.block.seven_help.yml | 12 +- .../install/block.block.seven_login.yml | 12 +- .../ConditionAccessResolverTraitTest.php | 107 +++++++++++ .../Tests/Core/Plugin/ContextHandlerTest.php | 2 +- 57 files changed, 988 insertions(+), 599 deletions(-) create mode 100644 core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php create mode 100644 core/lib/Drupal/Core/Condition/ConditionPluginBag.php create mode 100644 core/modules/block/src/Event/BlockConditionContextEvent.php create mode 100644 core/modules/block/src/Event/BlockEvents.php create mode 100644 core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php create mode 100644 core/modules/block/src/EventSubscriber/CurrentLanguageContext.php create mode 100644 core/modules/block/src/EventSubscriber/CurrentUserContext.php create mode 100644 core/modules/block/src/EventSubscriber/NodeRouteContext.php create mode 100644 core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml index 349835d0fd0c..7c0b05d362fb 100644 --- a/core/config/schema/core.data_types.schema.yml +++ b/core/config/schema/core.data_types.schema.yml @@ -283,6 +283,12 @@ block_settings: view_mode: type: string label: 'View mode' + visibility: + type: sequence + label: 'Visibility Conditions' + sequence: + - type: condition.plugin.[id] + label: 'Visibility Condition' provider: type: string label: 'Provider' @@ -297,3 +303,11 @@ condition.plugin: negate: type: boolean label: 'Negate' + uuid: + type: string + label: 'UUID' + context_mapping: + type: sequence + label: 'Context assignments' + sequence: + - type: string diff --git a/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php b/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php new file mode 100644 index 000000000000..35587ebb4507 --- /dev/null +++ b/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php @@ -0,0 +1,53 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Condition\ConditionAccessResolverTrait. + */ + +namespace Drupal\Core\Condition; + +use Drupal\Component\Plugin\Exception\PluginException; + +/** + * Resolves a set of conditions. + */ +trait ConditionAccessResolverTrait { + + /** + * Resolves the given conditions based on the condition logic ('and'/'or'). + * + * @param \Drupal\Core\Condition\ConditionInterface[] $conditions + * A set of conditions. + * @param string $condition_logic + * The logic used to compute access, either 'and' or 'or'. + * + * @return bool + * Whether these conditions grant or deny access. + */ + protected function resolveConditions($conditions, $condition_logic) { + foreach ($conditions as $condition) { + try { + $pass = $condition->execute(); + } + catch (PluginException $e) { + // If a condition is missing context, consider that a fail. + $pass = FALSE; + } + + // If a condition fails and all conditions were needed, deny access. + if (!$pass && $condition_logic == 'and') { + return FALSE; + } + // If a condition passes and only one condition was needed, grant access. + elseif ($pass && $condition_logic == 'or') { + return TRUE; + } + } + + // Return TRUE if logic was 'and', meaning all rules passed. + // Return FALSE if logic was 'or', meaning no rule passed. + return $condition_logic == 'and'; + } + +} diff --git a/core/lib/Drupal/Core/Condition/ConditionPluginBag.php b/core/lib/Drupal/Core/Condition/ConditionPluginBag.php new file mode 100644 index 000000000000..394bebf8b2f9 --- /dev/null +++ b/core/lib/Drupal/Core/Condition/ConditionPluginBag.php @@ -0,0 +1,76 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Condition\ConditionPluginBag. + */ + +namespace Drupal\Core\Condition; + +use Drupal\Component\Plugin\Context\ContextInterface; +use Drupal\Core\Plugin\DefaultPluginBag; + +/** + * Provides a collection of condition plugins. + */ +class ConditionPluginBag extends DefaultPluginBag { + + /** + * An array of collected contexts for conditions. + * + * @var \Drupal\Component\Plugin\Context\ContextInterface[] + */ + protected $conditionContexts = array(); + + /** + * {@inheritdoc} + * + * @return \Drupal\Core\Condition\ConditionInterface + */ + public function &get($instance_id) { + return parent::get($instance_id); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + $configuration = parent::getConfiguration(); + // Remove configuration if it matches the defaults. + foreach ($configuration as $instance_id => $instance_config) { + $default_config = array(); + $default_config['id'] = $instance_id; + $default_config += $this->get($instance_id)->defaultConfiguration(); + if ($default_config === $instance_config) { + unset($configuration[$instance_id]); + } + } + return $configuration; + } + + /** + * Sets the condition context for a given name. + * + * @param string $name + * The name of the context. + * @param \Drupal\Component\Plugin\Context\ContextInterface $context + * The context to add. + * + * @return $this + */ + public function addContext($name, ContextInterface $context) { + $this->conditionContexts[$name] = $context; + return $this; + } + + /** + * Gets the values for all defined contexts. + * + * @return \Drupal\Component\Plugin\Context\ContextInterface[] + * An array of set contexts, keyed by context name. + */ + public function getConditionContexts() { + return $this->conditionContexts; + } + +} diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php index 68fbda3eae64..672c61366a5f 100644 --- a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Plugin\Context; +use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Component\Plugin\Context\ContextInterface; use Drupal\Component\Plugin\ContextAwarePluginInterface; use Drupal\Component\Plugin\Exception\ContextException; @@ -122,6 +123,12 @@ public function getMatchingContexts(array $contexts, DataDefinitionInterface $de * {@inheritdoc} */ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = array()) { + if ($plugin instanceof ConfigurablePluginInterface) { + $configuration = $plugin->getConfiguration(); + if (isset($configuration['context_mapping'])) { + $mappings += array_flip($configuration['context_mapping']); + } + } $plugin_contexts = $plugin->getContextDefinitions(); // Loop through each context and set it on the plugin if it matches one of // the contexts expected by the plugin. diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 8c40c497e666..13121ef9acff 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -11,22 +11,6 @@ use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; -/** - * Shows this block on every page except the listed pages. - */ -const BLOCK_VISIBILITY_NOTLISTED = 0; - -/** - * Shows this block on only the listed pages. - */ -const BLOCK_VISIBILITY_LISTED = 1; - -/** - * Shows this block if the associated PHP code returns TRUE. - */ -const BLOCK_VISIBILITY_PHP = 2; - - /** * Implements hook_help(). */ @@ -398,10 +382,11 @@ function template_preprocess_block(&$variables) { */ function block_user_role_delete($role) { foreach (entity_load_multiple('block') as $block) { - $visibility = $block->get('visibility'); - if (isset($visibility['roles']['roles'][$role->id()])) { - unset($visibility['roles']['roles'][$role->id()]); - $block->set('visibility', $visibility); + /** @var $block \Drupal\block\BlockInterface */ + $visibility = $block->getVisibility(); + if (isset($visibility['user_role']['roles'][$role->id()])) { + unset($visibility['user_role']['roles'][$role->id()]); + $block->getPlugin()->setVisibilityConfig('user_role', $visibility['user_role']); $block->save(); } } @@ -428,10 +413,11 @@ function block_menu_delete(Menu $menu) { function block_language_entity_delete(Language $language) { // Remove the block visibility settings for the deleted language. foreach (entity_load_multiple('block') as $block) { - $visibility = $block->get('visibility'); + /** @var $block \Drupal\block\BlockInterface */ + $visibility = $block->getVisibility(); if (isset($visibility['language']['langcodes'][$language->id()])) { unset($visibility['language']['langcodes'][$language->id()]); - $block->set('visibility', $visibility); + $block->getPlugin()->setVisibilityConfig('language', $visibility['language']); $block->save(); } } diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index bacc48e2d4c8..5373645739f6 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -6,3 +6,18 @@ services: class: Drupal\block\Theme\AdminDemoNegotiator tags: - { name: theme_negotiator, priority: 1000 } + 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: ['@request_stack'] + tags: + - { name: 'event_subscriber' } diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml index fdf50f555afc..c142f898f757 100644 --- a/core/modules/block/config/schema/block.schema.yml +++ b/core/modules/block/config/schema/block.schema.yml @@ -19,40 +19,6 @@ block.block.*: provider: type: string label: 'Provider' - visibility: - type: mapping - label: 'Visibility settings' - mapping: - path: - type: mapping - label: 'Pages' - mapping: - visibility: - type: integer - label: 'Visibility' - pages: - type: string - label: 'Show block on specific pages' - role: - type: mapping - label: 'Roles' - mapping: - roles: - type: sequence - label: 'Show block for specific roles' - sequence: - - type: string - label: 'Role' - node_type: - type: mapping - label: 'Content types' - mapping: - types: - type: sequence - label: 'Show block for specific content types' - sequence: - - type: string - label: 'Node type' plugin: type: string label: 'Plugin' diff --git a/core/modules/block/js/block.js b/core/modules/block/js/block.js index 302c3151c730..3affe12afb15 100644 --- a/core/modules/block/js/block.js +++ b/core/modules/block/js/block.js @@ -26,10 +26,10 @@ return vals.join(', '); } - $('#edit-visibility-node-type, #edit-visibility-language, #edit-visibility-role').drupalSetSummary(checkboxesSummary); + $('#edit-settings-visibility-node-type, #edit-settings-visibility-language, #edit-settings-visibility-user-role').drupalSetSummary(checkboxesSummary); - $('#edit-visibility-path').drupalSetSummary(function (context) { - var $pages = $(context).find('textarea[name="visibility[path][pages]"]'); + $('#edit-settings-visibility-request-path').drupalSetSummary(function (context) { + var $pages = $(context).find('textarea[name="settings[visibility][request_path][pages]"]'); if (!$pages.val()) { return Drupal.t('Not restricted'); } diff --git a/core/modules/block/src/BlockAccessController.php b/core/modules/block/src/BlockAccessController.php index 3bd9fb3d4d45..0089470968d4 100644 --- a/core/modules/block/src/BlockAccessController.php +++ b/core/modules/block/src/BlockAccessController.php @@ -8,53 +8,19 @@ namespace Drupal\block; use Drupal\Core\Entity\EntityAccessController; -use Drupal\Core\Entity\EntityControllerInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\Core\Path\AliasManagerInterface; -use Drupal\Component\Utility\Unicode; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a Block access controller. */ -class BlockAccessController extends EntityAccessController implements EntityControllerInterface { - - /** - * The node grant storage. - * - * @var \Drupal\Core\Path\AliasManagerInterface - */ - protected $aliasManager; - - /** - * Constructs a BlockAccessController object. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * The entity type definition. - * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager - * The alias manager. - */ - public function __construct(EntityTypeInterface $entity_type, AliasManagerInterface $alias_manager) { - parent::__construct($entity_type); - $this->aliasManager = $alias_manager; - } - - /** - * {@inheritdoc} - */ - public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { - return new static( - $entity_type, - $container->get('path.alias_manager') - ); - } +class BlockAccessController extends EntityAccessController { /** * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) { + /** @var $entity \Drupal\block\BlockInterface */ if ($operation != 'view') { return parent::checkAccess($entity, $operation, $langcode, $account); } @@ -64,65 +30,8 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A return FALSE; } - // User role access handling. - // If a block has no roles associated, it is displayed for every role. - // For blocks with roles associated, if none of the user's roles matches - // the settings from this block, access is denied. - $visibility = $entity->get('visibility'); - if (!empty($visibility['role']['roles']) && !array_intersect(array_filter($visibility['role']['roles']), $account->getRoles())) { - // No match. - return FALSE; - } - - // Page path handling. - // Limited visibility blocks must list at least one page. - if (!empty($visibility['path']['visibility']) && $visibility['path']['visibility'] == BLOCK_VISIBILITY_LISTED && empty($visibility['path']['pages'])) { - return FALSE; - } - - // Match path if necessary. - if (!empty($visibility['path']['pages'])) { - // Assume there are no matches until one is found. - $page_match = FALSE; - - // Convert path to lowercase. This allows comparison of the same path - // with different case. Ex: /Page, /page, /PAGE. - $pages = drupal_strtolower($visibility['path']['pages']); - if ($visibility['path']['visibility'] < BLOCK_VISIBILITY_PHP) { - // Compare the lowercase path alias (if any) and internal path. - $path = current_path(); - $path_alias = Unicode::strtolower($this->aliasManager->getAliasByPath($path)); - $page_match = drupal_match_path($path_alias, $pages) || (($path != $path_alias) && drupal_match_path($path, $pages)); - // When $block->visibility has a value of 0 - // (BLOCK_VISIBILITY_NOTLISTED), the block is displayed on all pages - // except those listed in $block->pages. When set to 1 - // (BLOCK_VISIBILITY_LISTED), it is displayed only on those pages - // listed in $block->pages. - $page_match = !($visibility['path']['visibility'] xor $page_match); - } - - // If there are page visibility restrictions and this page does not - // match, deny access. - if (!$page_match) { - return FALSE; - } - } - - // Language visibility settings. - if (!empty($visibility['language']['langcodes']) && array_filter($visibility['language']['langcodes'])) { - if (empty($visibility['language']['langcodes'][\Drupal::languageManager()->getCurrentLanguage($visibility['language']['language_type'])->id])) { - return FALSE; - } - } - - // If the plugin denies access, then deny access. Apply plugin access checks - // last, because it's almost certainly cheaper to first apply Block's own - // visibility checks. - if (!$entity->getPlugin()->access($account)) { - return FALSE; - } - - return TRUE; + // Delegate to the plugin. + return $entity->getPlugin()->access($account); } } diff --git a/core/modules/block/src/BlockBase.php b/core/modules/block/src/BlockBase.php index 994b12eee2c2..258d23d7d131 100644 --- a/core/modules/block/src/BlockBase.php +++ b/core/modules/block/src/BlockBase.php @@ -7,6 +7,11 @@ namespace Drupal\block; +use Drupal\block\Event\BlockConditionContextEvent; +use Drupal\block\Event\BlockEvents; +use Drupal\Component\Plugin\ContextAwarePluginInterface; +use Drupal\Core\Condition\ConditionAccessResolverTrait; +use Drupal\Core\Condition\ConditionPluginBag; use Drupal\Core\Plugin\ContextAwarePluginBase; use Drupal\block\BlockInterface; use Drupal\Component\Utility\Unicode; @@ -27,6 +32,22 @@ */ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface { + use ConditionAccessResolverTrait; + + /** + * The condition plugin bag. + * + * @var \Drupal\Core\Condition\ConditionPluginBag + */ + protected $conditionBag; + + /** + * The condition plugin manager. + * + * @var \Drupal\Core\Executable\ExecutableManagerInterface + */ + protected $conditionPluginManager; + /** * {@inheritdoc} */ @@ -54,7 +75,9 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition * {@inheritdoc} */ public function getConfiguration() { - return $this->configuration; + return array( + 'visibility' => $this->getVisibilityConditions()->getConfiguration(), + ) + $this->configuration; } /** @@ -75,6 +98,11 @@ public function setConfiguration(array $configuration) { * An associative array with the default configuration. */ protected function baseConfigurationDefaults() { + // @todo Allow list of conditions to be configured in + // https://drupal.org/node/2284687. + $visibility = array_map(function ($definition) { + return array('id' => $definition['id']); + }, $this->conditionPluginManager()->getDefinitions()); return array( 'id' => $this->getPluginId(), 'label' => '', @@ -84,6 +112,7 @@ protected function baseConfigurationDefaults() { 'max_age' => 0, 'contexts' => array(), ), + 'visibility' => $visibility, ); } @@ -112,10 +141,38 @@ public function calculateDependencies() { * {@inheritdoc} */ public function access(AccountInterface $account) { - // @todo Move block visibility here in https://drupal.org/node/2278541. + // @todo Add in a context mapping until the UI supports configuring them, + // see https://drupal.org/node/2284687. + $mappings['user_role']['current_user'] = 'user'; + + $conditions = $this->getVisibilityConditions(); + $contexts = $this->getConditionContexts(); + foreach ($conditions as $condition_id => $condition) { + if ($condition instanceof ContextAwarePluginInterface) { + if (!isset($mappings[$condition_id])) { + $mappings[$condition_id] = array(); + } + $this->contextHandler()->applyContextMapping($condition, $contexts, $mappings[$condition_id]); + } + } + if ($this->resolveConditions($conditions, 'and', $contexts, $mappings) === FALSE) { + return FALSE; + } return $this->blockAccess($account); } + /** + * Gets the values for all defined contexts. + * + * @return \Drupal\Component\Plugin\Context\ContextInterface[] + * An array of set contexts, keyed by context name. + */ + protected function getConditionContexts() { + $conditions = $this->getVisibilityConditions(); + $this->eventDispatcher()->dispatch(BlockEvents::CONDITION_CONTEXT, new BlockConditionContextEvent($conditions)); + return $conditions->getConditionContexts(); + } + /** * Indicates whether the block should be shown. * @@ -212,6 +269,53 @@ public function buildConfigurationForm(array $form, array &$form_state) { $form['cache']['contexts']['#description'] .= ' ' . t('This block is <em>always</em> varied by the following contexts: %required-context-list.', array('%required-context-list' => $required_context_list)); } + $form['visibility_tabs'] = array( + '#type' => 'vertical_tabs', + '#title' => $this->t('Visibility'), + '#parents' => array('visibility_tabs'), + '#attached' => array( + 'library' => array( + 'block/drupal.block', + ), + ), + ); + foreach ($this->getVisibilityConditions() as $condition_id => $condition) { + $condition_form = $condition->buildConfigurationForm(array(), $form_state); + $condition_form['#type'] = 'details'; + $condition_form['#title'] = $condition->getPluginDefinition()['label']; + $condition_form['#group'] = 'visibility_tabs'; + $form['visibility'][$condition_id] = $condition_form; + } + + // @todo Determine if there is a better way to rename the conditions. + if (isset($form['visibility']['node_type'])) { + $form['visibility']['node_type']['#title'] = $this->t('Content types'); + $form['visibility']['node_type']['bundles']['#title'] = $this->t('Content types'); + $form['visibility']['node_type']['negate']['#type'] = 'value'; + $form['visibility']['node_type']['negate']['#title_display'] = 'invisible'; + $form['visibility']['node_type']['negate']['#value'] = $form['visibility']['node_type']['negate']['#default_value']; + } + if (isset($form['visibility']['user_role'])) { + $form['visibility']['user_role']['#title'] = $this->t('Roles'); + unset($form['visibility']['user_role']['roles']['#description']); + $form['visibility']['user_role']['negate']['#type'] = 'value'; + $form['visibility']['user_role']['negate']['#value'] = $form['visibility']['user_role']['negate']['#default_value']; + } + if (isset($form['visibility']['request_path'])) { + $form['visibility']['request_path']['#title'] = $this->t('Pages'); + $form['visibility']['request_path']['negate']['#type'] = 'radios'; + $form['visibility']['request_path']['negate']['#title_display'] = 'invisible'; + $form['visibility']['request_path']['negate']['#default_value'] = (int) $form['visibility']['request_path']['negate']['#default_value']; + $form['visibility']['request_path']['negate']['#options'] = array( + $this->t('Show for the listed pages'), + $this->t('Hide for the listed pages'), + ); + } + if (isset($form['visibility']['language'])) { + $form['visibility']['language']['negate']['#type'] = 'value'; + $form['visibility']['language']['negate']['#value'] = $form['visibility']['language']['negate']['#default_value']; + } + // Add plugin-specific settings for this block type. $form += $this->blockForm($form, $form_state); return $form; @@ -236,6 +340,14 @@ public function validateConfigurationForm(array &$form, array &$form_state) { // Transform the #type = checkboxes value to a numerically indexed array. $form_state['values']['cache']['contexts'] = array_values(array_filter($form_state['values']['cache']['contexts'])); + foreach ($this->getVisibilityConditions() as $condition_id => $condition) { + // Allow the condition to validate the form. + $condition_values = array( + 'values' => &$form_state['values']['visibility'][$condition_id], + ); + $condition->validateConfigurationForm($form, $condition_values); + } + $this->blockValidate($form, $form_state); } @@ -259,6 +371,13 @@ public function submitConfigurationForm(array &$form, array &$form_state) { $this->configuration['label_display'] = $form_state['values']['label_display']; $this->configuration['provider'] = $form_state['values']['provider']; $this->configuration['cache'] = $form_state['values']['cache']; + foreach ($this->getVisibilityConditions() as $condition_id => $condition) { + // Allow the condition to submit the form. + $condition_values = array( + 'values' => &$form_state['values']['visibility'][$condition_id], + ); + $condition->submitConfigurationForm($form, $condition_values); + } $this->blockSubmit($form, $form_state); } } @@ -347,4 +466,61 @@ public function isCacheable() { return $max_age === Cache::PERMANENT || $max_age > 0; } + /** + * {@inheritdoc} + */ + public function getVisibilityConditions() { + if (!isset($this->conditionBag)) { + $this->conditionBag = new ConditionPluginBag($this->conditionPluginManager(), $this->configuration['visibility']); + } + return $this->conditionBag; + } + + /** + * {@inheritdoc} + */ + public function getVisibilityCondition($instance_id) { + return $this->getVisibilityConditions()->get($instance_id); + } + + /** + * {@inheritdoc} + */ + public function setVisibilityConfig($instance_id, array $configuration) { + $this->getVisibilityConditions()->setInstanceConfiguration($instance_id, $configuration); + return $this; + } + + /** + * Gets the condition plugin manager. + * + * @return \Drupal\Core\Executable\ExecutableManagerInterface + * The condition plugin manager. + */ + protected function conditionPluginManager() { + if (!isset($this->conditionPluginManager)) { + $this->conditionPluginManager = \Drupal::service('plugin.manager.condition'); + } + return $this->conditionPluginManager; + } + + /** + * Wraps the event dispatcher. + * + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface + * The event dispatcher. + */ + protected function eventDispatcher() { + return \Drupal::service('event_dispatcher'); + } + + /** + * Wraps the context handler. + * + * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface + */ + protected function contextHandler() { + return \Drupal::service('context.handler'); + } + } diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php index 9631404c1e4e..af8641c2a590 100644 --- a/core/modules/block/src/BlockForm.php +++ b/core/modules/block/src/BlockForm.php @@ -10,9 +10,6 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Language\LanguageManagerInterface; -use Drupal\language\ConfigurableLanguageManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -34,24 +31,14 @@ class BlockForm extends EntityForm { */ protected $storage; - /** - * The language manager. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - /** * Constructs a BlockForm object. * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager. */ - public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) { + public function __construct(EntityManagerInterface $entity_manager) { $this->storage = $entity_manager->getStorage('block'); - $this->languageManager = $language_manager; } /** @@ -59,8 +46,7 @@ public function __construct(EntityManagerInterface $entity_manager, LanguageMana */ public static function create(ContainerInterface $container) { return new static( - $container->get('entity.manager'), - $container->get('language_manager') + $container->get('entity.manager') ); } @@ -94,122 +80,6 @@ public function form(array $form, array &$form_state) { '#disabled' => !$entity->isNew(), ); - // Visibility settings. - $form['visibility'] = array( - '#type' => 'vertical_tabs', - '#title' => $this->t('Visibility settings'), - '#attached' => array( - 'library' => array( - 'block/drupal.block', - ), - ), - '#tree' => TRUE, - '#weight' => 10, - '#parents' => array('visibility'), - ); - - // Per-path visibility. - $form['visibility']['path'] = array( - '#type' => 'details', - '#title' => $this->t('Pages'), - '#group' => 'visibility', - '#weight' => 0, - ); - - // @todo remove this access check and inject it in some other way. In fact - // this entire visibility settings section probably needs a separate user - // interface in the near future. - $visibility = $entity->get('visibility'); - $access = $this->currentUser()->hasPermission('use PHP for settings'); - if (!empty($visibility['path']['visibility']) && $visibility['path']['visibility'] == BLOCK_VISIBILITY_PHP && !$access) { - $form['visibility']['path']['visibility'] = array( - '#type' => 'value', - '#value' => BLOCK_VISIBILITY_PHP, - ); - $form['visibility']['path']['pages'] = array( - '#type' => 'value', - '#value' => !empty($visibility['path']['pages']) ? $visibility['path']['pages'] : '', - ); - } - else { - $options = array( - BLOCK_VISIBILITY_NOTLISTED => $this->t('All pages except those listed'), - BLOCK_VISIBILITY_LISTED => $this->t('Only the listed pages'), - ); - $description = $this->t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %user for the current user's page and %user-wildcard for every user page. %front is the front page.", array('%user' => 'user', '%user-wildcard' => 'user/*', '%front' => '<front>')); - - $form['visibility']['path']['visibility'] = array( - '#type' => 'radios', - '#title' => $this->t('Show block on specific pages'), - '#options' => $options, - '#default_value' => !empty($visibility['path']['visibility']) ? $visibility['path']['visibility'] : BLOCK_VISIBILITY_NOTLISTED, - ); - $form['visibility']['path']['pages'] = array( - '#type' => 'textarea', - '#title' => '<span class="visually-hidden">' . $this->t('Pages') . '</span>', - '#default_value' => !empty($visibility['path']['pages']) ? $visibility['path']['pages'] : '', - '#description' => $description, - ); - } - - // Configure the block visibility per language. - if ($this->languageManager->isMultilingual() && $this->languageManager instanceof ConfigurableLanguageManagerInterface) { - $language_types = $this->languageManager->getLanguageTypes(); - - // Fetch languages. - $languages = $this->languageManager->getLanguages(LanguageInterface::STATE_ALL); - $langcodes_options = array(); - foreach ($languages as $language) { - // @todo $language->name is not wrapped with t(), it should be replaced - // by CMI translation implementation. - $langcodes_options[$language->id] = $language->name; - } - $form['visibility']['language'] = array( - '#type' => 'details', - '#title' => $this->t('Languages'), - '#group' => 'visibility', - '#weight' => 5, - ); - // If there are multiple configurable language types, let the user pick - // which one should be applied to this visibility setting. This way users - // can limit blocks by interface language or content language for example. - $info = $this->languageManager->getDefinedLanguageTypesInfo(); - $language_type_options = array(); - foreach ($language_types as $type_key) { - $language_type_options[$type_key] = $info[$type_key]['name']; - } - $form['visibility']['language']['language_type'] = array( - '#type' => 'radios', - '#title' => $this->t('Language type'), - '#options' => $language_type_options, - '#default_value' => !empty($visibility['language']['language_type']) ? $visibility['language']['language_type'] : reset($language_types), - '#access' => count($language_type_options) > 1, - ); - $form['visibility']['language']['langcodes'] = array( - '#type' => 'checkboxes', - '#title' => $this->t('Show this block only for specific languages'), - '#default_value' => !empty($visibility['language']['langcodes']) ? $visibility['language']['langcodes'] : array(), - '#options' => $langcodes_options, - '#description' => $this->t('Show this block only for the selected language(s). If you select no languages, the block will be visible in all languages.'), - ); - } - - // Per-role visibility. - $role_options = array_map(array('\Drupal\Component\Utility\String', 'checkPlain'), user_role_names()); - $form['visibility']['role'] = array( - '#type' => 'details', - '#title' => $this->t('Roles'), - '#group' => 'visibility', - '#weight' => 10, - ); - $form['visibility']['role']['roles'] = array( - '#type' => 'checkboxes', - '#title' => $this->t('Show block for specific roles'), - '#default_value' => !empty($visibility['role']['roles']) ? $visibility['role']['roles'] : array(), - '#options' => $role_options, - '#description' => $this->t('Show this block only for the selected role(s). If you select no roles, the block will be visible to all users.'), - ); - // Theme settings. if ($entity->get('theme')) { $form['theme'] = array( @@ -276,8 +146,6 @@ protected function actions(array $form, array &$form_state) { public function validate(array $form, array &$form_state) { parent::validate($form, $form_state); - // Remove empty lines from the role visibility list. - $form_state['values']['visibility']['role']['roles'] = array_filter($form_state['values']['visibility']['role']['roles']); // The Block Entity form puts all block plugin form elements in the // settings form element, so just pass that to the block for validation. $settings = array( @@ -323,21 +191,6 @@ public function submit(array $form, array &$form_state) { ); } - /** - * {@inheritdoc} - */ - public function buildEntity(array $form, array &$form_state) { - $entity = parent::buildEntity($form, $form_state); - - // visibility__active_tab is Form API state and not configuration. - // @todo Fix vertical tabs. - $visibility = $entity->get('visibility'); - unset($visibility['visibility__active_tab']); - $entity->set('visibility', $visibility); - - return $entity; - } - /** * Generates a unique machine name for a block. * diff --git a/core/modules/block/src/BlockInterface.php b/core/modules/block/src/BlockInterface.php index de0d9de6f415..30179bd1bba3 100644 --- a/core/modules/block/src/BlockInterface.php +++ b/core/modules/block/src/BlockInterface.php @@ -32,4 +32,12 @@ interface BlockInterface extends ConfigEntityInterface { */ public function getPlugin(); + /** + * Returns an array of visibility condition configurations. + * + * @return array + * An array of visibility condition configuration keyed by the condition ID. + */ + public function getVisibility(); + } diff --git a/core/modules/block/src/BlockPluginInterface.php b/core/modules/block/src/BlockPluginInterface.php index f45ecc5a2a98..9f3090a0a7b3 100644 --- a/core/modules/block/src/BlockPluginInterface.php +++ b/core/modules/block/src/BlockPluginInterface.php @@ -7,6 +7,7 @@ namespace Drupal\block; +use Drupal\Component\Plugin\Context\ContextInterface; use Drupal\Core\Cache\CacheableInterface; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Component\Plugin\ConfigurablePluginInterface; @@ -140,4 +141,35 @@ public function blockSubmit($form, &$form_state); */ public function getMachineNameSuggestion(); + /** + * Gets conditions for this block. + * + * @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginBag + * An array or bag of configured condition plugins. + */ + public function getVisibilityConditions(); + + /** + * Gets a visibility condition plugin instance. + * + * @param string $instance_id + * The condition plugin instance ID. + * + * @return \Drupal\Core\Condition\ConditionInterface + * A condition plugin. + */ + public function getVisibilityCondition($instance_id); + + /** + * Sets the visibility condition configuration. + * + * @param string $instance_id + * The condition instance ID. + * @param array $configuration + * The condition configuration. + * + * @return $this + */ + public function setVisibilityConfig($instance_id, array $configuration); + } diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php index 3f0903b86ce0..4a63d25b1277 100644 --- a/core/modules/block/src/Entity/Block.php +++ b/core/modules/block/src/Entity/Block.php @@ -85,13 +85,6 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin */ protected $pluginBag; - /** - * The visibility settings. - * - * @var array - */ - protected $visibility; - /** * {@inheritdoc} */ @@ -174,4 +167,11 @@ public function getListCacheTags() { return array('theme' => $this->theme); } + /** + * {@inheritdoc} + */ + public function getVisibility() { + return $this->getPlugin()->getVisibilityConditions()->getConfiguration(); + } + } diff --git a/core/modules/block/src/Event/BlockConditionContextEvent.php b/core/modules/block/src/Event/BlockConditionContextEvent.php new file mode 100644 index 000000000000..a290ae6c2898 --- /dev/null +++ b/core/modules/block/src/Event/BlockConditionContextEvent.php @@ -0,0 +1,39 @@ +<?php + +/** + * @file + * Contains \Drupal\block\Event\BlockContextEvent. + */ + +namespace Drupal\block\Event; + +use Drupal\Core\Condition\ConditionPluginBag; +use Symfony\Component\EventDispatcher\Event; + +/** + * Wraps block conditions in order for event subscribers to add context. + * + * @see \Drupal\block\Event\BlockEvents::CONDITION_CONTEXT + */ +class BlockConditionContextEvent extends Event { + + /** + * @var \Drupal\Core\Condition\ConditionPluginBag + */ + protected $conditions; + + /** + * @param \Drupal\Core\Condition\ConditionPluginBag $conditions + */ + public function __construct(ConditionPluginBag $conditions) { + $this->conditions = $conditions; + } + + /** + * @return \Drupal\block\BlockPluginInterface + */ + public function getConditions() { + return $this->conditions; + } + +} diff --git a/core/modules/block/src/Event/BlockEvents.php b/core/modules/block/src/Event/BlockEvents.php new file mode 100644 index 000000000000..070eb6c7473e --- /dev/null +++ b/core/modules/block/src/Event/BlockEvents.php @@ -0,0 +1,23 @@ +<?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. + * + * @see \Drupal\block\BlockBase::getConditionContexts() + * @see \Drupal\block\Event\BlockConditionContextEvent + */ + const CONDITION_CONTEXT = 'block.condition_context'; + +} diff --git a/core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php b/core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php new file mode 100644 index 000000000000..73c9c303508b --- /dev/null +++ b/core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php @@ -0,0 +1,61 @@ +<?php + +/** + * @file + * Contains \Drupal\block\EventSubscriber\BlockContextSubscriberBase. + */ + +namespace Drupal\block\EventSubscriber; + +use Drupal\block\Event\BlockConditionContextEvent; +use Drupal\block\Event\BlockEvents; +use Drupal\Component\Plugin\Context\ContextInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Provides a base class for block context subscribers. + */ +abstract class BlockConditionContextSubscriberBase implements EventSubscriberInterface { + + /** + * @var \Drupal\Core\Condition\ConditionPluginBag + */ + protected $conditions; + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[BlockEvents::CONDITION_CONTEXT][] = 'onBlockConditionContext'; + return $events; + } + + /** + * Subscribes to the event and delegates to the subclass. + */ + public function onBlockConditionContext(BlockConditionContextEvent $event) { + $this->conditions = $event->getConditions(); + $this->determineBlockContext(); + } + + /** + * Determines the contexts for a given block. + */ + abstract protected function determineBlockContext(); + + /** + * Sets the condition context for a given name. + * + * @param string $name + * The name of the context. + * @param \Drupal\Component\Plugin\Context\ContextInterface $context + * The context to add. + * + * @return $this + */ + public function addContext($name, ContextInterface $context) { + $this->conditions->addContext($name, $context); + return $this; + } + +} diff --git a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php new file mode 100644 index 000000000000..054d0de805a2 --- /dev/null +++ b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php @@ -0,0 +1,50 @@ +<?php + +/** + * @file + * Contains \Drupal\block\EventSubscriber\CurrentLanguageContext. + */ + +namespace Drupal\block\EventSubscriber; + +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Sets the current language as a context. + */ +class CurrentLanguageContext extends BlockConditionContextSubscriberBase { + + use StringTranslationTrait; + + /** + * The language manager. + * + * @var \Drupal\Core\Language\LanguageManagerInterface + */ + protected $languageManager; + + /** + * Constructs a new CurrentLanguageContext. + * + * @param \Drupal\Core\Language\LanguageManagerInterface + * The language manager. + */ + public function __construct(LanguageManagerInterface $language_manager) { + $this->languageManager = $language_manager; + } + + /** + * {@inheritdoc} + */ + protected function determineBlockContext() { + $context = new Context(array( + 'type' => 'language', + 'label' => $this->t('Current language'), + )); + $context->setContextValue($this->languageManager->getCurrentLanguage()); + $this->addContext('language', $context); + } + +} diff --git a/core/modules/block/src/EventSubscriber/CurrentUserContext.php b/core/modules/block/src/EventSubscriber/CurrentUserContext.php new file mode 100644 index 000000000000..14565aad9139 --- /dev/null +++ b/core/modules/block/src/EventSubscriber/CurrentUserContext.php @@ -0,0 +1,63 @@ +<?php + +/** + * @file + * Contains \Drupal\block\Event\CurrentUserContext. + */ + +namespace Drupal\block\EventSubscriber; + +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; + +/** + * Sets the current user as a context. + */ +class CurrentUserContext extends BlockConditionContextSubscriberBase { + + use StringTranslationTrait; + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $account; + + /** + * The user storage. + * + * @var \Drupal\user\UserStorageInterface + */ + protected $userStorage; + + /** + * Constructs a new CurrentUserContext. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The current user. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + */ + public function __construct(AccountInterface $account, EntityManagerInterface $entity_manager) { + $this->account = $account; + $this->userStorage = $entity_manager->getStorage('user'); + } + + /** + * {@inheritdoc} + */ + protected function determineBlockContext() { + $current_user = $this->userStorage->load($this->account->id()); + + $context = new Context(array( + 'type' => 'entity:user', + 'label' => $this->t('Current user'), + )); + $context->setContextValue($current_user); + $this->addContext('current_user', $context); + } + +} diff --git a/core/modules/block/src/EventSubscriber/NodeRouteContext.php b/core/modules/block/src/EventSubscriber/NodeRouteContext.php new file mode 100644 index 000000000000..117bf21b74c8 --- /dev/null +++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php @@ -0,0 +1,57 @@ +<?php + +/** + * @file + * Contains \Drupal\block\EventSubscriber\NodeRouteContext. + */ + +namespace Drupal\block\EventSubscriber; + +use Drupal\Core\Plugin\Context\Context; +use Drupal\node\Entity\Node; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * Sets the current node as a context on node routes. + */ +class NodeRouteContext extends BlockConditionContextSubscriberBase { + + /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + /** + * Constructs a new NodeRouteContext. + * + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. + */ + public function __construct(RequestStack $request_stack) { + $this->requestStack = $request_stack; + } + + /** + * {@inheritdoc} + */ + protected function determineBlockContext() { + $request = $this->requestStack->getCurrentRequest(); + if ($request->attributes->has(RouteObjectInterface::ROUTE_OBJECT) && ($route_contexts = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getOption('parameters')) && isset($route_contexts['node'])) { + $context = new Context($route_contexts['node']); + if ($request->attributes->has('node')) { + $context->setContextValue($request->attributes->get('node')); + } + $this->addContext('node', $context); + } + elseif ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.add') { + $node_type = $request->attributes->get('node_type'); + $context = new Context(array('type' => 'entity:node')); + $context->setContextValue(Node::create(array('type' => $node_type->id()))); + $this->addContext('node', $context); + } + } + +} diff --git a/core/modules/block/src/Tests/BlockInterfaceTest.php b/core/modules/block/src/Tests/BlockInterfaceTest.php index 0b71146b3edf..c69548eea00a 100644 --- a/core/modules/block/src/Tests/BlockInterfaceTest.php +++ b/core/modules/block/src/Tests/BlockInterfaceTest.php @@ -44,6 +44,7 @@ public function testBlockInterface() { 'label' => 'Custom Display Message', ); $expected_configuration = array( + 'visibility' => array(), 'id' => 'test_block_instantiation', 'label' => 'Custom Display Message', 'provider' => 'block_test', @@ -55,6 +56,7 @@ public function testBlockInterface() { 'display_message' => 'no message set', ); // Initial configuration of the block at construction time. + /** @var $display_block \Drupal\block\BlockPluginInterface */ $display_block = $manager->createInstance('test_block_instantiation', $configuration); $this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block was configured correctly.'); @@ -124,7 +126,10 @@ public function testBlockInterface() { ); $form_state = array(); // Ensure there are no form elements that do not belong to the plugin. - $this->assertIdentical($display_block->buildConfigurationForm(array(), $form_state), $expected_form, 'Only the expected form elements were present.'); + $actual_form = $display_block->buildConfigurationForm(array(), $form_state); + // Remove the visibility sections, as that just tests condition plugins. + unset($actual_form['visibility'], $actual_form['visibility_tabs']); + $this->assertIdentical($actual_form, $expected_form, 'Only the expected form elements were present.'); $expected_build = array( '#children' => 'My custom display message.', diff --git a/core/modules/block/src/Tests/BlockLanguageTest.php b/core/modules/block/src/Tests/BlockLanguageTest.php index 246e92cb4ec2..7984345cf061 100644 --- a/core/modules/block/src/Tests/BlockLanguageTest.php +++ b/core/modules/block/src/Tests/BlockLanguageTest.php @@ -57,11 +57,11 @@ public function testLanguageBlockVisibility() { $default_theme = \Drupal::config('system.theme')->get('default'); $this->drupalGet('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme); - $this->assertField('visibility[language][langcodes][en]', 'Language visibility field is visible.'); + $this->assertField('settings[visibility][language][langcodes][en]', 'Language visibility field is visible.'); // Enable a standard block and set the visibility setting for one language. $edit = array( - 'visibility[language][langcodes][en]' => TRUE, + 'settings[visibility][language][langcodes][en]' => TRUE, 'id' => strtolower($this->randomName(8)), 'region' => 'sidebar_first', ); @@ -100,7 +100,7 @@ public function testLanguageBlockVisibilityLanguageDelete() { $block = $this->drupalPlaceBlock('system_powered_by_block', $edit); // Check that we have the language in config after saving the setting. - $visibility = $block->get('visibility'); + $visibility = $block->getVisibility(); $language = $visibility['language']['langcodes']['fr']; $this->assertTrue('fr' === $language, 'Language is set in the block configuration.'); @@ -110,7 +110,7 @@ public function testLanguageBlockVisibilityLanguageDelete() { // Check that the language is no longer stored in the configuration after // it is deleted. $block = entity_load('block', $block->id()); - $visibility = $block->get('visibility'); + $visibility = $block->getVisibility(); $this->assertTrue(empty($visibility['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.'); } diff --git a/core/modules/block/src/Tests/BlockStorageUnitTest.php b/core/modules/block/src/Tests/BlockStorageUnitTest.php index ab5496cf59b2..0b26399151e1 100644 --- a/core/modules/block/src/Tests/BlockStorageUnitTest.php +++ b/core/modules/block/src/Tests/BlockStorageUnitTest.php @@ -98,9 +98,9 @@ protected function createTests() { 'region' => '-1', 'weight' => NULL, 'provider' => NULL, - 'visibility' => NULL, 'plugin' => 'test_html', 'settings' => array( + 'visibility' => array(), 'id' => 'test_html', 'label' => '', 'provider' => 'block_test', diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php index 882863df4f52..741fab1de73b 100644 --- a/core/modules/block/src/Tests/BlockTest.php +++ b/core/modules/block/src/Tests/BlockTest.php @@ -40,8 +40,9 @@ function testBlockVisibility() { ); // Set the block to be hidden on any user path, and to be shown only to // authenticated users. - $edit['visibility[path][pages]'] = 'user*'; - $edit['visibility[role][roles][' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE; + $edit['settings[visibility][request_path][pages]'] = 'user*'; + $edit['settings[visibility][request_path][negate]'] = TRUE; + $edit['settings[visibility][user_role][roles][' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE; $this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block')); $this->assertText('The block configuration has been saved.', 'Block was saved'); @@ -65,8 +66,7 @@ function testBlockVisibility() { } /** - * Test block visibility when using "pages" restriction but leaving - * "pages" textarea empty + * Test block visibility when leaving "pages" textarea empty. */ function testBlockVisibilityListedEmpty() { $block_name = 'system_powered_by_block'; @@ -78,7 +78,7 @@ function testBlockVisibilityListedEmpty() { 'id' => strtolower($this->randomName(8)), 'region' => 'sidebar_first', 'settings[label]' => $title, - 'visibility[path][visibility]' => BLOCK_VISIBILITY_LISTED, + 'settings[visibility][request_path][negate]' => TRUE, ); // Set the block to be hidden on any user path, and to be shown only to // authenticated users. diff --git a/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml b/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml index 232c64a024ca..72510d029a02 100644 --- a/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml +++ b/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml @@ -6,17 +6,10 @@ langcode: en region: '-1' plugin: test_html settings: + visibility: { } label: 'Test HTML block' provider: block_test label_display: 'hidden' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: { } dependencies: module: - block_test diff --git a/core/modules/block/tests/src/BlockBaseTest.php b/core/modules/block/tests/src/BlockBaseTest.php index 913130b96bdd..575a6fbfb68f 100644 --- a/core/modules/block/tests/src/BlockBaseTest.php +++ b/core/modules/block/tests/src/BlockBaseTest.php @@ -9,7 +9,6 @@ use Drupal\block_test\Plugin\Block\TestBlockInstantiation; use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Transliteration\PHPTransliteration; use Drupal\Tests\UnitTestCase; /** @@ -38,19 +37,78 @@ public function testGetMachineNameSuggestion() { ->setMethods(array('readLanguageOverrides')) ->getMock(); + $condition_plugin_manager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface'); + $condition_plugin_manager->expects($this->atLeastOnce()) + ->method('getDefinitions') + ->will($this->returnValue(array())); $container = new ContainerBuilder(); + $container->set('plugin.manager.condition', $condition_plugin_manager); $container->set('transliteration', $transliteraton); \Drupal::setContainer($container); $config = array(); - $definition = array('admin_label' => 'Admin label', 'provider' => 'block_test'); + $definition = array( + 'admin_label' => 'Admin label', + 'provider' => 'block_test', + ); $block_base = new TestBlockInstantiation($config, 'test_block_instantiation', $definition); $this->assertEquals('adminlabel', $block_base->getMachineNameSuggestion()); // Test with more unicodes. - $definition = array('admin_label' =>'über åwesome', 'provider' => 'block_test'); + $definition = array( + 'admin_label' => 'über åwesome', + 'provider' => 'block_test', + ); $block_base = new TestBlockInstantiation($config, 'test_block_instantiation', $definition); $this->assertEquals('uberawesome', $block_base->getMachineNameSuggestion()); } + /** + * Tests initializing the condition plugins initialization. + */ + public function testConditionsBagInitialization() { + $plugin_manager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface'); + $plugin_manager->expects($this->once()) + ->method('getDefinitions') + ->will($this->returnValue(array( + 'request_path' => array( + 'id' => 'request_path', + ), + 'user_role' => array( + 'id' => 'user_role', + ), + 'node_type' => array( + 'id' => 'node_type', + ), + 'language' => array( + 'id' => 'language', + ), + ))); + $container = new ContainerBuilder(); + $container->set('plugin.manager.condition', $plugin_manager); + \Drupal::setContainer($container); + $config = array(); + $definition = array( + 'admin_label' => 'Admin label', + 'provider' => 'block_test', + ); + + $block_base = new TestBlockInstantiation($config, 'test_block_instantiation', $definition); + $conditions_bag = $block_base->getVisibilityConditions(); + + $this->assertEquals(4, $conditions_bag->count(), "There are 4 condition plugins"); + + $instance_id = $this->randomName(); + $pages = 'node/1'; + $condition_config = array('id' => 'request_path', 'pages' => $pages); + $block_base->setVisibilityConfig($instance_id, $condition_config); + + $plugin_manager->expects($this->once())->method('createInstance') + ->withAnyParameters()->will($this->returnValue('test')); + + $condition = $block_base->getVisibilityCondition($instance_id); + + $this->assertEquals('test', $condition, "The correct condition is returned."); + } + } diff --git a/core/modules/language/src/Plugin/Condition/Language.php b/core/modules/language/src/Plugin/Condition/Language.php index e6268c2dbffa..41dab719c5f6 100644 --- a/core/modules/language/src/Plugin/Condition/Language.php +++ b/core/modules/language/src/Plugin/Condition/Language.php @@ -29,7 +29,6 @@ class Language extends ConditionPluginBase { * {@inheritdoc} */ public function buildConfigurationForm(array $form, array &$form_state) { - $form = parent::buildConfigurationForm($form, $form_state); if (\Drupal::languageManager()->isMultilingual()) { // Fetch languages. $languages = language_list(LanguageInterface::STATE_ALL); @@ -51,7 +50,7 @@ public function buildConfigurationForm(array $form, array &$form_state) { '#value' => $this->configuration['langcodes'], ); } - return $form; + return parent::buildConfigurationForm($form, $form_state); } /** diff --git a/core/modules/migrate_drupal/config/install/migrate.migration.d6_block.yml b/core/modules/migrate_drupal/config/install/migrate.migration.d6_block.yml index 05bd07d763a1..4fa9ea714dbd 100644 --- a/core/modules/migrate_drupal/config/install/migrate.migration.d6_block.yml +++ b/core/modules/migrate_drupal/config/install/migrate.migration.d6_block.yml @@ -46,9 +46,6 @@ process: region: region theme: theme label: title - 'visibility.path.visibility': visibility - 'visibility.path.pages': pages - 'visibility.role.roles': roles weight: weight settings: plugin: d6_block_settings @@ -56,6 +53,9 @@ process: - @plugin - delta - settings + - visibility + 'settings.visibility.request_path.pages': pages + 'settings.visibility.user_role.roles': roles destination: plugin: entity:block migration_dependencies: diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockSettings.php b/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockSettings.php index a804352b2e62..94ea60010f59 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockSettings.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockSettings.php @@ -24,8 +24,9 @@ class BlockSettings extends ProcessPluginBase { * Set the block configuration. */ public function transform($value, MigrateExecutable $migrate_executable, Row $row, $destination_property) { - list($plugin, $delta, $old_settings) = $value; + list($plugin, $delta, $old_settings, $visibility) = $value; $settings = array(); + $settings['visibility']['request_path']['negate'] = !$visibility; switch ($plugin) { case 'aggregator_feed_block': list(, $id) = explode('-', $delta); diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php index 800e78493cdc..313e7802558b 100644 --- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php +++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php @@ -75,6 +75,7 @@ public function setUp() { * Test the block settings migration. */ public function testBlockMigration() { + /** @var $blocks \Drupal\block\BlockInterface[] */ $blocks = entity_load_multiple('block'); $this->assertEqual(count($blocks), 11); @@ -83,36 +84,36 @@ public function testBlockMigration() { $this->assertNotNull($test_block_user); $this->assertEqual('left', $test_block_user->get('region')); $this->assertEqual('garland', $test_block_user->get('theme')); - $visibility = $test_block_user->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_user->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(0, $test_block_user->weight); $test_block_user_1 = $blocks['user_1']; $this->assertNotNull($test_block_user_1); $this->assertEqual('left', $test_block_user_1->get('region')); $this->assertEqual('garland', $test_block_user_1->get('theme')); - $visibility = $test_block_user_1->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_user_1->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(0, $test_block_user_1->weight); $test_block_user_2 = $blocks['user_2']; $this->assertNotNull($test_block_user_2); $this->assertEqual('', $test_block_user_2->get('region')); $this->assertEqual('garland', $test_block_user_2->get('theme')); - $visibility = $test_block_user_2->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_user_2->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-3, $test_block_user_2->weight); $test_block_user_3 = $blocks['user_3']; $this->assertNotNull($test_block_user_3); $this->assertEqual('', $test_block_user_3->get('region')); $this->assertEqual('garland', $test_block_user_3->get('theme')); - $visibility = $test_block_user_3->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_user_3->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-1, $test_block_user_3->weight); // Check system block @@ -120,9 +121,9 @@ public function testBlockMigration() { $this->assertNotNull($test_block_system); $this->assertEqual('footer', $test_block_system->get('region')); $this->assertEqual('garland', $test_block_system->get('theme')); - $visibility = $test_block_system->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_system->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-5, $test_block_system->weight); // Check comment block @@ -130,9 +131,9 @@ public function testBlockMigration() { $this->assertNotNull($test_block_comment); $this->assertEqual('', $test_block_comment->get('region')); $this->assertEqual('garland', $test_block_comment->get('theme')); - $visibility = $test_block_comment->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_comment->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-6, $test_block_comment->weight); // Check menu blocks @@ -140,18 +141,18 @@ public function testBlockMigration() { $this->assertNotNull($test_block_menu); $this->assertEqual('header', $test_block_menu->get('region')); $this->assertEqual('garland', $test_block_menu->get('theme')); - $visibility = $test_block_menu->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_menu->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-5, $test_block_menu->weight); $test_block_menu_1 = $blocks['menu_1']; $this->assertNotNull($test_block_menu_1); $this->assertEqual('', $test_block_menu_1->get('region')); $this->assertEqual('garland', $test_block_menu_1->get('theme')); - $visibility = $test_block_menu_1->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_menu_1->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-5, $test_block_menu_1->weight); // Check node block @@ -159,9 +160,9 @@ public function testBlockMigration() { $this->assertNotNull($test_block_node); $this->assertEqual('', $test_block_node->get('region')); $this->assertEqual('garland', $test_block_node->get('theme')); - $visibility = $test_block_node->get('visibility'); - $this->assertEqual(0, $visibility['path']['visibility']); - $this->assertEqual('', $visibility['path']['pages']); + $visibility = $test_block_node->getVisibility(); + $this->assertEqual(TRUE, $visibility['request_path']['negate']); + $this->assertEqual('', $visibility['request_path']['pages']); $this->assertEqual(-4, $test_block_node->weight); // Check custom blocks @@ -169,18 +170,18 @@ public function testBlockMigration() { $this->assertNotNull($test_block_block); $this->assertEqual('content', $test_block_block->get('region')); $this->assertEqual('garland', $test_block_block->get('theme')); - $visibility = $test_block_block->get('visibility'); - $this->assertEqual(1, $visibility['path']['visibility']); - $this->assertEqual('<front>', $visibility['path']['pages']); + $visibility = $test_block_block->getVisibility(); + $this->assertEqual(FALSE, $visibility['request_path']['negate']); + $this->assertEqual('<front>', $visibility['request_path']['pages']); $this->assertEqual(0, $test_block_block->weight); $test_block_block_1 = $blocks['block_1']; $this->assertNotNull($test_block_block_1); $this->assertEqual('right', $test_block_block_1->get('region')); $this->assertEqual('bluemarine', $test_block_block_1->get('theme')); - $visibility = $test_block_block_1->get('visibility'); - $this->assertEqual(1, $visibility['path']['visibility']); - $this->assertEqual('node', $visibility['path']['pages']); + $visibility = $test_block_block_1->getVisibility(); + $this->assertEqual(FALSE, $visibility['request_path']['negate']); + $this->assertEqual('node', $visibility['request_path']['pages']); $this->assertEqual(-4, $test_block_block_1->weight); } } diff --git a/core/modules/node/node.module b/core/modules/node/node.module index d4f7db02f9ae..f094d61d01e8 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -932,71 +932,6 @@ function node_get_recent($number = 10) { return $nodes ? $nodes : array(); } -/** - * Implements hook_form_FORM_ID_alter() for block_form(). - * - * Adds node-type specific visibility options to block configuration form. - */ -function node_form_block_form_alter(&$form, &$form_state) { - $block = $form_state['controller']->getEntity(); - $visibility = $block->get('visibility'); - $form['visibility']['node_type'] = array( - '#type' => 'details', - '#title' => t('Content types'), - '#group' => 'visibility', - '#weight' => 5, - ); - $form['visibility']['node_type']['types'] = array( - '#type' => 'checkboxes', - '#title' => t('Show block for specific content types'), - '#default_value' => !empty($visibility['node_type']['types']) ? $visibility['node_type']['types'] : array(), - '#options' => node_type_get_names(), - '#description' => t('Show this block only on pages that display content of the given type(s). If you select no types, there will be no type-specific limitation.'), - ); -} - -/** - * Implements hook_block_access(). - * - * Checks the content type specific visibility settings and removes the block - * if the visibility conditions are not met. - */ -function node_block_access(Block $block, $operation, AccountInterface $account, $langcode) { - // Only affect access when viewing the block. - if ($operation != 'view') { - return; - } - $visibility = $block->get('visibility'); - if (!empty($visibility)) { - if (!empty($visibility['node_type']['types'])) { - $allowed_types = array_filter($visibility['node_type']['types']); - } - if (empty($allowed_types)) { - // There are no node types selected in visibility settings so there is - // nothing to do. - // @see node_form_block_form_alter() - return; - } - - // For blocks with node types associated, if the node type does not match - // the settings from this block, deny access to it. - $request = \Drupal::request(); - if ($node = $request->attributes->get('node')) { - // Page has node. - return in_array($node->bundle(), $allowed_types); - } - // This is a node creation page. - // $request->attributes->has('node_type') would also match administration - // configuration pages, which the previous URI path options did not. - if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.add') { - $node_type = $request->attributes->get('node_type'); - return in_array($node_type->id(), $allowed_types); - } - - return FALSE; - } -} - /** * Page callback: Generates and prints an RSS feed. * diff --git a/core/modules/node/src/Plugin/Condition/NodeType.php b/core/modules/node/src/Plugin/Condition/NodeType.php index 5b093bf91fa0..66e57e3120c9 100644 --- a/core/modules/node/src/Plugin/Condition/NodeType.php +++ b/core/modules/node/src/Plugin/Condition/NodeType.php @@ -28,7 +28,6 @@ class NodeType extends ConditionPluginBase { * {@inheritdoc} */ public function buildConfigurationForm(array $form, array &$form_state) { - $form = parent::buildConfigurationForm($form, $form_state); $options = array(); foreach (node_type_get_types() as $type) { $options[$type->type] = $type->name; @@ -39,7 +38,7 @@ public function buildConfigurationForm(array $form, array &$form_state) { '#options' => $options, '#default_value' => $this->configuration['bundles'], ); - return $form; + return parent::buildConfigurationForm($form, $form_state); } /** diff --git a/core/modules/node/src/Tests/NodeBlockFunctionalTest.php b/core/modules/node/src/Tests/NodeBlockFunctionalTest.php index 148ec233a641..e806bc9eba73 100644 --- a/core/modules/node/src/Tests/NodeBlockFunctionalTest.php +++ b/core/modules/node/src/Tests/NodeBlockFunctionalTest.php @@ -128,14 +128,14 @@ public function testRecentNodeBlock() { $block = $this->drupalPlaceBlock('system_powered_by_block', array( 'visibility' => array( 'node_type' => array( - 'types' => array( + 'bundles' => array( 'article' => 'article', ), ), ), )); - $visibility = $block->get('visibility'); - $this->assertTrue(isset($visibility['node_type']['types']['article']), 'Visibility settings were saved to configuration'); + $visibility = $block->getVisibility(); + $this->assertTrue(isset($visibility['node_type']['bundles']['article']), 'Visibility settings were saved to configuration'); // Create a page node. $node5 = $this->drupalCreateNode(array('uid' => $this->adminUser->id(), 'type' => 'page')); diff --git a/core/modules/search/src/Tests/SearchBlockTest.php b/core/modules/search/src/Tests/SearchBlockTest.php index d4e45db3d14f..311f69af1853 100644 --- a/core/modules/search/src/Tests/SearchBlockTest.php +++ b/core/modules/search/src/Tests/SearchBlockTest.php @@ -63,9 +63,9 @@ protected function testSearchFormBlock() { $this->assertResponse(200); $this->assertText('Your search yielded no results'); - $visibility = $block->get('visibility'); - $visibility['path']['pages'] = 'search'; - $block->set('visibility', $visibility); + $visibility = $block->getVisibility(); + $visibility['request_path']['pages'] = 'search'; + $block->getPlugin()->setVisibilityConfig('request_path', $visibility['request_path']); $this->submitGetForm('', $terms, t('Search')); $this->assertResponse(200); diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index 9bf6c23e0b65..740e95246fd9 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -429,11 +429,14 @@ protected function drupalPlaceBlock($plugin_id, array $settings = array()) { 'max_age' => 0, ), ); - foreach (array('region', 'id', 'theme', 'plugin', 'visibility', 'weight') as $key) { + foreach (array('region', 'id', 'theme', 'plugin', 'weight') as $key) { $values[$key] = $settings[$key]; // Remove extra values that do not belong in the settings array. unset($settings[$key]); } + foreach ($settings['visibility'] as $id => $visibility) { + $settings['visibility'][$id]['id'] = $id; + } $values['settings'] = $settings; $block = entity_create('block', $values); $block->save(); diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php index f310b24f1303..a22dfb23b3bd 100644 --- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php +++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php @@ -67,8 +67,7 @@ function testPageCacheTags() { // Place a block, but only make it visible on full node page 2. $block = $this->drupalPlaceBlock('views_block:comments_recent-block_1', array( 'visibility' => array( - 'path' => array( - 'visibility' => BLOCK_VISIBILITY_LISTED, + 'request_path' => array( 'pages' => 'node/' . $node_2->id(), ), ) diff --git a/core/modules/user/src/Plugin/Condition/UserRole.php b/core/modules/user/src/Plugin/Condition/UserRole.php index e9740da1fd64..cd38ee3686f5 100644 --- a/core/modules/user/src/Plugin/Condition/UserRole.php +++ b/core/modules/user/src/Plugin/Condition/UserRole.php @@ -28,7 +28,6 @@ class UserRole extends ConditionPluginBase { * {@inheritdoc} */ public function buildConfigurationForm(array $form, array &$form_state) { - $form = parent::buildConfigurationForm($form, $form_state); $form['roles'] = array( '#type' => 'checkboxes', '#title' => $this->t('When the user has the following roles'), @@ -36,7 +35,7 @@ public function buildConfigurationForm(array $form, array &$form_state) { '#options' => array_map('\Drupal\Component\Utility\String::checkPlain', user_role_names()), '#description' => $this->t('If you select no roles, the condition will evaluate to TRUE for all users.'), ); - return $form; + return parent::buildConfigurationForm($form, $form_state); } /** diff --git a/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php b/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php index dd2d2e8597bf..0475e232addc 100644 --- a/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php +++ b/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php @@ -109,11 +109,14 @@ protected function createBlock($plugin_id, array $settings = array()) { 'max_age' => 0, ), ); - foreach (array('region', 'id', 'theme', 'plugin', 'visibility', 'weight') as $key) { + foreach (array('region', 'id', 'theme', 'plugin', 'weight') as $key) { $values[$key] = $settings[$key]; // Remove extra values that do not belong in the settings array. unset($settings[$key]); } + foreach ($settings['visibility'] as $id => $visibility) { + $settings['visibility'][$id]['id'] = $id; + } $values['settings'] = $settings; $block = entity_create('block', $values); $block->save(); diff --git a/core/modules/views/tests/src/Plugin/Block/ViewsBlockTest.php b/core/modules/views/tests/src/Plugin/Block/ViewsBlockTest.php index eb46cb3425ed..c1029ddd4f66 100644 --- a/core/modules/views/tests/src/Plugin/Block/ViewsBlockTest.php +++ b/core/modules/views/tests/src/Plugin/Block/ViewsBlockTest.php @@ -7,6 +7,7 @@ namespace Drupal\views\Tests\Plugin\Block { +use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Tests\UnitTestCase; use Drupal\views\Plugin\Block\ViewsBlock; use Drupal\block\Plugin\views\display\Block; @@ -71,6 +72,13 @@ public static function getInfo() { */ protected function setUp() { parent::setUp(); // TODO: Change the autogenerated stub + $condition_plugin_manager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface'); + $condition_plugin_manager->expects($this->any()) + ->method('getDefinitions') + ->will($this->returnValue(array())); + $container = new ContainerBuilder(); + $container->set('plugin.manager.condition', $condition_plugin_manager); + \Drupal::setContainer($container); $this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable') ->disableOriginalConstructor() @@ -128,6 +136,10 @@ public function testBuild() { $definition['provider'] = 'views'; $plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storage, $this->account); + $reflector = new \ReflectionClass($plugin); + $property = $reflector->getProperty('conditionPluginManager'); + $property->setAccessible(TRUE); + $property->setValue($plugin, $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface')); $this->assertEquals($build, $plugin->build()); } @@ -150,6 +162,10 @@ public function testBuildFailed() { $definition['provider'] = 'views'; $plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storage, $this->account); + $reflector = new \ReflectionClass($plugin); + $property = $reflector->getProperty('conditionPluginManager'); + $property->setAccessible(TRUE); + $property->setValue($plugin, $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface')); $this->assertEquals(array(), $plugin->build()); } diff --git a/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php b/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php index 3384b0f95db3..1a405d9bea9c 100644 --- a/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php +++ b/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php @@ -120,9 +120,9 @@ function testWizardMixedDefaultOverriddenDisplays() { // presence/absence of the view's title in both the page and the block). $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", array( 'visibility' => array( - 'path' => array( - 'visibility' => BLOCK_VISIBILITY_NOTLISTED, + 'request_path' => array( 'pages' => $view['page[path]'], + 'negate' => TRUE, ), ), )); diff --git a/core/profiles/minimal/config/install/block.block.stark_admin.yml b/core/profiles/minimal/config/install/block.block.stark_admin.yml index 5d5c2c1e5a1f..a7cc0a5b8c44 100644 --- a/core/profiles/minimal/config/install/block.block.stark_admin.yml +++ b/core/profiles/minimal/config/install/block.block.stark_admin.yml @@ -6,17 +6,10 @@ langcode: en region: sidebar_first plugin: 'system_menu_block:admin' settings: + visibility: { } label: Administration provider: system label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: { } dependencies: entity: - system.menu.admin diff --git a/core/profiles/minimal/config/install/block.block.stark_login.yml b/core/profiles/minimal/config/install/block.block.stark_login.yml index 91ba3e08d68f..9081e1afde07 100644 --- a/core/profiles/minimal/config/install/block.block.stark_login.yml +++ b/core/profiles/minimal/config/install/block.block.stark_login.yml @@ -6,17 +6,10 @@ langcode: en region: sidebar_first plugin: user_login_block settings: + visibility: { } label: 'User login' provider: user label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: { } dependencies: module: - user diff --git a/core/profiles/minimal/config/install/block.block.stark_tools.yml b/core/profiles/minimal/config/install/block.block.stark_tools.yml index 36481b3d1301..aeb99bbf456d 100644 --- a/core/profiles/minimal/config/install/block.block.stark_tools.yml +++ b/core/profiles/minimal/config/install/block.block.stark_tools.yml @@ -6,17 +6,10 @@ langcode: en region: sidebar_first plugin: 'system_menu_block:tools' settings: + visibility: { } label: Tools provider: system label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: { } dependencies: entity: - system.menu.tools diff --git a/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml b/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml index 23cc5d0507b6..2128a6149be2 100644 --- a/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml +++ b/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml @@ -6,19 +6,11 @@ langcode: en region: '-1' plugin: system_breadcrumb_block settings: + visibility: { } + id: system_breadcrumb_block label: Breadcrumbs provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.bartik_content.yml b/core/profiles/standard/config/install/block.block.bartik_content.yml index dd89d12fb0a5..d30a7f30cf52 100644 --- a/core/profiles/standard/config/install/block.block.bartik_content.yml +++ b/core/profiles/standard/config/install/block.block.bartik_content.yml @@ -6,19 +6,11 @@ langcode: en region: content plugin: system_main_block settings: + visibility: { } + id: system_main_block label: 'Main page content' provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.bartik_footer.yml b/core/profiles/standard/config/install/block.block.bartik_footer.yml index ef96ea94c85f..26acc4619e73 100644 --- a/core/profiles/standard/config/install/block.block.bartik_footer.yml +++ b/core/profiles/standard/config/install/block.block.bartik_footer.yml @@ -6,19 +6,11 @@ langcode: en region: footer plugin: 'system_menu_block:footer' settings: + visibility: { } + id: 'system_menu_block:footer' label: 'Footer menu' provider: system label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: entity: - system.menu.footer diff --git a/core/profiles/standard/config/install/block.block.bartik_help.yml b/core/profiles/standard/config/install/block.block.bartik_help.yml index 3e139c69ac68..3a2f48a5f703 100644 --- a/core/profiles/standard/config/install/block.block.bartik_help.yml +++ b/core/profiles/standard/config/install/block.block.bartik_help.yml @@ -6,19 +6,11 @@ langcode: en region: help plugin: system_help_block settings: + visibility: { } + id: system_help_block label: 'System Help' provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.bartik_login.yml b/core/profiles/standard/config/install/block.block.bartik_login.yml index 1465ce8885bc..6d58c23bcea4 100644 --- a/core/profiles/standard/config/install/block.block.bartik_login.yml +++ b/core/profiles/standard/config/install/block.block.bartik_login.yml @@ -6,19 +6,11 @@ langcode: en region: sidebar_first plugin: user_login_block settings: + visibility: { } + id: user_login_block label: 'User login' provider: user label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - user diff --git a/core/profiles/standard/config/install/block.block.bartik_powered.yml b/core/profiles/standard/config/install/block.block.bartik_powered.yml index c72223122326..106c2791a78f 100644 --- a/core/profiles/standard/config/install/block.block.bartik_powered.yml +++ b/core/profiles/standard/config/install/block.block.bartik_powered.yml @@ -6,19 +6,11 @@ langcode: en region: footer plugin: system_powered_by_block settings: + visibility: { } + id: system_powered_by_block label: 'Powered by Drupal' provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.bartik_search.yml b/core/profiles/standard/config/install/block.block.bartik_search.yml index 7b00a7035240..ac4130a32bc7 100644 --- a/core/profiles/standard/config/install/block.block.bartik_search.yml +++ b/core/profiles/standard/config/install/block.block.bartik_search.yml @@ -6,19 +6,11 @@ langcode: en region: sidebar_first plugin: search_form_block settings: + visibility: { } + id: search_form_block label: Search provider: search label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - search diff --git a/core/profiles/standard/config/install/block.block.bartik_tools.yml b/core/profiles/standard/config/install/block.block.bartik_tools.yml index 12efff10fcdf..6ed680850faf 100644 --- a/core/profiles/standard/config/install/block.block.bartik_tools.yml +++ b/core/profiles/standard/config/install/block.block.bartik_tools.yml @@ -6,19 +6,11 @@ langcode: en region: sidebar_first plugin: 'system_menu_block:tools' settings: + visibility: { } + id: 'system_menu_block:tools' label: Tools provider: system label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: entity: - system.menu.tools diff --git a/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml b/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml index 844a54642589..cd7d7acbf8c0 100644 --- a/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml +++ b/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml @@ -6,19 +6,11 @@ langcode: en region: '-1' plugin: system_breadcrumb_block settings: + visibility: { } + id: system_breadcrumb_block label: Breadcrumbs provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.seven_content.yml b/core/profiles/standard/config/install/block.block.seven_content.yml index ec7a43209680..12c2c5fa0d03 100644 --- a/core/profiles/standard/config/install/block.block.seven_content.yml +++ b/core/profiles/standard/config/install/block.block.seven_content.yml @@ -6,19 +6,11 @@ langcode: en region: content plugin: system_main_block settings: + visibility: { } + id: system_main_block label: 'Main page content' provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.seven_help.yml b/core/profiles/standard/config/install/block.block.seven_help.yml index f54a908e7ba7..1cf9fa315cd4 100644 --- a/core/profiles/standard/config/install/block.block.seven_help.yml +++ b/core/profiles/standard/config/install/block.block.seven_help.yml @@ -6,19 +6,11 @@ langcode: en region: help plugin: system_help_block settings: + visibility: { } + id: system_help_block label: 'System Help' provider: system label_display: '0' -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - system diff --git a/core/profiles/standard/config/install/block.block.seven_login.yml b/core/profiles/standard/config/install/block.block.seven_login.yml index 9f4578c0e706..321d92b4d7b8 100644 --- a/core/profiles/standard/config/install/block.block.seven_login.yml +++ b/core/profiles/standard/config/install/block.block.seven_login.yml @@ -6,19 +6,11 @@ langcode: en region: content plugin: user_login_block settings: + visibility: { } + id: user_login_block label: 'User login' provider: user label_display: visible -visibility: - path: - visibility: 0 - pages: '' - role: - roles: { } - node_type: - types: - article: '0' - page: '0' dependencies: module: - user diff --git a/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php b/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php new file mode 100644 index 000000000000..dc676c5641c6 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php @@ -0,0 +1,107 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Core\Condition\ConditionAccessResolverTraitTest. + */ + +namespace Drupal\Tests\Core\Condition; + +use Drupal\Core\Condition\ConditionAccessResolverTrait; +use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Tests\UnitTestCase; + +/** + * Tests resolving a set of conditions. + * + * @coversDefaultClass \Drupal\Core\Condition\ConditionAccessResolverTrait + * + * @group Drupal + * @group Condition + */ +class ConditionAccessResolverTraitTest extends UnitTestCase { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'Tests resolving a set of conditions', + 'description' => '', + 'group' => 'Condition', + ); + } + + /** + * Tests the resolveConditions() method. + * + * @covers ::resolveConditions + * + * @dataProvider providerTestResolveConditions + */ + public function testResolveConditions($conditions, $logic, $expected) { + $trait_object = new TestConditionAccessResolverTrait(); + $this->assertEquals($expected, $trait_object->resolveConditions($conditions, $logic)); + } + + public function providerTestResolveConditions() { + $data = array(); + + $condition_true = $this->getMock('Drupal\Core\Condition\ConditionInterface'); + $condition_true->expects($this->any()) + ->method('execute') + ->will($this->returnValue(TRUE)); + $condition_false = $this->getMock('Drupal\Core\Condition\ConditionInterface'); + $condition_false->expects($this->any()) + ->method('execute') + ->will($this->returnValue(FALSE)); + $condition_exception = $this->getMock('Drupal\Core\Condition\ConditionInterface'); + $condition_exception->expects($this->any()) + ->method('execute') + ->will($this->throwException(new PluginException())); + + $conditions = array(); + $data[] = array($conditions, 'and', TRUE); + $data[] = array($conditions, 'or', FALSE); + + $conditions = array($condition_false); + $data[] = array($conditions, 'or', FALSE); + $data[] = array($conditions, 'and', FALSE); + + $conditions = array($condition_true); + $data[] = array($conditions, 'or', TRUE); + $data[] = array($conditions, 'and', TRUE); + + $conditions = array($condition_true, $condition_false); + $data[] = array($conditions, 'or', TRUE); + $data[] = array($conditions, 'and', FALSE); + + $conditions = array($condition_exception); + $data[] = array($conditions, 'or', FALSE); + $data[] = array($conditions, 'and', FALSE); + + $conditions = array($condition_true, $condition_exception); + $data[] = array($conditions, 'or', TRUE); + $data[] = array($conditions, 'and', FALSE); + + $conditions = array($condition_exception, $condition_true); + $data[] = array($conditions, 'or', TRUE); + $data[] = array($conditions, 'and', FALSE); + + $conditions = array($condition_false, $condition_exception); + $data[] = array($conditions, 'or', FALSE); + $data[] = array($conditions, 'and', FALSE); + + $conditions = array($condition_exception, $condition_false); + $data[] = array($conditions, 'or', FALSE); + $data[] = array($conditions, 'and', FALSE); + return $data; + } + +} + +class TestConditionAccessResolverTrait { + use \Drupal\Core\Condition\ConditionAccessResolverTrait { + resolveConditions as public; + } +} diff --git a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php index d162042d1519..fa7c80c7c3ed 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php @@ -16,7 +16,7 @@ /** * Tests the ContextHandler class. * - * @coversDefaultClass \Drupal\Core\Plugin\ContextHandler + * @coversDefaultClass \Drupal\Core\Plugin\Context\ContextHandler * * @group Drupal * @group Plugin -- GitLab