Add some blockquote buttons to Wagtail CMS’ WYSIWYG Editor

Wagtail is an amazing Django based open source CMS, find out more at http://wagtail.io/

Wagtail’s Editor is based on Hallo.js, we needed to add a normal blockquote button,and one with a class attribute for a pull out style quote.

There are two steps to doing this, firstly override the whitelister’s element rules (this stops certain tags from being saved in wagtail, if you don’t do this the Blockquote HTML tag gets stripped out. Secondly add the JS to introduce the custom buttons.

The editor icons are based on font awesome fonts, so it’s easy to pick whatever icon you want by changing the icon: ‘fa fa-stack-exchange’ value

To start add a wagtails_hooks.py file into the app folder of your wagtail project.

wagtail_hooks.py


from django.utils.safestring import mark_safe
from django.utils.html import format_html, format_html_join
from django.conf import settings
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.whitelist import attribute_rule, check_url, allow_without_attributes

def whitelister_element_rules():
    return {
        'a': attribute_rule({'href': check_url, 'target': True}),
        'blockquote': attribute_rule({'class': True}),
    }
hooks.register('construct_whitelister_element_rules', whitelister_element_rules)

def editor_js():
	js_files = [
		'yourapp/js/hallo-custombuttons.js',
	]
	js_includes = format_html_join('\n', '<script src="{0}{1}"></script>',
		((settings.STATIC_URL, filename) for filename in js_files)
	)

	return js_includes + format_html(
		"""
		<script>
			registerHalloPlugin('blockquotebutton');
      registerHalloPlugin('blockquotebuttonwithclass');
		</script>
		"""
	)

hooks.register('insert_editor_js', editor_js)

def editor_css():
	return format_html('<link rel="stylesheet" href="'+ settings.STATIC_URL + 'yourapp/css/vendor/font-awesome/css/font-awesome.min.css">')

hooks.register('insert_editor_css', editor_css)

Then add a JS file into your app’s static JS folder.

hallo_custombuttons.js


(function() {
    (function($) {
        return $.widget('IKS.blockquotebutton', {
            options: {
                uuid: '',
                editable: null
            },
            populateToolbar: function(toolbar) {
                var button, widget;

                widget = this;

                button = $('<span></span>');
                button.hallobutton({
                    uuid: this.options.uuid,
                    editable: this.options.editable,
                    label: 'Blockquote',
                    icon: 'fa fa-quote-left',
                    command: null
                });

                toolbar.append(button);

                button.on('click', function(event) {
                    return widget.options.editable.execute('formatBlock',
                                                           'blockquote');
                });
            }
        });
    })(jQuery);
}).call(this);

(function() {
  (function($) {
    return $.widget("IKS.blockquotebuttonwithclass", {
      options: {
        uuid: '',
        editable: null
      },
      populateToolbar: function(toolbar) {
        var button, widget;

        widget = this;
        button = $('<span></span>');
        button.hallobutton({
          uuid: this.options.uuid,
          editable: this.options.editable,
          label: 'Pull Out Quote',
          icon: 'fa fa-stack-exchange',
          command: null
        });
        toolbar.append(button);
        return button.on("click", function(event) {
          var insertionPoint, lastSelection;

          lastSelection = widget.options.editable.getSelection();
          insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last();
					var elem;
					elem = "<blockquote class='pullout'>" + lastSelection + "</blockquote>";

					var node = lastSelection.createContextualFragment(elem);

					lastSelection.deleteContents();
					lastSelection.insertNode(node);

					return widget.options.editable.element.trigger('change');
        });
      }
    });
  })(jQuery);

}).call(this);

There’s probably a more concise way to do this but it worked for me!

Advertisements