diff --git a/core/includes/common.inc b/core/includes/common.inc index ab11ddf..51d1aaa 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3895,7 +3895,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) { // Collect all #post_render_cache callbacks associated with this element when: // - about to store this element in the render cache, or when; // - about to apply #post_render_cache callbacks. - if (isset($elements['#cache']) || !$is_recursive_call) { + if (isset($elements['#cache'])) { $post_render_cache = drupal_render_collect_post_render_cache($elements); if ($post_render_cache) { $elements['#post_render_cache'] = $post_render_cache; @@ -4005,6 +4005,15 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) { $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; $elements['#markup'] = $prefix . $elements['#children'] . $suffix; + + // Collect any post_render_cache callbacks that were added during theming. + if (isset($elements['#cache']) || !$is_recursive_call) { + $post_render_cache = drupal_render_collect_post_render_cache($elements); + if ($post_render_cache) { + $elements['#post_render_cache'] = $post_render_cache; + } + } + // Cache the processed element if #cache is set. if (isset($elements['#cache'])) { drupal_render_cache_set($elements['#markup'], $elements); diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 7f86532..bb375ad 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -13,6 +13,7 @@ use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\TypedData\TranslatableInterface; use Drupal\entity\Entity\EntityViewDisplay; +use Drupal\Core\Render\Element; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -92,8 +93,10 @@ public static function createInstance(ContainerInterface $container, EntityTypeI public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) { $entities_by_bundle = array(); foreach ($entities as $id => $entity) { - // Remove previously built content, if exists. - $entity->content = array( + if (empty($entity->content)) { + $entity->content = array(); + } + $entity->content += array( '#view_mode' => $view_mode, ); // Initialize the field item attributes for the fields being displayed. @@ -120,7 +123,9 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang foreach ($entities_by_bundle as $bundle => $bundle_entities) { $build = $displays[$bundle]->buildMultiple($bundle_entities); foreach ($bundle_entities as $id => $entity) { - $entity->content += $build[$id]; + $entity->content += array( + '#view_mode' => $view_mode, + ) + $build[$id]; } } } @@ -144,6 +149,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco "#{$this->entityTypeId}" => $entity, '#view_mode' => $view_mode, '#langcode' => $langcode, + '#pre_render' => array(array($this, 'entityViewBuilderPreRender')), ); // Cache the rendered output if permitted by the view mode and global entity @@ -168,6 +174,64 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco } /** + * Performs pre-render tasks on an entity view. + * + * This function is assigned as a #pre_render callback in + * \Drupal\Core\Entity\EntityViewBuilder::getBuildDefaults(). + * + * @param array $elements + * A structured array containing build information and context for an + * entity view. + * + * @see drupal_render() + */ + public function entityViewBuilderPreRender(array $elements) { + $entity = $elements['#entity']; + $bundle = $entity->bundle(); + $view_hook = "{$this->entityTypeId}_view"; + $view_mode = $elements['#view_mode']; + $langcode = $elements['#langcode']; + $context = array('langcode' => $langcode); + + // Allow modules to change the view mode. + $this->moduleHandler->alter('entity_view_mode', $view_mode, $entity, $context); + + // Get the corresponding display settings. + $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode); + + // Build field renderables. + $entity->content = $elements; + $this->buildContent(array($entity->id() => $entity), array($bundle => $display), $view_mode, $langcode); + $view_mode = isset($entity->content['#view_mode']) ? $entity->content['#view_mode'] : $view_mode; + + $this->moduleHandler()->invokeAll($view_hook, array($entity, $display, $view_mode, $langcode)); + $this->moduleHandler()->invokeAll('entity_view', array($entity, $display, $view_mode, $langcode)); + + // Do not override $build = $elements because hook_view implementations + // are expected to add content, not alter it. For example, mymodule_view + // should not change the #theme key. + $build = $entity->content; + // We don't need duplicate rendering info in $entity->content. + unset($entity->content); + + $this->alterBuild($build, $entity, $display, $view_mode, $langcode); + + // Assign the weights configured in the display. + // @todo: Once https://drupal.org/node/1875974 provides the missing API, + // only do it for 'extra fields', since other components have been taken + // care of in EntityViewDisplay::buildMultiple(). + foreach ($display->getComponents() as $name => $options) { + if (isset($build[$name])) { + $build[$name]['#weight'] = $options['weight']; + } + } + + // Allow modules to modify the render array. + \Drupal::moduleHandler()->alter(array($view_hook, 'entity_view'), $build, $entity, $display); + return $build; + } + + /** * Specific per-entity building. * * @param array $build @@ -202,58 +266,24 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la } // Build the view modes and display objects. - $view_modes = array(); - $context = array('langcode' => $langcode); - foreach ($entities as $key => $entity) { - $bundle = $entity->bundle(); + $build = array('#sorted' => TRUE); + $weight = 0; + foreach ($entities as $key => $entity) { // Ensure that from now on we are dealing with the proper translation // object. $entity = $this->entityManager->getTranslationFromContext($entity, $langcode); $entities[$key] = $entity; - // Allow modules to change the view mode. - $entity_view_mode = $view_mode; - $this->moduleHandler->alter('entity_view_mode', $entity_view_mode, $entity, $context); - // Store entities for rendering by view_mode. - $view_modes[$entity_view_mode][$entity->id()] = $entity; - } - - foreach ($view_modes as $mode => $view_mode_entities) { - $displays[$mode] = EntityViewDisplay::collectRenderDisplays($view_mode_entities, $mode); - $this->buildContent($view_mode_entities, $displays[$mode], $mode, $langcode); - } + //$build[$key] = $entity->content; + $build[$key] = array( + '#entity' => $entity + ); - $view_hook = "{$this->entityTypeId}_view"; - $build = array('#sorted' => TRUE); - $weight = 0; - foreach ($entities as $key => $entity) { - $entity_view_mode = isset($entity->content['#view_mode']) ? $entity->content['#view_mode'] : $view_mode; - $display = $displays[$entity_view_mode][$entity->bundle()]; - \Drupal::moduleHandler()->invokeAll($view_hook, array($entity, $display, $entity_view_mode, $langcode)); - \Drupal::moduleHandler()->invokeAll('entity_view', array($entity, $display, $entity_view_mode, $langcode)); - - $build[$key] = $entity->content; - // We don't need duplicate rendering info in $entity->content. - unset($entity->content); - - $build[$key] += $this->getBuildDefaults($entity, $entity_view_mode, $langcode); - $this->alterBuild($build[$key], $entity, $display, $entity_view_mode, $langcode); - - // Assign the weights configured in the display. - // @todo: Once https://drupal.org/node/1875974 provides the missing API, - // only do it for 'extra fields', since other components have been taken - // care of in EntityViewDisplay::buildMultiple(). - foreach ($display->getComponents() as $name => $options) { - if (isset($build[$key][$name])) { - $build[$key][$name]['#weight'] = $options['weight']; - } - } + // Set defaults for #pre_render. + $build[$key] += $this->getBuildDefaults($entity, $view_mode, $langcode); $build[$key]['#weight'] = $weight++; - - // Allow modules to modify the render array. - $this->moduleHandler->alter(array($view_hook, 'entity_view'), $build[$key], $entity, $display); } return $build; diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 0070378..51d0561 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1312,17 +1312,14 @@ function comment_preview(CommentInterface $comment, array &$form_state) { // includes the comment reply form, which contains the comment preview and // therefore the rendered parent entity. This results in an infinite loop of // parent entity output rendering the comment form and the comment form - // rendering the parent entity. To prevent this infinite loop we temporarily - // set the value of the comment field on the rendered entity to hidden + // rendering the parent entity. To prevent this infinite loop we clone + // the commented entity and change the value of the comment field to hidden // before calling entity_view(). That way when the output of the commented - // entity is rendered, it excludes the comment field output. As objects are - // always addressed by reference we ensure changes are not lost by setting - // the value back to its original state after the call to entity_view(). + // entity is rendered, it excludes the comment field output. + $entity = clone $entity; $field_name = $comment->getFieldName(); - $original_status = $entity->get($field_name)->status; $entity->get($field_name)->status = COMMENT_HIDDEN; $build = entity_view($entity, 'full'); - $entity->get($field_name)->status = $original_status; } $preview_build['comment_output_below'] = $build;