diff --git a/core/modules/media/config/schema/media.schema.yml b/core/modules/media/config/schema/media.schema.yml index 363f61b47a..a63aa5c35d 100644 --- a/core/modules/media/config/schema/media.schema.yml +++ b/core/modules/media/config/schema/media.schema.yml @@ -42,7 +42,7 @@ media.source.file: media.source.image: type: media.source.field_aware - label: 'Image source configuration' + label: '"Image" media source configuration' action.configuration.media_delete_action: type: action_configuration_default diff --git a/core/modules/media/src/MediaSourceInterface.php b/core/modules/media/src/MediaSourceInterface.php index 4c3b783222..c25fd49e08 100644 --- a/core/modules/media/src/MediaSourceInterface.php +++ b/core/modules/media/src/MediaSourceInterface.php @@ -82,6 +82,16 @@ const METADATA_ATTRIBUTE_SIZE = 'size'; /** + * Key for width metadata attribute. + */ + const METADATA_ATTRIBUTE_WIDTH = 'width'; + + /** + * Key for height metadata attribute. + */ + const METADATA_ATTRIBUTE_HEIGHT = 'height'; + + /** * Gets a list of metadata attributes provided by this plugin. * * Most media sources have associated metadata, describing attributes diff --git a/core/modules/media/src/Plugin/media/Source/Image.php b/core/modules/media/src/Plugin/media/Source/Image.php index 9717a503d5..18967283b0 100644 --- a/core/modules/media/src/Plugin/media/Source/Image.php +++ b/core/modules/media/src/Plugin/media/Source/Image.php @@ -11,6 +11,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Image\ImageFactory; use Drupal\media\MediaInterface; +use Drupal\media\MediaSourceInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -21,7 +22,7 @@ * @MediaSource( * id = "image", * label = @Translation("Image"), - * description = @Translation("Allows local images to be used as media."), + * description = @Translation("Use local images for reusable media."), * allowed_field_types = {"image"}, * default_thumbnail_filename = "no-thumbnail.png" * ) @@ -102,8 +103,8 @@ public function getMetadataAttributes() { $attributes = parent::getMetadataAttributes(); $attributes += [ - 'width' => $this->t('Width'), - 'height' => $this->t('Height'), + MediaSourceInterface::METADATA_ATTRIBUTE_WIDTH => $this->t('Width'), + MediaSourceInterface::METADATA_ATTRIBUTE_HEIGHT => $this->t('Height'), ]; if ($this->canReadExifData()) { @@ -127,18 +128,18 @@ public function getMetadata(MediaInterface $media, $name) { // Get the file, image and EXIF data. /** @var \Drupal\file\FileInterface $file */ $file = $media->get($this->configuration['source_field'])->entity; + // If field media type is not required. if (!$file) { return NULL; } $uri = $file->getFileUri(); $image = $this->imageFactory->get($uri); - // Return the field. switch ($name) { - case 'width': + case MediaSourceInterface::METADATA_ATTRIBUTE_WIDTH: return $image->getWidth() ?: NULL; - case 'height': + case MediaSourceInterface::METADATA_ATTRIBUTE_HEIGHT: return $image->getHeight() ?: NULL; case 'thumbnail_uri': @@ -178,10 +179,9 @@ public function getMetadata(MediaInterface $media, $name) { public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = parent::buildConfigurationForm($form, $form_state); - // Show message if it's not possible to read EXIF data. if (!$this->canReadExifData()) { $form['no_exif_data_reader'] = [ - '#markup' => $this->t('Unable to read EXIF data for Image. In order to provide EXIF data reading functionality please take a look at PHP documentation of exif_read_data function.'), + '#markup' => $this->t('Unable to read EXIF data for images. In order to provide EXIF data reading functionality please take a look at PHP documentation of the exif_read_data function.'), ]; } @@ -202,12 +202,12 @@ protected function canReadExifData() { * Gets EXIF field value. * * @param string $uri - * The uri for the file that we are getting the EXIF. + * The URI for the file that we are getting the EXIF for. * @param string $field - * The name of the EXIF field. + * The name of the requested EXIF field. * * @return string|bool - * The value for the requested field or FALSE if is not set. + * The value for the requested field or FALSE if it is not set. */ protected function getExifField($uri, $field) { if (empty($this->exif)) { @@ -221,10 +221,10 @@ protected function getExifField($uri, $field) { * Reads EXIF. * * @param string $uri - * The URI for the file that we are getting the EXIF. + * The URI for the file that we are getting the EXIF field value. * * @return array|bool - * An associative array where the indexes are header names and values are + * An associative array where the keys are header names and values are * the values associated with those headers. We return FALSE if the data * can't be read. */ diff --git a/core/modules/media/tests/fixtures/exif_example.jpeg b/core/modules/media/tests/fixtures/exif_example_1.jpeg similarity index 95% rename from core/modules/media/tests/fixtures/exif_example.jpeg rename to core/modules/media/tests/fixtures/exif_example_1.jpeg index 2cba9b9b9c..ac79b36804 100644 --- a/core/modules/media/tests/fixtures/exif_example.jpeg +++ b/core/modules/media/tests/fixtures/exif_example_1.jpeg @@ -1,4 +1,4 @@ -JFIFHHExifMM*bv~(i%Drupal EXIF CameraHH02100100NE4` Əhttp://ns.adobe.com/xap/1.0/ +JFIFHH ExifMM*bx(i%Drupal EXIF Camera 1HH02100100NE4` Ə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 new file mode 100644 index 0000000000..0cbd028f76 --- /dev/null +++ b/core/modules/media/tests/fixtures/exif_example_2.jpeg @@ -0,0 +1,50 @@ +JFIFHH ExifMM*bx(i%Drupal EXIF Camera 2HH02100100NE4` Əhttp://ns.adobe.com/xap/1.0/ + + + + + Drupal EXIF Camera + 72 + 72 + Inch + Centered + Exif Version 2.1 + FlashPix Version 1.0 + Uncalibrated + N + 52, 30, 32.751 + E + 13, 22, 25.5432 + + + + + +C + + +    C   Y HNƋ`:Q&Y9nZl(-p㓟{'h\R@o3uv'N%Wi}cʭ0E-%+[}[/OI^fdp,Ć^_?}:ΞQ]gYh2Z]ՋEgDhf )ɋK0q,LGduV٭N#kOHW+Tס_D;|t!ΪNrvb=%qkMtQ#}vw]vHxzysl_i-vtv¯\.Ыj]]$:o,o>ma_[ +ȔJYԷ+ 콟C!yWK?܋2jU潲˅ˤQX' 6@03PH}jr5fKl\m5 qYqhc i8Ǻ7}Fzetc4 +B/,( %UR:sZy<ٖ.~ɟR`= ^%#?1ZwFh?;3Z#.K֓8#s;qWB/(]iCI =*@/.&{')Mt ZQ5Xn5P𛇤JfKʻ s}%eg"d!?iqIp kůhU衈>=քN6:~J )anjZYp{YgPMR\Q;5vA*>2m YC[~(SHl"ٵk֦%r- ?@Mi7D$lSC`Yt4t77:*8LW~isR7+C(lyԋLW')5UԷzNL4JEEX@=hI¤%p4Ji"$nqՑ@Á5|PT*AB )r)܂>XԬ='l>JSs=¡,|ULJ%! !G\uTgz j34SWƀ`*I@dc" ѧ:5C6Th2iԞ'iS! q(5m͑{fO}yM)Y>tJS] j hKvȓ +"RpVڑFb!Ѵf_sܝf 9¶{CN# <+ШܺEQ4VX'g: !"1A#2Qq 0@a45R$3Pc?QDq}(Oz4*TЎ7ړ +[$^֯BԜ9?ôu:ţ]jrUՆd0RU .%ar ԑ:z`nα eJM#)Dd1` 6OÃUY_v 쿟SumwjOrJ ⧢teN\ 7+4#!w5Cw)~'K{5{t%u$y ]b_J_a)`5Q.,sQ*.< V,$V2ǹ;OM8iM1~ ¤]'GϢr,C3tq6M u(mK(VR0i0,+ mRӪaar6_L[ҏXq0|0_*!5 Oݛʗ&;P>T]Sx=1 +ް)TÉLK >Z]BO/Ʈ($m7~sœFϟ4}i*̖AvK9^bӎiyRFt)EszU\Gp@d:|C !1"AQaq2B #@0RSTbrs$3P? 36S(2wT&kkw~r^Pt7a@$2a,Lˌ:ie #4dD Ǘlk߸oɓ|#nfBU++BBhD&Z}^0 +|Nr]5n / +Juro=U֏J$|PnjqJOخ5XeA{Tc"?ԕѬWU7DZUr?dJm*0ے(OeUv鷘KpG&YNjCvE)WD +i!xQƔn]dja! 90U'! o!T Y/+XS*$mH(AwJ)- БS&0haJkFQNN/Q0*dڽh-8ȴEBwy^ȓߨM'bArGtK!62xD3CJSRxԟl).v$") _Ma]&w\2n)w~9S?Q/6txGL#"^szjV6+Nix\GK&Y҂kw2ᇦՃj5L4f/P6ƧiRGth,S(J x (vjNDzyg_v_[^d _cbe +@Y'L)Lxȥ2ҐG,O}*iE_1Ӡe+Dޛ>3&~f6ML~k8T @"[@-ɥ1Yܸ TbhSJfbm 4}X.N,~MJ6%BV(]w#@Iԓ`Q%޵5b9K1"l +@3%|mZB`yzI}SgJ=0ًz,v4 g]I3 G$I$I$rI$I$*I$I$)Sl$I$Dm%I$Lm%5 so{BjNdrrI$$I$I$AI$I$I$I$I$O*!1AQaq@ 0P?ɘDvҍ;]qV~6XUnjX+yv-Rn^)_K JtA} x֖\.ƄAΞz)J6%7[a-I؁JUf"#zEl!bH7C@+9j u@-lUB:SY qĢ$_ 5*o*Oabj;PÀ-7]=@Dv58]1]37M76MN{U^p|-eאVL wPO:ߍhcC9Q5αu6{W;2U({RAVF +f&+`} #a8ڈA3paDPDUIn#WUb[/sBW"Ѽw4E7 F{kI%t\T],;V+-zik$J!eY|˭,Ɔ=uC33B6H,,̓h-ڇ8ne LYlҏV܋k9kS*qʀ,Ӏǘ LVLbm,c.νP5kEI b3`>?O1a[k + 8D4jz? @KA>_ sȤ`!/3*!1AQaq@ 0P? +n_oB5 xcRNRZ$O/'/=i7q:ڈD"L%<~v^X}eeC†u@Yj'Î^)ǙHʅ3.'e9tyCЏy0KąSTNRFR< m³&gNUybDֳP5ӝB149Nc]: 1_^FVĦtʭRU鹣z4S5}IB +pUȃǖbxGύӒys޼o +1cXaYvlrŇQY<{SI@7Kछ+8s484;E0]+Yy]3پXkСWl"oPd̴sʅԈjE]Rա ÏlFX-k e^bNed&81p,$ RhFdIl~.i7u1$\,Hi&#^ C9+?QX0bRG=*a9CJbiٟ} D#}U+|Ten9.Rol1b528$nMgšɗEtJ0E)u~+SL&!1AQaq @0P?;Y(>*Tʯ $s pF-JJ$4 Z`ZiHM|AD yb0[f)ۿG]W&c^gXTRW @HD)QiϑP1 +0FTv3^1*x4}!meKTUniK GQ&6L~]-d(%᎐qS!rU^qtQv P9@khKKwfD5j;{ޏMGuChV@1Qކa#6 r^~FFǒhbE;K5?YcB2G&ƃgWgPlRgetPage(); $assert_session = $this->assertSession(); - // Create image media source. - $this->createMediaTypeTest($type_id, 'image', $provided_fields); + $this->createMediaTypeTest($media_type_id, 'image', $provided_fields); // Create custom fields for the media type to store metadata attributes. $fields = [ @@ -46,12 +45,12 @@ public function testMediaImageSource() { 'field_string_width' => 'string', 'field_string_model' => 'string', ]; - $this->createMediaTypeFields($fields, $type_id); + $this->createMediaTypeFields($fields, $media_type_id); // Hide the name field widget to test default name generation. - $this->hideMediaTypeFieldWidget('name', $type_id); + $this->hideMediaTypeFieldWidget('name', $media_type_id); - $this->drupalGet("admin/structure/media/manage/{$type_id}"); + $this->drupalGet("admin/structure/media/manage/{$media_type_id}"); $page->selectFieldOption("field_map[mime]", 'field_string_mime'); $page->selectFieldOption("field_map[size]", 'field_string_size'); $page->selectFieldOption("field_map[width]", 'field_string_width'); @@ -59,24 +58,37 @@ public function testMediaImageSource() { $page->pressButton('Save'); // Create a media item. - $this->drupalGet("media/add/{$type_id}"); - $page->attachFileToField("files[{$source_field_id}_0]", \Drupal::root() . '/core/modules/media/tests/fixtures/exif_example.jpeg'); + $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'); + $page->fillField("{$source_field_id}[0][alt]", 'EXIF Image Alt Text 1'); $page->pressButton('Save and publish'); $assert_session->addressEquals('media/1'); // Make sure the thumbnail is displayed from uploaded image. - $assert_session->elementAttributeContains('css', '.image-style-thumbnail', 'src', 'exif_example.jpeg'); + $assert_session->elementAttributeContains('css', '.image-style-thumbnail', 'src', 'exif_example_1.jpeg'); // Load the media and check that all fields are properly populated. $media = Media::load(1); - $this->assertEquals('exif_example.jpeg', $media->label()); - $this->assertEquals('Drupal EXIF Camera', $media->get('field_string_model')->value); + $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('image/jpeg', $media->get('field_string_mime')->value); - $this->assertEquals(filesize(\Drupal::root() . '/core/modules/media/tests/fixtures/exif_example.jpeg'), $media->get('field_string_size')->value); + $this->assertEquals(filesize(\Drupal::root() . '/core/modules/media/tests/fixtures/exif_example_1.jpeg'), $media->get('field_string_size')->value); + + + // Create a second 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_2.jpeg'); + $assert_session->assertWaitOnAjaxRequest(); + $page->fillField("{$source_field_id}[0][alt]", 'EXIF Image Alt Text 2'); + $page->pressButton('Save and publish'); + + // Load second media and ensure that statically cached EXIF data not reused. + $media_2 = Media::load(2); + $this->assertEquals('Drupal EXIF Camera 1', $media->get('field_string_model')->value); + $this->assertEquals('Drupal EXIF Camera 2', $media_2->get('field_string_model')->value); } } diff --git a/core/profiles/standard/config/optional/media.type.image.yml b/core/profiles/standard/config/optional/media.type.image.yml index 719c88a9a7..baea949668 100644 --- a/core/profiles/standard/config/optional/media.type.image.yml +++ b/core/profiles/standard/config/optional/media.type.image.yml @@ -5,7 +5,7 @@ dependencies: - media id: image label: Image -description: 'Use the "Image" media type for uploading local images.' +description: 'Use the "Image" media type for storing image media locally.' source: image queue_thumbnail_downloads: false new_revision: false