diff --git a/core/core.services.yml b/core/core.services.yml index 8bce755..cdc7818 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1653,4 +1653,6 @@ services: response_filter.rss.relative_url: class: Drupal\Core\EventSubscriber\RssResponseRelativeUrlFilter tags: - - { name: event_subscriber } + plugin.manager.entity.display_component_handler: + class: Drupal\Core\Entity\DisplayComponentHandlerPluginManager + parent: default_plugin_manager diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php index cd90ecc..744deb7 100644 --- a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php +++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php @@ -129,33 +129,6 @@ public function __construct(array $values, $entity_type) { /** * {@inheritdoc} */ - public function getRenderer($field_name) { - if (isset($this->plugins[$field_name])) { - return $this->plugins[$field_name]; - } - - // Instantiate the widget object from the stored display properties. - if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) { - $widget = $this->pluginManager->getInstance(array( - 'field_definition' => $definition, - 'form_mode' => $this->originalMode, - // No need to prepare, defaults have been merged in setComponent(). - 'prepare' => FALSE, - 'configuration' => $configuration - )); - } - else { - $widget = NULL; - } - - // Persist the widget object. - $this->plugins[$field_name] = $widget; - return $widget; - } - - /** - * {@inheritdoc} - */ public function buildForm(FieldableEntityInterface $entity, array &$form, FormStateInterface $form_state) { // Set #parents to 'top-level' by default. $form += array('#parents' => array()); diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php index b6c73e5..053f661 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php @@ -79,6 +79,11 @@ protected $hidden = array(); /** + * The renderer objects used for this display, keyed by component name. + */ + protected $renderers = array(); + + /** * The original view or form mode that was requested (case of view/form modes * being configured to fall back to the 'default' display). * @@ -115,6 +120,18 @@ protected $renderer; /** + * A mapping of display elements and its corresponding handler. + */ + protected $handlers; + + /** + * The display component handler plugin manager. + * + * @var \Drupal\Core\Entity\DisplayComponentHandlerPluginManager + */ + protected $handlerManager; + + /** * {@inheritdoc} */ public function __construct(array $values, $entity_type) { @@ -138,6 +155,8 @@ public function __construct(array $values, $entity_type) { parent::__construct($values, $entity_type); + $this->handlerManager = \Drupal::service('plugin.manager.entity.display_component_handler'); + $this->originalMode = $this->mode; $this->init(); @@ -321,13 +340,14 @@ public function calculateDependencies() { */ public function toArray() { $properties = parent::toArray(); - // Do not store options for fields whose display is not set to be - // configurable. - foreach ($this->getFieldDefinitions() as $field_name => $definition) { - if (!$definition->isDisplayConfigurable($this->displayContext)) { - unset($properties['content'][$field_name]); - unset($properties['hidden'][$field_name]); - } + + // Let the component handlers add missing components. + if (!$this->handlerManager) { + $this->handlerManager = \Drupal::service('plugin.manager.entity.display_component_handler'); + } + $handlers = $this->handlerManager->getDefinitions(); + foreach (array_keys($handlers) as $type) { + $properties = $this->getComponentHandler($type)->massageOut($properties); } return $properties; @@ -366,16 +386,20 @@ public function setComponent($name, array $options = array()) { $options['weight'] = isset($max) ? $max + 1 : 0; } - // For a field, fill in default options. - if ($field_definition = $this->getFieldDefinition($name)) { - $options = $this->pluginManager->prepareConfiguration($field_definition->getType(), $options); + // Massage in some values. + $handler = $this->getComponentHandlerByElementName($name); + if ($handler) { + $options = $handler->massageIn($name, $options); } // Ensure we always have an empty settings and array. $options += ['settings' => [], 'third_party_settings' => []]; $this->content[$name] = $options; + + // Unset these to ensure new settings are picked up. unset($this->hidden[$name]); + unset($this->renderers[$name]); unset($this->plugins[$name]); return $this; @@ -595,4 +619,69 @@ protected function getLogger() { return \Drupal::logger('system'); } + /** + * Finds component handler by element name. + * + * @param string $name + * The element name. + * + * @return \Drupal\Core\Entity\DisplayComponentHandlerInterface + */ + public function getComponentHandlerByElementName($name) { + if (!isset($this->handlers[$name])) { + $handlers = $this->handlerManager->getDefinitions(); + foreach (array_keys($handlers) as $type) { + $handler = $this->getComponentHandler($type); + if ($handler && $handler->hasElement($name)) { + break; + } + $handler = NULL; + } + $this->handlers[$name] = $handler; + } + + return $this->handlers[$name]; + } + + /** + * Instantiates component handler. + * + * @param string $type + * The type of component handler (field, extra_field). + * + * @return \Drupal\Core\Entity\DisplayComponentHandlerInterface + */ + public function getComponentHandler($type) { + $handler = $this->handlerManager->getInstance(array('type' => $type)); + if ($handler) { + $handler->setContext(array( + 'entity_type' => $this->targetEntityType, + 'bundle' => $this->bundle, + 'mode' => $this->originalMode, + 'display_context' => $this->displayContext, + )); + } + return $handler; + } + + /**+ + * {@inheritdoc} + */ + public function getRenderer($name) { + if (!isset($this->content[$name])) { + return NULL; + } + + if (!array_key_exists($name, $this->renderers)) { + if ($handler = $this->getComponentHandlerByElementName($name)) { + $options = $this->getComponent($name); + $this->renderers[$name] = $handler->getRenderer($name, $options); + } + else { + $this->renderers[$name] = NULL; + } + } + return $this->renderers[$name]; + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 1b3e6c0..4c7f7e4 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -465,6 +465,12 @@ protected function getSingleFieldDisplay($entity, $field_name, $display_options) $bundle = $entity->bundle(); $key = $entity_type_id . ':' . $bundle . ':' . $field_name . ':' . Crypt::hashBase64(serialize($display_options)); if (!isset($this->singleFieldDisplays[$key])) { + + $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_id); + if (isset($definitions[$field_name])) { + $display_options = \Drupal::service('plugin.manager.field.formatter')->prepareConfiguration($definitions[$field_name]->getType(), $display_options); + } + $this->singleFieldDisplays[$key] = EntityViewDisplay::create(array( 'targetEntityType' => $entity_type_id, 'bundle' => $bundle, diff --git a/core/lib/Drupal/Core/Entity/entity.api.php b/core/lib/Drupal/Core/Entity/entity.api.php index 9f6ef8b..bda7740 100644 --- a/core/lib/Drupal/Core/Entity/entity.api.php +++ b/core/lib/Drupal/Core/Entity/entity.api.php @@ -2017,5 +2017,20 @@ function hook_entity_extra_field_info_alter(&$info) { } /** + * Modify the list of available component handler plugins. + * + * This hook may be used to modify plugin properties after they have been + * specified by other modules. + * + * @param $plugins + * An array of all the existing plugin definitions, passed by reference. + * + * @see DisplayComponentHandlerPluginManager + */ +function hook_display_component_handler_info_alter(array &$plugins) { + $plugins['someplugin']['label'] = t('Better name'); +} + +/** * @} End of "addtogroup hooks". */ diff --git a/core/lib/Drupal/Core/Field/FormatterPluginManager.php b/core/lib/Drupal/Core/Field/FormatterPluginManager.php index ca9bd06..65e1d3a 100644 --- a/core/lib/Drupal/Core/Field/FormatterPluginManager.php +++ b/core/lib/Drupal/Core/Field/FormatterPluginManager.php @@ -56,6 +56,12 @@ public function createInstance($plugin_id, array $configuration = array()) { $plugin_definition = $this->getDefinition($plugin_id); $plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition); + // @TODO, is this missing somewhere else? + if (!isset($configuration['label'])) { + $configuration['label'] = ''; + assert(FALSE, sprintf('The %s does not have a label', $plugin_id)); + } + // @todo This is copied from \Drupal\Core\Plugin\Factory\ContainerFactory. // Find a way to restore sanity to // \Drupal\Core\Field\FormatterBase::__construct().