diff --git a/core/includes/common.inc b/core/includes/common.inc index 48db270..d1e9b97 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4103,7 +4103,7 @@ function drupal_render_cache_set(&$markup, $elements) { } $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache'; $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CacheBackendInterface::CACHE_PERMANENT; - $tags = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array(); + $tags = drupal_render_collect_cache_tags($elements, TRUE); cache($bin)->set($cid, $data, $expire, $tags); } @@ -4151,6 +4151,57 @@ function drupal_render_collect_attached($elements, $return = FALSE) { } /** + * Collects cache tags for an element and its children into a single array. + * + * When caching elements, it is necessary to collect all cache tags into a + * single array, from both the element itself and all child elements. This + * allows items to be invalidated based on all tags attached to the content + * they're constituted from. + * + * @param array $elements + * The element to collect cache tags from. + * @param bool $return + * Whether to return the attached elements and reset the internal static. + * + * @return array + * The cache tags array for this element and its descendants. + */ +function drupal_render_collect_cache_tags($elements, $return = FALSE) { + $tags = &drupal_static(__FUNCTION__, array()); + + // Collect all cache tags for this element. + if (isset($elements['#cache']['tags'])) { + foreach ($elements['#cache']['tags'] as $namespace => $values) { + if (is_array($values)) { + foreach ($values as $value) { + if (!isset($tags[$namespace][$value])) { + $tags[$namespace][$value] = $value; + } + } + } + else { + if (!isset($tags[$namespace])) { + $tags[$namespace] = $values; + } + } + } + } + if ($children = element_children($elements)) { + foreach ($children as $child) { + drupal_render_collect_cache_tags($elements[$child]); + } + } + + // If this was the first call to the function, return all attached elements + // and reset the static cache. + if ($return) { + $return = $tags; + $tags = array(); + return $return; + } +} + +/** * Prepares an element for caching based on a query. * * This smart caching strategy saves Drupal from querying and rendering to HTML diff --git a/core/includes/entity.inc b/core/includes/entity.inc index 7822d7a..8697c0e 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -48,6 +48,18 @@ function entity_info_cache_clear() { } /** + * Clears the entity render cache for all entity types. + */ +function entity_render_cache_clear() { + $entity_manager = Drupal::entityManager(); + foreach ($entity_manager->getDefinitions() as $entity_type => $info) { + if ($entity_manager->hasController($entity_type, 'render')) { + $entity_manager->getRenderController($entity_type)->resetCache(); + } + } +} + +/** * Returns the entity bundle info. * * @param string|null $entity_type @@ -622,14 +634,19 @@ function entity_render_controller($entity_type) { * @param string $langcode * (optional) For which language the entity should be rendered, defaults to * the current content language. + * @param bool $reset + * (optional) Whether to reset the render cache for the requested entity. + * Defaults to FALSE. * * @return array * A render array for the entity. */ -function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL) { - return Drupal::entityManager() - ->getRenderController($entity->entityType()) - ->view($entity, $view_mode, $langcode); +function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) { + $render_controller = Drupal::entityManager()->getRenderController($entity->entityType()); + if ($reset) { + $render_controller->resetCache(array($entity->id())); + } + return $render_controller->view($entity, $view_mode, $langcode); } /** @@ -642,15 +659,20 @@ function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL) { * @param string $langcode * (optional) For which language the entity should be rendered, defaults to * the current content language. + * @param bool $reset + * (optional) Whether to reset the render cache for the requested entities. + * Defaults to FALSE. * * @return array * A render array for the entities, indexed by the same keys as the * entities array passed in $entities. */ -function entity_view_multiple(array $entities, $view_mode, $langcode = NULL) { - return Drupal::entityManager() - ->getRenderController(reset($entities)->entityType()) - ->viewMultiple($entities, $view_mode, $langcode); +function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) { + $render_controller = Drupal::entityManager()->getRenderController(reset($entities)->entityType()); + if ($reset) { + $render_controller->resetCache(array_keys($entities)); + } + return $render_controller->viewMultiple($entities, $view_mode, $langcode); } /** diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php index b8e4061..79e5c52 100644 --- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php +++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php @@ -138,6 +138,14 @@ class EntityType extends Plugin { public $static_cache = TRUE; /** + * Boolean indicating whether the rendered output of entities should be + * cached. + * + * @var bool (optional) + */ + public $render_cache = TRUE; + + /** * Boolean indicating whether entities of this type have multilingual support. * * At an entity level, this indicates language support and at a bundle level diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index ec05c1a..908b36c 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Entity; use Drupal\Component\Uuid\Uuid; +use Drupal\Core\Entity\Plugin\DataType\EntityReferenceItem; use Drupal\Core\Language\Language; use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TypedDataInterface; @@ -354,7 +355,9 @@ public function getTranslationLanguages($include_default = TRUE) { * Implements \Drupal\Core\Entity\EntityInterface::save(). */ public function save() { - return \Drupal::entityManager()->getStorageController($this->entityType)->save($this); + $return = \Drupal::entityManager()->getStorageController($this->entityType)->save($this); + $this->changed(); + return $return; } /** @@ -363,6 +366,7 @@ public function save() { public function delete() { if (!$this->isNew()) { \Drupal::entityManager()->getStorageController($this->entityType)->delete(array($this->id() => $this)); + $this->changed(); } } @@ -642,4 +646,45 @@ public function initTranslation($langcode) { // http://drupal.org/node/2004244 } + /** + * {@inheritdoc} + */ + public function relatedEntities() { + $relationships = array(); + + // @todo Remove when all entities are converted to EntityNG. + if (!$this->getPropertyDefinitions()) { + return $relationships; + } + + // Gather a list of related entities. + foreach ($this->getProperties() as $name => $definition) { + $field_item = $this->get($name)->offsetGet(0); + if ($field_item instanceof EntityReferenceItem && $entity = $field_item->entity) { + $relationships[] = $entity; + } + } + + return $relationships; + } + + /** + * {@inheritdoc} + */ + public function changed() { + $related_entity_ids = array( + $this->entityType() => array($this->id() => TRUE), + ); + + foreach ($this->relatedEntities() as $related_entity) { + $related_entity_ids[$related_entity->entityType()][$related_entity->id()] = TRUE; + } + + foreach ($related_entity_ids as $entity_type => $entity_ids) { + if (\Drupal::entityManager()->hasController($entity_type, 'render')) { + \Drupal::entityManager()->getRenderController($entity_type)->resetCache(array_keys($entity_ids)); + } + } + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php index a3ffcb9..55ee3b4 100644 --- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php +++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php @@ -626,4 +626,20 @@ public function initTranslation($langcode) { $this->decorated->initTranslation($langcode); } + /** + * {@inheritdoc} + * + * @return array + */ + public function relatedEntities() { + return array(); + } + + /** + * {@inheritdoc} + */ + public function changed(array $tags = array()) { + $this->decorated->changed($tags); + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index e965b8e..a1a6642 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -326,4 +326,16 @@ public function isTranslatable(); */ public function initTranslation($langcode); + /** + * @todo + * + * @return array + */ + public function relatedEntities(); + + /** + * @todo + */ + public function changed(); + } diff --git a/core/lib/Drupal/Core/Entity/EntityRenderController.php b/core/lib/Drupal/Core/Entity/EntityRenderController.php index cd92f52..9d82d09 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderController.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderController.php @@ -6,9 +6,10 @@ */ namespace Drupal\Core\Entity; -use Drupal\entity\Plugin\Core\Entity\EntityDisplay; +use Drupal\Core\Entity\Plugin\DataType\EntityReferenceItem; use Drupal\Core\Language\Language; +use Drupal\entity\Plugin\Core\Entity\EntityDisplay; /** * Base class for entity view controllers. @@ -22,8 +23,37 @@ class EntityRenderController implements EntityRenderControllerInterface { */ protected $entityType; + /** + * The entity info array. + * + * @var array + * + * @see entity_get_info() + */ + protected $entityInfo; + + /** + * An array of view mode info for the type of entities for which this + * controller is instantiated. + * + * @var array + */ + protected $viewModesInfo; + + /** + * The cache bin used to store the render cache. + * + * @todo Defaults to 'cache' for now, until http://drupal.org/node/1194136 is + * fixed. + * + * @var string + */ + protected $cacheBin = 'cache'; + public function __construct($entity_type) { $this->entityType = $entity_type; + $this->entityInfo = entity_get_info($entity_type); + $this->viewModesInfo = entity_get_view_modes($entity_type); } /** @@ -62,6 +92,24 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco '#view_mode' => $view_mode, '#langcode' => $langcode, ); + + // Cache the rendered output if permitted by the view mode settings. + // @todo Fix the tests that require non-existent view modes and remove the + // isset() checks below. + $view_mode_is_cacheable = !isset($this->viewModesInfo[$view_mode]) || (isset($this->viewModesInfo[$view_mode]) && $this->viewModesInfo[$view_mode]['cache']); + if (!isset($entity->in_preview) && $this->entityInfo['render_cache'] && $view_mode_is_cacheable) { + $request = \Drupal::request(); + $return['#cache'] = array( + 'keys' => array('entity_view', $this->entityType ,$entity->id(), $view_mode, $request->getQueryString()), + 'granularity' => DRUPAL_CACHE_PER_ROLE, + 'bin' => $this->cacheBin, + 'tags' => array( + $this->entityType . '_view' => TRUE, + $this->entityType => array($entity->id()), + ), + ); + } + return $return; } @@ -164,4 +212,20 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la return $build; } + + /** + * {@inheritdoc} + */ + public function resetCache(array $ids = NULL) { + if (isset($ids)) { + $tags = array(); + foreach ($ids as $entity_id) { + $tags[$this->entityType][$entity_id] = $entity_id; + } + \Drupal::cache($this->cacheBin)->deleteTags($tags); + } + else { + \Drupal::cache($this->cacheBin)->deleteTags(array($this->entityType . '_view' => TRUE)); + } + } } diff --git a/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php index 03ba014..a112fc2 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php @@ -75,4 +75,14 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N * be available for loading. */ public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL); + + /** + * Resets the entity render cache. + * + * @param array|null $ids + * (optional) If specified, the cache is reset for the given entity IDs + * only. + */ + public function resetCache(array $ids = NULL); + } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php index da85242..d31c46e 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php @@ -20,7 +20,7 @@ class IntegrationTest extends ViewUnitTestBase { * * @var array */ - public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'field'); + public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'entity', 'field'); /** * Views used by this test. diff --git a/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml b/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml index ebacec5..6c2a3d1 100644 --- a/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml +++ b/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml @@ -1,4 +1,5 @@ id: custom_block.full label: Full status: '0' +cache: '1' targetEntityType: custom_block diff --git a/core/modules/block/lib/Drupal/block/BlockRenderController.php b/core/modules/block/lib/Drupal/block/BlockRenderController.php index 57a5d62..19418ae 100644 --- a/core/modules/block/lib/Drupal/block/BlockRenderController.php +++ b/core/modules/block/lib/Drupal/block/BlockRenderController.php @@ -62,4 +62,10 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la return $build; } + /** + * {@inheritdoc} + */ + public function resetCache(array $ids = NULL) { + // @todo Move block render caching logic to this controller? + } } diff --git a/core/modules/book/config/entity.view_mode.node.print.yml b/core/modules/book/config/entity.view_mode.node.print.yml index fe45f50..6f7333f 100644 --- a/core/modules/book/config/entity.view_mode.node.print.yml +++ b/core/modules/book/config/entity.view_mode.node.print.yml @@ -1,4 +1,5 @@ id: node.print label: Print status: '0' +cache: '1' targetEntityType: node diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc index 221fbfc..d88b63a 100644 --- a/core/modules/comment/comment.pages.inc +++ b/core/modules/comment/comment.pages.inc @@ -81,6 +81,7 @@ function comment_reply(EntityInterface $node, $pid = NULL) { // This is the case where the comment is in response to a node. Display the node. elseif (user_access('access content')) { $build['comment_node'] = node_view($node); + unset($build['comment_node']['#cache']); } // Should we show the reply box? diff --git a/core/modules/comment/config/entity.view_mode.comment.full.yml b/core/modules/comment/config/entity.view_mode.comment.full.yml index e48fbd7..abfc646 100644 --- a/core/modules/comment/config/entity.view_mode.comment.full.yml +++ b/core/modules/comment/config/entity.view_mode.comment.full.yml @@ -1,4 +1,5 @@ id: comment.full label: Full comment status: '0' +cache: '1' targetEntityType: comment diff --git a/core/modules/comment/lib/Drupal/comment/CommentRenderController.php b/core/modules/comment/lib/Drupal/comment/CommentRenderController.php index c6b21f0..14585d6 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentRenderController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentRenderController.php @@ -79,9 +79,10 @@ protected function alterBuild(array &$build, EntityInterface $comment, EntityDis $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $comment->bundle(), COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED; - // Add 'new' anchor if needed. + // Add 'new' anchor and disable render cache if needed. if (!empty($comment->first_new)) { $prefix .= "\n"; + unset($build['#cache']); } // Add indentation div or close open divs as needed. diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php index 5bad9ea..fc91ed3 100644 --- a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php @@ -35,6 +35,7 @@ * uri_callback = "comment_uri", * fieldable = TRUE, * translatable = TRUE, + * render_cache = FALSE, * route_base_path = "admin/structure/types/manage/{bundle}/comment", * bundle_prefix = "comment_node_", * entity_keys = { diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php index 5fe19ba..6ae734e 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php @@ -148,6 +148,8 @@ function testAnonymous() { 'post comments' => FALSE, 'skip comment approval' => FALSE, )); + \Drupal::entityManager()->getRenderController('node')->resetCache(); + \Drupal::entityManager()->getRenderController('comment')->resetCache(); $this->drupalGet('node/' . $this->node->id()); $this->assertPattern('@]*>Comments@', 'Comments were displayed.'); $this->assertLink('Log in', 1, 'Link to log in was found.'); @@ -158,6 +160,8 @@ function testAnonymous() { 'post comments' => TRUE, 'skip comment approval' => TRUE, )); + \Drupal::entityManager()->getRenderController('node')->resetCache(); + \Drupal::entityManager()->getRenderController('comment')->resetCache(); $this->drupalGet('node/' . $this->node->id()); $this->assertNoPattern('@]*>Comments@', 'Comments were not displayed.'); $this->assertFieldByName('subject', '', 'Subject field found.'); diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php index 0d27bf9..1d3b704 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php @@ -268,6 +268,7 @@ function setCommentsPerPage($number) { */ function setCommentSettings($name, $value, $message) { variable_set($name . '_article', $value); + \Drupal::entityManager()->getRenderController('comment')->resetCache(); // Display status message. $this->pass($message); } diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php index 6a1c9de..5f997f7 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php +++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php @@ -121,6 +121,20 @@ public function id() { /** * {@inheritdoc} */ + public function save() { + $return = parent::save(); + + // Reset the render cache for the target entity type. + if (\Drupal::entityManager()->hasController($this->targetEntityType, 'render')) { + \Drupal::entityManager()->getRenderController($this->targetEntityType)->resetCache(); + } + + return $return; + } + + /** + * {@inheritdoc} + */ public function getExportProperties() { $names = array( 'id', diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php index 8fe73cb..1541143 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php +++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php @@ -58,6 +58,13 @@ public $status = TRUE; /** + * Whether or not the rendered output of this view mode is cached by default. + * + * @var bool + */ + public $cache = TRUE; + + /** * {@inheritdoc} */ public static function sort($a, $b) { diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php index aa93124..e89eaf6 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php @@ -96,8 +96,21 @@ public function view(EntityInterface $entity, $langcode, FieldInterface $items) '#object' => $entity, '#items' => $items->getValue(TRUE), '#formatter' => $this->getPluginId(), + '#cache' => array('tags' => array()) ); + // Gather cache tags from reference fields. + foreach ($items as $item) { + if (isset($item->format)) { + $info['#cache']['tags']['filter_format'] = $item->format; + } + + if (isset($item->entity)) { + $info['#cache']['tags'][$item->entity->entityType()][] = $item->entity->id(); + $info['#cache']['tags'][$item->entity->entityType() . '_view'] = TRUE; + } + } + $addition[$field_name] = array_merge($info, $elements); } diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php index 4580e7b..a7cc0a8 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php @@ -34,8 +34,9 @@ */ function setUp() { parent::setUp(); - $this->installSchema('system', array('sequences', 'variable', 'config_snapshot')); $this->installSchema('entity_test', 'entity_test'); + $this->installSchema('system', array('sequences', 'variable', 'config_snapshot')); + $this->installSchema('user', array('users', 'users_roles')); // Set default storage backend and configure the theme system. $this->installConfig(array('field', 'system')); diff --git a/core/modules/node/config/entity.view_mode.node.full.yml b/core/modules/node/config/entity.view_mode.node.full.yml index af6d938..e4d8bd0 100644 --- a/core/modules/node/config/entity.view_mode.node.full.yml +++ b/core/modules/node/config/entity.view_mode.node.full.yml @@ -1,4 +1,5 @@ id: node.full label: Full content status: '0' +cache: '1' targetEntityType: node diff --git a/core/modules/node/config/entity.view_mode.node.rss.yml b/core/modules/node/config/entity.view_mode.node.rss.yml index 984b05f..0dbf7c1 100644 --- a/core/modules/node/config/entity.view_mode.node.rss.yml +++ b/core/modules/node/config/entity.view_mode.node.rss.yml @@ -1,4 +1,5 @@ id: node.rss label: RSS status: '0' +cache: '1' targetEntityType: node diff --git a/core/modules/node/config/entity.view_mode.node.teaser.yml b/core/modules/node/config/entity.view_mode.node.teaser.yml index 2089b94..636de15 100644 --- a/core/modules/node/config/entity.view_mode.node.teaser.yml +++ b/core/modules/node/config/entity.view_mode.node.teaser.yml @@ -1,4 +1,5 @@ id: node.teaser label: Teaser status: '1' +cache: '1' targetEntityType: node diff --git a/core/modules/node/lib/Drupal/node/NodeRenderController.php b/core/modules/node/lib/Drupal/node/NodeRenderController.php index e7fb844..c79f776 100644 --- a/core/modules/node/lib/Drupal/node/NodeRenderController.php +++ b/core/modules/node/lib/Drupal/node/NodeRenderController.php @@ -85,6 +85,10 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityDisp if ($entity->id()) { $build['#contextual_links']['node'] = array('node', array($entity->id())); } + + // The node 'submitted' info is not rendered in a standard way (renderable + // array) so we have to add a cache tag manually. + $build['#cache']['tags']['user'][] = $entity->uid; } } diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php index 6572ba9..3be598a 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php +++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php @@ -39,6 +39,7 @@ * uri_callback = "node_uri", * fieldable = TRUE, * translatable = TRUE, + * render_cache = FALSE, * entity_keys = { * "id" = "nid", * "revision" = "vid", diff --git a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php index 31d515f..95ce430 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php @@ -14,7 +14,7 @@ */ class NodeConditionTest extends DrupalUnitTestBase { - public static $modules = array('system', 'node', 'field'); + public static $modules = array('system', 'node', 'entity', 'field'); public static function getInfo() { return array( diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php index 4924f56..b43fc3c 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php @@ -137,6 +137,7 @@ public function testRowPlugin() { // Test with links disabled. $view->rowPlugin->options['links'] = FALSE; + \Drupal::entityManager()->getRenderController('node')->resetCache(); $output = $view->preview(); $output = drupal_render($output); $this->drupalSetContent($output); @@ -146,6 +147,7 @@ public function testRowPlugin() { // Test with links enabled. $view->rowPlugin->options['links'] = TRUE; + \Drupal::entityManager()->getRenderController('node')->resetCache(); $output = $view->preview(); $output = drupal_render($output); $this->drupalSetContent($output); @@ -155,6 +157,7 @@ public function testRowPlugin() { // Test with comments enabled. $view->rowPlugin->options['comments'] = TRUE; + \Drupal::entityManager()->getRenderController('node')->resetCache(); $output = $view->preview(); $output = drupal_render($output); foreach ($this->nodes as $node) { @@ -165,6 +168,7 @@ public function testRowPlugin() { // Test with comments disabled. $view->rowPlugin->options['comments'] = FALSE; + \Drupal::entityManager()->getRenderController('node')->resetCache(); $output = $view->preview(); $output = drupal_render($output); foreach ($this->nodes as $node) { diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 26f11a2..5586349 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -621,13 +621,15 @@ function node_revision_delete($revision_id) { * @see node_menu() */ function node_show(EntityInterface $node, $message = FALSE) { + // For markup consistency with other pages, use node_view_multiple() rather than node_view(). + $nodes = array('nodes' => node_view_multiple(array($node->id() => $node), 'full')); + if ($message) { drupal_set_title(t('Revision of %title from %date', array('%title' => $node->label(), '%date' => format_date($node->getRevisionCreationTime()))), PASS_THROUGH); + // Don't use the render cache when a revision is displayed. + unset($nodes['nodes'][$node->id()]['#cache']); } - // For markup consistency with other pages, use node_view_multiple() rather than node_view(). - $nodes = array('nodes' => node_view_multiple(array($node->id() => $node), 'full')); - // Update the history table, stating that this user viewed this node. if (module_exists('history')) { history_write($node->id()); diff --git a/core/modules/search/config/entity.view_mode.node.search_index.yml b/core/modules/search/config/entity.view_mode.node.search_index.yml index ed22c30..e12156d 100644 --- a/core/modules/search/config/entity.view_mode.node.search_index.yml +++ b/core/modules/search/config/entity.view_mode.node.search_index.yml @@ -1,4 +1,5 @@ id: node.search_index label: Search index status: '0' +cache: '1' targetEntityType: node diff --git a/core/modules/search/config/entity.view_mode.node.search_result.yml b/core/modules/search/config/entity.view_mode.node.search_result.yml index 1608657..776ada9 100644 --- a/core/modules/search/config/entity.view_mode.node.search_result.yml +++ b/core/modules/search/config/entity.view_mode.node.search_result.yml @@ -1,4 +1,5 @@ id: node.search_result label: Search result status: '0' +cache: '1' targetEntityType: node diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php index 74fa8d9..981d6c7 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php @@ -16,12 +16,13 @@ * * @var array */ - public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage'); + public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage', 'user'); protected function setUp() { parent::setUp(); $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_property_revision', 'entity_test_mulrev_property_data')); + $this->installSchema('user', array('users', 'users_roles')); $this->installSchema('system', array('url_alias')); $this->installConfig(array('field')); diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module index c40fc6a..f532340 100644 --- a/core/modules/statistics/statistics.module +++ b/core/modules/statistics/statistics.module @@ -73,6 +73,17 @@ function statistics_node_view(EntityInterface $node, EntityDisplay $display, $vi } /** + * Implements hook_node_view_alter(). + */ +function statistics_node_view_alter(&$build, EntityInterface $node, EntityDisplay $display) { + // If statistics were added to the node render array, we can't use the render + // cache. + if (isset($build['links']['statistics'])) { + unset($build['#cache']); + } +} + +/** * Implements hook_menu(). */ function statistics_menu() { diff --git a/core/modules/system/lib/Drupal/system/Form/DateFormatLocalizeResetForm.php b/core/modules/system/lib/Drupal/system/Form/DateFormatLocalizeResetForm.php index d3de481..ab4de83 100644 --- a/core/modules/system/lib/Drupal/system/Form/DateFormatLocalizeResetForm.php +++ b/core/modules/system/lib/Drupal/system/Form/DateFormatLocalizeResetForm.php @@ -105,6 +105,7 @@ public function submitForm(array &$form, array &$form_state) { foreach (config_get_storage_names_with_prefix('locale.config.' . $this->language->id . '.system.date_format.') as $config_id) { $this->configFactory->get($config_id)->delete(); } + entity_render_cache_clear(); $form_state['redirect'] = 'admin/config/regional/date-time/locale'; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php index 87e5113..757c9e9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php @@ -18,7 +18,7 @@ class ActionUnitTest extends DrupalUnitTestBase { /** * {@inheritdoc} */ - public static $modules = array('system', 'field', 'user', 'action_test'); + public static $modules = array('system','entity' , 'field', 'user', 'action_test'); /** * The action manager. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index f6a7056..de7b016 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -29,7 +29,6 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles')); $this->installSchema('system', array('variable', 'url_alias')); $this->installConfig(array('language')); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php index f9746d2..6b15158 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php @@ -31,7 +31,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles', 'users_data')); + $this->installSchema('user', array('users_data')); $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_access')); $this->installSchema('comment', array('comment', 'node_comment_statistics')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php index 2c452d4..84231d3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php @@ -43,7 +43,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles', 'users_data')); + $this->installSchema('user', array('users_data')); $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_access')); $this->installSchema('comment', array('comment', 'node_comment_statistics')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index 77c816c..776ef60 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -35,7 +35,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles', 'users_data')); + $this->installSchema('user', array('users_data')); $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_access')); $this->installSchema('entity_test', array( 'entity_test_mul', diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php index 34fb4cc..b1746ae 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -42,7 +42,7 @@ public function setUp() { $this->entityManager = $this->container->get('plugin.manager.entity'); $this->state = $this->container->get('state'); - $this->installSchema('user', 'users'); + $this->installSchema('user', array('users', 'users_roles')); $this->installSchema('system', 'sequences'); $this->installSchema('entity_test', 'entity_test'); $this->installConfig(array('field')); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php index f00a35b..8e96d3b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php @@ -37,7 +37,7 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles', 'users_data')); + $this->installSchema('user', array('users_data')); $this->installSchema('entity_test', array( 'entity_test_mul', 'entity_test_mul_property_data', diff --git a/core/modules/system/lib/Drupal/system/Tests/System/DateTimeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/DateTimeTest.php index 938c7e6..8794a88 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/DateTimeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/DateTimeTest.php @@ -65,6 +65,7 @@ function testTimeZoneHandling() { // Set time zone to Los Angeles time. $config->set('timezone.default', 'America/Los_Angeles')->save(); + \Drupal::entityManager()->getRenderController('node')->resetCache(array($node1->id(), $node2->id())); // Confirm date format and time zone. $this->drupalGet('node/' . $node1->id()); diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php index 6b1f0c5..91e4413 100644 --- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php @@ -29,7 +29,7 @@ class TypedDataTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('system', 'field', 'file'); + public static $modules = array('system', 'entity', 'field', 'file'); public static function getInfo() { return array( diff --git a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.full.yml b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.full.yml index 8902bc3..4ce40f1 100644 --- a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.full.yml +++ b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.full.yml @@ -1,4 +1,5 @@ id: entity_test_render.full label: Full status: '0' +cache: '1' targetEntityType: entity_test_render diff --git a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.test.yml b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.test.yml index a0d108b..310bceb 100644 --- a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.test.yml +++ b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test_render.test.yml @@ -1,4 +1,5 @@ id: entity_test_render.test label: Test status: '0' +cache: '1' targetEntityType: entity_test_render diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 05498f7..cd1086a 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -143,15 +143,17 @@ function entity_test_entity_bundle_info() { function entity_test_entity_view_mode_info_alter(&$view_modes) { $entity_info = entity_get_info(); foreach ($entity_info as $entity_type => $info) { - if ($entity_info[$entity_type]['module'] == 'entity_test') { + if ($entity_info[$entity_type]['module'] == 'entity_test' && !isset($view_modes[$entity_type])) { $view_modes[$entity_type] = array( 'full' => array( 'label' => t('Full object'), 'status' => TRUE, + 'cache' => TRUE, ), 'teaser' => array( 'label' => t('Teaser'), 'status' => TRUE, + 'cache' => TRUE, ), ); } diff --git a/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml b/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml index 100547e..2666012 100644 --- a/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml +++ b/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml @@ -1,4 +1,5 @@ id: taxonomy_term.full label: Taxonomy term page status: '0' +cache: '1' targetEntityType: taxonomy_term diff --git a/core/modules/user/config/entity.view_mode.user.compact.yml b/core/modules/user/config/entity.view_mode.user.compact.yml index c27265b..c211e99 100644 --- a/core/modules/user/config/entity.view_mode.user.compact.yml +++ b/core/modules/user/config/entity.view_mode.user.compact.yml @@ -1,4 +1,5 @@ id: user.compact label: Compact status: '1' +cache: '1' targetEntityType: user diff --git a/core/modules/user/config/entity.view_mode.user.full.yml b/core/modules/user/config/entity.view_mode.user.full.yml index ac1ca20..343e909 100644 --- a/core/modules/user/config/entity.view_mode.user.full.yml +++ b/core/modules/user/config/entity.view_mode.user.full.yml @@ -1,4 +1,5 @@ id: user.full label: User account status: '0' +cache: '1' targetEntityType: user diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php index 4b79760..643f14e 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php @@ -121,6 +121,8 @@ function testPictureOnNodeComment() { ->set('features.node_user_picture', FALSE) ->set('features.comment_user_picture', FALSE) ->save(); + \Drupal::entityManager()->getRenderController('node')->resetCache(array($node->id())); + \Drupal::entityManager()->getRenderController('comment')->resetCache(); $this->drupalGet('node/' . $node->id()); $this->assertNoRaw(file_uri_target($file->getFileUri()), 'User picture not found on node and comment.'); diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php index 9350338..91285d6 100644 --- a/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php +++ b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php @@ -20,7 +20,7 @@ * * @var array */ - public static $modules = array('user_test_views', 'user', 'system', 'field'); + public static $modules = array('user_test_views', 'user', 'system', 'entity', 'field'); /** * Users to use during this test. diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php index 95a543f..50e4fc0 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php @@ -20,7 +20,7 @@ * * @var array */ - public static $modules = array('system', 'user', 'field'); + public static $modules = array('system', 'user', 'entity', 'field'); /** * Overrides \Drupal\views\Tests\ViewUnitTestBase::setUpFixtures(). diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php index 78bbeca..9977931 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php @@ -1243,4 +1243,18 @@ public function mergeDefaultDisplaysOptions() { public function uriRelationships() { return $this->storage->uriRelationships(); } + + /** + * {@inheritdoc} + */ + public function relatedEntities() { + return $this->storage->relatedEntities(); + } + + /** + * {@inheritdoc} + */ + public function changed() { + return $this->storage->changed(); + } }