/** * {@inheritdoc} */ function isEnabled(Editor $editor) { if (!$editor->hasAssociatedFilterFormat()) { return FALSE; } // Automatically enable this plugin if the text format associated with this // text editor uses the filter_align or filter_caption filter and the // DrupalImage button is enabled. $format = $editor->getFilterFormat(); if ($format->filters('filter_align')->status || $format->filters('filter_caption')->status) { $enabled = FALSE; $settings = $editor->getSettings(); foreach ($settings['toolbar']['rows'] as $row) { foreach ($row as $group) { foreach ($group['items'] as $button) { if ($button === 'DrupalImage') { $enabled = TRUE; } } } } return $enabled; } return FALSE; }
/** * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) { $editor_settings = $editor->getSettings(); $plugin_settings = NestedArray::getValue($editor_settings, ['plugins', 'video_embed', 'defaults', 'children']); $settings = $plugin_settings ?: []; $form['defaults'] = ['#title' => $this->t('Default Settings'), '#type' => 'fieldset', '#tree' => TRUE, 'children' => Video::mockInstance($settings)->settingsForm([], new FormState())]; return $form; }
/** * {@inheritdoc} */ public function getJSSettings(EditorEntity $editor) { $js_settings = array(); $settings = $editor->getSettings(); if ($settings['stumpy_arms']) { $js_settings['doMyArmsLookStumpy'] = TRUE; } return $js_settings; }
/** * {@inheritdoc} */ function getJSSettings(EditorEntity $editor) { $js_settings = array(); $settings = $editor->getSettings(); if ($settings['ponies too']) { $js_settings['ponyModeEnabled'] = TRUE; } return $js_settings; }
/** * Implements \Drupal\ckeditor\Plugin\CKEditorPluginConfigurableInterface::settingsForm(). */ public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) { // Defaults. $config = array('styles' => ''); $settings = $editor->getSettings(); if (isset($settings['plugins']['stylescombo'])) { $config = $settings['plugins']['stylescombo']; } $form['styles'] = array('#title' => t('Styles'), '#title_display' => 'invisible', '#type' => 'textarea', '#default_value' => $config['styles'], '#description' => t('A list of classes that will be provided in the "Styles" dropdown. Enter one class on each line in the format: element.class|Label. Example: h1.title|Title.<br />These styles should be available in your theme\'s CSS file.'), '#attached' => array('library' => array('ckeditor/drupal.ckeditor.stylescombo.admin')), '#element_validate' => array(array($this, 'validateStylesValue'))); return $form; }
/** * Implements \Drupal\ckeditor\Plugin\CKEditorPluginConfigurableInterface::settingsForm(). */ function settingsForm(array $form, array &$form_state, Editor $editor) { // Defaults. $config = array('ultra_llama_mode' => FALSE); $settings = $editor->getSettings(); if (isset($settings['plugins']['llama_contextual_and_button'])) { $config = $settings['plugins']['llama_contextual_and_button']; } $form['ultra_llama_mode'] = array('#title' => t('Ultra llama mode'), '#type' => 'checkbox', '#default_value' => $config['ultra_llama_mode']); return $form; }
/** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->installEntitySchema('editor'); $this->installEntitySchema('filter_format'); $this->format = FilterFormat::create(['format' => 'test_format', 'name' => $this->randomMachineName()]); $this->format->save(); $this->editor = Editor::create(['editor' => 'ckeditor', 'format' => 'test_format', 'settings' => ['toolbar' => ['rows' => [[['name' => 'Enabled Buttons', 'items' => ['Format']]]]]]]); $this->editor->save(); $this->ckeditorPluginManager = $this->container->get('plugin.manager.ckeditor.plugin'); }
/** * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) { $settings = $editor->getSettings(); $styles = $this->getStyles(); $config = \Drupal::config('codesnippet.settings'); $default_style = $config->get('style'); $languages = $config->get('languages'); asort($languages); $form['#attached']['library'][] = 'codesnippet/codesnippet.admin'; $form['highlight_style'] = array('#type' => 'select', '#title' => 'highlight.js Style', '#description' => $this->t('Select a style to apply to all highlighted code snippets. You can preview the styles at @link.', array('@link' => \Drupal::l('https://highlightjs.org/static/demo', Url::fromUri('https://highlightjs.org/static/demo/')))), '#options' => $styles, '#default_value' => !empty($settings['plugins']['codesnippet']['highlight_style']) ? $settings['plugins']['codesnippet']['highlight_style'] : $default_style); $form['highlight_languages'] = array('#type' => 'checkboxes', '#title' => 'Supported Languages', '#options' => $languages, '#description' => t('Enter languages you want to have as options in the editor dialog. To add a language not in this list, please see the README.txt of this module.'), '#default_value' => $settings['plugins']['codesnippet']['highlight_languages']); return $form; }
/** * {@inheritdoc} */ function isEnabled(Editor $editor) { // Automatically enable this plugin if the Underline button is enabled. $settings = $editor->getSettings(); foreach ($settings['toolbar']['rows'] as $row) { foreach ($row as $group) { if (in_array('Strike', $group['items'])) { return TRUE; } } } return FALSE; }
/** * Tests the entity embed button markup. */ public function testEntityEmbedButtonMarkup() { // Ensure that the route is not accessible with text format without the // button configured. $this->getEmbedDialog('plain_text', 'node'); $this->assertResponse(404, 'Embed dialog is not accessible with a filter that does not have an editor configuration.'); // Add an empty configuration for the plain_text editor configuration. $editor = Editor::create(['format' => 'plain_text', 'editor' => 'ckeditor']); $editor->save(); $this->getEmbedDialog('plain_text', 'node'); $this->assertResponse(403, 'Embed dialog is not accessible with a filter that does not have the embed button assigned to it.'); // Ensure that the route is accessible with a valid embed button. // 'Node' embed button is provided by default by the module and hence the // request must be successful. $this->getEmbedDialog('custom_format', 'node'); $this->assertResponse(200, 'Embed dialog is accessible with correct filter format and embed button.'); // Ensure form structure of the 'select' step and submit form. $this->assertFieldByName('attributes[data-entity-id]', '', 'Entity ID/UUID field is present.'); // Check that 'Next' is a primary button. $this->assertFieldByXPath('//input[contains(@class, "button--primary")]', 'Next', 'Next is a primary button'); $title = $this->node->getTitle() . ' (' . $this->node->id() . ')'; $edit = ['attributes[data-entity-id]' => $title]; $this->drupalPostAjaxForm(NULL, $edit, 'op'); /*$this->drupalPostForm(NULL, $edit, 'Next'); // Ensure form structure of the 'embed' step and submit form. $this->assertFieldByName('attributes[data-entity-embed-display]', 'Entity Embed Display plugin field is present.'); // Check that 'Embed' is a primary button. $this->assertFieldByXPath('//input[contains(@class, "button--primary")]', 'Embed', 'Embed is a primary button');*/ }
/** * Test access to the editor image dialog. */ public function testEditorImageDialogAccess() { $url = Url::fromRoute('editor.image_dialog', ['editor' => 'plain_text']); $session = $this->assertSession(); // With no text editor, expect a 404. $this->drupalGet($url); $session->statusCodeEquals(404); // With a text editor but without image upload settings, expect a 200, but // there should not be an input[type=file]. $editor = Editor::create(['editor' => 'ckeditor', 'format' => 'plain_text', 'settings' => ['toolbar' => ['rows' => [[['name' => 'Media', 'items' => ['DrupalImage']]]]], 'plugins' => []], 'image_upload' => ['status' => FALSE, 'scheme' => 'public', 'directory' => 'inline-images', 'max_size' => '', 'max_dimensions' => ['width' => 0, 'height' => 0]]]); $editor->save(); $this->resetAll(); $this->drupalGet($url); $this->assertTrue($this->cssSelect('input[type=text][name="attributes[src]"]'), 'Image uploads disabled: input[type=text][name="attributes[src]"] is present.'); $this->assertFalse($this->cssSelect('input[type=file]'), 'Image uploads disabled: input[type=file] is absent.'); $session->statusCodeEquals(200); // With image upload settings, expect a 200, and now there should be an // input[type=file]. $editor->setImageUploadSettings(['status' => TRUE] + $editor->getImageUploadSettings())->save(); $this->resetAll(); $this->drupalGet($url); $this->assertFalse($this->cssSelect('input[type=text][name="attributes[src]"]'), 'Image uploads enabled: input[type=text][name="attributes[src]"] is absent.'); $this->assertTrue($this->cssSelect('input[type=file]'), 'Image uploads enabled: input[type=file] is present.'); $session->statusCodeEquals(200); }
/** * Tests StylesCombo settings for an existing text format. */ function testExistingFormat() { $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor'); $default_settings = $ckeditor->getDefaultSettings(); $this->drupalLogin($this->adminUser); $this->drupalGet('admin/config/content/formats/manage/' . $this->format); // Ensure an Editor config entity exists, with the proper settings. $expected_settings = $default_settings; $editor = Editor::load($this->format); $this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); // Case 1: Configure the Styles plugin with different labels for each style, // and ensure the updated settings are saved. $this->drupalGet('admin/config/content/formats/manage/' . $this->format); $edit = ['editor[settings][plugins][stylescombo][styles]' => "h1.title|Title\np.callout|Callout\n\n"]; $this->drupalPostForm(NULL, $edit, t('Save configuration')); $expected_settings['plugins']['stylescombo']['styles'] = "h1.title|Title\np.callout|Callout\n\n"; $editor = Editor::load($this->format); $this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); // Case 2: Configure the Styles plugin with same labels for each style, and // ensure that an error is displayed and that the updated settings are not // saved. $this->drupalGet('admin/config/content/formats/manage/' . $this->format); $edit = ['editor[settings][plugins][stylescombo][styles]' => "h1.title|Title\np.callout|Title\n\n"]; $this->drupalPostForm(NULL, $edit, t('Save configuration')); $this->assertRaw(t('Each style must have a unique label.')); $expected_settings['plugins']['stylescombo']['styles'] = "h1.title|Title\np.callout|Callout\n\n"; $editor = Editor::load($this->format); $this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); }
/** * Tests the configurable text editor manager. */ public function testManager() { $this->editorManager = $this->container->get('plugin.manager.editor'); // Case 1: no text editor available: // - listOptions() should return an empty list of options // - getAttachments() should return an empty #attachments array (and not // a JS settings structure that is empty) $this->assertIdentical(array(), $this->editorManager->listOptions(), 'When no text editor is enabled, the manager works correctly.'); $this->assertIdentical(array(), $this->editorManager->getAttachments(array()), 'No attachments when no text editor is enabled and retrieving attachments for zero text formats.'); $this->assertIdentical(array(), $this->editorManager->getAttachments(array('filtered_html', 'full_html')), 'No attachments when no text editor is enabled and retrieving attachments for multiple text formats.'); // Enable the Text Editor Test module, which has the Unicorn Editor and // clear the editor manager's cache so it is picked up. $this->enableModules(array('editor_test')); $this->editorManager = $this->container->get('plugin.manager.editor'); $this->editorManager->clearCachedDefinitions(); // Case 2: a text editor available. $this->assertIdentical('Unicorn Editor', (string) $this->editorManager->listOptions()['unicorn'], 'When some text editor is enabled, the manager works correctly.'); // Case 3: a text editor available & associated (but associated only with // the 'Full HTML' text format). $unicorn_plugin = $this->editorManager->createInstance('unicorn'); $editor = Editor::create(['format' => 'full_html', 'editor' => 'unicorn']); $editor->save(); $this->assertIdentical(array(), $this->editorManager->getAttachments(array()), 'No attachments when one text editor is enabled and retrieving attachments for zero text formats.'); $expected = array('library' => array(0 => 'editor_test/unicorn'), 'drupalSettings' => ['editor' => ['formats' => ['full_html' => ['format' => 'full_html', 'editor' => 'unicorn', 'editorSettings' => $unicorn_plugin->getJSSettings($editor), 'editorSupportsContentFiltering' => TRUE, 'isXssSafe' => FALSE]]]]); $this->assertIdentical($expected, $this->editorManager->getAttachments(array('filtered_html', 'full_html')), 'Correct attachments when one text editor is enabled and retrieving attachments for multiple text formats.'); // Case 4: a text editor available associated, but now with its JS settings // being altered via hook_editor_js_settings_alter(). \Drupal::state()->set('editor_test_js_settings_alter_enabled', TRUE); $expected['drupalSettings']['editor']['formats']['full_html']['editorSettings']['ponyModeEnabled'] = FALSE; $this->assertIdentical($expected, $this->editorManager->getAttachments(array('filtered_html', 'full_html')), 'hook_editor_js_settings_alter() works correctly.'); }
/** * Ensure comment form works with history and big_pipe modules. * * @see https://www.drupal.org/node/2698811 */ public function testCommentForm_2698811() { $this->assertTrue($this->container->get('module_installer')->install(['comment', 'history', 'ckeditor'], TRUE), 'Installed modules.'); // Ensure an `article` node type exists. $this->createContentType(['type' => 'article']); $this->addDefaultCommentField('node', 'article'); // Enable CKEditor. $format = $this->randomMachineName(); FilterFormat::create(['format' => $format, 'name' => $this->randomString(), 'weight' => 1, 'filters' => []])->save(); $settings['toolbar']['rows'] = [[['name' => 'Links', 'items' => ['DrupalLink', 'DrupalUnlink']]]]; $editor = Editor::create(['format' => $format, 'editor' => 'ckeditor']); $editor->setSettings($settings); $editor->save(); $admin_user = $this->drupalCreateUser(['access comments', 'post comments', 'use text format ' . $format]); $this->drupalLogin($admin_user); $node = $this->createNode(['type' => 'article', 'comment' => CommentItemInterface::OPEN]); // Create some comments. foreach (range(1, 5) as $i) { $comment = Comment::create(['status' => CommentInterface::PUBLISHED, 'field_name' => 'comment', 'entity_type' => 'node', 'entity_id' => $node->id()]); $comment->save(); } $this->drupalGet($node->toUrl()->toString()); // Confirm that CKEditor loaded. $javascript = <<<JS (function(){ return Object.keys(CKEDITOR.instances).length > 0; }()); JS; $this->assertJsCondition($javascript); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filter_format = NULL) { // Add AJAX support. $form['#prefix'] = '<div id="video-embed-dialog-form">'; $form['#suffix'] = '</div>'; // Ensure relevant dialog libraries are attached. $form['#attached']['library'][] = 'editor/drupal.editor.dialog'; // Simple URL field and submit button for video URL. $form['video_url'] = ['#type' => 'textfield', '#title' => $this->t('Video URL'), '#required' => TRUE, '#default_value' => $this->getUserInput($form_state, 'video_url')]; // If no settings are found, use the defaults configured in the filter // formats interface. $settings = $this->getUserInput($form_state, 'settings'); if (empty($settings) && ($editor = Editor::load($filter_format->id()))) { $editor_settings = $editor->getSettings(); $plugin_settings = NestedArray::getValue($editor_settings, ['plugins', 'video_embed', 'defaults', 'children']); $settings = $plugin_settings ? $plugin_settings : []; } // Create a settings form from the existing video formatter. $form['settings'] = Video::mockInstance($settings)->settingsForm([], new FormState()); $form['settings']['#type'] = 'fieldset'; $form['settings']['#title'] = $this->t('Settings'); $form['actions'] = ['#type' => 'actions']; $form['actions']['save_modal'] = ['#type' => 'submit', '#value' => $this->t('Save'), '#submit' => [], '#ajax' => ['callback' => '::ajaxSubmit', 'event' => 'click', 'wrapper' => 'video-embed-dialog-form']]; return $form; }
/** * Additional #pre_render callback for 'text_format' elements. */ function preRenderTextFormat(array $element) { // Allow modules to programmatically enforce no client-side editor by // setting the #editor property to FALSE. if (isset($element['#editor']) && !$element['#editor']) { return $element; } // filter_process_format() copies properties to the expanded 'value' child // element, including the #pre_render property. Skip this text format // widget, if it contains no 'format'. if (!isset($element['format'])) { return $element; } $format_ids = array_keys($element['format']['format']['#options']); // Early-return if no text editor is associated with any of the text formats. $editors = Editor::loadMultiple($format_ids); if (count($editors) === 0) { return $element; } // Use a hidden element for a single text format. $field_id = $element['value']['#id']; if (!$element['format']['format']['#access']) { // Use the first (and only) available text format. $format_id = $format_ids[0]; $element['format']['editor'] = array('#type' => 'hidden', '#name' => $element['format']['format']['#name'], '#value' => $format_id, '#attributes' => array('class' => array('editor'), 'data-editor-for' => $field_id)); } else { $element['format']['format']['#attributes']['class'][] = 'editor'; $element['format']['format']['#attributes']['data-editor-for'] = $field_id; } // Hide the text format's filters' guidelines of those text formats that have // a text editor associated: they're rather useless when using a text editor. foreach ($editors as $format_id => $editor) { $element['format']['guidelines'][$format_id]['#access'] = FALSE; } // Attach Text Editor module's (this module) library. $element['#attached']['library'][] = 'editor/drupal.editor'; // Attach attachments for all available editors. $element['#attached'] = drupal_merge_attached($element['#attached'], $this->pluginManager->getAttachments($format_ids)); // Apply XSS filters when editing content if necessary. Some types of text // editors cannot guarantee that the end user won't become a victim of XSS. if (!empty($element['value']['#value'])) { $original = $element['value']['#value']; $format = FilterFormat::load($element['format']['format']['#value']); // Ensure XSS-safety for the current text format/editor. $filtered = editor_filter_xss($original, $format); if ($filtered !== FALSE) { $element['value']['#value'] = $filtered; } // Only when the user has access to multiple text formats, we must add data- // attributes for the original value and change tracking, because they are // only necessary when the end user can switch between text formats/editors. if ($element['format']['format']['#access']) { $element['value']['#attributes']['data-editor-value-is-changed'] = 'false'; $element['value']['#attributes']['data-editor-value-original'] = $original; } } return $element; }
/** * @covers ::calculateDependencies */ public function testCalculateDependencies() { $format_id = 'filter.format.test'; $values = array('editor' => $this->editorId, 'format' => $format_id); $plugin = $this->getMockBuilder('Drupal\\editor\\Plugin\\EditorPluginInterface')->disableOriginalConstructor()->getMock(); $plugin->expects($this->once())->method('getPluginDefinition')->will($this->returnValue(array('provider' => 'test_module'))); $plugin->expects($this->once())->method('getDefaultSettings')->will($this->returnValue(array())); $this->editorPluginManager->expects($this->any())->method('createInstance')->with($this->editorId)->will($this->returnValue($plugin)); $entity = new Editor($values, $this->entityTypeId); $filter_format = $this->getMock('Drupal\\Core\\Config\\Entity\\ConfigEntityInterface'); $filter_format->expects($this->once())->method('getConfigDependencyName')->will($this->returnValue('filter.format.test')); $storage = $this->getMock('Drupal\\Core\\Entity\\EntityStorageInterface'); $storage->expects($this->once())->method('load')->with($format_id)->will($this->returnValue($filter_format)); $this->entityManager->expects($this->once())->method('getStorage')->with('filter_format')->will($this->returnValue($storage)); $dependencies = $entity->calculateDependencies()->getDependencies(); $this->assertContains('test_module', $dependencies['module']); $this->assertContains('filter.format.test', $dependencies['config']); }
/** * {@inheritdoc} */ protected function setUp() { parent::setUp(); // Create a text format and associate this with CKEditor. FilterFormat::create(['format' => 'full_html', 'name' => 'Full HTML', 'weight' => 1, 'filters' => []])->save(); Editor::create(['format' => 'full_html', 'editor' => 'ckeditor'])->save(); // Create a new user with admin rights. $this->admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'administer site configuration', 'administer filters']); }
/** * Retrieves enabled plugins' files, keyed by plugin ID. * * For CKEditor plugins that implement: * - CKEditorPluginButtonsInterface, not CKEditorPluginContextualInterface, * a plugin is enabled if at least one of its buttons is in the toolbar; * - CKEditorPluginContextualInterface, not CKEditorPluginButtonsInterface, * a plugin is enabled if its isEnabled() method returns TRUE * - both of these interfaces, a plugin is enabled if either is the case. * * Internal plugins (those that are part of the bundled build of CKEditor) are * excluded by default, since they are loaded implicitly. If you need to know * even implicitly loaded (i.e. internal) plugins, then set the optional * second parameter. * * @param \Drupal\editor\Entity\Editor $editor * A configured text editor object. * @param bool $include_internal_plugins * Defaults to FALSE. When set to TRUE, plugins whose isInternal() method * returns TRUE will also be included. * @return array * A list of the enabled CKEditor plugins, with the plugin IDs as keys and * the Drupal root-relative plugin files as values. * For internal plugins, the value is NULL. */ public function getEnabledPluginFiles(Editor $editor, $include_internal_plugins = FALSE) { $plugins = array_keys($this->getDefinitions()); // Flatten each row. $toolbar_rows = array(); $settings = $editor->getSettings(); foreach ($settings['toolbar']['rows'] as $row_number => $row) { $toolbar_rows[] = array_reduce($settings['toolbar']['rows'][$row_number], function (&$result, $button_group) { return array_merge($result, $button_group['items']); }, array()); } $toolbar_buttons = array_unique(NestedArray::mergeDeepArray($toolbar_rows)); $enabled_plugins = array(); $additional_plugins = array(); foreach ($plugins as $plugin_id) { $plugin = $this->createInstance($plugin_id); if (!$include_internal_plugins && $plugin->isInternal()) { continue; } $enabled = FALSE; // Enable this plugin if it provides a button that has been enabled. if ($plugin instanceof CKEditorPluginButtonsInterface) { $plugin_buttons = array_keys($plugin->getButtons()); $enabled = count(array_intersect($toolbar_buttons, $plugin_buttons)) > 0; } // Otherwise enable this plugin if it declares itself as enabled. if (!$enabled && $plugin instanceof CKEditorPluginContextualInterface) { $enabled = $plugin->isEnabled($editor); } if ($enabled) { $enabled_plugins[$plugin_id] = $plugin->isInternal() ? NULL : $plugin->getFile(); // Check if this plugin has dependencies that also need to be enabled. $additional_plugins = array_merge($additional_plugins, array_diff($plugin->getDependencies($editor), $additional_plugins)); } } // Add the list of dependent plugins. foreach ($additional_plugins as $plugin_id) { $plugin = $this->createInstance($plugin_id); $enabled_plugins[$plugin_id] = $plugin->isInternal() ? NULL : $plugin->getFile(); } // Always return plugins in the same order. asort($enabled_plugins); return $enabled_plugins; }
/** * Tests \Drupal\embed\Access\EmbedButtonEditorAccessCheck. */ public function testEmbedButtonEditorAccessCheck() { // The anonymous user should have access to the plain_text format, but it // hasn't been configured to use an editor yet. $this->getRoute('plain_text', 'embed_test_default'); $this->assertResponse(404); $this->assertCacheContext('route'); $this->assertNoCacheTag('config:editor.editor.embed_test'); $this->assertNoCacheTag('config:embed.button.embed_test_default'); // The anonymous user should not have permission to use embed_test format. $this->getRoute('embed_test', 'embed_test_default'); $this->assertResponse(403); $this->assertCacheContext('route'); $this->assertNoCacheTag('config:editor.editor.embed_test'); $this->assertNoCacheTag('config:embed.button.embed_test_default'); // Now login a user that can use the embed_test format. $this->drupalLogin($this->webUser); $this->getRoute('plain_text', 'embed_test_default'); $this->assertResponse(404); $this->assertCacheContext('route'); $this->assertNoCacheTag('config:editor.editor.plain_text'); $this->assertNoCacheTag('config:embed.button.embed_test_default'); // Add an empty configuration for the plain_text editor configuration. $editor = Editor::create(['format' => 'plain_text', 'editor' => 'ckeditor']); $editor->save(); $this->getRoute('plain_text', 'embed_test_default'); $this->assertResponse(403); $this->assertCacheContext('route'); $this->assertCacheTag('config:editor.editor.plain_text'); $this->assertCacheTag('config:embed.button.embed_test_default'); $this->getRoute('embed_test', 'embed_test_default'); $this->assertResponse(200); $this->assertCacheContext('route'); $this->assertCacheTag('config:editor.editor.embed_test'); $this->assertCacheTag('config:embed.button.embed_test_default'); $this->assertText(static::SUCCESS); // Test route with an empty request. $this->getRoute('embed_test', 'embed_test_default', ''); $this->assertResponse(404); $this->assertCacheContext('route'); $this->assertCacheTag('config:editor.editor.embed_test'); $this->assertCacheTag('config:embed.button.embed_test_default'); // Test route with an invalid text format. $this->getRoute('invalid_editor', 'embed_test_default'); $this->assertResponse(404); $this->assertCacheContext('route'); $this->assertNoCacheTag('config:editor.editor.invalid_editor'); $this->assertNoCacheTag('config:embed.button.embed_test_default'); // Test route with an invalid embed button. $this->getRoute('embed_test', 'invalid_button'); $this->assertResponse(404); $this->assertCacheContext('route'); $this->assertNoCacheTag('config:editor.editor.embed_test'); $this->assertNoCacheTag('config:embed.button.invalid_button'); }
/** * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) { // Defaults. $config = ['language_list' => 'un']; $settings = $editor->getSettings(); if (isset($settings['plugins']['language'])) { $config = $settings['plugins']['language']; } $predefined_languages = LanguageManager::getStandardLanguageList(); $form['language_list'] = array('#title' => $this->t('Language list'), '#title_display' => 'invisible', '#type' => 'select', '#options' => ['un' => $this->t("United Nations' official languages"), 'all' => $this->t('All @count languages', ['@count' => count($predefined_languages)])], '#default_value' => $config['language_list'], '#description' => $this->t('The list of languages to show in the language dropdown. The basic list will only show the <a href=":url">six official languages of the UN</a>. The extended list will show all @count languages that are available in Drupal.', [':url' => Url::fromUri('http://www.un.org/en/aboutun/languages.shtml/')->toString(), '@count' => count($predefined_languages)]), '#attached' => ['library' => ['ckeditor/drupal.ckeditor.language.admin']]); return $form; }
protected function setUp() { parent::setUp(); // Install the Filter module. // Create a field. $this->fieldName = 'field_textarea'; $this->createFieldWithStorage($this->fieldName, 'text', 1, 'Long text field', array(), 'text_textarea', array('size' => 42), 'text_default', array()); // Create text format. $full_html_format = FilterFormat::create(array('format' => 'full_html', 'name' => 'Full HTML', 'weight' => 1, 'filters' => array())); $full_html_format->save(); // Associate text editor with text format. $editor = Editor::create(['format' => $full_html_format->id(), 'editor' => 'unicorn']); $editor->save(); // Also create a text format without an associated text editor. FilterFormat::create(array('format' => 'no_editor', 'name' => 'No Text Editor', 'weight' => 2, 'filters' => array()))->save(); }
protected function setUp() { parent::setUp(); // Create text format, associate CKEditor. $filtered_html_format = FilterFormat::create(array('format' => 'filtered_html', 'name' => 'Filtered HTML', 'weight' => 0, 'filters' => array())); $filtered_html_format->save(); $editor = Editor::create(['format' => 'filtered_html', 'editor' => 'ckeditor']); $editor->save(); // Create a second format without an associated editor so a drop down select // list is created when selecting formats. $full_html_format = FilterFormat::create(array('format' => 'full_html', 'name' => 'Full HTML', 'weight' => 1, 'filters' => array())); $full_html_format->save(); // Create node type. $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); $this->untrustedUser = $this->drupalCreateUser(array('create article content', 'edit any article content')); $this->normalUser = $this->drupalCreateUser(array('create article content', 'edit any article content', 'use text format filtered_html', 'use text format full_html')); }
/** * {@inheritdoc} */ protected function setUp() { parent::setUp(); // Create Filtered HTML text format and enable entity_embed filter. $format = FilterFormat::create(['format' => 'embed_test', 'name' => 'Embed format', 'filters' => []]); $format->save(); $editor_group = ['name' => 'Embed', 'items' => ['embed_test_default']]; $editor = Editor::create(['format' => 'embed_test', 'editor' => 'ckeditor', 'settings' => ['toolbar' => ['rows' => [[$editor_group]]]]]); $editor->save(); // Create a user with required permissions. $this->adminUser = $this->drupalCreateUser(['administer embed buttons', 'use text format embed_test']); // Create a user with required permissions. $this->webUser = $this->drupalCreateUser(['use text format embed_test']); // Set up some standard blocks for the testing theme (Classy). // @see https://www.drupal.org/node/507488?page=1#comment-10291517 $this->drupalPlaceBlock('local_tasks_block'); $this->drupalPlaceBlock('local_actions_block'); }
protected function setUp() { parent::setUp(); $this->installEntitySchema('file'); $this->installSchema('node', array('node_access')); $this->installSchema('file', array('file_usage')); $this->installConfig(['node']); // Add text formats. $filtered_html_format = FilterFormat::create(array('format' => 'filtered_html', 'name' => 'Filtered HTML', 'weight' => 0, 'filters' => array())); $filtered_html_format->save(); // Set cardinality for body field. FieldStorageConfig::loadByName('node', 'body')->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)->save(); // Set up text editor. $editor = Editor::create(['format' => 'filtered_html', 'editor' => 'unicorn']); $editor->save(); // Create a node type for testing. $type = NodeType::create(['type' => 'page', 'name' => 'page']); $type->save(); node_add_body_field($type); }
/** * Sets up the test. */ protected function setUp() { parent::setUp(); $this->installEntitySchema('file'); $this->installSchema('system', ['key_value_expire']); $this->installSchema('node', array('node_access')); $this->installSchema('file', array('file_usage')); $this->installConfig(['node']); // Add text formats. $this->format = FilterFormat::create(['format' => 'filtered_html', 'name' => 'Filtered HTML', 'weight' => 0, 'filters' => ['filter_align' => ['status' => TRUE], 'filter_caption' => ['status' => TRUE]]]); $this->format->save(); // Set up text editor. $editor = Editor::create(['format' => 'filtered_html', 'editor' => 'unicorn', 'image_upload' => ['max_size' => 100, 'scheme' => 'public', 'directory' => '', 'status' => TRUE]]); $editor->save(); // Create a node type for testing. $type = NodeType::create(['type' => 'page', 'name' => 'page']); $type->save(); node_add_body_field($type); $this->installEntitySchema('user'); \Drupal::service('router.builder')->rebuild(); }
protected function setUp() { parent::setUp(); // Create a page content type. $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']); // Create a text format and enable the entity_embed filter. $format = FilterFormat::create(['format' => 'custom_format', 'name' => 'Custom format', 'filters' => ['entity_embed' => ['status' => 1]]]); $format->save(); $editor_group = ['name' => 'Entity Embed', 'items' => ['node']]; $editor = Editor::create(['format' => 'custom_format', 'editor' => 'ckeditor', 'settings' => ['toolbar' => ['rows' => [[$editor_group]]]]]); $editor->save(); // Create a user with required permissions. $this->webUser = $this->drupalCreateUser(['access content', 'create page content', 'use text format custom_format']); $this->drupalLogin($this->webUser); // Create a sample node to be embedded. $settings = array(); $settings['type'] = 'page'; $settings['title'] = 'Embed Test Node'; $settings['body'] = array('value' => 'This node is to be used for embedding in other nodes.', 'format' => 'custom_format'); $this->node = $this->drupalCreateNode($settings); }
/** * Tests text format removal or disabling. */ public function testTextFormatIntegration() { // Create an arbitrary text format. $format = FilterFormat::create(['format' => Unicode::strtolower($this->randomMachineName()), 'name' => $this->randomString()]); $format->save(); // Create a paired editor. Editor::create(['format' => $format->id(), 'editor' => 'unicorn'])->save(); // Disable the text format. $format->disable()->save(); // The paired editor should be disabled too. $this->assertFalse(Editor::load($format->id())->status()); // Re-enable the text format. $format->enable()->save(); // The paired editor should be enabled too. $this->assertTrue(Editor::load($format->id())->status()); // Completely remove the text format. Usually this cannot occur via UI, but // can be triggered from API. $format->delete(); // The paired editor should be removed. $this->assertNull(Editor::load($format->id())); }
/** * Tests presence of essential configuration even without Internal's buttons. */ protected function testLoadingWithoutInternalButtons() { // Change the CKEditor text editor configuration to only have link buttons. // This means: // - 0 buttons are from \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal // - 2 buttons are from \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalLink $filtered_html_editor = Editor::load('filtered_html'); $settings = $filtered_html_editor->getSettings(); $settings['toolbar']['rows'] = [0 => [0 => ['name' => 'Links', 'items' => ['DrupalLink', 'DrupalUnlink']]]]; $filtered_html_editor->setSettings($settings)->save(); // Even when no buttons of \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal // are in use, its configuration (Internal::getConfig()) is still essential: // this is configuration that is associated with the (custom, optimized) // build of CKEditor that Drupal core ships with. For example, it configures // CKEditor to not perform its default action of loading a config.js file, // to not convert special characters into HTML entities, and the allowedContent // setting to configure CKEditor's Advanced Content Filter. $this->drupalLogin($this->normalUser); $this->drupalGet('node/add/article'); $editor_settings = $this->getDrupalSettings()['editor']['formats']['filtered_html']['editorSettings']; $this->assertTrue(isset($editor_settings['customConfig'])); $this->assertTrue(isset($editor_settings['entities'])); $this->assertTrue(isset($editor_settings['allowedContent'])); $this->assertTrue(isset($editor_settings['disallowedContent'])); }
/** * {@inheritdoc} * * @param \Drupal\editor\Entity\Editor $editor * The text editor to which this dialog corresponds. */ public function buildForm(array $form, FormStateInterface $form_state, Editor $editor = NULL) { // This form is special, in that the default values do not come from the // server side, but from the client side, from a text editor. We must cache // this data in form state, because when the form is rebuilt, we will be // receiving values from the form, instead of the values from the text // editor. If we don't cache it, this data will be lost. if (isset($form_state->getUserInput()['editor_object'])) { // By convention, the data that the text editor sends to any dialog is in // the 'editor_object' key. And the image dialog for text editors expects // that data to be the attributes for an <img> element. $image_element = $form_state->getUserInput()['editor_object']; $form_state->set('image_element', $image_element); $form_state->setCached(TRUE); } else { // Retrieve the image element's attributes from form state. $image_element = $form_state->get('image_element') ?: []; } $form['#tree'] = TRUE; $form['#attached']['library'][] = 'editor/drupal.editor.dialog'; $form['#prefix'] = '<div id="editor-image-dialog-form">'; $form['#suffix'] = '</div>'; // 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::toInt($image_upload['max_size']), file_upload_max_size()); $existing_file = isset($image_element['data-entity-uuid']) ? \Drupal::entityManager()->loadEntityByUuid('file', $image_element['data-entity-uuid']) : NULL; $fid = $existing_file ? $existing_file->id() : NULL; $form['fid'] = array('#title' => $this->t('Image'), '#type' => 'managed_file', '#upload_location' => $image_upload['scheme'] . '://' . $image_upload['directory'], '#default_value' => $fid ? array($fid) : NULL, '#upload_validators' => array('file_validate_extensions' => array('gif png jpg jpeg'), 'file_validate_size' => array($max_filesize), 'file_validate_image_resolution' => array($max_dimensions)), '#required' => TRUE); $form['attributes']['src'] = array('#title' => $this->t('URL'), '#type' => 'textfield', '#default_value' => isset($image_element['src']) ? $image_element['src'] : '', '#maxlength' => 2048, '#required' => TRUE); // If the editor has image uploads enabled, show a managed_file form item, // otherwise show a (file URL) text form item. if ($image_upload['status']) { $form['attributes']['src']['#access'] = FALSE; $form['attributes']['src']['#required'] = FALSE; } else { $form['fid']['#access'] = FALSE; $form['fid']['#required'] = FALSE; } // The alt attribute is *required*, but we allow users to opt-in to empty // alt attributes for the very rare edge cases where that is valid by // specifying two double quotes as the alternative text in the dialog. // However, that *is* stored as an empty alt attribute, so if we're editing // an existing image (which means the src attribute is set) and its alt // attribute is empty, then we show that as two double quotes in the dialog. // @see https://www.drupal.org/node/2307647 $alt = isset($image_element['alt']) ? $image_element['alt'] : ''; if ($alt === '' && !empty($image_element['src'])) { $alt = '""'; } $form['attributes']['alt'] = array('#title' => $this->t('Alternative text'), '#placeholder' => $this->t('Short description for the visually impaired'), '#type' => 'textfield', '#required' => TRUE, '#required_error' => $this->t('Alternative text is required.<br />(Only in rare cases should this be left empty. To create empty alternative text, enter <code>""</code> — two double quotes without any content).'), '#default_value' => $alt, '#maxlength' => 2048); // When Drupal core's filter_align is being used, the text editor may // offer the ability to change the alignment. if (isset($image_element['data-align']) && $editor->getFilterFormat()->filters('filter_align')->status) { $form['align'] = array('#title' => $this->t('Align'), '#type' => 'radios', '#options' => array('none' => $this->t('None'), 'left' => $this->t('Left'), 'center' => $this->t('Center'), 'right' => $this->t('Right')), '#default_value' => $image_element['data-align'] === '' ? 'none' : $image_element['data-align'], '#wrapper_attributes' => array('class' => array('container-inline')), '#attributes' => array('class' => array('container-inline')), '#parents' => array('attributes', 'data-align')); } // When Drupal core's filter_caption is being used, the text editor may // offer the ability to in-place edit the image's caption: show a toggle. if (isset($image_element['hasCaption']) && $editor->getFilterFormat()->filters('filter_caption')->status) { $form['caption'] = array('#title' => $this->t('Caption'), '#type' => 'checkbox', '#default_value' => $image_element['hasCaption'] === 'true', '#parents' => array('attributes', 'hasCaption')); } $form['actions'] = array('#type' => 'actions'); $form['actions']['save_modal'] = array('#type' => 'submit', '#value' => $this->t('Save'), '#submit' => array(), '#ajax' => array('callback' => '::submitForm', 'event' => 'click')); return $form; }