Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
8
web/core/modules/block_place/block_place.info.yml
Normal file
8
web/core/modules/block_place/block_place.info.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
name: Place Blocks
|
||||
type: module
|
||||
description: 'Allow administrators to place blocks from any Drupal page'
|
||||
package: Core (Experimental)
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- block
|
11
web/core/modules/block_place/block_place.libraries.yml
Normal file
11
web/core/modules/block_place/block_place.libraries.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
drupal.block_place:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/block-place.css: {}
|
||||
|
||||
drupal.block_place.icons:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/block-place.icons.theme.css: {}
|
84
web/core/modules/block_place/block_place.module
Normal file
84
web/core/modules/block_place/block_place.module
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Controls the placement of blocks from all pages.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function block_place_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.block_place':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Place Blocks module allows you to place blocks from every page. For more information, see the <a href=":blocks-documentation">online documentation for the Place Blocks module</a>.', [':blocks-documentation' => 'https://www.drupal.org/documentation/modules/block_place/']) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<p>' . t('Block placement is specific to each theme on your site. This module allows you to place blocks in the context of your content pages.') . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_toolbar().
|
||||
*/
|
||||
function block_place_toolbar() {
|
||||
// Link to the current page with a query parameter.
|
||||
$query = \Drupal::request()->query->all();
|
||||
$wrapper_class = '';
|
||||
$status_class = '';
|
||||
$description = '';
|
||||
if (isset($query['block-place'])) {
|
||||
$status_class = 'active';
|
||||
$wrapper_class = 'is-active';
|
||||
$description = t('Exit Place block mode.');
|
||||
unset($query['block-place']);
|
||||
unset($query['destination']);
|
||||
}
|
||||
else {
|
||||
$status_class = 'inactive';
|
||||
$description = t('Show regions to Place blocks.');
|
||||
$query['block-place'] = '1';
|
||||
// Setting destination is both a work-around for the toolbar "Back to site"
|
||||
// link in escapeAdmin.js and used for the destination after picking a
|
||||
// block.
|
||||
$query['destination'] = Url::fromRoute('<current>')->toString();
|
||||
}
|
||||
|
||||
// Remove on Admin routes.
|
||||
$admin_route = \Drupal::service('router.admin_context')->isAdminRoute();
|
||||
// Remove on Block Demo page.
|
||||
$admin_demo = \Drupal::routeMatch()->getRouteName() === 'block.admin_demo';
|
||||
$access = (\Drupal::currentUser()->hasPermission('administer blocks') && !$admin_route && !$admin_demo);
|
||||
|
||||
// The 'Place Block' tab is a simple link, with no corresponding tray.
|
||||
$items['block_place'] = [
|
||||
'#cache' => [
|
||||
'contexts' => ['user.permissions', 'url.query_args'],
|
||||
],
|
||||
'#type' => 'toolbar_item',
|
||||
'tab' => [
|
||||
'#access' => $access,
|
||||
'#type' => 'link',
|
||||
'#title' => t('Place block'),
|
||||
'#url' => Url::fromRoute('<current>', [], ['query' => $query]),
|
||||
'#attributes' => [
|
||||
'title' => $description,
|
||||
'class' => ['toolbar-icon', 'toolbar-icon-place-block-' . $status_class],
|
||||
],
|
||||
],
|
||||
'#wrapper_attributes' => [
|
||||
'class' => ['toolbar-tab', 'block-place-toolbar-tab', $wrapper_class],
|
||||
],
|
||||
'#weight' => 100,
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'block_place/drupal.block_place.icons',
|
||||
],
|
||||
],
|
||||
];
|
||||
return $items;
|
||||
}
|
6
web/core/modules/block_place/block_place.services.yml
Normal file
6
web/core/modules/block_place/block_place.services.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
block_place.page_display_variant_subscriber.block:
|
||||
class: Drupal\block_place\EventSubscriber\BlockPlaceEventSubscriber
|
||||
arguments: ['@request_stack', '@current_user']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
29
web/core/modules/block_place/css/block-place.css
Normal file
29
web/core/modules/block_place/css/block-place.css
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @file
|
||||
* Styling for block_place module regions and buttons during block placement.
|
||||
*/
|
||||
|
||||
.block-place-region {
|
||||
outline: 1px dashed rgba(0,0,0,0.5);
|
||||
box-shadow: 0 0 0 1px rgba(255,255,255,0.7);
|
||||
margin: 1em 0;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.block-place-region a.button {
|
||||
background: url(../../../misc/icons/bebebe/plus.svg) #ffffff center center / 16px 16px no-repeat;
|
||||
border: 1px solid #cccccc;
|
||||
box-sizing: border-box;
|
||||
font-size: 1rem;
|
||||
padding: 0;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.block-place-region:hover a.button, .block-place-region:focus a.button {
|
||||
background-image: url(../../../misc/icons/787878/plus.svg);
|
||||
transition: all 0.25s ease;
|
||||
}
|
24
web/core/modules/block_place/css/block-place.icons.theme.css
Normal file
24
web/core/modules/block_place/css/block-place.icons.theme.css
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @file
|
||||
* Styling for block_place module toolbar icons.
|
||||
*/
|
||||
.toolbar .block-place-toolbar-tab.is-active {
|
||||
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
|
||||
background-image: linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
|
||||
}
|
||||
|
||||
.toolbar .toolbar-bar .block-place-toolbar-tab {
|
||||
float: right;
|
||||
}
|
||||
|
||||
[dir="rtl"] .toolbar .toolbar-bar .block-place-toolbar-tab {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.toolbar-bar .toolbar-icon-place-block-active:before {
|
||||
background-image: url(../icons/ffffff/place-block.svg);
|
||||
}
|
||||
|
||||
.toolbar-bar .toolbar-icon-place-block-inactive:before {
|
||||
background-image: url(../icons/bebebe/place-block.svg);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0" y="0" width="16" height="16" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"><rect x="1" y="6" fill="#bebebe" width="14" height="9"/><ellipse fill="#bebebe" cx="11.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="9.25" y="3.68" fill="#bebebe" width="4.45" height="3.35"/><ellipse fill="#bebebe" cx="4.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="2.25" y="3.68" fill="#bebebe" width="4.45" height="3.35"/></svg>
|
After Width: | Height: | Size: 548 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0" y="0" width="16" height="16" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"><rect x="1" y="6" fill="#ffffff" width="14" height="9"/><ellipse fill="#ffffff" cx="11.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="9.25" y="3.68" fill="#ffffff" width="4.45" height="3.35"/><ellipse fill="#ffffff" cx="4.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="2.25" y="3.68" fill="#ffffff" width="4.45" height="3.35"/></svg>
|
After Width: | Height: | Size: 548 B |
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_place\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Render\PageDisplayVariantSelectionEvent;
|
||||
use Drupal\Core\Render\RenderEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* @see \Drupal\block_place\Plugin\DisplayVariant\PlaceBlockPageVariant
|
||||
*/
|
||||
class BlockPlaceEventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\block_place\EventSubscriber\BlockPlaceEventSubscriber object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack used to retrieve the current request.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(RequestStack $request_stack, AccountInterface $account) {
|
||||
$this->requestStack = $request_stack;
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the block place override of the block page display variant.
|
||||
*
|
||||
* @param \Drupal\Core\Render\PageDisplayVariantSelectionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onBlockPageDisplayVariantSelected(PageDisplayVariantSelectionEvent $event) {
|
||||
if ($event->getPluginId() === 'block_page') {
|
||||
if ($this->requestStack->getCurrentRequest()->query->has('block-place') && $this->account->hasPermission('administer blocks')) {
|
||||
$event->setPluginId('block_place_page');
|
||||
}
|
||||
$event->addCacheContexts(['user.permissions', 'url.query_args']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Set a very low priority, so that it runs last.
|
||||
$events[RenderEvents::SELECT_PAGE_DISPLAY_VARIANT][] = ['onBlockPageDisplayVariantSelected', -1000];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_place\Plugin\DisplayVariant;
|
||||
|
||||
use Drupal\block\BlockRepositoryInterface;
|
||||
use Drupal\block\Plugin\DisplayVariant\BlockPageVariant;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||
use Drupal\Core\Routing\RedirectDestinationInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Allows blocks to be placed directly within a region.
|
||||
*
|
||||
* @PageDisplayVariant(
|
||||
* id = "block_place_page",
|
||||
* admin_label = @Translation("Page with blocks and place block buttons")
|
||||
* )
|
||||
*/
|
||||
class PlaceBlockPageVariant extends BlockPageVariant {
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* The redirect destination.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RedirectDestinationInterface
|
||||
*/
|
||||
protected $redirectDestination;
|
||||
|
||||
/**
|
||||
* Constructs a new PlaceBlockPageVariant.
|
||||
*
|
||||
* @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 string[] $block_list_cache_tags
|
||||
* The Block entity type list cache tags.
|
||||
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
|
||||
* The theme manager.
|
||||
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
|
||||
* The redirect destination.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, array $block_list_cache_tags, ThemeManagerInterface $theme_manager, RedirectDestinationInterface $redirect_destination) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $block_repository, $block_view_builder, $block_list_cache_tags);
|
||||
|
||||
$this->themeManager = $theme_manager;
|
||||
$this->redirectDestination = $redirect_destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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_type.manager')->getViewBuilder('block'),
|
||||
$container->get('entity_type.manager')->getDefinition('block')->getListCacheTags(),
|
||||
$container->get('theme.manager'),
|
||||
$container->get('redirect.destination')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$build = parent::build();
|
||||
|
||||
$active_theme = $this->themeManager->getActiveTheme();
|
||||
$theme_name = $active_theme->getName();
|
||||
$destination = $this->redirectDestination->get();
|
||||
$visible_regions = $this->getVisibleRegionNames($theme_name);
|
||||
|
||||
// Build an array of the region names in the right order.
|
||||
$build += array_fill_keys(array_keys($visible_regions), []);
|
||||
|
||||
foreach ($visible_regions as $region => $region_name) {
|
||||
$query = [
|
||||
'region' => $region,
|
||||
];
|
||||
if ($destination) {
|
||||
$query['destination'] = $destination;
|
||||
}
|
||||
$title = $this->t('<span class="visually-hidden">Place block in the %region region</span>', ['%region' => $region_name]);
|
||||
$operations['block_description'] = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="block-place-region">{{ link }}</div>',
|
||||
'#context' => [
|
||||
'link' => Link::createFromRoute($title, 'block.admin_library', ['theme' => $theme_name], [
|
||||
'query' => $query,
|
||||
'attributes' => [
|
||||
'title' => $title,
|
||||
'class' => ['use-ajax', 'button', 'button--small'],
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => Json::encode([
|
||||
'width' => 700,
|
||||
]),
|
||||
],
|
||||
]),
|
||||
],
|
||||
];
|
||||
$build[$region] = ['block_place_operations' => $operations] + $build[$region];
|
||||
}
|
||||
$build['#attached']['library'][] = 'block_place/drupal.block_place';
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\block_place\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the placing a block.
|
||||
*
|
||||
* @group block_place
|
||||
*/
|
||||
class BlockPlaceTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'block_place', 'toolbar'];
|
||||
|
||||
/**
|
||||
* Tests placing blocks as an admin.
|
||||
*/
|
||||
public function testPlacingBlocksAdmin() {
|
||||
// Create administrative user.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access toolbar',
|
||||
'administer blocks',
|
||||
'view the administration theme',
|
||||
]));
|
||||
$this->drupalGet(Url::fromRoute('<front>'));
|
||||
$this->clickLink('Place block');
|
||||
|
||||
// Each region should have one link to place a block.
|
||||
$theme_name = $this->container->get('theme.manager')->getActiveTheme()->getName();
|
||||
$visible_regions = system_region_list($theme_name, REGIONS_VISIBLE);
|
||||
$this->assertGreaterThan(0, count($visible_regions));
|
||||
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$block_library_url = Url::fromRoute('block.admin_library', ['theme' => $default_theme]);
|
||||
foreach ($visible_regions as $region => $name) {
|
||||
$block_library_url->setOption('query', ['region' => $region]);
|
||||
$links = $this->xpath('//a[contains(@href, :href)]', [':href' => $block_library_url->toString()]);
|
||||
$this->assertEquals(1, count($links));
|
||||
|
||||
list(, $query_string) = explode('?', $links[0]->getAttribute('href'), 2);
|
||||
parse_str($query_string, $query_parts);
|
||||
$this->assertNotEmpty($query_parts['destination']);
|
||||
|
||||
// Get the text inside the div->a->span->em.
|
||||
$demo_block = $this->xpath('//div[@class="block-place-region"]/a/span[text()="Place block in the "]/em[text()="' . $name . '"]');
|
||||
$this->assertEquals(1, count($demo_block));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests placing blocks as an unprivileged user.
|
||||
*/
|
||||
public function testPlacingBlocksUnprivileged() {
|
||||
// Create a user who cannot administer blocks.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access toolbar',
|
||||
'view the administration theme',
|
||||
]));
|
||||
$this->drupalGet(Url::fromRoute('<front>'));
|
||||
$links = $this->xpath('//a[text()=:label]', [':label' => 'Place block']);
|
||||
$this->assertEmpty($links);
|
||||
|
||||
$this->drupalGet(Url::fromRoute('block.admin_library', ['theme' => 'classy']));
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue