Skip to content

Instantly share code, notes, and snippets.

@loicteixeira
Last active August 17, 2017 23:01
Show Gist options
  • Save loicteixeira/27d978802b7549c3c423d741ba270cc1 to your computer and use it in GitHub Desktop.
Save loicteixeira/27d978802b7549c3c423d741ba270cc1 to your computer and use it in GitHub Desktop.
[Wagtail 1.6.x] Rich editor configuration per field

From Wagtail 1.7 (maybe?) options will be passed to the widget when instantiated. See https://github.com/torchbox/wagtail/commit/5732e215b0774c22d8e9bf210aa1491353595406

Given the following config:

WAGTAILADMIN_RICH_TEXT_EDITORS = {
    'default': {
        'WIDGET': 'wagtail.wagtailadmin.rich_text.HalloRichTextArea'
    },

    'simple_editor': {
        'WIDGET': 'core.rich_text.CustomisableRichTextArea',
        'OPTIONS': { ... }
    },

    'advanced_editor': {
        'WIDGET': 'core.rich_text.CustomisableRichTextArea',
        'OPTIONS': { ... }
    }
}

So it is possible to use the same "editor" with different configurations. The RichTextBlock will simply need to be defined as follow RichTextBlock(editor='advanced_editor') to use that specific config and CustomisableRichTextArea will be passed an id and the options.

CustomisableRichTextArea should handle the upgrade gracefully (if the implementation does not change) but the config will need to be updated as shown above.

In the meantime, it is needed to subclass CustomisableRichTextArea and define editor_name so the corresponding options can be fetched. Each "editor" will have a corresponding class.

The config will then look like this:

WAGTAILADMIN_RICH_TEXT_EDITORS = {
    'default': {
        'WIDGET': 'wagtail.wagtailadmin.rich_text.HalloRichTextArea'
    },

    'simple_editor': {
        'WIDGET': 'core.rich_text.SimpleRichTextArea',
        'OPTIONS': { ... }
    },

    'advanced_editor': {
        'WIDGET': 'core.rich_text.AdvancedRichTextArea',
        'OPTIONS': { ... }
    }
}

Other useful discussions around this topic:

import json
from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.forms import Media
from wagtail.wagtailadmin.rich_text import HalloRichTextArea, DEFAULT_RICH_TEXT_EDITORS
class CustomisableRichTextArea(HalloRichTextArea):
editor_name = None # Overwrite in subclasses
def __init__(self, attrs=None, options=None):
self.options = options or self.get_options()
super().__init__(attrs)
def get_options(self):
editor_settings = getattr(settings, 'WAGTAILADMIN_RICH_TEXT_EDITORS', DEFAULT_RICH_TEXT_EDITORS)
editor = editor_settings.get(self.editor_name, {})
options = editor.get('OPTIONS', {})
return options
def render_js_init(self, id_, name, value):
return "CustomisableRichTextEditable({0}, {1});".format(
json.dumps(id_), json.dumps(self.options))
@property
def media(self):
hallo_media = super().media
widget_media = Media(js=[
static('js/wagtailadmin-customisable-richtextarea.js')
])
return hallo_media + widget_media
class FormatOnlyRichTextArea(CustomisableRichTextArea):
editor_name = 'format_only'
class FormatAndLinkRichTextArea(CustomisableRichTextArea):
editor_name = 'format_and_link'
class SimpleRichTextArea(CustomisableRichTextArea):
editor_name = 'simple'
class ComplexLinkRichTextArea(CustomisableRichTextArea):
editor_name = 'complex_link'
'use strict';
/* This function is mostly a copy of `makeRichTextEditable`
from `wagtail/wagtailadmin/static_src/wagtailadmin/js/page-editor.js`
but allows an editorConfig to be passed to.
If `makeRichTextEditable` was updated to allow such thing, we can probably do the following:
- This file should be deleted.
- `core.rich_text.CustomisableRichTextArea.render_js_init` should be updated to use `makeRichTextEditable` instead.
- `core.rich_text.CustomisableRichTextArea.media` should be deleted.
*/
window.CustomisableRichTextEditable = function(id, editorConfig) {
var input, richText, removeStylingPending, closestObj, toolbarCssClass, halloConfig;
input = $('#' + id);
richText = $('<div class="richtext"></div>').html(input.val());
richText.insertBefore(input);
input.hide();
removeStylingPending = false;
function removeStyling() {
/* Strip the 'style' attribute from spans that have no other attributes.
(we don't remove the span entirely as that messes with the cursor position,
and spans will be removed anyway by our whitelisting)
*/
$('span[style]', richText).filter(function() {
return this.attributes.length === 1;
}).removeAttr('style');
removeStylingPending = false;
}
closestObj = input.closest('.object');
toolbarCssClass = (closestObj.hasClass('full')) ? 'full' : (closestObj.hasClass('stream-field')) ? 'stream-field' : '';
halloConfig = {
plugins: {
halloformat: {},
halloheadings: {formatBlocks: ['p', 'h2', 'h3', 'h4', 'h5']},
hallolists: {},
hallohr: {},
halloreundo: {},
hallowagtaillink: {},
hallorequireparagraphs: {}
},
toolbarCssClass: toolbarCssClass,
toolbar: 'halloToolbarFixed'
};
$.extend(halloConfig, editorConfig);
richText.hallo(halloConfig).bind('hallomodified', function(event, data) {
input.val(data.content);
if (!removeStylingPending) {
setTimeout(removeStyling, 100);
removeStylingPending = true;
}
}).bind('paste', function(event, data) {
setTimeout(removeStyling, 1);
/* Animate the fields open when you click into them. */
}).bind('halloactivated', function(event, data) {
$(event.target).addClass('expanded', 200, function(e) {
/* Hallo's toolbar will reposition itself on the scroll event.
This is useful since animating the fields can cause it to be
positioned badly initially. */
$(window).trigger('scroll');
});
}).bind('hallodeactivated', function(event, data) {
$(event.target).removeClass('expanded', 200, function(e) {
$(window).trigger('scroll');
});
});
};
@loicteixeira
Copy link
Author

With Wagtail 1.7, the CustomisableRichTextArea won't need to call get_options since the options will be passed directly to it but is still needed because the HalloRichTextArea still don't accept options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment