Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -5,7 +5,7 @@ package: Core
version: VERSION
core: 8.x
dependencies:
- block
- text
- user
- drupal:block
- drupal:text
- drupal:user
configure: entity.block_content.collection

View file

@ -6,6 +6,7 @@
*/
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
* Add 'revision_translation_affected' field to 'block_content' entities.
@ -70,5 +71,70 @@ function block_content_update_8300() {
$entity_type = $definition_update_manager->getEntityType('block_content');
$entity_type->set('revision_data_table', 'block_content_field_revision');
$definition_update_manager->updateEntityType($entity_type);
}
/**
* Add a publishing status field for block_content entities.
*/
function block_content_update_8400() {
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
// Add the published entity key to the block_content entity type.
$entity_type = $definition_update_manager->getEntityType('block_content');
$entity_keys = $entity_type->getKeys();
$entity_keys['published'] = 'status';
$entity_type->set('entity_keys', $entity_keys);
$definition_update_manager->updateEntityType($entity_type);
// Add the publishing status field to the block_content entity type.
$status = BaseFieldDefinition::create('boolean')
->setLabel(new TranslatableMarkup('Publishing status'))
->setDescription(new TranslatableMarkup('A boolean indicating the published state.'))
->setRevisionable(TRUE)
->setTranslatable(TRUE)
->setDefaultValue(TRUE);
$has_content_translation_status_field = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content');
if ($has_content_translation_status_field) {
$status->setInitialValueFromField('content_translation_status', TRUE);
}
else {
$status->setInitialValue(TRUE);
}
$definition_update_manager->installFieldStorageDefinition('status', 'block_content', 'block_content', $status);
// Uninstall the 'content_translation_status' field if needed.
$database = \Drupal::database();
if ($has_content_translation_status_field) {
// First we have to remove the field data.
$database->update($entity_type->getDataTable())
->fields(['content_translation_status' => NULL])
->execute();
// A site may have disabled revisionability for this entity type.
if ($entity_type->isRevisionable()) {
$database->update($entity_type->getRevisionDataTable())
->fields(['content_translation_status' => NULL])
->execute();
}
$content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content');
$definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
}
}
/**
* Add 'reusable' field to 'block_content' entities.
*/
function block_content_update_8600() {
$reusable = BaseFieldDefinition::create('boolean')
->setLabel(t('Reusable'))
->setDescription(t('A boolean indicating whether this block is reusable.'))
->setTranslatable(FALSE)
->setRevisionable(FALSE)
->setDefaultValue(TRUE)
->setInitialValue(TRUE);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('reusable', 'block_content', 'block_content', $reusable);
}

View file

@ -8,6 +8,9 @@
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\ConditionInterface;
/**
* Implements hook_help().
@ -18,7 +21,7 @@ function block_content_help($route_name, RouteMatchInterface $route_match) {
$field_ui = \Drupal::moduleHandler()->moduleExists('field_ui') ? \Drupal::url('help.page', ['name' => 'field_ui']) : '#';
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Custom Block module allows you to create and manage custom <em>block types</em> and <em>content-containing blocks</em> from the <a href = ":block-library" >Custom block library</a> page. Custom block types have fields; see the <a href=":field-help">Field module help</a> for more information. Once created, custom blocks can be placed in regions just like blocks provided by other modules; see the <a href=":blocks">Block module help</a> page for details. For more information, see the <a href=":online-help">online documentation for the Custom Block module</a>.', [':block-library' => \Drupal::url('entity.block_content.collection'), ':block-content' => \Drupal::url('entity.block_content.collection'), ':field-help' => \Drupal::url('help.page', ['name' => 'field']), ':blocks' => \Drupal::url('help.page', ['name' => 'block']), ':online-help' => 'https://www.drupal.org/documentation/modules/block_content']) . '</p>';
$output .= '<p>' . t('The Custom Block module allows you to create and manage custom <em>block types</em> and <em>content-containing blocks</em> from the <a href=":block-library">Custom block library</a> page. Custom block types have fields; see the <a href=":field-help">Field module help</a> for more information. Once created, custom blocks can be placed in regions just like blocks provided by other modules; see the <a href=":blocks">Block module help</a> page for details. For more information, see the <a href=":online-help">online documentation for the Custom Block module</a>.', [':block-library' => \Drupal::url('entity.block_content.collection'), ':block-content' => \Drupal::url('entity.block_content.collection'), ':field-help' => \Drupal::url('help.page', ['name' => 'field']), ':blocks' => \Drupal::url('help.page', ['name' => 'block']), ':online-help' => 'https://www.drupal.org/documentation/modules/block_content']) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Creating and managing custom block types') . '</dt>';
@ -105,3 +108,73 @@ function block_content_add_body_field($block_type_id, $label = 'Body') {
return $field;
}
/**
* Implements hook_query_TAG_alter().
*
* Alters any 'entity_reference' query where the entity type is
* 'block_content' and the query has the tag 'block_content_access'.
*
* These queries should only return reusable blocks unless a condition on
* 'reusable' is explicitly set.
*
* Block_content entities that are reusable should by default not be selectable
* as entity reference values. A module can still create an instance of
* \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* that will allow selection of non-reusable blocks by explicitly setting
* a condition on the 'reusable' field.
*
* @see \Drupal\block_content\BlockContentAccessControlHandler
*/
function block_content_query_entity_reference_alter(AlterableInterface $query) {
if ($query instanceof SelectInterface && $query->getMetaData('entity_type') === 'block_content' && $query->hasTag('block_content_access')) {
$data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable();
if (array_key_exists($data_table, $query->getTables()) && !_block_content_has_reusable_condition($query->conditions(), $query->getTables())) {
$query->condition("$data_table.reusable", TRUE);
}
}
}
/**
* Utility function to find nested conditions using the reusable field.
*
* @todo Replace this function with a call to the API in
* https://www.drupal.org/project/drupal/issues/2984930
*
* @param array $condition
* The condition or condition group to check.
* @param array $tables
* The tables from the related select query.
*
* @see \Drupal\Core\Database\Query\SelectInterface::getTables
*
* @return bool
* Whether the conditions contain any condition using the reusable field.
*/
function _block_content_has_reusable_condition(array $condition, array $tables) {
// If this is a condition group call this function recursively for each nested
// condition until a condition is found that return TRUE.
if (isset($condition['#conjunction'])) {
foreach (array_filter($condition, 'is_array') as $nested_condition) {
if (_block_content_has_reusable_condition($nested_condition, $tables)) {
return TRUE;
}
}
return FALSE;
}
if (isset($condition['field'])) {
$field = $condition['field'];
if (is_object($field) && $field instanceof ConditionInterface) {
return _block_content_has_reusable_condition($field->conditions(), $tables);
}
$field_parts = explode('.', $field);
$data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable();
foreach ($tables as $table) {
if ($table['table'] === $data_table && $field_parts[0] === $table['alias'] && $field_parts[1] === 'reusable') {
return TRUE;
}
}
}
return FALSE;
}

View file

@ -0,0 +1,90 @@
<?php
/**
* @file
* Post update functions for Custom Block.
*/
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
/**
* Adds a 'reusable' filter to all Custom Block views.
*/
function block_content_post_update_add_views_reusable_filter(&$sandbox = NULL) {
$entity_type = \Drupal::entityTypeManager()->getDefinition('block_content');
$storage = \Drupal::entityTypeManager()->getStorage('block_content');
// If the storage class is an instance SqlContentEntityStorage we can use it
// to determine the table to use, otherwise we have to get the table from the
// entity type.
if ($storage instanceof SqlContentEntityStorage) {
$table = $entity_type->isTranslatable() ? $storage->getDataTable() : $storage->getBaseTable();
}
else {
$table = $entity_type->isTranslatable() ? $entity_type->getDataTable() : $entity_type->getBaseTable();
}
// If we were not able to get a table name we can not update the views.
if (empty($table)) {
return;
}
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function ($view) use ($table) {
/** @var \Drupal\views\ViewEntityInterface $view */
if ($view->get('base_table') !== $table) {
return FALSE;
}
$save_view = FALSE;
$displays = $view->get('display');
foreach ($displays as $display_name => &$display) {
// Update the default display and displays that have overridden filters.
if (!isset($display['display_options']['filters']['reusable']) &&
($display_name === 'default' || isset($display['display_options']['filters']))) {
$display['display_options']['filters']['reusable'] = [
'id' => 'reusable',
'table' => $table,
'field' => 'reusable',
'relationship' => 'none',
'group_type' => 'group',
'admin_label' => '',
'operator' => '=',
'value' => '1',
'group' => 1,
'exposed' => FALSE,
'expose' => [
'operator_id' => '',
'label' => '',
'description' => '',
'use_operator' => FALSE,
'operator' => '',
'identifier' => '',
'required' => FALSE,
'remember' => FALSE,
'multiple' => FALSE,
],
'is_grouped' => FALSE,
'group_info' => [
'label' => '',
'description' => '',
'identifier' => '',
'optional' => TRUE,
'widget' => 'select',
'multiple' => FALSE,
'remember' => FALSE,
'default_group' => 'All',
'default_group_multiple' => [],
'group_items' => [],
],
'entity_type' => 'block_content',
'entity_field' => 'reusable',
'plugin_id' => 'boolean',
];
$save_view = TRUE;
}
}
if ($save_view) {
$view->set('display', $displays);
}
return $save_view;
});
}

View file

@ -1,11 +1,3 @@
entity.block_content_type.collection:
path: '/admin/structure/block/block-content/types'
defaults:
_entity_list: 'block_content_type'
_title: 'Custom block library'
requirements:
_permission: 'administer blocks'
block_content.add_page:
path: '/block/add'
defaults:
@ -26,16 +18,6 @@ block_content.add_form:
requirements:
_permission: 'administer blocks'
entity.block_content_type.delete_form:
path: '/admin/structure/block/block-content/manage/{block_content_type}/delete'
defaults:
_entity_form: 'block_content_type.delete'
_title: 'Delete'
requirements:
_entity_access: 'block_content_type.delete'
options:
_admin_route: TRUE
entity.block_content.canonical:
path: '/block/{block_content}'
defaults:
@ -75,14 +57,6 @@ block_content.type_add:
requirements:
_permission: 'administer blocks'
entity.block_content_type.edit_form:
path: '/admin/structure/block/block-content/manage/{block_content_type}'
defaults:
_entity_form: 'block_content_type.edit'
_title_callback: '\Drupal\Core\Entity\Controller\EntityController::title'
requirements:
_entity_access: 'block_content_type.update'
entity.block_content.collection:
path: '/admin/structure/block/block-content'
defaults:

View file

@ -0,0 +1,6 @@
services:
block_content.uuid_lookup:
class: \Drupal\block_content\BlockContentUuidLookup
arguments: ['@cache.bootstrap', '@lock', '@entity_type.manager']
tags:
- { name: needs_destruction }

View file

@ -431,6 +431,44 @@ display:
entity_type: block_content
entity_field: type
plugin_id: bundle
reusable:
id: reusable
table: block_content_field_data
field: reusable
relationship: none
group_type: group
admin_label: ''
operator: '='
value: '1'
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: block_content
entity_field: reusable
plugin_id: boolean
sorts: { }
title: 'Custom block library'
header: { }
@ -445,7 +483,7 @@ display:
admin_label: ''
empty: true
tokenize: false
content: 'There are no custom blocks available. '
content: 'There are no custom blocks available.'
plugin_id: text_custom
block_content_listing_empty:
admin_label: ''

View file

@ -3,6 +3,7 @@ label: Block content body field configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@ -19,6 +20,7 @@ source:
type: string
field_name:
type: string
source_module: block
process:
entity_type: entity_type
bundle: bundle

View file

@ -3,6 +3,7 @@ label: Body field display configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@ -22,6 +23,7 @@ source:
type: string
field_name:
type: string
source_module: block
process:
entity_type: entity_type
bundle: bundle
@ -36,4 +38,3 @@ migration_dependencies:
provider:
- block_content
- migrate_drupal

View file

@ -3,6 +3,7 @@ label: Body field form display configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@ -20,6 +21,7 @@ source:
type: string
field_name:
type: string
source_module: block
process:
entity_type: entity_type
bundle: bundle
@ -33,4 +35,3 @@ migration_dependencies:
provider:
- block_content
- migrate_drupal

View file

@ -3,6 +3,7 @@ label: Block content type
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@ -12,6 +13,7 @@ source:
ids:
id:
type: string
source_module: block
process:
id: id
label: label

View file

@ -1,7 +1,9 @@
id: d6_custom_block
label: Custom blocks
audit: true
migration_tags:
- Drupal 6
- Content
source:
plugin: d6_box
process:

View file

@ -1,7 +1,9 @@
id: d7_custom_block
label: Custom blocks
audit: true
migration_tags:
- Drupal 7
- Content
source:
plugin: d7_block_custom
process:

View file

