diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php index bcdf86f..62df554 100644 --- a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php +++ b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php @@ -130,13 +130,13 @@ public function viewElements(FieldItemListInterface $items) { $this->currentUser->hasPermission('administer comments'))) { $mode = $comment_settings['default_mode']; $comments_per_page = $comment_settings['per_page']; - if ($cids = comment_get_thread($entity, $field_name, $mode, $comments_per_page, $this->settings['pager_id'])) { + if ($cids = comment_get_thread($entity, $field_name, $mode, $comments_per_page, $this->getSetting('pager_id'))) { $comments = $this->storageController->loadMultiple($cids); comment_prepare_thread($comments); $build = $this->viewBuilder->viewMultiple($comments); $build['pager']['#theme'] = 'pager'; - if (!empty($this->settings['pager_id'])) { - $build['pager']['#element'] = $this->settings['pager_id']; + if ($this->getSetting('pager_id')) { + $build['pager']['#element'] = $this->getSetting('pager_id'); } $output['comments'] = $build; } diff --git a/core/modules/entity/entity.services.yml b/core/modules/entity/entity.services.yml new file mode 100644 index 0000000..81ab979 --- /dev/null +++ b/core/modules/entity/entity.services.yml @@ -0,0 +1,4 @@ +services: + plugin.manager.entity.display_component_handler: + class: Drupal\entity\Plugin\Type\DisplayComponentHandlerPluginManager + parent: default_plugin_manager diff --git a/core/modules/entity/lib/Drupal/entity/Annotation/DisplayComponent.php b/core/modules/entity/lib/Drupal/entity/Annotation/DisplayComponent.php new file mode 100644 index 0000000..9a41976 --- /dev/null +++ b/core/modules/entity/lib/Drupal/entity/Annotation/DisplayComponent.php @@ -0,0 +1,26 @@ +context = $context; + } + + /** + * {@inheritdoc} + */ + public function prepareDisplayComponents(array &$components, array &$hidden_components) { + } + + /** + * {@inheritdoc} + */ + public function hasElement($name) { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function massageIn($name, array $options) { + return $options; + } + + /** + * {@inheritdoc} + */ + public function massageOut($properties) { + return $properties; + } + + /** + * {@inheritdoc} + */ + public function getRenderer($name, array $options) { + } + +} diff --git a/core/modules/entity/lib/Drupal/entity/DisplayComponentHandlerInterface.php b/core/modules/entity/lib/Drupal/entity/DisplayComponentHandlerInterface.php new file mode 100644 index 0000000..a13a184 --- /dev/null +++ b/core/modules/entity/lib/Drupal/entity/DisplayComponentHandlerInterface.php @@ -0,0 +1,88 @@ +pluginManager = \Drupal::service('plugin.manager.field.widget'); - parent::__construct($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 serialize() { // Only store the definition, not external objects or derived data. $data = $this->getExportProperties() + array('entityType' => $this->getEntityTypeId()); diff --git a/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php b/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php index 9e6642f..9be0bf7 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php @@ -35,40 +35,4 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn */ protected $displayContext = 'view'; - /** - * {@inheritdoc} - */ - public function __construct(array $values, $entity_type) { - $this->pluginManager = \Drupal::service('plugin.manager.field.formatter'); - - parent::__construct($values, $entity_type); - } - - /** - * {@inheritdoc} - */ - public function getRenderer($field_name) { - if (isset($this->plugins[$field_name])) { - return $this->plugins[$field_name]; - } - - // Instantiate the formatter object from the stored display properties. - if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) { - $formatter = $this->pluginManager->getInstance(array( - 'field_definition' => $definition, - 'view_mode' => $this->originalMode, - // No need to prepare, defaults have been merged in setComponent(). - 'prepare' => FALSE, - 'configuration' => $configuration - )); - } - else { - $formatter = NULL; - } - - // Persist the formatter object. - $this->plugins[$field_name] = $formatter; - return $formatter; - } - } diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php index 6a90eab..a0c912a 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php +++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php @@ -46,12 +46,6 @@ */ public $bundle; - /** - * A list of field definitions eligible for configuration in this display. - * - * @var \Drupal\Core\Field\FieldDefinitionInterface[] - */ - protected $fieldDefinitions; /** * View or form mode to be displayed. @@ -91,25 +85,28 @@ public $originalMode; /** - * The plugin objects used for this display, keyed by field name. + * The renderer objects used for this display, keyed by component name. + */ + protected $renderers = array(); + + /* + * The display component handler plugin manager. * - * @var array + * @var \Drupal\entity\Plugin\Type\DisplayComponentHandlerPluginManager */ - protected $plugins = array(); + protected $handlerManager; /** - * Context in which this entity will be used (e.g. 'display', 'form'). + * Context in which this entity will be used (e.g. 'view', 'form'). * * @var string */ protected $displayContext; /** - * The plugin manager used by this entity type. - * - * @var \Drupal\Component\Plugin\PluginManagerBase + * A mapping of display elements and its corresponding handler */ - protected $pluginManager; + protected $handlers; /** * {@inheritdoc} @@ -123,16 +120,15 @@ public function __construct(array $values, $entity_type) { // throw new \InvalidArgumentException('Missing required properties for an EntityDisplayBase entity.'); // } - // A plugin manager and a context type needs to be set by extending classes. - if (!isset($this->pluginManager)) { - throw new \RuntimeException('Missing plugin manager.'); - } + if (!isset($this->displayContext)) { throw new \RuntimeException('Missing display context type.'); } parent::__construct($values, $entity_type); + $this->handlerManager =\Drupal::service('plugin.manager.entity.display_component_handler'); + $this->originalMode = $this->mode; $this->init(); @@ -182,13 +178,10 @@ public function getExportProperties() { $properties[$name] = $this->get($name); } - // 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. + $handlers = $this->handlerManager->getDefinitions(); + foreach (array_keys($handlers) as $type) { + $properties = $this->getComponentHandler($type)->massageOut($properties); } return $properties; @@ -203,38 +196,10 @@ public function getExportProperties() { * - or that are not supposed to be configurable. */ protected function init() { - // Fill in defaults for extra fields. - $extra_fields = field_info_extra_fields($this->targetEntityType, $this->bundle, ($this->displayContext == 'view' ? 'display' : $this->displayContext)); - foreach ($extra_fields as $name => $definition) { - if (!isset($this->content[$name]) && !isset($this->hidden[$name])) { - // Extra fields are visible by default unless they explicitly say so. - if (!isset($definition['visible']) || $definition['visible'] == TRUE) { - $this->content[$name] = array( - 'weight' => $definition['weight'] - ); - } - else { - $this->hidden[$name] = TRUE; - } - } - } - - // Fill in defaults for fields. - $fields = $this->getFieldDefinitions(); - foreach ($fields as $name => $definition) { - if (!$definition->isDisplayConfigurable($this->displayContext) || (!isset($this->content[$name]) && !isset($this->hidden[$name]))) { - $options = $definition->getDisplayOptions($this->displayContext); - - if (!empty($options['type']) && $options['type'] == 'hidden') { - $this->hidden[$name] = TRUE; - } - elseif ($options) { - $this->content[$name] = $this->pluginManager->prepareConfiguration($definition->getType(), $options); - } - // Note: (base) fields that do not specify display options are not - // tracked in the display at all, in order to avoid cluttering the - // configuration that gets saved back. - } + // Let the component handlers add missing components. + $handlers = $this->handlerManager->getDefinitions(); + foreach (array_keys($handlers) as $type) { + $this->getComponentHandler($type)->prepareDisplayComponents($this->content, $this->hidden); } } @@ -271,14 +236,16 @@ 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) { + // @todo sometimes handler not found. + $options = $handler->massageIn($name, $options); } $this->content[$name] = $options; unset($this->hidden[$name]); - unset($this->plugins[$name]); + unset($this->renderers[$name]); return $this; } @@ -289,7 +256,6 @@ public function setComponent($name, array $options = array()) { public function removeComponent($name) { $this->hidden[$name] = TRUE; unset($this->content[$name]); - unset($this->plugins[$name]); return $this; } @@ -314,41 +280,68 @@ public function getHighestWeight() { } /** - * Returns the field definition of a field. + * Finds component handler by element name. + * + * @param string $name + * The element name. + * + * @return \Drupal\entity\DisplayComponentHandlerInterface */ - protected function getFieldDefinition($field_name) { - $definitions = $this->getFieldDefinitions(); - return isset($definitions[$field_name]) ? $definitions[$field_name] : NULL; + 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]; } /** - * Returns the definitions of the fields that are candidate for display. + * Instantiates component handler. + * + * @param string $type + * The type of component handler (field, extra_field). + * + * @return \Drupal\entity\DisplayComponentHandlerInterface */ - protected function getFieldDefinitions() { - if (!isset($this->fieldDefinitions)) { - // @todo Replace this with \Drupal::entityManager()->getFieldDefinition() - // when it can hand the $instance objects (and then reconsider the - // $this->fieldDefinitions static cache ?) - // https://drupal.org/node/2114707 - $entity_manager = \Drupal::entityManager(); - $entity_info = $entity_manager->getDefinition($this->targetEntityType); - $definitions = array(); - if ($entity_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { - $entity = _field_create_entity_from_ids((object) array('entity_type' => $this->targetEntityType, 'bundle' => $this->bundle, 'entity_id' => NULL)); - foreach ($entity as $field_name => $items) { - $definitions[$field_name] = $items->getFieldDefinition(); - } - } + public function getComponentHandler($type) { + $handler = $this->handlerManager->getInstance(array('type' => $type)); + if ($handler) { + $handler->setContext(array( + 'entity_type' => $this->targetEntityType, + 'bundle' => $this->bundle, + 'view_mode' => $this->originalMode, + 'display_context' => $this->displayContext, + )); + } + return $handler; + } - // The display only cares about fields that specify display options. - // Discard base fields that are not rendered through formatters / widgets. - $display_context = $this->displayContext; - $this->fieldDefinitions = array_filter($definitions, function (FieldDefinitionInterface $definition) use ($display_context) { - return $definition->getDisplayOptions($display_context); - }); + /**+ + * {@inheritdoc} + */ + public function getRenderer($name) { + if (!isset($this->content[$name])) { + return NULL; } - return $this->fieldDefinitions; + 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/modules/entity/lib/Drupal/entity/Plugin/DisplayComponent/ExtraFieldDisplayComponentHandler.php b/core/modules/entity/lib/Drupal/entity/Plugin/DisplayComponent/ExtraFieldDisplayComponentHandler.php new file mode 100644 index 0000000..3363071 --- /dev/null +++ b/core/modules/entity/lib/Drupal/entity/Plugin/DisplayComponent/ExtraFieldDisplayComponentHandler.php @@ -0,0 +1,50 @@ +context['entity_type'], $this->context['bundle'], ($this->context['display_context'] == 'view' ? 'display' : $this->context['display_context'])); + foreach ($extra_fields as $name => $definition) { + if (!isset($components[$name]) && !isset($hidden_components[$name])) { + // Extra fields are visible by default unless they explicitly say so. + if (!isset($definition['visible']) || $definition['visible'] == TRUE) { + $components[$name] = array( + 'weight' => $definition['weight'] + ); + } + else { + $hidden_components[$name] = TRUE; + } + } + } + } + + /** + * {@inheritdoc} + */ + public function hasElement($name) { + $extra_fields = field_info_extra_fields($this->context['entity_type'], $this->context['bundle'], ($this->context['display_context'] == 'view' ? 'display' : $this->context['display_context'])); + return isset($extra_fields[$name]); + } + +} diff --git a/core/modules/entity/lib/Drupal/entity/Plugin/Type/DisplayComponentHandlerPluginManager.php b/core/modules/entity/lib/Drupal/entity/Plugin/Type/DisplayComponentHandlerPluginManager.php new file mode 100644 index 0000000..1081065 --- /dev/null +++ b/core/modules/entity/lib/Drupal/entity/Plugin/Type/DisplayComponentHandlerPluginManager.php @@ -0,0 +1,65 @@ +alterInfo($module_handler, 'display_component_handler_info'); + $this->setCacheBackend($cache_backend, $language_manager, 'display_component_handlers'); + } + + /** + * {@inheritdoc} + */ + public function getInstance(array $options) { + $plugin_id = $options['type']; + + if (!isset($this->plugins[$plugin_id]) && !array_key_exists($plugin_id, $this->plugins)) { + $this->plugins[$plugin_id] = $this->discovery->getDefinition($plugin_id) ? $this->createInstance($plugin_id) : NULL; + } + + return $this->plugins[$plugin_id]; + } + +} diff --git a/core/modules/field/lib/Drupal/field/Plugin/DisplayComponent/FieldDisplayComponentHandler.php b/core/modules/field/lib/Drupal/field/Plugin/DisplayComponent/FieldDisplayComponentHandler.php new file mode 100644 index 0000000..b56f459 --- /dev/null +++ b/core/modules/field/lib/Drupal/field/Plugin/DisplayComponent/FieldDisplayComponentHandler.php @@ -0,0 +1,223 @@ +fieldInfo = $field_info; + $this->formatterPluginManager = $formatter_plugin_manager; + $this->widgetPluginManager = $widget_plugin_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, + $container->get('field.info'), + $container->get('plugin.manager.field.formatter'), + $container->get('plugin.manager.field.widget') + ); + } + + /** + * {@inheritdoc} + */ + public function massageIn($name, array $options) { + $field_definition = $this->getFieldDefinition($name); + if (!isset($field_definition)) { + // The field in process of removal from display. + return $options; + } + if ($this->context['display_context'] == 'view') { + return $this->formatterPluginManager->prepareConfiguration($field_definition->getType(), $options); + } + else { + return $this->widgetPluginManager->prepareConfiguration($field_definition->getType(), $options); + } + } + + /** + * {@inheritdoc} + */ + public function massageOut($properties) { + // 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->context['display_context'])) { + unset($properties['content'][$field_name]); + unset($properties['hidden'][$field_name]); + } + } + + return $properties; + } + + /** + * {@inheritdoc} + */ + public function prepareDisplayComponents(array &$components, array &$hidden_components) { + if ($this->context['display_context'] == 'view') { + $plugin_manager = $this->formatterPluginManager; + } + else { + $plugin_manager = $this->widgetPluginManager; + } + + // Fill in defaults for fields. + $fields = $this->getFieldDefinitions(); + foreach ($fields as $name => $definition) { + if (!$definition->isDisplayConfigurable($this->context['display_context']) || (!isset($components[$name]) && !isset($hidden_components[$name]))) { + $options = $definition->getDisplayOptions($this->context['display_context']); + + if (!empty($options['type']) && $options['type'] == 'hidden') { + $hidden_components[$name] = TRUE; + } + elseif ($options) { + $components[$name] = $plugin_manager->prepareConfiguration($definition->getType(), $options); + } + // Note: (base) fields that do not specify display options are not + // tracked in the display at all, in order to avoid cluttering the + // configuration that gets saved back. + } + } + } + + /** + * {@inheritdoc} + */ + public function getRenderer($name, array $options) { + if (isset($options['type']) && ($definition = $this->getFieldDefinition($name))) { + if ($this->context['display_context'] == 'view') { + $plugin_manager = $this->formatterPluginManager; + $mode_key = 'view_mode'; + } + else { + $plugin_manager = $this->widgetPluginManager; + $mode_key = 'form_mode'; + } + + return $plugin_manager->getInstance(array( + 'field_definition' => $definition, + $mode_key => $this->context['view_mode'], + // No need to prepare, defaults have been merged when the options were + // written in the display. + 'prepare' => FALSE, + 'configuration' => $options, + )); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function hasElement($name) { + $field_defintion = $this->getFieldDefinition($name); + return isset($field_defintion); + } + + /** + * Returns the field definition of a field. + */ + protected function getFieldDefinition($field_name) { + $definitions = $this->getFieldDefinitions(); + return isset($definitions[$field_name]) ? $definitions[$field_name] : NULL; + } + + /** + * Returns the definitions of the fields that are candidate for display. + */ + protected function getFieldDefinitions() { + // @todo Replace this with \Drupal::entityManager()->getFieldDefinition() + // when it can hand the $instance objects (and then reconsider the + // $this->fieldDefinitions static cache ?) + // https://drupal.org/node/2114707 + $entity_manager = \Drupal::entityManager(); + $entity_info = $entity_manager->getDefinition($this->context['entity_type']); + $definitions = array(); + if ($entity_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { + $entity = _field_create_entity_from_ids((object) array('entity_type' => $this->context['entity_type'], 'bundle' => $this->context['bundle'], 'entity_id' => NULL)); + foreach ($entity as $field_name => $items) { + $definitions[$field_name] = $items->getFieldDefinition(); + } + } + + // The display only cares about fields that specify display options. + // Discard base fields that are not rendered through formatters / widgets. + $display_context = $this->context['display_context']; + $this->fieldDefinitions = array_filter($definitions, function (FieldDefinitionInterface $definition) use ($display_context) { + return $definition->getDisplayOptions($display_context); + }); + + return $this->fieldDefinitions; + } + +}