Django, Flatpages, Markdown, and Syntax Highlighting

11
Oct/09
0

For what should have been an easy task, this turned out to be extraordinarily difficult.

I assume you have Django already installed. If not, the tutorials on djangoproject.com aren’t too terrible, provided you’re not on a shared server (that’s another story!). I’m using Flatpages, but you can use whatever you want to follow along with this tutorial.

1. Install Markdown and Pygments for Python if you haven’t already. If you’re on Ubuntu, you can `sudo apt-get install python-markdown` or `easy_install Markdown` and `sudo apt-get install python-pygments`.

2. Go into your Django app and `mkdir templatetags`. This should be at the same level as `manage.py`, `settings.py` and `urls.py`. Then `cd` into that directory, and `touch __init__.py` (so that it becomes a module) and then add another file called something like `tags.py` (don’t name it “markdown.py” — that’ll bite you later). Paste this code in there:

from django import template
from django.template.defaultfilters import stringfilter

from django.conf import settings

register = template.Library()

@register.filter(name='markdown')
@stringfilter
def markdown(value, arg=''):
    """
    Filter to create HTML out of Markdown, using custom extensions.

    The diffrence between this filter and the django-internal markdown
    filter (located in ``django/contrib/markup/templatetags/markup.py``)
    is that this filter enables extensions to be load.

    Usage::

        {{ object.text|markdown }}
        {{ object.text|markdown:"save" }}
        {{ object.text|markdown:"codehilite" }}
        {{ object.text|markdown:"save,codehilite" }}

    This code is taken from
    http://www.freewisdom.org/projects/python-markdown/Django
    """

    try:
        import markdown
    except ImportError:
        if settings.DEBUG:
            raise (template.TemplateSyntaxError,
                   "Error in {% markdown %} filter: "
                   + "The markdown library isn't installed.")
        else :
            from django.utils.html import escape, linebreaks
            return linebreaks(escape(value))
    else:
        extensions=arg.split(",")
        if len(extensions) > 0 and extensions[0] == "safe" :
            extensions = extensions[1:]
            safe_mode = True
        else :
            safe_mode = False
        return markdown.markdown(value, extensions, safe_mode=safe_mode)

Yes, I’m leaving the link in there; I didn’t write that code.

3. Go into `settings.py` and add your app to the `INSTALLED_APPS`. You actually have to `import` your own app too; this baffled me for about an hour. My app is called `tech_eval` so my `INSTALLED_APPS` looks like this:

import tech_eval # you can put this line at the top of settings.py if you want

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'django.contrib.flatpages',
    'tech_eval',
)

4. Like I said, I’m using Flatpages, so my template looks like this (I saved it to djangoproject/app/templates/flatpages/default.html).