@ -0,0 +1,49 @@
<?php
namespace Drupal\block_content\Access;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
/**
* An access group where all the dependencies must be allowed.
*
* @internal
*/
class AccessGroupAnd implements AccessibleInterface {
/**
* The access dependencies.
*
* @var \Drupal\Core\Access\AccessibleInterface[]
*/
protected $dependencies = [];
/**
* {@inheritdoc}
*/
public function addDependency(AccessibleInterface $dependency) {
$this->dependencies[] = $dependency;
return $this;
}
/**
* {@inheritdoc}
*/
public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
$access_result = AccessResult::neutral();
foreach (array_slice($this->dependencies, 1) as $dependency) {
$access_result = $access_result->andIf($dependency->access($operation, $account, TRUE));
}
return $return_as_object ? $access_result : $access_result->isAllowed();
}
/**
* {@inheritdoc}
*/
public function getDependencies() {
return $this->dependencies;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\block_content\Access;
/**
* Interface for AccessibleInterface objects that have an access dependency.
*
* Objects should implement this interface when their access depends on access
* to another object that implements \Drupal\Core\Access\AccessibleInterface.
* This interface simply provides the getter method for the access
* dependency object. Objects that implement this interface are responsible for
* checking access of the access dependency because the dependency may not take
* effect in all cases. For instance an entity may only need the access
* dependency set when it is embedded within another entity and its access
* should be dependent on access to the entity in which it is embedded.
*
* To check the access to the dependency the object implementing this interface
* can use code like this:
* @code
* $accessible->getAccessDependency()->access($op, $account, TRUE);
* @endcode
*
* @internal
*/
interface DependentAccessInterface {
/**
* Gets the access dependency.
*
* @return \Drupal\Core\Access\AccessibleInterface|null
* The access dependency or NULL if none has been set.
*/
public function getAccessDependency();
}

View file

@ -0,0 +1,48 @@
<?php
namespace Drupal\block_content\Access;
use Drupal\Core\Access\AccessibleInterface;
/**
* An interface to allow adding an access dependency.
*
* @internal
*/
interface RefinableDependentAccessInterface extends DependentAccessInterface {
/**
* Sets the access dependency.
*
* If an access dependency is already set this will replace the existing
* dependency.
*
* @param \Drupal\Core\Access\AccessibleInterface $access_dependency
* The object upon which access depends.
*
* @return $this
*/
public function setAccessDependency(AccessibleInterface $access_dependency);
/**
* Adds an access dependency into the existing access dependency.
*
* If no existing dependency is currently set this will set the dependency
* will be set to the new value.
*
* If there is an existing dependency and it is not an instance of
* AccessGroupAnd the dependency will be set as a new AccessGroupAnd
* instance with the existing and new dependencies as the members of the
* group.
*
* If there is an existing dependency and it is a instance of AccessGroupAnd
* the dependency will be added to the existing access group.
*
* @param \Drupal\Core\Access\AccessibleInterface $access_dependency
* The access dependency to merge.
*
* @return $this
*/
public function addAccessDependency(AccessibleInterface $access_dependency);
}

View file

@ -0,0 +1,52 @@
<?php
namespace Drupal\block_content\Access;
use Drupal\Core\Access\AccessibleInterface;
/**
* Trait for \Drupal\block_content\Access\RefinableDependentAccessInterface.
*
* @internal
*/
trait RefinableDependentAccessTrait {
/**
* The access dependency.
*
* @var \Drupal\Core\Access\AccessibleInterface
*/
protected $accessDependency;
/**
* {@inheritdoc}
*/
public function setAccessDependency(AccessibleInterface $access_dependency) {
$this->accessDependency = $access_dependency;
return $this;
}
/**
* {@inheritdoc}
*/
public function getAccessDependency() {
return $this->accessDependency;
}
/**
* {@inheritdoc}
*/
public function addAccessDependency(AccessibleInterface $access_dependency) {
if (empty($this->accessDependency)) {
$this->accessDependency = $access_dependency;
return $this;
}
if (!$this->accessDependency instanceof AccessGroupAnd) {
$accessGroup = new AccessGroupAnd();
$this->accessDependency = $accessGroup->addDependency($this->accessDependency);
}
$this->accessDependency->addDependency($access_dependency);
return $this;
}
}

View file

@ -2,26 +2,87 @@
namespace Drupal\block_content;
use Drupal\block_content\Access\DependentAccessInterface;
use Drupal\block_content\Event\BlockContentGetDependencyEvent;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Defines the access control handler for the custom block entity type.
*
* @see \Drupal\block_content\Entity\BlockContent
*/
class BlockContentAccessControlHandler extends EntityAccessControlHandler {
class BlockContentAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* BlockContentAccessControlHandler constructor.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
* The event dispatcher.
*/
public function __construct(EntityTypeInterface $entity_type, EventDispatcherInterface $dispatcher) {
parent::__construct($entity_type);
$this->eventDispatcher = $dispatcher;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('event_dispatcher')
);
}
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
if ($operation === 'view') {
return AccessResult::allowed();
$access = AccessResult::allowedIf($entity->isPublished())
->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks'));
}
return parent::checkAccess($entity, $operation, $account);
else {
$access = parent::checkAccess($entity, $operation, $account);
}
// Add the entity as a cacheable dependency because access will at least be
// determined by whether the block is reusable.
$access->addCacheableDependency($entity);
/** @var \Drupal\block_content\BlockContentInterface $entity */
if ($entity->isReusable() === FALSE) {
if (!$entity instanceof DependentAccessInterface) {
throw new \LogicException("Non-reusable block entities must implement \Drupal\block_content\Access\DependentAccessInterface for access control.");
}
$dependency = $entity->getAccessDependency();
if (empty($dependency)) {
// If an access dependency has not been set let modules set one.
$event = new BlockContentGetDependencyEvent($entity);
$this->eventDispatcher->dispatch(BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY, $event);
$dependency = $event->getAccessDependency();
if (empty($dependency)) {
return AccessResult::forbidden("Non-reusable blocks must set an access dependency for access control.");
}
}
/** @var \Drupal\Core\Entity\EntityInterface $dependency */
$access = $access->andIf($dependency->access($operation, $account, TRUE));
}
return $access;
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\block_content;
/**
* Defines events for the block_content module.
*
* @see \Drupal\block_content\Event\BlockContentGetDependencyEvent
*
* @internal
*/
final class BlockContentEvents {
/**
* Name of the event when getting the dependency of a non-reusable block.
*
* This event allows modules to provide a dependency for non-reusable block
* access if
* \Drupal\block_content\Access\DependentAccessInterface::getAccessDependency()
* did not return a dependency during access checking.
*
* @Event
*
* @see \Drupal\block_content\Event\BlockContentGetDependencyEvent
* @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess()
*
* @var string
*/
const BLOCK_CONTENT_GET_DEPENDENCY = 'block_content.get_dependency';
}

View file

@ -8,6 +8,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Form handler for the custom block edit forms.
*
* @internal
*/
class BlockContentForm extends ContentEntityForm {
@ -52,11 +54,11 @@ class BlockContentForm extends ContentEntityForm {
if ($insert) {
$logger->notice('@type: added %info.', $context);
drupal_set_message($this->t('@type %info has been created.', $t_args));
$this->messenger()->addStatus($this->t('@type %info has been created.', $t_args));
}
else {
$logger->notice('@type: updated %info.', $context);
drupal_set_message($this->t('@type %info has been updated.', $t_args));
$this->messenger()->addStatus($this->t('@type %info has been updated.', $t_args));
}
if ($block->id()) {
@ -81,7 +83,7 @@ class BlockContentForm extends ContentEntityForm {
else {
// In the unlikely case something went wrong on save, the block will be
// rebuilt and block form redisplayed.
drupal_set_message($this->t('The block could not be saved.'), 'error');
$this->messenger()->addError($this->t('The block could not be saved.'));
$form_state->setRebuild();
}
}

View file

@ -2,14 +2,16 @@
namespace Drupal\block_content;
use Drupal\block_content\Access\RefinableDependentAccessInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\RevisionLogInterface;
/**
* Provides an interface defining a custom block entity.
*/
interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface {
interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface, EntityPublishedInterface, RefinableDependentAccessInterface {
/**
* Returns the block revision log message.
@ -47,6 +49,28 @@ interface BlockContentInterface extends ContentEntityInterface, EntityChangedInt
*/
public function setRevisionLog($revision_log);
/**
* Determines if the block is reusable or not.
*
* @return bool
* Returns TRUE if reusable and FALSE otherwise.
*/
public function isReusable();
/**
* Sets the block to be reusable.
*
* @return $this
*/
public function setReusable();
/**
* Sets the block to be non-reusable.
*
* @return $this
*/
public function setNonReusable();
/**
* Sets the theme value.
*

View file

@ -4,7 +4,6 @@ namespace Drupal\block_content;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Routing\RedirectDestinationTrait;
/**
* Defines a class to build a listing of custom block entities.
@ -13,8 +12,6 @@ use Drupal\Core\Routing\RedirectDestinationTrait;
*/
class BlockContentListBuilder extends EntityListBuilder {
use RedirectDestinationTrait;
/**
* {@inheritdoc}
*/
@ -34,12 +31,16 @@ class BlockContentListBuilder extends EntityListBuilder {
/**
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
if (isset($operations['edit'])) {
$operations['edit']['query']['destination'] = $this->getRedirectDestination()->get();
protected function getEntityIds() {
$query = $this->getStorage()->getQuery()
->sort($this->entityType->getKey('id'));
$query->condition('reusable', TRUE);
// Only add the pager if a limit is specified.
if ($this->limit) {
$query->pager($this->limit);
}
return $operations;
return $query->execute();
}
}

View file

@ -8,7 +8,9 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\language\Entity\ContentLanguageSettings;
/**
* Base form for category edit forms.
* The block content type entity form.
*
* @internal
*/
class BlockContentTypeForm extends BundleEntityFormBase {
@ -98,12 +100,12 @@ class BlockContentTypeForm extends BundleEntityFormBase {
$edit_link = $this->entity->link($this->t('Edit'));
$logger = $this->logger('block_content');
if ($status == SAVED_UPDATED) {
drupal_set_message(t('Custom block type %label has been updated.', ['%label' => $block_type->label()]));
$this->messenger()->addStatus(t('Custom block type %label has been updated.', ['%label' => $block_type->label()]));
$logger->notice('Custom block type %label has been updated.', ['%label' => $block_type->label(), 'link' => $edit_link]);
}
else {
block_content_add_body_field($block_type->id());
drupal_set_message(t('Custom block type %label has been added.', ['%label' => $block_type->label()]));
$this->messenger()->addStatus(t('Custom block type %label has been added.', ['%label' => $block_type->label()]));
$logger->notice('Custom block type %label has been added.', ['%label' => $block_type->label(), 'link' => $edit_link]);
}

View file

@ -0,0 +1,63 @@
<?php
namespace Drupal\block_content;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheCollector;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Lock\LockBackendInterface;
/**
* A cache collector that caches IDs for block_content UUIDs.
*
* As block_content entities are used as block plugin derivatives, it is a
* fairly safe limitation that there are not hundreds of them, a site will
* likely run into problems with too many block content entities in other places
* than a cache that only stores UUID's and IDs. The same assumption is not true
* for other content entities.
*
* @internal
*/
class BlockContentUuidLookup extends CacheCollector {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a BlockContentUuidLookup instance.
*
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend.
* @param \Drupal\Core\Lock\LockBackendInterface $lock
* The lock backend.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct('block_content_uuid', $cache, $lock);
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
protected function resolveCacheMiss($key) {
$ids = $this->entityTypeManager->getStorage('block_content')->getQuery()
->condition('uuid', $key)
->execute();
// Only cache if there is a match, otherwise creating new entities would
// require to invalidate the cache.
$id = reset($ids);
if ($id) {
$this->storage[$key] = $id;
$this->persist($key);
}
return $id;
}
}

View file

@ -2,7 +2,6 @@
namespace Drupal\block_content;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityViewBuilder;
@ -41,18 +40,4 @@ class BlockContentViewBuilder extends EntityViewBuilder {
return $build;
}
/**
* {@inheritdoc}
*/
protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
parent::alterBuild($build, $entity, $display, $view_mode);
// Add contextual links for this custom block.
if (!$entity->isNew()) {
$build['#contextual_links']['block_content'] = [
'route_parameters' => ['block_content' => $entity->id()],
'metadata' => ['changed' => $entity->getChangedTime()],
];
}
}
}

View file

@ -23,6 +23,8 @@ class BlockContentViewsData extends EntityViewsData {
$data['block_content_field_data']['type']['field']['id'] = 'field';
$data['block_content_field_data']['table']['wizard_id'] = 'block_content';
$data['block_content']['block_content_listing_empty'] = [
'title' => $this->t('Empty block library behavior'),
'help' => $this->t('Provides a link to add a new block.'),

View file

@ -98,11 +98,12 @@ class BlockContentController extends ControllerBase {
* The current request object.
*
* @return array
* A form array as expected by drupal_render().
* A form array as expected by
* \Drupal\Core\Render\RendererInterface::render().
*/
public function addForm(BlockContentTypeInterface $block_content_type, Request $request) {
$block = $this->blockContentStorage->create([
'type' => $block_content_type->id()
'type' => $block_content_type->id(),
]);
if (($theme = $request->query->get('theme')) && in_array($theme, array_keys($this->themeHandler->listInfo()))) {
// We have navigated to this page from the block library and will keep track

View file

@ -2,8 +2,8 @@
namespace Drupal\block_content\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\block_content\Access\RefinableDependentAccessTrait;
use Drupal\Core\Entity\EditorialContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
@ -16,6 +16,13 @@ use Drupal\user\UserInterface;
* @ContentEntityType(
* id = "block_content",
* label = @Translation("Custom block"),
* label_collection = @Translation("Custom blocks"),
* label_singular = @Translation("custom block"),
* label_plural = @Translation("custom blocks"),
* label_count = @PluralTranslation(
* singular = "@count custom block",
* plural = "@count custom blocks",
* ),
* bundle_label = @Translation("Custom block type"),
* handlers = {
* "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
@ -42,6 +49,7 @@ use Drupal\user\UserInterface;
* "delete-form" = "/block/{block_content}/delete",
* "edit-form" = "/block/{block_content}",
* "collection" = "/admin/structure/block/block-content",
* "create" = "/block",
* },
* translatable = TRUE,
* entity_keys = {
@ -50,7 +58,13 @@ use Drupal\user\UserInterface;
* "bundle" = "type",
* "label" = "info",
* "langcode" = "langcode",
* "uuid" = "uuid"
* "uuid" = "uuid",
* "published" = "status",
* },
* revision_metadata_keys = {
* "revision_user" = "revision_user",
* "revision_created" = "revision_created",
* "revision_log_message" = "revision_log"
* },
* bundle_entity_type = "block_content_type",
* field_ui_base_route = "entity.block_content_type.edit_form",
@ -62,9 +76,9 @@ use Drupal\user\UserInterface;
* caching.
* See https://www.drupal.org/node/2284917#comment-9132521 for more information.
*/
class BlockContent extends ContentEntityBase implements BlockContentInterface {
class BlockContent extends EditorialContentEntityBase implements BlockContentInterface {
use EntityChangedTrait;
use RefinableDependentAccessTrait;
/**
* The theme the block is being created in.
@ -107,7 +121,9 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
static::invalidateBlockPluginCache();
if ($this->isReusable() || (isset($this->original) && $this->original->isReusable())) {
static::invalidateBlockPluginCache();
}
}
/**
@ -115,7 +131,14 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
*/
public static function postDelete(EntityStorageInterface $storage, array $entities) {
parent::postDelete($storage, $entities);
static::invalidateBlockPluginCache();
/** @var \Drupal\block_content\BlockContentInterface $block */
foreach ($entities as $block) {
if ($block->isReusable()) {
// If any deleted blocks are reusable clear the block cache.
static::invalidateBlockPluginCache();
return;
}
}
}
/**
@ -168,6 +191,8 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
$fields['type']->setLabel(t('Block type'))
->setDescription(t('The block type.'));
$fields['revision_log']->setDescription(t('The log entry explaining the changes in this revision.'));
$fields['info'] = BaseFieldDefinition::create('string')
->setLabel(t('Block description'))
->setDescription(t('A brief description of your block.'))
@ -181,41 +206,18 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
->setDisplayConfigurable('form', TRUE)
->addConstraint('UniqueField', []);
$fields['revision_log'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Revision log message'))
->setDescription(t('The log entry explaining the changes in this revision.'))
->setRevisionable(TRUE)
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 25,
'settings' => [
'rows' => 4,
],
]);
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the custom block was last edited.'))
->setTranslatable(TRUE)
->setRevisionable(TRUE);
$fields['revision_created'] = BaseFieldDefinition::create('created')
->setLabel(t('Revision create time'))
->setDescription(t('The time that the current revision was created.'))
->setRevisionable(TRUE);
$fields['revision_user'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Revision user'))
->setDescription(t('The user ID of the author of the current revision.'))
->setSetting('target_type', 'user')
->setRevisionable(TRUE);
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
$fields['reusable'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Reusable'))
->setDescription(t('A boolean indicating whether this block is reusable.'))
->setTranslatable(FALSE)
->setRevisionable(FALSE)
->setDefaultValue(TRUE);
return $fields;
}
@ -299,6 +301,27 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
return $this;
}
/**
* {@inheritdoc}
*/
public function isReusable() {
return (bool) $this->get('reusable')->value;
}
/**
* {@inheritdoc}
*/
public function setReusable() {
return $this->set('reusable', TRUE);
}
/**
* {@inheritdoc}
*/
public function setNonReusable() {
return $this->set('reusable', FALSE);
}
/**
* Invalidates the block plugin cache after changes and deletions.
*/

View file

@ -11,6 +11,14 @@ use Drupal\block_content\BlockContentTypeInterface;
* @ConfigEntityType(
* id = "block_content_type",
* label = @Translation("Custom block type"),
* label_collection = @Translation("Custom block types"),
* label_singular = @Translation("custom block type"),
* label_plural = @Translation("custom block types"),
* label_count = @PluralTranslation(
* singular = "@count custom block type",
* plural = "@count custom block types",
* ),
* label_collection = @Translation("Custom block library"),
* handlers = {
* "form" = {
* "default" = "Drupal\block_content\BlockContentTypeForm",
@ -18,6 +26,9 @@ use Drupal\block_content\BlockContentTypeInterface;
* "edit" = "Drupal\block_content\BlockContentTypeForm",
* "delete" = "Drupal\block_content\Form\BlockContentTypeDeleteForm"
* },
* "route_provider" = {
* "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider"
* },
* "list_builder" = "Drupal\block_content\BlockContentTypeListBuilder"
* },
* admin_permission = "administer blocks",

View file

@ -0,0 +1,70 @@
<?php
namespace Drupal\block_content\Event;
use Drupal\block_content\BlockContentInterface;
use Drupal\Core\Access\AccessibleInterface;
use Symfony\Component\EventDispatcher\Event;
/**
* Block content event to allow setting an access dependency.
*
* @internal
*/
class BlockContentGetDependencyEvent extends Event {
/**
* The block content entity.
*
* @var \Drupal\block_content\BlockContentInterface
*/
protected $blockContent;
/**
* The dependency.
*
* @var \Drupal\Core\Access\AccessibleInterface
*/
protected $accessDependency;
/**
* BlockContentGetDependencyEvent constructor.
*
* @param \Drupal\block_content\BlockContentInterface $blockContent
* The block content entity.
*/
public function __construct(BlockContentInterface $blockContent) {
$this->blockContent = $blockContent;
}
/**
* Gets the block content entity.
*
* @return \Drupal\block_content\BlockContentInterface
* The block content entity.
*/
public function getBlockContentEntity() {
return $this->blockContent;
}
/**
* Gets the access dependency.
*
* @return \Drupal\Core\Access\AccessibleInterface
* The access dependency.
*/
public function getAccessDependency() {
return $this->accessDependency;
}
/**
* Sets the access dependency.
*
* @param \Drupal\Core\Access\AccessibleInterface $access_dependency
* The access dependency.
*/
public function setAccessDependency(AccessibleInterface $access_dependency) {
$this->accessDependency = $access_dependency;
}
}

View file

@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Provides a confirmation form for deleting a custom block entity.
*
* @internal
*/
class BlockContentDeleteForm extends ContentEntityDeleteForm {

View file

@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Provides a confirmation form for deleting a custom block type entity.
*
* @internal
*/
class BlockContentTypeDeleteForm extends EntityDeleteForm {

View file

@ -2,10 +2,12 @@
namespace Drupal\block_content\Plugin\Block;
use Drupal\block_content\BlockContentUuidLookup;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\UrlGeneratorInterface;
@ -27,21 +29,21 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
/**
* The Plugin Block Manager.
*
* @var \Drupal\Core\Block\BlockManagerInterface.
* @var \Drupal\Core\Block\BlockManagerInterface
*/
protected $blockManager;
/**
* The entity manager service.
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityManager;
protected $entityTypeManager;
/**
* The Drupal account to use for checking for access to block.
*
* @var \Drupal\Core\Session\AccountInterface.
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
@ -59,6 +61,20 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
*/
protected $urlGenerator;
/**
* The block content UUID lookup service.
*
* @var \Drupal\block_content\BlockContentUuidLookup
*/
protected $uuidLookup;
/**
* The entity display repository.
*
* @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
*/
protected $entityDisplayRepository;
/**
* Constructs a new BlockContentBlock.
*
@ -70,20 +86,26 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
* The plugin implementation definition.
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
* The Plugin Block Manager.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
* @param \Drupal\Core\Session\AccountInterface $account
* The account for which view access should be checked.
* @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
* The URL generator.
* @param \Drupal\block_content\BlockContentUuidLookup $uuid_lookup
* The block content UUID lookup service.
* @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
* The entity display repository.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockManagerInterface $block_manager, EntityManagerInterface $entity_manager, AccountInterface $account, UrlGeneratorInterface $url_generator) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockManagerInterface $block_manager, EntityTypeManagerInterface $entity_type_manager, AccountInterface $account, UrlGeneratorInterface $url_generator, BlockContentUuidLookup $uuid_lookup, EntityDisplayRepositoryInterface $entity_display_repository) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->blockManager = $block_manager;
$this->entityManager = $entity_manager;
$this->entityTypeManager = $entity_type_manager;
$this->account = $account;
$this->urlGenerator = $url_generator;
$this->uuidLookup = $uuid_lookup;
$this->entityDisplayRepository = $entity_display_repository;
}
/**
@ -95,9 +117,11 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.block'),
$container->get('entity.manager'),
$container->get('entity_type.manager'),
$container->get('current_user'),
$container->get('url_generator')
$container->get('url_generator'),
$container->get('block_content.uuid_lookup'),
$container->get('entity_display.repository')
);
}
@ -118,9 +142,11 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
* Adds body and description fields to the block configuration form.
*/
public function blockForm($form, FormStateInterface $form_state) {
$uuid = $this->getDerivativeId();
$block = $this->entityManager->loadEntityByUuid('block_content', $uuid);
$options = $this->entityManager->getViewModeOptionsByBundle('block_content', $block->bundle());
$block = $this->getEntity();
if (!$block) {
return $form;
}
$options = $this->entityDisplayRepository->getViewModeOptionsByBundle('block_content', $block->bundle());
$form['view_mode'] = [
'#type' => 'select',
@ -158,15 +184,15 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
*/
public function build() {
if ($block = $this->getEntity()) {
return $this->entityManager->getViewBuilder($block->getEntityTypeId())->view($block, $this->configuration['view_mode']);
return $this->entityTypeManager->getViewBuilder($block->getEntityTypeId())->view($block, $this->configuration['view_mode']);
}
else {
return [
'#markup' => $this->t('Block with uuid %uuid does not exist. <a href=":url">Add custom block</a>.', [
'%uuid' => $this->getDerivativeId(),
':url' => $this->urlGenerator->generate('block_content.add_page')
':url' => $this->urlGenerator->generate('block_content.add_page'),
]),
'#access' => $this->account->hasPermission('administer blocks')
'#access' => $this->account->hasPermission('administer blocks'),
];
}
}
@ -178,9 +204,11 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
* The block content entity.
*/
protected function getEntity() {
$uuid = $this->getDerivativeId();
if (!isset($this->blockContent)) {
$this->blockContent = $this->entityManager->loadEntityByUuid('block_content', $uuid);
$uuid = $this->getDerivativeId();
if ($id = $this->uuidLookup->get($uuid)) {
$this->blockContent = $this->entityTypeManager->getStorage('block_content')->load($id);
}
}
return $this->blockContent;
}

View file

@ -43,7 +43,7 @@ class BlockContent extends DeriverBase implements ContainerDeriverInterface {
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$block_contents = $this->blockContentStorage->loadMultiple();
$block_contents = $this->blockContentStorage->loadByProperties(['reusable' => TRUE]);
// Reset the discovered definitions.
$this->derivatives = [];
/** @var $block_content \Drupal\block_content\Entity\BlockContent */
@ -51,7 +51,7 @@ class BlockContent extends DeriverBase implements ContainerDeriverInterface {
$this->derivatives[$block_content->uuid()] = $base_plugin_definition;
$this->derivatives[$block_content->uuid()]['admin_label'] = $block_content->label();
$this->derivatives[$block_content->uuid()]['config_dependencies']['content'] = [
$block_content->getConfigDependencyName()
$block_content->getConfigDependencyName(),
];
}
return parent::getDerivativeDefinitions($base_plugin_definition);

View file

@ -8,7 +8,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
* Drupal 6 block source from database.
*
* @MigrateSource(
* id = "d6_box"
* id = "d6_box",
* source_module = "block"
* )
*/
class Box extends DrupalSqlBase {

View file

@ -0,0 +1,23 @@
<?php
namespace Drupal\block_content\Plugin\migrate\source\d6;
use Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation as D7BlockCustomTranslation;
/**
* Gets Drupal 6 i18n custom block translations from database.
*
* @MigrateSource(
* id = "d6_box_translation",
* source_module = "i18nblocks"
* )
*/
class BoxTranslation extends D7BlockCustomTranslation {
/**
* Drupal 6 table names.
*/
const CUSTOM_BLOCK_TABLE = 'boxes';
const I18N_STRING_TABLE = 'i18n_strings';
}

View file

@ -8,7 +8,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
* Drupal 7 custom block source from database.
*
* @MigrateSource(
* id = "d7_block_custom"
* id = "d7_block_custom",
* source_module = "block"
* )
*/
class BlockCustom extends DrupalSqlBase {

View file

@ -0,0 +1,99 @@
<?php
namespace Drupal\block_content\Plugin\migrate\source\d7;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\content_translation\Plugin\migrate\source\I18nQueryTrait;
/**
* Gets Drupal 7 custom block translation from database.
*
* @MigrateSource(
* id = "d7_block_custom_translation",
* source_module = "block"
* )
*/
class BlockCustomTranslation extends DrupalSqlBase {
use I18nQueryTrait;
/**
* Drupal 7 table names.
*/
const CUSTOM_BLOCK_TABLE = 'block_custom';
const I18N_STRING_TABLE = 'i18n_string';
/**
* {@inheritdoc}
*/
public function query() {
// Build a query based on blockCustomTable table where each row has the
// translation for only one property, either title or description. The
// method prepareRow() is then used to obtain the translation for the
// other property.
$query = $this->select(static::CUSTOM_BLOCK_TABLE, 'b')
->fields('b', ['bid', 'format', 'body'])
->fields('i18n', ['property'])
->fields('lt', ['lid', 'translation', 'language'])
->orderBy('b.bid')
->isNotNull('lt.lid');
// Use 'title' for the info field to match the property name in
// i18nStringTable.
$query->addField('b', 'info', 'title');
// Add in the property, which is either title or body. Cast the bid to text
// so PostgreSQL can make the join.
$query->leftJoin(static::I18N_STRING_TABLE, 'i18n', 'i18n.objectid = CAST(b.bid as CHAR(255))');
$query->condition('i18n.type', 'block');
// Add in the translation for the property.
$query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
return $query;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
parent::prepareRow($row);
// Set the i18n string table for use in I18nQueryTrait.
$this->i18nStringTable = static::I18N_STRING_TABLE;
// Save the translation for this property.
$property_in_row = $row->getSourceProperty('property');
// Get the translation for the property not already in the row and save it
// in the row.
$property_not_in_row = ($property_in_row === 'title') ? 'body' : 'title';
return $this->getPropertyNotInRowTranslation($row, $property_not_in_row, 'bid', $this->idMap);
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'bid' => $this->t('The block numeric identifier.'),
'format' => $this->t('Input format of the custom block/box content.'),
'lid' => $this->t('i18n_string table id'),
'language' => $this->t('Language for this field.'),
'property' => $this->t('Block property'),
'translation' => $this->t('The translation of the value of "property".'),
'title' => $this->t('Block title.'),
'title_translated' => $this->t('Block title translation.'),
'body' => $this->t('Block body.'),
'body_translated' => $this->t('Block body translation.'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['bid']['type'] = 'integer';
$ids['bid']['alias'] = 'b';
$ids['language']['type'] = 'string';
return $ids;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\block_content\Plugin\views\wizard;
use Drupal\views\Plugin\views\wizard\WizardPluginBase;
/**
* Used for creating 'block_content' views with the wizard.
*
* @ViewsWizard(
* id = "block_content",
* base_table = "block_content_field_data",
* title = @Translation("Custom Block"),
* )
*/
class BlockContent extends WizardPluginBase {
/**
* {@inheritdoc}
*/
public function getFilters() {
$filters = parent::getFilters();
$filters['reusable'] = [
'id' => 'reusable',
'plugin_id' => 'boolean',
'table' => $this->base_table,
'field' => 'reusable',
'value' => '1',
'entity_type' => $this->entityTypeId,
'entity_field' => 'reusable',
];
return $filters;
}
}

View file

@ -16,6 +16,8 @@ abstract class BlockContentTestBase extends WebTestBase {
/**
* Profile to use.
*
* @var string
*/
protected $profile = 'testing';
@ -32,7 +34,7 @@ abstract class BlockContentTestBase extends WebTestBase {
* @var array
*/
protected $permissions = [
'administer blocks'
'administer blocks',
];
/**
@ -81,7 +83,7 @@ abstract class BlockContentTestBase extends WebTestBase {
$block_content = BlockContent::create([
'info' => $title,
'type' => $bundle,
'langcode' => 'en'
'langcode' => 'en',
]);
if ($block_content && $save === TRUE) {
$block_content->save();

View file

@ -2,14 +2,19 @@
namespace Drupal\block_content\Tests\Views;
@trigger_error('\Drupal\block_content\Tests\Views\BlockContentTestBase is deprecated in 8.4.0 and will be removed before Drupal 9.0.0. Use \Drupal\Tests\block_content\Functional\Views\BlockContentTestBase.', E_USER_DEPRECATED);
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\views\Tests\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Base class for all block_content tests.
*
* @deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0.
* Use \Drupal\Tests\block_content\Functional\Views\BlockContentTestBase.
*/
abstract class BlockContentTestBase extends ViewTestBase {
@ -68,7 +73,7 @@ abstract class BlockContentTestBase extends ViewTestBase {
if ($block_content = BlockContent::create($settings)) {
$status = $block_content->save();
}
$this->assertEqual($status, SAVED_NEW, SafeMarkup::format('Created block content %info.', ['%info' => $block_content->label()]));
$this->assertEqual($status, SAVED_NEW, new FormattableMarkup('Created block content %info.', ['%info' => $block_content->label()]));
return $block_content;
}
@ -94,13 +99,13 @@ abstract class BlockContentTestBase extends ViewTestBase {
$values += [
'id' => $id,
'label' => $id,
'revision' => FALSE
'revision' => FALSE,
];
$bundle = BlockContentType::create($values);
$status = $bundle->save();
block_content_add_body_field($bundle->id());
$this->assertEqual($status, SAVED_NEW, SafeMarkup::format('Created block content type %bundle.', ['%bundle' => $bundle->id()]));
$this->assertEqual($status, SAVED_NEW, new FormattableMarkup('Created block content type %bundle.', ['%bundle' => $bundle->id()]));
return $bundle;
}

View file

@ -0,0 +1,22 @@
<?php
/**
* @file
* Contains database additions to drupal-8.bare.standard.php.gz for testing the
* upgrade path of https://www.drupal.org/project/drupal/issues/2976334.
*/
use Drupal\Core\Database\Database;
use Drupal\Core\Serialization\Yaml;
$connection = Database::getConnection();
// Override configuration for 'block_content' View with extra display with with
// overridden filters.
$config = Yaml::decode(file_get_contents(__DIR__ . '/views.view.block_content_2976334.yml'));
$connection->update('config')
->fields([
'data' => serialize($config),
])
->condition('name', 'views.view.' . $config['id'])
->execute();

View file

@ -0,0 +1,602 @@
langcode: en
status: true
dependencies:
module:
- block_content
- user
id: block_content
label: 'Custom block library'
module: views
description: 'Find and manage custom blocks.'
tag: default
base_table: block_content_field_data
base_field: id
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'administer blocks'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 50
offset: 0
id: 0
total_pages: null
tags:
previous: ' Previous'
next: 'Next '
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
style:
type: table
options:
grouping: { }
row_class: ''
default_row_class: true
override: true
sticky: false
caption: ''
summary: ''
description: ''
columns:
info: info
type: type
changed: changed
operations: operations
info:
info:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
type:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
changed:
sortable: true
default_sort_order: desc
align: ''
separator: ''
empty_column: false
responsive: ''
operations:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
default: changed
empty_table: true
row:
type: fields
fields:
info:
id: info
table: block_content_field_data
field: info
relationship: none
group_type: group
admin_label: ''
label: 'Block description'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: null
entity_field: info
plugin_id: field
type:
id: type
table: block_content_field_data
field: type
relationship: none
group_type: group
admin_label: ''
label: 'Block type'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: false
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: block_content
entity_field: type
plugin_id: field
changed:
id: changed
table: block_content_field_data
field: changed
relationship: none
group_type: group
admin_label: ''
label: Updated
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
entity_type: block_content
entity_field: changed
type: timestamp
settings:
date_format: short
custom_date_format: ''
timezone: ''
plugin_id: field
operations:
id: operations
table: block_content
field: operations
relationship: none
group_type: group
admin_label: ''
label: Operations
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
destination: true
entity_type: block_content
plugin_id: entity_operations
filters:
info:
id: info
table: block_content_field_data
field: info
relationship: none
group_type: group
admin_label: ''
operator: contains
value: ''
group: 1
exposed: true
expose:
operator_id: info_op
label: 'Block description'
description: ''
use_operator: false
operator: info_op
identifier: info
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: block_content
entity_field: info
plugin_id: string
type:
id: type
table: block_content_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value: { }
group: 1
exposed: true
expose:
operator_id: type_op
label: 'Block type'
description: ''
use_operator: false
operator: type_op
identifier: type
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: block_content
entity_field: type
plugin_id: bundle
sorts: { }
title: 'Custom block library'
header: { }
footer: { }
empty:
area_text_custom:
id: area_text_custom
table: views
field: area_text_custom
relationship: none
group_type: group
admin_label: ''
empty: true
tokenize: false
content: 'There are no custom blocks available.'
plugin_id: text_custom
block_content_listing_empty:
admin_label: ''
empty: true
field: block_content_listing_empty
group_type: group
id: block_content_listing_empty
label: ''
relationship: none
table: block_content
plugin_id: block_content_listing_empty
entity_type: block_content
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
max-age: 0
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: admin/structure/block/block-content
menu:
type: tab
title: 'Custom block library'
description: ''
parent: block.admin_display
weight: 0
context: '0'
menu_name: admin
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
max-age: 0
tags: { }
page_2:
display_plugin: page
id: page_2
display_title: 'Page 2'
position: 2
display_options:
display_extenders: { }
path: extra-view-display
filters:
type:
id: type
table: block_content_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value: { }
group: 1
exposed: true
expose:
operator_id: type_op
label: 'Block type'
description: ''
use_operator: false
operator: type_op
identifier: type
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: block_content
entity_field: type
plugin_id: bundle
info:
id: info
table: block_content_field_data
field: info
relationship: none
group_type: group
admin_label: ''
operator: 'contains'
value: block2
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
placeholder: ''
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: block_content
entity_field: info
plugin_id: string
defaults:
filters: false
filter_groups: false
filter_groups:
operator: AND
groups:
1: AND
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }

View file

@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- block_content
- drupal:block_content

View file

@ -0,0 +1,80 @@
<?php
namespace Drupal\block_content_test\Plugin\EntityReferenceSelection;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
/**
* Test EntityReferenceSelection with conditions on the 'reusable' field.
*/
class TestSelection extends DefaultSelection {
/**
* The condition type.
*
* @var string
*/
protected $conditionType;
/**
* Whether to set the condition for reusable or non-reusable blocks.
*
* @var bool
*/
protected $isReusable;
/**
* Sets the test mode.
*
* @param string $condition_type
* The condition type.
* @param bool $is_reusable
* Whether to set the condition for reusable or non-reusable blocks.
*/
public function setTestMode($condition_type = NULL, $is_reusable = NULL) {
$this->conditionType = $condition_type;
$this->isReusable = $is_reusable;
}
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
$query = parent::buildEntityQuery($match, $match_operator);
if ($this->conditionType) {
/** @var \Drupal\Core\Database\Query\ConditionInterface $add_condition */
$add_condition = NULL;
switch ($this->conditionType) {
case 'base':
$add_condition = $query;
break;
case 'group':
$group = $query->andConditionGroup()
->exists('type');
$add_condition = $group;
$query->condition($group);
break;
case "nested_group":
$query->exists('type');
$sub_group = $query->andConditionGroup()
->exists('type');
$add_condition = $sub_group;
$group = $query->andConditionGroup()
->exists('type')
->condition($sub_group);
$query->condition($group);
break;
}
if ($this->isReusable) {
$add_condition->condition('reusable', 1);
}
else {
$add_condition->condition('reusable', 0);
}
}
return $query;
}
}

View file

@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- block_content
- views
- drupal:block_content
- drupal:views

View file

@ -7,7 +7,7 @@ use Drupal\block_content\Entity\BlockContentType;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\system\Tests\Entity\EntityCacheTagsTestBase;
use Drupal\Tests\system\Functional\Entity\EntityCacheTagsTestBase;
use Symfony\Component\HttpFoundation\Request;
/**
@ -29,7 +29,7 @@ class BlockContentCacheTagsTest extends EntityCacheTagsTestBase {
$block_content_type = BlockContentType::create([
'id' => 'basic',
'label' => 'basic',
'revision' => FALSE
'revision' => FALSE,
]);
$block_content_type->save();
block_content_add_body_field($block_content_type->id());
@ -76,7 +76,7 @@ class BlockContentCacheTagsTest extends EntityCacheTagsTestBase {
// Render the block.
// @todo The request stack manipulation won't be necessary once
// https://www.drupal.org/node/2367555 is fixed and the
// corresponding $request->isMethodSafe() checks are removed from
// corresponding $request->isMethodCacheable() checks are removed from
// Drupal\Core\Render\Renderer.
$request_stack = $this->container->get('request_stack');
$request_stack->push(new Request());

View file

@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\block_content\Functional;
/**
* Tests views contextual links on block content.
*
* @group block_content
*/
class BlockContentContextualLinksTest extends BlockContentTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'contextual',
];
/**
* Tests contextual links.
*/
public function testBlockContentContextualLinks() {
$block_content = $this->createBlockContent();
$block = $this->placeBlock('block_content:' . $block_content->uuid());
$user = $this->drupalCreateUser([
'administer blocks',
'access contextual links',
]);
$this->drupalLogin($user);
$this->drupalGet('<front>');
$this->assertSession()->elementAttributeContains('css', 'div[data-contextual-id]', 'data-contextual-id', 'block:block=' . $block->id() . ':langcode=en|block_content:block_content=' . $block_content->id() . ':');
}
}

View file

@ -1,9 +1,8 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Database\Database;
/**
@ -30,7 +29,7 @@ class BlockContentCreationTest extends BlockContentTestBase {
*/
protected $permissions = [
'administer blocks',
'administer block_content display'
'administer block_content display',
];
/**
@ -56,14 +55,16 @@ class BlockContentCreationTest extends BlockContentTestBase {
// Check that the Basic block has been created.
$this->assertRaw(format_string('@block %name has been created.', [
'@block' => 'basic',
'%name' => $edit['info[0][value]']
'%name' => $edit['info[0][value]'],
]), 'Basic block created.');
// Check that the view mode setting is hidden because only one exists.
$this->assertNoFieldByXPath('//select[@name="settings[view_mode]"]', NULL, 'View mode setting hidden because only one exists');
// Check that the block exists in the database.
$blocks = entity_load_multiple_by_properties('block_content', ['info' => $edit['info[0][value]']]);
$blocks = \Drupal::entityTypeManager()
->getStorage('block_content')
->loadByProperties(['info' => $edit['info[0][value]']]);
$block = reset($blocks);
$this->assertTrue($block, 'Custom Block found in database.');
@ -73,7 +74,7 @@ class BlockContentCreationTest extends BlockContentTestBase {
// Check that the Basic block has been created.
$this->assertRaw(format_string('A custom block with block description %value already exists.', [
'%value' => $edit['info[0][value]']
'%value' => $edit['info[0][value]'],
]));
$this->assertResponse(200);
}
@ -103,7 +104,7 @@ class BlockContentCreationTest extends BlockContentTestBase {
// Check that the Basic block has been created.
$this->assertRaw(format_string('@block %name has been created.', [
'@block' => 'basic',
'%name' => $edit['info[0][value]']
'%name' => $edit['info[0][value]'],
]), 'Basic block created.');
// Save our block permanently
@ -140,10 +141,12 @@ class BlockContentCreationTest extends BlockContentTestBase {
// Go to the configure page and verify the view mode has changed.
$this->drupalGet('admin/structure/block/manage/testblock');
$this->assertFieldByXPath('//select[@name="settings[view_mode]"]/option[@selected="selected"]/@value', 'test_view_mode', 'View mode changed to Test View Mode');
$this->assertFieldByXPath('//select[@name="settings[view_mode]"]/option[@selected="selected"]', 'test_view_mode', 'View mode changed to Test View Mode');
// Check that the block exists in the database.
$blocks = entity_load_multiple_by_properties('block_content', ['info' => $edit['info[0][value]']]);
$blocks = \Drupal::entityTypeManager()
->getStorage('block_content')
->loadByProperties(['info' => $edit['info[0][value]']]);
$block = reset($blocks);
$this->assertTrue($block, 'Custom Block found in database.');
@ -153,7 +156,7 @@ class BlockContentCreationTest extends BlockContentTestBase {
// Check that the Basic block has been created.
$this->assertRaw(format_string('A custom block with block description %value already exists.', [
'%value' => $edit['info[0][value]']
'%value' => $edit['info[0][value]'],
]));
$this->assertResponse(200);
}
@ -178,7 +181,9 @@ class BlockContentCreationTest extends BlockContentTestBase {
]), 'Basic block created.');
// Check that the block exists in the database.
$blocks = entity_load_multiple_by_properties('block_content', ['info' => $edit['info[0][value]']]);
$blocks = \Drupal::entityTypeManager()
->getStorage('block_content')
->loadByProperties(['info' => $edit['info[0][value]']]);
$block = reset($blocks);
$this->assertTrue($block, 'Default Custom Block found in database.');
}
@ -233,7 +238,7 @@ class BlockContentCreationTest extends BlockContentTestBase {
// Place the block.
$instance = [
'id' => Unicode::strtolower($edit['info[0][value]']),
'id' => mb_strtolower($edit['info[0][value]']),
'settings[label]' => $edit['info[0][value]'],
'region' => 'sidebar_first',
];
@ -285,7 +290,7 @@ class BlockContentCreationTest extends BlockContentTestBase {
public function testConfigDependencies() {
$block = $this->createBlockContent();
// Place the block.
$block_placement_id = Unicode::strtolower($block->label());
$block_placement_id = mb_strtolower($block->label());
$instance = [
'id' => $block_placement_id,
'settings[label]' => $block->label(),

View file

@ -1,6 +1,8 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
/**
* Tests the listing of custom blocks.
@ -41,7 +43,7 @@ class BlockContentListTest extends BlockContentTestBase {
// Test the contents of each th cell.
$expected_items = [t('Block description'), t('Operations')];
foreach ($elements as $key => $element) {
$this->assertEqual($element[0], $expected_items[$key]);
$this->assertEqual($element->getText(), $expected_items[$key]);
}
$label = 'Antelope';
@ -66,7 +68,7 @@ class BlockContentListTest extends BlockContentTestBase {
// Check the contents of each row cell. The first cell contains the label,
// the second contains the machine name, and the third contains the
// operations list.
$this->assertIdentical((string) $elements[0], $label);
$this->assertIdentical($elements[0]->getText(), $label);
// Edit the entity using the operations link.
$blocks = $this->container
@ -103,7 +105,20 @@ class BlockContentListTest extends BlockContentTestBase {
$this->assertNoFieldByXpath('//td', $new_label, 'No label found for deleted custom block.');
// Confirm that the empty text is displayed.
$this->assertText(t('There is no Custom block yet.'));
$this->assertText(t('There are no custom blocks yet.'));
$block_content = BlockContent::create([
'info' => 'Non-reusable block',
'type' => 'basic',
'reusable' => FALSE,
]);
$block_content->save();
$this->drupalGet('admin/structure/block/block-content');
// Confirm that the empty text is displayed.
$this->assertSession()->pageTextContains('There are no custom blocks yet.');
// Confirm the non-reusable block is not on the page.
$this->assertSession()->pageTextNotContains('Non-reusable block');
}
}

View file

@ -1,6 +1,8 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
/**
* Tests the Views-powered listing of custom blocks.
@ -41,13 +43,13 @@ class BlockContentListViewsTest extends BlockContentTestBase {
$this->assertEqual(count($elements), 4, 'Correct number of table header cells found.');
// Test the contents of each th cell.
$expected_items = ['Block description', 'Block type', 'Updated', 'Operations'];
$expected_items = ['Block description', 'Block type', 'Updated Sort ascending', 'Operations'];
foreach ($elements as $key => $element) {
if ($element->xpath('a')) {
$this->assertIdentical(trim((string) $element->xpath('a')[0]), $expected_items[$key]);
if ($element->find('xpath', 'a')) {
$this->assertIdentical(trim($element->find('xpath', 'a')->getText()), $expected_items[$key]);
}
else {
$this->assertIdentical(trim((string) $element[0]), $expected_items[$key]);
$this->assertIdentical(trim($element->getText()), $expected_items[$key]);
}
}
@ -73,7 +75,7 @@ class BlockContentListViewsTest extends BlockContentTestBase {
// Check the contents of each row cell. The first cell contains the label,
// the second contains the machine name, and the third contains the
// operations list.
$this->assertIdentical((string) $elements[0]->xpath('a')[0], $label);
$this->assertIdentical($elements[0]->find('xpath', 'a')->getText(), $label);
// Edit the entity using the operations link.
$blocks = $this->container
@ -112,6 +114,19 @@ class BlockContentListViewsTest extends BlockContentTestBase {
// Confirm that the empty text is displayed.
$this->assertText('There are no custom blocks available.');
$this->assertLink('custom block');
$block_content = BlockContent::create([
'info' => 'Non-reusable block',
'type' => 'basic',
'reusable' => FALSE,
]);
$block_content->save();
$this->drupalGet('admin/structure/block/block-content');
// Confirm that the empty text is displayed.
$this->assertSession()->pageTextContains('There are no custom blocks available.');
// Confirm the non-reusable block is not on the page.
$this->assertSession()->pageTextNotContains('Non-reusable block');
}
}

View file

@ -31,7 +31,7 @@ class BlockContentRevisionsTest extends BlockContentTestBase {
protected function setUp() {
parent::setUp();
/** @var UserInterface $user */
/** @var \Drupal\user\Entity\UserInterface $user */
$user = User::load(1);
// Create initial block.

View file

@ -39,7 +39,7 @@ class BlockContentSaveTest extends BlockContentTestBase {
'info' => $info,
'body' => ['value' => $this->randomMachineName(32)],
'type' => 'basic',
'id' => $test_id
'id' => $test_id,
];
$block = BlockContent::create($block_array);
$block->enforceIsNew(TRUE);

View file

@ -13,6 +13,8 @@ abstract class BlockContentTestBase extends BrowserTestBase {
/**
* Profile to use.
*
* @var string
*/
protected $profile = 'testing';
@ -29,7 +31,7 @@ abstract class BlockContentTestBase extends BrowserTestBase {
* @var array
*/
protected $permissions = [
'administer blocks'
'administer blocks',
];
/**
@ -78,7 +80,7 @@ abstract class BlockContentTestBase extends BrowserTestBase {
$block_content = BlockContent::create([
'info' => $title,
'type' => $bundle,
'langcode' => 'en'
'langcode' => 'en',
]);
if ($block_content && $save === TRUE) {
$block_content->save();

View file

@ -1,11 +1,10 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Component\Utility\Unicode;
use Drupal\content_translation\Tests\ContentTranslationUITestBase;
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
/**
* Tests the block content translation UI.
@ -24,7 +23,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
'content_translation',
'block',
'field_ui',
'block_content'
'block_content',
];
/**
@ -60,7 +59,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
$bundle = BlockContentType::create([
'id' => $this->bundle,
'label' => $this->bundle,
'revision' => FALSE
'revision' => FALSE,
]);
$bundle->save();
}
@ -73,7 +72,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
'translate any entity',
'access administration pages',
'administer blocks',
'administer block_content fields'
'administer block_content fields',
]);
}
@ -96,7 +95,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
$block_content = BlockContent::create([
'info' => $title,
'type' => $bundle,
'langcode' => 'en'
'langcode' => 'en',
]);
$block_content->save();
return $block_content;
@ -106,7 +105,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
* {@inheritdoc}
*/
protected function getNewEntityValues($langcode) {
return ['info' => Unicode::strtolower($this->randomMachineName())] + parent::getNewEntityValues($langcode);
return ['info' => mb_strtolower($this->randomMachineName())] + parent::getNewEntityValues($langcode);
}
/**
@ -162,7 +161,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
$bundle = BlockContentType::create([
'id' => $disabled_bundle,
'label' => $disabled_bundle,
'revision' => FALSE
'revision' => FALSE,
]);
$bundle->save();

View file

@ -1,11 +1,11 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\system\Tests\Menu\AssertBreadcrumbTrait;
use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait;
/**
* Ensures that custom block type functions work correctly.
@ -29,7 +29,7 @@ class BlockContentTypeTest extends BlockContentTestBase {
*/
protected $permissions = [
'administer blocks',
'administer block_content fields'
'administer block_content fields',
];
/**
@ -108,7 +108,7 @@ class BlockContentTypeTest extends BlockContentTestBase {
// Verify that title and body fields are displayed.
$this->drupalGet('block/add/basic');
$this->assertRaw('Block description', 'Block info field was found.');
$this->assertRaw('Body', 'Body field was found.');
$this->assertNotEmpty($this->cssSelect('#edit-body-0-value'), 'Body field was found.');
// Change the block type name.
$edit = [
@ -122,7 +122,7 @@ class BlockContentTypeTest extends BlockContentTestBase {
$front_page_path => 'Home',
'admin/structure/block' => 'Block layout',
'admin/structure/block/block-content' => 'Custom block library',
'admin/structure/block/block-content/manage/basic' => 'Bar',
'admin/structure/block/block-content/manage/basic' => 'Edit Bar',
]);
\Drupal::entityManager()->clearCachedFieldDefinitions();
@ -137,7 +137,7 @@ class BlockContentTypeTest extends BlockContentTestBase {
$this->drupalPostForm('admin/structure/block/block-content/manage/basic', [], t('Save'));
// Check that the body field doesn't exist.
$this->drupalGet('block/add/basic');
$this->assertNoRaw('Body', 'Body field was not found.');
$this->assertEmpty($this->cssSelect('#edit-body-0-value'), 'Body field was not found.');
}
/**
@ -204,7 +204,7 @@ class BlockContentTypeTest extends BlockContentTestBase {
// block configure form.
$path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme";
$this->drupalGet($path);
$this->clickLinkPartialName('Place block');
$this->clickLink('Place block');
$this->clickLink(t('Add custom block'));
// The seven theme has markup inside the link, we cannot use clickLink().
if ($default_theme == 'seven') {

View file

@ -1,6 +1,6 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional;
/**
* Tests block content validation constraints.

View file

@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\block_content\Functional\Hal;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\block_content\Functional\Rest\BlockContentResourceTestBase;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group hal
*/
class BlockContentHalJsonAnonTest extends BlockContentResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/block/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/block_content/basic',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/block_content/basic',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// The 'url.site' cache context is added for '_links' in the response.
return Cache::mergeTags(parent::getExpectedCacheContexts(), ['url.site']);
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\block_content\Functional\Hal;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class BlockContentHalJsonBasicAuthTest extends BlockContentHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\block_content\Functional\Hal;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class BlockContentHalJsonCookieTest extends BlockContentHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\block_content\Functional\Hal;
use Drupal\Tests\block_content\Functional\Rest\BlockContentTypeResourceTestBase;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group hal
*/
class BlockContentTypeHalJsonAnonTest extends BlockContentTypeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\block_content\Functional\Hal;
use Drupal\Tests\block_content\Functional\Rest\BlockContentTypeResourceTestBase;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class BlockContentTypeHalJsonBasicAuthTest extends BlockContentTypeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\block_content\Functional\Hal;
use Drupal\Tests\block_content\Functional\Rest\BlockContentTypeResourceTestBase;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class BlockContentTypeHalJsonCookieTest extends BlockContentTypeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -3,7 +3,6 @@
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Component\Utility\Unicode;
/**
* Create a block and test block edit functionality.
@ -28,7 +27,7 @@ class PageEditTest extends BlockContentTestBase {
$body_key = 'body[0][value]';
// Create block to edit.
$edit = [];
$edit['info[0][value]'] = Unicode::strtolower($this->randomMachineName(8));
$edit['info[0][value]'] = mb_strtolower($this->randomMachineName(8));
$edit[$body_key] = $this->randomMachineName(16);
$this->drupalPostForm('block/add/basic', $edit, t('Save'));

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class BlockContentJsonAnonTest extends BlockContentResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class BlockContentJsonBasicAuthTest extends BlockContentResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class BlockContentJsonCookieTest extends BlockContentResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,203 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
/**
* ResourceTestBase for BlockContent entity.
*/
abstract class BlockContentResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['block_content'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'block_content';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed' => NULL,
];
/**
* @var \Drupal\block_content\BlockContentInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer blocks']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
if (!BlockContentType::load('basic')) {
$block_content_type = BlockContentType::create([
'id' => 'basic',
'label' => 'basic',
'revision' => TRUE,
]);
$block_content_type->save();
block_content_add_body_field($block_content_type->id());
}
// Create a "Llama" custom block.
$block_content = BlockContent::create([
'info' => 'Llama',
'type' => 'basic',
'body' => [
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
'format' => 'plain_text',
],
])
->setUnpublished();
$block_content->save();
return $block_content;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'id' => [
[
'value' => 1,
],
],
'uuid' => [
[
'value' => $this->entity->uuid(),
],
],
'langcode' => [
[
'value' => 'en',
],
],
'reusable' => [
[
'value' => TRUE,
],
],
'type' => [
[
'target_id' => 'basic',
'target_type' => 'block_content_type',
'target_uuid' => BlockContentType::load('basic')->uuid(),
],
],
'info' => [
[
'value' => 'Llama',
],
],
'revision_log' => [],
'changed' => [
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'revision_id' => [
[
'value' => 1,
],
],
'revision_created' => [
$this->formatExpectedTimestampItemValues((int) $this->entity->getRevisionCreationTime()),
],
'revision_user' => [],
'revision_translation_affected' => [
[
'value' => TRUE,
],
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'body' => [
[
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
'format' => 'plain_text',
'summary' => NULL,
'processed' => "<p>The name &quot;llama&quot; was adopted by European settlers from native Peruvians.</p>\n",
],
],
'status' => [
[
'value' => FALSE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'type' => [
[
'target_id' => 'basic',
],
],
'info' => [
[
'value' => 'Dramallama',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
return parent::getExpectedUnauthorizedAccessMessage($method);
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessCacheability() {
// @see \Drupal\block_content\BlockContentAccessControlHandler()
return parent::getExpectedUnauthorizedAccessCacheability()
->addCacheTags(['block_content:1']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class BlockContentTypeJsonAnonTest extends BlockContentTypeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class BlockContentTypeJsonBasicAuthTest extends BlockContentTypeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class BlockContentTypeJsonCookieTest extends BlockContentTypeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\block_content\Entity\BlockContentType;
abstract class BlockContentTypeResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block_content'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'block_content_type';
/**
* @var \Drupal\block_content\Entity\BlockContentTypeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer blocks']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$block_content_type = BlockContentType::create([
'id' => 'pascal',
'label' => 'Pascal',
'revision' => FALSE,
'description' => 'Provides a competitive alternative to the "basic" type',
]);
$block_content_type->save();
return $block_content_type;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'description' => 'Provides a competitive alternative to the "basic" type',
'id' => 'pascal',
'label' => 'Pascal',
'langcode' => 'en',
'revision' => 0,
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockContentTypeXmlAnonTest extends BlockContentTypeResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockContentTypeXmlBasicAuthTest extends BlockContentTypeResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockContentTypeXmlCookieTest extends BlockContentTypeResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,26 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockContentXmlAnonTest extends BlockContentResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockContentXmlBasicAuthTest extends BlockContentResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\block_content\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockContentXmlCookieTest extends BlockContentResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,47 @@
<?php
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\simpletest\BlockCreationTrait;
use Drupal\Tests\BrowserTestBase;
/**
* Tests unpublishing of block_content entities.
*
* @group block_content
*/
class UnpublishedBlockTest extends BrowserTestBase {
use BlockCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['block_content'];
/**
* Tests unpublishing of block_content entities.
*/
public function testViewShowsCorrectStates() {
$block_content = BlockContent::create([
'info' => 'Test block',
'type' => 'basic',
]);
$block_content->save();
$this->placeBlock('block_content:' . $block_content->uuid());
$this->drupalGet('<front>');
$page = $this->getSession()->getPage();
$this->assertTrue($page->has('css', '.block-block-content' . $block_content->uuid()));
$block_content->setUnpublished();
$block_content->save();
$this->drupalGet('<front>');
$page = $this->getSession()->getPage();
$this->assertFalse($page->has('css', '.block-block-content' . $block_content->uuid()));
}
}

View file

@ -0,0 +1,153 @@
<?php
namespace Drupal\Tests\block_content\Functional\Update;
use Drupal\block_content\Entity\BlockContent;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests 'reusable' field related update functions for the Block Content module.
*
* @group Update
* @group block_content
* @group legacy
*/
class BlockContentReusableUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
// Override the 'block_content' view with an extra display with overridden
// filters. This extra display should also have a filter added for
// 'reusable' field so that it does not expose non-reusable fields. This
// display also has a filter that only shows blocks that contain 'block2'
// in the 'info' field.
__DIR__ . '/../../../fixtures/update/drupal-8.views_block_content-2976334.php',
];
}
/**
* Tests adding 'reusable' entity base field to the block content entity type.
*
* @see block_content_update_8600()
* @see block_content_post_update_add_views_reusable_filter()
*/
public function testReusableFieldAddition() {
$assert_session = $this->assertSession();
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
// Ensure that 'reusable' field is not present before updates.
$this->assertEmpty($entity_definition_update_manager->getFieldStorageDefinition('reusable', 'block_content'));
// Ensure that 'reusable' filter is not present before updates.
$view_config = \Drupal::configFactory()->get('views.view.block_content');
$this->assertFalse($view_config->isNew());
$this->assertEmpty($view_config->get('display.default.display_options.filters.reusable'));
$this->assertEmpty($view_config->get('display.page_2.display_options.filters.reusable'));
// Run updates.
$this->runUpdates();
// Ensure that 'reusable' filter is present after updates.
\Drupal::configFactory()->clearStaticCache();
$view_config = \Drupal::configFactory()->get('views.view.block_content');
$this->assertNotEmpty($view_config->get('display.default.display_options.filters.reusable'));
$this->assertNotEmpty($view_config->get('display.page_2.display_options.filters.reusable'));
// Check that the field exists and is configured correctly.
$reusable_field = $entity_definition_update_manager->getFieldStorageDefinition('reusable', 'block_content');
$this->assertEquals('Reusable', $reusable_field->getLabel());
$this->assertEquals('A boolean indicating whether this block is reusable.', $reusable_field->getDescription());
$this->assertEquals(FALSE, $reusable_field->isRevisionable());
$this->assertEquals(FALSE, $reusable_field->isTranslatable());
$after_block1 = BlockContent::create([
'info' => 'After update block1',
'type' => 'basic_block',
]);
$after_block1->save();
// Add second block that will be shown with the 'info' filter on the
// additional view display.
$after_block2 = BlockContent::create([
'info' => 'After update block2',
'type' => 'basic_block',
]);
$after_block2->save();
$this->assertTrue($after_block1->isReusable());
$this->assertTrue($after_block2->isReusable());
$admin_user = $this->drupalCreateUser(['administer blocks']);
$this->drupalLogin($admin_user);
$block_non_reusable = BlockContent::create([
'info' => 'block1 non reusable',
'type' => 'basic_block',
'reusable' => FALSE,
]);
$block_non_reusable->save();
// Add second block that would be shown with the 'info' filter on the
// additional view display if the 'reusable' filter was not added.
$block2_non_reusable = BlockContent::create([
'info' => 'block2 non reusable',
'type' => 'basic_block',
'reusable' => FALSE,
]);
$block2_non_reusable->save();
$this->assertFalse($block_non_reusable->isReusable());
$this->assertFalse($block2_non_reusable->isReusable());
// Ensure the Custom Block view shows the reusable blocks only.
$this->drupalGet('admin/structure/block/block-content');
$assert_session->statusCodeEquals('200');
$assert_session->responseContains('view-id-block_content');
$assert_session->pageTextContains($after_block1->label());
$assert_session->pageTextContains($after_block2->label());
$assert_session->pageTextNotContains($block_non_reusable->label());
$assert_session->pageTextNotContains($block2_non_reusable->label());
// Ensure the view's other display also only shows reusable blocks and still
// filters on the 'info' field.
$this->drupalGet('extra-view-display');
$assert_session->statusCodeEquals('200');
$assert_session->responseContains('view-id-block_content');
$assert_session->pageTextNotContains($after_block1->label());
$assert_session->pageTextContains($after_block2->label());
$assert_session->pageTextNotContains($block_non_reusable->label());
$assert_session->pageTextNotContains($block2_non_reusable->label());
// Ensure the Custom Block listing without Views installed shows the only
// reusable blocks.
$this->drupalGet('admin/structure/block/block-content');
$this->container->get('module_installer')->uninstall(['views_ui', 'views']);
$this->drupalGet('admin/structure/block/block-content');
$assert_session->statusCodeEquals('200');
$assert_session->responseNotContains('view-id-block_content');
$assert_session->pageTextContains($after_block1->label());
$assert_session->pageTextContains($after_block2->label());
$assert_session->pageTextNotContains($block_non_reusable->label());
$assert_session->pageTextNotContains($block2_non_reusable->label());
$this->drupalGet('block/' . $after_block1->id());
$assert_session->statusCodeEquals('200');
// Ensure the non-reusable block is not accessible in the form.
$this->drupalGet('block/' . $block_non_reusable->id());
$assert_session->statusCodeEquals('403');
$this->drupalLogout();
$this->drupalLogin($this->createUser([
'access user profiles',
'administer blocks',
]));
$this->drupalGet('block/' . $after_block1->id());
$assert_session->statusCodeEquals('200');
$this->drupalGet('block/' . $block_non_reusable->id());
$assert_session->statusCodeEquals('403');
}
}

View file

@ -1,14 +1,15 @@
<?php
namespace Drupal\block_content\Tests;
namespace Drupal\Tests\block_content\Functional\Update;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\system\Tests\Update\UpdatePathTestBase;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests update functions for the Block Content module.
*
* @group Update
* @group legacy
*/
class BlockContentUpdateTest extends UpdatePathTestBase {
@ -17,7 +18,7 @@ class BlockContentUpdateTest extends UpdatePathTestBase {
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
}
@ -43,4 +44,27 @@ class BlockContentUpdateTest extends UpdatePathTestBase {
$this->assertEqual('block_content_field_revision', $entity_type->getRevisionDataTable());
}
/**
* Tests adding a status field to the block content entity type.
*
* @see block_content_update_8400()
*/
public function testStatusFieldAddition() {
$schema = \Drupal::database()->schema();
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
// Run updates.
$this->runUpdates();
// Check that the field exists and has the correct label.
$updated_field = $entity_definition_update_manager->getFieldStorageDefinition('status', 'block_content');
$this->assertEqual('Publishing status', $updated_field->getLabel());
$content_translation_status = $entity_definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content');
$this->assertNull($content_translation_status);
$this->assertFalse($schema->fieldExists('block_content_field_revision', 'content_translation_status'));
$this->assertFalse($schema->fieldExists('block_content_field_data', 'content_translation_status'));
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Drupal\block_content\Tests\Views;
namespace Drupal\Tests\block_content\Functional\Views;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
@ -31,12 +31,11 @@ class BlockContentFieldFilterTest extends BlockContentTestBase {
*/
public $blockContentInfos = [];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
public function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
// Add two new languages.
ConfigurableLanguage::createFromLangcode('fr')->save();

View file

@ -1,6 +1,6 @@
<?php
namespace Drupal\block_content\Tests\Views;
namespace Drupal\Tests\block_content\Functional\Views;
/**
* Tests the block_content integration into views.
@ -59,7 +59,7 @@ class BlockContentIntegrationTest extends BlockContentTestBase {
$result = $this->xpath('//span[@class="field-content"]');
$ids = [];
foreach ($result as $element) {
$ids[] = (int) $element;
$ids[] = $element->getText();
}
$this->assertEqual($ids, $expected_ids);
}

View file

@ -1,6 +1,6 @@
<?php
namespace Drupal\block_content\Tests\Views;
namespace Drupal\Tests\block_content\Functional\Views;
/**
* Tests the redirect destination on block content on entity operations.

View file

@ -0,0 +1,107 @@
<?php
namespace Drupal\Tests\block_content\Functional\Views;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Base class for all block_content tests.
*/
abstract class BlockContentTestBase extends ViewTestBase {
/**
* Admin user
*
* @var object
*/
protected $adminUser;
/**
* Permissions to grant admin user.
*
* @var array
*/
protected $permissions = [
'administer blocks',
];
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'block_content', 'block_content_test_views'];
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
// Ensure the basic bundle exists. This is provided by the standard profile.
$this->createBlockContentType(['id' => 'basic']);
$this->adminUser = $this->drupalCreateUser($this->permissions);
if ($import_test_views) {
ViewTestData::createTestViews(get_class($this), ['block_content_test_views']);
}
}
/**
* Creates a custom block.
*
* @param array $settings
* (optional) An associative array of settings for the block_content, as
* used in entity_create().
*
* @return \Drupal\block_content\Entity\BlockContent
* Created custom block.
*/
protected function createBlockContent(array $settings = []) {
$status = 0;
$settings += [
'info' => $this->randomMachineName(),
'type' => 'basic',
'langcode' => 'en',
];
if ($block_content = BlockContent::create($settings)) {
$status = $block_content->save();
}
$this->assertEqual($status, SAVED_NEW, new FormattableMarkup('Created block content %info.', ['%info' => $block_content->label()]));
return $block_content;
}
/**
* Creates a custom block type (bundle).
*
* @param array $values
* An array of settings to change from the defaults.
*
* @return \Drupal\block_content\Entity\BlockContentType
* Created custom block type.
*/
protected function createBlockContentType(array $values = []) {
// Find a non-existent random type name.
if (!isset($values['id'])) {
do {
$id = strtolower($this->randomMachineName(8));
} while (BlockContentType::load($id));
}
else {
$id = $values['id'];
}
$values += [
'id' => $id,
'label' => $id,
'revision' => FALSE,
];
$bundle = BlockContentType::create($values);
$status = $bundle->save();
block_content_add_body_field($bundle->id());
$this->assertEqual($status, SAVED_NEW, new FormattableMarkup('Created block content type %bundle.', ['%bundle' => $bundle->id()]));
return $bundle;
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Drupal\Tests\block_content\Functional\Views;
use Drupal\Tests\block_content\Functional\BlockContentTestBase;
/**
* Tests block_content wizard and generic entity integration.
*
* @group block_content
*/
class BlockContentWizardTest extends BlockContentTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block_content', 'views_ui'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['administer views']));
$this->createBlockContentType('Basic block');
}
/**
* Tests creating a 'block_content' entity view.
*/
public function testViewAddBlockContent() {
$view = [];
$view['label'] = $this->randomMachineName(16);
$view['id'] = strtolower($this->randomMachineName(16));
$view['description'] = $this->randomMachineName(16);
$view['page[create]'] = FALSE;
$view['show[wizard_key]'] = 'block_content';
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
$view_storage_controller = $this->container->get('entity_type.manager')->getStorage('view');
/** @var \Drupal\views\Entity\View $view */
$view = $view_storage_controller->load($view['id']);
$display_options = $view->getDisplay('default')['display_options'];
$this->assertEquals('block_content', $display_options['filters']['reusable']['entity_type']);
$this->assertEquals('reusable', $display_options['filters']['reusable']['entity_field']);
$this->assertEquals('boolean', $display_options['filters']['reusable']['plugin_id']);
$this->assertEquals('1', $display_options['filters']['reusable']['value']);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Drupal\block_content\Tests\Views;
namespace Drupal\Tests\block_content\Functional\Views;
use Drupal\views\Views;

View file

@ -1,10 +1,10 @@
<?php
namespace Drupal\block_content\Tests\Views;
namespace Drupal\Tests\block_content\Functional\Views;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\block_content\Entity\BlockContent;
use Drupal\views\Tests\ViewTestBase;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestData;
@ -29,8 +29,8 @@ class RevisionRelationshipsTest extends ViewTestBase {
*/
public static $testViews = ['test_block_content_revision_id', 'test_block_content_revision_revision_id'];
protected function setUp() {
parent::setUp();
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
BlockContentType::create([
'id' => 'basic',
'label' => 'basic',

View file

@ -0,0 +1,300 @@
<?php
namespace Drupal\Tests\block_content\Kernel;
use Drupal\block_content\BlockContentAccessControlHandler;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
/**
* Tests the block content entity access handler.
*
* @coversDefaultClass \Drupal\block_content\BlockContentAccessControlHandler
*
* @group block_content
*/
class BlockContentAccessHandlerTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'block_content',
'system',
'user',
];
/**
* The BlockContent access controller to test.
*
* @var \Drupal\block_content\BlockContentAccessControlHandler
*/
protected $accessControlHandler;
/**
* The BlockContent entity used for testing.
*
* @var \Drupal\block_content\Entity\BlockContent
*/
protected $blockEntity;
/**
* The test role.
*
* @var \Drupal\user\RoleInterface
*/
protected $role;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['sequence']);
$this->installSchema('system', ['sequences']);
$this->installSchema('user', ['users_data']);
$this->installEntitySchema('user');
$this->installEntitySchema('block_content');
// Create a block content type.
$block_content_type = BlockContentType::create([
'id' => 'square',
'label' => 'A square block type',
'description' => "Provides a block type that is square.",
]);
$block_content_type->save();
$this->blockEntity = BlockContent::create([
'info' => 'The Block',
'type' => 'square',
]);
$this->blockEntity->save();
// Create user 1 test does not have all permissions.
User::create([
'name' => 'admin',
])->save();
$this->role = Role::create([
'id' => 'roly',
'label' => 'roly poly',
]);
$this->role->save();
$this->accessControlHandler = new BlockContentAccessControlHandler(\Drupal::entityTypeManager()->getDefinition('block_content'), \Drupal::service('event_dispatcher'));
}
/**
* @covers ::checkAccess
*
* @dataProvider providerTestAccess
*/
public function testAccess($operation, $published, $reusable, $permissions, $parent_access, $expected_access) {
$published ? $this->blockEntity->setPublished() : $this->blockEntity->setUnpublished();
$reusable ? $this->blockEntity->setReusable() : $this->blockEntity->setNonReusable();
$user = User::create([
'name' => 'Someone',
'mail' => 'hi@example.com',
]);
if ($permissions) {
foreach ($permissions as $permission) {
$this->role->grantPermission($permission);
}
$this->role->save();
}
$user->addRole($this->role->id());
$user->save();
if ($parent_access) {
$parent_entity = $this->prophesize(AccessibleInterface::class);
$expected_parent_result = NULL;
switch ($parent_access) {
case 'allowed':
$expected_parent_result = AccessResult::allowed();
break;
case 'neutral':
$expected_parent_result = AccessResult::neutral();
break;
case 'forbidden':
$expected_parent_result = AccessResult::forbidden();
break;
}
$parent_entity->access($operation, $user, TRUE)
->willReturn($expected_parent_result)
->shouldBeCalled();
$this->blockEntity->setAccessDependency($parent_entity->reveal());
}
$this->blockEntity->save();
$result = $this->accessControlHandler->access($this->blockEntity, $operation, $user, TRUE);
switch ($expected_access) {
case 'allowed':
$this->assertTrue($result->isAllowed());
break;
case 'forbidden':
$this->assertTrue($result->isForbidden());
break;
case 'neutral':
$this->assertTrue($result->isNeutral());
break;
default:
$this->fail('Unexpected access type');
}
}
/**
* Dataprovider for testAccess().
*/
public function providerTestAccess() {
$cases = [
'view:published:reusable' => [
'view',
TRUE,
TRUE,
[],
NULL,
'allowed',
],
'view:unpublished:reusable' => [
'view',
FALSE,
TRUE,
[],
NULL,
'neutral',
],
'view:unpublished:reusable:admin' => [
'view',
FALSE,
TRUE,
['administer blocks'],
NULL,
'allowed',
],
'view:published:reusable:admin' => [
'view',
TRUE,
TRUE,
['administer blocks'],
NULL,
'allowed',
],
'view:published:non_reusable' => [
'view',
TRUE,
FALSE,
[],
NULL,
'forbidden',
],
'view:published:non_reusable:parent_allowed' => [
'view',
TRUE,
FALSE,
[],
'allowed',
'allowed',
],
'view:published:non_reusable:parent_neutral' => [
'view',
TRUE,
FALSE,
[],
'neutral',
'neutral',
],
'view:published:non_reusable:parent_forbidden' => [
'view',
TRUE,
FALSE,
[],
'forbidden',
'forbidden',
],
];
foreach (['update', 'delete'] as $operation) {
$cases += [
$operation . ':published:reusable' => [
$operation,
TRUE,
TRUE,
[],
NULL,
'neutral',
],
$operation . ':unpublished:reusable' => [
$operation,
FALSE,
TRUE,
[],
NULL,
'neutral',
],
$operation . ':unpublished:reusable:admin' => [
$operation,
FALSE,
TRUE,
['administer blocks'],
NULL,
'allowed',
],
$operation . ':published:reusable:admin' => [
$operation,
TRUE,
TRUE,
['administer blocks'],
NULL,
'allowed',
],
$operation . ':published:non_reusable' => [
$operation,
TRUE,
FALSE,
[],
NULL,
'forbidden',
],
$operation . ':published:non_reusable:parent_allowed' => [
$operation,
TRUE,
FALSE,
[],
'allowed',
'neutral',
],
$operation . ':published:non_reusable:parent_neutral' => [
$operation,
TRUE,
FALSE,
[],
'neutral',
'neutral',
],
$operation . ':published:non_reusable:parent_forbidden' => [
$operation,
TRUE,
FALSE,
[],
'forbidden',
'forbidden',
],
];
return $cases;
}
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Drupal\Tests\block_content\Kernel;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Component\Plugin\PluginBase;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests block content plugin deriver.
*
* @group block_content
*/
class BlockContentDeriverTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block', 'block_content', 'system', 'user'];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->installSchema('system', ['sequence']);
$this->installEntitySchema('user');
$this->installEntitySchema('block_content');
}
/**
* Tests that only reusable blocks are derived.
*/
public function testReusableBlocksOnlyAreDerived() {
// Create a block content type.
$block_content_type = BlockContentType::create([
'id' => 'spiffy',
'label' => 'Mucho spiffy',
'description' => "Provides a block type that increases your site's spiffiness by up to 11%",
]);
$block_content_type->save();
// And a block content entity.
$block_content = BlockContent::create([
'info' => 'Spiffy prototype',
'type' => 'spiffy',
]);
$block_content->save();
// Ensure the reusable block content is provided as a derivative block
// plugin.
/** @var \Drupal\Core\Block\BlockManagerInterface $block_manager */
$block_manager = $this->container->get('plugin.manager.block');
$plugin_id = 'block_content' . PluginBase::DERIVATIVE_SEPARATOR . $block_content->uuid();
$this->assertTrue($block_manager->hasDefinition($plugin_id));
// Set the block not to be reusable.
$block_content->setNonReusable();
$block_content->save();
// Ensure the non-reusable block content is not provided a derivative block
// plugin.
$this->assertFalse($block_manager->hasDefinition($plugin_id));
}
}

View file

@ -0,0 +1,189 @@
<?php
namespace Drupal\Tests\block_content\Kernel;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\block_content_test\Plugin\EntityReferenceSelection\TestSelection;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests EntityReference selection handlers don't return non-reusable blocks.
*
* @see block_content_query_entity_reference_alter()
*
* @group block_content
*/
class BlockContentEntityReferenceSelectionTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'block_content',
'block_content_test',
'system',
'user',
];
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Test reusable block.
*
* @var \Drupal\block_content\BlockContentInterface
*/
protected $blockReusable;
/**
* Test non-reusable block.
*
* @var \Drupal\block_content\BlockContentInterface
*/
protected $blockNonReusable;
/**
* Test selection handler.
*
* @var \Drupal\block_content_test\Plugin\EntityReferenceSelection\TestSelection
*/
protected $selectionHandler;
/**
* Test block expectations.
*
* @var array
*/
protected $expectations;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->installSchema('system', ['sequence']);
$this->installSchema('system', ['sequences']);
$this->installEntitySchema('user');
$this->installEntitySchema('block_content');
// Create a block content type.
$block_content_type = BlockContentType::create([
'id' => 'spiffy',
'label' => 'Mucho spiffy',
'description' => "Provides a block type that increases your site's spiffiness by up to 11%",
]);
$block_content_type->save();
$this->entityTypeManager = $this->container->get('entity_type.manager');
// And reusable block content entities.
$this->blockReusable = BlockContent::create([
'info' => 'Reusable Block',
'type' => 'spiffy',
]);
$this->blockReusable->save();
$this->blockNonReusable = BlockContent::create([
'info' => 'Non-reusable Block',
'type' => 'spiffy',
'reusable' => FALSE,
]);
$this->blockNonReusable->save();
$configuration = [
'target_type' => 'block_content',
'target_bundles' => ['spiffy' => 'spiffy'],
'sort' => ['field' => '_none'],
];
$this->selectionHandler = new TestSelection($configuration, '', '', $this->container->get('entity.manager'), $this->container->get('module_handler'), \Drupal::currentUser());
// Setup the 3 expectation cases.
$this->expectations = [
'both_blocks' => [
'spiffy' => [
$this->blockReusable->id() => $this->blockReusable->label(),
$this->blockNonReusable->id() => $this->blockNonReusable->label(),
],
],
'block_reusable' => ['spiffy' => [$this->blockReusable->id() => $this->blockReusable->label()]],
'block_non_reusable' => ['spiffy' => [$this->blockNonReusable->id() => $this->blockNonReusable->label()]],
];
}
/**
* Tests to make sure queries without the expected tags are not altered.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function testQueriesNotAltered() {
// Ensure that queries without all the tags are not altered.
$query = $this->entityTypeManager->getStorage('block_content')->getQuery();
$this->assertCount(2, $query->execute());
$query = $this->entityTypeManager->getStorage('block_content')->getQuery();
$query->addTag('block_content_access');
$this->assertCount(2, $query->execute());
$query = $this->entityTypeManager->getStorage('block_content')->getQuery();
$query->addTag('entity_query_block_content');
$this->assertCount(2, $query->execute());
}
/**
* Test with no conditions set.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function testNoConditions() {
$this->assertEquals(
$this->expectations['block_reusable'],
$this->selectionHandler->getReferenceableEntities()
);
$this->blockNonReusable->setReusable();
$this->blockNonReusable->save();
// Ensure that the block is now returned as a referenceable entity.
$this->assertEquals(
$this->expectations['both_blocks'],
$this->selectionHandler->getReferenceableEntities()
);
}
/**
* Tests setting 'reusable' condition on different levels.
*
* @dataProvider fieldConditionProvider
*
* @throws \Exception
*/
public function testFieldConditions($condition_type, $is_reusable) {
$this->selectionHandler->setTestMode($condition_type, $is_reusable);
$this->assertEquals(
$is_reusable ? $this->expectations['block_reusable'] : $this->expectations['block_non_reusable'],
$this->selectionHandler->getReferenceableEntities()
);
}
/**
* Provides possible fields and condition types.
*/
public function fieldConditionProvider() {
$cases = [];
foreach (['base', 'group', 'nested_group'] as $condition_type) {
foreach ([TRUE, FALSE] as $reusable) {
$cases["$condition_type:" . ($reusable ? 'reusable' : 'non-reusable')] = [
$condition_type,
$reusable,
];
}
}
return $cases;
}
}

View file

@ -35,7 +35,7 @@ class MigrateBlockContentEntityFormDisplayTest extends MigrateDrupal7TestBase {
*
* @param string $id
* The entity ID.
* @param string $component
* @param string $component_id
* The ID of the form component.
*/
protected function assertDisplay($id, $component_id) {

View file

@ -37,7 +37,7 @@ class MigrateBlockContentTest extends MigrateDrupal6TestBase {
* Tests the Drupal 6 custom block to Drupal 8 migration.
*/
public function testBlockMigration() {
/** @var BlockContent $block */
/** @var \Drupal\block_content\Entity\BlockContent $block */
$block = BlockContent::load(1);
$this->assertIdentical('My block 1', $block->label());
$this->assertTrue(REQUEST_TIME <= $block->getChangedTime() && $block->getChangedTime() <= time());

View file

@ -0,0 +1,73 @@
<?php
namespace Drupal\Tests\block_content\Kernel\Migrate\d6;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests migration of i18n custom block strings.
*
* @group migrate_drupal_6
*/
class MigrateCustomBlockContentTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block_content',
'content_translation',
'language',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['block_content']);
$this->installEntitySchema('block_content');
$this->executeMigrations([
'language',
'd6_filter_format',
'block_content_type',
'block_content_body_field',
'd6_custom_block',
'd6_custom_block_translation',
]);
}
/**
* Tests the Drupal 6 i18n custom block strings to Drupal 8 migration.
*/
public function testCustomBlockContentTranslation() {
/** @var \Drupal\block_content\Entity\BlockContent $block */
$block = BlockContent::load(1)->getTranslation('fr');
$this->assertSame('fr - Static Block', $block->label());
$this->assertGreaterThanOrEqual(REQUEST_TIME, $block->getChangedTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('fr', $block->language()->getId());
$this->assertSame('<h3>fr - My first custom block body</h3>', $block->body->value);
$this->assertSame('full_html', $block->body->format);
$block = $block->getTranslation('zu');
$this->assertSame('My block 1', $block->label());
$this->assertGreaterThanOrEqual(REQUEST_TIME, $block->getChangedTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('zu', $block->language()->getId());
$this->assertSame('<h3>zu - My first custom block body</h3>', $block->body->value);
$this->assertSame('full_html', $block->body->format);
$block = BlockContent::load(2)->getTranslation('fr');
$this->assertSame('Encore un bloc statique', $block->label());
$this->assertGreaterThanOrEqual(REQUEST_TIME, $block->getChangedTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('fr', $block->language()->getId());
$this->assertSame('Nom de vocabulaire beaucoup plus long que trente-deux caractères', $block->body->value);
$this->assertSame('full_html', $block->body->format);
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\block_content\Kernel\Migrate\d7;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of i18n custom block strings.
*
* @group migrate_drupal_7
*/
class MigrateCustomBlockContentTranslationTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block_content',
'content_translation',
'filter',
'language',
'text',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['block_content']);
$this->installEntitySchema('block_content');
$this->executeMigrations([
'language',
'd7_filter_format',
'block_content_type',
'block_content_body_field',
'd7_custom_block',
'd7_custom_block_translation',
]);
}
/**
* Tests the Drupal 7 i18n custom block strings to Drupal 8 migration.
*/
public function testCustomBlockContentTranslation() {
/** @var \Drupal\block_content\Entity\BlockContent $block */
$block = BlockContent::load(1)->getTranslation('fr');
$this->assertSame('fr - Mildly amusing limerick of the day', $block->label());
$this->assertGreaterThanOrEqual($block->getChangedTime(), \Drupal::time()->getRequestTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('fr', $block->language()->getId());
$translation = "fr - A fellow jumped off a high wall\r\nAnd had a most terrible fall\r\nHe went back to bed\r\nWith a bump on his head\r\nThat's why you don't jump off a wall";
$this->assertSame($translation, $block->body->value);
$this->assertSame('filtered_html', $block->body->format);
$block = $block->getTranslation('is');
$this->assertSame('is - Mildly amusing limerick of the day', $block->label());
$this->assertGreaterThanOrEqual($block->getChangedTime(), \Drupal::time()->getRequestTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('is', $block->language()->getId());
$text = "A fellow jumped off a high wall\r\nAnd had a most terrible fall\r\nHe went back to bed\r\nWith a bump on his head\r\nThat's why you don't jump off a wall";
$this->assertSame($text, $block->body->value);
$this->assertSame('filtered_html', $block->body->format);
}
}

View file

@ -0,0 +1,139 @@
<?php
namespace Drupal\Tests\block_content\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests i18n custom block translations source plugin.
*
* @covers \Drupal\block_content\Plugin\migrate\source\d6\BoxTranslation
*
* @group content_translation
*/
class BoxTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block_content', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['database']['boxes'] = [
[
'bid' => 1,
'body' => 'box 1 body',
'info' => 'box 1 title',
'format' => '2',
],
[
'bid' => 2,
'body' => 'box 2 body',
'info' => 'box 2 title',
'format' => '2',
],
];
$tests[0]['database']['i18n_strings'] = [
[
'lid' => 1,
'objectid' => 1,
'type' => 'block',
'property' => 'title',
'objectindex' => 1,
'format' => 0,
],
[
'lid' => 2,
'objectid' => 1,
'type' => 'block',
'property' => 'body',
'objectindex' => 1,
'format' => 0,
],
[
'lid' => 3,
'objectid' => 2,
'type' => 'block',
'property' => 'body',
'objectindex' => 2,
'format' => 2,
],
];
$tests[0]['database']['locales_target'] = [
[
'lid' => 1,
'language' => 'fr',
'translation' => 'fr - title translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 2,
'language' => 'fr',
'translation' => 'fr - body translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 3,
'language' => 'zu',
'translation' => 'zu - body translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
$tests[0]['expected_results'] = [
[
'lid' => '1',
'property' => 'title',
'language' => 'fr',
'translation' => 'fr - title translation',
'bid' => '1',
'format' => '2',
'title_translated' => 'fr - title translation',
'body_translated' => 'fr - body translation',
'title' => 'box 1 title',
'body' => 'box 1 body',
],
[
'lid' => '2',
'property' => 'body',
'language' => 'fr',
'translation' => 'fr - body translation',
'bid' => '1',
'format' => '2',
'title_translated' => 'fr - title translation',
'body_translated' => 'fr - body translation',
'title' => 'box 1 title',
'body' => 'box 1 body',
],
[
'lid' => '3',
'property' => 'body',
'language' => 'zu',
'translation' => 'zu - body translation',
'bid' => '2',
'format' => '2',
'title_translated' => NULL,
'body_translated' => 'zu - body translation',
'title' => 'box 2 title',
'body' => 'box 2 body',
],
];
return $tests;
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace Drupal\Tests\block_content\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests i18n custom block translations source plugin.
*
* @covers \Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation
*
* @group content_translation
*/
class BlockCustomTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block_content', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['database']['block_custom'] = [
[
'bid' => 1,
'body' => 'box 1 body',
'info' => 'box 1 title',
'format' => '2',
],
[
'bid' => 2,
'body' => 'box 2 body',
'info' => 'box 2 title',
'format' => '2',
],
];
$tests[0]['database']['i18n_string'] = [
[
'lid' => 1,
'objectid' => 1,
'type' => 'block',
'property' => 'title',
'objectindex' => 1,
'format' => 0,
],
[
'lid' => 2,
'objectid' => 1,
'type' => 'block',
'property' => 'body',
'objectindex' => 1,
'format' => 0,
],
[
'lid' => 3,
'objectid' => 2,
'type' => 'block',
'property' => 'body',
'objectindex' => 2,
'format' => 2,
],
];
$tests[0]['database']['locales_target'] = [
[
'lid' => 1,
'language' => 'fr',
'translation' => 'fr - title translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 2,
'language' => 'fr',
'translation' => 'fr - body translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 3,
'language' => 'zu',
'translation' => 'zu - body translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
$tests[0]['database']['system'] = [
[
'type' => 'module',
'name' => 'system',
'schema_version' => '7001',
'status' => '1',
],
];
$tests[0]['expected_results'] = [
[
'lid' => '1',
'property' => 'title',
'language' => 'fr',
'translation' => 'fr - title translation',
'bid' => '1',
'format' => '2',
'title_translated' => 'fr - title translation',
'body_translated' => 'fr - body translation',
'title' => 'box 1 title',
'body' => 'box 1 body',
],
[
'lid' => '2',
'property' => 'body',
'language' => 'fr',
'translation' => 'fr - body translation',
'bid' => '1',
'format' => '2',
'title_translated' => 'fr - title translation',
'body_translated' => 'fr - body translation',
'title' => 'box 1 title',
'body' => 'box 1 body',
],
[
'lid' => '3',
'property' => 'body',
'language' => 'zu',
'translation' => 'zu - body translation',
'bid' => '2',
'format' => '2',
'title_translated' => NULL,
'body_translated' => 'zu - body translation',
'title' => 'box 2 title',
'body' => 'box 2 body',
],
];
return $tests;
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Drupal\Tests\block_content\Unit\Access;
use Drupal\block_content\Access\AccessGroupAnd;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\Tests\UnitTestCase;
/**
* Tests accessible groups.
*
* @group block_content
*/
class AccessGroupAndTest extends UnitTestCase {
use AccessibleTestingTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->account = $this->prophesize(AccountInterface::class)->reveal();
}
/**
* @covers \Drupal\block_content\Access\AccessGroupAnd
*/
public function testGroups() {
$allowedAccessible = $this->createAccessibleDouble(AccessResult::allowed());
$forbiddenAccessible = $this->createAccessibleDouble(AccessResult::forbidden());
$neutralAccessible = $this->createAccessibleDouble(AccessResult::neutral());
// Ensure that groups with no dependencies return a neutral access result.
$this->assertTrue((new AccessGroupAnd())->access('view', $this->account, TRUE)->isNeutral());
$andNeutral = new AccessGroupAnd();
$andNeutral->addDependency($allowedAccessible)->addDependency($neutralAccessible);
$this->assertTrue($andNeutral->access('view', $this->account, TRUE)->isNeutral());
$andForbidden = $andNeutral;
$andForbidden->addDependency($forbiddenAccessible);
$this->assertTrue($andForbidden->access('view', $this->account, TRUE)->isForbidden());
// Ensure that groups added to other groups works.
$andGroupsForbidden = new AccessGroupAnd();
$andGroupsForbidden->addDependency($andNeutral)->addDependency($andForbidden);
$this->assertTrue($andGroupsForbidden->access('view', $this->account, TRUE)->isForbidden());
// Ensure you can add a non-group accessible object.
$andGroupsForbidden->addDependency($allowedAccessible);
$this->assertTrue($andGroupsForbidden->access('view', $this->account, TRUE)->isForbidden());
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\block_content\Unit\Access;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Access\AccessResultInterface;
/**
* Helper methods testing accessible interfaces.
*/
trait AccessibleTestingTrait {
/**
* The test account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* Creates AccessibleInterface object from access result object for testing.
*
* @param \Drupal\Core\Access\AccessResultInterface $accessResult
* The accessible result to return.
*
* @return \Drupal\Core\Access\AccessibleInterface
* The AccessibleInterface object.
*/
private function createAccessibleDouble(AccessResultInterface $accessResult) {
$accessible = $this->prophesize(AccessibleInterface::class);
$accessible->access('view', $this->account, TRUE)
->willReturn($accessResult);
return $accessible->reveal();
}
}

View file

@ -0,0 +1,159 @@
<?php
namespace Drupal\Tests\block_content\Unit\Access;
use Drupal\block_content\Access\AccessGroupAnd;
use Drupal\Core\Access\AccessResult;
use Drupal\block_content\Access\RefinableDependentAccessInterface;
use Drupal\block_content\Access\RefinableDependentAccessTrait;
use Drupal\Core\Session\AccountInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block_content\Access\RefinableDependentAccessTrait
*
* @group block_content
*/
class DependentAccessTest extends UnitTestCase {
use AccessibleTestingTrait;
/**
* An accessible object that results in forbidden access result.
*
* @var \Drupal\Core\Access\AccessibleInterface
*/
protected $forbidden;
/**
* An accessible object that results in neutral access result.
*
* @var \Drupal\Core\Access\AccessibleInterface
*/
protected $neutral;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->account = $this->prophesize(AccountInterface::class)->reveal();
$this->forbidden = $this->createAccessibleDouble(AccessResult::forbidden('Because I said so'));
$this->neutral = $this->createAccessibleDouble(AccessResult::neutral('I have no opinion'));
}
/**
* Test that the previous dependency is replaced when using set.
*
* @covers ::setAccessDependency
*
* @dataProvider providerTestSetFirst
*/
public function testSetAccessDependency($use_set_first) {
$testRefinable = new RefinableDependentAccessTraitTestClass();
if ($use_set_first) {
$testRefinable->setAccessDependency($this->forbidden);
}
else {
$testRefinable->addAccessDependency($this->forbidden);
}
$accessResult = $testRefinable->getAccessDependency()->access('view', $this->account, TRUE);
$this->assertTrue($accessResult->isForbidden());
$this->assertEquals('Because I said so', $accessResult->getReason());
// Calling setAccessDependency() replaces the existing dependency.
$testRefinable->setAccessDependency($this->neutral);
$dependency = $testRefinable->getAccessDependency();
$this->assertFalse($dependency instanceof AccessGroupAnd);
$accessResult = $dependency->access('view', $this->account, TRUE);
$this->assertTrue($accessResult->isNeutral());
$this->assertEquals('I have no opinion', $accessResult->getReason());
}
/**
* Tests merging a new dependency with existing non-group access dependency.
*
* @dataProvider providerTestSetFirst
*/
public function testMergeNonGroup($use_set_first) {
$testRefinable = new RefinableDependentAccessTraitTestClass();
if ($use_set_first) {
$testRefinable->setAccessDependency($this->forbidden);
}
else {
$testRefinable->addAccessDependency($this->forbidden);
}
$accessResult = $testRefinable->getAccessDependency()->access('view', $this->account, TRUE);
$this->assertTrue($accessResult->isForbidden());
$this->assertEquals('Because I said so', $accessResult->getReason());
$testRefinable->addAccessDependency($this->neutral);
/** @var \Drupal\block_content\Access\AccessGroupAnd $dependency */
$dependency = $testRefinable->getAccessDependency();
// Ensure the new dependency create a new AND group when merged.
$this->assertTrue($dependency instanceof AccessGroupAnd);
$dependencies = $dependency->getDependencies();
$accessResultForbidden = $dependencies[0]->access('view', $this->account, TRUE);
$this->assertTrue($accessResultForbidden->isForbidden());
$this->assertEquals('Because I said so', $accessResultForbidden->getReason());
$accessResultNeutral = $dependencies[1]->access('view', $this->account, TRUE);
$this->assertTrue($accessResultNeutral->isNeutral());
$this->assertEquals('I have no opinion', $accessResultNeutral->getReason());
}
/**
* Tests merging a new dependency with an existing access group dependency.
*
* @dataProvider providerTestSetFirst
*/
public function testMergeGroup($use_set_first) {
$andGroup = new AccessGroupAnd();
$andGroup->addDependency($this->forbidden);
$testRefinable = new RefinableDependentAccessTraitTestClass();
if ($use_set_first) {
$testRefinable->setAccessDependency($andGroup);
}
else {
$testRefinable->addAccessDependency($andGroup);
}
$testRefinable->addAccessDependency($this->neutral);
/** @var \Drupal\block_content\Access\AccessGroupAnd $dependency */
$dependency = $testRefinable->getAccessDependency();
// Ensure the new dependency is merged with the existing group.
$this->assertTrue($dependency instanceof AccessGroupAnd);
$dependencies = $dependency->getDependencies();
$accessResultForbidden = $dependencies[0]->access('view', $this->account, TRUE);
$this->assertTrue($accessResultForbidden->isForbidden());
$this->assertEquals('Because I said so', $accessResultForbidden->getReason());
$accessResultNeutral = $dependencies[1]->access('view', $this->account, TRUE);
$this->assertTrue($accessResultNeutral->isNeutral());
$this->assertEquals('I have no opinion', $accessResultNeutral->getReason());
}
/**
* Dataprovider for all test methods.
*
* Provides test cases for calling setAccessDependency() or
* mergeAccessDependency() first. A call to either should behave the same on a
* new RefinableDependentAccessInterface object.
*/
public function providerTestSetFirst() {
return [
[TRUE],
[FALSE],
];
}
}
/**
* Test class that implements RefinableDependentAccessInterface.
*/
class RefinableDependentAccessTraitTestClass implements RefinableDependentAccessInterface {
use RefinableDependentAccessTrait;
}

Some files were not shown because too many files have changed in this diff Show more