Commit 5ff0c0d3 authored by Dries's avatar Dries

- Patch #553298 by yched, te-brian, chx, sun: redesign the 'Manage Display' screen.

parent 5d4fa6f7
...@@ -6386,6 +6386,11 @@ function entity_get_info($entity_type = NULL) { ...@@ -6386,6 +6386,11 @@ function entity_get_info($entity_type = NULL) {
'revision' => '', 'revision' => '',
'bundle' => '', '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 // If no bundle key is provided, assume a single bundle, named after
// the entity type. // the entity type.
if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) { if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
......
...@@ -77,7 +77,7 @@ function blog_view($node, $view_mode) { ...@@ -77,7 +77,7 @@ function blog_view($node, $view_mode) {
/** /**
* Implements hook_node_view(). * Implements hook_node_view().
*/ */
function blog_node_view($node, $view_mode = 'full') { function blog_node_view($node, $view_mode) {
if ($view_mode != 'rss') { if ($view_mode != 'rss') {
if ($node->type == 'blog' && (arg(0) != 'blog' || arg(1) != $node->uid)) { if ($node->type == 'blog' && (arg(0) != 'blog' || arg(1) != $node->uid)) {
$links['blog_usernames_blog'] = array( $links['blog_usernames_blog'] = array(
......
...@@ -240,6 +240,7 @@ function book_entity_info_alter(&$info) { ...@@ -240,6 +240,7 @@ function book_entity_info_alter(&$info) {
$info['node']['view modes'] += array( $info['node']['view modes'] += array(
'print' => array( 'print' => array(
'label' => t('Print'), 'label' => t('Print'),
'custom settings' => FALSE,
), ),
); );
} }
......
...@@ -279,10 +279,12 @@ function comment_update_7012() { ...@@ -279,10 +279,12 @@ function comment_update_7012() {
'label' => 'Comment', 'label' => 'Comment',
'entity_type' => 'comment', 'entity_type' => 'comment',
'settings' => array('text_processing' => 1), 'settings' => array('text_processing' => 1),
// Hide field label by default. 'required' => TRUE,
'display' => array( 'display' => array(
'full' => array( 'default' => array(
'label' => 'hidden', 'label' => 'hidden',
'type' => 'text_default',
'weight' => 0,
), ),
), ),
); );
......
...@@ -108,6 +108,7 @@ function comment_entity_info() { ...@@ -108,6 +108,7 @@ function comment_entity_info() {
'view modes' => array( 'view modes' => array(
'full' => array( 'full' => array(
'label' => t('Full comment'), 'label' => t('Full comment'),
'custom settings' => FALSE,
), ),
), ),
'static cache' => FALSE, 'static cache' => FALSE,
...@@ -165,15 +166,17 @@ function comment_field_extra_fields() { ...@@ -165,15 +166,17 @@ function comment_field_extra_fields() {
foreach (node_type_get_types() as $type) { foreach (node_type_get_types() as $type) {
if (variable_get('comment_subject_field_' . $type->type, 1) == 1) { if (variable_get('comment_subject_field_' . $type->type, 1) == 1) {
$return['comment']['comment_node_' . $type->type] = array( $return['comment']['comment_node_' . $type->type] = array(
'author' => array( 'form' => array(
'label' => t('Author'), 'author' => array(
'description' => t('Author textfield'), 'label' => t('Author'),
'weight' => -2, 'description' => t('Author textfield'),
), 'weight' => -2,
'title' => array( ),
'label' => t('Subject'), 'title' => array(
'description' => t('Subject textfield'), 'label' => t('Subject'),
'weight' => -1, 'description' => t('Subject textfield'),
'weight' => -1,
),
), ),
); );
} }
...@@ -362,10 +365,11 @@ function _comment_body_field_instance_create($info) { ...@@ -362,10 +365,11 @@ function _comment_body_field_instance_create($info) {
'bundle' => 'comment_node_' . $info->type, 'bundle' => 'comment_node_' . $info->type,
'settings' => array('text_processing' => 1), 'settings' => array('text_processing' => 1),
'required' => TRUE, 'required' => TRUE,
// Hides field label by default.
'display' => array( 'display' => array(
'full' => array( 'default' => array(
'label' => 'hidden', 'label' => 'hidden',
'type' => 'text_default',
'weight' => 0,
), ),
), ),
); );
......
...@@ -7,55 +7,59 @@ ...@@ -7,55 +7,59 @@
*/ */
/** /**
* Expose "pseudo-field" components on fieldable entities. * Exposes "pseudo-field" components on fieldable entities.
* *
* Field UI's 'Manage fields' page lets users re-order fields, but also * Field UI's "Manage fields" and "Manage display" pages let users re-order
* non-field components. For nodes, these include the title, menu settings, and * fields, but also non-field components. For nodes, these include the title,
* other elements exposed by contributed modules through hook_form() and * poll choices, and other elements exposed by modules through hook_form() or
* hook_form_alter(). * hook_form_alter().
* *
* Fieldable entities or contributed modules that want to have their components * Fieldable entities or modules that want to have their components supported
* supported should expose them using this hook, and use * should expose them using this hook. The user-defined settings (weight,
* field_attach_extra_weight() to retrieve the user-defined weight when * visibility) are automatically applied on rendered forms and displayed
* inserting the component. * entities in a #pre_render callback added by field_attach_form() and
* field_attach_view().
*
* @see _field_extra_fields_pre_render()
* @see hook_field_extra_fields_alter()
* *
* @return * @return
* A nested array of 'pseudo-field' components. Each list is nested within the * A nested array of 'pseudo-field' components. Each list is nested within
* field bundle to which those components apply. The keys are the name of the * the following keys: entity type, bundle name, context (either 'form' or
* element as it appears in the form structure. The values are arrays with the * 'display'). The keys are the name of the elements as appearing in the
* following key/value pairs: * renderable array (either the entity form or the displayed entity). The
* value is an associative array:
* - label: The human readable name of the component. * - label: The human readable name of the component.
* - description: A short description of the component contents. * - description: A short description of the component contents.
* - weight: The default weight of the element. * - 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() { function hook_field_extra_fields() {
$extra = array(); $extra['node']['poll'] = array(
'form' => array(
foreach (node_type_get_types() as $bundle) { 'choice_wrapper' => array(
if ($type->has_title) { 'label' => t('Poll choices'),
$extra['node'][$bundle]['title'] = array( 'description' => t('Poll choices'),
'label' => $type->title_label, 'weight' => -4,
'description' => t('Node module element.'), ),
'weight' => -5, 'settings' => array(
); 'label' => t('Poll settings'),
} 'description' => t('Poll module settings'),
} 'weight' => -3,
if (module_exists('poll')) { ),
$extra['node']['poll']['choice_wrapper'] = array( ),
'label' => t('Poll choices'), 'display' => array(
'description' => t('Poll module choices.'), 'poll_view_voting' => array(
'weight' => -4, 'label' => t('Poll vote'),
); 'description' => t('Poll vote'),
$extra['node']['poll']['settings'] = array( 'weight' => 0,
'label' => t('Poll settings'), ),
'description' => t('Poll module settings.'), 'poll_view_results' => array(
'weight' => -3, 'label' => t('Poll results'),
); 'description' => t('Poll results'),
} 'weight' => 0,
),
)
);
return $extra; return $extra;
} }
...@@ -1651,6 +1655,91 @@ function hook_field_storage_pre_query($field_name, $conditions, $options, &$skip ...@@ -1651,6 +1655,91 @@ function hook_field_storage_pre_query($field_name, $conditions, $options, &$skip
// @todo Needs function body. // @todo Needs function body.
} }
/**
* Alters the display settings of a field before it gets displayed.
*
* Note that instead of hook_field_display_alter(), which is called for all
* fields on all entity types, hook_field_display_ENTITY_TYPE_alter() may be
* used to alter display settings for fields on a specific entity type only.
*
* This hook is called once per field per displayed entity. If the result of the
* hook involves reading from the database, it is highly recommended to
* statically cache the information.
*
* @param $display
* The display settings that will be used to display the field values, as
* found in the 'display' key of $instance definitions.
* @param $context
* An associative array containing:
* - entity_type: The entity type; e.g. 'node' or 'user'.
* - field: The field being rendered.
* - instance: The instance being rendered.
* - view_mode: The view mode, e.g. 'full', 'teaser'...
*
* @see hook_field_display_ENTITY_TYPE_alter()
*/
function hook_field_display_alter(&$display, $context) {
// Leave field labels out of the search index.
// Note: The check against $context['entity_type'] == 'node' could be avoided
// by using hook_field_display_node_alter() instead of
// hook_field_display_alter(), resulting in less function calls when
// rendering non-node entities.
if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
$display['label'] = 'hidden';
}
}
/**
* Alters the display settings of a field on a given entity type before it gets displayed.
*
* Modules can implement hook_field_display_ENTITY_TYPE_alter() to alter display
* settings for fields on a specific entity type, rather than implementing
* hook_field_display_alter().
*
* This hook is called once per field per displayed entity. If the result of the
* hook involves reading from the database, it is highly recommended to
* statically cache the information.
*
* @param $display
* The display settings that will be used to display the field values, as
* found in the 'display' key of $instance definitions.
* @param $context
* An associative array containing:
* - entity_type: The entity type; e.g. 'node' or 'user'.
* - field: The field being rendered.
* - instance: The instance being rendered.
* - view_mode: The view mode, e.g. 'full', 'teaser'...
*
* @see hook_field_display_alter()
*/
function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
// Leave field labels out of the search index.
if ($context['view_mode'] == 'search_index') {
$display['label'] = 'hidden';
}
}
/**
* Alters the display settings of pseudo-fields before an entity is displayed.
*
* This hook is called once per displayed entity. If the result of the hook
* involves reading from the database, it is highly recommended to statically
* cache the information.
*
* @param $displays
* An array of display settings for the pseudo-fields in the entity, keyed
* by pseudo-field names.
* @param $context
* An associative array containing:
* - entity_type: The entity type; e.g. 'node' or 'user'.
* - bundle: The bundle name.
* - view_mode: The view mode, e.g. 'full', 'teaser'...
*/
function hook_field_extra_fields_display_alter(&$displays, $context) {
if ($context['entity_type'] == 'taxonomy_term' && $context['view_mode'] == 'full') {
$displays['description']['visibility'] = FALSE;
}
}
/** /**
* @} End of "ingroup field_storage" * @} End of "ingroup field_storage"
*/ */
......
...@@ -552,8 +552,9 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod ...@@ -552,8 +552,9 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod
// Add custom weight handling. // Add custom weight handling.
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$form['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; $form['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css';
$form['#pre_render'][] = '_field_extra_weights_pre_render'; $form['#pre_render'][] = '_field_extra_fields_pre_render';
$form['#extra_fields'] = field_extra_fields($entity_type, $bundle); $form['#entity_type'] = $entity_type;
$form['#bundle'] = $bundle;
// Save the original entity to allow later re-use. // Save the original entity to allow later re-use.
$form_state['entity'] = $entity; $form_state['entity'] = $entity;
...@@ -1185,7 +1186,7 @@ function field_attach_query_revisions($field_id, $conditions, $options = array() ...@@ -1185,7 +1186,7 @@ function field_attach_query_revisions($field_id, $conditions, $options = array()
* @param $view_mode * @param $view_mode
* View mode, e.g. 'full', 'teaser'... * 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 // To ensure hooks are only run once per entity, only process items without
// the _field_view_prepared flag. // the _field_view_prepared flag.
// @todo: resolve this more generally for both entity and field level hooks. // @todo: resolve this more generally for both entity and field level hooks.
...@@ -1250,7 +1251,7 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode = 'full') ...@@ -1250,7 +1251,7 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode = 'full')
* @return * @return
* A renderable array for the field values. * 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 // Determine the actual language to display for each field, given the
// languages available in the field data. // languages available in the field data.
$display_language = field_language($entity_type, $entity, NULL, $langcode); $display_language = field_language($entity_type, $entity, NULL, $langcode);
...@@ -1262,8 +1263,9 @@ function field_attach_view($entity_type, $entity, $view_mode = 'full', $langcode ...@@ -1262,8 +1263,9 @@ function field_attach_view($entity_type, $entity, $view_mode = 'full', $langcode
// Add custom weight handling. // Add custom weight handling.
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$output['#pre_render'][] = '_field_extra_weights_pre_render'; $output['#pre_render'][] = '_field_extra_fields_pre_render';
$output['#extra_fields'] = field_extra_fields($entity_type, $bundle); $output['#entity_type'] = $entity_type;
$output['#bundle'] = $bundle;
// Include CSS styles. // Include CSS styles.
$output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css'; $output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css';
...@@ -1381,6 +1383,14 @@ function field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { ...@@ -1381,6 +1383,14 @@ function field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
// Clear the cache. // Clear the cache.
field_cache_clear(); 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. // Let other modules act on renaming the bundle.
module_invoke_all('field_attach_rename_bundle', $entity_type, $bundle_old, $bundle_new); module_invoke_all('field_attach_rename_bundle', $entity_type, $bundle_old, $bundle_new);
} }
...@@ -1410,6 +1420,13 @@ function field_attach_delete_bundle($entity_type, $bundle) { ...@@ -1410,6 +1420,13 @@ function field_attach_delete_bundle($entity_type, $bundle) {
// Clear the cache. // Clear the cache.
field_cache_clear(); 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. // Let other modules act on deleting the bundle.
module_invoke_all('field_attach_delete_bundle', $entity_type, $bundle, $instances); module_invoke_all('field_attach_delete_bundle', $entity_type, $bundle, $instances);
} }
......
...@@ -31,8 +31,7 @@ ...@@ -31,8 +31,7 @@
* field_attach_load() then loads the 'subtitle' and 'photo' fields * field_attach_load() then loads the 'subtitle' and 'photo' fields
* because they are both attached to the 'node' bundle 'article'. * because they are both attached to the 'node' bundle 'article'.
* *
* Field definitions are (currently) represented as an array of key/value * Field definitions are represented as an array of key/value pairs.
* pairs. The array properties are:
* *
* @param array $field: * @param array $field:
* - id (integer, read-only) * - id (integer, read-only)
...@@ -96,8 +95,7 @@ ...@@ -96,8 +95,7 @@
* A sub-array of key/value pairs of settings. Each storage backend * A sub-array of key/value pairs of settings. Each storage backend
* defines and documents its own settings. * defines and documents its own settings.
* *
* Field Instance definitions are (currently) represented as an array of * Field instance definitions are represented as an array of key/value pairs.
* key/value pairs. The array properties are:
* *
* @param array $instance: * @param array $instance:
* - id (integer, read-only) * - id (integer, read-only)
...@@ -154,11 +152,20 @@ ...@@ -154,11 +152,20 @@
* - module (string, read-only) * - module (string, read-only)
* The name of the module that implements the widget type. * The name of the module that implements the widget type.
* - display (array) * - display (array)
* A sub-array of key/value pairs identifying view modes and the way the * A sub-array of key/value pairs identifying the way field values should
* field values should be displayed in each mode. * be displayed in each of the entity type's view modes, plus the 'default'
* - full (array) * mode. For each view mode, Field UI lets site administrators define
* A sub-array of key/value pairs of the display options to be used * whether they want to use a dedicated set of display options or the
* when the field is being displayed in the "full" view mode. * 'default' options to reduce the number of displays to maintain as they
* add new fields. For nodes, on a fresh install, only the 'teaser' view
* mode is configured to use custom display options, all other view modes
* defined use the 'default' options by default. When programmatically
* adding field instances on nodes, it is therefore recommended to at least
* specify display options for 'default' and 'teaser'.
* - default (array)
* A sub-array of key/value pairs describing the display options to be
* used when the field is being displayed in view modes that are not
* configured to use dedicated display options.
* - label (string) * - label (string)
* Position of the label. 'inline', 'above' and 'hidden' are the * Position of the label. 'inline', 'above' and 'hidden' are the
* values recognized by the default 'field' theme implementation. * values recognized by the default 'field' theme implementation.
...@@ -172,7 +179,11 @@ ...@@ -172,7 +179,11 @@
* displayed in this view mode. * displayed in this view mode.
* - module (string, read-only) * - module (string, read-only)
* The name of the module which implements the display formatter. * The name of the module which implements the display formatter.
* - teaser * - some_mode
* A sub-array of key/value pairs describing the display options to be
* used when the field is being displayed in the 'some_mode' view mode.
* Those options will only be actually applied at run time if the view
* mode is not configured to use default settings for this bundle.
* - ... * - ...
* - other_mode * - other_mode
* - ... * - ...
...@@ -607,12 +618,16 @@ function field_delete_field($field_name) { ...@@ -607,12 +618,16 @@ function field_delete_field($field_name) {
* - settings: each omitted setting is given the default value specified in * - settings: each omitted setting is given the default value specified in
* hook_field_widget_info(). * hook_field_widget_info().
* - display: * - display:
* Settings for the 'full' view mode will be added, and each view mode * Settings for the 'default' view mode will be added if not present, and
* will be completed with the following default values: * each view mode in the definition will be completed with the following
* default values:
* - label: 'above' * - label: 'above'
* - type: the default formatter specified in hook_field_info(). * - type: the default formatter specified in hook_field_info().
* - settings: each omitted setting is given the default value specified in * - settings: each omitted setting is given the default value specified in
* hook_field_formatter_info(). * hook_field_formatter_info().
* View modes not present in the definition are left empty, and the field
* will not be displayed in this mode.
*
* @return * @return
* The $instance array with the id property filled in. * The $instance array with the id property filled in.
* @throw * @throw
...@@ -730,7 +745,6 @@ function _field_write_instance($instance, $update = FALSE) { ...@@ -730,7 +745,6 @@ function _field_write_instance($instance, $update = FALSE) {
'required' => FALSE, 'required' => FALSE,
'label' => $instance['field_name'], 'label' => $instance['field_name'],
'description' => '', 'description' => '',
'weight' => 0,
'deleted' => 0, 'deleted' => 0,
); );
...@@ -742,30 +756,55 @@ function _field_write_instance($instance, $update = FALSE) { ...@@ -742,30 +756,55 @@ function _field_write_instance($instance, $update = FALSE) {
// TODO: what if no 'default_widget' specified ? // TODO: what if no 'default_widget' specified ?
'type' => $field_type['default_widget'], 'type' => $field_type['default_widget'],
'settings' => array(), '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. // Check widget module.
$widget_type = field_info_widget_types($instance['widget']['type']); $widget_type = field_info_widget_types($instance['widget']['type']);
$instance['widget']['module'] = $widget_type['module']; $instance['widget']['module'] = $widget_type['module'];
$instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']); $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( $instance['display'] += array(
'full' => array(), 'default' => array(),
); );
// Set default display settings for each view mode.
foreach ($instance['display'] as $view_mode => $display) { foreach ($instance['display'] as $view_mode => $display) {
$instance['display'][$view_mode] += array( $display += array(
'label' => 'above', 'label' => 'above',
// TODO: what if no 'default_formatter' specified ? 'type' => isset($field_type['default_formatter']) ? $field_type['default_formatter'] : 'hidden',
'type' => $field_type['default_formatter'],
'settings' => array(), 'settings' => array(),
'weight' => 0,
); );
$formatter_type = field_info_formatter_types($instance['display'][$view_mode]['type']); if ($display['type'] != 'hidden') {
// TODO : 'hidden' will raise PHP warnings. $formatter_type = field_info_formatter_types($display['type']);
$instance['display'][$view_mode]['module'] = $formatter_type['module']; $display['module'] = $formatter_type['module'];
$instance['display'][$view_mode]['settings'] += field_info_formatter_settings($instance['display'][$view_mode]['type']); $display['settings'] += field_info_formatter_settings($display['type']);
}
// If no weight specified, make sure the field sinks at the bottom.
if (!isset($display['weight'])) {
$weights = array();
foreach (field_info_instances($instance['entity_type'], $instance['bundle']) as $existing_instance) {
if ($instance['field_name'] != $existing_instance['field_name']) {
$weights[] = $existing_instance['display'][$view_mode]['weight'];
}
}
foreach (field_extra_fields($instance['entity_type'], $instance['bundle'], 'display') as $extra) {
$weights[] = $extra['display'][$view_mode]['weight'];
}
$display['weight'] = $weights ? max($weights) + 1 : 0;
}
$instance['display'][$view_mode] = $display;
} }
// The serialized 'data' column contains everything from $instance that does // The serialized 'data' column contains everything from $instance that does
......
...@@ -132,7 +132,10 @@ function field_default_prepare_view($entity_type, $entities, $field, $instances, ...@@ -132,7 +132,10 @@ function field_default_prepare_view($entity_type, $entities, $field, $instances,
// Group entities, instances and items by formatter module. // Group entities, instances and items by formatter module.
$modules = array(); $modules = array();
foreach ($instances as $id => $instance) { foreach ($instances as $id => $instance) {
$display = is_string($display) ? $instance