Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -5,5 +5,5 @@ core: 8.x
|
|||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- filter
|
||||
- ckeditor
|
||||
- drupal:filter
|
||||
- drupal:ckeditor
|
||||
|
|
237
web/core/modules/editor/tests/src/Functional/EditorAdminTest.php
Normal file
237
web/core/modules/editor/tests/src/Functional/EditorAdminTest.php
Normal file
|
@ -0,0 +1,237 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests administration of text editors.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorAdminTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['filter', 'editor'];
|
||||
|
||||
/**
|
||||
* A user with the 'administer filters' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Add text format.
|
||||
$filtered_html_format = FilterFormat::create([
|
||||
'format' => 'filtered_html',
|
||||
'name' => 'Filtered HTML',
|
||||
'weight' => 0,
|
||||
'filters' => [],
|
||||
]);
|
||||
$filtered_html_format->save();
|
||||
|
||||
// Create admin user.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer filters']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an existing format without any editors available.
|
||||
*/
|
||||
public function testNoEditorAvailable() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/config/content/formats/manage/filtered_html');
|
||||
|
||||
// Ensure the form field order is correct.
|
||||
$raw_content = $this->getSession()->getPage()->getContent();
|
||||
$roles_pos = strpos($raw_content, 'Roles');
|
||||
$editor_pos = strpos($raw_content, 'Text editor');
|
||||
$filters_pos = strpos($raw_content, 'Enabled filters');
|
||||
$this->assertTrue($roles_pos < $editor_pos && $editor_pos < $filters_pos, '"Text Editor" select appears in the correct location of the text format configuration UI.');
|
||||
|
||||
// Verify the <select>.
|
||||
$select = $this->xpath('//select[@name="editor[editor]"]');
|
||||
$select_is_disabled = $this->xpath('//select[@name="editor[editor]" and @disabled="disabled"]');
|
||||
$options = $this->xpath('//select[@name="editor[editor]"]/option');
|
||||
$this->assertTrue(count($select) === 1, 'The Text Editor select exists.');
|
||||
$this->assertTrue(count($select_is_disabled) === 1, 'The Text Editor select is disabled.');
|
||||
$this->assertTrue(count($options) === 1, 'The Text Editor select has only one option.');
|
||||
$this->assertTrue(($options[0]->getText()) === 'None', 'Option 1 in the Text Editor select is "None".');
|
||||
$this->assertRaw('This option is disabled because no modules that provide a text editor are currently enabled.', 'Description for select present that tells users to install a text editor module.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a text editor to an existing text format.
|
||||
*/
|
||||
public function testAddEditorToExistingFormat() {
|
||||
$this->enableUnicornEditor();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/config/content/formats/manage/filtered_html');
|
||||
$edit = $this->selectUnicornEditor();
|
||||
// Configure Unicorn Editor's setting to another value.
|
||||
$edit['editor[settings][ponies_too]'] = FALSE;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
$this->verifyUnicornEditorConfiguration('filtered_html', FALSE);
|
||||
|
||||
// Switch back to 'None' and check the Unicorn Editor's settings are gone.
|
||||
$edit = [
|
||||
'editor[editor]' => '',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, 'Configure');
|
||||
$unicorn_setting = $this->xpath('//input[@name="editor[settings][ponies_too]" and @type="checkbox" and @checked]');
|
||||
$this->assertTrue(count($unicorn_setting) === 0, "Unicorn Editor's settings form is no longer present.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a text editor to a new text format.
|
||||
*/
|
||||
public function testAddEditorToNewFormat() {
|
||||
$this->addEditorToNewFormat('monocerus', 'Monocerus');
|
||||
$this->verifyUnicornEditorConfiguration('monocerus');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests format disabling.
|
||||
*/
|
||||
public function testDisableFormatWithEditor() {
|
||||
$formats = ['monocerus' => 'Monocerus', 'tattoo' => 'Tattoo'];
|
||||
|
||||
// Install the node module.
|
||||
$this->container->get('module_installer')->install(['node']);
|
||||
$this->resetAll();
|
||||
// Create a new node type and attach the 'body' field to it.
|
||||
$node_type = NodeType::create(['type' => mb_strtolower($this->randomMachineName())]);
|
||||
$node_type->save();
|
||||
node_add_body_field($node_type, $this->randomString());
|
||||
|
||||
$permissions = ['administer filters', "edit any {$node_type->id()} content"];
|
||||
foreach ($formats as $format => $name) {
|
||||
// Create a format and add an editor to this format.
|
||||
$this->addEditorToNewFormat($format, $name);
|
||||
// Add permission for this format.
|
||||
$permissions[] = "use text format $format";
|
||||
}
|
||||
|
||||
// Create a node having the body format value 'moncerus'.
|
||||
$node = Node::create([
|
||||
'type' => $node_type->id(),
|
||||
'title' => $this->randomString(),
|
||||
]);
|
||||
$node->body->value = $this->randomString(100);
|
||||
$node->body->format = 'monocerus';
|
||||
$node->save();
|
||||
|
||||
// Log in as an user able to use both formats and edit nodes of created type.
|
||||
$account = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// The node edit page header.
|
||||
$text = (string) new FormattableMarkup('<em>Edit @type</em> @title', ['@type' => $node_type->label(), '@title' => $node->label()]);
|
||||
|
||||
// Go to node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertRaw($text);
|
||||
|
||||
// Disable the format assigned to the 'body' field of the node.
|
||||
FilterFormat::load('monocerus')->disable()->save();
|
||||
|
||||
// Edit again the node.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertRaw($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an editor to a new format using the UI.
|
||||
*
|
||||
* @param string $format_id
|
||||
* The format id.
|
||||
* @param string $format_name
|
||||
* The format name.
|
||||
*/
|
||||
protected function addEditorToNewFormat($format_id, $format_name) {
|
||||
$this->enableUnicornEditor();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/config/content/formats/add');
|
||||
// Configure the text format name.
|
||||
$edit = [
|
||||
'name' => $format_name,
|
||||
'format' => $format_id,
|
||||
];
|
||||
$edit += $this->selectUnicornEditor();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the unicorn editor.
|
||||
*/
|
||||
protected function enableUnicornEditor() {
|
||||
if (!$this->container->get('module_handler')->moduleExists('editor_test')) {
|
||||
$this->container->get('module_installer')->install(['editor_test']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests and selects the unicorn editor.
|
||||
*
|
||||
* @return array
|
||||
* Returns an edit array containing the values to be posted.
|
||||
*/
|
||||
protected function selectUnicornEditor() {
|
||||
// Verify the <select> when a text editor is available.
|
||||
$select = $this->xpath('//select[@name="editor[editor]"]');
|
||||
$select_is_disabled = $this->xpath('//select[@name="editor[editor]" and @disabled="disabled"]');
|
||||
$options = $this->xpath('//select[@name="editor[editor]"]/option');
|
||||
$this->assertTrue(count($select) === 1, 'The Text Editor select exists.');
|
||||
$this->assertTrue(count($select_is_disabled) === 0, 'The Text Editor select is not disabled.');
|
||||
$this->assertTrue(count($options) === 2, 'The Text Editor select has two options.');
|
||||
$this->assertTrue(($options[0]->getText()) === 'None', 'Option 1 in the Text Editor select is "None".');
|
||||
$this->assertTrue(($options[1]->getText()) === 'Unicorn Editor', 'Option 2 in the Text Editor select is "Unicorn Editor".');
|
||||
$this->assertTrue($options[0]->hasAttribute('selected'), 'Option 1 ("None") is selected.');
|
||||
// Ensure the none option is selected.
|
||||
$this->assertNoRaw('This option is disabled because no modules that provide a text editor are currently enabled.', 'Description for select absent that tells users to install a text editor module.');
|
||||
|
||||
// Select the "Unicorn Editor" editor and click the "Configure" button.
|
||||
$edit = [
|
||||
'editor[editor]' => 'unicorn',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, 'Configure');
|
||||
$unicorn_setting = $this->xpath('//input[@name="editor[settings][ponies_too]" and @type="checkbox" and @checked]');
|
||||
$this->assertTrue(count($unicorn_setting), "Unicorn Editor's settings form is present.");
|
||||
|
||||
return $edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies unicorn editor configuration.
|
||||
*
|
||||
* @param string $format_id
|
||||
* The format machine name.
|
||||
* @param bool $ponies_too
|
||||
* The expected value of the ponies_too setting.
|
||||
*/
|
||||
protected function verifyUnicornEditorConfiguration($format_id, $ponies_too = TRUE) {
|
||||
$editor = editor_load($format_id);
|
||||
$settings = $editor->getSettings();
|
||||
$this->assertIdentical($editor->getEditor(), 'unicorn', 'The text editor is configured correctly.');
|
||||
$this->assertIdentical($settings['ponies_too'], $ponies_too, 'The text editor settings are stored correctly.');
|
||||
$this->drupalGet('admin/config/content/formats/manage/' . $format_id);
|
||||
$select = $this->xpath('//select[@name="editor[editor]"]');
|
||||
$select_is_disabled = $this->xpath('//select[@name="editor[editor]" and @disabled="disabled"]');
|
||||
$options = $this->xpath('//select[@name="editor[editor]"]/option');
|
||||
$this->assertTrue(count($select) === 1, 'The Text Editor select exists.');
|
||||
$this->assertTrue(count($select_is_disabled) === 0, 'The Text Editor select is not disabled.');
|
||||
$this->assertTrue(count($options) === 2, 'The Text Editor select has two options.');
|
||||
$this->assertTrue($options[1]->hasAttribute('selected'), 'Option 2 ("Unicorn Editor") is selected.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests loading of text editors.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorLoadingTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['filter', 'editor', 'editor_test', 'node'];
|
||||
|
||||
/**
|
||||
* An untrusted user, with access to the 'plain_text' format.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $untrustedUser;
|
||||
|
||||
/**
|
||||
* A normal user with additional access to the 'filtered_html' format.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $normalUser;
|
||||
|
||||
/**
|
||||
* A privileged user with additional access to the 'full_html' format.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $privilegedUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Let there be T-rex.
|
||||
\Drupal::state()->set('editor_test_give_me_a_trex_thanks', TRUE);
|
||||
\Drupal::service('plugin.manager.editor')->clearCachedDefinitions();
|
||||
|
||||
// Add text formats.
|
||||
$filtered_html_format = FilterFormat::create([
|
||||
'format' => 'filtered_html',
|
||||
'name' => 'Filtered HTML',
|
||||
'weight' => 0,
|
||||
'filters' => [],
|
||||
]);
|
||||
$filtered_html_format->save();
|
||||
$full_html_format = FilterFormat::create([
|
||||
'format' => 'full_html',
|
||||
'name' => 'Full HTML',
|
||||
'weight' => 1,
|
||||
'filters' => [],
|
||||
]);
|
||||
$full_html_format->save();
|
||||
|
||||
// Create article node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'article',
|
||||
'name' => 'Article',
|
||||
]);
|
||||
|
||||
// Create page node type, but remove the body.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'page',
|
||||
'name' => 'Page',
|
||||
]);
|
||||
$body = FieldConfig::loadByName('node', 'page', 'body');
|
||||
$body->delete();
|
||||
|
||||
// Create a formatted text field, which uses an <input type="text">.
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => 'field_text',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => 'field_text',
|
||||
'entity_type' => 'node',
|
||||
'label' => 'Textfield',
|
||||
'bundle' => 'page',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent('field_text')
|
||||
->save();
|
||||
|
||||
// Create 3 users, each with access to different text formats.
|
||||
$this->untrustedUser = $this->drupalCreateUser(['create article content', 'edit any article content']);
|
||||
$this->normalUser = $this->drupalCreateUser(['create article content', 'edit any article content', 'use text format filtered_html']);
|
||||
$this->privilegedUser = $this->drupalCreateUser(['create article content', 'edit any article content', 'create page content', 'edit any page content', 'use text format filtered_html', 'use text format full_html']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests loading of text editors.
|
||||
*/
|
||||
public function testLoading() {
|
||||
// Only associate a text editor with the "Full HTML" text format.
|
||||
$editor = Editor::create([
|
||||
'format' => 'full_html',
|
||||
'editor' => 'unicorn',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'scheme' => file_default_scheme(),
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => ['width' => '', 'height' => ''],
|
||||
],
|
||||
]);
|
||||
$editor->save();
|
||||
|
||||
// The normal user:
|
||||
// - has access to 2 text formats;
|
||||
// - doesn't have access to the full_html text format, so: no text editor.
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
list(, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$this->assertFalse($editor_settings_present, 'No Text Editor module settings.');
|
||||
$this->assertFalse($editor_js_present, 'No Text Editor JavaScript.');
|
||||
$this->assertTrue(count($body) === 1, 'A body field exists.');
|
||||
$this->assertTrue(count($format_selector) === 0, 'No text format selector exists on the page because the user only has access to a single format.');
|
||||
$this->drupalLogout($this->normalUser);
|
||||
|
||||
// The privileged user:
|
||||
// - has access to 2 text formats (and the fallback format);
|
||||
// - does have access to the full_html text format, so: Unicorn text editor.
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$expected = [
|
||||
'formats' => [
|
||||
'full_html' => [
|
||||
'format' => 'full_html',
|
||||
'editor' => 'unicorn',
|
||||
'editorSettings' => ['ponyModeEnabled' => TRUE],
|
||||
'editorSupportsContentFiltering' => TRUE,
|
||||
'isXssSafe' => FALSE,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
|
||||
$this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
|
||||
$this->assertTrue(count($body) === 1, 'A body field exists.');
|
||||
$this->assertTrue(count($format_selector) === 1, 'A single text format selector exists on the page.');
|
||||
$specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and @data-editor-for="edit-body-0-value"]');
|
||||
$this->assertTrue(count($specific_format_selector) === 1, 'A single text format selector exists on the page and has a "data-editor-for" attribute with the correct value.');
|
||||
|
||||
// Load the editor image dialog form and make sure it does not fatal.
|
||||
$this->drupalGet('editor/dialog/image/full_html');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalLogout($this->privilegedUser);
|
||||
|
||||
// Also associate a text editor with the "Plain Text" text format.
|
||||
$editor = Editor::create([
|
||||
'format' => 'plain_text',
|
||||
'editor' => 'unicorn',
|
||||
]);
|
||||
$editor->save();
|
||||
|
||||
// The untrusted user:
|
||||
// - has access to 1 text format (plain_text);
|
||||
// - has access to the plain_text text format, so: Unicorn text editor.
|
||||
$this->drupalLogin($this->untrustedUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$expected = [
|
||||
'formats' => [
|
||||
'plain_text' => [
|
||||
'format' => 'plain_text',
|
||||
'editor' => 'unicorn',
|
||||
'editorSettings' => ['ponyModeEnabled' => TRUE],
|
||||
'editorSupportsContentFiltering' => TRUE,
|
||||
'isXssSafe' => FALSE,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
|
||||
$this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
|
||||
$this->assertTrue(count($body) === 1, 'A body field exists.');
|
||||
$this->assertTrue(count($format_selector) === 0, 'No text format selector exists on the page.');
|
||||
$hidden_input = $this->xpath('//input[@type="hidden" and @value="plain_text" and @data-editor-for="edit-body-0-value"]');
|
||||
$this->assertTrue(count($hidden_input) === 1, 'A single text format hidden input exists on the page and has a "data-editor-for" attribute with the correct value.');
|
||||
|
||||
// Create an "article" node that uses the full_html text format, then try
|
||||
// to let the untrusted user edit it.
|
||||
$this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
['value' => $this->randomMachineName(32), 'format' => 'full_html'],
|
||||
],
|
||||
]);
|
||||
|
||||
// The untrusted user tries to edit content that is written in a text format
|
||||
// that (s)he is not allowed to use. The editor is still loaded. CKEditor,
|
||||
// for example, supports being loaded in a disabled state.
|
||||
$this->drupalGet('node/1/edit');
|
||||
list(, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$this->assertTrue($editor_settings_present, 'Text Editor module settings.');
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript.');
|
||||
$this->assertTrue(count($body) === 1, 'A body field exists.');
|
||||
$this->assertFieldByXPath('//textarea[@id="edit-body-0-value" and @disabled="disabled"]', t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
|
||||
$this->assertTrue(count($format_selector) === 0, 'No text format selector exists on the page.');
|
||||
$hidden_input = $this->xpath('//input[@type="hidden" and contains(@class, "editor")]');
|
||||
$this->assertTrue(count($hidden_input) === 0, 'A single text format hidden input does not exist on the page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test supported element types.
|
||||
*/
|
||||
public function testSupportedElementTypes() {
|
||||
// Associate the unicorn text editor with the "Full HTML" text format.
|
||||
$editor = Editor::create([
|
||||
'format' => 'full_html',
|
||||
'editor' => 'unicorn',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'scheme' => file_default_scheme(),
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => ['width' => '', 'height' => ''],
|
||||
],
|
||||
]);
|
||||
$editor->save();
|
||||
|
||||
// Create an "page" node that uses the full_html text format.
|
||||
$this->drupalCreateNode([
|
||||
'type' => 'page',
|
||||
'field_text' => [
|
||||
['value' => $this->randomMachineName(32), 'format' => 'full_html'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Assert the unicorn editor works with textfields.
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
$this->drupalGet('node/1/edit');
|
||||
list(, $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input');
|
||||
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
|
||||
$this->assertTrue(count($field) === 1, 'A text field exists.');
|
||||
$this->assertTrue(count($format_selector) === 1, 'A single text format selector exists on the page.');
|
||||
$specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and contains(@class, "editor") and @data-editor-for="edit-field-text-0-value"]');
|
||||
$this->assertTrue(count($specific_format_selector) === 1, 'A single text format selector exists on the page and has the "editor" class and a "data-editor-for" attribute with the correct value.');
|
||||
|
||||
// Associate the trex text editor with the "Full HTML" text format.
|
||||
$editor->delete();
|
||||
Editor::create([
|
||||
'format' => 'full_html',
|
||||
'editor' => 'trex',
|
||||
])->save();
|
||||
|
||||
$this->drupalGet('node/1/edit');
|
||||
list(, $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input');
|
||||
$this->assertFalse($editor_settings_present, "Text Editor module's JavaScript settings are not on the page.");
|
||||
$this->assertFalse($editor_js_present, 'Text Editor JavaScript is not present.');
|
||||
$this->assertTrue(count($field) === 1, 'A text field exists.');
|
||||
$this->assertTrue(count($format_selector) === 1, 'A single text format selector exists on the page.');
|
||||
$specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and contains(@class, "editor") and @data-editor-for="edit-field-text-0-value"]');
|
||||
$this->assertFalse(count($specific_format_selector) === 1, 'A single text format selector exists on the page and has the "editor" class and a "data-editor-for" attribute with the correct value.');
|
||||
}
|
||||
|
||||
protected function getThingsToCheck($field_name, $type = 'textarea') {
|
||||
$settings = $this->getDrupalSettings();
|
||||
return [
|
||||
// JavaScript settings.
|
||||
$settings,
|
||||
// Editor.module's JS settings present.
|
||||
isset($settings['editor']),
|
||||
// Editor.module's JS present.
|
||||
strpos($this->getSession()->getPage()->getContent(), drupal_get_path('module', 'editor') . '/js/editor.js') !== FALSE,
|
||||
// Body field.
|
||||
$this->xpath('//' . $type . '[@id="edit-' . $field_name . '-0-value"]'),
|
||||
// Format selector.
|
||||
$this->xpath('//select[contains(@class, "filter-list")]'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -112,7 +112,7 @@ class EditorPrivateFileReferenceFilterTest extends BrowserTestBase {
|
|||
|
||||
// When the published node is also unpublished, the image should also
|
||||
// become inaccessible to anonymous users.
|
||||
$published_node->setPublished(FALSE)->save();
|
||||
$published_node->setUnpublished()->save();
|
||||
|
||||
$this->drupalGet($published_node->toUrl());
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
@ -121,7 +121,7 @@ class EditorPrivateFileReferenceFilterTest extends BrowserTestBase {
|
|||
|
||||
// Disallow anonymous users to view the entity, which then should also
|
||||
// disallow them to view the image.
|
||||
$published_node->setPublished(TRUE)->save();
|
||||
$published_node->setPublished()->save();
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->revokePermission('access content')
|
||||
->save();
|
||||
|
|
|
@ -0,0 +1,453 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests XSS protection for content creators when using text editors.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorSecurityTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The sample content to use in all tests.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $sampleContent = '<p style="color: red">Hello, Dumbo Octopus!</p><script>alert(0)</script><embed type="image/svg+xml" src="image.svg" />';
|
||||
|
||||
/**
|
||||
* The secured sample content to use in most tests.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $sampleContentSecured = '<p>Hello, Dumbo Octopus!</p>alert(0)';
|
||||
|
||||
/**
|
||||
* The secured sample content to use in tests when the <embed> tag is allowed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $sampleContentSecuredEmbedAllowed = '<p>Hello, Dumbo Octopus!</p>alert(0)<embed type="image/svg+xml" src="image.svg" />';
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['filter', 'editor', 'editor_test', 'node'];
|
||||
|
||||
/**
|
||||
* User with access to Restricted HTML text format without text editor.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $untrustedUser;
|
||||
|
||||
/**
|
||||
* User with access to Restricted HTML text format with text editor.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $normalUser;
|
||||
|
||||
/**
|
||||
* User with access to Restricted HTML text format, dangerous tags allowed
|
||||
* with text editor.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $trustedUser;
|
||||
|
||||
/**
|
||||
* User with access to all text formats and text editors.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $privilegedUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create 5 text formats, to cover all potential use cases:
|
||||
// 1. restricted_without_editor (untrusted: anonymous)
|
||||
// 2. restricted_with_editor (normal: authenticated)
|
||||
// 3. restricted_plus_dangerous_tag_with_editor (privileged: trusted)
|
||||
// 4. unrestricted_without_editor (privileged: admin)
|
||||
// 5. unrestricted_with_editor (privileged: admin)
|
||||
// With text formats 2, 3 and 5, we also associate a text editor that does
|
||||
// not guarantee XSS safety. "restricted" means the text format has XSS
|
||||
// filters on output, "unrestricted" means the opposite.
|
||||
$format = FilterFormat::create([
|
||||
'format' => 'restricted_without_editor',
|
||||
'name' => 'Restricted HTML, without text editor',
|
||||
'weight' => 0,
|
||||
'filters' => [
|
||||
// A filter of the FilterInterface::TYPE_HTML_RESTRICTOR type.
|
||||
'filter_html' => [
|
||||
'status' => 1,
|
||||
'settings' => [
|
||||
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$format->save();
|
||||
$format = FilterFormat::create([
|
||||
'format' => 'restricted_with_editor',
|
||||
'name' => 'Restricted HTML, with text editor',
|
||||
'weight' => 1,
|
||||
'filters' => [
|
||||
// A filter of the FilterInterface::TYPE_HTML_RESTRICTOR type.
|
||||
'filter_html' => [
|
||||
'status' => 1,
|
||||
'settings' => [
|
||||
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$format->save();
|
||||
$editor = Editor::create([
|
||||
'format' => 'restricted_with_editor',
|
||||
'editor' => 'unicorn',
|
||||
]);
|
||||
$editor->save();
|
||||
$format = FilterFormat::create([
|
||||
'format' => 'restricted_plus_dangerous_tag_with_editor',
|
||||
'name' => 'Restricted HTML, dangerous tag allowed, with text editor',
|
||||
'weight' => 1,
|
||||
'filters' => [
|
||||
// A filter of the FilterInterface::TYPE_HTML_RESTRICTOR type.
|
||||
'filter_html' => [
|
||||
'status' => 1,
|
||||
'settings' => [
|
||||
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a> <embed>',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$format->save();
|
||||
$editor = Editor::create([
|
||||
'format' => 'restricted_plus_dangerous_tag_with_editor',
|
||||
'editor' => 'unicorn',
|
||||
]);
|
||||
$editor->save();
|
||||
$format = FilterFormat::create([
|
||||
'format' => 'unrestricted_without_editor',
|
||||
'name' => 'Unrestricted HTML, without text editor',
|
||||
'weight' => 0,
|
||||
'filters' => [],
|
||||
]);
|
||||
$format->save();
|
||||
$format = FilterFormat::create([
|
||||
'format' => 'unrestricted_with_editor',
|
||||
'name' => 'Unrestricted HTML, with text editor',
|
||||
'weight' => 1,
|
||||
'filters' => [],
|
||||
]);
|
||||
$format->save();
|
||||
$editor = Editor::create([
|
||||
'format' => 'unrestricted_with_editor',
|
||||
'editor' => 'unicorn',
|
||||
]);
|
||||
$editor->save();
|
||||
|
||||
// Create node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'article',
|
||||
'name' => 'Article',
|
||||
]);
|
||||
|
||||
// Create 4 users, each with access to different text formats/editors:
|
||||
// - "untrusted": restricted_without_editor
|
||||
// - "normal": restricted_with_editor,
|
||||
// - "trusted": restricted_plus_dangerous_tag_with_editor
|
||||
// - "privileged": restricted_without_editor, restricted_with_editor,
|
||||
// restricted_plus_dangerous_tag_with_editor,
|
||||
// unrestricted_without_editor and unrestricted_with_editor
|
||||
$this->untrustedUser = $this->drupalCreateUser([
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'use text format restricted_without_editor',
|
||||
]);
|
||||
$this->normalUser = $this->drupalCreateUser([
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'use text format restricted_with_editor',
|
||||
]);
|
||||
$this->trustedUser = $this->drupalCreateUser([
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'use text format restricted_plus_dangerous_tag_with_editor',
|
||||
]);
|
||||
$this->privilegedUser = $this->drupalCreateUser([
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'use text format restricted_without_editor',
|
||||
'use text format restricted_with_editor',
|
||||
'use text format restricted_plus_dangerous_tag_with_editor',
|
||||
'use text format unrestricted_without_editor',
|
||||
'use text format unrestricted_with_editor',
|
||||
]);
|
||||
|
||||
// Create an "article" node for each possible text format, with the same
|
||||
// sample content, to do our tests on.
|
||||
$samples = [
|
||||
['author' => $this->untrustedUser->id(), 'format' => 'restricted_without_editor'],
|
||||
['author' => $this->normalUser->id(), 'format' => 'restricted_with_editor'],
|
||||
['author' => $this->trustedUser->id(), 'format' => 'restricted_plus_dangerous_tag_with_editor'],
|
||||
['author' => $this->privilegedUser->id(), 'format' => 'unrestricted_without_editor'],
|
||||
['author' => $this->privilegedUser->id(), 'format' => 'unrestricted_with_editor'],
|
||||
];
|
||||
foreach ($samples as $sample) {
|
||||
$this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
['value' => self::$sampleContent, 'format' => $sample['format']],
|
||||
],
|
||||
'uid' => $sample['author'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests initial security: is the user safe without switching text formats?
|
||||
*
|
||||
* Tests 8 scenarios. Tests only with a text editor that is not XSS-safe.
|
||||
*/
|
||||
public function testInitialSecurity() {
|
||||
$expected = [
|
||||
[
|
||||
'node_id' => 1,
|
||||
'format' => 'restricted_without_editor',
|
||||
// No text editor => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'users' => [
|
||||
$this->untrustedUser,
|
||||
$this->privilegedUser,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 2,
|
||||
'format' => 'restricted_with_editor',
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecured,
|
||||
'users' => [
|
||||
$this->normalUser,
|
||||
$this->privilegedUser,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 3,
|
||||
'format' => 'restricted_plus_dangerous_tag_with_editor',
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecuredEmbedAllowed,
|
||||
'users' => [
|
||||
$this->trustedUser,
|
||||
$this->privilegedUser,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 4,
|
||||
'format' => 'unrestricted_without_editor',
|
||||
// No text editor => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'users' => [
|
||||
$this->privilegedUser,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 5,
|
||||
'format' => 'unrestricted_with_editor',
|
||||
// Text editor, no security filter => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'users' => [
|
||||
$this->privilegedUser,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Log in as each user that may edit the content, and assert the value.
|
||||
foreach ($expected as $case) {
|
||||
foreach ($case['users'] as $account) {
|
||||
$this->pass(format_string('Scenario: sample %sample_id, %format.', [
|
||||
'%sample_id' => $case['node_id'],
|
||||
'%format' => $case['format'],
|
||||
]));
|
||||
$this->drupalLogin($account);
|
||||
$this->drupalGet('node/' . $case['node_id'] . '/edit');
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical($case['value'], $dom_node[0]->getText(), 'The value was correctly filtered for XSS attack vectors.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests administrator security: is the user safe when switching text formats?
|
||||
*
|
||||
* Tests 24 scenarios. Tests only with a text editor that is not XSS-safe.
|
||||
*
|
||||
* When changing from a more restrictive text format with a text editor (or a
|
||||
* text format without a text editor) to a less restrictive text format, it is
|
||||
* possible that a malicious user could trigger an XSS.
|
||||
*
|
||||
* E.g. when switching a piece of text that uses the Restricted HTML text
|
||||
* format and contains a <script> tag to the Full HTML text format, the
|
||||
* <script> tag would be executed. Unless we apply appropriate filtering.
|
||||
*/
|
||||
public function testSwitchingSecurity() {
|
||||
$expected = [
|
||||
[
|
||||
'node_id' => 1,
|
||||
// No text editor => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'format' => 'restricted_without_editor',
|
||||
'switch_to' => [
|
||||
'restricted_with_editor' => self::$sampleContentSecured,
|
||||
// Intersection of restrictions => most strict XSS filtering.
|
||||
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
|
||||
// No text editor => no XSS filtering.
|
||||
'unrestricted_without_editor' => FALSE,
|
||||
'unrestricted_with_editor' => self::$sampleContentSecured,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 2,
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecured,
|
||||
'format' => 'restricted_with_editor',
|
||||
'switch_to' => [
|
||||
// No text editor => no XSS filtering.
|
||||
'restricted_without_editor' => FALSE,
|
||||
// Intersection of restrictions => most strict XSS filtering.
|
||||
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
|
||||
// No text editor => no XSS filtering.
|
||||
'unrestricted_without_editor' => FALSE,
|
||||
'unrestricted_with_editor' => self::$sampleContentSecured,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 3,
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecuredEmbedAllowed,
|
||||
'format' => 'restricted_plus_dangerous_tag_with_editor',
|
||||
'switch_to' => [
|
||||
// No text editor => no XSS filtering.
|
||||
'restricted_without_editor' => FALSE,
|
||||
// Intersection of restrictions => most strict XSS filtering.
|
||||
'restricted_with_editor' => self::$sampleContentSecured,
|
||||
// No text editor => no XSS filtering.
|
||||
'unrestricted_without_editor' => FALSE,
|
||||
// Intersection of restrictions => most strict XSS filtering.
|
||||
'unrestricted_with_editor' => self::$sampleContentSecured,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 4,
|
||||
// No text editor => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'format' => 'unrestricted_without_editor',
|
||||
'switch_to' => [
|
||||
// No text editor => no XSS filtering.
|
||||
'restricted_without_editor' => FALSE,
|
||||
'restricted_with_editor' => self::$sampleContentSecured,
|
||||
// Intersection of restrictions => most strict XSS filtering.
|
||||
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
|
||||
// From no editor, no security filters, to editor, still no security
|
||||
// filters: resulting content when viewed was already vulnerable, so
|
||||
// it must be intentional.
|
||||
'unrestricted_with_editor' => FALSE,
|
||||
],
|
||||
],
|
||||
[
|
||||
'node_id' => 5,
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecured,
|
||||
'format' => 'unrestricted_with_editor',
|
||||
'switch_to' => [
|
||||
// From editor, no security filters to security filters, no editor: no
|
||||
// risk.
|
||||
'restricted_without_editor' => FALSE,
|
||||
'restricted_with_editor' => self::$sampleContentSecured,
|
||||
// Intersection of restrictions => most strict XSS filtering.
|
||||
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
|
||||
// From no editor, no security filters, to editor, still no security
|
||||
// filters: resulting content when viewed was already vulnerable, so
|
||||
// it must be intentional.
|
||||
'unrestricted_without_editor' => FALSE,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Log in as the privileged user, and for every sample, do the following:
|
||||
// - switch to every other text format/editor
|
||||
// - assert the XSS-filtered values that we get from the server
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
$cookies = $this->getSessionCookies();
|
||||
|
||||
foreach ($expected as $case) {
|
||||
$this->drupalGet('node/' . $case['node_id'] . '/edit');
|
||||
|
||||
// Verify data- attributes.
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical(self::$sampleContent, $dom_node[0]->getAttribute('data-editor-value-original'), 'The data-editor-value-original attribute is correctly set.');
|
||||
$this->assertIdentical('false', (string) $dom_node[0]->getAttribute('data-editor-value-is-changed'), 'The data-editor-value-is-changed attribute is correctly set.');
|
||||
|
||||
// Switch to every other text format/editor and verify the results.
|
||||
foreach ($case['switch_to'] as $format => $expected_filtered_value) {
|
||||
$this->pass(format_string('Scenario: sample %sample_id, switch from %original_format to %format.', [
|
||||
'%sample_id' => $case['node_id'],
|
||||
'%original_format' => $case['format'],
|
||||
'%format' => $format,
|
||||
]));
|
||||
|
||||
$post = [
|
||||
'value' => self::$sampleContent,
|
||||
'original_format_id' => $case['format'],
|
||||
];
|
||||
$client = $this->getHttpClient();
|
||||
$response = $client->post($this->buildUrl('/editor/filter_xss/' . $format), [
|
||||
'body' => http_build_query($post),
|
||||
'cookies' => $cookies,
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$json = Json::decode($response->getBody());
|
||||
$this->assertIdentical($json, $expected_filtered_value, 'The value was correctly filtered for XSS attack vectors.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the standard text editor XSS filter being overridden.
|
||||
*/
|
||||
public function testEditorXssFilterOverride() {
|
||||
// First: the Standard text editor XSS filter.
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('node/2/edit');
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical(self::$sampleContentSecured, $dom_node[0]->getText(), 'The value was filtered by the Standard text editor XSS filter.');
|
||||
|
||||
// Enable editor_test.module's hook_editor_xss_filter_alter() implementation
|
||||
// to alter the text editor XSS filter class being used.
|
||||
\Drupal::state()->set('editor_test_editor_xss_filter_alter_enabled', TRUE);
|
||||
|
||||
// First: the Insecure text editor XSS filter.
|
||||
$this->drupalGet('node/2/edit');
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical(self::$sampleContent, $dom_node[0]->getText(), 'The value was filtered by the Insecure text editor XSS filter.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests scaling of inline images.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorUploadImageScaleTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['editor', 'editor_test'];
|
||||
|
||||
/**
|
||||
* A user with permission as administer for testing.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Add text format.
|
||||
FilterFormat::create([
|
||||
'format' => 'basic_html',
|
||||
'name' => 'Basic HTML',
|
||||
'weight' => 0,
|
||||
])->save();
|
||||
|
||||
// Set up text editor.
|
||||
Editor::create([
|
||||
'format' => 'basic_html',
|
||||
'editor' => 'unicorn',
|
||||
'image_upload' => [
|
||||
'status' => TRUE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Create admin user.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer filters', 'use text format basic_html']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests scaling of inline images.
|
||||
*/
|
||||
public function testEditorUploadImageScale() {
|
||||
// Generate testing images.
|
||||
$testing_image_list = $this->getTestFiles('image');
|
||||
|
||||
// Case 1: no max dimensions set: uploaded image not scaled.
|
||||
$test_image = $testing_image_list[0];
|
||||
list($image_file_width, $image_file_height) = $this->getTestImageInfo($test_image->uri);
|
||||
$max_width = NULL;
|
||||
$max_height = NULL;
|
||||
$this->setMaxDimensions($max_width, $max_height);
|
||||
$this->assertSavedMaxDimensions($max_width, $max_height);
|
||||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $image_file_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $image_file_height);
|
||||
$this->assertNoRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 2: max width smaller than uploaded image: image scaled down.
|
||||
$test_image = $testing_image_list[1];
|
||||
list($image_file_width, $image_file_height) = $this->getTestImageInfo($test_image->uri);
|
||||
$max_width = $image_file_width - 5;
|
||||
$max_height = $image_file_height;
|
||||
$this->setMaxDimensions($max_width, $max_height);
|
||||
$this->assertSavedMaxDimensions($max_width, $max_height);
|
||||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $max_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $uploaded_image_file_height * ($uploaded_image_file_width / $max_width));
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 3: max height smaller than uploaded image: image scaled down.
|
||||
$test_image = $testing_image_list[2];
|
||||
list($image_file_width, $image_file_height) = $this->getTestImageInfo($test_image->uri);
|
||||
$max_width = $image_file_width;
|
||||
$max_height = $image_file_height - 5;
|
||||
$this->setMaxDimensions($max_width, $max_height);
|
||||
$this->assertSavedMaxDimensions($max_width, $max_height);
|
||||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $uploaded_image_file_width * ($uploaded_image_file_height / $max_height));
|
||||
$this->assertEqual($uploaded_image_file_height, $max_height);
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 4: max dimensions greater than uploaded image: image not scaled.
|
||||
$test_image = $testing_image_list[3];
|
||||
list($image_file_width, $image_file_height) = $this->getTestImageInfo($test_image->uri);
|
||||
$max_width = $image_file_width + 5;
|
||||
$max_height = $image_file_height + 5;
|
||||
$this->setMaxDimensions($max_width, $max_height);
|
||||
$this->assertSavedMaxDimensions($max_width, $max_height);
|
||||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $image_file_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $image_file_height);
|
||||
$this->assertNoRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 5: only max width dimension was provided and it was smaller than
|
||||
// uploaded image: image scaled down.
|
||||
$test_image = $testing_image_list[4];
|
||||
list($image_file_width, $image_file_height) = $this->getTestImageInfo($test_image->uri);
|
||||
$max_width = $image_file_width - 5;
|
||||
$max_height = NULL;
|
||||
$this->setMaxDimensions($max_width, $max_height);
|
||||
$this->assertSavedMaxDimensions($max_width, $max_height);
|
||||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $max_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $uploaded_image_file_height * ($uploaded_image_file_width / $max_width));
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed width of %width pixels.', ['%width' => $max_width]));
|
||||
|
||||
// Case 6: only max height dimension was provided and it was smaller than
|
||||
// uploaded image: image scaled down.
|
||||
$test_image = $testing_image_list[5];
|
||||
list($image_file_width, $image_file_height) = $this->getTestImageInfo($test_image->uri);
|
||||
$max_width = NULL;
|
||||
$max_height = $image_file_height - 5;
|
||||
$this->setMaxDimensions($max_width, $max_height);
|
||||
$this->assertSavedMaxDimensions($max_width, $max_height);
|
||||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $uploaded_image_file_width * ($uploaded_image_file_height / $max_height));
|
||||
$this->assertEqual($uploaded_image_file_height, $max_height);
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed height of %height pixels.', ['%height' => $max_height]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dimensions of an uploaded image.
|
||||
*
|
||||
* @param string $uri
|
||||
* The URI of the image.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the uploaded image's width and height.
|
||||
*/
|
||||
protected function getTestImageInfo($uri) {
|
||||
$image_file = $this->container->get('image.factory')->get($uri);
|
||||
return [
|
||||
(int) $image_file->getWidth(),
|
||||
(int) $image_file->getHeight(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum dimensions and saves the configuration.
|
||||
*
|
||||
* @param string|int $width
|
||||
* The width of the image.
|
||||
* @param string|int $height
|
||||
* The height of the image.
|
||||
*/
|
||||
protected function setMaxDimensions($width, $height) {
|
||||
$editor = Editor::load('basic_html');
|
||||
$image_upload_settings = $editor->getImageUploadSettings();
|
||||
$image_upload_settings['max_dimensions']['width'] = $width;
|
||||
$image_upload_settings['max_dimensions']['height'] = $height;
|
||||
$editor->setImageUploadSettings($image_upload_settings);
|
||||
$editor->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image via the editor dialog.
|
||||
*
|
||||
* @param string $uri
|
||||
* The URI of the image.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the uploaded image's width and height.
|
||||
*/
|
||||
protected function uploadImage($uri) {
|
||||
$edit = [
|
||||
'files[fid]' => \Drupal::service('file_system')->realpath($uri),
|
||||
];
|
||||
$this->drupalGet('editor/dialog/image/basic_html');
|
||||
$this->drupalPostForm('editor/dialog/image/basic_html', $edit, t('Upload'));
|
||||
$uploaded_image_file = $this->container->get('image.factory')->get('public://inline-images/' . basename($uri));
|
||||
return [
|
||||
(int) $uploaded_image_file->getWidth(),
|
||||
(int) $uploaded_image_file->getHeight(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts whether the saved maximum dimensions equal the ones provided.
|
||||
*
|
||||
* @param string $width
|
||||
* The expected width of the uploaded image.
|
||||
* @param string $height
|
||||
* The expected height of the uploaded image.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function assertSavedMaxDimensions($width, $height) {
|
||||
$image_upload_settings = Editor::load('basic_html')->getImageUploadSettings();
|
||||
$expected = [
|
||||
'width' => $image_upload_settings['max_dimensions']['width'],
|
||||
'height' => $image_upload_settings['max_dimensions']['height'],
|
||||
];
|
||||
$same_width = $this->assertEqual($width, $expected['width'], 'Actual width of "' . $width . '" equals the expected width of "' . $expected['width'] . '"');
|
||||
$same_height = $this->assertEqual($height, $expected['height'], 'Actual height of "' . $height . '" equals the expected width of "' . $expected['height'] . '"');
|
||||
return $same_width && $same_height;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class EditorHalJsonAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class EditorHalJsonBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class EditorHalJsonCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests Quick Edit module integration endpoints.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class QuickEditIntegrationLoadingTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['quickedit', 'filter', 'node', 'editor'];
|
||||
|
||||
/**
|
||||
* The basic permissions necessary to view content and use in-place editing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $basicPermissions = ['access content', 'create article content', 'use text format filtered_html', 'access contextual links'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a text format.
|
||||
$filtered_html_format = FilterFormat::create([
|
||||
'format' => 'filtered_html',
|
||||
'name' => 'Filtered HTML',
|
||||
'weight' => 0,
|
||||
'filters' => [
|
||||
'filter_caption' => [
|
||||
'status' => 1,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$filtered_html_format->save();
|
||||
|
||||
// Create a node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'article',
|
||||
'name' => 'Article',
|
||||
]);
|
||||
|
||||
// Create one node of the above node type using the above text format.
|
||||
$this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
0 => [
|
||||
'value' => '<p>Do you also love Drupal?</p><img src="druplicon.png" data-caption="Druplicon" />',
|
||||
'format' => 'filtered_html',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading of untransformed text when a user doesn't have access to it.
|
||||
*/
|
||||
public function testUsersWithoutPermission() {
|
||||
// Create 3 users, each with insufficient permissions, i.e. without either
|
||||
// or both of the following permissions:
|
||||
// - the 'access in-place editing' permission
|
||||
// - the 'edit any article content' permission (necessary to edit node 1)
|
||||
$users = [
|
||||
$this->drupalCreateUser(static::$basicPermissions),
|
||||
$this->drupalCreateUser(array_merge(static::$basicPermissions, ['edit any article content'])),
|
||||
$this->drupalCreateUser(array_merge(static::$basicPermissions, ['access in-place editing'])),
|
||||
];
|
||||
|
||||
// Now test with each of the 3 users with insufficient permissions.
|
||||
foreach ($users as $user) {
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
// Ensure the text is transformed.
|
||||
$this->assertRaw('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>');
|
||||
|
||||
$client = $this->getHttpClient();
|
||||
|
||||
// Retrieving the untransformed text should result in an 403 response and
|
||||
// return a different error message depending of the missing permission.
|
||||
$response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [
|
||||
'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']),
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
if (!$user->hasPermission('access in-place editing')) {
|
||||
$message = "The 'access in-place editing' permission is required.";
|
||||
}
|
||||
else {
|
||||
$message = '';
|
||||
}
|
||||
|
||||
$body = Json::decode($response->getBody());
|
||||
$this->assertIdentical($message, $body['message']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading of untransformed text when a user does have access to it.
|
||||
*/
|
||||
public function testUserWithPermission() {
|
||||
$user = $this->drupalCreateUser(array_merge(static::$basicPermissions, ['edit any article content', 'access in-place editing']));
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
// Ensure the text is transformed.
|
||||
$this->assertRaw('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>');
|
||||
$client = $this->getHttpClient();
|
||||
$response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [
|
||||
'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']),
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$ajax_commands = Json::decode($response->getBody());
|
||||
$this->assertIdentical(1, count($ajax_commands), 'The untransformed text POST request results in one AJAX command.');
|
||||
$this->assertIdentical('editorGetUntransformedText', $ajax_commands[0]['command'], 'The first AJAX command is an editorGetUntransformedText command.');
|
||||
$this->assertIdentical('<p>Do you also love Drupal?</p><img src="druplicon.png" data-caption="Druplicon" />', $ajax_commands[0]['data'], 'The editorGetUntransformedText command contains the expected data.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for Editor entity.
|
||||
*/
|
||||
abstract class EditorResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ckeditor', 'editor'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'editor';
|
||||
|
||||
/**
|
||||
* The Editor entity.
|
||||
*
|
||||
* @var \Drupal\editor\EditorInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer filters']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Llama" filter format.
|
||||
$llama_format = FilterFormat::create([
|
||||
'name' => 'Llama',
|
||||
'format' => 'llama',
|
||||
'langcode' => 'es',
|
||||
'filters' => [
|
||||
'filter_html' => [
|
||||
'status' => TRUE,
|
||||
'settings' => [
|
||||
'allowed_html' => '<p> <a> <b> <lo>',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$llama_format->save();
|
||||
|
||||
// Create a "Camelids" editor.
|
||||
$camelids = Editor::create([
|
||||
'format' => 'llama',
|
||||
'editor' => 'ckeditor',
|
||||
]);
|
||||
$camelids
|
||||
->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'scheme' => file_default_scheme(),
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => '',
|
||||
'height' => '',
|
||||
],
|
||||
])
|
||||
->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [
|
||||
'config' => [
|
||||
'filter.format.llama',
|
||||
],
|
||||
'module' => [
|
||||
'ckeditor',
|
||||
],
|
||||
],
|
||||
'editor' => 'ckeditor',
|
||||
'format' => 'llama',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
],
|
||||
'langcode' => 'en',
|
||||
'settings' => [
|
||||
'toolbar' => [
|
||||
'rows' => [
|
||||
[
|
||||
[
|
||||
'name' => 'Formatting',
|
||||
'items' => [
|
||||
'Bold',
|
||||
'Italic',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Links',
|
||||
'items' => [
|
||||
'DrupalLink',
|
||||
'DrupalUnlink',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Lists',
|
||||
'items' => [
|
||||
'BulletedList',
|
||||
'NumberedList',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Media',
|
||||
'items' => [
|
||||
'Blockquote',
|
||||
'DrupalImage',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Tools',
|
||||
'items' => [
|
||||
'Source',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'plugins' => [
|
||||
'language' => [
|
||||
'language_list' => 'un',
|
||||
],
|
||||
],
|
||||
],
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
// @see ::createEntity()
|
||||
return ['user.permissions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer filters' permission is required.";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorXmlAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorXmlBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorXmlCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests Editor module database updates.
|
||||
*
|
||||
* @group editor
|
||||
* @group legacy
|
||||
*/
|
||||
class EditorUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
// Simulate an un-synchronized environment.
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.editor-editor_update_8001.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editor_update_8001().
|
||||
*
|
||||
* @see editor_update_8001()
|
||||
*/
|
||||
public function testEditorUpdate8001() {
|
||||
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
|
||||
$format_basic_html = $config_factory->get('filter.format.basic_html');
|
||||
$editor_basic_html = $config_factory->get('editor.editor.basic_html');
|
||||
$format_full_html = $config_factory->get('filter.format.full_html');
|
||||
$editor_full_html = $config_factory->get('editor.editor.full_html');
|
||||
|
||||
// Checks if the 'basic_html' format and editor statuses differ.
|
||||
$this->assertTrue($format_basic_html->get('status'));
|
||||
$this->assertFalse($editor_basic_html->get('status'));
|
||||
$this->assertNotIdentical($format_basic_html->get('status'), $editor_basic_html->get('status'));
|
||||
|
||||
// Checks if the 'full_html' format and editor statuses differ.
|
||||
$this->assertFalse($format_full_html->get('status'));
|
||||
$this->assertTrue($editor_full_html->get('status'));
|
||||
$this->assertNotIdentical($format_full_html->get('status'), $editor_full_html->get('status'));
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Reload text formats and editors.
|
||||
$format_basic_html = $config_factory->get('filter.format.basic_html');
|
||||
$editor_basic_html = $config_factory->get('editor.editor.basic_html');
|
||||
$format_full_html = $config_factory->get('filter.format.full_html');
|
||||
$editor_full_html = $config_factory->get('editor.editor.full_html');
|
||||
|
||||
// Checks if the 'basic_html' format and editor statuses are in sync.
|
||||
$this->assertTrue($format_basic_html->get('status'));
|
||||
$this->assertTrue($editor_basic_html->get('status'));
|
||||
$this->assertIdentical($format_basic_html->get('status'), $editor_basic_html->get('status'));
|
||||
|
||||
// Checks if the 'full_html' format and editor statuses are in sync.
|
||||
$this->assertFalse($format_full_html->get('status'));
|
||||
$this->assertFalse($editor_full_html->get('status'));
|
||||
$this->assertIdentical($format_full_html->get('status'), $editor_full_html->get('status'));
|
||||
}
|
||||
|
||||
}
|
|
@ -46,7 +46,7 @@ class EditorFileReferenceFilterTest extends KernelTestBase {
|
|||
public function testEditorFileReferenceFilter() {
|
||||
$filter = $this->filters['editor_file_reference'];
|
||||
|
||||
$test = function($input) use ($filter) {
|
||||
$test = function ($input) use ($filter) {
|
||||
return $filter->process($input, 'und');
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\Tests\editor\Kernel;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
@ -25,7 +24,7 @@ class EditorFilterIntegrationTest extends KernelTestBase {
|
|||
public function testTextFormatIntegration() {
|
||||
// Create an arbitrary text format.
|
||||
$format = FilterFormat::create([
|
||||
'format' => Unicode::strtolower($this->randomMachineName()),
|
||||
'format' => mb_strtolower($this->randomMachineName()),
|
||||
'name' => $this->randomString(),
|
||||
]);
|
||||
$format->save();
|
||||
|
|
|
@ -9,7 +9,7 @@ use Drupal\editor\Entity\Editor;
|
|||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\quickedit\MetadataGenerator;
|
||||
use Drupal\Tests\quickedit\Kernel\QuickEditTestBase;
|
||||
use Drupal\quickedit_test\MockEditEntityFieldAccessCheck;
|
||||
use Drupal\quickedit_test\MockQuickEditEntityFieldAccessCheck;
|
||||
use Drupal\editor\EditorController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
|
@ -38,7 +38,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
/**
|
||||
* The metadata generator object to be tested.
|
||||
*
|
||||
* @var \Drupal\quickedit\MetadataGeneratorInterface.php
|
||||
* @var \Drupal\quickedit\MetadataGeneratorInterface
|
||||
*/
|
||||
protected $metadataGenerator;
|
||||
|
||||
|
@ -52,7 +52,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
/**
|
||||
* The access checker object to be used by the metadata generator object.
|
||||
*
|
||||
* @var \Drupal\quickedit\Access\EditEntityFieldAccessCheckInterface
|
||||
* @var \Drupal\quickedit\Access\QuickEditEntityFieldAccessCheckInterface
|
||||
*/
|
||||
protected $accessChecker;
|
||||
|
||||
|
@ -165,7 +165,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
*/
|
||||
public function testMetadata() {
|
||||
$this->editorManager = $this->container->get('plugin.manager.quickedit.editor');
|
||||
$this->accessChecker = new MockEditEntityFieldAccessCheck();
|
||||
$this->accessChecker = new MockQuickEditEntityFieldAccessCheck();
|
||||
$this->editorSelector = $this->container->get('quickedit.editor.selector');
|
||||
$this->metadataGenerator = new MetadataGenerator($this->accessChecker, $this->editorSelector, $this->editorManager);
|
||||
|
||||
|
@ -221,7 +221,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
[
|
||||
'command' => 'editorGetUntransformedText',
|
||||
'data' => 'Test',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$ajax_response_attachments_processor = \Drupal::service('ajax_response.attachments_processor');
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\Tests\editor\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\EntityManager;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
|
@ -22,9 +24,9 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
/**
|
||||
* The entity manager used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The ID of the type of the entity under test.
|
||||
|
@ -66,8 +68,8 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
->method('getProvider')
|
||||
->will($this->returnValue('editor'));
|
||||
|
||||
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->entityManager->expects($this->any())
|
||||
$this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
|
||||
$this->entityTypeManager->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->with($this->entityTypeId)
|
||||
->will($this->returnValue($this->entityType));
|
||||
|
@ -78,10 +80,16 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$entity_manager = new EntityManager();
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('entity.manager', $this->entityManager);
|
||||
$container->set('entity.manager', $entity_manager);
|
||||
$container->set('entity_type.manager', $this->entityTypeManager);
|
||||
$container->set('uuid', $this->uuid);
|
||||
$container->set('plugin.manager.editor', $this->editorPluginManager);
|
||||
// Inject the container into entity.manager so it can defer to
|
||||
// entity_type.manager.
|
||||
$entity_manager->setContainer($container);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
|
@ -120,7 +128,7 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
->with($format_id)
|
||||
->will($this->returnValue($filter_format));
|
||||
|
||||
$this->entityManager->expects($this->once())
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getStorage')
|
||||
->with('filter_format')
|
||||
->will($this->returnValue($storage));
|
||||
|
|
|
@ -364,8 +364,11 @@ class StandardTest extends UnitTestCase {
|
|||
|
||||
// IMG STYLE with expression.
|
||||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IMG_STYLE_with_expression
|
||||
$data[] = ['exp/*<A STYLE=\'no\xss:noxss("*//*");
|
||||
xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>', 'exp/*<A>'];
|
||||
$data[] = [
|
||||
'exp/*<A STYLE=\'no\xss:noxss("*//*");
|
||||
xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>',
|
||||
'exp/*<A>',
|
||||
];
|
||||
|
||||
// STYLE tag (Older versions of Netscape only).
|
||||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_tag_.28Older_versions_of_Netscape_only.29
|
||||
|
@ -443,7 +446,9 @@ xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>', 'exp/*<A>'];
|
|||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Downlevel-Hidden_block
|
||||
$data[] = ['<!--[if gte IE 4]>
|
||||
<SCRIPT>alert(\'XSS\');</SCRIPT>
|
||||
<![endif]-->', "\n alert('XSS');\n "];
|
||||
<![endif]-->',
|
||||
"\n alert('XSS');\n ",
|
||||
];
|
||||
|
||||
// BASE tag.
|
||||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#BASE_tag
|
||||
|
@ -578,20 +583,20 @@ xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>', 'exp/*<A>'];
|
|||
'<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'<unknown>Pink Fairy Armadillo</unknown><video src="gerenuk.mp4">alert(0)',
|
||||
'Disallow only the script tag',
|
||||
['script']
|
||||
['script'],
|
||||
],
|
||||
[
|
||||
'<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'<unknown>Pink Fairy Armadillo</unknown>alert(0)',
|
||||
'Disallow both the script and video tags',
|
||||
['script', 'video']
|
||||
['script', 'video'],
|
||||
],
|
||||
// No real use case for this, but it is an edge case we must ensure works.
|
||||
[
|
||||
'<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'<unknown>Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'Disallow no tags',
|
||||
[]
|
||||
[],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
Reference in a new issue