diff --git a/core/lib/Drupal/Core/Cache/RuntimeCacheContextsDependencyInterface.php b/core/lib/Drupal/Core/Cache/RuntimeCacheContextsDependencyInterface.php new file mode 100644 index 0000000..1971d59 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/RuntimeCacheContextsDependencyInterface.php @@ -0,0 +1,27 @@ +runtimeCacheContexts; } /** @@ -449,6 +456,9 @@ public function getCacheContexts() { public function getCacheTags() { // @todo Add bundle-specific listing cache tag? // https://www.drupal.org/node/2145751 + if ($this->isNew()) { + return []; + } return [$this->entityTypeId . ':' . $this->id()]; } @@ -608,4 +618,10 @@ public function getConfigTarget() { return $this->uuid(); } + /** + * {@inheritdoc} + */ + public function addRuntimeCacheContexts(array $cache_contexts) { + $this->runtimeCacheContexts = Cache::mergeContexts($this->runtimeCacheContexts, $cache_contexts); + } } diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php index 6d61d0c..8fca582 100644 --- a/core/lib/Drupal/Core/Entity/EntityForm.php +++ b/core/lib/Drupal/Core/Entity/EntityForm.php @@ -101,6 +101,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { $this->init($form_state); } + // Ensure that edit forms have the correct cacheable metadata so they can be + // cached. + if (!$this->entity->isNew()) { + \Drupal::service('renderer')->addCacheableDependency($form, $this->entity); + } + // Retrieve the form array using the possibly updated entity in form state. $form = $this->form($form, $form_state); diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index 68dafd0..495ffaa 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -9,13 +9,14 @@ use Drupal\Core\Access\AccessibleInterface; use Drupal\Core\Cache\CacheableDependencyInterface; +use Drupal\Core\Cache\RuntimeCacheContextsDependencyInterface; /** * Defines a common interface for all entity objects. * * @ingroup entity_api */ -interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface { +interface EntityInterface extends AccessibleInterface, CacheableDependencyInterface, RuntimeCacheContextsDependencyInterface { /** * Gets the entity UUID (Universally Unique Identifier). diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 1e1323e..dd76354 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -969,6 +969,7 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) { if (empty($langcode)) { $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); + $entity->addRuntimeCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]); } // Retrieve language fallback candidates to perform the entity language diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 09736d2..8281eb9 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -117,14 +117,10 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N * {@inheritdoc} */ public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { - if (!isset($langcode)) { - $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); - } - $build_list = array( '#sorted' => TRUE, '#pre_render' => array(array($this, 'buildMultiple')), - '#langcode' => $langcode, + '#langcode' => $langcode ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(), ); $weight = 0; foreach ($entities as $key => $entity) { @@ -133,9 +129,10 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la $entity = $this->entityManager->getTranslationFromContext($entity, $langcode); // Set build defaults. - $build_list[$key] = $this->getBuildDefaults($entity, $view_mode, $langcode); + $entity_langcode = $entity->language()->getId(); + $build_list[$key] = $this->getBuildDefaults($entity, $view_mode, $entity_langcode); $entityType = $this->entityTypeId; - $this->moduleHandler()->alter(array($entityType . '_build_defaults', 'entity_build_defaults'), $build_list[$key], $entity, $view_mode, $langcode); + $this->moduleHandler()->alter(array($entityType . '_build_defaults', 'entity_build_defaults'), $build_list[$key], $entity, $view_mode, $entity_langcode); $build_list[$key]['#weight'] = $weight++; } diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php index 6923220..d9ae407 100644 --- a/core/modules/comment/src/CommentViewBuilder.php +++ b/core/modules/comment/src/CommentViewBuilder.php @@ -71,11 +71,9 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco ->getFieldDefinition($entity->getFieldName()) ->getSetting('default_mode') === CommentManagerInterface::COMMENT_MODE_THREADED; // If threading is enabled, don't render cache individual comments, but do - // keep the cache tags, so they can bubble up. + // keep the cacheable metadata, so it can bubble up. if ($build['#comment_threaded']) { - $cache_tags = $build['#cache']['tags']; - $build['#cache'] = []; - $build['#cache']['tags'] = $cache_tags; + unset($build['#cache']['keys']); } return $build; diff --git a/core/modules/comment/src/Tests/CommentTranslationUITest.php b/core/modules/comment/src/Tests/CommentTranslationUITest.php index cfb63c8..1e6d716 100644 --- a/core/modules/comment/src/Tests/CommentTranslationUITest.php +++ b/core/modules/comment/src/Tests/CommentTranslationUITest.php @@ -33,6 +33,20 @@ class CommentTranslationUITest extends ContentTranslationUITestBase { protected $adminUser; /** + * Default cache contexts expected on a non-translated entity. + * + * @var array + */ + protected $defaultCacheContexts = [ + 'languages:language_interface', + 'theme', + 'user.permissions', + 'timezone', + 'url.query_args.pagers:0', + 'user.roles' + ]; + + /** * Modules to install. * * @var array diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php index e524359..a9c87ec 100644 --- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php +++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php @@ -7,18 +7,22 @@ namespace Drupal\content_translation\Tests; +use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Component\Utility\SafeMarkup; +use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait; /** * Tests the Content Translation UI. */ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase { + use AssertPageCacheContextsAndTagsTrait; + /** * The id of the entity being translated. * @@ -34,6 +38,15 @@ protected $testLanguageSelector = TRUE; /** + * Default cache contexts expected on a non-translated entity. + * + * Cache contexts will not be checked if this list is empty. + * + * @var array + */ + protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user.permissions']; + + /** * Tests the basic translation UI. */ function testTranslationUI() { @@ -64,6 +77,11 @@ protected function doTestBasicTranslation() { $this->assertTrue($entity, 'Entity found in the database.'); $this->drupalGet($entity->urlInfo()); $this->assertResponse(200, 'Entity URL is valid.'); + + // Ensure that the content language cache context is not yet added to the + // page. + $this->assertCacheContexts($this->defaultCacheContexts); + $this->drupalGet($entity->urlInfo('drupal:content-translation-overview')); $this->assertNoText('Source language', 'Source language column correctly hidden.'); @@ -87,9 +105,14 @@ protected function doTestBasicTranslation() { ], array('language' => $language)); $this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), $this->getFormSubmitActionForNewTranslation($entity, $langcode)); - // Get the entity and reset its cache, so that the new translation gets the + // Ensure that the content language cache context is not yet added to the + // page. + $entity = entity_load($this->entityTypeId, $this->entityId, TRUE); + $this->drupalGet($entity->urlInfo()); + $this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts)); + + // Reset the cache of the entity, so that the new translation gets the // updated values. - $entity = entity_load($this->entityTypeId, $this->entityId, TRUE); $metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode)); $metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode)); diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php index b068808..faab9e6 100644 --- a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php @@ -18,6 +18,13 @@ class MenuLinkContentTranslationUITest extends ContentTranslationUITestBase { /** + * Default cache contexts expected on a non-translated entity. + * + * @var array + */ + protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user.permissions', 'user.roles:authenticated']; + + /** * Modules to enable. * * @var array diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php index 287920b..5476ed9 100644 --- a/core/modules/node/src/Tests/NodeTranslationUITest.php +++ b/core/modules/node/src/Tests/NodeTranslationUITest.php @@ -22,6 +22,23 @@ class NodeTranslationUITest extends ContentTranslationUITestBase { /** + * Default cache contexts expected on a non-translated entity. + * + * @var array + */ + protected $defaultCacheContexts = [ + 'languages:language_interface', + 'theme', + 'user.permissions', + 'route.menu_active_trails:account', + 'route.menu_active_trails:footer', + 'route.menu_active_trails:main', + 'route.menu_active_trails:tools', + 'timezone', + 'user.roles' + ]; + + /** * Modules to enable. * * @var array diff --git a/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php b/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php index 3fc8232..007baa4 100644 --- a/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php +++ b/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php @@ -19,6 +19,13 @@ class ShortcutTranslationUITest extends ContentTranslationUITestBase { /** + * Default cache contexts expected on a non-translated entity. + * + * @var array + */ + protected $defaultCacheContexts = ['languages:language_interface', 'theme' , 'user']; + + /** * Modules to enable. * * @var array diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 05451be..b738d94 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -1336,4 +1336,11 @@ public function hasTrustedData() { return $this->storage->hasTrustedData(); } + /** + * {@inheritdoc} + */ + public function addRuntimeCacheContexts(array $cache_contexts) { + $this->storage->addRuntimeCacheContexts($cache_contexts); + } + }