diff --git a/core/modules/media_entity/config/schema/media_entity.schema.yml b/core/modules/media_entity/config/schema/media_entity.schema.yml index 3322fe3..155892d 100644 --- a/core/modules/media_entity/config/schema/media_entity.schema.yml +++ b/core/modules/media_entity/config/schema/media_entity.schema.yml @@ -44,6 +44,14 @@ media_entity.bundle.type.document: type: string label: 'Field with source information' +media_entity.bundle.type.image: + type: mapping + label: 'Media Image handler configuration' + mapping: + source_field: + type: string + label: 'Field with image information' + action.configuration.media_delete_action: type: action_configuration_default label: 'Delete media configuration' diff --git a/core/modules/media_entity/images/icons/no-thumbnail.png b/core/modules/media_entity/images/icons/no-thumbnail.png new file mode 100644 index 0000000..c50e3c7 --- /dev/null +++ b/core/modules/media_entity/images/icons/no-thumbnail.png @@ -0,0 +1,6 @@ +PNG + + IHDRetEXtSoftwareAdobe ImageReadyqe<0IDATx1O"[ݛb6TRQYA4Ra쬬 +o +=+2,sȲ{z4wf<3~zߤe#Cp!8CCp!8CpHp!8Cp !8Cp!8$8Cp!8C[sjvoWRQ.7 x}}}~~ެM}ʟZ>f8Vl.tJҊq K+t)d%.tp.~8rh4pDa[%H>XpG 8 8 8 8 8 8 8v ˫fpcy'''Jpcy".pt:y9pg`Sן +Gp DFk]7_Q*:NőFC?JjZQx /߿ñ t-F$cEFbqttG:[uRZdőWaEEF8Tq{{;L$͑@p"#ɘ_X]d±4D(Ԏm%(d±sd c,ZV:S.|qZz-GDGh4 #'U׶Ñ8]/pNFp⎪鷀{H­\<# ]k"HҜ[^WZM>,NX^^/|x:uss3W*|l {g=o9S8"p<(s#v2Y#q8j9>Ҳ4]xyKr},|\yqtmAp,Yl-h[[y{{[롴?Z\N L988G%^l!cEp!8CpHp!8Cp !8Cp!8$8Cp!8Cp!8CpmA 0{b~aIENDB` \ No newline at end of file diff --git a/core/modules/media_entity/src/Plugin/MediaEntity/Type/Image.php b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Image.php new file mode 100644 index 0000000..4cbd777 --- /dev/null +++ b/core/modules/media_entity/src/Plugin/MediaEntity/Type/Image.php @@ -0,0 +1,299 @@ +imageFactory = $image_factory; + $this->fileSystem = $file_system; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('entity_field.manager'), + $container->get('config.factory'), + $container->get('image.factory'), + $container->get('file_system') + ); + } + + /** + * {@inheritdoc} + */ + public function providedFields() { + $fields = [ + 'mime' => $this->t('Image MIME'), + 'width' => $this->t('Width'), + 'height' => $this->t('Height'), + ]; + + if ($this->canReadExifData()) { + $fields += [ + 'model' => $this->t('Camera model'), + 'created' => $this->t('Image creation datetime'), + 'iso' => $this->t('Iso'), + 'exposure' => $this->t('Exposure time'), + 'aperture' => $this->t('Aperture value'), + 'focal_length' => $this->t('Focal length'), + ]; + } + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getField(MediaInterface $media, $name) { + // Get the file, image and EXIF data. + /** @var \Drupal\file\FileInterface $file */ + $file = $this->getSourceField($media); + $uri = $file->getFileUri(); + $image = $this->imageFactory->get($uri); + + // Return the field. + switch ($name) { + case 'mime': + return $file->getMimeType() ?: FALSE; + + case 'width': + return $image->getWidth() ?: FALSE; + + case 'height': + return $image->getHeight() ?: FALSE; + + case 'size': + return $file->getSize() ?: FALSE; + } + + if ($this->canReadExifData()) { + switch ($name) { + case 'created': + /** @var \DateTime $date */ + $date = new DrupalDateTime($this->getExifField($uri, 'DateTimeOriginal')); + return $date->getTimestamp(); + + case 'model': + return $this->getExifField($uri, 'Model'); + + case 'iso': + return $this->getExifField($uri, 'ISOSpeedRatings'); + + case 'exposure': + return $this->getExifField($uri, 'ExposureTime'); + + case 'aperture': + return $this->getExifField($uri, 'FNumber'); + + case 'focal_length': + return $this->getExifField($uri, 'FocalLength'); + } + } + + return FALSE; + } + + /** + * Get source field file entity. + * + * @param \Drupal\media_entity\MediaInterface $media + * The media object. + * + * @return \Drupal\Core\Entity\EntityInterface + * Returns file entity. + */ + protected function getSourceField(MediaInterface $media) { + $source_field = $this->configuration['source_field']; + + if (empty($source_field)) { + throw new \RuntimeException('Source field for media image handler is not defined.'); + } + + $file = $media->get($source_field)->entity; + if (empty($file)) { + throw new \RuntimeException('Entity does not exist for source field.'); + } + + return $file; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $options = []; + + /** @var \Drupal\media_entity\MediaBundleInterface $bundle */ + $bundle = $form_state->getFormObject()->getEntity(); + foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) { + if ( + in_array($field->getType(), static::$allowedSourceFieldTypes, TRUE) + && !$field->getFieldStorageDefinition()->isBaseField() + ) { + $options[$field_name] = $field->getLabel(); + } + } + + $form['source_field'] = [ + '#type' => 'select', + '#title' => $this->t('Field with source information'), + '#description' => $this->t('Field on media entity that stores Image file. You can create a handler without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the handler.'), + '#default_value' => empty($this->configuration['source_field']) ? NULL : $this->configuration['source_field'], + '#options' => $options, + ]; + + // 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.'), + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function getDefaultThumbnail() { + return $this->configFactory->get('media_entity.settings') + ->get('icon_base') . '/no-thumbnail.png'; + } + + /** + * {@inheritdoc} + */ + public function thumbnail(MediaInterface $media) { + return $this->getSourceField($media)->getFileUri(); + } + + /** + * Check does functionality for reading EXIF data exist. + * + * @return bool + * Returns TRUE if functionality for reading of EXIF data is provided. + */ + protected function canReadExifData() { + return function_exists('exif_read_data'); + } + + /** + * Get EXIF field value. + * + * @param string $uri + * The uri for the file that we are getting the EXIF. + * @param string $field + * The name of the EXIF field. + * + * @return string|bool + * The value for the requested field or FALSE if is not set. + */ + protected function getExifField($uri, $field) { + if (empty($this->exif)) { + $this->exif = $this->getExifData($uri); + } + + return $this->exif[$field] ?: FALSE; + } + + /** + * Read EXIF. + * + * @param string $uri + * The uri for the file that we are getting the Exif. + * + * @return array|bool + * An associative array where the array indexes are the header names and + * the array values are the values associated with those headers or FALSE + * if the data can't be read. + */ + protected function getExifData($uri) { + return exif_read_data($this->fileSystem->realpath($uri), 'EXIF'); + } + + /** + * {@inheritdoc} + */ + public function getDefaultName(MediaInterface $media) { + return $this->getSourceField($media)->getFilename(); + } + +} diff --git a/core/modules/media_entity/tests/src/FunctionalJavascript/MediaTypeImageTest.php b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaTypeImageTest.php new file mode 100644 index 0000000..53016c5 --- /dev/null +++ b/core/modules/media_entity/tests/src/FunctionalJavascript/MediaTypeImageTest.php @@ -0,0 +1,72 @@ +getSession(); + $page = $session->getPage(); + $assert_session = $this->assertSession(); + + // Create image media handler. + $this->createMediaBundleTest($bundle_name, 'image', $provided_fields); + + // Create a supported and a non-supported field. + $fields = [ + 'field_string1' => 'string', + 'field_file1' => 'file', + 'field_image1' => 'image', + ]; + $this->createMediaFields($fields, $bundle_name); + + // Adjust the allowed extensions on the file field. + $file_field = FieldConfig::load("media.$bundle_name.field_image1"); + $file_field->setSetting('file_extensions', 'png')->save(); + + $this->drupalGet("admin/structure/media/manage/$bundle_name"); + $this->assertSelectOptions("type_configuration[image][source_field]", + [ + 'field_file1', + 'field_image1', + ], + ['field_string1'] + ); + $page->selectFieldOption("type_configuration[image][source_field]", 'field_image1'); + $page->pressButton('Save media bundle'); + + // Hide the media name to test default name generation. + $this->hideMediaField('name', $bundle_name); + + // Create a media item. + $this->drupalGet("media/add/$bundle_name"); + $page->attachFileToField("files[field_image1_0]", \Drupal::root() . '/core/misc/druplicon.png'); + $assert_session->assertWaitOnAjaxRequest(); + $page->fillField('field_image1[0][alt]', 'Druplicon Image Alt Text'); + $page->pressButton('Save and publish'); + + $assert_session->addressEquals('media/1'); + // Make sure the thumbnail shows up. + $assert_session->elementAttributeContains('css', '.image-style-thumbnail', 'src', 'druplicon.png'); + // Load the media and check its default name. + $media = Media::load(1); + $this->assertEquals($media->label(), 'druplicon.png'); + } + +}