diff --git a/core/modules/editor/editor.libraries.yml b/core/modules/editor/editor.libraries.yml index 6f6c8f1b201c8976712707f361d8d8c32a27c068..bd46f6e8649198c3680fb4c39ab70e1484519d21 100644 --- a/core/modules/editor/editor.libraries.yml +++ b/core/modules/editor/editor.libraries.yml @@ -27,13 +27,3 @@ drupal.editor.dialog: - core/drupal.dialog - core/drupal.ajax - core/drupalSettings - -quickedit.inPlaceEditor.formattedText: - version: VERSION - js: - js/editor.formattedTextEditor.js: { attributes: { defer: true } } - dependencies: - - quickedit/quickedit - - editor/drupal.editor - - core/drupal.ajax - - core/drupalSettings diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module index f95dd8bfd022151a4935175b05894075b316e783..fcada7ffb8a4ad1fc498f6162614a120c66e7dd3 100644 --- a/core/modules/editor/editor.module +++ b/core/modules/editor/editor.module @@ -68,18 +68,6 @@ function editor_element_info_alter(&$types) { $types['text_format']['#pre_render'][] = 'element.editor:preRenderTextFormat'; } -/** - * Implements hook_field_formatter_info_alter(). - * - * @see quickedit_field_formatter_info_alter() - */ -function editor_field_formatter_info_alter(&$info) { - // Update \Drupal\text\Plugin\Field\FieldFormatter\TextDefaultFormatter's - // annotation to indicate that it supports the 'editor' in-place editor - // provided by this module. - $info['text_default']['quickedit'] = ['editor' => 'editor']; -} - /** * Implements hook_form_FORM_ID_alter(). */ diff --git a/core/modules/editor/editor.routing.yml b/core/modules/editor/editor.routing.yml index 0c5b2249bba9809abd5cdad802eea5f9d981b2d3..5bc0ce5c885fa3f40df631ac8cf5ee3272ba5495 100644 --- a/core/modules/editor/editor.routing.yml +++ b/core/modules/editor/editor.routing.yml @@ -5,18 +5,6 @@ editor.filter_xss: requirements: _entity_access: 'filter_format.use' -editor.field_untransformed_text: - path: '/editor/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}' - defaults: - _controller: '\Drupal\editor\EditorController::getUntransformedText' - options: - parameters: - entity: - type: entity:{entity_type} - requirements: - _permission: 'access in-place editing' - _access_quickedit_entity_field: 'TRUE' - editor.image_dialog: path: '/editor/dialog/image/{editor}' defaults: diff --git a/core/modules/editor/src/EditorController.php b/core/modules/editor/src/EditorController.php index 81441772c09fb9abce2913baf595c366436fb0be..7c4500e55363a227b00599b9d0ad616ee842c0cd 100644 --- a/core/modules/editor/src/EditorController.php +++ b/core/modules/editor/src/EditorController.php @@ -2,11 +2,7 @@ namespace Drupal\editor; -use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Controller\ControllerBase; -use Drupal\Core\Entity\EntityInterface; -use Drupal\editor\Ajax\GetUntransformedTextCommand; -use Drupal\filter\Plugin\FilterInterface; use Drupal\filter\FilterFormatInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -17,33 +13,6 @@ */ class EditorController extends ControllerBase { - /** - * Returns an Ajax response to render a text field without transformation filters. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity of which a formatted text field is being rerendered. - * @param string $field_name - * The name of the (formatted text) field that is being rerendered - * @param string $langcode - * The name of the language for which the formatted text field is being - * rerendered. - * @param string $view_mode_id - * The view mode the formatted text field should be rerendered in. - * - * @return \Drupal\Core\Ajax\AjaxResponse - * The Ajax response. - */ - public function getUntransformedText(EntityInterface $entity, $field_name, $langcode, $view_mode_id) { - $response = new AjaxResponse(); - - // Direct text editing is only supported for single-valued fields. - $field = $entity->getTranslation($langcode)->$field_name; - $editable_text = check_markup($field->value, $field->format, $langcode, [FilterInterface::TYPE_TRANSFORM_REVERSIBLE, FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE]); - $response->addCommand(new GetUntransformedTextCommand($editable_text)); - - return $response; - } - /** * Apply the necessary XSS filtering for using a certain text format's editor. * diff --git a/core/modules/editor/js/editor.formattedTextEditor.es6.js b/core/modules/quickedit/js/editors/formattedTextEditor.es6.js similarity index 99% rename from core/modules/editor/js/editor.formattedTextEditor.es6.js rename to core/modules/quickedit/js/editors/formattedTextEditor.es6.js index 6e69d785f7c0c4a752e1ec8ea059c62ed9b08b97..05bc4e7168ea4684e420e30e9afc1c5c84e0679b 100644 --- a/core/modules/editor/js/editor.formattedTextEditor.es6.js +++ b/core/modules/quickedit/js/editors/formattedTextEditor.es6.js @@ -219,7 +219,7 @@ url: Drupal.quickedit.util.buildUrl( fieldID, Drupal.url( - 'editor/!entity_type/!id/!field_name/!langcode/!view_mode', + 'quickedit/!entity_type/!id/!field_name/!langcode/!view_mode', ), ), submit: { nocssjs: true }, diff --git a/core/modules/editor/js/editor.formattedTextEditor.js b/core/modules/quickedit/js/editors/formattedTextEditor.js similarity index 98% rename from core/modules/editor/js/editor.formattedTextEditor.js rename to core/modules/quickedit/js/editors/formattedTextEditor.js index 0421b8da20fa250eb2e51e0f0e274e2f4e40e3fc..139429710a638ae1ae5f382c1f69ef7e8ebea3b3 100644 --- a/core/modules/editor/js/editor.formattedTextEditor.js +++ b/core/modules/quickedit/js/editors/formattedTextEditor.js @@ -124,7 +124,7 @@ _getUntransformedText(callback) { const fieldID = this.fieldModel.get('fieldID'); const textLoaderAjax = Drupal.ajax({ - url: Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('editor/!entity_type/!id/!field_name/!langcode/!view_mode')), + url: Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/!entity_type/!id/!field_name/!langcode/!view_mode')), submit: { nocssjs: true } diff --git a/core/modules/quickedit/quickedit.libraries.yml b/core/modules/quickedit/quickedit.libraries.yml index af5bcb2d7a778e30d74f13abc43fd1229c7aa011..e005342ee8f27a2af54b2027ac6ca7f6d72b9303 100644 --- a/core/modules/quickedit/quickedit.libraries.yml +++ b/core/modules/quickedit/quickedit.libraries.yml @@ -48,6 +48,16 @@ quickedit.inPlaceEditor.form: dependencies: - quickedit/quickedit +quickedit.inPlaceEditor.formattedText: + version: VERSION + js: + js/editors/formattedTextEditor.js: { attributes: { defer: true } } + dependencies: + - quickedit/quickedit + - editor/drupal.editor + - core/drupal.ajax + - core/drupalSettings + quickedit.inPlaceEditor.plainText: version: VERSION js: diff --git a/core/modules/quickedit/quickedit.module b/core/modules/quickedit/quickedit.module index 5ea1e24b593e1e120cd1428cc0756ac3fc8129a1..d571c667a7a6a656b566c1302b61c824545cae8d 100644 --- a/core/modules/quickedit/quickedit.module +++ b/core/modules/quickedit/quickedit.module @@ -119,6 +119,13 @@ function quickedit_field_formatter_info_alter(&$info) { $info[$key]['quickedit'] = ['editor' => 'form']; } } + + // Update \Drupal\text\Plugin\Field\FieldFormatter\TextDefaultFormatter's + // annotation to indicate that it supports the 'editor' in-place editor + // provided by this module, if enabled. + if (\Drupal::moduleHandler()->moduleExists('editor')) { + $info['text_default']['quickedit'] = ['editor' => 'editor']; + } } /** diff --git a/core/modules/quickedit/quickedit.routing.yml b/core/modules/quickedit/quickedit.routing.yml index b63b8a2f13161adc207168bebea89f10c1867028..35723d5aebb05ee54009925dff9bc0800a7f2a1c 100644 --- a/core/modules/quickedit/quickedit.routing.yml +++ b/core/modules/quickedit/quickedit.routing.yml @@ -61,3 +61,15 @@ quickedit.image_info: _permission: 'access in-place editing' _access_quickedit_entity_field: 'TRUE' _method: 'GET' + +quickedit.field_untransformed_text: + path: '/quickedit/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}' + defaults: + _controller: '\Drupal\quickedit\QuickEditController::getUntransformedText' + options: + parameters: + entity: + type: entity:{entity_type} + requirements: + _permission: 'access in-place editing' + _access_quickedit_entity_field: 'TRUE' diff --git a/core/modules/editor/src/Ajax/GetUntransformedTextCommand.php b/core/modules/quickedit/src/Ajax/GetUntransformedTextCommand.php similarity index 74% rename from core/modules/editor/src/Ajax/GetUntransformedTextCommand.php rename to core/modules/quickedit/src/Ajax/GetUntransformedTextCommand.php index 5a3768667935df8b63d610f16aaec27a9d05f460..1f934516155ba11dca17786048b2327d41cb60df 100644 --- a/core/modules/editor/src/Ajax/GetUntransformedTextCommand.php +++ b/core/modules/quickedit/src/Ajax/GetUntransformedTextCommand.php @@ -1,12 +1,11 @@ <?php -namespace Drupal\editor\Ajax; +namespace Drupal\quickedit\Ajax; use Drupal\Core\Ajax\BaseCommand; /** - * AJAX command to rerender a formatted text field without any transformation - * filters. + * AJAX command to rerender formatted text field without transformation filters. */ class GetUntransformedTextCommand extends BaseCommand { diff --git a/core/modules/editor/src/Plugin/InPlaceEditor/Editor.php b/core/modules/quickedit/src/Plugin/InPlaceEditor/Editor.php similarity index 92% rename from core/modules/editor/src/Plugin/InPlaceEditor/Editor.php rename to core/modules/quickedit/src/Plugin/InPlaceEditor/Editor.php index dab14ef01f5d3dbe9da42d343e2d481162c24c2f..5eb6805cbd358b10c0313451b3824e931285bfd2 100644 --- a/core/modules/editor/src/Plugin/InPlaceEditor/Editor.php +++ b/core/modules/quickedit/src/Plugin/InPlaceEditor/Editor.php @@ -1,18 +1,19 @@ <?php -namespace Drupal\editor\Plugin\InPlaceEditor; +namespace Drupal\quickedit\Plugin\InPlaceEditor; use Drupal\Component\Plugin\PluginBase; use Drupal\Core\Field\FieldItemListInterface; use Drupal\filter\Entity\FilterFormat; -use Drupal\quickedit\Plugin\InPlaceEditorInterface; use Drupal\filter\Plugin\FilterInterface; +use Drupal\quickedit\Plugin\InPlaceEditorInterface; /** * Defines the formatted text in-place editor. * * @InPlaceEditor( - * id = "editor" + * id = "editor", + * provider = "editor", * ) */ class Editor extends PluginBase implements InPlaceEditorInterface { @@ -87,8 +88,8 @@ public function getAttachments() { // Get the attachments for all text editors that the user might use. $attachments = $manager->getAttachments($formats); - // Also include editor.module's formatted text editor. - $attachments['library'][] = 'editor/quickedit.inPlaceEditor.formattedText'; + // Also include quickedit.module's formatted text editor. + $attachments['library'][] = 'quickedit/quickedit.inPlaceEditor.formattedText'; return $attachments; } diff --git a/core/modules/quickedit/src/QuickEditController.php b/core/modules/quickedit/src/QuickEditController.php index 57982d8949ca2a7a089ec3688db12ebfd4290286..4a8223fb3dfceaf8abe3efcef6afb50e07e8d1b3 100644 --- a/core/modules/quickedit/src/QuickEditController.php +++ b/core/modules/quickedit/src/QuickEditController.php @@ -8,6 +8,7 @@ use Drupal\Core\Render\RendererInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TempStore\PrivateTempStoreFactory; +use Drupal\filter\Plugin\FilterInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -20,6 +21,7 @@ use Drupal\quickedit\Ajax\FieldFormSavedCommand; use Drupal\quickedit\Ajax\FieldFormValidationErrorsCommand; use Drupal\quickedit\Ajax\EntitySavedCommand; +use Drupal\quickedit\Ajax\GetUntransformedTextCommand; /** * Returns responses for Quick Edit module routes. @@ -356,4 +358,34 @@ public function entitySave(EntityInterface $entity) { return $response; } + /** + * Returns Ajax response to render text field without transformation filters. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity of which a formatted text field is being rerendered. + * @param string $field_name + * The name of the (formatted text) field that is being rerendered. + * @param string $langcode + * The name of the language for which the formatted text field is being + * rerendered. + * @param string $view_mode_id + * The view mode the formatted text field should be rerendered in. + * + * @return \Drupal\Core\Ajax\AjaxResponse + * The Ajax response. + */ + public function getUntransformedText(EntityInterface $entity, $field_name, $langcode, $view_mode_id) { + $response = new AjaxResponse(); + + // Direct text editing is only supported for single-valued fields. + $field = $entity->getTranslation($langcode)->$field_name; + $editable_text = check_markup($field->value, $field->format, $langcode, [ + FilterInterface::TYPE_TRANSFORM_REVERSIBLE, + FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE, + ]); + $response->addCommand(new GetUntransformedTextCommand($editable_text)); + + return $response; + } + } diff --git a/core/modules/quickedit/tests/src/Functional/EditorIntegrationLoadingTest.php b/core/modules/quickedit/tests/src/Functional/EditorIntegrationLoadingTest.php index bce74ceec80d74c33ea62462565bb40dafcb794e..ac384563e6f403ebfa32094ea8769401e1ebc179 100644 --- a/core/modules/quickedit/tests/src/Functional/EditorIntegrationLoadingTest.php +++ b/core/modules/quickedit/tests/src/Functional/EditorIntegrationLoadingTest.php @@ -93,7 +93,7 @@ public function testUsersWithoutPermission() { // Retrieving the untransformed text should result in a 403 response and // return a different error message depending of the missing permission. - $response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [ + $response = $client->post($this->buildUrl('quickedit/node/1/body/en/full'), [ 'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']), 'cookies' => $this->getSessionCookies(), 'headers' => [ @@ -127,7 +127,7 @@ public function testUserWithPermission() { // Ensure the text is transformed. $this->assertSession()->responseContains('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>'); $client = $this->getHttpClient(); - $response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [ + $response = $client->post($this->buildUrl('quickedit/node/1/body/en/full'), [ 'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']), 'cookies' => $this->getSessionCookies(), 'headers' => [ diff --git a/core/modules/quickedit/tests/src/FunctionalJavascript/LayoutBuilderIntegrationTest.php b/core/modules/quickedit/tests/src/FunctionalJavascript/LayoutBuilderIntegrationTest.php index ab8f1f0a1b20e8a4ba1c9b8e01ad7afb4f26b858..9064538b941b0b781c5ecfc27b408036e5030ae4 100644 --- a/core/modules/quickedit/tests/src/FunctionalJavascript/LayoutBuilderIntegrationTest.php +++ b/core/modules/quickedit/tests/src/FunctionalJavascript/LayoutBuilderIntegrationTest.php @@ -286,7 +286,6 @@ public function testArticleNode() { * Tests if a custom can be in-place edited with Quick Edit. */ public function testCustomBlock() { - $this->markTestSkipped('This test fails pretty consistently on the latest Chromedriver'); $block_content_type = BlockContentType::create([ 'id' => 'basic', 'label' => 'basic', @@ -321,6 +320,13 @@ public function testCustomBlock() { $this->assertEntityInstanceStates([ 'block_content/1[0]' => 'opened', ]); + + // The label 'body' will only be shown when the pointer hovers over the + // body. This can't be guaranteed by "just" opening the block in QuickEdit. + // We explicitly move the pointer first over the page title and afterwards + // over the block body to be sure. + $this->movePointerTo('.page-title'); + $this->movePointerTo('[data-quickedit-field-id="block_content/1/body/en/full"]'); $this->assertQuickEditEntityToolbar((string) $block_content->label(), 'Body'); $this->assertEntityInstanceFieldStates('block_content', 1, 0, [ 'block_content/1/body/en/full' => 'highlighted', @@ -340,4 +346,16 @@ public function testCustomBlock() { $this->assertSession()->elementExists('css', '#quickedit-entity-toolbar .quickedit-toolgroup.wysiwyg-main > .cke_chrome .cke_top[role="presentation"] .cke_toolbar[role="toolbar"] .cke_toolgroup[role="presentation"] > .cke_button[title~="Bold"][role="button"]'); } + /** + * Moves mouse pointer to location of $selector. + * + * @param string $selector + * CSS selector. + */ + protected function movePointerTo($selector) { + $driver_session = $this->getSession()->getDriver()->getWebDriverSession(); + $element = $driver_session->element('css selector', $selector); + $driver_session->moveto(['element' => $element->getID()]); + } + } diff --git a/core/modules/quickedit/tests/src/Kernel/EditorIntegrationTest.php b/core/modules/quickedit/tests/src/Kernel/EditorIntegrationTest.php index 4dd1bd037655e6d8796cbbb5f84a04af5b31356a..29e5aa1d2e9eb21f8626d0355e2c3076c7ebf2aa 100644 --- a/core/modules/quickedit/tests/src/Kernel/EditorIntegrationTest.php +++ b/core/modules/quickedit/tests/src/Kernel/EditorIntegrationTest.php @@ -8,8 +8,8 @@ use Drupal\editor\Entity\Editor; use Drupal\entity_test\Entity\EntityTest; use Drupal\quickedit\MetadataGenerator; +use Drupal\quickedit\QuickEditController; use Drupal\quickedit_test\MockQuickEditEntityFieldAccessCheck; -use Drupal\editor\EditorController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -56,7 +56,7 @@ class EditorIntegrationTest extends QuickEditTestBase { protected $accessChecker; /** - * The name of the field ued for tests. + * The name of the field used for tests. * * @var string */ @@ -147,7 +147,8 @@ public function testEditorSelection() { $entity->{$this->fieldName}->format = 'filtered_html'; $entity->save(); - // Editor selection w/ cardinality 1, text format w/o associated text editor. + // Editor selection with cardinality 1, text format without associated text + // editor. $this->assertEquals('form', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality 1, and the filtered_html text format, the 'form' editor is selected."); // Editor selection w/ cardinality 1, text format w/ associated text editor. @@ -155,7 +156,7 @@ public function testEditorSelection() { $entity->save(); $this->assertEquals('editor', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality 1, and the full_html text format, the 'editor' editor is selected."); - // Editor selection with text processing, cardinality >1 + // Editor selection with text processing, cardinality > 1. $this->fields->field_textarea_field_storage->setCardinality(2); $this->fields->field_textarea_field_storage->save(); $this->assertEquals('form', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality >1, and both items using the full_html text format, the 'form' editor is selected."); @@ -205,13 +206,18 @@ public function testAttachments() { $editors = ['editor']; $attachments = $this->editorSelector->getEditorAttachments($editors); - $this->assertSame(['library' => ['editor/quickedit.inPlaceEditor.formattedText']], $attachments, "Expected attachments for Editor module's in-place editor found."); + $this->assertSame(['library' => ['quickedit/quickedit.inPlaceEditor.formattedText']], $attachments, "Expected attachments for Editor module's in-place editor found."); } /** * Tests GetUntransformedTextCommand AJAX command. */ public function testGetUntransformedTextCommand() { + $this->accessChecker = new MockQuickEditEntityFieldAccessCheck(); + $this->editorSelector = $this->container->get('quickedit.editor.selector'); + $this->editorManager = $this->container->get('plugin.manager.quickedit.editor'); + $this->metadataGenerator = new MetadataGenerator($this->accessChecker, $this->editorSelector, $this->editorManager); + // Create an entity with values for the field. $entity = EntityTest::create(); $entity->{$this->fieldName}->value = 'Test'; @@ -220,7 +226,14 @@ public function testGetUntransformedTextCommand() { $entity = EntityTest::load($entity->id()); // Verify AJAX response. - $controller = new EditorController(); + $controller = new QuickEditController( + $this->container->get('tempstore.private'), + $this->metadataGenerator, + $this->editorSelector, + $this->container->get('renderer'), + $this->container->get('entity_display.repository'), + $this->container->get('entity.repository'), + ); $request = new Request(); $response = $controller->getUntransformedText($entity, $this->fieldName, LanguageInterface::LANGCODE_DEFAULT, 'default'); $expected = [