Yawd website

sep16

Embed the elRTE WYSIWYG editor in your django applications

Elrte is an open-source "What You See Is What You Get" editor written in jQuery. In my latest projects i tend to prefer this over other solutions (such as TinyMCE, (F)CKEditor etc) for its elegant UI, ease of use and mainly for its accompanying File manager (the elFinder). In this article we'll se how to integrate elRTE with django models, so that your text fields can take advantage of this editor.

First of all you must download the elRTE source code and place under your static files directory. Although chances are that you already have configured your static files management, if not please see Django documentation on static failes. Note that the provided link describes the latest Django 1.3 release, which is significantly different from version 1.2 as far as static files management is concerned. Aside from the elRTE source code, you must also download the jQuery javascript library as well as jQuery UI - a very popular set of jQuery effects and UI widgets.

Screenshot of the yawd website admin panel - the elRTE django widgetFigure 1. Screenshot of the yawd website admin panel -the elRTE django widget

One of the best approaches for integrating a WYSIWYG editor in django, is to create a widget that can later be assigned to our TextField model fields. For the time being we will disable the elFinder file manager, as it needs a special connector implementation to work with django (I will probably cover this on a future article). Create a new module named widgets.py in your application and place the following widget declaration inside:

widgets.py:
from django import forms
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
import settings

class ElrteWidget(forms.Textarea):
    """
    A widget that draws the Elrte WYSIWYG editor for large text fields.
    """
    def __init__(self, attrs=None, lang='en', styleWithCSS=False, height=400, width=0, toolbar='maxi'):
        self.lang, self.styleWithCSS, self.height = lang, styleWithCSS, height
        self.width, self.fmAllow, self.toolbar = width, fmAllow, toolbar
        super(ElrteWidget, self).__init__(attrs)

    def _media(self):
        l_js = [ settings.JQUERY_URL, settings.ELRTE_JS_ELRTE_URL, settings.JQUERY_UI_URL ]
        if self.lang != 'en':
            l_js.append( '%selrte.%s.js' % (settings.ELRTE_LANG_URL,self.lang) )

        l_css = [settings.ELRTE_CSS_ELRTE_URL, settings.JQUERY_UI_CSS_URL]

        return forms.Media( css= {'screen':  l_css }, js=l_js )

    media = property(_media)

    def render(self, name, value, attrs=None):
        width = ('    width: %i,' % self.width) if self.width >0 else ''

        html = super(ElrteWidget, self).render(name, value, attrs)
        html += ('<script type="text/javascript">'
                '(function($) { '
                 '$(document).ready(function() {'
                 '  var opts = {'
                 '    doctype : \'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">\','
                 '    lang : \'%(lang)s\','
                 '    styleWithCss: %(style)s,'
                 '    height: %(height)i,'
                 '%(width)s'
                 '    fmAllow: false,'
                 '    toolbar: \'%(toolbar)s\','
                 '     };'
                 '  $("#%(id)s").elrte(opts);'
                 '});'
                 '})(jQuery);'
                 '</script>' % { 'lang' : self.lang, 
                                'id' : attrs['id'],
                                'style' : str(self.styleWithCSS).lower(),
                                'width' : width,
                                'height' : self.height,
                                'toolbar' : self.toolbar,
                                })
        return mark_safe(html)

The __init__ accepts a couple of elRTE's options as arguments to adjust elRTE according to our taste. The 'lang' argument -which defaults to 'en'- sets elRTE's language. For a list of available languages please visit the elRTE website. If the 'styleWithCSSb argument if set to True, text will be formated using span-tag with style attribute, else semantic tags like strong, em etc will be used. The 'width' and 'height' attributes define the editor's size as expected. Finaly 'toolbar' chooses which one of the available predefined toolbars will be used. Options are:

  • tiny: only buttons to change text style (bold, italic, underline, strike, subscript, superscript)
  • compact: the same as tiny + save, undo/redo, text alignment, list, link, fullscreen
  • normal: compact + copy/paste, colors, paddings, block-elemet, images
  • complete: normal + text size, style and font formating
  • maxi: complete + tables

All in all, the widget simply renders the javascript code needed for your textareas to be transformed into a full-featured editor. To let the widget know the filepaths it needs (such as the jQuery library js file, elRTE source files, CSS files etc) we will create a settings.py inside the application (you could also use the global settings.py file of your project as long as you modify the import settings statement on the ElrteWidget code above) and define the following settings:

settings.py
from django.conf import settings

JAVASCRIPT_URL = settings.STATIC_URL+'js/'
CSS_URL = getattr(settings, 'CSS_URL', settings.MEDIA_URL+'css/')

JQUERY_URL = getattr(settings, 'JQUERY_URL', '%sjs/jquery-1.6.2.min.js' % settings.STATIC_URL)
JQUERY_UI_URL = getattr(settings, 'JQUERY_UI_URL', 
                  '%sjs/jquery-ui-1.8.11.custom.min.js' % settings.STATIC_URL)
JQUERY_UI_CSS_URL = getattr(settings, 'JQUERY_UI_CSS_URL',
                  settings.STATIC_URL+'css/jquery-ui/custom/jquery-ui-1.8.7.custom.css')

ELRTE_ROOT_URL = getattr(settings, 'ELRTE_ROOT_URL', settings.STATIC_URL+'elrte/')
ELRTE_LANG_URL = getattr(settings, 'ELRTE_LANG_URL', ELRTE_ROOT_URL+'js/i18n/')
ELRTE_JS_ELRTE_URL = getattr(settings, 'ELRTE_JS_ELRTE_URL',
                  ELRTE_ROOT_URL+'js/elrte.min.js')
ELRTE_CSS_ELRTE_URL = getattr(settings, 'ELRTE_CSS_ELRTE_URL',
                  ELRTE_ROOT_URL+'css/elrte.min.css')

Of course you should replace the above file paths with the actual paths of the downloaded files. We are now ready to use the widget. Say that we have an Article model which is defined as follows:

models.py:
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

If we want content to be an elRTE editor field in the admin site (as shown in Figure 1), all we have to do is replace the default TextArea widget with our ElrteWidget:

from django.contrib import admin
from django.forms import ModelForm
from widgets import ElrteWidget
from models import Article

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        widgets = {
            'name': ElrteWidget(),
        }

class ArticleAdmin(admin.ModelAdmin):
    form = ArticleForm

admin.site.register(Article, ArticleAdmin)

In my own projects I have also implemented a custom ModelField that uses the ElrteWidget as well as the Elfinder file manager. Once my implementation is mature enough I might publish the code as a standalone open source django application. Have fun!

Meta

Published: Sept. 16, 2011
Comments:  
Word Count: 1,506
Comments powered by Disqus