views:

767

answers:

3

How can i hook-up WMD editor on to my django forms?

+3  A: 

From the readme.txt in the current WMD download:

To install the editor, include wmd.js right before your closing <body> tag:

<script type="text/javascript" src="wmd/wmd.js"></script>

Example:

<!DOCTYPE html>
<html>
  <head>
    <title>My Page</title>
  </head>
  <body>
    <textarea></textarea>
    <script type="text/javascript" src="wmd/wmd.js"></script>
  </body>
</html>

By default, WMD will turn the first textarea on your page into an editor. You can modify this behavior with the wmd-ignore class, described below. (It's also possible to disable autostart and instantiate the editor through JavaScript, as shown in apiExample.html. But be warned that the current API will change a lot in the upcoming open-source release; it was never actually meant for public consumption.)

So, add the necessary code to the template you're using to render the form, and make sure that the textarea you want to use WMD on is the first on the page, and you'll be good to go.

Harper Shelby
+8  A: 

Here is complete Django widget class:

class WMDEditor(forms.Textarea):

    def __init__(self, *args, **kwargs):
        attrs = kwargs.setdefault('attrs', {})
        if 'cols' not in attrs:
            attrs['cols'] = 58
        if 'rows' not in attrs:
            attrs['rows'] = 8
        super(WMDEditor, self).__init__(*args, **kwargs)

    def render(self, name, value, attrs=None):
        rendered = super(WMDEditor, self).render(name, value, attrs)
        return rendered + mark_safe(u'''<script type="text/javascript">
            wmd_options = {
                output: "Markdown",
                buttons: "bold italic | link blockquote code image | ol ul"
            };
            </script>
            <script type="text/javascript" src="%sjs/wmd/wmd.js"></script>''' % settings.MEDIA_URL)

Use it in your form definition like text = forms.CharField(widget=WMDEditor).

zgoda
I'll need to try that... I never knew you could do that!
Lucas Jones
Credit should go to Jannis Leidel, I borrowed the idea from him. :)
zgoda
+1  A: 

I just finished doing this. There are a couple more subtleties to be aware of that aren't covered in the other answers (so far).

  1. First, in order for Django to properly retrieve the value of WMD editor when the form is submitted, it needs to have Django's value for id="" set on it. Normally this would look something like 'id_'

  2. But #1 creates a problem: WMD Editor is hard-coded to look for id="wmd-input" to know what text area it makes use of.

  3. Therefore, what we need is a way to pass the value of the id attribute to WMD. I've done this myself by having the Django template render a global javascript variable, that when executed client side will be used by WMD to properly locate the textarea tag.

  4. If we're re-doing the WMD id tag, then we'll also need to make sure the CSS still works

  5. Lastly, in order for the value to be pre-populated by Django, we need to make sure that value is being rendered in the textarea tag

So here's some code for you.

widgets.py

from django import forms
from django.contrib.admin import widgets as admin_widgets
from django.template import loader, Context
from django.utils.html import conditional_escape
from django.utils.encoding import force_unicode

class WMDEditor(forms.Textarea):
    def render(self, name, value, attrs=None):
        # Prepare values
        if not value:
            value = ''
        attrs = self.build_attrs(attrs, name=name)

        # Render widget to HTML
        t = loader.get_template('wmd/widget.html')
        c = Context({
            'attributes' : self._render_attrs(attrs),
            'value' : conditional_escape(force_unicode(value)),
            'id' : attrs['id'],
        })

        return t.render(c)

wmd/widget.html

(name this whatever you need for your app)

<div class="wmd-wrapper">
    <div id="wmd-button-bar" class="wmd-panel"></div><br/>
    <textarea class="wmd-panel wmd-input" {{ attributes|safe }}>{{ value }}</textarea><br/>
    Preview
    <div id="wmd-preview" class="wmd-panel"></div><br/>
</div>
<script type="text/javascript">// <![CDATA[
    var WMD_TEXTAREA_ID = '{{ id }}'
// ]]> </script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/wmd/wmd.js"></script>

*NOTE: You might need to adjust the MEDIA_URL depending on how you're handling that (custom template tag, middleware, whatever). If you're new to Django and don't understand what I just said, just hardcode the value in for now to get it working and learn what that all means later.*

Lastly, you'll need to make 1 minor edit to the WMD source (note that I'm using the StackOverflow fork, so this might be slightly different for other versions)

wmd.js

// This is around line 69
// Change this -> this.input = doc.getElementById("wmd-input");
// Into this:
this.input = doc.getElementById(WMD_TEXTAREA_ID);

If you're using the wmd.css and haven't written your own already, you'll also need to make a slight update to that. Because that element is no longer #wmd-input, we'll need to update it to make sure it uses the wmd-input class:

wmd.css

.wmd-input,   /* <-- Add this here, around line 33 */
#wmd-input 
{ 
    height: 250px;
    width: 100%;
    background-color: #FFFFFF;
    border: 1px solid #4d86c1;
}

Whew! That was a bunch. Hope that helps everyone.

T. Stone