diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml b/core/modules/media/config/install/core.entity_form_display.media.image.default.yml similarity index 100% rename from core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml rename to core/modules/media/config/install/core.entity_form_display.media.image.default.yml diff --git a/core/profiles/standard/config/optional/core.entity_view_display.media.image.default.yml b/core/modules/media/config/install/core.entity_view_display.media.image.default.yml similarity index 100% rename from core/profiles/standard/config/optional/core.entity_view_display.media.image.default.yml rename to core/modules/media/config/install/core.entity_view_display.media.image.default.yml diff --git a/core/profiles/standard/config/optional/field.field.media.image.field_media_image.yml b/core/modules/media/config/install/field.field.media.image.field_media_image.yml similarity index 94% rename from core/profiles/standard/config/optional/field.field.media.image.field_media_image.yml rename to core/modules/media/config/install/field.field.media.image.field_media_image.yml index 628c386df8..bd9a2f5737 100644 --- a/core/profiles/standard/config/optional/field.field.media.image.field_media_image.yml +++ b/core/modules/media/config/install/field.field.media.image.field_media_image.yml @@ -4,23 +4,22 @@ dependencies: config: - field.storage.media.field_media_image - media.type.image - module: - - image enforced: module: - media + module: + - image id: media.image.field_media_image field_name: field_media_image entity_type: media bundle: image -label: field_media_image +label: Image description: '' -required: false +required: true translatable: true default_value: { } default_value_callback: '' settings: - file_extensions: 'png gif jpg jpeg' alt_field: true alt_field_required: true title_field: false @@ -34,6 +33,7 @@ settings: width: null height: null file_directory: '[date:custom:Y]-[date:custom:m]' + file_extensions: 'png gif jpg jpeg' max_filesize: '' handler: 'default:file' handler_settings: { } diff --git a/core/profiles/standard/config/optional/field.storage.media.field_media_image.yml b/core/modules/media/config/install/field.storage.media.field_media_image.yml similarity index 100% rename from core/profiles/standard/config/optional/field.storage.media.field_media_image.yml rename to core/modules/media/config/install/field.storage.media.field_media_image.yml diff --git a/core/modules/media/config/install/media.type.file.yml b/core/modules/media/config/install/media.type.file.yml index af0abc2716..a0a12202a5 100644 --- a/core/modules/media/config/install/media.type.file.yml +++ b/core/modules/media/config/install/media.type.file.yml @@ -1,6 +1,9 @@ langcode: en status: true -dependencies: { } +dependencies: + module: + - file + - media id: file label: File description: "Use local files for reusable media." diff --git a/core/profiles/standard/config/optional/media.type.image.yml b/core/modules/media/config/install/media.type.image.yml similarity index 95% rename from core/profiles/standard/config/optional/media.type.image.yml rename to core/modules/media/config/install/media.type.image.yml index cb96e20438..59b0f8a97b 100644 --- a/core/profiles/standard/config/optional/media.type.image.yml +++ b/core/modules/media/config/install/media.type.image.yml @@ -2,6 +2,7 @@ langcode: en status: true dependencies: module: + - image - media id: image label: Image diff --git a/core/modules/media/src/Plugin/media/Source/Image.php b/core/modules/media/src/Plugin/media/Source/Image.php index 167f7f4fdf..93d5006113 100644 --- a/core/modules/media/src/Plugin/media/Source/Image.php +++ b/core/modules/media/src/Plugin/media/Source/Image.php @@ -3,6 +3,7 @@ namespace Drupal\media\Plugin\media\Source; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Datetime\DateFormatter; use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; @@ -93,13 +94,20 @@ class Image extends File { protected $imageFactory; /** - * File system service. + * The file system service. * * @var \Drupal\Core\File\FileSystem */ protected $fileSystem; /** + * The date formatter service. + * + * @var \Drupal\Core\Datetime\DateFormatter + */ + protected $dateFormatter; + + /** * The EXIF data. * * @var array @@ -126,13 +134,16 @@ class Image extends File { * @param \Drupal\Core\Image\ImageFactory $image_factory * The image factory. * @param \Drupal\Core\File\FileSystem $file_system - * File system service. + * The file system service. + * @param \Drupal\Core\Datetime\DateFormatter $date_formatter + * The date formatter service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, ConfigFactoryInterface $config_factory, ImageFactory $image_factory, FileSystem $file_system) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, ConfigFactoryInterface $config_factory, ImageFactory $image_factory, FileSystem $file_system, DateFormatter $date_formatter) { parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_field_manager, $field_type_manager, $config_factory); $this->imageFactory = $image_factory; $this->fileSystem = $file_system; + $this->dateFormatter = $date_formatter; } /** @@ -148,7 +159,8 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('plugin.manager.field.field_type'), $container->get('config.factory'), $container->get('image.factory'), - $container->get('file_system') + $container->get('file_system'), + $container->get('date.formatter') ); } @@ -207,7 +219,7 @@ public function getMetadata(MediaInterface $media, $name) { case static::METADATA_ATTRIBUTE_CREATED: /** @var \DateTime $date */ $date = new DrupalDateTime($this->getExifField($uri, 'DateTimeOriginal')); - return $date->getTimestamp(); + return $this->dateFormatter->format($date->getTimestamp()); case static::METADATA_ATTRIBUTE_MODEL: return $this->getExifField($uri, 'Model'); diff --git a/core/modules/media/tests/fixtures/exif_example_1.jpeg b/core/modules/media/tests/fixtures/exif_example_1.jpeg index ac79b36804..e04cde60ca 100644 --- a/core/modules/media/tests/fixtures/exif_example_1.jpeg +++ b/core/modules/media/tests/fixtures/exif_example_1.jpeg @@ -1,4 +1,5 @@ -JFIFHH ExifMM*bx(i%Drupal EXIF Camera 1HH02100100NE4` Əhttp://ns.adobe.com/xap/1.0/ +JFIFHHExifMM*bx(i%Drupal EXIF Camera 1HH'0210 +01002014:08:28 06:52:007NPEh4` Əhttp://ns.adobe.com/xap/1.0/ diff --git a/core/modules/media/tests/fixtures/exif_example_2.jpeg b/core/modules/media/tests/fixtures/exif_example_2.jpeg index 0cbd028f76..c0b67c2177 100644 --- a/core/modules/media/tests/fixtures/exif_example_2.jpeg +++ b/core/modules/media/tests/fixtures/exif_example_2.jpeg @@ -1,4 +1,5 @@ -JFIFHH ExifMM*bx(i%Drupal EXIF Camera 2HH02100100NE4` Əhttp://ns.adobe.com/xap/1.0/ +JFIFHHExifMM*bx(i%Drupal EXIF Camera 2HH'0210 +01002017:05:11 22:38:007NPEh4` Əhttp://ns.adobe.com/xap/1.0/ diff --git a/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml b/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml index 089aeac35f..a8e78ff354 100644 --- a/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml +++ b/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml @@ -13,3 +13,7 @@ media.source.test_translation: media.source.test_constraints: type: media.source.test label: 'Test media source with constraints configuration' + +media.source.image_without_exif: + type: media.source.field_aware + label: 'Test image media source without EXIF data' diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestImageWithoutExifData.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestImageWithoutExifData.php new file mode 100644 index 0000000000..1e1f2542b6 --- /dev/null +++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestImageWithoutExifData.php @@ -0,0 +1,27 @@ +assertSession(); + + // Create a media item. + $this->drupalGet('/media/add/image'); + $page = $this->getSession()->getPage(); + $page->fillField('Name', 'Foobar'); + $page->attachFileToField('Image', \Drupal::root() . '/core/modules/media/tests/fixtures/exif_example_1.jpeg'); + $page->pressButton('Save and publish'); + $assert->addressMatches('/^\/media\/[0-9]+$/'); + + // The media item was just created, so it should only have one revision. + $media = $this->container + ->get('entity_type.manager') + ->getStorage('media') + ->load(1); + $this->assertRevisionCount($media, 1); + + // If we edit the item, we should get a new revision. + $this->drupalGet('/media/1/edit'); + $assert->checkboxChecked('Create new revision'); + $page = $this->getSession()->getPage(); + $page->fillField('Name', 'Foobaz'); + $page->pressButton('Save and keep published'); + $this->assertRevisionCount($media, 2); + } + + /** * Asserts that an entity has a certain number of revisions. * * @param \Drupal\Core\Entity\EntityInterface $entity diff --git a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php index e37b850c75..8f3f67506a 100644 --- a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php +++ b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php @@ -33,6 +33,7 @@ protected function setUp() { // We need to test without any default configuration in place. // @TODO: Remove this when https://www.drupal.org/node/2883813 lands. MediaType::load('file')->delete(); + MediaType::load('image')->delete(); } /** diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaSourceImageTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceImageTest.php index 9c85587ad9..f94e576f6f 100644 --- a/core/modules/media/tests/src/FunctionalJavascript/MediaSourceImageTest.php +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceImageTest.php @@ -2,7 +2,10 @@ namespace Drupal\Tests\media\FunctionalJavascript; +use Drupal\Core\Datetime\DrupalDateTime; use Drupal\media\Entity\Media; +use Drupal\media\Entity\MediaType; +use Drupal\media\Plugin\media\Source\Image; /** * Tests the image media source. @@ -12,20 +15,41 @@ class MediaSourceImageTest extends MediaSourceTestBase { /** + * Modules to enable. + * + * @var array + */ + public static $modules = [ + 'datetime', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + // We need to test without any default configuration in place. + // @TODO: Remove this as part of https://www.drupal.org/node/2883813. + MediaType::load('file')->delete(); + MediaType::load('image')->delete(); + } + + /** * Tests the image media source. */ public function testMediaImageSource() { $media_type_id = 'test_media_image_type'; $source_field_id = 'field_media_image'; $provided_fields = [ - 'width', - 'height', - 'created', - 'model', - 'iso', - 'exposure', - 'aperture', - 'focal_length', + Image::METADATA_ATTRIBUTE_WIDTH, + Image::METADATA_ATTRIBUTE_HEIGHT, + Image::METADATA_ATTRIBUTE_CREATED, + Image::METADATA_ATTRIBUTE_MODEL, + Image::METADATA_ATTRIBUTE_ISO, + Image::METADATA_ATTRIBUTE_EXPOSURE, + Image::METADATA_ATTRIBUTE_APERTURE, + Image::METADATA_ATTRIBUTE_FOCAL_LENGTH, ]; $session = $this->getSession(); @@ -37,7 +61,13 @@ public function testMediaImageSource() { // Create custom fields for the media type to store metadata attributes. $fields = [ 'field_string_width' => 'string', + 'field_string_height' => 'string', + 'field_string_created' => 'string', 'field_string_model' => 'string', + 'field_string_iso' => 'string', + 'field_string_exposure' => 'string', + 'field_string_aperture' => 'string', + 'field_string_focal_length' => 'string', ]; $this->createMediaTypeFields($fields, $media_type_id); @@ -45,8 +75,14 @@ public function testMediaImageSource() { $this->hideMediaTypeFieldWidget('name', $media_type_id); $this->drupalGet("admin/structure/media/manage/{$media_type_id}"); - $page->selectFieldOption("field_map[width]", 'field_string_width'); - $page->selectFieldOption("field_map[model]", 'field_string_model'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_WIDTH . "]", 'field_string_width'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_HEIGHT . "]", 'field_string_height'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_CREATED . "]", 'field_string_created'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_MODEL . "]", 'field_string_model'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_ISO . "]", 'field_string_iso'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_EXPOSURE . "]", 'field_string_exposure'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_APERTURE . "]", 'field_string_aperture'); + $page->selectFieldOption("field_map[" . Image::METADATA_ATTRIBUTE_FOCAL_LENGTH . "]", 'field_string_focal_length'); $page->pressButton('Save'); // Create a media item. @@ -64,8 +100,16 @@ public function testMediaImageSource() { // Load the media and check that all fields are properly populated. $media = Media::load(1); $this->assertEquals('exif_example_1.jpeg', $media->label()); - $this->assertEquals('Drupal EXIF Camera 1', $media->get('field_string_model')->value); $this->assertEquals('200', $media->get('field_string_width')->value); + $this->assertEquals('89', $media->get('field_string_height')->value); + /** @var \DateTime $created_date */ + $created_date = new DrupalDateTime('2014:08:28 06:52:00'); + $this->assertEquals(\Drupal::service('date.formatter')->format($created_date->getTimestamp()), $media->get('field_string_created')->value); + $this->assertEquals('Drupal EXIF Camera 1', $media->get('field_string_model')->value); + $this->assertEquals('400', $media->get('field_string_iso')->value); + $this->assertEquals('1/400', $media->get('field_string_exposure')->value); + $this->assertEquals('14/1', $media->get('field_string_aperture')->value); + $this->assertEquals('55/1', $media->get('field_string_focal_length')->value); // Create a second media item. $this->drupalGet("media/add/{$media_type_id}"); @@ -80,4 +124,55 @@ public function testMediaImageSource() { $this->assertEquals('Drupal EXIF Camera 2', $media_2->get('field_string_model')->value); } + /** + * Tests the image media source without EXIF data. + */ + public function testMediaImageSourceWithoutExifData() { + $media_type_id = 'test_image_type_without_exif'; + $source_field_id = 'field_media_image_without_exif'; + + $session = $this->getSession(); + $page = $session->getPage(); + $assert_session = $this->assertSession(); + + $media_type = $this->doTestCreateMediaType($media_type_id, 'image_without_exif'); + + // Create custom fields for the media type to store metadata attributes. + $fields = [ + 'field_string_width' => 'string', + 'field_string_model' => 'string', + 'field_string_iso' => 'string', + ]; + $this->createMediaTypeFields($fields, $media_type_id); + + // Set field map. + $field_map = [ + Image::METADATA_ATTRIBUTE_WIDTH => 'field_string_width', + Image::METADATA_ATTRIBUTE_MODEL => 'field_string_model', + Image::METADATA_ATTRIBUTE_ISO => 'field_string_iso', + ]; + $media_type->setFieldMap($field_map) + ->save(); + + // Hide the name field widget to test default name generation. + $this->hideMediaTypeFieldWidget('name', $media_type_id); + + // Create a media item. + $this->drupalGet("media/add/{$media_type_id}"); + $page->attachFileToField("files[{$source_field_id}_0]", \Drupal::root() . '/core/modules/media/tests/fixtures/exif_example_1.jpeg'); + $assert_session->assertWaitOnAjaxRequest(); + $page->fillField("{$source_field_id}[0][alt]", 'EXIF Image Alt Text 1'); + $page->pressButton('Save and publish'); + + $assert_session->addressEquals('media/1'); + + // Load the media and check that all fields are properly populated. Fields + // mapped to EXIF data should be null. + $media = Media::load(1); + $this->assertEquals('exif_example_1.jpeg', $media->label()); + $this->assertEquals('200', $media->get('field_string_width')->value); + $this->assertNull($media->get('field_string_model')->value); + $this->assertNull($media->get('field_string_iso')->value); + } + } diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaSourceTestBase.php b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceTestBase.php index 5af6d5c143..24cd134266 100644 --- a/core/modules/media/tests/src/FunctionalJavascript/MediaSourceTestBase.php +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceTestBase.php @@ -37,10 +37,22 @@ protected function createMediaTypeField($field_name, $field_type, $media_type_id // Make the field widget visible in the form display. $component = \Drupal::service('plugin.manager.field.widget') ->prepareConfiguration($field_type, []); - /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity_form_display */ + + // @todo Replace entity_get_form_display() when #2367933 is done. + // https://www.drupal.org/node/2872159. $entity_form_display = entity_get_form_display('media', $media_type_id, 'default'); $entity_form_display->setComponent($field_name, $component) ->save(); + + // Use the default formatter and settings. + $component = \Drupal::service('plugin.manager.field.formatter') + ->prepareConfiguration($field_type, []); + + // @todo Replace entity_get_display() when #2367933 is done. + // https://www.drupal.org/node/2872159. + $entity_display = entity_get_display('media', $media_type_id, 'default'); + $entity_display->setComponent($field_name, $component) + ->save(); } /** diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php index bb49fefc56..b150f894da 100644 --- a/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaUiJavascriptTest.php @@ -42,6 +42,7 @@ protected function setUp() { // We need to test without any default configuration in place. // @TODO: Remove this as part of https://www.drupal.org/node/2883813. MediaType::load('file')->delete(); + MediaType::load('image')->delete(); } /** diff --git a/core/modules/media/tests/src/Kernel/MediaSourceFileTest.php b/core/modules/media/tests/src/Kernel/MediaSourceFileTest.php index 9ad6916a38..d716e15b40 100644 --- a/core/modules/media/tests/src/Kernel/MediaSourceFileTest.php +++ b/core/modules/media/tests/src/Kernel/MediaSourceFileTest.php @@ -20,6 +20,7 @@ protected function setUp() { // We need to test without any default configuration in place. // @TODO: Remove this as part of https://www.drupal.org/node/2883813. MediaType::load('file')->delete(); + MediaType::load('image')->delete(); } /**