diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 07f9fc3..12fa4fd 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -486,10 +486,10 @@ function image_field_config_delete(FieldConfigInterface $field) {
}
/**
- * Implements hook_form_FORM_ID_alter() for EditorImageDialog.
+ * Implements hook_form_FORM_ID_alter().
*
* Alters the image dialog form for text editor, to allow the user to select an
- * image style.
+ * image style.
*
* @see \Drupal\editor\Form\EditorImageDialog::buildForm()
*/
@@ -512,6 +512,7 @@ function image_form_editor_image_dialog_alter(&$form, FormStateInterface $form_s
'#default_value' => isset($image_element['data-image-style']) ? $image_element['data-image-style'] : NULL,
'#options' => $image_style_options,
'#required' => TRUE,
+ '#parents' => array('attributes', 'data-image-style'),
);
$form['actions']['save_modal']['#validate'][] = 'image_form_editor_image_dialog_validate';
@@ -538,17 +539,13 @@ function image_form_editor_image_dialog_validate(array &$form, FormStateInterfac
/** @var \Drupal\file\FileInterface $file */
$file = File::load($form_state->getValue('fid')[0]);
- // Get the URI of the image from the file.
$uri = $file->getFileUri();
- // Transform absolute image URLs to relative image URLs: prevent problems
- // on multisite set-ups and prevent mixed content errors.
- $image_url = file_create_url($uri);
- $image_url = file_url_transform_relative($image_url);
// Set the 'src' attribute to the image style URL. FilterImageStyle will
// look at the 'data-editor-file-uuid' attribute, not the 'src' attribute to
// render the appropriate output.
- $attributes['src'] = $image_style->buildUrl($image_url);
+ $image_url = $image_style->buildUrl($uri);
+ $attributes['src'] = file_url_transform_relative($image_url);
/** @var \Drupal\Core\Image\ImageInterface $image */
$image = \Drupal::service('image.factory')->get($uri);
@@ -557,7 +554,7 @@ function image_form_editor_image_dialog_validate(array &$form, FormStateInterfac
// Get the original width and height of the image.
$dimensions = array(
'width' => $image->getWidth(),
- 'height' => $image->getHeight()
+ 'height' => $image->getHeight(),
);
// Transform the 'width' and 'height' dimensions of the image based on the
diff --git a/core/modules/image/js/plugins/drupalimagestyle/plugin.js b/core/modules/image/js/plugins/drupalimagestyle/plugin.js
index bc7b7fa..ef8eae1 100644
--- a/core/modules/image/js/plugins/drupalimagestyle/plugin.js
+++ b/core/modules/image/js/plugins/drupalimagestyle/plugin.js
@@ -35,7 +35,7 @@
var requiredContent = widgetDefinition.requiredContent.getDefinition();
requiredContent.attributes['data-image-style'] = '';
widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
- widgetDefinition.allowedContent.img.attributes += ',!data-image-style';
+ widgetDefinition.allowedContent.img.attributes['!data-image-style'] = true;
// Decorate downcast().
var originalDowncast = widgetDefinition.downcast;
diff --git a/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php b/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php
index 6689e30..d076508 100644
--- a/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php
+++ b/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php
@@ -63,12 +63,12 @@ public function isEnabled(Editor $editor) {
}
// Automatically enable this plugin if the text format associated with this
- // text editor uses the filter_image_style filter and the drupalimagestyle
+ // text editor uses the filter_image_style filter and the DrupalImage
// button is enabled.
$format = $editor->getFilterFormat();
if ($format->filters('filter_image_style')->status) {
$toolbarButtons = CKEditorPluginManager::getEnabledButtons($editor);
- return in_array('drupalimagestyle', $toolbarButtons);
+ return in_array('DrupalImage', $toolbarButtons);
}
return FALSE;
diff --git a/core/modules/image/src/Plugin/Filter/FilterImageStyle.php b/core/modules/image/src/Plugin/Filter/FilterImageStyle.php
index bb29180..328daed 100644
--- a/core/modules/image/src/Plugin/Filter/FilterImageStyle.php
+++ b/core/modules/image/src/Plugin/Filter/FilterImageStyle.php
@@ -93,16 +93,14 @@ public static function create(ContainerInterface $container, array $configuratio
*/
public function process($text, $langcode) {
if (stristr($text, 'data-image-style') !== FALSE) {
- // Load all the image styles so each imgage found in the text can be
- // checked to ensure it has a valid image style.
+ // Load all image styles so each image found in the text can be
+ // checked against a valid image style.
$image_styles = $this->loadImageStyles();
- // Load the text that is being processed into XML to find images.
$dom = HTML::load($text);
$xpath = new \DOMXPath($dom);
- // Process each img element DOM element found with the necessary
- // attributes.
+ // Process each img element found with the necessary attributes.
/** @var \DOMElement $dom_element */
foreach ($xpath->query('//*[@data-entity-type="file" and @data-entity-uuid and @data-image-style]') as $dom_element) {
// Get the UUID and image style for the file.
@@ -116,23 +114,12 @@ public function process($text, $langcode) {
}
// Transform the HTML for the img element by applying an image style.
- $altered_img = $this->getImageStyleHtml($file_uuid, $image_style_id, $dom_element);
-
- // Load the altered HTML into a new DOMDocument and retrieve the element.
- $updated_node = HTML::load($altered_img)->getElementsByTagName('body')
- ->item(0)
- ->childNodes
- ->item(0);
-
- // Import the updated node from the new DOMDocument into the original
- // one, importing also the child nodes of the updated node.
- $updated_node = $dom->importNode($updated_node, TRUE);
-
- // Finally, replace the original image node with the new image node.
- $dom_element->parentNode->replaceChild($updated_node, $dom_element);
+ $altered_img_markup = $this->getImageStyleHtml($file_uuid, $image_style_id, $dom_element);
+ $altered_img = $dom->createDocumentFragment();
+ $altered_img->appendXML($altered_img_markup);
+ $dom_element->parentNode->replaceChild($altered_img, $dom_element);
}
- // Process the filter with the newly updated DOM.
return new FilterProcessResult(HTML::serialize($dom));
}
@@ -141,9 +128,10 @@ public function process($text, $langcode) {
}
/**
- * Loads the image styles.
+ * Loads the available image style names.
*
* @return string[]
+ * Array keys of the available image styles.
*/
protected function loadImageStyles() {
return array_keys($this->entityTypeManager->getStorage('image_style')->loadMultiple());
@@ -159,7 +147,7 @@ protected function loadImageStyles() {
* The image information.
*/
protected function getImageInfo($file_uuid) {
- /** @var \Drupal\file\FileInterface $file; */
+ /** @var \Drupal\file\FileInterface $file */
$file = $this->entityRepository->loadEntityByUuid('file', $file_uuid);
// Determine uri, width and height of the source image.
@@ -174,7 +162,7 @@ protected function getImageInfo($file_uuid) {
return [
'uri' => $image_uri,
'width' => $image_width,
- 'height' => $image_height
+ 'height' => $image_height,
];
}
@@ -185,7 +173,7 @@ protected function getImageInfo($file_uuid) {
* The DOM element for the img element.
*
* @return array
- * The attributes array.
+ * The attributes array.
*/
protected function prepareImageAttributes(\DOMElement $dom_element) {
// Remove attributes that are no longer needed.
@@ -217,7 +205,7 @@ protected function prepareImageAttributes(\DOMElement $dom_element) {
* @param string $image_style_id
* The ID for the image style.
* @param \DOMElement $dom_element
- * The DOM element for the image element.
+ * The DOM element for the image element.
*
* @return string
* The img element with the image style applied.
@@ -238,9 +226,11 @@ protected function getImageStyleHtml($file_uuid, $image_style_id, \DOMElement $d
'#attributes' => $attributes,
];
- $this->renderer->executeInRenderContext(new RenderContext(), function () use (&$image) {
+ $output = $this->renderer->executeInRenderContext(new RenderContext(), function () use (&$image) {
return $this->renderer->render($image);
});
+
+ return $output;
}
/**
@@ -256,5 +246,7 @@ public function tips($long = FALSE) {
else {
return t('You can display images using site-wide styles by adding a data-image-style attribute.');
}
+
}
+
}
diff --git a/core/modules/image/tests/src/Functional/FilterImageStyleTest.php b/core/modules/image/tests/src/Functional/FilterImageStyleTest.php
new file mode 100644
index 0000000..63528cb
--- /dev/null
+++ b/core/modules/image/tests/src/Functional/FilterImageStyleTest.php
@@ -0,0 +1,121 @@
+format = FilterFormat::create([
+ 'format' => $this->randomMachineName(),
+ 'name' => $this->randomString(),
+ 'filters' => [
+ 'filter_html' => [
+ 'status' => 1,
+ 'settings' => [
+ 'allowed_html' => '',
+ ],
+ ],
+ 'filter_image_style' => ['status' => 1],
+ 'editor_file_reference' => ['status' => 1],
+ ],
+ ]);
+ $this->format->save();
+
+ $user = $this->drupalCreateUser(['access content', 'administer nodes']);
+ $this->drupalLogin($user);
+
+ $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
+ }
+
+ /**
+ * Helper function to create a test node with configurable image html.
+ */
+ protected function nodeHelper($image_html) {
+ $node = $this->drupalCreateNode([
+ 'type' => 'page',
+ 'title' => $this->randomString(),
+ 'body' => [
+ [
+ 'format' => $this->format->id(),
+ 'value' => $image_html,
+ ],
+ ],
+ ]);
+ $node->save();
+
+ $this->drupalGet($node->toUrl());
+ $this->assertSession()->statusCodeEquals(200);
+ }
+
+ /**
+ * Tests that images not uploaded through media module are unmolested.
+ */
+ public function testImageNoStyle() {
+ $file_url = Url::fromUri('base:core/themes/bartik/screenshot.png')->toString();
+
+ $image_html = '
';
+ $this->nodeHelper($image_html);
+
+ /** @var \Behat\Mink\Element\NodeElement $img_element */
+ $image_element = $this->getSession()->getPage()->find('css', "img");
+ $this->assertFalse(empty($image_element));
+
+ $this->assertFalse($image_element->hasAttribute('class'));
+ $this->assertEquals($file_url, $image_element->getAttribute('src'));
+ $this->assertEquals('220', $image_element->getAttribute('width'));
+ $this->assertFalse($image_element->hasAttribute('height'));
+ }
+
+ /**
+ * Tests image style modification of images.
+ */
+ public function testImageStyle() {
+ $this->assertTrue(array_key_exists('medium', $this->container->get('entity_type.manager')->getStorage('image_style')->loadMultiple()));
+
+ $file = File::create(['uri' => 'core/themes/bartik/screenshot.png']);
+ $file->save();
+
+ $image_html = '
';
+ $this->nodeHelper($image_html);
+
+ /** @var \Behat\Mink\Element\NodeElement $img_element */
+ $image_element = $this->getSession()->getPage()->find('css', 'img.image-style-medium');
+ $this->assertFalse(empty($image_element));
+
+ $this->assertFalse($image_element->hasAttribute('data-entity-type'));
+ $this->assertFalse($image_element->hasAttribute('data-entity-uuid'));
+ $this->assertFalse($image_element->hasAttribute('data-image-style'));
+
+ $this->assertTrue(strpos($image_element->getAttribute('src'), 'medium'));
+ $this->assertEquals('220', $image_element->getAttribute('width'));
+ $this->assertEquals('164', $image_element->getAttribute('height'));
+ }
+
+}
diff --git a/core/modules/image/tests/src/FunctionalJavascript/AddImageTest.php b/core/modules/image/tests/src/FunctionalJavascript/AddImageTest.php
new file mode 100644
index 0000000..2b33a5b
--- /dev/null
+++ b/core/modules/image/tests/src/FunctionalJavascript/AddImageTest.php
@@ -0,0 +1,106 @@
+ 'filtered_html',
+ 'name' => $this->randomString(),
+ 'filters' => [
+ 'filter_image_style' => ['status' => 1],
+ ],
+ ]);
+ $format->save();
+
+ $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
+
+ Editor::create([
+ 'format' => 'filtered_html',
+ 'editor' => 'ckeditor',
+ ])->save();
+
+ $user = $this->drupalCreateUser([
+ 'access content',
+ 'administer nodes',
+ 'create page content',
+ 'use text format filtered_html',
+ ]);
+ $this->drupalLogin($user);
+ }
+
+ /**
+ * Tests if an image can be placed inline with the data-image-style attribute.
+ */
+ public function testDataImageStyleElement() {
+ $image_url = Url::fromUri('base:core/themes/bartik/screenshot.png')->toString();
+
+ $this->drupalGet('node/add/page');
+ $this->assertSession()->statusCodeEquals(200);
+
+ $page = $this->getSession()->getPage();
+ // Wait for the ckeditor toolbar elements to appear (loading is done).
+ $image_button_selector = 'a.cke_button__drupalimage';
+ $this->assertJsCondition("jQuery('$image_button_selector').length > 0", 20000);
+ $this->assertSession()->assertWaitOnAjaxRequest();
+
+ $image_button = $page->find('css', $image_button_selector);
+ $this->assertNotEmpty($image_button);
+ $image_button->click();
+ // Wait for the modal form elements to appear (loading is done).
+ $this->assertJsCondition("jQuery('input[data-drupal-selector=\"edit-attributes-alt\"]').length > 0", 20000);
+
+ $url_input = $page->findField('attributes[src]');
+ $this->assertNotEmpty($url_input);
+ $url_input->setValue($image_url);
+
+ $alt_input = $page->findField('attributes[alt]');
+ $this->assertNotEmpty($alt_input);
+ $alt_input->setValue('asd');
+
+ $image_style_input_name = 'attributes[data-image-style]';
+ $this->assertNotEmpty($page->findField($image_style_input_name));
+ $page->selectFieldOption($image_style_input_name, 'thumbnail');
+
+ // To prevent 403s on save, we re-set our request (cookie) state.
+ $this->prepareRequest();
+
+ // Using NodeElement::click() on the button or NodeElement::submit() on the
+ // form generated phantomjs-internal exceptions. This was the only recourse.
+ $script = "jQuery('input[id^=\"edit-actions-save-modal\"]').click()";
+ $this->getSession()->executeScript($script);
+ $this->assertSession()->assertWaitOnAjaxRequest();
+
+ $source_button = $page->find('css', 'a.cke_button__source');
+ $this->assertNotEmpty($source_button);
+ $source_button->click();
+ $this->assertSession()->assertWaitOnAjaxRequest();
+
+ $this->assertContains('data-image-style="thumbnail"', $page->find('css', 'textarea.cke_source')->getValue());
+ }
+
+}
diff --git a/core/modules/image/tests/src/Kernel/EditorImageStyleDialogTest.php b/core/modules/image/tests/src/Kernel/EditorImageStyleDialogTest.php
index a062273..9282c32 100644
--- a/core/modules/image/tests/src/Kernel/EditorImageStyleDialogTest.php
+++ b/core/modules/image/tests/src/Kernel/EditorImageStyleDialogTest.php
@@ -39,7 +39,7 @@ class EditorImageStyleDialogTest extends EntityKernelTestBase {
'image',
'node',
'system',
- 'user'
+ 'user',
];
/**
@@ -54,19 +54,40 @@ protected function setUp() {
$this->installSchema('file', ['file_usage']);
$this->installConfig(['node']);
- // Add text formats.
+ // Install the image module config so we have the medium image style.
+ $this->installConfig('image');
+
+ // Create a node type for testing.
+ $type = NodeType::create(['type' => 'page', 'name' => 'page']);
+ $type->save();
+ node_add_body_field($type);
+ $this->installEntitySchema('user');
+ $this->container->get('router.builder')->rebuild();
+ }
+
+ /**
+ * Fixture to consolidate tasks while making filter status configurable.
+ *
+ * @param bool $enable_image_filter
+ * Whether to activate filter_image_style in the text format.
+ *
+ * @return array|\Symfony\Component\HttpFoundation\Response
+ * The submitted form.
+ */
+ protected function setUpForm($enable_image_filter) {
$format = FilterFormat::create([
'format' => $this->randomMachineName(),
'name' => $this->randomString(),
'weight' => 0,
'filters' => [
- 'filter_image_style' => ['status' => TRUE]
+ 'filter_image_style' => ['status' => $enable_image_filter],
],
]);
$format->save();
// Set up text editor.
- $this->editor = Editor::create([
+ /** @var \Drupal\editor\EditorInterface $editor */
+ $editor = Editor::create([
'format' => $format->id(),
'editor' => 'ckeditor',
'image_upload' => [
@@ -76,23 +97,8 @@ protected function setUp() {
'status' => TRUE,
],
]);
- $this->editor->save();
-
- // Install the image module config so we have the medium image style.
- $this->installConfig('image');
-
- // Create a node type for testing.
- $type = NodeType::create(['type' => 'page', 'name' => 'page']);
- $type->save();
- node_add_body_field($type);
- $this->installEntitySchema('user');
- $this->container->get('router.builder')->rebuild();
- }
+ $editor->save();
- /**
- * Tests that editor image dialog works as expected.
- */
- public function testEditorImageStyleDialog() {
/** @var \Drupal\file\FileInterface $file */
$file = file_save_data(file_get_contents($this->root . '/core/modules/image/sample.png'), 'public://');
@@ -102,7 +108,6 @@ public function testEditorImageStyleDialog() {
'alt' => 'Balloons floating above a field.',
'data-entity-type' => 'file',
'data-entity-uuid' => $file->uuid(),
- 'data-image-style' => 'medium',
],
'dialogOptions' => [
'title' => 'Edit Image',
@@ -116,10 +121,14 @@ public function testEditorImageStyleDialog() {
'libraries' => '',
],
];
+ if ($enable_image_filter) {
+ $input['editor_object']['data-image-style'] = 'medium';
+ }
+
$form_state = (new FormState())
->setRequestMethod('POST')
->setUserInput($input)
- ->addBuildInfo('args', [$this->editor]);
+ ->addBuildInfo('args', [$editor]);
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
@@ -129,14 +138,34 @@ public function testEditorImageStyleDialog() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
- $renderer->executeInRenderContext(new RenderContext(), function() use (&$form, $form_builder, $form_id, $form_state) {
+ $renderer->executeInRenderContext(new RenderContext(), function () use (&$form, $form_builder, $form_id, $form_state) {
$form = $form_builder->retrieveForm($form_id, $form_state);
$form_builder->prepareForm($form_id, $form, $form_state);
$form_builder->processForm($form_id, $form, $form_state);
});
- // Image style should be medium.
+ return $form;
+ }
+
+ /**
+ * Tests that style selection is hidden when filter_image_style is disabled.
+ */
+ public function testDialogNoStyles() {
+ $form = $this->setUpForm(FALSE);
+
+ $this->assertFalse(array_key_exists('image_style', $form));
+ }
+
+ /**
+ * Tests EditorImageDialog when filter_image_style is enabled.
+ */
+ public function testDialogStyles() {
+ $form = $this->setUpForm(TRUE);
+
+ $this->assertEquals(['large', 'medium', 'thumbnail'], array_keys($form['image_style']['selection']['#options']));
+
$this->assertEquals('medium', $form['image_style']['selection']['#default_value']);
+ $this->assertEquals(TRUE, $form['image_style']['selection']['#required']);
}
}
diff --git a/core/modules/image/tests/src/Unit/FilterImageStyleTest.php b/core/modules/image/tests/src/Unit/FilterImageStyleTest.php
deleted file mode 100644
index ad1b047..0000000
--- a/core/modules/image/tests/src/Unit/FilterImageStyleTest.php
+++ /dev/null
@@ -1,106 +0,0 @@
- 'image'];
- $entityTypeManager = $this->prophesize(EntityTypeManager::class);
- $entityRepository = $this->prophesize(EntityRepository::class);
- $imageFactory = $this->prophesize(ImageFactory::class);
- $renderer = $this->prophesize(Renderer::class);
-
- $this->filterImageStyle = $this->getMockBuilder('Drupal\image\Plugin\Filter\FilterImageStyle')
- ->setConstructorArgs([
- $configuration,
- $plugin_id,
- $plugin_definition,
- $entityTypeManager->reveal(),
- $entityRepository->reveal(),
- $imageFactory->reveal(),
- $renderer->reveal()
- ])
- ->setMethods([
- 'loadImageStyles',
- 'getImageStyleHtml'
- ])
- ->getMock();
- }
-
- public function testProcessWithoutImage() {
- $output = $this->filterImageStyle->process('', 'en');
- $this->assertEquals('', $output);
- }
-
- /**
- * @covers ::process
- */
- public function testProcessWithImage() {
- $original_src = 'image.png';
- $original_uuid = 'abcd-1234-ghij-5678';
- $original_image_style = 'medium';
- $original_width = '400';
- $original_height = '300';
- $original_alt = 'A wooly mammoth trumpets as a crevasse breaks open in the glacier.';
-
- $original_img = '
';
- $original_text = '
' . $original_img . '
'; - - $expected_src = 'styles/medium/public/inline-images/image.png'; - $expected_width = '200'; - $expected_height = '150'; - - $expected_img = '' . $expected_img . '
'; - - $this->filterImageStyle - ->method('loadImageStyles') - ->willReturn([ - 'thumbnail', - 'medium', - 'large' - ]); - - $this->filterImageStyle - ->method('getImageStyleHtml') - ->with( - $this->equalTo($original_uuid), - $this->equalTo($original_image_style), - $this->anything() - ) - ->willReturn($expected_img); - - $output = $this->filterImageStyle->process($original_text, 'en'); - $this->assertEquals($expected_text, $output); - } -}