diff --git a/core/modules/editor/src/Form/EditorImageDialog.php b/core/modules/editor/src/Form/EditorImageDialog.php
index 6f18546..816ba41 100644
--- a/core/modules/editor/src/Form/EditorImageDialog.php
+++ b/core/modules/editor/src/Form/EditorImageDialog.php
@@ -209,7 +209,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
// Transform absolute image URLs to relative image URLs: prevent problems
// on multisite set-ups and prevent mixed content errors.
$file_url = file_url_transform_relative($file_url);
- $form_state->setValue(array('attributes', 'src'), $file_url);
+ if (!$form_state->getValue(array('attributes', 'src'))) {
+ $form_state->setValue(array('attributes', 'src'), $file_url);
+ }
$form_state->setValue(array('attributes', 'data-entity-uuid'), $file->uuid());
$form_state->setValue(array('attributes', 'data-entity-type'), 'file');
}
diff --git a/core/modules/image/image.install b/core/modules/image/image.install
index fd14d03..eb5f197 100644
--- a/core/modules/image/image.install
+++ b/core/modules/image/image.install
@@ -61,3 +61,25 @@ function image_requirements($phase) {
return $requirements;
}
+
+/**
+ * Add allowed attributes to existing html filters.
+ */
+function image_update_8001() {
+ $config_factory = \Drupal::configFactory();
+ foreach ($config_factory->listAll('filter.format') as $name) {
+ $config = $config_factory->getEditable($name);
+ if (!$config->get('filters.filter_image_style.status')) {
+ continue;
+ }
+ $allowed_html = $config->get('filters.filter_html.settings.allowed_html');
+ $matches = array();
+ if (!empty($allowed_html) && preg_match('/]*)>/', $allowed_html, $matches)) {
+ $new_attributes = array_filter(explode(' ', $matches[1]));
+ $new_attributes[] = 'data-image-style';
+ $allowed_html = preg_replace('/]*)>/', '', $allowed_html);
+ $config->set('filters.filter_html.settings.allowed_html', $allowed_html);
+ $config->save();
+ }
+ }
+}
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 7f8314a..5d023e3 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -6,6 +6,7 @@
*/
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\file\Entity\File;
use Drupal\field\FieldStorageConfigInterface;
@@ -483,3 +484,114 @@ function image_field_config_delete(FieldConfigInterface $field) {
\Drupal::service('file.usage')->delete($file, 'image', 'default_image', $field->uuid());
}
}
+
+/**
+ * Implements hook_form_FORM_ID_alter() for EditorImageDialog.
+ *
+ * Alters the image dialog form for text editor, to allow the user to select an
+ * image style.
+ *
+ * @see \Drupal\editor\Form\EditorImageDialog::buildForm()
+ */
+function image_form_editor_image_dialog_alter(&$form, FormStateInterface $form_state) {
+ /** @var \Drupal\editor\Entity\Editor $editor */
+ $editor = $form_state->getBuildInfo()['args'][0];
+ $filters = $editor->getFilterFormat()->filters()->getAll();
+
+ // Alter the editor image dialog when image style functionality is available,
+ // unless the responsive image style filter is also in use.
+ if ((isset($filters['filter_image_style']) && $filters['filter_image_style']->status)) {
+ $filter_image_style_status = TRUE;
+ }
+ else {
+ $filter_image_style_status = FALSE;
+ }
+
+ if ((isset($filters['filter_responsive_image_style']) && $filters['filter_responsive_image_style']->status)) {
+ $filter_responsive_image_style_status = TRUE;
+ }
+ else {
+ $filter_responsive_image_style_status = FALSE;
+ }
+
+ if ($filter_image_style_status && !$filter_responsive_image_style_status) {
+ // Get the image from the form.
+ $image_element = $form_state->getStorage()['image_element'];
+
+ // Disallow the user from specifying image dimensions manualy.
+ $form['dimensions']['#access'] = FALSE;
+
+ // Get an array of image styles to present as options for selection.
+ $image_style_options = image_style_options(FALSE);
+
+ // Create a form item for image style selection.
+ $form['image_style'] = array(
+ '#type' => 'item',
+ '#field_prefix' => '
',
+ '#field_suffix' => '
',
+ );
+
+ // Add a select element to choose an image style.
+ $form['image_style']['selection'] = array(
+ '#title' => t('Image style'),
+ '#type' => 'select',
+ '#default_value' => isset($image_element['data-image-style']) ? $image_element['data-image-style'] : array_keys($image_style_options)[0],
+ '#options' => $image_style_options,
+ '#required' => TRUE,
+ '#wrapper_attributes' => array('class' => array('container-inline')),
+ '#attributes' => array('class' => array('container-inline')),
+ '#parents' =>array('attributes', 'data-image-style'),
+ );
+
+ // Validate that the image shown in the text editor matches the image style.
+ $form['actions']['save_modal']['#validate'][] = 'image_form_editor_image_dialog_validate';
+ }
+}
+
+/**
+ * Form validation handler for EditorImageDialog.
+ *
+ * Ensures the image shown in the text editor matches the chosen image style.
+ *
+ * @see \Drupal\editor\Form\EditorImageDialog::buildForm()
+ * @see \Drupal\editor\Form\EditorImageDialog::validateForm()
+ * @see image_form_editor_image_dialog_alter()
+ */
+function image_form_editor_image_dialog_validate(array &$form, FormStateInterface &$form_state) {
+ if (!empty($form_state->getValue('fid')[0])) {
+
+ // Get the image style attributes.
+ $attributes = &$form_state->getValue('attributes');
+
+ /** @var \Drupal\image\ImageStyleInterface $image_style */
+ $image_style = \Drupal::service('entity_type.manager')->getStorage('image_style')->load($attributes['data-image-style']);
+
+ /** @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();
+
+ // 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($uri);
+
+ /** @var \Drupal\Core\Image\ImageInterface $image */
+ $image = \Drupal::service('image.factory')->get($uri);
+
+ if ($image->isValid()) {
+ // Get the original width and height of the image.
+ $dimensions = array(
+ 'width' => $image->getWidth(),
+ 'height' => $image->getHeight()
+ );
+
+ // Transform the 'width' and 'height' dimensions of the image based on the
+ // image style.
+ $image_style->transformDimensions($dimensions, $attributes['src']);
+ $attributes['width'] = $dimensions['width'];
+ $attributes['height'] = $dimensions['height'];
+ }
+ }
+}
diff --git a/core/modules/image/js/plugins/drupalimagestyle/plugin.js b/core/modules/image/js/plugins/drupalimagestyle/plugin.js
new file mode 100644
index 0000000..8de686f
--- /dev/null
+++ b/core/modules/image/js/plugins/drupalimagestyle/plugin.js
@@ -0,0 +1,112 @@
+/**
+ * @file
+ * Drupal Image Style plugin.
+ *
+ * This alters the existing CKEditor image2 widget plugin, which is already
+ * altered by the Drupal Image plugin, to allow for the data-image-style
+ * attribute to be set.
+ *
+ * @ignore
+ */
+
+(function (CKEDITOR) {
+
+ 'use strict';
+
+ CKEDITOR.plugins.add('drupalimagestyle', {
+ requires: 'drupalimage',
+
+ beforeInit: function (editor) {
+ // Override the image2 widget definition to handle the additional
+ // data-image-style attributes.
+ editor.on('widgetDefinition', function (event) {
+ var widgetDefinition = event.data;
+ if (widgetDefinition.name !== 'image') {
+ return;
+ }
+ // Override default features definitions for drupalimagestyle.
+ CKEDITOR.tools.extend(widgetDefinition.features, {
+ responsiveimage: {
+ requiredContent: 'img[data-image-style]'
+ }
+ }, true);
+
+ // Override requiredContent & allowedContent.
+ var requiredContent = widgetDefinition.requiredContent.getDefinition();
+ requiredContent.attributes['data-image-style'] = '';
+ widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
+ widgetDefinition.allowedContent.img.attributes += ',!data-image-style';
+
+ // Override downcast().
+ var originalDowncast = widgetDefinition.downcast;
+ widgetDefinition.downcast = function (element) {
+ var img = originalDowncast.call(this, element);
+ if (!img) {
+ img = findElementByName(element, 'img');
+ }
+ img.attributes['data-image-style'] = this.data['data-image-style'];
+ return img;
+ };
+
+ // Override upcast().
+ var originalUpcast = widgetDefinition.upcast;
+ widgetDefinition.upcast = function (element, data) {
+ if (element.name !== 'img' || !element.attributes['data-entity-type'] || !element.attributes['data-entity-uuid']) {
+ return;
+ }
+ // Don't initialize on pasted fake objects.
+ else if (element.attributes['data-cke-realelement']) {
+ return;
+ }
+
+ // Parse the data-image-style attribute.
+ data['data-image-style'] = element.attributes['data-image-style'];
+
+ // Upcast after parsing so correct element attributes are parsed.
+ element = originalUpcast.call(this, element, data);
+
+ return element;
+ };
+
+ // Protected; keys of the widget data to be sent to the Drupal dialog.
+ // Append to the values defined by the drupalimage plugin.
+ // @see core/modules/ckeditor/js/plugins/drupalimage/plugin.js
+ CKEDITOR.tools.extend(widgetDefinition._mapDataToDialog, {
+ 'data-image-style': 'data-image-style'
+ });
+ // Low priority to ensure drupalimage's event handler runs first.
+ }, null, null, 20);
+ }
+ });
+
+ /**
+ * Finds an element by its name.
+ *
+ * Function will check first the passed element itself and then all its
+ * children in DFS order.
+ *
+ * @param {CKEDITOR.htmlParser.element} element
+ * The element to search.
+ * @param {string} name
+ * The element name to search for.
+ *
+ * @return {?CKEDITOR.htmlParser.element}
+ * The found element, or null.
+ */
+ function findElementByName(element, name) {
+ if (element.name === name) {
+ return element;
+ }
+
+ var found = null;
+ element.forEach(function (el) {
+ if (el.name === name) {
+ found = el;
+ // Stop here.
+ return false;
+ }
+ }, CKEDITOR.NODE_ELEMENT);
+ return found;
+ }
+
+})(CKEDITOR);
diff --git a/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php b/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php
new file mode 100644
index 0000000..30ea87c
--- /dev/null
+++ b/core/modules/image/src/Plugin/CKEditorPlugin/DrupalImageStyle.php
@@ -0,0 +1,86 @@
+hasAssociatedFilterFormat()) {
+ return FALSE;
+ }
+
+ // Automatically enable this plugin if the text format associated with this
+ // text editor uses the filter_image_style filter and the DrupalImage button
+ // is enabled.
+ $format = $editor->getFilterFormat();
+ if ($format->filters('filter_image_style')->status) {
+ $enabled = FALSE;
+ $settings = $editor->getSettings();
+ foreach ($settings['toolbar']['rows'] as $row) {
+ foreach ($row as $group) {
+ foreach ($group['items'] as $button) {
+ if ($button === 'DrupalImage') {
+ $enabled = TRUE;
+ }
+ }
+ }
+ }
+ return $enabled;
+ }
+
+ return FALSE;
+ }
+
+}
diff --git a/core/modules/image/src/Plugin/Filter/FilterImageStyle.php b/core/modules/image/src/Plugin/Filter/FilterImageStyle.php
new file mode 100644
index 0000000..8c6a008
--- /dev/null
+++ b/core/modules/image/src/Plugin/Filter/FilterImageStyle.php
@@ -0,0 +1,253 @@
+entityTypeManager = $entity_type_manager;
+ $this->entityRepository = $entity_repository;
+ $this->imageFactory = $image_factory;
+ $this->renderer = $renderer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager'), $container->get('entity.repository'), $container->get('image.factory'), $container->get('renderer'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function process($text, $langcode) {
+ if (stristr($text, 'data-image-style') !== FALSE) {
+ // Load all the image styles so each img found in the text can be checked
+ // to ensure it has 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.
+ /** @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.
+ $file_uuid = $dom_element->getAttribute('data-entity-uuid');
+ $image_style_id = $dom_element->getAttribute('data-image-style');
+
+ // If the image style is not a valid one, then don't transform the HTML.
+ if (empty($file_uuid) || !in_array($image_style_id, $image_styles)) {
+ continue;
+ }
+
+ // 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);
+ }
+
+ // Process the filter with the newly updated DOM.
+ return new FilterProcessResult(HTML::serialize($dom));
+ }
+
+ // Process the filter if no image style img elements are found.
+ return new FilterProcessResult($text);
+ }
+
+ /**
+ * Loads the image styles.
+ *
+ * @return string[]
+ */
+ protected function loadImageStyles() {
+ return array_keys($this->entityTypeManager->getStorage('image_style')->loadMultiple());
+ }
+
+ /**
+ * Get the the width and height of an image based on the file UUID.
+ *
+ * @param string $file_uuid
+ * The UUID for the file.
+ *
+ * @return array
+ * The image information.
+ */
+ protected function getImageInfo($file_uuid) {
+ /** @var \Drupal\file\FileInterface $file; */
+ $file = $this->entityRepository->loadEntityByUuid('file', $file_uuid);
+
+ // Determine uri, width and height of the source image.
+ $image_uri = $image_width = $image_height = NULL;
+ $image = $this->imageFactory->get($file->getFileUri());
+ if ($image->isValid()) {
+ $image_uri = $file->getFileUri();
+ $image_width = $image->getWidth();
+ $image_height = $image->getHeight();
+ }
+
+ return [
+ 'uri' => $image_uri,
+ 'width' => $image_width,
+ 'height' => $image_height
+ ];
+ }
+
+ /**
+ * Removes attributes that will be generated from image style theme function.
+ *
+ * @param \DOMElement $dom_element
+ * The DOM element for the img element.
+ *
+ * @return array
+ * The attributes array.
+ */
+ protected function prepareImageAttributes(\DOMElement $dom_element) {
+ // Remove attributes that are no longer needed.
+ $dom_element->removeAttribute('data-entity-type');
+ $dom_element->removeAttribute('data-entity-uuid');
+ $dom_element->removeAttribute('data-image-style');
+
+ // Remove attributes that are generated by the image style.
+ $dom_element->removeAttribute('width');
+ $dom_element->removeAttribute('height');
+ $dom_element->removeAttribute('src');
+
+ // Make sure all non-regenerated attributes are retained.
+ $attributes = [];
+ for ($i = 0; $i < $dom_element->attributes->length; $i++) {
+ $attr = $dom_element->attributes->item($i);
+ $attributes[$attr->name] = $attr->value;
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * Get the HTML for the img element after image style is applied.
+ *
+ * @param string $file_uuid
+ * The UUID for the file.
+ * @param string $image_style_id
+ * The ID for the image style.
+ * @param \DOMElement $dom_element
+ * The DOM element for the image element.
+ *
+ * @return string
+ * The img element with the image style applied.
+ */
+ protected function getImageStyleHtml($file_uuid, $image_style_id, \DOMElement $dom_element) {
+ $image_info = $this->getImageInfo($file_uuid);
+
+ // Remove attributes that will be generated by the image style.
+ $attributes = $this->prepareImageAttributes($dom_element);
+
+ // Re-render as an image style.
+ $image = [
+ '#theme' => 'image_style',
+ '#style_name' => $image_style_id,
+ '#uri' => $image_info['uri'],
+ '#width' => $image_info['width'],
+ '#height' => $image_info['height'],
+ '#attributes' => $attributes,
+ ];
+
+ return $this->renderer->render($image);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tips($long = FALSE) {
+ if ($long) {
+ $image_styles = $this->loadImageStyles();
+ $list = '' . implode('
, ', $image_styles) . '
';
+ return t('
+ You can display images using site-wide styles by adding a data-image-style
attribute, whose values is one of the image style machine names: !image-style-machine-name-list.
', ['!image-style-machine-name-list' => $list]);
+ }
+ 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/Kernel/EditorImageStyleDialogTest.php b/core/modules/image/tests/src/Kernel/EditorImageStyleDialogTest.php
new file mode 100644
index 0000000..bd42c57
--- /dev/null
+++ b/core/modules/image/tests/src/Kernel/EditorImageStyleDialogTest.php
@@ -0,0 +1,144 @@
+installEntitySchema('file');
+ $this->installSchema('system', ['key_value_expire']);
+ $this->installSchema('node', ['node_access']);
+ $this->installSchema('file', ['file_usage']);
+ $this->installConfig(['node']);
+
+ // Add text formats.
+ $format = FilterFormat::create([
+ 'format' => 'filtered_html',
+ 'name' => 'Filtered HTML',
+ 'weight' => 0,
+ 'filters' => [
+ 'filter_image_style' => ['status' => TRUE]
+ ],
+ ]);
+ $format->save();
+
+ // Set up text editor.
+ $this->editor = Editor::create([
+ 'format' => 'filtered_html',
+ 'editor' => 'ckeditor',
+ 'image_upload' => [
+ 'max_size' => 100,
+ 'scheme' => 'public',
+ 'directory' => '',
+ '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();
+ }
+
+ /**
+ * 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://');
+
+ $input = [
+ 'editor_object' => [
+ 'src' => file_url_transform_relative($file->getFileUri()),
+ 'alt' => 'Balloons floating above a field.',
+ 'data-entity-type' => 'file',
+ 'data-entity-uuid' => $file->uuid(),
+ 'data-image-style' => 'medium',
+ ],
+ 'dialogOptions' => [
+ 'title' => 'Edit Image',
+ 'dialogClass' => 'editor-image-dialog',
+ 'autoResize' => 'true',
+ ],
+ '_drupal_ajax' => '1',
+ 'ajax_page_state' => [
+ 'theme' => 'bartik',
+ 'theme_token' => 'some-token',
+ 'libraries' => '',
+ ],
+ ];
+ $form_state = (new FormState())
+ ->setRequestMethod('POST')
+ ->setUserInput($input)
+ ->addBuildInfo('args', [$this->editor]);
+
+ /** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
+ $form_builder = $this->container->get('form_builder');
+ $form_object = new EditorImageDialog(\Drupal::entityTypeManager()->getStorage('file'));
+ $form_id = $form_builder->getFormId($form_object, $form_state);
+ $form = [];
+
+ /** @var \Drupal\Core\Render\RendererInterface $renderer */
+ $renderer = \Drupal::service('renderer');
+ $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);
+ });
+
+ // If image style is selected, image dimensions should not be available.
+ $this->assertFalse($form['dimensions']['#access']);
+ // Image style should be medium.
+ $this->assertEquals('medium', $form['image_style']['selection']['#default_value']);
+ }
+
+}
diff --git a/core/modules/image/tests/src/Unit/FilterImageStyleTest.php b/core/modules/image/tests/src/Unit/FilterImageStyleTest.php
new file mode 100644
index 0000000..ad1b047
--- /dev/null
+++ b/core/modules/image/tests/src/Unit/FilterImageStyleTest.php
@@ -0,0 +1,106 @@
+ '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_text = '' . $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);
+ }
+}
diff --git a/core/modules/system/css/components/container-inline.module.css b/core/modules/system/css/components/container-inline.module.css
index ba99a36..103e3e3 100644
--- a/core/modules/system/css/components/container-inline.module.css
+++ b/core/modules/system/css/components/container-inline.module.css
@@ -11,3 +11,7 @@
.container-inline .details-wrapper {
display: block;
}
+/* Allow items inside inline items to render themselves as blocks. */
+.container-inline .container-block {
+ display: block;
+}
diff --git a/core/profiles/standard/config/install/filter.format.basic_html.yml b/core/profiles/standard/config/install/filter.format.basic_html.yml
index 92224c2..1c38622 100644
--- a/core/profiles/standard/config/install/filter.format.basic_html.yml
+++ b/core/profiles/standard/config/install/filter.format.basic_html.yml
@@ -3,6 +3,7 @@ status: true
dependencies:
module:
- editor
+ - image
name: 'Basic HTML'
format: basic_html
weight: 0
@@ -15,7 +16,7 @@ filters:
status: true
weight: -10
settings:
- allowed_html: '