NextGen Editor Documentation

How to use and integrate your plugins with the NextGen Editor

Found an issue or a problem that you can't find an answer for? Create an issue in our Premium Issue Tracker.

Before you start

Before you can install a Premium Plugin, you are required to have the License Manager plugin installed. It is a free plugin available in GPM and can be installed like any other through the Plugin section of the admin plugin, or via command line.

$ bin/gpm install license-manager

Installation

First ensure you are running the latest version of Grav 1.7 (-f forces a refresh of the GPM index).

$ bin/gpm selfupgrade -f

The NextGen Editor doesn't have any dependencies, so installation is a simple process of running the command:

$ bin/gpm install nextgen-editor

You can also install the plugin via the Plugins section in the admin plugin.

Configuration

Before configuring this plugin, you should copy the user/plugins/ck-editor5/ck-editor5.yaml to user/config/plugins/ck-editor5.yaml and only edit that copy.

Here is the default configuration and an explanation of available options:

enabled: true

Note that if you use the admin plugin, a file with your configuration, and named ck-editor5.yaml will be saved in the user/config/plugins/ folder once the configuration is saved in the admin.

Usage

After installation, the default behavior sets the NextGen Editor as the Default for All. You can change this behavior in the plugin configuration.

Default for all

If you are running Grav 1.7, there is a new option that lets you configure which editor to use per-user. Simply click on your user profile (or the user in the Users list if you are using FlexUsers), and choose the editor you prefer to use:

Profile editor selection

Keyboard Shortcuts

Platform Specific Shortcuts

Action PC Mac
Save Ctrl + S + S
Copy Ctrl + C + C
Paste Ctrl + V + V
Copy + Cut Ctrl + X + X
Undo Ctrl + Z + Z
Redo Ctrl + Y + V
Ctrl + Shift + Z + Shift + Z
Bold Ctrl + B + B
Italic Ctrl + I + I
Link Ctrl + K + K

General Shortcuts

Action Keyboard
Insert a hard break (e.g. a new paragraph) Enter
Insert a soft break (e.g. carriage return) Shift + Enter
Nest the current list item (when in a list) Tab

Widget/Shortcode Shortcuts

Action Keyboard
Insert a new paragraph directly after a widget Enter
Insert a new paragraph directly before a widget Shift + Enter
Display the caret to allow typing directly before a widget /
Display the caret to allow typing directly after a widget /

Table Cell Shortcuts

Action Keyboard
Move the selection to the next cell Tab
Move the selection to the previous cell Shift + Tab
Insert a new table row (when in the last cell of a table) Tab
Navigate through the table / / /

User Interface and Navigation Shortcuts

Action Keyboard
Close contextual balloons and UI components like dropdowns Esc
Move focus to the visible contextual balloon Tab
Move focus between fields in contextual balloons Tab
Move focus to the toolbar Alt + F10
Navigate through the toolbar / / /
Execute the currently focused button Enter

Editor Options

Editor Options

Here you can specify:

  • Fixed Height - The ability to show only a fixed height editor and a scrollbar, rather than the full height editor which is the default behavior
  • Stikcy Toolbar - Particularly useful for the default full-height editor, the toolbar will stick to the page as you scroll down large content.
  • Toolbar - Gives you the ability to remove specific toolbar items. The default list displays all the currently available toolbar items.
  • Show HTML Snippets Preview - By default, HTML Snippets will be displayed as HTML code. When this setting is enabled, NextGen Editor will render the HTML within the editor. Both views will have a pencil icon that allows for editing the HTML directly.

    Even with previews enable, JavaScript won't be executed.

Automatic Text Transformations

It is possible to enable and disable sets of text transformations, conveniently grouped by typhography, quotations, symbols, mathematical. You can also create custom text transformations to meet your need. Whenever the input text matches a text transformation, this will get automatically converted to the appropriate output.

Automatic Text Transformations

Transformations per language

You can also customize the transformations per-language. For example, it might be certain countries quotes' transformation do not conform with the default transformations offered by NextGen Editor. In this case you might want to disable it only for this specific language.

Custom transformations can also be specified per language and will always take precedence over the default, global, definition.

Language Text Transformations

Advanced Transformations

NextGen Editor also supports custom transformations with regex and match replacement. This is considered an advanced feature and it is not represented in the Admin UI settings.

To create a custom advanced transformation, you can edit your configuration user/config/plugins/nextgen-editor.yaml and add a custom_advanced entry under the options.nextgenEditor.transformations. Languages are also supported, see the examples below for both regular and language specific custom advanced transformations.

