On this page

All pages






What is the Weka editor?

Weka is a new content editor tool introduced in Totara 13. It can be selected and used as a default editor along with Atto, plain text, and other editor plugins. Along with the editor itself, Weka brings a new format type into Totara editor types (FORMAT_JSON = 42) in addition to existing HTML, plain text, markdown and Moodle formats.

The main reason for creating a new editor was the limitations of existing formats for mobile rendering. This editor is built with native mobile support in mind and allows proper rendering of all content types on a mobile client. This also brings some limitations to formatting capabilities and styling because each node implemented for Weka must also be implemented on a mobile client for each supported platform and checked that it is displaying correctly.

However, as Weka is a pluggable editor the number of node types and styles will grow over time in line with users' needs and requirements. Custom plugins can also be added when needed.

On a website, Weka can render content in three formats: TUI, Legacy HTML, and Plain text. TUI is a front-end component library made for Totara and based on the vue.js framework. Most standard Weka plugins are made using TUI and allow the adding and displaying of interactive content. In places, where TUI is not used, such as standard course pages or dashboards, content created in Weka will be displayed in a simplified HTML format (this is also standard output). 

How the Weka editor works

The Weka editor is made using the ProseMirror set of tools. Their guide provides extensive documentation and examples which will be particularly helpful for anyone wishing to extend or improve Weka editor. The main difference to existing editors is the new format used to store content. To allow consistent cross-platform compatibility Weka uses JSON to store content data.

As an example, below is a simplified data flow:

Simplified data flow diagram

The data that a user puts into the editor will be converted into JSON, and that JSON content will be saved into the database. During content rendering, the formatter converts the JSON into HTML content. To do that the formatter needs to know all the node types declared in the system. Each of the node types has a structure described in https://prosemirror.net/docs/guide/#doc.data_structures.

Note that the JSON editor format is a part of the core while the implementation of this format is done via plugins. Weka editor, in turn, is a plugin that generates FORMAT_JSON content. This means that the JSON schema for each node must always be the same across the system, and future editors will need to support the same JSON schema.

Using the Weka editor

To add the Weka editor just add the <Weka> component in required places, as shown in the example below:

<template>
<!-- .... ->
        <Weka
          :id="id"e
		  :usage-identifier="{component: 'totara_playlist', area: 'summary'}"
		  :variant="'variant-name'" // the variant name help to identify what extensions to use within the editor.
          :value="doc" // Document model (content)
          :placeholder="$str('adddescription', 'totara_playlist')"
          class="tui-playlistForm__description__formRow__textArea"
        />
<!-- .... ->
</template>
<script>
// ...
import Weka from 'editor_weka/components/Weka';
// ...
export default {
  components: {
    // ...
    Weka,
    // ...
  }
}

Adjusting Weka configuration and enabled features for components

At the moment, the Weka configurations are stored as a variant preset within the core of the Weka plugin. Currently, there is no support to define which extensions should be enabled on the component level. However, developers can still include extra extensions via the front-end, for example:

<template>
<!-- .... ->
        <Weka
          :id="id"e
		  :usage-identifier="{component: 'totara_playlist', area: 'summary'}"
		  :variant="'variant-name'" // the variant name help to identify what extensions to use within the editor.
          :value="doc" // Document model (content)
          :placeholder="$str('adddescription', 'totara_playlist')"
		  :extra-extensions="[
		  	{
				name: "weka_extension_name",
				options: {} // Whatever the parameters that you want to pass to the extensions.
			}
		  ]"
          class="tui-playlistForm__description__formRow__textArea"
        />
<!-- .... ->
</template>

Adding an extension

To add a new component to the Weka editor developers need to write an extension, which is about creating a new sub plugin of Weka located in directory "server/lib/weka/extensions"

The class file must be located at the root of the directory "classes", and the class name must be "extension" to allow the Weka API to discover it.

For example:

server/lib/editor/weka/extensions/emoticon/classes/extension.php
namespace weka_emoticon;

/**
 * Extensions for emoticon.
 */
final class extension extends extension {
    /**
     * @return string
     */
    public function get_js_path(): string {
        return 'weka_emoticon/js/emoticon';  // Path to frontend part of the component
    }
}

The name of the extension is the name of the sub-plugin plus the class name as a suffix.  From the example above it would be: weka_emoticon_extension

Front-end extensions must extend BaseExtension class whilst adding required functionality such as nodes, toolbar items, keymaps, etc. as shown in the example below:

client/src/weka_emoticon/js/emoticon.js
import BaseExtension from './Base';
import { ToolbarItem } from '../toolbar';
import { langString } from 'tui/i18n';

class Emoticon extends BaseExtension {
  nodes() { // returns object (hashmap) of nodes
    return {
      emoticon: { // Node name
        schema: {
          group: 'block',
          parseDOM: [{ tag: 'hr' }], // ParseDOM used for HTML parsing during copy-paste
          toDOM() {  // Renderer
            return ['hr'];
          },
        },
      },
    };
  }

  toolbarItems() {
    return [
      new ToolbarItem({
        group: 'embeds',
        label: langString('emoticon', 'editor'),
        icon: 'editor_weka|arrows',
        execute: editor => {
          editor.execute((state, dispatch) => {
            let hr = state.schema.nodes.ruler;
            dispatch(state.tr.replaceSelectionWith(hr.create()));
            editor.view.focus();
          });
        },
      }),
    ];
  }

  keymap(bind) {
    const { emoticon } = this.getSchema().nodes;
    bind('Mod-_', (state, dispatch) => {
      dispatch(state.tr.replaceSelectionWith(ruler.create()).scrollIntoView());
      return true;
    });
  }
}

export default opt => new RulerExtension(opt);

Enabling multi lang extension

Multi lang needs to be enabled manually since it doesn't belong to any existing variant. The object needs to be passed as part of the array prop ":extra-extensions", as shown in the example below:

		<Weka
          ...
          :extra-extensions="[
            {
              name: 'weka_simple_multi_lang_extension',
              options: {
                context_id: contextId,
              },
            },
          ]"
          ...
        />