diff --git a/src/EntityEmbedDisplay/EntityEmbedDisplayBase.php b/src/EntityEmbedDisplay/EntityEmbedDisplayBase.php index a696fbc..3c4460b 100644 --- a/src/EntityEmbedDisplay/EntityEmbedDisplayBase.php +++ b/src/EntityEmbedDisplay/EntityEmbedDisplayBase.php @@ -237,7 +237,7 @@ abstract class EntityEmbedDisplayBase extends PluginBase implements ContainerFac * The currently set context value. */ public function getContextValue($name) { - return $this->context[$name]; + return !empty($this->context[$name]) ? $this->context[$name] : NULL; } /** @@ -323,6 +323,20 @@ abstract class EntityEmbedDisplayBase extends PluginBase implements ContainerFac return array_key_exists($name, $attributes) ? $attributes[$name] : $default; } + /** + * Check if attribute is set. + * + * @param string $name + * The name of the attribute. + * + * @return bool + * Returns true of value is set. + */ + public function hasAttribute($name) { + $attributes = $this->getAttributeValues(); + return array_key_exists($name, $attributes); + } + /** * Gets the current language code. * diff --git a/src/EntityEmbedDisplay/EntityEmbedDisplayManager.php b/src/EntityEmbedDisplay/EntityEmbedDisplayManager.php index ce560df..b245cae 100644 --- a/src/EntityEmbedDisplay/EntityEmbedDisplayManager.php +++ b/src/EntityEmbedDisplay/EntityEmbedDisplayManager.php @@ -5,8 +5,10 @@ namespace Drupal\entity_embed\EntityEmbedDisplay; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Image\Image; use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\entity_embed\Plugin\entity_embed\EntityEmbedDisplay\MediaImageDecorator; /** * Provides an Entity Embed display plugin manager. @@ -176,4 +178,12 @@ class EntityEmbedDisplayManager extends DefaultPluginManager { }, $definitions); } + public function createInstance($plugin_id, array $configuration = []) { + $instance = parent::createInstance($plugin_id, $configuration); + // Use decorator pattern to add alt and title fields to dialog when + // embedding media with image source. + $decorator = new MediaImageDecorator($instance); + return $decorator; + } + } diff --git a/src/EntityEmbedDisplay/FieldFormatterEntityEmbedDisplayBase.php b/src/EntityEmbedDisplay/FieldFormatterEntityEmbedDisplayBase.php index 038bba2..364c345 100644 --- a/src/EntityEmbedDisplay/FieldFormatterEntityEmbedDisplayBase.php +++ b/src/EntityEmbedDisplay/FieldFormatterEntityEmbedDisplayBase.php @@ -169,6 +169,11 @@ abstract class FieldFormatterEntityEmbedDisplayBase extends EntityEmbedDisplayBa $formatter = $this->getFieldFormatter(); $formatter->prepareView([$fakeEntity->id() => $items]); $build = $formatter->viewElements($items, $this->getLangcode()); + // Don't ever cache a representation of an embedded entity, since the host + // entity may be overriding specific values (such as an `alt` attribute) + // which means that this particular rendered representation is unique to the + // host entity, and hence nonsensical to cache separately anyway. + unset($build[0]['#cache']['keys']); // For some reason $build[0]['#printed'] is TRUE, which means it will fail // to render later. So for now we manually fix that. // @todo Investigate why this is needed. diff --git a/src/Form/EntityEmbedDialog.php b/src/Form/EntityEmbedDialog.php index f2b3597..6f725da 100644 --- a/src/Form/EntityEmbedDialog.php +++ b/src/Form/EntityEmbedDialog.php @@ -487,7 +487,10 @@ class EntityEmbedDialog extends FormBase { ]; $plugin_id = !empty($values['attributes']['data-entity-embed-display']) ? $values['attributes']['data-entity-embed-display'] : $entity_element['data-entity-embed-display']; if (!empty($plugin_id)) { - if (is_string($entity_element['data-entity-embed-display-settings'])) { + if (empty($entity_element['data-entity-embed-display-settings'])) { + $entity_element['data-entity-embed-display-settings'] = []; + } + elseif (is_string($entity_element['data-entity-embed-display-settings'])) { $entity_element['data-entity-embed-display-settings'] = Json::decode($entity_element['data-entity-embed-display-settings']); } $display = $this->entityEmbedDisplayManager->createInstance($plugin_id, $entity_element['data-entity-embed-display-settings']); diff --git a/src/Plugin/Derivative/FieldFormatterDeriver.php b/src/Plugin/Derivative/FieldFormatterDeriver.php index 26a8b06..6c0970c 100644 --- a/src/Plugin/Derivative/FieldFormatterDeriver.php +++ b/src/Plugin/Derivative/FieldFormatterDeriver.php @@ -5,6 +5,7 @@ namespace Drupal\entity_embed\Plugin\Derivative; use Drupal\Component\Plugin\Derivative\DeriverBase; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; +use Drupal\entity_embed\Plugin\entity_embed\EntityEmbedDisplay\MediaReferenceFieldFormatter; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Field\FormatterPluginManager; diff --git a/src/Plugin/Derivative/ViewModeDeriver.php b/src/Plugin/Derivative/ViewModeDeriver.php index f23d70e..0a408f2 100644 --- a/src/Plugin/Derivative/ViewModeDeriver.php +++ b/src/Plugin/Derivative/ViewModeDeriver.php @@ -6,6 +6,8 @@ use Drupal\Component\Plugin\Derivative\DeriverBase; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityDisplayRepositoryInterface; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; +use Drupal\entity_embed\Plugin\entity_embed\EntityEmbedDisplay\MediaViewModeFieldFormatter; +use Drupal\entity_embed\Plugin\entity_embed\EntityEmbedDisplay\ViewModeFieldFormatter; use Symfony\Component\DependencyInjection\ContainerInterface; /** diff --git a/src/Plugin/entity_embed/EntityEmbedDisplay/MediaImageDecorator.php b/src/Plugin/entity_embed/EntityEmbedDisplay/MediaImageDecorator.php new file mode 100644 index 0000000..1202338 --- /dev/null +++ b/src/Plugin/entity_embed/EntityEmbedDisplay/MediaImageDecorator.php @@ -0,0 +1,275 @@ +decorated = $decorated; + } + + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = $this->decorated->buildConfigurationForm($form, $form_state); + + $plugin_id = $this->decorated->getPluginId(); + + if (in_array($plugin_id, $this->excludeList)) { + return $form; + } + + /** @var EntityInterface $entity */ + $entity = $this->decorated->getEntityFromContext(); + + if ($entity->getEntityTypeId() == 'media' && $image_field = $this->getMediaImageSourceField($entity)) { + + $settings = $entity->{$image_field}->getItemDefinition()->getSettings(); + $attributes = $this->getAttributeValues(); + + $alt = isset($attributes['alt']) ? $attributes['alt'] : $entity->{$image_field}->alt; + $title = isset($attributes['title']) ? $attributes['title'] : $entity->{$image_field}->title; + + // Setting empty alt to double quotes. See ImageFieldFormatter. + if ($settings['alt_field_required'] && $alt == '') { + $alt = '""'; + } + + if (!empty($settings['alt_field'])) { + + // Add support for editing the alternate and title text attributes. + $form['alt'] = [ + '#type' => 'textfield', + '#title' => $this->t('Alternate text'), + '#default_value' => $alt, + '#description' => $this->t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'), + '#required' => $settings['alt_field_required'], + '#required_error' => $this->t('Alternative text is required.
(Only in rare cases should this be left empty. To create empty alternative text, enter "" — two double quotes without any content).'), + '#maxlength' => 512, + ]; + } + + if (!empty($settings['title_field'])) { + $form['title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title'), + '#default_value' => $title, + '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'), + '#maxlength' => 1024, + '#required' => $settings['title_field_required'], + ]; + } + } + + return $form; + } + + public function access(AccountInterface $account = NULL) { + return $this->decorated->access($account); + } + + public function calculateDependencies() { + return $this->decorated->calculateDependencies(); + } + + public function getConfiguration() { + return $this->decorated->getConfiguration(); + } + + public function build() { + $build = $this->decorated->build(); + + + $plugin_id = $this->decorated->getPluginId(); + + if (in_array($plugin_id, $this->excludeList)) { + return $build; + } + + /** @var EntityInterface $entity */ + $entity = $this->decorated->getEntityFromContext(); + + if ($entity->getEntityTypeId() == 'media' && $image_field = $this->getMediaImageSourceField($entity)) { + $settings = $entity->{$image_field}->getItemDefinition()->getSettings(); + + if (!empty($settings['alt_field']) && $this->hasAttribute('alt')) { + $entity->{$image_field}->alt = $this->getAttributeValue('alt'); + $entity->thumbnail->alt = $this->getAttributeValue('alt'); + } + + if (!empty($settings['title_field']) && $this->hasAttribute('title')) { + $entity->{$image_field}->title = $this->getAttributeValue('title'); + $entity->thumbnail->title = $this->getAttributeValue('title'); + } + } + + return $build; + } + + public function getPluginDefinition() { + return $this->decorated->getPluginDefinition(); + } + + public function getPluginId() { + return $this->decorated->getPluginId(); + } + + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + return $this->decorated->validateConfigurationForm($form, $form_state); + } + + public function defaultConfiguration() { + return $this->decorated->defaultConfiguration(); + } + + public function setConfiguration(array $configuration) { + return $this->decorated->setConfiguration($configuration); + } + + public function setContextValue($name, $value) { + if (method_exists($this->decorated, 'setContextValue')) { + $this->decorated->setContextValue($name, $value); + } + } + + public function getContextValues() { + if (method_exists($this->decorated, 'getContextValues')) { + return $this->decorated->getContextValues(); + } + else { + return []; + } + } + + public function getContextValue($name) { + if (method_exists($this->decorated, 'getContextValue')) { + return $this->decorated->getContextValue($name); + } + return NULL; + } + + public function hasContextValue($name) { + if (method_exists($this->decorated, 'hasContextValue')) { + return $this->decorated->hasContextValue($name); + } + } + + public function setAttributes(array $attributes) { + if (method_exists($this->decorated, 'setAttributes')) { + $this->decorated->setAttributes($attributes); + } + } + + public function getAttributeValues() { + if (method_exists($this->decorated, 'getAttributeValues')) { + return $this->decorated->getAttributeValues(); + } + } + + public function getAttributeValue($name, $default = NULL) { + if (method_exists($this->decorated, 'getAttributeValue')) { + return $this->decorated->getAttributeValue($name, $default); + } + } + + public function hasAttribute($name) { + if (method_exists($this->decorated, 'hasAttribute')) { + return $this->decorated->hasAttribute($name); + } + } + + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + + $plugin_id = $this->decorated->getPluginId(); + + if (in_array($plugin_id, $this->excludeList)) { + $this->decorated->submitConfigurationForm($form, $form_state); + return; + } + + /** @var EntityInterface $entity */ + $entity = $this->decorated->getEntityFromContext(); + + if ($image_field = $this->getMediaImageSourceField($entity)) { + $settings = $entity->{$image_field}->getItemDefinition()->getSettings(); + $values = $form_state->getValue(['attributes', 'data-entity-embed-display-settings']); + + if (!empty($settings['alt_field'])) { + // When the alt attribute is set to two double quotes, transform it to + // the empty string: two double quotes signify "empty alt attribute". + // See ImagefieldFormatter. + if (trim($values['alt']) === '""') { + $values['alt'] = '""'; + } + // If the alt text is unchanged from the values set on the + // field, there's no need for the alt property to be set. + if ($values['alt'] === $entity->{$image_field}->alt) { + $values['alt'] = ''; + } + + $form_state->setValue(['attributes', 'alt'], $values['alt']); + $form_state->unsetValue(['attributes', 'data-entity-embed-display-settings', 'alt']); + } + + if (!empty($settings['title_field'])) { + if (empty($values['title'])) { + $values['title'] = ''; + } + // If the title text is unchanged from the values set on the + // field, there's no need for the title property to be set. + if ($values['title'] === $entity->{$image_field}->title) { + $values['title'] = ''; + } + + $form_state->setValue(['attributes', 'title'], $values['title']); + $form_state->unsetValue(['attributes', 'data-entity-embed-display-settings', 'title']); + } + } + $this->decorated->submitConfigurationForm($form, $form_state); + } + + /** + * Get image field from source config. + * + * @param \Drupal\media\MediaInterface $media + * Embedded entity. + * + * @return string|null + * String of image field name. + */ + protected function getMediaImageSourceField(MediaInterface $media) { + + if (!$media->getSource() instanceof Image) { + return NULL; + } + + $config = $media->getSource()->getConfiguration(); + + if (!empty($config['source_field'])) { + return $config['source_field']; + } + + return NULL; + } + +} diff --git a/tests/modules/entity_embed_translation_test/config/install/core.entity_form_display.node.article.default.yml b/tests/modules/entity_embed_test/config/install/core.entity_form_display.node.article.default.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/core.entity_form_display.node.article.default.yml rename to tests/modules/entity_embed_test/config/install/core.entity_form_display.node.article.default.yml diff --git a/tests/modules/entity_embed_test/config/install/core.entity_view_display.media.image.embed.yml b/tests/modules/entity_embed_test/config/install/core.entity_view_display.media.image.embed.yml new file mode 100644 index 0000000..60ae55d --- /dev/null +++ b/tests/modules/entity_embed_test/config/install/core.entity_view_display.media.image.embed.yml @@ -0,0 +1,29 @@ +langcode: en +status: true +dependencies: + config: + - field.field.media.image.field_media_image + - image.style.medium + - media.type.image + module: + - image + - user +id: media.image.default +targetEntityType: media +bundle: image +mode: embed +content: + field_media_image: + type: image + weight: 2 + region: content + label: hidden + settings: + image_style: medium + image_link: '' + third_party_settings: { } +hidden: + name: true + thumbnail: true + created: true + uid: true diff --git a/tests/modules/entity_embed_translation_test/config/install/core.entity_view_display.node.article.default.yml b/tests/modules/entity_embed_test/config/install/core.entity_view_display.node.article.default.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/core.entity_view_display.node.article.default.yml rename to tests/modules/entity_embed_test/config/install/core.entity_view_display.node.article.default.yml diff --git a/tests/modules/entity_embed_test/config/install/core.entity_view_mode.media.embed.yml b/tests/modules/entity_embed_test/config/install/core.entity_view_mode.media.embed.yml new file mode 100644 index 0000000..6a87a05 --- /dev/null +++ b/tests/modules/entity_embed_test/config/install/core.entity_view_mode.media.embed.yml @@ -0,0 +1,9 @@ +langcode: en +status: false +dependencies: + module: + - media +id: media.embed +label: 'Embed' +targetEntityType: media +cache: true diff --git a/tests/modules/entity_embed_translation_test/config/install/editor.editor.full_html.yml b/tests/modules/entity_embed_test/config/install/editor.editor.full_html.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/editor.editor.full_html.yml rename to tests/modules/entity_embed_test/config/install/editor.editor.full_html.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/embed.button.test_media_entity_embed.yml b/tests/modules/entity_embed_test/config/install/embed.button.test_media_entity_embed.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/embed.button.test_media_entity_embed.yml rename to tests/modules/entity_embed_test/config/install/embed.button.test_media_entity_embed.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/embed.button.test_node.yml b/tests/modules/entity_embed_test/config/install/embed.button.test_node.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/embed.button.test_node.yml rename to tests/modules/entity_embed_test/config/install/embed.button.test_node.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/field.field.media.image.field_media_image.yml b/tests/modules/entity_embed_test/config/install/field.field.media.image.field_media_image.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/field.field.media.image.field_media_image.yml rename to tests/modules/entity_embed_test/config/install/field.field.media.image.field_media_image.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/field.field.node.article.body.yml b/tests/modules/entity_embed_test/config/install/field.field.node.article.body.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/field.field.node.article.body.yml rename to tests/modules/entity_embed_test/config/install/field.field.node.article.body.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/field.storage.media.field_media_image.yml b/tests/modules/entity_embed_test/config/install/field.storage.media.field_media_image.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/field.storage.media.field_media_image.yml rename to tests/modules/entity_embed_test/config/install/field.storage.media.field_media_image.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/filter.format.full_html.yml b/tests/modules/entity_embed_test/config/install/filter.format.full_html.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/filter.format.full_html.yml rename to tests/modules/entity_embed_test/config/install/filter.format.full_html.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/media.type.image.yml b/tests/modules/entity_embed_test/config/install/media.type.image.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/media.type.image.yml rename to tests/modules/entity_embed_test/config/install/media.type.image.yml diff --git a/tests/modules/entity_embed_translation_test/config/install/node.type.article.yml b/tests/modules/entity_embed_test/config/install/node.type.article.yml similarity index 100% rename from tests/modules/entity_embed_translation_test/config/install/node.type.article.yml rename to tests/modules/entity_embed_test/config/install/node.type.article.yml diff --git a/tests/modules/entity_embed_test/entity_embed_test.info.yml b/tests/modules/entity_embed_test/entity_embed_test.info.yml index 45e7865..9b44c30 100644 --- a/tests/modules/entity_embed_test/entity_embed_test.info.yml +++ b/tests/modules/entity_embed_test/entity_embed_test.info.yml @@ -3,3 +3,13 @@ type: module description: 'Support module for the Entity Embed module tests.' core: 8.x package: Testing +dependencies: + - drupal:file + - drupal:image + - drupal:node + - drupal:text + - drupal:media + - drupal:ckeditor + - drupal:editor + - embed:embed + - entity_embed:entity_embed diff --git a/tests/modules/entity_embed_translation_test/entity_embed_translation_test.info.yml b/tests/modules/entity_embed_translation_test/entity_embed_translation_test.info.yml index 722468e..7d019ab 100644 --- a/tests/modules/entity_embed_translation_test/entity_embed_translation_test.info.yml +++ b/tests/modules/entity_embed_translation_test/entity_embed_translation_test.info.yml @@ -14,3 +14,4 @@ dependencies: - drupal:editor - embed:embed - entity_embed:entity_embed + diff --git a/tests/src/FunctionalJavascript/EntityEmbedTestBase.php b/tests/src/FunctionalJavascript/EntityEmbedTestBase.php index c56f12f..aa3e559 100644 --- a/tests/src/FunctionalJavascript/EntityEmbedTestBase.php +++ b/tests/src/FunctionalJavascript/EntityEmbedTestBase.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\entity_embed\FunctionalJavascript; +use Drupal\Component\Utility\Html; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; /** @@ -42,4 +43,23 @@ JS; $this->getSession()->wait(5000, "(typeof CKEDITOR != 'undefined' && typeof CKEDITOR.instances['edit-body-0-value'] != 'undefined' && CKEDITOR.instances['edit-body-0-value'].instanceReady)"); } + /** + * Get an embed code with given attributes. + * + * @param array $attributes + * The attributes to add. + * + * @return string + * A string containing a drupal-entity dom element. + */ + protected function getEmbedCode(array $attributes) { + $dom = Html::load('This placeholder should not be rendered.'); + $xpath = new \DOMXPath($dom); + $drupal_entity = $xpath->query('//drupal-entity')[0]; + foreach ($attributes as $attribute => $value) { + $drupal_entity->setAttribute($attribute, $value); + } + return Html::serialize($dom); + } + } diff --git a/tests/src/FunctionalJavascript/MediaImageTest.php b/tests/src/FunctionalJavascript/MediaImageTest.php new file mode 100644 index 0000000..d0ae6df --- /dev/null +++ b/tests/src/FunctionalJavascript/MediaImageTest.php @@ -0,0 +1,397 @@ +adminUser = $this->drupalCreateUser([ + 'use text format full_html', + 'administer nodes', + 'edit any article content', + ]); + } + + /** + * Tests the host entity's langcode is available in EntityEmbedDialog. + */ + public function testAltAndTitle() { + + \Drupal::service('file_system')->copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg'); + /** @var \Drupal\file\FileInterface $file */ + $file = File::create([ + 'uri' => 'public://example.jpg', + 'uid' => $this->adminUser->id(), + ]); + $file->save(); + + $media = Media::create([ + 'bundle' => 'image', + 'name' => 'Media Image', + 'field_media_image' => [ + [ + 'target_id' => $file->id(), + 'alt' => 'default alt', + 'title' => 'default title', + ], + ], + ]); + $media->save(); + + $host = $this->createNode([ + 'type' => 'article', + 'title' => 'host', + 'body' => [ + 'value' => '', + 'format' => 'full_html', + ], + ]); + + $this->drupalLogin($this->adminUser); + $this->drupalGet('node/' . $host->id() . '/edit'); + $this->waitForEditor(); + + $this->assignNameToCkeditorIframe(); + + $this->assertSession() + ->waitForElementVisible('css', 'a.cke_button__test_media_entity_embed') + ->click(); + $this->assertSession()->waitForId('drupal-modal'); + + $this->assertSession() + ->fieldExists('entity_id') + ->setValue('Media Image (1)'); + $this->assertSession()->elementExists('css', 'button.js-button-next')->click(); + $form = $this->assertSession()->waitForElementVisible('css', 'form.entity-embed-dialog-step--embed'); + + // Assert that the review step displays the selected entity with the label. + $text = $form->getText(); + $this->assertContains('Media Image', $text); + + $select = $this->assertSession() + ->selectExists('attributes[data-entity-embed-display]'); + + $select->setValue('entity_reference:entity_reference_entity_id'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Assert the id display doesn't have alt and title fields. + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][alt]'); + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][title]'); + + $select->setValue('entity_reference:entity_reference_label'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Assert the label display doesn't have alt and title fields. + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][alt]'); + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][title]'); + + $select->setValue('entity_reference:entity_reference_label'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // @todo entity_reference:media_thumbnail should also have alt and title text. + + $select->setValue('view_mode:media.embed'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Assert that the view_mode:media.embed display has alt and title fields, + // and that the default values match the values on the media's + // source image field. + $this->assertSession() + ->selectExists('attributes[data-entity-embed-display]') + ->setValue('view_mode:media.embed'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $alt = $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][alt]'); + $this->assertEquals($media->field_media_image->alt, $alt->getValue()); + $title = $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][title]'); + $this->assertEquals($media->field_media_image->title, $title->getValue()); + + $this->submitDialog(); + + $img = $this->assertSession()->elementExists('css', 'img'); + $this->assertEquals("default alt", $img->getAttribute('alt')); + $this->assertEquals("default title", $img->getAttribute('title')); + + $this->reopenDialog(); + + $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][alt]') + ->setValue('overridden alt'); + $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][title]') + ->setValue('overridden title'); + + $this->submitDialog(); + + $img = $this->assertSession()->elementExists('css', 'img'); + $this->assertEquals('overridden alt', $img->getAttribute('alt')); + $this->assertEquals('overridden title', $img->getAttribute('title')); + + $this->config('field.field.media.image.field_media_image') + ->set('settings.alt_field', FALSE) + ->set('settings.title_field', FALSE) + ->save(); + + $field = FieldConfig::load('media.image.field_media_image'); + $settings = $field->getSettings(); + $settings['alt_field'] = FALSE; + $settings['title_field'] = FALSE; + $field->set('settings', $settings); + $field->save(); + + // @todo flush only what's necessary. + drupal_flush_all_caches(); + + $this->reopenDialog(); + + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][alt]'); + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][title]'); + + $this->submitDialog(); + + $img = $this->assertSession()->elementExists('css', 'img'); + $this->assertEquals('default alt', $img->getAttribute('alt')); + $this->assertEquals('default title', $img->getAttribute('title')); + + $field = FieldConfig::load('media.image.field_media_image'); + $settings = $field->getSettings(); + $settings['alt_field'] = TRUE; + $field->set('settings', $settings); + $field->save(); + + // @todo flush only what's necessary. + drupal_flush_all_caches(); + + $this->reopenDialog(); + + // With only alt field enabled, only alt field should display. + $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][alt]')->setValue('overridden alt'); + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][title]'); + + $this->submitDialog(); + + $img = $this->assertSession()->elementExists('css', 'img'); + $this->assertEquals('overridden alt', $img->getAttribute('alt')); + $this->assertEquals('default title', $img->getAttribute('title')); + + $field = FieldConfig::load('media.image.field_media_image'); + $settings = $field->getSettings(); + $settings['alt_field'] = FALSE; + $settings['title_field'] = TRUE; + $field->set('settings', $settings); + $field->save(); + + // @todo flush only what's necessary. + drupal_flush_all_caches(); + + $this->reopenDialog(); + + // With only title field enabled, only title field should display. + $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][title]')->setValue('overridden title'); + $this->assertSession() + ->fieldNotExists('attributes[data-entity-embed-display-settings][alt]'); + + $this->submitDialog(); + + $img = $this->assertSession()->elementExists('css', 'img'); + $this->assertEquals('overridden title', $img->getAttribute('title')); + $this->assertEquals('default alt', $img->getAttribute('alt')); + + $field = FieldConfig::load('media.image.field_media_image'); + $settings = $field->getSettings(); + $settings['alt_field'] = TRUE; + $settings['title_field'] = TRUE; + $settings['alt_field_required'] = FALSE; + $settings['title_field_required'] = TRUE; + $field->set('settings', $settings); + $field->save(); + + // @todo flush only what's necessary. + drupal_flush_all_caches(); + + $this->reopenDialog(); + + $alt = $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][alt]'); + $this->assertFalse($alt->hasAttribute('required')); + $title = $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][title]'); + $this->assertTrue($title->hasAttribute('required')); + + $this->submitDialog(); + + $field = FieldConfig::load('media.image.field_media_image'); + $settings = $field->getSettings(); + $settings['alt_field_required'] = TRUE; + $settings['title_field_required'] = FALSE; + $field->set('settings', $settings); + $field->save(); + + // @todo flush only what's necessary. + drupal_flush_all_caches(); + + $this->reopenDialog(); + + $alt = $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][alt]'); + $this->assertTrue($alt->hasAttribute('required')); + $title = $this->assertSession() + ->fieldExists('attributes[data-entity-embed-display-settings][title]'); + $this->assertFalse($title->hasAttribute('required')); + + // Test that setting value to double quote will allow setting the alt and title + // to empty. + $alt->setValue('""'); + $title->setValue('""'); + + $this->submitDialog(); + + $img = $this->assertSession()->elementExists('css', 'img'); + $this->assertEmpty($img->getAttribute('alt')); + $this->assertEmpty($img->getAttribute('title')); + + // Test the same embed with different alt and title text. + $input = $this->getEmbedCode([ + 'alt' => 'alt 1', + 'title' => 'title 1', + 'data-embed-button' => 'test_media_entity_embed', + 'data-entity-embed-display' => 'view_mode:media.embed', + 'data-entity-embed-display-settings' => '', + 'data-entity-type' => 'media', + 'data-entity-uuid' => $media->uuid(), + 'data-langcode' => 'en', + ]); + $input .= $this->getEmbedCode([ + 'alt' => 'alt 2', + 'title' => 'title 2', + 'data-embed-button' => 'test_media_entity_embed', + 'data-entity-embed-display' => 'view_mode:media.embed', + 'data-entity-embed-display-settings' => '', + 'data-entity-type' => 'media', + 'data-entity-uuid' => $media->uuid(), + 'data-langcode' => 'en', + ]); + $input .= $this->getEmbedCode([ + 'alt' => 'alt 3', + 'title' => 'title 3', + 'data-embed-button' => 'test_media_entity_embed', + 'data-entity-embed-display' => 'view_mode:media.embed', + 'data-entity-embed-display-settings' => '', + 'data-entity-type' => 'media', + 'data-entity-uuid' => $media->uuid(), + 'data-langcode' => 'en', + ]); + + $this->getSession()->switchToIFrame(); + + $this->assertSession() + ->waitForElementVisible('css', 'a.cke_button__source') + ->click(); + + $source = $this->assertSession() + ->waitForElementVisible('xpath', "//textarea[contains(@class, 'cke_source')]"); + $source->setValue($input); + + // Exit "source" mode. + $this->assertSession() + ->waitForElementVisible('css', 'a.cke_button__source') + ->click(); + + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assignNameToCkeditorIframe(); + $this->getSession()->switchToIFrame('ckeditor'); + + $img = $this->assertSession()->waitForElement('xpath', "//img[contains(@alt, 'alt 1')]"); + $this->assertEquals('alt 1', $img->getAttribute('alt')); + $this->assertEquals('title 1', $img->getAttribute('title')); + + $img = $this->assertSession()->elementExists('xpath', "//img[contains(@alt, 'alt 2')]"); + $this->assertEquals('alt 2', $img->getAttribute('alt')); + $this->assertEquals('title 2', $img->getAttribute('title')); + + $img = $this->assertSession()->elementExists('xpath', "//img[contains(@alt, 'alt 3')]"); + $this->assertEquals('alt 3', $img->getAttribute('alt')); + $this->assertEquals('title 3', $img->getAttribute('title')); + + // Save the host entity. + $this->getSession()->switchToIFrame(); + $this->assertSession()->buttonExists('Save')->press(); + + $img = $this->assertSession()->waitForElement('xpath', "//img[contains(@alt, 'alt 1')]"); + $this->assertEquals('alt 1', $img->getAttribute('alt')); + $this->assertEquals('title 1', $img->getAttribute('title')); + + $img = $this->assertSession()->elementExists('xpath', "//img[contains(@alt, 'alt 2')]"); + $this->assertEquals('alt 2', $img->getAttribute('alt')); + $this->assertEquals('title 2', $img->getAttribute('title')); + + $img = $this->assertSession()->elementExists('xpath', "//img[contains(@alt, 'alt 3')]"); + $this->assertEquals('alt 3', $img->getAttribute('alt')); + $this->assertEquals('title 3', $img->getAttribute('title')); + + } + + /** + * Helper function to reopen EntityEmbedDialog for first embed. + */ + protected function reopenDialog() { + $this->getSession()->switchToIFrame(); + $select_and_edit_embed = "var editor = CKEDITOR.instances['edit-body-0-value']; + var entityEmbed = editor.widgets.getByElement(editor.editable().findOne('div')); + entityEmbed.focus(); + editor.execCommand('editdrupalentity');"; + $this->getSession()->executeScript($select_and_edit_embed); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertSession()->waitForElementVisible('css', 'form.entity-embed-dialog-step--embed'); + } + + /** + * Helper function to submit dialog and focus on ckeditor frame. + */ + protected function submitDialog() { + $this->assertSession()->elementExists('css', 'button.button--primary')->press(); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Verify that the embedded entity preview in CKEditor displays the image + // with the default alt and title. + $this->getSession()->switchToIFrame('ckeditor'); + } + +}