? .settings Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1146 diff -u -p -r1.1146 common.inc --- includes/common.inc 11 Apr 2010 18:33:43 -0000 1.1146 +++ includes/common.inc 14 Apr 2010 12:15:56 -0000 @@ -6295,6 +6295,11 @@ function entity_get_info($entity_type = 'revision' => '', 'bundle' => '', ); + foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) { + $entity_info[$name]['view modes'][$view_mode] += array( + 'custom settings' => FALSE, + ); + } // If no bundle key is provided, assume a single bundle, named after // the entity type. if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) { Index: modules/blog/blog.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v retrieving revision 1.352 diff -u -p -r1.352 blog.module --- modules/blog/blog.module 18 Mar 2010 06:26:36 -0000 1.352 +++ modules/blog/blog.module 14 Apr 2010 12:15:56 -0000 @@ -77,7 +77,7 @@ function blog_view($node, $view_mode) { /** * Implements hook_node_view(). */ -function blog_node_view($node, $view_mode = 'full') { +function blog_node_view($node, $view_mode) { if ($view_mode != 'rss') { if ($node->type == 'blog' && arg(0) != 'blog' || arg(1) != $node->uid) { $links['blog_usernames_blog'] = array( Index: modules/book/book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book/book.module,v retrieving revision 1.539 diff -u -p -r1.539 book.module --- modules/book/book.module 13 Apr 2010 15:23:02 -0000 1.539 +++ modules/book/book.module 14 Apr 2010 12:15:56 -0000 @@ -240,6 +240,7 @@ function book_entity_info_alter(&$info) $info['node']['view modes'] += array( 'print' => array( 'label' => t('Print'), + 'custom settings' => FALSE, ), ); } Index: modules/comment/comment.install =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.install,v retrieving revision 1.65 diff -u -p -r1.65 comment.install --- modules/comment/comment.install 7 Apr 2010 14:36:16 -0000 1.65 +++ modules/comment/comment.install 14 Apr 2010 12:15:56 -0000 @@ -279,10 +279,12 @@ function comment_update_7012() { 'label' => 'Comment', 'entity_type' => 'comment', 'settings' => array('text_processing' => 1), - // Hide field label by default. + 'required' => TRUE, 'display' => array( - 'full' => array( + 'default' => array( 'label' => 'hidden', + 'type' => 'text_default', + 'weight' => 0, ), ), ); Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.866 diff -u -p -r1.866 comment.module --- modules/comment/comment.module 13 Apr 2010 23:12:26 -0000 1.866 +++ modules/comment/comment.module 14 Apr 2010 12:15:56 -0000 @@ -108,6 +108,7 @@ function comment_entity_info() { 'view modes' => array( 'full' => array( 'label' => t('Full comment'), + 'custom settings' => FALSE, ), ), 'static cache' => FALSE, @@ -165,15 +166,17 @@ function comment_field_extra_fields() { foreach (node_type_get_types() as $type) { if (variable_get('comment_subject_field_' . $type->type, 1) == 1) { $return['comment']['comment_node_' . $type->type] = array( - 'author' => array( - 'label' => t('Author'), - 'description' => t('Author textfield'), - 'weight' => -2, - ), - 'title' => array( - 'label' => t('Subject'), - 'description' => t('Subject textfield'), - 'weight' => -1, + 'form' => array( + 'author' => array( + 'label' => t('Author'), + 'description' => t('Author textfield'), + 'weight' => -2, + ), + 'title' => array( + 'label' => t('Subject'), + 'description' => t('Subject textfield'), + 'weight' => -1, + ), ), ); } @@ -362,10 +365,11 @@ function _comment_body_field_instance_cr 'bundle' => 'comment_node_' . $info->type, 'settings' => array('text_processing' => 1), 'required' => TRUE, - // Hides field label by default. 'display' => array( - 'full' => array( + 'default' => array( 'label' => 'hidden', + 'type' => 'text_default', + 'weight' => 0, ), ), ); Index: modules/field/field.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.api.php,v retrieving revision 1.71 diff -u -p -r1.71 field.api.php --- modules/field/field.api.php 6 Apr 2010 16:49:12 -0000 1.71 +++ modules/field/field.api.php 14 Apr 2010 13:15:34 -0000 @@ -9,53 +9,56 @@ /** * Expose "pseudo-field" components on fieldable entities. * - * Field UI's 'Manage fields' page lets users re-order fields, but also - * non-field components. For nodes, that would be title, menu settings, or - * other elements exposed by contributed modules through hook_form() or - * hook_form_alter(). + * Field UI's 'Manage fields' and 'Manage display' pages let users re-order + * fields, but also non-field components. For nodes, that would be the title, + * poll choices, or other elements exposed by contributed 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. + * 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() * * @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: + * @todo * - 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 module 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; } @@ -1666,3 +1669,49 @@ function hook_field_read_instance($insta */ function hook_field_access($op, $field, $entity_type, $entity, $account) { } + +/** + * Alter the display settings of a field before it gets displayed. + * + * 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 informations. + * + * @param $display + * The display settings that will be used to display the field values, as + * found in the 'display' entry 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'... + */ +function hook_field_display_alter(&$display, $context) { + // Leave field labels out of the search index. + if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') { + $display['label'] = 'hidden'; + } +} + +/** + * Alter the display settings of a 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 informations. + * + * @param $displays + * An array of display settings for the pseudo-fields in the entity, keyed + * by pseudo-fields 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; + } +} \ No newline at end of file Index: modules/field/field.attach.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v retrieving revision 1.86 diff -u -p -r1.86 field.attach.inc --- modules/field/field.attach.inc 4 Apr 2010 12:48:18 -0000 1.86 +++ modules/field/field.attach.inc 14 Apr 2010 12:15:56 -0000 @@ -552,8 +552,9 @@ function field_attach_form($entity_type, // 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; // Let other modules make changes to the form. // Avoid module_invoke_all() to let parameters be taken by reference. @@ -1182,7 +1183,7 @@ function field_attach_query_revisions($f * @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. @@ -1247,7 +1248,7 @@ function field_attach_prepare_view($enti * @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); @@ -1259,8 +1260,9 @@ function field_attach_view($entity_type, // 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'; @@ -1378,6 +1380,14 @@ function field_attach_rename_bundle($ent // 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); } @@ -1407,6 +1417,13 @@ function field_attach_delete_bundle($ent // 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); } Index: modules/field/field.crud.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.crud.inc,v retrieving revision 1.55 diff -u -p -r1.55 field.crud.inc --- modules/field/field.crud.inc 28 Mar 2010 11:54:06 -0000 1.55 +++ modules/field/field.crud.inc 14 Apr 2010 12:29:59 -0000 @@ -31,8 +31,8 @@ * 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. + * The array properties are: * * @param array $field: * - id (integer, read-only) @@ -96,8 +96,8 @@ * 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. + * The array properties are: * * @param array $instance: * - id (integer, read-only) @@ -152,11 +152,21 @@ * - 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 simply + * use the 'default' options, thus reducing the number of displays to + * configure and keep to-date as they add new fields. For nodes, on a fresh + * install, only the 'teaser' view mode is configured to use custom display + * options, all others view modes defined in core use the 'default' + * options. When programmatically adding fields 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 settings. * - label (string) * Position of the label. 'inline', 'above' and 'hidden' are the * values recognized by the default 'field' theme implementation. @@ -171,6 +181,11 @@ * - module (string, read-only) * The name of the module which implements the display formatter. * - teaser + * A sub-array of key/value pairs describing the display options to be + * used when the field is being displayed in the 'teaser' view mode. + * Those options might be overlooked if the site administrator + * specified that the 'teaser' view mode should use default settings + * for this bundle. * - ... * - other_mode * - ... @@ -603,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 present 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 on the definition are left empty, and the field will + * not be displayed in this mode until site administrators configure fields + * display with Field UI. * @return * The $instance array with the id property filled in. * @throw @@ -722,7 +741,6 @@ function _field_write_instance($instance 'required' => FALSE, 'label' => $instance['field_name'], 'description' => '', - 'weight' => 0, 'deleted' => 0, ); @@ -734,30 +752,55 @@ function _field_write_instance($instance // 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[$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 Index: modules/field/field.default.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v retrieving revision 1.34 diff -u -p -r1.34 field.default.inc --- modules/field/field.default.inc 27 Mar 2010 05:52:49 -0000 1.34 +++ modules/field/field.default.inc 14 Apr 2010 12:15:56 -0000 @@ -122,7 +122,10 @@ function field_default_prepare_view($ent // 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; @@ -173,18 +176,13 @@ function field_default_view($entity_type // 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'; Index: modules/field/field.info.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.info.inc,v retrieving revision 1.44 diff -u -p -r1.44 field.info.inc --- modules/field/field.info.inc 27 Mar 2010 05:52:49 -0000 1.44 +++ modules/field/field.info.inc 14 Apr 2010 12:15:56 -0000 @@ -290,11 +290,16 @@ function _field_info_prepare_instance($i $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, + ); } } Index: modules/field/field.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.module,v retrieving revision 1.72 diff -u -p -r1.72 field.module --- modules/field/field.module 13 Apr 2010 15:23:02 -0000 1.72 +++ modules/field/field.module 14 Apr 2010 12:36:35 -0000 @@ -365,75 +365,272 @@ function _field_sort_items_value_helper( } /** - * Registry of pseudo-field components in a given bundle. + * Gets or sets administration-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 the $settings parameter is not set, returns the current value of the + * settings. + */ +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 user settings for view modes in a given bundle. * * @param $entity_type * The type of $entity; e.g. 'node' or 'user'. * @param $bundle * The bundle name. + * + * @return + * An array keyed by view modes, 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). + */ +function field_view_mode_settings($entity_type, $bundle) { + $settings = &drupal_static(__FUNCTION__, NULL); + + if (is_null($settings)) { + $bundle_settings = field_bundle_settings($entity_type, $bundle); + $settings = $bundle_settings['view_modes']; + // Include view modes fow which nothing is 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; + } + } + } + + return $settings; +} + +/** + * Returns the list and settings for pseudo-field elements in a given bundle. + * + * @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. + * @todo format. */ -function field_extra_fields($entity_type, $bundle) { - $info = &drupal_static(__FUNCTION__, array()); +function field_extra_fields($entity_type, $bundle, $context) { + $extra = &drupal_static(__FUNCTION__, NULL); - if (empty($info)) { + if (is_null($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 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][$view_mode] = $settings[$view_mode]; + } + else { + $data[$name][$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(); } + /** - * Retrieve the user-defined weight for a 'pseudo-field' component. + * 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 its own 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 3rd party 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('field_display', $display, $context); + + return $display; +} + +/** + * 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 its own 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][$actual_mode]; + } + + // Let 3rd party 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['#view_mode'])) { + // We are viewing an entity. + $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']; } - elseif (isset($elements[$key])) { - $elements[$key]['#weight'] = $value['weight']; + } + } + else { + // We are displaying an entity 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']; } } } + return $elements; } @@ -543,7 +740,7 @@ function field_view_value($entity_type, * 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 +771,6 @@ function field_view_field($entity_type, // 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. Index: modules/field/modules/text/text.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.test,v retrieving revision 1.22 diff -u -p -r1.22 text.test --- modules/field/modules/text/text.test 10 Apr 2010 10:01:15 -0000 1.22 +++ modules/field/modules/text/text.test 14 Apr 2010 12:15:56 -0000 @@ -46,7 +46,7 @@ class TextFieldTestCase extends DrupalWe 'type' => 'text_textfield', ), 'display' => array( - 'full' => array( + 'default' => array( 'type' => 'text_default', ), ), @@ -94,7 +94,12 @@ class TextFieldTestCase extends DrupalWe ), '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 DrupalWe // 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 DrupalWe ), '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 DrupalWe // 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 DrupalWe // 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')); } Index: modules/field/tests/field.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/tests/field.test,v retrieving revision 1.27 diff -u -p -r1.27 field.test --- modules/field/tests/field.test 11 Apr 2010 18:33:43 -0000 1.27 +++ modules/field/tests/field.test 14 Apr 2010 12:15:56 -0000 @@ -871,8 +871,8 @@ class FieldAttachOtherTestCase extends F ), ); 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 F $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 F ), ); 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 F ), ); 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 F ), ); 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 FieldTes $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 FieldTes $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 Fi '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 Fi // 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 Fi // 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 $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 $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 $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']); Index: modules/field/tests/field_test.entity.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/tests/field_test.entity.inc,v retrieving revision 1.7 diff -u -p -r1.7 field_test.entity.inc --- modules/field/tests/field_test.entity.inc 27 Mar 2010 18:41:14 -0000 1.7 +++ modules/field/tests/field_test.entity.inc 14 Apr 2010 12:15:56 -0000 @@ -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, ), ); Index: modules/field_ui/field_ui-display-overview-form.tpl.php =================================================================== RCS file: modules/field_ui/field_ui-display-overview-form.tpl.php diff -N modules/field_ui/field_ui-display-overview-form.tpl.php --- modules/field_ui/field_ui-display-overview-form.tpl.php 19 Aug 2009 13:31:13 -0000 1.1 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,55 +0,0 @@ - - - - - - - $value): ?> - - - - $value): ?> - - - - - - - - - - $title): ?> - - - - - - -
  - -
human_name; ?>{$context}->label; ?>{$context}->type; ?>
- - Index: modules/field_ui/field_ui-display-overview-table.tpl.php =================================================================== RCS file: modules/field_ui/field_ui-display-overview-table.tpl.php diff -N modules/field_ui/field_ui-display-overview-table.tpl.php --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/field_ui/field_ui-display-overview-table.tpl.php 14 Apr 2010 12:15:56 -0000 @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
human_name; ?>weight . $row->hidden_name; ?>label)) print $row->label; ?>type; ?>
human_name; ?>weight . $row->hidden_name; ?>label)) print $row->label; ?>type; ?>
+ Index: modules/field_ui/field_ui.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.admin.inc,v retrieving revision 1.48 diff -u -p -r1.48 field_ui.admin.inc --- modules/field_ui/field_ui.admin.inc 11 Apr 2010 18:33:43 -0000 1.48 +++ modules/field_ui/field_ui.admin.inc 14 Apr 2010 12:15:56 -0000 @@ -83,7 +83,7 @@ function field_ui_field_overview_form($f $field_types = field_info_field_types(); $widget_types = field_info_widget_types(); - $extra = field_extra_fields($entity_type, $bundle); + $extra_fields = field_extra_fields($entity_type, $bundle, 'form'); // Store each default weight so that we can add the 'add new' rows after them. $weights = array(); @@ -93,7 +93,7 @@ function field_ui_field_overview_form($f '#entity_type' => $entity_type, '#bundle' => $bundle, '#fields' => array_keys($instances), - '#extra' => array_keys($extra), + '#extra' => array_keys($extra_fields), '#field_rows' => array(), ); @@ -155,17 +155,17 @@ function field_ui_field_overview_form($f } // Non-field elements. - foreach ($extra as $name => $label) { - $weight = $extra[$name]['weight']; + foreach ($extra_fields as $name => $extra_field) { + $weight = $extra_field['weight']; $form[$name] = array( 'label' => array( - '#markup' => t($extra[$name]['label']), + '#markup' => t($extra_field['label']), ), 'name' => array( '#markup' => $name, ), 'description' => array( - '#markup' => isset($extra[$name]['description']) ? $extra[$name]['description'] : '', + '#markup' => isset($extra_field['description']) ? $extra_field['description'] : '', ), 'weight' => array( '#type' => 'textfield', @@ -173,10 +173,10 @@ function field_ui_field_overview_form($f '#size' => 3, ), 'edit' => array( - '#markup' => isset($extra[$name]['edit']) ? $extra[$name]['edit'] : '', + '#markup' => isset($extra_field['edit']) ? $extra_field['edit'] : '', ), 'delete' => array( - '#markup' => isset($extra[$name]['delete']) ? $extra[$name]['delete'] : '', + '#markup' => isset($extra_field['delete']) ? $extra_field['delete'] : '', ), 'hidden_name' => array( '#type' => 'hidden', @@ -463,8 +463,9 @@ function field_ui_field_overview_form_su $bundle = $form['#bundle']; $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle); + $bundle_settings = field_bundle_settings($entity_type, $bundle); + // Update field weights. - $extra = array(); foreach ($form_values as $key => $values) { if (in_array($key, $form['#fields'])) { $instance = field_read_instance($entity_type, $key, $bundle); @@ -475,13 +476,11 @@ function field_ui_field_overview_form_su field_update_instance($instance); } elseif (in_array($key, $form['#extra'])) { - $extra[$key] = $values['weight']; + $bundle_settings['extra_fields']['form'][$key]['weight'] = $values['weight']; } } - $extra_weights = variable_get('field_extra_weights', array()); - $extra_weights[$entity_type][$bundle] = $extra; - variable_set('field_extra_weights', $extra_weights); + field_bundle_settings($entity_type, $bundle, $bundle_settings); $destinations = array(); @@ -562,12 +561,9 @@ function field_ui_field_overview_form_su } /** - * Menu callback; presents a listing of fields display settings for a bundle. - * - * This form includes form widgets to select which fields appear in teaser and - * full view modes, and how the field labels should be rendered. + * Menu callback; presents fields display settings for a given view mode. */ -function field_ui_display_overview_form($form, &$form_state, $entity_type, $bundle, $view_modes_selector = 'basic') { +function field_ui_display_overview_form($form, &$form_state, $entity_type, $bundle, $view_mode) { $bundle = field_extract_bundle($entity_type, $bundle); field_ui_inactive_message($entity_type, $bundle); @@ -576,14 +572,14 @@ function field_ui_display_overview_form( // Gather type information. $instances = field_info_instances($entity_type, $bundle); $field_types = field_info_field_types(); - $view_modes = field_ui_view_modes_tabs($entity_type, $view_modes_selector); + $extra_fields = field_extra_fields($entity_type, $bundle, 'display'); $form += array( - '#tree' => TRUE, '#entity_type' => $entity_type, '#bundle' => $bundle, + '#view_mode' => $view_mode, '#fields' => array_keys($instances), - '#contexts' => $view_modes_selector, + '#extra' => array_keys($extra_fields), ); if (empty($instances)) { @@ -591,84 +587,160 @@ function field_ui_display_overview_form( return $form; } - // Fields. + $table = array( + '#theme' => 'field_ui_display_overview_table', + '#field_rows' => array(), + '#tree' => TRUE, + ); + $label_options = array( 'above' => t('Above'), 'inline' => t('Inline'), 'hidden' => t(''), ); + foreach ($instances as $name => $instance) { - $field = field_info_field($instance['field_name']); - $weight = $instance['widget']['weight']; + $display = $instance['display'][$view_mode]; - $form[$name] = array( - 'human_name' => array('#markup' => check_plain($instance['label'])), - 'weight' => array('#type' => 'value', '#value' => $weight), + $table[$name]['human_name'] = array( + '#markup' => check_plain($instance['label']), + ); + $table[$name]['weight'] = array( + '#type' => 'textfield', + '#default_value' => $display['weight'], + '#size' => 3, ); - $defaults = $instance['display']; + $table[$name]['hidden_name'] = array( + '#type' => 'hidden', + '#default_value' => $name, + ); + $table[$name]['label'] = array( + '#type' => 'select', + '#options' => $label_options, + '#default_value' => $display['label'], + ); + $field = field_info_field($instance['field_name']); $formatter_options = field_ui_formatter_options($field['type']); $formatter_options['hidden'] = t(''); - foreach ($view_modes as $view_mode) { - $display = isset($instance['display'][$view_mode]) ? $instance['display'][$view_mode] : $instance['display']['full']; - $form[$name][$view_mode]['label'] = array( - '#type' => 'select', - '#options' => $label_options, - '#default_value' => $display['label'], - ); - $form[$name][$view_mode]['type'] = array( - '#type' => 'select', - '#options' => $formatter_options, - '#default_value' => $display['type'], - ); + $table[$name]['type'] = array( + '#type' => 'select', + '#options' => $formatter_options, + '#default_value' => $display['type'], + ); + $table['#field_rows'][] = $name; + + // Collect default formatters for the JS script. + $field_type_info = field_info_field_types($field['type']); + $default_formatters[$name] = $field_type_info['default_formatter']; + } + + // Non-field elements. + foreach ($extra_fields as $name => $extra_field) { + $display = $extra_field[$view_mode]; + $table[$name]['human_name'] = array( + '#markup' => t($extra_field['label']), + ); + $table[$name]['weight'] = array( + '#type' => 'textfield', + '#default_value' => $display['weight'], + '#size' => 3, + ); + $table[$name]['hidden_name'] = array( + '#type' => 'hidden', + '#default_value' => $name, + ); + $table[$name]['type'] = array( + '#type' => 'select', + '#options' => array('visible' => t('Visible'), 'hidden' => t('Hidden')), + '#default_value' => $display['visible'] ? 'visible' : 'hidden', + ); + $table['#field_rows'][] = $name; + } + $form['settings'] = $table; + + // Custom display settings. + if ($view_mode == 'default') { + $form['modes'] = array( + '#type' => 'fieldset', + '#title' => t('Custom display settings'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + // Collect options and default values for the 'Custom display settings' + // checkboxes. + $options = array(); + $default = array(); + $entity_info = entity_get_info($entity_type); + $view_modes = $entity_info['view modes']; + $view_mode_settings = field_view_mode_settings($entity_type, $bundle); + foreach ($view_modes as $view_mode_name => $view_mode_info) { + $options[$view_mode_name] = $view_mode_info['label']; + if (!empty($view_mode_settings[$view_mode_name]['custom_settings'])) { + $default[] = $view_mode_name; + } } + $form['modes']['view_modes_custom'] = array( + '#type' => 'checkboxes', + '#title' => t('Use custom display settings for the following contexts'), + '#options' => $options, + '#default_value' => $default, + ); } - $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); + $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 10); + + $form['#attached']['js'][] = drupal_get_path('module', 'field_ui') . '/field_ui.js'; + $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css'; + drupal_add_js(array('fieldDefaultFormatters' => $default_formatters), 'setting'); + return $form; } /** - * Theme preprocess function for field_ui-display-overview-form.tpl.php. + * Theme preprocess function for field_ui-display-overview-table.tpl.php. */ -function template_preprocess_field_ui_display_overview_form(&$vars) { - $form = &$vars['form']; +function template_preprocess_field_ui_display_overview_table(&$vars) { + $elements = &$vars['elements']; - $contexts_selector = $form['#contexts']; - $view_modes = field_ui_view_modes_tabs($form['#entity_type'], $contexts_selector); - $entity_info = entity_get_info($form['#entity_type']); - $view_modes_info = $entity_info['view modes']; - $vars['contexts'] = array(); - foreach ($view_modes as $view_mode) { - $vars['contexts'][$view_mode] = $view_modes_info[$view_mode]['label']; - } - - $order = _field_ui_overview_order($form, $form['#fields']); - if (empty($order)) { - $vars['rows'] = array(); - $vars['submit'] = ''; - return; - } - $rows = array(); - foreach ($order as $key) { - $element = &$form[$key]; - $row = new stdClass(); - foreach (element_children($element) as $child) { - if (array_key_exists('label', $element[$child])) { - $row->{$child} = new stdClass(); - $row->{$child}->label = drupal_render($element[$child]['label']); - $row->{$child}->type = drupal_render($element[$child]['type']); - } - else { - $row->{$child} = drupal_render($element[$child]); + $rows = array( + 'visible' => array(), + 'hidden' => array(), + ); + + if (!empty($elements['#field_rows'])) { + drupal_add_tabledrag('field-display-overview', 'order', 'sibling', 'field-weight'); + + $order = _field_ui_overview_order($elements, $elements['#field_rows']); + foreach ($order as $key) { + $element = &$elements[$key]; + $visibility = $element['type']['#value'] == 'hidden' ? 'hidden' : 'visible'; + + // Add target classes for the tabledrag behavior. + $element['weight']['#attributes']['class'][] = 'field-weight'; + $element['hidden_name']['#attributes']['class'][] = 'field-name'; + $element['type']['#attributes']['class'][] = 'field-formatter-type'; + $element['type']['#attributes']['class'][] = "field-display-$visibility"; + $element['type']['#attributes']['class'][] = "field-name-$key"; + + $row = new stdClass(); + foreach (element_children($element) as $child) { + if (array_key_exists('label', $element[$child])) { + $row->{$child} = new stdClass(); + $row->{$child}->label = drupal_render($element[$child]['label']); + $row->{$child}->type = drupal_render($element[$child]['type']); + } + else { + $row->{$child} = drupal_render($element[$child]); + } } + $row->class = 'draggable'; + $row->label_class = 'label-field'; + $rows[$visibility][] = $row; } - $row->label_class = 'label-field'; - $rows[] = $row; } $vars['rows'] = $rows; - $vars['submit'] = drupal_render_children($form); } /** @@ -676,17 +748,46 @@ function template_preprocess_field_ui_di */ function field_ui_display_overview_form_submit($form, &$form_state) { $form_values = $form_state['values']; - foreach ($form_values as $key => $values) { - if (in_array($key, $form['#fields'])) { - $instance = field_info_instance($form['#entity_type'], $key, $form['#bundle']); - foreach ($instance['display'] as $view_mode => $display) { - if (isset($values[$view_mode])) { - $instance['display'][$view_mode] = array_merge($instance['display'][$view_mode], $values[$view_mode]); - } + $entity_type = $form['#entity_type']; + $bundle = $form['#bundle']; + $view_mode = $form['#view_mode']; + + // Save data for 'regular' fields. + foreach ($form['#fields'] as $field_name) { + $instance = field_info_instance($entity_type, $field_name, $bundle); + $instance['display'][$view_mode] = $form_values['settings'][$field_name]; + field_update_instance($instance); + } + + // Get current bundle settings. + $bundle_settings = field_bundle_settings($entity_type, $bundle); + + // Save data for 'extra' fields. + foreach ($form['#extra'] as $name) { + $bundle_settings['extra_fields']['display'][$name][$view_mode] = array( + 'weight' => $form_values['settings'][$name]['weight'], + 'visible' => $form_values['settings'][$name]['type'] == 'visible', + ); + } + + // Save view modes data. + if ($view_mode == 'default') { + $entity_info = entity_get_info($entity_type); + foreach ($form_values['view_modes_custom'] as $view_mode_name => $value) { + // Display a message for each view mode newly configured to use custom + // settings. + if (!empty($value) && empty($bundle_settings['view_modes'][$view_mode_name]['custom_settings'])) { + $view_mode_label = $entity_info['view modes'][$view_mode_name]['label']; + $path = _field_ui_bundle_admin_path($entity_type, $bundle) . "/display/$view_mode_name"; + drupal_set_message(t('The %view_mode mode now uses custom display settings. You might want to configure them.', array('%view_mode' => $view_mode_label, '@url' => url($path)))); } - field_update_instance($instance); + $bundle_settings['view_modes'][$view_mode_name]['custom_settings'] = !empty($value); } } + + // Save updated bundle settings. + field_bundle_settings($entity_type, $bundle, $bundle_settings); + drupal_set_message(t('Your settings have been saved.')); } Index: modules/field_ui/field_ui.css =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.css,v retrieving revision 1.1 diff -u -p -r1.1 field_ui.css --- modules/field_ui/field_ui.css 19 Aug 2009 13:31:13 -0000 1.1 +++ modules/field_ui/field_ui.css 14 Apr 2010 12:15:56 -0000 @@ -16,3 +16,10 @@ padding-bottom: .5em; } +.field-display-overview tr.region-title td { + font-weight:bold; +} +.field-display-overview tr.region-populated { + display:none; +} + Index: modules/field_ui/field_ui.js =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.js,v retrieving revision 1.2 diff -u -p -r1.2 field_ui.js --- modules/field_ui/field_ui.js 9 Jan 2010 23:23:43 -0000 1.2 +++ modules/field_ui/field_ui.js 14 Apr 2010 12:15:56 -0000 @@ -81,4 +81,142 @@ jQuery.fn.fieldPopulateOptions = functio }); }; +/** + * Moves a field in the display settings table from visible to hidden. + * + * This behavior is dependent on the tableDrag behavior, since it uses the + * objects initialized in that behavior to update the row. + */ +Drupal.behaviors.fieldManageDisplayDrag = { + attach: function (context, settings) { + // tableDrag is required for this behavior. + if (!$('table.field-display-overview', context).length || typeof Drupal.tableDrag == 'undefined') { + return; + } + + var defaultFormatters = Drupal.settings.fieldDefaultFormatters; + var tableDrag = Drupal.tableDrag['field-display-overview']; + + // Add a handler for when a row is swapped, update empty regions. + tableDrag.row.prototype.onSwap = function (swappedRow) { + checkEmptyRegions(this.table, this); + }; + + // Add a handler to update the formatter selector when a row is dropped in + // or out of the 'Hidden' section. + tableDrag.onDrop = function () { + var dragObject = this; + var regionRow = $(dragObject.rowObject.element).prevAll('tr.region-message').get(0); + var visibility = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2'); + + // Update the 'format' selector if the visibility changed. + var $select = $('select.field-formatter-type', dragObject.rowObject.element); + var oldVisibility = $select[0].className.replace(/([^ ]+[ ]+)*field-display-([^ ]+)([ ]+[^ ]+)*/, '$2'); + if (visibility != oldVisibility) { + $select.removeClass('field-display-' + oldVisibility).addClass('field-display-' + visibility); + + // Update the selected formatter if coming from an actual drag. + if (!$select.data('noUpdate')) { + if (visibility == 'visible') { + // Restore the formatter back to the previously selected one if + // available, or to the default formatter. + var value = $select.data('oldFormatter'); + if (typeof value == 'undefined') { + // Extract field name from the name of the select. + var fieldName = $select[0].className.match(/\bfield-name-(\S+)\b/)[1].replace('-', '_'); + // Pseudo-fields do not have an entry in the defaultFormatters + // array, we just return to 'visible' for those. + value = (fieldName in defaultFormatters) ? defaultFormatters[fieldName] : 'visible'; + } + $select.data('oldFormatter', value); + } + else { + var value = 'hidden'; + } + $select.val(value); + } + $select.removeData('noUpdate'); + } + }; + + // Add the behavior to each formatter select list. + $('select.field-formatter-type', context).once('field-formatter-type', function () { + // Initialize 'previously selected formatter' as the incoming value. + if ($(this).val() != 'hidden') { + $(this).data('oldFormatter', $(this).val()); + } + + // Add change listener. + $(this).change(function (event) { + var $select = $(this); + var value = $select.val(); + + // Keep track of the last selected formatter. + if (value != 'hidden') { + $select.data('oldFormatter', value); + } + + var visibility = (value == 'hidden') ? 'hidden' : 'visible'; + var oldVisibility = $select[0].className.replace(/([^ ]+[ ]+)*field-display-([^ ]+)([ ]+[^ ]+)*/, '$2'); + if (visibility != oldVisibility) { + // Prevent the onDrop handler from overriding the selected option. + $select.data('noUpdate', true); + + // Make our new row and select field. + var $row = $(this).parents('tr:first'); + var $table = $(this).parents('table'); + var tableDrag = Drupal.tableDrag[$table.attr('id')]; + tableDrag.rowObject = new tableDrag.row($row); + + // Move the row at the bottom of the new section. + if (visibility == 'hidden') { + $('tr:last', tableDrag.table).after($row); + } + else { + $('tr.region-title-hidden', tableDrag.table).before($row); + } + + // Manually update weights and restripe. + tableDrag.updateFields($row.get(0)); + tableDrag.rowObject.changed = true; + if (tableDrag.oldRowElement) { + $(tableDrag.oldRowElement).removeClass('drag-previous'); + } + tableDrag.oldRowElement = $row.get(0); + tableDrag.restripeTable(); + tableDrag.rowObject.markChanged(); + tableDrag.oldRowElement = $row; + $row.addClass('drag-previous'); + + // Modify empty regions with added or removed fields. + checkEmptyRegions($table, tableDrag.rowObject); + } + + // Remove focus from selectbox. + $select.get(0).blur(); + }); + }); + + var checkEmptyRegions = function ($table, rowObject) { + $('tr.region-message', $table).each(function () { + // If the dragged row is in this region, but above the message row, swap it down one space. + if ($(this).prev('tr').get(0) == rowObject.element) { + // Prevent a recursion problem when using the keyboard to move rows up. + if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) { + rowObject.swap('after', this); + } + } + // This region has become empty. + if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) { + $(this).removeClass('region-populated').addClass('region-empty'); + } + // This region has become populated. + else if ($(this).is('.region-empty')) { + $(this).removeClass('region-empty').addClass('region-populated'); + } + }); + }; + } +}; + })(jQuery); Index: modules/field_ui/field_ui.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.module,v retrieving revision 1.28 diff -u -p -r1.28 field_ui.module --- modules/field_ui/field_ui.module 28 Mar 2010 11:08:30 -0000 1.28 +++ modules/field_ui/field_ui.module 14 Apr 2010 12:15:56 -0000 @@ -66,10 +66,11 @@ function field_ui_menu() { if (defined('MAINTENANCE_MODE')) { return $items; } + // Create tabs for all possible bundles. - foreach (entity_get_info() as $entity_type => $info) { - if ($info['fieldable']) { - foreach ($info['bundles'] as $bundle_name => $bundle_info) { + foreach (entity_get_info() as $entity_type => $entity_info) { + if ($entity_info['fieldable']) { + foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) { if (isset($bundle_info['admin'])) { // Extract path information from the bundle. $path = $bundle_info['admin']['path']; @@ -92,7 +93,12 @@ function field_ui_menu() { // items below. $field_position = count(explode('/', $path)) + 1; + // Extract access information, providing defaults. $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments'))); + $access += array( + 'access callback' => 'user_access', + 'access arguments' => array('administer site configuration'), + ); $items["$path/fields"] = array( 'title' => 'Manage fields', @@ -145,24 +151,38 @@ function field_ui_menu() { 'file' => 'field_ui.admin.inc', ) + $access; - // 'Manage display' tab and context secondary tabs. + // 'Manage display' tab. $items["$path/display"] = array( 'title' => 'Manage display', 'page callback' => 'drupal_get_form', - 'page arguments' => array('field_ui_display_overview_form', $entity_type, $bundle_arg), + 'page arguments' => array('field_ui_display_overview_form', $entity_type, $bundle_arg, 'default'), 'type' => MENU_LOCAL_TASK, 'weight' => 2, 'file' => 'field_ui.admin.inc', ) + $access; - $tabs = field_ui_view_modes_tabs($entity_type); - foreach ($tabs as $key => $tab) { - $items["$path/display/$key"] = array( - 'title' => $tab['title'], - 'page arguments' => array('field_ui_display_overview_form', $entity_type, $bundle_arg, $key), - 'type' => $key == 'basic' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, - 'weight' => $key == 'basic' ? 0 : 1, + + // View modes secondary tabs. + // The same base $path for the menu item (with a placeholder) can be + // used for all bundles of a given entity type; but depending on + // administrator settings, each bundle has a different set of view + // modes available for customisation. So we define menu items for all + // view modes, and use an access callback to determine which ones are + // actually visible for a given bundle. + $weight = 0; + $view_modes = array('default' => array('label' => t('Default'))) + $entity_info['view modes']; + foreach ($view_modes as $view_mode => $view_mode_info) { + $items["$path/display/$view_mode"] = array( + 'title' => $view_mode_info['label'], + 'page arguments' => array('field_ui_display_overview_form', $entity_type, $bundle_arg, $view_mode), + // The access callback needs to check both the current 'custom + // display' setting for the view mode, and the overall access + // rules for the bundle admin pages. + 'access callback' => '_field_ui_view_mode_menu_access', + 'access arguments' => array_merge(array($entity_type, $bundle_arg, $view_mode, $access['access callback']), $access['access arguments']), + 'type' => $view_mode == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, + 'weight' => $weight++, 'file' => 'field_ui.admin.inc', - ) + $access; + ); } } } @@ -219,96 +239,53 @@ function field_ui_menu_title($instance) } /** - * Implements hook_theme(). - */ -function field_ui_theme() { - return array( - 'field_ui_field_overview_form' => array( - 'render element' => 'form', - 'file' => 'field_ui.admin.inc', - 'template' => 'field_ui-field-overview-form', - ), - 'field_ui_display_overview_form' => array( - 'render element' => 'form', - 'file' => 'field_ui.admin.inc', - 'template' => 'field_ui-display-overview-form', - ), - ); -} - -/** - * Group available view modes on tabs on the 'Manage display' page. - * - * @todo Remove this completely and use vertical tabs? + * Menu access callback for the 'view mode display settings' pages. */ -function field_ui_view_modes_tabs($entity_type, $tab_selector = NULL) { - $info = &drupal_static(__FUNCTION__); - - if (!isset($info[$entity_type])) { - $info[$entity_type] = module_invoke_all('field_ui_view_modes_tabs', $entity_type); - // Filter out inactive modes. - $entity_info = entity_get_info($entity_type); - foreach ($info[$entity_type] as $tab => $values) { - $modes = array(); - foreach ($info[$entity_type][$tab]['view modes'] as $mode) { - if (isset($entity_info['view modes'][$mode])) { - $modes[] = $mode; - } - } - if ($modes) { - $info[$entity_type][$tab]['view modes'] = $modes; +function _field_ui_view_mode_menu_access($entity_type, $bundle, $view_mode, $access_callback) { + // First, determine visibility according to the 'use custom display' + // setting for the view mode. + $bundle = field_extract_bundle($entity_type, $bundle); + $view_mode_settings = field_view_mode_settings($entity_type, $bundle); + $visibility = ($view_mode == 'default') || !empty($view_mode_settings[$view_mode]['custom_settings']); + + // Then, determine access according to the $access parameter. This duplicates + // part of _menu_check_access(). + if ($visibility) { + // Grab the variable 'access arguments' part. + $args = array_slice(func_get_args(), 4); + $callback = empty($access_callback) ? 0 : trim($access_callback); + if (is_numeric($callback)) { + return (bool)$callback; + } + else { + // As call_user_func_array is quite slow and user_access is a very common + // callback, it is worth making a special case for it. + if ($access_callback == 'user_access') { + return (count($args) == 1) ? user_access($args[0]) : user_access($args[0], $args[1]); } - else { - unset($info[$entity_type][$tab]); + elseif (function_exists($access_callback)) { + return call_user_func_array($access_callback, $args); } } } - if ($tab_selector) { - return isset($info[$entity_type][$tab_selector]) ? $info[$entity_type][$tab_selector]['view modes'] : array(); - } - return $info[$entity_type]; } /** - * Implements hook_field_ui_view_modes_tabs() on behalf of other core modules. - * - * @return - * An array describing the view modes defined by the module, grouped by tabs. - * - * A module can add its render modes to a tab defined by another module. - * Expected format: - * @code - * array( - * 'tab1' => array( - * 'title' => t('The human-readable title of the tab'), - * 'view modes' => array('mymodule_mode1', 'mymodule_mode2'), - * ), - * 'tab2' => array( - * // ... - * ), - * ); - * @endcode + * Implements hook_theme(). */ -function field_ui_field_ui_view_modes_tabs() { - $modes = array( - 'basic' => array( - 'title' => t('Basic'), - 'view modes' => array('teaser', 'full'), - ), - 'rss' => array( - 'title' => t('RSS'), - 'view modes' => array('rss'), - ), - 'print' => array( - 'title' => t('Print'), - 'view modes' => array('print'), +function field_ui_theme() { + return array( + 'field_ui_field_overview_form' => array( + 'render element' => 'form', + 'file' => 'field_ui.admin.inc', + 'template' => 'field_ui-field-overview-form', ), - 'search' => array( - 'title' => t('Search'), - 'view modes' => array('search_index', 'search_result'), + 'field_ui_display_overview_table' => array( + 'render element' => 'elements', + 'file' => 'field_ui.admin.inc', + 'template' => 'field_ui-display-overview-table', ), ); - return $modes; } /** @@ -321,31 +298,6 @@ function field_ui_field_attach_create_bu } /** - * Implements hook_field_attach_rename_bundle(). - */ -function field_ui_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { - if ($bundle_old !== $bundle_new) { - $extra_weights = variable_get('field_extra_weights', array()); - if (isset($info[$entity_type][$bundle_old])) { - $extra_weights[$entity_type][$bundle_new] = $extra_weights[$entity_type][$bundle_old]; - unset($extra_weights[$entity_type][$bundle_old]); - variable_set('field_extra_weights', $extra_weights); - } - } -} - -/** - * Implements hook_field_attach_delete_bundle(). - */ -function field_ui_field_attach_delete_bundle($entity_type, $bundle) { - $extra_weights = variable_get('field_extra_weights', array()); - if (isset($extra_weights[$entity_type][$bundle])) { - unset($extra_weights[$entity_type][$bundle]); - variable_set('field_extra_weights', $extra_weights); - } -} - -/** * Helper function to create the right administration path for a bundle. */ function _field_ui_bundle_admin_path($entity_type, $bundle_name) { Index: modules/forum/forum.install =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.install,v retrieving revision 1.43 diff -u -p -r1.43 forum.install --- modules/forum/forum.install 27 Mar 2010 05:52:50 -0000 1.43 +++ modules/forum/forum.install 14 Apr 2010 12:15:56 -0000 @@ -61,6 +61,16 @@ function forum_enable() { 'widget' => array( 'type' => 'options_select', ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ) ); field_create_instance($instance); Index: modules/image/image.test =================================================================== RCS file: /cvs/drupal/drupal/modules/image/image.test,v retrieving revision 1.18 diff -u -p -r1.18 image.test --- modules/image/image.test 11 Apr 2010 18:33:44 -0000 1.18 +++ modules/image/image.test 14 Apr 2010 12:15:56 -0000 @@ -617,21 +617,21 @@ class ImageFieldDisplayTestCase extends // Test the image linked to file formatter. $instance = field_info_instance('node', $field_name, 'article'); - $instance['display']['full']['type'] = 'image_link_file'; + $instance['display']['default']['type'] = 'image_link_file'; field_update_instance($instance); $default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE)); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, t('Image linked to file formatter displaying correctly on full node view.')); // Test the image linked to content formatter. - $instance['display']['full']['type'] = 'image_link_content'; + $instance['display']['default']['type'] = 'image_link_content'; field_update_instance($instance); $default_output = l(theme('image', $image_info), 'node/' . $nid, array('html' => TRUE, 'attributes' => array('class' => 'active'))); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, t('Image linked to content formatter displaying correctly on full node view.')); // Test the image style 'thumbnail' formatter. - $instance['display']['full']['type'] = 'image__thumbnail'; + $instance['display']['default']['type'] = 'image__thumbnail'; field_update_instance($instance); // Ensure the derrivative image is generated so we do not have to deal with // image style callback paths. Index: modules/locale/locale.test =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v retrieving revision 1.69 diff -u -p -r1.69 locale.test --- modules/locale/locale.test 31 Mar 2010 20:05:06 -0000 1.69 +++ modules/locale/locale.test 14 Apr 2010 12:15:56 -0000 @@ -1888,16 +1888,6 @@ class LocaleMultilingualFieldsFunctional $this->drupalGet("node/$node->nid"); $body = $this->xpath('//div[@id=:id]//div[@property="content:encoded"]/p', array(':id' => 'node-' . $node->nid)); $this->assertEqual(current($body), $node->body['en'][0]['value'], 'Node body is correctly showed.'); - - $settings['body[full][type]'] = 'hidden'; - $this->drupalPost('admin/structure/types/manage/page/display', $settings, t('Save')); - $select = $this->xpath('//select[@id="edit-body-full-type"]/option[@selected="selected"]'); - // Check if body display is actually "hidden" for the "full" view mode. - $this->assertEqual(current($select), '', 'Body display is actually "hidden" for the "full" view mode'); - $this->drupalGet("node/$node->nid"); - // Check if node body is not showed. - $body = $this->xpath('//div[@id=:id]//div[@property="content:encoded"]/p', array(':id' => 'node-' . $node->nid)); - $this->assertFalse(is_array($body), 'Body correctly not showed.'); } } Index: modules/node/node.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.api.php,v retrieving revision 1.66 diff -u -p -r1.66 node.api.php --- modules/node/node.api.php 26 Mar 2010 17:14:45 -0000 1.66 +++ modules/node/node.api.php 14 Apr 2010 12:15:56 -0000 @@ -1157,7 +1157,7 @@ function hook_validate($node, &$form) { * * @ingroup node_api_hooks */ -function hook_view($node, $view_mode = 'full') { +function hook_view($node, $view_mode) { if (node_is_page($node)) { $breadcrumb = array(); $breadcrumb[] = l(t('Home'), NULL); Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1262 diff -u -p -r1.1262 node.module --- modules/node/node.module 13 Apr 2010 23:16:56 -0000 1.1262 +++ modules/node/node.module 14 Apr 2010 13:03:44 -0000 @@ -189,12 +189,15 @@ function node_entity_info() { 'view modes' => array( 'full' => array( 'label' => t('Full content'), + 'custom settings' => FALSE, ), 'teaser' => array( 'label' => t('Teaser'), + 'custom settings' => TRUE, ), 'rss' => array( 'label' => t('RSS'), + 'custom settings' => FALSE, ), ), ), @@ -206,9 +209,11 @@ function node_entity_info() { $return['node']['view modes'] += array( 'search_index' => array( 'label' => t('Search index'), + 'custom settings' => FALSE, ), 'search_result' => array( 'label' => t('Search result'), + 'custom settings' => FALSE, ), ); } @@ -231,6 +236,16 @@ function node_entity_info() { } /** + * Implements hook_field_display_alter(). + */ +function node_field_display_alter(&$display, $context) { + // Leave field labels out of the search index. + if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') { + $display['label'] = 'hidden'; + } +} + +/** * Entity uri callback. */ function node_uri($node) { @@ -445,7 +460,7 @@ function node_type_get_name($node) { * * All new module-defined node types are saved to the database via a call to * node_type_save(), and obsolete ones are deleted via a call to - * node_type_delete(). See _node_types_build() for an explanation of the new + * node_type_delete(). See _node_types_build() for an explanation of the new * and obsolete types. */ function node_types_rebuild() { @@ -568,10 +583,8 @@ function node_configure_fields($type) { 'label' => $type->body_label, 'widget_type' => 'text_textarea_with_summary', 'settings' => array('display_summary' => TRUE), - - // Define default formatters for the teaser and full view. 'display' => array( - 'full' => array( + 'default' => array( 'label' => 'hidden', 'type' => 'text_default', ), @@ -603,10 +616,12 @@ function node_field_extra_fields() { foreach (node_type_get_types() as $type) { if ($type->has_title) { $extra['node'][$type->type] = array( - 'title' => array( - 'label' => $type->title_label, - 'description' => t('Node module element.'), - 'weight' => -5, + 'form' => array( + 'title' => array( + 'label' => $type->title_label, + 'description' => t('Node module element.'), + 'weight' => -5, + ), ), ); } Index: modules/poll/poll.module =================================================================== RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v retrieving revision 1.342 diff -u -p -r1.342 poll.module --- modules/poll/poll.module 13 Apr 2010 15:23:03 -0000 1.342 +++ modules/poll/poll.module 14 Apr 2010 13:07:55 -0000 @@ -208,16 +208,30 @@ function poll_node_info() { */ function poll_field_extra_fields() { $extra['node']['poll'] = array( - 'choice_wrapper' => array( - 'label' => t('Poll choices'), - 'description' => t('Poll module choices.'), - 'weight' => -4, - ), - 'settings' => array( - 'label' => t('Poll settings'), - 'description' => t('Poll module settings.'), - 'weight' => -3, + 'form' => array( + 'choice_wrapper' => array( + 'label' => t('Poll choices'), + 'description' => t('Poll module 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; @@ -627,7 +641,7 @@ function poll_block_latest_poll_view($no /** * Implements hook_view(). */ -function poll_view($node, $view_mode = 'full') { +function poll_view($node, $view_mode) { global $user; $output = ''; Index: modules/rdf/rdf.test =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/rdf.test,v retrieving revision 1.20 diff -u -p -r1.20 rdf.test --- modules/rdf/rdf.test 11 Apr 2010 18:33:44 -0000 1.20 +++ modules/rdf/rdf.test 14 Apr 2010 12:15:56 -0000 @@ -124,18 +124,27 @@ class RdfMarkupTestCase extends DrupalWe $langcode = LANGUAGE_NONE; $bundle_name = "article"; - // Create file field. - $file_field = 'file_test'; - $edit = array( - '_add_new_field[label]' => $file_field, - '_add_new_field[field_name]' => $file_field, - '_add_new_field[type]' => 'file', - '_add_new_field[widget_type]' => 'file_generic', + $field_name = 'file_test'; + $field = array( + 'field_name' => $field_name, + 'type' => 'file', ); - $this->drupalPost('admin/structure/types/manage/' . $bundle_name . '/fields', $edit, t('Save')); + field_create_field($field); + $instance = array( + 'field_name' => $field_name, + 'entity_type' => 'node', + 'bundle' => $bundle_name, + 'display' => array( + 'teaser' => array( + 'type' => 'file_default', + ), + ), + ); + field_create_instance($instance); + // Set the RDF mapping for the new field. $rdf_mapping = rdf_mapping_load('node', $bundle_name); - $rdf_mapping += array('field_' . $file_field => array('predicates' => array('rdfs:seeAlso'), 'type' => 'rel')); + $rdf_mapping += array($field_name => array('predicates' => array('rdfs:seeAlso'), 'type' => 'rel')); $rdf_mapping_save = array('mapping' => $rdf_mapping, 'type' => 'node', 'bundle' => $bundle_name); rdf_mapping_save($rdf_mapping_save); @@ -149,7 +158,7 @@ class RdfMarkupTestCase extends DrupalWe // Create an array for drupalPost with the field names as the keys and // the uris for the test files as the values. - $edit = array("files[field_" . $file_field . "_" . $langcode . "_0]" => drupal_realpath($file->uri), + $edit = array("files[" . $field_name . "_" . $langcode . "_0]" => drupal_realpath($file->uri), "files[" . $image_field . "_" . $langcode . "_0]" => drupal_realpath($image->uri)); // Create node and save, then edit node to upload files. Index: modules/system/system.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v retrieving revision 1.151 diff -u -p -r1.151 system.api.php --- modules/system/system.api.php 10 Apr 2010 17:30:15 -0000 1.151 +++ modules/system/system.api.php 14 Apr 2010 12:15:56 -0000 @@ -122,9 +122,21 @@ function hook_hook_info() { * ('full' mode), on the home page or taxonomy listings ('teaser' mode), or * in an RSS feed ('rss' mode). Modules taking part in the display of the * entity (notably the Field API) can adjust their behavior depending on - * the requested view mode. Keys of the array are view mode names. Each - * view mode is described by an array with the following key/value pairs: + * the requested view mode. An additional 'default' view mode is available + * for all entity types. This view mode is not intended for actual entity + * display, but holds default display settings. For each available view + * mode, administrators can configure whether it should use its own set of + * field display settings, or just replicate the settings of the 'default' + * view mode, thus reducing the amount of display configurations to keep + * track of. Keys of the array are view mode names. Each view mode is + * described by an array with the following key/value pairs: * - label: The human-readable name of the view mode + * - custom settings: A boolean specifying whether the view mode should by + * default use its own custom field display settings. If FALSE, entities + * displayed in this view mode will reuse the 'default' display settings by + * default (e.g. right after the module exposing the view mode is enabled), + * but administrators can later use the Field UI to apply custom display + * settings specific to the view mode. */ function hook_entity_info() { $return = array( @@ -133,7 +145,7 @@ function hook_entity_info() { 'controller class' => 'NodeController', 'base table' => 'node', 'revision table' => 'node_revision', - 'path callback' => 'node_path', + 'uri callback' => 'node_uri', 'fieldable' => TRUE, 'entity keys' => array( 'id' => 'nid', @@ -146,13 +158,16 @@ function hook_entity_info() { 'bundles' => array(), 'view modes' => array( 'full' => array( - 'label' => t('Full node'), + 'label' => t('Full content'), + 'custom settings' => FALSE, ), 'teaser' => array( 'label' => t('Teaser'), + 'custom settings' => TRUE, ), 'rss' => array( 'label' => t('RSS'), + 'custom settings' => FALSE, ), ), ), @@ -164,9 +179,11 @@ function hook_entity_info() { $return['node']['view modes'] += array( 'search_index' => array( 'label' => t('Search index'), + 'custom settings' => FALSE, ), 'search_result' => array( 'label' => t('Search result'), + 'custom settings' => FALSE, ), ); } Index: modules/taxonomy/taxonomy.install =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.install,v retrieving revision 1.40 diff -u -p -r1.40 taxonomy.install --- modules/taxonomy/taxonomy.install 4 Apr 2010 13:53:22 -0000 1.40 +++ modules/taxonomy/taxonomy.install 14 Apr 2010 12:15:56 -0000 @@ -374,6 +374,16 @@ function taxonomy_update_7004() { 'widget' => array( 'type' => $vocabulary->tags ? 'taxonomy_autocomplete' : 'select', ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ), ); field_create_instance($instance); } Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.585 diff -u -p -r1.585 taxonomy.module --- modules/taxonomy/taxonomy.module 10 Apr 2010 16:37:19 -0000 1.585 +++ modules/taxonomy/taxonomy.module 14 Apr 2010 13:15:07 -0000 @@ -101,6 +101,7 @@ function taxonomy_entity_info() { // @todo View mode for display as a field (when attached to nodes etc). 'full' => array( 'label' => t('Taxonomy term page'), + 'custom settings' => FALSE, ), ), ), @@ -146,16 +147,25 @@ function taxonomy_field_extra_fields() { foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) { $return['taxonomy_term'][$machine_name] = array( - 'name' => array( - 'label' => t('Name'), - 'description' => t('Term name textfield'), - 'weight' => -5, + 'form' => array( + 'name' => array( + 'label' => t('Name'), + 'description' => t('Term name textfield'), + 'weight' => -5, + ), + 'description' => array( + 'label' => t('Description'), + 'description' => t('Term description textarea'), + 'weight' => 0, + ), + ), + 'display' => array( + 'description' => array( + 'label' => t('Description'), + 'description' => t('Description'), + 'weight' => 0, + ), ), - 'description' => array( - 'label' => t('Description'), - 'description' => t('Term description textarea'), - 'weight' => 0, - ) ); } Index: modules/taxonomy/taxonomy.test =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.test,v retrieving revision 1.76 diff -u -p -r1.76 taxonomy.test --- modules/taxonomy/taxonomy.test 6 Apr 2010 05:44:07 -0000 1.76 +++ modules/taxonomy/taxonomy.test 14 Apr 2010 12:15:56 -0000 @@ -347,7 +347,7 @@ class TaxonomyTermTestCase extends Taxon 'type' => 'options_select', ), 'display' => array( - 'full' => array( + 'default' => array( 'type' => 'taxonomy_term_reference_link', ), ), @@ -831,7 +831,12 @@ class TaxonomyTermFieldTestCase extends 'label' => $this->randomName() . '_label', 'widget' => array( 'type' => 'options_select', - ) + ), + 'display' => array( + 'full' => array( + 'type' => 'taxonomy_term_reference_link', + ), + ), ); field_create_instance($this->instance); @@ -856,7 +861,7 @@ class TaxonomyTermFieldTestCase extends $entity = field_test_entity_test_load($id); $entities = array($id => $entity); field_attach_prepare_view($entity_type, $entities, 'full'); - $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($term->name, t('Term name is displayed')); } @@ -904,7 +909,7 @@ class TaxonomyTokenReplaceTestCase exten 'type' => 'options_select', ), 'display' => array( - 'full' => array( + 'default' => array( 'type' => 'taxonomy_term_reference_link', ), ), Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1159 diff -u -p -r1.1159 user.module --- modules/user/user.module 13 Apr 2010 15:23:03 -0000 1.1159 +++ modules/user/user.module 14 Apr 2010 12:15:56 -0000 @@ -146,6 +146,7 @@ function user_entity_info() { 'view modes' => array( 'full' => array( 'label' => t('User account'), + 'custom settings' => FALSE, ), ), ), @@ -167,20 +168,24 @@ function user_uri($user) { */ function user_field_extra_fields() { $return['user']['user'] = array( - 'account' => array( - 'label' => 'User name and password', - 'description' => t('User module account form elements'), - 'weight' => -10, - ), - 'timezone' => array( - 'label' => 'Timezone', - 'description' => t('User module timezone form element.'), - 'weight' => 6, + 'form' => array( + 'account' => array( + 'label' => 'User name and password', + 'description' => t('User module account form elements'), + 'weight' => -10, + ), + 'timezone' => array( + 'label' => 'Timezone', + 'description' => t('User module timezone form element.'), + 'weight' => 6, + ), ), - 'summary' => array( - 'label' => 'History', - 'description' => t('User module history view element.'), - 'weight' => 5, + 'display' => array( + 'summary' => array( + 'label' => 'History', + 'description' => t('User module history view element.'), + 'weight' => 5, + ), ), ); Index: profiles/standard/standard.install =================================================================== RCS file: /cvs/drupal/drupal/profiles/standard/standard.install,v retrieving revision 1.11 diff -u -p -r1.11 standard.install --- profiles/standard/standard.install 27 Mar 2010 05:52:50 -0000 1.11 +++ profiles/standard/standard.install 14 Apr 2010 12:15:56 -0000 @@ -309,6 +309,16 @@ function standard_install() { 'type' => 'taxonomy_autocomplete', 'weight' => 4, ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ), ); field_create_instance($instance); @@ -366,34 +376,14 @@ function standard_install() { ), 'display' => array( - 'full' => array( + 'default' => array( 'label' => 'hidden', 'type' => 'image__large', - 'settings' => array(), 'weight' => -1, ), 'teaser' => array( 'label' => 'hidden', 'type' => 'image_link_content__medium', - 'settings' => array(), - 'weight' => -1, - ), - 'rss' => array( - 'label' => 'hidden', - 'type' => 'image__large', - 'settings' => array(), - 'weight' => -1, - ), - 'search_index' => array( - 'label' => 'hidden', - 'type' => 'image__large', - 'settings' => array(), - 'weight' => -1, - ), - 'search_results' => array( - 'label' => 'hidden', - 'type' => 'image__large', - 'settings' => array(), 'weight' => -1, ), ),