diff -u b/core/modules/media/src/Entity/MediaType.php b/core/modules/media/src/Entity/MediaType.php --- b/core/modules/media/src/Entity/MediaType.php +++ b/core/modules/media/src/Entity/MediaType.php @@ -3,10 +3,8 @@ namespace Drupal\media\Entity; use Drupal\Core\Config\Entity\ConfigEntityBundleBase; -use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; -use Drupal\field\FieldStorageConfigInterface; use Drupal\media\MediaTypeInterface; /** @@ -132,15 +130,6 @@ protected $field_map = []; /** - * Whether to auto-create source field. - * - * This is used internally when saving a media type. - * - * @var bool - */ - protected $auto_create_source_field = FALSE; - - /** * {@inheritdoc} */ public function getPluginCollections() { @@ -224,69 +213,6 @@ /** * {@inheritdoc} */ - public function preSave(EntityStorageInterface $storage) { - // If the media handler uses a source field, we'll need to store - // its name before saving. We'd need to double-save if we did - // this in postSave(). - $handler = $this->getHandler(); - if ($this->auto_create_source_field) { - $field_storage = $handler->getSourceField($this)->getFieldStorageDefinition(); - // If the field storage is a new (unsaved) config entity, save it. - if ($field_storage instanceof FieldStorageConfigInterface && $field_storage->isNew()) { - $field_storage->save(); - } - } - - parent::preSave($storage); - } - - /** - * {@inheritdoc} - */ - public function postSave(EntityStorageInterface $storage, $update = TRUE) { - parent::postSave($storage, $update); - - // If the media handler is using a source field, we may need to save - // it if it's new. The field storage is guaranteed to exist already - // because preSave() took care of that. - $handler = $this->getHandler(); - if ($this->auto_create_source_field) { - $field = $handler->getSourceField($this); - - // If the field is new, save it and add it to this media type's view - // and form displays. - if ($field->isNew()) { - // Ensure the field is saved correctly before adding it to the displays. - $field->save(); - - $entity_type = $field->getTargetEntityTypeId(); - $media_type = $field->getTargetBundle(); - - if ($field->isDisplayConfigurable('form')) { - // Use the default widget and settings. - $component = \Drupal::service('plugin.manager.field.widget') - ->prepareConfiguration($field->getType(), []); - - entity_get_form_display($entity_type, $media_type, 'default') - ->setComponent($field->getName(), $component) - ->save(); - } - if ($field->isDisplayConfigurable('view')) { - // Use the default formatter and settings. - $component = \Drupal::service('plugin.manager.field.formatter') - ->prepareConfiguration($field->getType(), []); - - entity_get_display($entity_type, $media_type, 'default') - ->setComponent($field->getName(), $component) - ->save(); - } - } - } - } - - /** - * {@inheritdoc} - */ public function getFieldMap() { return $this->field_map; } diff -u b/core/modules/media/src/MediaStorageProxyBase.php b/core/modules/media/src/MediaStorageProxyBase.php --- b/core/modules/media/src/MediaStorageProxyBase.php +++ b/core/modules/media/src/MediaStorageProxyBase.php @@ -129,7 +129,7 @@ return TRUE; } - $source_field_name = $this->getSourceField($media->bundle->entity)->getName(); + $source_field_name = $this->configuration['source_field']; // Update thumbnail if we don't have a thumbnail yet, for new items, // or if the value of the source field changed. @@ -179,8 +179,8 @@ $form['source_field']['#access'] = FALSE; $field_definition = $this->fieldTypeManager->getDefinition(reset($this->pluginDefinition['allowed_field_types'])); $form['source_field_message'] = [ - '#markup' => $this->t('%field_name field will be automatically created on this type to store the essential information about the media item.', [ - '%field_name' => $field_definition['label'], + '#markup' => $this->t('%field_type field will be automatically created on this type to store the essential information about the media item.', [ + '%field_type' => $field_definition['label'], ]), ]; } @@ -212,6 +212,13 @@ $this->configuration[$config_key] = $config_value; } } + + // If no source field is explicitly set, create it now. + if (empty($this->configuration['source_field'])) { + $field_storage = $this->createSourceFieldStorage(); + $field_storage->save(); + $this->configuration['source_field'] = $field_storage->getName(); + } } /** @@ -224,17 +231,16 @@ /** * {@inheritdoc} */ - public function getSourceField(MediaTypeInterface $type, $retry = TRUE) { - // If we don't know the name of the source field, we definitely need to - // create it. - if (empty($this->configuration['source_field'])) { - return $this->createSourceField($type); - } - // Even if we do know the name of the source field, there is no guarantee - // that it already exists. So check for the field and create it if needed. + public function getSourceField(MediaTypeInterface $type) { + // Nothing to do if no source field is configured yet. $field = $this->configuration['source_field']; - $fields = $this->entityFieldManager->getFieldDefinitions('media', $type->id()); - return isset($fields[$field]) ? $fields[$field] : $this->createSourceField($type); + if ($field) { + // Even if we do know the name of the source field, there is no guarantee + // that it already exists. + $fields = $this->entityFieldManager->getFieldDefinitions('media', $type->id()); + return isset($fields[$field]) ? $fields[$field] : NULL; + } + return NULL; } /** @@ -263,34 +269,23 @@ * The field storage definition or NULL if it doesn't exists. */ protected function getSourceFieldStorage() { - // If we don't know the name of the source field, we can't get its storage. - if (empty($this->configuration['source_field'])) { - return NULL; - } - // Even if we do know the name of the source field, we cannot guarantee that - // its storage exists. So check for the storage. + // Nothing to do if no source field is configured yet. $field = $this->configuration['source_field']; - $fields = $this->entityFieldManager->getFieldStorageDefinitions('media'); - return isset($fields[$field]) ? $fields[$field] : NULL; + if ($field) { + // Even if we do know the name of the source field, there's no guarantee + // that it exists. + $fields = $this->entityFieldManager->getFieldStorageDefinitions('media'); + return isset($fields[$field]) ? $fields[$field] : NULL; + } + return NULL; } /** - * Creates the source field definition for a type. - * - * @param \Drupal\media\MediaTypeInterface $type - * The media type. - * - * @return \Drupal\field\FieldConfigInterface - * The unsaved field definition. The field storage definition, if new, - * should also be unsaved. - */ - protected function createSourceField(MediaTypeInterface $type) { - // Get the storage or create if it doesn't exists yet. - $storage = $this->getSourceFieldStorage(); - if (is_null($storage)) { - $storage = $this->createSourceFieldStorage(); - $this->configuration['source_field'] = $storage->getName(); - } + * {@inheritdoc} + */ + public function createSourceField(MediaTypeInterface $type) { + $storage = $this->getSourceFieldStorage() ?: $this->createSourceFieldStorage(); + return $this->entityTypeManager ->getStorage('field_config') ->create([ @@ -309,10 +304,6 @@ * returned. Otherwise, a new, unused one is generated. */ protected function getSourceFieldName() { - if ($this->configuration['source_field']) { - return $this->configuration['source_field']; - } - $base_id = 'field_media_' . $this->getPluginId(); $tries = 0; $storage = $this->entityTypeManager->getStorage('field_storage_config'); diff -u b/core/modules/media/src/MediaStorageProxySourceFieldInterface.php b/core/modules/media/src/MediaStorageProxySourceFieldInterface.php --- b/core/modules/media/src/MediaStorageProxySourceFieldInterface.php +++ b/core/modules/media/src/MediaStorageProxySourceFieldInterface.php @@ -17,15 +17,28 @@ * @param \Drupal\media\MediaTypeInterface $type * A media type. * - * @return \Drupal\field\FieldConfigInterface - * The source field definition. + * @return \Drupal\field\FieldConfigInterface|null + * The source field definition, or NULL if it does not exist yet or has not + * been set in the handler configuration. */ public function getSourceField(MediaTypeInterface $type); /** - * Gets media storage proxy plugin-specific media item validation constraints. + * Creates the source field definition for a type. * * @param \Drupal\media\MediaTypeInterface $type + * The media type. + * + * @return \Drupal\field\FieldConfigInterface + * The unsaved field definition. The field storage definition, if new, + * should also be unsaved. + */ + public function createSourceField(MediaTypeInterface $type); + + /** + * Gets media storage proxy plugin-specific media item validation constraints. + * + * @param \Drupal\media\MediaTypeInterface $media_type * A media type. (Because e.g. an oEmbed plugin could be restricted to just * YouTube for a particular media type, so constraints need to be able to be * MediaType-specific.) diff -u b/core/modules/media/src/MediaTypeForm.php b/core/modules/media/src/MediaTypeForm.php --- b/core/modules/media/src/MediaTypeForm.php +++ b/core/modules/media/src/MediaTypeForm.php @@ -306,10 +306,49 @@ * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { - $this->entity->set('auto_create_source_field', TRUE); $status = parent::save($form, $form_state); $media_type = $this->entity; + // If the handler is using a source field, ensure it's properly created. + $handler = $media_type->getHandler(); + if (method_exists($handler, 'getSourceField')) { + $source_field = $handler->getSourceField($media_type); + if (!$source_field) { + $source_field = $handler->createSourceField($media_type); + + /** @var \Drupal\field\FieldStorageConfigInterface $storage */ + $storage = $source_field->getFieldStorageDefinition(); + if ($storage->isNew()) { + $storage->save(); + } + $source_field->save(); + + // Add the new field to the default form and view displays for this + // media type. + $field_name = $source_field->getName(); + $field_type = $source_field->getType(); + + if ($source_field->isDisplayConfigurable('form')) { + // Use the default widget and settings. + $component = \Drupal::service('plugin.manager.field.widget') + ->prepareConfiguration($field_type, []); + + entity_get_form_display('media', $media_type->id(), 'default') + ->setComponent($field_name, $component) + ->save(); + } + if ($source_field->isDisplayConfigurable('view')) { + // Use the default formatter and settings. + $component = \Drupal::service('plugin.manager.field.formatter') + ->prepareConfiguration($field_type, []); + + entity_get_display('media', $media_type->id(), 'default') + ->setComponent($field_name, $component) + ->save(); + } + } + } + $t_args = ['%name' => $media_type->label()]; if ($status === SAVED_UPDATED) { drupal_set_message($this->t('The media type %name has been updated.', $t_args)); diff -u b/core/modules/media/tests/modules/media_test_handler/src/Plugin/MediaStorageProxy/Test.php b/core/modules/media/tests/modules/media_test_handler/src/Plugin/MediaStorageProxy/Test.php --- b/core/modules/media/tests/modules/media_test_handler/src/Plugin/MediaStorageProxy/Test.php +++ b/core/modules/media/tests/modules/media_test_handler/src/Plugin/MediaStorageProxy/Test.php @@ -5,6 +5,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\media\MediaInterface; use Drupal\media\MediaStorageProxyBase; +use Drupal\media\MediaStorageProxyMetadataSourceFieldHandlerInterface; use Drupal\media\MediaTypeInterface; /** diff -u b/core/modules/media/tests/src/Functional/MediaCacheTagsTest.php b/core/modules/media/tests/src/Functional/MediaCacheTagsTest.php --- b/core/modules/media/tests/src/Functional/MediaCacheTagsTest.php +++ b/core/modules/media/tests/src/Functional/MediaCacheTagsTest.php @@ -50,7 +50,6 @@ 'handler_configuration' => [], 'field_map' => [], 'new_revision' => FALSE, - 'auto_create_source_field' => FALSE, ])->save(); // Create a media item. diff -u b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php --- b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php +++ b/core/modules/media/tests/src/Functional/MediaFunctionalTestTrait.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\media\Functional; use Drupal\media\Entity\MediaType; +use Drupal\media\MediaTypeInterface; /** * Trait with helpers for Media functional tests. @@ -35,7 +36,6 @@ 'handler_configuration' => [], 'field_map' => [], 'new_revision' => FALSE, - 'auto_create_source_field' => FALSE, ]; $media_type = MediaType::create($values); @@ -43,6 +43,23 @@ $this->assertEquals(SAVED_NEW, $status, 'Media type was created successfully.'); + // Ensure that the source field exists. + $handler = $media_type->getHandler(); + if (method_exists($handler, 'getSourceField')) { + $source_field = $handler->getSourceField($media_type); + if (!$source_field) { + $source_field = $handler->createSourceField($media_type); + $source_field->getFieldStorageDefinition()->save(); + $source_field->save(); + + $media_type + ->set('handler_configuration', [ + 'source_field' => $source_field->getName(), + ]) + ->save(); + } + } + return $media_type; } diff -u b/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php --- b/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php @@ -2,7 +2,9 @@ namespace Drupal\Tests\media\FunctionalJavascript; +use Drupal\field\FieldConfigInterface; use Drupal\media\Entity\Media; +use Drupal\media\Entity\MediaType; /** * Ensures that media UI works correctly. @@ -68,6 +70,19 @@ $assert_session->pageTextContains($name); $assert_session->pageTextContains($description); + // We need to clear the statically cached field definitions to account for + // fields that have been created by API calls in this test, since they exist + // in a separate memory space from the web server. + $this->container->get('entity_field.manager')->clearCachedFieldDefinitions(); + // Assert that the field and field storage were created. + $media_type = MediaType::load($machine_name); + $handler = $media_type->getHandler(); + /** @var \Drupal\field\FieldConfigInterface $source_field */ + $source_field = $handler->getSourceField($media_type); + $this->assertInstanceOf(FieldConfigInterface::class, $source_field, 'Source field exists.'); + $this->assertFalse($source_field->isNew(), 'Source field was saved.'); + $this->assertFalse($source_field->getFieldStorageDefinition()->isNew(), 'Source field definition was saved.'); + /** @var \Drupal\media\MediaTypeInterface $media_type_storage */ $media_type_storage = $this->container->get('entity_type.manager')->getStorage('media_type'); $this->testMediaType = $media_type_storage->load(strtolower($name)); @@ -165,14 +180,14 @@ $assert_session->pageTextContains('The media type ' . $new_name . ' has been deleted.'); // Test type delete prevention when there is existing media. - $media_type2 = $this->createMediaType(['auto_create_source_field' => TRUE]); + $media_type2 = $this->createMediaType(); $label2 = $media_type2->label(); $media = Media::create(['name' => 'lorem ipsum', 'bundle' => $media_type2->id()]); $media->save(); $this->drupalGet('admin/structure/media/manage/' . $media_type2->id()); $page->clickLink('Delete'); $assert_session->addressEquals('admin/structure/media/manage/' . $media_type2->id() . '/delete'); - $assert_session->fieldNotExists('edit-submit'); + $assert_session->buttonNotExists('edit-submit'); $assert_session->pageTextContains("$label2 is used by 1 media item on your site. You can not remove this media type until you have removed all of the $label2 media items."); } diff -u b/core/modules/media/tests/src/Kernel/BasicCreationTest.php b/core/modules/media/tests/src/Kernel/BasicCreationTest.php --- b/core/modules/media/tests/src/Kernel/BasicCreationTest.php +++ b/core/modules/media/tests/src/Kernel/BasicCreationTest.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\media\Kernel; -use Drupal\field\Entity\FieldConfig; use Drupal\KernelTests\KernelTestBase; use Drupal\media\Entity\Media; use Drupal\media\Entity\MediaType; @@ -57,9 +56,16 @@ 'label' => $id, 'handler' => 'test', 'new_revision' => FALSE, - 'auto_create_source_field' => TRUE, ]); $this->testMediaType->save(); + + $source_field = $this->testMediaType->getHandler()->createSourceField($this->testMediaType); + $source_field->getFieldStorageDefinition()->save(); + $source_field->save(); + + $this->testMediaType->set('handler_configuration', [ + 'source_field' => $source_field->getName(), + ])->save(); } /** @@ -115,90 +121,2 @@ - /** - * Tests creating and updating media type programmatically. - */ - public function testProgrammaticMediaTypeManipulation() { - // Creating a media type programmatically without specifying a source field - // should create one automagically. - /** @var FieldConfig $field */ - $field = $this->testMediaType->getHandler()->getSourceField($this->testMediaType); - $this->assertInstanceOf(FieldConfig::class, $field); - $this->assertEquals('field_media_test', $field->getName()); - $this->assertFalse($field->isNew()); - - // Saving with a non-existent source field should create it if the related - // flag is set. - $this->testMediaType->getHandler()->setConfiguration([ - 'source_field' => 'field_magick', - ]); - $this->testMediaType->set('auto_create_source_field', TRUE); - $this->testMediaType->save(); - $field = $this->testMediaType->getHandler()->getSourceField($this->testMediaType); - $this->assertInstanceOf(FieldConfig::class, $field); - $this->assertEquals('field_magick', $field->getName()); - $this->assertFalse($field->isNew()); - - // Trying to save without a source field should create a new, de-duped one. - $this->testMediaType->getHandler()->setConfiguration([]); - $this->testMediaType->set('auto_create_source_field', TRUE); - $this->testMediaType->save(); - $field = $this->testMediaType->getHandler()->getSourceField($this->testMediaType); - $this->assertInstanceOf(FieldConfig::class, $field); - $this->assertEquals('field_media_test_1', $field->getName()); - $this->assertFalse($field->isNew()); - - // Trying to reuse an existing field should, well, reuse the existing field - // even if the auto create flag is set. - $this->testMediaType->getHandler()->setConfiguration([ - 'source_field' => 'field_magick', - ]); - $this->testMediaType->set('auto_create_source_field', TRUE); - $this->testMediaType->save(); - $field = $this->testMediaType->getHandler()->getSourceField($this->testMediaType); - $this->assertInstanceOf(FieldConfig::class, $field); - $this->assertEquals('field_magick', $field->getName()); - $this->assertFalse($field->isNew()); - // No new de-duped fields should have been created. - $duplicates = FieldConfig::loadMultiple([ - 'media.' . $this->testMediaType->id() . '.field_magick_1', - 'media.' . $this->testMediaType->id() . '.field_media_generic_2', - ]); - $this->assertEmpty($duplicates); - - // If auto-create flag is not set fields shouldn't be created. - // ... if an imaginary field is set. - $this->testMediaType->getHandler()->setConfiguration([ - 'source_field' => 'field_imaginary', - ]); - $this->testMediaType->set('auto_create_source_field', FALSE); - $this->testMediaType->save(); - $field = $this->container->get('entity_type.manager') - ->getStorage('field_config') - ->load('media.' . $this->testMediaType->id() . '.field_imaginary'); - $field_storage = $this->container->get('entity_type.manager') - ->getStorage('field_storage_config') - ->load('media.field_imaginary'); - $this->assertNull($field, 'Field was not automatically created.'); - $this->assertNull($field_storage, 'Field was not automatically created.'); - - // ... if no field is set. - $this->testMediaType->getHandler()->setConfiguration([]); - $this->testMediaType->set('auto_create_source_field', FALSE); - $this->testMediaType->save(); - $config = $this->testMediaType->getHandler()->getConfiguration(); - $this->assertEquals('', $config['source_field'], 'Source field value was not automatically populated.'); - // Check what would the field be named like if it was created. - $proposed_field = $this->testMediaType->getHandler()->getSourceField($this->testMediaType); - $this->assertInstanceOf(FieldConfig::class, $proposed_field); - $this->assertEquals('field_media_test_2', $proposed_field->getName()); - $this->assertTrue($proposed_field->isNew()); - $field = $this->container->get('entity_type.manager') - ->getStorage('field_config') - ->load('media.' . $this->testMediaType->id() . '.' . $proposed_field->getName()); - $field_storage = $this->container->get('entity_type.manager') - ->getStorage('field_storage_config') - ->load('media.' . $proposed_field->getName()); - $this->assertNull($field, 'Field was not automatically created.'); - $this->assertNull($field_storage, 'Field was not automatically created.'); - } - }