1<?php
2
3namespace Drupal\Tests\editor\Kernel;
4
5use Drupal\Component\Serialization\Json;
6use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
7use Drupal\Core\Language\LanguageInterface;
8use Drupal\editor\Entity\Editor;
9use Drupal\entity_test\Entity\EntityTest;
10use Drupal\quickedit\MetadataGenerator;
11use Drupal\Tests\quickedit\Kernel\QuickEditTestBase;
12use Drupal\quickedit_test\MockQuickEditEntityFieldAccessCheck;
13use Drupal\editor\EditorController;
14use Symfony\Component\HttpFoundation\Request;
15use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
16use Symfony\Component\HttpKernel\HttpKernelInterface;
17use Drupal\filter\Entity\FilterFormat;
18
19/**
20 * Tests Edit module integration (Editor module's inline editing support).
21 *
22 * @group editor
23 */
24class QuickEditIntegrationTest extends QuickEditTestBase {
25
26  /**
27   * {@inheritdoc}
28   */
29  public static $modules = ['editor', 'editor_test'];
30
31  /**
32   * The manager for editor plug-ins.
33   *
34   * @var \Drupal\Component\Plugin\PluginManagerInterface
35   */
36  protected $editorManager;
37
38  /**
39   * The metadata generator object to be tested.
40   *
41   * @var \Drupal\quickedit\MetadataGeneratorInterface
42   */
43  protected $metadataGenerator;
44
45  /**
46   * The editor selector object to be used by the metadata generator object.
47   *
48   * @var \Drupal\quickedit\EditorSelectorInterface
49   */
50  protected $editorSelector;
51
52  /**
53   * The access checker object to be used by the metadata generator object.
54   *
55   * @var \Drupal\quickedit\Access\QuickEditEntityFieldAccessCheckInterface
56   */
57  protected $accessChecker;
58
59  /**
60   * The name of the field ued for tests.
61   *
62   * @var string
63   */
64  protected $fieldName;
65
66  protected function setUp() {
67    parent::setUp();
68
69    // Install the Filter module.
70
71    // Create a field.
72    $this->fieldName = 'field_textarea';
73    $this->createFieldWithStorage(
74      $this->fieldName, 'text', 1, 'Long text field',
75      // Instance settings.
76      [],
77      // Widget type & settings.
78      'text_textarea',
79      ['size' => 42],
80      // 'default' formatter type & settings.
81      'text_default',
82      []
83    );
84
85    // Create text format.
86    $full_html_format = FilterFormat::create([
87      'format' => 'full_html',
88      'name' => 'Full HTML',
89      'weight' => 1,
90      'filters' => [],
91    ]);
92    $full_html_format->save();
93
94    // Associate text editor with text format.
95    $editor = Editor::create([
96      'format' => $full_html_format->id(),
97      'editor' => 'unicorn',
98    ]);
99    $editor->save();
100
101    // Also create a text format without an associated text editor.
102    FilterFormat::create([
103      'format' => 'no_editor',
104      'name' => 'No Text Editor',
105      'weight' => 2,
106      'filters' => [],
107    ])->save();
108  }
109
110  /**
111   * Returns the in-place editor that quickedit selects.
112   *
113   * @param int $entity_id
114   *   An entity ID.
115   * @param string $field_name
116   *   A field name.
117   * @param string $view_mode
118   *   A view mode.
119   *
120   * @return string
121   *   Returns the selected in-place editor.
122   */
123  protected function getSelectedEditor($entity_id, $field_name, $view_mode = 'default') {
124    $storage = $this->container->get('entity_type.manager')->getStorage('entity_test');
125    $storage->resetCache([$entity_id]);
126    $entity = $storage->load($entity_id);
127    $items = $entity->get($field_name);
128    $options = \Drupal::service('entity_display.repository')
129      ->getViewDisplay('entity_test', 'entity_test', $view_mode)
130      ->getComponent($field_name);
131    return $this->editorSelector->getEditor($options['type'], $items);
132  }
133
134  /**
135   * Tests editor selection when the Editor module is present.
136   *
137   * Tests a textual field, with text filtering, with cardinality 1 and >1,
138   * always with a ProcessedTextEditor plug-in present, but with varying text
139   * format compatibility.
140   */
141  public function testEditorSelection() {
142    $this->editorManager = $this->container->get('plugin.manager.quickedit.editor');
143    $this->editorSelector = $this->container->get('quickedit.editor.selector');
144
145    // Create an entity with values for this text field.
146    $entity = EntityTest::create();
147    $entity->{$this->fieldName}->value = 'Hello, world!';
148    $entity->{$this->fieldName}->format = 'filtered_html';
149    $entity->save();
150
151    // Editor selection w/ cardinality 1, text format w/o associated text editor.
152    $this->assertEqual('form', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality 1, and the filtered_html text format, the 'form' editor is selected.");
153
154    // Editor selection w/ cardinality 1, text format w/ associated text editor.
155    $entity->{$this->fieldName}->format = 'full_html';
156    $entity->save();
157    $this->assertEqual('editor', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality 1, and the full_html text format, the 'editor' editor is selected.");
158
159    // Editor selection with text processing, cardinality >1
160    $this->fields->field_textarea_field_storage->setCardinality(2);
161    $this->fields->field_textarea_field_storage->save();
162    $this->assertEqual('form', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality >1, and both items using the full_html text format, the 'form' editor is selected.");
163  }
164
165  /**
166   * Tests (custom) metadata when the formatted text editor is used.
167   */
168  public function testMetadata() {
169    $this->editorManager = $this->container->get('plugin.manager.quickedit.editor');
170    $this->accessChecker = new MockQuickEditEntityFieldAccessCheck();
171    $this->editorSelector = $this->container->get('quickedit.editor.selector');
172    $this->metadataGenerator = new MetadataGenerator($this->accessChecker, $this->editorSelector, $this->editorManager);
173
174    // Create an entity with values for the field.
175    $entity = EntityTest::create();
176    $entity->{$this->fieldName}->value = 'Test';
177    $entity->{$this->fieldName}->format = 'full_html';
178    $entity->save();
179    $entity = EntityTest::load($entity->id());
180
181    // Verify metadata.
182    $items = $entity->get($this->fieldName);
183    \Drupal::state()->set('quickedit_test_field_access', 'forbidden');
184    $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default'));
185    \Drupal::state()->set('quickedit_test_field_access', 'neutral');
186    $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default'));
187    \Drupal::state()->set('quickedit_test_field_access', 'allowed');
188    $metadata = $this->metadataGenerator->generateFieldMetadata($items, 'default');
189    $expected = [
190      'access' => TRUE,
191      'label' => 'Long text field',
192      'editor' => 'editor',
193      'custom' => [
194        'format' => 'full_html',
195        'formatHasTransformations' => FALSE,
196      ],
197    ];
198    $this->assertEqual($expected, $metadata, 'The correct metadata (including custom metadata) is generated.');
199  }
200
201  /**
202   * Tests in-place editor attachments when the Editor module is present.
203   */
204  public function testAttachments() {
205    $this->editorSelector = $this->container->get('quickedit.editor.selector');
206
207    $editors = ['editor'];
208    $attachments = $this->editorSelector->getEditorAttachments($editors);
209    $this->assertIdentical($attachments, ['library' => ['editor/quickedit.inPlaceEditor.formattedText']], "Expected attachments for Editor module's in-place editor found.");
210  }
211
212  /**
213   * Tests GetUntransformedTextCommand AJAX command.
214   */
215  public function testGetUntransformedTextCommand() {
216    // Create an entity with values for the field.
217    $entity = EntityTest::create();
218    $entity->{$this->fieldName}->value = 'Test';
219    $entity->{$this->fieldName}->format = 'full_html';
220    $entity->save();
221    $entity = EntityTest::load($entity->id());
222
223    // Verify AJAX response.
224    $controller = new EditorController();
225    $request = new Request();
226    $response = $controller->getUntransformedText($entity, $this->fieldName, LanguageInterface::LANGCODE_DEFAULT, 'default');
227    $expected = [
228      [
229        'command' => 'editorGetUntransformedText',
230        'data' => 'Test',
231      ],
232    ];
233
234    $ajax_response_attachments_processor = \Drupal::service('ajax_response.attachments_processor');
235    $subscriber = new AjaxResponseSubscriber($ajax_response_attachments_processor);
236    $event = new FilterResponseEvent(
237      \Drupal::service('http_kernel'),
238      $request,
239      HttpKernelInterface::MASTER_REQUEST,
240      $response
241    );
242    $subscriber->onResponse($event);
243
244    $this->assertEqual(Json::encode($expected), $response->getContent(), 'The GetUntransformedTextCommand AJAX command works correctly.');
245  }
246
247}
248