From ef60f4cf91c43daeb3e8fa7dff72a8c72e467522 Mon Sep 17 00:00:00 2001
From: Frederic G. MARAND <fgm@osinet.fr>
Date: Mon, 20 Aug 2012 23:37:16 +0200
Subject: [PATCH 2/2] Issue #1026616: introduce an entity render controller. Third pass WIP.

- all 4 controllers are working.
- view() and buildContent() have been implemented
- new entity_view() and entity_view_controller() functions
- original procedural functions have been converted to entity_view().
- manually tested, no full test run yet.
- vocabularies and file can also be arguments of entity_view().
---
 core/modules/comment/comment.module                |   48 +-------
 .../lib/Drupal/comment/CommentViewController.php   |   84 +++++++++++++
 core/modules/entity/entity.module                  |   34 +++++
 .../lib/Drupal/entity/EntityViewController.php     |  130 ++++++++++++++++++++
 .../entity/EntityViewControllerInterface.php       |   36 ++++++
 core/modules/field/field.multilingual.inc          |    1 -
 .../node/lib/Drupal/node/NodeViewController.php    |   57 +++++++++
 core/modules/node/node.module                      |   31 +-----
 .../lib/Drupal/taxonomy/TermViewController.php     |   41 ++++++
 core/modules/taxonomy/taxonomy.module              |   25 +----
 .../user/lib/Drupal/user/UserViewController.php    |   23 ++++
 core/modules/user/user.module                      |   23 +---
 12 files changed, 413 insertions(+), 120 deletions(-)
 create mode 100644 core/modules/comment/lib/Drupal/comment/CommentViewController.php
 create mode 100644 core/modules/entity/lib/Drupal/entity/EntityViewController.php
 create mode 100644 core/modules/entity/lib/Drupal/entity/EntityViewControllerInterface.php
 create mode 100644 core/modules/node/lib/Drupal/node/NodeViewController.php
 create mode 100644 core/modules/taxonomy/lib/Drupal/taxonomy/TermViewController.php
 create mode 100644 core/modules/user/lib/Drupal/user/UserViewController.php

diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index aef6209..5ead707 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -119,6 +119,7 @@ function comment_entity_info() {
         'uuid' => 'uuid',
       ),
       'bundles' => array(),
