diff -u b/core/includes/entity.inc b/core/includes/entity.inc --- b/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -563,21 +563,62 @@ return entity_render_controller(reset($entities)->entityType())->viewMultiple($entities, $view_mode, $langcode); } - /** - * Returns an entity_display object for the view mode. + * Returns the entity_display object configured for a bundle and view mode. + * + * The function reads the entity_display object from the current configuration, + * or returns a ready-to-use empty one (with no cmponent set to be displayed) + * if no display is currently configured yet for this bundle and view mode. We + * do not preemptively create new empty entity_display configuration entries + * for each existing entity type and bundle whenever a new view mode becomes + * available. Instead, the actual configuration entries are only created when + * an entity_display object is explicitely configured and saved. This function + * streamlines manipulation of entity_display objects by always returning a + * consistent object that reflects the current state of the configuration. + * + * Note that the entity_display is only actually used at render time if the view + * mode itself is configured to use dedicated display settings for the bundle + * (if not, the entity_display for the 'default' view mode is used intead). + * Thus, when fecthing the display object to use for rendering an entity, the + * field_get_actual_view_mode() function is used first to determine which + * display to load. * - * The function reads the object from the current configuration, or returns a - * new object if the display is not configured yet. + * In the much more frequent use case of assigning suggested display options + * for a field in a given view mode, the entity_display object for this view + * mode should be used directly, without using field_get_actual_view_mode(), in + * order to avoid inadvertently modifying the output of other view modes that + * might use the 'default' display too. Those options will then be effectively + * applied only if the view mode is configured to use them. + * + * Example usage: + * - Set display options for the 'body' field on article nodes in the 'default' mode: + * @code + * entity_get_display('article', 'node', 'default') + * ->setContent('body', array( + * 'type' => 'text_summary_or_trimmed', + * 'settings' => array('trim_length' => '200') + * 'weight' => 1, + * )) + * ->save(); + * @endcode + * - Get options used for the 'body' field when rendering an article node in 'full' view mode; + * @code + * $view_mode = field_get_actual_view_mode('node', 'article', 'full'); + * $display = entity_get_display('node', 'article', $view_mode); + * $options = $display->getContent('body'); + * @endcode * * @param string $entity_type - * Machine readable name of the content entity. + * The entity type. * @param string $bundle - * Machine readable name of the content entity bundle. + * The bundle. * @param string $view_mode - * Machine readable name of the content entity view_mode. + * The view mode. * * @return \Drupal\entity\Plugin\Core\Entity\EntityDisplay + * The current entity_display configuration object for the view mode. + * + * @see field_get_actual_view_mode() */ function entity_get_display($entity_type, $bundle, $view_mode) { // Try loading the display from configuration. diff -u b/core/modules/entity/entity.install b/core/modules/entity/entity.install --- b/core/modules/entity/entity.install +++ b/core/modules/entity/entity.install @@ -10,8 +10,8 @@ /** * Returns the raw configuration object for an EntityDisplay entity. * - * The function reads from an existing CMI file if it exists, or creates a - * fresh structure. + * The function returns the existing configuration entry if it exists, or + * creates a fresh structure. * * @param string $entity_type * The entity type. diff -u b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php --- b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php @@ -23,7 +23,7 @@ * config_prefix = "entity.display", * entity_keys = { * "id" = "id", - * 'uuid" = "uuid" + * "uuid" = "uuid" * } * ) */ @@ -69,7 +69,7 @@ * * @var array */ - public $content = array(); + protected $content = array(); /** * The original view mode that was requested (case of view modes being @@ -117,6 +117,40 @@ } /** + * Creates a duplicate of the EntityDisplay object on a different view mode. + * + * The new object necessarily has the same $targetEntityType and $bundle + * properties than the original one. + * + * @param $view_mode + * The view mode for the new object. + * + * @return \Drupal\entity\Plugin\Core\Entity\EntityDisplay + * The new object. + */ + public function createCopy($view_mode) { + $display = $this->createDuplicate(); + $display->viewMode = $display->originalViewMode = $view_mode; + return $display; + } + + /** + * Gets the display options for all components. + * + * @return array + * The array of display options, keyed by component name. + */ + public function getComponents() { + $options = array(); + foreach ($this->content as $name => $options) { + if (!isset($options['visible']) || $options['visible']) { + $options[$name] = $options; + } + } + return $options; + } + + /** * Gets the display options set for a component. * * @param string $name diff -u b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php --- b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php @@ -14,7 +14,7 @@ */ class EntityDisplayTest extends DrupalUnitTestBase { - public static $modules = array('system', 'entity_test'); + public static $modules = array('entity_test'); public static function getInfo() { return array( @@ -24,12 +24,16 @@ ); } + protected function setUp() { + parent::setUp(); + + $this->enableModules(array('system', 'field')); + } + /** * Tests basic CRUD operations on EntityDisplay objects. */ public function testEntityDisplayCRUD() { - $this->enableModules(array('system', 'field')); - $display = entity_create('entity_display', array( 'targetEntityType' => 'entity_test', 'bundle' => 'entity_test', @@ -73,10 +77,8 @@ * 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. + // configuration entry exists. $display = entity_get_display('entity_test', 'entity_test', 'default'); $this->assertTrue($display->isNew()); @@ -95,8 +97,6 @@ * Tests the behavior of a field conponent within an EntityDisplay object. */ public function testExtraFieldComponent() { - $this->enableModules(array('system', 'field')); - $display = entity_create('entity_display', array( 'targetEntityType' => 'entity_test', 'bundle' => 'entity_test', @@ -119,7 +119,7 @@ * Tests the behavior of a field conponent within an EntityDisplay object. */ public function testFieldComponent() { - $this->enableModules(array('system', 'field', 'field_sql_storage', 'field_test')); + $this->enableModules(array('field_sql_storage', 'field_test')); $display = entity_create('entity_display', array( 'targetEntityType' => 'entity_test', diff -u b/core/modules/field/field.install b/core/modules/field/field.install --- b/core/modules/field/field.install +++ b/core/modules/field/field.install @@ -475,7 +475,7 @@ } } - // Write CMI files for the entity display objects. + // Save the displays to configuration. foreach ($displays as $config) { $config->save(); } diff -u b/core/modules/field/field.module b/core/modules/field/field.module --- b/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -740,6 +740,9 @@ * * @return string * The actual view mode to use for this entity type and bundle. + * + * @todo Move this function over to the entity API when the underlying + * variable is moved to configuration files. */ function field_get_actual_view_mode($entity_type, $bundle, $view_mode) { $view_mode_settings = field_view_mode_settings($entity_type, $bundle); diff -u b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php --- b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php @@ -421,7 +421,7 @@ */ 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); + $display = entity_get_display($this->entity_type, $this->bundle, $this->view_mode); // Collect data for 'regular' fields. foreach ($form['#fields'] as $field_name) { @@ -430,7 +430,7 @@ $values = $form_values['fields'][$field_name]; if ($values['type'] == 'hidden') { - $entity_display->removeComponent($field_name); + $display->removeComponent($field_name); } else { // Get formatter settings. They lie either directly in submitted form @@ -443,7 +443,7 @@ elseif (isset($form_state['formatter_settings'][$field_name])) { $settings = $form_state['formatter_settings'][$field_name]; } - elseif ($current_options = $entity_display->getComponent($field_name)) { + elseif ($current_options = $display->getComponent($field_name)) { $settings = $current_options['settings']; } @@ -451,7 +451,7 @@ $default_settings = field_info_formatter_settings($values['type']); $settings = array_intersect_key($settings, $default_settings); - $entity_display->setComponent($field_name, array( + $display->setComponent($field_name, array( 'label' => $values['label'], 'type' => $values['type'], 'weight' => $values['weight'], @@ -463,17 +463,17 @@ // Collect data for 'extra' fields. foreach ($form['#extra'] as $name) { if ($form_values['fields'][$name]['type'] == 'hidden') { - $entity_display->removeComponent($name); + $display->removeComponent($name); } else { - $entity_display->setComponent($name, array( + $display->setComponent($name, array( 'weight' => $form_values['fields'][$name]['weight'], )); } } // Save the display. - $entity_display->save(); + $display->save(); // Handle the 'view modes' checkboxes if present. if ($this->view_mode == 'default' && !empty($form_values['view_modes_custom'])) { @@ -481,22 +481,21 @@ $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) { - if (!empty($value) && empty($view_mode_settings[$view_mode_name]['custom_settings'])) { + foreach ($form_values['view_modes_custom'] as $view_mode => $value) { + if (!empty($value) && empty($view_mode_settings[$view_mode]['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; + if (!entity_load('entity_display', $this->entity_type . '.' . $this->bundle . '.' . $view_mode)) { + $display = entity_get_display($this->entity_type, $this->bundle, 'default')->createCopy($view_mode); $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"; + $view_mode_label = $entity_info['view_modes'][$view_mode]['label']; + $path = field_ui_bundle_admin_path($this->entity_type, $this->bundle) . "/display/$view_mode"; 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)))); } - $bundle_settings['view_modes'][$view_mode_name]['custom_settings'] = !empty($value); + $bundle_settings['view_modes'][$view_mode]['custom_settings'] = !empty($value); } // Save updated bundle settings. diff -u b/core/modules/node/node.module b/core/modules/node/node.module --- b/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -218,9 +218,10 @@ function node_entity_display_alter(EntityDisplay $display, $context) { // Hide field labels in 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'; + foreach ($display->getComponents() as $name => $options) { + if (isset($options['label'])) { + $options['label'] = 'hidden'; + $display->setComponent($name, $options); } } } diff -u b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php --- b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php @@ -34,7 +34,7 @@ public function testEntityDisplayUpgrade() { $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); - // Check that the entity display configuration files were created. + // Check that the configuration entries were created. $displays = array( 'default' => config('entity.display.node.article.default')->get(), 'teaser' => config('entity.display.node.article.teaser')->get(),