- Patch #612894 by yched: display field improvements.
parent
8874a4e2f7
commit
e9f1814748
|
@ -338,7 +338,7 @@ function _field_invoke_multiple($op, $obj_type, $objects, &$a = NULL, &$b = NULL
|
||||||
if (function_exists($function)) {
|
if (function_exists($function)) {
|
||||||
// Iterate over all the field translations.
|
// Iterate over all the field translations.
|
||||||
foreach ($grouped_items[$field_id] as $langcode => $items) {
|
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)) {
|
if (isset($results)) {
|
||||||
// Collect results by object.
|
// Collect results by object.
|
||||||
// For hooks with array results, we merge results together.
|
// 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['#pre_render'][] = '_field_extra_weights_pre_render';
|
||||||
$output['#extra_fields'] = field_extra_fields($bundle);
|
$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(
|
$context = array(
|
||||||
'obj_type' => $obj_type,
|
'obj_type' => $obj_type,
|
||||||
'object' => $object,
|
'object' => $object,
|
||||||
|
|
|
@ -97,13 +97,32 @@ function field_default_insert($obj_type, $object, $field, $instance, $langcode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke hook_field_formatter_prepare_view() on the relavant formatters.
|
* Invokes hook_field_formatter_prepare_view() on the relevant formatters.
|
||||||
*/
|
*
|
||||||
function field_default_prepare_view($obj_type, $objects, $field, $instances, $langcode, &$items, $options, $build_mode) {
|
* @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.
|
// Group objects, instances and items by formatter module.
|
||||||
$modules = array();
|
$modules = array();
|
||||||
foreach ($instances as $id => $instance) {
|
foreach ($instances as $id => $instance) {
|
||||||
$display = $instance['display'][$build_mode];
|
$display = is_string($display) ? $instance['display'][$display] : $display;
|
||||||
if ($display['type'] !== 'hidden') {
|
if ($display['type'] !== 'hidden') {
|
||||||
$module = $display['module'];
|
$module = $display['module'];
|
||||||
$modules[$module] = $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);
|
list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
|
||||||
|
|
||||||
$addition = array();
|
$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') {
|
if ($display['type'] !== 'hidden') {
|
||||||
// We never want to index fields labels.
|
// We never want to index fields labels.
|
||||||
if ($build_mode == 'search_index') {
|
if ($build_mode == 'search_index') {
|
||||||
|
|
|
@ -278,22 +278,10 @@ function _field_info_prepare_instance($instance, $field) {
|
||||||
$instance['default_value'] = NULL;
|
$instance['default_value'] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to default widget if widget type is not available.
|
$instance['widget'] = _field_info_prepare_instance_widget($field, $instance['widget']);
|
||||||
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']);
|
|
||||||
|
|
||||||
foreach ($instance['display'] as $build_mode => $display) {
|
foreach ($instance['display'] as $build_mode => $display) {
|
||||||
if ($display['type'] != 'hidden') {
|
$instance['display'][$build_mode] = _field_info_prepare_instance_display($field, $display);
|
||||||
// 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']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to 'full' display settings for unspecified build modes.
|
// 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.
|
* Determines the behavior of a widget with respect to an operation.
|
||||||
*
|
*
|
||||||
* @param $op
|
* @param $op
|
||||||
|
|
|
@ -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
|
* The resulting output is a fully themed field with label and multiple values.
|
||||||
* an isolated field. Do *not* use inside node templates, use
|
|
||||||
* render($content[FIELD_NAME]) instead.
|
|
||||||
*
|
*
|
||||||
* The field will be displayed using the display options (label display,
|
* This function can be used by third-party modules that need to output an
|
||||||
* formatter settings...) specified in the $instance structure for the given
|
* isolated field.
|
||||||
* build mode: $instance['display'][$build_mode].
|
* - 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
|
* @param $object
|
||||||
* The object containing the field to display. Must at least contain the id key,
|
* The object containing the field to display. Must at least contain the id
|
||||||
* revision key (if applicable), bundle key, and the field data.
|
* key and the field data to display.
|
||||||
* @param $field
|
* @param $field_name
|
||||||
* The field structure.
|
* The name of the field to display.
|
||||||
* @param $instance
|
* @param $display
|
||||||
* The instance structure for $field on $object's bundle.
|
* Can be either:
|
||||||
* @param $build_mode
|
* - The name of a build mode. The field will be displayed according to the
|
||||||
* Build mode, e.g. 'full', 'teaser'...
|
* 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
|
* @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') {
|
function field_view_field($obj_type, $object, $field_name, $display = array(), $langcode = NULL) {
|
||||||
$output = '';
|
$output = array();
|
||||||
if (isset($object->$field['field_name'])) {
|
|
||||||
$items = $object->$field['field_name'];
|
|
||||||
|
|
||||||
// One-field equivalent to _field_invoke('sanitize').
|
if ($field = field_info_field($field_name)) {
|
||||||
$function = $field['module'] . '_field_sanitize';
|
if (is_array($display)) {
|
||||||
if (function_exists($function)) {
|
// When using custom display settings, fill in default values.
|
||||||
$function($obj_type, $object, $field, $instance, $items);
|
$display = _field_info_prepare_instance_display($field, $display);
|
||||||
$object->$field['field_name'] = $items;
|
}
|
||||||
|
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);
|
// Hook invocations are done through the _field_invoke() functions in
|
||||||
// TODO : what about hook_field_attach_view ?
|
// '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);
|
||||||
|
|
||||||
$output = $view[$field['field_name']];
|
// 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);
|
||||||
|
|
||||||
|
if (isset($result[$field_name])) {
|
||||||
|
$output = $result[$field_name];
|
||||||
|
$output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
class FieldCrudTestCase extends FieldTestCase {
|
||||||
public static function getInfo() {
|
public static function getInfo() {
|
||||||
return array(
|
return array(
|
||||||
|
|
Loading…
Reference in New Issue