+      'view controller class' => 'Drupal\comment\CommentViewController',
       'view modes' => array(
         'full' => array(
           'label' => t('Full comment'),
@@ -977,52 +978,7 @@ function comment_prepare_thread(&$comments) {
  *   An array as expected by drupal_render().
  */
 function comment_view(Comment $comment, Node $node, $view_mode = 'full', $langcode = NULL) {
-  if (!isset($langcode)) {
-    $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
-  }
-
-  // Populate $comment->content with a render() array.
-  comment_build_content($comment, $node, $view_mode, $langcode);
-
-  $build = $comment->content;
-  // We don't need duplicate rendering info in comment->content.
-  unset($comment->content);
-
-  $build += array(
-    '#theme' => 'comment__node_' . $node->type,
-    '#comment' => $comment,
-    '#node' => $node,
-    '#view_mode' => $view_mode,
-    '#language' => $langcode,
-  );
-
-  if (empty($comment->in_preview)) {
-    $prefix = '';
-    $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED;
-
-    // Add 'new' anchor if needed.
-    if (!empty($comment->first_new)) {
-      $prefix .= "<a id=\"new\"></a>\n";
-    }
-
-    // Add indentation div or close open divs as needed.
-    if ($is_threaded) {
-      $prefix .= $comment->divs <= 0 ? str_repeat('</div>', abs($comment->divs)) : "\n" . '<div class="indented">';
-    }
-
-    // Add anchor for each comment.
-    $prefix .= "<a id=\"comment-$comment->cid\"></a>\n";
-    $build['#prefix'] = $prefix;
-
-    // Close all open divs.
-    if ($is_threaded && !empty($comment->divs_final)) {
-      $build['#suffix'] = str_repeat('</div>', $comment->divs_final);
-    }
-  }
-
-  // Allow modules to modify the structured comment.
-  drupal_alter(array('comment_view', 'entity_view'), $build, $comment);
-
+  $build = entity_view($comment, $view_mode, array('node' => $node), $langcode);
   return $build;
 }
 
diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewController.php b/core/modules/comment/lib/Drupal/comment/CommentViewController.php
new file mode 100644
index 0000000..b95efe4
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/CommentViewController.php
@@ -0,0 +1,84 @@
+<?php
+namespace Drupal\comment;
+
+use Drupal\comment\Comment;
+use Drupal\entity\EntityInterface;
+use Drupal\entity\EntityViewController;
+use Drupal\node\Node;
+
+class CommentViewController extends EntityViewController {
+
+  /**
+   * $dependencies msust contain a valid Node as the value for the "node" key.
+   */
+  public function buildContent(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array()) {
+    parent::buildContent($entity, $view_mode, $langcode, $dependencies);
+    parent::prepareView($entity, $view_mode, $langcode, $dependencies);
+
+    $entity->content['links'] = array(
+        '#theme' => 'links__comment',
+        '#pre_render' => array('drupal_pre_render_links'),
+        '#attributes' => array('class' => array('links', 'inline')),
+    );
+    if (empty($entity->in_preview)) {
+      $entity->content['links'][$this->entityType] = array(
+        '#theme' => 'links__comment__comment',
+        // The "node" key is specified to be present, so no need to check.
+        '#links' => comment_links($entity, $dependencies['node']),
+        '#attributes' => array('class' => array('links', 'inline')),
+      );
+    }
+  }
+
+  protected function getBuildDefaults(EntityInterface $entity, $view_mode, array $dependencies, $langcode) {
+    $return = parent::getBuildDefaults($entity, $view_mode, $dependencies, $langcode);
+    $node = $dependencies['node'];
+    $return = array_merge($return, array(
+      '#theme' => 'comment__node_' . $node->type,
+      '#node' => $node,
+    ));
+    return $return;
+  }
+
+  protected function normalizeArguments(&$view_mode, array &$dependencies = array(), &$langcode) {
+    parent::normalizeArguments($view_mode, $dependencies, $langcode);
+
+    if (!isset($dependencies['node'])) {
+      $node = node_load($comment->nid);
+      if (empty($node)) {
+        throw new \InvalidArgumentException(t('Invalid node for comment'));
+      }
+      $dependencies['node'] = $node;
+    }
+  }
+
+  protected function prepareBuild(array $build, EntityInterface $comment, $view_mode, $dependencies = array(), $langcode = NULL) {
+    if (empty($comment->in_preview)) {
+      $prefix = '';
+      // key 'node' has been initialized in EntityViewController::normalizeArguments().
+      $node = $dependencies['node'];
+      $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED;
+
+      // Add 'new' anchor if needed.
+      if (!empty($comment->first_new)) {
+        $prefix .= "<a id=\"new\"></a>\n";
+      }
+
+      // Add indentation div or close open divs as needed.
+      if ($is_threaded) {
+        $prefix .= $comment->divs <= 0 ? str_repeat('</div>', abs($comment->divs)) : "\n" . '<div class="indented">';
+      }
+
+      // Add anchor for each comment.
+      $prefix .= "<a id=\"comment-$comment->cid\"></a>\n";
+      $build['#prefix'] = $prefix;
+
+      // Close all open divs.
+      if ($is_threaded && !empty($comment->divs_final)) {
+        $build['#suffix'] = str_repeat('</div>', $comment->divs_final);
+      }
+    }
+
+    return $build;
+  }
+}
diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index 74f2fcf..957bda3 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -210,6 +210,40 @@ function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) {
 }
 
 /**
+ * Return the render array for an entity.
+ *
+ * @param EntityInterface $entity
+ * @param string $view_mode
+ * @param array $dependencies
+ * @param string $langcode
+ */
+function entity_view(EntityInterface $entity, $view_mode, array $dependencies = array(), $langcode = NULL) {
+  return entity_view_controller($entity->entityType())
+    ->view($entity, $view_mode, $dependencies, $langcode);
+}
+
+/**
+ * Returns an entity view controller.
+ *
+ * @see hook_entity_info()
+ *
+ * @param $entity_type
+ *   The type of the entity.
+ *
+ * @return Drupal\entity\EntityViewControllerInterface
+ *   An entity view controller instance.
+ */
+function entity_view_controller($entity_type) {
+  $info = entity_get_info($entity_type);
+
+  $class = isset($info['view controller class'])
+    ? $info['view controller class']
+    : 'Drupal\entity\EntityViewController';
+
+  return new $class($entity_type);
+}
+
+/**
  * Loads multiple entities from the database.
  *
  * This function should be used whenever you need to load more than one entity
diff --git a/core/modules/entity/lib/Drupal/entity/EntityViewController.php b/core/modules/entity/lib/Drupal/entity/EntityViewController.php
new file mode 100644
index 0000000..364f2d6
--- /dev/null
+++ b/core/modules/entity/lib/Drupal/entity/EntityViewController.php
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity\EntityViewController.
+ */
+
+namespace Drupal\entity;
+
+/**
+ * Base class for entity view controllers.
+ */
+class EntityViewController implements EntityViewControllerInterface {
+
+  /**
+   * Override in descendent classes.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  public function __construct($entity_type) {
+    $this->entityType = $entity_type;
+  }
+
+  public function buildContent(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array()) {
+    // Remove previously built content, if exists.
+    $entity->content = array();
+
+    // Allow modules to change the view mode.
+    $context = array('langcode' => $langcode);
+    drupal_alter('entity_view_mode', $view_mode, $entity, $context);
+  }
+
+  /**
+   * Build fields content.
+   *
+   * In case of a multiple view, "{$entity}_view_multiple"() already ran the
+   * 'prepare_view' step. An internal flag prevents the operation from running
+   * twice.
+   *
+   * @param EntityInterface $entity
+   * @param string $view_mode
+   * @param string $langcode
+   * @param array $dependencies
+   */
+  protected function prepareView(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array()) {
+    $entry = array($entity->id() => $entity);
+    field_attach_prepare_view($this->entityType, $entry, $view_mode, $langcode);
+    entity_prepare_view($this->entityType, $entry, $langcode);
+    $entity->content += field_attach_view($this->entityType, $entity, $view_mode, $langcode);
+  }
+
+  /**
+   * Provide entity-specific defaults to the build process.
+   *
+   * @param EntityInterface $entity
+   * @param string $view_mode
+   * @param string $langcode
+   *
+   * @return array
+   */
+  protected function getBuildDefaults(EntityInterface $entity, $view_mode, array $dependencies, $langcode) {
+    $return = array(
+      '#theme' => $this->entityType,
+      "#{$this->entityType}" => $entity,
+      '#view_mode' => $view_mode,
+      '#langcode' => $langcode,
+    );
+
+    return $return;
+  }
+
+  /**
+   * Fill in the needed default values from the environment.
+   *
+   * After that step, all needed parameters can be required instead of having to
+   * be defaulted each time.
+   *
+   * @param string $view_mode
+   * @param array $dependencies
+   * @param string $langcode
+   */
+  protected function normalizeArguments(&$view_mode, array &$dependencies = array(), &$langcode) {
+    if (!isset($langcode)) {
+      $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
+    }
+  }
+
+  /**
+   * Specific per-entity building.
+   *
+   * This method is typically not invoked by the child reimplementations, but
+   * simply replaced.
+   *
+   * @param array $build
+   * @param EntityInterface $entity
+   * @param string $view_mode
+   * @param array $dependencies
+   * @param string $langcode
+   *
+   * @return array
+   *   The build array.
+   */
+  protected function prepareBuild(array $build, EntityInterface $entity, $view_mode, $dependencies = array(), $langcode = NULL) {
+    return $build;
+  }
+
+  public function view(EntityInterface $entity, $view_mode, $dependencies = array(), $langcode = NULL) {
+    $this->entityType = $entity->entityType();
+    $this->normalizeArguments($view_mode, $dependencies, $langcode);
+    $this->buildContent($entity, $view_mode, $langcode, $dependencies);
+
+    $view_hook = "{$this->entityType}_view";
+    module_invoke_all($view_hook, $entity, $view_mode, $langcode);
+    module_invoke_all('entity_view', $entity, $view_mode, $langcode);
+
+    $build = $entity->content;
+    // We don't need duplicate rendering info in $entity->content.
+    unset($entity->content);
+
+    $build += $this->getBuildDefaults($entity, $view_mode, $dependencies, $langcode);
+    $build = $this->prepareBuild($build, $entity, $view_mode, $dependencies, $langcode);
+
+    // Allow modules to modify the structured comment.
+    drupal_alter(array($view_hook, 'entity_view'), $build, $entity);
+
+    return $build;
+  }
+}
diff --git a/core/modules/entity/lib/Drupal/entity/EntityViewControllerInterface.php b/core/modules/entity/lib/Drupal/entity/EntityViewControllerInterface.php
new file mode 100644
index 0000000..49297e8
--- /dev/null
+++ b/core/modules/entity/lib/Drupal/entity/EntityViewControllerInterface.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity\EntityViewControllerInterface.
+ */
+
+namespace Drupal\entity;
+
+/**
+ * Defines a common interface for entity view controller classes.
+ */
+interface EntityViewControllerInterface {
+  /**
+   * Main Entity view method.
+   *
+   * @param EntityInterface $entity
+   * @param string $view_mode
+   * @param array $dependencies
+   * @param string $langcode
+   *
+   * @throws \InvalidArgumentException
+   */
+  public function view(EntityInterface $entity, $view_mode, $dependencies = array(), $langcode = NULL);
+
+  /**
+   * Build the structured $content property on the entity.
+   *
+   * @param EntityInterface $entity
+   * @param string $view_mode
+   * @param string $langcode
+   * @param array $dependencies
+   *   Required contents are per-entity type.
+   */
+  public function buildContent(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array());
+}
diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc
index e3fbd2d..cba12bd 100644
--- a/core/modules/field/field.multilingual.inc
+++ b/core/modules/field/field.multilingual.inc
@@ -298,7 +298,6 @@ function field_language($entity_type, $entity, $field_name = NULL, $langcode = N
   $id = $entity->bundle();
   $bundle = $entity->bundle();
   $langcode = field_valid_language($langcode, FALSE);
-
   if (!isset($display_langcodes[$entity_type][$id][$langcode])) {
     $display_langcode = array();
 
diff --git a/core/modules/node/lib/Drupal/node/NodeViewController.php b/core/modules/node/lib/Drupal/node/NodeViewController.php
new file mode 100644
index 0000000..67e2f13
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/NodeViewController.php
@@ -0,0 +1,57 @@
+<?php
+namespace Drupal\node;
+
+use Drupal\entity\EntityInterface;
+use Drupal\entity\EntityViewController;
+
+class NodeViewController extends EntityViewController {
+
+  public function buildContent(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array()) {
+    parent::buildContent($entity, $view_mode, $langcode, $dependencies);
+
+    // The 'view' hook can be implemented to overwrite the default function
+    // to display nodes.
+    if (node_hook($entity, 'view')) {
+      $entity = node_invoke($entity, 'view', $view_mode, $langcode);
+    }
+
+    parent::prepareView($entity, $view_mode, $langcode, $dependencies);
+
+    $entity->content['links'] = array(
+      '#theme' => 'links__node',
+      '#pre_render' => array('drupal_pre_render_links'),
+      '#attributes' => array('class' => array('links', 'inline')),
+    );
+
+    // Always display a read more link on teasers because we have no way
+    // to know when a teaser view is different than a full view.
+    $links = array();
+    if ($view_mode == 'teaser') {
+      $node_title_stripped = strip_tags($entity->label());
+      $links['node-readmore'] = array(
+        'title' => t('Read more<span class="element-invisible"> about @title</span>', array('@title' => $node_title_stripped)),
+        'href' => 'node/' . $entity->nid,
+        'html' => TRUE,
+        'attributes' => array('rel' => 'tag', 'title' => $node_title_stripped),
+      );
+    }
+
+    $entity->content['links']['node'] = array(
+      '#theme' => 'links__node__node',
+      '#links' => $links,
+      '#attributes' => array('class' => array('links', 'inline')),
+    );
+  }
+
+  protected function prepareBuild(array $build, EntityInterface $node, $view_mode, $dependencies = array(), $langcode = NULL) {
+    // Add contextual links for this node, except when the node is already being
+    // displayed on its own page. Modules may alter this behavior (for example,
+    // to restrict contextual links to certain view modes) by implementing
+    // hook_node_view_alter().
+    if (!empty($node->nid) && !($view_mode == 'full' && node_is_page($node))) {
+      $build['#contextual_links']['node'] = array('node', array($node->nid));
+    }
+
+    return $build;
+  }
+}
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 349aab5..c74809a 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -215,6 +215,7 @@ function node_entity_info() {
         'bundle' => 'type',
       ),
       'bundles' => array(),
+      'view controller class' => 'Drupal\node\NodeViewController',
       'view modes' => array(
         'full' => array(
           'label' => t('Full content'),
@@ -1134,35 +1135,7 @@ function node_revision_delete($revision_id) {
  *   An array as expected by drupal_render().
  */
 function node_view(Node $node, $view_mode = 'full', $langcode = NULL) {
-  if (!isset($langcode)) {
-    $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
-  }
-
-  // Populate $node->content with a render() array.
-  node_build_content($node, $view_mode, $langcode);
-
-  $build = $node->content;
-  // We don't need duplicate rendering info in node->content.
-  unset($node->content);
-
-  $build += array(
-    '#theme' => 'node',
-    '#node' => $node,
-    '#view_mode' => $view_mode,
-    '#langcode' => $langcode,
-  );
-
-  // Add contextual links for this node, except when the node is already being
-  // displayed on its own page. Modules may alter this behavior (for example,
-  // to restrict contextual links to certain view modes) by implementing
-  // hook_node_view_alter().
-  if (!empty($node->nid) && !($view_mode == 'full' && node_is_page($node))) {
-    $build['#contextual_links']['node'] = array('node', array($node->nid));
-  }
-
-  // Allow modules to modify the structured node.
-  drupal_alter(array('node_view', 'entity_view'), $build, $node);
-
+  $build = entity_view($node, $view_mode, array(), $langcode);
   return $build;
 }
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewController.php
new file mode 100644
index 0000000..0b75cbe
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewController.php
@@ -0,0 +1,41 @@
+<?php
+namespace Drupal\taxonomy;
+
+use Drupal\entity\EntityInterface;
+use Drupal\entity\EntityViewController;
+
+class TermViewController extends EntityViewController {
+
+  public function buildContent(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array()) {
+    parent::buildContent($entity, $view_mode, $langcode, $dependencies);
+
+    // Try to add in the core taxonomy pieces like description and nodes
+    $settings = field_view_mode_settings($this->entityType, $entity->bundle());
+    $fields = field_extra_fields_get_display($this->entityType, $entity->bundle(), $view_mode);
+    if (!empty($entity->description) && isset($fields['description']) && $fields['description']['visible']) {
+      $entity->content['description'] = array(
+        '#markup' => check_markup($entity->description, $entity->format, '', TRUE),
+        '#weight' => $fields['description']['weight'],
+        '#prefix' => '<div class="taxonomy-term-description">',
+        '#suffix' => '</div>',
+      );
+    }
+
+    parent::prepareView($entity, $view_mode, $langcode, $dependencies);
+  }
+
+  protected function getBuildDefaults(EntityInterface $entity, $view_mode, array $dependencies, $langcode) {
+    $return = parent::getBuildDefaults($entity, $view_mode, $dependencies, $langcode);
+
+    // TODO: rename "term" to "taxonomy_term" in theme_taxonomy_term() ?
+    $return['#term'] = $return["#{$this->entityType}"];
+    unset($return[$this->entityType]);
+
+    return $return;
+  }
+
+  protected function prepareBuild(array $build, EntityInterface $node, $view_mode, $dependencies = array(), $langcode = NULL) {
+    $build['#attached']['css'][] = drupal_get_path('module', 'taxonomy') . '/taxonomy.css';
+    return $build;
+  }
+}
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 6e42c1c..1bc7367 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -129,6 +129,7 @@ function taxonomy_entity_info() {
         'bundle' => 'machine_name',
       ),
       'bundles' => array(),
+      'view controller class' => 'Drupal\taxonomy\TermViewController',
       'view modes' => array(
         // @todo View mode for display as a field (when attached to nodes etc).
         'full' => array(
@@ -682,29 +683,7 @@ function taxonomy_term_build_content(Term $term, $view_mode = 'full', $langcode
  *   An array as expected by drupal_render().
  */
 function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
-  if (!isset($langcode)) {
-    $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
-  }
-
-  // Populate $term->content with a render() array.
-  taxonomy_term_build_content($term, $view_mode, $langcode);
-  $build = $term->content;
-
-  // We don't need duplicate rendering info in $term->content.
-  unset($term->content);
-
-  $build += array(
-    '#theme' => 'taxonomy_term',
-    '#term' => $term,
-    '#view_mode' => $view_mode,
-    '#language' => $langcode,
-  );
-
-  $build['#attached']['css'][] = drupal_get_path('module', 'taxonomy') . '/taxonomy.css';
-
-  // Allow modules to modify the structured term.
-  drupal_alter(array('taxonomy_term_view', 'entity_view'), $build, $term);
-
+  $build = entity_view($term, $view_mode, array(), $langcode);
   return $build;
 }
 
diff --git a/core/modules/user/lib/Drupal/user/UserViewController.php b/core/modules/user/lib/Drupal/user/UserViewController.php
new file mode 100644
index 0000000..148c84b
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserViewController.php
@@ -0,0 +1,23 @@
+<?php
+namespace Drupal\user;
+
+use Drupal\entity\EntityInterface;
+use Drupal\entity\EntityViewController;
+
+class UserViewController extends EntityViewController {
+
+  public function buildContent(EntityInterface $entity, $view_mode, $langcode, array $dependencies = array()) {
+    parent::buildContent($entity, $view_mode, $langcode, $dependencies);
+    parent::prepareView($entity, $view_mode, $langcode, $dependencies);
+  }
+
+  protected function getBuildDefaults(EntityInterface $entity, $view_mode, array $dependencies, $langcode) {
+    $return = parent::getBuildDefaults($entity, $view_mode, $dependencies, $langcode);
+
+    // TODO: rename "theme_user_profile" to "theme_user", and 'account' to 'user' ?
+    $return['#theme'] = 'user_profile';
+    $return['#account'] = $return['#user'];
+
+    return $return;
+  }
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 44cb266..7b184c7 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -170,6 +170,7 @@ function user_entity_info() {
           ),
         ),
       ),
+      'view controller class' => 'Drupal\user\UserViewController',
       'view modes' => array(
         'full' => array(
           'label' => t('User account'),
@@ -2072,27 +2073,7 @@ function user_view_page($account) {
  *   An array as expected by drupal_render().
  */
 function user_view($account, $view_mode = 'full', $langcode = NULL) {
-  if (!isset($langcode)) {
-    $langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
-  }
-
-  // Retrieve all profile fields and attach to $account->content.
-  user_build_content($account, $view_mode, $langcode);
-
-  $build = $account->content;
-  // We don't need duplicate rendering info in account->content.
-  unset($account->content);
-
-  $build += array(
-    '#theme' => 'user_profile',
-    '#account' => $account,
-    '#view_mode' => $view_mode,
-    '#language' => $langcode,
-  );
-
-  // Allow modules to modify the structured user.
-  drupal_alter(array('user_view', 'entity_view'), $build, $account);
-
+  $build = entity_view($account, $view_mode, array(), $langcode);
   return $build;
 }
 
-- 
1.7.4.1