{% load tags %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>{{ flatpage.title }}</title>
    <link rel="stylesheet" type="text/css" href="/tech_eval/media/css/code_style.css" />
</head>
<body>
    <h1>{{ flatpage.title }}</h1>
    {{ flatpage.content|markdown:"codehilite"|safe}}
</body>
</html>

Notice how we filter the content with markdown, make sure the codehilite extension is used, and flag it as `safe` afterwords (otherwise Django will automatically character-encode everything).

5. Hopefully this is already working for you at this point, but you probably want to add some color to your code. You can generate the appropriate css file with this:

pygmentize -f html -S default -a .codehilite >> code_style.css

Some different styles you can use (at least on my system) are:

['manni', 'colorful', 'murphy', 'autumn', 'bw', 'pastie', 'native', 'perldoc', 'borland', 'trac', 'default', 'fruity', 'emacs', 'friendly']

(stolen from here).

Or just copy and paste this CSS file if you’re lazy:

.codehilite .c { color: #408080; font-style: italic } /* Comment */
.codehilite .err { border: 1px solid #FF0000 } /* Error */
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
.codehilite .o { color: #666666 } /* Operator */
.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
.codehilite .ge { font-style: italic } /* Generic.Emph */
.codehilite .gr { color: #FF0000 } /* Generic.Error */
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
.codehilite .go { color: #808080 } /* Generic.Output */
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.codehilite .gs { font-weight: bold } /* Generic.Strong */
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.codehilite .gt { color: #0040D0 } /* Generic.Traceback */
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.codehilite .kt { color: #B00040 } /* Keyword.Type */
.codehilite .m { color: #666666 } /* Literal.Number */
.codehilite .s { color: #BA2121 } /* Literal.String */
.codehilite .na { color: #7D9029 } /* Name.Attribute */
.codehilite .nb { color: #008000 } /* Name.Builtin */
.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
.codehilite .no { color: #880000 } /* Name.Constant */
.codehilite .nd { color: #AA22FF } /* Name.Decorator */
.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.codehilite .nf { color: #0000FF } /* Name.Function */
.codehilite .nl { color: #A0A000 } /* Name.Label */
.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
.codehilite .nv { color: #19177C } /* Name.Variable */
.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
.codehilite .mf { color: #666666 } /* Literal.Number.Float */
.codehilite .mh { color: #666666 } /* Literal.Number.Hex */
.codehilite .mi { color: #666666 } /* Literal.Number.Integer */
.codehilite .mo { color: #666666 } /* Literal.Number.Oct */
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.codehilite .sx { color: #008000 } /* Literal.String.Other */
.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
.codehilitetable { border: 1px solid #666; width: 100%; background-color: #666; }
.codehilitetable .linenos { background-color: #666; padding: 0 5px; width: 20px; }
.codehilitetable .code { padding: 0 15px; background-color: #fff; }

And that should pretty much be it. Let me know if I missed anything or this was too difficult to follow along.

All I want is a drag-and-drop CD burner

5
Jul/09
0

I know I’ve mostly been posting code snippets here, but I originally started this site to post more in-depth tutorials. This isn’t going to be one of those. I’m taking a spin and would like to say a few words on the design of good software — the second half of this site that’s been neglected.

My brother’s girlfriend asked me to burn some files for her using her laptop the other day, so I tried. I didn’t think it would be so difficult.

So I popped in the blank disc, and iTunes came up, exclaiming that it could burn stuff for me. Fine, I’ll try that. It told me I had to create a new playlist (Why? I’m just going to delete it in 5 seconds). So I did. And then I tried to drag and drop my videos onto the playlist. Nothing. Okay. Let’s dig through the menus: “Add to playlist”? No. “Import files”? No. It looks to me like I have to add the files to my library, then search for those files that I just added, figure out how to add them to my newly created playlist, burn them, and then delete the playlist I didn’t want in the first place. Brilliant. Except I couldn’t figure that out, and I still haven’t the faintest idea how to use iTunes. Call me idiot, iTune-lovers, but software really shouldn’t take more than about 3 minutes to figure out how to do what you want with it.

Next I tried ImgBurn, something a friend recommended to me, which was indeed great for burning images, as the name suggests, but pretty horrid at burning a list of files. Fail.

I eventually just went into “My Computer” and dragged some files on it. Windows told me I had to format the CD (it was rewriteable) first. Fine. That took an extraordinarily long time, and then finally when it started burning it told me it would be finished in 0 seconds, but it literally took between thirty minutes and an hour. I know her drive isn’t that slow, it’s a new computer.

CDBurnerXP has worked great in the past for me, but apparently it’s a failure on Windows 7. The “used space” bar was just plain wrong, and when I tried telling it that it was an idiot, it threw a fit and crashed.

Nero was good, back in the day, until they figured out they were getting popular and thought they could get away with adding whatever crapware they wanted. Now I just refuse to support their bullshit.

All I wanted to do was to drag-and-drop some files onto a program window, list them for me, and tell me how much space I’ve got left. Click burn, and that’s it. Don’t touch my files, don’t convert them, don’t move, copy, sort, cache or archive them. Don’t install useless crap on my computer, and don’t have me six thousand features I don’t need.

Give me what I want. Make it big, bold, obvious, easy to find. And make it do what it’s supposed to do when I click it. No more, no less. Is that so hard? Why can’t people develop nice, clean, simple, intuitive UIs? We’re not asking you to invent anything new, just take what’s already been done, and works, and use that. There’s frameworks for it. Use them!

Insert into MySQL datetime column from PHP

16
Jun/09
2

You can use this simple function to convert a unix timestamp (like the one obtained from time()) to MySQL datetime format:

function mysql_datetime($timestamp = null) {
    if(!isset($timestamp)) $timestamp = time();
    return date('Y-m-d H:i:s', $timestamp);
}

I’m using this instead of MySQL’s NOW() function because my MySQL server isn’t localized to my timezone, but my PHP is by usage of date_default_timezone_set.

Edit: And you can use this function to convert back the other way (you can do this directly in the SQL too, but just in case)

function datetime_to_unix($datetime) {
    list($date, $time) = explode(' ', $datetime);
    list($year, $month, $day) = explode('-', $date);
    list($hour, $minute, $second) = explode(':', $time);
    return mktime($hour, $minute, $second, $month, $day, $year);
}

Simple Rounded Corners with CSS

26
Apr/09
0

I know there are umpteen billion tutorials on rounded corners out there, but here’s an easy way that I like to do it. Requires only one image, and allows you to nudge your text as close to the corners are you like.

If you want a 3px radius corner, have a black background, and white foreground, you need to create a 6×6 (i.e. radius*2) image of a white circle on a black background. This method won’t support transparency unfortunately.

The HTML:

<div id="content">
    <div id="tl"></div><div id="tr"></div><div id="bl"></div><div id="br"></div>
    Your content here.
</div>

The CSS:

#content {
    position:relative;
    background-color: #fff;
    width:600px;
    height:300px;
}

#tl,#tr,#bl,#br{
    background: #000 url(images/circle.gif);
    width:3px;
    height:3px;
    position:absolute;
}

#tl{top:0;left:0}
#tr{top:0;right:0;background-position:3px 0}
#bl{bottom:0;left:0;background-position:0 3px}
#br{bottom:0;right:0;background-position:3px 3px}

Adjust all the “3px”s to match your radius, and modify the background attribute to meet your needs, and style content however you want. You probably want to put some padding on there. The important thing is that #content has position:relative so that the corners are aligned to the content box rather than the page.

I used this technique on my personal site, mnbayazit.com.

Easy Current Page Tab

9
Apr/09
0

So, you have a bunch of links along the top of your page, and you want to highlight the current one when you click on it. Easy enough to do if you copy and paste the entire navbar onto every page and add some CSS to the current tab, but that’s just bad practice! You want to throw the navbar in a layout, and still have it work.

Well, here’s an easy solution! Just drop this in your layout:

<ul id="navbar">
<?php
$pages = array(
    'url1' => 'linktext1',
    'url2' => 'linktext2',
    'url3' => 'linktext3'
);
foreach($pages as $url => $name) {
    if($url == $currentPage) echo '<li class="current">'.$name.'</li>';
    else echo '<li><a href="'.$url.'">'.$name.'</a></li>';
}
?>
</ul>

And make sure you set $currentPage properly. You could use $_SERVER['REQUEST_URI'] if your URLs are in the same format (leading and trailing slashes if necessary).

Update: Made a little wrapper function out of this too.

function list_links($links) {
    global $currentPage;
    foreach($links as $name => $url) {
        $class = strtolower($name);
        $class = preg_replace('`[^a-z\s]`', '', $class);
        $class = preg_replace('`\s+`', '-', $class);
        $class = trim($class, '-');
        if(ltrim($url,'/') == $currentPage) echo '<li class="',$class,'"><a href="',$url,'" class="selected">',$name,'</a></li>';
        else echo '<li class="',$class,'"><a href="',$url,'">',$name,'</a></li>';
    }
}

This one adds a class to each <li> too so you can style it in your CSS.