Login

Wagtail Markdown Streamfield

Author:
stuartaccent
Posted:
April 7, 2017
Language:
Python
Version:
Not specified
Score:
0 (after 0 ratings)

A markdown streamfield for wagtail that includes a live preview.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# Add a utility to render the markdown
from markdown import markdown

def markdownify(content):
    return markdown(
        content,
        [
            'markdown.extensions.fenced_code',
            'codehilite',
        ],
    )

# add the view to somewhere
from django.http import HttpResponse
from django.utils.safestring import mark_safe
from django.views import View
from somewhere.utils import markdownify

class MarkdownifyView(View):
    def post(self, request, *args, **kwargs):
        markdown = markdownify(request.POST['content'])
        return HttpResponse(mark_safe(markdown))

# and the url
urlpatterns = [
    ...
    url(r'^markdownify/$', MarkdownifyView.as_view(), name='markdownify'),
]

# The Textblock looks like
from django.urls import reverse
from django.utils.safestring import mark_safe
from wagtail.wagtailcore import blocks
from somewhere.utils import markdownify

class MarkDownBlock(blocks.TextBlock):
    """ MarkDown Block """

    class Meta:
        icon = 'code'

    def render_basic(self, value, context=None):
        md = markdownify(value)
        return mark_safe(md)

    def render_form(self, value, prefix='', errors=None):
        content = super(MarkDownBlock, self).render_form(value, prefix, errors)
        url = reverse('markdownify')
        return mark_safe("""
        <div class="markdown" data-url="{url}">
            {content}
            <div class="markdown-preview field-content"></div>
        </div>
        <script>$('#{prefix}').closest('.markdown').markdown();</script>
        """.format(content=content, prefix=prefix, url=url))

# And a javascript file to handle live reload in the preview (static/js/markdown.js)
(function ($) {
    $.fn.markdown = function() {

        return this.each( function() {

            var getMarkdown = function() {
                var form = new FormData();
                form.append('content', markdownEditor.val());
                form.append('csrfmiddlewaretoken', getCookie());
                $.ajax({
                    type: 'POST',
                    url: markdown.data('url'),
                    data: form,
                    processData: false,
                    contentType: false,

                    success: function(response) {
                        markdownPreview.html(response);
                        markdown.trigger('markdown.update', [response]);
                    },

                    error: function(response) {
                        console.log("error", response);
                        markdown.trigger('markdown.update_error', [response]);
                    }
                });
            };

            var getCookie = function() {
                return $('[name=csrfmiddlewaretoken]').val();
            };

            var timeout;

            var markdownify = function() {
                clearTimeout(timeout);
                timeout = setTimeout(getMarkdown, 500);
            };

            // events
            var onKeyDownEvent = function(e) {
                if (e.keyCode === 9) { // Tab
                    var start = this.selectionStart;
                    var end = this.selectionEnd;

                    $(this).val($(this).val().substring(0, start) + "\t" + $(this).val().substring(end));
                    this.selectionStart = this.selectionEnd = start + 1;

                    markdownify();

                    return false;
                }
            };

            var onInputChangeEvent = function() {
                markdownify();
            };

            // initialize
            var markdown = $(this);
            var markdownEditor = $(this).find('textarea');
            var markdownPreview = $(this).find('.markdown-preview');

            markdownEditor.on('keydown.markdown', onKeyDownEvent);
            markdownEditor.on('input.markdown propertychange.markdown', onInputChangeEvent);

            markdown.trigger('markdown.init');

            markdownify();
        });
    };

})(jQuery);

# finally link the js file into the cms admin site (wagtail_hooks.py in the root of an app)
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.utils.html import format_html
from wagtail.wagtailcore import hooks

@hooks.register('insert_editor_js')
def editor_js():
    return format_html(
        '<script type="text/javascript" src="{}">',
        static('js/markdown.js')
    )

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

sam-mi (on June 5, 2017):

This looks nice, I tried to implement it but there was an error:<br /> wagtailcore/blocks/stream_block.py", line 162, in value_from_datadict int(data['%s-%d-order' % (prefix, i)]), ValueError: invalid literal for int() with base 10: ''

You should look at integrating your work with<br /> https://github.com/FlipperPA/wagtailmarkdownblock

#

Please login first before commenting.