diff --git a/core/modules/media/src/Annotation/MediaSource.php b/core/modules/media/src/Annotation/MediaSource.php index 59f9e29ba0..2440ebec53 100644 --- a/core/modules/media/src/Annotation/MediaSource.php +++ b/core/modules/media/src/Annotation/MediaSource.php @@ -80,6 +80,20 @@ class MediaSource extends Plugin { public $thumbnail_uri_metadata_attribute = 'thumbnail_uri'; /** + * The metadata attribute name to provide the thumbnail alt. + * + * @var string|null + */ + public $thumbnail_alt_metadata_attribute; + + /** + * The metadata attribute name to provide the thumbnail title. + * + * @var string|null + */ + public $thumbnail_title_metadata_attribute; + + /** * The metadata attribute name to provide the default name. * * @var string diff --git a/core/modules/media/src/Entity/Media.php b/core/modules/media/src/Entity/Media.php index f05c1c937e..9c389235ab 100644 --- a/core/modules/media/src/Entity/Media.php +++ b/core/modules/media/src/Entity/Media.php @@ -163,8 +163,22 @@ protected function updateThumbnail($from_queue = FALSE) { $this->thumbnail->target_id = $file->id(); } - $this->thumbnail->alt = $this->t('Thumbnail'); - $this->thumbnail->title = $this->label(); + // Set the image alt. + $plugin_definition = $this->getSource()->getPluginDefinition(); + if (!empty($plugin_definition['thumbnail_alt_metadata_attribute'])) { + $this->thumbnail->alt = $this->getSource()->getMetadata($this, $plugin_definition['thumbnail_alt_metadata_attribute']); + } + else { + $this->thumbnail->alt = $this->t('Thumbnail'); + } + + // Set the image title. + if (!empty($plugin_definition['thumbnail_title_metadata_attribute'])) { + $this->thumbnail->title = $this->getSource()->getMetadata($this, $plugin_definition['thumbnail_title_metadata_attribute']); + } + else { + $this->thumbnail->title = $this->label(); + } return $this; } @@ -234,11 +248,7 @@ protected function sourceFieldChanged() { protected function shouldUpdateThumbnail($is_new = FALSE) { // Update thumbnail if we don't have a thumbnail yet or when the source // field value changes. - if (!$this->get('thumbnail')->entity || $is_new || $this->sourceFieldChanged()) { - return TRUE; - } - - return FALSE; + return !$this->get('thumbnail')->entity || $is_new || $this->sourceFieldChanged(); } /** diff --git a/core/modules/media/src/Entity/MediaType.php b/core/modules/media/src/Entity/MediaType.php index 796e892b53..5000f24114 100644 --- a/core/modules/media/src/Entity/MediaType.php +++ b/core/modules/media/src/Entity/MediaType.php @@ -218,4 +218,12 @@ public function getFieldMap() { return $this->field_map; } + /** + * {@inheritdoc} + */ + public function setFieldMap($map) { + $this->field_map = $map; + return $this; + } + } diff --git a/core/modules/media/src/MediaSourceBase.php b/core/modules/media/src/MediaSourceBase.php index 2fe0880af3..2f117b066a 100644 --- a/core/modules/media/src/MediaSourceBase.php +++ b/core/modules/media/src/MediaSourceBase.php @@ -132,7 +132,6 @@ public function getMetadata(MediaInterface $media, $attribute_name) { case 'thumbnail_uri': $default_thumbnail_filename = $this->pluginDefinition['default_thumbnail_filename']; return $this->configFactory->get('media.settings')->get('icon_base_uri') . '/' . $default_thumbnail_filename; - } } diff --git a/core/modules/media/src/MediaTypeInterface.php b/core/modules/media/src/MediaTypeInterface.php index af775c9b8b..7d1bf291f3 100644 --- a/core/modules/media/src/MediaTypeInterface.php +++ b/core/modules/media/src/MediaTypeInterface.php @@ -83,4 +83,15 @@ public function setNewRevision($new_revision); */ public function getFieldMap(); + /** + * Sets the metadata field map. + * + * @param array $map + * Field mapping with metadata attribute names as keys and entity field + * names as values. + * + * @return $this + */ + public function setFieldMap($map); + } diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php index 3b6085776a..2e6653559d 100644 --- a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php +++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php @@ -2,6 +2,7 @@ namespace Drupal\media_test_source\Plugin\media\Source; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; use Drupal\media\MediaInterface; use Drupal\media\MediaSourceBase; @@ -22,25 +23,37 @@ class Test extends MediaSourceBase { * {@inheritdoc} */ public function getMetadataAttributes() { - return [ - 'attribute_1' => $this->t('Attribute 1'), - 'attribute_2' => $this->t('Attribute 2'), - ]; + $attributes = \Drupal::state()->get('media_source_test_attributes', [ + 'attribute_1' => ['label' => $this->t('Attribute 1'), 'value' => 'Value 1'], + 'attribute_2' => ['label' => $this->t('Attribute 2'), 'value' => 'Value 1'], + ]); + return array_map(function ($item) {return $item['label'];}, $attributes); } /** * {@inheritdoc} */ public function getMetadata(MediaInterface $media, $attribute_name) { - switch ($attribute_name) { + $attributes = \Drupal::state()->get('media_source_test_attributes', [ + 'attribute_1' => ['label' => $this->t('Attribute 1'), 'value' => 'Value 1'], + 'attribute_2' => ['label' => $this->t('Attribute 2'), 'value' => 'Value 1'], + ]); - case 'thumbnail_uri': - return DRUPAL_ROOT . '/core/misc/druplicon.png'; + if (in_array($attribute_name, array_keys($attributes))) { + return $attributes[$attribute_name]['value']; + } - default: - return parent::getMetadata($media, $attribute_name); + return parent::getMetadata($media, $attribute_name); + } - } + /** + * {@inheritdoc} + */ + public function getPluginDefinition() { + return NestedArray::mergeDeep( + parent::getPluginDefinition(), + \Drupal::state()->get('media_source_test_definition', []) + ); } /** diff --git a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php index b868cab39e..21fe5e486c 100644 --- a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php +++ b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php @@ -164,38 +164,4 @@ public function testMediaWithMultipleMediaTypes() { $assert_session->pageTextContains($second_media_item->label()); } - /** - * Tests the queue worker for media item thumbnails. - */ - public function testMediaThumbnailQueueWorker() { - // Tests and creates the media type with queued thumbnails. - $media_type = $this->createMediaType(['queue_thumbnail_downloads' => TRUE]); - - // Create a media item. - $media_item = Media::create(['bundle' => $media_type->id()]); - $media_item->save(); - - $filename = $media_item->getSource()->getPluginDefinition()['default_thumbnail_filename']; - $default_thumb_uri = \Drupal::service('config.factory')->get('media.settings')->get('icon_base_uri') . '/' . $filename; - $source_thumb_uri = $media_item->getSource()->getMetadata($media_item, $media_item->getSource()->getPluginDefinition()['thumbnail_uri_metadata_attribute']); - - // Media thumbnail should be the default image. - $this->assertEquals($default_thumb_uri, $media_item->get('thumbnail')->entity->getFileUri()); - - // Check if queue item was created. - $queue_name = 'media_entity_thumbnail'; - $queue_worker = \Drupal::service('plugin.manager.queue_worker')->createInstance($queue_name); - $queue = \Drupal::queue($queue_name); - $item = $queue->claimItem(); - $this->assertEquals($media_item->id(), $item->data['id']); - - // Process queue worker. - $queue_worker->processItem($item->data); - $queue->deleteItem($item); - - // Load updated media item to check changed thumbnail. - $updated_media_item = Media::load($media_item->id()); - $this->assertEquals($source_thumb_uri, $updated_media_item->get('thumbnail')->entity->getFileUri()); - } - } diff --git a/core/modules/media/tests/src/Kernel/BasicCreationTest.php b/core/modules/media/tests/src/Kernel/BasicCreationTest.php index b0cd2f59f8..5e0ab64481 100644 --- a/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\KernelTests\KernelTestBase; use Drupal\media\Entity\Media; use Drupal\media\Entity\MediaType; use Drupal\media\MediaInterface; @@ -13,60 +12,7 @@ * * @group media */ -class BasicCreationTest extends KernelTestBase { - - /** - * Modules to install. - * - * @var array - */ - public static $modules = [ - 'media', - 'media_test_source', - 'image', - 'user', - 'field', - 'system', - 'file', - ]; - - /** - * The test media type. - * - * @var \Drupal\media\MediaTypeInterface - */ - protected $testMediaType; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->installEntitySchema('user'); - $this->installEntitySchema('file'); - $this->installSchema('file', 'file_usage'); - $this->installEntitySchema('media'); - $this->installConfig(['field', 'system', 'image', 'file']); - - // Create a test media type. - $id = strtolower($this->randomMachineName()); - $this->testMediaType = MediaType::create([ - 'id' => $id, - 'label' => $id, - 'source' => 'test', - 'new_revision' => FALSE, - ]); - $this->testMediaType->save(); - - $source_field = $this->testMediaType->getSource()->createSourceField($this->testMediaType); - $source_field->getFieldStorageDefinition()->save(); - $source_field->save(); - - $this->testMediaType->set('source_configuration', [ - 'source_field' => $source_field->getName(), - ])->save(); - } +class BasicCreationTest extends MediaKernelTestBase { /** * Tests creating a media type programmatically. @@ -107,16 +53,6 @@ public function testMediaEntityCreation() { $this->assertEquals('Unnamed', $media->label(), 'The media item was not created with the correct name.'); $source_field_name = $media->bundle->entity->getSource()->getSourceFieldDefinition($media->bundle->entity)->getName(); $this->assertEquals('Nation of sheep, ruled by wolves, owned by pigs.', $media->get($source_field_name)->value, 'Source returns correct source field.'); - - // Test the creation of a media item without user-defined label and - // check if a default name is provided. - $media = Media::create([ - 'bundle' => $this->testMediaType->id(), - ]); - $media->save(); - $expected_name = 'media' . ':' . $this->testMediaType->id() . ':' . $media->uuid(); - $this->assertEquals($this->testMediaType->id(), $media->bundle(), 'The media item was not created with correct type.'); - $this->assertEquals($expected_name, $media->label(), 'The media item was not created with a default name.'); } } diff --git a/core/modules/media/tests/src/Kernel/MediaKernelTestBase.php b/core/modules/media/tests/src/Kernel/MediaKernelTestBase.php new file mode 100644 index 0000000000..e13150e0ff --- /dev/null +++ b/core/modules/media/tests/src/Kernel/MediaKernelTestBase.php @@ -0,0 +1,66 @@ +installEntitySchema('user'); + $this->installEntitySchema('file'); + $this->installSchema('file', 'file_usage'); + $this->installEntitySchema('media'); + $this->installConfig(['field', 'system', 'image', 'file', 'media']); + + // Create a test media type. + $id = strtolower($this->randomMachineName()); + $this->testMediaType = MediaType::create([ + 'id' => $id, + 'label' => $id, + 'source' => 'test', + 'new_revision' => FALSE, + ]); + $this->testMediaType->save(); + + $source_field = $this->testMediaType->getSource()->createSourceField($this->testMediaType); + $source_field->getFieldStorageDefinition()->save(); + $source_field->save(); + + $this->testMediaType->set('source_configuration', [ + 'source_field' => $source_field->getName(), + ])->save(); + } + +} diff --git a/core/modules/media/tests/src/Kernel/MediaSourceTest.php b/core/modules/media/tests/src/Kernel/MediaSourceTest.php new file mode 100644 index 0000000000..14349c69aa --- /dev/null +++ b/core/modules/media/tests/src/Kernel/MediaSourceTest.php @@ -0,0 +1,231 @@ + $this->testMediaType->id()]); + $this->assertEquals('default_name', $media->getSource()->getPluginDefinition()['default_name_metadata_attribute'], 'Default metadata attribute is used for the default name.'); + $this->assertEquals('media:' . $media->bundle() . ':' . $media->uuid(), $media->getSource()->getMetadata($media, 'default_name'), 'Value of the default name metadata attribute looks correct.'); + $media->save(); + $this->assertEquals('media:' . $media->bundle() . ':' . $media->uuid(), $media->label(), 'Default name was set correctly.'); + + // Make sure that the user-supplied name is used. + /** @var \Drupal\media\MediaInterface $media */ + $name = 'User-supplied name'; + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'name' => $name, + ]); + $this->assertEquals('default_name', $media->getSource()->getPluginDefinition()['default_name_metadata_attribute'], 'Default metadata attribute is used for the default name.'); + $this->assertEquals('media:' . $media->bundle() . ':' . $media->uuid(), $media->getSource()->getMetadata($media, 'default_name'), 'Value of the default name metadata attribute looks correct.'); + $media->save(); + $this->assertEquals($name, $media->label(), 'User-supplied name was set correctly.'); + + // Change the default name attribute and see if it is used to set the name. + $name = 'Old Major'; + \Drupal::state()->set('media_source_test_attributes', ['alternative_name' => ['title' => 'Alternative name', 'value' => $name]]); + \Drupal::state()->set('media_source_test_definition', ['default_name_metadata_attribute' => 'alternative_name']); + /** @var \Drupal\media\MediaInterface $media */ + $media = Media::create(['bundle' => $this->testMediaType->id()]); + $this->assertEquals('alternative_name', $media->getSource()->getPluginDefinition()['default_name_metadata_attribute'], 'Correct metadata attribute is used for the default name.'); + $this->assertEquals($name, $media->getSource()->getMetadata($media, 'alternative_name'), 'Value of the default name metadata attribute looks correct.'); + $media->save(); + $this->assertEquals($name, $media->label(), 'Default name was set correctly.'); + } + + /** + * Tests metadata mapping functionality. + */ + public function testMetadataMapping() { + $field_name = 'field_to_map_to'; + $attribute_name = 'attribute_to_map'; + $storage = $this->container->get('entity_type.manager') + ->getStorage('field_storage_config') + ->create([ + 'entity_type' => 'media', + 'field_name' => $field_name, + 'type' => 'string', + ]); + $storage->save(); + + $this->container->get('entity_type.manager') + ->getStorage('field_config') + ->create([ + 'field_storage' => $storage, + 'bundle' => $this->testMediaType->id(), + 'label' => 'Field to map to', + ])->save(); + + // Save the entity without defining the metadata mapping and check that the + // field stays empty. + /** @var \Drupal\media\MediaInterface $media */ + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'field_media_test' => 'some_value', + ]); + $media->save(); + $this->assertEmpty($media->get($field_name)->value, 'Field stayed empty.'); + + // Define mapping and make sure that the value was stored in the field. + \Drupal::state()->set('media_source_test_attributes', [ + $attribute_name => ['title' => 'Attribute to map', 'value' => 'Snowball'] + ]); + $this->testMediaType->setFieldMap([$attribute_name => $field_name])->save(); + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'field_media_test' => 'some_value', + ]); + $this->assertEquals('Snowball', $media->getSource()->getMetadata($media, $attribute_name), 'Value of the metadata attribute is correct.'); + $media->save(); + $this->assertEquals('Snowball', $media->get($field_name)->value, 'Metadata attribute was correctly mapped to the field.'); + + // Change the metadata attribute value and re-save the entity. Field value + // should stay the same. + \Drupal::state()->set('media_source_test_attributes', [ + $attribute_name => ['title' => 'Attribute to map', 'value' => 'Pinkeye'] + ]); + $this->assertEquals('Pinkeye', $media->getSource()->getMetadata($media, $attribute_name), 'Value of the metadata attribute is correct.'); + $media->save(); + $this->assertEquals('Snowball', $media->get($field_name)->value, 'Metadata attribute was correctly mapped to the field.'); + + // Now change the value of the source field and make sure that the mapped + // values update too. + $this->assertEquals('Pinkeye', $media->getSource()->getMetadata($media, $attribute_name), 'Value of the metadata attribute is correct.'); + $media->set('field_media_test', 'some_new_value'); + $media->save(); + $this->assertEquals('Pinkeye', $media->get($field_name)->value, 'Metadata attribute was correctly mapped to the field.'); + } + + /** + * Tests the default thumbnail functionality. + */ + public function testThumbnail() { + file_put_contents('public://thumbnail1.jpg', ''); + file_put_contents('public://thumbnail2.jpg', ''); + + // Save a media entity and make sure thumbnail was added. + \Drupal::state()->set('media_source_test_attributes', [ + 'thumbnail_uri' => ['title' => 'Thumbnail', 'value' => 'public://thumbnail1.jpg'], + ]); + /** @var \Drupal\media\MediaInterface $media */ + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'name' => 'Mr. Jones', + 'field_media_test' => 'some_value', + ]); + $this->assertEquals('public://thumbnail1.jpg', $media->getSource()->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is correct.'); + $media->save(); + $this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'Thumbnail was added to the media entity.'); + $this->assertEquals('Mr. Jones', $media->thumbnail->title, 'Title text was set on the thumbnail.'); + $this->assertEquals('Thumbnail', $media->thumbnail->alt, 'Alt text was set on the thumbnail.'); + + // Now change the metadata attribute and make sure that the thumbnail stays + // the same. + \Drupal::state()->set('media_source_test_attributes', [ + 'thumbnail_uri' => ['title' => 'Thumbnail', 'value' => 'public://thumbnail2.jpg'], + ]); + $this->assertEquals('public://thumbnail2.jpg', $media->getSource()->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is correct.'); + $media->save(); + $this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'Thumbnail was preserved.'); + + // Remove the thumbnail and make sure that it is auto-updated on save. + $media->thumbnail->target_id = NULL; + $this->assertEquals('public://thumbnail2.jpg', $media->getSource()->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is correct.'); + $media->save(); + $this->assertEquals('public://thumbnail2.jpg', $media->thumbnail->entity->getFileUri(), 'New thumbnail was added to the media entity.'); + + // Change the metadata attribute again, change the source field value and + // make sure that the thumbnail updates too. + \Drupal::state()->set('media_source_test_attributes', [ + 'thumbnail_uri' => ['title' => 'Thumbnail', 'value' => 'public://thumbnail1.jpg'], + ]); + $media->field_media_test->value = 'some_new_value'; + $this->assertEquals('public://thumbnail1.jpg', $media->getSource()->getMetadata($media, 'thumbnail_uri'), 'Value of the thumbnail metadata attribute is correct.'); + $media->save(); + $this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'New thumbnail was added to the media entity.'); + + // Change the thumbnail metadata attribute and make sure that the thumbnail + // is set correctly. + \Drupal::state()->set('media_source_test_attributes', [ + 'thumbnail_uri' => ['title' => 'Should not be used', 'value' => 'public://thumbnail1.jpg'], + 'alternative_thumbnail_uri' => ['title' => 'Should be used', 'value' => 'public://thumbnail2.jpg'], + ]); + \Drupal::state()->set('media_source_test_definition', ['thumbnail_uri_metadata_attribute' => 'alternative_thumbnail_uri']); + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'name' => 'Mr. Jones', + 'field_media_test' => 'some_value', + ]); + $this->assertEquals('public://thumbnail1.jpg', $media->getSource()->getMetadata($media, 'thumbnail_uri'), 'Value of the metadata attribute is correct.'); + $this->assertEquals('public://thumbnail2.jpg', $media->getSource()->getMetadata($media, 'alternative_thumbnail_uri'), 'Value of the thumbnail metadata attribute is correct.'); + $media->save(); + $this->assertEquals('public://thumbnail2.jpg', $media->thumbnail->entity->getFileUri(), 'Correct metadata attribute was used for the thumbnail.'); + + // Enable queued thumbnails and make sure that the entity gets the default + // thumbnail initially. + \Drupal::state()->set('media_source_test_definition', []); + \Drupal::state()->set('media_source_test_attributes', [ + 'thumbnail_uri' => ['title' => 'Should not be used', 'value' => 'public://thumbnail1.jpg'], + ]); + $this->testMediaType->setQueueThumbnailDownloadsStatus(TRUE)->save(); + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'name' => 'Mr. Jones', + 'field_media_test' => 'some_value', + ]); + $this->assertEquals('public://thumbnail1.jpg', $media->getSource()->getMetadata($media, 'thumbnail_uri'), 'Value of the metadata attribute is correct.'); + $media->save(); + $this->assertEquals('public://media-icons/generic/generic.png', $media->thumbnail->entity->getFileUri(), 'Default thumbnail was set initially.'); + + // Process the queue item and make sure that the thumbnail was updated too. + $queue_name = 'media_entity_thumbnail'; + /** @var \Drupal\Core\Queue\QueueWorkerInterface $queue_worker */ + $queue_worker = \Drupal::service('plugin.manager.queue_worker')->createInstance($queue_name); + $queue = \Drupal::queue($queue_name); + $this->assertEquals(1, $queue->numberOfItems(), 'Item was added to the queue.'); + + $item = $queue->claimItem(); + $this->assertEquals($media->id(), $item->data['id'], 'Queue item that was created belongs to the correct entity.'); + + $queue_worker->processItem($item->data); + $queue->deleteItem($item); + $this->assertEquals(0, $queue->numberOfItems(), 'Item was removed from the queue.'); + + $media = Media::load($media->id()); + $this->assertEquals('public://thumbnail1.jpg', $media->thumbnail->entity->getFileUri(), 'Thumbnail was updated by the queue.'); + + // Set alt and title metadata attributes and make sure they are used for the + // thumbnail. + \Drupal::state()->set('media_source_test_definition', [ + 'thumbnail_alt_metadata_attribute' => 'alt', + 'thumbnail_title_metadata_attribute' => 'title', + ]); + \Drupal::state()->set('media_source_test_attributes', [ + 'alt' => ['title' => 'Alt text', 'value' => 'This will be alt.'], + 'title' => ['title' => 'Title text', 'value' => 'This will be title.'], + ]); + $media = Media::create([ + 'bundle' => $this->testMediaType->id(), + 'name' => 'Boxer', + 'field_media_test' => 'some_value', + ]); + $media->save(); + $this->assertEquals('Boxer', $media->label(), 'Correct name was set on the media entity.'); + $this->assertEquals('This will be title.', $media->thumbnail->title, 'Title text was set on the thumbnail.'); + $this->assertEquals('This will be alt.', $media->thumbnail->alt, 'Alt text was set on the thumbnail.'); + } +}