Issue #3412361 by Wim Leers, phenaproxima, catch, effulgentsia: Mark Editor config schema as fully validatable
parent
661f5453d0
commit
83874f2bcf
|
@ -52,6 +52,13 @@ trait SchemaCheckTrait {
|
|||
'This value should not be blank.',
|
||||
],
|
||||
],
|
||||
'editor.editor.*' => [
|
||||
// @todo Fix stream wrappers not being available early enough in
|
||||
// https://www.drupal.org/project/drupal/issues/3416735
|
||||
'image_upload.scheme' => [
|
||||
'^The file storage you selected is not a visible, readable and writable stream wrapper\. Possible choices: <em class="placeholder"><\/em>\.$',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -175,7 +175,9 @@ class CKEditor5ImageController extends ControllerBase {
|
|||
* Gets the image upload validators.
|
||||
*/
|
||||
protected function getImageUploadValidators(array $settings): array {
|
||||
$max_filesize = min(Bytes::toNumber($settings['max_size']), Environment::getUploadMaxSize());
|
||||
$max_filesize = $settings['max_size']
|
||||
? Bytes::toNumber($settings['max_size'])
|
||||
: Environment::getUploadMaxSize();
|
||||
$max_dimensions = 0;
|
||||
if (!empty($settings['max_dimensions']['width']) || !empty($settings['max_dimensions']['height'])) {
|
||||
$max_dimensions = $settings['max_dimensions']['width'] . 'x' . $settings['max_dimensions']['height'];
|
||||
|
|
|
@ -63,16 +63,27 @@ class Image extends CKEditor5PluginDefault implements CKEditor5PluginConfigurabl
|
|||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->setValue('status', (bool) $form_state->getValue('status'));
|
||||
$form_state->setValue(['max_dimensions', 'width'], (int) $form_state->getValue(['max_dimensions', 'width']));
|
||||
$form_state->setValue(['max_dimensions', 'height'], (int) $form_state->getValue(['max_dimensions', 'height']));
|
||||
$directory = $form_state->getValue(['directory']);
|
||||
$form_state->setValue(['directory'], trim($directory) === '' ? NULL : $directory);
|
||||
$max_size = $form_state->getValue(['max_size']);
|
||||
$form_state->setValue(['max_size'], trim($max_size) === '' ? NULL : $max_size);
|
||||
$max_width = $form_state->getValue(['max_dimensions', 'width']);
|
||||
$form_state->setValue(['max_dimensions', 'width'], trim($max_width) === '' ? NULL : (int) $max_width);
|
||||
$max_height = $form_state->getValue(['max_dimensions', 'height']);
|
||||
$form_state->setValue(['max_dimensions', 'height'], trim($max_height) === '' ? NULL : (int) $max_height);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$settings = $form_state->getValues();
|
||||
if (!$settings['status']) {
|
||||
// Remove all other settings to comply with config schema.
|
||||
$settings = ['status' => FALSE];
|
||||
}
|
||||
// Store this configuration in its out-of-band location.
|
||||
$form_state->get('editor')->setImageUploadSettings($form_state->getValues());
|
||||
$form_state->get('editor')->setImageUploadSettings($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -660,7 +660,13 @@ class CKEditor5 extends EditorBase implements ContainerFactoryPluginInterface {
|
|||
// All plugin settings have been collected, including defaults that depend
|
||||
// on visibility. Store the collected settings, throw away the interim state
|
||||
// that allowed determining which defaults to add.
|
||||
// Create a new clone, because the plugins whose data is being stored
|
||||
// out-of-band may have modified the Text Editor config entity in the form
|
||||
// state.
|
||||
// @see \Drupal\editor\EditorInterface::setImageUploadSettings()
|
||||
// @see \Drupal\ckeditor5\Plugin\CKEditor5Plugin\Image::submitConfigurationForm()
|
||||
unset($eventual_editor_and_format_for_plugin_settings_visibility);
|
||||
$submitted_editor = clone $form_state->get('editor');
|
||||
$submitted_editor->setSettings($settings);
|
||||
|
||||
// Validate the text editor + text format pair.
|
||||
|
@ -903,6 +909,14 @@ class CKEditor5 extends EditorBase implements ContainerFactoryPluginInterface {
|
|||
return implode('][', array_merge(explode('.', $property_path), ['settings']));
|
||||
}
|
||||
|
||||
// Image upload settings are stored out-of-band and may also trigger
|
||||
// validation errors.
|
||||
// @see \Drupal\ckeditor5\Plugin\CKEditor5Plugin\Image
|
||||
if (str_starts_with($property_path, 'image_upload.')) {
|
||||
$image_upload_setting_property_path = str_replace('image_upload.', '', $property_path);
|
||||
return 'editor][settings][plugins][ckeditor5_image][' . implode('][', explode('.', $image_upload_setting_property_path));
|
||||
}
|
||||
|
||||
// Everything else is in the subform.
|
||||
return 'editor][' . static::mapViolationPropertyPathsToFormNames($property_path, $form);
|
||||
}
|
||||
|
|
|
@ -29,8 +29,13 @@ trait TextEditorObjectDependentValidatorTrait {
|
|||
]);
|
||||
}
|
||||
else {
|
||||
assert($this->context->getRoot()->getDataDefinition()->getDataType() === 'editor.editor.*');
|
||||
assert(in_array($this->context->getRoot()->getDataDefinition()->getDataType(), ['editor.editor.*', 'entity:editor'], TRUE));
|
||||
$text_format = FilterFormat::load($this->context->getRoot()->get('format')->getValue());
|
||||
// This validator must not complain about a missing text format.
|
||||
// @see \Drupal\Tests\editor\Kernel\EditorValidationTest::testInvalidFormat()
|
||||
if ($text_format === NULL) {
|
||||
$text_format = FilterFormat::create([]);
|
||||
}
|
||||
}
|
||||
assert($text_format instanceof FilterFormatInterface);
|
||||
|
||||
|
|
|
@ -27,8 +27,14 @@ class ImageUploadAccessTest extends ImageUploadTest {
|
|||
$response = $this->uploadRequest($url, $test_image, 'test.jpg');
|
||||
$this->assertSame(404, $response->getStatusCode());
|
||||
|
||||
$editor = $this->createEditorWithUpload([
|
||||
'status' => FALSE,
|
||||
$editor = $this->createEditorWithUpload(['status' => FALSE]);
|
||||
|
||||
// Ensure that images cannot be uploaded when image upload is disabled.
|
||||
$response = $this->uploadRequest($url, $test_image, 'test.jpg');
|
||||
$this->assertSame(403, $response->getStatusCode());
|
||||
|
||||
$editor->setImageUploadSettings([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
|
@ -36,14 +42,7 @@ class ImageUploadAccessTest extends ImageUploadTest {
|
|||
'width' => 0,
|
||||
'height' => 0,
|
||||
],
|
||||
]);
|
||||
|
||||
// Ensure that images cannot be uploaded when image upload is disabled.
|
||||
$response = $this->uploadRequest($url, $test_image, 'test.jpg');
|
||||
$this->assertSame(403, $response->getStatusCode());
|
||||
|
||||
$editor->setImageUploadSettings(['status' => TRUE] + $editor->getImageUploadSettings())
|
||||
->save();
|
||||
])->save();
|
||||
$response = $this->uploadRequest($url, $test_image, 'test.jpg');
|
||||
$this->assertSame(201, $response->getStatusCode());
|
||||
|
||||
|
|
|
@ -138,7 +138,14 @@ class CKEditor5UpdateImageToolbarItemTest extends UpdatePathTestBase {
|
|||
function (ConstraintViolation $v) {
|
||||
return (string) $v->getMessage();
|
||||
},
|
||||
iterator_to_array(CKEditor5::validatePair($editor_after, $filter_format_after))
|
||||
// @todo Fix stream wrappers not being available early enough in
|
||||
// https://www.drupal.org/project/drupal/issues/3416735. Then remove the
|
||||
// array_filter().
|
||||
// @see \Drupal\Core\Config\Schema\SchemaCheckTrait::$ignoredPropertyPaths
|
||||
array_filter(
|
||||
iterator_to_array(CKEditor5::validatePair($editor_after, $filter_format_after)),
|
||||
fn(ConstraintViolation $v) => $v->getMessage() != 'The file storage you selected is not a visible, readable and writable stream wrapper. Possible choices: <em class="placeholder"></em>.',
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,55 @@ JS;
|
|||
$this->assertFalse($media_tab->isVisible(), 'Media settings should be removed when media filter disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that image upload settings (stored out of band) are validated too.
|
||||
*/
|
||||
public function testImageUploadSettingsAreValidated(): void {
|
||||
$page = $this->getSession()->getPage();
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$this->addNewTextFormat($page, $assert_session);
|
||||
$this->drupalGet('admin/config/content/formats/manage/ckeditor5');
|
||||
|
||||
// Add the image plugin to the CKEditor 5 toolbar.
|
||||
$this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-item-drupalInsertImage'));
|
||||
$this->triggerKeyUp('.ckeditor5-toolbar-item-drupalInsertImage', 'ArrowDown');
|
||||
$assert_session->assertExpectedAjaxRequest(1);
|
||||
|
||||
// Open the vertical tab with its settings.
|
||||
$page->find('css', '[href^="#edit-editor-settings-plugins-ckeditor5-image"]')->click();
|
||||
$this->assertTrue($assert_session->waitForText('Enable image uploads'));
|
||||
|
||||
// Check the "Enable image uploads" checkbox.
|
||||
$assert_session->checkboxNotChecked('editor[settings][plugins][ckeditor5_image][status]');
|
||||
$page->checkField('editor[settings][plugins][ckeditor5_image][status]');
|
||||
$assert_session->assertExpectedAjaxRequest(2);
|
||||
|
||||
// Enter a nonsensical maximum file size.
|
||||
$page->fillField('editor[settings][plugins][ckeditor5_image][max_size]', 'foobar');
|
||||
$this->assertNoRealtimeValidationErrors();
|
||||
|
||||
// Enable another toolbar item to trigger validation.
|
||||
$this->triggerKeyUp('.ckeditor5-toolbar-item-sourceEditing', 'ArrowDown');
|
||||
$assert_session->assertExpectedAjaxRequest(3);
|
||||
|
||||
// The expected validation error must be present.
|
||||
$assert_session->elementExists('css', '[role=alert]:contains("This value must be a number of bytes, optionally with a unit such as "MB" or "megabytes".")');
|
||||
|
||||
// Enter no maximum file size because it is optional, this should result in
|
||||
// no validation error and it being set to `null`.
|
||||
$page->findField('editor[settings][plugins][ckeditor5_image][max_size]')->setValue('');
|
||||
|
||||
// Remove a toolbar item to trigger validation.
|
||||
$this->triggerKeyUp('.ckeditor5-toolbar-item-sourceEditing', 'ArrowUp');
|
||||
$assert_session->assertExpectedAjaxRequest(4);
|
||||
|
||||
// No more validation errors, let's save.
|
||||
$this->assertNoRealtimeValidationErrors();
|
||||
$page->pressButton('Save configuration');
|
||||
$assert_session->pageTextContains('The text format ckeditor5 has been updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure CKEditor 5 admin UI's real-time validation errors do not accumulate.
|
||||
*/
|
||||
|
|
|
@ -95,6 +95,10 @@ class CKEditor5Test extends CKEditor5TestBase {
|
|||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
$this->assertSame([], array_map(
|
||||
|
@ -643,6 +647,7 @@ JS;
|
|||
'reversed' => FALSE,
|
||||
'startIndex' => FALSE,
|
||||
],
|
||||
'multiBlock' => TRUE,
|
||||
],
|
||||
'ckeditor5_sourceEditing' => [
|
||||
'allowed_tags' => [],
|
||||
|
|
|
@ -1030,7 +1030,6 @@ PHP,
|
|||
$sneaky_plugin_id => ['configured_subset' => $configured_subset],
|
||||
],
|
||||
],
|
||||
'image_upload' => [],
|
||||
]);
|
||||
|
||||
// Invalid subsets are allowed on unsaved Text Editor config entities,
|
||||
|
@ -1257,7 +1256,9 @@ PHP,
|
|||
'format' => 'dummy',
|
||||
'editor' => 'ckeditor5',
|
||||
'settings' => $text_editor_settings,
|
||||
'image_upload' => [],
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
],
|
||||
]);
|
||||
FilterFormat::create([
|
||||
'format' => 'dummy',
|
||||
|
|
|
@ -86,7 +86,9 @@ class ValidatorsTest extends KernelTestBase {
|
|||
'format' => 'dummy',
|
||||
'editor' => 'ckeditor5',
|
||||
'settings' => $ckeditor5_settings,
|
||||
'image_upload' => [],
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
],
|
||||
]);
|
||||
|
||||
$typed_config = $this->typedConfig->createFromNameAndData(
|
||||
|
@ -182,7 +184,10 @@ class ValidatorsTest extends KernelTestBase {
|
|||
],
|
||||
],
|
||||
'violations' => [
|
||||
'settings.plugins.ckeditor5_language' => 'Configuration for the enabled plugin "<em class="placeholder">Language</em>" (<em class="placeholder">ckeditor5_language</em>) is missing.',
|
||||
'settings.plugins.ckeditor5_language' => [
|
||||
'Configuration for the enabled plugin "<em class="placeholder">Language</em>" (<em class="placeholder">ckeditor5_language</em>) is missing.',
|
||||
"'language_list' is a required key because settings.plugins.%key is ckeditor5_language (see config schema type ckeditor5.plugin.ckeditor5_language).",
|
||||
],
|
||||
],
|
||||
];
|
||||
$data['valid language plugin configuration: un'] = [
|
||||
|
@ -1056,7 +1061,7 @@ class ValidatorsTest extends KernelTestBase {
|
|||
],
|
||||
],
|
||||
'image_upload' => [
|
||||
'status' => TRUE,
|
||||
'status' => FALSE,
|
||||
],
|
||||
'filters' => [],
|
||||
'violations' => [
|
||||
|
@ -1102,7 +1107,7 @@ class ValidatorsTest extends KernelTestBase {
|
|||
],
|
||||
],
|
||||
'image_upload' => [
|
||||
'status' => TRUE,
|
||||
'status' => FALSE,
|
||||
],
|
||||
'filters' => [],
|
||||
'violations' => [
|
||||
|
@ -1163,8 +1168,15 @@ class ValidatorsTest extends KernelTestBase {
|
|||
],
|
||||
],
|
||||
],
|
||||
'image' => [
|
||||
'image_upload' => [
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => NULL,
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
],
|
||||
'filters' => [],
|
||||
'violations' => [],
|
||||
|
@ -1621,7 +1633,6 @@ class ValidatorsTest extends KernelTestBase {
|
|||
],
|
||||
'plugins' => [],
|
||||
],
|
||||
'image_upload' => [],
|
||||
]);
|
||||
|
||||
$this->assertSame([], $this->validatePairToViolationsArray($text_editor, $text_format, TRUE));
|
||||
|
|
|
@ -7,6 +7,11 @@ editor.editor.*:
|
|||
format:
|
||||
type: string
|
||||
label: 'Name'
|
||||
constraints:
|
||||
# @see \Drupal\editor\Entity\Editor::getFilterFormat()
|
||||
# @see \Drupal\editor\Entity\Editor::calculateDependencies()
|
||||
ConfigExists:
|
||||
prefix: 'filter.format.'
|
||||
editor:
|
||||
type: string
|
||||
label: 'Text editor'
|
||||
|
@ -17,30 +22,71 @@ editor.editor.*:
|
|||
settings:
|
||||
type: editor.settings.[%parent.editor]
|
||||
image_upload:
|
||||
type: editor.image_upload_settings.[status]
|
||||
constraints:
|
||||
FullyValidatable: ~
|
||||
|
||||
editor.image_upload_settings.*:
|
||||
type: mapping
|
||||
label: 'Image uploads'
|
||||
constraints:
|
||||
FullyValidatable: ~
|
||||
mapping:
|
||||
status:
|
||||
type: boolean
|
||||
label: 'Status'
|
||||
|
||||
editor.image_upload_settings.1:
|
||||
type: editor.image_upload_settings.*
|
||||
label: 'Image upload settings'
|
||||
constraints:
|
||||
FullyValidatable: ~
|
||||
mapping:
|
||||
scheme:
|
||||
type: string
|
||||
label: 'File storage'
|
||||
constraints:
|
||||
Choice:
|
||||
callback: \Drupal\editor\Entity\Editor::getValidStreamWrappers
|
||||
message: 'The file storage you selected is not a visible, readable and writable stream wrapper. Possible choices: %choices.'
|
||||
directory:
|
||||
type: string
|
||||
label: 'Upload directory'
|
||||
nullable: true
|
||||
constraints:
|
||||
# `""` is not allowed, but `null` is.
|
||||
NotBlank:
|
||||
allowNull: true
|
||||
Regex:
|
||||
# Forbid any kind of control character.
|
||||
# @see https://stackoverflow.com/a/66587087
|
||||
pattern: '/([^\PC])/u'
|
||||
match: false
|
||||
message: 'The image upload directory is not allowed to span multiple lines or contain control characters.'
|
||||
max_size:
|
||||
# @see \Drupal\file\Plugin\Validation\Constraint\FileSizeLimitConstraintValidator
|
||||
type: bytes
|
||||
label: 'Maximum file size'
|
||||
nullable: true
|
||||
max_dimensions:
|
||||
type: mapping
|
||||
label: 'Image upload settings'
|
||||
label: 'Maximum dimensions'
|
||||
mapping:
|
||||
status:
|
||||
type: boolean
|
||||
label: 'Status'
|
||||
scheme:
|
||||
type: string
|
||||
label: 'File storage'
|
||||
directory:
|
||||
type: string
|
||||
label: 'Upload directory'
|
||||
max_size:
|
||||
type: string
|
||||
label: 'Maximum file size'
|
||||
max_dimensions:
|
||||
type: mapping
|
||||
label: 'Maximum dimensions'
|
||||
mapping:
|
||||
width:
|
||||
type: integer
|
||||
nullable: true
|
||||
label: 'Maximum width'
|
||||
height:
|
||||
type: integer
|
||||
nullable: true
|
||||
label: 'Maximum height'
|
||||
width:
|
||||
type: integer
|
||||
nullable: true
|
||||
label: 'Maximum width'
|
||||
constraints:
|
||||
Range:
|
||||
# @see editor_image_upload_settings_form()
|
||||
min: 1
|
||||
max: 99999
|
||||
height:
|
||||
type: integer
|
||||
nullable: true
|
||||
label: 'Maximum height'
|
||||
constraints:
|
||||
Range:
|
||||
# @see editor_image_upload_settings_form()
|
||||
min: 1
|
||||
max: 99999
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
use Drupal\Core\Url;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Form\SubformState;
|
||||
use Drupal\editor\EditorInterface;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
|
@ -252,6 +253,15 @@ function editor_form_filter_admin_format_submit($form, FormStateInterface $form_
|
|||
if ($settings = $form_state->getValue(['editor', 'settings'])) {
|
||||
$editor->setSettings($settings);
|
||||
}
|
||||
// When image uploads are disabled (status = FALSE), the schema for image
|
||||
// upload settings does not allow other keys to be present.
|
||||
// @see editor.image_upload_settings.*
|
||||
// @see editor.image_upload_settings.1
|
||||
// @see editor.schema.yml
|
||||
$image_upload_settings = $editor->getImageUploadSettings();
|
||||
if (!$image_upload_settings['status']) {
|
||||
$editor->setImageUploadSettings(['status' => FALSE]);
|
||||
}
|
||||
$editor->save();
|
||||
}
|
||||
}
|
||||
|
@ -641,3 +651,41 @@ function editor_filter_format_presave(FilterFormatInterface $format) {
|
|||
$editor->setStatus($status)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_presave().
|
||||
*/
|
||||
function editor_editor_presave(EditorInterface $editor) {
|
||||
// @see editor_post_update_sanitize_image_upload_settings()
|
||||
$image_upload_settings = $editor->getImageUploadSettings();
|
||||
// When image uploads are disabled, then none of the other key-value pairs
|
||||
// make sense.
|
||||
// TRICKY: the configuration system has historically stored `type: boolean`
|
||||
// not as `true` and `false`, but as `1` and `0`, so use `==`, not `===`.
|
||||
// @see editor_post_update_sanitize_image_upload_settings()
|
||||
if (!array_key_exists('status', $image_upload_settings) || $image_upload_settings['status'] == FALSE) {
|
||||
$editor->setImageUploadSettings(['status' => FALSE]);
|
||||
}
|
||||
else {
|
||||
// When image uploads are enabled, then some of the key-value pairs need
|
||||
// some conversions to comply with the config schema. Note that all these
|
||||
// keys SHOULD exist, but because validation has historically been absent,
|
||||
// err on the side of caution.
|
||||
// @see editor_post_update_sanitize_image_upload_settings()
|
||||
if (array_key_exists('directory', $image_upload_settings) && $image_upload_settings['directory'] === '') {
|
||||
$image_upload_settings['directory'] = NULL;
|
||||
}
|
||||
if (array_key_exists('max_size', $image_upload_settings) && $image_upload_settings['max_size'] === '') {
|
||||
$image_upload_settings['max_size'] = NULL;
|
||||
}
|
||||
if (array_key_exists('max_dimensions', $image_upload_settings)) {
|
||||
if (!array_key_exists('width', $image_upload_settings['max_dimensions']) || $image_upload_settings['max_dimensions']['width'] === 0) {
|
||||
$image_upload_settings['max_dimensions']['width'] = NULL;
|
||||
}
|
||||
if (!array_key_exists('height', $image_upload_settings['max_dimensions']) || $image_upload_settings['max_dimensions']['height'] === 0) {
|
||||
$image_upload_settings['max_dimensions']['height'] = NULL;
|
||||
}
|
||||
}
|
||||
$editor->setImageUploadSettings($image_upload_settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Post update functions for Editor.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
|
||||
use Drupal\editor\EditorInterface;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\filter\FilterFormatInterface;
|
||||
use Drupal\filter\FilterPluginCollection;
|
||||
|
@ -44,3 +46,24 @@ function editor_post_update_image_lazy_load(): void {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up image upload settings.
|
||||
*/
|
||||
function editor_post_update_sanitize_image_upload_settings(&$sandbox = []) {
|
||||
$config_entity_updater = \Drupal::classResolver(ConfigEntityUpdater::class);
|
||||
|
||||
$callback = function (EditorInterface $editor) {
|
||||
$image_upload_settings = $editor->getImageUploadSettings();
|
||||
// Only update if the editor has image uploads:
|
||||
// - empty image upload settings
|
||||
// - disabled and >=1 other keys in its image upload settings
|
||||
// - enabled (to tighten the key-value pairs in its settings).
|
||||
// @see editor_editor_presave()
|
||||
return !array_key_exists('status', $image_upload_settings)
|
||||
|| ($image_upload_settings['status'] == FALSE && count($image_upload_settings) >= 2)
|
||||
|| $image_upload_settings['status'] == TRUE;
|
||||
};
|
||||
|
||||
$config_entity_updater->update($sandbox, 'editor', $callback);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\editor\Entity;
|
|||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\editor\EditorInterface;
|
||||
|
||||
/**
|
||||
|
@ -207,4 +208,18 @@ class Editor extends ConfigEntityBase implements EditorInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes all valid choices for the "image_upload.scheme" setting.
|
||||
*
|
||||
* @see editor.schema.yml
|
||||
*
|
||||
* @return string[]
|
||||
* All valid choices.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function getValidStreamWrappers(): array {
|
||||
return array_keys(\Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -96,25 +96,15 @@ class EditorImageDialog extends FormBase {
|
|||
|
||||
// Construct strings to use in the upload validators.
|
||||
$image_upload = $editor->getImageUploadSettings();
|
||||
if (!empty($image_upload['max_dimensions']['width']) || !empty($image_upload['max_dimensions']['height'])) {
|
||||
$max_dimensions = $image_upload['max_dimensions']['width'] . 'x' . $image_upload['max_dimensions']['height'];
|
||||
}
|
||||
else {
|
||||
$max_dimensions = 0;
|
||||
}
|
||||
$max_filesize = min(Bytes::toNumber($image_upload['max_size']), Environment::getUploadMaxSize());
|
||||
$existing_file = isset($image_element['data-entity-uuid']) ? \Drupal::service('entity.repository')->loadEntityByUuid('file', $image_element['data-entity-uuid']) : NULL;
|
||||
$fid = $existing_file ? $existing_file->id() : NULL;
|
||||
|
||||
$form['fid'] = [
|
||||
'#title' => $this->t('Image'),
|
||||
'#type' => 'managed_file',
|
||||
'#upload_location' => $image_upload['scheme'] . '://' . $image_upload['directory'],
|
||||
'#default_value' => $fid ? [$fid] : NULL,
|
||||
'#upload_validators' => [
|
||||
'FileExtension' => ['extensions' => 'gif png jpg jpeg'],
|
||||
'FileSizeLimit' => ['fileLimit' => $max_filesize],
|
||||
'FileImageDimensions' => ['maxDimensions' => $max_dimensions],
|
||||
],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
@ -132,6 +122,17 @@ class EditorImageDialog extends FormBase {
|
|||
if ($image_upload['status']) {
|
||||
$form['attributes']['src']['#access'] = FALSE;
|
||||
$form['attributes']['src']['#required'] = FALSE;
|
||||
|
||||
if (!empty($image_upload['max_dimensions']['width']) || !empty($image_upload['max_dimensions']['height'])) {
|
||||
$max_dimensions = $image_upload['max_dimensions']['width'] . 'x' . $image_upload['max_dimensions']['height'];
|
||||
}
|
||||
else {
|
||||
$max_dimensions = 0;
|
||||
}
|
||||
$max_filesize = min(Bytes::toNumber($image_upload['max_size'] ?? 0), Environment::getUploadMaxSize());
|
||||
$form['fid']['#upload_location'] = $image_upload['scheme'] . '://' . ($image_upload['directory'] ?? '');
|
||||
$form['fid']['#upload_validators']['FileSizeLimit'] = ['fileLimit' => $max_filesize];
|
||||
$form['fid']['#upload_validators']['FileImageDimensions'] = ['maxDimensions' => $max_dimensions];
|
||||
}
|
||||
else {
|
||||
$form['fid']['#access'] = FALSE;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
$umami_basic_html_format = Yaml::decode(file_get_contents(__DIR__ . '/filter.format.umami_basic_html.yml'));
|
||||
$umami_basic_html_format['format'] = 'umami_basic_html';
|
||||
$connection->insert('config')
|
||||
->fields([
|
||||
'collection',
|
||||
'name',
|
||||
'data',
|
||||
])
|
||||
->values([
|
||||
'collection' => '',
|
||||
'name' => 'filter.format.umami_basic_html',
|
||||
'data' => serialize($umami_basic_html_format),
|
||||
])
|
||||
->execute();
|
||||
|
||||
$umami_basic_html_editor = Yaml::decode(file_get_contents(__DIR__ . '/editor.editor.umami_basic_html.yml'));
|
||||
$umami_basic_html_editor['format'] = 'umami_basic_html';
|
||||
$connection->insert('config')
|
||||
->fields([
|
||||
'collection',
|
||||
'name',
|
||||
'data',
|
||||
])
|
||||
->values([
|
||||
'collection' => '',
|
||||
'name' => 'editor.editor.umami_basic_html',
|
||||
'data' => serialize($umami_basic_html_editor),
|
||||
])
|
||||
->execute();
|
64
core/modules/editor/tests/fixtures/update/editor.editor.umami_basic_html.yml
vendored
Normal file
64
core/modules/editor/tests/fixtures/update/editor.editor.umami_basic_html.yml
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
uuid: c82794ef-c451-49c6-be67-39e2b0649a47
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- filter.format.basic_html
|
||||
module:
|
||||
- ckeditor5
|
||||
format: basic_html
|
||||
editor: ckeditor5
|
||||
settings:
|
||||
toolbar:
|
||||
items:
|
||||
- bold
|
||||
- italic
|
||||
- '|'
|
||||
- link
|
||||
- '|'
|
||||
- bulletedList
|
||||
- numberedList
|
||||
- '|'
|
||||
- blockQuote
|
||||
- '|'
|
||||
- heading
|
||||
- '|'
|
||||
- sourceEditing
|
||||
- '|'
|
||||
plugins:
|
||||
ckeditor5_heading:
|
||||
enabled_headings:
|
||||
- heading2
|
||||
- heading3
|
||||
- heading4
|
||||
- heading5
|
||||
- heading6
|
||||
ckeditor5_list:
|
||||
properties:
|
||||
reversed: false
|
||||
startIndex: true
|
||||
multiBlock: false
|
||||
ckeditor5_sourceEditing:
|
||||
allowed_tags:
|
||||
- '<cite>'
|
||||
- '<dl>'
|
||||
- '<dt>'
|
||||
- '<dd>'
|
||||
- '<a hreflang>'
|
||||
- '<blockquote cite>'
|
||||
- '<ul type>'
|
||||
- '<ol type>'
|
||||
- '<h2 id>'
|
||||
- '<h3 id>'
|
||||
- '<h4 id>'
|
||||
- '<h5 id>'
|
||||
- '<h6 id>'
|
||||
- '<img src alt data-entity-type data-entity-uuid data-align data-caption width height loading>'
|
||||
image_upload:
|
||||
status: false
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_dimensions:
|
||||
width: null
|
||||
height: null
|
55
core/modules/editor/tests/fixtures/update/filter.format.umami_basic_html.yml
vendored
Normal file
55
core/modules/editor/tests/fixtures/update/filter.format.umami_basic_html.yml
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
uuid: 32fd1f3a-8ea1-44be-851f-64659c260bea
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- editor
|
||||
name: 'Basic HTML'
|
||||
format: basic_html
|
||||
weight: 0
|
||||
filters:
|
||||
editor_file_reference:
|
||||
id: editor_file_reference
|
||||
provider: editor
|
||||
status: true
|
||||
weight: 11
|
||||
settings: { }
|
||||
filter_align:
|
||||
id: filter_align
|
||||
provider: filter
|
||||
status: true
|
||||
weight: 7
|
||||
settings: { }
|
||||
filter_autop:
|
||||
id: filter_autop
|
||||
provider: filter
|
||||
status: true
|
||||
weight: 0
|
||||
settings: { }
|
||||
filter_caption:
|
||||
id: filter_caption
|
||||
provider: filter
|
||||
status: true
|
||||
weight: 8
|
||||
settings: { }
|
||||
filter_html:
|
||||
id: filter_html
|
||||
provider: filter
|
||||
status: true
|
||||
weight: -10
|
||||
settings:
|
||||
allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>'
|
||||
filter_html_help: false
|
||||
filter_html_nofollow: false
|
||||
filter_html_image_secure:
|
||||
id: filter_html_image_secure
|
||||
provider: filter
|
||||
status: true
|
||||
weight: 9
|
||||
settings: { }
|
||||
filter_image_lazy_load:
|
||||
id: filter_image_lazy_load
|
||||
provider: filter
|
||||
status: true
|
||||
weight: 15
|
||||
settings: { }
|
|
@ -45,13 +45,6 @@ class EditorDialogAccessTest extends BrowserTestBase {
|
|||
'format' => 'plain_text',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$editor->save();
|
||||
|
@ -63,8 +56,16 @@ class EditorDialogAccessTest extends BrowserTestBase {
|
|||
|
||||
// With image upload settings, expect a 200, and now there should be an
|
||||
// input[type=file].
|
||||
$editor->setImageUploadSettings(['status' => TRUE] + $editor->getImageUploadSettings())
|
||||
->save();
|
||||
$editor->setImageUploadSettings([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => NULL,
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
])->save();
|
||||
$this->resetAll();
|
||||
$this->drupalGet($url);
|
||||
$this->assertEmpty($this->cssSelect('input[type=text][name="attributes[src]"]'), 'Image uploads enabled: input[type=text][name="attributes[src]"] is absent.');
|
||||
|
|
|
@ -66,7 +66,7 @@ abstract class EditorResourceTestBase extends ConfigEntityResourceTestBase {
|
|||
]);
|
||||
$camelids
|
||||
->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
|
@ -96,10 +96,10 @@ abstract class EditorResourceTestBase extends ConfigEntityResourceTestBase {
|
|||
'editor' => 'ckeditor5',
|
||||
'format' => 'llama',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_size' => NULL,
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* @group Update
|
||||
* @group editor
|
||||
* @see editor_post_update_sanitize_image_upload_settings()
|
||||
*/
|
||||
class EditorSanitizeImageUploadSettingsUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles(): void {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/editor-3412361.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure image upload settings for Text Editor config entities are corrected.
|
||||
*
|
||||
* @see editor_post_update_sanitize_image_upload_settings()
|
||||
*/
|
||||
public function testUpdateRemoveMeaninglessImageUploadSettings(): void {
|
||||
$basic_html_before = $this->config('editor.editor.basic_html');
|
||||
$this->assertSame([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
],
|
||||
], $basic_html_before->get('image_upload'));
|
||||
$full_html_before = $this->config('editor.editor.full_html');
|
||||
$this->assertSame([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
],
|
||||
], $full_html_before->get('image_upload'));
|
||||
$umami_basic_html_before = $this->config('editor.editor.umami_basic_html');
|
||||
$this->assertSame([
|
||||
'status' => FALSE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
], $umami_basic_html_before->get('image_upload'));
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
$basic_html_after = $this->config('editor.editor.basic_html');
|
||||
$this->assertNotSame($basic_html_before->get('image_upload'), $basic_html_after->get('image_upload'));
|
||||
$this->assertSame([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => NULL,
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
], $basic_html_after->get('image_upload'));
|
||||
$full_html_after = $this->config('editor.editor.full_html');
|
||||
$this->assertNotSame($full_html_before->get('image_upload'), $full_html_after->get('image_upload'));
|
||||
$this->assertSame([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => NULL,
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
], $full_html_after->get('image_upload'));
|
||||
$umami_basic_html_after = $this->config('editor.editor.umami_basic_html');
|
||||
$this->assertNotSame($umami_basic_html_before->get('image_upload'), $umami_basic_html_after->get('image_upload'));
|
||||
$this->assertSame([
|
||||
'status' => FALSE,
|
||||
], $umami_basic_html_after->get('image_upload'));
|
||||
}
|
||||
|
||||
}
|
|
@ -68,6 +68,10 @@ class EditorImageDialogTest extends EntityKernelTestBase {
|
|||
'max_size' => 100,
|
||||
'scheme' => 'public',
|
||||
'directory' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
'status' => TRUE,
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Tests\editor\Kernel;
|
||||
|
||||
use Drupal\ckeditor5\Plugin\CKEditor5Plugin\Heading;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase;
|
||||
|
@ -16,7 +17,18 @@ class EditorValidationTest extends ConfigEntityValidationTestBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['editor', 'editor_test', 'filter'];
|
||||
protected static $modules = ['ckeditor5', 'editor', 'filter'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static array $propertiesWithRequiredKeys = [
|
||||
'settings' => [
|
||||
"'toolbar' is a required key because editor is ckeditor5 (see config schema type editor.settings.ckeditor5).",
|
||||
"'plugins' is a required key because editor is ckeditor5 (see config schema type editor.settings.ckeditor5).",
|
||||
],
|
||||
'image_upload' => "'status' is a required key.",
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -32,11 +44,34 @@ class EditorValidationTest extends ConfigEntityValidationTestBase {
|
|||
|
||||
$this->entity = Editor::create([
|
||||
'format' => $format->id(),
|
||||
'editor' => 'unicorn',
|
||||
'editor' => 'ckeditor5',
|
||||
'settings' => [
|
||||
// @see \Drupal\ckeditor5\Plugin\Editor\CKEditor5::getDefaultSettings()
|
||||
'toolbar' => [
|
||||
'items' => ['heading', 'bold', 'italic'],
|
||||
],
|
||||
'plugins' => [
|
||||
'ckeditor5_heading' => Heading::DEFAULT_CONFIGURATION,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testImmutableProperties(array $valid_values = [], ?array $additional_expected_validation_errors_when_missing = NULL): void {
|
||||
// TRICKY: Every Text Editor is associated with a Text Format. It must exist
|
||||
// to avoid triggering a validation error.
|
||||
// @see \Drupal\editor\EditorInterface::hasAssociatedFilterFormat
|
||||
FilterFormat::create([
|
||||
'format' => 'another',
|
||||
'name' => 'Another',
|
||||
])->save();
|
||||
parent::testImmutableProperties(['format' => 'another']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that validation fails if config dependencies are invalid.
|
||||
*/
|
||||
|
@ -70,6 +105,17 @@ class EditorValidationTest extends ConfigEntityValidationTestBase {
|
|||
$this->assertValidationErrors(['editor' => "The 'non_existent' plugin does not exist."]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validating an editor with a non-existent `format`.
|
||||
*/
|
||||
public function testInvalidFormat(): void {
|
||||
$this->entity->set('format', 'non_existent');
|
||||
$this->assertValidationErrors([
|
||||
'' => "The 'format' property cannot be changed.",
|
||||
'format' => "The 'filter.format.non_existent' config does not exist.",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -79,6 +125,107 @@ class EditorValidationTest extends ConfigEntityValidationTestBase {
|
|||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
/**
|
||||
* `image_upload.status = TRUE` must cause additional keys to be required.
|
||||
*/
|
||||
public function testImageUploadSettingsAreDynamicallyRequired(): void {
|
||||
// When image uploads are disabled, no other key-value pairs are needed.
|
||||
$this->entity->setImageUploadSettings(['status' => FALSE]);
|
||||
$this->assertValidationErrors([]);
|
||||
|
||||
// But when they are enabled, many others are needed.
|
||||
$this->entity->setImageUploadSettings(['status' => TRUE]);
|
||||
$this->assertValidationErrors([
|
||||
'image_upload' => [
|
||||
"'scheme' is a required key because image_upload.status is 1 (see config schema type editor.image_upload_settings.1).",
|
||||
"'directory' is a required key because image_upload.status is 1 (see config schema type editor.image_upload_settings.1).",
|
||||
"'max_size' is a required key because image_upload.status is 1 (see config schema type editor.image_upload_settings.1).",
|
||||
"'max_dimensions' is a required key because image_upload.status is 1 (see config schema type editor.image_upload_settings.1).",
|
||||
],
|
||||
]);
|
||||
|
||||
// Specify all required keys, but forget one.
|
||||
$this->entity->setImageUploadSettings([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'uploaded-images',
|
||||
'max_size' => '5 MB',
|
||||
]);
|
||||
$this->assertValidationErrors(['image_upload' => "'max_dimensions' is a required key because image_upload.status is 1 (see config schema type editor.image_upload_settings.1)."]);
|
||||
|
||||
// Specify all required keys.
|
||||
$this->entity->setImageUploadSettings([
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'uploaded-images',
|
||||
'max_size' => '5 MB',
|
||||
'max_dimensions' => [
|
||||
'width' => 10000,
|
||||
'height' => 10000,
|
||||
],
|
||||
]);
|
||||
$this->assertValidationErrors([]);
|
||||
|
||||
// Specify all required keys … but now disable image uploads again. This
|
||||
// should trigger a validation error from the ValidKeys constraint.
|
||||
$this->entity->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'uploaded-images',
|
||||
'max_size' => '5 MB',
|
||||
'max_dimensions' => [
|
||||
'width' => 10000,
|
||||
'height' => 10000,
|
||||
],
|
||||
]);
|
||||
$this->assertValidationErrors([
|
||||
'image_upload' => [
|
||||
"'scheme' is an unknown key because image_upload.status is 0 (see config schema type editor.image_upload_settings.*).",
|
||||
"'directory' is an unknown key because image_upload.status is 0 (see config schema type editor.image_upload_settings.*).",
|
||||
"'max_size' is an unknown key because image_upload.status is 0 (see config schema type editor.image_upload_settings.*).",
|
||||
"'max_dimensions' is an unknown key because image_upload.status is 0 (see config schema type editor.image_upload_settings.*).",
|
||||
],
|
||||
]);
|
||||
|
||||
// Remove the values that the messages said are unknown.
|
||||
$this->entity->setImageUploadSettings(['status' => FALSE]);
|
||||
$this->assertValidationErrors([]);
|
||||
|
||||
// Note how this is the same as the initial value. This proves that `status`
|
||||
// being FALSE prevents any meaningless key-value pairs to be present, and
|
||||
// `status` being TRUE requires those then meaningful pairs to be present.
|
||||
}
|
||||
|
||||
/**
|
||||
* @testWith [{"scheme": "public"}, {}]
|
||||
* [{"scheme": "private"}, {"image_upload.scheme": "The file storage you selected is not a visible, readable and writable stream wrapper. Possible choices: <em class=\"placeholder\">"public"</em>."}]
|
||||
* [{"directory": null}, {}]
|
||||
* [{"directory": ""}, {"image_upload.directory": "This value should not be blank."}]
|
||||
* [{"directory": "inline\nimages"}, {"image_upload.directory": "The image upload directory is not allowed to span multiple lines or contain control characters."}]
|
||||
* [{"directory": "foo\b\b\binline-images"}, {"image_upload.directory": "The image upload directory is not allowed to span multiple lines or contain control characters."}]
|
||||
* [{"max_size": null}, {}]
|
||||
* [{"max_size": "foo"}, {"image_upload.max_size": "This value must be a number of bytes, optionally with a unit such as \"MB\" or \"megabytes\". <em class=\"placeholder\">foo</em> does not represent a number of bytes."}]
|
||||
* [{"max_size": ""}, {"image_upload.max_size": "This value must be a number of bytes, optionally with a unit such as \"MB\" or \"megabytes\". <em class=\"placeholder\"></em> does not represent a number of bytes."}]
|
||||
* [{"max_size": "7 exabytes"}, {}]
|
||||
* [{"max_dimensions": {"width": null, "height": 15}}, {}]
|
||||
* [{"max_dimensions": {"width": null, "height": null}}, {}]
|
||||
* [{"max_dimensions": {"width": null, "height": 0}}, {"image_upload.max_dimensions.height": "This value should be between <em class=\"placeholder\">1</em> and <em class=\"placeholder\">99999</em>."}]
|
||||
* [{"max_dimensions": {"width": 100000, "height": 1}}, {"image_upload.max_dimensions.width": "This value should be between <em class=\"placeholder\">1</em> and <em class=\"placeholder\">99999</em>."}]
|
||||
*/
|
||||
public function testImageUploadSettingsValidation(array $invalid_setting, array $expected_message): void {
|
||||
$this->entity->setImageUploadSettings($invalid_setting + [
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'uploaded-images',
|
||||
'max_size' => '5 MB',
|
||||
'max_dimensions' => [
|
||||
'width' => 10000,
|
||||
'height' => 10000,
|
||||
],
|
||||
]);
|
||||
$this->assertValidationErrors($expected_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -89,6 +236,9 @@ class EditorValidationTest extends ConfigEntityValidationTestBase {
|
|||
// @see \Drupal\Core\Config\Plugin\Validation\Constraint\RequiredConfigDependenciesConstraintValidator
|
||||
'' => 'This text editor requires a text format.',
|
||||
],
|
||||
'settings' => [
|
||||
'settings.plugins.ckeditor5_heading' => 'Configuration for the enabled plugin "<em class="placeholder">Headings</em>" (<em class="placeholder">ckeditor5_heading</em>) is missing.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -102,6 +252,9 @@ class EditorValidationTest extends ConfigEntityValidationTestBase {
|
|||
// @see \Drupal\Core\Config\Plugin\Validation\Constraint\RequiredConfigDependenciesConstraintValidator
|
||||
'' => 'This text editor requires a text format.',
|
||||
],
|
||||
'settings' => [
|
||||
'settings.plugins.ckeditor5_heading' => 'Configuration for the enabled plugin "<em class="placeholder">Headings</em>" (<em class="placeholder">ckeditor5_heading</em>) is missing.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ class EditorTest extends ConfigEntityResourceTestBase {
|
|||
]);
|
||||
$camelids
|
||||
->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
|
@ -129,10 +129,10 @@ class EditorTest extends ConfigEntityResourceTestBase {
|
|||
],
|
||||
'editor' => 'ckeditor5',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_size' => NULL,
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
|
@ -193,7 +193,7 @@ class EditorTest extends ConfigEntityResourceTestBase {
|
|||
]);
|
||||
|
||||
$entity->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
|
|
|
@ -59,9 +59,3 @@ settings:
|
|||
allow_view_mode_override: true
|
||||
image_upload:
|
||||
status: false
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_dimensions:
|
||||
width: null
|
||||
height: null
|
||||
|
|
|
@ -51,7 +51,7 @@ image_upload:
|
|||
status: true
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_size: null
|
||||
max_dimensions:
|
||||
width: null
|
||||
height: null
|
||||
|
|
|
@ -59,7 +59,7 @@ image_upload:
|
|||
status: true
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_size: null
|
||||
max_dimensions:
|
||||
width: 0
|
||||
height: 0
|
||||
width: null
|
||||
height: null
|
||||
|
|
|
@ -96,7 +96,7 @@ image_upload:
|
|||
status: true
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_size: null
|
||||
max_dimensions:
|
||||
width: 0
|
||||
height: 0
|
||||
width: null
|
||||
height: null
|
||||
|
|
|
@ -89,6 +89,7 @@ class BytesTest extends TestCase {
|
|||
*
|
||||
* @dataProvider providerTestValidate
|
||||
* @covers ::validate
|
||||
* @covers ::validateConstraint
|
||||
*/
|
||||
public function testValidate($string, bool $expected_result): void {
|
||||
$this->assertSame($expected_result, Bytes::validate($string));
|
||||
|
|
Loading…
Reference in New Issue