diff --git a/modules/field/field.module b/modules/field/field.module index 9b21d3ba419fc5adddf6cf82294aa621d9f42a3f..5370838dba06e5cfed3d4697798d6645ba9c8cfd 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -471,98 +471,50 @@ function _field_filter_xss_display_allowed_tags() { } /** - * Format a field item for display. + * Returns a renderable array for a single field value. * - * TODO D7 : do we still need field_format ? - * - backwards compatibility of templates - check what fallbacks we can propose... - * - was used by Views integration in CCK in D6 - do we need now? - * At least needs a little rehaul/update... - * - * Used to display a field's values outside the context of the $node, as - * when fields are displayed in Views, or to display a field in a template - * using a different formatter than the one set up on the 'Manage display' tab - * for the node's context. - * - * @param $field - * Either a field array or the name of the field. + * @param $entity_type + * The type of $entity; e.g. 'node' or 'user'. + * @param $entity + * The entity containing the field to display. Must at least contain the id + * key and the field data to display. + * @param $field_name + * The name of the field to display. * @param $item - * The field item(s) to be formatted (such as $node->field_foo[0], - * or $node->field_foo if the formatter handles multiple values itself) - * @param $formatter_type - * The name of the formatter type to use. - * @param $node - * Optionally, the containing node object for context purposes and - * field-instance options. - * + * The field value to display, as found in + * $entity->field_name[$langcode][$delta]. + * @param $display + * Can be either the name of a view mode, or an array of display settings. + * See field_view_field() for more information. + * @param $langcode + * (Optional) The language of the value in $item. If not provided, the + * current language will be assumed. * @return - * A string containing the contents of the field item(s) sanitized for display. - * It will have been passed through the necessary check_plain() or check_markup() - * functions as necessary. + * A renderable array for the field value. */ -function field_format($entity_type, $entity, $field, $item, $formatter_type = NULL, $formatter_settings = array()) { - if (!is_array($field)) { - $field = field_info_field($field); - } - - if (field_access('view', $field, $entity_type, $entity)) { - $field_type = field_info_field_types($field['type']); +function field_view_value($entity_type, $entity, $field_name, $item, $display = array(), $langcode = NULL) { + $output = array(); - // We need $field, $instance, $entity_type, $entity to be able to display a value... - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - $instance = field_info_instance($entity_type, $field['field_name'], $bundle); + if ($field = field_info_field($field_name)) { + $langcode = field_multilingual_valid_language($langcode, FALSE); - $display = array( - 'type' => $formatter_type ? $formatter_type : $field_type['default_formatter'], - 'settings' => $formatter_settings, - ); - $display['settings'] += field_info_formatter_settings($display['type']); - - if ($display['type'] !== 'hidden') { - $theme = $formatter['module'] . '_formatter_' . $display['type']; - - $element = array( - '#theme' => $theme, - '#field_name' => $field['field_name'], - '#object_type' => $entity_type, - '#bundle' => $bundle, - '#formatter' => $display['type'], - '#settings' => $display['settings'], - '#object' => $entity, - '#object_type' => $entity_type, - '#delta' => isset($item['#delta']) ? $item['#delta'] : NULL, - ); - - if (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT) { - // Single value formatter. - - // hook_field('sanitize') expects an array of items, so we build one. - $items = array($item); - $function = $field['module'] . '_field_sanitize'; - if (function_exists($function)) { - $function($entity_type, $entity, $field, $instance, $items); - } + // Determine the langcode that will be used by language fallback. + $langcode = current(field_multilingual_available_languages($entity_type, $field, array($langcode))); - $element['#item'] = $items[0]; - } - else { - // Multiple values formatter. - $items = $item; - $function = $field['module'] . '_field_sanitize'; - if (function_exists($function)) { - $function($entity_type, $entity, $field, $instance, $items); - } + // Push the item as the single value for the field, and defer to + // field_view_field() to build the render array for the whole field. + $clone = clone $entity; + $clone->{$field_name}[$langcode] = array($item); + $elements = field_view_field($entity_type, $clone, $field_name, $display, $langcode); - foreach ($items as $delta => $item) { - $element[$delta] = array( - '#item' => $item, - '#weight' => $delta, - ); - } - } - - return theme($theme, $element); + // Extract the part of the render array we need. + $output = isset($elements[0]) ? $elements[0] : array(); + if (isset($elements['#access'])) { + $output['#access'] = $elements['#access']; } } + + return $output; } /** @@ -638,12 +590,17 @@ function field_view_field($entity_type, $entity, $field_name, $display = array() // 'single field' mode, to reuse the language fallback logic. $options = array('field_name' => $field_name, 'language' => field_multilingual_valid_language($langcode, FALSE)); $null = NULL; - list($id) = entity_extract_ids($entity_type, $entity); - // First let the field types do their preparation. - _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options); - // Then let the formatters do their own specific massaging. - _field_invoke_multiple_default('prepare_view', $entity_type, array($id => $entity), $display, $null, $options); + // Invoke prepare_view steps if needed. + if (empty($entity->_field_view_prepared)) { + list($id) = entity_extract_ids($entity_type, $entity); + + // First let the field types do their preparation. + _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options); + // Then let the formatters do their own specific massaging. + _field_invoke_multiple_default('prepare_view', $entity_type, array($id => $entity), $display, $null, $options); + } + // Build the renderable array. $result = _field_invoke_default('view', $entity_type, $entity, $display, $null, $options); diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index f1bc28c71a50baa8658bc8480e2de6fb072d6eb8..dc56f4eb5fd0494cd39c839bfb4b6a0d9e83e056 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -925,7 +925,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { $this->instance['display'] = array( 'full' => array( 'label' => 'above', - 'type' => 'field_test_needs_additional_data', + 'type' => 'field_test_with_prepare_view', 'settings' => array( 'test_formatter_setting_additional' => $formatter_setting, ) @@ -1805,7 +1805,7 @@ class FieldDisplayAPITestCase extends FieldTestCase { /** * Test the field_view_field() function. */ - function testFieldBuildField() { + function testFieldViewField() { // No display settings: check that default display settings are used. $output = field_view_field('test_entity', $this->entity, $this->field_name); $this->drupalSetContent(drupal_render($output)); @@ -1834,6 +1834,23 @@ class FieldDisplayAPITestCase extends FieldTestCase { } $this->assertText($setting . '|' . implode('|', $array), t('Values were displayed with expected setting.')); + // Check the prepare_view steps are invoked. + $display = array( + 'label' => 'hidden', + 'type' => 'field_test_with_prepare_view', + 'settings' => array( + 'test_formatter_setting_additional' => $this->randomName(), + ), + ); + $output = field_view_field('test_entity', $this->entity, $this->field_name, $display); + $view = drupal_render($output); + $this->drupalSetContent($view); + $setting = $display['settings']['test_formatter_setting_additional']; + $this->assertNoText($this->label, t('Label was not displayed.')); + foreach ($this->values as $delta => $value) { + $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); + } + // View mode: check that display settings specified in the instance are // used. $output = field_view_field('test_entity', $this->entity, $this->field_name, 'full'); @@ -1854,6 +1871,73 @@ class FieldDisplayAPITestCase extends FieldTestCase { $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); } } + + /** + * Test the field_view_value() function. + */ + function testFieldViewValue() { + // No display settings: check that default display settings are used. + $settings = field_info_formatter_settings('field_test_default'); + $setting = $settings['test_formatter_setting']; + foreach ($this->values as $delta => $value) { + $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta]; + $output = field_view_value('test_entity', $this->entity, $this->field_name, $item); + $this->drupalSetContent(drupal_render($output)); + $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); + } + + // Check that explicit display settings are used. + $display = array( + 'type' => 'field_test_multiple', + 'settings' => array( + 'test_formatter_setting_multiple' => $this->randomName(), + ), + ); + $setting = $display['settings']['test_formatter_setting_multiple']; + $array = array(); + foreach ($this->values as $delta => $value) { + $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta]; + $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display); + $this->drupalSetContent(drupal_render($output)); + $this->assertText($setting . '|0:' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); + } + + // Check that prepare_view steps are invoked. + $display = array( + 'type' => 'field_test_with_prepare_view', + 'settings' => array( + 'test_formatter_setting_additional' => $this->randomName(), + ), + ); + $setting = $display['settings']['test_formatter_setting_additional']; + $array = array(); + foreach ($this->values as $delta => $value) { + $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta]; + $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display); + $this->drupalSetContent(drupal_render($output)); + $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); + } + + // View mode: check that display settings specified in the instance are + // used. + $setting = $this->instance['display']['full']['settings']['test_formatter_setting']; + foreach ($this->values as $delta => $value) { + $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta]; + $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'full'); + $this->drupalSetContent(drupal_render($output)); + $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); + } + + // Unknown view mode: check that display settings for 'full' view mode + // are used. + $setting = $this->instance['display']['full']['settings']['test_formatter_setting']; + foreach ($this->values as $delta => $value) { + $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta]; + $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'unknown_view_mode'); + $this->drupalSetContent(drupal_render($output)); + $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta))); + } + } } class FieldCrudTestCase extends FieldTestCase { diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index 1213d4d4dab5716b80b09cd3393a79a8c3b382f7..faf1b1469739234cb27cabfbac5a32ed8344ab23 100644 --- a/modules/field/tests/field_test.field.inc +++ b/modules/field/tests/field_test.field.inc @@ -248,7 +248,7 @@ function field_test_field_formatter_info() { 'test_formatter_setting_multiple' => 'dummy test string', ), ), - 'field_test_needs_additional_data' => array( + 'field_test_with_prepare_view' => array( 'label' => t('Tests hook_field_formatter_prepare_view()'), 'field types' => array('test_field'), 'settings' => array( @@ -264,8 +264,8 @@ function field_test_field_formatter_info() { function field_test_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) { foreach ($items as $id => $item) { // To keep the test non-intrusive, only act on the - // 'field_test_needs_additional_data' formatter. - if ($displays[$id]['type'] == 'field_test_needs_additional_data') { + // 'field_test_with_prepare_view' formatter. + if ($displays[$id]['type'] == 'field_test_with_prepare_view') { foreach ($item as $delta => $value) { // Don't add anything on empty values. if ($value) { @@ -290,7 +290,7 @@ function field_test_field_formatter_view($entity_type, $entity, $field, $instanc } break; - case 'field_test_needs_additional_data': + case 'field_test_with_prepare_view': foreach ($items as $delta => $item) { $element[$delta] = array('#markup' => $settings['test_formatter_setting_additional'] . '|' . $item['value'] . '|' . $item['additional_formatter_value']); }