Global Advanced Custom Transformation

The following example will replace "quoted strings" to « quoted strings ».

options:
  nextgenEditor:
    transformations:
      custom_advanced:
        '(^|\s)(")([^"]*)(")$': [null, '« ', null, ' »']

Language Specific Advanced Custom Transformation

This example works exactly like the one above, but is specific to fr languages only.

options:
  nextgenEditor:
    lang:
      transformations:
        custom_advanced:
          fr:
            '(^|\s)(")([^"]*)(")$': [null, '« ', null, ' »']

Additional Code Block Languages

It is possible to create additional code block languages. These will render fenced code with the specified language (ie, ```scss and on frontend as <code class="language-scss">).

It is the theme/plugin responsibility to provide highlight colors and style for the languages, NextGen Editor is only able to properly set classes based on the selected language and to provide backend visual aid regarding it.

Additional Code Block Languages

You can add as many additional languages as needed by clicking on the "+ Add Item" button:

  • Language - The language name. This should be lowercase, no spaces, single worded. Usually matches the filename extension, and it will become part of the classname when rendering.
  • Label - The label that identifies the language. You can use any character, but it is advised to keep it simple. (ie, Sass (Scss)).

Extra Media Embed Providers

The Media Embed feature brings support for inserting embeddable media such as YouTube or Vimeo videos and tweets into your rich text content.

NextGen Editor allows configuring custom Providers to expand on the predefined embeddable media. You can use the “Insert media” button in the toolbar to embed media, or you can also paste the media URL directly into the editor content, and it will be automatically embedded.

Media Embed Providers

You can create as many Extra Media Embed as you would like, each one requires a few fields to be filled:

  • Name - The name of the Media Embed, this should be unique and it is recommended to be a simple no-space, camelCased name
  • URL Regex - The regexp matching the URL when inserting media or pasting.
  • HTML - The HTML to be inserted for embedding the media. You can use ${match[X]} where X is the matching index from the URL Regex

Example

Here is an example of an embeddable media based on Unsplash Random API.
Consider the URL https://source.unsplash.com/random?nature,water. We want to be able to paste this URL or add it via "Insert media" and automatically convert it to an embedded image. As shown in the screenshot above, these are the parameters you would use:

Name URL Regex HTML
unsplashRandom /^source\.unsplash\.com\/random?.*/ <img src="https://${match[0]}" />

Displaying embedded media on your website

The media embed content saved by NextGen Editor is not going to be including previews or preformatted HTML, instead it is going to produce a semantic output that needs to be transformed on frontend by your theme.

NextGen Editor recommends using a client-side solution for transforming the embedded media on frontend via either Iframely or Embedly. More details can be found on the very detailed guide on CKEditor 5 Documentation

Markdown → Editor Options

These options control the loading behavior when NextGen Editor loads markdown from the .md file and converts it into a WYSIWYM editor environment.

Markdown to Editor

  • Line Breaks - Convert \n in paragraphs into <br> HTML tags
  • Linkify Text Links - Automatically turn text links into HTML links
  • Typographer - Enable some language-neutral replacement + quotes beautification
  • Codeblock Language Prefix - what to prefix codeblocks with
  • Fancy Quotes - Fancy quotes to be converted to regular quotes
  • Highlight Function - Advanced use only, change this only if you know what you are doing

Editor → Markdown Options

These options control the saving behavior when NextGen Editor saves the markdown to the .md file from the WYSIWYM editor environment. These control the markdown format behavior.

Editor to Markdown

HTML Snippets

At some point during writing content, you will find yourself needing to save raw HTML in your content but do not want said HTML to get converted to Markdown, nor having the HTML get modified by any internal operations.

NextGen Editor makes this possible thanks to the HTML Snippets, a custom shortcut available in the toolbar that allows to write any HTML that, upon saving, gets wrapped by a div with class name raw-html-embed.

HTML Snippets Editor

HTML Snippets Saved

Editing Source Code (Advanced)

It is possible to enable the sourceEditing plugin (by adding it into the toolbar options) to have a greater and more advanced control over the content of your page. This might be especially useful if you wish to write HTML code directly into the editor while having the spellcheck enabled.

HTML Source Code 1 HTML Source Code 2

The representation of the Source Code is not necessary the final value that will be stored after saving. There is a lot of conversions to Markdown and Shortcodes and cleanup that happen when saving and that cannot be captured by the Source Editing.

Extending NextGen Editor

The NextGen Editor has powerful functionality that allows it to be extended via plugins. Good examples of this capability can be found in both Shortcode Core and Page Inject plugins. These both already have support for NextGen editor custom blocks.

There a single plugin events that you can utilize to register your plugin's custom functionality: registerNextGenEditorPlugin. In Shortcode Core plugin, two methods are called from this one event. One to register some global functionality, and another to register specific shortcodes:

   public function registerNextGenEditorPlugin($event) {
        $config = $this->config->get('plugins.shortcode-core.nextgen-editor');
        $plugins = $event['plugins'];

        if ($config['env'] !== 'development') {
            $plugins['css'][] = 'plugin://shortcode-core/nextgen-editor/dist/css/app.css';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/dist/js/app.js';
        } else {
            $plugins['js'][]  = 'http://' . $config['dev_host'] . ':' . $config['dev_port'] . '/js/app.js';
        }

        $event['plugins']  = $plugins;
        return $event;
    }

In this example, some core CSS and JS functionality is added to the plugins element on the $event object that is passed to the event. This particular example even has the ability to load a dynamic app.js when in development mode.

The Shortcode Core plugin is providing everything you need to handle custom shortcode logic, so you don't need to repeat this functionality if all you developing a shortcode plugin. The Shortcode UI plugin is a custom shortcode plugin that relies on Shortcode Core for it's core functionality and also relies on it for it's core support for the NextGen Editor.

The Shortcode UI plugin listens for the registerNextGenEditorPlugin event and calls the registerNextGenEditorPluginShortcodes method.:

public static function getSubscribedEvents()
{
    return [
        ...
        'registerNextGenEditorPlugin' => ['registerNextGenEditorPluginShortcodes', 0],
    ];
}

Then this method simply adds the JS that controls the shortcode handling, and optionally any CSS that may be required:

public function registerNextGenEditorPluginShortcodes($event) {
    $plugins = $event['plugins'];

    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/shortcode-ui.js';
    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-accordion/ui-accordion.js';
    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-tabs/ui-tabs.js';
    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-browser/ui-browser.js';
    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-polaroid/ui-polaroid.js';
    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-animated-text/ui-animated-text.js';
    $plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-image-compare/ui-image-compare.js';

    $event['plugins']  = $plugins;
    return $event;
}

The first JavaScript file shortcode-ui.js contains the logic to create the plugin reference and adds a button group that will used by all the subsequent shortcodes so they can appear in a dropdown:

window.nextgenEditor.addHook('hookInit', () => {
  window.nextgenEditor.addShortcodePlugin('shortcode-ui', {
    title: 'Shortcode UI',
  });

  window.nextgenEditor.addButtonGroup('shortcode-ui', {
    icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2.5 19h19a2.5 2.5 0 002.5-2.5v-14A2.5 2.5 0 0021.5 0h-19A2.5 2.5 0 000 2.5v14A2.5 2.5 0 002.5 19zM18.25 7.5a.75.75 0 011.28-.53l2 2a.749.749 0 010 1.06l-2 2a.746.746 0 01-.53.22.738.738 0 01-.287-.057.75.75 0 01-.463-.693zM2.47 8.97l2-2a.75.75 0 011.28.53v4a.75.75 0 01-.463.693.738.738 0 01-.287.057.746.746 0 01-.53-.22l-2-2a.749.749 0 010-1.06z"/><circle cx="7.5" cy="22.5" r="1.5"/><circle cx="12" cy="22.5" r="1.5"/><circle cx="16.5" cy="22.5" r="1.5"/></svg>',
    label: 'Shortcode UI',
  });
});

The individual shortcode JavaScript files contain the logic that is pertinent to that shortcode. A relatively simple example is the UI Browser shortcode (ui-browser/ui-browser.js):

window.nextgenEditor.addShortcode('ui-browser', {
  type: 'block',
  plugin: 'shortcode-ui',
  title: 'UI Browser',
  button: {
    group: 'shortcode-ui',
    label: 'UI Browser',
    icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="4" y="8.25" style="width:16px;height:3px;" rx=".5" ry=".5"/><path d="M13.25 12.75a.75.75 0 000 1.5h6a.75.75 0 000-1.5zM19.25 16.25h-6a.75.75 0 000 1.5h6a.75.75 0 000-1.5z"/><rect x="4" y="12.75" style="width:7px;height:6.5px" rx="1" ry="1"/><path d="M24 4.75a3 3 0 00-3-3H3a3 3 0 00-3 3v14.5a3 3 0 003 3h18a3 3 0 003-3zm-14.346-1a.966.966 0 011.692 0 .969.969 0 01.154.5.969.969 0 01-.154.5.966.966 0 01-1.692 0 .969.969 0 01-.154-.5.969.969 0 01.154-.5zm-3.5 0a.966.966 0 011.692 0 .969.969 0 01.154.5.969.969 0 01-.154.5.966.966 0 01-1.692 0A.969.969 0 016 4.25a.969.969 0 01.154-.5zm-3.562.092A1 1 0 013.5 3.25a.985.985 0 01.846.5.969.969 0 01.154.5.969.969 0 01-.154.5.966.966 0 01-1.692 0 .969.969 0 01-.154-.5.979.979 0 01.092-.408zM22 19.25a1 1 0 01-1 1H3a1 1 0 01-1-1V7a.25.25 0 01.25-.25h19.5A.25.25 0 0122 7z"/></svg>',
  },
  attributes: {
    address: {
      type: String,
      title: 'Address',
      widget: 'input-text',
      default: '',
    },
    class: {
      type: String,
      title: 'Class',
      widget: 'input-text',
      default: '',
    },
  },
  titlebar({ attributes }) {
    return `address: <strong>${attributes.address}</strong>`;
  },
  content() {
    return `<div>{{content_editable}}</div>`;
  },
});

Configuration Details:

  • name (String) - shortcode name
  • schema (Object) - shortcode schema
    • type (String) - shortcode type ('block' or 'inline')
    • title (String) - shortcode title
    • parent (String) - parent shortcode name
    • attributes (Object) - shortcode attributes
    • type (JS Type) - attribute value type
    • title (String) - attribute title
    • widget (String|Object) - attribute editing widget type or schema
      • type (String) - widget type
      • visible (Function) - whether the widget is displayed in the settings popup
      • (arg) attributes - shortcode attribute values
      • (arg) parentAttributes - parent shortcode attribute values (if shortcode has parent in schema)
    • default (Any|Object) - attribute default value
      • value (Any) - attribute default value
      • preserve (Boolean) - whether to cut attribute if value is equal to default value on save
    • titlebar (Function) - creating shortcode titlebar content using ckeditor model writer (only for block type)
    • (arg) writer - ckeditor model writer instance
    • (arg) container - ckeditor model element of parent container (place where to insert titlebar content)
    • (arg) attributes - shortcode attribute values
    • (arg) parentAttributes - parent shortcode attribute values (if shortcode has parent in schema)
    • content (Function) - creating wrapper for shorcode inner content using ckeditor model writer
    • (arg) writer - ckeditor model writer instance
    • (arg) container - ckeditor model element of parent container (place where to insert wrapper content)
    • (arg) attributes - shortcode attribute values
    • (arg) parentAttributes - parent shortcode attribute values (if shortcode has parent in schema)
    • (return) - ckeditor model element of parent container for content (place where to put shortcode inner content)
    • preserve (Object) - list of html tags used in wrapper for shortcode inner content
    • block (Array) - for block type
      • (String|Object)
    • inline (Array) - for inline type
      • (String|Object)

Example #1 (block type)

window.nextgenEditor.addShortcode('fieldset', {
  type: 'block',
  title: 'Fieldset',
  attributes: {
    title: {
      type: String,
      title: 'Title',
      widget: 'input-text',
      default: '',
    },
  },
  titlebar(writer, container, attributes) {
    const strong = writer.createElement('strong');
    writer.append(writer.createText(attributes.title), strong);
    writer.append(writer.createText('title: '), container);
    writer.append(strong, container);
  },
  upcast(writer, container, attributes) {
    const fieldset = writer.createElement('fieldset');
    writer.append(fieldset, container);

    const legend = writer.createElement('legend');
    writer.append(writer.createText(attributes.title), legend);
    writer.append(legend, fieldset);

    const content = writer.createElement('div');
    writer.append(content, fieldset);

    return content;
  },
  preserve: {
    block: [
      'fieldset',
      { name: 'legend', readonly: true },
    ],
  },
});

Example #2 (inline type)

window.nextgenEditor.addShortcode('color', {
  type: 'inline',
  title: 'Color',
  attributes: {
    value: {
      type: String,
      title: 'Value',
      widget: 'input-text',
      default: '',
    },
  },
  upcast(writer, container, attributes) {
    const font = writer.createElement('font', { color: attributes.value });
    writer.append(font, container);

    return font;
  },
  preserve: {
    inline: ['font'],
  },
});

Credits

This plugin is built on-top of the fantastic CKEditor5 plugin