summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-12-20 21:08:26 +0000
committerDries Buytaert <dries@buytaert.net>2009-12-20 21:08:26 +0000
commite9f18147482f6a3e4b9e177d84e9deebd4410778 (patch)
tree8a769a32591e495d6976756cdb54c86899d1de64
parent8874a4e2f7299d3068ea6c0346999fc9d2f7d2ce (diff)
downloadbrdo-e9f18147482f6a3e4b9e177d84e9deebd4410778.tar.gz
brdo-e9f18147482f6a3e4b9e177d84e9deebd4410778.tar.bz2
- Patch #612894 by yched: display field improvements.
-rw-r--r--modules/field/field.attach.inc4
-rw-r--r--modules/field/field.default.inc60
-rw-r--r--modules/field/field.info.inc80
-rw-r--r--modules/field/field.module114
-rw-r--r--modules/field/tests/field.test101
5 files changed, 307 insertions, 52 deletions
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index 48eab2dec..d87ac3a1a 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -338,7 +338,7 @@ function _field_invoke_multiple($op, $obj_type, $objects, &$a = NULL, &$b = NULL
if (function_exists($function)) {
// Iterate over all the field translations.
foreach ($grouped_items[$field_id] as $langcode => $items) {
- $results = $function($obj_type, $grouped_objects[$field_id], $field, $grouped_instances[$field_id], $langcode, $grouped_items[$field_id][$langcode], $options, $a, $b);
+ $results = $function($obj_type, $grouped_objects[$field_id], $field, $grouped_instances[$field_id], $langcode, $grouped_items[$field_id][$langcode], $a, $b);
if (isset($results)) {
// Collect results by object.
// For hooks with array results, we merge results together.
@@ -1177,7 +1177,7 @@ function field_attach_view($obj_type, $object, $build_mode = 'full', $langcode =
$output['#pre_render'][] = '_field_extra_weights_pre_render';
$output['#extra_fields'] = field_extra_fields($bundle);
- // Let other modules make changes after rendering the view.
+ // Let other modules alter the renderable array.
$context = array(
'obj_type' => $obj_type,
'object' => $object,
diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc
index 7305b2db9..4e7dca6cb 100644
--- a/modules/field/field.default.inc
+++ b/modules/field/field.default.inc
@@ -97,13 +97,32 @@ 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) {
+ * Invokes hook_field_formatter_prepare_view() on the relevant formatters.
+ *
+ * @param $obj_type
+ * The type of $object; e.g. 'node' or 'user'.
+ * @param $objects
+ * An 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 associated to $items.
+ * @param $items
+ * Array of field values already loaded for the objects, keyed by object id.
+ * @param $display
+ * Can be either:
+ * - the name of a build mode
+ * - or an array of display settings to use for display, as found in the
+ * 'display' entry of $instance definitions.
+*/
+function field_default_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $display) {
// Group objects, instances and items by formatter module.
$modules = array();
foreach ($instances as $id => $instance) {
- $display = $instance['display'][$build_mode];
+ $display = is_string($display) ? $instance['display'][$display] : $display;
if ($display['type'] !== 'hidden') {
$module = $display['module'];
$modules[$module] = $module;
@@ -125,16 +144,41 @@ function field_default_prepare_view($obj_type, $objects, $field, $instances, $la
}
/**
- * Default field 'view' operation.
+ * Builds a renderable array for field values.
*
- * @see field_attach_view()
+ * @param $obj_type
+ * The type of $object; e.g. 'node' or 'user'.
+ * @param $objects
+ * An 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 associated to $items.
+ * @param $items
+ * Array of field values already loaded for the objects, keyed by object id.
+ * @param $display
+ * Can be either:
+ * - the name of a build mode;
+ * - or an array of custom display settings, as found in the 'display' entry
+ * of $instance definitions.
*/
-function field_default_view($obj_type, $object, $field, $instance, $langcode, $items, $build_mode) {
+function field_default_view($obj_type, $object, $field, $instance, $langcode, $items, $display) {
list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
$addition = array();
- $display = $instance['display'][$build_mode];
+ // Prepare incoming display specifications.
+ if (is_string($display)) {
+ $build_mode = $display;
+ $display = $instance['display'][$build_mode];
+ }
+ else {
+ $build_mode = '_custom_display';
+ }
+
if ($display['type'] !== 'hidden') {
// We never want to index fields labels.
if ($build_mode == 'search_index') {
diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc
index e4df3a391..4921fdaee 100644
--- a/modules/field/field.info.inc
+++ b/modules/field/field.info.inc
@@ -278,22 +278,10 @@ function _field_info_prepare_instance($instance, $field) {
$instance['default_value'] = NULL;
}
- // Fallback to default widget if widget type is not available.
- if (!field_info_widget_types($instance['widget']['type'])) {
- $instance['widget']['type'] = $field_type['default_widget'];
- }
- // Make sure all expected widget settings are present.
- $instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']);
+ $instance['widget'] = _field_info_prepare_instance_widget($field, $instance['widget']);
foreach ($instance['display'] as $build_mode => $display) {
- if ($display['type'] != 'hidden') {
- // Fallback to default formatter if formatter type is not available.
- if (!field_info_formatter_types($instance['display'][$build_mode]['type'])) {
- $instance['display'][$build_mode]['type'] = $field_type['default_formatter'];
- }
- // Make sure all expected formatter settings are present.
- $instance['display'][$build_mode]['settings'] += field_info_formatter_settings($instance['display'][$build_mode]['type']);
- }
+ $instance['display'][$build_mode] = _field_info_prepare_instance_display($field, $display);
}
// Fallback to 'full' display settings for unspecified build modes.
@@ -307,6 +295,70 @@ function _field_info_prepare_instance($instance, $field) {
}
/**
+ * Adapts display specifications to the current run-time context.
+ *
+ * @param $field
+ * The field structure for the instance.
+ * @param $display
+ * Display specifications as found in
+ * $instance['display']['some_build_mode'].
+ */
+function _field_info_prepare_instance_display($field, $display) {
+ $field_type = field_info_field_types($field['type']);
+
+ // Fill in default values.
+ $display += array(
+ 'label' => 'above',
+ 'type' => $field_type['default_formatter'],
+ 'settings' => array(),
+ 'weight' => 0,
+ );
+ if ($display['type'] != 'hidden') {
+ $formatter_type = field_info_formatter_types($display['type']);
+ // Fallback to default formatter if formatter type is not available.
+ if (!$formatter_type) {
+ $display['type'] = $field_type['default_formatter'];
+ $formatter_type = field_info_formatter_types($display['type']);
+ }
+ $display['module'] = $formatter_type['module'];
+ // Fill in default settings for the formatter.
+ $display['settings'] += field_info_formatter_settings($display['type']);
+ }
+
+ return $display;
+}
+
+/**
+ * Prepares widget specifications for the current run-time context.
+ *
+ * @param $field
+ * The field structure for the instance.
+ * @param $widget
+ * Widget specifications as found in $instance['widget'].
+ */
+function _field_info_prepare_instance_widget($field, $widget) {
+ $field_type = field_info_field_types($field['type']);
+
+ // Fill in default values.
+ $widget += array(
+ 'type' => $field_type['default_widget'],
+ 'settings' => array(),
+ 'weight' => 0,
+ );
+
+ $widget_type = field_info_widget_types($widget['type']);
+ // Fallback to default formatter if formatter type is not available.
+ if (!$widget_type) {
+ $widget['type'] = $field_type['default_widget'];
+ $widget_type = field_info_widget_types($widget['type']);
+ }
+ $widget['module'] = $widget_type['module'];
+ // Fill in default settings for the widget.
+ $widget['settings'] += field_info_widget_settings($widget['type']);
+
+ return $widget;
+}
+
* Determines the behavior of a widget with respect to an operation.
*
* @param $op
diff --git a/modules/field/field.module b/modules/field/field.module
index d89b76fa3..f0dc6bb8e 100644
--- a/modules/field/field.module
+++ b/modules/field/field.module
@@ -565,45 +565,103 @@ function field_format($obj_type, $object, $field, $item, $formatter_type = NULL,
}
/**
- * Return a single field, fully themed with label and multiple values.
+ * Returns a renderable array for the value of a single field in an object.
*
- * To be used by third-party code (Views, Panels...) that needs to output
- * an isolated field. Do *not* use inside node templates, use
- * render($content[FIELD_NAME]) instead.
+ * The resulting output is a fully themed field with label and multiple values.
*
- * The field will be displayed using the display options (label display,
- * formatter settings...) specified in the $instance structure for the given
- * build mode: $instance['display'][$build_mode].
+ * This function can be used by third-party modules that need to output an
+ * isolated field.
+ * - Do not use inside node (or other entities) templates, use
+ * render($content[FIELD_NAME]) instead.
+ * - Do not use to display all fields in an object, use
+ * field_attach_prepare_view() and field_attach_view() instead.
*
+ * The function takes care of invoking the prepare_view steps. It also respects
+ * field access permissions.
+ *
+ * @param $obj_type
+ * The type of $object; e.g. 'node' or 'user'.
* @param $object
- * The object containing the field to display. Must at least contain the id key,
- * revision key (if applicable), bundle key, and the field data.
- * @param $field
- * The field structure.
- * @param $instance
- * The instance structure for $field on $object's bundle.
- * @param $build_mode
- * Build mode, e.g. 'full', 'teaser'...
+ * The object 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 $display
+ * Can be either:
+ * - The name of a build mode. The field will be displayed according to the
+ * display settings specified for this build mode in the $instance
+ * definition for the field in the object's bundle.
+ * If no display settings are found for the build mode, the settings for
+ * the 'full' build mode will be used.
+ * - An array of display settings, as found in the 'display' entry of
+ * $instance definitions. The following kay/value pairs are allowed:
+ * - label: (string) Position of the label. The default 'field' theme
+ * implementation supports the values 'inline', 'above' and 'hidden'.
+ * Defaults to 'above'.
+ * - type: (string) The formatter to use. Defaults to the
+ * 'default_formatter' for the field type, specified in
+ * hook_field_info(). The default formatter will also be used if the
+ * requested formatter is not available.
+ * - settings: (array) Settings specific to the formatter. Defaults to the
+ * formatter's default settings, specified in
+ * hook_field_formatter_info().
+ * - weight: (float) The weight to assign to the renderable element.
+ * Defaults to 0.
+ * @param $langcode
+ * (Optional) The language the field values are to be shown in. The site's
+ * current language fallback logic will be applied no values are available
+ * for the language. If no language is provided the current language will be
+ * used.
* @return
- * The themed output for the field.
+ * A renderable array for the field value.
*/
-function field_view_field($obj_type, $object, $field, $instance, $build_mode = 'full') {
- $output = '';
- if (isset($object->$field['field_name'])) {
- $items = $object->$field['field_name'];
+function field_view_field($obj_type, $object, $field_name, $display = array(), $langcode = NULL) {
+ $output = array();
- // One-field equivalent to _field_invoke('sanitize').
- $function = $field['module'] . '_field_sanitize';
- if (function_exists($function)) {
- $function($obj_type, $object, $field, $instance, $items);
- $object->$field['field_name'] = $items;
+ if ($field = field_info_field($field_name)) {
+ if (is_array($display)) {
+ // When using custom display settings, fill in default values.
+ $display = _field_info_prepare_instance_display($field, $display);
+ }
+ else {
+ // When using a build mode, make sure we have settings for it, or fall
+ // back to the 'full' build mode.
+ list(, , $bundle) = entity_extract_ids($obj_type, $object);
+ $instance = field_info_instance($obj_type, $field_name, $bundle);
+ if (!isset($instance['display'][$display])) {
+ $display = 'full';
+ }
}
- $view = field_default_view($obj_type, $object, $field, $instance, $items, $build_mode);
- // TODO : what about hook_field_attach_view ?
+ // Hook invocations are done through the _field_invoke() functions in
+ // '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($obj_type, $object);
+
+ // First let the field types do their preparation.
+ _field_invoke_multiple('prepare_view', $obj_type, array($id => $object), $display, $null, $options);
+ // Then let the formatters do their own specific massaging.
+ _field_invoke_multiple_default('prepare_view', $obj_type, array($id => $object), $display, $null, $options);
+ // Build the renderable array.
+ $result = _field_invoke_default('view', $obj_type, $object, $display, $null, $options);
+
+ // Invoke hook_field_attach_view_alter() to tet other modules alter the
+ // renderable array, as in a full field_attach_view() execution.
+ $context = array(
+ 'obj_type' => $obj_type,
+ 'object' => $object,
+ 'build_mode' => '_custom',
+ 'langcode' => $langcode,
+ );
+ drupal_alter('field_attach_view', $result, $context);
- $output = $view[$field['field_name']];
+ if (isset($result[$field_name])) {
+ $output = $result[$field_name];
+ $output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css';
+ }
}
+
return $output;
}
diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test
index 1bbb36744..b148f31c8 100644
--- a/modules/field/tests/field.test
+++ b/modules/field/tests/field.test
@@ -1728,6 +1728,107 @@ class FieldFormTestCase extends FieldTestCase {
}
}
+class FieldDisplayAPITestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Field Display API tests',
+ 'description' => 'Test the display API.',
+ 'group' => 'Field API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+
+ // Create a field and instance.
+ $this->field_name = 'test_field';
+ $this->label = $this->randomName();
+ $this->cardinality = 4;
+
+ $this->field = array(
+ 'field_name' => $this->field_name,
+ 'type' => 'test_field',
+ 'cardinality' => $this->cardinality,
+ );
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'object_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'label' => $this->label,
+ 'display' => array(
+ 'full' => array(
+ 'type' => 'field_test_default',
+ 'settings' => array(
+ 'test_formatter_setting' => $this->randomName(),
+ ),
+ ),
+ ),
+ );
+ field_create_field($this->field);
+ field_create_instance($this->instance);
+
+ // Create an entity with values.
+ $this->values = $this->_generateTestFieldValues($this->cardinality);
+ $this->entity = field_test_create_stub_entity();
+ $this->is_new = TRUE;
+ $this->entity->{$this->field_name}[LANGUAGE_NONE] = $this->values;
+ field_test_entity_save($this->entity);
+ }
+
+ /**
+ * Test the field_view_field() function.
+ */
+ function testFieldBuildField() {
+ // 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));
+ $settings = field_info_formatter_settings('field_test_default');
+ $setting = $settings['test_formatter_setting'];
+ $this->assertText($this->label, t('Label was displayed.'));
+ foreach($this->values as $delta => $value) {
+ $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(
+ 'label' => 'hidden',
+ 'type' => 'field_test_multiple',
+ 'settings' => array(
+ 'test_formatter_setting_multiple' => $this->randomName(),
+ ),
+ );
+ $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
+ $this->drupalSetContent(drupal_render($output));
+ $setting = $display['settings']['test_formatter_setting_multiple'];
+ $this->assertNoText($this->label, t('Label was not displayed.'));
+ $array = array();
+ foreach($this->values as $delta => $value) {
+ $array[] = $delta . ':' . $value['value'];
+ }
+ $this->assertText($setting . '|' . implode('|', $array), t('Values were displayed with expected setting.'));
+
+ // Build mode: check that display settings specified in the instance are
+ // used.
+ $output = field_view_field('test_entity', $this->entity, $this->field_name, 'full');
+ $this->drupalSetContent(drupal_render($output));
+ $setting = $this->instance['display']['full']['settings']['test_formatter_setting'];
+ $this->assertText($this->label, t('Label was displayed.'));
+ foreach($this->values as $delta => $value) {
+ $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+ }
+
+ // Unknown build mode: check that display settings for 'full' build mode
+ // are used.
+ $output = field_view_field('test_entity', $this->entity, $this->field_name, 'unknown_build_mode');
+ $this->drupalSetContent(drupal_render($output));
+ $setting = $this->instance['display']['full']['settings']['test_formatter_setting'];
+ $this->assertText($this->label, t('Label was displayed.'));
+ foreach($this->values as $delta => $value) {
+ $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+ }
+ }
+}
+
class FieldCrudTestCase extends FieldTestCase {
public static function getInfo() {
return array(