EditorMediaDialog.php
Same filename in other branches
Namespace
Drupal\media\FormFile
-
core/
modules/ media/ src/ Form/ EditorMediaDialog.php
View source
<?php
namespace Drupal\media\Form;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\editor\EditorInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\editor\Ajax\EditorDialogSave;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\filter\Plugin\FilterInterface;
use Drupal\image\Plugin\Field\FieldType\ImageItem;
use Drupal\media\MediaInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
/**
* Provides a media embed dialog for text editors.
*
* Depending on the configuration of the filters associated with the text
* editor, this dialog allows users to set the alt text, alignment, and
* captioning status for embedded media items.
*
* @internal
* This is an internal part of the media system in Drupal core and may be
* subject to change in minor releases. This class should not be
* instantiated or extended by external code.
*/
class EditorMediaDialog extends FormBase {
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* The entity display repository.
*
* @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
*/
protected $entityDisplayRepository;
/**
* Constructs a EditorMediaDialog object.
*
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository.
* @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
* The entity display repository.
*/
public function __construct(EntityRepositoryInterface $entity_repository, EntityDisplayRepositoryInterface $entity_display_repository) {
$this->entityRepository = $entity_repository;
$this->entityDisplayRepository = $entity_display_repository;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('entity.repository'), $container->get('entity_display.repository'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'editor_media_dialog';
}
/**
* {@inheritdoc}
*
* @param array $form
* A nested array form elements comprising the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\editor\EditorInterface $editor
* The text editor to which this dialog corresponds.
*/
public function buildForm(array $form, FormStateInterface $form_state, EditorInterface $editor = NULL) {
// This form is special, in that the default values do not come from the
// server side, but from the client side, from a text editor. We must cache
// this data in form state, because when the form is rebuilt, we will be
// receiving values from the form, instead of the values from the text
// editor. If we don't cache it, this data will be lost. By convention,
// the data that the text editor sends to any dialog is in the
// 'editor_object' key.
if (isset($form_state->getUserInput()['editor_object'])) {
$editor_object = $form_state->getUserInput()['editor_object'];
// The data that the text editor sends to any dialog is in
// the 'editor_object' key.
$media_embed_element = $editor_object['attributes'];
$form_state->set('media_embed_element', $media_embed_element);
$has_caption = $editor_object['hasCaption'];
$form_state->set('hasCaption', $has_caption)
->setCached(TRUE);
}
else {
// Retrieve the user input from form state.
$media_embed_element = $form_state->get('media_embed_element');
$has_caption = $form_state->get('hasCaption');
}
$form['#tree'] = TRUE;
$form['#attached']['library'][] = 'editor/drupal.editor.dialog';
$form['#prefix'] = '<div id="editor-media-dialog-form">';
$form['#suffix'] = '</div>';
$filters = $editor->getFilterFormat()
->filters();
$filter_html = $filters->get('filter_html');
$filter_align = $filters->get('filter_align');
$filter_caption = $filters->get('filter_caption');
$media_embed_filter = $filters->get('media_embed');
$allowed_attributes = [];
if ($filter_html->status) {
$restrictions = $filter_html->getHTMLRestrictions();
$allowed_attributes = $restrictions['allowed']['drupal-media'];
}
$media = $this->entityRepository
->loadEntityByUuid('media', $media_embed_element['data-entity-uuid']);
if ($image_field_name = $this->getMediaImageSourceFieldName($media)) {
// We'll want the alt text from the same language as the host.
if (!empty($editor_object['hostEntityLangcode']) && $media->hasTranslation($editor_object['hostEntityLangcode'])) {
$media = $media->getTranslation($editor_object['hostEntityLangcode']);
}
$settings = $media->{$image_field_name}
->getItemDefinition()
->getSettings();
$alt = $media_embed_element['alt'] ?? NULL;
$form['alt'] = [
'#type' => 'textfield',
'#title' => $this->t('Alternate text'),
'#default_value' => $alt,
'#description' => $this->t('Short description of the image used by screen readers and displayed when the image is not loaded. This is important for accessibility.'),
'#required_error' => $this->t('Alternative text is required.<br />(Only in rare cases should this be left empty. To create empty alternative text, enter <code>""</code> — two double quotes without any content).'),
'#maxlength' => 2048,
'#placeholder' => $media->{$image_field_name}->alt,
'#parents' => [
'attributes',
'alt',
],
'#access' => !empty($settings['alt_field']) && ($filter_html->status === FALSE || !empty($allowed_attributes['alt'])),
];
}
// When Drupal core's filter_align is being used, the text editor offers the
// ability to change the alignment.
$form['align'] = [
'#title' => $this->t('Align'),
'#type' => 'radios',
'#options' => [
'none' => $this->t('None'),
'left' => $this->t('Left'),
'center' => $this->t('Center'),
'right' => $this->t('Right'),
],
'#default_value' => empty($media_embed_element['data-align']) ? 'none' : $media_embed_element['data-align'],
'#attributes' => [
'class' => [
'container-inline',
],
],
'#parents' => [
'attributes',
'data-align',
],
'#access' => $filter_align->status && ($filter_html->status === FALSE || !empty($allowed_attributes['data-align'])),
];
// When Drupal core's filter_caption is being used, the text editor offers
// the ability to in-place edit the media's caption: show a toggle.
$form['caption'] = [
'#title' => $this->t('Caption'),
'#type' => 'checkbox',
'#default_value' => $has_caption === 'true',
'#parents' => [
'hasCaption',
],
'#access' => $filter_caption->status && ($filter_html->status === FALSE || !empty($allowed_attributes['data-caption'])),
];
$view_mode_options = array_intersect_key($this->entityDisplayRepository
->getViewModeOptionsByBundle('media', $media->bundle()), $media_embed_filter->settings['allowed_view_modes']);
$default_view_mode = static::getViewModeDefaultValue($view_mode_options, $media_embed_filter, $media_embed_element['data-view-mode'] ?? NULL);
$form['view_mode'] = [
'#title' => $this->t("Display"),
'#type' => 'select',
'#options' => $view_mode_options,
'#default_value' => $default_view_mode,
'#parents' => [
'attributes',
'data-view-mode',
],
'#access' => count($view_mode_options) >= 2,
];
// Store the default from the MediaEmbed filter, so that if the selected
// view mode matches the default, we can drop the 'data-view-mode'
// attribute.
$form_state->set('filter_default_view_mode', $media_embed_filter->settings['default_view_mode']);
if ((empty($form['alt']) || $form['alt']['#access'] === FALSE) && $form['align']['#access'] === FALSE && $form['caption']['#access'] === FALSE && $form['view_mode']['#access'] === FALSE) {
$format = $editor->getFilterFormat();
$warning = $this->t('There is nothing to configure for this media.');
$form['no_access_notice'] = [
'#markup' => $warning,
];
if ($format->access('update')) {
$tparams = [
'@warning' => $warning,
'@edit_url' => $format->toUrl('edit-form')
->toString(),
'%format' => $format->label(),
];
$form['no_access_notice']['#markup'] = $this->t('@warning <a href="@edit_url">Edit the text format %format</a> to modify the attributes that can be overridden.', $tparams);
}
}
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['save_modal'] = [
'#type' => 'submit',
'#value' => $this->t('Save'),
// No regular submit-handler. This form only works via JavaScript.
'#submit' => [],
'#ajax' => [
'callback' => '::submitForm',
'event' => 'click',
],
// Prevent this hidden element from being tabbable.
'#attributes' => [
'tabindex' => -1,
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
// When the `alt` attribute is set to two double quotes, transform it to the
// empty string: two double quotes signify "empty alt attribute". See above.
if (trim($form_state->getValue([
'attributes',
'alt',
], '')) === '""') {
$form_state->setValue([
'attributes',
'alt',
], '""');
}
// The `alt` attribute is optional: if it isn't set, the default value
// simply will not be overridden. It's important to set it to FALSE
// instead of unsetting the value. This way we explicitly inform
// the client side about the new value.
if ($form_state->hasValue([
'attributes',
'alt',
]) && trim($form_state->getValue([
'attributes',
'alt',
])) === '') {
$form_state->setValue([
'attributes',
'alt',
], FALSE);
}
// If the selected view mode matches the default on the filter, remove the
// attribute.
if (!empty($form_state->get('filter_default_view_mode')) && $form_state->getValue([
'attributes',
'data-view-mode',
]) === $form_state->get('filter_default_view_mode')) {
$form_state->setValue([
'attributes',
'data-view-mode',
], FALSE);
}
if ($form_state->getErrors()) {
unset($form['#prefix'], $form['#suffix']);
$form['status_messages'] = [
'#type' => 'status_messages',
'#weight' => -10,
];
$response->addCommand(new HtmlCommand('#editor-media-dialog-form', $form));
}
else {
// Only send back the relevant values.
$values = [
'hasCaption' => $form_state->getValue('hasCaption'),
'attributes' => $form_state->getValue('attributes'),
];
$response->addCommand(new EditorDialogSave($values));
$response->addCommand(new CloseModalDialogCommand());
}
return $response;
}
/**
* Gets the default value for the view mode form element.
*
* @param array $view_mode_options
* The array of options for the view mode form element.
* @param \Drupal\filter\Plugin\FilterInterface $media_embed_filter
* The media embed filter.
* @param string $media_element_view_mode_attribute
* The data-view-mode attribute on the <drupal-media> element.
*
* @return string|null
* The default value for the view mode form element.
*/
public static function getViewModeDefaultValue(array $view_mode_options, FilterInterface $media_embed_filter, $media_element_view_mode_attribute) {
// The select element won't display without at least two options, so if
// that's the case, just return NULL.
if (count($view_mode_options) < 2) {
return NULL;
}
$filter_default_view_mode = $media_embed_filter->settings['default_view_mode'];
// If the current media embed ($media_embed_element) has a set view mode,
// we want to use that as the default in the select form element,
// otherwise we'll want to use the default for all embedded media.
if (!empty($media_element_view_mode_attribute) && array_key_exists($media_element_view_mode_attribute, $view_mode_options)) {
return $media_element_view_mode_attribute;
}
elseif (array_key_exists($filter_default_view_mode, $view_mode_options)) {
return $filter_default_view_mode;
}
return NULL;
}
/**
* Gets the name of an image media item's source field.
*
* @param \Drupal\media\MediaInterface $media
* The media item being embedded.
*
* @return string|null
* The name of the image source field configured for the media item, or
* NULL if the source field is not an image field.
*/
protected function getMediaImageSourceFieldName(MediaInterface $media) {
$field_definition = $media->getSource()
->getSourceFieldDefinition($media->bundle->entity);
$item_class = $field_definition->getItemDefinition()
->getClass();
if (is_a($item_class, ImageItem::class, TRUE)) {
return $field_definition->getName();
}
return NULL;
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
EditorMediaDialog | Provides a media embed dialog for text editors. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.