Commit 087a47ba authored by webchick's avatar webchick

#493314 by yched and catch: Add multiple hook for formatters.

parent 13d3072f
......@@ -844,6 +844,7 @@ function comment_build_content($comment, $node, $build_mode = 'full') {
'#markup' => check_markup($comment->comment, $comment->format, '', TRUE),
);
field_attach_prepare_view('comment', array($comment->cid => $comment), $build_mode);
$comment->content += field_attach_view('comment', $comment, $build_mode);
if (empty($comment->in_preview)) {
......@@ -937,6 +938,8 @@ function comment_links($comment, $node) {
* An array in the format expected by drupal_render().
*/
function comment_build_multiple($comments, $node, $build_mode = 'full', $weight = 0) {
field_attach_prepare_view('comment', $comments, $build_mode);
$build = array(
'#sorted' => TRUE,
);
......
......@@ -248,8 +248,11 @@ function hook_field_schema($field) {
* For performance reasons, information for all available objects should be
* loaded in a single query where possible.
*
* Note that the changes made to the field values get cached by the
* field cache for subsequent loads.
* Note that the changes made to the field values get cached by the field cache
* for subsequent loads. You should never use this hook to load fieldable
* entities, since this is likely to cause infinite recursions when
* hook_field_load() is run on those as well. Use
* hook_field_formatter_prepare_view() instead.
*
* @param $obj_type
* The type of $object.
......@@ -825,6 +828,34 @@ function theme_field_formatter_FORMATTER_MULTIPLE($variables) {
return $output;
}
/**
* Allow formatters to load information for multiple objects.
*
* This should be used when a formatter needs to load additional information
* from the database in order to render a field, for example a reference field
* which displays properties of the referenced objects such as name or type.
*
* @param $obj_type
* The type of $object.
* @param $objects
* Array of objects being displayed, keyed by object id.
* @param $field
* The field structure for the operation.
* @param $instances
* Array of instance structures for $field for each object, keyed by object id.
* @param $langcode
* The language the field values are to be shown in. If no language is
* provided the current language is used.
* @param $items
* Array of field values for the objects, keyed by object id.
* @return
* Changes or additions to field values are done by altering the $items
* parameter by reference.
*/
function hook_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $build_mode) {
}
/**
* @} End of "ingroup field_type"
*/
......@@ -842,6 +873,41 @@ function theme_field_formatter_FORMATTER_MULTIPLE($variables) {
* See field_attach_form() for details and arguments.
*/
function hook_field_attach_form($obj_type, $object, &$form, &$form_state, $langcode) {
$tids = array();
// Collect every possible term attached to any of the fieldable entities.
foreach ($objects as $id => $object) {
foreach ($items[$id] as $delta => $item) {
// Force the array key to prevent duplicates.
$tids[$item['value']] = $item['value'];
}
}
if ($tids) {
$terms = array();
// Avoid calling taxonomy_term_load_multiple because it could lead to
// circular references.
$query = db_select('taxonomy_term_data', 't');
$query->fields('t');
$query->condition('t.tid', $tids, 'IN');
$query->addTag('term_access');
$terms = $query->execute()->fetchAllAssoc('tid');
// Iterate through the fieldable entities again to attach the loaded term data.
foreach ($objects as $id => $object) {
foreach ($items[$id] as $delta => $item) {
// Check whether the taxonomy term field instance value could be loaded.
if (isset($terms[$item['value']])) {
// Replace the instance value with the term data.
$items[$id][$delta]['taxonomy_term'] = $terms[$item['value']];
}
// Otherwise, unset the instance value, since the term does not exist.
else {
unset($items[$id][$delta]);
}
}
}
}
}
/**
......
......@@ -1109,6 +1109,13 @@ function field_attach_query_revisions($field_id, $conditions, $count, &$cursor =
return field_attach_query($field_id, $conditions, $count, $cursor, FIELD_LOAD_REVISION);
}
/**
* Allow formatters to act on fieldable objects prior to rendering.
*/
function field_attach_prepare_view($obj_type, $objects, $build_mode = 'full') {
_field_invoke_multiple_default('prepare_view', $obj_type, $objects, $build_mode);
}
/**
* Generate and return a structured content array tree suitable for
* drupal_render() for all of the fields on an object. The format of
......
......@@ -53,6 +53,30 @@ function field_default_insert($obj_type, $object, $field, $instance, $langcode,
}
}
/**
* Invoke hook_field_formatter_prepare_view() on the relavant formatters.
*/
function field_default_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $options, $build_mode) {
// Group objects, instances and items by formatter module.
$modules = array();
foreach ($instances as $id => $instance) {
$module = $instance['display'][$build_mode]['module'];
$modules[$module] = $module;
$grouped_objects[$module][$id] = $objects[$id];
$grouped_instances[$module][$id] = $instance;
// hook_field_formatter_prepare_view() alters $items by reference.
$grouped_items[$module][$id] = &$items[$id];
}
foreach ($modules as $module) {
// Invoke hook_field_formatter_prepare_view().
$function = $module . '_field_formatter_prepare_view';
if (function_exists($function)) {
$function($obj_type, $grouped_objects[$module], $field, $grouped_instances[$module], $langcode, $grouped_items[$module], $build_mode);
}
}
}
/**
* Default field 'view' operation.
*
......
......@@ -786,18 +786,19 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
/**
* Test field_attach_views() and field_attach_preprocess().
* Test field_attach_view() and field_atach_prepare_view().
*/
function testFieldAttachViewAndPreprocess() {
function testFieldAttachView() {
$entity_type = 'test_entity';
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
$entity_init = field_test_create_stub_entity();
$langcode = FIELD_LANGUAGE_NONE;
// Populate values to be displayed.
$values = $this->_generateTestFieldValues($this->field['cardinality']);
$entity->{$this->field_name}[$langcode] = $values;
$entity_init->{$this->field_name}[$langcode] = $values;
// Simple formatter, label displayed.
$entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
......@@ -809,6 +810,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
......@@ -819,14 +821,17 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
// Label hidden.
$entity = clone($entity_init);
$this->instance['display']['full']['label'] = 'hidden';
field_update_instance($this->instance);
field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
$this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed.");
// Field hidden.
$entity = clone($entity_init);
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
......@@ -834,6 +839,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
......@@ -843,6 +849,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
}
// Multiple formatter.
$entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
......@@ -854,6 +861,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
),
);
field_update_instance($this->instance);
field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$display = $formatter_setting;
......@@ -863,6 +871,29 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$this->content = $output;
$this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied.");
// Test a formatter that uses hook_field_formatter_prepare_view()..
$entity = clone($entity_init);
$formatter_setting = $this->randomName();
$this->instance['display'] = array(
'full' => array(
'label' => 'above',
'type' => 'field_test_needs_additional_data',
'settings' => array(
'test_formatter_setting_additional' => $formatter_setting,
)
),
);
field_update_instance($this->instance);
field_attach_prepare_view($entity_type, array($entity->ftid => $entity));
$entity->content = field_attach_view($entity_type, $entity);
$output = drupal_render($entity->content);
$this->content = $output;
foreach ($values as $delta => $value) {
$this->content = $output;
$expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
$this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
}
// TODO:
// - check display order with several fields
......
......@@ -1241,7 +1241,7 @@ function node_show($node, $message = FALSE) {
node_tag_new($node->nid);
// For markup consistency with other pages, use node_build_multiple() rather than node_build().
return node_build_multiple(array($node), 'full');
return node_build_multiple(array($node->nid => $node), 'full');
}
/**
......@@ -2034,6 +2034,7 @@ function node_feed($nids = FALSE, $channel = array()) {
* An array in the format expected by drupal_render().
*/
function node_build_multiple($nodes, $build_mode = 'teaser', $weight = 0) {
field_attach_prepare_view('node', $nodes, $build_mode);
$build = array();
foreach ($nodes as $node) {
$build['nodes'][$node->nid] = node_build($node, $build_mode);
......
......@@ -355,6 +355,8 @@ function node_preview($node) {
}
$node->changed = REQUEST_TIME;
$nodes = array($node->nid => $node);
field_attach_prepare_view('node', $nodes, 'full');
// Display a preview of the node.
// Previewing alters $node so it needs to be cloned.
......
......@@ -564,6 +564,13 @@ function field_test_field_formatter_info() {
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
),
),
'field_test_needs_additional_data' => array(
'label' => t('Tests hook_field_formatter_prepare_view()'),
'field types' => array('test_field'),
'settings' => array(
'test_formatter_setting_additional' => 'dummy test string',
),
),
);
}
......@@ -578,9 +585,30 @@ function field_test_theme() {
'field_formatter_field_test_multiple' => array(
'arguments' => array('element' => NULL),
),
'field_formatter_field_test_needs_additional_data' => array(
'arguments' => array('element' => NULL),
),
);
}
/**
* Implement hook_field_formatter_prepare_view().
*/
function field_test_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $build_mode) {
foreach ($items as $id => $item) {
// To keep the test non-intrusive, only act on the
// 'field_test_needs_additional_data' formatter.
if ($instances[$id]['display'][$build_mode]['type'] == 'field_test_needs_additional_data') {
foreach ($item as $delta => $value) {
// Don't add anything on empty values.
if ($value) {
$items[$id][$delta]['additional_formatter_value'] = $value['value'] + 1;
}
}
}
}
}
/**
* Theme function for 'field_test_default' formatter.
*/
......@@ -609,6 +637,18 @@ function theme_field_formatter_field_test_multiple($variables) {
return $settings['test_formatter_setting_multiple'] . '|' . $output;
}
/**
* Theme function for 'field_test_needs_additional_data' formatter.
*/
function theme_field_formatter_field_test_needs_additional_data($variables) {
$element = $variables['element'];
$value = $element['#item']['value'];
$additional = $element['#item']['additional_formatter_value'];
$settings = $element['#settings'];
return $settings['test_formatter_setting_additional'] . '|' . $value . '|' . $additional;
}
/**
* Sample function to test default value assignment.
*/
......
......@@ -1131,7 +1131,7 @@ function taxonomy_allowed_values($field) {
* This preloads all taxonomy terms for multiple loaded objects at once and
* unsets values for invalid terms that do not exist.
*/
function taxonomy_field_load($obj_type, $objects, $field, $instances, $langcode, &$items, $age) {
function taxonomy_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $age) {
$tids = array();
// Collect every possible term attached to any of the fieldable entities.
......
......@@ -30,6 +30,7 @@ function taxonomy_term_page($term) {
drupal_add_feed(url('taxonomy/term/' . $term->tid . '/feed'), 'RSS - ' . $term->name);
drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
field_attach_prepare_view('taxonomy_term', array($term->tid => $term), 'full');
$build = array();
$build += field_attach_view('taxonomy_term', $term);
if (!empty($term->description)) {
......
......@@ -758,6 +758,8 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase {
// Display the object.
$entity = field_test_entity_load($id);
$entities = array($id => $entity);
field_attach_prepare_view($entity_type, $entities, 'full');
$entity->content = field_attach_view($entity_type, $entity);
$this->content = drupal_render($entity->content);
$this->assertText($term->name, t('Term name is displayed'));
......
......@@ -2102,6 +2102,9 @@ function user_build($account) {
function user_build_content($account) {
$account->content = array();
$accounts = array($account->uid, $account);
field_attach_prepare_view('user', $accounts, 'full');
// Build fields content.
$account->content += field_attach_view('user', $account);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment