Skip to content
Snippets Groups Projects
Commit 5d8bbfed authored by Brian Canini's avatar Brian Canini
Browse files

Merge branch 'block_field' into module-updates-rc

parents 40a4e5cf afe2a3fe
No related branches found
No related tags found
No related merge requests found
Showing
with 608 additions and 67 deletions
......@@ -2005,17 +2005,17 @@
},
{
"name": "drupal/block_field",
"version": "1.0.0-rc1",
"version": "1.0.0-rc2",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/block_field.git",
"reference": "8.x-1.0-rc1"
"reference": "8.x-1.0-rc2"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/block_field-8.x-1.0-rc1.zip",
"reference": "8.x-1.0-rc1",
"shasum": "a366cb012ece9987404168a4570c79f701e7819a"
"url": "https://ftp.drupal.org/files/projects/block_field-8.x-1.0-rc2.zip",
"reference": "8.x-1.0-rc2",
"shasum": "44472f8e89c319af884b25ac0a66337329a9b309"
},
"require": {
"drupal/core": "^8.7.7 || ^9"
......@@ -2023,8 +2023,8 @@
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-1.0-rc1",
"datestamp": "1588279781",
"version": "8.x-1.0-rc2",
"datestamp": "1621950813",
"security-coverage": {
"status": "not-covered",
"message": "RC releases are not covered by Drupal security advisories."
......
......@@ -413,12 +413,12 @@ class InstalledVersions
),
'drupal/block_field' =>
array (
'pretty_version' => '1.0.0-rc1',
'version' => '1.0.0.0-RC1',
'pretty_version' => '1.0.0-rc2',
'version' => '1.0.0.0-RC2',
'aliases' =>
array (
),
'reference' => '8.x-1.0-rc1',
'reference' => '8.x-1.0-rc2',
),
'drupal/block_permissions' =>
array (
......
......@@ -2060,18 +2060,18 @@
},
{
"name": "drupal/block_field",
"version": "1.0.0-rc1",
"version_normalized": "1.0.0.0-RC1",
"version": "1.0.0-rc2",
"version_normalized": "1.0.0.0-RC2",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/block_field.git",
"reference": "8.x-1.0-rc1"
"reference": "8.x-1.0-rc2"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/block_field-8.x-1.0-rc1.zip",
"reference": "8.x-1.0-rc1",
"shasum": "a366cb012ece9987404168a4570c79f701e7819a"
"url": "https://ftp.drupal.org/files/projects/block_field-8.x-1.0-rc2.zip",
"reference": "8.x-1.0-rc2",
"shasum": "44472f8e89c319af884b25ac0a66337329a9b309"
},
"require": {
"drupal/core": "^8.7.7 || ^9"
......@@ -2079,8 +2079,8 @@
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-1.0-rc1",
"datestamp": "1588279781",
"version": "8.x-1.0-rc2",
"datestamp": "1621950813",
"security-coverage": {
"status": "not-covered",
"message": "RC releases are not covered by Drupal security advisories."
......@@ -2112,6 +2112,10 @@
{
"name": "michaellander",
"homepage": "https://www.drupal.org/user/636494"
},
{
"name": "paulocs",
"homepage": "https://www.drupal.org/user/3640109"
}
],
"description": "Provides a field that allows a content entity to create and configure custom block instances.",
......
......@@ -389,12 +389,12 @@
),
'drupal/block_field' =>
array (
'pretty_version' => '1.0.0-rc1',
'version' => '1.0.0.0-RC1',
'pretty_version' => '1.0.0-rc2',
'version' => '1.0.0.0-RC2',
'aliases' =>
array (
),
'reference' => '8.x-1.0-rc1',
'reference' => '8.x-1.0-rc2',
),
'drupal/block_permissions' =>
array (
......
......@@ -4,7 +4,7 @@ description: 'Provides a field that allows a content entity to create and config
package: Field
core_version_requirement: ^8.7.7 || ^9
# Information added by Drupal.org packaging script on 2020-04-30
version: '8.x-1.0-rc1'
# Information added by Drupal.org packaging script on 2021-05-25
version: '8.x-1.0-rc2'
project: 'block_field'
datestamp: 1588279783
datestamp: 1621950815
......@@ -4,9 +4,17 @@
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Security\TrustedCallbackInterface;
/**
* Plugin implementation of the 'block_field' formatter.
......@@ -19,7 +27,84 @@
* }
* )
*/
class BlockFieldFormatter extends FormatterBase {
class BlockFieldFormatter extends FormatterBase implements TrustedCallbackInterface {
/**
* The Drupal context repository.
*
* @var \Drupal\context\Entity\ContextRepositoryInterface
*/
protected $contextRepository;
/**
* The plugin context handler.
*
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
*/
protected $contextHandler;
/**
* Drupal\Core\Session\AccountProxy definition.
*
* @var \Drupal\Core\Session\AccountProxy
*/
protected $currentUser;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a StringFormatter instance.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings settings.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, ContextRepositoryInterface $contextRepository, ContextHandlerInterface $contextHandler, AccountProxyInterface $current_user, ModuleHandlerInterface $module_handler){
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->contextRepository = $contextRepository;
$this->contextHandler = $contextHandler;
$this->currentUser = $current_user;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('context.repository'),
$container->get('context.handler'),
$container->get('current_user'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
......@@ -33,8 +118,8 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
// 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);
$contexts = $this->contextRepository->getRuntimeContexts($block_instance->getContextMapping());
$this->contextHandler->applyContextMapping($block_instance, $contexts);
}
catch (ContextException $e) {
continue;
......@@ -42,28 +127,122 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
}
// Make sure the block exists and is accessible.
if (!$block_instance || !$block_instance->access(\Drupal::currentUser())) {
if (!$block_instance) {
continue;
}
$access = $block_instance->access($this->currentUser->getAccount(), TRUE);
CacheableMetadata::createFromRenderArray($elements)
->addCacheableDependency($access)
->applyTo($elements);
if (!$access->isAllowed()) {
continue;
}
// See \Drupal\block\BlockViewBuilder::buildPreRenderableBlock
// See template_preprocess_block()
$base_id = $block_instance->getBaseId();
$elements[$delta] = [
'#theme' => 'block',
'#attributes' => [],
'#configuration' => $block_instance->getConfiguration(),
'#plugin_id' => $block_instance->getPluginId(),
'#base_plugin_id' => $block_instance->getBaseId(),
'#base_plugin_id' => $base_id,
'#derivative_plugin_id' => $block_instance->getDerivativeId(),
'content' => $block_instance->build(),
'#id' => $block_instance->getMachineNameSuggestion(),
'#pre_render' => [
[$this, 'preRender'],
],
'#block' => $block_instance,
];
CacheableMetadata::createFromRenderArray($elements[$delta])
->merge(CacheableMetadata::createFromRenderArray($elements[$delta]['content']))
->addCacheableDependency($block_instance)
->applyTo($elements[$delta]);
// If an alter hook wants to modify the block contents, it can append
// another #pre_render hook.
$this->moduleHandler->alter([
'block_view',
"block_view_{$base_id}"
], $elements[$delta], $block_instance);
// Allow altering of cacheability metadata or setting #create_placeholder.
$this->moduleHandler->alter([
'block_build',
"block_build_{$base_id}"
], $elements[$delta], $block_instance);
}
return $elements;
}
/**
* The #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.
*
* @see \Drupal\block\BlockViewBuilder::preRender
*/
public function preRender($build) {
$content = $build['#block']->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 (for instance, its own #theme) without
// conflicting with the properties used above, or alternate ones used by
// alternate block rendering approaches in contrib (for instance, 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 (['#attributes', '#contextual_links'] as $property) {
if (isset($content[$property])) {
if (empty($build[$property])) {
$build[$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. For instance, modifying or adding
// entities could cause the block to no longer be empty.
$build = [
'#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;
}
public static function trustedCallbacks() {
return ['preRender'];
}
}
......@@ -5,6 +5,9 @@
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'block_field_label' formatter.
......@@ -19,6 +22,36 @@
*/
class BlockFieldLabelFormatter extends FormatterBase {
/**
* Constructs a FormatterBase object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountProxyInterface $current_user) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'], $container->get('current_user'));
}
/**
* {@inheritdoc}
*/
......@@ -28,7 +61,15 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
/** @var \Drupal\block_field\BlockFieldItemInterface $item */
$block_instance = $item->getBlock();
// Make sure the block exists and is accessible.
if (!$block_instance || !$block_instance->access(\Drupal::currentUser())) {
if (!$block_instance) {
continue;
}
$access = $block_instance->access($this->currentUser->getAccount(), TRUE);
CacheableMetadata::createFromRenderArray($elements)
->addCacheableDependency($access)
->applyTo($elements);
if (!$access->isAllowed()) {
continue;
}
......@@ -37,7 +78,6 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
];
CacheableMetadata::createFromRenderArray($elements[$delta])
->merge(CacheableMetadata::createFromRenderArray($elements[$delta]['content']))
->addCacheableDependency($block_instance)
->applyTo($elements[$delta]);
}
......
......@@ -95,7 +95,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
];
$form['selection'] = [
'#type' => 'details',
'#title' => t('Available blocks'),
'#title' => $this->t('Available blocks'),
'#open' => TRUE,
'#tree' => TRUE,
'#process' => [[get_class($this), 'formProcessMergeParent']],
......@@ -103,7 +103,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$form['selection']['selection'] = [
'#type' => 'select',
'#title' => t('Selection method'),
'#title' => $this->t('Selection method'),
'#options' => $options,
'#default_value' => $field->getSetting('selection'),
'#required' => TRUE,
......@@ -112,7 +112,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
];
$form['selection']['selection_submit'] = [
'#type' => 'submit',
'#value' => t('Change selection'),
'#value' => $this->t('Change selection'),
'#limit_validation_errors' => [],
'#attributes' => [
'class' => ['js-hide'],
......
......@@ -13,6 +13,7 @@
use Drupal\Core\Form\SubformState;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -28,6 +29,13 @@
*/
class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInterface {
/**
* The Drupal context repository.
*
* @var \Drupal\context\Entity\ContextRepositoryInterface
*/
protected $contextRepository;
/**
* The block manager.
*
......@@ -45,10 +53,12 @@ class BlockFieldWidget extends WidgetBase implements ContainerFactoryPluginInter
/**
* {@inheritdoc}
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, BlockManagerInterface $block_manager, BlockFieldSelectionManager $block_field_selection_manager) {
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, BlockManagerInterface $block_manager, BlockFieldSelectionManager $block_field_selection_manager, ContextRepositoryInterface $contextRepository) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$this->blockManager = $block_manager;
$this->blockFieldSelectionManager = $block_field_selection_manager;
$this->contextRepository = $contextRepository;
$this->blockManager = $block_manager;
}
/**
......@@ -62,7 +72,8 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration['settings'],
$configuration['third_party_settings'],
$container->get('plugin.manager.block'),
$container->get('plugin.manager.block_field_selection')
$container->get('plugin.manager.block_field_selection'),
$container->get('context.repository')
);
}
......@@ -129,7 +140,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
$options = $this->blockFieldSelectionManager->getWidgetOptions($this->fieldDefinition);
if ($item->plugin_id) {
// If plugin_id not in second level arrays, unset plugin_id and settings..
if (!in_array($item->plugin_id, array_keys(call_user_func_array('array_merge', $options)), TRUE)) {
if (!in_array($item->plugin_id, array_keys(call_user_func_array('array_merge', array_values($options))), TRUE)) {
$item->plugin_id = '';
$item->setting = [];
}
......@@ -160,9 +171,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
// 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());
$form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts());
$element['settings'] += $block_instance->buildConfigurationForm([], $form_state);
......@@ -202,10 +211,8 @@ public function configurationForm(array $form, FormStateInterface $form_state) {
// Set the label #value to the default block instance's label.
$plugin_id = $trigger_element['#value'];
/** @var \Drupal\Core\Block\BlockManagerInterface $block_manager */
$block_manager = \Drupal::service('plugin.manager.block');
/** @var \Drupal\Core\Block\BlockPluginInterface $block_instance */
if ($block_instance = $block_manager->createInstance($plugin_id)) {
if ($block_instance = $this->blockManager->createInstance($plugin_id)) {
$settings_element['label']['#value'] = $block_instance->label();
}
......@@ -271,7 +278,7 @@ public function massageFormValues(array $values, array $form, FormStateInterface
// values during the first submission.
$form_state = clone $form_state;
foreach ($values as $delta => &$value) {
foreach ($values as &$value) {
// Execute block submit configuration in order to transform the form
// values into block configuration.
if (!empty($value['plugin_id']) && !empty($value['settings']) && $block = $this->blockManager->createInstance($value['plugin_id'])) {
......
......@@ -57,7 +57,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
],
'#options' => $options,
'#js_select' => TRUE,
'#empty' => t('No categories are available.'),
'#empty' => $this->t('No categories are available.'),
'#required' => TRUE,
'#default_value' => array_combine($default_value, $default_value),
];
......
......@@ -5,13 +5,14 @@ package: Testing
core_version_requirement: ^8.7.7 || ^9
dependencies:
- drupal:block_field
- drupal:block_content
- drupal:field
- drupal:menu_ui
- drupal:node
- drupal:path
- drupal:user
# Information added by Drupal.org packaging script on 2020-04-30
version: '8.x-1.0-rc1'
# Information added by Drupal.org packaging script on 2021-05-25
version: '8.x-1.0-rc2'
project: 'block_field'
datestamp: 1588279783
datestamp: 1621950815
langcode: en
status: true
dependencies: { }
id: basic
label: 'Basic block'
revision: 0
description: 'A basic block, contains a title.'
<?php
namespace Drupal\block_field_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 'Block field test access' block.
*
* @Block(
* id = "block_field_test_access",
* admin_label = @Translation("Block field test access"),
* category = @Translation("Block field test")
* )
*/
class BlockFieldAccessTestBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'access' => TRUE,
];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form['access'] = [
'#type' => 'checkbox',
'#title' => $this->t('Access'),
'#default_value' => $this->configuration['access'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['access'] = $form_state->getValue('access');
}
/**
* {@inheritdoc}
*/
public function build() {
return [
'#type' => 'markup',
'#markup' => '<div> Custom access tag. </div>',
];
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
return AccessResult::allowedIf($this->configuration['access'])->addCacheTags(['custom_tag']);
}
}
......@@ -4,7 +4,10 @@
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a 'Block field test authenticated' block.
......@@ -15,7 +18,34 @@
* category = @Translation("Block field test")
* )
*/
class BlockFieldTestAuthenticatedBlock extends BlockBase {
class BlockFieldTestAuthenticatedBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* Drupal\Core\Session\AccountProxy definition.
*
* @var \Drupal\Core\Session\AccountProxy
*/
protected $currentUser;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountProxyInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_user')
);
}
/**
* {@inheritdoc}
......@@ -23,7 +53,7 @@ class BlockFieldTestAuthenticatedBlock extends BlockBase {
public function build() {
return [
'#theme' => 'username',
'#account' => \Drupal::currentUser()->getAccount(),
'#account' => $this->currentUser->getAccount(),
'#prefix' => '<p>',
'#suffix' => '</p>',
];
......@@ -47,7 +77,7 @@ public function getCacheContexts() {
* {@inheritdoc}
*/
public function getCacheTags() {
return ['user:' . \Drupal::currentUser()->id()];
return ['user:' . $this->currentUser->id()];
}
}
......@@ -51,6 +51,10 @@ public function build() {
return [
'#type' => 'markup',
'#markup' => $this->configuration['content'],
'#attributes' => [
'class' => ['block-field-test-content-block--custom-class'],
'data-custom-attr' => 'block-field-test-content-block--custom-data-attribute',
],
];
}
......
......@@ -12,7 +12,7 @@ dependencies:
- drupal:user
- drupal:views
# Information added by Drupal.org packaging script on 2020-04-30
version: '8.x-1.0-rc1'
# Information added by Drupal.org packaging script on 2021-05-25
version: '8.x-1.0-rc2'
project: 'block_field'
datestamp: 1588279783
datestamp: 1621950815
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\block_field\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Tests\BrowserTestBase;
/**
......@@ -34,6 +35,12 @@ class BlockFieldTest extends BrowserTestBase {
* Tests block field.
*/
public function testBlockField() {
$block_content = BlockContent::create([
'info' => $this->randomMachineName(),
'type' => 'basic',
'status' => 1,
]);
$block_content->save();
$assert = $this->assertSession();
$admin_user = $this->drupalCreateUser([
......@@ -47,21 +54,23 @@ public function testBlockField() {
// Create block field test using the three test blocks.
// Check that add more and ajax callbacks are working as expected.
$this->drupalPostForm('node/add/block_field_test', [
$this->drupalGet('node/add/block_field_test');
$this->submitForm([
'title[0][value]' => 'Block field test',
], 'Add another item');
$this->drupalPostForm(NULL, [], 'Add another item');
$this->drupalPostForm(NULL, [
$this->submitForm([], 'Add another item');
$this->submitForm([
'field_block_field_test[0][plugin_id]' => 'block_field_test_authenticated',
'field_block_field_test[1][plugin_id]' => 'block_field_test_content',
'field_block_field_test[2][plugin_id]' => 'block_field_test_time',
], 'Add another item');
$this->drupalPostForm(NULL, [
$this->submitForm([
'field_block_field_test[0][plugin_id]' => 'block_field_test_authenticated',
'field_block_field_test[1][plugin_id]' => 'block_field_test_content',
'field_block_field_test[2][plugin_id]' => 'block_field_test_time',
'field_block_field_test[3][plugin_id]' => 'block_content:' . $block_content->uuid(),
], 'Add another item');
$this->drupalPostForm(NULL, [], 'Save');
$this->submitForm([], 'Save');
// Check blocks displayed to authenticated.
$node = $this->drupalGetNodeByTitle('Block field test');
......@@ -72,14 +81,23 @@ public function testBlockField() {
$assert->elementContains('css', $selector, '<h2>You are logged in as...</h2>');
$assert->elementTextContains('css', $selector, $admin_user->label());
$assert->elementContains('css', $selector, '<h2>Block field test content</h2>');
$assert->elementContains('css', $selector, '<div class="block-field-test-content-block--custom-class ');
$assert->elementContains('css', $selector, ' data-custom-attr="block-field-test-content-block--custom-data-attribute"');
$assert->elementTextContains('css', $selector, 'This block was created at');
$assert->elementContains('css', $selector, '<h2>The time is...</h2>');
$assert->responseMatches('/\d\d:\d\d:\d\d/');
$assert->elementTextContains('css', $selector, $block_content->label());
// Check that a referenced block that is not published is not visible.
$block_content->status = 0;
$block_content->save();
$this->drupalGet($node->toUrl());
$assert->elementTextNotContains('css', $selector, $block_content->label());
// Check adjusting block weights maintains plugin settings.
$this->drupalGet($node->toUrl('edit-form'));
// Switch the position of block 1 and 2.
$this->drupalPostForm(NULL, [
$this->submitForm([
'field_block_field_test[0][_weight]' => 1,
'field_block_field_test[1][_weight]' => 0,
], 'Save');
......@@ -119,6 +137,25 @@ public function testBlockField() {
$this->drupalGet($block_node->toUrl());
$assert->elementNotExists('css', $selector);
// Check custom block.
$block_node->field_block_field_test->plugin_id = 'block_content:' . $block_content->uuid();
$block_node->field_block_field_test->settings = [
'label' => $block_content->label(),
'label_display' => TRUE,
];
$block_node->save();
$this->drupalGet($block_node->toUrl());
// When the block is not published anonymous users can't see it.
$assert->elementNotExists('css', $selector);
// Check the cache metadata of the referenced block is propagated by
// publishing it and checking if it is visible for anonymous users.
$block_content->status = 1;
$block_content->save();
$this->drupalGet($block_node->toUrl());
$assert->elementTextContains('css', $selector, $block_content->label());
// Check content block.
$block_node->field_block_field_test->plugin_id = 'block_field_test_content';
$block_node->field_block_field_test->settings = [
......@@ -161,8 +198,22 @@ public function testBlockField() {
$assert->responseMatches('/\d\d:\d\d:\d\d \(\d+\)/');
$assert->responseNotContains($time);
// Use the Block Field Label formatter for the field_block_field_test
// display.
\Drupal::service('entity_display.repository')
->getViewDisplay('node', 'block_field_test', 'default')
->setComponent('field_block_field_test', [
'type' => 'block_field_label',
])
->save();
// Assert only the label is shown.
$this->drupalGet($block_node->toUrl());
$assert->responseNotMatches('/\d\d:\d\d:\d\d \(\d+\)/');
$assert->elementContains('css', $selector, 'Time');
$this->drupalGet('admin/structure/types/manage/block_field_test/fields/node.block_field_test.field_block_field_test');
$this->drupalPostForm(NULL, ['settings[selection_settings][plugin_ids][page_title_block]' => FALSE], 'Save settings');
$this->submitForm(['settings[selection_settings][plugin_ids][page_title_block]' => FALSE], 'Save settings');
$this->drupalGet('admin/structure/types/manage/block_field_test/fields/node.block_field_test.field_block_field_test');
$assert->statusCodeEquals(200);
......
......@@ -52,7 +52,8 @@ public function setUp() {
'bypass node access',
]));
$this->drupalPostForm('node/add/block_node', [
$this->drupalGet('node/add/block_node');
$this->submitForm([
'title[0][value]' => 'Block field test',
'field_block[0][plugin_id]' => 'views_block:items-block_1',
], 'Save');
......@@ -123,11 +124,11 @@ public function testConfigurationFormOptions() {
// Configuration form: hidden.
$this->drupalGet('admin/structure/types/manage/block_node/form-display');
$this->drupalPostForm(NULL, [], 'field_block_settings_edit');
$this->submitForm([], 'field_block_settings_edit');
$edit = [
'fields[field_block][settings_edit_form][settings][configuration_form]' => 'hidden',
];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->submitForm($edit, 'Save');
$this->drupalGet($this->blockNode->toUrl('edit-form'));
$assert->fieldNotExists('field_block[0][settings][label_display]');
$assert->fieldNotExists('field_block[0][settings][override][items_per_page]');
......@@ -140,7 +141,8 @@ public function testConfigurationFormOptions() {
*/
public function testBlockFieldValidation() {
$assert = $this->assertSession();
$this->drupalPostForm('node/add/block_node', [
$this->drupalGet('node/add/block_node');
$this->submitForm([
'title[0][value]' => 'Block field validation test',
'field_block[0][plugin_id]' => 'block_field_test_validation',
], 'Save');
......
<?php
namespace Drupal\Tests\block_field\Kernel;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\Tests\block_field\Traits\BlockFieldTestTrait;
/**
* Tests the formatters functionality.
*
* @group entity_reference
*/
class BlockFieldFormatterTest extends EntityKernelTestBase {
use BlockFieldTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['block', 'block_field', 'block_field_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['user']);
// Add a block field to the test entity.
$this->installEntitySchema('entity_test');
$this->createBlockField('entity_test', 'entity_test', 'field_test', 'Field test', 'default', [], FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
}
/**
* Tests that block access cache metadata is propagated.
*/
public function testBlockAccessCacheMetadata() {
$renderer = $this->container->get('renderer');
// Create a referencing entity.
$referencing_entity = $this->container->get('entity_type.manager')
->getStorage('entity_test')
->create(['name' => $this->randomMachineName()]);
$referencing_entity->field_test->plugin_id = 'block_field_test_access';
$referencing_entity->field_test->settings = [
'label' => 'Custom access.',
'label_display' => TRUE,
'access' => TRUE,
];
$referencing_entity->save();
$items = $referencing_entity->get('field_test');
$formatter_manager = $this->container->get('plugin.manager.field.formatter');
// Get all the existing formatters.
foreach ($formatter_manager->getOptions('block_field') as $formatter => $name) {
// Build the renderable array for the field.
$build = $items->view(['type' => $formatter, 'settings' => []]);
$markup = $renderer->renderRoot($build);
// Assert cache tags are propagated correctly.
$this->assertEquals($build['#cache']['tags'], ['custom_tag'], sprintf('The formatter %s does not propagate tags correctly.', $name));
// Assert block contents are properly rendered.
$this->assertStringContainsString('Custom access.', (string) $markup, sprintf('The contents of the block are missing when using the %s formatter', $name));
}
// Assert that the tags are still propagated with a denied access.
$referencing_entity->field_test->settings = [
'label' => 'Custom access.',
'label_display' => TRUE,
'access' => FALSE,
];
$referencing_entity->save();
// Get all the existing formatters.
foreach ($formatter_manager->getOptions('block_field') as $formatter => $name) {
// Build the renderable array for the field.
$build = $items->view(['type' => $formatter, 'settings' => []]);
$markup = $renderer->renderRoot($build);
// Assert cache tags are propagated correctly.
$this->assertEquals($build['#cache']['tags'], ['custom_tag'], sprintf('The formatter %s does not propagate tags correctly.', $name));
// Assert block contents are not rendered.
$this->assertStringNotContainsString('Custom access.', $markup, sprintf('The block contents show even when we denied access when using the %s formatter', $name));
}
}
}
<?php
namespace Drupal\Tests\block_field\Traits;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Provides common functionality for the Block Field test classes.
*/
trait BlockFieldTestTrait {
/**
* Creates a block field on the specified bundle.
*
* @param string $entity_type
* The type of entity the field will be attached to.
* @param string $bundle
* The bundle name of the entity the field will be attached to.
* @param string $field_name
* The name of the field; if it already exists, a new instance of the
* existing field will be created.
* @param string $field_label
* The label of the field.
* @param string $selection_handler
* The selection handler used by this field.
* @param array $selection_handler_settings
* An array of settings supported by the selection handler specified above.
* (e.g. 'plugin_ids').
* @param int $cardinality
* The cardinality of the field.
*
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase::buildConfigurationForm()
*/
protected function createBlockField($entity_type, $bundle, $field_name, $field_label, $selection_handler = 'blocks', array $selection_handler_settings = [], $cardinality = 1) {
// Look for or add the specified field to the requested entity bundle.
if (!FieldStorageConfig::loadByName($entity_type, $field_name)) {
FieldStorageConfig::create([
'field_name' => $field_name,
'type' => 'block_field',
'entity_type' => $entity_type,
'cardinality' => $cardinality,
'settings' => [],
])->save();
}
if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) {
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $field_label,
'settings' => [
'selection' => $selection_handler,
'selection_settings' => $selection_handler_settings,
],
])->save();
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment