diff options
Diffstat (limited to 'modules/field')
-rw-r--r-- | modules/field/field.api.php | 167 | ||||
-rw-r--r-- | modules/field/field.attach.inc | 29 | ||||
-rw-r--r-- | modules/field/field.crud.inc | 89 | ||||
-rw-r--r-- | modules/field/field.default.inc | 12 | ||||
-rw-r--r-- | modules/field/field.info.inc | 9 | ||||
-rw-r--r-- | modules/field/field.module | 308 | ||||
-rw-r--r-- | modules/field/modules/text/text.test | 22 | ||||
-rw-r--r-- | modules/field/tests/field.test | 82 | ||||
-rw-r--r-- | modules/field/tests/field_test.entity.inc | 2 |
9 files changed, 554 insertions, 166 deletions
diff --git a/modules/field/field.api.php b/modules/field/field.api.php index bc4e26690..f43322c75 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -7,55 +7,59 @@ */ /** - * Expose "pseudo-field" components on fieldable entities. + * Exposes "pseudo-field" components on fieldable entities. * - * Field UI's 'Manage fields' page lets users re-order fields, but also - * non-field components. For nodes, these include the title, menu settings, and - * other elements exposed by contributed modules through hook_form() and + * Field UI's "Manage fields" and "Manage display" pages let users re-order + * fields, but also non-field components. For nodes, these include the title, + * poll choices, and other elements exposed by modules through hook_form() or * hook_form_alter(). * - * Fieldable entities or contributed modules that want to have their components - * supported should expose them using this hook, and use - * field_attach_extra_weight() to retrieve the user-defined weight when - * inserting the component. + * Fieldable entities or modules that want to have their components supported + * should expose them using this hook. The user-defined settings (weight, + * visibility) are automatically applied on rendered forms and displayed + * entities in a #pre_render callback added by field_attach_form() and + * field_attach_view(). + * + * @see _field_extra_fields_pre_render() + * @see hook_field_extra_fields_alter() * * @return - * A nested array of 'pseudo-field' components. Each list is nested within the - * field bundle to which those components apply. The keys are the name of the - * element as it appears in the form structure. The values are arrays with the - * following key/value pairs: + * A nested array of 'pseudo-field' components. Each list is nested within + * the following keys: entity type, bundle name, context (either 'form' or + * 'display'). The keys are the name of the elements as appearing in the + * renderable array (either the entity form or the displayed entity). The + * value is an associative array: * - label: The human readable name of the component. * - description: A short description of the component contents. * - weight: The default weight of the element. - * - view: (optional) The name of the element as it appears in the rendered - * structure, if different from the name in the form. - * - * @see hook_field_extra_fields_alter() */ function hook_field_extra_fields() { - $extra = array(); - - foreach (node_type_get_types() as $bundle) { - if ($type->has_title) { - $extra['node'][$bundle]['title'] = array( - 'label' => $type->title_label, - 'description' => t('Node module element.'), - 'weight' => -5, - ); - } - } - if (module_exists('poll')) { - $extra['node']['poll']['choice_wrapper'] = array( - 'label' => t('Poll choices'), - 'description' => t('Poll module choices.'), - 'weight' => -4, - ); - $extra['node']['poll']['settings'] = array( - 'label' => t('Poll settings'), - 'description' => t('Poll module settings.'), - 'weight' => -3, - ); - } + $extra['node']['poll'] = array( + 'form' => array( + 'choice_wrapper' => array( + 'label' => t('Poll choices'), + 'description' => t('Poll choices'), + 'weight' => -4, + ), + 'settings' => array( + 'label' => t('Poll settings'), + 'description' => t('Poll module settings'), + 'weight' => -3, + ), + ), + 'display' => array( + 'poll_view_voting' => array( + 'label' => t('Poll vote'), + 'description' => t('Poll vote'), + 'weight' => 0, + ), + 'poll_view_results' => array( + 'label' => t('Poll results'), + 'description' => t('Poll results'), + 'weight' => 0, + ), + ) + ); return $extra; } @@ -1652,6 +1656,91 @@ function hook_field_storage_pre_query($field_name, $conditions, $options, &$skip } /** + * Alters the display settings of a field before it gets displayed. + * + * Note that instead of hook_field_display_alter(), which is called for all + * fields on all entity types, hook_field_display_ENTITY_TYPE_alter() may be + * used to alter display settings for fields on a specific entity type only. + * + * This hook is called once per field per displayed entity. If the result of the + * hook involves reading from the database, it is highly recommended to + * statically cache the information. + * + * @param $display + * The display settings that will be used to display the field values, as + * found in the 'display' key of $instance definitions. + * @param $context + * An associative array containing: + * - entity_type: The entity type; e.g. 'node' or 'user'. + * - field: The field being rendered. + * - instance: The instance being rendered. + * - view_mode: The view mode, e.g. 'full', 'teaser'... + * + * @see hook_field_display_ENTITY_TYPE_alter() + */ +function hook_field_display_alter(&$display, $context) { + // Leave field labels out of the search index. + // Note: The check against $context['entity_type'] == 'node' could be avoided + // by using hook_field_display_node_alter() instead of + // hook_field_display_alter(), resulting in less function calls when + // rendering non-node entities. + if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') { + $display['label'] = 'hidden'; + } +} + +/** + * Alters the display settings of a field on a given entity type before it gets displayed. + * + * Modules can implement hook_field_display_ENTITY_TYPE_alter() to alter display + * settings for fields on a specific entity type, rather than implementing + * hook_field_display_alter(). + * + * This hook is called once per field per displayed entity. If the result of the + * hook involves reading from the database, it is highly recommended to + * statically cache the information. + * + * @param $display + * The display settings that will be used to display the field values, as + * found in the 'display' key of $instance definitions. + * @param $context + * An associative array containing: + * - entity_type: The entity type; e.g. 'node' or 'user'. + * - field: The field being rendered. + * - instance: The instance being rendered. + * - view_mode: The view mode, e.g. 'full', 'teaser'... + * + * @see hook_field_display_alter() + */ +function hook_field_display_ENTITY_TYPE_alter(&$display, $context) { + // Leave field labels out of the search index. + if ($context['view_mode'] == 'search_index') { + $display['label'] = 'hidden'; + } +} + +/** + * Alters the display settings of pseudo-fields before an entity is displayed. + * + * This hook is called once per displayed entity. If the result of the hook + * involves reading from the database, it is highly recommended to statically + * cache the information. + * + * @param $displays + * An array of display settings for the pseudo-fields in the entity, keyed + * by pseudo-field names. + * @param $context + * An associative array containing: + * - entity_type: The entity type; e.g. 'node' or 'user'. + * - bundle: The bundle name. + * - view_mode: The view mode, e.g. 'full', 'teaser'... + */ +function hook_field_extra_fields_display_alter(&$displays, $context) { + if ($context['entity_type'] == 'taxonomy_term' && $context['view_mode'] == 'full') { + $displays['description']['visibility'] = FALSE; + } +} +/** * @} End of "ingroup field_storage" */ diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 3896e2134..8a7991d41 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -552,8 +552,9 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod // Add custom weight handling. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); $form['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; - $form['#pre_render'][] = '_field_extra_weights_pre_render'; - $form['#extra_fields'] = field_extra_fields($entity_type, $bundle); + $form['#pre_render'][] = '_field_extra_fields_pre_render'; + $form['#entity_type'] = $entity_type; + $form['#bundle'] = $bundle; // Save the original entity to allow later re-use. $form_state['entity'] = $entity; @@ -1185,7 +1186,7 @@ function field_attach_query_revisions($field_id, $conditions, $options = array() * @param $view_mode * View mode, e.g. 'full', 'teaser'... */ -function field_attach_prepare_view($entity_type, $entities, $view_mode = 'full') { +function field_attach_prepare_view($entity_type, $entities, $view_mode) { // To ensure hooks are only run once per entity, only process items without // the _field_view_prepared flag. // @todo: resolve this more generally for both entity and field level hooks. @@ -1250,7 +1251,7 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode = 'full') * @return * A renderable array for the field values. */ -function field_attach_view($entity_type, $entity, $view_mode = 'full', $langcode = NULL) { +function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL) { // Determine the actual language to display for each field, given the // languages available in the field data. $display_language = field_language($entity_type, $entity, NULL, $langcode); @@ -1262,8 +1263,9 @@ function field_attach_view($entity_type, $entity, $view_mode = 'full', $langcode // Add custom weight handling. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); - $output['#pre_render'][] = '_field_extra_weights_pre_render'; - $output['#extra_fields'] = field_extra_fields($entity_type, $bundle); + $output['#pre_render'][] = '_field_extra_fields_pre_render'; + $output['#entity_type'] = $entity_type; + $output['#bundle'] = $bundle; // Include CSS styles. $output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; @@ -1381,6 +1383,14 @@ function field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { // Clear the cache. field_cache_clear(); + // Update bundle settings. + $settings = variable_get('field_bundle_settings', array()); + if (isset($settings[$entity_type][$bundle_old])) { + $settings[$entity_type][$bundle_new] = $settings[$entity_type][$bundle_old]; + unset($settings[$entity_type][$bundle_old]); + variable_set('field_bundle_settings', $settings); + } + // Let other modules act on renaming the bundle. module_invoke_all('field_attach_rename_bundle', $entity_type, $bundle_old, $bundle_new); } @@ -1410,6 +1420,13 @@ function field_attach_delete_bundle($entity_type, $bundle) { // Clear the cache. field_cache_clear(); + // Clear bundle display settings. + $settings = variable_get('field_bundle_settings', array()); + if (isset($settings[$entity_type][$bundle])) { + unset($settings[$entity_type][$bundle]); + variable_set('field_bundle_settings', $settings); + } + // Let other modules act on deleting the bundle. module_invoke_all('field_attach_delete_bundle', $entity_type, $bundle, $instances); } diff --git a/modules/field/field.crud.inc b/modules/field/field.crud.inc index 904e69d26..4f58cd445 100644 --- a/modules/field/field.crud.inc +++ b/modules/field/field.crud.inc @@ -31,8 +31,7 @@ * field_attach_load() then loads the 'subtitle' and 'photo' fields * because they are both attached to the 'node' bundle 'article'. * - * Field definitions are (currently) represented as an array of key/value - * pairs. The array properties are: + * Field definitions are represented as an array of key/value pairs. * * @param array $field: * - id (integer, read-only) @@ -96,8 +95,7 @@ * A sub-array of key/value pairs of settings. Each storage backend * defines and documents its own settings. * - * Field Instance definitions are (currently) represented as an array of - * key/value pairs. The array properties are: + * Field instance definitions are represented as an array of key/value pairs. * * @param array $instance: * - id (integer, read-only) @@ -154,11 +152,20 @@ * - module (string, read-only) * The name of the module that implements the widget type. * - display (array) - * A sub-array of key/value pairs identifying view modes and the way the - * field values should be displayed in each mode. - * - full (array) - * A sub-array of key/value pairs of the display options to be used - * when the field is being displayed in the "full" view mode. + * A sub-array of key/value pairs identifying the way field values should + * be displayed in each of the entity type's view modes, plus the 'default' + * mode. For each view mode, Field UI lets site administrators define + * whether they want to use a dedicated set of display options or the + * 'default' options to reduce the number of displays to maintain as they + * add new fields. For nodes, on a fresh install, only the 'teaser' view + * mode is configured to use custom display options, all other view modes + * defined use the 'default' options by default. When programmatically + * adding field instances on nodes, it is therefore recommended to at least + * specify display options for 'default' and 'teaser'. + * - default (array) + * A sub-array of key/value pairs describing the display options to be + * used when the field is being displayed in view modes that are not + * configured to use dedicated display options. * - label (string) * Position of the label. 'inline', 'above' and 'hidden' are the * values recognized by the default 'field' theme implementation. @@ -172,7 +179,11 @@ * displayed in this view mode. * - module (string, read-only) * The name of the module which implements the display formatter. - * - teaser + * - some_mode + * A sub-array of key/value pairs describing the display options to be + * used when the field is being displayed in the 'some_mode' view mode. + * Those options will only be actually applied at run time if the view + * mode is not configured to use default settings for this bundle. * - ... * - other_mode * - ... @@ -607,12 +618,16 @@ function field_delete_field($field_name) { * - settings: each omitted setting is given the default value specified in * hook_field_widget_info(). * - display: - * Settings for the 'full' view mode will be added, and each view mode - * will be completed with the following default values: + * Settings for the 'default' view mode will be added if not present, and + * each view mode in the definition will be completed with the following + * default values: * - label: 'above' * - type: the default formatter specified in hook_field_info(). * - settings: each omitted setting is given the default value specified in * hook_field_formatter_info(). + * View modes not present in the definition are left empty, and the field + * will not be displayed in this mode. + * * @return * The $instance array with the id property filled in. * @throw @@ -730,7 +745,6 @@ function _field_write_instance($instance, $update = FALSE) { 'required' => FALSE, 'label' => $instance['field_name'], 'description' => '', - 'weight' => 0, 'deleted' => 0, ); @@ -742,30 +756,55 @@ function _field_write_instance($instance, $update = FALSE) { // TODO: what if no 'default_widget' specified ? 'type' => $field_type['default_widget'], 'settings' => array(), - 'weight' => 0, ); + // If no weight specified, make sure the field sinks at the bottom. + if (!isset($instance['widget']['weight'])) { + $weights = array(); + foreach (field_info_instances($instance['entity_type'], $instance['bundle']) as $existing_instance) { + if ($instance['field_name'] != $existing_instance['field_name']) { + $weights[] = $existing_instance['widget']['weight']; + } + } + foreach (field_extra_fields($instance['entity_type'], $instance['bundle'], 'form') as $extra) { + $weights[] = $extra['weight']; + } + $instance['widget']['weight'] = $weights ? max($weights) + 1 : 0; + } // Check widget module. $widget_type = field_info_widget_types($instance['widget']['type']); $instance['widget']['module'] = $widget_type['module']; $instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']); - // Make sure there is at least display info for the 'full' view mode. + // Make sure there are at least display settings for the 'default' view mode, + // and fill in defaults for each view mode specified in the definition. $instance['display'] += array( - 'full' => array(), + 'default' => array(), ); - // Set default display settings for each view mode. foreach ($instance['display'] as $view_mode => $display) { - $instance['display'][$view_mode] += array( + $display += array( 'label' => 'above', - // TODO: what if no 'default_formatter' specified ? - 'type' => $field_type['default_formatter'], + 'type' => isset($field_type['default_formatter']) ? $field_type['default_formatter'] : 'hidden', 'settings' => array(), - 'weight' => 0, ); - $formatter_type = field_info_formatter_types($instance['display'][$view_mode]['type']); - // TODO : 'hidden' will raise PHP warnings. - $instance['display'][$view_mode]['module'] = $formatter_type['module']; - $instance['display'][$view_mode]['settings'] += field_info_formatter_settings($instance['display'][$view_mode]['type']); + if ($display['type'] != 'hidden') { + $formatter_type = field_info_formatter_types($display['type']); + $display['module'] = $formatter_type['module']; + $display['settings'] += field_info_formatter_settings($display['type']); + } + // If no weight specified, make sure the field sinks at the bottom. + if (!isset($display['weight'])) { + $weights = array(); + foreach (field_info_instances($instance['entity_type'], $instance['bundle']) as $existing_instance) { + if ($instance['field_name'] != $existing_instance['field_name']) { + $weights[] = $existing_instance['display'][$view_mode]['weight']; + } + } + foreach (field_extra_fields($instance['entity_type'], $instance['bundle'], 'display') as $extra) { + $weights[] = $extra['display'][$view_mode]['weight']; + } + $display['weight'] = $weights ? max($weights) + 1 : 0; + } + $instance['display'][$view_mode] = $display; } // The serialized 'data' column contains everything from $instance that does diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc index 62ab6178e..eebde1e14 100644 --- a/modules/field/field.default.inc +++ b/modules/field/field.default.inc @@ -132,7 +132,10 @@ function field_default_prepare_view($entity_type, $entities, $field, $instances, // Group entities, instances and items by formatter module. $modules = array(); foreach ($instances as $id => $instance) { - $display = is_string($display) ? $instance['display'][$display] : $display; + if (is_string($display)) { + $view_mode = $display; + $display = field_get_display($instance, $view_mode); + } if ($display['type'] !== 'hidden') { $module = $display['module']; $modules[$module] = $module; @@ -183,18 +186,13 @@ function field_default_view($entity_type, $entity, $field, $instance, $langcode, // Prepare incoming display specifications. if (is_string($display)) { $view_mode = $display; - $display = $instance['display'][$view_mode]; + $display = field_get_display($instance, $view_mode); } else { $view_mode = '_custom_display'; } if ($display['type'] !== 'hidden') { - // We never want to index fields labels. - if ($view_mode == 'search_index') { - $display['label'] = 'hidden'; - } - // Calling the formatter function through module_invoke() can have a // performance impact on pages with many fields and values. $function = $display['module'] . '_field_formatter_view'; diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index a4517684c..ff5cd4e0e 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -290,11 +290,16 @@ function _field_info_prepare_instance($instance, $field) { $instance['display'][$view_mode] = _field_info_prepare_instance_display($field, $display); } - // Fallback to 'full' display settings for unspecified view modes. + // Fallback to 'hidden' for unspecified view modes. $entity_info = entity_get_info($instance['entity_type']); foreach ($entity_info['view modes'] as $view_mode => $info) { if (!isset($instance['display'][$view_mode])) { - $instance['display'][$view_mode] = $instance['display']['full']; + $instance['display'][$view_mode] = array( + 'type' => 'hidden', + 'label' => 'above', + 'settings' => array(), + 'weight' => 0, + ); } } diff --git a/modules/field/field.module b/modules/field/field.module index d470c9fd7..4182e88a1 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -365,75 +365,308 @@ function _field_sort_items_value_helper($a, $b) { } /** - * Registry of pseudo-field components in a given bundle. + * Gets or sets administratively defined bundle settings. + * + * For each bundle, settings are provided as a nested array with the following + * structure: + * @code + * array( + * 'view_modes' => array( + * // One sub-array per view mode for the entity type: + * 'full' => array( + * 'custom_display' => Whether the view mode uses custom display + * settings or settings of the 'default' mode, + * ), + * 'teaser' => ... + * ), + * 'extra_fields' => array( + * 'form' => array( + * // One sub-array per pseudo-field in displayed entities: + * 'extra_field_1' => array( + * 'weight' => The weight of the pseudo-field, + * ), + * 'extra_field_2' => ... + * ), + * 'display' => array( + * // One sub-array per pseudo-field in displayed entities: + * 'extra_field_1' => array( + * // One sub-array per view mode for the entity type, including + * // the 'default' mode: + * 'default' => array( + * 'weight' => The weight of the pseudo-field, + * 'visibility' => Whether the pseudo-field is visible or hidden, + * ), + * 'full' => ... + * ), + * 'extra_field_2' => ... + * ), + * ), + * ), + * @encode * * @param $entity_type * The type of $entity; e.g. 'node' or 'user'. * @param $bundle * The bundle name. + * @param $settings + * (optional) The settings to store. + * + * @return + * If no $settings are passed, the current settings are returned. + */ +function field_bundle_settings($entity_type, $bundle, $settings = NULL) { + $stored_settings = variable_get('field_bundle_settings', array()); + + if (isset($settings)) { + $stored_settings[$entity_type][$bundle] = $settings; + variable_set('field_bundle_settings', $stored_settings); + drupal_static_reset('field_view_mode_settings'); + drupal_static_reset('field_extra_fields'); + } + else { + $settings = isset($stored_settings[$entity_type][$bundle]) ? $stored_settings[$entity_type][$bundle] : array(); + $settings += array( + 'view_modes' => array(), + 'extra_fields' => array(), + ); + + return $settings; + } +} + +/** + * Returns view mode settings in a given bundle. + * + * @param $entity_type + * The type of entity; e.g. 'node' or 'user'. + * @param $bundle + * The bundle name to return view mode settings for. + * + * @return + * An array keyed by view mode, with the following key/value pairs: + * - custom_settings: Boolean specifying whether the view mode uses a + * dedicated set of display options (TRUE), or the 'default' options + * (FALSE). Defaults to FALSE. + */ +function field_view_mode_settings($entity_type, $bundle) { + $cache = &drupal_static(__FUNCTION__, array()); + + if (!isset($cache[$entity_type][$bundle])) { + $bundle_settings = field_bundle_settings($entity_type, $bundle); + $settings = $bundle_settings['view_modes']; + // Include view modes for which nothing has been stored yet, but whose + // definition in hook_entity_info() specify they should use custom settings + // by default. + $entity_info = entity_get_info($entity_type); + foreach ($entity_info['view modes'] as $view_mode => $view_mode_info) { + if (!isset($settings[$view_mode]['custom_settings']) && $view_mode_info['custom settings']) { + $settings[$view_mode]['custom_settings'] = TRUE; + } + } + $cache[$entity_type][$bundle] = $settings; + } + + return $cache[$entity_type][$bundle]; +} + +/** + * Returns a list and settings of pseudo-field elements in a given bundle. + * + * If $context is 'form', an array with the following structure: + * @code + * array( + * 'name_of_pseudo_field_component' => array( + * 'label' => The human readable name of the component, + * 'description' => A short description of the component content, + * 'weight' => The weight of the component in edit forms, + * ), + * 'name_of_other_pseudo_field_component' => array( + * // ... + * ), + * ); + * @endcode + * + * If $context is 'display', an array with the following structure: + * @code + * array( + * 'name_of_pseudo_field_component' => array( + * 'label' => The human readable name of the component, + * 'description' => A short description of the component content, + * // One entry per view mode, including the 'default' mode: + * 'display' => array( + * 'default' => array( + * 'weight' => The weight of the component in displayed entities in + * this view mode, + * 'visibility' => Whether the component is visible or hidden in + * displayed entities in this view mode, + * ), + * 'teaser' => array( + * // ... + * ), + * ), + * ), + * 'name_of_other_pseudo_field_component' => array( + * // ... + * ), + * ); + * @endcode + * + * @param $entity_type + * The type of entity; e.g. 'node' or 'user'. + * @param $bundle + * The bundle name. + * @param $context + * The context for which the list of pseudo-fields is requested. Either + * 'form' or 'display'. + * * @return * The array of pseudo-field elements in the bundle. */ -function field_extra_fields($entity_type, $bundle) { - $info = &drupal_static(__FUNCTION__, array()); +function field_extra_fields($entity_type, $bundle, $context) { + $extra = &drupal_static(__FUNCTION__); - if (empty($info)) { + if (!isset($extra)) { $info = (array) module_invoke_all('field_extra_fields'); drupal_alter('field_extra_fields', $info); - // Add saved weights. The array is keyed by entity type, bundle and - // element name. - $extra_weights = variable_get('field_extra_weights', array()); - foreach ($extra_weights as $entity_type_name => $bundles) { - foreach ($bundles as $bundle_name => $weights) { - foreach ($weights as $key => $value) { - if (isset($info[$entity_type_name][$bundle_name][$key])) { - $info[$entity_type_name][$bundle_name][$key]['weight'] = $value; + // Merge in saved settings, and make sure we have settings for all view + // modes. + foreach ($info as $entity_type_name => $bundles) { + $entity_type_info = entity_get_info($entity_type_name); + foreach ($bundles as $bundle_name => $extra_fields) { + $bundle_settings = field_bundle_settings($entity_type_name, $bundle_name); + $extra_fields += array('form' => array(), 'display' => array()); + + // Extra fields in forms. + $data = $extra_fields['form']; + foreach ($data as $name => $field_data) { + $settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array(); + if (isset($settings['weight'])) { + $data[$name]['weight'] = $settings['weight']; } } + $extra[$entity_type_name][$bundle_name]['form'] = $data; + + // Extra fields in displayed entities. + $data = $extra_fields['display']; + foreach ($data as $name => $field_data) { + $settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array(); + $view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes'])); + foreach ($view_modes as $view_mode) { + if (isset($settings[$view_mode])) { + $data[$name]['display'][$view_mode] = $settings[$view_mode]; + } + else { + $data[$name]['display'][$view_mode] = array( + 'weight' => $field_data['weight'], + 'visible' => TRUE, + ); + } + unset($data[$name]['weight']); + } + } + $extra[$entity_type_name][$bundle_name]['display'] = $data; } } } - return isset($info[$entity_type][$bundle]) ? $info[$entity_type][$bundle]: array(); + return isset($extra[$entity_type][$bundle][$context]) ? $extra[$entity_type][$bundle][$context] : array(); +} + + +/** + * Returns the display settings to use for an instance in a given view mode. + * + * @param $instance + * The field instance being displayed. + * @param $view_mode + * The view mode. + * + * @return + * The display settings to be used when displaying the field values. + */ +function field_get_display($instance, $view_mode) { + // Check whether the view mode uses custom display settings or the 'default' + // mode. + $view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']); + $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default'); + $display = $instance['display'][$actual_mode]; + + // Let modules alter the display settings. + $context = array( + 'entity_type' => $instance['entity_type'], + 'field' => field_info_field($instance['field_name']), + 'instance' => $instance, + 'view_mode' => $view_mode, + ); + drupal_alter(array('field_display', 'field_display_' . $instance['entity_type']), $display, $context); + + return $display; } /** - * Retrieve the user-defined weight for a 'pseudo-field' component. + * Returns the display settings to use for pseudo-fields in a given view mode. * * @param $entity_type * The type of $entity; e.g. 'node' or 'user'. * @param $bundle * The bundle name. - * @param $pseudo_field - * The name of the 'pseudo-field'. + * @param $view_mode + * The view mode. + * * @return - * The weight for the 'pseudo-field', respecting the user settings stored by - * field.module. + * The display settings to be used when viewing the bundle's pseudo-fields. */ -function field_extra_field_weight($entity_type, $bundle, $pseudo_field) { - $extra = field_extra_fields($entity_type, $bundle); - if (isset($extra[$pseudo_field])) { - return $extra[$pseudo_field]['weight']; +function field_extra_fields_get_display($entity_type, $bundle, $view_mode) { + // Check whether the view mode uses custom display settings or the 'default' + // mode. + $view_mode_settings = field_view_mode_settings($entity_type, $bundle); + $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings'])) ? $view_mode : 'default'; + $extra_fields = field_extra_fields($entity_type, $bundle, 'display'); + + $displays = array(); + foreach ($extra_fields as $name => $value) { + $displays[$name] = $extra_fields[$name]['display'][$actual_mode]; } + + // Let modules alter the display settings. + $context = array( + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'view_mode' => $view_mode, + ); + drupal_alter('field_extra_fields_display', $displays, $context); + + return $displays; } /** - * Pre-render callback to adjust weights of non-field elements on entities. + * Pre-render callback to adjust weights and visibility of non-field elements. */ -function _field_extra_weights_pre_render($elements) { - if (isset($elements['#extra_fields'])) { - foreach ($elements['#extra_fields'] as $key => $value) { - // Some core 'fields' use a different key in node forms and in 'view' - // render arrays. Ensure that we are not on a form first. - if (!isset($elements['#build_id']) && isset($value['view']) && isset($elements[$value['view']])) { - $elements[$value['view']]['#weight'] = $value['weight']; +function _field_extra_fields_pre_render($elements) { + $entity_type = $elements['#entity_type']; + $bundle = $elements['#bundle']; + + if (isset($elements['#type']) && $elements['#type'] == 'form') { + $extra_fields = field_extra_fields($entity_type, $bundle, 'form'); + foreach ($extra_fields as $name => $settings) { + if (isset($elements[$name])) { + $elements[$name]['#weight'] = $settings['weight']; } - elseif (isset($elements[$key])) { - $elements[$key]['#weight'] = $value['weight']; + } + } + elseif (isset($elements['#view_mode'])) { + $view_mode = $elements['#view_mode']; + $extra_fields = field_extra_fields_get_display($entity_type, $bundle, $view_mode); + foreach ($extra_fields as $name => $settings) { + if (isset($elements[$name])) { + $elements[$name]['#weight'] = $settings['weight']; + // Visibility: make sure we do not accidentally show a hidden element. + $elements[$name]['#access'] = isset($elements[$name]['#access']) ? ($elements[$name]['#access'] && $settings['visible']) : $settings['visible']; } } } + return $elements; } @@ -543,7 +776,7 @@ function field_view_value($entity_type, $entity, $field_name, $item, $display = * display settings specified for this view mode in the $instance * definition for the field in the entity's bundle. * If no display settings are found for the view mode, the settings for - * the 'full' view mode will be used. + * the 'default' view mode will be used. * - An array of display settings, as found in the 'display' entry of * $instance definitions. The following key/value pairs are allowed: * - label: (string) Position of the label. The default 'field' theme @@ -574,15 +807,6 @@ function field_view_field($entity_type, $entity, $field_name, $display = array() // When using custom display settings, fill in default values. $display = _field_info_prepare_instance_display($field, $display); } - else { - // When using a view mode, make sure we have settings for it, or fall - // back to the 'full' view mode. - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - $instance = field_info_instance($entity_type, $field_name, $bundle); - if (!isset($instance['display'][$display])) { - $display = 'full'; - } - } // Hook invocations are done through the _field_invoke() functions in // 'single field' mode, to reuse the language fallback logic. diff --git a/modules/field/modules/text/text.test b/modules/field/modules/text/text.test index cbd135f74..52f757e63 100644 --- a/modules/field/modules/text/text.test +++ b/modules/field/modules/text/text.test @@ -46,7 +46,7 @@ class TextFieldTestCase extends DrupalWebTestCase { 'type' => 'text_textfield', ), 'display' => array( - 'full' => array( + 'default' => array( 'type' => 'text_default', ), ), @@ -94,7 +94,12 @@ class TextFieldTestCase extends DrupalWebTestCase { ), 'widget' => array( 'type' => $widget_type, - ) + ), + 'display' => array( + 'full' => array( + 'type' => 'text_default', + ), + ), ); field_create_instance($this->instance); $langcode = LANGUAGE_NONE; @@ -116,7 +121,7 @@ class TextFieldTestCase extends DrupalWebTestCase { // Display the entity. $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view($entity_type, $entity); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $this->content = drupal_render($entity->content); $this->assertText($value, 'Filtered tags are not displayed'); } @@ -148,7 +153,12 @@ class TextFieldTestCase extends DrupalWebTestCase { ), 'widget' => array( 'type' => $widget_type, - ) + ), + 'display' => array( + 'full' => array( + 'type' => 'text_default', + ), + ), ); field_create_instance($this->instance); $langcode = LANGUAGE_NONE; @@ -180,7 +190,7 @@ class TextFieldTestCase extends DrupalWebTestCase { // Display the entity. $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view($entity_type, $entity); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $this->content = drupal_render($entity->content); $this->assertNoRaw($value, t('HTML tags are not displayed.')); $this->assertRaw(check_plain($value), t('Escaped HTML is displayed correctly.')); @@ -213,7 +223,7 @@ class TextFieldTestCase extends DrupalWebTestCase { // Display the entity. $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view($entity_type, $entity); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $this->content = drupal_render($entity->content); $this->assertRaw($value, t('Value is displayed unfiltered')); } diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 632e474ca..fa24ada88 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -871,8 +871,8 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { ), ); field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity)); - $entity->content = field_attach_view($entity_type, $entity); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $output = drupal_render($entity->content); $this->content = $output; $this->assertRaw($this->instance['label'], "Label is displayed."); @@ -885,8 +885,8 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { $entity = clone($entity_init); $this->instance['display']['full']['label'] = 'hidden'; field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity)); - $entity->content = field_attach_view($entity_type, $entity); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $output = drupal_render($entity->content); $this->content = $output; $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed."); @@ -900,8 +900,8 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { ), ); field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity)); - $entity->content = field_attach_view($entity_type, $entity); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $output = drupal_render($entity->content); $this->content = $output; $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed."); @@ -922,8 +922,8 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { ), ); field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity)); - $entity->content = field_attach_view($entity_type, $entity); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $output = drupal_render($entity->content); $display = $formatter_setting; foreach ($values as $delta => $value) { @@ -945,8 +945,8 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { ), ); field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity)); - $entity->content = field_attach_view($entity_type, $entity); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); + $entity->content = field_attach_view($entity_type, $entity, 'full'); $output = drupal_render($entity->content); $this->content = $output; foreach ($values as $delta => $value) { @@ -1332,8 +1332,8 @@ class FieldInfoTestCase extends FieldTestCase { $data['settings'] = array(); $data['widget']['settings'] = 'unavailable_widget'; $data['widget']['settings'] = array(); - $data['display']['full']['type'] = 'unavailable_formatter'; - $data['display']['full']['settings'] = array(); + $data['display']['default']['type'] = 'unavailable_formatter'; + $data['display']['default']['settings'] = array(); db_update('field_config_instance') ->fields(array('data' => serialize($data))) ->condition('field_name', $instance_definition['field_name']) @@ -1354,13 +1354,11 @@ class FieldInfoTestCase extends FieldTestCase { $widget_type = field_info_widget_types($instance['widget']['type']); $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.')); - // Check that the default formatter is used and expected settings are in place. - $entity_info = entity_get_info('test_entity'); - foreach ($entity_info['view modes'] as $view_mode => $info) { - $this->assertIdentical($instance['display'][$view_mode]['type'], $field_type['default_formatter'], t('Unavailable formatter replaced with default formatter in view_mode %view_mode', array('%view_mode' => $view_mode))); - $formatter_type = field_info_formatter_types($instance['display'][$view_mode]['type']); - $this->assertIdentical($instance['display'][$view_mode]['settings'], $formatter_type['settings'] , t('All expected formatter settings are present in view_mode %view_mode', array('%view_mode' => $view_mode))); - } + // Check that display settings are set for the 'default' mode. + $display = $instance['display']['default']; + $this->assertIdentical($display['type'], $field_type['default_formatter'], t("Formatter is set for the 'default' view mode")); + $formatter_type = field_info_formatter_types($display['type']); + $this->assertIdentical($display['settings'], $formatter_type['settings'] , t("Formatter settings are set for the 'default' view mode")); } /** @@ -1792,7 +1790,13 @@ class FieldDisplayAPITestCase extends FieldTestCase { 'bundle' => 'test_bundle', 'label' => $this->label, 'display' => array( - 'full' => array( + 'default' => array( + 'type' => 'field_test_default', + 'settings' => array( + 'test_formatter_setting' => $this->randomName(), + ), + ), + 'teaser' => array( 'type' => 'field_test_default', 'settings' => array( 'test_formatter_setting' => $this->randomName(), @@ -1862,19 +1866,19 @@ class FieldDisplayAPITestCase extends FieldTestCase { // View mode: check that display settings specified in the instance are // used. - $output = field_view_field('test_entity', $this->entity, $this->field_name, 'full'); + $output = field_view_field('test_entity', $this->entity, $this->field_name, 'teaser'); $this->drupalSetContent(drupal_render($output)); - $setting = $this->instance['display']['full']['settings']['test_formatter_setting']; + $setting = $this->instance['display']['teaser']['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 view mode: check that display settings for 'full' view mode + // Unknown view mode: check that display settings for 'default' view mode // are used. $output = field_view_field('test_entity', $this->entity, $this->field_name, 'unknown_view_mode'); $this->drupalSetContent(drupal_render($output)); - $setting = $this->instance['display']['full']['settings']['test_formatter_setting']; + $setting = $this->instance['display']['default']['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))); @@ -1929,17 +1933,17 @@ class FieldDisplayAPITestCase extends FieldTestCase { // View mode: check that display settings specified in the instance are // used. - $setting = $this->instance['display']['full']['settings']['test_formatter_setting']; + $setting = $this->instance['display']['teaser']['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'); + $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'teaser'); $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 + // Unknown view mode: check that display settings for 'default' view mode // are used. - $setting = $this->instance['display']['full']['settings']['test_formatter_setting']; + $setting = $this->instance['display']['default']['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'); @@ -2446,13 +2450,13 @@ class FieldInstanceCrudTestCase extends FieldTestCase { $this->assertIdentical($record['data']['label'], $this->instance_definition['field_name'], t('Label defaults to field name.')); $this->assertIdentical($record['data']['description'], '', t('Description defaults to empty string.')); $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], t('Default widget has been written.')); - $this->assertTrue(isset($record['data']['display']['full']), t('Display for "full" view_mode has been written.')); - $this->assertIdentical($record['data']['display']['full']['type'], $field_type['default_formatter'], t('Default formatter for "full" view_mode has been written.')); + $this->assertTrue(isset($record['data']['display']['default']), t('Display for "full" view_mode has been written.')); + $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], t('Default formatter for "full" view_mode has been written.')); // Check that default settings are set. $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , t('Default instance settings have been written.')); $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , t('Default widget settings have been written.')); - $this->assertIdentical($record['data']['display']['full']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" view_mode have been written.')); + $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], t('Default formatter settings for "full" view_mode have been written.')); // Guarantee that the field/bundle combination is unique. try { @@ -2535,8 +2539,8 @@ class FieldInstanceCrudTestCase extends FieldTestCase { $instance['settings']['test_instance_setting'] = $this->randomName(); $instance['widget']['settings']['test_widget_setting'] =$this->randomName(); $instance['widget']['weight']++; - $instance['display']['full']['settings']['test_formatter_setting'] = $this->randomName(); - $instance['display']['full']['weight']++; + $instance['display']['default']['settings']['test_formatter_setting'] = $this->randomName(); + $instance['display']['default']['weight']++; field_update_instance($instance); $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); @@ -2545,23 +2549,23 @@ class FieldInstanceCrudTestCase extends FieldTestCase { $this->assertEqual($instance['description'], $instance_new['description'], t('"description" change is saved')); $this->assertEqual($instance['widget']['settings']['test_widget_setting'], $instance_new['widget']['settings']['test_widget_setting'], t('Widget setting change is saved')); $this->assertEqual($instance['widget']['weight'], $instance_new['widget']['weight'], t('Widget weight change is saved')); - $this->assertEqual($instance['display']['full']['settings']['test_formatter_setting'], $instance_new['display']['full']['settings']['test_formatter_setting'], t('Formatter setting change is saved')); - $this->assertEqual($instance['display']['full']['weight'], $instance_new['display']['full']['weight'], t('Widget weight change is saved')); + $this->assertEqual($instance['display']['default']['settings']['test_formatter_setting'], $instance_new['display']['default']['settings']['test_formatter_setting'], t('Formatter setting change is saved')); + $this->assertEqual($instance['display']['default']['weight'], $instance_new['display']['default']['weight'], t('Widget weight change is saved')); // Check that changing widget and formatter types updates the default settings. $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); $instance['widget']['type'] = 'test_field_widget_multiple'; - $instance['display']['full']['type'] = 'field_test_multiple'; + $instance['display']['default']['type'] = 'field_test_multiple'; field_update_instance($instance); $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); $this->assertEqual($instance['widget']['type'], $instance_new['widget']['type'] , t('Widget type change is saved.')); $settings = field_info_widget_settings($instance_new['widget']['type']); $this->assertIdentical($settings, array_intersect_key($instance_new['widget']['settings'], $settings) , t('Widget type change updates default settings.')); - $this->assertEqual($instance['display']['full']['type'], $instance_new['display']['full']['type'] , t('Formatter type change is saved.')); - $info = field_info_formatter_types($instance_new['display']['full']['type']); + $this->assertEqual($instance['display']['default']['type'], $instance_new['display']['default']['type'] , t('Formatter type change is saved.')); + $info = field_info_formatter_types($instance_new['display']['default']['type']); $settings = $info['settings']; - $this->assertIdentical($settings, array_intersect_key($instance_new['display']['full']['settings'], $settings) , t('Changing formatter type updates default settings.')); + $this->assertIdentical($settings, array_intersect_key($instance_new['display']['default']['settings'], $settings) , t('Changing formatter type updates default settings.')); // Check that adding a new view mode is saved and gets default settings. $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); diff --git a/modules/field/tests/field_test.entity.inc b/modules/field/tests/field_test.entity.inc index cdd3b0c34..6bf582731 100644 --- a/modules/field/tests/field_test.entity.inc +++ b/modules/field/tests/field_test.entity.inc @@ -14,9 +14,11 @@ function field_test_entity_info() { $test_entity_modes = array( 'full' => array( 'label' => t('Full object'), + 'custom settings' => TRUE, ), 'teaser' => array( 'label' => t('Teaser'), + 'custom settings' => TRUE, ), ); |