-
Adding a simple WYSIWYM Markdown editor to your models
March 9, 2009 at 10:01:54 CETI'm a Markdown fan. Period. Since I first discovered it some years ago, I've been using it for storing all my "output-to-HTML" texts.
Blango, the blog engine I wrote for this site, uses Markdown for post contents. As I've said, I really like Markdown, but typing (and sometimes escaping) all those *[]() gets boring from time to time. I've been looking for something like a WYSIWYG editor for some time, but none of the available choices seemed good enough for me. However, I recently stumbled upon WMD.
So, what's WMD and why should I care?
In the author's own words:
So WMD is something new: a Wysiwym Markdown editor.
Wysiwym stands for What You See Is What You Mean. WMD produces clean semantic HTML, leaving presentation details like fonts and colors up to style sheets. But you're not left in the dark about cosmetics; as you type, WMD's live preview shows you exactly what your text will look like after the current styles have been applied.
Markdown is a simple markup language that's as easy to read and write as plain-text email. WMD uses Markdown as an input format, converting it into HTML behind the scenes. You can also mix raw HTML with Markdown, so the full expressiveness of HTML is always there if you need it.
Plus, WMD is comprised of Javascript and image files, doesn't need server support, doesn't depend on any Javascript Framework and it's just a small rectangle over the textarea. Id est, it doesn't get in my way.
Downloading the required files
WMD was a hosted service until recently and is currently provided as a set of Javascript obfuscated files. The author is working on a new release, but this is all we got for now. Anyway, good enough.
You can get the last version (1.0.1 as the time of this writing) from project's downloads page. Just open the zipfile and copy the wmd directory to your MEDIA_ROOT.
If you're storing your posts as Markdown and doing the conversion to HTML (as you should be doing), you need to take one extra step. Open wmd/wmd.js and change line 4 to read:
Attacklab.wmd_defaults={version:1,output:"Markdown",lineLength:40,delayLoad:false};
WMD will preprocess the Markdown and send HTML to the server otherwise.
Adding WMD support to your models
By default, WMD will attach an editor to every textarea. So, all we need to do is tell the admin interface to include wmd/wmd.js when adding or editing our models. This can be done by using the inner class Media in your ModelAdmin subclass. For example:
class EntryAdmin(admin.ModelAdmin): class Media: js = ( 'js/wmd/wmd.js', )
And you're ready to go. Now your models will have a small rectangle featuring the WMD over every textarea.
-
Blango, django-geonames, oauthsp and wapi hosted at GitHub
Feb. 17, 2009 at 01:22:19 CETI've finished moving some of my git repos to GitHub. You can now check Blango, django-geonames, oauthsp and wapi from there. Those are, from now on, the official repos. The old ones at byNotes have already been closed.
-
Leaving byNotes
Feb. 15, 2009 at 23:18:54 CETWell, let me start by explaining all those mails about byNotes I haven't answered in the last few months. Around mid November I got contacted by the recruiting staff of a social network. At that time, I was still working in the project for my degree (computer science students in Spain are required to write a project for the university in order to get the degree) and I wanted to concentrate on it, but I decided to just take the interviews just in case. They finally told me they wanted me to join them and we agreed to continue the negotiations once I had finished my project.
Fast forward to January, my project is done and christmas holidays are gone, time to reestablish negotiations. Salary is good, schedule is very good (telecommuting 99% of the time) but there's something bad: I must leave byNotes, since management considers it's competing with them.
It wasn't an easy decision, but it was the best option. byNotes has been very fun, but also such a time drain to me, and I haven't got any single eurocent from it. Plus, I'm a coder, not a designer nor a business guy, and I don't have too much money to spend on it. So I really don't think I could make it profitable.
As of today, I've been already working for my new employer for some weeks. My name still figures as the domain holder for bynotes.com and in some about pages, but that should change soon. I'm sure the new maintainers will do a good job at keeping the project alive.
And now comes the interesting part for you, developers. What happens with my free software projects? Well, I'm keeping all of them, but I won't be able to spend some much time working on them. Since I won't be owning byNotes anymore, I'm moving them to GitHub and using LightHouse for ticket tracking. The following is the list of projects I'll be moving to GitHub in the next few days:
Django projects
- Blango
- wapi
- django-bundles
- django-geonames
- django-geocoding
- django-storage
- django-mediafiles
- django-oauthsp
Python projects
- ffmpeg bindings
Cocoa projects
- My fork of OAuthConsumer (fixing some bugs, implementing some goodies)
- BNMaps (map framework)
-
Using a metaclass for registering template tags
Jan. 8, 2009 at 17:59:45 CETWriting your template tags requires too much boilerplate code. Period. I think we all agree on that. Let's see how we can improve it.
A lot of times I find myself writing code like this (error handling omitted for clarity):
@register.tag def my_menu(parser, token): tag_name, argument1, argument2 = token.split_contents() return MyMenuNode(argument1, argument2) class MyMenuNode(template.Node): def __init__(self, argument1, argument2): ...
We are specifying the argument count and the template tag name twice, not exactly DRY friendly ;). After evaluating the solutions coming to my mind, I decided to go with a metaclass based approach, keeping the magic to a minimum.
import inspect from django import template register = template.Library() class NodeType(type): def __init__(mcs, name, bases, dct): super(NodeType, mcs).__init__(name, bases, dct) if not mcs.is_node(name, dct): tag_name = ''.join(char if char.islower() else '_%s' % char.lower() for char in name)[1:-5] init = mcs.get_init(bases, dct) (args, varargs, varkw, defaults) = inspect.getargspec(init) arg_count = len(args) # not exactly arg count, but this way we avoid adding one in tag_function def tag_function(parser, token): arguments = token.split_contents() if len(arguments) != arg_count: raise template.TemplateSyntaxError('%s tag requires %d arguments' % (arguments[0], arg_count - 1)) return mcs(*arguments[1:]) register.tag(name=tag_name, compile_function=tag_function) def is_node(mcs, name, dct): return name == 'Node' and dct['__module__'] == __name__ def get_init(mcs, bases, dct): if '__init__' in dct: return dct['__init__'] for base in bases: init = mcs.get_bases(base.__bases__, base.__dict__) if init: return init class Node(template.Node): __metaclass__ = NodeType
Inheriting from Node, we can now define a new tag just by creating a new class, which gets automatically registered:
class MyMenuNode(Node): def __init__(self, request): self.request = template.Variable(request) def render(self, context): # do something with self.request return 'my menu markup'
As soon as we load the file containing this code, we'll have a tag named my_menu defined, which takes the current request as its only argument.
How do you deal with template tags? Do you copy and paste your boilerplate code or have you found a better solution than this one?
-
Blango now hosted at byNotes repository
Oct. 31, 2008 at 10:38:49 CETBlango is now hosted at the byNotes source code repository and I'll start doing proper releases soon.
However, I feel like Blango has all I need and I don't see any new features I could add (you can check the current features at the project page. How would you enhance it? Leave a comment with your cool idea!