From cf805af249c160e5d8da632c72ee4d12719ff8f5 Mon Sep 17 00:00:00 2001 From: Lee Rowlands Date: Thu, 4 Jan 2018 07:29:24 +1000 Subject: [PATCH] Issue #2928699 by marcoscano, phenaproxima, amateescu, seanB, yoroy: Add an alter hook for the pre-configured field UI options and implement it in the Media module --- .../Core/Field/FieldTypePluginManager.php | 15 +++++++- .../Field/FieldTypePluginManagerInterface.php | 20 +++++++++++ .../PreconfiguredFieldUiOptionsInterface.php | 6 ++++ core/modules/field/field.api.php | 27 ++++++++++++++ .../modules/field_test/field_test.module | 11 ++++++ .../field_ui/src/Form/FieldStorageAddForm.php | 36 ++++++++++++++----- .../field_ui/src/Tests/ManageFieldsTest.php | 1 + core/modules/media/media.module | 19 ++++++++++ .../Functional/MediaFunctionalTestTrait.php | 1 + .../src/Functional/MediaUiFunctionalTest.php | 17 +++++++++ 10 files changed, 144 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php index 905cdb4f1bb..565fa4e6f74 100644 --- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php +++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php @@ -135,7 +135,7 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl // Add preconfigured definitions. foreach ($definitions as $id => $definition) { if (is_subclass_of($definition['class'], '\Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface')) { - foreach ($definition['class']::getPreconfiguredOptions() as $key => $option) { + foreach ($this->getPreconfiguredOptions($definition['id']) as $key => $option) { $definitions['field_ui:' . $id . ':' . $key] = [ 'label' => $option['label'], ] + $definition; @@ -150,6 +150,19 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl return $definitions; } + /** + * {@inheritdoc} + */ + public function getPreconfiguredOptions($field_type) { + $options = []; + $class = $this->getPluginClass($field_type); + if (is_subclass_of($class, '\Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface')) { + $options = $class::getPreconfiguredOptions(); + $this->moduleHandler->alter('field_ui_preconfigured_options', $options, $field_type); + } + return $options; + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManagerInterface.php b/core/lib/Drupal/Core/Field/FieldTypePluginManagerInterface.php index 3a5c7b546cd..d937aabd446 100644 --- a/core/lib/Drupal/Core/Field/FieldTypePluginManagerInterface.php +++ b/core/lib/Drupal/Core/Field/FieldTypePluginManagerInterface.php @@ -84,6 +84,26 @@ interface FieldTypePluginManagerInterface extends PluginManagerInterface, Catego */ public function getUiDefinitions(); + /** + * Returns preconfigured field options for a field type. + * + * This is a wrapper around + * \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions() + * allowing modules to alter the result of this method by implementing + * hook_field_ui_preconfigured_options_alter(). + * + * @param string $field_type + * The field type plugin ID. + * + * @return array + * A multi-dimensional array as returned from + * \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions(). + * + * @see \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions() + * @see hook_field_ui_preconfigured_options_alter() + */ + public function getPreconfiguredOptions($field_type); + /** * Returns the PHP class that implements the field type plugin. * diff --git a/core/lib/Drupal/Core/Field/PreconfiguredFieldUiOptionsInterface.php b/core/lib/Drupal/Core/Field/PreconfiguredFieldUiOptionsInterface.php index 9130e8c017c..684168aacb8 100644 --- a/core/lib/Drupal/Core/Field/PreconfiguredFieldUiOptionsInterface.php +++ b/core/lib/Drupal/Core/Field/PreconfiguredFieldUiOptionsInterface.php @@ -16,6 +16,11 @@ interface PreconfiguredFieldUiOptionsInterface { /** * Returns preconfigured field options for a field type. * + * Note that if you want to give modules an opportunity to alter the result + * of this method, you should call + * \Drupal\Core\Field\FieldTypePluginManagerInterface::getPreconfiguredOptions() + * instead. + * * @return mixed[][] * A multi-dimensional array with string keys and the following structure: * - label: The label to show in the field type selection list. @@ -35,6 +40,7 @@ interface PreconfiguredFieldUiOptionsInterface { * @see \Drupal\field\Entity\FieldStorageConfig * @see \Drupal\field\Entity\FieldConfig * @see \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent() + * @see \Drupal\Core\Field\FieldTypePluginManagerInterface::getPreconfiguredOptions() */ public static function getPreconfiguredOptions(); diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index 42bdd1e7125..130f9231803 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -58,6 +58,33 @@ function hook_field_info_alter(&$info) { } } +/** + * Perform alterations on preconfigured field options. + * + * @param array $options + * Array of options as returned from + * \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions(). + * @param string $field_type + * The field type plugin ID. + * + * @see \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions() + */ +function hook_field_ui_preconfigured_options_alter(array &$options, $field_type) { + // If the field is not an "entity_reference"-based field, bail out. + /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */ + $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); + $class = $field_type_manager->getPluginClass($field_type); + if (!is_a($class, 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', TRUE)) { + return; + } + + // Set the default formatter for media in entity reference fields to be the + // "Rendered entity" formatter. + if (!empty($options['media'])) { + $options['media']['entity_view_display']['type'] = 'entity_reference_entity_view'; + } +} + /** * Forbid a field storage update from occurring. * diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module index cc1f9fa9054..9121e942eae 100644 --- a/core/modules/field/tests/modules/field_test/field_test.module +++ b/core/modules/field/tests/modules/field_test/field_test.module @@ -171,3 +171,14 @@ function field_test_entity_bundle_field_info_alter(&$fields, EntityTypeInterface ]); } } + +/** + * Implements hook_field_ui_preconfigured_options_alter(). + */ +function field_test_field_ui_preconfigured_options_alter(array &$options, $field_type) { + if ($field_type === 'test_field_with_preconfigured_options') { + $options['custom_options']['entity_view_display']['settings'] = [ + 'test_formatter_setting_multiple' => 'altered dummy test string', + ]; + } +} diff --git a/core/modules/field_ui/src/Form/FieldStorageAddForm.php b/core/modules/field_ui/src/Form/FieldStorageAddForm.php index 97e3ce7d102..584cc968467 100644 --- a/core/modules/field_ui/src/Form/FieldStorageAddForm.php +++ b/core/modules/field_ui/src/Form/FieldStorageAddForm.php @@ -312,14 +312,16 @@ class FieldStorageAddForm extends FormBase { 'translatable' => FALSE, ]; $widget_id = $formatter_id = NULL; + $widget_settings = $formatter_settings = []; // Check if we're dealing with a preconfigured field. if (strpos($field_storage_values['type'], 'field_ui:') !== FALSE) { list(, $field_type, $option_key) = explode(':', $field_storage_values['type'], 3); $field_storage_values['type'] = $field_type; - $field_type_class = $this->fieldTypePluginManager->getDefinition($field_type)['class']; - $field_options = $field_type_class::getPreconfiguredOptions()[$option_key]; + $field_definition = $this->fieldTypePluginManager->getDefinition($field_type); + $options = $this->fieldTypePluginManager->getPreconfiguredOptions($field_definition['id']); + $field_options = $options[$option_key]; // Merge in preconfigured field storage options. if (isset($field_options['field_storage_config'])) { @@ -340,7 +342,9 @@ class FieldStorageAddForm extends FormBase { } $widget_id = isset($field_options['entity_form_display']['type']) ? $field_options['entity_form_display']['type'] : NULL; + $widget_settings = isset($field_options['entity_form_display']['settings']) ? $field_options['entity_form_display']['settings'] : []; $formatter_id = isset($field_options['entity_view_display']['type']) ? $field_options['entity_view_display']['type'] : NULL; + $formatter_settings = isset($field_options['entity_view_display']['settings']) ? $field_options['entity_view_display']['settings'] : []; } // Create the field storage and field. @@ -349,8 +353,8 @@ class FieldStorageAddForm extends FormBase { $field = $this->entityManager->getStorage('field_config')->create($field_values); $field->save(); - $this->configureEntityFormDisplay($values['field_name'], $widget_id); - $this->configureEntityViewDisplay($values['field_name'], $formatter_id); + $this->configureEntityFormDisplay($values['field_name'], $widget_id, $widget_settings); + $this->configureEntityViewDisplay($values['field_name'], $formatter_id, $formatter_settings); // Always show the field settings step, as the cardinality needs to be // configured for new fields. @@ -418,12 +422,20 @@ class FieldStorageAddForm extends FormBase { * The field name. * @param string|null $widget_id * (optional) The plugin ID of the widget. Defaults to NULL. + * @param array $widget_settings + * (optional) An array of widget settings. Defaults to an empty array. */ - protected function configureEntityFormDisplay($field_name, $widget_id = NULL) { + protected function configureEntityFormDisplay($field_name, $widget_id = NULL, array $widget_settings = []) { + $options = []; + if ($widget_id) { + $options['type'] = $widget_id; + if (!empty($widget_settings)) { + $options['settings'] = $widget_settings; + } + } // Make sure the field is displayed in the 'default' form mode (using // default widget and settings). It stays hidden for other form modes // until it is explicitly configured. - $options = $widget_id ? ['type' => $widget_id] : []; entity_get_form_display($this->entityTypeId, $this->bundle, 'default') ->setComponent($field_name, $options) ->save(); @@ -436,12 +448,20 @@ class FieldStorageAddForm extends FormBase { * The field name. * @param string|null $formatter_id * (optional) The plugin ID of the formatter. Defaults to NULL. + * @param array $formatter_settings + * (optional) An array of formatter settings. Defaults to an empty array. */ - protected function configureEntityViewDisplay($field_name, $formatter_id = NULL) { + protected function configureEntityViewDisplay($field_name, $formatter_id = NULL, array $formatter_settings = []) { + $options = []; + if ($formatter_id) { + $options['type'] = $formatter_id; + if (!empty($formatter_settings)) { + $options['settings'] = $formatter_settings; + } + } // 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. - $options = $formatter_id ? ['type' => $formatter_id] : []; entity_get_display($this->entityTypeId, $this->bundle, 'default') ->setComponent($field_name, $options) ->save(); diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php index d08ab03ba4c..112e3923c49 100644 --- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php +++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php @@ -750,6 +750,7 @@ class ManageFieldsTest extends WebTestBase { $this->assertEqual($form_display->getComponent('field_test_custom_options')['type'], 'test_field_widget_multiple'); $view_display = entity_get_display('node', 'article', 'default'); $this->assertEqual($view_display->getComponent('field_test_custom_options')['type'], 'field_test_multiple'); + $this->assertEqual($view_display->getComponent('field_test_custom_options')['settings']['test_formatter_setting_multiple'], 'altered dummy test string'); } /** diff --git a/core/modules/media/media.module b/core/modules/media/media.module index dc2d898e3c8..5df059c284b 100644 --- a/core/modules/media/media.module +++ b/core/modules/media/media.module @@ -96,3 +96,22 @@ function template_preprocess_media(array &$variables) { $variables['content'][$key] = $variables['elements'][$key]; } } + +/** + * Implements hook_field_ui_preconfigured_options_alter(). + */ +function media_field_ui_preconfigured_options_alter(array &$options, $field_type) { + // If the field is not an "entity_reference"-based field, bail out. + /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */ + $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); + $class = $field_type_manager->getPluginClass($field_type); + if (!is_a($class, 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', TRUE)) { + return; + } + + // Set the default formatter for media in entity reference fields to be the + // "Rendered entity" formatter. + if (!empty($options['media'])) { + $options['media']['entity_view_display']['type'] = 'entity_reference_entity_view'; + } +} diff --git a/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php index d56e1565f75..4fa7b54280f 100644 --- a/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php +++ b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php @@ -32,6 +32,7 @@ trait MediaFunctionalTestTrait { 'administer content types', 'administer node fields', 'administer node form display', + 'administer node display', 'bypass node access', ]; diff --git a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php index 4aa3f001f6d..8a0a82fffb0 100644 --- a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php +++ b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php @@ -175,4 +175,21 @@ class MediaUiFunctionalTest extends MediaFunctionalTestBase { $assert_session->pageTextContains($second_media_item->getName()); } + /** + * Test that media in ER fields use the Rendered Entity formatter by default. + */ + public function testRenderedEntityReferencedMedia() { + $page = $this->getSession()->getPage(); + $assert_session = $this->assertSession(); + + $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']); + $this->drupalGet('/admin/structure/types/manage/page/fields/add-field'); + $page->selectFieldOption('new_storage_type', 'field_ui:entity_reference:media'); + $page->fillField('label', 'Foo field'); + $page->fillField('field_name', 'foo_field'); + $page->pressButton('Save and continue'); + $this->drupalGet('/admin/structure/types/manage/page/display'); + $assert_session->fieldValueEquals('fields[field_foo_field][type]', 'entity_reference_entity_view'); + } + }