Commit e7f28734 authored by marcoscano's avatar marcoscano Committed by fenstrat

Issue #2862204 by marcoscano, Berdir, EricB1021, Jody Lynn, cbeier: Add...

Issue #2862204 by marcoscano, Berdir, EricB1021, Jody Lynn, cbeier: Add support for block contexts (was: views arguments)
parent 74039e82
services:
block_field.manager:
class: Drupal\block_field\BlockFieldManager
arguments: ['@plugin.manager.block']
arguments: ['@plugin.manager.block', '@context.repository']
......@@ -3,6 +3,7 @@
namespace Drupal\block_field;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
/**
* Defines a service that manages block plugins for the block field.
......@@ -16,34 +17,32 @@ class BlockFieldManager implements BlockFieldManagerInterface {
*/
protected $blockManager;
/**
* The context repository.
*
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
*/
protected $contextRepository;
/**
* Constructs a new BlockFieldManager.
*
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
* The block plugin manager.
* @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
* The context repository.
*/
public function __construct(BlockManagerInterface $block_manager) {
public function __construct(BlockManagerInterface $block_manager, ContextRepositoryInterface $context_repository) {
$this->blockManager = $block_manager;
$this->contextRepository = $context_repository;
}
/**
* {@inheritdoc}
*/
public function getBlockDefinitions() {
$definitions = $this->blockManager->getSortedDefinitions();
$block_definitions = [];
foreach ($definitions as $plugin_id => $definition) {
// Context aware plugins are not currently supported.
// Core and component plugins can be context-aware
// https://www.drupal.org/node/1938688
// @see \Drupal\ctools\Plugin\Block\EntityView
if (isset($definition['context'])) {
continue;
}
$block_definitions[$plugin_id] = $definition;
}
return $block_definitions;
$definitions = $this->blockManager->getDefinitionsForContexts($this->contextRepository->getAvailableContexts());
return $this->blockManager->getSortedDefinitions($definitions);
}
}
......@@ -2,8 +2,10 @@
namespace Drupal\block_field\Plugin\Field\FieldFormatter;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
/**
* Plugin implementation of the 'block_field' formatter.
......@@ -26,13 +28,25 @@ class BlockFieldFormatter extends FormatterBase {
foreach ($items as $delta => $item) {
/** @var \Drupal\block_field\BlockFieldItemInterface $item */
$block_instance = $item->getBlock();
// Inject runtime contexts.
if ($block_instance instanceof ContextAwarePluginInterface) {
try {
$contexts = \Drupal::service('context.repository')->getRuntimeContexts($block_instance->getContextMapping());
\Drupal::service('context.handler')->applyContextMapping($block_instance, $contexts);
}
catch (ContextException $e) {
continue;
}
}
// Make sure the block exists and is accessible.
if (!$block_instance || !$block_instance->access(\Drupal::currentUser())) {
continue;
}
// @see \Drupal\block\BlockViewBuilder::buildPreRenderableBlock
// @see template_preprocess_block()
// See \Drupal\block\BlockViewBuilder::buildPreRenderableBlock
// See template_preprocess_block()
$elements[$delta] = [
'#theme' => 'block',
'#attributes' => [],
......
......@@ -10,6 +10,7 @@ use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -168,6 +169,10 @@ class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInter
// If block plugin exists get the block's configuration form.
if ($block_instance = $item->getBlock()) {
/** @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository */
$context_repository = \Drupal::service('context.repository');
$form_state->setTemporaryValue('gathered_contexts', $context_repository->getAvailableContexts());
$element['settings'] += $block_instance->buildConfigurationForm([], $form_state);
// Hide admin label (aka description).
......@@ -269,6 +274,11 @@ class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInter
$elements = &$form[$field_name]['widget'][$value['_original_delta']]['settings'];
$subform_state = SubformState::createForSubform($elements, $form_state->getCompleteForm(), $form_state);
$block->submitConfigurationForm($elements, $subform_state);
// If this block is context-aware, set the context mapping.
if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) {
$context_mapping = $subform_state->getValue('context_mapping', []);
$block->setContextMapping($context_mapping);
}
$value['settings'] = $block->getConfiguration();
}
}
......
langcode: en
status: true
dependencies:
config:
- node.type.item
module:
- node
- user
......@@ -125,15 +123,6 @@ display:
expose:
operator: ''
group: 1
type:
id: type
table: node_field_data
field: type
value:
item: item
entity_type: node
entity_field: type
plugin_id: bundle
sorts:
created:
id: created
......@@ -154,7 +143,48 @@ display:
footer: { }
empty: { }
relationships: { }
arguments: { }
arguments:
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: Exclude
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: 'entity:node'
fail: 'not found'
validate_options:
operation: view
multiple: 0
bundles: { }
access: false
break_phrase: false
not: true
entity_type: node
entity_field: nid
plugin_id: node_nid
display_extenders: { }
title: Items
cache_metadata:
......@@ -162,6 +192,7 @@ display:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
tags: { }
......@@ -178,6 +209,7 @@ display:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
tags: { }
......@@ -61,16 +61,49 @@ class WidgetTest extends BrowserTestBase {
* Test block settings are stored correctly.
*/
public function testBlockSettingsAreStoredCorrectly() {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$items = $this->createDummyNodes('item', 5);
$this->drupalGet($this->blockNode->toUrl('edit-form'));
$this->submitForm([
'field_block[0][settings][override][items_per_page]' => 5,
], $this->t('Save'));
$assert_session->checkboxChecked('Display title');
$assert_session->checkboxNotChecked('Override title');
$items_per_page_element = $page->findField('Items per block');
$this->assertNotNull($items_per_page_element);
$this->assertEquals('none', $items_per_page_element->getValue());
$this->assertContains('1 (default setting)', $items_per_page_element->getText());
$page->selectFieldOption('Items per block', 10);
// This view has a contextual filter to exclude the node from the URL from
// showing up if the context is present. Initially we do not choose that
// context when placing the block.
$exclude_element = $page->findField('Exclude');
$this->assertNotNull($exclude_element);
$this->assertEmpty($exclude_element->getValue());
// Save the node and check the view items.
$page->pressButton('Save');
$assert_session->pageTextContains("Block node {$this->blockNode->getTitle()} has been updated");
foreach ($items as $item) {
$this->assertSession()->pageTextContains($item->getTitle());
}
// The node we are visiting shows up in the views results.
$first_result = $this->assertSession()->elementExists('css', '.view-items .view-content > .views-row:nth-child(1)');
$this->assertEquals('Block field test', $first_result->getText());
// Select the context to exclude the node from the URL and try again.
$this->drupalGet($this->blockNode->toUrl('edit-form'));
$page->selectFieldOption('Exclude', 'Node from URL');
$page->pressButton('Save');
$assert_session->pageTextContains("Block node {$this->blockNode->getTitle()} has been updated");
foreach ($items as $item) {
$this->assertSession()->pageTextContains($item->getTitle());
}
// The node we are visiting does not show up anymore.
$first_result = $this->assertSession()->elementExists('css', '.view-items .view-content > .views-row:nth-child(1)');
$this->assertNotEquals('Block field test', $first_result->getText());
$this->assertEquals($items[0]->getTitle(), $first_result->getText());
}
/**
......@@ -108,7 +141,7 @@ class WidgetTest extends BrowserTestBase {
* @param int $numberOfNodes
* The number of nodes to create.
*
* @return array
* @return \Drupal\node\NodeInterface[]
* And array of created nodes.
*/
private function createDummyNodes($bundle, $numberOfNodes) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment