DrupalCon Amsterdam Logo

CKEditor in Drupal 8

Wiktor Walc
Site Building – September 30, 2014

About Me

  • CKEditor Module Maintainer for Drupal 6/7
  • CTO @

Agenda

  • The status of WYSIWYG editing in Drupal 7
  • New features offered by CKEditor & Text Editor modules
  • What is ACF and how it works
  • Widgets: why use them and what they offer
  • Widgets: the new image plugin in Drupal 8
  • Adding plugins to CKEditor
  • Security: HTML Filter vs. ACF
  • Migration

Drupal 7

Default editor in Drupal 7 (plain textarea).

crying baby

https://www.flickr.com/photos/donnieray/10584676434

Drupal 7

  • Contributed modules: WYSIWYG and CKEditor
  • Together 575 000 installations in Drupal 7
  • Awesome! But...
  • Research

    • Which module to use?
    • Which editor to use?
    • What distribution of editor to use?
  • Installation, testing, configuration
  • Maintenance (updating, etc.)

...and

Popular WTFs

  • Images are lost after saving node (CKEditor Toolbar vs. Text format configuration mismatch)
  • Confusion: installed CKEditor module but need to download CKEditor (the editor)? o_O

Drupal 8

  • CKEditor in core. No need to search, test, install.
  • 575 000 x 1.5h = 860 000h available for fun ☺

    Yes!!

Drupal 8

  • Select text editor straight in "Text formats and editors" administration page
  • Drag & drop toolbar configurator for CKEditor
  • HTML filter settings automatically updated
  • Nice API for registering additional CKEditor plugins, enhancing CKEditor configuration form

Demo #1

How CKEditor toolbar configuration triggers an automatic update of Allowed HTML tags list

Demo #2

How automatic update of Allowed HTML tags list works when buttons (features) are removed

ACF (Advanced Content Filter)

ACF strips unwanted content

Justin Bieber mug shot

Source: Miami-Dade Police Department

ACF (Advanced Content Filter)

  • Everything that is not allowed is removed
  • Works also when pasting content – WYSIWYG!

    No need to submit form and wait for the server-side filter to remove disallowed tags to see the final markup

  • Smart (transformations - stripping tags, not content)

Switching text formats? Remember about ACF

Warning when switching text formats

Demo #3

Live example of how ACF works on different text formats

Editing possibilities

  • Classic editor
  • Inline editor

Administration backend

"Classic" editor

  • <iframe> – based
  • CSS-styles are not inherited from admin theme
  • Themes may register dedicated stylesheet for CKEditor through an entry in their .info file:

ckeditor_stylesheets:
    - css/ckeditor-iframe.css

Classic editor in action, note the CSS styles in WYSIWYG area:

Classic editor in Drupal 8

Quick editing

"Inline" editor

  • No <iframe>!
  • CSS-styles are inherited; the content looks exactly the same both in editor and on website.
  • Real WYSIWYG, finally!

Handy quick edit option in Drupal 8:

Linkt to quick editing in Drupal 8

Editing node using quick edit feature:

Quick editing in Drupal 8

Note that CSS styles did not change!

Styling content

  • CSS style definitions set in themes' CSS files
  • Selected style definitions (using CSS classes) available in Styles dropdown list
  • Separate: semantic markup and visual presentation
  • Handy: consistent styling, less work overall

Inline styles?

How to enable Text/Background Color buttons in Drupal 8?

  • CKEditor in Drupal Core != CKEditor "Full" preset
  • But... additional plugins might be added!

    Administration interface with checkboxes to enable additional modules

Enabled Color buttons in Full HTML text format:

Full HTML toolbar with color buttons
Funny web page - example

http://angels-heaven.org

Widgets

  • Introduced in CKEditor 4.3 (2013 Q4, exactly a year ago).
  • Rich content units that act as a single entity.
  • Any CKEditor plugin can provide widgets thanks to the Widget plugin.

Widgets vs Templates

Templates

  • HTML snippets that behave like normal HTML after being pasted into editor.
  • No control – easy to break, delete part of the content.
  • No Drag & Drop support.
  • By default not available in Drupal 8*.

* Any plugin that inserts complicated HTML structure suffers from the same issues as the template plugin.

Templates in action

Templates dialog window in CKEditor

Sample template

Image and Title template inserted in CKEditor

Common issues

Table in the middle of title
Case with deleted image and orphaned title.

Solution?

Widgets!

  • Select, copy, paste and delete as a whole.
  • Drag and drop as a whole.
  • Editable parts (with separate ACF rules!).
  • Upcasting & downcasting to a simpler data format.
  • Common dialog, context menu. Consistent look and feel.
  • Perfect UX. Bulletproof. Powerful.

Simple Box Widget

Simple Box Widget in CKEditor

Tutorial: Creating CKEditor Widget

Widget examples

Video #4

New Image Plugin in Drupal

Downcasted image

"Data" form saved in a database:

<img alt="JavaScript Logo"
     data-align="right"
     data-caption="JavaScript Logo"
     data-editor-file-uuid="8025f271-1db8-45cb-9400-f166c4683495"
     height="220"
     src="/sites/default/files/inline-images/JavaScript-logo.png"
     width="220" />

Upcasted image:

<div class="align-right cke_widget_block ..." contenteditable="false">
  --------------------------------------------------------------------
  <figure class="caption caption-img" data-widget="image" ... >
      <img alt="JavaScript Logo"
           height="220"
           src="/sites/default/files/inline-images/JavaScript-logo.png"
           width="220" >
      <figcaption contenteditable="true">JavaScript Logo</figcaption>
  </figure>
  --------------------------------------------------------------------
  <span class="cke_widget_drag_handler_container ...">...</span>
</div>

FilterCaption

FilterCaption filter in Drupal scans for images with data-caption attribute and wraps them with:

<figure class="caption caption-img {{ classes }}">
    {{ img }}
    <figcaption>{{ caption }}</figcaption>
</figure>
CKEditorDrupal
<figure>
					<img src="...">
					<figcaption></figcaption>
</figure>
⇡ UpcastingDowncasting ⇣
<img src="..." data-caption="...">
FilterCaption ⇣
<figure>
					<img src="...">
					<figcaption></figcaption>
</figure>

MathJax

Matematic formulas in CKEditor
Matematic formulas in CKEditor - source mode
(MathJax is run)

<span class="math-tex" data-cke-widget-data="...">
  <iframe>
    <script src="MathJax.js?config=TeX-AMS_HTML"></script>
    ...
    <span id="preview"> LaTeX formula </span>
  </iframe>
</span>
  • Upcast ⇢ Init ⇢ Data
  • Downcasting⇣
<span class="math-tex"> LaTeX formula </span>

Code Snippet

CodeSnippet in CKEditor
Code Snippet in CKEditor - source mode
(HighLightJS is run)

<pre data-cke-widget-data="...">
  <code class="language-php hljs">
    (*highlighted* code)
    <span class="hljs-function"><span class="hljs-keyword">function</span>
    <span class="hljs-title">isEmpty</span><span class="hljs-params">
    ( object ) </span> {</span>...
  </code>
</pre>
  • Upcast ⇢ Init ⇢ Data
  • Downcasting⇣
<pre><code class="language-php"> some code </code></pre>

Chart

Chart in CKEditor
Chart - source code
(Chart.js is run)

<div class="chartjs" data-chart="bar" data-chart-value="[{...(JSON)...}]">

    [rendered chart]
    -------------------------------------
    <canvas></canvas>
    <div class="chartjs-legend"></div>
    -------------------------------------
</div>
  • Upcast ⇢ Init ⇢ Data
  • Downcasting⇣
<div class="chartjs" data-chart="bar" data-chart-value="[{...(JSON)...}]"></div>

Widgets – Summary

  • Complex data structures that will not break
  • Convenient solution for using JavaScript presentation libraries in CKEditor
  • Save simple forms, render rich forms in WYSIWYG
  • Save simple forms, transform using filters in Drupal:

    • Possible to change the visual format of a data structure at any moment!

Writing plugins

  • Adding new plugins (e.g. from addons repository)
  • Extending the list of configuration options

Adding new plugins

CKEditorPluginInterface

(any plugin must implement)

  • getDependencies(Editor $editor)

    An array of required CKEditor plugins

  • getFile()

    Path to plugin.js

  • getConfig(Editor $editor)

    Configuration settings

CKEditorPluginButtonsInterface

(for plugins with toolbar buttons)

  • getButtons() An array of buttons*

    • label
    • image, image_rtl (icons)
    • ...

* list is used only in the administration backend.

CKEditorPluginConfigurableInterface

(if you wish to provide settings form)

settingsForm(array $form, ..., Editor $editor);

Configuring styles for CKEditor

CKEditorPluginContextualInterface

(for plugins enabled under certain conditions)

  • isEnabled(Editor $editor)

Namespace Drupal\ckeditor

Custom Configuration

Simple Hook

hook_editor_js_settings_alter(array &$settings)

HTML Filter vs. ACF

  • HTML Filter enabled <-> ACF enabled
  • HTML Filter disabled <-> ACF disabled

Need HTML Filter disabled & ACF enabled?

/**
* Implements hook_editor_js_settings_alter
* @param array $settings
*/
function mymodule_editor_js_settings_alter(array &$settings) {
    if (empty($settings['editor']['formats']['full_html'])) {
      return;
    }

    $full_html = &$settings['editor']['formats']['full_html'];
    if ($full_html['editorSettings']['allowedContent'] === true) {
      unset($full_html['editorSettings']['allowedContent']);
    }
}

Security

  • Drupal stores content as is, which is a potential threat for WYSIWYG editors
  • ACF != security filter
  • XSS protection included: security filters (TYPE_HTML_RESTRICTOR) are run before content can be edited in WYSIWYG editor

Migrating from Drupal 7

  • No way to port CKEditor profiles from D7 automatically
  • wysiwyg_linebreaks module for handling content that used auto-paragraph filter
  • Use Migrate API to port & transform content
  • Write plugins, widgets to allow editing custom markup

The End

Follow CKEditor on Twitter: @ckeditor

Contact me: @w_walcwwalc

DrupalCon Amsterdam Logo

What did you think?

Evaluate this session – amsterdam2014.drupal.org/schedule

Thank you!