diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php index c12e4db..4d634ed 100644 --- a/core/includes/entity.api.php +++ b/core/includes/entity.api.php @@ -279,6 +279,28 @@ function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInter } /** + * Alters the settings used for displaying an entity. + * + * @param \Drupal\field\Plugin\Core\Entity\EntityDisplay $display + * The entity_display object that will be used to display the entity components. + * @param array $context + * An associative array containing: + * - entity_type: The entity type, e.g., 'node' or 'user'. + * - bundle: The bundle, e.g., 'page' or 'article'. + * - view_mode: The view mode, e.g. 'full', 'teaser'... + */ +function hook_entity_display_alter(Drupal\field\Plugin\Core\Entity\EntityDisplay $display, array $context) { + // Leave field labels out of the search index. + if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') { + foreach ($display->content as $name => &$properties) { + if (isset($properties['label'])) { + $properties['label'] = 'hidden'; + } + } + } +} + +/** * Define custom entity properties. * * @param string $entity_type diff --git a/core/includes/entity.inc b/core/includes/entity.inc index a866864..b1a7a6c 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -563,6 +563,59 @@ function entity_view_multiple(array $entities, $view_mode, $langcode = NULL) { return entity_render_controller(reset($entities)->entityType())->viewMultiple($entities, $view_mode, $langcode); } + +/** + * Returns an entity_display object for the view mode. + * + * The function reads the object from the current configuration, or returns a + * new object if the display is not configured yet. + * + * @param string $entity_type + * Machine readable name of the content entity. + * @param string $bundle + * Machine readable name of the content entity bundle. + * @param string $view_mode + * Machine readable name of the content entity view_mode. + * + * @return \Drupal\field\Plugin\Core\Entity\EntityDisplay + */ +function entity_get_display($entity_type, $bundle, $view_mode) { + // Try loading the display from configuration. + if ($display = entity_load('entity_display', $entity_type . '.' . $bundle . '.' . $view_mode)) { + return $display; + } + + // If not found, create a fresh display object. + return entity_create('entity_display', array( + 'targetEntityType' => $entity_type, + 'bundle' => $bundle, + 'viewMode' => $view_mode, + )); +} + +/** + * Adjusts weights and visibility of components in displayed entities. + * + * This is used as a #pre_render callback. + */ +function _entity_view_pre_render($elements) { + $display = $elements['#entity_display']; + + $extra_fields = field_info_extra_fields($display->targetEntityType, $display->bundle, 'display'); + foreach (array_keys($extra_fields) as $name) { + if (isset($elements[$name]) && (!isset($elements[$name]['#access']) || $elements[$name]['#access'])) { + if ($options = $display->getComponent($name, FALSE)) { + $elements[$name]['#weight'] = $options['weight']; + } + else { + $elements[$name]['#access'] = FALSE; + } + } + } + + return $elements; +} + /** * Returns the entity query object for this entity type. * diff --git a/core/lib/Drupal/Core/Entity/EntityRenderController.php b/core/lib/Drupal/Core/Entity/EntityRenderController.php index 2cd0578..8c5606f 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderController.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderController.php @@ -30,37 +30,69 @@ public function buildContent(array $entities = array(), $view_mode = 'full', $la // Allow modules to change the view mode. $context = array('langcode' => $langcode); - $prepare = array(); - foreach ($entities as $key => $entity) { + $view_modes = array(); + $displays = array(); + + foreach ($entities as $entity) { // Remove previously built content, if exists. $entity->content = array(); drupal_alter('entity_view_mode', $view_mode, $entity, $context); $entity->content['#view_mode'] = $view_mode; - $prepare[$view_mode][$key] = $entity; + $view_modes[$view_mode][$entity->id()] = $entity; + + $bundle = $entity->bundle(); + + // Load the corresponding display settings if not stored yet. + if (!isset($displays[$view_mode][$bundle])) { + // If the view mode is not configured to use dedicated settings, + // rendering is done using 'default'. + $actual_mode = field_get_actual_view_mode($this->entityType, $bundle, $view_mode); + + // Get the display object for this bundle and view mode. + $display = entity_get_display($this->entityType, $bundle, $actual_mode); + $display->originalViewMode = $view_mode; + + // Let modules alter the display. + // @todo If config entities get a static cache at some point, the + // objects should be cloned before running drupal_alter(). + $display_context = array( + 'entity_type' => $this->entityType, + 'bundle' => $bundle, + 'view_mode' => $view_mode, + ); + drupal_alter('entity_display', $display, $display_context); + + $displays[$view_mode][$bundle] = $display; + } + + // Assigning weights to 'extra fields' is done in a pre_render callback. + $entity->content['#pre_render'] = array('_entity_view_pre_render'); + $entity->content['#entity_display'] = $displays[$view_mode][$bundle]; } // Prepare and build field content, grouped by view mode. - foreach ($prepare as $view_mode => $prepare_entities) { - $call = array(); + foreach ($view_modes as $view_mode => $view_mode_entities) { + $call_prepare = array(); // To ensure hooks are only run once per entity, check for an - // entity_view_prepared flag and only process items without it. - foreach ($prepare_entities as $entity) { - if (empty($entity->entity_view_prepared)) { + // entity_view_prepared flag and only process relevant entities. + foreach ($view_mode_entities as $entity) { + if (empty($entity->entity_view_prepared) || $entity->entity_view_prepared != $view_mode) { // Add this entity to the items to be prepared. - $call[$entity->id()] = $entity; + $call_prepare[$entity->id()] = $entity; - // Mark this item as prepared. - $entity->entity_view_prepared = TRUE; + // Mark this item as prepared for this view mode. + $entity->entity_view_prepared = $view_mode; } } - if (!empty($call)) { - field_attach_prepare_view($this->entityType, $call, $view_mode, $langcode); - module_invoke_all('entity_prepare_view', $call, $this->entityType); + if (!empty($call_prepare)) { + field_attach_prepare_view($this->entityType, $call_prepare, $displays[$view_mode], $langcode); + module_invoke_all('entity_prepare_view', $call_prepare, $this->entityType); } - foreach ($entities as $entity) { - $entity->content += field_attach_view($this->entityType, $entity, $view_mode, $langcode); + + foreach ($view_mode_entities as $entity) { + $entity->content += field_attach_view($this->entityType, $entity, $displays[$view_mode][$entity->bundle()], $langcode); } } } diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index ab757a9..14d6d3c 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -376,15 +376,15 @@ function _comment_body_field_create($info) { 'bundle' => 'comment_node_' . $info->type, 'settings' => array('text_processing' => 1), 'required' => TRUE, - 'display' => array( - 'default' => array( - 'label' => 'hidden', - 'type' => 'text_default', - 'weight' => 0, - ), - ), ); field_create_instance($instance); + entity_get_display('comment', 'comment_node_' . $info->type, 'default') + ->setComponent('comment_body', array( + 'label' => 'hidden', + 'type' => 'text_default', + 'weight' => 0, + )) + ->save(); } } diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index 22a9ed7..3aa0cb4 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -1942,94 +1942,6 @@ function hook_field_info_max_weight($entity_type, $bundle, $context) { } /** - * Alters the display settings of a field before it is 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 array $display_properties - * The display settings that will be used to display the field values, as - * found in the 'display' key of $instance definitions. - * @param array $context - * An associative array containing: - * - entity_type: The entity type, e.g., 'node' or 'user'. - * - bundle: The bundle, e.g., 'page' or 'article'. - * - 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(array &$display_properties, array $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_properties['label'] = 'hidden'; - } -} - -/** - * Alters the display settings of a field before it is 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 array $display_properties - * The display settings that will be used to display the field values, as - * found in the 'display' key of $instance definitions. - * @param array $context - * An associative array containing: - * - entity_type: The entity type, e.g., 'node' or 'user'. - * - bundle: The bundle, e.g., 'page' or 'article'. - * - 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(array &$display_properties, array $context) { - // Leave field labels out of the search index. - if ($context['view_mode'] == 'search_index') { - $display_properties['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']['visible'] = FALSE; - } -} - -/** * Alters the widget properties of a field instance on a given entity type * before it gets displayed. * diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc index 29ebd3b..b5107b8 100644 --- a/core/modules/field/field.attach.inc +++ b/core/modules/field/field.attach.inc @@ -7,6 +7,7 @@ use Drupal\field\FieldValidationException; use Drupal\Core\Entity\EntityInterface; +use Drupal\field\Plugin\Core\Entity\EntityDisplay; /** * @defgroup field_storage Field Storage API @@ -715,26 +716,6 @@ function _field_invoke_widget_target() { } /** - * Defines a 'target function' for field_invoke_method(). - * - * Used to invoke methods on an instance's formatter. - * - * @param mixed $display - * Can be either: - * - The name of a view mode. - * - An array of display properties, as found in - * $instance['display'][$view_mode]. - * - * @return callable $target_function - * A 'target function' for field_invoke_method(). - */ -function _field_invoke_formatter_target($display) { - return function ($instance) use ($display) { - return $instance->getFormatter($display); - }; -} - -/** * Adds form elements for all fields for an entity to a form structure. * * The form elements for the entity's fields are added by reference as direct @@ -1361,10 +1342,10 @@ function field_attach_delete_revision($entity_type, $entity) { * * @param $entity_type * The type of entities in $entities; e.g. 'node' or 'user'. - * @param $entities + * @param array $entities * An array of entities, keyed by entity ID. - * @param $view_mode - * View mode, e.g. 'full', 'teaser'... + * @param array $displays + * An array of entity display objects, keyed by bundle name. * @param $langcode * (Optional) The language the field values are to be shown in. If no language * is provided the current language is used. @@ -1372,7 +1353,7 @@ function field_attach_delete_revision($entity_type, $entity) { * An associative array of additional options. See field_invoke_method() for * details. */ -function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcode = NULL, array $options = array()) { +function field_attach_prepare_view($entity_type, array $entities, array $displays, $langcode = NULL, array $options = array()) { $options['langcode'] = array(); // To ensure hooks are only run once per entity, only process items without @@ -1396,8 +1377,15 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod $null = NULL; // First let the field types do their preparation. _field_invoke_multiple('prepare_view', $entity_type, $prepare, $null, $null, $options); - // Then let the formatters do their own specific massaging. - field_invoke_method_multiple('prepareView', _field_invoke_formatter_target($view_mode), $prepare, $view_mode, $null, $options); + // Then let the formatters do their own specific massaging. For each + // instance, call the prepareView() method on the formatter object handed by + // the entity display. + $target_function = function ($instance) use ($displays) { + if (isset($displays[$instance['bundle']])) { + return $displays[$instance['bundle']]->getFormatter($instance['field_name']); + } + }; + field_invoke_method_multiple('prepareView', $target_function, $prepare, $null, $null, $options); } /** @@ -1437,32 +1425,33 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod * The type of $entity; e.g. 'node' or 'user'. * @param Drupal\Core\Entity\EntityInterface $entity * The entity with fields to render. - * @param $view_mode - * View mode, e.g. 'full', 'teaser'... + * @param \Drupal\field\Plugin\Core\Entity\EntityDisplay $display + * The entity display object. * @param $langcode * The language the field values are to be shown in. If no language is * provided the current language is used. * @param array $options * An associative array of additional options. See field_invoke_method() for * details. - * @return + + * @return array * A renderable array for the field values. */ -function field_attach_view($entity_type, EntityInterface $entity, $view_mode, $langcode = NULL, array $options = array()) { +function field_attach_view($entity_type, EntityInterface $entity, EntityDisplay $display, $langcode = NULL, array $options = array()) { // Determine the actual language code to display for each field, given the // language codes available in the field data. $options['langcode'] = field_language($entity_type, $entity, NULL, $langcode); - // Invoke field_default_view(). + // For each instance, call the view() method on the formatter object handed + // by the entity display. + $target_function = function ($instance) use ($display) { + return $display->getFormatter($instance['field_name']); + }; $null = NULL; - $output = field_invoke_method('view', _field_invoke_formatter_target($view_mode), $entity, $view_mode, $null, $options); - - // Add custom weight handling. - $output['#pre_render'][] = '_field_extra_fields_pre_render'; - $output['#entity_type'] = $entity_type; - $output['#bundle'] = $entity->bundle(); + $output = field_invoke_method('view', $target_function, $entity, $null, $null, $options); // Let other modules alter the renderable array. + $view_mode = $display->originalViewMode; $context = array( 'entity_type' => $entity_type, 'entity' => $entity, diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc index ac66c77..a62ad2e 100644 --- a/core/modules/field/field.crud.inc +++ b/core/modules/field/field.crud.inc @@ -588,7 +588,6 @@ function _field_write_instance(&$instance, $update = FALSE) { // Set defaults. $instance += array( 'settings' => array(), - 'display' => array(), 'widget' => array(), 'required' => FALSE, 'label' => $instance['field_name'], @@ -615,30 +614,6 @@ function _field_write_instance(&$instance, $update = FALSE) { $instance['widget']['module'] = $widget_type['module']; $instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']); - // 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( - 'default' => array(), - ); - foreach ($instance['display'] as $view_mode => $display) { - $display += array( - 'label' => 'above', - 'type' => isset($field_type['default_formatter']) ? $field_type['default_formatter'] : 'hidden', - 'settings' => array(), - ); - 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'])) { - $max_weight = field_info_max_weight($instance['entity_type'], $instance['bundle'], $view_mode); - $display['weight'] = isset($max_weight) ? $max_weight + 1 : 0; - } - $instance['display'][$view_mode] = $display; - } - // The serialized 'data' column contains everything from $instance that does // not have its own column and is not automatically populated when the // instance is read. diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc index 5fab889..688d611 100644 --- a/core/modules/field/field.info.inc +++ b/core/modules/field/field.info.inc @@ -522,7 +522,7 @@ function field_info_extra_fields($entity_type, $bundle, $context) { } /** - * Returns the maximum weight of all the components in an entity. + * Returns the maximum weight of all the components in a form entity. * * This includes fields, 'extra_fields', and other components added by * third-party modules (e.g. field_group). @@ -531,33 +531,25 @@ function field_info_extra_fields($entity_type, $bundle, $context) { * The type of entity; e.g. 'node' or 'user'. * @param $bundle * The bundle name. - * @param $context - * The context for which the maximum weight is requested. Either 'form', or - * the name of a view mode. * * @return * The maximum weight of the entity's components, or NULL if no components * were found. */ -function field_info_max_weight($entity_type, $bundle, $context) { +function field_info_max_weight($entity_type, $bundle) { $weights = array(); // Collect weights for fields. foreach (field_info_instances($entity_type, $bundle) as $instance) { - if ($context == 'form') { - $weights[] = $instance['widget']['weight']; - } - elseif (isset($instance['display'][$context]['weight'])) { - $weights[] = $instance['display'][$context]['weight']; - } + $weights[] = $instance['widget']['weight']; } // Collect weights for extra fields. - foreach (field_info_extra_fields($entity_type, $bundle, $context) as $extra) { + foreach (field_info_extra_fields($entity_type, $bundle, 'form') as $extra) { $weights[] = $extra['weight']; } // Let other modules feedback about their own additions. - $weights = array_merge($weights, module_invoke_all('field_info_max_weight', $entity_type, $bundle, $context)); + $weights = array_merge($weights, module_invoke_all('field_info_max_weight', $entity_type, $bundle, 'form')); $max_weight = $weights ? max($weights) : NULL; return $max_weight; diff --git a/core/modules/field/field.install b/core/modules/field/field.install index b88faa8..f2364c8 100644 --- a/core/modules/field/field.install +++ b/core/modules/field/field.install @@ -357,15 +357,6 @@ function _update_7000_field_create_instance($field, &$instance) { 'deleted' => 0, ); - // Merge in display defaults. - if (isset($instance['display'])) { - foreach ($instance['display'] as &$display) { - $display += array( - 'weight' => 0, - ); - } - } - // The serialized 'data' column contains everything from $instance that does // not have its own column and is not automatically populated when the // instance is read. @@ -403,6 +394,114 @@ function field_update_8001() { } /** + * Migrate all instance display settings to configuration. + * + * @ingroup config_upgrade + */ +function field_update_8002() { + global $conf; + + $displays = array(); + + $query = db_select('field_config_instance', 'fc')->fields('fc'); + foreach ($query->execute() as $record) { + // Unserialize the data array and start investigating the display key + // which holds the configuration of this instance for all view modes. + $data = unserialize($record->data); + + // @todo Somehow, user picture field has no display key, + // check with user picture field upgrade path. + if (!isset($data['display'])) { + continue; + } + + foreach ($data['display'] as $view_mode => $display_options) { + // Determine name and create initial entry in the $displays array if it + // does not exist yet. + $name = 'field.entity_display.' . $record->entity_type . '.' . $record->bundle . '.' . $view_mode; + if (!isset($displays[$name])) { + $displays[$name] = _field_update_8002_initialize_display($record->entity_type, $record->bundle, $view_mode); + } + + // The display object does not store hidden fields. + if ($display_options['type'] != 'hidden') { + // We do not need the 'module' key anymore. + unset($display_options['module']); + $displays[$name]['content'][$record->field_name] = $display_options; + } + } + + // Remove the 'display' key and save the record back into the table. + unset($data['display']); + db_update('field_config_instance') + ->condition('id', $record->id) + ->fields(array( + 'data' => serialize($data), + )) + ->execute(); + } + + // Migration of 'extra_fields' display settings. Avoid calling + // entity_get_info() by iterating directly on the global $conf array. + foreach ($conf as $variable_name => $variable_value) { + if (preg_match('/field_bundle_settings_(.*)__(.*)/', $variable_name, $matches)) { + $entity_type = $matches[1]; + $bundle = $matches[2]; + if (isset($variable_value['extra_fields']['display'])) { + foreach ($variable_value['extra_fields']['display'] as $field_name => $field_settings) { + foreach ($field_settings as $view_mode => $display_options) { + // Determine name and create initial entry in the $displays array + // if it does not exist yet. + $name = 'field.entity_display.' . $entity_type . '.' . $bundle . '.' . $view_mode; + if (!isset($displays[$name])) { + $displays[$name] = _field_update_8002_initialize_display($entity_type, $bundle, $view_mode); + } + + $displays[$name]['content'][$field_name]['visible'] = $display_options['visible']; + // The display object only stores the weght for 'visible' extra fields. + if ($display_options['visible']) { + $displays[$name]['content'][$field_name]['weight'] = $display_options['weight']; + } + + // Remove the old entry. + unset($variable_value['extra_fields']['display']); + variable_set($variable_name, $variable_value); + } + } + } + } + } + + // Write CMI files for the entity display objects. + foreach ($displays as $name => $configuration) { + config($name)->setData($configuration)->save(); + } +} + +/** + * Initializes an empty structure for an EntityDisplay raw CMI file. + * + * @param string $entity_type + * The entity type. + * @param string $bundle + * The bundle name. + * @param string $view_mode + * The view mode. + * + * @return array + * The raw structure. + */ +function _field_update_8002_initialize_display($entity_type, $bundle, $view_mode) { + return array( + 'id' => $entity_type . '.' . $bundle . '.' . $view_mode, + 'targetEntityType' => $entity_type, + 'bundle' => $bundle, + 'viewMode' => $view_mode, + 'content' => array(), + ); +} + +/** * @} End of "addtogroup updates-7.x-to-8.x". * The next series of updates should start at 9000. */ diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 686e3f5..5b61ed5 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -682,7 +682,6 @@ function field_bundle_settings($entity_type, $bundle, $settings = NULL) { ); $settings['extra_fields'] += array( 'form' => array(), - 'display' => array(), ); return $settings; @@ -725,7 +724,30 @@ function field_view_mode_settings($entity_type, $bundle) { } /** - * Returns the display settings to use for pseudo-fields in a given view mode. + * Returns the actual view mode to use for a bundle. + * + * Depending on the current configuration of the bundle for this view mode, the + * function returns either: + * - the value of the incoming $view_mode parameter, + * - or 'default'. + * + * @param string $entity_type + * The type of $entity; e.g., 'node' or 'user'. + * @param string $bundle + * The bundle name. + * @param string $view_mode + * The view mode. + * + * @return string + * The actual view mode to use for this entity type and bundle. + */ +function field_get_actual_view_mode($entity_type, $bundle, $view_mode) { + $view_mode_settings = field_view_mode_settings($entity_type, $bundle); + return (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default'); +} + +/** + * Returns the display options to use for pseudo-fields in a given view mode. * * @param $entity_type * The type of $entity; e.g., 'node' or 'user'. @@ -735,29 +757,20 @@ function field_view_mode_settings($entity_type, $bundle) { * The view mode. * * @return - * The display settings to be used when viewing the bundle's pseudo-fields. + * The display options to be used when viewing the bundle's pseudo-fields. */ function field_extra_fields_get_display($entity_type, $bundle, $view_mode) { - // Check whether the view mode uses custom display settings or the 'default' - // mode. - $view_mode_settings = field_view_mode_settings($entity_type, $bundle); - $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings'])) ? $view_mode : 'default'; + + $actual_mode = field_get_actual_view_mode($entity_type, $bundle, $view_mode); + $entity_display = entity_get_display($entity_type, $bundle, $actual_mode); $extra_fields = field_info_extra_fields($entity_type, $bundle, 'display'); - $displays = array(); + $options = array(); foreach ($extra_fields as $name => $value) { - $displays[$name] = $extra_fields[$name]['display'][$actual_mode]; + $options[$name] = $entity_display->getComponent($name); } - // Let modules alter the display settings. - $context = array( - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'view_mode' => $view_mode, - ); - drupal_alter('field_extra_fields_display', $displays, $context); - - return $displays; + return $options; } /** @@ -767,23 +780,10 @@ function _field_extra_fields_pre_render($elements) { $entity_type = $elements['#entity_type']; $bundle = $elements['#bundle']; - if (isset($elements['#type']) && $elements['#type'] == 'form') { - $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form'); - foreach ($extra_fields as $name => $settings) { - if (isset($elements[$name])) { - $elements[$name]['#weight'] = $settings['weight']; - } - } - } - elseif (isset($elements['#view_mode'])) { - $view_mode = $elements['#view_mode']; - $extra_fields = field_extra_fields_get_display($entity_type, $bundle, $view_mode); - foreach ($extra_fields as $name => $settings) { - if (isset($elements[$name])) { - $elements[$name]['#weight'] = $settings['weight']; - // Visibility: make sure we do not accidentally show a hidden element. - $elements[$name]['#access'] = isset($elements[$name]['#access']) ? ($elements[$name]['#access'] && $settings['visible']) : $settings['visible']; - } + $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form'); + foreach ($extra_fields as $name => $settings) { + if (isset($elements[$name])) { + $elements[$name]['#weight'] = $settings['weight']; } } @@ -902,15 +902,14 @@ function field_view_value($entity_type, $entity, $field_name, $item, $display = * key and the field data to display. * @param $field_name * The name of the field to display. - * @param $display + * @param $display_options * Can be either: * - The name of a view mode. The field will be displayed according to the * 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 '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: + * - An array of display options. The following key/value pairs are allowed: * - label: (string) Position of the label. The default 'field' theme * implementation supports the values 'inline', 'above' and 'hidden'. * Defaults to 'above'. @@ -933,51 +932,68 @@ function field_view_value($entity_type, $entity, $field_name, $item, $display = * * @see field_view_value() */ -function field_view_field($entity_type, $entity, $field_name, $display = array(), $langcode = NULL) { +function field_view_field($entity_type, $entity, $field_name, $display_options = array(), $langcode = NULL) { $output = array(); + $bundle = $entity->bundle(); - if ($instance = field_info_instance($entity_type, $field_name, $entity->bundle())) { - if (is_array($display) && empty($display['type'])) { - $field = field_info_field($instance['field_name']); - $field_type_info = field_info_field_types($field['type']); - $display['type'] = $field_type_info['default_formatter']; - } - if ($formatter = $instance->getFormatter($display)) { - $display_langcode = field_language($entity_type, $entity, $field_name, $langcode); - $items = $entity->{$field_name}[$display_langcode]; - - // Invoke prepare_view steps if needed. - if (empty($entity->_field_view_prepared)) { - $id = $entity->id(); - - // First let the field types do their preparation. - // @todo invoke hook_field_prepare_view() directly ? - $options = array('field_name' => $field_name, 'langcode' => $display_langcode); - $null = NULL; - _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options); - - // Then let the formatters do their own specific massaging. - $items_multi = array($id => $entity->{$field_name}[$display_langcode]); - $formatter->prepareView(array($id => $entity), $display_langcode, $items_multi); - $items = $items_multi[$id]; - } + // Return nothing if the field doesn't exist. + $instance = field_info_instance($entity_type, $field_name, $bundle); + if (!$instance) { + return $output; + } + + // Get the formatter object. + if (is_string($display_options)) { + $view_mode = $display_options; + // If the view mode is not configured to use dedicated settings, + // rendering is done using 'default'. + $actual_mode = field_get_actual_view_mode($entity_type, $entity->bundle(), $view_mode); + $formatter = entity_get_display($entity_type, $bundle, $actual_mode)->getFormatter($field_name); + } + else { + $view_mode = '_custom'; + $formatter = drupal_container()->get('plugin.manager.field.formatter')->getInstance(array( + 'instance' => $instance, + 'view_mode' => $view_mode, + 'configuration' => $display_options, + )); + } - // Build the renderable array. - $result = $formatter->view($entity, $display_langcode, $items); + if ($formatter) { + $display_langcode = field_language($entity_type, $entity, $field_name, $langcode); + $items = $entity->{$field_name}[$display_langcode]; - // Invoke hook_field_attach_view_alter() to let other modules alter the - // renderable array, as in a full field_attach_view() execution. - $context = array( - 'entity_type' => $entity_type, - 'entity' => $entity, - 'view_mode' => '_custom', - 'display' => $display, - ); - drupal_alter('field_attach_view', $result, $context); + // Invoke prepare_view steps if needed. + if (empty($entity->_field_view_prepared)) { + $id = $entity->id(); - if (isset($result[$field_name])) { - $output = $result[$field_name]; - } + // First let the field types do their preparation. + $options = array('field_name' => $field_name, 'langcode' => $display_langcode); + $null = NULL; + _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $null, $null, $options); + + // Then let the formatter do its own specific massaging. + $items_multi = array($id => $entity->{$field_name}[$display_langcode]); + $formatter->prepareView(array($id => $entity), $display_langcode, $items_multi); + $items = $items_multi[$id]; + } + + // Build the renderable array. + $result = $formatter->view($entity, $display_langcode, $items); + + // Invoke hook_field_attach_view_alter() to let other modules alter the + // renderable array, as in a full field_attach_view() execution. + $context = array( + 'entity_type' => $entity_type, + 'entity' => $entity, + 'view_mode' => $view_mode, + // @todo see http://drupal.org/node/990148 + 'display' => $display_options, + ); + drupal_alter('field_attach_view', $result, $context); + + if (isset($result[$field_name])) { + $output = $result[$field_name]; } } diff --git a/core/modules/field/lib/Drupal/field/EntityDisplayStorageController.php b/core/modules/field/lib/Drupal/field/EntityDisplayStorageController.php new file mode 100644 index 0000000..3a0ac84 --- /dev/null +++ b/core/modules/field/lib/Drupal/field/EntityDisplayStorageController.php @@ -0,0 +1,37 @@ +get($name); + } + return $properties; + } + +} diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php index 65aa26c..2f33c89 100644 --- a/core/modules/field/lib/Drupal/field/FieldInfo.php +++ b/core/modules/field/lib/Drupal/field/FieldInfo.php @@ -543,25 +543,7 @@ public function prepareExtraFields($extra_fields, $entity_type, $bundle) { } // Extra fields in displayed entities. - $data = $extra_fields['display']; - foreach ($extra_fields['display'] 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])) { - $field_data['display'][$view_mode] = $settings[$view_mode]; - } - else { - $field_data['display'][$view_mode] = array( - 'weight' => $field_data['weight'], - 'visible' => isset($field_data['visible']) ? $field_data['visible'] : TRUE, - ); - } - } - unset($field_data['weight']); - unset($field_data['visible']); - $result['display'][$name] = $field_data; - } + $result['display'] = $extra_fields['display']; return $result; } diff --git a/core/modules/field/lib/Drupal/field/FieldInstance.php b/core/modules/field/lib/Drupal/field/FieldInstance.php index a1e7a4f..ba6ea44 100644 --- a/core/modules/field/lib/Drupal/field/FieldInstance.php +++ b/core/modules/field/lib/Drupal/field/FieldInstance.php @@ -27,13 +27,6 @@ class FieldInstance implements \ArrayAccess { protected $widget; /** - * The formatter objects used for this instance, keyed by view mode. - * - * @var array - */ - protected $formatters; - - /** * Constructs a FieldInstance object. * * @param array $definition @@ -75,88 +68,6 @@ public function getWidget() { } /** - * Returns a Formatter plugin for the instance. - * - * @param mixed $display_properties - * Can be either: - * - The name of a view mode. - * - An array of display properties, as found in the 'display' entry of - * $instance definitions. - * - * @return Drupal\field\Plugin\Type\Formatter\FormatterInterface|null - * The Formatter plugin to be used for the instance, or NULL if the field - * is hidden. - */ - public function getFormatter($display_properties) { - if (is_string($display_properties)) { - // A view mode was provided. Switch to 'default' if the view mode is not - // configured to use dedicated settings. - $view_mode = $display_properties; - $view_mode_settings = field_view_mode_settings($this->definition['entity_type'], $this->definition['bundle']); - $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default'); - - if (isset($this->formatters[$actual_mode])) { - return $this->formatters[$actual_mode]; - } - - // Switch to 'hidden' if the instance has no properties for the view - // mode. - if (isset($this->definition['display'][$actual_mode])) { - $display_properties = $this->definition['display'][$actual_mode]; - } - else { - $display_properties = array( - 'type' => 'hidden', - 'settings' => array(), - 'label' => 'above', - 'weight' => 0, - ); - } - - // Let modules alter the widget properties. - $context = array( - 'entity_type' => $this->definition['entity_type'], - 'bundle' => $this->definition['bundle'], - 'field' => field_info_field($this->definition['field_name']), - 'instance' => $this, - 'view_mode' => $view_mode, - ); - drupal_alter(array('field_display', 'field_display_' . $this->definition['entity_type']), $display_properties, $context); - } - else { - // Arbitrary display settings. Make sure defaults are present. - $display_properties += array( - 'settings' => array(), - 'label' => 'above', - 'weight' => 0, - ); - $view_mode = '_custom_display'; - } - - if (!empty($display_properties['type']) && $display_properties['type'] != 'hidden') { - $options = array( - 'instance' => $this, - 'type' => $display_properties['type'], - 'settings' => $display_properties['settings'], - 'label' => $display_properties['label'], - 'weight' => $display_properties['weight'], - 'view_mode' => $view_mode, - ); - $formatter = drupal_container()->get('plugin.manager.field.formatter')->getInstance($options); - } - else { - $formatter = NULL; - } - - // Persist the object if we were not passed custom display settings. - if (isset($actual_mode)) { - $this->formatters[$actual_mode] = $formatter; - } - - return $formatter; - } - - /** * Implements ArrayAccess::offsetExists(). */ public function offsetExists($offset) { @@ -185,9 +96,6 @@ public function offsetSet($offset, $value) { if ($offset == 'widget') { unset($this->widget); } - if ($offset == 'display') { - unset($this->formatters); - } } /** @@ -201,9 +109,6 @@ public function offsetUnset($offset) { if ($offset == 'widget') { unset($this->widget); } - if ($offset == 'display') { - unset($this->formatters); - } } /** diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/EntityDisplay.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/EntityDisplay.php new file mode 100644 index 0000000..b7eb9e7 --- /dev/null +++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/EntityDisplay.php @@ -0,0 +1,286 @@ +originalViewMode = $this->viewMode; + } + + /** + * Overrides \Drupal\Core\Entity\Entity::id(). + */ + public function id() { + return $this->targetEntityType . "." . $this->bundle . "." . $this->viewMode; + } + + /** + * Overrides \Drupal\config\ConfigEntityBase::save(). + */ + public function save() { + // Build an id if none is set. + if (empty($this->id)) { + $this->id = $this->id(); + } + return parent::save(); + } + + /** + * Gets the display options set for a component. + * + * @param string $name + * The name of the component.. + * + * @return array|null + * The display options for the comppnent, or NULL if the component is not + * displayed. + */ + public function getComponent($name) { + // We always store 'extra fields', whether they are visible or hidden. + $extra_fields = field_info_extra_fields($this->targetEntityType, $this->bundle, 'display'); + if (isset($extra_fields[$name])) { + // If we have explicit settings, return an array or NULL depending on + // visibility. + if (isset($this->content[$name])) { + if ($this->content[$name]['visible']) { + return array( + 'weight' => $this->content[$name]['weight'], + ); + } + else { + return NULL; + } + } + + // If no explicit settings for the extra field, look at the default + // visibility in its definition. + $definition = $extra_fields[$name]; + if (!isset($definition['visible']) || $definition['visible'] == TRUE) { + return array( + 'weight' => $definition['weight'] + ); + } + else { + return NULL; + } + } + + if (isset($this->content[$name])) { + return $this->content[$name]; + } + } + + /** + * Sets the display options for a component. + * + * @param string $name + * The name of the component. + * @param array $options + * The display options. + * + * @return \Drupal\field\Plugin\Core\Entity\EntityDisplay + * The EntityDisplay object. + */ + public function setComponent($name, array $options = array()) { + // If no weight specified, make sure the field sinks at the bottom. + if (!isset($options['weight'])) { + $max = $this->getHighestWeight(); + $options['weight'] = isset($max) ? $max + 1 : 0; + } + + if ($instance = field_info_instance($this->targetEntityType, $name, $this->bundle)) { + $field = field_info_field($instance['field_name']); + $options = drupal_container()->get('plugin.manager.field.formatter')->prepareConfiguration($field['type'], $options); + + // Clear the persisted formatter, if any. + unset($this->formatters[$name]); + } + + // We always store 'extra fields', whether they are visible or hidden. + $extra_fields = field_info_extra_fields($this->targetEntityType, $this->bundle, 'display'); + if (isset($extra_fields[$name])) { + $options['visible'] = TRUE; + } + + $this->content[$name] = $options; + + return $this; + } + + /** + * Sets a component to be hidden. + * + * @param string $name + * The name of the component. + * + * @return \Drupal\field\Plugin\Core\Entity\EntityDisplay + * The EntityDisplay object. + */ + public function removeComponent($name) { + $extra_fields = field_info_extra_fields($this->targetEntityType, $this->bundle, 'display'); + if (isset($extra_fields[$name])) { + // 'Extra fields' are exposed in hooks and can appear at any given time. + // Therefore we store extra fields that are explicitly being hidden, so + // that we can differenciate with those that are simply not configured + // yet. + $this->content[$name] = array( + 'visible' => FALSE, + ); + } + else { + unset($this->content[$name]); + unset($this->formatters[$name]); + } + + return $this; + } + + /** + * Returns the highest weight of the components in the display. + * + * @return int|null + * The highest weight of the components in the display, or NULL if the + * display is empty. + */ + public function getHighestWeight() { + $weights = array(); + + // Collect weights for the components in the display. + foreach ($this->content as $options) { + if (isset($options['weight'])) { + $weights[] = $options['weight']; + } + } + + // Let other modules feedback about their own additions. + // @todo If "other components" (e.g field_groups) can live in the + // $entity_display, then this is not needed anymore. + $weights = array_merge($weights, module_invoke_all('field_info_max_weight', $this->targetEntityType, $this->bundle, $this->viewMode)); + + return $weights ? max($weights) : NULL; + } + + /** + * Returns the Formatter plugin for a field. + * + * @param string $field_name + * The field name. + * + * @return \Drupal\field\Plugin\Type\Formatter\FormatterInterface + * If the field is not hidden, the Formatter plugin to use for rendering + * it. + */ + public function getFormatter($field_name) { + if (isset($this->formatters[$field_name])) { + return $this->formatters[$field_name]; + } + + // Instanciate the formatter object from the stored display properties. + if ($configuration = $this->getComponent($field_name)) { + $instance = field_info_instance($this->targetEntityType, $field_name, $this->bundle); + $formatter = drupal_container()->get('plugin.manager.field.formatter')->getInstance(array( + 'instance' => $instance, + 'view_mode' => $this->originalViewMode, + // No need to prepare, defaults have been merged in setComponent(). + 'prepare' => FALSE, + 'configuration' => $configuration + )); + } + else { + $formatter = NULL; + } + + // Persist the formatter object. + $this->formatters[$field_name] = $formatter; + return $formatter; + } + +} diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php index 55d0e3f..551d056 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php @@ -11,8 +11,9 @@ use Drupal\Component\Plugin\Discovery\ProcessDecorator; use Drupal\Core\Plugin\Discovery\CacheDecorator; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; -use Drupal\field\Plugin\Type\Formatter\FormatterLegacyDiscoveryDecorator; use Drupal\Core\Plugin\Discovery\AlterDecorator; +use Drupal\field\Plugin\Type\Formatter\FormatterLegacyDiscoveryDecorator; +use Drupal\field\FieldInstance; /** * Plugin type manager for field formatters. @@ -42,31 +43,87 @@ public function __construct() { /** * Overrides PluginManagerBase::getInstance(). + * + * @param array $options + * An array with the following key/value pairs: + * - instance; (FieldInstance) The field instance. + * - view_mode: (string) The view mode. + * - prepare: (bool, optional) Whether default values should get merged in + * the 'configuration' array. Defaults to TRUE. + * - configuration: (array) the configuration for the formatter. The + * following key value pairs are allowed, and are all optional if + * 'prepare' is TRUE: + * - label: (string) Position of the label. The default 'field' theme + * implementation supports the values 'inline', 'above' and 'hidden'. + * Defaults to 'above'. + * - type: (string) The formatter to use. Defaults to the + * 'default_formatter' for the field type, specified in + * hook_field_info(). The default formatter will also be used if the + * requested formatter is not available. + * - settings: (array) Settings specific to the formatter. Each setting + * defaults to the default value specified in the formatter definition. + * - weight: (float) The weight to assign to the renderable element. + * Defaults to 0. + * + * @return \Drupal\field\Plugin\Type\Formatter\FormatterInterface + * A formatter object. */ public function getInstance(array $options) { + $configuration = $options['configuration']; $instance = $options['instance']; - $type = $options['type']; - - $definition = $this->getDefinition($type); $field = field_info_field($instance['field_name']); + // Fill in default configuration if needed. + if (!isset($options['prepare']) || $options['prepare'] == TRUE) { + $configuration = $this->prepareConfiguration($field['type'], $configuration); + } + + $plugin_id = $configuration['type']; + // Switch back to default formatter if either: // - $type_info doesn't exist (the widget type is unknown), // - the field type is not allowed for the widget. + $definition = $this->getDefinition($configuration['type']); if (!isset($definition['class']) || !in_array($field['type'], $definition['field_types'])) { // Grab the default widget for the field type. $field_type_definition = field_info_field_types($field['type']); - $type = $field_type_definition['default_formatter']; + $plugin_id = $field_type_definition['default_formatter']; } - $configuration = array( + $configuration += array( 'instance' => $instance, - 'settings' => $options['settings'], - 'weight' => $options['weight'], - 'label' => $options['label'], 'view_mode' => $options['view_mode'], ); - return $this->createInstance($type, $configuration); + return $this->createInstance($plugin_id, $configuration); + } + + /** + * Merges default values for formatter configuration. + * + * @param string $field_type + * The field type. + * @param array $properties + * An array of formatter configuration. + * + * @return array + * The display properties with defaults added. + */ + public function prepareConfiguration($field_type, array $configuration) { + // Fill in defaults for missing properties. + $configuration += array( + 'label' => 'above', + 'settings' => array(), + 'weight' => 0, + ); + // If no formatter is specified, use the default formatter. + if (!isset($configuration['type'])) { + $field_type = field_info_field_types($field_type); + $configuration['type'] = $field_type['default_formatter']; + } + // Fill in default settings values for the formatter. + $configuration['settings'] += field_info_formatter_settings($configuration['type']); + + return $configuration; } } diff --git a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php index 1c8f166..32f91fd 100644 --- a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php @@ -42,23 +42,33 @@ function setUp() { 'entity_type' => 'test_entity', 'bundle' => 'test_bundle', 'label' => $this->label, - 'display' => array( - 'default' => array( - 'type' => 'field_test_default', - 'settings' => array( - 'test_formatter_setting' => $this->randomName(), - ), + ); + + $this->display_options = 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(), - ), + ), + 'teaser' => array( + 'type' => 'field_test_default', + 'settings' => array( + 'test_formatter_setting' => $this->randomName(), ), ), ); + field_create_field($this->field); field_create_instance($this->instance); + // Create a display for the default view mode. + entity_get_display($this->instance['entity_type'], $this->instance['bundle'], 'default') + ->setComponent($this->field_name, $this->display_options['default']) + ->save(); + // Create a display for the teaser view mode. + entity_get_display($this->instance['entity_type'], $this->instance['bundle'], 'teaser') + ->setComponent($this->field_name, $this->display_options['teaser']) + ->save(); // Create an entity with values. $this->values = $this->_generateTestFieldValues($this->cardinality); @@ -120,11 +130,11 @@ function testFieldViewField() { $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta))); } - // View mode: check that display settings specified in the instance are - // used. + // View mode: check that display settings specified in the display object + // are used. $output = field_view_field('test_entity', $this->entity, $this->field_name, 'teaser'); $this->drupalSetContent(drupal_render($output)); - $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting']; + $setting = $this->display_options['teaser']['settings']['test_formatter_setting']; $this->assertText($this->label, 'Label was displayed.'); foreach ($this->values as $delta => $value) { $this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta))); @@ -134,7 +144,7 @@ function testFieldViewField() { // 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']['default']['settings']['test_formatter_setting']; + $setting = $this->display_options['default']['settings']['test_formatter_setting']; $this->assertText($this->label, 'Label was displayed.'); foreach ($this->values as $delta => $value) { $this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta))); @@ -189,7 +199,7 @@ function testFieldViewValue() { // View mode: check that display settings specified in the instance are // used. - $setting = $this->instance['display']['teaser']['settings']['test_formatter_setting']; + $setting = $this->display_options['teaser']['settings']['test_formatter_setting']; foreach ($this->values as $delta => $value) { $item = $this->entity->{$this->field_name}[LANGUAGE_NOT_SPECIFIED][$delta]; $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'teaser'); @@ -199,7 +209,7 @@ function testFieldViewValue() { // Unknown view mode: check that display settings for 'default' view mode // are used. - $setting = $this->instance['display']['default']['settings']['test_formatter_setting']; + $setting = $this->display_options['default']['settings']['test_formatter_setting']; foreach ($this->values as $delta => $value) { $item = $this->entity->{$this->field_name}[LANGUAGE_NOT_SPECIFIED][$delta]; $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'unknown_view_mode'); diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php index 7f87887..bc1a380 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php @@ -49,14 +49,16 @@ function setUp() { 'widget' => array( 'type' => 'text_textfield', ), - 'display' => array( - 'default' => array( - 'type' => 'text_default', - ), - ), ); field_create_instance($this->instance); + // Assign display properties for the 'default' and 'teaser' view modes. + foreach (array('default', 'teaser') as $view_mode) { + entity_get_display('node', $this->content_type, $view_mode) + ->setComponent($this->field['field_name']) + ->save(); + } + // Create test node. $this->test_view_field_value = 'This is some text'; $settings = array(); diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php index f971099..5f8ad5d 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php @@ -40,31 +40,32 @@ function testFieldAttachView() { // Simple formatter, label displayed. $entity = clone($entity_init); + $display = entity_get_display($entity_type, $entity->bundle(), 'full'); + $displays = array($entity->bundle() => $display); + $formatter_setting = $this->randomName(); - $this->instance['display'] = array( - 'full' => array( - 'label' => 'above', - 'type' => 'field_test_default', - 'settings' => array( - 'test_formatter_setting' => $formatter_setting, - ) + $display_options = array( + 'label' => 'above', + 'type' => 'field_test_default', + 'settings' => array( + 'test_formatter_setting' => $formatter_setting, ), ); - field_update_instance($this->instance); + $display->setComponent($this->field['field_name'], $display_options); + $formatter_setting_2 = $this->randomName(); - $this->instance_2['display'] = array( - 'full' => array( - 'label' => 'above', - 'type' => 'field_test_default', - 'settings' => array( - 'test_formatter_setting' => $formatter_setting_2, - ) + $display_options_2 = array( + 'label' => 'above', + 'type' => 'field_test_default', + 'settings' => array( + 'test_formatter_setting' => $formatter_setting_2, ), ); - field_update_instance($this->instance_2); + $display->setComponent($this->field_2['field_name'], $display_options_2); + // View all fields. - field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), $displays); + $entity->content = field_attach_view($entity_type, $entity, $display); $output = drupal_render($entity->content); $this->content = $output; $this->assertRaw($this->instance['label'], "First field's label is displayed."); @@ -78,8 +79,8 @@ function testFieldAttachView() { $this->assertRaw("$formatter_setting_2|{$value['value']}", "Value $delta is displayed, formatter settings are applied."); } // View single field (the second field). - field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full', $langcode, $options); - $entity->content = field_attach_view($entity_type, $entity, 'full', $langcode, $options); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), $displays, $langcode, $options); + $entity->content = field_attach_view($entity_type, $entity, $display, $langcode, $options); $output = drupal_render($entity->content); $this->content = $output; $this->assertNoRaw($this->instance['label'], "First field's label is not displayed."); @@ -95,25 +96,19 @@ function testFieldAttachView() { // Label hidden. $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), 'full'); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + $display_options['label'] = 'hidden'; + $display->setComponent($this->field['field_name'], $display_options); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), $displays); + $entity->content = field_attach_view($entity_type, $entity, $display); $output = drupal_render($entity->content); $this->content = $output; $this->assertNoRaw($this->instance['label'], "Hidden label: label is not displayed."); // Field hidden. $entity = clone($entity_init); - $this->instance['display'] = array( - 'full' => array( - 'label' => 'above', - 'type' => 'hidden', - ), - ); - field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + $display->removeComponent($this->field['field_name']); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), $displays); + $entity->content = field_attach_view($entity_type, $entity, $display); $output = drupal_render($entity->content); $this->content = $output; $this->assertNoRaw($this->instance['label'], "Hidden field: label is not displayed."); @@ -124,45 +119,38 @@ function testFieldAttachView() { // Multiple formatter. $entity = clone($entity_init); $formatter_setting = $this->randomName(); - $this->instance['display'] = array( - 'full' => array( - 'label' => 'above', - 'type' => 'field_test_multiple', - 'settings' => array( - 'test_formatter_setting_multiple' => $formatter_setting, - ) + $display->setComponent($this->field['field_name'], array( + 'label' => 'above', + 'type' => 'field_test_multiple', + 'settings' => array( + 'test_formatter_setting_multiple' => $formatter_setting, ), - ); - field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + )); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), $displays); + $entity->content = field_attach_view($entity_type, $entity, $display); $output = drupal_render($entity->content); - $display = $formatter_setting; + $expected_output = $formatter_setting; foreach ($values as $delta => $value) { - $display .= "|$delta:{$value['value']}"; + $expected_output .= "|$delta:{$value['value']}"; } $this->content = $output; - $this->assertRaw($display, "Multiple formatter: all values are displayed, formatter settings are applied."); + $this->assertRaw($expected_output, "Multiple formatter: all values are displayed, formatter settings are applied."); // Test a formatter that uses hook_field_formatter_prepare_view(). $entity = clone($entity_init); $formatter_setting = $this->randomName(); - $this->instance['display'] = array( - 'full' => array( - 'label' => 'above', - 'type' => 'field_test_with_prepare_view', - 'settings' => array( - 'test_formatter_setting_additional' => $formatter_setting, - ) + $display->setComponent($this->field['field_name'], array( + 'label' => 'above', + 'type' => 'field_test_with_prepare_view', + 'settings' => array( + 'test_formatter_setting_additional' => $formatter_setting, ), - ); - field_update_instance($this->instance); - field_attach_prepare_view($entity_type, array($entity->ftid => $entity), 'full'); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + )); + field_attach_prepare_view($entity_type, array($entity->ftid => $entity), $displays); + $entity->content = field_attach_view($entity_type, $entity, $display); $output = drupal_render($entity->content); $this->content = $output; foreach ($values as $delta => $value) { - $this->content = $output; $expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1); $this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied."); } @@ -191,8 +179,8 @@ function testFieldAttachPrepareViewMultiple() { $langcode = LANGUAGE_NOT_SPECIFIED; // Set the instance to be hidden. - $this->instance['display']['full']['type'] = 'hidden'; - field_update_instance($this->instance); + $display = entity_get_display('test_entity', 'test_bundle', 'full') + ->removeComponent($this->field['field_name']); // Set up a second instance on another bundle, with a formatter that uses // hook_field_formatter_prepare_view(). @@ -200,14 +188,18 @@ function testFieldAttachPrepareViewMultiple() { $formatter_setting = $this->randomName(); $this->instance2 = $this->instance; $this->instance2['bundle'] = 'test_bundle_2'; - $this->instance2['display']['full'] = array( - 'type' => 'field_test_with_prepare_view', - 'settings' => array( - 'test_formatter_setting_additional' => $formatter_setting, - ) - ); field_create_instance($this->instance2); + $display_2 = entity_get_display('test_entity', 'test_bundle_2', 'full') + ->setComponent($this->field['field_name'], array( + 'type' => 'field_test_with_prepare_view', + 'settings' => array( + 'test_formatter_setting_additional' => $formatter_setting, + ), + )); + + $displays = array('test_bundle' => $display, 'test_bundle_2' => $display_2); + // Create one entity in each bundle. $entity1_init = field_test_create_entity(1, 1, 'test_bundle'); $values1 = $this->_generateTestFieldValues($this->field['cardinality']); @@ -220,14 +212,16 @@ function testFieldAttachPrepareViewMultiple() { // Run prepare_view, and check that the entities come out as expected. $entity1 = clone($entity1_init); $entity2 = clone($entity2_init); - field_attach_prepare_view($entity_type, array($entity1->ftid => $entity1, $entity2->ftid => $entity2), 'full'); + $entities = array($entity1->ftid => $entity1, $entity2->ftid => $entity2); + field_attach_prepare_view($entity_type, $entities, $displays); $this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.'); $this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.'); // Same thing, reversed order. $entity1 = clone($entity1_init); $entity2 = clone($entity2_init); - field_attach_prepare_view($entity_type, array($entity2->ftid => $entity2, $entity1->ftid => $entity1), 'full'); + $entities = array($entity1->ftid => $entity1, $entity2->ftid => $entity2); + field_attach_prepare_view($entity_type, $entities, $displays); $this->assertFalse(isset($entity1->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 1 did not run through the prepare_view hook.'); $this->assertTrue(isset($entity2->{$this->field_name}[$langcode][0]['additional_formatter_value']), 'Entity 2 ran through the prepare_view hook.'); } diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php index cfc1718..720bf87 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php @@ -168,15 +168,13 @@ function testInstancePrepare() { field_create_instance($instance_definition); // Simulate a stored instance definition missing various settings (e.g. a - // third-party module adding instance, widget or display settings has been - // enabled, but existing instances do not know the new settings). + // third-party module adding instance or widget settings has been enabled, + // but existing instances do not know the new settings). $data = db_query('SELECT data FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $instance_definition['field_name'], ':bundle' => $instance_definition['bundle']))->fetchField(); $data = unserialize($data); $data['settings'] = array(); $data['widget']['settings'] = 'unavailable_widget'; $data['widget']['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']) @@ -197,13 +195,6 @@ function testInstancePrepare() { $this->assertIdentical($widget->getPluginId(), $field_type['default_widget'], 'Unavailable widget replaced with default widget.'); $widget_type = $widget->getDefinition(); $this->assertIdentical($widget->getSettings(), $widget_type['settings'] , 'All expected widget settings are present.'); - - // Check that display settings are set for the 'default' mode. - $formatter = $instance->getFormatter('default'); - $this->assertIdentical($formatter->getPluginId(), $field_type['default_formatter'], "Formatter is set for the 'default' view mode"); - $formatter_type = $formatter->getDefinition(); - $this->assertIdentical($formatter->getSettings(), $formatter_type['settings'] , "Formatter settings are set for the 'default' view mode"); - } /** diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php index 84cee72..90fcc05 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php @@ -62,7 +62,6 @@ function testCreateFieldInstance() { $field_type = field_info_field_types($this->field['type']); $widget_type = field_info_widget_types($field_type['default_widget']); - $formatter_type = field_info_formatter_types($field_type['default_formatter']); // Check that the ID key is filled in. $this->assertIdentical($record['id'], $this->instance_definition['id'], 'The instance id is filled in'); @@ -72,13 +71,10 @@ function testCreateFieldInstance() { $this->assertIdentical($record['data']['label'], $this->instance_definition['field_name'], 'Label defaults to field name.'); $this->assertIdentical($record['data']['description'], '', 'Description defaults to empty string.'); $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], 'Default widget has been written.'); - $this->assertTrue(isset($record['data']['display']['default']), 'Display for "full" view_mode has been written.'); - $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], 'Default formatter for "full" view_mode has been written.'); // Check that default settings are set. $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , 'Default instance settings have been written.'); $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , 'Default widget settings have been written.'); - $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], 'Default formatter settings for "full" view_mode have been written.'); // Guarantee that the field/bundle combination is unique. try { @@ -151,7 +147,6 @@ function testReadFieldInstance() { */ function testUpdateFieldInstance() { field_create_instance($this->instance_definition); - $field_type = field_info_field_types($this->field['type']); // Check that basic changes are saved. $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); @@ -161,8 +156,6 @@ function testUpdateFieldInstance() { $instance['settings']['test_instance_setting'] = $this->randomName(); $instance['widget']['settings']['test_widget_setting'] =$this->randomName(); $instance['widget']['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']); @@ -171,35 +164,16 @@ function testUpdateFieldInstance() { $this->assertEqual($instance['description'], $instance_new['description'], '"description" change is saved'); $this->assertEqual($instance['widget']['settings']['test_widget_setting'], $instance_new['widget']['settings']['test_widget_setting'], 'Widget setting change is saved'); $this->assertEqual($instance['widget']['weight'], $instance_new['widget']['weight'], 'Widget weight change is saved'); - $this->assertEqual($instance['display']['default']['settings']['test_formatter_setting'], $instance_new['display']['default']['settings']['test_formatter_setting'], 'Formatter setting change is saved'); - $this->assertEqual($instance['display']['default']['weight'], $instance_new['display']['default']['weight'], 'Widget weight change is saved'); - // Check that changing widget and formatter types updates the default settings. + // Check that changing the widget type 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']['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'] , '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) , 'Widget type change updates default settings.'); - $this->assertEqual($instance['display']['default']['type'], $instance_new['display']['default']['type'] , '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']['default']['settings'], $settings) , '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']); - $instance['display']['teaser'] = array(); - field_update_instance($instance); - - $instance_new = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); - $this->assertTrue(isset($instance_new['display']['teaser']), 'Display for the new view_mode has been written.'); - $this->assertIdentical($instance_new['display']['teaser']['type'], $field_type['default_formatter'], 'Default formatter for the new view_mode has been written.'); - $info = field_info_formatter_types($instance_new['display']['teaser']['type']); - $settings = $info['settings']; - $this->assertIdentical($settings, $instance_new['display']['teaser']['settings'] , 'Default formatter settings for the new view_mode have been written.'); // TODO: test failures. } diff --git a/core/modules/field/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php b/core/modules/field/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php index c88f563..37d4ee5 100644 --- a/core/modules/field/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php +++ b/core/modules/field/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php @@ -60,13 +60,14 @@ function testEmailField() { 'placeholder' => 'example@example.com', ), ), - 'display' => array( - 'full' => array( - 'type' => 'email_mailto', - ), - ), ); field_create_instance($this->instance); + // Create a display for the full view mode. + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], array( + 'type' => 'email_mailto', + )) + ->save(); // Display creation form. $this->drupalGet('test-entity/add/test_bundle'); @@ -87,7 +88,8 @@ function testEmailField() { // Verify that a mailto link is displayed. $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view('test_entity', $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + $entity->content = field_attach_view('test_entity', $entity, $display); $this->drupalSetContent(drupal_render($entity->content)); $this->assertLinkByHref('mailto:test@example.com'); } diff --git a/core/modules/field/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/field/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php index a3366d2..a04a43f 100644 --- a/core/modules/field/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php +++ b/core/modules/field/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php @@ -62,13 +62,14 @@ function testURLValidation() { 'placeholder_url' => 'http://example.com', ), ), - 'display' => array( - 'full' => array( - 'type' => 'link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], array( + 'type' => 'link', + )) + ->save(); + $langcode = LANGUAGE_NOT_SPECIFIED; // Display creation form. @@ -130,14 +131,15 @@ function testLinkTitle() { 'placeholder_title' => 'Enter a title for this link', ), ), - 'display' => array( - 'full' => array( - 'type' => 'link', - 'label' => 'hidden', - ), - ), ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], array( + 'type' => 'link', + 'label' => 'hidden', + )) + ->save(); + $langcode = LANGUAGE_NOT_SPECIFIED; // Verify that the title field works according to the field setting. @@ -235,14 +237,16 @@ function testLinkFormatter() { 'widget' => array( 'type' => 'link_default', ), - 'display' => array( - 'full' => array( - 'type' => 'link', - 'label' => 'hidden', - ), - ), + ); + $display_options = array( + 'type' => 'link', + 'label' => 'hidden', ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], $display_options) + ->save(); + $langcode = LANGUAGE_NOT_SPECIFIED; // Create an entity with two link field values: @@ -288,12 +292,14 @@ function testLinkFormatter() { foreach ($values as $new_value) { // Update the field formatter settings. if (!is_array($new_value)) { - $this->instance['display']['full']['settings'] = array($setting => $new_value); + $display_options['settings'] = array($setting => $new_value); } else { - $this->instance['display']['full']['settings'] = $new_value; + $display_options['settings'] = $new_value; } - field_update_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], $display_options) + ->save(); $this->renderTestEntity($id); switch ($setting) { @@ -367,14 +373,16 @@ function testLinkSeparateFormatter() { 'widget' => array( 'type' => 'link_default', ), - 'display' => array( - 'full' => array( - 'type' => 'link_separate', - 'label' => 'hidden', - ), - ), + ); + $display_options = array( + 'type' => 'link_separate', + 'label' => 'hidden', ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], $display_options) + ->save(); + $langcode = LANGUAGE_NOT_SPECIFIED; // Create an entity with two link field values: @@ -406,8 +414,10 @@ function testLinkSeparateFormatter() { foreach ($options as $setting => $values) { foreach ($values as $new_value) { // Update the field formatter settings. - $this->instance['display']['full']['settings'] = array($setting => $new_value); - field_update_instance($this->instance); + $display_options['settings'] = array($setting => $new_value); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field['field_name'], $display_options) + ->save(); $this->renderTestEntity($id); switch ($setting) { @@ -461,8 +471,9 @@ protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) { entity_get_controller('test_entity')->resetCache(array($id)); } $entity = field_test_entity_test_load($id); - field_attach_prepare_view('test_entity', array($entity->id() => $entity), $view_mode); - $entity->content = field_attach_view('test_entity', $entity, $view_mode); + $display = entity_get_display($entity->entityType(), $entity->bundle(), $view_mode); + field_attach_prepare_view('test_entity', array($entity->id() => $entity), array($entity->bundle() => $display)); + $entity->content = field_attach_view('test_entity', $entity, $display); $output = drupal_render($entity->content); $this->drupalSetContent($output); diff --git a/core/modules/field/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php b/core/modules/field/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php index 7588942..c02b5c4 100644 --- a/core/modules/field/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php +++ b/core/modules/field/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php @@ -70,6 +70,13 @@ function testNumberDecimalField() { ), ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'default') + ->setComponent($this->field['field_name'], array( + 'default' => array( + 'type' => 'number_decimal', + ), + )) + ->save(); // Display creation form. $this->drupalGet('test-entity/add/test_bundle'); diff --git a/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php b/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php index d61eebb..6804b6a 100644 --- a/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php +++ b/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php @@ -489,9 +489,9 @@ function testOnOffCheckbox() { 'entity_type' => 'node', 'bundle' => 'page', 'widget' => array( - 'type' => 'options_onoff', - 'module' => 'options', - ), + 'type' => 'options_onoff', + 'module' => 'options', + ), ); field_create_instance($instance); diff --git a/core/modules/field/modules/text/lib/Drupal/text/Tests/TextFieldTest.php b/core/modules/field/modules/text/lib/Drupal/text/Tests/TextFieldTest.php index 8efc516..4c58ebe 100644 --- a/core/modules/field/modules/text/lib/Drupal/text/Tests/TextFieldTest.php +++ b/core/modules/field/modules/text/lib/Drupal/text/Tests/TextFieldTest.php @@ -65,13 +65,16 @@ function testTextFieldValidation() { 'widget' => array( 'type' => 'text_textfield', ), - 'display' => array( + ); + field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'default') + ->setComponent($this->field['field_name'], array( 'default' => array( 'type' => 'text_default', ), - ), - ); - field_create_instance($this->instance); + )) + ->save(); + // Test valid and invalid values with field_attach_validate(). $entity = field_test_create_entity(); $langcode = LANGUAGE_NOT_SPECIFIED; @@ -118,13 +121,16 @@ function _testTextfieldWidgets($field_type, $widget_type) { 'placeholder' => 'A placeholder on ' . $widget_type, ), ), - 'display' => array( + ); + field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field_name, array( 'full' => array( 'type' => 'text_default', ), - ), - ); - field_create_instance($this->instance); + )) + ->save(); + $langcode = LANGUAGE_NOT_SPECIFIED; // Display creation form. @@ -145,7 +151,8 @@ function _testTextfieldWidgets($field_type, $widget_type) { // Display the entity. $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + $entity->content = field_attach_view($entity_type, $entity, $display); $this->content = drupal_render($entity->content); $this->assertText($value, 'Filtered tags are not displayed'); } @@ -178,13 +185,16 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { 'widget' => array( 'type' => $widget_type, ), - 'display' => array( + ); + field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field_name, array( 'full' => array( 'type' => 'text_default', ), - ), - ); - field_create_instance($this->instance); + )) + ->save(); + $langcode = LANGUAGE_NOT_SPECIFIED; // Disable all text formats besides the plain text fallback format. @@ -214,7 +224,8 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { // Display the entity. $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + $entity->content = field_attach_view($entity_type, $entity, $display); $this->content = drupal_render($entity->content); $this->assertNoRaw($value, 'HTML tags are not displayed.'); $this->assertRaw(check_plain($value), 'Escaped HTML is displayed correctly.'); @@ -254,7 +265,8 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { // Display the entity. entity_get_controller('test_entity')->resetCache(array($id)); $entity = field_test_entity_test_load($id); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + $entity->content = field_attach_view($entity_type, $entity, $display); $this->content = drupal_render($entity->content); $this->assertRaw($value, 'Value is displayed unfiltered'); } diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc index d919eed..490c0c5 100644 --- a/core/modules/field_ui/field_ui.admin.inc +++ b/core/modules/field_ui/field_ui.admin.inc @@ -374,51 +374,6 @@ function field_ui_display_overview($entity_type, $bundle, $view_mode) { } /** - * Populates display settings for a new view mode from the default view mode. - * - * When an administrator decides to use custom display settings for a view mode, - * that view mode needs to be initialized with the display settings for the - * 'default' view mode, which it was previously using. This helper function - * adds the new custom display settings to this bundle's instances, and saves - * them. It also modifies the passed-in $settings array, which the caller can - * then save using field_bundle_settings(). - * - * @param $entity_type - * The bundle's entity type. - * @param $bundle - * The bundle whose view mode is being customized. - * @param $view_mode - * The view mode that the administrator has set to use custom settings. - * @param $settings - * An associative array of bundle settings, as expected by - * field_bundle_settings(). - * - * @see Drupal\field_ui\DisplayOverview::submit(). - * @see field_bundle_settings() - */ -function _field_ui_add_default_view_mode_settings($entity_type, $bundle, $view_mode, &$settings) { - // Update display settings for field instances. - $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle)); - foreach ($instances as $instance) { - // If this field instance has display settings defined for this view mode, - // respect those settings. - if (!isset($instance['display'][$view_mode])) { - // The instance doesn't specify anything for this view mode, so use the - // default display settings. - $instance['display'][$view_mode] = $instance['display']['default']; - field_update_instance($instance); - } - } - - // Update display settings for 'extra fields'. - foreach (array_keys($settings['extra_fields']['display']) as $name) { - if (!isset($settings['extra_fields']['display'][$name][$view_mode])) { - $settings['extra_fields']['display'][$name][$view_mode] = $settings['extra_fields']['display'][$name]['default']; - } - } -} - -/** * Returns an array of field_type options. */ function field_ui_field_type_options() { diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php index 6117de9..b5b3f7c 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php @@ -49,6 +49,7 @@ public function form(array $form, array &$form_state) { $instances = field_info_instances($this->entity_type, $this->bundle); $field_types = field_info_field_types(); $extra_fields = field_info_extra_fields($this->entity_type, $this->bundle, 'display'); + $entity_display = entity_get_display($this->entity_type, $this->bundle, $this->view_mode); $form_state += array( 'formatter_settings_edit' => NULL, @@ -94,24 +95,14 @@ public function form(array $form, array &$form_state) { 'hidden' => t(''), ); $extra_visibility_options = array( - 'content' => t('Visible'), + 'visible' => t('Visible'), 'hidden' => t('Hidden'), ); // Field rows. foreach ($instances as $name => $instance) { - $field = field_info_field($instance['field_name']); - - if (isset($instance['display'][$this->view_mode])) { - $display = $instance['display'][$this->view_mode]; - } - else { - $display = array( - 'type' => 'hidden', - 'label' => 'above', - 'weight' => 0, - ); - } + $field = field_info_field($name); + $display_options = $entity_display->getComponent($name); $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), @@ -128,7 +119,7 @@ public function form(array $form, array &$form_state) { '#type' => 'textfield', '#title' => t('Weight for @title', array('@title' => $instance['label'])), '#title_display' => 'invisible', - '#default_value' => $display['weight'], + '#default_value' => $display_options ? $display_options['weight'] : '0', '#size' => 3, '#attributes' => array('class' => array('field-weight')), ), @@ -153,7 +144,7 @@ public function form(array $form, array &$form_state) { '#title' => t('Label display for @title', array('@title' => $instance['label'])), '#title_display' => 'invisible', '#options' => $field_label_options, - '#default_value' => $display['label'], + '#default_value' => $display_options ? $display_options['label'] : 'above', ), ); @@ -165,7 +156,7 @@ public function form(array $form, array &$form_state) { '#title' => t('Formatter for @title', array('@title' => $instance['label'])), '#title_display' => 'invisible', '#options' => $formatter_options, - '#default_value' => $display['type'], + '#default_value' => $display_options ? $display_options['type'] : 'hidden', '#parents' => array('fields', $name, 'type'), '#attributes' => array('class' => array('field-formatter-type')), ), @@ -175,12 +166,23 @@ public function form(array $form, array &$form_state) { // Check the currently selected formatter, and merge persisted values for // formatter settings. if (isset($form_state['values']['fields'][$name]['type'])) { - $display['type'] = $form_state['values']['fields'][$name]['type']; + $display_options['type'] = $form_state['values']['fields'][$name]['type']; } if (isset($form_state['formatter_settings'][$name])) { - $display['settings'] = $form_state['formatter_settings'][$name]; + $display_options['settings'] = $form_state['formatter_settings'][$name]; + } + + // Get the corresponding formatter object. + if ($display_options && $display_options['type'] != 'hidden') { + $formatter = drupal_container()->get('plugin.manager.field.formatter')->getInstance(array( + 'instance' => $instance, + 'view_mode' => $this->view_mode, + 'configuration' => $display_options + )); + } + else { + $formatter = NULL; } - $formatter = $instance->getFormatter($display); // Base button element for the various formatter settings actions. $base_button = array( @@ -287,7 +289,8 @@ public function form(array $form, array &$form_state) { // Non-field elements. foreach ($extra_fields as $name => $extra_field) { - $display = $extra_field['display'][$this->view_mode]; + $display_options = $entity_display->getComponent($name); + $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), '#row_type' => 'extra_field', @@ -300,7 +303,7 @@ public function form(array $form, array &$form_state) { '#type' => 'textfield', '#title' => t('Weight for @title', array('@title' => $extra_field['label'])), '#title_display' => 'invisible', - '#default_value' => $display['weight'], + '#default_value' => $display_options ? $display_options['weight'] : 0, '#size' => 3, '#attributes' => array('class' => array('field-weight')), ), @@ -329,7 +332,7 @@ public function form(array $form, array &$form_state) { '#title' => t('Visibility for @title', array('@title' => $extra_field['label'])), '#title_display' => 'invisible', '#options' => $extra_visibility_options, - '#default_value' => $display['visible'] ? 'content' : 'hidden', + '#default_value' => $display_options ? 'visible' : 'hidden', '#parents' => array('fields', $name, 'type'), '#attributes' => array('class' => array('field-formatter-type')), ), @@ -418,73 +421,87 @@ public function form(array $form, array &$form_state) { */ public function submit(array $form, array &$form_state) { $form_values = $form_state['values']; + $entity_display = entity_get_display($this->entity_type, $this->bundle, $this->view_mode); - // Save data for 'regular' fields. + // Collect data for 'regular' fields. foreach ($form['#fields'] as $field_name) { // Retrieve the stored instance settings to merge with the incoming // values. - $instance = field_read_instance($this->entity_type, $field_name, $this->bundle); $values = $form_values['fields'][$field_name]; - // Get formatter settings. They lie either directly in submitted form - // values (if the whole form was submitted while some formatter - // settings were being edited), or have been persisted in - // $form_state. - $settings = array(); - if (isset($values['settings_edit_form']['settings'])) { - $settings = $values['settings_edit_form']['settings']; - } - elseif (isset($form_state['formatter_settings'][$field_name])) { - $settings = $form_state['formatter_settings'][$field_name]; - } - elseif (isset($instance['display'][$this->view_mode]['settings'])) { - $settings = $instance['display'][$this->view_mode]['settings']; + + if ($values['type'] == 'hidden') { + $entity_display->removeComponent($field_name); } + else { + // Get formatter settings. They lie either directly in submitted form + // values (if the whole form was submitted while some formatter + // settings were being edited), or have been persisted in $form_state. + $settings = array(); + if (isset($values['settings_edit_form']['settings'])) { + $settings = $values['settings_edit_form']['settings']; + } + elseif (isset($form_state['formatter_settings'][$field_name])) { + $settings = $form_state['formatter_settings'][$field_name]; + } + elseif ($current_options = $entity_display->getComponent($field_name)) { + $settings = $current_options['settings']; + } - // Only save settings actually used by the selected formatter. - $default_settings = field_info_formatter_settings($values['type']); - $settings = array_intersect_key($settings, $default_settings); + // Only save settings actually used by the selected formatter. + $default_settings = field_info_formatter_settings($values['type']); + $settings = array_intersect_key($settings, $default_settings); - $instance['display'][$this->view_mode] = array( - 'label' => $values['label'], - 'type' => $values['type'], - 'weight' => $values['weight'], - 'settings' => $settings, - ); - field_update_instance($instance); + $entity_display->setComponent($field_name, array( + 'label' => $values['label'], + 'type' => $values['type'], + 'weight' => $values['weight'], + 'settings' => $settings, + )); + } } - // Get current bundle settings. - $bundle_settings = field_bundle_settings($this->entity_type, $this->bundle); - - // Save data for 'extra' fields. + // Collect data for 'extra' fields. foreach ($form['#extra'] as $name) { - $bundle_settings['extra_fields']['display'][$name][$this->view_mode] = array( - 'weight' => $form_values['fields'][$name]['weight'], - 'visible' => $form_values['fields'][$name]['type'] == 'content', - ); + if ($form_values['fields'][$name]['type'] == 'hidden') { + $entity_display->removeComponent($name); + } + else { + $entity_display->setComponent($name, array( + 'weight' => $form_values['fields'][$name]['weight'], + )); + } } - // Save view modes data. + // Save the display. + $entity_display->save(); + + // Handle the 'view modes' checkboxes if present. if ($this->view_mode == 'default' && !empty($form_values['view_modes_custom'])) { $entity_info = entity_get_info($this->entity_type); + $bundle_settings = field_bundle_settings($this->entity_type, $this->bundle); + $view_mode_settings = field_view_mode_settings($this->entity_type, $this->bundle); + foreach ($form_values['view_modes_custom'] as $view_mode_name => $value) { - // Display a message for each view mode newly configured to use custom - // settings. - $view_mode_settings = field_view_mode_settings($this->entity_type, $this->bundle); if (!empty($value) && empty($view_mode_settings[$view_mode_name]['custom_settings'])) { + // If no display exists for the newly enabled view mode, initialize + // it with those from the 'default' view mode, which were used so + // far. + $display = entity_get_display($this->entity_type, $this->bundle, $view_mode_name); + if ($display->isNew()) { + $display->content = entity_get_display($this->entity_type, $this->bundle, 'default')->content; + $display->save(); + } + $view_mode_label = $entity_info['view_modes'][$view_mode_name]['label']; $path = field_ui_bundle_admin_path($this->entity_type, $this->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)))); - // Initialize the newly customized view mode with the display settings - // from the default view mode. - _field_ui_add_default_view_mode_settings($this->entity_type, $this->bundle, $view_mode_name, $bundle_settings); } $bundle_settings['view_modes'][$view_mode_name]['custom_settings'] = !empty($value); } - } - // Save updated bundle settings. - field_bundle_settings($this->entity_type, $this->bundle, $bundle_settings); + // Save updated bundle settings. + field_bundle_settings($this->entity_type, $this->bundle, $bundle_settings); + } drupal_set_message(t('Your settings have been saved.')); } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php index 177e3fd..9ebe6fa 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php @@ -586,6 +586,13 @@ public function submit(array $form, array &$form_state) { field_create_field($field); field_create_instance($instance); + // Make sure the field is displayed in the 'default' view mode (using + // default formatter and settings). It stays hidden for other view + // modes until it is explicitly configured. + entity_get_display($this->entity_type, $this->bundle, 'default') + ->setComponent($field['field_name']) + ->save(); + $destinations[] = $this->adminPath. '/fields/' . $field['field_name'] . '/field-settings'; $destinations[] = $this->adminPath . '/fields/' . $field['field_name']; @@ -618,6 +625,14 @@ public function submit(array $form, array &$form_state) { try { field_create_instance($instance); + + // Make sure the field is displayed in the 'default' view mode (using + // default formatter and settings). It stays hidden for other view + // modes until it is explicitly configured. + entity_get_display($this->entity_type, $this->bundle, 'default') + ->setComponent($field['field_name']) + ->save(); + $destinations[] = $this->adminPath . '/fields/' . $instance['field_name'] . '/edit'; // Store new field information for any additional submit handlers. $form_state['fields_added']['_add_existing_field'] = $instance['field_name']; diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php index 8396c7f..45de66e 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php @@ -44,12 +44,12 @@ function testFormatterUI() { $this->fieldUIAddNewField($manage_fields, $edit); // Clear the test-side cache and get the saved field instance. - field_info_cache_clear(); - $instance = field_info_instance('node', 'field_test', $this->type); - $format = $instance['display']['default']['type']; + $display = entity_get_display('node', $this->type, 'default'); + $display_options = $display->getComponent('field_test'); + $format = $display_options['type']; $default_settings = field_info_formatter_settings($format); $setting_name = key($default_settings); - $setting_value = $instance['display']['default']['settings'][$setting_name]; + $setting_value = $display_options['settings'][$setting_name]; // Display the "Manage display" screen and check that the expected formatter is // selected. @@ -67,12 +67,12 @@ function testFormatterUI() { $this->assertFieldByName('fields[field_test][type]', $format, 'The expected formatter is selected.'); $this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.'); - // Submit the form and check that the instance is updated. + // Submit the form and check that the display is updated. $this->drupalPost(NULL, array(), t('Save')); - field_info_cache_clear(); - $instance = field_info_instance('node', 'field_test', $this->type); - $current_format = $instance['display']['default']['type']; - $current_setting_value = $instance['display']['default']['settings'][$setting_name]; + $display = entity_get_display('node', $this->type, 'default'); + $display_options = $display->getComponent('field_test'); + $current_format = $display_options['type']; + $current_setting_value = $display_options['settings'][$setting_name]; $this->assertEqual($current_format, $format, 'The formatter was updated.'); $this->assertEqual($current_setting_value, $setting_value, 'The setting was updated.'); diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 56f8402..69954cb 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -90,18 +90,22 @@ 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); + + // Assign display settings for the 'default' and 'teaser' view modes. + entity_get_display('node', 'forum', 'default') + ->setComponent('taxonomy_forums', array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); + entity_get_display('node', 'forum', 'teaser') + ->setComponent('taxonomy_forums', array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); } // Ensure the forum node type is available. diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 221e07c..8312420 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -334,21 +334,23 @@ function image_image_style_save($style) { // Loop through all fields searching for image fields. foreach ($instances as $instance) { if ($instance['widget']['module'] == 'image') { - $instance_changed = FALSE; - foreach ($instance['display'] as $view_mode => $display) { + $entity_info = entity_get_info($instance['entity_type']); + $view_modes = array('default') + array_keys($entity_info['view_modes']); + foreach ($view_modes as $view_mode) { + $display = entity_get_display($instance['entity_type'], $instance['bundle'], $view_mode); + $display_options = $display->getComponent($instance['field_name']); + // Check if the formatter involves an image style. - if ($display['type'] == 'image' && $display['settings']['image_style'] == $style->getOriginalID()) { + if ($display_options && $display_options['type'] == 'image' && $display_options['settings']['image_style'] == $style->getOriginalID()) { // Update display information for any instance using the image // style that was just deleted. - $instance['display'][$view_mode]['settings']['image_style'] = $style->id(); - $instance_changed = TRUE; + $display_options['settings']['image_style'] = $style->id(); + $display->setComponent($instance['field_name'], $display_options) + ->save(); } } if ($instance['widget']['settings']['preview_image_style'] == $style->getOriginalID()) { $instance['widget']['settings']['preview_image_style'] = $style->id(); - $instance_changed = TRUE; - } - if ($instance_changed) { field_update_instance($instance); } } diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php index 958610d..0d09461 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php @@ -253,10 +253,12 @@ function testStyleReplacement() { // Create an image field that uses the new style. $field_name = strtolower($this->randomName(10)); $this->createImageField($field_name, 'article'); - $instance = field_info_instance('node', $field_name, 'article'); - $instance['display']['default']['type'] = 'image'; - $instance['display']['default']['settings']['image_style'] = $style_name; - field_update_instance($instance); + entity_get_display('node', 'article', 'default') + ->setComponent($field_name, array( + 'type' => 'image', + 'settings' => array('image_style' => $style_name), + )) + ->save(); // Create a new node with an image attached. $test_image = current($this->drupalGetTestFiles('image')); diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php index f7670fe..fea19ee 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php @@ -69,7 +69,9 @@ function testDefaultImages() { ); field_create_instance($instance2); $instance2 = field_info_instance('node', $field_name, 'page'); - + entity_get_display('node', 'page', 'default') + ->setComponent($field['field_name']) + ->save(); // Confirm the defaults are present on the article field admin form. $this->drupalGet("admin/structure/types/manage/article/fields/$field_name"); diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php index c07acf6..ccbe686 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php @@ -49,6 +49,7 @@ function testImageFieldFormattersPrivate() { function _testImageFieldFormatters($scheme) { $field_name = strtolower($this->randomName()); $this->createImageField($field_name, 'article', array('uri_scheme' => $scheme)); + // Create a new node with an image attached. $test_image = current($this->drupalGetTestFiles('image')); $nid = $this->uploadNodeImage($test_image, $field_name, 'article'); @@ -65,10 +66,14 @@ function _testImageFieldFormatters($scheme) { $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.'); // Test the image linked to file formatter. - $instance = field_info_instance('node', $field_name, 'article'); - $instance['display']['default']['type'] = 'image'; - $instance['display']['default']['settings']['image_link'] = 'file'; - field_update_instance($instance); + $display_options = array( + 'type' => 'image', + 'settings' => array('image_link' => 'file'), + ); + $display = entity_get_display('node', $node->type, 'default'); + $display->setComponent($field_name, $display_options) + ->save(); + $default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE)); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, 'Image linked to file formatter displaying correctly on full node view.'); @@ -91,16 +96,19 @@ function _testImageFieldFormatters($scheme) { } // Test the image linked to content formatter. - $instance['display']['default']['settings']['image_link'] = 'content'; - field_update_instance($instance); + $display_options['settings']['image_link'] = 'content'; + $display->setComponent($field_name, $display_options) + ->save(); $default_output = l(theme('image', $image_info), 'node/' . $nid, array('html' => TRUE, 'attributes' => array('class' => 'active'))); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, 'Image linked to content formatter displaying correctly on full node view.'); // Test the image style 'thumbnail' formatter. - $instance['display']['default']['settings']['image_link'] = ''; - $instance['display']['default']['settings']['image_style'] = 'thumbnail'; - field_update_instance($instance); + $display_options['settings']['image_link'] = ''; + $display_options['settings']['image_style'] = 'thumbnail'; + $display->setComponent($field_name, $display_options) + ->save(); + // Ensure the derivative image is generated so we do not have to deal with // image style callback paths. $this->drupalGet(image_style_url('thumbnail', $image_uri)); @@ -264,6 +272,9 @@ function testImageFieldDefaultImage() { 'files[field_settings_default_image]' => drupal_realpath($images[1]->uri), ); $this->drupalPost('admin/structure/types/manage/article/fields/' . $private_field_name, $edit, t('Save settings')); + // Clear field info cache so the new default image is detected. + field_info_cache_clear(); + $private_field = field_info_field($private_field_name); $image = file_load($private_field['settings']['default_image']); $this->assertEqual('private', file_uri_scheme($image->uri), 'Default image uses private:// scheme.'); diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php index fc964ac..439db01 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php @@ -93,7 +93,15 @@ function createImageField($name, $type_name, $field_settings = array(), $instanc ); $instance['settings'] = array_merge($instance['settings'], $instance_settings); $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings); - return field_create_instance($instance); + + $field_instance = field_create_instance($instance); + + entity_get_display('node', $type_name, 'default') + ->setComponent($field['field_name']) + ->save(); + + return $field_instance; + } /** diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php index 8dd2ab6..1794e89 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php @@ -45,6 +45,9 @@ public function setUp() { 'bundle' => 'page', ); $this->instance = field_create_instance($instance); + entity_get_display('node', 'page', 'default') + ->setComponent($this->field_name) + ->save(); } /** diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTypeInitialLanguageTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTypeInitialLanguageTest.php index 5c906d7..b689141 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTypeInitialLanguageTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTypeInitialLanguageTest.php @@ -118,11 +118,11 @@ function testLanguageFieldVisibility() { // Changes Language field visibility to true and check if it is saved. $edit = array( - 'fields[language][type]' => 'content', + 'fields[language][type]' => 'visible', ); $this->drupalPost('admin/structure/types/manage/article/display', $edit, t('Save')); $this->drupalGet('admin/structure/types/manage/article/display'); - $this->assertOptionSelected('edit-fields-language-type', 'content', 'Language field has been set to visible.'); + $this->assertOptionSelected('edit-fields-language-type', 'visible', 'Language field has been set to visible.'); // Loads node page and check if Language field is shown. $this->drupalGet('node/' . $node->nid); diff --git a/core/modules/node/lib/Drupal/node/Tests/SummaryLengthTest.php b/core/modules/node/lib/Drupal/node/Tests/SummaryLengthTest.php index 4276146..6b966e8 100644 --- a/core/modules/node/lib/Drupal/node/Tests/SummaryLengthTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/SummaryLengthTest.php @@ -46,9 +46,11 @@ function testSummaryLength() { $this->assertRaw($expected); // Change the teaser length for "Basic page" content type. - $instance = field_info_instance('node', 'body', $node->type); - $instance['display']['teaser']['settings']['trim_length'] = 200; - field_update_instance($instance); + $display = entity_get_display('node', $node->type, 'teaser'); + $display_options = $display->getComponent('body'); + $display_options['settings']['trim_length'] = 200; + $display->setComponent('body', $display_options) + ->save(); // Render the node as a teaser again and check that the summary is now only // 200 characters in length and so does not include 'What is a Drupalism?'. diff --git a/core/modules/node/node.module b/core/modules/node/node.module index ea2b4ce..94ca203 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -19,6 +19,7 @@ use Drupal\node\Plugin\Core\Entity\Node; use Drupal\file\Plugin\Core\Entity\File; use Drupal\Core\Entity\EntityInterface; +use Drupal\field\Plugin\Core\Entity\EntityDisplay; /** * Denotes that the node is not published. @@ -212,12 +213,16 @@ function node_entity_info(&$info) { } /** - * Implements hook_field_display_ENTITY_TYPE_alter(). + * Implements hook_entity_display_alter(). */ -function node_field_display_node_alter(&$display, $context) { +function node_entity_display_alter(EntityDisplay $display, $context) { // Hide field labels in search index. - if ($context['view_mode'] == 'search_index') { - $display['label'] = 'hidden'; + if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') { + foreach ($display->content as $name => &$properties) { + if (isset($properties['label'])) { + $properties['label'] = 'hidden'; + } + } } } @@ -559,19 +564,24 @@ function node_add_body_field($type, $label = 'Body') { 'label' => $label, 'widget' => array('type' => 'text_textarea_with_summary'), 'settings' => array('display_summary' => TRUE), - 'display' => array( - 'default' => array( - 'label' => 'hidden', - 'type' => 'text_default', - ), - 'teaser' => array( - 'label' => 'hidden', - 'type' => 'text_summary_or_trimmed', - ), - ), ); $instance = field_create_instance($instance); + + // Assign display settings for the 'default' and 'teaser' view modes. + entity_get_display('node', $type->type, 'default') + ->setComponent($field['field_name'], array( + 'label' => 'hidden', + 'type' => 'text_default', + )) + ->save(); + entity_get_display('node', $type->type, 'teaser') + ->setComponent($field['field_name'], array( + 'label' => 'hidden', + 'type' => 'text_summary_or_trimmed', + )) + ->save(); } + return $instance; } diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc index 0a18ec1..10e734a 100644 --- a/core/modules/node/node.pages.inc +++ b/core/modules/node/node.pages.inc @@ -144,8 +144,6 @@ function node_preview(Node $node) { } $node->changed = REQUEST_TIME; - $nodes = array($node->nid => $node); - field_attach_prepare_view('node', $nodes, 'full'); // Display a preview of the node. if (!form_get_errors()) { @@ -180,7 +178,7 @@ function theme_node_preview($variables) { $elements = node_view(clone $node, 'teaser'); $elements['#attached']['library'][] = array('node', 'drupal.node.preview'); $trimmed = drupal_render($elements); - $elements = node_view($node, 'full'); + $elements = node_view(clone $node, 'full'); $full = drupal_render($elements); // Do we need to preview trimmed version of post as well as full version? diff --git a/core/modules/node/node.tokens.inc b/core/modules/node/node.tokens.inc index 2d783c9..6d201a6 100644 --- a/core/modules/node/node.tokens.inc +++ b/core/modules/node/node.tokens.inc @@ -149,7 +149,19 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr // A summary was requested. if ($name == 'summary') { // Generate an optionally trimmed summary of the body field. - $output = text_summary($output, $instance['settings']['text_processing'] ? $items[0]['format'] : NULL, $instance['display']['teaser']['settings']['trim_length']); + + // Get the 'trim_length' size used for the 'teaser' mode, if present, or + // use the default trim_length size. + $display_options = entity_get_display('node', $node->type, 'teaser')->getComponent('body'); + if (isset($display_options['settings']['trim_length'])) { + $length = $display_options['settings']['trim_length']; + } + else { + $settings = field_info_formatter_settings('text_summary_or_trimmed'); + $length = $settings['trim_length']; + } + + $output = text_summary($output, $instance['settings']['text_processing'] ? $items[0]['format'] : NULL, $length); } } $replacements[$original] = $output; diff --git a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php index 69b0231..de0f782 100644 --- a/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php +++ b/core/modules/picture/lib/Drupal/picture/Tests/PictureFieldDisplayTest.php @@ -120,11 +120,6 @@ public function _testPictureFieldFormatters($scheme) { $nid = $this->uploadNodeImage($test_image, $field_name, 'article'); $node = node_load($nid, TRUE); - // Use the picture formatter. - $instance = field_info_instance('node', $field_name, 'article'); - $instance['display']['default']['type'] = 'picture'; - $instance['display']['default']['module'] = 'picture'; - // Test that the default formatter is being used. $image_uri = file_load($node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0]['fid'])->uri; $image_info = array( @@ -136,11 +131,15 @@ public function _testPictureFieldFormatters($scheme) { $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.'); // Use the picture formatter linked to file formatter. - $instance = field_info_instance('node', $field_name, 'article'); - $instance['display']['default']['type'] = 'picture'; - $instance['display']['default']['module'] = 'picture'; - $instance['display']['default']['settings']['image_link'] = 'file'; - field_update_instance($instance); + $display_options = array( + 'type' => 'picture', + 'module' => 'picture', + 'settings' => array('image_link' => 'file'), + ); + $display = entity_get_display('node', 'article', 'default'); + $display->setComponent($field_name, $display_options) + ->save(); + $default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE)); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, 'Image linked to file formatter displaying correctly on full node view.'); @@ -163,8 +162,10 @@ public function _testPictureFieldFormatters($scheme) { } // Use the picture formatter with a picture mapping. - $instance['display']['default']['settings']['picture_mapping'] = 'mapping_one'; - field_update_instance($instance); + $display_options['settings']['picture_mapping'] = 'mapping_one'; + $display->setComponent($field_name, $display_options) + ->save(); + // Output should contain all image styles and all breakpoints. $this->drupalGet('node/' . $nid); $this->assertRaw('/styles/thumbnail/'); @@ -175,9 +176,10 @@ public function _testPictureFieldFormatters($scheme) { $this->assertRaw('media="(min-width: 600px)"'); // Test the fallback image style. - $instance['display']['default']['settings']['image_link'] = ''; - $instance['display']['default']['settings']['fallback_image_style'] = 'large'; - field_update_instance($instance); + $display_options['settings']['image_link'] = ''; + $display_options['settings']['fallback_image_style'] = 'large'; + $display->setComponent($field_name, $display_options) + ->save(); $this->drupalGet(image_style_url('large', $image_uri)); $image_info['uri'] = $image_uri; diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php index db24097..368d221 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php @@ -115,13 +115,13 @@ function testAttributesInMarkupFile() { 'field_name' => $field_name, 'entity_type' => 'node', 'bundle' => $bundle_name, - 'display' => array( - 'teaser' => array( - 'type' => 'file_default', - ), - ), ); field_create_instance($instance); + entity_get_display('node', $bundle_name, 'teaser') + ->setComponent($field_name, array( + 'type' => 'file_default', + )) + ->save(); // Set the RDF mapping for the new field. $rdf_mapping = rdf_mapping_load('node', $bundle_name); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityDisplayTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityDisplayTest.php new file mode 100644 index 0000000..7fbda0e --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityDisplayTest.php @@ -0,0 +1,187 @@ + 'Entity display configuration entities', + 'description' => 'Tests the entity display configuration entities.', + 'group' => 'Entity API', + ); + } + + /** + * Tests basic CRUD operations on EntityDisplay objects. + */ + function testEntityDisplayCRUD() { + $this->enableModules(array('system', 'field')); + + $display = entity_create('entity_display', array( + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'view_mode' => 'default', + )); + + $expected = array(); + + // Check that providing no 'weight' results in the highest current weight + // being being assigned. + $expected['component_1'] = array('weight' => 0); + $expected['component_2'] = array('weight' => 1); + $display->setComponent('component_1'); + $display->setComponent('component_2'); + $this->assertEqual($display->getComponent('component_1'), $expected['component_1']); + $this->assertEqual($display->getComponent('component_2'), $expected['component_2']); + + // Check that arbitrary options are correctly stored. + $expected['component_3'] = array('weight' => 10, 'foo' => 'bar'); + $display->setComponent('component_3', $expected['component_3']); + $this->assertEqual($display->getComponent('component_3'), $expected['component_3']); + + // Check that the display can be properly saved and read back. + $display->save(); + $display = entity_load('entity_display', $display->id()); + foreach (array('component_1', 'component_2', 'component_3') as $name) { + $this->assertEqual($display->getComponent($name), $expected[$name]); + } + + // Check that a component can be removed. + $display->removeComponent('component_3'); + $this->assertNULL($display->getComponent('component_3')); + + // Check that the removal is correctly persisted. + $display->save(); + $display = entity_load('entity_display', $display->id()); + $this->assertNULL($display->getComponent('component_3')); + } + + /** + * Tests entity_get_display(). + */ + public function testEntityGetDisplay() { + $this->enableModules(array('system', 'field')); + + // Check that entity_get_display() returns a fresh object when no + // configuration file exists. + $display = entity_get_display('entity_test', 'entity_test', 'default'); + $this->assertTrue($display->isNew()); + + // Add some components and save the display. + $display->setComponent('component_1', array('weight' => 10)) + ->save(); + + // Check that entity_get_display() returns the correct object. + $display = entity_get_display('entity_test', 'entity_test', 'default'); + $this->assertFalse($display->isNew()); + $this->assertEqual($display->id, 'entity_test.entity_test.default'); + $this->assertEqual($display->getComponent('component_1'), array('weight' => 10)); + } + + /** + * Tests the behavior of a field conponent within an EntityDisplay object. + */ + function testExtraFieldComponent() { + $this->enableModules(array('system', 'field')); + + $display = entity_create('entity_display', array( + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'view_mode' => 'default', + )); + + // Check that the default visibility taken into account for extra fields + // unknown in the display. + $this->assertEqual($display->getComponent('display_extra_field'), array('weight' => 5)); + $this->assertNull($display->getComponent('display_extra_field_hidden')); + + // Check that setting explicit options overrides the defaults. + $display->removeComponent('display_extra_field'); + $display->setComponent('display_extra_field_hidden', array('weight' => 10)); + $this->assertNull($display->getComponent('display_extra_field')); + $this->assertEqual($display->getComponent('display_extra_field_hidden'), array('weight' => 10)); + } + + /** + * Tests the behavior of a field conponent within an EntityDisplay object. + */ + function testFieldComponent() { + $this->enableModules(array('system', 'field', 'field_sql_storage', 'field_test')); + + $display = entity_create('entity_display', array( + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'view_mode' => 'default', + )); + + // Create a field and an instance. + $field = array( + 'field_name' => 'test_field', + 'type' => 'test_field' + ); + field_create_field($field); + $instance = array( + 'field_name' => $field['field_name'], + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ); + field_create_instance($instance); + + // Check that providing no options results in default values being used. + $display->setComponent($field['field_name']); + $field_type_info = field_info_field_types($field['type']); + $default_formatter = $field_type_info['default_formatter']; + $default_settings = field_info_formatter_settings($default_formatter); + $expected = array( + 'weight' => 0, + 'label' => 'above', + 'type' => $default_formatter, + 'settings' => $default_settings, + ); + $this->assertEqual($display->getComponent($field['field_name']), $expected); + + // Check that the getFormatter() method returns the correct formatter plugin. + $formatter = $display->getFormatter($field['field_name']); + $this->assertEqual($formatter->getPluginId(), $default_formatter); + $this->assertEqual($formatter->getSettings(), $default_settings); + + // Check that the formatter is statically persisted, by assigning an + // arbitrary property and reading it back. + $random_value = $this->randomString(); + $formatter->randomValue = $random_value; + $formatter = $display->getFormatter($field['field_name']); + $this->assertEqual($formatter->randomValue, $random_value ); + + // Check that changing the definition creates a new formatter. + $display->setComponent($field['field_name'], array( + 'type' => 'field_test_multiple', + )); + $formatter = $display->getFormatter($field['field_name']); + $this->assertEqual($formatter->getPluginId(), 'field_test_multiple'); + $this->assertFalse(isset($formatter->randomValue)); + + // Check that specifying an unknown formatter (e.g. case of a disabled + // module) gets stored as is in the display, but results in the default + // formatter being used. + $display->setComponent($field['field_name'], array( + 'type' => 'unknown_formatter', + )); + $options = $display->getComponent($field['field_name']); + $this->assertEqual($options['type'], 'unknown_formatter'); + $formatter = $display->getFormatter($field['field_name']); + $this->assertEqual($formatter->getPluginId(), $default_formatter); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php new file mode 100644 index 0000000..a1c4a73 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php @@ -0,0 +1,82 @@ + 'Field upgrade test', + 'description' => 'Tests upgrade of Field API.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + $this->databaseDumpFiles = array( + drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.bare.standard_all.database.php.gz', + drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.field.database.php', + ); + parent::setUp(); + } + + /** + * Tests upgrade of entity displays. + */ + public function testEntityDisplayUpgrade() { + $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); + + // Check that the entity display configuration files were created. + $displays = array( + 'default' => config('field.entity_display.node.article.default')->get(), + 'teaser' => config('field.entity_display.node.article.teaser')->get(), + ); + $this->assertTrue(!empty($displays['default'])); + $this->assertTrue(!empty($displays['teaser'])); + + // Check that the 'body' field is configured as expected. + $expected = array( + 'default' => array( + 'label' => 'hidden', + 'type' => 'text_default', + 'weight' => 0, + 'settings' => array(), + ), + 'teaser' => array( + 'label' => 'hidden', + 'type' => 'text_summary_or_trimmed', + 'weight' => 0, + 'settings' => array( + 'trim_length' => 600, + ), + ), + ); + $this->assertEqual($displays['default']['content']['body'], $expected['default']); + $this->assertEqual($displays['teaser']['content']['body'], $expected['teaser']); + + // Check that the display key in the instance data was removed. + $body_instance = field_info_instance('node', 'body', 'article'); + $this->assertTrue(!isset($body_instance['display'])); + + // Check that the 'language' extra field is configured as expected. + $expected = array( + 'default' => array( + 'weight' => -1, + 'visible' => 1, + ), + 'teaser' => array( + 'visible' => 0, + ), + ); + $this->assertEqual($displays['default']['content']['language'], $expected['default']); + $this->assertEqual($displays['teaser']['content']['language'], $expected['teaser']); + } +} + diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php index d86f7cd..04070c9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php @@ -57,7 +57,9 @@ public function testUserPictureUpgrade() { $this->assertEqual($instance['settings']['max_filesize'], '700 KB', 'User picture maximum filesize has been migrated.'); $this->assertEqual($instance['description'], 'These are user picture guidelines.', 'User picture guidelines are now the user picture field description.'); $this->assertEqual($instance['settings']['file_directory'], 'user_pictures_dir', 'User picture directory path has been migrated.'); - $this->assertEqual($instance['display']['default']['settings']['image_style'], 'thumbnail', 'User picture image style setting has been migrated.'); + + $display_options = entity_get_display('user', 'user', 'default')->getComponent('user_picture'); + $this->assertEqual($display_options['settings']['image_style'], 'thumbnail', 'User picture image style setting has been migrated.'); // Verify compact view mode default settings. $this->drupalGet('admin/config/people/accounts/display/compact'); diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 2bb1dde..677323f 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -23,6 +23,32 @@ function entity_test_entity_info_alter(&$info) { } /** + * Implements hook_field_extra_fields(). + */ +function entity_test_field_extra_fields() { + $extra['entity_test']['entity_test'] = array( + 'display' => array( + // Note: those extra fields do not currently display anything, they are + // just used in \Drupal\system\Tests\Entity\EntityDisplayTest to test the + // behavior of entity display objects, + 'display_extra_field' => array( + 'label' => t('Display extra field'), + 'description' => t('An extra field on the display side.'), + 'weight' => 5, + 'vidible' => TRUE, + ), + 'display_extra_field_hidden' => array( + 'label' => t('Display extra field (hidden)'), + 'description' => t('An extra field on the display side, hidden by default.'), + 'visible' => FALSE, + ), + ) + ); + + return $extra; +} + +/** * Implements hook_permission(). */ function entity_test_permission() { diff --git a/core/modules/system/tests/upgrade/drupal-7.field.database.php b/core/modules/system/tests/upgrade/drupal-7.field.database.php new file mode 100644 index 0000000..0862650 --- /dev/null +++ b/core/modules/system/tests/upgrade/drupal-7.field.database.php @@ -0,0 +1,51 @@ + array( + 'teaser' => array( + 'custom_settings' => 1, + ), + 'full' => array( + 'custom_settings' => 0, + ), + 'rss' => array( + 'custom_settings' => 0, + ), + 'search_index' => array( + 'custom_settings' => 0, + ), + 'search_result' => array( + 'custom_settings' => 0, + ), + ), + 'extra_fields' => array( + 'form' => array(), + 'display' => array( + 'language' => array( + 'default' => array( + 'weight' => -1, + 'visible' => 1, + ), + 'teaser' => array( + 'weight' => 0, + 'visible' => 0, + ), + ), + ), + ), +); +db_insert('variable') + ->fields(array( + 'name' => 'field_bundle_settings_node__article', + 'value' => serialize($value), + )) + ->execute(); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermRenderController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermRenderController.php index c22f6e3..56cc036 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermRenderController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermRenderController.php @@ -25,11 +25,11 @@ public function buildContent(array $entities = array(), $view_mode = 'full', $la // Add the description if enabled. $bundle = $entity->bundle(); $entity_view_mode = $entity->content['#view_mode']; - $fields = field_extra_fields_get_display($this->entityType, $bundle, $entity_view_mode); - if (!empty($entity->description) && isset($fields['description']) && $fields['description']['visible']) { + $display = field_extra_fields_get_display($this->entityType, $bundle, $entity_view_mode); + if (!empty($entity->description) && !empty($display['description'])) { $entity->content['description'] = array( '#markup' => check_markup($entity->description, $entity->format, '', TRUE), - '#weight' => $fields['description']['weight'], + '#weight' => $display['description']['weight'], '#prefix' => '
', '#suffix' => '
', ); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php index e87d931..2e9712e 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php @@ -56,13 +56,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('node', 'article', 'default') + ->setComponent('taxonomy_' . $this->vocabulary->machine_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); } /** diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php index 37cbf4e..d377642 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php @@ -66,13 +66,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'full' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); } /** @@ -98,8 +98,9 @@ function testTaxonomyTermFieldMultipleVocabularies() { // Render the entity. $entity = field_test_entity_test_load($id); $entities = array($id => $entity); - field_attach_prepare_view('test_entity', $entities, 'full'); - $entity->content = field_attach_view('test_entity', $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + field_attach_prepare_view('test_entity', $entities, array($entity->bundle() => $display)); + $entity->content = field_attach_view('test_entity', $entity, $display); $this->content = drupal_render($entity->content); $this->assertText($term1->name, 'Term 1 name is displayed.'); $this->assertText($term2->name, 'Term 2 name is displayed.'); @@ -110,8 +111,9 @@ function testTaxonomyTermFieldMultipleVocabularies() { // Re-render the content. $entity = field_test_entity_test_load($id); $entities = array($id => $entity); - field_attach_prepare_view('test_entity', $entities, 'full'); - $entity->content = field_attach_view('test_entity', $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + field_attach_prepare_view('test_entity', $entities, array($entity->bundle() => $display)); + $entity->content = field_attach_view('test_entity', $entity, $display); $this->plainTextContent = FALSE; $this->content = drupal_render($entity->content); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php index 34065e1..578e454 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php @@ -61,13 +61,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'full' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('test_entity', 'test_bundle', 'full') + ->setComponent($this->field_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); } /** @@ -123,8 +123,9 @@ function testTaxonomyTermFieldWidgets() { // Display the object. $entity = field_test_entity_test_load($id); $entities = array($id => $entity); - field_attach_prepare_view('test_entity', $entities, 'full'); - $entity->content = field_attach_view('test_entity', $entity, 'full'); + $display = entity_get_display($entity->entityType(), $entity->bundle(), 'full'); + field_attach_prepare_view('test_entity', $entities, array($entity->bundle() => $display)); + $entity->content = field_attach_view('test_entity', $entity, $display); $this->content = drupal_render($entity->content); $this->assertText($term->label(), 'Term label is displayed.'); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php index 31d01c5..173637e 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php @@ -52,13 +52,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance_1); + entity_get_display('node', 'article', 'default') + ->setComponent($this->field_name_1, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); $this->field_name_2 = drupal_strtolower($this->randomName()); $this->field_2 = array( @@ -82,13 +82,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance_2); + entity_get_display('node', 'article', 'default') + ->setComponent($this->field_name_2, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); } /** diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index 4abbf62..9687dd8 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -48,13 +48,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('node', 'article', 'default') + ->setComponent($this->instance['field_name'], array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); } /** diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php index e273c1b..16acd2c 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php @@ -49,13 +49,13 @@ function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('node', 'article', 'default') + ->setComponent('taxonomy_' . $this->vocabulary->machine_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); } /** diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php index 26545d8..5be6808 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserPictureTest.php @@ -48,12 +48,9 @@ function setUp() { _user_install_picture_field(); // Remove 'summary' pseudo-field from compact view mode on the User entity. - $bundle_settings = field_bundle_settings('user', 'user'); - $bundle_settings['extra_fields']['display']['member_for']['compact'] = array( - 'visible' => FALSE, - 'weight' => 10, - ); - field_bundle_settings('user', 'user', $bundle_settings); + $display = entity_get_display('user', 'user', 'compact') + ->removeComponent('member_for') + ->save(); } /** diff --git a/core/modules/user/user.install b/core/modules/user/user.install index bb86002..6ed31a4 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -1026,19 +1026,30 @@ function _user_install_picture_field(array $settings = array()) { ), 'weight' => -1, ), - 'display' => array( - 'default' => array( - 'label' => 'hidden', - 'type' => $settings['formatter'], - 'settings' => array('image_style' => 'thumbnail', 'image_link' => 'content'), - ), - 'compact' => array( - 'label' => 'hidden', - 'type' => $settings['formatter'], - 'settings' => array('image_style' => $settings['image_style'], 'image_link' => 'content'), - ), - ), ); _update_7000_field_create_instance($field, $instance); + + // Assign display settings for the 'default' and 'compact' view modes. + entity_get_display('user', 'user', 'default') + ->setComponent('user_picture', array( + 'label' => 'hidden', + 'type' => $settings['formatter'], + 'settings' => array( + 'image_style' => 'thumbnail', + 'image_link' => 'content', + ), + )) + ->save(); + entity_get_display('user', 'user', 'compact') + ->setComponent('user_picture', array( + 'label' => 'hidden', + 'type' => $settings['formatter'], + 'settings' => array( + 'image_style' => $settings['image_style'], + 'image_link' => 'content', + ), + )) + ->save(); + return $field; } diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php index 7e37452..81c8cf3 100644 --- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php @@ -77,13 +77,13 @@ protected function setUp() { 'widget' => array( 'type' => 'options_select', ), - 'display' => array( - 'full' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), ); field_create_instance($this->instance); + entity_get_display('node', 'page', 'full') + ->setComponent($this->field_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); // Create a time in the past for the archive. $time = time() - 3600; diff --git a/core/modules/views/lib/Drupal/views/Tests/Taxonomy/TaxonomyTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Taxonomy/TaxonomyTestBase.php index 9c2835a..f5dfc18 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Taxonomy/TaxonomyTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/Taxonomy/TaxonomyTestBase.php @@ -101,18 +101,21 @@ protected function mockStandardInstall() { '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); + + entity_get_display('node', 'article', 'default') + ->setComponent($instance['field_name'], array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); + entity_get_display('node', 'article', 'teaser') + ->setComponent($instance['field_name'], array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php index cf3d19e..8b3e019 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php @@ -77,18 +77,21 @@ function setUp() { 'widget' => array( 'type' => 'taxonomy_autocomplete', ), - 'display' => array( - 'default' => array( - 'type' => 'taxonomy_term_reference_link', - 'weight' => 10, - ), - 'teaser' => array( - 'type' => 'taxonomy_term_reference_link', - 'weight' => 10, - ), - ), ); field_create_instance($this->tag_instance); + + entity_get_display('node', $this->node_type_with_tags->type, 'default') + ->setComponent('field_views_testing_tags', array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); + entity_get_display('node', $this->node_type_with_tags->type, 'teaser') + ->setComponent('field_views_testing_tags', array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); } /** diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index 071bf31..9b471ce 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -295,19 +295,22 @@ 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); + // Assign display settings for the 'default' and 'teaser' view modes. + entity_get_display('node', 'article', 'default') + ->setComponent($instance['field_name'], array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); + entity_get_display('node', 'article', 'teaser') + ->setComponent($instance['field_name'], array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + )) + ->save(); // Create an image field named "Image", enabled for the 'article' content type. // Many of the following values will be defaulted, they're included here as an illustrative examples. @@ -359,34 +362,34 @@ function standard_install() { ), 'weight' => -1, ), - - 'display' => array( - 'default' => array( - 'label' => 'hidden', - 'type' => 'image', - 'settings' => array('image_style' => 'large', 'image_link' => ''), - 'weight' => -1, - ), - 'teaser' => array( - 'label' => 'hidden', - 'type' => 'image', - 'settings' => array('image_style' => 'medium', 'image_link' => 'content'), - 'weight' => -1, - ), - ), ); field_create_instance($instance); + // Assign display settings for the 'default' and 'teaser' view modes. + entity_get_display('node', 'article', 'default') + ->setComponent($instance['field_name'], array( + 'label' => 'hidden', + 'type' => 'image', + 'settings' => array('image_style' => 'large', 'image_link' => ''), + 'weight' => -1, + )) + ->save(); + entity_get_display('node', 'article', 'teaser') + ->setComponent($instance['field_name'], array( + 'label' => 'hidden', + 'type' => 'image', + 'settings' => array('image_style' => 'medium', 'image_link' => 'content'), + 'weight' => -1, + )) + ->save(); + // Create user picture field. module_load_install('user'); _user_install_picture_field(); - // Remove 'summary' pseudo-field from compact view mode on the User entity. - $bundle_settings = field_bundle_settings('user', 'user'); - $bundle_settings['extra_fields']['display']['member_for']['compact'] = array( - 'visible' => FALSE, - 'weight' => 10, - ); - field_bundle_settings('user', 'user', $bundle_settings); + // Hide 'summary' pseudo-field from compact view mode on the User entity. + entity_get_display('user', 'user', 'compact') + ->removeComponent('member_for') + ->save(); // Enable default permissions for system roles. $filtered_html_permission = filter_permission_name($filtered_html_format);