diff --git a/core/includes/common.inc b/core/includes/common.inc index 4668a90..fe1c9d9 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -5478,7 +5478,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); } @@ -5526,6 +5526,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 53d8bcf..32c9da1 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -46,6 +46,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 @@ -579,14 +591,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)); + } + return $render_controller->view($entity, $view_mode, $langcode); } /** @@ -599,15 +616,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($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 30c8252..cd0b139 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/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index 6bb91e1..03a3cfe 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -538,6 +538,7 @@ public function save(EntityInterface $entity) { $this->saveRevision($entity); } $this->resetCache(array($entity->id())); + $this->postSave($entity, TRUE); $this->invokeHook('update', $entity); } diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php index 9b7020a..2a0f42e 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php @@ -305,6 +305,7 @@ public function save(EntityInterface $entity) { $this->savePropertyData($entity); } $this->resetCache(array($entity->id())); + $this->postSave($entity, TRUE); $this->invokeHook('update', $entity); } diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php index 98d4a3d..5eeb08a 100644 --- a/core/lib/Drupal/Core/Entity/EntityFormController.php +++ b/core/lib/Drupal/Core/Entity/EntityFormController.php @@ -263,7 +263,13 @@ public function submit(array $form, array &$form_state) { * A reference to a keyed array containing the current state of the form. */ public function save(array $form, array &$form_state) { - // @todo Perform common save operations. + // Clear the render cache. + try { + \Drupal::entityManager()->getRenderController($this->entity->entityType())->resetCache(array($this->entity)); + } + catch (\Exception $e) { + // Nothing to do if the entity type doesn't have a render controller. + } } /** diff --git a/core/lib/Drupal/Core/Entity/EntityRenderController.php b/core/lib/Drupal/Core/Entity/EntityRenderController.php index 53457aa..ee5aa65 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderController.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderController.php @@ -20,8 +20,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); } /** @@ -60,6 +89,21 @@ 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. + $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) { + $return['#cache'] = array( + 'keys' => array('entity_view', $this->entityType ,$entity->id(), $view_mode), + 'granularity' => DRUPAL_CACHE_PER_ROLE, + 'bin' => $this->cacheBin, + 'tags' => array( + $this->entityType . '_view' => TRUE, + $this->entityType => array($entity->id()), + ), + ); + } + return $return; } @@ -162,4 +206,35 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la return $build; } + + /** + * Implements \Drupal\Core\Entity\EntityRenderControllerInterface::resetCache(). + */ + public function resetCache(array $entities = NULL) { + if (isset($entities)) { + $tags = array(); + foreach ($entities as $entity) { + $tags[$this->entityType][$entity->id()] = $entity->id(); + + // @todo Remove when all entities are converted to EntityNG. + if ($entity->getPropertyDefinitions() === NULL) { + continue; + } + + // Add all the referenced entity types and IDs to the tags that will be + // cleared. + foreach ($entity->getPropertyDefinitions() as $name => $definition) { + if ($definition['type'] == 'entity_reference_field' && $field_values = $entity->get($name)->getValue()) { + foreach ($field_values as $value) { + $tags[$definition['settings']['target_type']][$value['target_id']] = $value['target_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..88e1461 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php @@ -75,4 +75,12 @@ 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 $entities + * (optional) If specified, the cache is reset for the given entities only. + */ + public function resetCache(array $entities = NULL); } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php index 459a99b..50adbb9 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedFormController.php @@ -111,6 +111,8 @@ public function validate(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $feed = $this->entity; $insert = (bool) $feed->id(); if (!empty($form_state['values']['category'])) { 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/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php index 1a09dbf..b548634 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php @@ -159,6 +159,8 @@ public function submit(array $form, array &$form_state) { * Overrides \Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $block = $this->entity; $insert = empty($block->id->value); $block->save(); diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php index 6e5de33..bb9159a 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeFormController.php @@ -87,6 +87,8 @@ public function form(array $form, array &$form_state) { * Overrides \Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $block_type = $this->entity; $status = $block_type->save(); diff --git a/core/modules/block/lib/Drupal/block/BlockRenderController.php b/core/modules/block/lib/Drupal/block/BlockRenderController.php index 12fff82..deaeb94 100644 --- a/core/modules/block/lib/Drupal/block/BlockRenderController.php +++ b/core/modules/block/lib/Drupal/block/BlockRenderController.php @@ -44,4 +44,10 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la return $build; } + /** + * Implements \Drupal\Core\Entity\EntityRenderControllerInterface::resetCache(). + */ + public function resetCache(array $entities = 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.admin.inc b/core/modules/comment/comment.admin.inc index 488c2f9..cf29b50 100644 --- a/core/modules/comment/comment.admin.inc +++ b/core/modules/comment/comment.admin.inc @@ -203,6 +203,7 @@ function comment_admin_overview_submit($form, &$form_state) { } drupal_set_message(t('The update has been performed.')); $form_state['redirect'] = 'admin/content/comment'; + Drupal::entityManager()->getRenderController('comment')->resetCache(array($comment)); cache_invalidate_tags(array('content' => TRUE)); } @@ -250,8 +251,15 @@ function comment_multiple_delete_confirm($form, &$form_state) { */ function comment_multiple_delete_confirm_submit($form, &$form_state) { if ($form_state['values']['confirm']) { - comment_delete_multiple(array_keys($form_state['values']['comments'])); + $entity_manager = \Drupal::entityManager(); + + $controller = $entity_manager->getStorageController('comment'); + $entities = $controller->load(array_keys($form_state['values']['comments'])); + $controller->delete($entities); + + $entity_manager->getRenderController('comment')->resetCache($entities); cache_invalidate_tags(array('content' => TRUE)); + $count = count($form_state['values']['comments']); watchdog('content', 'Deleted @count comments.', array('@count' => $count)); drupal_set_message(format_plural($count, 'Deleted 1 comment.', 'Deleted @count comments.')); @@ -307,6 +315,7 @@ function comment_confirm_delete_submit($form, &$form_state) { drupal_set_message(t('The comment and all its replies have been deleted.')); watchdog('content', 'Deleted comment @cid and its replies.', array('@cid' => $comment->id())); // Clear the cache so an anonymous user sees that his comment was deleted. + Drupal::entityManager()->getRenderController('comment')->resetCache(array($comment)); cache_invalidate_tags(array('content' => TRUE)); $form_state['redirect'] = "node/{$comment->nid->target_id}"; diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 717a25b..bf334e9 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1920,6 +1920,7 @@ function comment_unpublish_by_keyword_action_submit($form, $form_state) { */ function comment_save_action(Comment $comment) { comment_save($comment); + Drupal::entityManager()->getRenderController('comment')->resetCache(array($comment)); cache_invalidate_tags(array('content' => TRUE)); watchdog('action', 'Saved comment %title', array('%title' => $comment->subject->value)); } diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc index 5e1b190..371da22 100644 --- a/core/modules/comment/comment.pages.inc +++ b/core/modules/comment/comment.pages.inc @@ -80,6 +80,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? @@ -118,6 +119,7 @@ function comment_approve(Comment $comment) { $comment->status->value = COMMENT_PUBLISHED; $comment->save(); + Drupal::entityManager()->getRenderController('comment')->resetCache(array($comment)); drupal_set_message(t('Comment approved.')); drupal_goto('node/' . $comment->nid->target_id); 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/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php index 179410c..a2fecc6 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php @@ -309,6 +309,8 @@ public function preview(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $node = node_load($form_state['values']['nid']); $comment = $this->entity; diff --git a/core/modules/comment/lib/Drupal/comment/CommentRenderController.php b/core/modules/comment/lib/Drupal/comment/CommentRenderController.php index dab8dc4..0e1ce5e 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/Tests/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php index fe4a11c..aff941d 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php @@ -267,6 +267,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/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php index efba401..1c3894f 100644 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php @@ -64,6 +64,8 @@ public function form(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $entity = $this->entity; $status = $entity->save(); diff --git a/core/modules/contact/lib/Drupal/contact/CategoryFormController.php b/core/modules/contact/lib/Drupal/contact/CategoryFormController.php index 8febed8..ee95b57 100644 --- a/core/modules/contact/lib/Drupal/contact/CategoryFormController.php +++ b/core/modules/contact/lib/Drupal/contact/CategoryFormController.php @@ -94,6 +94,8 @@ public function validate(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $category = $this->entity; $status = $category->save(); diff --git a/core/modules/contact/lib/Drupal/contact/MessageFormController.php b/core/modules/contact/lib/Drupal/contact/MessageFormController.php index 6f73d96..a754711 100644 --- a/core/modules/contact/lib/Drupal/contact/MessageFormController.php +++ b/core/modules/contact/lib/Drupal/contact/MessageFormController.php @@ -137,6 +137,8 @@ public function preview(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + global $user; $language_interface = language(LANGUAGE_TYPE_INTERFACE); diff --git a/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php index 461a240..014636c 100644 --- a/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php @@ -120,7 +120,17 @@ public function save() { if (empty($this->id)) { $this->id = $this->id(); } - return parent::save(); + $return = parent::save(); + + // Reset the render cache for the target entity type. + try { + \Drupal::entityManager()->getRenderController($this->targetEntityType)->resetCache(); + } + catch (\Exception $e) { + // Nothing to do if the entity type doesn't have a render controller. + } + + return $return; } /** diff --git a/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php index c2c54dd..a29bb82 100644 --- a/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php +++ b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php @@ -91,4 +91,11 @@ class EntityViewMode extends ConfigEntityBase implements EntityViewModeInterface */ public $status = FALSE; + /** + * Whether or not the rendered output of this view mode is cached by default. + * + * @var bool + */ + public $cache = TRUE; + } 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 6b316ae..445cd11 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 @@ -106,6 +106,14 @@ public function view(EntityInterface $entity, $langcode, array $items) { '#formatter' => $this->getPluginId(), ); + // Gather cache tags from reference fields. + foreach ($items as $item) { + if (isset($item['entity'])) { + $info['#cache']['tags'][$item['entity']->entityType()][] = $item['entity']->id(); + $info['#cache']['tags'][$item['entity']->entityType() . '_view'] = TRUE; + } + } + $addition[$field['field_name']] = array_merge($info, $elements); } diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityFormController.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityFormController.php index d5a4508..7d48c0d 100644 --- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityFormController.php +++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityFormController.php @@ -37,6 +37,8 @@ public function form(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $entity = $this->entity; $is_new = $entity->isNew(); $entity->save(); diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php index c1a490d..8c56588 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php @@ -496,6 +496,9 @@ public function submitForm(array &$form, array &$form_state) { field_bundle_settings($this->entity_type, $this->bundle, $bundle_settings); } + // Clear the render cache for this entity type. + \Drupal::entityManager()->getRenderController($this->entity_type)->resetCache(); + drupal_set_message(t('Your settings have been saved.')); } diff --git a/core/modules/file/config/entity.view_mode.file.full.yml b/core/modules/file/config/entity.view_mode.file.full.yml index 60808f9..4d4ed1f 100644 --- a/core/modules/file/config/entity.view_mode.file.full.yml +++ b/core/modules/file/config/entity.view_mode.file.full.yml @@ -1,4 +1,5 @@ id: file.full label: File default status: '0' +cache: '1' targetEntityType: file diff --git a/core/modules/filter/filter.admin.inc b/core/modules/filter/filter.admin.inc index aa3018c..6430687 100644 --- a/core/modules/filter/filter.admin.inc +++ b/core/modules/filter/filter.admin.inc @@ -358,5 +358,6 @@ function filter_admin_format_form_submit($form, &$form_state) { break; } + entity_render_cache_clear(); $form_state['redirect'] = 'admin/config/content/formats'; } diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php index c30c686..fb8e716 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php @@ -229,6 +229,7 @@ function testImageFieldDefaultImage() { $this->drupalPost("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings')); // Clear field info cache so the new default image is detected. field_info_cache_clear(); + \Drupal::entityManager()->getRenderController('node')->resetCache(array($node)); $field = field_info_field($field_name); $image = file_load($field['settings']['default_image'][0]); $this->assertTrue($image->status == FILE_STATUS_PERMANENT, 'The default image status is permanent.'); diff --git a/core/modules/menu/lib/Drupal/menu/MenuFormController.php b/core/modules/menu/lib/Drupal/menu/MenuFormController.php index 4eeab27..7aeac04 100644 --- a/core/modules/menu/lib/Drupal/menu/MenuFormController.php +++ b/core/modules/menu/lib/Drupal/menu/MenuFormController.php @@ -83,6 +83,8 @@ protected function actions(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $menu = $this->entity; $system_menus = menu_list_system_menus(); diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php index 79bd8de..09e978d 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php @@ -202,8 +202,9 @@ public function submit(array $form, array &$form_state) { * Overrides EntityFormController::save(). */ public function save(array $form, array &$form_state) { - $menu_link = $this->entity; + parent::save($form, $form_state); + $menu_link = $this->entity; $saved = $menu_link->save(); if ($saved) { 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/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index 5145a57..a6d6042 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -427,6 +427,8 @@ public function unpublish(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $node = $this->entity; $insert = empty($node->nid); $node->save(); diff --git a/core/modules/node/lib/Drupal/node/NodeRenderController.php b/core/modules/node/lib/Drupal/node/NodeRenderController.php index 6a6608a..5973fbd 100644 --- a/core/modules/node/lib/Drupal/node/NodeRenderController.php +++ b/core/modules/node/lib/Drupal/node/NodeRenderController.php @@ -91,6 +91,10 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityDisp if (!empty($entity->nid)) { $build['#contextual_links']['node'] = array('node', array($entity->nid)); } + + // 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/Tests/NodeRevisionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php index 46244b8..dba9816 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php @@ -139,6 +139,7 @@ function testRevisions() { $new_node_revision->setNewRevision(); $new_node_revision->isDefaultRevision = FALSE; node_save($new_node_revision); + \Drupal::entityManager()->getRenderController('node')->resetCache(array($node)); $this->drupalGet("node/$node->nid"); $this->assertNoText($new_body, 'Revision body text is not present on default version of node.'); 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 6a28f7f..7a0373c 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php @@ -136,6 +136,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); @@ -145,6 +146,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); @@ -154,6 +156,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) { @@ -164,6 +167,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 ae25bb1..4c91efe 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1039,16 +1039,18 @@ 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->revision_timestamp))), 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->nid => $node), 'full')); - // Update the history table, stating that this user viewed this node. if (module_exists('history')) { - history_write($node->nid); + history_write($node->id()); } return $nodes; @@ -2208,7 +2210,7 @@ function _node_index_node(EntityInterface $node) { foreach ($languages as $language) { // Render the node. - $build = node_view($node, 'search_index', $language->langcode); + $build = entity_view($node, 'search_index', $language->langcode, TRUE); unset($build['#theme']); $node->rendered = drupal_render($build); diff --git a/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php b/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php index 4b93dc5..55f969b 100644 --- a/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php +++ b/core/modules/picture/lib/Drupal/picture/PictureMappingFormController.php @@ -134,6 +134,8 @@ public function validate(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $picture_mapping = $this->entity; $picture_mapping->save(); diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php index dbdba31..6a67605 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php @@ -285,6 +285,7 @@ function saveComment($nid, $uid, $contact = NULL, $pid = 0) { $comment = entity_create('comment', $values); $comment->save(); + \Drupal::entityManager()->getRenderController('comment')->resetCache(array($comment)); return $comment; } } 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/shortcut/lib/Drupal/shortcut/ShortcutFormController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php index 1e25679..0eed29e 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php @@ -73,6 +73,8 @@ public function validate(array $form, array &$form_state) { * Overrides \Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $entity = $this->entity; $is_new = !$entity->getOriginalID(); $entity->save(); diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module index 9d0c19e..4b867e6 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/Tests/System/DateTimeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/DateTimeTest.php index c867581..f2afd30 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('default', 'America/Los_Angeles')->save(); + \Drupal::entityManager()->getRenderController('node')->resetCache(array($node1, $node2)); // Confirm date format and time zone. $this->drupalGet("node/$node1->nid"); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index d344d37..f101504 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -2327,6 +2327,7 @@ function system_date_format_localize_reset_form($form, &$form_state, $langcode) function system_date_format_localize_reset_form_submit($form, &$form_state) { config('locale.config.' . $form['langcode']['#value'] . '.system.date') ->delete(); + entity_render_cache_clear(); $form_state['redirect'] = 'admin/config/regional/date-time/locale'; } 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/lib/Drupal/entity_test/EntityTestFormController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php index acea3a1..f1431df 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php @@ -57,6 +57,8 @@ public function form(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $entity = $this->entity; $is_new = $entity->isNew(); $entity->save(); 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/taxonomy/config/entity.view_mode.taxonomy_vocabulary.full.yml b/core/modules/taxonomy/config/entity.view_mode.taxonomy_vocabulary.full.yml index bb47091..8961a6f 100644 --- a/core/modules/taxonomy/config/entity.view_mode.taxonomy_vocabulary.full.yml +++ b/core/modules/taxonomy/config/entity.view_mode.taxonomy_vocabulary.full.yml @@ -1,4 +1,5 @@ id: vocabulary.full label: Taxonomy vocabulary status: '0' +cache: '1' targetEntityType: taxonomy_vocabulary diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php index 24d252b..bc9e8f1 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php @@ -154,6 +154,8 @@ public function buildEntity(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $term = $this->entity; switch ($term->save()) { diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index b9b1108..c175964 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -333,6 +333,7 @@ function testTermInterface() { // Check that it does NOT show a description when description is blank. $term->description = ''; $term->save(); + \Drupal::entityManager()->getRenderController('taxonomy_term')->resetCache(array($term)); $this->drupalGet('taxonomy/term/' . $term->id()); $this->assertNoPattern('|class="taxonomy-term-description"|', 'Term page did not display the term description when description was blank.'); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php index 96e8697..eab308d 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php @@ -144,6 +144,8 @@ public function submit(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $vocabulary = $this->entity; // Prevent leading and trailing spaces in vocabulary names. diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc index 0703071..8a73c87 100644 --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -513,13 +513,15 @@ function taxonomy_term_confirm_delete($form, &$form_state, Term $term) { * @see taxonomy_term_confirm_delete() */ function taxonomy_term_confirm_delete_submit($form, &$form_state) { - entity_delete_multiple('taxonomy_term', array($form_state['values']['tid'])); + $entity = taxonomy_term_load($form_state['values']['tid']); + $entity->delete(); taxonomy_check_vocabulary_hierarchy($form_state['taxonomy']['vocabulary'], $form_state['values']); drupal_set_message(t('Deleted term %name.', array('%name' => $form_state['values']['name']))); watchdog('taxonomy', 'Deleted term %name.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE); if (!isset($_GET['destination'])) { $form_state['redirect'] = 'admin/structure/taxonomy'; } + Drupal::entityManager()->getRenderController('taxonomy_term')->resetCache(array($entity)); cache_invalidate_tags(array('content' => TRUE)); return; } 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/ProfileFormController.php b/core/modules/user/lib/Drupal/user/ProfileFormController.php index 484a4af..8e72e76 100644 --- a/core/modules/user/lib/Drupal/user/ProfileFormController.php +++ b/core/modules/user/lib/Drupal/user/ProfileFormController.php @@ -41,6 +41,8 @@ public function submit(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $account = $this->entity; $account->save(); $form_state['values']['uid'] = $account->id(); diff --git a/core/modules/user/lib/Drupal/user/RegisterFormController.php b/core/modules/user/lib/Drupal/user/RegisterFormController.php index ceecd65..3c0bdbd 100644 --- a/core/modules/user/lib/Drupal/user/RegisterFormController.php +++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php @@ -93,6 +93,8 @@ public function submit(array $form, array &$form_state) { * Overrides Drupal\Core\Entity\EntityFormController::submit(). */ public function save(array $form, array &$form_state) { + parent::save($form, $form_state); + $account = $this->entity; $pass = $account->pass; $admin = $form_state['values']['administer_users']; diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 322311f..7e316c8 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1963,6 +1963,8 @@ function user_role_grant_permissions($rid, array $permissions = array()) { // Clear the user access cache. drupal_static_reset('user_access'); drupal_static_reset('user_role_permissions'); + // Entity render cache is based on user roles, so we need to clear it as well. + entity_render_cache_clear(); } /** @@ -1986,6 +1988,8 @@ function user_role_revoke_permissions($rid, array $permissions = array()) { // Clear the user access cache. drupal_static_reset('user_access'); drupal_static_reset('user_role_permissions'); + // Entity render cache is based on user roles, so we need to clear it as well. + entity_render_cache_clear(); } /**