cloudinary-8.x-1.x-dev/modules/cloudinary_media_library_widget/cloudinary_media_library_widget.module
modules/cloudinary_media_library_widget/cloudinary_media_library_widget.module
<?php
/**
* @file
* Main module file.
*/
use Cloudinary\Api\Admin\AdminApi;
use Cloudinary\Configuration\Configuration;
use Drupal\cloudinary_media_library_widget\Service\CloudinaryMediaWidgetHelper;
use Drupal\cloudinary_media_library_widget\Plugin\Field\FieldType\CloudinaryImage;
use Drupal\cloudinary_sdk\Service\CloudinaryAssetHelper;
use Drupal\cloudinary_stream_wrapper\StreamWrapper\CloudinaryStreamWrapper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\file\FileInterface;
use Drupal\media\MediaInterface;
/**
* Implements hook_form_FORM_ID_alter() for cloudinary_sdk_settings().
*
* Alters the setting form for Cloudinary settings.
*
* @see cloudinary_sdk_settings()
*/
function cloudinary_media_library_widget_form_cloudinary_sdk_settings_alter(&$form, $form_state) {
$config = \Drupal::config('cloudinary_media_library_widget.settings');
$form['widget'] = [
'#type' => 'fieldset',
'#title' => t('Media library widget settings'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
];
$form['widget']['cloudinary_saml_auth'] = [
'#type' => 'checkbox',
'#title' => t('SAML Authentication'),
'#default_value' => $config->get('cloudinary_saml_auth'),
'#description' => t('Whether to perform automatic SSO login via SAML for the specified username. This parameter is only needed if the "Enforce SAML login" option is disabled for your account.'),
];
try {
$api = new AdminApi();
$root_folders = $api->rootFolders();
$folders = [];
foreach ($root_folders['folders'] as $folder) {
$folders[$folder['path']] = t('Folder: /@path', [
'@path' => $folder['path'],
]);
}
}
catch (\Exception $e) {
$folders = [];
}
$form['widget']['cloudinary_starting_folder'] = [
'#type' => 'select',
'#title' => t('Starting folder'),
'#options' => $folders,
'#empty_value' => '/',
'#empty_option' => t('Root'),
'#default_value' => $config->get('cloudinary_starting_folder'),
'#description' => t('Instructs the widget to open in browse mode, displaying the contents of the specified folder.'),
];
$form['metadata'] = [
'#type' => 'fieldset',
'#title' => t('Metadata attributes'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
];
$title_options = [
'context.custom.caption' => t('Contextual metadata: Title (caption)'),
];
$description_options = [
'context.custom.alt' => t('Contextual metadata: Description (alt)'),
];
try {
Configuration::instance()->validate();
// Fetch list of custom metadata fields.
$fields = CloudinaryAssetHelper::cloudinary()->adminApi()->listMetadataFields();
foreach ($fields->offsetGet('metadata_fields') as $metadata) {
if ($metadata['type'] === 'string') {
$title_options[$metadata['external_id']] = $metadata['label'];
$description_options[$metadata['external_id']] = $metadata['label'];
}
}
}
catch (\Exception $e) {
$form['metadata']['#description'] = t('Please set up API connection and save the form to see list of all metadata including structure one.');
}
$form['metadata']['cloudinary_attribute_default_title'] = [
'#title' => t('Default title (caption) attribute'),
'#type' => 'select',
'#description' => t('Use this attribute to automatically populate title attribute for an image and name for a media.'),
'#options' => $title_options,
'#default_value' => $config->get('cloudinary_attribute_default_title'),
];
$form['metadata']['cloudinary_attribute_default_description'] = [
'#title' => t('Default description (alt) attribute'),
'#type' => 'select',
'#description' => t('Use this attribute to automatically populate "alt text" for an image and "description" for a video/document.'),
'#options' => $description_options,
'#default_value' => $config->get('cloudinary_attribute_default_description'),
];
$form['optimizations'] = [
'#type' => 'fieldset',
'#title' => t('Optimizations'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
];
$image_optimizations = $config->get('cloudinary_image_optimizations') ?? [];
$form['optimizations']['cloudinary_image_optimizations'] = [
'#type' => 'textarea',
'#title' => t('Default optimizations for image'),
'#default_value' => $image_optimizations,
'#description' => t('Represents a different component (separated by a "/"), for example: c_scale,w_400/f_auto/q_auto. Check official @docs to get more details.', [
'@docs' => Link::fromTextAndUrl(
t('docs'),
Url::fromUri('https://cloudinary.com/documentation/image_transformations')->setOption('attributes', ['target' => '_blank'])
)->toString(),
]),
'#rows' => 2,
'#weight' => 1,
];
$breakpoints = $config->get('cloudinary_responsive_breakpoints') ?? [];
$form['optimizations']['cloudinary_responsive_breakpoints'] = [
'#type' => 'textarea',
'#title' => t('Responsive breakpoints'),
'#default_value' => CloudinaryMediaWidgetHelper::convertArrayOptionsToString($breakpoints),
'#description' => t('Enter one value per line or separate it by comma. Check official @docs to get more details.', [
'@docs' => Link::fromTextAndUrl(
t('docs'),
Url::fromUri('https://cloudinary.com/documentation/responsive_images#overriding_default_values')->setOption('attributes', ['target' => '_blank'])
)->toString(),
]),
'#weight' => 3,
];
$form['#submit'][] = 'cloudinary_media_library_widget_settings_submit';
}
/**
* Submit for the cloudinary_sdk_settings() form.
*/
function cloudinary_media_library_widget_settings_submit($form, FormStateInterface $form_state) {
$widget_config = \Drupal::configFactory()
->getEditable('cloudinary_media_library_widget.settings');
$settings = [
'cloudinary_saml_auth',
'cloudinary_starting_folder',
'cloudinary_image_optimizations',
'cloudinary_attribute_default_title',
'cloudinary_attribute_default_description',
];
// Set standard configuration.
foreach ($settings as $option) {
$widget_config->set($option, $form_state->getValue($option));
}
// Convert breakpoints to the proper format if it was seperated by comma.
$breakpoints = explode(',', $form_state->getValue('cloudinary_responsive_breakpoints', ''));
$breakpoints = array_map('trim', $breakpoints);
$breakpoints_value = CloudinaryMediaWidgetHelper::convertArrayOptionsToString($breakpoints);
$widget_config->set('cloudinary_responsive_breakpoints', CloudinaryMediaWidgetHelper::convertStringOptionsToArray($breakpoints_value));
$widget_config->save();
}
/**
* Implements hook_theme().
*/
function cloudinary_media_library_widget_theme() {
return [
'cloudinary_image_formatter' => [
'variables' => [
'item' => NULL,
'item_attributes' => NULL,
'url' => NULL,
],
],
];
}
/**
* Implements hook_preprocess_HOOK() for image_formatter.
*
* Apply custom transformation for default image formatter.
*/
function cloudinary_media_library_widget_preprocess_image_formatter(&$variables) {
$item = $variables['item'];
if ($item instanceof CloudinaryImage) {
/** @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');
$info = $item->getValue();
$value = $asset_helper->generateStringValue($info);
$variables['image']['#uri'] = $file_helper->generateUriForFileWithTransformation($value);
}
}
/**
* Implements hook_preprocess_HOOK() for responsive_image_formatter.
*
* Apply custom transformation for responsive image formatter.
*/
function cloudinary_media_library_widget_preprocess_responsive_image_formatter(&$variables) {
$item = $variables['item'];
if ($item instanceof CloudinaryImage) {
/** @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');
$info = $item->getValue();
$value = $asset_helper->generateStringValue($info);
$variables['responsive_image']['#uri'] = $file_helper->generateUriForFileWithTransformation($value);
}
}
/**
* Implements hook_preprocess_HOOK() for drimage_formatter.
*
* Apply custom transformation for drimage formatter and improve performance.
*/
function cloudinary_media_library_widget_preprocess_drimage_formatter(&$variables) {
$item = $variables['item'];
// Properly handle aspect ratio behaviour, so it well integrated with
// cloudinary behaviour.
if ($item->entity instanceof FileInterface) {
$uri = $item->entity->getFileUri();
[$scheme] = explode('://', $uri, 2);
if ($scheme !== 'cloudinary') {
return;
}
$info = CloudinaryStreamWrapper::parseUri($uri);
// Fix ration and add a placeholder to be replaced in htaccess by drimage
// width and height. See README.md for more details about htaccess changes.
$ratio = $variables['width'] / $variables['height'];
$info['transformation'] = implode('/', array_filter([
$info['transformation'],
"ar_{$ratio},c_crop",
"%replaceme%",
]));
/** @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');
$value = $asset_helper->generateStringValue($info);
$uri = $file_helper->generateUriForExternalUrl($value);
$variables['data']['original_source'] = \Drupal::service('file_url_generator')->generateAbsoluteString($uri);
}
}
/**
* Implements hook_field_formatter_info_alter().
*/
function cloudinary_media_library_widget_field_formatter_info_alter(array &$info) {
// Support contrib/core image formatters for custom cloudinary field type.
$field_formatters = [
'cloudinary_image' => [
'image',
'responsive_image',
'drimage',
],
'cloudinary_raw' => [
'file_default',
'file_url_plain',
'file_table',
],
];
foreach ($field_formatters as $field_type => $formatters) {
foreach ($formatters as $field_formatter) {
if (isset($info[$field_formatter])) {
$info[$field_formatter]['field_types'][] = $field_type;
}
}
}
}
/**
* Implements hook_ENTITY_TYPE_update() for media.
*
* Force updating media thumbnail when uri changes.
*/
function cloudinary_media_library_widget_media_update(MediaInterface $entity) {
if ($entity->getSource()->getPluginId() === 'cloudinary' && isset($entity->original)) {
$new_uri = $entity->getSource()->getMetadata($entity, 'thumbnail_uri');
$old_uri = $entity->getSource()->getMetadata($entity->original, 'thumbnail_uri');
if ($new_uri === $old_uri) {
return;
}
$values = [
'uri' => $new_uri,
];
$file_storage = \Drupal::entityTypeManager()->getStorage('file');
if ($existing = $file_storage->loadByProperties($values)) {
$file = reset($existing);
}
else {
/** @var \Drupal\file\FileInterface $file */
$file = $file_storage->create($values);
if ($owner = $entity->getOwner()) {
$file->setOwner($owner);
}
$file->setPermanent();
$file->save();
}
$entity->thumbnail->target_id = $file->id();
}
}
