Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
167
core/modules/block/block.api.php
Normal file
167
core/modules/block/block.api.php
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Block module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
|
||||
/**
|
||||
* @defgroup block_api Block API
|
||||
* @{
|
||||
* Information about the classes and interfaces that make up the Block API.
|
||||
*
|
||||
* Blocks are a combination of a configuration entity and a plugin. The
|
||||
* configuration entity stores placement information (theme, region, weight) and
|
||||
* any other configuration that is specific to the block. The block plugin does
|
||||
* the work of rendering the block's content for display.
|
||||
*
|
||||
* To define a block in a module you need to:
|
||||
* - Define a Block plugin by creating a new class that implements the
|
||||
* \Drupal\Core\Block\BlockPluginInterface, in namespace Plugin\Block under your
|
||||
* module namespace. For more information about creating plugins, see the
|
||||
* @link plugin_api Plugin API topic. @endlink
|
||||
* - Usually you will want to extend the \Drupal\Core\Block\BlockBase class, which
|
||||
* provides a common configuration form and utility methods for getting and
|
||||
* setting configuration in the block configuration entity.
|
||||
* - Block plugins use the annotations defined by
|
||||
* \Drupal\Core\Block\Annotation\Block. See the
|
||||
* @link annotation Annotations topic @endlink for more information about
|
||||
* annotations.
|
||||
*
|
||||
* The Block API also makes use of Condition plugins, for conditional block
|
||||
* placement. Condition plugins have interface
|
||||
* \Drupal\Core\Condition\ConditionInterface, base class
|
||||
* \Drupal\Core\Condition\ConditionPluginBase, and go in plugin namespace
|
||||
* Plugin\Condition. Again, see the Plugin API and Annotations topics for
|
||||
* details of how to create a plugin class and annotate it.
|
||||
*
|
||||
* There are also several block-related hooks, which allow you to affect
|
||||
* the content and access permissions for blocks:
|
||||
* - hook_block_view_alter()
|
||||
* - hook_block_view_BASE_BLOCK_ID_alter()
|
||||
* - hook_block_access()
|
||||
*
|
||||
* Further information and examples:
|
||||
* - \Drupal\system\Plugin\Block\SystemPoweredByBlock provides a simple example
|
||||
* of defining a block.
|
||||
* - \Drupal\user\Plugin\Condition\UserRole is a straightforward example of a
|
||||
* block placement condition plugin.
|
||||
* - \Drupal\book\Plugin\Block\BookNavigationBlock is an example of a block with
|
||||
* a custom configuration form.
|
||||
* - For a more in-depth discussion of the Block API, see
|
||||
* https://www.drupal.org/developing/api/8/block_api.
|
||||
* - The Examples for Developers project also provides a Block example in
|
||||
* https://www.drupal.org/project/examples.
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter the result of \Drupal\Core\Block\BlockBase::build().
|
||||
*
|
||||
* This hook is called after the content has been assembled in a structured
|
||||
* array and may be used for doing processing which requires that the complete
|
||||
* block content structure has been built.
|
||||
*
|
||||
* If the module wishes to act on the rendered HTML of the block rather than
|
||||
* the structured content array, it may use this hook to add a #post_render
|
||||
* callback. Alternatively, it could also implement hook_preprocess_HOOK() for
|
||||
* block.html.twig. See drupal_render() documentation or the
|
||||
* @link themeable Default theme implementations topic @endlink for details.
|
||||
*
|
||||
* In addition to hook_block_view_alter(), which is called for all blocks, there
|
||||
* is hook_block_view_BASE_BLOCK_ID_alter(), which can be used to target a
|
||||
* specific block or set of similar blocks.
|
||||
*
|
||||
* @param array &$build
|
||||
* A renderable array of data, as returned from the build() implementation of
|
||||
* the plugin that defined the block:
|
||||
* - #title: The default localized title of the block.
|
||||
* @param \Drupal\Core\Block\BlockPluginInterface $block
|
||||
* The block plugin instance.
|
||||
*
|
||||
* @see hook_block_view_BASE_BLOCK_ID_alter()
|
||||
* @see entity_crud
|
||||
*
|
||||
* @ingroup block_api
|
||||
*/
|
||||
function hook_block_view_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) {
|
||||
// Remove the contextual links on all blocks that provide them.
|
||||
if (isset($build['#contextual_links'])) {
|
||||
unset($build['#contextual_links']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a block plugin specific block_view alteration.
|
||||
*
|
||||
* In this hook name, BASE_BLOCK_ID refers to the block implementation's plugin
|
||||
* id, regardless of whether the plugin supports derivatives. For example, for
|
||||
* the \Drupal\system\Plugin\Block\SystemPoweredByBlock block, this would be
|
||||
* 'system_powered_by_block' as per that class's annotation. And for the
|
||||
* \Drupal\system\Plugin\Block\SystemMenuBlock block, it would be
|
||||
* 'system_menu_block' as per that class's annotation, regardless of which menu
|
||||
* the derived block is for.
|
||||
*
|
||||
* @param array $build
|
||||
* A renderable array of data, as returned from the build() implementation of
|
||||
* the plugin that defined the block:
|
||||
* - #title: The default localized title of the block.
|
||||
* @param \Drupal\Core\Block\BlockPluginInterface $block
|
||||
* The block plugin instance.
|
||||
*
|
||||
* @see hook_block_view_alter()
|
||||
* @see entity_crud
|
||||
*
|
||||
* @ingroup block_api
|
||||
*/
|
||||
function hook_block_view_BASE_BLOCK_ID_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) {
|
||||
// Change the title of the specific block.
|
||||
$build['#title'] = t('New title of the block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Control access to a block instance.
|
||||
*
|
||||
* Modules may implement this hook if they want to have a say in whether or not
|
||||
* a given user has access to perform a given operation on a block instance.
|
||||
*
|
||||
* @param \Drupal\block\Entity\Block $block
|
||||
* The block instance.
|
||||
* @param string $operation
|
||||
* The operation to be performed, e.g., 'view', 'create', 'delete', 'update'.
|
||||
* @param \Drupal\user\Entity\User $account
|
||||
* The user object to perform the access check operation on.
|
||||
* @param string $langcode
|
||||
* The language code to perform the access check operation on.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result. If all implementations of this hook return
|
||||
* AccessResultInterface objects whose value is !isAllowed() and
|
||||
* !isForbidden(), then default access rules from
|
||||
* \Drupal\block\BlockAccessControlHandler::checkAccess() are used.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityAccessControlHandler::access()
|
||||
* @see \Drupal\block\BlockAccessControlHandler::checkAccess()
|
||||
* @ingroup block_api
|
||||
*/
|
||||
function hook_block_access(\Drupal\block\Entity\Block $block, $operation, \Drupal\user\Entity\User $account, $langcode) {
|
||||
// Example code that would prevent displaying the 'Powered by Drupal' block in
|
||||
// a region different than the footer.
|
||||
if ($operation == 'view' && $block->getPluginId() == 'system_powered_by_block') {
|
||||
return AccessResult::forbiddenIf($block->getRegion() != 'footer')->cacheUntilEntityChanges($block);
|
||||
}
|
||||
|
||||
// No opinion.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
7
core/modules/block/block.info.yml
Normal file
7
core/modules/block/block.info.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
name: Block
|
||||
type: module
|
||||
description: 'Controls the visual building blocks a page is constructed with. Blocks are boxes of content rendered into an area, or region, of a web page.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
configure: block.admin_display
|
19
core/modules/block/block.libraries.yml
Normal file
19
core/modules/block/block.libraries.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
drupal.block:
|
||||
version: VERSION
|
||||
js:
|
||||
js/block.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
|
||||
drupal.block.admin:
|
||||
version: VERSION
|
||||
js:
|
||||
js/block.admin.js: {}
|
||||
css:
|
||||
theme:
|
||||
css/block.admin.css: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupal.ajax
|
4
core/modules/block/block.links.contextual.yml
Normal file
4
core/modules/block/block.links.contextual.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
block_configure:
|
||||
title: 'Configure block'
|
||||
route_name: 'entity.block.edit_form'
|
||||
group: 'block'
|
5
core/modules/block/block.links.menu.yml
Normal file
5
core/modules/block/block.links.menu.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
block.admin_display:
|
||||
title: 'Block layout'
|
||||
parent: system.admin_structure
|
||||
description: 'Configure what block content appears in your site''s sidebars and other regions.'
|
||||
route_name: block.admin_display
|
15
core/modules/block/block.links.task.yml
Normal file
15
core/modules/block/block.links.task.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
entity.block.edit_form:
|
||||
title: 'Configure block'
|
||||
route_name: entity.block.edit_form
|
||||
base_route: entity.block.edit_form
|
||||
|
||||
# Per theme block layout pages.
|
||||
block.admin_display:
|
||||
title: 'Block layout'
|
||||
route_name: block.admin_display
|
||||
base_route: block.admin_display
|
||||
block.admin_display_theme:
|
||||
title: 'Block layout'
|
||||
route_name: block.admin_display_theme
|
||||
parent_id: block.admin_display
|
||||
deriver: 'Drupal\block\Plugin\Derivative\ThemeLocalTask'
|
319
core/modules/block/block.module
Normal file
319
core/modules/block/block.module
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Controls the visual building blocks a page is constructed with.
|
||||
*/
|
||||
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\ConfigurableLanguageInterface;
|
||||
use Drupal\system\Entity\Menu;
|
||||
use Drupal\block\Entity\Block;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function block_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.block':
|
||||
$block_content = \Drupal::moduleHandler()->moduleExists('block_content') ? \Drupal::url('help.page', array('name' => 'block_content')) : '#';
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Block module allows you to place blocks in regions of your installed themes, and configure block settings. For more information, see the <a href="!blocks-documentation">online documentation for the Block module</a>.', array('!blocks-documentation' => 'https://www.drupal.org/documentation/modules/block/')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Placing and moving blocks') . '</dt>';
|
||||
$output .= '<dd>' . t('You can place a new block by clicking on its title in the <em>Place blocks</em> list on the <a href="!blocks">Block layout page</a> and choosing a region to place it in. Once a block is placed, it can be moved to a different region by dragging or using the drop-down <em>Region</em> list, and then clicking <em>Save blocks</em>.', array('!blocks' => \Drupal::url('block.admin_display'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Toggling between different themes') . '</dt>';
|
||||
$output .= '<dd>' . t('Blocks are placed and configured specifically for each theme. The Block layout page opens with the default theme, but you can toggle to other installed themes.') . '</dd>';
|
||||
$output .= '<dt>' . t('Demonstrating block regions for a theme') . '</dt>';
|
||||
$output .= '<dd>' . t('You can see where the regions are for the current theme by clicking the <em>Demonstrate block regions</em> link on the <a href="!blocks">Block layout page</a>. Regions are specific to each theme.', array('!blocks' => \Drupal::url('block.admin_display'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Configuring block settings') . '</dt>';
|
||||
$output .= '<dd>' . t('To change the settings of an individual block click on the <em>Configure</em> link on the <a href="!blocks">Block layout page</a>. The available options vary depending on the module that provides the block. For all blocks you can change the block title and toggle whether to display it.', array('!blocks' => Drupal::url('block.admin_display'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Controlling visibility') . '</dt>';
|
||||
$output .= '<dd>' . t('You can control the visibility of a block by restricting it to specific pages, content types, and/or roles by setting the appropriate options under <em>Visibility settings</em> of the block configuration.') . '</dd>';
|
||||
$output .= '<dt>' . t('Adding custom blocks') . '</dt>';
|
||||
$output .= '<dd>' . t('You can add custom blocks, if the <em>Custom Block</em> module is installed. For more information, see the <a href="!blockcontent-help">Custom Block help page</a>.', array('!blockcontent-help' => $block_content)) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
if ($route_name == 'block.admin_display' || $route_name == 'block.admin_display_theme') {
|
||||
$demo_theme = $route_match->getParameter('theme') ?: \Drupal::config('system.theme')->get('default');
|
||||
$themes = \Drupal::service('theme_handler')->listInfo();
|
||||
$output = '<p>' . t('This page provides a drag-and-drop interface for adding a block to a region, and for controlling the order of blocks within regions. To add a block to a region, or to configure its specific title and visibility settings, click the block title under <em>Place blocks</em>. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the <em>Save blocks</em> button at the bottom of the page.') . '</p>';
|
||||
$output .= '<p>' . \Drupal::l(t('Demonstrate block regions (@theme)', array('@theme' => $themes[$demo_theme]->info['name'])), new Url('block.admin_demo', array('theme' => $demo_theme))) . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function block_theme() {
|
||||
return array(
|
||||
'block' => array(
|
||||
'render element' => 'elements',
|
||||
),
|
||||
'block_list' => array(
|
||||
'render element' => 'form',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_page_top().
|
||||
*/
|
||||
function block_page_top(array &$page_top) {
|
||||
if (\Drupal::routeMatch()->getRouteName() === 'block.admin_demo') {
|
||||
$theme = \Drupal::theme()->getActiveTheme()->getName();
|
||||
$page_top['backlink'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => t('Exit block region demonstration'),
|
||||
'#options' => array('attributes' => array('class' => array('block-demo-backlink'))),
|
||||
'#weight' => -10,
|
||||
);
|
||||
if (\Drupal::config('system.theme')->get('default') == $theme) {
|
||||
$page_top['backlink']['#url'] = Url::fromRoute('block.admin_display');
|
||||
}
|
||||
else {
|
||||
$page_top['backlink']['#url'] = Url::fromRoute('block.admin_display_theme', ['theme' => $theme]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of block class instances by theme.
|
||||
*
|
||||
* @param $theme
|
||||
* The theme to rehash blocks for. If not provided, defaults to the currently
|
||||
* used theme.
|
||||
*
|
||||
* @return
|
||||
* Blocks currently exported by modules.
|
||||
*/
|
||||
function _block_rehash($theme = NULL) {
|
||||
$theme = $theme ? $theme : \Drupal::config('system.theme')->get('default');
|
||||
$regions = system_region_list($theme);
|
||||
$blocks = entity_load_multiple_by_properties('block', array('theme' => $theme));
|
||||
foreach ($blocks as $block_id => $block) {
|
||||
// Remove any invalid block from the list.
|
||||
// @todo Remove this check as part of https://www.drupal.org/node/1776830.
|
||||
if (!$block->getPlugin()) {
|
||||
unset($blocks[$block_id]);
|
||||
continue;
|
||||
}
|
||||
$region = $block->getRegion();
|
||||
$status = $block->status();
|
||||
// Disable blocks in invalid regions.
|
||||
if (!empty($region) && $region != BlockInterface::BLOCK_REGION_NONE && !isset($regions[$region]) && $status) {
|
||||
drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block_id, '%region' => $region)), 'warning');
|
||||
// Disabled blocks are moved into the BlockInterface::BLOCK_REGION_NONE
|
||||
// later so no need to move the block to another region.
|
||||
$block->disable()->save();
|
||||
}
|
||||
// Set region to none if not enabled.
|
||||
if (!$status && $region != BlockInterface::BLOCK_REGION_NONE) {
|
||||
$block->setRegion(BlockInterface::BLOCK_REGION_NONE);
|
||||
$block->save();
|
||||
}
|
||||
}
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes blocks for installed themes.
|
||||
*
|
||||
* @param $theme_list
|
||||
* An array of theme names.
|
||||
*/
|
||||
function block_themes_installed($theme_list) {
|
||||
foreach ($theme_list as $theme) {
|
||||
block_theme_initialize($theme);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns an initial, default set of blocks for a theme.
|
||||
*
|
||||
* This function is called the first time a new theme is installed. The new
|
||||
* theme gets a copy of the default theme's blocks, with the difference that if
|
||||
* a particular region isn't available in the new theme, the block is assigned
|
||||
* to the new theme's default region.
|
||||
*
|
||||
* @param $theme
|
||||
* The name of a theme.
|
||||
*/
|
||||
function block_theme_initialize($theme) {
|
||||
// Initialize theme's blocks if none already registered.
|
||||
$has_blocks = entity_load_multiple_by_properties('block', array('theme' => $theme));
|
||||
if (!$has_blocks) {
|
||||
$default_theme = \Drupal::config('system.theme')->get('default');
|
||||
// Apply only to new theme's visible regions.
|
||||
$regions = system_region_list($theme, REGIONS_VISIBLE);
|
||||
$default_theme_blocks = entity_load_multiple_by_properties('block', array('theme' => $default_theme));
|
||||
foreach ($default_theme_blocks as $default_theme_block_id => $default_theme_block) {
|
||||
if (strpos($default_theme_block_id, $default_theme . '_') === 0) {
|
||||
$id = str_replace($default_theme, $theme, $default_theme_block_id);
|
||||
}
|
||||
else {
|
||||
$id = $theme . '_' . $default_theme_block_id;
|
||||
}
|
||||
$block = $default_theme_block->createDuplicateBlock($id, $theme);
|
||||
// If the region isn't supported by the theme, assign the block to the
|
||||
// theme's default region.
|
||||
if (!isset($regions[$block->getRegion()])) {
|
||||
$block->setRegion(system_default_region($theme));
|
||||
}
|
||||
$block->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rebuild().
|
||||
*/
|
||||
function block_rebuild() {
|
||||
foreach (\Drupal::service('theme_handler')->listInfo() as $name => $data) {
|
||||
if ($data->status) {
|
||||
_block_rehash($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme_suggestions_HOOK().
|
||||
*/
|
||||
function block_theme_suggestions_block(array $variables) {
|
||||
$suggestions = array();
|
||||
|
||||
$suggestions[] = 'block__' . $variables['elements']['#configuration']['provider'];
|
||||
// Hyphens (-) and underscores (_) play a special role in theme suggestions.
|
||||
// Theme suggestions should only contain underscores, because within
|
||||
// drupal_find_theme_templates(), underscores are converted to hyphens to
|
||||
// match template file names, and then converted back to underscores to match
|
||||
// pre-processing and other function names. So if your theme suggestion
|
||||
// contains a hyphen, it will end up as an underscore after this conversion,
|
||||
// and your function names won't be recognized. So, we need to convert
|
||||
// hyphens to underscores in block deltas for the theme suggestions.
|
||||
|
||||
// We can safely explode on : because we know the Block plugin type manager
|
||||
// enforces that delimiter for all derivatives.
|
||||
$parts = explode(':', $variables['elements']['#plugin_id']);
|
||||
$suggestion = 'block';
|
||||
while ($part = array_shift($parts)) {
|
||||
$suggestions[] = $suggestion .= '__' . strtr($part, '-', '_');
|
||||
}
|
||||
|
||||
if (!empty($variables['elements']['#id'])) {
|
||||
$suggestions[] = 'block__' . $variables['elements']['#id'];
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for block templates.
|
||||
*
|
||||
* Default template: block.html.twig.
|
||||
*
|
||||
* Prepares the values passed to the theme_block function to be passed
|
||||
* into a pluggable template engine. Uses block properties to generate a
|
||||
* series of template file suggestions. If none are found, the default
|
||||
* block.html.twig is used.
|
||||
*
|
||||
* Most themes use their own copy of block.html.twig. The default is located
|
||||
* inside "core/modules/block/templates/block.html.twig". Look in there for the
|
||||
* full list of available variables.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - elements: An associative array containing the properties of the element.
|
||||
* Properties used: #block, #configuration, #children, #plugin_id.
|
||||
*/
|
||||
function template_preprocess_block(&$variables) {
|
||||
$variables['configuration'] = $variables['elements']['#configuration'];
|
||||
$variables['plugin_id'] = $variables['elements']['#plugin_id'];
|
||||
$variables['base_plugin_id'] = $variables['elements']['#base_plugin_id'];
|
||||
$variables['derivative_plugin_id'] = $variables['elements']['#derivative_plugin_id'];
|
||||
$variables['label'] = !empty($variables['configuration']['label_display']) ? $variables['configuration']['label'] : '';
|
||||
$variables['content'] = $variables['elements']['content'];
|
||||
// A block's label is configuration: it is static. Allow dynamic labels to be
|
||||
// set in the render array.
|
||||
if (isset($variables['elements']['content']['#title']) && !empty($variables['configuration']['label_display'])) {
|
||||
$variables['label'] = $variables['elements']['content']['#title'];
|
||||
}
|
||||
|
||||
// Create a valid HTML ID and make sure it is unique.
|
||||
if (!empty($variables['elements']['#id'])) {
|
||||
$variables['attributes']['id'] = Html::getUniqueId('block-' . $variables['elements']['#id']);
|
||||
}
|
||||
|
||||
// Proactively add aria-describedby if possible to improve accessibility.
|
||||
if ($variables['label'] && isset($variables['attributes']['role'])) {
|
||||
$variables['title_attributes']['id'] = Html::getUniqueId($variables['label']);
|
||||
$variables['attributes']['aria-describedby'] = $variables['title_attributes']['id'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete() for user_role entities.
|
||||
*
|
||||
* Removes deleted role from blocks that use it.
|
||||
*/
|
||||
function block_user_role_delete($role) {
|
||||
foreach (Block::loadMultiple() as $block) {
|
||||
/** @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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete() for menu entities.
|
||||
*/
|
||||
function block_menu_delete(Menu $menu) {
|
||||
if (!$menu->isSyncing()) {
|
||||
foreach (Block::loadMultiple() as $block) {
|
||||
if ($block->getPluginId() == 'system_menu_block:' . $menu->id()) {
|
||||
$block->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete() for 'configurable_language'.
|
||||
*
|
||||
* Delete the potential block visibility settings of the deleted language.
|
||||
*/
|
||||
function block_configurable_language_delete(ConfigurableLanguageInterface $language) {
|
||||
// Remove the block visibility settings for the deleted language.
|
||||
foreach (Block::loadMultiple() as $block) {
|
||||
/** @var $block \Drupal\block\BlockInterface */
|
||||
$visibility = $block->getVisibility();
|
||||
if (isset($visibility['language']['langcodes'][$language->id()])) {
|
||||
unset($visibility['language']['langcodes'][$language->id()]);
|
||||
$block->setVisibilityConfig('language', $visibility['language']);
|
||||
$block->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function block_install() {
|
||||
// Because the Block module upon installation unconditionally overrides all
|
||||
// HTML output by selecting a different page display variant, we must
|
||||
// invalidate all cached HTML output.
|
||||
Cache::invalidateTags(['rendered']);
|
||||
}
|
2
core/modules/block/block.permissions.yml
Normal file
2
core/modules/block/block.permissions.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
administer blocks:
|
||||
title: 'Administer blocks'
|
59
core/modules/block/block.routing.yml
Normal file
59
core/modules/block/block.routing.yml
Normal file
|
@ -0,0 +1,59 @@
|
|||
block.admin_demo:
|
||||
path: '/admin/structure/block/demo/{theme}'
|
||||
defaults:
|
||||
_controller: '\Drupal\block\Controller\BlockController::demo'
|
||||
_title_callback: 'theme_handler:getName'
|
||||
requirements:
|
||||
_access_theme: 'TRUE'
|
||||
_permission: 'administer blocks'
|
||||
options:
|
||||
_admin_route: FALSE
|
||||
|
||||
entity.block.delete_form:
|
||||
path: '/admin/structure/block/manage/{block}/delete'
|
||||
defaults:
|
||||
_entity_form: 'block.delete'
|
||||
_title: 'Delete block'
|
||||
requirements:
|
||||
_permission: 'administer blocks'
|
||||
|
||||
entity.block.edit_form:
|
||||
path: '/admin/structure/block/manage/{block}'
|
||||
defaults:
|
||||
_entity_form: 'block.default'
|
||||
_title: 'Configure block'
|
||||
requirements:
|
||||
_entity_access: 'block.update'
|
||||
|
||||
block.admin_display:
|
||||
path: '/admin/structure/block'
|
||||
defaults:
|
||||
_controller: '\Drupal\block\Controller\BlockListController::listing'
|
||||
_title: 'Block layout'
|
||||
requirements:
|
||||
_permission: 'administer blocks'
|
||||
|
||||
block.admin_display_theme:
|
||||
path: 'admin/structure/block/list/{theme}'
|
||||
defaults:
|
||||
_controller: '\Drupal\block\Controller\BlockListController::listing'
|
||||
_title: 'Block layout'
|
||||
requirements:
|
||||
_access_theme: 'TRUE'
|
||||
_permission: 'administer blocks'
|
||||
|
||||
block.admin_add:
|
||||
path: '/admin/structure/block/add/{plugin_id}/{theme}'
|
||||
defaults:
|
||||
_controller: '\Drupal\block\Controller\BlockAddController::blockAddConfigureForm'
|
||||
theme: null
|
||||
_title: 'Configure block'
|
||||
requirements:
|
||||
_permission: 'administer blocks'
|
||||
|
||||
block.category_autocomplete:
|
||||
path: '/block-category/autocomplete'
|
||||
defaults:
|
||||
_controller: '\Drupal\block\Controller\CategoryAutocompleteController::autocomplete'
|
||||
requirements:
|
||||
_permission: 'administer blocks'
|
27
core/modules/block/block.services.yml
Normal file
27
core/modules/block/block.services.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
services:
|
||||
theme.negotiator.block.admin_demo:
|
||||
class: Drupal\block\Theme\AdminDemoNegotiator
|
||||
tags:
|
||||
- { name: theme_negotiator, priority: 1000 }
|
||||
block.page_display_variant_subscriber:
|
||||
class: Drupal\block\EventSubscriber\BlockPageDisplayVariantSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
block.current_user_context:
|
||||
class: Drupal\block\EventSubscriber\CurrentUserContext
|
||||
arguments: ['@current_user', '@entity.manager']
|
||||
tags:
|
||||
- { name: 'event_subscriber' }
|
||||
block.current_language_context:
|
||||
class: Drupal\block\EventSubscriber\CurrentLanguageContext
|
||||
arguments: ['@language_manager']
|
||||
tags:
|
||||
- { name: 'event_subscriber' }
|
||||
block.node_route_context:
|
||||
class: Drupal\block\EventSubscriber\NodeRouteContext
|
||||
arguments: ['@current_route_match']
|
||||
tags:
|
||||
- { name: 'event_subscriber' }
|
||||
block.repository:
|
||||
class: Drupal\block\BlockRepository
|
||||
arguments: ['@entity.manager', '@theme.manager', '@context.handler']
|
35
core/modules/block/config/schema/block.schema.yml
Normal file
35
core/modules/block/config/schema/block.schema.yml
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Schema for the configuration files of the Block module.
|
||||
|
||||
block.block.*:
|
||||
type: config_entity
|
||||
label: 'Block'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'ID'
|
||||
theme:
|
||||
type: string
|
||||
label: 'Theme'
|
||||
region:
|
||||
type: string
|
||||
label: 'Region'
|
||||
weight:
|
||||
type: integer
|
||||
label: 'Weight'
|
||||
provider:
|
||||
type: string
|
||||
label: 'Provider'
|
||||
plugin:
|
||||
type: string
|
||||
label: 'Plugin'
|
||||
settings:
|
||||
type: block.settings.[%parent.plugin]
|
||||
visibility:
|
||||
type: sequence
|
||||
label: 'Visibility Conditions'
|
||||
sequence:
|
||||
type: condition.plugin.[id]
|
||||
label: 'Visibility Condition'
|
||||
|
||||
block.settings.*:
|
||||
type: block_settings
|
108
core/modules/block/css/block.admin.css
Normal file
108
core/modules/block/css/block.admin.css
Normal file
|
@ -0,0 +1,108 @@
|
|||
.block-region {
|
||||
background-color: #ff6;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
padding: 3px;
|
||||
}
|
||||
a.block-demo-backlink,
|
||||
a.block-demo-backlink:link,
|
||||
a.block-demo-backlink:visited {
|
||||
background-color: #b4d7f0;
|
||||
border-radius: 0 0 10px 10px;
|
||||
color: #000;
|
||||
font-family: "Lucida Grande", Verdana, sans-serif;
|
||||
font-size: small;
|
||||
line-height: 20px;
|
||||
left: 20px; /*LTR*/
|
||||
padding: 5px 10px;
|
||||
position: fixed;
|
||||
z-index: 499;
|
||||
}
|
||||
a.block-demo-backlink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.layout-region {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.block-list-secondary {
|
||||
border: 1px solid #bfbfbf;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.block-list {
|
||||
padding: 0 0.75em;
|
||||
margin: 0;
|
||||
}
|
||||
.block-list li {
|
||||
list-style: none;
|
||||
padding: 0.1em 0;
|
||||
}
|
||||
.block-list a:before {
|
||||
content: '+ ';
|
||||
}
|
||||
.block-list-secondary .form-type-search {
|
||||
padding: 0 1em;
|
||||
}
|
||||
.block-form .form-item-settings-admin-label label {
|
||||
display: inline;
|
||||
}
|
||||
.block-form .form-item-settings-admin-label label:after {
|
||||
content: ':';
|
||||
}
|
||||
|
||||
/* Wide screens */
|
||||
@media
|
||||
screen and (min-width: 780px),
|
||||
(orientation: landscape) and (min-device-height: 780px) {
|
||||
|
||||
.block-list-primary {
|
||||
float: left; /* LTR */
|
||||
width: 75%;
|
||||
padding-right: 2em;
|
||||
}
|
||||
[dir="rtl"] .block-list-primary {
|
||||
float: right;
|
||||
padding-left: 2em;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.block-list-secondary {
|
||||
float: right; /* LTR */
|
||||
width: 25%;
|
||||
}
|
||||
[dir="rtl"] .block-list-secondary {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* @todo File an issue to add a standard class to all text-like inputs */
|
||||
.block-list-secondary .form-autocomplete,
|
||||
.block-list-secondary .form-text,
|
||||
.block-list-secondary .form-tel,
|
||||
.block-list-secondary .form-email,
|
||||
.block-list-secondary .form-url,
|
||||
.block-list-secondary .form-search,
|
||||
.block-list-secondary .form-number,
|
||||
.block-list-secondary .form-color,
|
||||
.block-list-secondary textarea {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The vertical toolbar mode gets triggered for narrow screens, which throws off
|
||||
* the intent of media queries written for the viewport width. When the vertical
|
||||
* toolbar is on, we need to suppress layout for the original media width + the
|
||||
* toolbar width (240px). In this case, 240px + 780px.
|
||||
*/
|
||||
@media
|
||||
screen and (max-width: 1020px) {
|
||||
|
||||
.toolbar-vertical.toolbar-tray-open .block-list-primary,
|
||||
.toolbar-vertical.toolbar-tray-open .block-list-secondary {
|
||||
float: none;
|
||||
width: auto;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
107
core/modules/block/js/block.admin.js
Normal file
107
core/modules/block/js/block.admin.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* @file
|
||||
* Block admin behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Filters the block list by a text input search string.
|
||||
*
|
||||
* Text search input: input.block-filter-text
|
||||
* Target element: input.block-filter-text[data-element]
|
||||
* Source text: .block-filter-text-source
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.blockFilterByText = {
|
||||
attach: function (context, settings) {
|
||||
var $input = $('input.block-filter-text').once('block-filter-text');
|
||||
var $element = $($input.attr('data-element'));
|
||||
var $blocks;
|
||||
var $details;
|
||||
|
||||
/**
|
||||
* Hides the `<details>` element for a category if it has no visible blocks.
|
||||
*
|
||||
* @param {number} index
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
function hideCategoryDetails(index, element) {
|
||||
var $catDetails = $(element);
|
||||
$catDetails.toggle($catDetails.find('li:visible').length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the block list.
|
||||
*
|
||||
* @param {jQuery.Event} e
|
||||
*/
|
||||
function filterBlockList(e) {
|
||||
var query = $(e.target).val().toLowerCase();
|
||||
|
||||
/**
|
||||
* Shows or hides the block entry based on the query.
|
||||
*
|
||||
* @param {number} index
|
||||
* @param {HTMLElement} block
|
||||
*/
|
||||
function showBlockEntry(index, block) {
|
||||
var $block = $(block);
|
||||
var $sources = $block.find('.block-filter-text-source');
|
||||
var textMatch = $sources.text().toLowerCase().indexOf(query) !== -1;
|
||||
$block.toggle(textMatch);
|
||||
}
|
||||
|
||||
// Filter if the length of the query is at least 2 characters.
|
||||
if (query.length >= 2) {
|
||||
$blocks.each(showBlockEntry);
|
||||
|
||||
// Note that we first open all <details> to be able to use ':visible'.
|
||||
// Mark the <details> elements that were closed before filtering, so
|
||||
// they can be reclosed when filtering is removed.
|
||||
$details.not('[open]').attr('data-drupal-block-state', 'forced-open');
|
||||
// Hide the category <details> if they don't have any visible rows.
|
||||
$details.attr('open', 'open').each(hideCategoryDetails);
|
||||
}
|
||||
else {
|
||||
$blocks.show();
|
||||
$details.show();
|
||||
// Return <details> elements that had been closed before filtering
|
||||
// to a closed state.
|
||||
$details.filter('[data-drupal-block-state="forced-open"]').removeAttr('open data-drupal-block-state');
|
||||
}
|
||||
}
|
||||
|
||||
if ($element.length) {
|
||||
$details = $element.find('details');
|
||||
$blocks = $details.find('li');
|
||||
|
||||
$input.on('keyup', filterBlockList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the block that was just placed into the block listing.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.blockHighlightPlacement = {
|
||||
attach: function (context, settings) {
|
||||
if (settings.blockPlacement) {
|
||||
$(context).find('[data-drupal-selector="edit-blocks"]').once('block-highlight').each(function () {
|
||||
var $container = $(this);
|
||||
// Just scrolling the document.body will not work in Firefox. The html
|
||||
// element is needed as well.
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.js-block-placed').offset().top - $container.offset().top + $container.scrollTop()
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery, Drupal));
|
144
core/modules/block/js/block.js
Normal file
144
core/modules/block/js/block.js
Normal file
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* @file
|
||||
* Block behaviors.
|
||||
*/
|
||||
|
||||
(function ($, window) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Provide the summary information for the block settings vertical tabs.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.blockSettingsSummary = {
|
||||
attach: function () {
|
||||
// The drupalSetSummary method required for this behavior is not available
|
||||
// on the Blocks administration page, so we need to make sure this
|
||||
// behavior is processed only if drupalSetSummary is defined.
|
||||
if (typeof jQuery.fn.drupalSetSummary === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
function checkboxesSummary(context) {
|
||||
var vals = [];
|
||||
var $checkboxes = $(context).find('input[type="checkbox"]:checked + label');
|
||||
var il = $checkboxes.length;
|
||||
for (var i = 0; i < il; i++) {
|
||||
vals.push($($checkboxes[i]).text());
|
||||
}
|
||||
if (!vals.length) {
|
||||
vals.push(Drupal.t('Not restricted'));
|
||||
}
|
||||
return vals.join(', ');
|
||||
}
|
||||
|
||||
$('[data-drupal-selector="edit-visibility-node-type"], [data-drupal-selector="edit-visibility-language"], [data-drupal-selector="edit-visibility-user-role"]').drupalSetSummary(checkboxesSummary);
|
||||
|
||||
$('[data-drupal-selector="edit-visibility-request-path"]').drupalSetSummary(function (context) {
|
||||
var $pages = $(context).find('textarea[name="visibility[request_path][pages]"]');
|
||||
if (!$pages.val()) {
|
||||
return Drupal.t('Not restricted');
|
||||
}
|
||||
else {
|
||||
return Drupal.t('Restricted to certain pages');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Move a block in the blocks table from one region to another via select list.
|
||||
*
|
||||
* This behavior is dependent on the tableDrag behavior, since it uses the
|
||||
* objects initialized in that behavior to update the row.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.blockDrag = {
|
||||
attach: function (context, settings) {
|
||||
// tableDrag is required and we should be on the blocks admin page.
|
||||
if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag.blocks === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var table = $('#blocks');
|
||||
// Get the blocks tableDrag object.
|
||||
var tableDrag = Drupal.tableDrag.blocks;
|
||||
// Add a handler for when a row is swapped, update empty regions.
|
||||
tableDrag.row.prototype.onSwap = function (swappedRow) {
|
||||
checkEmptyRegions(table, this);
|
||||
};
|
||||
|
||||
// Add a handler so when a row is dropped, update fields dropped into new regions.
|
||||
tableDrag.onDrop = function () {
|
||||
var dragObject = this;
|
||||
var $rowElement = $(dragObject.rowObject.element);
|
||||
// Use "region-message" row instead of "region" row because
|
||||
// "region-{region_name}-message" is less prone to regexp match errors.
|
||||
var regionRow = $rowElement.prevAll('tr.region-message').get(0);
|
||||
var regionName = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
|
||||
var regionField = $rowElement.find('select.block-region-select');
|
||||
// Check whether the newly picked region is available for this block.
|
||||
if (regionField.find('option[value=' + regionName + ']').length === 0) {
|
||||
// If not, alert the user and keep the block in its old region setting.
|
||||
window.alert(Drupal.t('The block cannot be placed in this region.'));
|
||||
// Simulate that there was a selected element change, so the row is put
|
||||
// back to from where the user tried to drag it.
|
||||
regionField.trigger('change');
|
||||
}
|
||||
else if ($rowElement.prev('tr').is('.region-message')) {
|
||||
var weightField = $rowElement.find('select.block-weight');
|
||||
var oldRegionName = weightField[0].className.replace(/([^ ]+[ ]+)*block-weight-([^ ]+)([ ]+[^ ]+)*/, '$2');
|
||||
|
||||
if (!regionField.is('.block-region-' + regionName)) {
|
||||
regionField.removeClass('block-region-' + oldRegionName).addClass('block-region-' + regionName);
|
||||
weightField.removeClass('block-weight-' + oldRegionName).addClass('block-weight-' + regionName);
|
||||
regionField.val(regionName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add the behavior to each region select list.
|
||||
$(context).find('select.block-region-select').once('block-region-select').each(function () {
|
||||
$(this).on('change', function (event) {
|
||||
// Make our new row and select field.
|
||||
var row = $(this).closest('tr');
|
||||
var select = $(this);
|
||||
tableDrag.rowObject = new tableDrag.row(row);
|
||||
|
||||
// Find the correct region and insert the row as the last in the region.
|
||||
table.find('.region-' + select[0].value + '-message').nextUntil('.region-message').eq(-1).before(row);
|
||||
|
||||
// Modify empty regions with added or removed fields.
|
||||
checkEmptyRegions(table, row);
|
||||
// Remove focus from selectbox.
|
||||
select.trigger('blur');
|
||||
});
|
||||
});
|
||||
|
||||
var checkEmptyRegions = function (table, rowObject) {
|
||||
table.find('tr.region-message').each(function () {
|
||||
var $this = $(this);
|
||||
// If the dragged row is in this region, but above the message row, swap it down one space.
|
||||
if ($this.prev('tr').get(0) === rowObject.element) {
|
||||
// Prevent a recursion problem when using the keyboard to move rows up.
|
||||
if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) {
|
||||
rowObject.swap('after', this);
|
||||
}
|
||||
}
|
||||
// This region has become empty.
|
||||
if ($this.next('tr').is(':not(.draggable)') || $this.next('tr').length === 0) {
|
||||
$this.removeClass('region-populated').addClass('region-empty');
|
||||
}
|
||||
// This region has become populated.
|
||||
else if ($this.is('.region-empty')) {
|
||||
$this.removeClass('region-empty').addClass('region-populated');
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, window);
|
118
core/modules/block/src/BlockAccessControlHandler.php
Normal file
118
core/modules/block/src/BlockAccessControlHandler.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockAccessControlHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\ContextException;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Condition\ConditionAccessResolverTrait;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Executable\ExecutableManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the block entity type.
|
||||
*
|
||||
* @see \Drupal\block\Entity\Block
|
||||
*/
|
||||
class BlockAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
|
||||
|
||||
use ConditionAccessResolverTrait;
|
||||
|
||||
/**
|
||||
* The condition plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Executable\ExecutableManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The plugin context handler.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
|
||||
*/
|
||||
protected $contextHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('plugin.manager.condition'),
|
||||
$container->get('context.handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the block access control handler instance
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
|
||||
* The ConditionManager for checking visibility of blocks.
|
||||
* @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
|
||||
* The ContextHandler for applying contexts to conditions properly.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, ExecutableManagerInterface $manager, ContextHandlerInterface $context_handler) {
|
||||
parent::__construct($entity_type);
|
||||
$this->manager = $manager;
|
||||
$this->contextHandler = $context_handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
/** @var \Drupal\block\BlockInterface $entity */
|
||||
if ($operation != 'view') {
|
||||
return parent::checkAccess($entity, $operation, $langcode, $account);
|
||||
}
|
||||
|
||||
// Don't grant access to disabled blocks.
|
||||
if (!$entity->status()) {
|
||||
return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
else {
|
||||
$contexts = $entity->getContexts();
|
||||
$conditions = [];
|
||||
foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
|
||||
if ($condition instanceof ContextAwarePluginInterface) {
|
||||
try {
|
||||
$this->contextHandler->applyContextMapping($condition, $contexts);
|
||||
}
|
||||
catch (ContextException $e) {
|
||||
return AccessResult::forbidden()->setCacheMaxAge(0);
|
||||
}
|
||||
}
|
||||
$conditions[$condition_id] = $condition;
|
||||
}
|
||||
if ($this->resolveConditions($conditions, 'and') !== FALSE) {
|
||||
// Delegate to the plugin.
|
||||
$access = $entity->getPlugin()->access($account, TRUE);
|
||||
}
|
||||
else {
|
||||
$access = AccessResult::forbidden();
|
||||
}
|
||||
// This should not be hardcoded to an uncacheable access check result, but
|
||||
// in order to fix that, we need condition plugins to return cache contexts,
|
||||
// otherwise it will be impossible to determine by which cache contexts the
|
||||
// result should be varied.
|
||||
// @todo Change this to use $access->cacheUntilEntityChanges($entity) once
|
||||
// https://www.drupal.org/node/2375695 is resolved.
|
||||
return $access->setCacheMaxAge(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
397
core/modules/block/src/BlockForm.php
Normal file
397
core/modules/block/src/BlockForm.php
Normal file
|
@ -0,0 +1,397 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\block\Event\BlockContextEvent;
|
||||
use Drupal\block\Event\BlockEvents;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Executable\ExecutableManagerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Provides form for block instance forms.
|
||||
*/
|
||||
class BlockForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The block entity.
|
||||
*
|
||||
* @var \Drupal\block\BlockInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* The condition plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Condition\ConditionManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The event dispatcher service.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* The language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandler
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a BlockForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
|
||||
* The ConditionManager for building the visibility UI.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* The EventDispatcher for gathering administrative contexts.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, EventDispatcherInterface $dispatcher, LanguageManagerInterface $language, ThemeHandlerInterface $theme_handler) {
|
||||
$this->storage = $entity_manager->getStorage('block');
|
||||
$this->manager = $manager;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->language = $language;
|
||||
$this->themeHandler = $theme_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('plugin.manager.condition'),
|
||||
$container->get('event_dispatcher'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('theme_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$entity = $this->entity;
|
||||
|
||||
// Store theme settings in $form_state for use below.
|
||||
if (!$theme = $entity->getTheme()) {
|
||||
$theme = $this->config('system.theme')->get('default');
|
||||
}
|
||||
$form_state->set('block_theme', $theme);
|
||||
|
||||
// Store the gathered contexts in the form state for other objects to use
|
||||
// during form building.
|
||||
$form_state->setTemporaryValue('gathered_contexts', $this->dispatcher->dispatch(BlockEvents::ADMINISTRATIVE_CONTEXT, new BlockContextEvent())->getContexts());
|
||||
|
||||
$form['#tree'] = TRUE;
|
||||
$form['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
|
||||
$form['visibility'] = $this->buildVisibilityInterface([], $form_state);
|
||||
|
||||
// If creating a new block, calculate a safe default machine name.
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#maxlength' => 64,
|
||||
'#description' => $this->t('A unique name for this block instance. Must be alpha-numeric and underscore separated.'),
|
||||
'#default_value' => !$entity->isNew() ? $entity->id() : $this->getUniqueMachineName($entity),
|
||||
'#machine_name' => array(
|
||||
'exists' => '\Drupal\block\Entity\Block::load',
|
||||
'replace_pattern' => '[^a-z0-9_.]+',
|
||||
'source' => array('settings', 'label'),
|
||||
),
|
||||
'#required' => TRUE,
|
||||
'#disabled' => !$entity->isNew(),
|
||||
);
|
||||
|
||||
// Theme settings.
|
||||
if ($entity->getTheme()) {
|
||||
$form['theme'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $theme,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$theme_options = array();
|
||||
foreach ($this->themeHandler->listInfo() as $theme_name => $theme_info) {
|
||||
if (!empty($theme_info->status)) {
|
||||
$theme_options[$theme_name] = $theme_info->info['name'];
|
||||
}
|
||||
}
|
||||
$form['theme'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $theme_options,
|
||||
'#title' => t('Theme'),
|
||||
'#default_value' => $theme,
|
||||
'#ajax' => array(
|
||||
'callback' => '::themeSwitch',
|
||||
'wrapper' => 'edit-block-region-wrapper',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Region settings.
|
||||
$form['region'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Region'),
|
||||
'#description' => $this->t('Select the region where this block should be displayed.'),
|
||||
'#default_value' => $entity->getRegion(),
|
||||
'#empty_value' => BlockInterface::BLOCK_REGION_NONE,
|
||||
'#options' => system_region_list($theme, REGIONS_VISIBLE),
|
||||
'#prefix' => '<div id="edit-block-region-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['#attached']['library'][] = 'block/drupal.block.admin';
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles switching the available regions based on the selected theme.
|
||||
*/
|
||||
public function themeSwitch($form, FormStateInterface $form_state) {
|
||||
$form['region']['#options'] = system_region_list($form_state->getValue('theme'), REGIONS_VISIBLE);
|
||||
return $form['region'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for building the visibility UI form.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* The form array with the visibility UI added in.
|
||||
*/
|
||||
protected function buildVisibilityInterface(array $form, FormStateInterface $form_state) {
|
||||
$form['visibility_tabs'] = [
|
||||
'#type' => 'vertical_tabs',
|
||||
'#title' => $this->t('Visibility'),
|
||||
'#parents' => ['visibility_tabs'],
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'block/drupal.block',
|
||||
],
|
||||
],
|
||||
];
|
||||
// @todo Allow list of conditions to be configured in
|
||||
// https://www.drupal.org/node/2284687.
|
||||
$visibility = $this->entity->getVisibility();
|
||||
foreach ($this->manager->getDefinitions() as $condition_id => $definition) {
|
||||
// Don't display the current theme condition.
|
||||
if ($condition_id == 'current_theme') {
|
||||
continue;
|
||||
}
|
||||
// Don't display the language condition until we have multiple languages.
|
||||
if ($condition_id == 'language' && !$this->language->isMultilingual()) {
|
||||
continue;
|
||||
}
|
||||
/** @var \Drupal\Core\Condition\ConditionInterface $condition */
|
||||
$condition = $this->manager->createInstance($condition_id, isset($visibility[$condition_id]) ? $visibility[$condition_id] : []);
|
||||
$form_state->set(['conditions', $condition_id], $condition);
|
||||
$condition_form = $condition->buildConfigurationForm([], $form_state);
|
||||
$condition_form['#type'] = 'details';
|
||||
$condition_form['#title'] = $condition->getPluginDefinition()['label'];
|
||||
$condition_form['#group'] = 'visibility_tabs';
|
||||
$form[$condition_id] = $condition_form;
|
||||
}
|
||||
|
||||
if (isset($form['node_type'])) {
|
||||
$form['node_type']['#title'] = $this->t('Content types');
|
||||
$form['node_type']['bundles']['#title'] = $this->t('Content types');
|
||||
$form['node_type']['negate']['#type'] = 'value';
|
||||
$form['node_type']['negate']['#title_display'] = 'invisible';
|
||||
$form['node_type']['negate']['#value'] = $form['node_type']['negate']['#default_value'];
|
||||
}
|
||||
if (isset($form['user_role'])) {
|
||||
$form['user_role']['#title'] = $this->t('Roles');
|
||||
unset($form['user_role']['roles']['#description']);
|
||||
$form['user_role']['negate']['#type'] = 'value';
|
||||
$form['user_role']['negate']['#value'] = $form['user_role']['negate']['#default_value'];
|
||||
}
|
||||
if (isset($form['request_path'])) {
|
||||
$form['request_path']['#title'] = $this->t('Pages');
|
||||
$form['request_path']['negate']['#type'] = 'radios';
|
||||
$form['request_path']['negate']['#default_value'] = (int) $form['request_path']['negate']['#default_value'];
|
||||
$form['request_path']['negate']['#title_display'] = 'invisible';
|
||||
$form['request_path']['negate']['#options'] = [
|
||||
$this->t('Show for the listed pages'),
|
||||
$this->t('Hide for the listed pages'),
|
||||
];
|
||||
}
|
||||
if (isset($form['language'])) {
|
||||
$form['language']['negate']['#type'] = 'value';
|
||||
$form['language']['negate']['#value'] = $form['language']['negate']['#default_value'];
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save block');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array $form, FormStateInterface $form_state) {
|
||||
parent::validate($form, $form_state);
|
||||
|
||||
// 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 = (new FormState())->setValues($form_state->getValue('settings'));
|
||||
// Call the plugin validate handler.
|
||||
$this->entity->getPlugin()->validateConfigurationForm($form, $settings);
|
||||
// Update the original form values.
|
||||
$form_state->setValue('settings', $settings->getValues());
|
||||
$this->validateVisibility($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to independently validate the visibility UI.
|
||||
*
|
||||
* @param array $form
|
||||
* A nested array form elements comprising the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected function validateVisibility(array $form, FormStateInterface $form_state) {
|
||||
// Validate visibility condition settings.
|
||||
foreach ($form_state->getValue('visibility') as $condition_id => $values) {
|
||||
// All condition plugins use 'negate' as a Boolean in their schema.
|
||||
// However, certain form elements may return it as 0/1. Cast here to
|
||||
// ensure the data is in the expected type.
|
||||
if (array_key_exists('negate', $values)) {
|
||||
$values['negate'] = (bool) $values['negate'];
|
||||
}
|
||||
|
||||
// Allow the condition to validate the form.
|
||||
$condition = $form_state->get(['conditions', $condition_id]);
|
||||
$condition_values = (new FormState())
|
||||
->setValues($values);
|
||||
$condition->validateConfigurationForm($form, $condition_values);
|
||||
// Update the original form values.
|
||||
$form_state->setValue(['visibility', $condition_id], $condition_values->getValues());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
$entity = $this->entity;
|
||||
// The Block Entity form puts all block plugin form elements in the
|
||||
// settings form element, so just pass that to the block for submission.
|
||||
// @todo Find a way to avoid this manipulation.
|
||||
$settings = (new FormState())->setValues($form_state->getValue('settings'));
|
||||
|
||||
// Call the plugin submit handler.
|
||||
$entity->getPlugin()->submitConfigurationForm($form, $settings);
|
||||
// Update the original form values.
|
||||
$form_state->setValue('settings', $settings->getValues());
|
||||
|
||||
// Submit visibility condition settings.
|
||||
foreach ($form_state->getValue('visibility') as $condition_id => $values) {
|
||||
// Allow the condition to submit the form.
|
||||
$condition = $form_state->get(['conditions', $condition_id]);
|
||||
$condition_values = (new FormState())
|
||||
->setValues($values);
|
||||
$condition->submitConfigurationForm($form, $condition_values);
|
||||
if ($condition instanceof ContextAwarePluginInterface) {
|
||||
$context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
|
||||
$condition->setContextMapping($context_mapping);
|
||||
}
|
||||
// Update the original form values.
|
||||
$condition_configuration = $condition->getConfiguration();
|
||||
$form_state->setValue(['visibility', $condition_id], $condition_configuration);
|
||||
// Update the visibility conditions on the block.
|
||||
$entity->getVisibilityConditions()->addInstanceId($condition_id, $condition_configuration);
|
||||
}
|
||||
|
||||
// Save the settings of the plugin.
|
||||
$entity->save();
|
||||
|
||||
drupal_set_message($this->t('The block configuration has been saved.'));
|
||||
$form_state->setRedirect(
|
||||
'block.admin_display_theme',
|
||||
array(
|
||||
'theme' => $form_state->getValue('theme'),
|
||||
),
|
||||
array('query' => array('block-placement' => Html::getClass($this->entity->id())))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique machine name for a block.
|
||||
*
|
||||
* @param \Drupal\block\BlockInterface $block
|
||||
* The block entity.
|
||||
*
|
||||
* @return string
|
||||
* Returns the unique name.
|
||||
*/
|
||||
public function getUniqueMachineName(BlockInterface $block) {
|
||||
$suggestion = $block->getPlugin()->getMachineNameSuggestion();
|
||||
|
||||
// Get all the blocks which starts with the suggested machine name.
|
||||
$query = $this->storage->getQuery();
|
||||
$query->condition('id', $suggestion, 'CONTAINS');
|
||||
$block_ids = $query->execute();
|
||||
|
||||
$block_ids = array_map(function ($block_id) {
|
||||
$parts = explode('.', $block_id);
|
||||
return end($parts);
|
||||
}, $block_ids);
|
||||
|
||||
// Iterate through potential IDs until we get a new one. E.g.
|
||||
// 'plugin', 'plugin_2', 'plugin_3', etc.
|
||||
$count = 1;
|
||||
$machine_default = $suggestion;
|
||||
while (in_array($machine_default, $block_ids)) {
|
||||
$machine_default = $suggestion . '_' . ++$count;
|
||||
}
|
||||
return $machine_default;
|
||||
}
|
||||
|
||||
}
|
158
core/modules/block/src/BlockInterface.php
Normal file
158
core/modules/block/src/BlockInterface.php
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a block entity.
|
||||
*/
|
||||
interface BlockInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Indicates the block label (title) should be displayed to end users.
|
||||
*/
|
||||
const BLOCK_LABEL_VISIBLE = 'visible';
|
||||
|
||||
/**
|
||||
* Denotes that a block is not enabled in any region and should not be shown.
|
||||
*/
|
||||
const BLOCK_REGION_NONE = -1;
|
||||
|
||||
/**
|
||||
* Returns the plugin instance.
|
||||
*
|
||||
* @return \Drupal\Core\Block\BlockPluginInterface
|
||||
* The plugin instance for this block.
|
||||
*/
|
||||
public function getPlugin();
|
||||
|
||||
/**
|
||||
* Returns the plugin ID.
|
||||
*
|
||||
* @return string
|
||||
* The plugin ID for this block.
|
||||
*/
|
||||
public function getPluginId();
|
||||
|
||||
/**
|
||||
* Returns the region this block is placed in.
|
||||
*
|
||||
* @return string
|
||||
* The region this block is placed in.
|
||||
*/
|
||||
public function getRegion();
|
||||
|
||||
/**
|
||||
* Returns the theme ID.
|
||||
*
|
||||
* @return string
|
||||
* The theme ID for this block instance.
|
||||
*/
|
||||
public function getTheme();
|
||||
|
||||
/**
|
||||
* Returns an array of visibility condition configurations.
|
||||
*
|
||||
* @return array
|
||||
* An array of visibility condition configuration keyed by the condition ID.
|
||||
*/
|
||||
public function getVisibility();
|
||||
|
||||
/**
|
||||
* Gets conditions for this block.
|
||||
*
|
||||
* @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
|
||||
* An array or collection 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);
|
||||
|
||||
/**
|
||||
* Get all available contexts.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
||||
* An array of set contexts, keyed by context name.
|
||||
*/
|
||||
public function getContexts();
|
||||
|
||||
/**
|
||||
* Set the contexts that are available for use within the block entity.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts to set on the block.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContexts(array $contexts);
|
||||
|
||||
/**
|
||||
* Returns the weight of this block (used for sorting).
|
||||
*
|
||||
* @return int
|
||||
* The block weight.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Sets the region this block is placed in.
|
||||
*
|
||||
* @param string $region
|
||||
* The region to place this block in.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRegion($region);
|
||||
|
||||
/**
|
||||
* Sets the block weight.
|
||||
*
|
||||
* @param int $weight
|
||||
* The desired weight.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
/**
|
||||
* Creates a duplicate of the block entity.
|
||||
*
|
||||
* @param string $new_id
|
||||
* (optional) The new ID on the duplicate block.
|
||||
* @param string $new_theme
|
||||
* (optional) The theme on the duplicate block.
|
||||
*
|
||||
* @return static
|
||||
* A clone of $this with all identifiers unset, so saving it inserts a new
|
||||
* entity into the storage system.
|
||||
*/
|
||||
public function createDuplicateBlock($new_id = NULL, $new_theme = NULL);
|
||||
|
||||
}
|
420
core/modules/block/src/BlockListBuilder.php
Normal file
420
core/modules/block/src/BlockListBuilder.php
Normal file
|
@ -0,0 +1,420 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of block entities.
|
||||
*
|
||||
* @see \Drupal\block\Entity\Block
|
||||
*/
|
||||
class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface {
|
||||
|
||||
/**
|
||||
* The regions containing the blocks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $regions;
|
||||
|
||||
/**
|
||||
* The theme containing the blocks.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* The current request.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The block manager.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockManagerInterface
|
||||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* Constructs a new BlockListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
|
||||
* The block manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, BlockManagerInterface $block_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
|
||||
$this->blockManager = $block_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('plugin.manager.block')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
// If no theme was specified, use the current theme.
|
||||
if (!$this->theme) {
|
||||
$this->theme = \Drupal::theme()->getActiveTheme()->getName();
|
||||
}
|
||||
|
||||
// Store the region list.
|
||||
$this->regions = system_region_list($this->theme, REGIONS_VISIBLE);
|
||||
|
||||
// Load only blocks for this theme, and sort them.
|
||||
// @todo Move the functionality of _block_rehash() out of the listing page.
|
||||
$entities = _block_rehash($this->theme);
|
||||
|
||||
// Sort the blocks using \Drupal\block\Entity\Block::sort().
|
||||
uasort($entities, array($this->entityType->getClass(), 'sort'));
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string|null $theme
|
||||
* (optional) The theme to display the blocks for. If NULL, the current
|
||||
* theme will be used.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* The block list as a renderable array.
|
||||
*/
|
||||
public function render($theme = NULL, Request $request = NULL) {
|
||||
$this->request = $request;
|
||||
// If no theme was specified, use the current theme.
|
||||
$this->theme = $theme ?: \Drupal::theme()->getActiveTheme()->getName();
|
||||
|
||||
return \Drupal::formBuilder()->getForm($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'block_admin_display_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::buildForm().
|
||||
*
|
||||
* Form constructor for the main block administration form.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$placement = FALSE;
|
||||
if ($this->request->query->has('block-placement')) {
|
||||
$placement = $this->request->query->get('block-placement');
|
||||
$form['#attached']['drupalSettings']['blockPlacement'] = $placement;
|
||||
}
|
||||
$entities = $this->load();
|
||||
$form['#theme'] = array('block_list');
|
||||
$form['#attached']['library'][] = 'core/drupal.tableheader';
|
||||
$form['#attached']['library'][] = 'block/drupal.block';
|
||||
$form['#attached']['library'][] = 'block/drupal.block.admin';
|
||||
$form['#attributes']['class'][] = 'clearfix';
|
||||
|
||||
// Add a last region for disabled blocks.
|
||||
$block_regions_with_disabled = $this->regions + array(BlockInterface::BLOCK_REGION_NONE => BlockInterface::BLOCK_REGION_NONE);
|
||||
$form['block_regions'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $block_regions_with_disabled,
|
||||
);
|
||||
|
||||
// Weights range from -delta to +delta, so delta should be at least half
|
||||
// of the amount of blocks present. This makes sure all blocks in the same
|
||||
// region get an unique weight.
|
||||
$weight_delta = round(count($entities) / 2);
|
||||
|
||||
// Build the form tree.
|
||||
$form['edited_theme'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $this->theme,
|
||||
);
|
||||
$form['blocks'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array(
|
||||
t('Block'),
|
||||
t('Category'),
|
||||
t('Region'),
|
||||
t('Weight'),
|
||||
t('Operations'),
|
||||
),
|
||||
'#attributes' => array(
|
||||
'id' => 'blocks',
|
||||
),
|
||||
);
|
||||
|
||||
// Build blocks first for each region.
|
||||
foreach ($entities as $entity_id => $entity) {
|
||||
$definition = $entity->getPlugin()->getPluginDefinition();
|
||||
$blocks[$entity->getRegion()][$entity_id] = array(
|
||||
'label' => $entity->label(),
|
||||
'entity_id' => $entity_id,
|
||||
'weight' => $entity->getWeight(),
|
||||
'entity' => $entity,
|
||||
'category' => $definition['category'],
|
||||
);
|
||||
}
|
||||
|
||||
// Loop over each region and build blocks.
|
||||
foreach ($block_regions_with_disabled as $region => $title) {
|
||||
$form['blocks']['#tabledrag'][] = array(
|
||||
'action' => 'match',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'block-region-select',
|
||||
'subgroup' => 'block-region-' . $region,
|
||||
'hidden' => FALSE,
|
||||
);
|
||||
$form['blocks']['#tabledrag'][] = array(
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'block-weight',
|
||||
'subgroup' => 'block-weight-' . $region,
|
||||
);
|
||||
|
||||
$form['blocks'][$region] = array(
|
||||
'#attributes' => array(
|
||||
'class' => array('region-title', 'region-title-' . $region),
|
||||
'no_striping' => TRUE,
|
||||
),
|
||||
);
|
||||
$form['blocks'][$region]['title'] = array(
|
||||
'#markup' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : t('Disabled', array(), array('context' => 'Plural')),
|
||||
'#wrapper_attributes' => array(
|
||||
'colspan' => 5,
|
||||
),
|
||||
);
|
||||
|
||||
$form['blocks'][$region . '-message'] = array(
|
||||
'#attributes' => array(
|
||||
'class' => array(
|
||||
'region-message',
|
||||
'region-' . $region . '-message',
|
||||
empty($blocks[$region]) ? 'region-empty' : 'region-populated',
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['blocks'][$region . '-message']['message'] = array(
|
||||
'#markup' => '<em>' . t('No blocks in this region') . '</em>',
|
||||
'#wrapper_attributes' => array(
|
||||
'colspan' => 5,
|
||||
),
|
||||
);
|
||||
|
||||
if (isset($blocks[$region])) {
|
||||
foreach ($blocks[$region] as $info) {
|
||||
$entity_id = $info['entity_id'];
|
||||
|
||||
$form['blocks'][$entity_id] = array(
|
||||
'#attributes' => array(
|
||||
'class' => array('draggable'),
|
||||
),
|
||||
);
|
||||
if ($placement && $placement == Html::getClass($entity_id)) {
|
||||
$form['blocks'][$entity_id]['#attributes']['class'][] = 'color-warning';
|
||||
$form['blocks'][$entity_id]['#attributes']['class'][] = 'js-block-placed';
|
||||
}
|
||||
$form['blocks'][$entity_id]['info'] = array(
|
||||
'#markup' => SafeMarkup::checkPlain($info['label']),
|
||||
'#wrapper_attributes' => array(
|
||||
'class' => array('block'),
|
||||
),
|
||||
);
|
||||
$form['blocks'][$entity_id]['type'] = array(
|
||||
'#markup' => $info['category'],
|
||||
);
|
||||
$form['blocks'][$entity_id]['region-theme']['region'] = array(
|
||||
'#type' => 'select',
|
||||
'#default_value' => $region,
|
||||
'#empty_value' => BlockInterface::BLOCK_REGION_NONE,
|
||||
'#title' => t('Region for @block block', array('@block' => $info['label'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $this->regions,
|
||||
'#attributes' => array(
|
||||
'class' => array('block-region-select', 'block-region-' . $region),
|
||||
),
|
||||
'#parents' => array('blocks', $entity_id, 'region'),
|
||||
);
|
||||
$form['blocks'][$entity_id]['region-theme']['theme'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $this->theme,
|
||||
'#parents' => array('blocks', $entity_id, 'theme'),
|
||||
);
|
||||
$form['blocks'][$entity_id]['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#default_value' => $info['weight'],
|
||||
'#delta' => $weight_delta,
|
||||
'#title' => t('Weight for @block block', array('@block' => $info['label'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#attributes' => array(
|
||||
'class' => array('block-weight', 'block-weight-' . $region),
|
||||
),
|
||||
);
|
||||
$form['blocks'][$entity_id]['operations'] = $this->buildOperations($info['entity']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow disabling the main system content block when it is present.
|
||||
if (isset($form['blocks']['system_main']['region'])) {
|
||||
$form['blocks']['system_main']['region']['#required'] = TRUE;
|
||||
}
|
||||
|
||||
$form['actions'] = array(
|
||||
'#tree' => FALSE,
|
||||
'#type' => 'actions',
|
||||
);
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save blocks'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
|
||||
$form['place_blocks']['title'] = array(
|
||||
'#type' => 'container',
|
||||
'#markup' => '<h3>' . t('Place blocks') . '</h3>',
|
||||
'#attributes' => array(
|
||||
'class' => array(
|
||||
'entity-meta__header',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form['place_blocks']['filter'] = array(
|
||||
'#type' => 'search',
|
||||
'#title' => t('Filter'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 30,
|
||||
'#placeholder' => t('Filter by block name'),
|
||||
'#attributes' => array(
|
||||
'class' => array('block-filter-text'),
|
||||
'data-element' => '.entity-meta',
|
||||
'title' => t('Enter a part of the block name to filter by.'),
|
||||
),
|
||||
);
|
||||
|
||||
$form['place_blocks']['list']['#type'] = 'container';
|
||||
$form['place_blocks']['list']['#attributes']['class'][] = 'entity-meta';
|
||||
|
||||
// Only add blocks which work without any available context.
|
||||
$definitions = $this->blockManager->getDefinitionsForContexts();
|
||||
$sorted_definitions = $this->blockManager->getSortedDefinitions($definitions);
|
||||
foreach ($sorted_definitions as $plugin_id => $plugin_definition) {
|
||||
$category = SafeMarkup::checkPlain($plugin_definition['category']);
|
||||
$category_key = 'category-' . $category;
|
||||
if (!isset($form['place_blocks']['list'][$category_key])) {
|
||||
$form['place_blocks']['list'][$category_key] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $category,
|
||||
'#open' => TRUE,
|
||||
'content' => array(
|
||||
'#theme' => 'links',
|
||||
'#links' => array(),
|
||||
'#attributes' => array(
|
||||
'class' => array(
|
||||
'block-list',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
$form['place_blocks']['list'][$category_key]['content']['#links'][$plugin_id] = array(
|
||||
'title' => $plugin_definition['admin_label'],
|
||||
'url' => Url::fromRoute('block.admin_add', [
|
||||
'plugin_id' => $plugin_id,
|
||||
'theme' => $this->theme
|
||||
]),
|
||||
'attributes' => array(
|
||||
'class' => array('use-ajax', 'block-filter-text-source'),
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => Json::encode(array(
|
||||
'width' => 700,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
if (isset($operations['edit'])) {
|
||||
$operations['edit']['title'] = t('Configure');
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::validateForm().
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// No validation.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::submitForm().
|
||||
*
|
||||
* Form submission handler for the main block administration form.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$entities = $this->storage->loadMultiple(array_keys($form_state->getValue('blocks')));
|
||||
foreach ($entities as $entity_id => $entity) {
|
||||
$entity_values = $form_state->getValue(array('blocks', $entity_id));
|
||||
$entity->setWeight($entity_values['weight']);
|
||||
$entity->setRegion($entity_values['region']);
|
||||
if ($entity->getRegion() == BlockInterface::BLOCK_REGION_NONE) {
|
||||
$entity->disable();
|
||||
}
|
||||
else {
|
||||
$entity->enable();
|
||||
}
|
||||
$entity->save();
|
||||
}
|
||||
drupal_set_message(t('The block settings have been updated.'));
|
||||
|
||||
// Remove any previously set block placement.
|
||||
$this->request->query->remove('block-placement');
|
||||
}
|
||||
|
||||
}
|
76
core/modules/block/src/BlockPluginCollection.php
Normal file
76
core/modules/block/src/BlockPluginCollection.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockPluginCollection.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
|
||||
|
||||
/**
|
||||
* Provides a collection of block plugins.
|
||||
*/
|
||||
class BlockPluginCollection extends DefaultSingleLazyPluginCollection {
|
||||
|
||||
/**
|
||||
* The block ID this plugin collection belongs to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $blockId;
|
||||
|
||||
/**
|
||||
* Constructs a new BlockPluginCollection.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
|
||||
* The manager to be used for instantiating plugins.
|
||||
* @param string $instance_id
|
||||
* The ID of the plugin instance.
|
||||
* @param array $configuration
|
||||
* An array of configuration.
|
||||
* @param string $block_id
|
||||
* The unique ID of the block entity using this plugin.
|
||||
*/
|
||||
public function __construct(PluginManagerInterface $manager, $instance_id, array $configuration, $block_id) {
|
||||
parent::__construct($manager, $instance_id, $configuration);
|
||||
|
||||
$this->blockId = $block_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\Core\Block\BlockPluginInterface
|
||||
*/
|
||||
public function &get($instance_id) {
|
||||
return parent::get($instance_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializePlugin($instance_id) {
|
||||
if (!$instance_id) {
|
||||
throw new PluginException(SafeMarkup::format("The block '@block' did not specify a plugin.", array('@block' => $this->blockId)));
|
||||
}
|
||||
|
||||
try {
|
||||
parent::initializePlugin($instance_id);
|
||||
}
|
||||
catch (PluginException $e) {
|
||||
$module = $this->configuration['provider'];
|
||||
// Ignore blocks belonging to uninstalled modules, but re-throw valid
|
||||
// exceptions when the module is installed and the plugin is
|
||||
// misconfigured.
|
||||
if (!$module || \Drupal::moduleHandler()->moduleExists($module)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
76
core/modules/block/src/BlockRepository.php
Normal file
76
core/modules/block/src/BlockRepository.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockRepository.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides a repository for Block config entities.
|
||||
*/
|
||||
class BlockRepository implements BlockRepositoryInterface {
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $blockStorage;
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new BlockRepository.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
|
||||
* The theme manager.
|
||||
* @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
|
||||
* The plugin context handler.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager, ContextHandlerInterface $context_handler) {
|
||||
$this->blockStorage = $entity_manager->getStorage('block');
|
||||
$this->themeManager = $theme_manager;
|
||||
$this->contextHandler = $context_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibleBlocksPerRegion(array $contexts) {
|
||||
$active_theme = $this->themeManager->getActiveTheme();
|
||||
// Build an array of the region names in the right order.
|
||||
$empty = array_fill_keys($active_theme->getRegions(), array());
|
||||
|
||||
$full = array();
|
||||
foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) {
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
// Set the contexts on the block before checking access.
|
||||
if ($block->setContexts($contexts)->access('view')) {
|
||||
$full[$block->getRegion()][$block_id] = $block;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge it with the actual values to maintain the region ordering.
|
||||
$assignments = array_intersect_key(array_merge($empty, $full), $empty);
|
||||
foreach ($assignments as &$assignment) {
|
||||
// Suppress errors because PHPUnit will indirectly modify the contents,
|
||||
// triggering https://bugs.php.net/bug.php?id=50688.
|
||||
@uasort($assignment, 'Drupal\block\Entity\Block::sort');
|
||||
}
|
||||
return $assignments;
|
||||
}
|
||||
|
||||
}
|
24
core/modules/block/src/BlockRepositoryInterface.php
Normal file
24
core/modules/block/src/BlockRepositoryInterface.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockRepositoryInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
interface BlockRepositoryInterface {
|
||||
|
||||
/**
|
||||
* Returns an array of regions and their block entities.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts to set on the blocks.
|
||||
*
|
||||
* @return array
|
||||
* The array is first keyed by region machine name, with the values
|
||||
* containing an array keyed by block ID, with block entities as the values.
|
||||
*/
|
||||
public function getVisibleBlocksPerRegion(array $contexts);
|
||||
|
||||
}
|
151
core/modules/block/src/BlockViewBuilder.php
Normal file
151
core/modules/block/src/BlockViewBuilder.php
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\BlockViewBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Entity\EntityViewBuilder;
|
||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Provides a Block view builder.
|
||||
*/
|
||||
class BlockViewBuilder extends EntityViewBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
|
||||
$build = $this->viewMultiple(array($entity), $view_mode, $langcode);
|
||||
return reset($build);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
|
||||
/** @var \Drupal\block\BlockInterface[] $entities */
|
||||
$build = array();
|
||||
foreach ($entities as $entity) {
|
||||
$entity_id = $entity->id();
|
||||
$plugin = $entity->getPlugin();
|
||||
$plugin_id = $plugin->getPluginId();
|
||||
$base_id = $plugin->getBaseId();
|
||||
$derivative_id = $plugin->getDerivativeId();
|
||||
$configuration = $plugin->getConfiguration();
|
||||
|
||||
// Create the render array for the block as a whole.
|
||||
// @see template_preprocess_block().
|
||||
$build[$entity_id] = array(
|
||||
'#theme' => 'block',
|
||||
'#attributes' => array(),
|
||||
// All blocks get a "Configure block" contextual link.
|
||||
'#contextual_links' => array(
|
||||
'block' => array(
|
||||
'route_parameters' => array('block' => $entity->id()),
|
||||
),
|
||||
),
|
||||
'#weight' => $entity->getWeight(),
|
||||
'#configuration' => $configuration,
|
||||
'#plugin_id' => $plugin_id,
|
||||
'#base_plugin_id' => $base_id,
|
||||
'#derivative_plugin_id' => $derivative_id,
|
||||
'#id' => $entity->id(),
|
||||
'#cache' => [
|
||||
'keys' => ['entity_view', 'block', $entity->id()],
|
||||
'contexts' => $plugin->getCacheContexts(),
|
||||
'tags' => Cache::mergeTags(
|
||||
$this->getCacheTags(), // Block view builder cache tag.
|
||||
$entity->getCacheTags(), // Block entity cache tag.
|
||||
$plugin->getCacheTags() // Block plugin cache tags.
|
||||
),
|
||||
'max-age' => $plugin->getCacheMaxAge(),
|
||||
],
|
||||
'#pre_render' => [
|
||||
[$this, 'buildBlock'],
|
||||
],
|
||||
// Add the entity so that it can be used in the #pre_render method.
|
||||
'#block' => $entity,
|
||||
);
|
||||
$build[$entity_id]['#configuration']['label'] = SafeMarkup::checkPlain($configuration['label']);
|
||||
|
||||
// Don't run in ::buildBlock() to ensure cache keys can be altered. If an
|
||||
// alter hook wants to modify the block contents, it can append another
|
||||
// #pre_render hook.
|
||||
$this->moduleHandler()->alter(array('block_view', "block_view_$base_id"), $build[$entity_id], $plugin);
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for building a block.
|
||||
*
|
||||
* Renders the content using the provided block plugin, and then:
|
||||
* - if there is no content, aborts rendering, and makes sure the block won't
|
||||
* be rendered.
|
||||
* - if there is content, moves the contextual links from the block content to
|
||||
* the block itself.
|
||||
*/
|
||||
public function buildBlock($build) {
|
||||
$content = $build['#block']->getPlugin()->build();
|
||||
// Remove the block entity from the render array, to ensure that blocks
|
||||
// can be rendered without the block config entity.
|
||||
unset($build['#block']);
|
||||
if ($content !== NULL && !Element::isEmpty($content)) {
|
||||
// Place the $content returned by the block plugin into a 'content' child
|
||||
// element, as a way to allow the plugin to have complete control of its
|
||||
// properties and rendering (e.g., its own #theme) without conflicting
|
||||
// with the properties used above, or alternate ones used by alternate
|
||||
// block rendering approaches in contrib (e.g., Panels). However, the use
|
||||
// of a child element is an implementation detail of this particular block
|
||||
// rendering approach. Semantically, the content returned by the plugin
|
||||
// "is the" block, and in particular, #attributes and #contextual_links is
|
||||
// information about the *entire* block. Therefore, we must move these
|
||||
// properties from $content and merge them into the top-level element.
|
||||
foreach (array('#attributes', '#contextual_links') as $property) {
|
||||
if (isset($content[$property])) {
|
||||
$build[$property] += $content[$property];
|
||||
unset($content[$property]);
|
||||
}
|
||||
}
|
||||
$build['content'] = $content;
|
||||
}
|
||||
// Either the block's content is completely empty, or it consists only of
|
||||
// cacheability metadata.
|
||||
else {
|
||||
// Abort rendering: render as the empty string and ensure this block is
|
||||
// render cached, so we can avoid the work of having to repeatedly
|
||||
// determine whether the block is empty. E.g. modifying or adding entities
|
||||
// could cause the block to no longer be empty.
|
||||
$build = array(
|
||||
'#markup' => '',
|
||||
'#cache' => $build['#cache'],
|
||||
);
|
||||
// If $content is not empty, then it contains cacheability metadata, and
|
||||
// we must merge it with the existing cacheability metadata. This allows
|
||||
// blocks to be empty, yet still bubble cacheability metadata, to indicate
|
||||
// why they are empty.
|
||||
if (!empty($content)) {
|
||||
CacheableMetadata::createFromRenderArray($build)
|
||||
->merge(CacheableMetadata::createFromRenderArray($content))
|
||||
->applyTo($build);
|
||||
}
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
36
core/modules/block/src/Controller/BlockAddController.php
Normal file
36
core/modules/block/src/Controller/BlockAddController.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Controller\BlockAddController.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Controller for building the block instance add form.
|
||||
*/
|
||||
class BlockAddController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Build the block instance add form.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the block instance.
|
||||
* @param string $theme
|
||||
* The name of the theme for the block instance.
|
||||
*
|
||||
* @return array
|
||||
* The block instance edit form.
|
||||
*/
|
||||
public function blockAddConfigureForm($plugin_id, $theme) {
|
||||
// Create a block entity.
|
||||
$entity = $this->entityManager()->getStorage('block')->create(array('plugin' => $plugin_id, 'theme' => $theme));
|
||||
|
||||
return $this->entityFormBuilder()->getForm($entity);
|
||||
}
|
||||
|
||||
}
|
101
core/modules/block/src/Controller/BlockController.php
Normal file
101
core/modules/block/src/Controller/BlockController.php
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Controller\BlockController.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Extension\ThemeHandler;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Controller routines for admin block routes.
|
||||
*/
|
||||
class BlockController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a new BlockController instance.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(ThemeHandlerInterface $theme_handler) {
|
||||
$this->themeHandler = $theme_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('theme_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a block theme demo page.
|
||||
*
|
||||
* @param string $theme
|
||||
* The name of the theme.
|
||||
*
|
||||
* @return array
|
||||
* A #type 'page' render array containing the block region demo.
|
||||
*/
|
||||
public function demo($theme) {
|
||||
$page = [
|
||||
'#title' => $this->themeHandler->getName($theme),
|
||||
'#type' => 'page',
|
||||
'#attached' => array(
|
||||
'drupalSettings' => [
|
||||
// The block demonstration page is not marked as an administrative
|
||||
// page by \Drupal::service('router.admin_context')->isAdminRoute()
|
||||
// function in order to use the frontend theme. Since JavaScript
|
||||
// relies on a proper separation of admin pages, it needs to know this
|
||||
// is an actual administrative page.
|
||||
'path' => ['currentPathIsAdmin' => TRUE],
|
||||
],
|
||||
'library' => array(
|
||||
'block/drupal.block.admin',
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
// Show descriptions in each visible page region, nothing else.
|
||||
$visible_regions = $this->getVisibleRegionNames($theme);
|
||||
foreach (array_keys($visible_regions) as $region) {
|
||||
$page[$region]['block_description'] = array(
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="block-region demo-block">{{ region_name }}</div>',
|
||||
'#context' => array('region_name' => $visible_regions[$region]),
|
||||
);
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable list of regions keyed by machine name.
|
||||
*
|
||||
* @param string $theme
|
||||
* The name of the theme.
|
||||
*
|
||||
* @return array
|
||||
* An array of human-readable region names keyed by machine name.
|
||||
*/
|
||||
protected function getVisibleRegionNames($theme) {
|
||||
return system_region_list($theme, REGIONS_VISIBLE);
|
||||
}
|
||||
|
||||
}
|
34
core/modules/block/src/Controller/BlockListController.php
Normal file
34
core/modules/block/src/Controller/BlockListController.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Controller\BlockListController.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Controller;
|
||||
|
||||
use Drupal\Core\Entity\Controller\EntityListController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Defines a controller to list blocks.
|
||||
*/
|
||||
class BlockListController extends EntityListController {
|
||||
|
||||
/**
|
||||
* Shows the block administration page.
|
||||
*
|
||||
* @param string|null $theme
|
||||
* Theme key of block list.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function listing($theme = NULL, Request $request = NULL) {
|
||||
$theme = $theme ?: $this->config('system.theme')->get('default');
|
||||
return $this->entityManager()->getListBuilder('block')->render($theme, $request);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Controller\CategoryAutocompleteController.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Controller;
|
||||
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Returns autocomplete responses for block categories.
|
||||
*/
|
||||
class CategoryAutocompleteController implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The block manager.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockManagerInterface
|
||||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* Constructs a new CategoryAutocompleteController.
|
||||
*
|
||||
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
|
||||
* The block manager.
|
||||
*/
|
||||
public function __construct(BlockManagerInterface $block_manager) {
|
||||
$this->blockManager = $block_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.block')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves suggestions for block category autocompletion.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* A JSON response containing autocomplete suggestions.
|
||||
*/
|
||||
public function autocomplete(Request $request) {
|
||||
$typed_category = $request->query->get('q');
|
||||
$matches = array();
|
||||
foreach ($this->blockManager->getCategories() as $category) {
|
||||
if (stripos($category, $typed_category) === 0) {
|
||||
$matches[] = array('value' => $category, 'label' => SafeMarkup::checkPlain($category));
|
||||
}
|
||||
}
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
}
|
351
core/modules/block/src/Entity/Block.php
Normal file
351
core/modules/block/src/Entity/Block.php
Normal file
|
@ -0,0 +1,351 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Entity\Block.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Entity;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Condition\ConditionPluginCollection;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\block\BlockPluginCollection;
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
|
||||
/**
|
||||
* Defines a Block configuration entity class.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "block",
|
||||
* label = @Translation("Block"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\block\BlockAccessControlHandler",
|
||||
* "view_builder" = "Drupal\block\BlockViewBuilder",
|
||||
* "list_builder" = "Drupal\block\BlockListBuilder",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\block\BlockForm",
|
||||
* "delete" = "Drupal\block\Form\BlockDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* admin_permission = "administer blocks",
|
||||
* entity_keys = {
|
||||
* "id" = "id"
|
||||
* },
|
||||
* links = {
|
||||
* "delete-form" = "/admin/structure/block/manage/{block}/delete",
|
||||
* "edit-form" = "/admin/structure/block/manage/{block}"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "theme",
|
||||
* "region",
|
||||
* "weight",
|
||||
* "provider",
|
||||
* "plugin",
|
||||
* "settings",
|
||||
* "visibility",
|
||||
* },
|
||||
* lookup_keys = {
|
||||
* "theme"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Block extends ConfigEntityBase implements BlockInterface, EntityWithPluginCollectionInterface {
|
||||
|
||||
/**
|
||||
* The ID of the block.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The plugin instance settings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = array();
|
||||
|
||||
/**
|
||||
* The region this block is placed in.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $region = self::BLOCK_REGION_NONE;
|
||||
|
||||
/**
|
||||
* The block weight.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight;
|
||||
|
||||
/**
|
||||
* The plugin instance ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* The visibility settings for this block.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $visibility = [];
|
||||
|
||||
/**
|
||||
* The plugin collection that holds the block plugin for this entity.
|
||||
*
|
||||
* @var \Drupal\block\BlockPluginCollection
|
||||
*/
|
||||
protected $pluginCollection;
|
||||
|
||||
/**
|
||||
* The available contexts for this block and its visibility conditions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $contexts = [];
|
||||
|
||||
/**
|
||||
* The visibility collection.
|
||||
*
|
||||
* @var \Drupal\Core\Condition\ConditionPluginCollection
|
||||
*/
|
||||
protected $visibilityCollection;
|
||||
|
||||
/**
|
||||
* The condition plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Executable\ExecutableManagerInterface
|
||||
*/
|
||||
protected $conditionPluginManager;
|
||||
|
||||
/**
|
||||
* The theme that includes the block plugin for this entity.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPlugin() {
|
||||
return $this->getPluginCollection()->get($this->plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the creation of the block's LazyPluginCollection.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\LazyPluginCollection
|
||||
* The block's plugin collection.
|
||||
*/
|
||||
protected function getPluginCollection() {
|
||||
if (!$this->pluginCollection) {
|
||||
$this->pluginCollection = new BlockPluginCollection(\Drupal::service('plugin.manager.block'), $this->plugin, $this->get('settings'), $this->id());
|
||||
}
|
||||
return $this->pluginCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPluginCollections() {
|
||||
return [
|
||||
'settings' => $this->getPluginCollection(),
|
||||
'visibility' => $this->getVisibilityConditions(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPluginId() {
|
||||
return $this->plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRegion() {
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTheme() {
|
||||
return $this->theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
$settings = $this->get('settings');
|
||||
if ($settings['label']) {
|
||||
return $settings['label'];
|
||||
}
|
||||
else {
|
||||
$definition = $this->getPlugin()->getPluginDefinition();
|
||||
return $definition['admin_label'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts active blocks by weight; sorts inactive blocks by name.
|
||||
*/
|
||||
public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
|
||||
// Separate enabled from disabled.
|
||||
$status = (int) $b->status() - (int) $a->status();
|
||||
if ($status !== 0) {
|
||||
return $status;
|
||||
}
|
||||
// Sort by weight, unless disabled.
|
||||
if ($a->getRegion() != static::BLOCK_REGION_NONE) {
|
||||
$weight = $a->getWeight() - $b->getWeight();
|
||||
if ($weight) {
|
||||
return $weight;
|
||||
}
|
||||
}
|
||||
// Sort by label.
|
||||
return strcmp($a->label(), $b->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
$this->addDependency('theme', $this->theme);
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
// Entity::postSave() calls Entity::invalidateTagsOnSave(), which only
|
||||
// handles the regular cases. The Block entity has one special case: a
|
||||
// newly created block may *also* appear on any page in the current theme,
|
||||
// so we must invalidate the associated block's cache tag (which includes
|
||||
// the theme cache tag).
|
||||
if (!$update) {
|
||||
Cache::invalidateTags($this->getCacheTags());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContexts(array $contexts) {
|
||||
$this->contexts = $contexts;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContexts() {
|
||||
return $this->contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibility() {
|
||||
return $this->getVisibilityConditions()->getConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setVisibilityConfig($instance_id, array $configuration) {
|
||||
$conditions = $this->getVisibilityConditions();
|
||||
if (!$conditions->has($instance_id)) {
|
||||
$configuration['id'] = $instance_id;
|
||||
$conditions->addInstanceId($instance_id, $configuration);
|
||||
}
|
||||
else {
|
||||
$conditions->setInstanceConfiguration($instance_id, $configuration);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibilityConditions() {
|
||||
if (!isset($this->visibilityCollection)) {
|
||||
$this->visibilityCollection = new ConditionPluginCollection($this->conditionPluginManager(), $this->get('visibility'));
|
||||
}
|
||||
return $this->visibilityCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibilityCondition($instance_id) {
|
||||
return $this->getVisibilityConditions()->get($instance_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the condition plugin manager.
|
||||
*
|
||||
* @return \Drupal\Core\Executable\ExecutableManagerInterface
|
||||
* The condition plugin manager.
|
||||
*/
|
||||
protected function conditionPluginManager() {
|
||||
$this->conditionPluginManager;
|
||||
if (!isset($this->conditionPluginManager)) {
|
||||
$this->conditionPluginManager = \Drupal::service('plugin.manager.condition');
|
||||
}
|
||||
return $this->conditionPluginManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRegion($region) {
|
||||
$this->region = $region;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
$this->weight = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createDuplicateBlock($new_id = NULL, $new_theme = NULL) {
|
||||
$duplicate = parent::createDuplicate();
|
||||
if (!empty($new_id)) {
|
||||
$duplicate->id = $new_id;
|
||||
}
|
||||
if (!empty($new_theme)) {
|
||||
$duplicate->theme = $new_theme;
|
||||
}
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
}
|
53
core/modules/block/src/Event/BlockContextEvent.php
Normal file
53
core/modules/block/src/Event/BlockContextEvent.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Event\BlockContextEvent.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Event;
|
||||
|
||||
use Drupal\Core\Plugin\Context\ContextInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Event subscribers can add context to be used by the block and its conditions.
|
||||
*
|
||||
* @see \Drupal\block\Event\BlockEvents::ACTIVE_CONTEXT
|
||||
* @see \Drupal\block\Event\BlockEvents::ADMINISTRATIVE_CONTEXT
|
||||
*/
|
||||
class BlockContextEvent extends Event {
|
||||
|
||||
/**
|
||||
* The array of available contexts for blocks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $contexts = [];
|
||||
|
||||
/**
|
||||
* Sets the context object for a given name.
|
||||
*
|
||||
* @param string $name
|
||||
* The name to store the context object under.
|
||||
* @param \Drupal\Core\Plugin\Context\ContextInterface $context
|
||||
* The context object to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContext($name, ContextInterface $context) {
|
||||
$this->contexts[$name] = $context;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context objects.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
||||
* An array of contexts that have been provided.
|
||||
*/
|
||||
public function getContexts() {
|
||||
return $this->contexts;
|
||||
}
|
||||
|
||||
}
|
55
core/modules/block/src/Event/BlockEvents.php
Normal file
55
core/modules/block/src/Event/BlockEvents.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Event\BlockEvents.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Event;
|
||||
|
||||
/**
|
||||
* Defines events for the Block module.
|
||||
*/
|
||||
final class BlockEvents {
|
||||
|
||||
/**
|
||||
* Name of the event when gathering condition context for a block plugin.
|
||||
*
|
||||
* This event allows you to provide additional context that can be used by
|
||||
* a condition plugin in order to determine the visibility of a block. The
|
||||
* event listener method receives a \Drupal\block\Event\BlockContextEvent
|
||||
* instance. Generally any new context is paired with a new condition plugin
|
||||
* that interprets the provided context and allows the block system to
|
||||
* determine whether or not the block should be displayed.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\Core\Block\BlockBase::getConditionContexts()
|
||||
* @see \Drupal\block\Event\BlockContextEvent
|
||||
* @see \Drupal\block\EventSubscriber\NodeRouteContext::onBlockActiveContext()
|
||||
* @see \Drupal\Core\Condition\ConditionInterface
|
||||
*/
|
||||
const ACTIVE_CONTEXT = 'block.active_context';
|
||||
|
||||
/**
|
||||
* Name of the event when gathering contexts for plugin configuration.
|
||||
*
|
||||
* This event allows you to provide information about your context to the
|
||||
* administration UI without having to provide a value for the context. For
|
||||
* example, during configuration there is no specific node to pass as context.
|
||||
* However, we still need to inform the system that a context named 'node' is
|
||||
* available and provide a definition so that blocks can be configured to use
|
||||
* it.
|
||||
*
|
||||
* The event listener method receives a \Drupal\block\Event\BlockContextEvent
|
||||
* instance.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\block\BlockForm::form()
|
||||
* @see \Drupal\block\Event\BlockContextEvent
|
||||
* @see \Drupal\block\EventSubscriber\NodeRouteContext::onBlockAdministrativeContext()
|
||||
*/
|
||||
const ADMINISTRATIVE_CONTEXT = 'block.administrative_context';
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\EventSubscriber\BlockContextSubscriberBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\EventSubscriber;
|
||||
|
||||
use Drupal\block\Event\BlockContextEvent;
|
||||
use Drupal\block\Event\BlockEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Provides a base class for block context subscribers.
|
||||
*/
|
||||
abstract class BlockContextSubscriberBase implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[BlockEvents::ACTIVE_CONTEXT][] = 'onBlockActiveContext';
|
||||
$events[BlockEvents::ADMINISTRATIVE_CONTEXT][] = 'onBlockAdministrativeContext';
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the available run-time contexts.
|
||||
*
|
||||
* For blocks to render correctly, all of the contexts that they require
|
||||
* must be populated with values. So this method must set a value for each
|
||||
* context that it adds. For example:
|
||||
* @code
|
||||
* // Determine a specific node to pass as context to blocks.
|
||||
* $node = ...
|
||||
*
|
||||
* // Set that specific node as the value of the 'node' context.
|
||||
* $context = new Context(new ContextDefinition('entity:node'));
|
||||
* $context->setContextValue($node);
|
||||
* $event->setContext('node.node', $context);
|
||||
* @endcode
|
||||
*
|
||||
* @param \Drupal\block\Event\BlockContextEvent $event
|
||||
* The Event to which to register available contexts.
|
||||
*/
|
||||
abstract public function onBlockActiveContext(BlockContextEvent $event);
|
||||
|
||||
/**
|
||||
* Determines the available configuration-time contexts.
|
||||
*
|
||||
* When a block is being configured, the configuration UI must know which
|
||||
* named contexts are potentially available, but does not care about the
|
||||
* value, since the value can be different for each request, and might not
|
||||
* be available at all during the configuration UI's request.
|
||||
*
|
||||
* For example:
|
||||
* @code
|
||||
* // During configuration, there is no specific node to pass as context.
|
||||
* // However, inform the system that a context named 'node.node' is
|
||||
* // available, and provide its definition, so that blocks can be
|
||||
* // configured to use it. When the block is rendered, the value of this
|
||||
* // context will be supplied by onBlockActiveContext().
|
||||
* $context = new Context(new ContextDefinition('entity:node'));
|
||||
* $event->setContext('node.node', $context);
|
||||
* @endcode
|
||||
*
|
||||
* @param \Drupal\block\Event\BlockContextEvent $event
|
||||
* The Event to which to register available contexts.
|
||||
*
|
||||
* @see static::onBlockActiveContext()
|
||||
*/
|
||||
abstract public function onBlockAdministrativeContext(BlockContextEvent $event);
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\EventSubscriber\BlockPageDisplayVariantSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\EventSubscriber;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Render\PageDisplayVariantSelectionEvent;
|
||||
use Drupal\Core\Render\RenderEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Selects the block page display variant.
|
||||
*
|
||||
* @see \Drupal\block\Plugin\DisplayVariant\BlockPageVariant
|
||||
*/
|
||||
class BlockPageDisplayVariantSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* Selects the block page display variant.
|
||||
*
|
||||
* @param \Drupal\Core\Render\PageDisplayVariantSelectionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onSelectPageDisplayVariant(PageDisplayVariantSelectionEvent $event) {
|
||||
$event->setPluginId('block_page');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[RenderEvents::SELECT_PAGE_DISPLAY_VARIANT][] = array('onSelectPageDisplayVariant');
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\EventSubscriber\CurrentLanguageContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\EventSubscriber;
|
||||
|
||||
use Drupal\block\Event\BlockContextEvent;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Sets the current language as a context.
|
||||
*/
|
||||
class CurrentLanguageContext extends BlockContextSubscriberBase {
|
||||
|
||||
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}
|
||||
*/
|
||||
public function onBlockActiveContext(BlockContextEvent $event) {
|
||||
// Add a context for each language type.
|
||||
$language_types = $this->languageManager->getLanguageTypes();
|
||||
$info = $this->languageManager->getDefinedLanguageTypesInfo();
|
||||
foreach ($language_types as $type_key) {
|
||||
if (isset($info[$type_key]['name'])) {
|
||||
$context = new Context(new ContextDefinition('language', $info[$type_key]['name']));
|
||||
$context->setContextValue($this->languageManager->getCurrentLanguage($type_key));
|
||||
$event->setContext('language.' . $type_key, $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBlockAdministrativeContext(BlockContextEvent $event) {
|
||||
$this->onBlockActiveContext($event);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\EventSubscriber\CurrentUserContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\EventSubscriber;
|
||||
|
||||
use Drupal\block\Event\BlockContextEvent;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Sets the current user as a context.
|
||||
*/
|
||||
class CurrentUserContext extends BlockContextSubscriberBase {
|
||||
|
||||
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}
|
||||
*/
|
||||
public function onBlockActiveContext(BlockContextEvent $event) {
|
||||
$current_user = $this->userStorage->load($this->account->id());
|
||||
|
||||
$context = new Context(new ContextDefinition('entity:user', $this->t('Current user')));
|
||||
$context->setContextValue($current_user);
|
||||
$event->setContext('user.current_user', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBlockAdministrativeContext(BlockContextEvent $event) {
|
||||
$this->onBlockActiveContext($event);
|
||||
}
|
||||
|
||||
}
|
65
core/modules/block/src/EventSubscriber/NodeRouteContext.php
Normal file
65
core/modules/block/src/EventSubscriber/NodeRouteContext.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\EventSubscriber\NodeRouteContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\EventSubscriber;
|
||||
|
||||
use Drupal\block\Event\BlockContextEvent;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Sets the current node as a context on node routes.
|
||||
*/
|
||||
class NodeRouteContext extends BlockContextSubscriberBase {
|
||||
|
||||
/**
|
||||
* The route match object.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteMatchInterface
|
||||
*/
|
||||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeRouteContext.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match object.
|
||||
*/
|
||||
public function __construct(RouteMatchInterface $route_match) {
|
||||
$this->routeMatch = $route_match;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBlockActiveContext(BlockContextEvent $event) {
|
||||
if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
|
||||
$context = new Context(new ContextDefinition($route_contexts['node']['type']));
|
||||
if ($node = $this->routeMatch->getParameter('node')) {
|
||||
$context->setContextValue($node);
|
||||
}
|
||||
$event->setContext('node.node', $context);
|
||||
}
|
||||
elseif ($this->routeMatch->getRouteName() == 'node.add') {
|
||||
$node_type = $this->routeMatch->getParameter('node_type');
|
||||
$context = new Context(new ContextDefinition('entity:node'));
|
||||
$context->setContextValue(Node::create(array('type' => $node_type->id())));
|
||||
$event->setContext('node.node', $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBlockAdministrativeContext(BlockContextEvent $event) {
|
||||
$context = new Context(new ContextDefinition('entity:node'));
|
||||
$event->setContext('node.node', $context);
|
||||
}
|
||||
|
||||
}
|
25
core/modules/block/src/Form/BlockDeleteForm.php
Normal file
25
core/modules/block/src/Form/BlockDeleteForm.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Form\BlockDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides a deletion confirmation form for the block instance deletion form.
|
||||
*/
|
||||
class BlockDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('block.admin_display');
|
||||
}
|
||||
|
||||
}
|
83
core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php
Normal file
83
core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Plugin\Derivative\ThemeLocalTask.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides dynamic tabs based on active themes.
|
||||
*/
|
||||
class ThemeLocalTask extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
/**
|
||||
* Stores the theme settings config object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeLocalTask.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ThemeHandlerInterface $theme_handler) {
|
||||
$this->config = $config_factory->get('system.theme');
|
||||
$this->themeHandler = $theme_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('theme_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$default_theme = $this->config->get('default');
|
||||
|
||||
foreach ($this->themeHandler->listInfo() as $theme_name => $theme) {
|
||||
if ($theme->status) {
|
||||
$this->derivatives[$theme_name] = $base_plugin_definition;
|
||||
$this->derivatives[$theme_name]['title'] = $theme->info['name'];
|
||||
$this->derivatives[$theme_name]['route_parameters'] = array('theme' => $theme_name);
|
||||
}
|
||||
// Default task!
|
||||
if ($default_theme == $theme_name) {
|
||||
$this->derivatives[$theme_name]['route_name'] = 'block.admin_display';
|
||||
// Emulate default logic because without the base plugin id we can't
|
||||
// change the base_route.
|
||||
$this->derivatives[$theme_name]['weight'] = -10;
|
||||
|
||||
unset($this->derivatives[$theme_name]['route_parameters']);
|
||||
}
|
||||
}
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Plugin\DisplayVariant\BlockPageVariant.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Plugin\DisplayVariant;
|
||||
|
||||
use Drupal\block\BlockRepositoryInterface;
|
||||
use Drupal\block\Event\BlockContextEvent;
|
||||
use Drupal\block\Event\BlockEvents;
|
||||
use Drupal\Core\Block\MainContentBlockPluginInterface;
|
||||
use Drupal\Core\Block\MessagesBlockPluginInterface;
|
||||
use Drupal\Core\Display\PageVariantInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Display\VariantBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Provides a page display variant that decorates the main content with blocks.
|
||||
*
|
||||
* To ensure essential information is displayed, each essential part of a page
|
||||
* has a corresponding block plugin interface, so that BlockPageVariant can
|
||||
* automatically provide a fallback in case no block for each of these
|
||||
* interfaces is placed.
|
||||
*
|
||||
* @see \Drupal\Core\Block\MainContentBlockPluginInterface
|
||||
* @see \Drupal\Core\Block\MessagesBlockPluginInterface
|
||||
*
|
||||
* @PageDisplayVariant(
|
||||
* id = "block_page",
|
||||
* admin_label = @Translation("Page with blocks")
|
||||
* )
|
||||
*/
|
||||
class BlockPageVariant extends VariantBase implements PageVariantInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The block repository.
|
||||
*
|
||||
* @var \Drupal\block\BlockRepositoryInterface
|
||||
*/
|
||||
protected $blockRepository;
|
||||
|
||||
/**
|
||||
* The block view builder.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityViewBuilderInterface
|
||||
*/
|
||||
protected $blockViewBuilder;
|
||||
|
||||
/**
|
||||
* The Block entity type list cache tags.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $blockListCacheTags;
|
||||
|
||||
/**
|
||||
* The render array representing the main page content.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mainContent = [];
|
||||
|
||||
/**
|
||||
* Constructs a new BlockPageVariant.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\block\BlockRepositoryInterface $block_repository
|
||||
* The block repository.
|
||||
* @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
|
||||
* The block view builder.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* The event dispatcher.
|
||||
* @param string[] $block_list_cache_tags
|
||||
* The Block entity type list cache tags.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, EventDispatcherInterface $dispatcher, array $block_list_cache_tags) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->blockRepository = $block_repository;
|
||||
$this->blockViewBuilder = $block_view_builder;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->blockListCacheTags = $block_list_cache_tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('block.repository'),
|
||||
$container->get('entity.manager')->getViewBuilder('block'),
|
||||
$container->get('event_dispatcher'),
|
||||
$container->get('entity.manager')->getDefinition('block')->getListCacheTags()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMainContent(array $main_content) {
|
||||
$this->mainContent = $main_content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
// Track whether blocks showing the main content and messages are displayed.
|
||||
$main_content_block_displayed = FALSE;
|
||||
$messages_block_displayed = FALSE;
|
||||
|
||||
$build = [
|
||||
'#cache' => [
|
||||
'tags' => $this->blockListCacheTags,
|
||||
],
|
||||
];
|
||||
$contexts = $this->getActiveBlockContexts();
|
||||
// Load all region content assigned via blocks.
|
||||
foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $blocks) {
|
||||
/** @var $blocks \Drupal\block\BlockInterface[] */
|
||||
foreach ($blocks as $key => $block) {
|
||||
$block_plugin = $block->getPlugin();
|
||||
if ($block_plugin instanceof MainContentBlockPluginInterface) {
|
||||
$block_plugin->setMainContent($this->mainContent);
|
||||
$main_content_block_displayed = TRUE;
|
||||
}
|
||||
elseif ($block_plugin instanceof MessagesBlockPluginInterface) {
|
||||
$messages_block_displayed = TRUE;
|
||||
}
|
||||
$build[$region][$key] = $this->blockViewBuilder->view($block);
|
||||
|
||||
// The main content block cannot be cached: it is a placeholder for the
|
||||
// render array returned by the controller. It should be rendered as-is,
|
||||
// with other placed blocks "decorating" it.
|
||||
if ($block_plugin instanceof MainContentBlockPluginInterface) {
|
||||
unset($build[$region][$key]['#cache']['keys']);
|
||||
}
|
||||
}
|
||||
if (!empty($build[$region])) {
|
||||
// \Drupal\block\BlockRepositoryInterface::getVisibleBlocksPerRegion()
|
||||
// returns the blocks in sorted order.
|
||||
$build[$region]['#sorted'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// If no block that shows the main content is displayed, still show the main
|
||||
// content. Otherwise the end user will see all displayed blocks, but not
|
||||
// the main content they came for.
|
||||
if (!$main_content_block_displayed) {
|
||||
$build['content']['system_main'] = $this->mainContent;
|
||||
}
|
||||
|
||||
// If no block displays status messages, still render them.
|
||||
if (!$messages_block_displayed) {
|
||||
$build['content']['messages'] = [
|
||||
'#weight' => -1000,
|
||||
'#type' => 'status_messages',
|
||||
];
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of context objects to set on the blocks.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
||||
* An array of contexts to set on the blocks.
|
||||
*/
|
||||
protected function getActiveBlockContexts() {
|
||||
return $this->dispatcher->dispatch(BlockEvents::ACTIVE_CONTEXT, new BlockContextEvent())->getContexts();
|
||||
}
|
||||
|
||||
}
|
46
core/modules/block/src/Tests/BlockAdminThemeTest.php
Normal file
46
core/modules/block/src/Tests/BlockAdminThemeTest.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockAdminThemeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the block system with admin themes.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockAdminThemeTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block');
|
||||
|
||||
/**
|
||||
* Check for the accessibility of the admin theme on the block admin page.
|
||||
*/
|
||||
function testAdminTheme() {
|
||||
// Create administrative user.
|
||||
$admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensure that access to block admin page is denied when theme is not
|
||||
// installed.
|
||||
$this->drupalGet('admin/structure/block/list/bartik');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Install admin theme and confirm that tab is accessible.
|
||||
\Drupal::service('theme_handler')->install(array('bartik'));
|
||||
$edit['admin_theme'] = 'bartik';
|
||||
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
|
||||
$this->drupalGet('admin/structure/block/list/bartik');
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
}
|
226
core/modules/block/src/Tests/BlockCacheTest.php
Normal file
226
core/modules/block/src/Tests/BlockCacheTest.php
Normal file
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockCacheTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests block caching.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockCacheTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test', 'test_page_test');
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books and to administer blocks.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* An authenticated user to test block caching.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $normalUser;
|
||||
|
||||
/**
|
||||
* Another authenticated user to test block caching.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $normalUserAlt;
|
||||
|
||||
/**
|
||||
* The block used by this test.
|
||||
*
|
||||
* @var \Drupal\block\BlockInterface
|
||||
*/
|
||||
protected $block;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an admin user, log in and enable test blocks.
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer blocks', 'access administration pages'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Create additional users to test caching modes.
|
||||
$this->normalUser = $this->drupalCreateUser();
|
||||
$this->normalUserAlt = $this->drupalCreateUser();
|
||||
// Sync the roles, since drupalCreateUser() creates separate roles for
|
||||
// the same permission sets.
|
||||
$this->normalUserAlt->roles = $this->normalUser->getRoles();
|
||||
$this->normalUserAlt->save();
|
||||
|
||||
// Enable our test block.
|
||||
$this->block = $this->drupalPlaceBlock('test_cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test "user.roles" cache context.
|
||||
*/
|
||||
function testCachePerRole() {
|
||||
\Drupal::state()->set('block_test.cache_contexts', ['user.roles']);
|
||||
|
||||
// Enable our test block. Set some content for it to display.
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($current_content, 'Block content displays.');
|
||||
|
||||
// Change the content, but the cached copy should still be served.
|
||||
$old_content = $current_content;
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($old_content, 'Block is served from the cache.');
|
||||
|
||||
// Clear the cache and verify that the stale data is no longer there.
|
||||
Cache::invalidateTags(array('block_view'));
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($old_content, 'Block cache clear removes stale cache data.');
|
||||
$this->assertText($current_content, 'Fresh block content is displayed after clearing the cache.');
|
||||
|
||||
// Test whether the cached data is served for the correct users.
|
||||
$old_content = $current_content;
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($old_content, 'Anonymous user does not see content cached per-role for normal user.');
|
||||
|
||||
$this->drupalLogin($this->normalUserAlt);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($old_content, 'User with the same roles sees per-role cached content.');
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($old_content, 'Admin user does not see content cached per-role for normal user.');
|
||||
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($old_content, 'Block is served from the per-role cache.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a cacheable block without any cache context.
|
||||
*/
|
||||
function testCacheGlobal() {
|
||||
\Drupal::state()->set('block_test.cache_contexts', []);
|
||||
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertText($current_content, 'Block content displays.');
|
||||
|
||||
$old_content = $current_content;
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('user');
|
||||
$this->assertText($old_content, 'Block content served from cache.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test non-cacheable block.
|
||||
*/
|
||||
function testNoCache() {
|
||||
$this->setBlockCacheConfig(array(
|
||||
'max_age' => 0,
|
||||
));
|
||||
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
|
||||
// If max_age = 0 has no effect, the next request would be cached.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($current_content, 'Block content displays.');
|
||||
|
||||
// A cached copy should not be served.
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($current_content, 'Maximum age of zero prevents blocks from being cached.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test "user" cache context.
|
||||
*/
|
||||
function testCachePerUser() {
|
||||
\Drupal::state()->set('block_test.cache_contexts', ['user']);
|
||||
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
$this->drupalLogin($this->normalUser);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertText($current_content, 'Block content displays.');
|
||||
|
||||
$old_content = $current_content;
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertText($old_content, 'Block is served from per-user cache.');
|
||||
|
||||
$this->drupalLogin($this->normalUserAlt);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($current_content, 'Per-user block cache is not served for other users.');
|
||||
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($old_content, 'Per-user block cache is persistent.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test "url" cache context.
|
||||
*/
|
||||
function testCachePerPage() {
|
||||
\Drupal::state()->set('block_test.cache_contexts', ['url']);
|
||||
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertText($current_content, 'Block content displays on the test page.');
|
||||
|
||||
$old_content = $current_content;
|
||||
$current_content = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test.content', $current_content);
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText($old_content, 'Block content cached for the test page does not show up for the user page.');
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($old_content, 'Block content cached for the test page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper method to set the test block's cache configuration.
|
||||
*/
|
||||
private function setBlockCacheConfig($cache_config) {
|
||||
$block = $this->block->getPlugin();
|
||||
$block->setConfigurationValue('cache', $cache_config);
|
||||
$this->block->save();
|
||||
}
|
||||
|
||||
}
|
98
core/modules/block/src/Tests/BlockConfigSchemaTest.php
Normal file
98
core/modules/block/src/Tests/BlockConfigSchemaTest.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockConfigSchemaTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the block config schema.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockConfigSchemaTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array(
|
||||
'block',
|
||||
'aggregator',
|
||||
'book',
|
||||
'block_content',
|
||||
'comment',
|
||||
'forum',
|
||||
'node',
|
||||
'statistics',
|
||||
// BlockManager->getModuleName() calls system_get_info().
|
||||
'system',
|
||||
'taxonomy',
|
||||
'user',
|
||||
'text',
|
||||
);
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfig;
|
||||
|
||||
/**
|
||||
* The block manager.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockManagerInterface
|
||||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->typedConfig = \Drupal::service('config.typed');
|
||||
$this->blockManager = \Drupal::service('plugin.manager.block');
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installSchema('book', array('book'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the block config schema for block plugins.
|
||||
*/
|
||||
public function testBlockConfigSchema() {
|
||||
foreach ($this->blockManager->getDefinitions() as $block_id => $definition) {
|
||||
$id = strtolower($this->randomMachineName());
|
||||
$block = Block::create(array(
|
||||
'id' => $id,
|
||||
'theme' => 'classy',
|
||||
'weight' => 00,
|
||||
'status' => TRUE,
|
||||
'region' => 'content',
|
||||
'plugin' => $block_id,
|
||||
'settings' => array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'provider' => 'system',
|
||||
'label_display' => FALSE,
|
||||
),
|
||||
'visibility' => array(),
|
||||
));
|
||||
$block->save();
|
||||
|
||||
$config = $this->config("block.block.$id");
|
||||
$this->assertEqual($config->get('id'), $id);
|
||||
$this->assertConfigSchema($this->typedConfig, $config->getName(), $config->get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
75
core/modules/block/src/Tests/BlockHiddenRegionTest.php
Normal file
75
core/modules/block/src/Tests/BlockHiddenRegionTest.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockHiddenRegionTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests that a newly installed theme does not inherit blocks to its hidden
|
||||
* regions.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockHiddenRegionTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* An administrative user to configure the test environment.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test', 'search');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create administrative user.
|
||||
$this->adminUser = $this->drupalCreateUser(array(
|
||||
'administer blocks',
|
||||
'administer themes',
|
||||
'search content',
|
||||
)
|
||||
);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPlaceBlock('search_form_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that hidden regions do not inherit blocks when a theme is installed.
|
||||
*/
|
||||
public function testBlockNotInHiddenRegion() {
|
||||
|
||||
// Ensure that the search form block is displayed.
|
||||
$this->drupalGet('');
|
||||
$this->assertText('Search', 'Block was displayed on the front page.');
|
||||
|
||||
// Install "block_test_theme" and set it as the default theme.
|
||||
$theme = 'block_test_theme';
|
||||
\Drupal::service('theme_handler')->install(array($theme));
|
||||
$this->config('system.theme')
|
||||
->set('default', $theme)
|
||||
->save();
|
||||
// Installing a theme will cause the kernel terminate event to rebuild the
|
||||
// router. Simulate that here.
|
||||
\Drupal::service('router.builder')->rebuildIfNeeded();
|
||||
|
||||
// Ensure that "block_test_theme" is set as the default theme.
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->assertText('Block test theme(' . t('active tab') . ')', 'Default local task on blocks admin page is the block test theme.');
|
||||
|
||||
// Ensure that the search form block is displayed.
|
||||
$this->drupalGet('');
|
||||
$this->assertText('Search', 'Block was displayed on the front page.');
|
||||
}
|
||||
|
||||
}
|
58
core/modules/block/src/Tests/BlockHookOperationTest.php
Normal file
58
core/modules/block/src/Tests/BlockHookOperationTest.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockHookOperationTest.
|
||||
*
|
||||
* Tests for Block module regarding hook_entity_operations_alter().
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Implement hook entity operations alter.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockHookOperationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'entity_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$permissions = array(
|
||||
'administer blocks',
|
||||
);
|
||||
|
||||
// Create and log in user.
|
||||
$admin_user = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests the block list to see if the test_operation link is added.
|
||||
*/
|
||||
public function testBlockOperationAlter() {
|
||||
// Add a test block, any block will do.
|
||||
// Set the machine name so the test_operation link can be built later.
|
||||
$block_id = Unicode::strtolower($this->randomMachineName(16));
|
||||
$this->drupalPlaceBlock('system_powered_by_block', array('id' => $block_id));
|
||||
|
||||
// Get the Block listing.
|
||||
$this->drupalGet('admin/structure/block');
|
||||
|
||||
$test_operation_link = 'admin/structure/block/manage/' . $block_id . '/test_operation';
|
||||
// Test if the test_operation link is on the page.
|
||||
$this->assertLinkByHref($test_operation_link);
|
||||
}
|
||||
|
||||
}
|
55
core/modules/block/src/Tests/BlockHtmlTest.php
Normal file
55
core/modules/block/src/Tests/BlockHtmlTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockHtmlTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests block HTML ID validity.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockHtmlTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
// Enable the test_html block, to test HTML ID and attributes.
|
||||
\Drupal::state()->set('block_test.attributes', array('data-custom-attribute' => 'foo'));
|
||||
\Drupal::state()->set('block_test.content', $this->randomMachineName());
|
||||
$this->drupalPlaceBlock('test_html', array('id' => 'test_html_block'));
|
||||
|
||||
// Enable a menu block, to test more complicated HTML.
|
||||
$this->drupalPlaceBlock('system_menu_block:admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for valid HTML for a block.
|
||||
*/
|
||||
function testHtml() {
|
||||
$this->drupalGet('');
|
||||
|
||||
// Ensure that a block's ID is converted to an HTML valid ID, and that
|
||||
// block-specific attributes are added to the same DOM element.
|
||||
$this->assertFieldByXPath('//div[@id="block-test-html-block" and @data-custom-attribute="foo"]', NULL, 'HTML ID and attributes for test block are valid and on the same DOM element.');
|
||||
|
||||
// Ensure expected markup for a menu block.
|
||||
$elements = $this->xpath('//nav[contains(@class, :nav-class)]/ul[contains(@class, :ul-class)]/li', array(':nav-class' => 'block-menu', ':ul-class' => 'menu'));
|
||||
$this->assertTrue(!empty($elements), 'The proper block markup was found.');
|
||||
}
|
||||
|
||||
}
|
38
core/modules/block/src/Tests/BlockInstallTest.php
Normal file
38
core/modules/block/src/Tests/BlockInstallTest.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockInstallTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests block module's installation.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockInstallTest extends WebTestBase {
|
||||
|
||||
public function testCacheTagInvalidationUponInstallation() {
|
||||
// Warm the page cache.
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText('Powered by Drupal');
|
||||
$this->assertNoCacheTag('config:block_list');
|
||||
|
||||
// Install the block module, and place the "Powered by Drupal" block.
|
||||
$this->container->get('module_installer')->install(['block', 'shortcut']);
|
||||
$this->rebuildContainer();
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
$this->drupalPlaceBlock('system_powered_by_block');
|
||||
|
||||
// Check the same page, block.module's hook_install() should have
|
||||
// invalidated the 'rendered' cache tag to make blocks show up.
|
||||
$this->drupalGet('');
|
||||
$this->assertCacheTag('config:block_list');
|
||||
$this->assertText('Powered by Drupal');
|
||||
}
|
||||
|
||||
}
|
125
core/modules/block/src/Tests/BlockInterfaceTest.php
Normal file
125
core/modules/block/src/Tests/BlockInterfaceTest.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockInterfaceTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\block\BlockInterface;
|
||||
|
||||
/**
|
||||
* Tests that the block plugin can work properly without a supporting entity.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockInterfaceTest extends KernelTestBase {
|
||||
public static $modules = array('system', 'block', 'block_test', 'user');
|
||||
|
||||
/**
|
||||
* Test configuration and subsequent form() and build() method calls.
|
||||
*
|
||||
* This test is attempting to test the existing block plugin api and all
|
||||
* functionality that is expected to remain consistent. The arrays that are
|
||||
* used for comparison can change, but only to include elements that are
|
||||
* contained within BlockBase or the plugin being tested. Likely these
|
||||
* comparison arrays should get smaller, not larger, as more form/build
|
||||
* elements are moved into a more suitably responsible class.
|
||||
*
|
||||
* Instantiation of the plugin is the primary element being tested here. The
|
||||
* subsequent method calls are just attempting to cause a failure if a
|
||||
* dependency outside of the plugin configuration is required.
|
||||
*/
|
||||
public function testBlockInterface() {
|
||||
$manager = $this->container->get('plugin.manager.block');
|
||||
$configuration = array(
|
||||
'label' => 'Custom Display Message',
|
||||
);
|
||||
$expected_configuration = array(
|
||||
'id' => 'test_block_instantiation',
|
||||
'label' => 'Custom Display Message',
|
||||
'provider' => 'block_test',
|
||||
'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
|
||||
'cache' => array(
|
||||
'max_age' => Cache::PERMANENT,
|
||||
),
|
||||
'display_message' => 'no message set',
|
||||
);
|
||||
// Initial configuration of the block at construction time.
|
||||
/** @var $display_block \Drupal\Core\Block\BlockPluginInterface */
|
||||
$display_block = $manager->createInstance('test_block_instantiation', $configuration);
|
||||
$this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block was configured correctly.');
|
||||
|
||||
// Updating an element of the configuration.
|
||||
$display_block->setConfigurationValue('display_message', 'My custom display message.');
|
||||
$expected_configuration['display_message'] = 'My custom display message.';
|
||||
$this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block configuration was updated correctly.');
|
||||
$definition = $display_block->getPluginDefinition();
|
||||
|
||||
$period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400);
|
||||
$period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($period, $period));
|
||||
$period[0] = '<' . t('no caching') . '>';
|
||||
$period[\Drupal\Core\Cache\Cache::PERMANENT] = t('Forever');
|
||||
$expected_form = array(
|
||||
'provider' => array(
|
||||
'#type' => 'value',
|
||||
'#value' => 'block_test',
|
||||
),
|
||||
'admin_label' => array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Block description'),
|
||||
'#markup' => SafeMarkup::checkPlain($definition['admin_label']),
|
||||
),
|
||||
'label' => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => 'Title',
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => 'Custom Display Message',
|
||||
'#required' => TRUE,
|
||||
),
|
||||
'label_display' => array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => 'Display title',
|
||||
'#default_value' => TRUE,
|
||||
'#return_value' => 'visible',
|
||||
),
|
||||
'cache' => array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Cache settings'),
|
||||
'max_age' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Maximum age'),
|
||||
'#description' => t('The maximum time this block may be cached.'),
|
||||
'#default_value' => Cache::PERMANENT,
|
||||
'#options' => $period,
|
||||
),
|
||||
),
|
||||
'display_message' => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Display message'),
|
||||
'#default_value' => 'My custom display message.',
|
||||
),
|
||||
);
|
||||
$form_state = new FormState();
|
||||
// Ensure there are no form elements that do not belong to the plugin.
|
||||
$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.',
|
||||
);
|
||||
// Ensure the build array is proper.
|
||||
$this->assertIdentical($display_block->build(), $expected_build, 'The plugin returned the appropriate build array.');
|
||||
|
||||
// Ensure the machine name suggestion is correct. In truth, this is actually
|
||||
// testing BlockBase's implementation, not the interface itself.
|
||||
$this->assertIdentical($display_block->getMachineNameSuggestion(), 'displaymessage', 'The plugin returned the expected machine name suggestion.');
|
||||
}
|
||||
}
|
67
core/modules/block/src/Tests/BlockInvalidRegionTest.php
Normal file
67
core/modules/block/src/Tests/BlockInvalidRegionTest.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockInvalidRegionTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\block\Entity\Block;
|
||||
|
||||
/**
|
||||
* Tests that an active block assigned to a non-existing region triggers the
|
||||
* warning message and is disabled.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockInvalidRegionTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create an admin user.
|
||||
$admin_user = $this->drupalCreateUser(array(
|
||||
'administer site configuration',
|
||||
'access administration pages',
|
||||
'administer blocks',
|
||||
));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that blocks assigned to invalid regions work correctly.
|
||||
*/
|
||||
function testBlockInInvalidRegion() {
|
||||
// Enable a test block and place it in an invalid region.
|
||||
$block = $this->drupalPlaceBlock('test_html');
|
||||
$block->setRegion('invalid_region');
|
||||
$block->save();
|
||||
|
||||
$warning_message = t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block->id(), '%region' => 'invalid_region'));
|
||||
|
||||
// Clearing the cache should disable the test block placed in the invalid region.
|
||||
$this->drupalPostForm('admin/config/development/performance', array(), 'Clear all caches');
|
||||
$this->assertRaw($warning_message, 'Enabled block was in the invalid region and has been disabled.');
|
||||
|
||||
// Clear the cache to check if the warning message is not triggered.
|
||||
$this->drupalPostForm('admin/config/development/performance', array(), 'Clear all caches');
|
||||
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
|
||||
|
||||
// Place disabled test block in the invalid region of the default theme.
|
||||
$block = Block::load($block->id());
|
||||
$block->setRegion('invalid_region');
|
||||
$block->save();
|
||||
|
||||
// Clear the cache to check if the warning message is not triggered.
|
||||
$this->drupalPostForm('admin/config/development/performance', array(), 'Clear all caches');
|
||||
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
|
||||
}
|
||||
}
|
79
core/modules/block/src/Tests/BlockLanguageCacheTest.php
Normal file
79
core/modules/block/src/Tests/BlockLanguageCacheTest.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockLanguageCacheTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests display of menu blocks with multiple languages.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockLanguageCacheTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'language', 'menu_ui');
|
||||
|
||||
/**
|
||||
* List of langcodes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $langcodes = array();
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create test languages.
|
||||
$this->langcodes = array(ConfigurableLanguage::load('en'));
|
||||
for ($i = 1; $i < 3; ++$i) {
|
||||
$language = ConfigurableLanguage::create(array(
|
||||
'id' => 'l' . $i,
|
||||
'label' => $this->randomString(),
|
||||
));
|
||||
$language->save();
|
||||
$this->langcodes[$i] = $language;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a block in a language, check blocks page in all languages.
|
||||
*/
|
||||
public function testBlockLinks() {
|
||||
// Create admin user to be able to access block admin.
|
||||
$admin_user = $this->drupalCreateUser(array(
|
||||
'administer blocks',
|
||||
'access administration pages',
|
||||
'administer menu',
|
||||
));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Create the block cache for all languages.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet('admin/structure/block', array('language' => $langcode));
|
||||
}
|
||||
|
||||
// Create a menu in the default language.
|
||||
$edit['label'] = $this->randomMachineName();
|
||||
$edit['id'] = Unicode::strtolower($edit['label']);
|
||||
$this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
|
||||
$this->assertText(t('Menu @label has been added.', array('@label' => $edit['label'])));
|
||||
|
||||
// Check that the block is listed for all languages.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet('admin/structure/block', array('language' => $langcode));
|
||||
$this->assertText($edit['label']);
|
||||
}
|
||||
}
|
||||
}
|
188
core/modules/block/src/Tests/BlockLanguageTest.php
Normal file
188
core/modules/block/src/Tests/BlockLanguageTest.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockLanguageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\block\Entity\Block;
|
||||
|
||||
/**
|
||||
* Tests if a block can be configured to be only visible on a particular
|
||||
* language.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockLanguageTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* An administrative user to configure the test environment.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'block', 'content_translation');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a new user, allow him to manage the blocks and the languages.
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer blocks', 'administer languages'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Add predefined language.
|
||||
$edit = array(
|
||||
'predefined_langcode' => 'fr',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
$this->assertText('French', 'Language added successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the visibility settings for the blocks based on language.
|
||||
*/
|
||||
public function testLanguageBlockVisibility() {
|
||||
// Check if the visibility setting is available.
|
||||
$default_theme = $this->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->assertNoField('visibility[language][context_mapping][language]', 'Language type field is not visible.');
|
||||
|
||||
// Enable a standard block and set the visibility setting for one language.
|
||||
$edit = array(
|
||||
'visibility[language][langcodes][en]' => TRUE,
|
||||
'id' => strtolower($this->randomMachineName(8)),
|
||||
'region' => 'sidebar_first',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block'));
|
||||
|
||||
// Change the default language.
|
||||
$edit = array(
|
||||
'site_default_language' => 'fr',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/language', $edit, t('Save configuration'));
|
||||
|
||||
// Check that a page has a block.
|
||||
$this->drupalGet('en');
|
||||
$this->assertText('Powered by Drupal', 'The body of the custom block appears on the page.');
|
||||
|
||||
// Check that a page doesn't has a block for the current language anymore.
|
||||
$this->drupalGet('fr');
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the custom block does not appear on the page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the visibility settings are removed if the language is deleted.
|
||||
*/
|
||||
public function testLanguageBlockVisibilityLanguageDelete() {
|
||||
// Enable a standard block and set the visibility setting for one language.
|
||||
$edit = array(
|
||||
'visibility' => array(
|
||||
'language' => array(
|
||||
'langcodes' => array(
|
||||
'fr' => 'fr',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$block = $this->drupalPlaceBlock('system_powered_by_block', $edit);
|
||||
|
||||
// Check that we have the language in config after saving the setting.
|
||||
$visibility = $block->getVisibility();
|
||||
$this->assertEqual('fr', $visibility['language']['langcodes']['fr'], 'Language is set in the block configuration.');
|
||||
|
||||
// Delete the language.
|
||||
$this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete'));
|
||||
|
||||
// Check that the language is no longer stored in the configuration after
|
||||
// it is deleted.
|
||||
$block = Block::load($block->id());
|
||||
$visibility = $block->getVisibility();
|
||||
$this->assertTrue(empty($visibility['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.');
|
||||
|
||||
// Ensure that the block visibility for language is gone from the UI.
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->clickLink('Configure');
|
||||
$elements = $this->xpath('//details[@id="edit-visibility-language"]');
|
||||
$this->assertTrue(empty($elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests block language visibility with different language types.
|
||||
*/
|
||||
public function testMultipleLanguageTypes() {
|
||||
// Customize content language detection to be different from interface
|
||||
// language detection.
|
||||
$edit = [
|
||||
// Interface language detection: only using session.
|
||||
'language_interface[enabled][language-url]' => FALSE,
|
||||
'language_interface[enabled][language-session]' => TRUE,
|
||||
// Content language detection: only using URL.
|
||||
'language_content[configurable]' => TRUE,
|
||||
'language_content[enabled][language-url]' => TRUE,
|
||||
'language_content[enabled][language-interface]' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
|
||||
// Check if the visibility setting is available with a type setting.
|
||||
$default_theme = $this->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('visibility[language][context_mapping][language]', 'Language type field is visible.');
|
||||
|
||||
// Enable a standard block and set visibility to French only.
|
||||
$block_id = strtolower($this->randomMachineName(8));
|
||||
$edit = [
|
||||
'visibility[language][context_mapping][language]' => 'language.language_interface',
|
||||
'visibility[language][langcodes][fr]' => TRUE,
|
||||
'id' => $block_id,
|
||||
'region' => 'sidebar_first',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block'));
|
||||
|
||||
// Interface negotiation depends on request arguments.
|
||||
$this->drupalGet('node', ['query' => ['language' => 'en']]);
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
|
||||
$this->drupalGet('node', ['query' => ['language' => 'fr']]);
|
||||
$this->assertText('Powered by Drupal', 'The body of the block appears on the page.');
|
||||
|
||||
// Re-login in order to clear the interface language stored in the session.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Content language does not depend on session/request arguments.
|
||||
// It will fall back on English (site default) and not display the block.
|
||||
$this->drupalGet('en');
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
|
||||
$this->drupalGet('fr');
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
|
||||
|
||||
// Change visibility to now depend on content language for this block.
|
||||
$edit = [
|
||||
'visibility[language][context_mapping][language]' => 'language.language_content'
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/block/manage/' . $block_id, $edit, t('Save block'));
|
||||
|
||||
// Content language negotiation does not depend on request arguments.
|
||||
// It will fall back on English (site default) and not display the block.
|
||||
$this->drupalGet('node', ['query' => ['language' => 'en']]);
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
|
||||
$this->drupalGet('node', ['query' => ['language' => 'fr']]);
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
|
||||
|
||||
// Content language negotiation depends on path prefix.
|
||||
$this->drupalGet('en');
|
||||
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
|
||||
$this->drupalGet('fr');
|
||||
$this->assertText('Powered by Drupal', 'The body of the block appears on the page.');
|
||||
}
|
||||
|
||||
}
|
84
core/modules/block/src/Tests/BlockRenderOrderTest.php
Normal file
84
core/modules/block/src/Tests/BlockRenderOrderTest.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockRenderOrderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests blocks are being rendered in order by weight.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockRenderOrderTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'block');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create a test user.
|
||||
$end_user = $this->drupalCreateUser(array(
|
||||
'access content',
|
||||
));
|
||||
$this->drupalLogin($end_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the render order of the blocks.
|
||||
*/
|
||||
function testBlockRenderOrder() {
|
||||
// Enable test blocks and place them in the same region.
|
||||
$region = 'header';
|
||||
$test_blocks = array(
|
||||
'stark_powered' => array(
|
||||
'weight' => '-3',
|
||||
'id' => 'stark_powered',
|
||||
'label' => 'Test block A',
|
||||
),
|
||||
'stark_by' => array(
|
||||
'weight' => '3',
|
||||
'id' => 'stark_by',
|
||||
'label' => 'Test block C',
|
||||
),
|
||||
'stark_drupal' => array(
|
||||
'weight' => '3',
|
||||
'id' => 'stark_drupal',
|
||||
'label' => 'Test block B',
|
||||
),
|
||||
);
|
||||
|
||||
// Place the test blocks.
|
||||
foreach ($test_blocks as $test_block) {
|
||||
$this->drupalPlaceBlock('system_powered_by_block', array(
|
||||
'label' => $test_block['label'],
|
||||
'region' => $region,
|
||||
'weight' => $test_block['weight'],
|
||||
'id' => $test_block['id'],
|
||||
));
|
||||
}
|
||||
|
||||
$this->drupalGet('');
|
||||
$test_content = $this->getRawContent('');
|
||||
|
||||
$controller = $this->container->get('entity.manager')->getStorage('block');
|
||||
foreach ($controller->loadMultiple() as $return_block) {
|
||||
$id = $return_block->id();
|
||||
if ($return_block_weight = $return_block->getWeight()) {
|
||||
$this->assertTrue($test_blocks[$id]['weight'] == $return_block_weight, 'Block weight is set as "' . $return_block_weight . '" for ' . $id . ' block.');
|
||||
$position[$id] = strpos($test_content, Html::getClass('block-' . $test_blocks[$id]['id']));
|
||||
}
|
||||
}
|
||||
$this->assertTrue($position['stark_powered'] < $position['stark_by'], 'Blocks with different weight are rendered in the correct order.');
|
||||
$this->assertTrue($position['stark_drupal'] < $position['stark_by'], 'Blocks with identical weight are rendered in alphabetical order.');
|
||||
}
|
||||
}
|
164
core/modules/block/src/Tests/BlockStorageUnitTest.php
Normal file
164
core/modules/block/src/Tests/BlockStorageUnitTest.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockStorageUnitTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\block_test\Plugin\Block\TestHtmlBlock;
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\block\BlockInterface;
|
||||
|
||||
/**
|
||||
* Tests the storage of blocks.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockStorageUnitTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test');
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface.
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->controller = $this->container->get('entity.manager')->getStorage('block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
public function testBlockCRUD() {
|
||||
$this->assertTrue($this->controller instanceof ConfigEntityStorage, 'The block storage is loaded.');
|
||||
|
||||
// Run each test method in the same installation.
|
||||
$this->createTests();
|
||||
$this->loadTests();
|
||||
$this->deleteTests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the creation of blocks.
|
||||
*/
|
||||
protected function createTests() {
|
||||
// Attempt to create a block without a plugin.
|
||||
try {
|
||||
$entity = $this->controller->create(array());
|
||||
$entity->getPlugin();
|
||||
$this->fail('A block without a plugin was created with no exception thrown.');
|
||||
}
|
||||
catch (PluginException $e) {
|
||||
$this->assertEqual('The block \'\' did not specify a plugin.', $e->getMessage(), 'An exception was thrown when a block was created without a plugin.');
|
||||
}
|
||||
|
||||
// Create a block with only required values.
|
||||
$entity = $this->controller->create(array(
|
||||
'id' => 'test_block',
|
||||
'theme' => 'stark',
|
||||
'plugin' => 'test_html',
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
$this->assertTrue($entity instanceof Block, 'The newly created entity is a Block.');
|
||||
|
||||
// Verify all of the block properties.
|
||||
$actual_properties = $this->config('block.block.test_block')->get();
|
||||
$this->assertTrue(!empty($actual_properties['uuid']), 'The block UUID is set.');
|
||||
unset($actual_properties['uuid']);
|
||||
|
||||
// Ensure that default values are filled in.
|
||||
$expected_properties = array(
|
||||
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
|
||||
'status' => TRUE,
|
||||
'dependencies' => array('module' => array('block_test'), 'theme' => array('stark')),
|
||||
'id' => 'test_block',
|
||||
'theme' => 'stark',
|
||||
'region' => '-1',
|
||||
'weight' => NULL,
|
||||
'provider' => NULL,
|
||||
'plugin' => 'test_html',
|
||||
'settings' => array(
|
||||
'id' => 'test_html',
|
||||
'label' => '',
|
||||
'provider' => 'block_test',
|
||||
'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
|
||||
'cache' => array(
|
||||
'max_age' => Cache::PERMANENT,
|
||||
),
|
||||
),
|
||||
'visibility' => array(),
|
||||
);
|
||||
|
||||
$this->assertIdentical($actual_properties, $expected_properties);
|
||||
|
||||
$this->assertTrue($entity->getPlugin() instanceof TestHtmlBlock, 'The entity has an instance of the correct block plugin.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the loading of blocks.
|
||||
*/
|
||||
protected function loadTests() {
|
||||
$entity = $this->controller->load('test_block');
|
||||
|
||||
$this->assertTrue($entity instanceof Block, 'The loaded entity is a Block.');
|
||||
|
||||
// Verify several properties of the block.
|
||||
$this->assertEqual($entity->getRegion(), '-1');
|
||||
$this->assertTrue($entity->status());
|
||||
$this->assertEqual($entity->getTheme(), 'stark');
|
||||
$this->assertTrue($entity->uuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the deleting of blocks.
|
||||
*/
|
||||
protected function deleteTests() {
|
||||
$entity = $this->controller->load('test_block');
|
||||
|
||||
// Ensure that the storage isn't currently empty.
|
||||
$config_storage = $this->container->get('config.storage');
|
||||
$config = $config_storage->listAll('block.block.');
|
||||
$this->assertFalse(empty($config), 'There are blocks in config storage.');
|
||||
|
||||
// Delete the block.
|
||||
$entity->delete();
|
||||
|
||||
// Ensure that the storage is now empty.
|
||||
$config = $config_storage->listAll('block.block.');
|
||||
$this->assertTrue(empty($config), 'There are no blocks in config storage.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the installation of default blocks.
|
||||
*/
|
||||
public function testDefaultBlocks() {
|
||||
\Drupal::service('theme_handler')->install(['classy']);
|
||||
$entities = $this->controller->loadMultiple();
|
||||
$this->assertTrue(empty($entities), 'There are no blocks initially.');
|
||||
|
||||
// Install the block_test.module, so that its default config is installed.
|
||||
$this->installConfig(array('block_test'));
|
||||
|
||||
$entities = $this->controller->loadMultiple();
|
||||
$entity = reset($entities);
|
||||
$this->assertEqual($entity->id(), 'test_block', 'The default test block was loaded.');
|
||||
}
|
||||
|
||||
}
|
116
core/modules/block/src/Tests/BlockSystemBrandingTest.php
Normal file
116
core/modules/block/src/Tests/BlockSystemBrandingTest.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockSystemBrandingTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
/**
|
||||
* Tests branding block display.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockSystemBrandingTest extends BlockTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Set a site slogan.
|
||||
$this->config('system.site')
|
||||
->set('slogan', 'Community plumbing')
|
||||
->save();
|
||||
// Add the system branding block to the page.
|
||||
$this->drupalPlaceBlock('system_branding_block', array('region' => 'header', 'id' => 'site-branding'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests system branding block configuration.
|
||||
*/
|
||||
public function testSystemBrandingSettings() {
|
||||
$site_logo_xpath = '//div[@id="block-site-branding"]//a[@class="site-logo"]';
|
||||
$site_name_xpath = '//div[@id="block-site-branding"]//div[@class="site-name"]';
|
||||
$site_slogan_xpath = '//div[@id="block-site-branding"]//div[@class="site-slogan"]';
|
||||
|
||||
// Set default block settings.
|
||||
$this->drupalGet('');
|
||||
$site_logo_element = $this->xpath($site_logo_xpath);
|
||||
$site_name_element = $this->xpath($site_name_xpath);
|
||||
$site_slogan_element = $this->xpath($site_slogan_xpath);
|
||||
// Test that all branding elements are displayed.
|
||||
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
|
||||
$this->assertTrue(!empty($site_name_element), 'The branding block site name was found.');
|
||||
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
|
||||
$this->assertCacheTag('config:system.site');
|
||||
|
||||
// Turn just the logo off.
|
||||
$this->config('block.block.site-branding')
|
||||
->set('settings.use_site_logo', 0)
|
||||
->save();
|
||||
$this->drupalGet('');
|
||||
$site_logo_element = $this->xpath($site_logo_xpath);
|
||||
$site_name_element = $this->xpath($site_name_xpath);
|
||||
$site_slogan_element = $this->xpath($site_slogan_xpath);
|
||||
// Re-test all branding elements.
|
||||
$this->assertTrue(empty($site_logo_element), 'The branding block logo was disabled.');
|
||||
$this->assertTrue(!empty($site_name_element), 'The branding block site name was found.');
|
||||
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
|
||||
$this->assertCacheTag('config:system.site');
|
||||
|
||||
// Turn just the site name off.
|
||||
$this->config('block.block.site-branding')
|
||||
->set('settings.use_site_logo', 1)
|
||||
->set('settings.use_site_name', 0)
|
||||
->save();
|
||||
$this->drupalGet('');
|
||||
$site_logo_element = $this->xpath($site_logo_xpath);
|
||||
$site_name_element = $this->xpath($site_name_xpath);
|
||||
$site_slogan_element = $this->xpath($site_slogan_xpath);
|
||||
// Re-test all branding elements.
|
||||
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
|
||||
$this->assertTrue(empty($site_name_element), 'The branding block site name was disabled.');
|
||||
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
|
||||
$this->assertCacheTag('config:system.site');
|
||||
|
||||
// Turn just the site slogan off.
|
||||
$this->config('block.block.site-branding')
|
||||
->set('settings.use_site_name', 1)
|
||||
->set('settings.use_site_slogan', 0)
|
||||
->save();
|
||||
$this->drupalGet('');
|
||||
$site_logo_element = $this->xpath($site_logo_xpath);
|
||||
$site_name_element = $this->xpath($site_name_xpath);
|
||||
$site_slogan_element = $this->xpath($site_slogan_xpath);
|
||||
// Re-test all branding elements.
|
||||
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
|
||||
$this->assertTrue(!empty($site_name_element), 'The branding block site name was found.');
|
||||
$this->assertTrue(empty($site_slogan_element), 'The branding block slogan was disabled.');
|
||||
$this->assertCacheTag('config:system.site');
|
||||
|
||||
// Turn the site name and the site slogan off.
|
||||
$this->config('block.block.site-branding')
|
||||
->set('settings.use_site_name', 0)
|
||||
->set('settings.use_site_slogan', 0)
|
||||
->save();
|
||||
$this->drupalGet('');
|
||||
$site_logo_element = $this->xpath($site_logo_xpath);
|
||||
$site_name_element = $this->xpath($site_name_xpath);
|
||||
$site_slogan_element = $this->xpath($site_slogan_xpath);
|
||||
// Re-test all branding elements.
|
||||
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
|
||||
$this->assertTrue(empty($site_name_element), 'The branding block site name was disabled.');
|
||||
$this->assertTrue(empty($site_slogan_element), 'The branding block slogan was disabled.');
|
||||
$this->assertCacheTag('config:system.site');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockTemplateSuggestionsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the block_theme_suggestions_block() function.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockTemplateSuggestionsTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block');
|
||||
|
||||
/**
|
||||
* Tests template suggestions from block_theme_suggestions_block().
|
||||
*/
|
||||
function testBlockThemeHookSuggestions() {
|
||||
// Define a block with a derivative to be preprocessed, which includes both
|
||||
// an underscore (not transformed) and a hyphen (transformed to underscore),
|
||||
// and generates possibilities for each level of derivative.
|
||||
// @todo Clarify this comment.
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
$block = entity_create('block', array(
|
||||
'plugin' => 'system_menu_block:admin',
|
||||
'region' => 'footer',
|
||||
'id' => 'machinename',
|
||||
));
|
||||
|
||||
$variables = array();
|
||||
$plugin = $block->getPlugin();
|
||||
$variables['elements']['#configuration'] = $plugin->getConfiguration();
|
||||
$variables['elements']['#plugin_id'] = $plugin->getPluginId();
|
||||
$variables['elements']['#id'] = $block->id();
|
||||
$variables['elements']['#base_plugin_id'] = $plugin->getBaseId();
|
||||
$variables['elements']['#derivative_plugin_id'] = $plugin->getDerivativeId();
|
||||
$variables['elements']['content'] = array();
|
||||
$suggestions = block_theme_suggestions_block($variables);
|
||||
$this->assertEqual($suggestions, array('block__system', 'block__system_menu_block', 'block__system_menu_block__admin', 'block__machinename'));
|
||||
}
|
||||
|
||||
}
|
441
core/modules/block/src/Tests/BlockTest.php
Normal file
441
core/modules/block/src/Tests/BlockTest.php
Normal file
|
@ -0,0 +1,441 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests basic block functionality.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockTest extends BlockTestBase {
|
||||
|
||||
/**
|
||||
* Tests block visibility.
|
||||
*/
|
||||
function testBlockVisibility() {
|
||||
$block_name = 'system_powered_by_block';
|
||||
// Create a random title for the block.
|
||||
$title = $this->randomMachineName(8);
|
||||
// Enable a standard block.
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName(8)),
|
||||
'region' => 'sidebar_first',
|
||||
'settings[label]' => $title,
|
||||
);
|
||||
// Set the block to be hidden on any user path, and to be shown only to
|
||||
// authenticated users.
|
||||
$edit['visibility[request_path][pages]'] = '/user*';
|
||||
$edit['visibility[request_path][negate]'] = TRUE;
|
||||
$edit['visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']'] = TRUE;
|
||||
$this->drupalGet('admin/structure/block/add/' . $block_name . '/' . $default_theme);
|
||||
$this->assertFieldChecked('edit-visibility-request-path-negate-0');
|
||||
|
||||
$this->drupalPostForm(NULL, $edit, t('Save block'));
|
||||
$this->assertText('The block configuration has been saved.', 'Block was saved');
|
||||
|
||||
$this->clickLink('Configure');
|
||||
$this->assertFieldChecked('edit-visibility-request-path-negate-1');
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertText($title, 'Block was displayed on the front page.');
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertNoText($title, 'Block was not displayed according to block visibility rules.');
|
||||
|
||||
// Confirm that the block is not displayed to anonymous users.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($title, 'Block was not displayed to anonymous users.');
|
||||
|
||||
// Confirm that an empty block is not displayed.
|
||||
$this->assertNoText('Powered by Drupal', 'Empty block not displayed.');
|
||||
$this->assertNoRaw('sidebar-first', 'Empty sidebar-first region is not displayed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that visibility can be properly toggled.
|
||||
*/
|
||||
public function testBlockToggleVisibility() {
|
||||
$block_name = 'system_powered_by_block';
|
||||
// Create a random title for the block.
|
||||
$title = $this->randomMachineName(8);
|
||||
// Enable a standard block.
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName(8)),
|
||||
'region' => 'sidebar_first',
|
||||
'settings[label]' => $title,
|
||||
);
|
||||
$block_id = $edit['id'];
|
||||
// Set the block to be shown only to authenticated users.
|
||||
$edit['visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']'] = TRUE;
|
||||
$this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
|
||||
$this->clickLink('Configure');
|
||||
$this->assertFieldChecked('edit-visibility-user-role-roles-authenticated');
|
||||
|
||||
$edit = [
|
||||
'visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, 'Save block');
|
||||
$this->clickLink('Configure');
|
||||
$this->assertNoFieldChecked('edit-visibility-user-role-roles-authenticated');
|
||||
|
||||
// Ensure that no visibility is configured.
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
$block = Block::load($block_id);
|
||||
$visibility_config = $block->getVisibilityConditions()->getConfiguration();
|
||||
$this->assertIdentical([], $visibility_config);
|
||||
$this->assertIdentical([], $block->get('visibility'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test block visibility when leaving "pages" textarea empty.
|
||||
*/
|
||||
function testBlockVisibilityListedEmpty() {
|
||||
$block_name = 'system_powered_by_block';
|
||||
// Create a random title for the block.
|
||||
$title = $this->randomMachineName(8);
|
||||
// Enable a standard block.
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName(8)),
|
||||
'region' => 'sidebar_first',
|
||||
'settings[label]' => $title,
|
||||
'visibility[request_path][negate]' => TRUE,
|
||||
);
|
||||
// Set the block to be hidden on any user path, and to be shown only to
|
||||
// authenticated users.
|
||||
$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');
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertNoText($title, 'Block was not displayed according to block visibility rules.');
|
||||
|
||||
$this->drupalGet('USER');
|
||||
$this->assertNoText($title, 'Block was not displayed according to block visibility rules regardless of path case.');
|
||||
|
||||
// Confirm that the block is not displayed to anonymous users.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($title, 'Block was not displayed to anonymous users on the front page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test configuring and moving a module-define block to specific regions.
|
||||
*/
|
||||
function testBlock() {
|
||||
// Select the 'Powered by Drupal' block to be configured and moved.
|
||||
$block = array();
|
||||
$block['id'] = 'system_powered_by_block';
|
||||
$block['settings[label]'] = $this->randomMachineName(8);
|
||||
$block['theme'] = $this->config('system.theme')->get('default');
|
||||
$block['region'] = 'header';
|
||||
|
||||
// Set block title to confirm that interface works and override any custom titles.
|
||||
$this->drupalPostForm('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], array('settings[label]' => $block['settings[label]'], 'id' => $block['id'], 'region' => $block['region']), t('Save block'));
|
||||
$this->assertText(t('The block configuration has been saved.'), 'Block title set.');
|
||||
// Check to see if the block was created by checking its configuration.
|
||||
$instance = Block::load($block['id']);
|
||||
|
||||
$this->assertEqual($instance->label(), $block['settings[label]'], 'Stored block title found.');
|
||||
|
||||
// Check whether the block can be moved to all available regions.
|
||||
foreach ($this->regions as $region) {
|
||||
$this->moveBlockToRegion($block, $region);
|
||||
}
|
||||
|
||||
// Set the block to the disabled region.
|
||||
$edit = array();
|
||||
$edit['blocks[' . $block['id'] . '][region]'] = -1;
|
||||
$this->drupalPostForm('admin/structure/block', $edit, t('Save blocks'));
|
||||
|
||||
// Confirm that the block is now listed as disabled.
|
||||
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to disabled region.');
|
||||
|
||||
// Confirm that the block instance title and markup are not displayed.
|
||||
$this->drupalGet('node');
|
||||
$this->assertNoText(t($block['settings[label]']));
|
||||
// Check for <div id="block-my-block-instance-name"> if the machine name
|
||||
// is my_block_instance_name.
|
||||
$xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-' . str_replace('_', '-', strtolower($block['id']))));
|
||||
$this->assertNoFieldByXPath($xpath, FALSE, 'Block found in no regions.');
|
||||
|
||||
// Test deleting the block from the edit form.
|
||||
$this->drupalGet('admin/structure/block/manage/' . $block['id']);
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the block %name?', array('%name' => $block['settings[label]'])));
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
$this->assertRaw(t('The block %name has been deleted.', array('%name' => $block['settings[label]'])));
|
||||
|
||||
// Test deleting a block via "Configure block" link.
|
||||
$block = $this->drupalPlaceBlock('system_powered_by_block');
|
||||
$this->drupalGet('admin/structure/block/manage/' . $block->id(), array('query' => array('destination' => 'admin')));
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the block %name?', array('%name' => $block->label())));
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
$this->assertRaw(t('The block %name has been deleted.', array('%name' => $block->label())));
|
||||
$this->assertUrl('admin');
|
||||
$this->assertNoRaw($block->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the block form has a theme selector when not passed via the URL.
|
||||
*/
|
||||
public function testBlockThemeSelector() {
|
||||
// Install all themes.
|
||||
\Drupal::service('theme_handler')->install(array('bartik', 'seven'));
|
||||
$theme_settings = $this->config('system.theme');
|
||||
foreach (array('bartik', 'classy', 'seven') as $theme) {
|
||||
$this->drupalGet('admin/structure/block/list/' . $theme);
|
||||
$this->assertTitle(t('Block layout') . ' | Drupal');
|
||||
// Select the 'Powered by Drupal' block to be placed.
|
||||
$block = array();
|
||||
$block['id'] = strtolower($this->randomMachineName());
|
||||
$block['theme'] = $theme;
|
||||
$block['region'] = 'content';
|
||||
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block', $block, t('Save block'));
|
||||
$this->assertText(t('The block configuration has been saved.'));
|
||||
$this->assertUrl('admin/structure/block/list/' . $theme . '?block-placement=' . Html::getClass($block['id']));
|
||||
|
||||
// Set the default theme and ensure the block is placed.
|
||||
$theme_settings->set('default', $theme)->save();
|
||||
$this->drupalGet('');
|
||||
$elements = $this->xpath('//div[@id = :id]', array(':id' => Html::getUniqueId('block-' . $block['id'])));
|
||||
$this->assertTrue(!empty($elements), 'The block was found.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test block display of theme titles.
|
||||
*/
|
||||
function testThemeName() {
|
||||
// Enable the help block.
|
||||
$this->drupalPlaceBlock('help_block', array('region' => 'help'));
|
||||
// Explicitly set the default and admin themes.
|
||||
$theme = 'block_test_specialchars_theme';
|
||||
\Drupal::service('theme_handler')->install(array($theme));
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->assertEscaped('<"Cat" & \'Mouse\'>');
|
||||
$this->drupalGet('admin/structure/block/list/block_test_specialchars_theme');
|
||||
$this->assertEscaped('Demonstrate block regions (<"Cat" & \'Mouse\'>)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test block title display settings.
|
||||
*/
|
||||
function testHideBlockTitle() {
|
||||
$block_name = 'system_powered_by_block';
|
||||
// Create a random title for the block.
|
||||
$title = $this->randomMachineName(8);
|
||||
$id = strtolower($this->randomMachineName(8));
|
||||
// Enable a standard block.
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$edit = array(
|
||||
'id' => $id,
|
||||
'region' => 'sidebar_first',
|
||||
'settings[label]' => $title,
|
||||
);
|
||||
$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');
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertText($title, 'Block title was displayed by default.');
|
||||
|
||||
$edit = array(
|
||||
'settings[label_display]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/block/manage/' . $id, $edit, t('Save block'));
|
||||
$this->assertText('The block configuration has been saved.', 'Block was saved');
|
||||
|
||||
$this->drupalGet('admin/structure/block/manage/' . $id);
|
||||
$this->assertNoFieldChecked('edit-settings-label-display', 'The display_block option has the correct default value on the configuration form.');
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertNoText($title, 'Block title was not displayed when hidden.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a block to a given region via the UI and confirms the result.
|
||||
*
|
||||
* @param array $block
|
||||
* An array of information about the block, including the following keys:
|
||||
* - module: The module providing the block.
|
||||
* - title: The title of the block.
|
||||
* - delta: The block's delta key.
|
||||
* @param string $region
|
||||
* The machine name of the theme region to move the block to, for example
|
||||
* 'header' or 'sidebar_first'.
|
||||
*/
|
||||
function moveBlockToRegion(array $block, $region) {
|
||||
// Set the created block to a specific region.
|
||||
$block += array('theme' => $this->config('system.theme')->get('default'));
|
||||
$edit = array();
|
||||
$edit['blocks[' . $block['id'] . '][region]'] = $region;
|
||||
$this->drupalPostForm('admin/structure/block', $edit, t('Save blocks'));
|
||||
|
||||
// Confirm that the block was moved to the proper region.
|
||||
$this->assertText(t('The block settings have been updated.'), format_string('Block successfully moved to %region_name region.', array( '%region_name' => $region)));
|
||||
|
||||
// Confirm that the block is being displayed.
|
||||
$this->drupalGet('');
|
||||
$this->assertText(t($block['settings[label]']), 'Block successfully being displayed on the page.');
|
||||
|
||||
// Confirm that the custom block was found at the proper region.
|
||||
$xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
|
||||
':region-class' => 'region region-' . Html::getClass($region),
|
||||
':block-id' => 'block-' . str_replace('_', '-', strtolower($block['id'])),
|
||||
));
|
||||
$this->assertFieldByXPath($xpath, NULL, t('Block found in %region_name region.', array('%region_name' => Html::getClass($region))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cache tags are properly set and bubbled up to the page cache.
|
||||
*
|
||||
* Verify that invalidation of these cache tags works:
|
||||
* - "block:<block ID>"
|
||||
* - "block_plugin:<block plugin ID>"
|
||||
*/
|
||||
public function testBlockCacheTags() {
|
||||
// The page cache only works for anonymous users.
|
||||
$this->drupalLogout();
|
||||
|
||||
// Enable page caching.
|
||||
$config = $this->config('system.performance');
|
||||
$config->set('cache.page.max_age', 300);
|
||||
$config->save();
|
||||
|
||||
// Place the "Powered by Drupal" block.
|
||||
$block = $this->drupalPlaceBlock('system_powered_by_block', array('id' => 'powered'));
|
||||
|
||||
// Prime the page cache.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
|
||||
// Verify a cache hit, but also the presence of the correct cache tags in
|
||||
// both the page and block caches.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
$cid_parts = array(\Drupal::url('<front>', array(), array('absolute' => TRUE)), 'html');
|
||||
$cid = implode(':', $cid_parts);
|
||||
$cache_entry = \Drupal::cache('render')->get($cid);
|
||||
$expected_cache_tags = array(
|
||||
'config:block_list',
|
||||
'block_view',
|
||||
'config:block.block.powered',
|
||||
'rendered',
|
||||
);
|
||||
sort($expected_cache_tags);
|
||||
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
|
||||
$cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:classy');
|
||||
$expected_cache_tags = array(
|
||||
'block_view',
|
||||
'config:block.block.powered',
|
||||
'rendered',
|
||||
);
|
||||
sort($expected_cache_tags);
|
||||
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
|
||||
|
||||
// The "Powered by Drupal" block is modified; verify a cache miss.
|
||||
$block->setRegion('content');
|
||||
$block->save();
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
|
||||
// Now we should have a cache hit again.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
|
||||
// Place the "Powered by Drupal" block another time; verify a cache miss.
|
||||
$block_2 = $this->drupalPlaceBlock('system_powered_by_block', array('id' => 'powered-2'));
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
|
||||
// Verify a cache hit, but also the presence of the correct cache tags.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
$cid_parts = array(\Drupal::url('<front>', array(), array('absolute' => TRUE)), 'html');
|
||||
$cid = implode(':', $cid_parts);
|
||||
$cache_entry = \Drupal::cache('render')->get($cid);
|
||||
$expected_cache_tags = array(
|
||||
'config:block_list',
|
||||
'block_view',
|
||||
'config:block.block.powered',
|
||||
'config:block.block.powered-2',
|
||||
'rendered',
|
||||
);
|
||||
sort($expected_cache_tags);
|
||||
$this->assertEqual($cache_entry->tags, $expected_cache_tags);
|
||||
$expected_cache_tags = array(
|
||||
'block_view',
|
||||
'config:block.block.powered',
|
||||
'rendered',
|
||||
);
|
||||
sort($expected_cache_tags);
|
||||
$cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:classy');
|
||||
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
|
||||
$expected_cache_tags = array(
|
||||
'block_view',
|
||||
'config:block.block.powered-2',
|
||||
'rendered',
|
||||
);
|
||||
sort($expected_cache_tags);
|
||||
$cache_entry = \Drupal::cache('render')->get('entity_view:block:powered-2:en:classy');
|
||||
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
|
||||
|
||||
// Now we should have a cache hit again.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
|
||||
// Delete the "Powered by Drupal" blocks; verify a cache miss.
|
||||
entity_delete_multiple('block', array('powered', 'powered-2'));
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that uninstalling a theme removes its block configuration.
|
||||
*/
|
||||
public function testUninstallTheme() {
|
||||
/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
|
||||
$theme_handler = \Drupal::service('theme_handler');
|
||||
|
||||
$theme_handler->install(['seven']);
|
||||
$theme_handler->setDefault('seven');
|
||||
$block = $this->drupalPlaceBlock('system_powered_by_block', ['theme' => 'seven', 'region' => 'help']);
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertText('Powered by Drupal');
|
||||
|
||||
$theme_handler->setDefault('classy');
|
||||
$theme_handler->uninstall(['seven']);
|
||||
|
||||
// Ensure that the block configuration does not exist anymore.
|
||||
$this->assertIdentical(NULL, Block::load($block->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the block access.
|
||||
*/
|
||||
public function testBlockAccess() {
|
||||
$this->drupalPlaceBlock('test_access', ['region' => 'help']);
|
||||
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertNoText('Hello test world');
|
||||
|
||||
\Drupal::state()->set('test_block_access', TRUE);
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertText('Hello test world');
|
||||
}
|
||||
|
||||
}
|
74
core/modules/block/src/Tests/BlockTestBase.php
Normal file
74
core/modules/block/src/Tests/BlockTestBase.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Provides setup and helper methods for block module tests.
|
||||
*/
|
||||
abstract class BlockTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'filter', 'test_page_test', 'help', 'block_test');
|
||||
|
||||
/**
|
||||
* A list of theme regions to test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $regions;
|
||||
|
||||
/**
|
||||
* A test user with administrative privileges.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Use the test page as the front page.
|
||||
$this->config('system.site')->set('page.front', '/test-page')->save();
|
||||
|
||||
// Create Full HTML text format.
|
||||
$full_html_format = entity_create('filter_format', array(
|
||||
'format' => 'full_html',
|
||||
'name' => 'Full HTML',
|
||||
));
|
||||
$full_html_format->save();
|
||||
|
||||
// Create and log in an administrative user having access to the Full HTML
|
||||
// text format.
|
||||
$this->adminUser = $this->drupalCreateUser(array(
|
||||
'administer blocks',
|
||||
$full_html_format->getPermissionName(),
|
||||
'access administration pages',
|
||||
));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Define the existing regions.
|
||||
$this->regions = array(
|
||||
'header',
|
||||
'sidebar_first',
|
||||
'content',
|
||||
'sidebar_second',
|
||||
'footer',
|
||||
);
|
||||
$block_storage = $this->container->get('entity.manager')->getStorage('block');
|
||||
$blocks = $block_storage->loadByProperties(array('theme' => $this->config('system.theme')->get('default')));
|
||||
foreach ($blocks as $block) {
|
||||
$block->delete();
|
||||
}
|
||||
}
|
||||
}
|
46
core/modules/block/src/Tests/BlockTitleXSSTest.php
Normal file
46
core/modules/block/src/Tests/BlockTitleXSSTest.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockTitleXSSTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests block XSS in title.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockTitleXSSTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('test_xss_title', array('label' => '<script>alert("XSS label");</script>'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test XSS in title.
|
||||
*/
|
||||
function testXSSInTitle() {
|
||||
\Drupal::state()->set('block_test.content', $this->randomMachineName());
|
||||
$this->drupalGet('');
|
||||
$this->assertNoRaw('<script>alert("XSS label");</script>', 'The block title was properly sanitized when rendered.');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer blocks', 'access administration pages')));
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$this->drupalGet('admin/structure/block/list/' . $default_theme);
|
||||
$this->assertNoRaw("<script>alert('XSS subject');</script>", 'The block title was properly sanitized in Block Plugin UI Admin page.');
|
||||
}
|
||||
|
||||
}
|
211
core/modules/block/src/Tests/BlockUiTest.php
Normal file
211
core/modules/block/src/Tests/BlockUiTest.php
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the block configuration UI exists and stores data correctly.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockUiTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test', 'help');
|
||||
|
||||
protected $regions;
|
||||
|
||||
/**
|
||||
* The submitted block values used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $blockValues;
|
||||
|
||||
/**
|
||||
* The block entities used by this test.
|
||||
*
|
||||
* @var \Drupal\block\BlockInterface[]
|
||||
*/
|
||||
protected $blocks;
|
||||
|
||||
/**
|
||||
* An administrative user to configure the test environment.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create and log in an administrative user.
|
||||
$this->adminUser = $this->drupalCreateUser(array(
|
||||
'administer blocks',
|
||||
'access administration pages',
|
||||
));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Enable some test blocks.
|
||||
$this->blockValues = array(
|
||||
array(
|
||||
'label' => 'Tools',
|
||||
'tr' => '5',
|
||||
'plugin_id' => 'system_menu_block:tools',
|
||||
'settings' => array('region' => 'sidebar_second', 'id' => 'tools'),
|
||||
'test_weight' => '-1',
|
||||
),
|
||||
array(
|
||||
'label' => 'Powered by Drupal',
|
||||
'tr' => '16',
|
||||
'plugin_id' => 'system_powered_by_block',
|
||||
'settings' => array('region' => 'footer', 'id' => 'powered'),
|
||||
'test_weight' => '0',
|
||||
),
|
||||
);
|
||||
$this->blocks = array();
|
||||
foreach ($this->blockValues as $values) {
|
||||
$this->blocks[] = $this->drupalPlaceBlock($values['plugin_id'], $values['settings']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test block demo page exists and functions correctly.
|
||||
*/
|
||||
public function testBlockDemoUiPage() {
|
||||
$this->drupalPlaceBlock('help_block', array('region' => 'help'));
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->clickLink(t('Demonstrate block regions (@theme)', array('@theme' => 'Classy')));
|
||||
$elements = $this->xpath('//div[contains(@class, "region-highlighted")]/div[contains(@class, "block-region") and contains(text(), :title)]', array(':title' => 'Highlighted'));
|
||||
$this->assertTrue(!empty($elements), 'Block demo regions are shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test block admin page exists and functions correctly.
|
||||
*/
|
||||
function testBlockAdminUiPage() {
|
||||
// Visit the blocks admin ui.
|
||||
$this->drupalGet('admin/structure/block');
|
||||
// Look for the blocks table.
|
||||
$blocks_table = $this->xpath("//table[@id='blocks']");
|
||||
$this->assertTrue(!empty($blocks_table), 'The blocks table is being rendered.');
|
||||
// Look for test blocks in the table.
|
||||
foreach ($this->blockValues as $delta => $values) {
|
||||
$block = $this->blocks[$delta];
|
||||
$label = $block->label();
|
||||
$element = $this->xpath('//*[@id="blocks"]/tbody/tr[' . $values['tr'] . ']/td[1]/text()');
|
||||
$this->assertTrue((string) $element[0] == $label, 'The "' . $label . '" block title is set inside the ' . $values['settings']['region'] . ' region.');
|
||||
// Look for a test block region select form element.
|
||||
$this->assertField('blocks[' . $values['settings']['id'] . '][region]', 'The block "' . $values['label'] . '" has a region assignment field.');
|
||||
// Move the test block to the header region.
|
||||
$edit['blocks[' . $values['settings']['id'] . '][region]'] = 'header';
|
||||
// Look for a test block weight select form element.
|
||||
$this->assertField('blocks[' . $values['settings']['id'] . '][weight]', 'The block "' . $values['label'] . '" has a weight assignment field.');
|
||||
// Change the test block's weight.
|
||||
$edit['blocks[' . $values['settings']['id'] . '][weight]'] = $values['test_weight'];
|
||||
}
|
||||
$this->drupalPostForm('admin/structure/block', $edit, t('Save blocks'));
|
||||
foreach ($this->blockValues as $values) {
|
||||
// Check if the region and weight settings changes have persisted.
|
||||
$this->assertOptionSelected(
|
||||
'edit-blocks-' . $values['settings']['id'] . '-region',
|
||||
'header',
|
||||
'The block "' . $label . '" has the correct region assignment (header).'
|
||||
);
|
||||
$this->assertOptionSelected(
|
||||
'edit-blocks-' . $values['settings']['id'] . '-weight',
|
||||
$values['test_weight'],
|
||||
'The block "' . $label . '" has the correct weight assignment (' . $values['test_weight'] . ').'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the block categories on the listing page.
|
||||
*/
|
||||
public function testCandidateBlockList() {
|
||||
$arguments = array(
|
||||
':ul_class' => 'block-list',
|
||||
':li_class' => 'test-block-instantiation',
|
||||
':href' => 'admin/structure/block/add/test_block_instantiation/classy',
|
||||
':text' => 'Display message',
|
||||
);
|
||||
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$elements = $this->xpath('//details[@id="edit-category-block-test"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(!empty($elements), 'The test block appears in the category for its module.');
|
||||
|
||||
// Trigger the custom category addition in block_test_block_alter().
|
||||
$this->container->get('state')->set('block_test_info_alter', TRUE);
|
||||
$this->container->get('plugin.manager.block')->clearCachedDefinitions();
|
||||
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$elements = $this->xpath('//details[@id="edit-category-custom-category"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(!empty($elements), 'The test block appears in a custom category controlled by block_test_block_alter().');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior of context-aware blocks.
|
||||
*/
|
||||
public function testContextAwareBlocks() {
|
||||
$arguments = array(
|
||||
':ul_class' => 'block-list',
|
||||
':li_class' => 'test-context-aware',
|
||||
':href' => 'admin/structure/block/add/test_context_aware/classy',
|
||||
':text' => 'Test context-aware block',
|
||||
);
|
||||
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$elements = $this->xpath('//details[@id="edit-category-block-test"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(empty($elements), 'The context-aware test block does not appear.');
|
||||
$definition = \Drupal::service('plugin.manager.block')->getDefinition('test_context_aware');
|
||||
$this->assertTrue(!empty($definition), 'The context-aware test block exists.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the BlockForm populates machine name correctly.
|
||||
*/
|
||||
public function testMachineNameSuggestion() {
|
||||
$url = 'admin/structure/block/add/test_block_instantiation/classy';
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldByName('id', 'displaymessage', 'Block form uses raw machine name suggestion when no instance already exists.');
|
||||
$this->drupalPostForm($url, array(), 'Save block');
|
||||
|
||||
// Now, check to make sure the form starts by autoincrementing correctly.
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldByName('id', 'displaymessage_2', 'Block form appends _2 to plugin-suggested machine name when an instance already exists.');
|
||||
$this->drupalPostForm($url, array(), 'Save block');
|
||||
|
||||
// And verify that it continues working beyond just the first two.
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldByName('id', 'displaymessage_3', 'Block form appends _3 to plugin-suggested machine name when two instances already exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the block placement indicator.
|
||||
*/
|
||||
public function testBlockPlacementIndicator() {
|
||||
// Select the 'Powered by Drupal' block to be placed.
|
||||
$block = array();
|
||||
$block['id'] = strtolower($this->randomMachineName());
|
||||
$block['theme'] = 'classy';
|
||||
$block['region'] = 'content';
|
||||
|
||||
// After adding a block, it will indicate which block was just added.
|
||||
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block', $block, t('Save block'));
|
||||
$this->assertUrl('admin/structure/block/list/classy?block-placement=' . Html::getClass($block['id']));
|
||||
|
||||
// Resaving the block page will remove the block indicator.
|
||||
$this->drupalPostForm(NULL, array(), t('Save blocks'));
|
||||
$this->assertUrl('admin/structure/block/list/classy');
|
||||
}
|
||||
|
||||
}
|
271
core/modules/block/src/Tests/BlockViewBuilderTest.php
Normal file
271
core/modules/block/src/Tests/BlockViewBuilderTest.php
Normal file
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockViewBuilderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\Context\UrlCacheContext;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\block\Entity\Block;
|
||||
|
||||
/**
|
||||
* Tests the block view builder.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockViewBuilderTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test', 'system');
|
||||
|
||||
/**
|
||||
* The block being tested.
|
||||
*
|
||||
* @var \Drupal\block\Entity\BlockInterface
|
||||
*/
|
||||
protected $block;
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->controller = $this->container
|
||||
->get('entity.manager')
|
||||
->getStorage('block');
|
||||
|
||||
\Drupal::state()->set('block_test.content', 'Llamas > unicorns!');
|
||||
|
||||
// Create a block with only required values.
|
||||
$this->block = $this->controller->create(array(
|
||||
'id' => 'test_block',
|
||||
'theme' => 'stark',
|
||||
'plugin' => 'test_cache',
|
||||
));
|
||||
$this->block->save();
|
||||
|
||||
$this->container->get('cache.render')->deleteAll();
|
||||
|
||||
$this->renderer = $this->container->get('renderer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the rendering of blocks.
|
||||
*/
|
||||
public function testBasicRendering() {
|
||||
\Drupal::state()->set('block_test.content', '');
|
||||
|
||||
$entity = $this->controller->create(array(
|
||||
'id' => 'test_block1',
|
||||
'theme' => 'stark',
|
||||
'plugin' => 'test_html',
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
// Test the rendering of a block.
|
||||
$entity = Block::load('test_block1');
|
||||
$output = entity_view($entity, 'block');
|
||||
$expected = array();
|
||||
$expected[] = '<div id="block-test-block1">';
|
||||
$expected[] = ' ';
|
||||
$expected[] = ' ';
|
||||
$expected[] = ' ';
|
||||
$expected[] = ' </div>';
|
||||
$expected[] = '';
|
||||
$expected_output = implode("\n", $expected);
|
||||
$this->assertEqual($this->renderer->renderRoot($output), $expected_output);
|
||||
|
||||
// Reset the HTML IDs so that the next render is not affected.
|
||||
Html::resetSeenIds();
|
||||
|
||||
// Test the rendering of a block with a given title.
|
||||
$entity = $this->controller->create(array(
|
||||
'id' => 'test_block2',
|
||||
'theme' => 'stark',
|
||||
'plugin' => 'test_html',
|
||||
'settings' => array(
|
||||
'label' => 'Powered by Bananas',
|
||||
),
|
||||
));
|
||||
$entity->save();
|
||||
$output = entity_view($entity, 'block');
|
||||
$expected = array();
|
||||
$expected[] = '<div id="block-test-block2">';
|
||||
$expected[] = ' ';
|
||||
$expected[] = ' <h2>Powered by Bananas</h2>';
|
||||
$expected[] = ' ';
|
||||
$expected[] = ' ';
|
||||
$expected[] = ' </div>';
|
||||
$expected[] = '';
|
||||
$expected_output = implode("\n", $expected);
|
||||
$this->assertEqual($this->renderer->renderRoot($output), $expected_output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests block render cache handling.
|
||||
*/
|
||||
public function testBlockViewBuilderCache() {
|
||||
// Verify cache handling for a non-empty block.
|
||||
$this->verifyRenderCacheHandling();
|
||||
|
||||
// Create an empty block.
|
||||
$this->block = $this->controller->create(array(
|
||||
'id' => 'test_block',
|
||||
'theme' => 'stark',
|
||||
'plugin' => 'test_cache',
|
||||
));
|
||||
$this->block->save();
|
||||
\Drupal::state()->set('block_test.content', NULL);
|
||||
|
||||
// Verify cache handling for an empty block.
|
||||
$this->verifyRenderCacheHandling();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies render cache handling of the block being tested.
|
||||
*
|
||||
* @see ::testBlockViewBuilderCache()
|
||||
*/
|
||||
protected function verifyRenderCacheHandling() {
|
||||
// Force a request via GET so we can test the render cache.
|
||||
$request = \Drupal::request();
|
||||
$request_method = $request->server->get('REQUEST_METHOD');
|
||||
$request->setMethod('GET');
|
||||
|
||||
// Test that a cache entry is created.
|
||||
$build = $this->getBlockRenderArray();
|
||||
$cid = 'entity_view:block:test_block:en:core';
|
||||
$this->renderer->renderRoot($build);
|
||||
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
|
||||
|
||||
// Re-save the block and check that the cache entry has been deleted.
|
||||
$this->block->save();
|
||||
$this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was saved.');
|
||||
|
||||
// Rebuild the render array (creating a new cache entry in the process) and
|
||||
// delete the block to check the cache entry is deleted.
|
||||
unset($build['#printed']);
|
||||
// Re-add the block because \Drupal\block\BlockViewBuilder::buildBlock()
|
||||
// removes it.
|
||||
$build['#block'] = $this->block;
|
||||
|
||||
$this->renderer->renderRoot($build);
|
||||
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
|
||||
$this->block->delete();
|
||||
$this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was deleted.');
|
||||
|
||||
// Restore the previous request method.
|
||||
$request->setMethod($request_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests block view altering.
|
||||
*/
|
||||
public function testBlockViewBuilderAlter() {
|
||||
// Establish baseline.
|
||||
$build = $this->getBlockRenderArray();
|
||||
$this->assertIdentical($this->renderer->renderRoot($build), 'Llamas > unicorns!');
|
||||
|
||||
// Enable the block view alter hook that adds a suffix, for basic testing.
|
||||
\Drupal::state()->set('block_test_view_alter_suffix', TRUE);
|
||||
Cache::invalidateTags($this->block->getCacheTags());
|
||||
$build = $this->getBlockRenderArray();
|
||||
$this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === '<br>Goodbye!', 'A block with content is altered.');
|
||||
$this->assertIdentical($this->renderer->renderRoot($build), 'Llamas > unicorns!<br>Goodbye!');
|
||||
\Drupal::state()->set('block_test_view_alter_suffix', FALSE);
|
||||
|
||||
// Force a request via GET so we can test the render cache.
|
||||
$request = \Drupal::request();
|
||||
$request_method = $request->server->get('REQUEST_METHOD');
|
||||
$request->setMethod('GET');
|
||||
|
||||
\Drupal::state()->set('block_test.content', NULL);
|
||||
Cache::invalidateTags($this->block->getCacheTags());
|
||||
|
||||
$default_keys = array('entity_view', 'block', 'test_block');
|
||||
$default_tags = array('block_view', 'config:block.block.test_block');
|
||||
|
||||
// Advanced: cached block, but an alter hook adds an additional cache key.
|
||||
$alter_add_key = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test_view_alter_cache_key', $alter_add_key);
|
||||
$cid = 'entity_view:block:test_block:' . $alter_add_key . ':en:core';
|
||||
$expected_keys = array_merge($default_keys, array($alter_add_key));
|
||||
$build = $this->getBlockRenderArray();
|
||||
$this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.');
|
||||
$this->assertIdentical($this->renderer->renderRoot($build), '');
|
||||
$cache_entry = $this->container->get('cache.render')->get($cid);
|
||||
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
|
||||
$expected_tags = array_merge($default_tags, ['rendered']);
|
||||
sort($expected_tags);
|
||||
$this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
|
||||
$this->container->get('cache.render')->delete($cid);
|
||||
|
||||
// Advanced: cached block, but an alter hook adds an additional cache tag.
|
||||
$alter_add_tag = $this->randomMachineName();
|
||||
\Drupal::state()->set('block_test_view_alter_cache_tag', $alter_add_tag);
|
||||
$expected_tags = Cache::mergeTags($default_tags, array($alter_add_tag));
|
||||
$build = $this->getBlockRenderArray();
|
||||
sort($build['#cache']['tags']);
|
||||
$this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
|
||||
$this->assertIdentical($this->renderer->renderRoot($build), '');
|
||||
$cache_entry = $this->container->get('cache.render')->get($cid);
|
||||
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
|
||||
$expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']);
|
||||
sort($expected_tags);
|
||||
$this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
|
||||
$this->container->get('cache.render')->delete($cid);
|
||||
|
||||
// Advanced: cached block, but an alter hook adds a #pre_render callback to
|
||||
// alter the eventual content.
|
||||
\Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
|
||||
$build = $this->getBlockRenderArray();
|
||||
$this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.');
|
||||
$this->assertIdentical($this->renderer->renderRoot($build), 'Hiya!<br>');
|
||||
$this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.');
|
||||
|
||||
// Restore the previous request method.
|
||||
$request->setMethod($request_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fully built render array for a block.
|
||||
*
|
||||
* @return array
|
||||
* The render array.
|
||||
*/
|
||||
protected function getBlockRenderArray() {
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('block')->view($this->block, 'block');
|
||||
|
||||
// Mock the build array to not require the theme registry.
|
||||
unset($build['#theme']);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
94
core/modules/block/src/Tests/BlockXssTest.php
Normal file
94
core/modules/block/src/Tests/BlockXssTest.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\BlockXssTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContent;
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\system\Entity\Menu;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests that the block module properly escapes block descriptions.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockXssTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'block_content', 'menu_ui', 'views'];
|
||||
|
||||
/**
|
||||
* Tests various modules that provide blocks for XSS.
|
||||
*/
|
||||
public function testBlockXss() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
$this->doViewTest();
|
||||
$this->doMenuTest();
|
||||
$this->doBlockContentTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests XSS coming from View block labels.
|
||||
*/
|
||||
protected function doViewTest() {
|
||||
$view = View::create([
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => '<script>alert("view");</script>',
|
||||
]);
|
||||
$view->addDisplay('block');
|
||||
$view->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('block.admin_display'));
|
||||
$this->clickLink('<script>alert("view");</script>');
|
||||
$this->assertRaw('<script>alert("view");</script>');
|
||||
$this->assertNoRaw('<script>alert("view");</script>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests XSS coming from Menu block labels.
|
||||
*/
|
||||
protected function doMenuTest() {
|
||||
Menu::create([
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => '<script>alert("menu");</script>',
|
||||
])->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('block.admin_display'));
|
||||
$this->clickLink('<script>alert("menu");</script>');
|
||||
$this->assertRaw('<script>alert("menu");</script>');
|
||||
$this->assertNoRaw('<script>alert("menu");</script>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests XSS coming from Block Content block info.
|
||||
*/
|
||||
protected function doBlockContentTest() {
|
||||
BlockContentType::create([
|
||||
'id' => 'basic',
|
||||
'label' => 'basic',
|
||||
'revision' => TRUE,
|
||||
])->save();
|
||||
BlockContent::create([
|
||||
'type' => 'basic',
|
||||
'info' => '<script>alert("block_content");</script>',
|
||||
])->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('block.admin_display'));
|
||||
$this->clickLink('<script>alert("block_content");</script>');
|
||||
$this->assertRaw('<script>alert("block_content");</script>');
|
||||
$this->assertNoRaw('<script>alert("block_content");</script>');
|
||||
}
|
||||
|
||||
}
|
70
core/modules/block/src/Tests/NewDefaultThemeBlocksTest.php
Normal file
70
core/modules/block/src/Tests/NewDefaultThemeBlocksTest.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\NewDefaultThemeBlocksTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the new default theme gets blocks.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class NewDefaultThemeBlocksTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block');
|
||||
|
||||
/**
|
||||
* Check the enabled Bartik blocks are correctly copied over.
|
||||
*/
|
||||
function testNewDefaultThemeBlocks() {
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
|
||||
// Add two instances of the user login block.
|
||||
$this->drupalPlaceBlock('user_login_block', array(
|
||||
'id' => $default_theme . '_' . strtolower($this->randomMachineName(8)),
|
||||
));
|
||||
$this->drupalPlaceBlock('user_login_block', array(
|
||||
'id' => $default_theme . '_' . strtolower($this->randomMachineName(8)),
|
||||
));
|
||||
|
||||
// Add an instance of a different block.
|
||||
$this->drupalPlaceBlock('system_powered_by_block', array(
|
||||
'id' => $default_theme . '_' . strtolower($this->randomMachineName(8)),
|
||||
));
|
||||
|
||||
// Install a different theme.
|
||||
$new_theme = 'bartik';
|
||||
$this->assertFalse($new_theme == $default_theme, 'The new theme is different from the previous default theme.');
|
||||
\Drupal::service('theme_handler')->install(array($new_theme));
|
||||
$this->config('system.theme')
|
||||
->set('default', $new_theme)
|
||||
->save();
|
||||
|
||||
// Ensure that the new theme has all the blocks as the previous default.
|
||||
$default_block_names = $this->container->get('entity.query')->get('block')
|
||||
->condition('theme', $default_theme)
|
||||
->execute();
|
||||
$new_blocks = $this->container->get('entity.query')->get('block')
|
||||
->condition('theme', $new_theme)
|
||||
->execute();
|
||||
$this->assertTrue(count($default_block_names) == count($new_blocks), 'The new default theme has the same number of blocks as the previous theme.');
|
||||
foreach ($default_block_names as $default_block_name) {
|
||||
// Remove the matching block from the list of blocks in the new theme.
|
||||
// E.g., if the old theme has block.block.stark_admin,
|
||||
// unset block.block.bartik_admin.
|
||||
unset($new_blocks[str_replace($default_theme . '_', $new_theme . '_', $default_block_name)]);
|
||||
}
|
||||
$this->assertTrue(empty($new_blocks), 'The new theme has exactly the same blocks as the previous default theme.');
|
||||
}
|
||||
|
||||
}
|
37
core/modules/block/src/Tests/NonDefaultBlockAdminTest.php
Normal file
37
core/modules/block/src/Tests/NonDefaultBlockAdminTest.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\NonDefaultBlockAdminTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the block administration page for a non-default theme.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class NonDefaultBlockAdminTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block');
|
||||
|
||||
/**
|
||||
* Test non-default theme admin.
|
||||
*/
|
||||
function testNonDefaultBlockAdmin() {
|
||||
$admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes'));
|
||||
$this->drupalLogin($admin_user);
|
||||
$new_theme = 'bartik';
|
||||
\Drupal::service('theme_handler')->install(array($new_theme));
|
||||
$this->drupalGet('admin/structure/block/list/' . $new_theme);
|
||||
$this->assertText('Bartik(' . t('active tab') . ')', 'Tab for non-default theme found.');
|
||||
}
|
||||
}
|
295
core/modules/block/src/Tests/Views/DisplayBlockTest.php
Normal file
295
core/modules/block/src/Tests/Views/DisplayBlockTest.php
Normal file
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Tests\Views\DisplayBlockTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Tests\Views;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
|
||||
/**
|
||||
* Tests the block display plugin.
|
||||
*
|
||||
* @group block
|
||||
* @see \Drupal\block\Plugin\views\display\Block
|
||||
*/
|
||||
class DisplayBlockTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'block_test_views', 'test_page_test', 'contextual', 'views_ui');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_block', 'test_view_block2');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('block_test_views'));
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests default and custom block categories.
|
||||
*/
|
||||
public function testBlockCategory() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer views', 'administer blocks')));
|
||||
|
||||
// Create a new view in the UI.
|
||||
$edit = array();
|
||||
$edit['label'] = $this->randomString();
|
||||
$edit['id'] = strtolower($this->randomMachineName());
|
||||
$edit['show[wizard_key]'] = 'standard:views_test_data';
|
||||
$edit['description'] = $this->randomString();
|
||||
$edit['block[create]'] = TRUE;
|
||||
$edit['block[style][row_plugin]'] = 'fields';
|
||||
$this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
|
||||
|
||||
// Test that the block was given a default category corresponding to its
|
||||
// base table.
|
||||
$arguments = array(
|
||||
':id' => 'edit-category-lists-views',
|
||||
':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-1',
|
||||
':href' => \Drupal::Url('block.admin_add', array(
|
||||
'plugin_id' => 'views_block:' . $edit['id'] . '-block_1',
|
||||
'theme' => 'classy',
|
||||
)),
|
||||
':text' => $edit['label'],
|
||||
);
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(!empty($elements), 'The test block appears in the category for its base table.');
|
||||
|
||||
// Duplicate the block before changing the category.
|
||||
$this->drupalPostForm('admin/structure/views/view/' . $edit['id'] . '/edit/block_1', array(), t('Duplicate @display_title', array('@display_title' => 'Block')));
|
||||
$this->assertUrl('admin/structure/views/view/' . $edit['id'] . '/edit/block_2');
|
||||
|
||||
// Change the block category to a random string.
|
||||
$this->drupalGet('admin/structure/views/view/' . $edit['id'] . '/edit/block_1');
|
||||
$label = t('Lists (Views)');
|
||||
$link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:label]', array(':label' => $label));
|
||||
$this->assertTrue(!empty($link));
|
||||
$this->clickLink($label);
|
||||
$category = $this->randomString();
|
||||
$this->drupalPostForm(NULL, array('block_category' => $category), t('Apply'));
|
||||
|
||||
// Duplicate the block after changing the category.
|
||||
$this->drupalPostForm(NULL, array(), t('Duplicate @display_title', array('@display_title' => 'Block')));
|
||||
$this->assertUrl('admin/structure/views/view/' . $edit['id'] . '/edit/block_3');
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Test that the blocks are listed under the correct categories.
|
||||
$category_id = Html::getUniqueId('edit-category-' . SafeMarkup::checkPlain($category));
|
||||
$arguments[':id'] = $category_id;
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(!empty($elements), 'The test block appears in the custom category.');
|
||||
|
||||
$arguments = array(
|
||||
':id' => 'edit-category-lists-views',
|
||||
':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-2',
|
||||
':href' => \Drupal::Url('block.admin_add', array(
|
||||
'plugin_id' => 'views_block:' . $edit['id'] . '-block_2',
|
||||
'theme' => 'classy',
|
||||
)),
|
||||
':text' => $edit['label'],
|
||||
);
|
||||
$elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(!empty($elements), 'The first duplicated test block remains in the original category.');
|
||||
|
||||
$arguments = array(
|
||||
':id' => $category_id,
|
||||
':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-3',
|
||||
':href' => \Drupal::Url('block.admin_add', array(
|
||||
'plugin_id' => 'views_block:' . $edit['id'] . '-block_3',
|
||||
'theme' => 'classy',
|
||||
)),
|
||||
':text' => $edit['label'],
|
||||
);
|
||||
$elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
|
||||
$this->assertTrue(!empty($elements), 'The second duplicated test block appears in the custom category.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests removing a block display.
|
||||
*/
|
||||
public function testDeleteBlockDisplay() {
|
||||
// To test all combinations possible we first place create two instances
|
||||
// of the block display of the first view.
|
||||
$block_1 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('label' => 'test_view_block-block_1:1'));
|
||||
$block_2 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('label' => 'test_view_block-block_1:2'));
|
||||
|
||||
// Then we add one instance of blocks for each of the two displays of the
|
||||
// second view.
|
||||
$block_3 = $this->drupalPlaceBlock('views_block:test_view_block2-block_1', array('label' => 'test_view_block2-block_1'));
|
||||
$block_4 = $this->drupalPlaceBlock('views_block:test_view_block2-block_2', array('label' => 'test_view_block2-block_2'));
|
||||
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertBlockAppears($block_1);
|
||||
$this->assertBlockAppears($block_2);
|
||||
$this->assertBlockAppears($block_3);
|
||||
$this->assertBlockAppears($block_4);
|
||||
|
||||
$block_storage = $this->container->get('entity.manager')->getStorage('block');
|
||||
|
||||
// Remove the block display, so both block entities from the first view
|
||||
// should both disappear.
|
||||
$view = Views::getView('test_view_block');
|
||||
$view->initDisplay();
|
||||
$view->displayHandlers->remove('block_1');
|
||||
$view->storage->save();
|
||||
|
||||
$this->assertFalse($block_storage->load($block_1->id()), 'The block for this display was removed.');
|
||||
$this->assertFalse($block_storage->load($block_2->id()), 'The block for this display was removed.');
|
||||
$this->assertTrue($block_storage->load($block_3->id()), 'A block from another view was unaffected.');
|
||||
$this->assertTrue($block_storage->load($block_4->id()), 'A block from another view was unaffected.');
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertNoBlockAppears($block_1);
|
||||
$this->assertNoBlockAppears($block_2);
|
||||
$this->assertBlockAppears($block_3);
|
||||
$this->assertBlockAppears($block_4);
|
||||
|
||||
// Remove the first block display of the second view and ensure the block
|
||||
// instance of the second block display still exists.
|
||||
$view = Views::getView('test_view_block2');
|
||||
$view->initDisplay();
|
||||
$view->displayHandlers->remove('block_1');
|
||||
$view->storage->save();
|
||||
|
||||
$this->assertFalse($block_storage->load($block_3->id()), 'The block for this display was removed.');
|
||||
$this->assertTrue($block_storage->load($block_4->id()), 'A block from another display on the same view was unaffected.');
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertNoBlockAppears($block_3);
|
||||
$this->assertBlockAppears($block_4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the block form for a Views block.
|
||||
*/
|
||||
public function testViewsBlockForm() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer blocks')));
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme);
|
||||
$elements = $this->xpath('//input[@name="label"]');
|
||||
$this->assertTrue(empty($elements), 'The label field is not found for Views blocks.');
|
||||
// Test that that machine name field is hidden from display and has been
|
||||
// saved as expected from the default value.
|
||||
$this->assertNoFieldById('edit-machine-name', 'views_block__test_view_block_1', 'The machine name is hidden on the views block form.');
|
||||
// Save the block.
|
||||
$this->drupalPostForm(NULL, array(), t('Save block'));
|
||||
$storage = $this->container->get('entity.manager')->getStorage('block');
|
||||
$block = $storage->load('views_block__test_view_block_block_1');
|
||||
// This will only return a result if our new block has been created with the
|
||||
// expected machine name.
|
||||
$this->assertTrue(!empty($block), 'The expected block was loaded.');
|
||||
|
||||
for ($i = 2; $i <= 3; $i++) {
|
||||
// Place the same block again and make sure we have a new ID.
|
||||
$this->drupalPostForm('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, array(), t('Save block'));
|
||||
$block = $storage->load('views_block__test_view_block_block_1_' . $i);
|
||||
// This will only return a result if our new block has been created with the
|
||||
// expected machine name.
|
||||
$this->assertTrue(!empty($block), 'The expected block was loaded.');
|
||||
}
|
||||
|
||||
// Tests the override capability of items per page.
|
||||
$this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme);
|
||||
$edit = array();
|
||||
$edit['settings[override][items_per_page]'] = 10;
|
||||
|
||||
$this->drupalPostForm('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, $edit, t('Save block'));
|
||||
|
||||
$block = $storage->load('views_block__test_view_block_block_1_4');
|
||||
$config = $block->getPlugin()->getConfiguration();
|
||||
$this->assertEqual(10, $config['items_per_page'], "'Items per page' is properly saved.");
|
||||
|
||||
$edit['settings[override][items_per_page]'] = 5;
|
||||
$this->drupalPostForm('admin/structure/block/manage/views_block__test_view_block_block_1_4', $edit, t('Save block'));
|
||||
|
||||
$block = $storage->load('views_block__test_view_block_block_1_4');
|
||||
|
||||
$config = $block->getPlugin()->getConfiguration();
|
||||
$this->assertEqual(5, $config['items_per_page'], "'Items per page' is properly saved.");
|
||||
|
||||
// Tests the override of the label capability.
|
||||
$edit = array();
|
||||
$edit['settings[views_label_checkbox]'] = 1;
|
||||
$edit['settings[views_label]'] = 'Custom title';
|
||||
$this->drupalPostForm('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, $edit, t('Save block'));
|
||||
|
||||
$block = $storage->load('views_block__test_view_block_block_1_5');
|
||||
$config = $block->getPlugin()->getConfiguration();
|
||||
$this->assertEqual('Custom title', $config['views_label'], "'Label' is properly saved.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the actual rendering of the views block.
|
||||
*/
|
||||
public function testBlockRendering() {
|
||||
// Create a block and set a custom title.
|
||||
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('label' => 'test_view_block-block_1:1', 'views_label' => 'Custom title'));
|
||||
$this->drupalGet('');
|
||||
|
||||
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
|
||||
$this->assertEqual((string) $result[0], 'Custom title');
|
||||
|
||||
// Don't override the title anymore.
|
||||
$plugin = $block->getPlugin();
|
||||
$plugin->setConfigurationValue('views_label', '');
|
||||
$block->save();
|
||||
|
||||
$this->drupalGet('');
|
||||
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
|
||||
$this->assertEqual((string) $result[0], 'test_view_block');
|
||||
|
||||
// Hide the title.
|
||||
$block->getPlugin()->setConfigurationValue('label_display', FALSE);
|
||||
$block->save();
|
||||
|
||||
$this->drupalGet('');
|
||||
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
|
||||
$this->assertTrue(empty($result), 'The title is not visible.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the contextual links on a Views block.
|
||||
*/
|
||||
public function testBlockContextualLinks() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer views', 'access contextual links', 'administer blocks')));
|
||||
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1');
|
||||
$cached_block = $this->drupalPlaceBlock('views_block:test_view_block-block_1');
|
||||
$this->drupalGet('test-page');
|
||||
|
||||
$id = 'block:block=' . $block->id() . ':langcode=en|entity.view.edit_form:view=test_view_block:location=block&name=test_view_block&display_id=block_1&langcode=en';
|
||||
$cached_id = 'block:block=' . $cached_block->id() . ':langcode=en|entity.view.edit_form:view=test_view_block:location=block&name=test_view_block&display_id=block_1&langcode=en';
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
|
||||
$this->assertRaw('<div' . new Attribute(array('data-contextual-id' => $id)) . '></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));
|
||||
$this->assertRaw('<div' . new Attribute(array('data-contextual-id' => $cached_id)) . '></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $cached_id)));
|
||||
|
||||
// Get server-rendered contextual links.
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks()
|
||||
$post = array('ids[0]' => $id, 'ids[1]' => $cached_id);
|
||||
$response = $this->drupalPostWithFormat('contextual/render', 'json', $post, array('query' => array('destination' => 'test-page')));
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1">Edit view</a></li></ul>');
|
||||
$this->assertIdentical($json[$cached_id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $cached_block->id() . '">Configure block</a></li><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1">Edit view</a></li></ul>');
|
||||
}
|
||||
|
||||
}
|
34
core/modules/block/src/Theme/AdminDemoNegotiator.php
Normal file
34
core/modules/block/src/Theme/AdminDemoNegotiator.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block\Theme\AdminDemoNegotiator.
|
||||
*/
|
||||
|
||||
namespace Drupal\block\Theme;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Theme\ThemeNegotiatorInterface;
|
||||
|
||||
/**
|
||||
* Negotiates the theme for the block admin demo page via the URL.
|
||||
*/
|
||||
class AdminDemoNegotiator implements ThemeNegotiatorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match) {
|
||||
return $route_match->getRouteName() == 'block.admin_demo';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match) {
|
||||
// We return exactly what was passed in, to guarantee that the page will
|
||||
// always be displayed using the theme whose blocks are being configured.
|
||||
return $route_match->getParameter('theme');
|
||||
}
|
||||
|
||||
}
|
23
core/modules/block/templates/block-list.html.twig
Normal file
23
core/modules/block/templates/block-list.html.twig
Normal file
|
@ -0,0 +1,23 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Two column template for the block add/edit form.
|
||||
*
|
||||
* This template will be used when a block edit form specifies 'block_edit_form'
|
||||
* as its #theme callback. Otherwise, by default, block add/edit forms will be
|
||||
* themed by form.html.twig.
|
||||
*
|
||||
* Available variables:
|
||||
* - form: The block add/edit form.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<div class="layout-block-list clearfix">
|
||||
<div class="layout-region block-list-primary">
|
||||
{{ form|without('place_blocks') }}
|
||||
</div>
|
||||
<div class="layout-region block-list-secondary">
|
||||
{{ form.place_blocks }}
|
||||
</div>
|
||||
</div>
|
46
core/modules/block/templates/block.html.twig
Normal file
46
core/modules/block/templates/block.html.twig
Normal file
|
@ -0,0 +1,46 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation to display a block.
|
||||
*
|
||||
* Available variables:
|
||||
* - plugin_id: The ID of the block implementation.
|
||||
* - label: The configured label of the block if visible.
|
||||
* - configuration: A list of the block's configuration values.
|
||||
* - label: The configured label for the block.
|
||||
* - label_display: The display settings for the label.
|
||||
* - module: The module that provided this block plugin.
|
||||
* - cache: The cache settings.
|
||||
* - Block plugin specific settings will also be stored here.
|
||||
* - block - The full block entity.
|
||||
* - label_hidden: The hidden block title value if the block was
|
||||
* configured to hide the title ('label' is empty in this case).
|
||||
* - module: The module that generated the block.
|
||||
* - delta: An ID for the block, unique within each module.
|
||||
* - region: The block region embedding the current block.
|
||||
* - content: The content of this block.
|
||||
* - attributes: array of HTML attributes populated by modules, intended to
|
||||
* be added to the main container tag of this template.
|
||||
* - id: A valid HTML ID and guaranteed unique.
|
||||
* - title_attributes: Same as attributes, except applied to the main title
|
||||
* tag that appears in the template.
|
||||
* - title_prefix: Additional output populated by modules, intended to be
|
||||
* displayed in front of the main title tag that appears in the template.
|
||||
* - title_suffix: Additional output populated by modules, intended to be
|
||||
* displayed after the main title tag that appears in the template.
|
||||
*
|
||||
* @see template_preprocess_block()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<div{{ attributes }}>
|
||||
{{ title_prefix }}
|
||||
{% if label %}
|
||||
<h2{{ title_attributes }}>{{ label }}</h2>
|
||||
{% endif %}
|
||||
{{ title_suffix }}
|
||||
{% block content %}
|
||||
{{ content }}
|
||||
{% endblock %}
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
name: 'Block test'
|
||||
type: module
|
||||
description: 'Provides test blocks.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- block
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide test blocks.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Implements hook_block_alter().
|
||||
*/
|
||||
function block_test_block_alter(&$block_info) {
|
||||
if (\Drupal::state()->get('block_test_info_alter') && isset($block_info['test_block_instantiation'])) {
|
||||
$block_info['test_block_instantiation']['category'] = t('Custom category');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_view_BASE_BLOCK_ID_alter().
|
||||
*/
|
||||
function block_test_block_view_test_cache_alter(array &$build, BlockPluginInterface $block) {
|
||||
if (\Drupal::state()->get('block_test_view_alter_suffix') !== NULL) {
|
||||
$build['#suffix'] = '<br>Goodbye!';
|
||||
}
|
||||
if (\Drupal::state()->get('block_test_view_alter_cache_key') !== NULL) {
|
||||
$build['#cache']['keys'][] = \Drupal::state()->get('block_test_view_alter_cache_key');
|
||||
}
|
||||
if (\Drupal::state()->get('block_test_view_alter_cache_tag') !== NULL) {
|
||||
$build['#cache']['tags'][] = \Drupal::state()->get('block_test_view_alter_cache_tag');
|
||||
}
|
||||
if (\Drupal::state()->get('block_test_view_alter_append_pre_render_prefix') !== NULL) {
|
||||
$build['#pre_render'][] = 'block_test_pre_render_alter_content';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for a block to alter its content.
|
||||
*/
|
||||
function block_test_pre_render_alter_content($build) {
|
||||
$build['#prefix'] = 'Hiya!<br>';
|
||||
return $build;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
id: test_block
|
||||
theme: stark
|
||||
weight: 0
|
||||
status: true
|
||||
langcode: en
|
||||
region: '-1'
|
||||
plugin: test_html
|
||||
settings:
|
||||
label: 'Test HTML block'
|
||||
provider: block_test
|
||||
label_display: 'hidden'
|
||||
dependencies:
|
||||
module:
|
||||
- block_test
|
||||
theme:
|
||||
- classy
|
||||
visibility: { }
|
|
@ -0,0 +1,7 @@
|
|||
block.settings.test_block_instantiation:
|
||||
type: block_settings
|
||||
label: 'Test block instantiation settings'
|
||||
mapping:
|
||||
display_message:
|
||||
type: string
|
||||
label: 'Message text'
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block_test\Plugin\Block\TestAccessBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a block to test access.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_access",
|
||||
* admin_label = @Translation("Test block access")
|
||||
* )
|
||||
*/
|
||||
class TestAccessBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Tests the test access block.
|
||||
*
|
||||
*
|
||||
* @param array $configuration
|
||||
* The plugin configuration, i.e. an array with configuration values keyed
|
||||
* by configuration option name. The special key 'context' may be used to
|
||||
* initialize the defined contexts by setting it to an array of context
|
||||
* values keyed by context names.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, StateInterface $state) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('state')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function blockAccess(AccountInterface $account) {
|
||||
return $this->state->get('test_block_access', FALSE) ? AccessResult::allowed() : AccessResult::forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return ['#markup' => 'Hello test world'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block_test\Plugin\Block\TestBlockInstantiation.
|
||||
*/
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides a basic block for testing block instantiation and configuration.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_block_instantiation",
|
||||
* admin_label = @Translation("Display message")
|
||||
* )
|
||||
*/
|
||||
class TestBlockInstantiation extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return array(
|
||||
'display_message' => 'no message set',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function blockAccess(AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'access content');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function blockForm($form, FormStateInterface $form_state) {
|
||||
$form['display_message'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Display message'),
|
||||
'#default_value' => $this->configuration['display_message'],
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function blockSubmit($form, FormStateInterface $form_state) {
|
||||
$this->configuration['display_message'] = $form_state->getValue('display_message');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return array(
|
||||
'#children' => $this->configuration['display_message'],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block_test\Plugin\Block\TestCacheBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Provides a block to test caching.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_cache",
|
||||
* admin_label = @Translation("Test block caching")
|
||||
* )
|
||||
*/
|
||||
class TestCacheBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$content = \Drupal::state()->get('block_test.content');
|
||||
|
||||
$build = array();
|
||||
if (!empty($content)) {
|
||||
$build['#markup'] = $content;
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return \Drupal::state()->get('block_test.cache_contexts', []);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block_test\Plugin\Block\TestContextAwareBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Provides a context-aware block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_context_aware",
|
||||
* admin_label = @Translation("Test context-aware block"),
|
||||
* context = {
|
||||
* "user" = @ContextDefinition("entity:user")
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TestContextAwareBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
/** @var $user \Drupal\user\UserInterface */
|
||||
$user = $this->getContextValue('user');
|
||||
return array(
|
||||
'#markup' => $user->getUsername(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block_test\Plugin\Block\TestHtmlBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Provides a block to test HTML.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_html",
|
||||
* admin_label = @Translation("Test HTML block")
|
||||
* )
|
||||
*/
|
||||
class TestHtmlBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return array(
|
||||
'#attributes' => \Drupal::state()->get('block_test.attributes'),
|
||||
'#children' => \Drupal::state()->get('block_test.content'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\block_test\Plugin\Block\TestXSSTitleBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
/**
|
||||
* Provides a block to test XSS in title.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_xss_title",
|
||||
* admin_label = "<script>alert('XSS subject');</script>"
|
||||
* )
|
||||
*/
|
||||
class TestXSSTitleBlock extends TestCacheBlock {
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
name: '<"Cat" & ''Mouse''>'
|
||||
type: theme
|
||||
description: 'Theme for testing special characters in block admin.'
|
||||
core: 8.x
|
||||
regions:
|
||||
content: Content
|
||||
help: Help
|
|
@ -0,0 +1,16 @@
|
|||
name: 'Block test theme'
|
||||
type: theme
|
||||
description: 'Theme for testing the block system'
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
regions:
|
||||
sidebar_first: 'Left sidebar'
|
||||
sidebar_second: 'Right sidebar'
|
||||
content: Content
|
||||
header: Header
|
||||
footer: Footer
|
||||
highlighted: Highlighted
|
||||
help: Help
|
||||
regions_hidden:
|
||||
- sidebar_first
|
||||
- sidebar_second
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Block test views'
|
||||
type: module
|
||||
description: 'Provides a view and block to test block displays in views.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- block
|
||||
- views
|
|
@ -0,0 +1,45 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: test_view_block
|
||||
label: test_view_block
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: views_test_data
|
||||
base_field: id
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: some
|
||||
options:
|
||||
items_per_page: 5
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: views_test_data
|
||||
field: name
|
||||
title: test_view_block
|
||||
block_1:
|
||||
display_plugin: block
|
||||
id: block_1
|
||||
display_title: Block
|
||||
position: null
|
|
@ -0,0 +1,57 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_view_block2
|
||||
label: test_view_block2
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: views_test_data
|
||||
base_field: id
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: some
|
||||
options:
|
||||
items_per_page: 5
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: views_test_data
|
||||
field: name
|
||||
title: test_view_block2
|
||||
block_1:
|
||||
display_plugin: block
|
||||
id: block_1
|
||||
display_title: Block
|
||||
position: null
|
||||
block_2:
|
||||
display_plugin: block
|
||||
id: block_2
|
||||
display_title: Block
|
||||
position: null
|
||||
block_3:
|
||||
display_plugin: block
|
||||
id: block_3
|
||||
display_title: Block
|
||||
position: null
|
108
core/modules/block/tests/src/Unit/BlockConfigEntityUnitTest.php
Normal file
108
core/modules/block/tests/src/Unit/BlockConfigEntityUnitTest.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\block\Unit\BlockConfigEntityUnitTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\block\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\Core\Plugin\Fixtures\TestConfigurablePlugin;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\block\Entity\Block
|
||||
* @group block
|
||||
*/
|
||||
class BlockConfigEntityUnitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The entity type used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The entity manager used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The ID of the type of the entity under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId;
|
||||
|
||||
/**
|
||||
* The UUID generator used for testing.
|
||||
*
|
||||
* @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $uuid;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = $this->randomMachineName();
|
||||
|
||||
$this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
|
||||
$this->entityType->expects($this->any())
|
||||
->method('getProvider')
|
||||
->will($this->returnValue('block'));
|
||||
|
||||
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->entityManager->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->with($this->entityTypeId)
|
||||
->will($this->returnValue($this->entityType));
|
||||
|
||||
$this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('entity.manager', $this->entityManager);
|
||||
$container->set('uuid', $this->uuid);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::calculateDependencies
|
||||
*/
|
||||
public function testCalculateDependencies() {
|
||||
$values = array('theme' => 'stark');
|
||||
// Mock the entity under test so that we can mock getPluginCollections().
|
||||
$entity = $this->getMockBuilder('\Drupal\block\Entity\Block')
|
||||
->setConstructorArgs(array($values, $this->entityTypeId))
|
||||
->setMethods(array('getPluginCollections'))
|
||||
->getMock();
|
||||
// Create a configurable plugin that would add a dependency.
|
||||
$instance_id = $this->randomMachineName();
|
||||
$instance = new TestConfigurablePlugin(array(), $instance_id, array('provider' => 'test'));
|
||||
|
||||
// Create a plugin collection to contain the instance.
|
||||
$plugin_collection = $this->getMockBuilder('\Drupal\Core\Plugin\DefaultLazyPluginCollection')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('get'))
|
||||
->getMock();
|
||||
$plugin_collection->expects($this->atLeastOnce())
|
||||
->method('get')
|
||||
->with($instance_id)
|
||||
->will($this->returnValue($instance));
|
||||
$plugin_collection->addInstanceId($instance_id);
|
||||
|
||||
// Return the mocked plugin collection.
|
||||
$entity->expects($this->once())
|
||||
->method('getPluginCollections')
|
||||
->will($this->returnValue(array($plugin_collection)));
|
||||
|
||||
$dependencies = $entity->calculateDependencies();
|
||||
$this->assertContains('test', $dependencies['module']);
|
||||
$this->assertContains('stark', $dependencies['theme']);
|
||||
}
|
||||
|
||||
}
|
125
core/modules/block/tests/src/Unit/BlockFormTest.php
Normal file
125
core/modules/block/tests/src/Unit/BlockFormTest.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\block\Unit\BlockFormTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\block\Unit;
|
||||
|
||||
use Drupal\block\BlockForm;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\block\BlockForm
|
||||
* @group block
|
||||
*/
|
||||
class BlockFormTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The condition plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Executable\ExecutableManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $conditionManager;
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* The event dispatcher service.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* The language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
|
||||
$this->language = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
|
||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
|
||||
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
|
||||
$this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
|
||||
$this->entityManager->expects($this->any())
|
||||
->method('getStorage')
|
||||
->will($this->returnValue($this->storage));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the unique machine name generator.
|
||||
*
|
||||
* @see \Drupal\block\BlockForm::getUniqueMachineName()
|
||||
*/
|
||||
public function testGetUniqueMachineName() {
|
||||
$blocks = array();
|
||||
|
||||
$blocks['test'] = $this->getBlockMockWithMachineName('test');
|
||||
$blocks['other_test'] = $this->getBlockMockWithMachineName('other_test');
|
||||
$blocks['other_test_1'] = $this->getBlockMockWithMachineName('other_test');
|
||||
$blocks['other_test_2'] = $this->getBlockMockWithMachineName('other_test');
|
||||
|
||||
$query = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
|
||||
$query->expects($this->exactly(5))
|
||||
->method('condition')
|
||||
->will($this->returnValue($query));
|
||||
|
||||
$query->expects($this->exactly(5))
|
||||
->method('execute')
|
||||
->will($this->returnValue(array('test', 'other_test', 'other_test_1', 'other_test_2')));
|
||||
|
||||
$this->storage->expects($this->exactly(5))
|
||||
->method('getQuery')
|
||||
->will($this->returnValue($query));
|
||||
|
||||
$block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->dispatcher, $this->language, $this->themeHandler);
|
||||
|
||||
// Ensure that the block with just one other instance gets the next available
|
||||
// name suggestion.
|
||||
$this->assertEquals('test_2', $block_form_controller->getUniqueMachineName($blocks['test']));
|
||||
|
||||
// Ensure that the block with already three instances (_0, _1, _2) gets the
|
||||
// 4th available name.
|
||||
$this->assertEquals('other_test_3', $block_form_controller->getUniqueMachineName($blocks['other_test']));
|
||||
$this->assertEquals('other_test_3', $block_form_controller->getUniqueMachineName($blocks['other_test_1']));
|
||||
$this->assertEquals('other_test_3', $block_form_controller->getUniqueMachineName($blocks['other_test_2']));
|
||||
|
||||
// Ensure that a block without an instance yet gets the suggestion as
|
||||
// unique machine name.
|
||||
$last_block = $this->getBlockMockWithMachineName('last_test');
|
||||
$this->assertEquals('last_test', $block_form_controller->getUniqueMachineName($last_block));
|
||||
}
|
||||
|
||||
}
|
185
core/modules/block/tests/src/Unit/BlockRepositoryTest.php
Normal file
185
core/modules/block/tests/src/Unit/BlockRepositoryTest.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\block\Unit\BlockRepositoryTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\block\Unit;
|
||||
|
||||
use Drupal\block\BlockRepository;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\block\BlockRepository
|
||||
* @group block
|
||||
*/
|
||||
class BlockRepositoryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\block\BlockRepository
|
||||
*/
|
||||
protected $blockRepository;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $blockStorage;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $contextHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$active_theme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->theme = $this->randomMachineName();
|
||||
$active_theme->expects($this->atLeastOnce())
|
||||
->method('getName')
|
||||
->willReturn($this->theme);
|
||||
$active_theme->expects($this->atLeastOnce())
|
||||
->method('getRegions')
|
||||
->willReturn([
|
||||
'top',
|
||||
'center',
|
||||
'bottom',
|
||||
]);
|
||||
|
||||
$theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
|
||||
$theme_manager->expects($this->once())
|
||||
->method('getActiveTheme')
|
||||
->will($this->returnValue($active_theme));
|
||||
|
||||
$this->contextHandler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
|
||||
$this->blockStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
|
||||
$entity_manager->expects($this->any())
|
||||
->method('getStorage')
|
||||
->willReturn($this->blockStorage);
|
||||
|
||||
$this->blockRepository = new BlockRepository($entity_manager, $theme_manager, $this->contextHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the retrieval of block entities.
|
||||
*
|
||||
* @covers ::getVisibleBlocksPerRegion
|
||||
*
|
||||
* @dataProvider providerBlocksConfig
|
||||
*/
|
||||
public function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blocks) {
|
||||
$blocks = [];
|
||||
foreach ($blocks_config as $block_id => $block_config) {
|
||||
$block = $this->getMock('Drupal\block\BlockInterface');
|
||||
$block->expects($this->once())
|
||||
->method('setContexts')
|
||||
->willReturnSelf();
|
||||
$block->expects($this->once())
|
||||
->method('access')
|
||||
->will($this->returnValue($block_config[0]));
|
||||
$block->expects($block_config[0] ? $this->atLeastOnce() : $this->never())
|
||||
->method('getRegion')
|
||||
->willReturn($block_config[1]);
|
||||
$blocks[$block_id] = $block;
|
||||
}
|
||||
|
||||
$this->blockStorage->expects($this->once())
|
||||
->method('loadByProperties')
|
||||
->with(['theme' => $this->theme])
|
||||
->willReturn($blocks);
|
||||
$result = [];
|
||||
foreach ($this->blockRepository->getVisibleBlocksPerRegion([]) as $region => $resulting_blocks) {
|
||||
$result[$region] = [];
|
||||
foreach ($resulting_blocks as $plugin_id => $block) {
|
||||
$result[$region][] = $plugin_id;
|
||||
}
|
||||
}
|
||||
$this->assertSame($result, $expected_blocks);
|
||||
}
|
||||
|
||||
public function providerBlocksConfig() {
|
||||
$blocks_config = array(
|
||||
'block1' => array(
|
||||
TRUE, 'top', 0
|
||||
),
|
||||
// Test a block without access.
|
||||
'block2' => array(
|
||||
FALSE, 'bottom', 0
|
||||
),
|
||||
// Test two blocks in the same region with specific weight.
|
||||
'block3' => array(
|
||||
TRUE, 'bottom', 5
|
||||
),
|
||||
'block4' => array(
|
||||
TRUE, 'bottom', -5
|
||||
),
|
||||
);
|
||||
|
||||
$test_cases = [];
|
||||
$test_cases[] = [$blocks_config,
|
||||
[
|
||||
'top' => ['block1'],
|
||||
'center' => [],
|
||||
'bottom' => ['block4', 'block3'],
|
||||
]
|
||||
];
|
||||
return $test_cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the retrieval of block entities that are context-aware.
|
||||
*
|
||||
* @covers ::getVisibleBlocksPerRegion
|
||||
*/
|
||||
public function testGetVisibleBlocksPerRegionWithContext() {
|
||||
$block = $this->getMock('Drupal\block\BlockInterface');
|
||||
$block->expects($this->once())
|
||||
->method('setContexts')
|
||||
->willReturnSelf();
|
||||
$block->expects($this->once())
|
||||
->method('access')
|
||||
->willReturn(TRUE);
|
||||
$block->expects($this->once())
|
||||
->method('getRegion')
|
||||
->willReturn('top');
|
||||
$blocks['block_id'] = $block;
|
||||
|
||||
$contexts = [];
|
||||
$this->blockStorage->expects($this->once())
|
||||
->method('loadByProperties')
|
||||
->with(['theme' => $this->theme])
|
||||
->willReturn($blocks);
|
||||
$result = [];
|
||||
foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $resulting_blocks) {
|
||||
$result[$region] = [];
|
||||
foreach ($resulting_blocks as $plugin_id => $block) {
|
||||
$result[$region][] = $plugin_id;
|
||||
}
|
||||
}
|
||||
$expected = [
|
||||
'top' => [
|
||||
'block_id',
|
||||
],
|
||||
'center' => [],
|
||||
'bottom' => [],
|
||||
];
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TestContextAwareBlockInterface extends BlockPluginInterface, ContextAwarePluginInterface {
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\block\Unit\CategoryAutocompleteTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\block\Unit;
|
||||
|
||||
use Drupal\block\Controller\CategoryAutocompleteController;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\block\Controller\CategoryAutocompleteController
|
||||
* @group block
|
||||
*/
|
||||
class CategoryAutocompleteTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The autocomplete controller.
|
||||
*
|
||||
* @var \Drupal\block\Controller\CategoryAutocompleteController
|
||||
*/
|
||||
protected $autocompleteController;
|
||||
|
||||
protected function setUp() {
|
||||
$block_manager = $this->getMock('Drupal\Core\Block\BlockManagerInterface');
|
||||
$block_manager->expects($this->any())
|
||||
->method('getCategories')
|
||||
->will($this->returnValue(array('Comment', 'Node', 'None & Such', 'User')));
|
||||
|
||||
$this->autocompleteController = new CategoryAutocompleteController($block_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the autocomplete method.
|
||||
*
|
||||
* @param string $string
|
||||
* The string entered into the autocomplete.
|
||||
* @param array $suggestions
|
||||
* The array of expected suggestions.
|
||||
*
|
||||
* @see \Drupal\block\Controller\CategoryAutocompleteController::autocomplete()
|
||||
*
|
||||
* @dataProvider providerTestAutocompleteSuggestions
|
||||
*/
|
||||
public function testAutocompleteSuggestions($string, $suggestions) {
|
||||
$suggestions = array_map(function ($suggestion) {
|
||||
return array('value' => $suggestion, 'label' => SafeMarkup::checkPlain($suggestion));
|
||||
}, $suggestions);
|
||||
$result = $this->autocompleteController->autocomplete(new Request(array('q' => $string)));
|
||||
$this->assertSame($suggestions, json_decode($result->getContent(), TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testAutocompleteSuggestions().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestAutocompleteSuggestions() {
|
||||
$test_parameters = array();
|
||||
$test_parameters[] = array(
|
||||
'string' => 'Com',
|
||||
'suggestions' => array(
|
||||
'Comment',
|
||||
),
|
||||
);
|
||||
$test_parameters[] = array(
|
||||
'string' => 'No',
|
||||
'suggestions' => array(
|
||||
'Node',
|
||||
'None & Such',
|
||||
),
|
||||
);
|
||||
$test_parameters[] = array(
|
||||
'string' => 'us',
|
||||
'suggestions' => array(
|
||||
'User',
|
||||
),
|
||||
);
|
||||
$test_parameters[] = array(
|
||||
'string' => 'Banana',
|
||||
'suggestions' => array(),
|
||||
);
|
||||
return $test_parameters;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\block\Unit\Menu\BlockLocalTasksTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\block\Unit\Menu;
|
||||
|
||||
use Drupal\Tests\Core\Menu\LocalTaskIntegrationTestBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Tests block local tasks.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class BlockLocalTasksTest extends LocalTaskIntegrationTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
$this->directoryList = array('block' => 'core/modules/block');
|
||||
parent::setUp();
|
||||
|
||||
$config_factory = $this->getConfigFactoryStub(array('system.theme' => array(
|
||||
'default' => 'test_c',
|
||||
)));
|
||||
|
||||
$themes = array();
|
||||
$themes['test_a'] = (object) array(
|
||||
'status' => 0,
|
||||
);
|
||||
$themes['test_b'] = (object) array(
|
||||
'status' => 1,
|
||||
'info' => array(
|
||||
'name' => 'test_b',
|
||||
),
|
||||
);
|
||||
$themes['test_c'] = (object) array(
|
||||
'status' => 1,
|
||||
'info' => array(
|
||||
'name' => 'test_c',
|
||||
),
|
||||
);
|
||||
$theme_handler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
|
||||
$theme_handler->expects($this->any())
|
||||
->method('listInfo')
|
||||
->will($this->returnValue($themes));
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('config.factory', $config_factory);
|
||||
$container->set('theme_handler', $theme_handler);
|
||||
$container->set('app.root', $this->root);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the admin edit local task.
|
||||
*/
|
||||
public function testBlockAdminLocalTasks() {
|
||||
$this->assertLocalTasks('entity.block.edit_form', array(array('entity.block.edit_form')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the block admin display local tasks.
|
||||
*
|
||||
* @dataProvider providerTestBlockAdminDisplay
|
||||
*/
|
||||
public function testBlockAdminDisplay($route, $expected) {
|
||||
$this->assertLocalTasks($route, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of routes to test.
|
||||
*/
|
||||
public function providerTestBlockAdminDisplay() {
|
||||
return array(
|
||||
array('block.admin_display', array(array('block.admin_display'), array('block.admin_display_theme:test_b', 'block.admin_display_theme:test_c'))),
|
||||
array('block.admin_display_theme', array(array('block.admin_display'), array('block.admin_display_theme:test_b', 'block.admin_display_theme:test_c'))),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\block\Unit\Plugin\DisplayVariant\BlockPageVariantTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\block\Unit\Plugin\DisplayVariant;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\block\Plugin\DisplayVariant\BlockPageVariant
|
||||
* @group block
|
||||
*/
|
||||
class BlockPageVariantTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The block repository.
|
||||
*
|
||||
* @var \Drupal\block\BlockRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $blockRepository;
|
||||
|
||||
/**
|
||||
* The block view builder.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityViewBuilderInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $blockViewBuilder;
|
||||
|
||||
/**
|
||||
* The event dispatcher.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* The plugin context handler.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $contextHandler;
|
||||
|
||||
/**
|
||||
* Sets up a display variant plugin for testing.
|
||||
*
|
||||
* @param array $configuration
|
||||
* An array of plugin configuration.
|
||||
* @param array $definition
|
||||
* The plugin definition array.
|
||||
*
|
||||
* @return \Drupal\block\Plugin\DisplayVariant\BlockPageVariant|\PHPUnit_Framework_MockObject_MockObject
|
||||
* A mocked display variant plugin.
|
||||
*/
|
||||
public function setUpDisplayVariant($configuration = array(), $definition = array()) {
|
||||
$this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
|
||||
$this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
|
||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$this->dispatcher->expects($this->any())
|
||||
->method('dispatch')
|
||||
->willReturnArgument(1);
|
||||
return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\BlockPageVariant')
|
||||
->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, $this->dispatcher, ['config:block_list']))
|
||||
->setMethods(array('getRegionNames'))
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function providerBuild() {
|
||||
$blocks_config = array(
|
||||
'block1' => array(
|
||||
// region, is main content block, is messages block
|
||||
'top', FALSE, FALSE,
|
||||
),
|
||||
// Test multiple blocks in the same region.
|
||||
'block2' => array(
|
||||
'bottom', FALSE, FALSE,
|
||||
),
|
||||
'block3' => array(
|
||||
'bottom', FALSE, FALSE,
|
||||
),
|
||||
// Test a block implementing MainContentBlockPluginInterface.
|
||||
'block4' => array(
|
||||
'center', TRUE, FALSE,
|
||||
),
|
||||
// Test a block implementing MessagesBlockPluginInterface.
|
||||
'block5' => array(
|
||||
'center', FALSE, TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
$test_cases = [];
|
||||
$test_cases[] = [$blocks_config, 5,
|
||||
[
|
||||
'#cache' => [
|
||||
'tags' => [
|
||||
'config:block_list',
|
||||
],
|
||||
],
|
||||
'top' => [
|
||||
'block1' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
// The main content was rendered via a block.
|
||||
'center' => [
|
||||
'block4' => [],
|
||||
'block5' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
'bottom' => [
|
||||
'block2' => [],
|
||||
'block3' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
],
|
||||
];
|
||||
unset($blocks_config['block5']);
|
||||
$test_cases[] = [$blocks_config, 4,
|
||||
[
|
||||
'#cache' => [
|
||||
'tags' => [
|
||||
'config:block_list',
|
||||
],
|
||||
],
|
||||
'top' => [
|
||||
'block1' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
'center' => [
|
||||
'block4' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
'bottom' => [
|
||||
'block2' => [],
|
||||
'block3' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
// The messages are rendered via the fallback in case there is no block
|
||||
// rendering the main content.
|
||||
'content' => [
|
||||
'messages' => [
|
||||
'#weight' => -1000,
|
||||
'#type' => 'status_messages',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
unset($blocks_config['block4']);
|
||||
$test_cases[] = [$blocks_config, 3,
|
||||
[
|
||||
'#cache' => [
|
||||
'tags' => [
|
||||
'config:block_list',
|
||||
],
|
||||
],
|
||||
'top' => [
|
||||
'block1' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
'bottom' => [
|
||||
'block2' => [],
|
||||
'block3' => [],
|
||||
'#sorted' => TRUE,
|
||||
],
|
||||
// The main content & messages are rendered via the fallback in case
|
||||
// there are no blocks rendering them.
|
||||
'content' => [
|
||||
'system_main' => ['#markup' => 'Hello kittens!'],
|
||||
'messages' => [
|
||||
'#weight' => -1000,
|
||||
'#type' => 'status_messages',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
return $test_cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the building of a full page variant.
|
||||
*
|
||||
* @covers ::build
|
||||
*
|
||||
* @dataProvider providerBuild
|
||||
*/
|
||||
public function testBuild(array $blocks_config, $visible_block_count, array $expected_render_array) {
|
||||
$display_variant = $this->setUpDisplayVariant();
|
||||
$display_variant->setMainContent(['#markup' => 'Hello kittens!']);
|
||||
|
||||
$blocks = ['top' => [], 'center' => [], 'bottom' => []];
|
||||
$block_plugin = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
|
||||
$main_content_block_plugin = $this->getMock('Drupal\Core\Block\MainContentBlockPluginInterface');
|
||||
$messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
|
||||
foreach ($blocks_config as $block_id => $block_config) {
|
||||
$block = $this->getMock('Drupal\block\BlockInterface');
|
||||
$block->expects($this->atLeastOnce())
|
||||
->method('getPlugin')
|
||||
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin));
|
||||
$blocks[$block_config[0]][$block_id] = $block;
|
||||
}
|
||||
|
||||
$this->blockViewBuilder->expects($this->exactly($visible_block_count))
|
||||
->method('view')
|
||||
->will($this->returnValue(array()));
|
||||
$this->blockRepository->expects($this->once())
|
||||
->method('getVisibleBlocksPerRegion')
|
||||
->will($this->returnValue($blocks));
|
||||
|
||||
$this->assertSame($expected_render_array, $display_variant->build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the building of a full page variant with no main content set.
|
||||
*
|
||||
* @covers ::build
|
||||
*/
|
||||
public function testBuildWithoutMainContent() {
|
||||
$display_variant = $this->setUpDisplayVariant();
|
||||
$this->blockRepository->expects($this->once())
|
||||
->method('getVisibleBlocksPerRegion')
|
||||
->willReturn([]);
|
||||
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'tags' => [
|
||||
'config:block_list',
|
||||
],
|
||||
],
|
||||
'content' => [
|
||||
'system_main' => [],
|
||||
'messages' => [
|
||||
'#weight' => -1000,
|
||||
'#type' => 'status_messages',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $display_variant->build());
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue