diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-12-11 16:49:40 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-12-11 16:49:40 +0000 |
commit | ef2c240086e5014db6bdd37c43a75f045bcd5504 (patch) | |
tree | a5cfc85ab30d66cc5326085dcec570467be43c72 | |
parent | 47182dfb1f65d2a107037a1386d85e6997afcd04 (diff) | |
download | brdo-ef2c240086e5014db6bdd37c43a75f045bcd5504.tar.gz brdo-ef2c240086e5014db6bdd37c43a75f045bcd5504.tar.bz2 |
- Patch #652834 by yched, effulgentsia: changed Field formatters as render arrays to increase performance (and to clean-up the code).
-rw-r--r-- | modules/field/field.api.php | 149 | ||||
-rw-r--r-- | modules/field/field.attach.inc | 88 | ||||
-rw-r--r-- | modules/field/field.default.inc | 71 | ||||
-rw-r--r-- | modules/field/field.module | 34 | ||||
-rw-r--r-- | modules/field/modules/list/list.module | 37 | ||||
-rw-r--r-- | modules/field/modules/number/number.module | 68 | ||||
-rw-r--r-- | modules/field/modules/text/text.module | 134 | ||||
-rw-r--r-- | modules/field/tests/field.test | 4 | ||||
-rw-r--r-- | modules/field/tests/field_test.field.inc | 75 | ||||
-rw-r--r-- | modules/file/file.field.inc | 48 | ||||
-rw-r--r-- | modules/file/file.module | 3 | ||||
-rw-r--r-- | modules/image/image.field.inc | 72 | ||||
-rw-r--r-- | modules/image/image.module | 10 | ||||
-rw-r--r-- | modules/locale/locale.field.inc | 2 | ||||
-rw-r--r-- | modules/node/node.module | 6 | ||||
-rw-r--r-- | modules/rdf/rdf.module | 37 | ||||
-rw-r--r-- | modules/taxonomy/taxonomy.module | 44 |
17 files changed, 418 insertions, 464 deletions
diff --git a/modules/field/field.api.php b/modules/field/field.api.php index ea0d70b2a..4e1481a86 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -688,30 +688,14 @@ function hook_field_widget_error($element, $error) { /** * Expose Field API formatter types. * - * Formatters are mainly theme functions that handle the output of individual - * field values. These theme calls are typically triggered during the execution - * of drupal_render() on the render structure built by field_attach_view(). - * - * The name of the theme hook invoked when displaying the values is derived - * from formatter type names, using the pattern field_formatter_FORMATTER_NAME. - * field.module takes care of exposing the corresponding theme functions - * through hook_theme(). Specifically, field.module defines the theme - * hook: - * - * @code - * 'field_formatter_FORMATTER_NAME' => array( - * 'render_element' => 'element', - * ) - * @code - * - * If a formatter requires a different theme hook definition, - * implement hook_theme_registry_alter(). + * Formatters handle the display of field values. Formatter hooks are typically + * called by the Field Attach API field_attach_prepare_view() and + * field_attach_view() functions. * * @see hook_field_formatter_info(). * @see hook_field_formatter_info_alter(). - * @see theme_field_formatter_FORMATTER_NAME(). - * @see hook_theme(). - * @see hook_theme_registry_alter(). + * @see hook_field_formatter(). + * @see hook_field_formatter_prepare_view(). * * @return * An array describing the formatter types implemented by the module. @@ -733,8 +717,8 @@ function hook_field_widget_error($element, $error) { * field value (most common case). The formatter theme will be invoked * iteratively on each of the field valies. * FIELD_BEHAVIOR_CUSTOM if one single invocation of the formatter theme - * takes care of displays all the field values. Examples: points on - * a generated graph picture, a Google map, a single link to a popup... + * takes care of displaying all the field values. Examples: points on a + * generated graph picture, a Google map, a single link to a popup... */ function hook_field_formatter_info() { return array( @@ -799,61 +783,7 @@ function hook_field_formatter_info_alter(&$info) { } /** - * Theme function for a field formatter. - * - * This is an example of a 'single' formatter, displaying one single field - * value (the hook_field_formatter_info() entry uses - * 'multiple values' = FIELD_BEHAVIOR_DEFAULT). - * - * @param $variables - * An associative array containing: - * - element: A render structure sub-array, containing the following keys: - * - #item: The field value being displayed. - * - #delta: The index of the value being displayed within the object's - * values for the field. - * - #field_name: The name of the field being displayed. - * - #bundle: The bundle of the object being displayed. - * - #object: The object being displayed. - * - #object_type: The type of the object being displayed. - * - #formatter: The name of the formatter being used. - * - #settings: The array of formatter settings. - */ -function theme_field_formatter_FORMATTER_SINGLE($variables) { - // This relies on a 'safe' element being prepared in hook_field_sanitize(). - return $variables['element']['#item']['safe']; -} - -/** - * Theme function for a field formatter. - * - * This is an example of a 'single' formatter, displaying all the field values - * (the hook_field_formatter_info() entry uses - * 'multiple values' = FIELD_BEHAVIOR_CUSTOM). - * - * @param $variables - * An associative array containing: - * - element: A render structure sub-array, containing the following keys: - * - #field_name: The name of the field being displayed. - * - #bundle: The bundle of the object being displayed. - * - #object: The object being displayed. - * - #object_type: The type of the object being displayed. - * - #formatter: The name of the formatter being used. - * - #settings: The array of formatter settings. - * - numeric indexes: the field values being displayed. - */ -function theme_field_formatter_FORMATTER_MULTIPLE($variables) { - $element = $variables['element']; - - $items = array(); - foreach (element_children($element) as $key) { - $items[$key] = $key .':'. $element[$key]['#item']['value']; - } - $output = implode('|', $items); - return $output; -} - -/** - * Allow formatters to load information for multiple objects. + * Allow formatters to load information for field values being displayed. * * 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 @@ -866,18 +796,77 @@ function theme_field_formatter_FORMATTER_MULTIPLE($variables) { * @param $field * The field structure for the operation. * @param $instances - * Array of instance structures for $field for each object, keyed by object id. + * 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. + * @param $displays + * Array of display settings to use for each object display, 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) { +function hook_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $displays) { + +} + +/** + * Builds a renderable array for a field value. + * + * @param $obj_type + * The type of $object. + * @param $object + * The object being displayed. + * @param $field + * The field structure. + * @param $instance + * The field instance. + * @param $langcode + * The language associated to $items. + * @param $display + * The display settings to use, as found in the 'display' entry of instance + * definitions. + * @param $items + * Array of values for this field. + * @param $delta + * + * @return + */ +function hook_field_formatter($obj_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $settings = $display['settings']; + + switch ($display['type']) { + case 'field_test_default': + // Sample code for a 'single' formatter, displaying one single field + // value (the hook_field_formatter_info() entry uses + // 'multiple values' = FIELD_BEHAVIOR_DEFAULT). + $item = $items[$delta]; + $result = array( + '#markup' => $item['value'], + ); + break; + + case 'field_test_multiple': + // Sample code for a 'multiple' formatter, displaying all the field + // values (the hook_field_formatter_info() entry uses + // 'multiple values' = FIELD_BEHAVIOR_CUSTOM). + $array = array(); + foreach ($items as $delta => $item) { + $array[] = $delta .':'. $item['value']; + } + $result = array( + '#markup' => implode('|', $array), + ); + break; + } + return array( + '#markup' => $output, + ); } /** diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 01b46b13e..7daca8468 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -1113,10 +1113,39 @@ function field_attach_prepare_view($obj_type, $objects, $build_mode = 'full') { } /** - * 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 - * its settings. + * Returns a renderable array for the fields on an object. + * + * Each field is displayed according to the display options specified in the + * $instance definition for the given $build_mode. + * + * Sample structure: + * @code + * array( + * 'field_foo' => array( + * '#theme' => 'field', + * '#title' => the label of the field instance, + * '#label_display' => the label display mode, + * '#object' => the fieldable object being displayed, + * '#object_type' => the type of the object being displayed, + * '#language' => the language of the field values being displayed, + * '#build_mode' => the build mode, + * '#field_name' => the name of the field, + * '#field_type' => the type of the field, + * '#formatter' => the name of the formatter, + * '#items' => the field values being displayed, + * // The element's children are the formatted values returned by + * // hook_field_formatter(), keyed by delta. + * 0 => array( + * // Renderable array for value 0, or for all values if the formatter + * // is 'multiple-values'. + * ), + * 1 => array( + * // Renderable array for value 1 (only for 'single-value' formatters). + * ), + * // ... + * ), + * ); + * @endcode * * @param $obj_type * The type of $object; e.g. 'node' or 'user'. @@ -1128,56 +1157,7 @@ function field_attach_prepare_view($obj_type, $objects, $build_mode = 'full') { * The language the field values are to be shown in. If no language is * provided the current language is used. * @return - * A structured content array tree for drupal_render(). - * Sample structure: - * @code - * array( - * 'field_foo' => array( - * // The structure of the array differs slightly depending on whether - * // the formatter is 'single-value' (displays one single field value, - * // most common case) or 'multiple-values' (displays all the field's - * // values, e.g. points on a graph or a map). - * '#theme' => 'field', - * '#title' => the label of the field instance, - * '#label_display' => the label display mode, - * '#object' => the fieldable object being displayed, - * '#object_type' => the type of the object being displayed, - * '#language' => the language of the field values being displayed, - * '#build_mode' => the build mode, - * '#field_name' => the name of the field, - * '#formatter_single' => boolean indicating whether the formatter is single or - * multiple, - * 'items' => array( - * // One sub-array per field value, keyed by delta. - * 0 => array( - * '#item' => the field value for delta 0, - * - * // Only for 'single-value' formatters: - * '#theme' => the formatter's theme function, - * '#formatter' => name of the formatter, - * '#settings' => array of formatter settings, - * '#object' => the fieldable object being displayed, - * '#object_type' => the type of the object being displayed, - * '#field_name' => the name of the field, - * '#bundle' => the object's bundle, - * '#delta' => 0, - * ), - * 1 => array( - * ... - * ), - * - * // Only for 'multiple-values' formatters: - * '#theme' => the formatter's theme function, - * '#formatter' => name of the formatter, - * '#settings' => array of formatter settings, - * '#object' => the fieldable object being displayed, - * '#object_type' => the type of the object being displayed, - * '#field_name' => the name of the field, - * '#bundle' => the object's bundle, - * ), - * ), - * ); - * @endcode + * A renderable array for the field values. */ function field_attach_view($obj_type, $object, $build_mode = 'full', $langcode = NULL) { // If no language is provided use the current UI language. diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc index e4d7a0102..b86f8b222 100644 --- a/modules/field/field.default.inc +++ b/modules/field/field.default.inc @@ -60,19 +60,23 @@ function field_default_prepare_view($obj_type, $objects, $field, $instances, $la // 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]; + $display = $instance['display'][$build_mode]; + if ($display['type'] !== 'hidden') { + $module = $display['module']; + $modules[$module] = $module; + $grouped_objects[$module][$id] = $objects[$id]; + $grouped_instances[$module][$id] = $instance; + $grouped_displays[$module][$id] = $display; + // 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); + $function($obj_type, $grouped_objects[$module], $field, $grouped_instances[$module], $langcode, $grouped_items[$module], $grouped_displays[$module]); } } } @@ -89,57 +93,38 @@ function field_default_view($obj_type, $object, $field, $instance, $langcode, $i $display = $instance['display'][$build_mode]; if ($display['type'] !== 'hidden') { - $theme = 'field_formatter_' . $display['type']; - $single = (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT); - - $label_display = $display['label']; if ($build_mode == 'search_index') { - $label_display = 'hidden'; + $display['label'] = 'hidden'; } - $info = array( - '#field_name' => $field['field_name'], - '#object_type' => $obj_type, - '#bundle' => $bundle, - '#object' => $object, - ); - - $element = $info + array( + $element = array( '#theme' => 'field', '#weight' => $display['weight'], - '#title' => check_plain(t($instance['label'])), + '#title' => t($instance['label']), '#access' => field_access('view', $field, $obj_type, $object), - '#label_display' => $label_display, + '#label_display' => $display['label'], '#build_mode' => $build_mode, '#language' => $langcode, - '#formatter_single' => $single, - 'items' => array(), - ); - - // Fill-in items. - foreach ($items as $delta => $item) { - $element['items'][$delta] = array( - '#item' => $item, - '#weight' => $delta, - ); - } - - // Append formatter information either on each item ('single-value' formatter) - // or at the upper 'items' level ('multiple-value' formatter) - $format_info = $info + array( - '#theme' => $theme, + '#field_name' => $field['field_name'], + '#field_type' => $field['type'], + '#object_type' => $obj_type, + '#bundle' => $bundle, + '#object' => $object, + '#items' => $items, '#formatter' => $display['type'], - '#settings' => $display['settings'], ); - if ($single) { + // Calling the formatter function through module_invoke() can have a + // performance impact on pages with many fields and values. + $formatter_function = $display['module'] . '_field_formatter'; + if (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT) { foreach ($items as $delta => $item) { - $element['items'][$delta] += $format_info; - $element['items'][$delta]['#item']['#delta'] = $delta; + $element[$delta] = $formatter_function($obj_type, $object, $field, $instance, $langcode, $display, $items, $delta); + $element[$delta]['#weight'] = $delta; } } else { - $element['items'] += $format_info; + $element[0] = $formatter_function($obj_type, $object, $field, $instance, $langcode, $display, $items, 0); } $addition = array($field['field_name'] => $element); diff --git a/modules/field/field.module b/modules/field/field.module index 2bff9cea1..2861841a4 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -181,15 +181,7 @@ function field_theme() { 'render element' => 'element', ), ); - $field_formatters = field_info_formatter_types(NULL); - foreach ($field_formatters as $key => $field_formatter) { - $items['field_formatter_' . $key] = array( - 'render element' => 'element', - ); - if (isset($field_formatter['theme'])) { - $items['field_formatter_' . $key] += $field_formatter['theme']; - } - } + return $items; } @@ -686,24 +678,18 @@ function field_extract_bundle($obj_type, $bundle) { */ function template_preprocess_field(&$variables) { $element = $variables['element']; - list(, , $bundle) = entity_extract_ids($element['#object_type'], $element['#object']); - $instance = field_info_instance($element['#object_type'], $element['#field_name'], $bundle); + $instance = field_info_instance($element['#object_type'], $element['#field_name'], $element['#bundle']); $field = field_info_field($element['#field_name']); + // @todo Convert to using drupal_html_class() after benchmarking the impact of + // doing so. $field_type_css = strtr($field['type'], '_', '-'); $field_name_css = strtr($field['field_name'], '_', '-'); // Prepare an $items variable that the template can simply loop on. - if ($element['#formatter_single']) { - // Filter out non-children properties that might have been added if the - // renderable array has gone through form_builder(). - $items = array_intersect_key($element['items'], array_flip(element_children($element['items']))); - } - else { - // If the formatter is multiple, the template sees only one 'item', which - // will include all values. - $items = array($element['items']); - } + // Filter out non-children properties that might have been added if the + // renderable array has gone through form_builder(). + $items = array_intersect_key($element, array_flip(element_children($element))); $additions = array( 'object' => $element['#object'], @@ -715,7 +701,7 @@ function template_preprocess_field(&$variables) { 'field_name' => $field['field_name'], 'field_type_css' => $field_type_css, 'field_name_css' => $field_name_css, - 'label' => $element['#title'], + 'label' => check_plain($element['#title']), 'label_display' => $element['#label_display'], 'label_hidden' => $element['#label_display'] == 'hidden', 'field_language' => $element['#language'], @@ -728,8 +714,8 @@ function template_preprocess_field(&$variables) { 'template_files' => array( 'field', 'field-' . $element['#field_name'], - 'field-' . $bundle, - 'field-' . $element['#field_name'] . '-' . $bundle, + 'field-' . $element['#bundle'], + 'field-' . $element['#field_name'] . '-' . $element['#bundle'], ), ); $variables = array_merge($variables, $additions); diff --git a/modules/field/modules/list/list.module b/modules/field/modules/list/list.module index ab2966814..b52dfd40b 100644 --- a/modules/field/modules/list/list.module +++ b/modules/field/modules/list/list.module @@ -298,22 +298,29 @@ function list_field_formatter_info() { } /** - * Theme function for 'default' list field formatter. + * Implements hook_field_formatter(). */ -function theme_field_formatter_list_default($variables) { - $element = $variables['element']; - $field = field_info_field($element['#field_name']); - if (($allowed_values = list_allowed_values($field)) && isset($allowed_values[$element['#item']['value']])) { - return $allowed_values[$element['#item']['value']]; +function list_field_formatter($object_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $value = $items[$delta]['value']; + + switch ($display['type']) { + case 'list_default': + $allowed_values = list_allowed_values($field); + if (isset($allowed_values[$value])) { + $output = $allowed_values[$value]; + } + else { + // If no match was found in allowed values, fall back to the key. + $output = $value; + } + break; + + case 'list_key': + $output = $value; + break; } - // If no match was found in allowed values, fall back to the key. - return $element['#item']['safe']; -} -/** - * Theme function for 'key' list field formatter. - */ -function theme_field_formatter_list_key($variables) { - $element = $variables['element']; - return $element['#item']['safe']; + return array( + '#markup' => $output, + ); } diff --git a/modules/field/modules/number/number.module b/modules/field/modules/number/number.module index e03b26503..710d82e54 100644 --- a/modules/field/modules/number/number.module +++ b/modules/field/modules/number/number.module @@ -7,15 +7,6 @@ */ /** - * Implements hook_theme_alter(). - */ -function number_theme_registry_alter(&$theme_registry) { - // The number_integer and number_decimal formatters use the same function. - $theme_registry['field_formatter_number_default']['function'] = 'theme_field_formatter_number'; - $theme_registry['field_formatter_number_decimal']['function'] = 'theme_field_formatter_number'; -} - -/** * Implements hook_field_info(). */ function number_field_info() { @@ -25,7 +16,7 @@ function number_field_info() { 'description' => t('This field stores a number in the database as an integer.'), 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''), 'default_widget' => 'number', - 'default_formatter' => 'number_default', + 'default_formatter' => 'number_integer', ), 'number_decimal' => array( 'label' => t('Decimal'), @@ -221,7 +212,7 @@ function number_field_is_empty($item, $field) { */ function number_field_formatter_info() { return array( - 'number_default' => array( + 'number_integer' => array( 'label' => t('default'), 'field types' => array('number_integer'), 'settings' => array( @@ -249,39 +240,36 @@ function number_field_formatter_info() { } /** - * Theme function for 'unformatted' number field formatter. - */ -function theme_field_formatter_number_unformatted($variables) { - $element = $variables['element']; - return $element['#item']['value']; -} - -/** - * Proxy theme function for number field formatters. + * Implements hook_field_formatter(). */ -function theme_field_formatter_number($variables) { - $element = $variables['element']; - $field = field_info_field($element['#field_name']); - $instance = field_info_instance($element['#object_type'], $element['#field_name'], $element['#bundle']); - $value = $element['#item']['value']; - $settings = $element['#settings']; - $formatter_type = $element['#formatter']; - - if (empty($value) && $value !== '0') { - return ''; - } - - $output = number_format($value, $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']); +function number_field_formatter($object_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $value = $items[$delta]['value']; + + switch ($display['type']) { + case 'number_integer': + case 'number_decimal': + $settings = $display['settings']; + if (empty($value) && $value !== '0') { + return ''; + } + $output = number_format($value, $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']); + if ($settings['prefix_suffix']) { + $prefixes = isset($instance['settings']['prefix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['prefix'])) : array(''); + $suffixes = isset($instance['settings']['suffix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['suffix'])) : array(''); + $prefix = (count($prefixes) > 1) ? format_plural($value, $prefixes[0], $prefixes[1]) : $prefixes[0]; + $suffix = (count($suffixes) > 1) ? format_plural($value, $suffixes[0], $suffixes[1]) : $suffixes[0]; + $output = $prefix . $output . $suffix; + } + break; - if ($settings['prefix_suffix']) { - $prefixes = isset($instance['settings']['prefix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['prefix'])) : array(''); - $suffixes = isset($instance['settings']['suffix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['suffix'])) : array(''); - $prefix = (count($prefixes) > 1) ? format_plural($value, $prefixes[0], $prefixes[1]) : $prefixes[0]; - $suffix = (count($suffixes) > 1) ? format_plural($value, $suffixes[0], $suffixes[1]) : $suffixes[0]; - $output = $prefix . $output . $suffix; + case 'number_unformatted': + $output = $value; + break; } - return $output; + return array( + '#markup' => $output, + ); } /** diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index abae03be2..97b71571f 100644 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -191,49 +191,12 @@ function text_field_validate($obj_type, $object, $field, $instance, $langcode, $ function text_field_load($obj_type, $objects, $field, $instances, $langcode, &$items) { foreach ($objects as $id => $object) { foreach ($items[$id] as $delta => $item) { - if (!empty($instances[$id]['settings']['text_processing'])) { - // Only process items with a cacheable format, the rest will be - // handled by text_field_sanitize(). - $format_id = $item['format']; - if (filter_format_allowcache($format_id)) { - $items[$id][$delta]['safe'] = isset($item['value']) ? check_markup($item['value'], $format_id, $langcode) : ''; - if ($field['type'] == 'text_with_summary') { - $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? check_markup($item['summary'], $format_id, $langcode) : ''; - } - } - } - else { - $items[$id][$delta]['safe'] = check_plain($item['value']); - if ($field['type'] == 'text_with_summary') { - $items[$id][$delta]['safe_summary'] = check_plain($item['summary']); - } - } - } - } -} - -/** - * Implements hook_field_sanitize(). - * - * @see text_field_load() - */ -function text_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) { - foreach ($items as $delta => $item) { - // Only sanitize items which were not already processed inside - // text_field_load(), i.e. items with uncacheable text formats, or coming - // from a form preview. - if (!isset($items[$delta]['safe'])) { - if (!empty($instance['settings']['text_processing'])) { - $format_id = $item['format']; - $items[$delta]['safe'] = isset($item['value']) ? check_markup($item['value'], $format_id, $langcode, TRUE) : ''; + // Only process items with a cacheable format, the rest will be + // handled by formatters if needed. + if (empty($instances[$id]['settings']['text_processing']) || filter_format_allowcache($item['format'])) { + $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $item, 'value') : ''; if ($field['type'] == 'text_with_summary') { - $items[$delta]['safe_summary'] = isset($item['summary']) ? check_markup($item['summary'], $format_id, $langcode, TRUE) : ''; - } - } - else { - $items[$delta]['safe'] = check_plain($item['value']); - if ($field['type'] == 'text_with_summary') { - $items[$delta]['safe_summary'] = check_plain($item['summary']); + $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? _text_sanitize($instances[$id], $langcode, $item, 'summary') : ''; } } } @@ -286,49 +249,66 @@ function text_field_formatter_info() { } /** - * Theme function for 'default' text field formatter. + * Implements hook_field_formatter(). */ -function theme_field_formatter_text_default($variables) { - $element = $variables['element']; - return $element['#item']['safe']; -} +function text_field_formatter($object_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $item = $items[$delta]; + + switch ($display['type']) { + case 'text_default': + case 'text_trimmed': + $output = _text_sanitize($instance, $langcode, $item, 'value'); + if ($display['type'] == 'text_trimemd') { + $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL); + } + break; -/** - * Theme function for 'plain' text field formatter. - */ -function theme_field_formatter_text_plain($variables) { - $element = $variables['element']; - return strip_tags($element['#item']['safe']); -} + case 'text_summary_or_trimmed': + if (!empty($item['summary'])) { + $output = _text_sanitize($instance, $langcode, $item, 'summary'); + } + else { + $output = _text_sanitize($instance, $langcode, $item, 'value'); + $size = variable_get('teaser_length_' . $instance['bundle'], 600); + $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $size); + } + break; -/** - * Theme function for 'trimmed' text field formatter. - */ -function theme_field_formatter_text_trimmed($variables) { - $element = $variables['element']; - $field = field_info_field($element['#field_name']); - $instance = field_info_instance($element['#object_type'], $element['#field_name'], $element['#bundle']); - return text_summary($element['#item']['safe'], $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL); + case 'text_plain': + $output = strip_tags($item['value']); + break; + } + + return array( + '#markup' => $output, + ); } /** - * Theme function for 'summary or trimmed' field formatter for - * text_with_summary fields. This formatter returns the summary - * element of the field or, if the summary is empty, the trimmed - * version of the full element of the field. + * Sanitizes the 'value' or 'summary' data of a text value. + * + * Depending on whether the field instance uses text processing, data is run + * through check_plain() or check_markup(). + * + * @param $instance + * The instance definition. + * @param $langcode + * The language associated to $item. + * @param $item + * The field value to sanitize. + * @param $column + * The column to sanitize (either 'value' or 'summary'). + * + * @return + * The sanitized string. */ -function theme_field_formatter_text_summary_or_trimmed($variables) { - $element = $variables['element']; - $field = field_info_field($element['#field_name']); - $instance = field_info_instance($element['#object_type'], $element['#field_name'], $element['#bundle']); - - if (!empty($element['#item']['safe_summary'])) { - return $element['#item']['safe_summary']; - } - else { - $size = variable_get('teaser_length_' . $element['#bundle'], 600); - return text_summary($element['#item']['safe'], $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL, $size); +function _text_sanitize($instance, $langcode, $item, $column) { + // If the value uses a cacheable text format, text_field_load() precomputes + // the sanitized string. + if (isset($item["safe_$column"])) { + return $item["safe_$column"]; } + return $instance['settings']['text_processing'] ? check_markup($item[$column], $item['format'], $langcode, TRUE) : check_plain($item[$column]); } /** diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 286d1a493..418017f68 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -808,7 +808,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { } /** - * Test field_attach_view() and field_atach_prepare_view(). + * Test field_attach_view() and field_attach_prepare_view(). */ function testFieldAttachView() { $entity_type = 'test_entity'; @@ -893,7 +893,7 @@ 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().. + // Test a formatter that uses hook_field_formatter_prepare_view(). $entity = clone($entity_init); $formatter_setting = $this->randomName(); $this->instance['display'] = array( diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index 0bdc93ea6..5097a3357 100644 --- a/modules/field/tests/field_test.field.inc +++ b/modules/field/tests/field_test.field.inc @@ -92,16 +92,6 @@ function field_test_field_validate($obj_type, $object, $field, $instance, $langc } /** - * Implements hook_field_sanitize(). - */ -function field_test_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) { - foreach ($items as $delta => $item) { - $value = check_plain($item['value']); - $items[$delta]['safe'] = $value; - } -} - -/** * Implements hook_field_is_empty(). */ function field_test_field_is_empty($item, $field) { @@ -238,11 +228,11 @@ function field_test_field_formatter_info() { /** * Implements hook_field_formatter_prepare_view(). */ -function field_test_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $build_mode) { +function field_test_field_formatter_prepare_view($obj_type, $objects, $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 ($instances[$id]['display'][$build_mode]['type'] == 'field_test_needs_additional_data') { + if ($displays[$id]['type'] == 'field_test_needs_additional_data') { foreach ($item as $delta => $value) { // Don't add anything on empty values. if ($value) { @@ -254,47 +244,38 @@ function field_test_field_formatter_prepare_view($obj_type, $objects, $field, $i } /** - * Theme function for 'field_test_default' formatter. + * Implements hook_field_formatter(). */ -function theme_field_formatter_field_test_default($variables) { - $element = $variables['element']; - - $value = $element['#item']['value']; - $settings = $element['#settings']; - - return $settings['test_formatter_setting'] . '|' . $value; -} - -/** - * Theme function for 'field_test_multiple' formatter. - */ -function theme_field_formatter_field_test_multiple($variables) { - $element = $variables['element']; - - $settings = $element['#settings']; - - $items = array(); - foreach (element_children($element) as $key) { - $items[$key] = $key .':'. $element[$key]['#item']['value']; +function field_test_field_formatter($object_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $settings = $display['settings']; + + switch ($display['type']) { + case 'field_test_default': + $item = $items[$delta]; + $output = $settings['test_formatter_setting'] . '|' . $item['value']; + break; + + case 'field_test_needs_additional_data': + $item = $items[$delta]; + $output = $settings['test_formatter_setting_additional'] . '|' . $item['value'] . '|' . $item['additional_formatter_value']; + break; + + case 'field_test_multiple': + $array = array(); + foreach ($items as $delta => $item) { + $array[] = $delta .':'. $item['value']; + } + $output = $settings['test_formatter_setting_multiple'] . '|' . implode('|', $array); + break; } - $output = implode('|', $items); - 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; + return array( + '#markup' => $output, + ); } /** - * Sample 'default vale' callback. + * Sample 'default value' callback. */ function field_test_default_value($obj_type, $object, $field, $instance) { return array(array('value' => 99)); diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index fb0d34925..7470d3d1e 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -844,33 +844,47 @@ function theme_file_upload_help($variables) { } /** - * Theme function for 'default' file field formatter. + * Implements hook_field_formatter(). */ -function theme_field_formatter_file_default($variables) { - $element = $variables['element']; - return theme('file_link', array('file' => (object) $element['#item'])); -} +function file_field_formatter($obj_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $item = $items[$delta]; -/** - * Theme function for 'url_plain' file field formatter. - */ -function theme_field_formatter_file_url_plain($variables) { - $element = $variables['element']; - return empty($element['#item']['uri']) ? '' : file_create_url($element['#item']['uri']); + switch ($display['type']) { + case 'file_default': + $result = array( + '#theme' => 'file_link', + '#file' => (object) $item, + ); + break; + + case 'file_url_plain': + $result = array( + '#markup' => empty($item['uri']) ? '' : file_create_url($item['uri']), + ); + break; + + case 'file_table': + // Multiple formatter, dealing with all $items. + $result = array( + '#theme' => 'file_formatter_table', + '#items' => $items, + ); + break; + } + + return $result; } /** * Theme function for the 'table' formatter. */ -function theme_field_formatter_file_table($variables) { - $element = $variables['element']; - +function theme_file_formatter_table($variables) { $header = array(t('Attachment'), t('Size')); $rows = array(); - foreach (element_children($element) as $key) { + foreach ($variables['items'] as $delta => $item) { $rows[] = array( - theme('file_link', array('file' => (object) $element[$key]['#item'])), - format_size($element[$key]['#item']['filesize']), + theme('file_link', array('file' => (object) $item)), + format_size($item['filesize']), ); } diff --git a/modules/file/file.module b/modules/file/file.module index 0768b1916..6b7ed6ad3 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -81,6 +81,9 @@ function file_theme() { 'file_widget_multiple' => array( 'render element' => 'element', ), + 'file_formatter_table' => array( + 'variables' => array('items' => NULL), + ), 'file_upload_help' => array( 'variables' => array('description' => NULL, 'upload_validators' => NULL), ), diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc index b78425ea8..3ab6f7090 100644 --- a/modules/image/image.field.inc +++ b/modules/image/image.field.inc @@ -433,17 +433,14 @@ function image_field_formatter_info() { $formatters['image__' . $style['name']] = array( 'label' => t('Image "@style"', array('@style' => $style['name'])), 'field types' => array('image'), - 'theme' => array('function' => 'theme_field_formatter_image'), ); $formatters['image_link_content__' . $style['name']] = array( 'label' => t('Image "@style" linked to content', array('@style' => $style['name'])), 'field types' => array('image'), - 'theme' => array('function' => 'theme_field_formatter_image_link_content'), ); $formatters['image_link_file__' . $style['name']] = array( 'label' => t('Image "@style" linked to file', array('@style' => $style['name'])), 'field types' => array('image'), - 'theme' => array('function' => 'theme_field_formatter_image_link_file'), ); } @@ -451,40 +448,57 @@ function image_field_formatter_info() { } /** - * Theme function for 'image' image field formatter. + * Implements hook_field_formatter(). */ -function theme_field_formatter_image($variables) { - $element = $variables['element']; - $image = array( - 'path' => $element['#item']['uri'], - 'alt' => $element['#item']['alt'], - 'title' => $element['#item']['title'], - ); +function image_field_formatter($obj_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $item = $items[$delta]; + $image_style = ''; + $path = ''; // Check if this requires a particular image style. $matches = array(); - if (preg_match('/__([a-z0-9_]+)/', $element['#formatter'], $matches)) { - $image['style_name'] = $matches[1]; - return theme('image_style', $image); + if (preg_match('/__([a-z0-9_]+)/', $display['type'], $matches)) { + $image_style = $matches[1]; } - else { - return theme('image', $image); + + if (strpos($display['type'], 'image_link_content') === 0) { + list($id) = entity_extract_ids($obj_type, $object); + $path = $obj_type . '/' . $id; + } + elseif (strpos($display['type'], 'image_link_file') === 0) { + $path = file_create_url($item['uri']); } -} -/** - * Theme function for 'image_link_content' image field formatter. - */ -function theme_field_formatter_image_link_content($variables) { - $element = $variables['element']; - list($id, $vid, $bundle) = entity_extract_ids($element['#object_type'], $element['#object']); - return l(theme('field_formatter_image', $variables), $element['#object_type'] . '/' . $id, array('html' => TRUE)); + return array( + '#theme' => 'image_formatter', + '#item' => $item, + '#image_style' => $image_style, + '#path' => $path, + ); } /** - * Theme function for 'image_link_file' image field formatter. + * Theme function for image field formatters. */ -function theme_field_formatter_image_link_file($variables) { - $element = $variables['element']; - return l(theme('field_formatter_image', $variables), file_create_url($element['#item']['uri']), array('html' => TRUE)); -} +function theme_image_formatter($variables) { + $item = $variables['item']; + $image = array( + 'path' => $item['uri'], + 'alt' => $item['alt'], + 'title' => $item['title'], + ); + + if ($variables['image_style']) { + $image['style_name'] = $variables['image_style']; + $output = theme('image_style', $image); + } + else { + $output = theme('image', $image); + } + + if ($variables['path']) { + $output = l($output, $variables['path'], array('html' => TRUE)); + } + + return $output; +}
\ No newline at end of file diff --git a/modules/image/image.module b/modules/image/image.module index 77a9317ce..8786d1032 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -212,14 +212,8 @@ function image_theme() { 'image_widget' => array( 'render element' => 'element', ), - 'field_formatter_image' => array( - 'render element' => 'element', - ), - 'field_formatter_image_link_content' => array( - 'render element' => 'element', - ), - 'field_formatter_image_link_file' => array( - 'render element' => 'element', + 'image_formatter' => array( + 'variables' => array('item' => NULL, 'path' => NULL, 'image_style' => NULL), ), ); } diff --git a/modules/locale/locale.field.inc b/modules/locale/locale.field.inc index 8093f3520..3ad4672ff 100644 --- a/modules/locale/locale.field.inc +++ b/modules/locale/locale.field.inc @@ -52,7 +52,7 @@ function locale_field_fallback_view(&$output, $context) { // If the items array is empty then we have a missing field translation. // @todo: Verify this assumption. - if (isset($output[$field_name]) && empty($output[$field_name]['items'])) { + if (isset($output[$field_name]) && count(element_children($output[$field_name])) == 0) { if (!isset($fallback_candidates)) { require_once DRUPAL_ROOT . '/includes/language.inc'; $fallback_candidates = language_fallback_get_candidates(); diff --git a/modules/node/node.module b/modules/node/node.module index 543c40f77..0f0d171cf 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -2203,7 +2203,11 @@ function node_page_default() { function node_page_view($node) { $return = node_show($node); if (isset($return['nodes'][$node->nid]['title'])) { - drupal_set_title($return['nodes'][$node->nid]['title']['items'][0]['#item']['value']); + // Get the language that was determined by language fallback logic during the + // field_attach_display() workflow. + // @todo That logic should be made available separately. + $langcode = $return['nodes'][$node->nid]['title']['#language']; + drupal_set_title($node->title[$langcode][0]['value']); } // Set the node path as the canonical URL to prevent duplicate content. drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)), TRUE); diff --git a/modules/rdf/rdf.module b/modules/rdf/rdf.module index 2da297a1d..7993b24c7 100644 --- a/modules/rdf/rdf.module +++ b/modules/rdf/rdf.module @@ -427,12 +427,11 @@ function rdf_preprocess_field(&$variables) { $instance = $variables['instance']; $mapping = rdf_mapping_load($entity_type, $instance['bundle']); $field_name = $instance['field_name']; + $items = $variables['element']['#items']; if (!empty($mapping) && !empty($mapping[$field_name])) { - foreach ($variables['items'] as $delta => $item) { - if (!empty($item['#item'])) { - $variables['item_attributes_array'][$delta] = rdf_rdfa_attributes($mapping[$field_name], $item['#item']); - } + foreach ($items as $delta => $item) { + $variables['item_attributes_array'][$delta] = rdf_rdfa_attributes($mapping[$field_name], $item); } } } @@ -554,15 +553,29 @@ function rdf_preprocess_comment(&$variables) { } /** - * Implements MODULE_preprocess_HOOK(). + * Implements hook_field_attach_view_alter(). */ -function rdf_preprocess_field_formatter_taxonomy_term_link(&$variables) { - $term = $variables['element']['#item']['taxonomy_term']; - if (!empty($term->rdf_mapping['rdftype'])) { - $variables['link_options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; - } - if (!empty($term->rdf_mapping['name']['predicates'])) { - $variables['link_options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; +function rdf_field_attach_view_alter(&$output, $context) { + // Append term mappings on displayed taxonomy links. + foreach (element_children($output) as $field_name) { + $element = &$output[$field_name]; + if ($element['#field_type'] == 'taxonomy_term' && $element['#formatter'] == 'taxonomy_term_link') { + foreach ($element['#items'] as $delta => $item) { + // @todo Remove this when "node_build() does not call + // field_attach_prepare_view()" bug is fixed. + // See http://drupal.org/node/493314. + if (!isset($item['taxonomy_term'])) { + $item['taxonomy_term'] = taxonomy_term_load($item['tid']); + } + $term = $item['taxonomy_term']; + if (!empty($term->rdf_mapping['rdftype'])) { + $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; + } + if (!empty($term->rdf_mapping['name']['predicates'])) { + $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; + } + } + } } } diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 290162e26..1eb295d39 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -1102,20 +1102,36 @@ function taxonomy_field_formatter_info() { } /** - * Theme function for 'link' term field formatter. + * Implements hook_field_formatter(). */ -function theme_field_formatter_taxonomy_term_link($variables) { - $term = $variables['element']['#item']['taxonomy_term']; - $attributes = empty($variables['link_options']) ? array() : $variables['link_options']; - return l($term->name, 'taxonomy/term/' . $term->tid, $attributes); -} +function taxonomy_field_formatter($object_type, $object, $field, $instance, $langcode, $display, $items, $delta) { + $item = $items[$delta]; + + switch ($display['type']) { + case 'taxonomy_term_link': + // @todo Remove this when "node_build() does not call + // field_attach_prepare_view()" bug is fixed. + // See http://drupal.org/node/493314. + if (!isset($item['taxonomy_term'])) { + $item['taxonomy_term'] = taxonomy_term_load($item['tid']); + } + $term = $item['taxonomy_term']; + $result = array( + '#type' => 'link', + '#title' => $term->name, + '#href' => 'taxonomy/term/' . $term->tid, + ); + break; -/** - * Theme function for 'plain' term field formatter. - */ -function theme_field_formatter_taxonomy_term_plain($variables) { - $term = $variables['element']['#item']['taxonomy_term']; - return check_plain($term->name); + case 'taxonomy_term_plain': + $term = $item['taxonomy_term']; + $result = array( + '#markup' => check_plain($term->name), + ); + break; + } + + return $result; } /** @@ -1140,12 +1156,12 @@ function taxonomy_allowed_values($field) { } /** - * Implements hook_field_load(). + * Implements hook_field_formatter_prepare_view(). * * This preloads all taxonomy terms for multiple loaded objects at once and * unsets values for invalid terms that do not exist. */ -function taxonomy_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $age) { +function taxonomy_field_formatter_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $displays) { $tids = array(); // Collect every possible term attached to any of the fieldable entities. |