Index: modules/field/field.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.test,v retrieving revision 1.41 diff -u -r1.41 field.test --- modules/field/field.test 17 Aug 2009 07:12:16 -0000 1.41 +++ modules/field/field.test 19 Aug 2009 23:23:02 -0000 @@ -449,15 +449,19 @@ $this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result')); } - function testFieldAttachViewAndPreprocess() { + /** + * Test field_attach_view() and field_atach_prepare_view(). + */ + function testFieldAttachView() { $entity_type = 'test_entity'; - $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + $entity_init = field_test_create_stub_entity(); // Populate values to be displayed. $values = $this->_generateTestFieldValues($this->field['cardinality']); - $entity->{$this->field_name} = $values; + $entity_init->{$this->field_name} = $values; // Simple formatter, label displayed. + $entity = clone($entity_init); $formatter_setting = $this->randomName(); $this->instance['display'] = array( 'full' => array( @@ -469,6 +473,7 @@ ), ); 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; @@ -479,14 +484,17 @@ } // 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', @@ -494,6 +502,7 @@ ), ); 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; @@ -503,6 +512,7 @@ } // Multiple formatter. + $entity = clone($entity_init); $formatter_setting = $this->randomName(); $this->instance['display'] = array( 'full' => array( @@ -514,6 +524,7 @@ ), ); 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; @@ -523,6 +534,29 @@ $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 } Index: modules/field/field.attach.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v retrieving revision 1.37 diff -u -r1.37 field.attach.inc --- modules/field/field.attach.inc 19 Aug 2009 13:31:12 -0000 1.37 +++ modules/field/field.attach.inc 19 Aug 2009 23:23:01 -0000 @@ -980,6 +980,13 @@ } /** + * 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 * each field's rendered content depends on the display formatter and Index: modules/field/field.default.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v retrieving revision 1.15 diff -u -r1.15 field.default.inc --- modules/field/field.default.inc 19 Aug 2009 13:31:12 -0000 1.15 +++ modules/field/field.default.inc 19 Aug 2009 23:23:01 -0000 @@ -47,6 +47,32 @@ $items = field_get_default_value($obj_type, $object, $field, $instance); } } + + +/** + * Invoke hook_field_formatter_prepare_view() on the relavant formatters. + */ +function field_default_prepare_view($obj_type, $objects, $field, $instances, &$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; + $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 (drupal_function_exists($function)) { + $function($obj_type, $grouped_objects[$module], $field, $grouped_instances[$module], $grouped_items[$module], $build_mode); + } + } +} + /** * Default field 'view' operation. * Index: modules/field/field.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.api.php,v retrieving revision 1.27 diff -u -r1.27 field.api.php --- modules/field/field.api.php 19 Aug 2009 13:31:12 -0000 1.27 +++ modules/field/field.api.php 19 Aug 2009 23:23:00 -0000 @@ -887,6 +887,30 @@ } /** + * 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 $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, &$items, $build_mode) { +} + +/** * @} End of "ingroup field_type" */ Index: modules/taxonomy/taxonomy.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.pages.inc,v retrieving revision 1.32 diff -u -r1.32 taxonomy.pages.inc --- modules/taxonomy/taxonomy.pages.inc 18 Aug 2009 06:01:07 -0000 1.32 +++ modules/taxonomy/taxonomy.pages.inc 19 Aug 2009 23:23:03 -0000 @@ -30,6 +30,7 @@ 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)) { @@ -109,8 +110,8 @@ ->condition('t.vid', $vid) // Select rows that either match by term or synonym name. ->condition(db_or() - ->where("LOWER(t.name) LIKE :last_string", array(':last_string' => '%' . $tag_last . '%')) - ->where("LOWER(ts.name) LIKE :last_string", array(':last_string' => '%' . $tag_last . '%')) + ->where("LOWER(t.name) LIKE :last_string", array(':last_string' => '%' . $tag_last . '%')) + ->where("LOWER(ts.name) LIKE :last_string", array(':last_string' => '%' . $tag_last . '%')) ) ->range(0, 10) ->execute() Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1101 diff -u -r1.1101 node.module --- modules/node/node.module 19 Aug 2009 13:31:13 -0000 1.1101 +++ modules/node/node.module 19 Aug 2009 23:23:03 -0000 @@ -1219,7 +1219,7 @@ 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'); } /** @@ -1977,6 +1977,7 @@ * 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); Index: modules/simpletest/tests/field_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/field_test.module,v retrieving revision 1.15 diff -u -r1.15 field_test.module --- modules/simpletest/tests/field_test.module 19 Aug 2009 22:46:05 -0000 1.15 +++ modules/simpletest/tests/field_test.module 19 Aug 2009 23:23:03 -0000 @@ -514,12 +514,9 @@ 'settings' => array( 'test_formatter_setting' => 'dummy test string', ), - 'behaviors' => array( - 'multiple values' => FIELD_BEHAVIOR_DEFAULT, - ), ), 'field_test_multiple' => array( - 'label' => t('Default'), + 'label' => t('Tests multiple value display'), 'field types' => array('test_field'), 'settings' => array( 'test_formatter_setting_multiple' => 'dummy test string', @@ -528,6 +525,13 @@ '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', + ), + ), ); } @@ -542,10 +546,31 @@ '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, &$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. */ function theme_field_formatter_field_test_default($element) { @@ -570,6 +595,17 @@ } /** + * Theme function for 'field_test_needs_additional_data' formatter. + */ +function theme_field_formatter_field_test_needs_additional_data($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. */ function field_test_default_value($obj_type, $object, $field, $instance) { Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1023 diff -u -r1.1023 user.module --- modules/user/user.module 19 Aug 2009 13:31:14 -0000 1.1023 +++ modules/user/user.module 19 Aug 2009 23:23:05 -0000 @@ -1696,7 +1696,7 @@ /** * The final validation handler on the login form. - * + * * Sets a form error if user has not been authenticated, or if too many * logins have been attempted. This validation function should always * be the last one. @@ -2106,11 +2106,12 @@ $account->content = array(); // Build fields content. + $accounts = array($account->uid, $account); + field_attach_prepare_view('user', $accounts, 'full'); // TODO D7 : figure out where exactly this needs to go $account->content += field_attach_view('user', $account); module_invoke_all('user_view', $account); - return $account->content; } /** Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.753 diff -u -r1.753 comment.module --- modules/comment/comment.module 17 Aug 2009 13:10:45 -0000 1.753 +++ modules/comment/comment.module 19 Aug 2009 23:23:00 -0000 @@ -819,6 +819,7 @@ '#markup' => check_markup($comment->comment, $comment->format, '', FALSE), ); + 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)) {