cloudinary-8.x-1.x-dev/modules/cloudinary_media_library_widget/src/Plugin/media/Source/Cloudinary.php
modules/cloudinary_media_library_widget/src/Plugin/media/Source/Cloudinary.php
<?php
namespace Drupal\cloudinary_media_library_widget\Plugin\media\source;
use Cloudinary\Asset\AssetType;
use Cloudinary\Cloudinary as CloudinarySdk;
use Cloudinary\Configuration\Configuration;
use Consolidation\OutputFormatters\Exception\UnknownFieldException;
use Drupal\cloudinary_media_library_widget\CloudinaryInterface;
use Drupal\cloudinary_sdk\Service\CloudinaryAssetHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\media\MediaInterface;
use Drupal\media\MediaSourceBase;
use Drupal\media\Plugin\media\Source\File;
use Drupal\media\Plugin\media\Source\Image;
/**
* Provides media source plugin for cloudinary assets.
*
* @MediaSource(
* id = "cloudinary",
* label = @Translation("Cloudinary"),
* description = @Translation("Use Cloudinary assets for reusable media."),
* allowed_field_types = {
* "cloudinary_image",
* "cloudinary_video",
* "cloudinary_raw",
* },
* default_thumbnail_filename = "generic.png",
* forms = {
* "media_library_add" = "\Drupal\cloudinary_media_library_widget\Form\CloudinaryUploadForm",
* },
* )
*/
class Cloudinary extends MediaSourceBase implements CloudinaryInterface {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'resource_type' => AssetType::IMAGE,
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function getMetadataAttributes() {
$attributes = [
'public_id' => $this->t('Asset public ID'),
'transformation' => $this->t('Asset transformation'),
'context' => $this->t('Asset metadata'),
'secure_url' => $this->t('Asset secure url'),
'resource_type' => $this->t('Asset type'),
'delivery_type' => $this->t('Asset delivery type'),
'duration' => $this->t('Video duration'),
File::METADATA_ATTRIBUTE_NAME => $this->t('Name'),
File::METADATA_ATTRIBUTE_MIME => $this->t('MIME type'),
File::METADATA_ATTRIBUTE_SIZE => $this->t('File size'),
Image::METADATA_ATTRIBUTE_WIDTH => $this->t('Width'),
Image::METADATA_ATTRIBUTE_HEIGHT => $this->t('Height'),
];
// Fetch list of custom metadata fields.
$fields = CloudinaryAssetHelper::cloudinary()->adminApi()->listMetadataFields();
foreach ($fields->offsetGet('metadata_fields') as $metadata) {
$attributes["external_metadata_{$metadata['external_id']}"] = $metadata['label'];
}
return $attributes;
}
/**
* Get instance of cloudinary API.
*
* @return \Cloudinary\Cloudinary
* The cloudinary API.
*/
public static function cloudinary() {
return new CloudinarySdk(Configuration::instance());
}
/**
* {@inheritdoc}
*/
public function getMetadata(MediaInterface $media, $attribute_name) {
$item = $media->get($this->configuration['source_field']);
/** @var \Drupal\file\FileInterface $file */
$file = $item->entity ?? NULL;
/** @var \Drupal\cloudinary_media_library_widget\Service\CloudinaryFileHelperInterface $file_helper */
$file_helper = \Drupal::service('cloudinary_media_library_widget.file_helper');
/** @var \Drupal\cloudinary_sdk\Service\AssetHelperInterface $asset_helper */
$asset_helper = \Drupal::service('cloudinary_sdk.asset_helper');
// @todo Add support for multiple items for common attributes.
switch ($attribute_name) {
case 'thumbnail_uri':
$info = $item->first()->getValue();
// For raw files we display a default icon.
if ($info['resource_type'] === AssetType::RAW) {
return parent::getMetadata($media, $attribute_name);
}
$value = $asset_helper->generateStringValue($info);
return $file_helper->generateUriForFileWithTransformation($value);
case 'thumbnail_alt_value':
if (isset($item->alt)) {
return $item->alt;
}
if (isset($item->description)) {
return $item->description;
}
// Use name as fallback alt text.
return $this->getMetadata($media, 'default_name');
case 'default_name':
if (isset($item->title)) {
return $item->title;
}
if (isset($item->description)) {
return $item->description;
}
return parent::getMetadata($media, 'default_name');
case 'public_id':
return $item->public_id;
case 'transformation':
return $item->transformation;
case 'resource_type':
return $item->resource_type;
case 'delivery_type':
return $item->delivery_type;
case File::METADATA_ATTRIBUTE_NAME:
if ($file) {
return $file->getFilename();
}
if (isset($item->description)) {
return $item->description;
}
return parent::getMetadata($media, File::METADATA_ATTRIBUTE_NAME);
case File::METADATA_ATTRIBUTE_MIME:
if ($file) {
return $file->getMimeType();
}
/** @var \Symfony\Component\Mime\MimeTypeGuesserInterface $guesser */
$guesser = \Drupal::service('file.mime_type.guesser');
$asset = $asset_helper->loadAssetByPublicId($item->public_id, $item->resource_type);
$target = implode('.', [$asset->getPublicId(), $asset->getFormat()]);
return $guesser->guessMimeType($target);
case File::METADATA_ATTRIBUTE_SIZE:
$asset = $asset_helper->loadAssetByPublicId($item->public_id, $item->resource_type);
return $asset->getBytes();
case Image::METADATA_ATTRIBUTE_WIDTH:
return $item->width;
case Image::METADATA_ATTRIBUTE_HEIGHT:
return $item->height;
case 'secure_url':
$value = $asset_helper->generateStringValue($item->first()->getValue());
$uri = $file_helper->generateUriForExternalUrl($value);
return \Drupal::service('file_url_generator')->generateAbsoluteString($uri);
case 'context':
$asset = $asset_helper->loadAssetByPublicId($item->public_id, $item->resource_type);
return $asset->getCustomContext();
case 'duration':
$asset = $asset_helper->loadAssetByPublicId($item->public_id, $item->resource_type);
return $asset->getDuration();
}
// Get metadata from the asset if metadata structured fields are used.
if (preg_match('/^external_metadata_(.+)$/', $attribute_name, $matches)) {
$asset = $asset_helper->loadAssetByPublicId($item->public_id, $item->resource_type);
$metadata_values = $asset->getMetadata();
return $metadata_values[$matches[1]] ?? '';
}
return parent::getMetadata($media, $attribute_name);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$is_source_visible = $form['source_field']['#access'] ?? TRUE;
$options = $form['source_field']['#options'];
if ($is_source_visible) {
// Group fields by field type.
foreach ($options as $field_name => $label) {
$field_resource_type = $this->getResourceTypeByFieldName($field_name);
unset($options[$field_name]);
$options[ucfirst($field_resource_type)][$field_name] = $label;
}
$form['source_field']['#options'] = $options;
}
elseif (!$options) {
// Display custom message to explain automatic field creation.
$form['source_field_message'] = [
'#markup' => $this->t('Cloudinary field will be automatically created based on resource type to store the essential information about the media item.'),
];
}
$form['resource_type'] = [
'#type' => 'select',
'#title' => $this->t('Resource type'),
'#default_value' => $this->configuration['resource_type'],
'#description' => $this->t('The type of the asset to work with.'),
'#disabled' => !empty($this->configuration['source_field']),
'#options' => [
AssetType::IMAGE => $this->t('Image'),
AssetType::VIDEO => $this->t('Video'),
AssetType::RAW => $this->t('Document'),
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
$source_field = $form_state->getValue('source_field');
// Check if existing field matches the resource type from the form.
if ($source_field) {
$field_resource_type = $this->getResourceTypeByFieldName($source_field);
if ($form_state->getValue('resource_type') !== $field_resource_type) {
$form_state->setErrorByName('resource_type', $this->t('The selected resource type does not match existing source field.'));
}
}
}
/**
* {@inheritdoc}
*/
protected function createSourceFieldStorage() {
$resource_type = $this->configuration['resource_type'];
$field_type = "cloudinary_{$resource_type}";
return $this->entityTypeManager
->getStorage('field_storage_config')
->create([
'entity_type' => 'media',
'field_name' => $this->getSourceFieldName(),
'type' => $field_type,
]);
}
/**
* {@inheritdoc}
*/
protected function getSourceFieldName() {
$resource_type = $this->configuration['resource_type'];
$base_id = "field_media_cloudinary_{$resource_type}";
$tries = 0;
$storage = $this->entityTypeManager->getStorage('field_storage_config');
// Iterate at least once, until no field with the generated ID is found.
do {
$id = $base_id;
// If we've tried before, increment and append the suffix.
if ($tries) {
$id .= '_' . $tries;
}
$field = $storage->load('media.' . $id);
$tries++;
} while ($field);
return $id;
}
/**
* Get the resource type based on field name of the media source.
*
* @param string $field_name
* The name of the field to check.
*
* @return string
* The resource name that matches the field.
*/
protected function getResourceTypeByFieldName($field_name) {
$storage = $this->entityTypeManager->getStorage('field_storage_config');
/** @var \Drupal\field\FieldStorageConfigInterface $field */
$field = $storage->load("media.{$field_name}");
if (!$field) {
throw new UnknownFieldException($field_name);
}
$bundles = $field->getBundles();
/** @var \Drupal\media\MediaTypeInterface $media_type */
$media_type = $this->entityTypeManager->getStorage('media_type')
->load(reset($bundles));
$configuration = $media_type->getSource()->getConfiguration();
return $configuration['resource_type'];
}
}
