Issue #1026616 by fgm, Berdir, Dave Reid, fago: Implement an entity render controller.

8.0.x
webchick 2012-10-13 23:40:03 -07:00
parent b149df70af
commit e9ca778b53
21 changed files with 625 additions and 489 deletions

View File

@ -27,6 +27,8 @@
* The class has to implement the
* Drupal\Core\Entity\EntityStorageControllerInterface interface. Leave blank
* to use the Drupal\Core\Entity\DatabaseStorageController implementation.
* - render controller class: The name of the class that is used to render
* the entities. Deafaults to Drupal\Core\Entity\EntityRenderController.
* - form controller class: An associative array where the keys are the names
* of the different form operations (such as creation, editing or deletion)
* and the values are the names of the controller classes. To facilitate

View File

@ -49,6 +49,7 @@ function entity_get_info($entity_type = NULL) {
'entity class' => 'Drupal\Core\Entity\Entity',
'controller class' => 'Drupal\Core\Entity\DatabaseStorageController',
'list controller class' => 'Drupal\Core\Entity\EntityListController',
'render controller class' => 'Drupal\Core\Entity\EntityRenderController',
'form controller class' => array(
'default' => 'Drupal\Core\Entity\EntityFormController',
),
@ -316,47 +317,6 @@ function entity_get_controller($entity_type) {
return $controllers[$entity_type];
}
/**
* Invokes hook_entity_prepare_view().
*
* 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 objects during this phase. This is needed for situations where
* information needs to be loaded outside of ENTITY_load() - particularly
* when loading entities into one another - i.e. a user object into a node, due
* to the potential for unwanted side-effects such as caching and infinite
* recursion. By convention, entity_prepare_view() is called after
* field_attach_prepare_view() to allow entity level hooks to act on content
* 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.
*
* @see hook_entity_prepare_view()
*/
function entity_prepare_view($entity_type, $entities) {
// To ensure hooks are only run once per entity, check for an
// entity_view_prepared flag and only process items without it.
// @todo: resolve this more generally for both entity and field level hooks.
$prepare = array();
foreach ($entities as $id => $entity) {
if (empty($entity->entity_view_prepared)) {
// Add this entity to the items to be prepared.
$prepare[$id] = $entity;
// Mark this item as prepared.
$entity->entity_view_prepared = TRUE;
}
}
if (!empty($prepare)) {
module_invoke_all('entity_prepare_view', $prepare, $entity_type);
}
}
/**
* Returns the label of an entity.
*
@ -562,3 +522,57 @@ function entity_list_controller($entity_type) {
$class = $entity_info['list controller class'];
return new $class($entity_type, $storage);
}
/**
* Returns an entity render controller for a given entity type.
*
* @param string $entity_type
* The type of the entity.
*
* @return Drupal\Core\Entity\EntityRenderControllerInterface
* An entity render controller.
*
* @see hook_entity_info()
*/
function entity_render_controller($entity_type) {
$info = entity_get_info($entity_type);
$class = $info['render controller class'];
return new $class($entity_type);
}
/**
* Returns the render array for an entity.
*
* @param Drupal\Core\Entity\EntityInterface $entity
* The entity to be rendered.
* @param string $view_mode
* The view mode that should be used to display the entity.
* @param string $langcode
* (optional) For which language the entity should be rendered, defaults to
* the current content language.
*
* @return array
* A render array for the entity.
*/
function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL) {
return entity_render_controller($entity->entityType())->view($entity, $view_mode, $langcode);
}
/**
* Returns the render array for the provided entities.
*
* @param array $entities
* The entities to be rendered, must be of the same type.
* @param string $view_mode
* The view mode that should be used to display the entity.
* @param string $langcode
* (optional) For which language the entity should be rendered, defaults to
* the current content language.
*
* @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 entity_render_controller(reset($entities)->entityType())->viewMultiple($entities, $view_mode, $langcode);
}

View File

@ -0,0 +1,145 @@
<?php
/**
* @file
* Definition of Drupal\Core\Entity\EntityRenderController.
*/
namespace Drupal\Core\Entity;
/**
* Base class for entity view controllers.
*/
class EntityRenderController implements EntityRenderControllerInterface {
/**
* The type of entities for which this controller is instantiated.
*
* @var string
*/
protected $entityType;
public function __construct($entity_type) {
$this->entityType = $entity_type;
}
/**
* Implements Drupal\Core\Entity\EntityRenderControllerInterface::buildContent().
*/
public function buildContent(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
// Allow modules to change the view mode.
$context = array('langcode' => $langcode);
$prepare = array();
foreach ($entities as $key => $entity) {
// Remove previously built content, if exists.
$entity->content = array();
drupal_alter('entity_view_mode', $view_mode, $entity, $context);
$entity->content['#view_mode'] = $view_mode;
$prepare[$view_mode][$key] = $entity;
}
// Prepare and build field content, grouped by view mode.
foreach ($prepare as $view_mode => $prepare_entities) {
$call = array();
// To ensure hooks are only run once per entity, check for an
// entity_view_prepared flag and only process items without it.
foreach ($prepare_entities as $entity) {
if (empty($entity->entity_view_prepared)) {
// Add this entity to the items to be prepared.
$call[$entity->id()] = $entity;
// Mark this item as prepared.
$entity->entity_view_prepared = TRUE;
}
}
if (!empty($call)) {
field_attach_prepare_view($this->entityType, $call, $view_mode, $langcode);
module_invoke_all('entity_prepare_view', $call, $this->entityType);
}
foreach ($entities as $entity) {
$entity->content += field_attach_view($this->entityType, $entity, $view_mode, $langcode);
}
}
}
/**
* Provides entity-specific defaults to the build process.
*
* @param Drupal\Core\Entity\EntityInterface $entity
* The entity for which the defaults should be provided.
* @param string $view_mode
* The view mode that should be used.
* @param string $langcode
* (optional) For which language the entity should be prepared, defaults to
* the current content language.
*
* @return array
*/
protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
$return = array(
'#theme' => $this->entityType,
"#{$this->entityType}" => $entity,
'#view_mode' => $view_mode,
'#langcode' => $langcode,
);
return $return;
}
/**
* Specific per-entity building.
*
* @param array $build
* The render array that is being created.
* @param Drupal\Core\Entity\EntityInterface $entity
* The entity to be prepared.
* @param string $view_mode
* The view mode that should be used to prepare the entity.
* @param string $langcode
* (optional) For which language the entity should be prepared, defaults to
* the current content language.
*/
protected function alterBuild(array &$build, EntityInterface $entity, $view_mode, $langcode = NULL) { }
/**
* Implements Drupal\Core\Entity\EntityRenderControllerInterface::view().
*/
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
$buildList = $this->viewMultiple(array($entity), $view_mode, $langcode);
return $buildList[0];
}
/**
* Implements Drupal\Core\Entity\EntityRenderControllerInterface::viewMultiple().
*/
public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
}
$this->buildContent($entities, $view_mode, $langcode);
$view_hook = "{$this->entityType}_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;
module_invoke_all($view_hook, $entity, $entity_view_mode, $langcode);
module_invoke_all('entity_view', $entity, $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, $entity_view_mode, $langcode);
$build[$key]['#weight'] = $weight++;
// Allow modules to modify the structured entity.
drupal_alter(array($view_hook, 'entity_view'), $build[$key], $entity);
}
return $build;
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* @file
* Definition of Drupal\Core\Entity\EntityRenderControllerInterface.
*/
namespace Drupal\Core\Entity;
/**
* Defines a common interface for entity view controller classes.
*/
interface EntityRenderControllerInterface {
/**
* Build the structured $content property on the entity.
*
* @param array $entities
* The entities, implementing EntityInterface, whose content is being built.
* @param string $view_mode
* (optional) The view mode that should be used to build the entity.
* @param string $langcode
* (optional) For which language the entity should be build, defaults to
* the current content language.
*
* @return array
* The content array.
*/
public function buildContent(array $entities = array(), $view_mode = 'full', $langcode = NULL);
/**
* Returns the render array for the provided entity.
*
* @param Drupal\Core\Entity\EntityInterface $entity
* The entity to render.
* @param string $view_mode
* (optional) The view mode that should be used to render the entity.
* @param string $langcode
* (optional) For which language the entity should be rendered, defaults to
* the current content language.
*
* @return array
* A render array for the entity.
*
* @throws \InvalidArgumentException
* Can be thrown when the set of parameters is inconsistent, like when
* trying to view a Comment and passing a Node which is not the one the
* comment belongs to, or not passing one, and having the comment node not
* be available for loading.
*/
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL);
/**
* Returns the render array for the provided entities.
*
* @param array $entities
* An array of entities implementing EntityInterface to view.
* @param string $view_mode
* (optional) The view mode that should be used to render the entity.
* @param string $langcode
* (optional) For which language the entity should be rendered, defaults to
* the current content language.
*
* @return
* A render array for the entities, indexed by the same keys as the
* entities array passed in $entities.
*
* @throws \InvalidArgumentException
* Can be thrown when the set of parameters is inconsistent, like when
* trying to view Comments and passing a Node which is not the one the
* comments belongs to, or not passing one, and having the comments node not
* be available for loading.
*/
public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL);
}

