class EditorMediaDialog in Drupal 10
Same name and namespace in other branches
- 8 core/modules/media/src/Form/EditorMediaDialog.php \Drupal\media\Form\EditorMediaDialog
- 9 core/modules/media/src/Form/EditorMediaDialog.php \Drupal\media\Form\EditorMediaDialog
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.
Hierarchy
- class \Drupal\Core\Form\FormBase implements ContainerInjectionInterface, FormInterface uses DependencySerializationTrait, LoggerChannelTrait, MessengerTrait, RedirectDestinationTrait, StringTranslationTrait
- class \Drupal\media\Form\EditorMediaDialog
Expanded class hierarchy of EditorMediaDialog
1 file declares its use of EditorMediaDialog
- EditorMediaDialogTest.php in core/
modules/ media/ tests/ src/ Kernel/ EditorMediaDialogTest.php
1 string reference to 'EditorMediaDialog'
- media.routing.yml in core/
modules/ media/ media.routing.yml - core/modules/media/media.routing.yml
File
- core/
modules/ media/ src/ Form/ EditorMediaDialog.php, line 31
Namespace
Drupal\media\FormView source
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;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
EditorMediaDialog:: |
protected | property | The entity display repository. | |
EditorMediaDialog:: |
protected | property | The entity repository. | |
EditorMediaDialog:: |
public | function |
Overrides FormInterface:: |
|
EditorMediaDialog:: |
public static | function |
Instantiates a new instance of this class. Overrides FormBase:: |
|
EditorMediaDialog:: |
public | function |
Returns a unique string identifying the form. Overrides FormInterface:: |
|
EditorMediaDialog:: |
protected | function | Gets the name of an image media item's source field. | |
EditorMediaDialog:: |
public static | function | Gets the default value for the view mode form element. | |
EditorMediaDialog:: |
public | function |
Form submission handler. Overrides FormInterface:: |
|
EditorMediaDialog:: |
public | function | Constructs a EditorMediaDialog object. | |
FormBase:: |
protected | property | The config factory. | 3 |
FormBase:: |
protected | property | The request stack. | |
FormBase:: |
protected | property | The route match. | |
FormBase:: |
protected | function | Retrieves a configuration object. | |
FormBase:: |
protected | function | Gets the config factory for this form. | 3 |
FormBase:: |
private | function | Returns the service container. | |
FormBase:: |
protected | function | Gets the current user. | |
FormBase:: |
protected | function | Gets the request object. | |
FormBase:: |
protected | function | Gets the route match. | |
FormBase:: |
protected | function | Gets the logger for a specific channel. | |
FormBase:: |
protected | function | Returns a redirect response object for the specified route. | |
FormBase:: |
public | function | Resets the configuration factory. | |
FormBase:: |
public | function | Sets the config factory for this form. | |
FormBase:: |
public | function | Sets the request stack object to use. | |
FormBase:: |
public | function |
Form validation handler. Overrides FormInterface:: |
49 |
LoggerChannelTrait:: |
protected | property | The logger channel factory service. | |
LoggerChannelTrait:: |
protected | function | Gets the logger for a specific channel. | |
LoggerChannelTrait:: |
public | function | Injects the logger channel factory. | |
MessengerTrait:: |
protected | property | The messenger. | 13 |
MessengerTrait:: |
public | function | Gets the messenger. | 13 |
MessengerTrait:: |
public | function | Sets the messenger. | |
RedirectDestinationTrait:: |
protected | property | The redirect destination service. | 1 |
RedirectDestinationTrait:: |
protected | function | Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url. | |
RedirectDestinationTrait:: |
protected | function | Returns the redirect destination service. | |
RedirectDestinationTrait:: |
public | function | Sets the redirect destination service. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 3 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |