wiki:TurboCheetahTemplates
Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Version 1 (modified by chris dot arndt at web dot de, 13 years ago) (diff)

Created

Templates for using TurboCheetah with TurboGears

Author:  Christopher Arndt

So you want to use Cheetah templates for your pages but still take advantage of the automatic Javascript/CSS inclusion that the standard Kid sitetemplate does? Here are the files you need to set up a mini TurboGears project using  TurboCheetah.

The main controller

We are going to test our templates with this minimal controller. Just replace the contents of controllers.py of our fresh, quickstarted project with this code:

# -*- coding: UTF-8 -*-

import cherrypy

import turbogears
from turbogears import controllers, expose

class Root(controllers.RootController):
    @expose(template="cheetah:cheetahtemplates.templates.welcome")
    def index(self):
        import time
        turbogears.flash(u"Herzliche Grüße & viel Spaß!")
        return dict(now=time.ctime())

Note the encoding declaration at the top of the file, so that we can use UTF-8 characters in our unicode strings. Make sure to save this file with UTF-8 encoding. Replace cheetahtemplates in the template name with the package name of your project.

The template base class

With Cheetah your templates can subclass a pure Python class. Attributes and functions of this class are available as template variables. This allows to define some handy utility functions for using in our templates and also to implement a fix for Cheetah's unicode handling.

from cgi import escape
from Cheetah.Filters import Filter
from Cheetah.Template import Template
from turbogears import url

class HSC(Filter):
    """A filter for Cheetah placeholder substitution supporting unicode objects.

    Supports the following arguments:

    escape    - if evaluates to True, escape the placeholder substitution
                with cgi.escape. If escape == 'quote', the 'quote' parameter of
                cgi.escape is set to True).
    maxlength - if set to an int > 0, truncate the placeholder substitution at
                maxlength characters, possibly replacing the end with suffix.
    suffix    - when truncating the placeholder to maxlength, replace the last
                len(suffix) characters with suffix (defaults to '...').
    """

    def filter(self, val, **kw):
        if val is None:
            return ''
        elif isinstance(val, unicode):
            val = val.encode(kw.get('encoding', 'utf-8'))
        val = str(val)
        if kw.get('escape', False):
            val = escape(val, kw.get('escape') == 'quote')
        if kw.get('maxlength'):
            try:
                length = int(kw['maxlength'])
            except ValueError: pass
            else:
                suffix = kw.get('suffix', '...')
                if len(val) > length and suffix:
                    length -= len(suffix)
                    val = val[:length] + suffix
                else:
                    val = val[:length]
        return val

class SiteBase(Template):

    image_base = url('/static/images/')
    css_base = url('/static/css/')
    js_base = url('/static/javascript/')

    _js_src_tmpl = '<script type="text/javascript" language="JavaScript%s">\n%s</script>'

    _js_link_tmpl = '<script type="text/javascript" language="JavaScript%s" src="%s"></script>'

    _css_link_tmpl = '<link rel="%s" type="text/css" href="%s" />'

    def __init__(self, *args, **kw):
        """Constructor sets the default filter to use."""

        kw['filter'] = HSC
        Template.__init__(self, *args, **kw)

    def js_link(self, relpath, version=''):
        return self._js_link_tmpl % (version, self.js_base + relpath)

    def js_src(self, relpath, version=''):
        return self._js_src_tmpl % (version, self.js_base + relpath)

    def css_link(self, relpath, type="stylesheet"):
        return self._css_link_tmpl % (type, self.css_base + relpath)

Save this file as SiteBase.py in the templates folder of your project (the exact name is important). You can delete the Kid templates, since we will not use them for our mini project. Don't delete the __init__.py file.

The custom filter HSC class implements unicode support for Cheetah up to version 2.0rc6. Newer versions come with a new standard filter that should support unicode out-of-the-box.

The site template

This defines the basic layout of all your pages. Customise to your liking or plug in another template in the inheritance structure. See the Cheetah user guide for more information. Be sure to always include the meta header that sets the page encoding to UTF-8.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
#extends SiteBase
#implements respond
<html>
<head>
    #block headtop
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    #end block

    #for $css in $tg_css
    ## normally you should be able to just use
    ## $css.render()
    ## but for some reasons that does not work for me
    ${getattr(css, 'render')()}
    #end for

    ## include your own external CSS stylesheet links here, e.g.
    ##$css_link('myproject.css')

    #for $js in $tg_js_head
    ${getattr(js, 'render')()}
    #end for

    ## include your own external JS files here links here, e.g.
    ##$js_link('myproject.js')

    #block pagetitle
    <title></title>
    #end block
</head>

<body>
#for $js in $tg_js_bodytop
${getattr(js, 'render')()}
#end for

<div id="header">
    #if $tg.config('identity.on', False)
    <div id="pageLogin">
        #if $tg.identity.anonymous
        You are not logged in
            #if 'loggin_in' not in locals()
            <span class="menu-sep">::</span>
            <a href="$tg.url('/login')">Login</a>
            #end if
        #else
        You are logged in as
        <span class="user_name">$tg.identity.user.display_name</span>
        <span class="menu-sep">::</span>
        <a href="$tg.url('/logout')">Logout</a>
        #end if
        <span class="menu-sep">::</span>
        <a href="$tg.url('/')">Home</a>
    </div>
    #end if
</div>

#if $tg_flash
<div class="tg_flash">${tg_flash, escape=True}</div>
#end if

<div id="pagecontent">
#block pagecontent
<p class="placeholder">Here comes the page content...</p>
#end block
</div>

#for $js in $tg_js_bodybottom
${getattr(js, 'render')()}
#end for
</body>
</html>

Save this file as SiteTemplate.tmpl in the templates folder of your project and compile it with cheetah-compile SiteTemplate.py.

The page template

Finally, here's an example for a page template. Note how we just write the actual content of the page by redefining the pagecontent block with the #def directive. All the rest of the HTML comes from SiteTemplate.

#extends cheetahtemplates.templates.SiteTemplate

#def pagetitle
    <title>Welcome to TurboGears!</title>
#end def

#def pagecontent
<h1>Welcome to TurboGears!</h1>

<p>This page was generated on $now</p>
#end def

Save this file as welcome.tmpl in the templates folder of your project. Again, replace cheetahtemplates in the #extends directive with the package name of your project.

Testing

Now start your Turbogears server and open your browser at http://localhost:8080. You should see a page with "Herzliche Grüße & viel Spaß!" at the top and the current date & time in place of the $now placeholder.

Conclusion

Here's a recap of the most important things to remember:

  • Prefix your template name in @expose with cheeath:
  • Compile your Cheetah site template with cheetah-compile, since TurboGears does not do this automatically as it does with Kid templates.
  • Put a meta header declaring the UTF-8 encoding for the HTML output in your site template.
  • Put an encoding declaration for UTF-8 in your python source files if you use literal unicode strings and save them UTF-8 encoded.
  • Put any complex function you want to use in all your templates as methods in the SiteBase class.
  • If your template variables may contain special HTML characters, use the escape parameter for placeholders, e.g. ${myvar, escape=True}

Attachments