View File

@ -119,6 +119,7 @@ function comment_entity_info() {
'uuid' => 'uuid',
),
'bundles' => array(),
'render controller class' => 'Drupal\comment\CommentRenderController',
'view modes' => array(
'full' => array(
'label' => t('Full comment'),
@ -768,7 +769,7 @@ function comment_node_page_additions(Node $node) {
if ($cids = comment_get_thread($node, $mode, $comments_per_page)) {
$comments = comment_load_multiple($cids);
comment_prepare_thread($comments);
$build = comment_view_multiple($comments, $node);
$build = comment_view_multiple($comments);
$build['pager']['#theme'] = 'pager';
$additions['comments'] = $build;
}
@ -968,8 +969,6 @@ function comment_prepare_thread(&$comments) {
*
* @param Drupal\comment\Comment $comment
* The comment object.
* @param Drupal\node\Node $node
* The node the comment is attached to.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param $langcode
@ -979,105 +978,8 @@ function comment_prepare_thread(&$comments) {
* @return
* 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);
return $build;
}
/**
* Builds a structured array representing the comment's content.
*
* The content built for the comment (field values, comments, file attachments
* or other comment components) will vary depending on the $view_mode parameter.
*
* @param Drupal\comment\Comment $comment
* A comment object.
* @param Drupal\node\Node $node
* The node the comment is attached to.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*/
function comment_build_content(Comment $comment, Node $node, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
}
// Remove previously built content, if exists.
$comment->content = array();
// Allow modules to change the view mode.
$context = array('langcode' => $langcode);
drupal_alter('entity_view_mode', $view_mode, $comment, $context);
// Build fields content.
field_attach_prepare_view('comment', array($comment->cid => $comment), $view_mode, $langcode);
entity_prepare_view('comment', array($comment->cid => $comment), $langcode);
$comment->content += field_attach_view('comment', $comment, $view_mode, $langcode);
$comment->content['links'] = array(
'#theme' => 'links__comment',
'#pre_render' => array('drupal_pre_render_links'),
'#attributes' => array('class' => array('links', 'inline')),
);
if (empty($comment->in_preview)) {
$comment->content['links']['comment'] = array(
'#theme' => 'links__comment__comment',
'#links' => comment_links($comment, $node),
'#attributes' => array('class' => array('links', 'inline')),
);
}
// Allow modules to make their own additions to the comment.
module_invoke_all('comment_view', $comment, $view_mode, $langcode);
module_invoke_all('entity_view', $comment, $view_mode, $langcode);
function comment_view(Comment $comment, $view_mode = 'full', $langcode = NULL) {
return entity_view($comment, $view_mode, $langcode);
}
/**
@ -1146,12 +1048,8 @@ function comment_links(Comment $comment, Node $node) {
*
* @param $comments
* An array of comments as returned by comment_load_multiple().
* @param Drupal\node\Node $node
* The node the comments are attached to.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param $weight
* An integer representing the weight of the first comment in the list.
* @param $langcode
* A string indicating the language field values are to be shown in. If no
* language is provided the current content language is used.
@ -1161,19 +1059,8 @@ function comment_links(Comment $comment, Node $node) {
*
* @see drupal_render()
*/
function comment_view_multiple($comments, Node $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(
'#sorted' => TRUE,
);
foreach ($comments as $comment) {
$build[$comment->cid] = comment_view($comment, $node, $view_mode, $langcode);
$build[$comment->cid]['#weight'] = $weight;
$weight++;
}
return $build;
function comment_view_multiple($comments, $view_mode = 'full', $langcode = NULL) {
return entity_view_multiple($comments, $view_mode, $langcode);
}
/**
@ -1411,7 +1298,7 @@ function comment_node_update_index(Node $node, $langcode) {
if ($node->comment && $cids = comment_get_thread($node, $mode, $comments_per_page)) {
$comments = comment_load_multiple($cids);
comment_prepare_thread($comments);
$build = comment_view_multiple($comments, $node, $langcode);
$build = comment_view_multiple($comments, $langcode);
return drupal_render($build);
}
}
@ -1639,7 +1526,7 @@ function comment_get_display_ordinal($cid, $node_type) {
else {
// For threaded comments, the c.thread column is used for ordering. We can
// use the sorting code for comparison, but must remove the trailing slash.
// See comment_view_multiple().
// See CommentRenderController.
$query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))');
}
@ -1687,7 +1574,6 @@ function comment_edit_page(Comment $comment) {
function comment_preview(Comment $comment) {
global $user;
$preview_build = array();
$node = node_load($comment->nid);
if (!form_get_errors()) {
$comment_body = field_get_items('comment', $comment, 'comment_body');
@ -1714,7 +1600,7 @@ function comment_preview(Comment $comment) {
$comment->created = !empty($comment->created) ? $comment->created : REQUEST_TIME;
$comment->changed = REQUEST_TIME;
$comment->in_preview = TRUE;
$comment_build = comment_view($comment, $node);
$comment_build = comment_view($comment);
$comment_build['#weight'] = -100;
$preview_build['comment_preview'] = $comment_build;
@ -1724,11 +1610,11 @@ function comment_preview(Comment $comment) {
$build = array();
$comment = comment_load($comment->pid);
if ($comment && $comment->status == COMMENT_PUBLISHED) {
$build = comment_view($comment, $node);
$build = comment_view($comment);
}
}
else {
$build = node_view($node);
$build = node_view(node_load($comment->nid));
}
$preview_build['comment_output_below'] = $build;

View File

@ -68,7 +68,7 @@ function comment_reply(Node $node, $pid = NULL) {
$comment->node_type = 'comment_node_' . $node->type;
field_attach_load('comment', array($comment->cid => $comment));
$comment->name = $comment->uid ? $comment->registered_name : $comment->name;
$build['comment_parent'] = comment_view($comment, $node);
$build['comment_parent'] = comment_view($comment);
}
else {
drupal_set_message(t('The comment you are replying to does not exist.'), 'error');

View File

@ -36,6 +36,11 @@ class Comment extends Entity implements ContentEntityInterface {
*/
public $pid;
/**
* The ID of the node to which the comment is attached.
*/
public $nid;
/**
* The comment language code.
*

View File

@ -0,0 +1,85 @@
<?php
/**
* @file
* Definition of Drupal\comment\CommentRenderController.
*/
namespace Drupal\comment;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRenderController;
/**
* Render controller for comments.
*/
class CommentRenderController extends EntityRenderController {
/**
* Overrides Drupal\Core\Entity\EntityRenderController::buildContent().
*
* In addition to modifying the content key on entities, this implementation
* will also set the node key which all comments carry.
*/
public function buildContent(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
$return = array();
if (empty($entities)) {
return $return;
}
parent::buildContent($entities, $view_mode, $langcode);
foreach ($entities as $entity) {
$node = node_load($entity->nid);
if (!$node) {
throw new \InvalidArgumentException(t('Invalid node for comment.'));
}
$entity->content['#node'] = $node;
$entity->content['#theme'] = 'comment__node_' . $node->bundle();
$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" property is specified to be present, so no need to check.
'#links' => comment_links($entity, $node),
'#attributes' => array('class' => array('links', 'inline')),
);
}
}
}
/**
* Overrides Drupal\Core\Entity\EntityRenderController::alterBuild().
*/
protected function alterBuild(array &$build, EntityInterface $comment, $view_mode, $langcode = NULL) {
parent::alterBuild($build, $comment, $view_mode, $langcode);
if (empty($comment->in_preview)) {
$prefix = '';
$is_threaded = isset($comment->divs)
&& variable_get('comment_default_mode_' . $comment->bundle(), 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);
}
}
}
}

View File

@ -39,7 +39,7 @@ class CommentContentRebuildTest extends CommentTestBase {
// Add the property to the content array and then see if it still exists on build.
$comment_loaded->content['test_property'] = array('#value' => $this->randomString());
$built_content = comment_view($comment_loaded, $this->node);
$built_content = comment_view($comment_loaded);
// This means that the content was rebuilt as the added test property no longer exists.
$this->assertFalse(isset($built_content['test_property']), 'Comment content was emptied before being built.');

View File

@ -304,7 +304,6 @@ function field_language($entity_type, $entity, $field_name = NULL, $langcode = N
$id = $entity->id();
$bundle = $entity->bundle();
$langcode = field_valid_language($langcode, FALSE);
if (!isset($display_langcodes[$entity_type][$id][$langcode])) {
$display_langcode = array();

View File

@ -100,6 +100,12 @@ function file_entity_info() {
'label' => 'filename',
'uuid' => 'uuid',
),
'view modes' => array(
'full' => array(
'label' => t('File default'),
'custom settings' => FALSE,
),
),
'static cache' => FALSE,
),
);

View File

@ -0,0 +1,93 @@
<?php
/**
* @file
* Definition of Drupal\node\NodeRenderController.
*/
namespace Drupal\node;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRenderController;
/**
* Render controller for nodes.
*/
class NodeRenderController extends EntityRenderController {
/**
* Overrides Drupal\Core\Entity\EntityRenderController::buildContent().
*/
public function buildContent(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
$return = array();
if (empty($entities)) {
return $return;
}
parent::buildContent($entities, $view_mode, $langcode);
foreach ($entities as $key => $entity) {
$entity_view_mode = $entity->content['#view_mode'];
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
if (node_hook($entity->bundle(), 'view')) {
$entity = node_invoke($entity, 'view', $entity_view_mode, $langcode);
}
$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 ($entity_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')),
);
// Add Language field text element to node render array.
$entity->content['language'] = array(
'#type' => 'item',
'#title' => t('Language'),
'#markup' => language_name($langcode),
'#weight' => 0,
'#prefix' => '<div id="field-language-display">',
'#suffix' => '</div>'
);
}
}
/**
* Overrides Drupal\Core\Entity\EntityRenderController::alterBuild().
*/
protected function alterBuild(array &$build, EntityInterface $entity, $view_mode, $langcode = NULL) {
parent::alterBuild($build, $entity, $view_mode, $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($entity->nid) && !($view_mode == 'full' && node_is_page($entity))) {
$build['#contextual_links']['node'] = array('node', array($entity->nid));
}
}
}

View File

@ -27,8 +27,11 @@ class NodeBuildContentTest extends NodeTestBase {
$node = $this->drupalCreateNode();
// Set a property in the content array so we can test for its existence later on.
$node->content['test_content_property'] = array('#value' => $this->randomString());
$content = node_build_content($node);
$node->content['test_content_property'] = array(
'#value' => $this->randomString(),
);
$nodes = array($node);
$content = entity_render_controller('node')->buildContent($nodes);
// If the property doesn't exist it means the node->content was rebuilt.
$this->assertFalse(isset($content['test_content_property']), 'Node content was emptied prior to being built.');

View File

@ -217,6 +217,7 @@ function node_entity_info() {
'bundle' => 'type',
),
'bundles' => array(),
'render controller class' => 'Drupal\node\NodeRenderController',
'view modes' => array(
'full' => array(
'label' => t('Full content'),
@ -1160,145 +1161,6 @@ function node_revision_delete($revision_id) {
entity_revision_delete('node', $revision_id);
}
/**
* Generates an array for rendering the given node.
*
* @param Drupal\node\Node $node
* A node entity.
* @param $view_mode
* (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'full.'
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return
* 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);
return $build;
}
/**
* Builds a structured array representing the node's content.
*
* The content built for the node (field values, comments, file attachments or
* other node components) will vary depending on the $view_mode parameter.
*
* Drupal core defines the following view modes for nodes, with the following
* default use cases:
* - full (default): node is being displayed on its own page (node/123)
* - teaser: node is being displayed on the default home page listing, or on
* taxonomy listing pages.
* - rss: node displayed in an RSS feed.
* If search.module is enabled:
* - search_index: node is being indexed for search.
* - search_result: node is being displayed as a search result.
* If book.module is enabled:
* - print: node is being displayed in print-friendly mode.
* Contributed modules might define additional view modes, or use existing
* view modes in additional contexts.
*
* @param Drupal\node\Node $node
* A node entity.
* @param $view_mode
* (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'full.'
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*/
function node_build_content(Node $node, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
}
// Remove previously built content, if exists.
$node->content = array();
// Allow modules to change the view mode.
$context = array('langcode' => $langcode);
drupal_alter('entity_view_mode', $view_mode, $node, $context);
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
if (node_hook($node->type, 'view')) {
$node = node_invoke($node, 'view', $view_mode, $langcode);
}
// Build fields content.
// In case of a multiple view, node_view_multiple() already ran the
// 'prepare_view' step. An internal flag prevents the operation from running
// twice.
field_attach_prepare_view('node', array($node->nid => $node), $view_mode, $langcode);
entity_prepare_view('node', array($node->nid => $node), $langcode);
$node->content += field_attach_view('node', $node, $view_mode, $langcode);
// 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();
$node->content['links'] = array(
'#theme' => 'links__node',
'#pre_render' => array('drupal_pre_render_links'),
'#attributes' => array('class' => array('links', 'inline')),
);
if ($view_mode == 'teaser') {
$node_title_stripped = strip_tags($node->label());
$links['node-readmore'] = array(
'title' => t('Read more<span class="element-invisible"> about @title</span>', array('@title' => $node_title_stripped)),
'href' => 'node/' . $node->nid,
'html' => TRUE,
'attributes' => array('rel' => 'tag', 'title' => $node_title_stripped),
);
}
$node->content['links']['node'] = array(
'#theme' => 'links__node__node',
'#links' => $links,
'#attributes' => array('class' => array('links', 'inline')),
);
// Add Language field text element to node render array.
$node->content['language'] = array(
'#type' => 'item',
'#title' => t('Language'),
'#markup' => language_name($langcode),
'#weight' => 0,
'#prefix' => '<div id="field-language-display">',
'#suffix' => '</div>'
);
// Allow modules to make their own additions to the node.
module_invoke_all('node_view', $node, $view_mode, $langcode);
module_invoke_all('entity_view', $node, $view_mode, $langcode);
}
/**
* Page callback: Generates an array which displays a node detail page.
*
@ -1319,7 +1181,7 @@ function node_show(Node $node, $message = FALSE) {
}
// For markup consistency with other pages, use node_view_multiple() rather than node_view().
$nodes = node_view_multiple(array($node->nid => $node), 'full');
$nodes = array('nodes' => node_view_multiple(array($node->nid => $node), 'full'));
// Update the history table, stating that this user viewed this node.
node_tag_new($node);
@ -2537,6 +2399,24 @@ function node_feed($nids = FALSE, $channel = array()) {
return new Response($output, 200, array('Content-Type' => 'application/rss+xml; charset=utf-8'));
}
/**
* Generates an array for rendering the given node.
*
* @param Drupal\node\Node $node
* A node entity.
* @param $view_mode
* (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'full.'
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return
* An array as expected by drupal_render().
*/
function node_view(Node $node, $view_mode = 'full', $langcode = NULL) {
return entity_view($node, $view_mode, $langcode);
}
/**
* Constructs a drupal_render() style array from an array of loaded nodes.
*
@ -2544,8 +2424,6 @@ function node_feed($nids = FALSE, $channel = array()) {
* An array of nodes as returned by node_load_multiple().
* @param $view_mode
* (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'teaser.'
* @param $weight
* (optional) Integer representing the weight of the first node in the list.
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
@ -2553,17 +2431,8 @@ function node_feed($nids = FALSE, $channel = array()) {
* @return
* 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();
foreach ($nodes as $node) {
$build['nodes'][$node->nid] = node_view($node, $view_mode, $langcode);
$build['nodes'][$node->nid]['#weight'] = $weight;
$weight++;
}
$build['nodes']['#sorted'] = TRUE;
return $build;
function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) {
return entity_view_multiple($nodes, $view_mode, $langcode);
}
/**
@ -2590,7 +2459,7 @@ function node_page_default() {
if (!empty($nids)) {
$nodes = node_load_multiple($nids);
$build = node_view_multiple($nodes);
$build['nodes'] = node_view_multiple($nodes);
// 'rss.xml' is a path, not a file, registered in node_menu().
drupal_add_feed('rss.xml', $site_config->get('name') . ' ' . t('RSS'));

View File

@ -121,7 +121,9 @@ class SearchMultilingualEntityTest extends SearchTestBase {
$body_language_variant = end($node->body);
$search_result = node_search_execute($body_language_variant[0]['value']);
// See whether we get the same node as a result.
$this->assertEqual($search_result[0]['node']->nid, $node->nid, 'The search has resulted the correct node.');
$sts = $this->assertTrue(!empty($search_result[0]['node']->nid)
&& $search_result[0]['node']->nid == $node->nid,
'The search has resulted the correct node.');
}
}
}

View File

@ -362,7 +362,7 @@ function hook_update_index() {
variable_set('node_cron_last', $node->changed);
// Render the node.
node_build_content($node, 'search_index');
$build = node_view($node, 'search_index');
$node->rendered = drupal_render($node->content);
$text = '<h1>' . check_plain($node->label()) . '</h1>' . $node->rendered;

View File

@ -0,0 +1,54 @@
<?php
/**
* @file
* Definition of Drupal\taxonomy\TermRenderController.
*/
namespace Drupal\taxonomy;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRenderController;
/**
* Render controller for taxonomy terms.
*/
class TermRenderController extends EntityRenderController {
/**
* Overrides Drupal\Core\Entity\EntityRenderController::buildContent().
*/
public function buildContent(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
parent::buildContent($entities, $view_mode, $langcode);
foreach ($entities as $entity) {
// Add the description if enabled.
$bundle = $entity->bundle();
$entity_view_mode = $entity->content['#view_mode'];
$fields = field_extra_fields_get_display($this->entityType, $bundle, $entity_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>',
);
}
}
}
protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
$return = parent::getBuildDefaults($entity, $view_mode, $langcode);
// TODO: rename "term" to "taxonomy_term" in theme_taxonomy_term().
$return['#term'] = $return["#{$this->entityType}"];
unset($return["#{$this->entityType}"]);
return $return;
}
protected function alterBuild(array &$build, EntityInterface $entity, $view_mode, $langcode = NULL) {
parent::alterBuild($build, $entity, $view_mode, $langcode);
$build['#attached']['css'][] = drupal_get_path('module', 'taxonomy') . '/taxonomy.css';
}
}

View File

@ -130,6 +130,7 @@ function taxonomy_entity_info() {
'bundle' => 'machine_name',
),
'bundles' => array(),
'render controller class' => 'Drupal\taxonomy\TermRenderController',
'view modes' => array(
// @todo View mode for display as a field (when attached to nodes etc).
'full' => array(
@ -163,6 +164,12 @@ function taxonomy_entity_info() {
'label' => 'name',
),
'fieldable' => FALSE,
'view modes' => array(
'full' => array(
'label' => t('Taxonomy vocabulary default'),
'custom settings' => FALSE,
),
),
);
return $return;
@ -574,22 +581,26 @@ function taxonomy_term_delete_multiple(array $tids) {
*
* @param Drupal\taxonomy\Term $term
* A taxonomy term object.
* @param string $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param string $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return array
* A $page element suitable for use by drupal_page_render().
*/
function taxonomy_term_show(Term $term) {
return taxonomy_term_view_multiple(array($term->tid => $term), 'full');
function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
return entity_view($term, $view_mode, $langcode);
}
/**
/**
* Constructs a drupal_render() style array from an array of loaded terms.
*
* @param array $terms
* An array of taxonomy terms as returned by taxonomy_term_load_multiple().
* @param string $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param int $weight
* An integer representing the weight of the first node in the list.
* @param string $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
@ -597,115 +608,8 @@ function taxonomy_term_show(Term $term) {
* @return array
* An array in the format expected by drupal_render().
*/
function taxonomy_term_view_multiple(array $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();
foreach ($terms as $term) {
$build['taxonomy_terms'][$term->tid] = taxonomy_term_view($term, $view_mode, $langcode);
$build['taxonomy_terms'][$term->tid]['#weight'] = $weight;
$weight++;
}
$build['taxonomy_terms']['#sorted'] = TRUE;
return $build;
}
/**
* Builds a structured array representing the term's content.
*
* The content built for the taxonomy term (field values, file attachments or
* other term components) will vary depending on the $view_mode parameter.
*
* Drupal core defines the following view modes for terms, with the following
* default use cases:
* - full (default): term is displayed on its own page (taxonomy/term/123)
* Contributed modules might define additional view modes, or use existing
* view modes in additional contexts.
*
* @param Drupal\taxonomy\Term $term
* A taxonomy term object.
* @param string $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param string $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*/
function taxonomy_term_build_content(Term $term, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
}
// Remove previously built content, if exists.
$term->content = array();
// Allow modules to change the view mode.
$context = array('langcode' => $langcode);
drupal_alter('entity_view_mode', $view_mode, $term, $context);
// Try to add in the core taxonomy pieces like description and nodes.
$settings = field_view_mode_settings($term->entityType(), $term->bundle());
$fields = field_extra_fields_get_display($term->entityType(), $term->bundle(), $view_mode);
if (!empty($term->description) && isset($fields['description']) && $fields['description']['visible']) {
$term->content['description'] = array(
'#markup' => check_markup($term->description, $term->format, '', TRUE),
'#weight' => $fields['description']['weight'],
'#prefix' => '<div class="taxonomy-term-description">',
'#suffix' => '</div>',
);
}
// Build fields content.
// In case of a multiple view, taxonomy_term_view_multiple() already ran the
// 'prepare_view' step. An internal flag prevents the operation from running
// twice.
field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode);
entity_prepare_view('taxonomy_term', array($term->tid => $term), $langcode);
$term->content += field_attach_view('taxonomy_term', $term, $view_mode, $langcode);
// Allow modules to make their own additions to the taxonomy term.
module_invoke_all('taxonomy_term_view', $term, $view_mode, $langcode);
module_invoke_all('entity_view', $term, $view_mode, $langcode);
}
/**
* Generate an array for rendering the given term.
*
* @param Drupal\taxonomy\Term $term
* A taxonomy term entity.
* @param string $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param string $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return
* 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);
return $build;
function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcode = NULL) {
return entity_view_multiple($terms, $view_mode, $langcode);
}
/**

View File

@ -45,10 +45,10 @@ function taxonomy_term_page(Term $term) {
// Set the non-aliased path as a default shortlink.
drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE);
$build = taxonomy_term_show($term);
$build['taxonomy_terms'] = taxonomy_term_view_multiple(array($term->id() => $term));
if ($nids = taxonomy_select_nodes($term->tid, TRUE, variable_get('default_nodes_main', 10))) {
$nodes = node_load_multiple($nids);
$build += node_view_multiple($nodes);
$build['nodes'] = node_view_multiple($nodes);
$build['pager'] = array(
'#theme' => 'pager',
'#weight' => 5,

View File

@ -0,0 +1,30 @@
<?php
/**
* @file
* Definition of Drupal\user\UserRenderController.
*/
namespace Drupal\user;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRenderController;
/**
* Render controller for users.
*/
class UserRenderController extends EntityRenderController {
/**
* Overrides Drupal\Core\Entity\EntityRenderController::getBuildDefaults().
*/
protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
$return = parent::getBuildDefaults($entity, $view_mode, $langcode);
// @todo rename "theme_user_profile" to "theme_user", 'account' to 'user'.
$return['#theme'] = 'user_profile';
$return['#account'] = $return['#user'];
return $return;
}
}

View File

@ -168,6 +168,7 @@ function user_entity_info() {
),
),
),
'render controller class' => 'Drupal\user\UserRenderController',
'view modes' => array(
'full' => array(
'label' => t('User account'),
@ -2062,61 +2063,25 @@ 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);
return $build;
return entity_view($account, $view_mode, $langcode);
}
/**
* Builds a structured array representing the profile content.
* Constructs a drupal_render() style array from an array of loaded users.
*
* @param $account
* A user object.
* @param $accounts
* An array of user accounts as returned by user_load_multiple().
* @param $view_mode
* View mode, e.g. 'full'.
* (optional) View mode, e.g., 'full', 'teaser'... Defaults to 'teaser.'
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return
* An array in the format expected by drupal_render().
*/
function user_build_content($account, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = language(LANGUAGE_TYPE_CONTENT)->langcode;
}
// Remove previously built content, if exists.
$account->content = array();
// Allow modules to change the view mode.
$context = array('langcode' => $langcode);
drupal_alter('entity_view_mode', $view_mode, $account, $context);
// Build fields content.
field_attach_prepare_view('user', array($account->uid => $account), $view_mode, $langcode);
entity_prepare_view('user', array($account->uid => $account), $langcode);
$account->content += field_attach_view('user', $account, $view_mode, $langcode);
// Populate $account->content with a render() array.
module_invoke_all('user_view', $account, $view_mode, $langcode);
module_invoke_all('entity_view', $account, $view_mode, $langcode);
function user_view_multiple($accounts, $view_mode = 'full', $langcode = NULL) {
return entity_view($accounts, $view_mode, $langcode);
}
/**