diff --git a/js/plugins/drupalentity/plugin.js b/js/plugins/drupalentity/plugin.js index 0f5d00c..27bbf56 100644 --- a/js/plugins/drupalentity/plugin.js +++ b/js/plugins/drupalentity/plugin.js @@ -36,7 +36,9 @@ var existingElement = getSelectedEmbeddedEntity(editor); - var existingValues = {}; + var existingValues = { + 'data-editor-langcode' : editor.langCode + }; if (existingElement && existingElement.$ && existingElement.$.firstChild) { var embedDOMElement = existingElement.$.firstChild; // Populate array with the entity's current attributes. diff --git a/src/Form/EntityEmbedDialog.php b/src/Form/EntityEmbedDialog.php index da1c9d0..5c7e767 100644 --- a/src/Form/EntityEmbedDialog.php +++ b/src/Form/EntityEmbedDialog.php @@ -10,6 +10,7 @@ use Drupal\Core\Ajax\SetDialogTitleCommand; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\TranslatableInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBuilderInterface; @@ -23,6 +24,7 @@ use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager; use Drupal\Component\Serialization\Json; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Drupal\Core\TempStore\PrivateTempStoreFactory; /** * Provides a form to embed entities by specifying data attributes. @@ -85,6 +87,13 @@ class EntityEmbedDialog extends FormBase { */ protected $entityBrowserSettings = []; + /** + * The temp store factory. + * + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory + */ + protected $tempStoreFactory; + /** * Constructs a EntityEmbedDialog object. * @@ -100,14 +109,17 @@ class EntityEmbedDialog extends FormBase { * The entity field manager. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory + * The tempstore factory. */ - public function __construct(EntityEmbedDisplayManager $entity_embed_display_manager, FormBuilderInterface $form_builder, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, EntityFieldManagerInterface $entity_field_manager, ModuleHandlerInterface $module_handler) { + public function __construct(EntityEmbedDisplayManager $entity_embed_display_manager, FormBuilderInterface $form_builder, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, EntityFieldManagerInterface $entity_field_manager, ModuleHandlerInterface $module_handler, PrivateTempStoreFactory $temp_store_factory) { $this->entityEmbedDisplayManager = $entity_embed_display_manager; $this->formBuilder = $form_builder; $this->entityTypeManager = $entity_type_manager; $this->eventDispatcher = $event_dispatcher; $this->entityFieldManager = $entity_field_manager; $this->moduleHandler = $module_handler; + $this->tempStoreFactory = $temp_store_factory; } /** @@ -120,7 +132,8 @@ class EntityEmbedDialog extends FormBase { $container->get('entity_type.manager'), $container->get('event_dispatcher'), $container->get('entity_field.manager'), - $container->get('module_handler') + $container->get('module_handler'), + $container->get('tempstore.private') ); } @@ -146,6 +159,15 @@ class EntityEmbedDialog extends FormBase { public function buildForm(array $form, FormStateInterface $form_state, EditorInterface $editor = NULL, EmbedButtonInterface $embed_button = NULL) { $values = $form_state->getValues(); $input = $form_state->getUserInput(); + + if (!empty($input['editor_object']['data-editor-langcode'])) { + $langcode = $input['editor_object']['data-editor-langcode']; + $this->tempStoreFactory->get('entity_embed_dialog')->set('editor_language', $langcode); + } + else { + $langcode = $this->tempStoreFactory->get('entity_embed_dialog')->get('editor_language'); + } + // Set embed button element in form state, so that it can be used later in // validateForm() function. $form_state->set('embed_button', $embed_button); @@ -168,7 +190,15 @@ class EntityEmbedDialog extends FormBase { $form_state->set('entity_element', $entity_element); $entity = $this->entityTypeManager->getStorage($entity_element['data-entity-type']) ->loadByProperties(['uuid' => $entity_element['data-entity-uuid']]); - $form_state->set('entity', current($entity) ?: NULL); + $entity = current($entity); + + if ($entity && $entity instanceof TranslatableInterface && !empty($langcode)) { + if ($entity->hasTranslation($langcode)) { + $entity = $entity->getTranslation($langcode); + } + } + + $form_state->set('entity', $entity ?: NULL); if (!$form_state->get('step')) { // If an entity has been selected, then always skip to the embed options. @@ -746,6 +776,15 @@ class EntityEmbedDialog extends FormBase { $entity = $this->entityTypeManager->getStorage($entity_element['data-entity-type']) ->loadByProperties(['uuid' => $entity_element['data-entity-uuid']]); $entity = current($entity); + + $langcode = $this->tempStoreFactory->get('entity_embed_dialog')->get('editor_language'); + + if ($entity && $entity instanceof TranslatableInterface && !empty($langcode)) { + if ($entity->hasTranslation($langcode)) { + $entity = $entity->getTranslation($langcode); + } + } + $plugin_id = $entity_element['data-entity-embed-display']; $plugin_settings = !empty($entity_element['data-entity-embed-display-settings']) ? $entity_element['data-entity-embed-display-settings'] : []; $display = $this->entityEmbedDisplayManager->createInstance($plugin_id, $plugin_settings); diff --git a/tests/modules/entity_embed_translation_test/config/install/core.entity_form_display.node.article.default.yml b/tests/modules/entity_embed_translation_test/config/install/core.entity_form_display.node.article.default.yml new file mode 100644 index 0000000..8f60990 --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/core.entity_form_display.node.article.default.yml @@ -0,0 +1,67 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.article.body + - node.type.article + module: + - text +id: node.article.default +targetEntityType: node +bundle: article +mode: default +content: + body: + type: text_textarea_with_summary + weight: 121 + settings: + rows: 9 + summary_rows: 3 + placeholder: '' + third_party_settings: { } + region: content + created: + type: datetime_timestamp + weight: 10 + region: content + settings: { } + third_party_settings: { } + promote: + type: boolean_checkbox + settings: + display_label: true + weight: 15 + region: content + third_party_settings: { } + status: + type: boolean_checkbox + settings: + display_label: true + weight: 120 + region: content + third_party_settings: { } + sticky: + type: boolean_checkbox + settings: + display_label: true + weight: 16 + region: content + third_party_settings: { } + title: + type: string_textfield + weight: -5 + region: content + settings: + size: 60 + placeholder: '' + third_party_settings: { } + uid: + type: entity_reference_autocomplete + weight: 5 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + region: content + third_party_settings: { } +hidden: { } diff --git a/tests/modules/entity_embed_translation_test/config/install/core.entity_view_display.node.article.default.yml b/tests/modules/entity_embed_translation_test/config/install/core.entity_view_display.node.article.default.yml new file mode 100644 index 0000000..edf834f --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/core.entity_view_display.node.article.default.yml @@ -0,0 +1,27 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.article.body + - node.type.article + module: + - text + - user +id: node.article.default +targetEntityType: node +bundle: article +mode: default +content: + body: + label: hidden + type: text_default + weight: 101 + settings: { } + third_party_settings: { } + region: content + links: + weight: 100 + settings: { } + third_party_settings: { } + region: content +hidden: { } diff --git a/tests/modules/entity_embed_translation_test/config/install/editor.editor.full_html.yml b/tests/modules/entity_embed_translation_test/config/install/editor.editor.full_html.yml new file mode 100644 index 0000000..4468000 --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/editor.editor.full_html.yml @@ -0,0 +1,64 @@ +langcode: en +status: true +dependencies: + config: + - filter.format.full_html + module: + - ckeditor +format: full_html +editor: ckeditor +settings: + toolbar: + rows: + - + - + name: Formatting + items: + - Bold + - Italic + - Strike + - Superscript + - Subscript + - '-' + - RemoveFormat + - + name: Linking + items: + - DrupalLink + - DrupalUnlink + - + name: Lists + items: + - BulletedList + - NumberedList + - + name: Media + items: + - Blockquote + - DrupalImage + - Table + - HorizontalRule + - test_node + - test_media_entity_embed + - + name: 'Block Formatting' + items: + - Format + - + name: Tools + items: + - ShowBlocks + - Source + plugins: + language: + language_list: un + stylescombo: + styles: '' +image_upload: + status: true + scheme: public + directory: inline-images + max_size: '' + max_dimensions: + width: null + height: null diff --git a/tests/modules/entity_embed_translation_test/config/install/embed.button.test_media_entity_embed.yml b/tests/modules/entity_embed_translation_test/config/install/embed.button.test_media_entity_embed.yml new file mode 100644 index 0000000..7373631 --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/embed.button.test_media_entity_embed.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - media.type.image + module: + - entity_embed + - media +label: 'Media Entity Embed' +id: test_media_entity_embed +type_id: entity +type_settings: + entity_type: media + bundles: + - image + display_plugins: { } + entity_browser: '' + entity_browser_settings: + display_review: false +icon_uuid: null +icon_path: null diff --git a/tests/modules/entity_embed_translation_test/config/install/embed.button.test_node.yml b/tests/modules/entity_embed_translation_test/config/install/embed.button.test_node.yml new file mode 100644 index 0000000..db25535 --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/embed.button.test_node.yml @@ -0,0 +1,15 @@ +langcode: en +status: true +dependencies: + module: + - entity_embed + - node +label: Node +id: test_node +type_id: entity +type_settings: + entity_type: node + bundles: { } + display_plugins: { } +icon_uuid: null +icon_path: null diff --git a/tests/modules/entity_embed_translation_test/config/install/field.field.node.article.body.yml b/tests/modules/entity_embed_translation_test/config/install/field.field.node.article.body.yml new file mode 100644 index 0000000..8f3681d --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/field.field.node.article.body.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.body + - node.type.article + module: + - text +id: node.article.body +field_name: body +entity_type: node +bundle: article +label: Body +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + display_summary: true +field_type: text_with_summary diff --git a/tests/modules/entity_embed_translation_test/config/install/field.storage.media.field_media_image.yml b/tests/modules/entity_embed_translation_test/config/install/field.storage.media.field_media_image.yml new file mode 100644 index 0000000..231200d --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/field.storage.media.field_media_image.yml @@ -0,0 +1,32 @@ +langcode: en +status: true +dependencies: + enforced: + module: + - media + module: + - file + - image + - media +id: media.field_media_image +field_name: field_media_image +entity_type: media +type: image +settings: + default_image: + uuid: null + alt: '' + title: '' + width: null + height: null + target_type: file + display_field: false + display_default: false + uri_scheme: public +module: image +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/tests/modules/entity_embed_translation_test/config/install/filter.format.full_html.yml b/tests/modules/entity_embed_translation_test/config/install/filter.format.full_html.yml new file mode 100644 index 0000000..821219c --- /dev/null +++ b/tests/modules/entity_embed_translation_test/config/install/filter.format.full_html.yml @@ -0,0 +1,49 @@ +langcode: en +status: true +dependencies: + module: + - editor + - entity_embed +name: 'Full HTML' +format: full_html +weight: 10 +filters: + filter_align: + id: filter_align + provider: filter + status: true + weight: 8 + settings: { } + filter_caption: + id: filter_caption + provider: filter + status: true + weight: 9 + settings: { } + filter_htmlcorrector: + id: filter_htmlcorrector + provider: filter + status: true + weight: 10 + settings: { } + editor_file_reference: + id: editor_file_reference + provider: editor + status: true + weight: 11 + settings: { } + entity_embed: + id: entity_embed + provider: entity_embed + status: true + weight: 0 + settings: { } + filter_html: + id: filter_html + provider: filter + status: false + weight: -10 + settings: + allowed_html: '

    1.  '
      +      filter_html_help: true
      +      filter_html_nofollow: false
      diff --git a/tests/modules/entity_embed_translation_test/config/install/language.content_settings.node.article.yml b/tests/modules/entity_embed_translation_test/config/install/language.content_settings.node.article.yml
      new file mode 100644
      index 0000000..de684ef
      --- /dev/null
      +++ b/tests/modules/entity_embed_translation_test/config/install/language.content_settings.node.article.yml
      @@ -0,0 +1,15 @@
      +langcode: en
      +status: true
      +dependencies:
      +  config:
      +    - node.type.article
      +  module:
      +    - content_translation
      +third_party_settings:
      +  content_translation:
      +    enabled: true
      +id: node.article
      +target_entity_type_id: node
      +target_bundle: article
      +default_langcode: site_default
      +language_alterable: true
      diff --git a/tests/modules/entity_embed_translation_test/config/install/language.entity.fr.yml b/tests/modules/entity_embed_translation_test/config/install/language.entity.fr.yml
      new file mode 100644
      index 0000000..280469e
      --- /dev/null
      +++ b/tests/modules/entity_embed_translation_test/config/install/language.entity.fr.yml
      @@ -0,0 +1,8 @@
      +langcode: en
      +status: true
      +dependencies: {  }
      +id: fr
      +label: French
      +direction: ltr
      +weight: 1
      +locked: false
      diff --git a/tests/modules/entity_embed_translation_test/config/install/media.type.image.yml b/tests/modules/entity_embed_translation_test/config/install/media.type.image.yml
      new file mode 100644
      index 0000000..3a53b77
      --- /dev/null
      +++ b/tests/modules/entity_embed_translation_test/config/install/media.type.image.yml
      @@ -0,0 +1,12 @@
      +langcode: en
      +status: true
      +dependencies: {  }
      +id: image
      +label: Image
      +description: 'Use local images for reusable media.'
      +source: image
      +queue_thumbnail_downloads: false
      +new_revision: true
      +source_configuration:
      +  source_field: field_media_image
      +field_map: {  }
      diff --git a/tests/modules/entity_embed_translation_test/config/install/node.type.article.yml b/tests/modules/entity_embed_translation_test/config/install/node.type.article.yml
      new file mode 100644
      index 0000000..a65ff90
      --- /dev/null
      +++ b/tests/modules/entity_embed_translation_test/config/install/node.type.article.yml
      @@ -0,0 +1,10 @@
      +langcode: en
      +status: true
      +dependencies: {  }
      +name: 'Article'
      +type: article
      +description: 'Article'
      +help: ''
      +new_revision: false
      +preview_mode: 1
      +display_submitted: true
      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
      new file mode 100644
      index 0000000..c4e1c05
      --- /dev/null
      +++ b/tests/modules/entity_embed_translation_test/entity_embed_translation_test.info.yml
      @@ -0,0 +1,16 @@
      +name: 'Entity Embed Translation test'
      +type: module
      +description: 'Aids in testing translation within entity embed'
      +core: 8.x
      +package: Testing
      +dependencies:
      + - drupal:content_translation
      + - drupal:locale
      + - drupal:file
      + - drupal:node
      + - drupal:text
      + - drupal:media
      + - drupal:ckeditor
      + - drupal:editor
      + - embed:embed
      + - entity_embed:entity_embed
      diff --git a/tests/src/FunctionalJavascript/ContentTranslationTest.php b/tests/src/FunctionalJavascript/ContentTranslationTest.php
      new file mode 100644
      index 0000000..ad2bc22
      --- /dev/null
      +++ b/tests/src/FunctionalJavascript/ContentTranslationTest.php
      @@ -0,0 +1,171 @@
      +translator = $this->drupalCreateUser($permissions);
      +
      +    $this->config('field.storage.node.body')
      +      ->set('translatable', TRUE)
      +      ->save();
      +  }
      +
      +  /**
      +   * Tests that a contextual link is available for translating a node.
      +   */
      +  public function testDialogLanguage() {
      +
      +    $node = $this->createNode([
      +      'type' => 'article',
      +      'title' => 'mouse',
      +    ]);
      +    $node_fr = $node->addTranslation('fr');
      +    $node_fr->title = 'souris';
      +    $node_fr->save();
      +
      +    $parent = $this->createNode([
      +      'type' => 'article',
      +      'title' => 'trap',
      +      'body' => [
      +        'value' => 'trap body',
      +        'format' => 'full_html',
      +      ],
      +    ]);
      +    $parent_fr = $parent->addTranslation('fr');
      +    $parent_fr->title = 'piège';
      +    $parent_fr->body->value = 'corp du piège';
      +    $parent_fr->body->format = 'full_html';
      +    $parent_fr->body->lang = 'fr';
      +    $parent_fr->save();
      +
      +    // Check that the translate link appears on the node page.
      +    $this->drupalLogin($this->translator);
      +    $this->drupalGet('node/' . $parent->id() . '/edit');
      +
      +    $this->assertSession()
      +      ->waitForElementVisible('css', 'a.cke_button__test_node')
      +      ->click();
      +    $this->assertSession()->waitForId('drupal-modal');
      +    $this->assertSession()
      +      ->waitForField('entity_id')
      +      ->setValue('mou');
      +
      +    $suggestions = $this->assertSession()
      +      ->waitForElementVisible('css', '#drupal-modal .ui-widget-content.ui-autocomplete')
      +      ->getText();
      +
      +    // Assert suggestions are in parent language (en).
      +    $this->assertContains('mouse', $suggestions);
      +
      +    $this->assertSession()
      +      ->fieldExists('entity_id')
      +      ->setValue('sour');
      +
      +    $suggestions = $this->assertSession()
      +      ->waitForElementVisible('css', '#drupal-modal .ui-widget-content.ui-autocomplete')
      +      ->getText();
      +
      +    // Assert suggestions do not contain translation.
      +    $this->assertNotContains('souris', $suggestions);
      +
      +    $this->assertSession()
      +      ->fieldExists('entity_id')
      +      ->setValue('mouse (1)');
      +
      +    $this->assertSession()->elementExists('css', 'button.js-button-next')->click();
      +    $this->assertSession()->assertWaitOnAjaxRequest();
      +    $this->assertSession()->responseContains('Selected entity');
      +
      +    $this->assertSession()->responseNotContains('souris');
      +    $this->assertSession()->responseContains('mouse');
      +
      +    // Get translation.
      +    $this->drupalGet('/fr/node/' . $parent->id() . '/edit');
      +
      +    $this->assertSession()
      +      ->waitForElementVisible('css', 'a.cke_button__test_node')
      +      ->click();
      +    $this->assertSession()->waitForId('drupal-modal');
      +    $this->assertSession()
      +      ->waitForField('entity_id')
      +      ->setValue('sour');
      +
      +    $suggestions = $this->assertSession()
      +      ->waitForElementVisible('css', '#drupal-modal .ui-widget-content.ui-autocomplete')
      +      ->getText();
      +
      +    // Assert suggestions are in parent language (en).
      +    $this->assertContains('souris', $suggestions);
      +
      +    $this->assertSession()
      +      ->fieldExists('entity_id')
      +      ->setValue('mouse');
      +
      +    $suggestions = $this->assertSession()
      +      ->waitForElementVisible('css', '#drupal-modal .ui-widget-content.ui-autocomplete')
      +      ->getText();
      +
      +    // Assert suggestions do not contain original language (en).
      +    $this->assertNotContains('mouse', $suggestions);
      +
      +    $this->assertSession()
      +      ->fieldExists('entity_id')
      +      ->setValue('souris (1)');
      +
      +    $this->assertSession()->elementExists('css', 'button.js-button-next')->click();
      +    $this->assertSession()->assertWaitOnAjaxRequest();
      +    $this->assertSession()->responseContains('Selected entity');
      +    // Assert the translated label appears, not the original.
      +    $this->assertSession()->responseContains('souris');
      +    $this->assertSession()->responseNotContains('mouse');
      +
      +  }
      +
      +  /**
      +   * Assigns a name to the CKEditor iframe, to allow use of ::switchToIFrame().
      +   *
      +   * @see \Behat\Mink\Session::switchToIFrame()
      +   */
      +  protected function assignNameToCkeditorIframe() {
      +    $javascript = <<getSession()->evaluateScript($javascript);
      +  }
      +
      +}