Commit c248cda0 authored by Wim Leers's avatar Wim Leers Committed by slashrsm

Issue #2593379 by Wim Leers, Denchev, slashrsm, Dave Reid: Fix handling of bubbleable metadata.

parent 657e32ee
<?php <?php
/**
* @file
* Contains \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayBase.
*/
namespace Drupal\entity_embed\EntityEmbedDisplay; namespace Drupal\entity_embed\EntityEmbedDisplay;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
...@@ -73,19 +69,11 @@ abstract class EntityEmbedDisplayBase extends PluginBase implements ContainerFac ...@@ -73,19 +69,11 @@ abstract class EntityEmbedDisplayBase extends PluginBase implements ContainerFac
*/ */
public function access(AccountInterface $account = NULL) { public function access(AccountInterface $account = NULL) {
// @todo Add a hook_entity_embed_display_access()? // @todo Add a hook_entity_embed_display_access()?
// Check that the plugin's registered entity types matches the current // Check that the plugin's registered entity types matches the current
// entity type. // entity type.
if (!$this->isValidEntityType()) { return AccessResult::allowedIf($this->isValidEntityType())
return FALSE; // @see \Drupal\Core\Entity\EntityTypeManager
} ->addCacheTags(['entity_types']);
// Check that the entity itself can be viewed by the user.
if ($entity = $this->getEntityFromContext()) {
return $entity->access('view', $account);
}
return TRUE;
} }
/** /**
...@@ -261,6 +249,13 @@ abstract class EntityEmbedDisplayBase extends PluginBase implements ContainerFac ...@@ -261,6 +249,13 @@ abstract class EntityEmbedDisplayBase extends PluginBase implements ContainerFac
/** /**
* Gets the entity from the current context. * Gets the entity from the current context.
* *
* @todo Where doe sthis come from? The value must come from somewhere, yet
* this does not implement any context-related interfaces. This is an *input*,
* so we need cache contexts and possibly cache tags to reflect where this
* came from. We need that for *everything* that this class does that relies
* on this, plus any of its subclasses. Right now, this is effectively a
* global that breaks cacheability metadata.
*
* @return \Drupal\Core\Entity\EntityInterface * @return \Drupal\Core\Entity\EntityInterface
*/ */
public function getEntityFromContext() { public function getEntityFromContext() {
......
...@@ -58,8 +58,8 @@ interface EntityEmbedDisplayInterface extends ConfigurablePluginInterface, Plugi ...@@ -58,8 +58,8 @@ interface EntityEmbedDisplayInterface extends ConfigurablePluginInterface, Plugi
* (optional) The user for which to check access, or NULL to check access * (optional) The user for which to check access, or NULL to check access
* for the current user. Defaults to NULL. * for the current user. Defaults to NULL.
* *
* @return bool * @return \Drupal\Core\Access\AccessResultInterface
* TRUE if this Entity Embed Display plugin can be used, or FALSE otherwise. * The access result.
*/ */
public function access(AccountInterface $account = NULL); public function access(AccountInterface $account = NULL);
......
...@@ -73,7 +73,9 @@ class EntityEmbedDisplayManager extends DefaultPluginManager { ...@@ -73,7 +73,9 @@ class EntityEmbedDisplayManager extends DefaultPluginManager {
foreach ($contexts as $name => $value) { foreach ($contexts as $name => $value) {
$display->setContextValue($name, $value); $display->setContextValue($name, $value);
} }
return $display->access(); // We lose cacheability metadata at this point. We should refactor to
// avoid this. @see https://www.drupal.org/node/2593379#comment-11368447
return $display->access()->isAllowed();
} }
catch (PluginException $e) { catch (PluginException $e) {
return FALSE; return FALSE;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\entity_embed\EntityEmbedDisplay; namespace Drupal\entity_embed\EntityEmbedDisplay;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FormatterPluginManager; use Drupal\Core\Field\FormatterPluginManager;
...@@ -112,12 +113,18 @@ abstract class FieldFormatterEntityEmbedDisplayBase extends EntityEmbedDisplayBa ...@@ -112,12 +113,18 @@ abstract class FieldFormatterEntityEmbedDisplayBase extends EntityEmbedDisplayBa
* {@inheritdoc} * {@inheritdoc}
*/ */
public function access(AccountInterface $account = NULL) { public function access(AccountInterface $account = NULL) {
if (!parent::access($account)) { return parent::access($account)->andIf($this->isApplicableFieldFormatter());
return FALSE; }
}
/**
* Checks if the field formatter is applicable.
*
* @return \Drupal\Core\Access\AccessResult
* Returns the access result.
*/
protected function isApplicableFieldFormatter() {
$definition = $this->formatterPluginManager->getDefinition($this->getDerivativeId()); $definition = $this->formatterPluginManager->getDefinition($this->getDerivativeId());
return $definition['class']::isApplicable($this->getFieldDefinition()); return AccessResult::allowedIf($definition['class']::isApplicable($this->getFieldDefinition()));
} }
/** /**
......
<?php <?php
/**
* @file
* Contains Drupal\entity_embed\EntityHelperTrait.
*/
namespace Drupal\entity_embed; namespace Drupal\entity_embed;
use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Html;
...@@ -12,7 +7,6 @@ use Drupal\Core\Entity\EntityInterface; ...@@ -12,7 +7,6 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager; use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager;
/** /**
...@@ -22,6 +16,11 @@ use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager; ...@@ -22,6 +16,11 @@ use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager;
* classes that would implement ContainerInjectionInterface. Services registered * classes that would implement ContainerInjectionInterface. Services registered
* in the Container should not use this trait but inject the appropriate service * in the Container should not use this trait but inject the appropriate service
* directly for easier testing. * directly for easier testing.
*
* @todo this duplicates/wraps much of the Entity API. Is this really worth
* keeping? The downside is painfully illustrated: the documentation must be
* kept up to date with the actual API documentation… and that's not happening.
* This causes subtle bugs and makes maintenance harder.
*/ */
trait EntityHelperTrait { trait EntityHelperTrait {
...@@ -46,13 +45,6 @@ trait EntityHelperTrait { ...@@ -46,13 +45,6 @@ trait EntityHelperTrait {
*/ */
protected $displayPluginManager; protected $displayPluginManager;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface.
*/
protected $renderer;
/** /**
* Loads an entity from the database. * Loads an entity from the database.
* *
...@@ -132,7 +124,7 @@ trait EntityHelperTrait { ...@@ -132,7 +124,7 @@ trait EntityHelperTrait {
} }
/** /**
* Returns the render array for an entity. * Builds the render array for the provided entity.
* *
* @param \Drupal\Core\Entity\EntityInterface $entity * @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be rendered. * The entity to be rendered.
...@@ -144,14 +136,19 @@ trait EntityHelperTrait { ...@@ -144,14 +136,19 @@ trait EntityHelperTrait {
* *
* @return array * @return array
* A render array for the entity. * A render array for the entity.
*
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::view()
*
* @todo Note that the signature here does NOT match that of \Drupal\Core\Entity\EntityViewBuilderInterface::view(), which can lead to subtle bugs.
*/ */
protected function renderEntity(EntityInterface $entity, $view_mode, $langcode = NULL) { protected function buildEntity(EntityInterface $entity, $view_mode, $langcode = NULL) {
$render_controller = $this->entityManager()->getViewBuilder($entity->getEntityTypeId()); return $this->entityManager()
return $render_controller->view($entity, $view_mode, $langcode); ->getViewBuilder($entity->getEntityTypeId())
->view($entity, $view_mode, $langcode);
} }
/** /**
* Renders an embedded entity. * Builds the render array for an embedded entity.
* *
* @param \Drupal\Core\Entity\EntityInterface $entity * @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be rendered. * The entity to be rendered.
...@@ -159,10 +156,12 @@ trait EntityHelperTrait { ...@@ -159,10 +156,12 @@ trait EntityHelperTrait {
* (optional) Array of context values, corresponding to the attributes on * (optional) Array of context values, corresponding to the attributes on
* the embed HTML tag. * the embed HTML tag.
* *
* @return string * @return array
* The HTML of the entity rendered with the Entity Embed Display plugin. * A render array.
*
* @todo improve documentation
*/ */
protected function renderEntityEmbed(EntityInterface $entity, array $context = array()) { protected function buildEntityEmbed(EntityInterface $entity, array $context = array()) {
// Support the deprecated view-mode data attribute. // Support the deprecated view-mode data attribute.
if (isset($context['data-view-mode']) && !isset($context['data-entity-embed-display']) && !isset($context['data-entity-embed-settings'])) { if (isset($context['data-view-mode']) && !isset($context['data-entity-embed-display']) && !isset($context['data-entity-embed-settings'])) {
$context['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view'; $context['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
...@@ -199,7 +198,7 @@ trait EntityHelperTrait { ...@@ -199,7 +198,7 @@ trait EntityHelperTrait {
'#entity' => $entity, '#entity' => $entity,
'#context' => $context, '#context' => $context,
); );
$build['entity'] = $this->renderEntityEmbedDisplayPlugin( $build['entity'] = $this->buildEntityEmbedDisplayPlugin(
$entity, $entity,
$context['data-entity-embed-display'], $context['data-entity-embed-display'],
$context['data-entity-embed-settings'], $context['data-entity-embed-settings'],
...@@ -219,15 +218,16 @@ trait EntityHelperTrait { ...@@ -219,15 +218,16 @@ trait EntityHelperTrait {
$build['#attributes']['data-caption'] = $context['data-caption']; $build['#attributes']['data-caption'] = $context['data-caption'];
} }
// Make sure that access to the entity is respected.
$build['#access'] = $entity->access('view', NULL, TRUE);
// @todo Should this hook get invoked if $build is an empty array? // @todo Should this hook get invoked if $build is an empty array?
$this->moduleHandler()->alter(array("{$context['data-entity-type']}_embed", 'entity_embed'), $build, $entity, $context); $this->moduleHandler()->alter(array("{$context['data-entity-type']}_embed", 'entity_embed'), $build, $entity, $context);
$entity_output = $this->renderer()->render($build); return $build;
return $entity_output;
} }
/** /**
* Renders an entity using an Entity Embed Display plugin. * Builds the render array for an entity using an Entity Embed Display plugin.
* *
* @param \Drupal\Core\Entity\EntityInterface $entity * @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be rendered. * The entity to be rendered.
...@@ -242,7 +242,7 @@ trait EntityHelperTrait { ...@@ -242,7 +242,7 @@ trait EntityHelperTrait {
* @return array * @return array
* A render array for the Entity Embed Display plugin. * A render array for the Entity Embed Display plugin.
*/ */
protected function renderEntityEmbedDisplayPlugin(EntityInterface $entity, $plugin_id, array $plugin_configuration = array(), array $context = array()) { protected function buildEntityEmbedDisplayPlugin(EntityInterface $entity, $plugin_id, array $plugin_configuration = array(), array $context = array()) {
// Build the Entity Embed Display plugin. // Build the Entity Embed Display plugin.
/** @var \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayBase $display */ /** @var \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayBase $display */
$display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_configuration); $display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_configuration);
...@@ -336,29 +336,4 @@ trait EntityHelperTrait { ...@@ -336,29 +336,4 @@ trait EntityHelperTrait {
return $this; return $this;
} }
/**
* Returns the renderer.
*
* @return \Drupal\Core\Render\RendererInterface
* The renderer.
*/
protected function renderer() {
if (!isset($this->renderer)) {
$this->renderer = \Drupal::service('renderer');
}
return $this->renderer;
}
/**
* Sets the renderer.
*
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
*
* @return self
*/
public function setRenderer(RendererInterface $renderer) {
$this->renderer = $renderer;
return $this;
}
} }
...@@ -11,7 +11,9 @@ use Drupal\Component\Utility\Html; ...@@ -11,7 +11,9 @@ use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\entity_embed\EntityHelperTrait; use Drupal\entity_embed\EntityHelperTrait;
use Drupal\entity_embed\Exception\EntityNotFoundException; use Drupal\entity_embed\Exception\EntityNotFoundException;
use Drupal\entity_embed\Exception\RecursiveRenderingException; use Drupal\entity_embed\Exception\RecursiveRenderingException;
...@@ -34,6 +36,13 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte ...@@ -34,6 +36,13 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte
use EntityHelperTrait; use EntityHelperTrait;
use DomHelperTrait; use DomHelperTrait;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/** /**
* Constructs a EntityEmbedFilter object. * Constructs a EntityEmbedFilter object.
* *
...@@ -48,10 +57,11 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte ...@@ -48,10 +57,11 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The Module Handler. * The Module Handler.
*/ */
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler) { public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, RendererInterface $renderer) {
parent::__construct($configuration, $plugin_id, $plugin_definition); parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->setEntityManager($entity_manager); $this->setEntityManager($entity_manager);
$this->setModuleHandler($module_handler); $this->setModuleHandler($module_handler);
$this->renderer = $renderer;
} }
/** /**
...@@ -63,7 +73,8 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte ...@@ -63,7 +73,8 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte
$plugin_id, $plugin_id,
$plugin_definition, $plugin_definition,
$container->get('entity.manager'), $container->get('entity.manager'),
$container->get('module_handler') $container->get('module_handler'),
$container->get('renderer')
); );
} }
...@@ -101,14 +112,22 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte ...@@ -101,14 +112,22 @@ class EntityEmbedFilter extends FilterBase implements ContainerFactoryPluginInte
$node->setAttribute('data-entity-uuid', $uuid); $node->setAttribute('data-entity-uuid', $uuid);
} }
$access = $entity->access('view', NULL, TRUE);
$access_metadata = CacheableMetadata::createFromObject($access);
$entity_metadata = CacheableMetadata::createFromObject($entity);
$result = $result->merge($entity_metadata)->merge($access_metadata);
$context = $this->getNodeAttributesAsArray($node); $context = $this->getNodeAttributesAsArray($node);
$context += array('data-langcode' => $langcode); $context += array('data-langcode' => $langcode);
$entity_output = $this->renderEntityEmbed($entity, $context); $build = $this->buildEntityEmbed($entity, $context);
// We need to render the embedded entity:
// - without replacing placeholders, so that the placeholders are
// only replaced at the last possible moment. Hence we cannot use
// either renderPlain() or renderRoot(), so we must use render().
// - without bubbling beyond this filter, because filters must
// ensure that the bubbleable metadata for the changes they make
// when filtering text makes it onto the FilterProcessResult
// object that they return ($result). To prevent that bubbling, we
// must wrap the call to render() in a render context.
$entity_output = $this->renderer->executeInRenderContext(new RenderContext(), function () use (&$build) {
return $this->renderer->render($build);
});
$result = $result->merge(BubbleableMetadata::createFromRenderArray($build));
$depth--; $depth--;
} }
......
...@@ -62,13 +62,4 @@ class FileFieldFormatter extends EntityReferenceFieldFormatter { ...@@ -62,13 +62,4 @@ class FileFieldFormatter extends EntityReferenceFieldFormatter {
return $form; return $form;
} }
/**
* {@inheritdoc}
*
* @return \Drupal\file\FileInterface
*/
public function getEntityFromContext() {
return parent::getEntityFromContext();
}
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\entity_embed\Plugin\entity_embed\EntityEmbedDisplay; namespace Drupal\entity_embed\Plugin\entity_embed\EntityEmbedDisplay;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Field\FormatterPluginManager; use Drupal\Core\Field\FormatterPluginManager;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
...@@ -85,15 +86,35 @@ class ImageFieldFormatter extends FileFieldFormatter { ...@@ -85,15 +86,35 @@ class ImageFieldFormatter extends FileFieldFormatter {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function access(AccountInterface $account = NULL) { public function access(AccountInterface $account = NULL) {
if (!parent::access($account)) { return parent::access($account)->andIf($this->isValidImage());
return FALSE; }
/**
* Checks if the image is valid.
*
* @return \Drupal\Core\Access\AccessResult
* Returns the access result.
*/
protected function isValidImage() {
// If entity type is not file we have to return early to prevent fatal in
// the condition above. Access should already be forbidden at this point,
// which means this won't have any effect.
// @see EntityEmbedDisplayBase::access()
if ($this->getEntityTypeFromContext() != 'file') {
return AccessResult::forbidden();
} }
$access = AccessResult::allowed();
// @todo needs cacheability metadata for getEntityFromContext.
// @see \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayBase::getEntityFromContext()
/** @var \Drupal\file\FileInterface $entity */
if ($entity = $this->getEntityFromContext()) { if ($entity = $this->getEntityFromContext()) {
return $this->imageFactory->get($entity->getFileUri())->isValid(); $access = AccessResult::allowedIf($this->imageFactory->get($entity->getFileUri())->isValid())
// See the above @todo, this is the best we can do for now.
->addCacheableDependency($entity);
} }
return TRUE; return $access;
} }
/** /**
......
<?php <?php
/**
* @file
* Contains \Drupal\entity_embed\Tests\EntityEmbedDialogTest.
*/
namespace Drupal\entity_embed\Tests; namespace Drupal\entity_embed\Tests;
use Drupal\editor\Entity\Editor; use Drupal\editor\Entity\Editor;
...@@ -16,6 +11,13 @@ use Drupal\editor\Entity\Editor; ...@@ -16,6 +11,13 @@ use Drupal\editor\Entity\Editor;
*/ */
class EntityEmbedDialogTest extends EntityEmbedTestBase { class EntityEmbedDialogTest extends EntityEmbedTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['image'];
/** /**
* Tests the entity embed dialog. * Tests the entity embed dialog.
*/ */
...@@ -101,6 +103,20 @@ class EntityEmbedDialogTest extends EntityEmbedTestBase { ...@@ -101,6 +103,20 @@ class EntityEmbedDialogTest extends EntityEmbedTestBase {
$this->assertFieldByXPath('//input[contains(@class, "button--primary")]', 'Embed', 'Embed is a primary button');*/ $this->assertFieldByXPath('//input[contains(@class, "button--primary")]', 'Embed', 'Embed is a primary button');*/
} }
/**
* Tests entity embed functionality.
*/
public function testEntityEmbedFunctionality() {
$edit = [
'entity_id' => $this->node->getTitle() . ' (' . $this->node->id() . ')',
];
$this->getEmbedDialog('custom_format', 'node');
$this->drupalPostForm(NULL, $edit, t('Next'));
// Tests that the embed dialog doesn't trow a fatal in
// ImageFieldFormatter::isValidImage()
$this->assertResponse(200);
}
/** /**
* Retrieves an embed dialog based on given parameters. * Retrieves an embed dialog based on given parameters.
* *
......
<?php <?php
/**
* @file
* Contains \Drupal\entity_embed\Tests\EntityEmbedFilterTest.
*/
namespace Drupal\entity_embed\Tests; namespace Drupal\entity_embed\Tests;
/** /**
...@@ -48,6 +43,31 @@ class EntityEmbedFilterTest extends EntityEmbedTestBase { ...@@ -48,6 +43,31 @@ class EntityEmbedFilterTest extends EntityEmbedTestBase {
$this->assertNoText(strip_tags($content), 'Placeholder does not appear in the output when embed is successful.'); $this->assertNoText(strip_tags($content), 'Placeholder does not appear in the output when embed is successful.');
$this->assertRaw('<article class="embedded-entity">', 'Embed container found.'); $this->assertRaw('<article class="embedded-entity">', 'Embed container found.');
// Tests that embedded entity is not rendered if not accessible.
$this->node->setPublished(FALSE)->save();
$settings = [];
$settings['type'] = 'page';
$settings['title'] = 'Test un-accessible entity embed with entity-id and view-mode';
$settings['body'] = [['value' => $content, 'format' => 'custom_format']];
$node = $this->drupalCreateNode($settings);
$this->drupalGet('node/' . $node->id());
$this->assertNoRaw('<drupal-entity data-entity-type="node" data-entity');
$this->assertNoText($this->node->body->value, 'Embedded node does not exist in the page.');
$this->assertNoText(strip_tags($content), 'Placeholder does not appear in the output when embed is successful.');
// Tests that embedded entity is displayed to the user who has the view
// unpublished content permission.
$this->createRole(['view own unpublished content'], 'access_unpublished');
$this->webUser->addRole('access_unpublished');
$this->webUser->save();
$this->drupalGet('node/' . $node->id());
$this->assertNoRaw('<drupal-entity data-entity-type="node" data-entity');
$this->assertText($this->node->body->value, 'Embedded node exists in the page.');
$this->assertNoText(strip_tags($content), 'Placeholder does not appear in the output when embed is successful.');
$this->assertRaw('<article class="embedded-entity">', 'Embed container found.');
$this->webUser->removeRole('access_unpublished');
$this->webUser->save();
$this->node->setPublished(TRUE)->save();
// Tests entity embed using entity UUID and view mode. // Tests entity embed using entity UUID and view mode.
$content = '<drupal-entity data-entity-type="node" data-entity-uuid="' . $this->node->uuid() . '" data-view-mode="teaser">This placeholder should not be rendered.</drupal-entity>'; $content = '<drupal-entity data-entity-type="node" data-entity-uuid="' . $this->node->uuid() . '" data-view-mode="teaser">This placeholder should not be rendered.</drupal-entity>';
$settings = array(); $settings = array();
...@@ -60,6 +80,7 @@ class EntityEmbedFilterTest extends EntityEmbedTestBase { ...@@ -60,6 +80,7 @@ class EntityEmbedFilterTest extends EntityEmbedTestBase {
$this->assertText($this->node->body->value, 'Embedded node exists in page.'); $this->assertText($this->node->body->value, 'Embedded node exists in page.');
$this->assertNoText(strip_tags($content), 'Placeholder does not appear in the output when embed is successful.'); $this->assertNoText(strip_tags($content), 'Placeholder does not appear in the output when embed is successful.');
$this->assertRaw('<article class="embedded-entity">', 'Embed container found.'); $this->assertRaw('<article class="embedded-entity">', 'Embed container found.');
$this->assertCacheTag('foo:' . $this->node->id());
// Ensure that placeholder is not replaced when embed is unsuccessful. // Ensure that placeholder is not replaced when embed is unsuccessful.
$content = '<drupal-entity data-entity-type="node" data-entity-id="InvalidID" data-view-mode="teaser">This placeholder should be rendered since specified entity does not exists.</drupal-entity>'; $content = '<drupal-entity data-entity-type="node" data-entity-id="InvalidID" data-view-mode="teaser">This placeholder should be rendered since specified entity does not exists.</drupal-entity>';
......
...@@ -74,7 +74,7 @@ class EntityEmbedTwigExtension extends \Twig_Extension { ...@@ -74,7 +74,7 @@ class EntityEmbedTwigExtension extends \Twig_Extension {
'data-entity-embed-display' => $display_plugin, 'data-entity-embed-display' => $display_plugin,
'data-entity-embed-settings' => $display_settings, 'data-entity-embed-settings' => $display_settings,
); );
return $this->renderEntityEmbed($entity, $context); return $this->buildEntityEmbed($entity, $context);
} }
} }
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
* Helper module for the Entity Embed tests. * Helper module for the Entity Embed tests.
*/ */
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/** /**
* Implements hook_theme(). * Implements hook_theme().
...@@ -72,3 +74,12 @@ function entity_embed_test_entity_embed_alter(array &$build, EntityInterface $en ...@@ -72,3 +74,12 @@ function entity_embed_test_entity_embed_alter(array &$build, EntityInterface $en
// Set title of the 'node' entity. // Set title of the 'node' entity.
$entity->setTitle("Title set by hook_entity_embed_alter"); $entity->setTitle("Title set by hook_entity_embed_alter");
} }
/**
* Implements hook_entity_access().
*/
function entity_embed_test_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
if ($entity->label() == 'Embed Test Node') {
return AccessResult::neutral()->addCacheTags(['foo:' . $entity->id()]);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment