From c312e5ee5589d060601f633deba3be26df494815 Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Sat, 1 Nov 2014 17:31:26 -0400 Subject: Issue #1968348 by znerol, David_Rothstein, peximo, DuaelFr: Fixed hook_field_formatter_prepare_view does not make use of hook_entity_view_mode_alter causing major errors. --- CHANGELOG.txt | 3 +++ includes/common.inc | 50 ++++++++++++++++++++++++++++++++++++++++ modules/comment/comment.module | 28 ++++++++++++---------- modules/node/node.module | 24 +++++++++++-------- modules/node/node.test | 43 ++++++++++++++++++++++++++++++++-- modules/taxonomy/taxonomy.module | 24 +++++++++++-------- modules/user/user.module | 7 +----- 7 files changed, 141 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 97a04b3f6..47d51ced1 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,9 @@ Drupal 7.33, xxxx-xx-xx (development version) ----------------------- +- Added an entity_view_mode_prepare() API function to allow entity-defining + modules to properly invoke hook_entity_view_mode_alter(), and used it + throughout Drupal core to fix bugs with the invocation of that hook. - Security improvement: Made the database API's orderBy() method sanitize the sort direction ("ASC" or "DESC") for queries built with db_select(), so that calling code does not have to. diff --git a/includes/common.inc b/includes/common.inc index 63b691e48..84c5bfa89 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -7898,6 +7898,56 @@ function entity_prepare_view($entity_type, $entities, $langcode = NULL) { } } +/** + * Invoke hook_entity_view_mode_alter(). + * + * If adding a new entity similar to nodes, comments or users, you should invoke + * this function during the ENTITY_build_content() or ENTITY_view_multiple() + * phases of rendering to allow other modules to alter the view mode during this + * phase. This function needs to be called before field_attach_prepare_view() to + * ensure that the correct content is loaded by field API. + * + * @param $entity_type + * The type of entity, i.e. 'node', 'user'. + * @param $entities + * The entity objects which are being prepared for view, keyed by object ID. + * @param $view_mode + * The original view mode e.g. 'full', 'teaser'... + * @param $langcode + * (optional) A language code to be used for rendering. Defaults to the global + * content language of the current request. + * @return + * An associative array with arrays of entities keyed by view mode. + * + * @see hook_entity_view_mode_alter() + */ +function entity_view_mode_prepare($entity_type, $entities, $view_mode, $langcode = NULL) { + if (!isset($langcode)) { + $langcode = $GLOBALS['language_content']->language; + } + + // To ensure hooks are never run after field_attach_prepare_view() only + // process items without the entity_view_prepared flag. + $entities_by_view_mode = array(); + foreach ($entities as $id => $entity) { + $entity_view_mode = $view_mode; + if (empty($entity->entity_view_prepared)) { + + // Allow modules to change the view mode. + $context = array( + 'entity_type' => $entity_type, + 'entity' => $entity, + 'langcode' => $langcode, + ); + drupal_alter('entity_view_mode', $entity_view_mode, $context); + } + + $entities_by_view_mode[$entity_view_mode][$id] = $entity; + } + + return $entities_by_view_mode; +} + /** * Returns the URI elements of an entity. * diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 3c942002c..2972474c0 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -993,12 +993,7 @@ function comment_build_content($comment, $node, $view_mode = 'full', $langcode = $comment->content = array(); // Allow modules to change the view mode. - $context = array( - 'entity_type' => 'comment', - 'entity' => $comment, - 'langcode' => $langcode, - ); - drupal_alter('entity_view_mode', $view_mode, $context); + $view_mode = key(entity_view_mode_prepare('comment', array($comment->cid => $comment), $view_mode, $langcode)); // Build fields content. field_attach_prepare_view('comment', array($comment->cid => $comment), $view_mode, $langcode); @@ -1108,17 +1103,26 @@ function comment_links($comment, $node) { * An array in the format expected by drupal_render(). */ function comment_view_multiple($comments, $node, $view_mode = 'full', $weight = 0, $langcode = NULL) { - field_attach_prepare_view('comment', $comments, $view_mode, $langcode); - entity_prepare_view('comment', $comments, $langcode); + $build = array(); + $entities_by_view_mode = entity_view_mode_prepare('comment', $comments, $view_mode, $langcode); + foreach ($entities_by_view_mode as $entity_view_mode => $entities) { + field_attach_prepare_view('comment', $entities, $entity_view_mode, $langcode); + entity_prepare_view('comment', $entities, $langcode); + + foreach ($entities as $entity) { + $build[$entity->cid] = comment_view($entity, $node, $entity_view_mode, $langcode); + } + } - $build = array( - '#sorted' => TRUE, - ); foreach ($comments as $comment) { - $build[$comment->cid] = comment_view($comment, $node, $view_mode, $langcode); $build[$comment->cid]['#weight'] = $weight; $weight++; } + // Sort here, to preserve the input order of the entities that were passed to + // this function. + uasort($build, 'element_sort'); + $build['#sorted'] = TRUE; + return $build; } diff --git a/modules/node/node.module b/modules/node/node.module index acca83ecb..e5b716630 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -1397,12 +1397,7 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) { $node->content = array(); // Allow modules to change the view mode. - $context = array( - 'entity_type' => 'node', - 'entity' => $node, - 'langcode' => $langcode, - ); - drupal_alter('entity_view_mode', $view_mode, $context); + $view_mode = key(entity_view_mode_prepare('node', array($node->nid => $node), $view_mode, $langcode)); // The 'view' hook can be implemented to overwrite the default function // to display nodes. @@ -2664,15 +2659,26 @@ function node_feed($nids = FALSE, $channel = array()) { * An array in the format expected by drupal_render(). */ function node_view_multiple($nodes, $view_mode = 'teaser', $weight = 0, $langcode = NULL) { - field_attach_prepare_view('node', $nodes, $view_mode, $langcode); - entity_prepare_view('node', $nodes, $langcode); $build = array(); + $entities_by_view_mode = entity_view_mode_prepare('node', $nodes, $view_mode, $langcode); + foreach ($entities_by_view_mode as $entity_view_mode => $entities) { + field_attach_prepare_view('node', $entities, $entity_view_mode, $langcode); + entity_prepare_view('node', $entities, $langcode); + + foreach ($entities as $entity) { + $build['nodes'][$entity->nid] = node_view($entity, $entity_view_mode, $langcode); + } + } + foreach ($nodes as $node) { - $build['nodes'][$node->nid] = node_view($node, $view_mode, $langcode); $build['nodes'][$node->nid]['#weight'] = $weight; $weight++; } + // Sort here, to preserve the input order of the entities that were passed to + // this function. + uasort($build['nodes'], 'element_sort'); $build['nodes']['#sorted'] = TRUE; + return $build; } diff --git a/modules/node/node.test b/modules/node/node.test index 0777e1137..5c9118ebb 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -2782,8 +2782,8 @@ class NodeEntityViewModeAlterTest extends NodeWebTestCase { $edit = array(); $langcode = LANGUAGE_NONE; $edit["title"] = $this->randomName(8); - $edit["body[$langcode][0][value]"] = t('Data that should appear only in the body for the node.'); - $edit["body[$langcode][0][summary]"] = t('Extra data that should appear only in the teaser for the node.'); + $edit["body[$langcode][0][value]"] = 'Data that should appear only in the body for the node.'; + $edit["body[$langcode][0][summary]"] = 'Extra data that should appear only in the teaser for the node.'; $this->drupalPost('node/add/page', $edit, t('Save')); $node = $this->drupalGetNodeByTitle($edit["title"]); @@ -2801,6 +2801,45 @@ class NodeEntityViewModeAlterTest extends NodeWebTestCase { $build = node_view($node); $this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.'); } + + /** + * Tests fields that were previously hidden when the view mode is changed. + */ + function testNodeViewModeChangeHiddenField() { + // Hide the tags field on the default display + $instance = field_info_instance('node', 'field_tags', 'article'); + $instance['display']['default']['type'] = 'hidden'; + field_update_instance($instance); + + $web_user = $this->drupalCreateUser(array('create article content', 'edit own article content')); + $this->drupalLogin($web_user); + + // Create a node. + $edit = array(); + $langcode = LANGUAGE_NONE; + $edit["title"] = $this->randomName(8); + $edit["body[$langcode][0][value]"] = 'Data that should appear only in the body for the node.'; + $edit["body[$langcode][0][summary]"] = 'Extra data that should appear only in the teaser for the node.'; + $edit["field_tags[$langcode]"] = 'Extra tag'; + $this->drupalPost('node/add/article', $edit, t('Save')); + + $node = $this->drupalGetNodeByTitle($edit["title"]); + + // Set the flag to alter the view mode and view the node. + variable_set('node_test_change_view_mode', 'teaser'); + $this->drupalGet('node/' . $node->nid); + + // Check that teaser mode is viewed. + $this->assertText('Extra data that should appear only in the teaser for the node.', 'Teaser text present'); + // Make sure body text is not present. + $this->assertNoText('Data that should appear only in the body for the node.', 'Body text not present'); + // Make sure tags are present. + $this->assertText('Extra tag', 'Taxonomy term present'); + + // Test that the correct build mode has been set. + $build = node_view($node); + $this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.'); + } } /** diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 4191146a4..e147c1cab 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -776,15 +776,26 @@ function taxonomy_term_show($term) { * An array in the format expected by drupal_render(). */ function taxonomy_term_view_multiple($terms, $view_mode = 'teaser', $weight = 0, $langcode = NULL) { - field_attach_prepare_view('taxonomy_term', $terms, $view_mode, $langcode); - entity_prepare_view('taxonomy_term', $terms, $langcode); $build = array(); + $entities_by_view_mode = entity_view_mode_prepare('taxonomy_term', $terms, $view_mode, $langcode); + foreach ($entities_by_view_mode as $entity_view_mode => $entities) { + field_attach_prepare_view('taxonomy_term', $entities, $entity_view_mode, $langcode); + entity_prepare_view('taxonomy_term', $entities, $langcode); + + foreach ($entities as $entity) { + $build['taxonomy_terms'][$entity->tid] = taxonomy_term_view($entity, $entity_view_mode, $langcode); + } + } + foreach ($terms as $term) { - $build['taxonomy_terms'][$term->tid] = taxonomy_term_view($term, $view_mode, $langcode); $build['taxonomy_terms'][$term->tid]['#weight'] = $weight; $weight++; } + // Sort here, to preserve the input order of the entities that were passed to + // this function. + uasort($build['taxonomy_terms'], 'element_sort'); $build['taxonomy_terms']['#sorted'] = TRUE; + return $build; } @@ -817,12 +828,7 @@ function taxonomy_term_build_content($term, $view_mode = 'full', $langcode = NUL $term->content = array(); // Allow modules to change the view mode. - $context = array( - 'entity_type' => 'taxonomy_term', - 'entity' => $term, - 'langcode' => $langcode, - ); - drupal_alter('entity_view_mode', $view_mode, $context); + $view_mode = key(entity_view_mode_prepare('taxonomy_term', array($term->tid => $term), $view_mode, $langcode)); // Add the term description if the term has one and it is visible. $type = 'taxonomy_term'; diff --git a/modules/user/user.module b/modules/user/user.module index b23979937..89d99da73 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -2633,12 +2633,7 @@ function user_build_content($account, $view_mode = 'full', $langcode = NULL) { $account->content = array(); // Allow modules to change the view mode. - $context = array( - 'entity_type' => 'user', - 'entity' => $account, - 'langcode' => $langcode, - ); - drupal_alter('entity_view_mode', $view_mode, $context); + $view_mode = key(entity_view_mode_prepare('user', array($account->uid => $account), $view_mode, $langcode)); // Build fields content. field_attach_prepare_view('user', array($account->uid => $account), $view_mode, $langcode); -- cgit v1.2.3