Difference between revisions of "Routing and Template Toolkit"

From Dreamwidth Notes
Jump to: navigation, search
(Replaced content with "Dreamwidth is currently in the process of moving away from BML and towards a system of routing that employs [http://template-toolkit.org/ Template Toolkit] for templat...")
 
(20 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
Dreamwidth is currently in the process of moving away from [[BML]] and towards a system of routing that employs [http://template-toolkit.org/ Template Toolkit] for templates.
 
Dreamwidth is currently in the process of moving away from [[BML]] and towards a system of routing that employs [http://template-toolkit.org/ Template Toolkit] for templates.
  
== The Basics ==
+
More information on how to use this system can be found in:
  
Making a page with the routing and Template Toolkit system requires a few things:
+
* [[Routing and Template Cookbook]]
  
# Define a URL string or pattern
 
# Create a handler function, attached to the URL string or pattern
 
# Make any needed templates
 
 
=== Defining a URL ===
 
 
Here are some examples of registering a URL.  There are two main functions to do this with, <tt>register_string</tt> and <tt>register_regex</tt>.
 
 
<source lang="perl">
 
# This registers a static string, which is an application page.
 
DW::Routing->register_string( '/misc/whereami', \&whereami_handler,
 
    app => 1 );
 
 
# This registers a regular expression.  Later, we can get the
 
# contents inside the parentheticals.  This lets us make "clean" urls
 
# like /nav/read and /nav/create that are equivalent to
 
# /nav?cat=create
 
DW::Routing->register_regex( qr!^/nav(?:/([a-z]*))?$!, \&nav_handler,
 
    app => 1 );
 
 
# This registers a user page, so, for example, it would be accessed at
 
# username.dreamwidth.org/data/edges
 
# This also explicitly sets our default output format to be JSON.
 
DW::Routing->register_string( "/data/edges", \&edges_handler,
 
    user => 1, format => 'json' );</source>
 
 
=== Creating a handler function ===
 
 
I'm going to use the Nav controller as an example.  You can see the entire page at [http://hg.dwscoalition.org/dw-free/file/tip/cgi-bin/DW/Controller/Nav.pm DW::Controller::Nav.pm].
 
 
We define our URL in the same file we place the handler function, so we'll start there:
 
 
<source lang="perl">package DW::Controller::Nav;
 
 
use strict;
 
use warnings;
 
use DW::Controller;
 
use DW::Routing;
 
use DW::Template;
 
use DW::Logic::MenuNav;
 
use JSON;
 
 
# Defines the URL for routing.  I could use register_string( '/nav' ... ) if I didn't want to capture arguments
 
# This is an application page, not a user styled page, and the default format is HTML (ie, /nav gives /nav.html)
 
DW::Routing->register_regex( qr!^/nav(?:/([a-z]*))?$!, \&nav_handler, app => 1 );</source>
 
 
First, I want to get the options and request:
 
 
<source lang="perl"># handles menu nav pages
 
sub nav_handler {
 
    my $opts = shift;
 
    my $r = DW::Request->get;</source>
 
 
Remember that subpattern I made in my URL regex?  I'll figure out if I'm using it or an argument here.
 
 
<source lang="perl">
 
    # Check for a category like nav/read, then for a ?cat=read argument, else no category
 
    my $cat = $opts->subpatterns->[0] || $r->get_args->{cat} || '';</source>
 
 
Here, I'm doing error checking.  If I don't get back the array of menu hashe, I'm going to serve an error page that contains a translated error message.  Notice how I have to define the whole path to the error message, and can't just use ".error.invalidcat".
 
 
<source lang="perl">    # this function returns an array reference of menu hashes
 
    my $menu_nav = DW::Logic::MenuNav->get_menu_display( $cat )
 
        or return error_ml( '/nav.tt.error.invalidcat' );</source>
 
 
I'm getting some cruft that is needed by the real menu that I don't want to display on this page, so I'll go through all the menus and strip out HTML from the titles.  Ideally there would be a Template Toolkit filter that could do this, but I have not found one yet; we may have to make one.
 
 
<source lang="perl">    # this data doesn't need HTML in the titles, like in the real menu
 
    for my $menu ( @$menu_nav ) {
 
        for my $item ( @{ $menu->{items} } ) {
 
            $item->{text} = LJ::strip_html( $item->{text} );
 
        }
 
    }</source>
 
 
This is a nifty part.  I can display the contents of this page either as HTML or as JSON (which is made for machine parsing), with very very little additional code.
 
 
The first one is how to return something as JSON. I just print out the object conversion to the request and return OK.
 
 
<source lang="perl">    # display according to the format
 
    my $format = $opts->format;
 
    if ( $format eq 'json' ) {
 
        # this prints out the menu navigation as JSON and returns
 
        $r->print( JSON::objToJson( $menu_nav ) );
 
        return $r->OK;</source>
 
 
The HTML format takes a bit more work.  We want to pass in more variables to the template and return the rendered template.
 
 
Here we go preparing the variables:
 
 
<source lang="perl">    } elsif ( $format eq 'html' ) {
 
        # these variables will get passed to the template
 
        my $vars = {
 
            menu_nav => $menu_nav,
 
            cat => $cat,
 
        };
 
 
        $vars->{cat_title} = $menu_nav->[0]->{title} if $cat;</source>
 
 
And this is the call to render our template (more on making that next):
 
 
<source lang="perl">        # Now we tell it what template to render and pass in our variables
 
        return DW::Template->render_template( 'nav.tt', $vars );</source>
 
 
If my format isn't HTML or JSON, we throw a 404.
 
 
<source lang="perl">    } else {
 
        # return 404 for an unknown format
 
        return $r->NOT_FOUND;
 
    }
 
}
 
 
1;</source>
 
 
And that's the handler!
 
 
=== Creating a template ===
 
 
The template will be placed somewhere logical in <tt>$LJHOME/views</tt>.  This one will be <tt>[http://hg.dwscoalition.org/dw-free/file/tip/views/nav.tt nav.tt]</tt>.  Currently, translation strings will go in <tt>nav.tt.text</tt>.
 
 
This section is a comment:
 
 
<source lang="html4strict">
 
[%# nav.tt
 
 
Page that shows the sub-level navigation links given the top-level navigation header
 
 
%]</source>
 
 
This section sets the title of the page.  If we have a category, it will be the category title.  Otherwise, it will use the ML translation of the title for the page.
 
 
<source lang="html4strict">[%- IF cat; sections.title = cat_title; ELSE; sections.title = '.title' | ml; END -%]</source>
 
 
This section creates the menu sections, applying code for each menu and each item in each menu.  If this isn't only displaying a category, it makes the title.  You can see the [http://template-toolkit.org/docs/manual/Filters.html#section_html html filter] being used in places like <tt>[% menu.title | html %]</tt> to ensure that all HTML is safe, and the [http://template-toolkit.org/docs/manual/Filters.html#section_url url filter] being used in <tt>[% menu_item.url | url %]</tt> to make sure that URL will be validly encoded.
 
 
<source lang="html4strict">[% FOREACH menu = menu_nav %]
 
    [% IF NOT cat %]<h2 class="[% menu.name %]">[% menu.title | html %]</h2>[% END %]
 
    <ul>
 
    [% FOREACH menu_item = menu.items %]
 
        <li><a href="[% menu_item.url | url %]">[% menu_item.text | html %]</a></li>
 
    [% END %]
 
    </ul>
 
[% END %]
 
</source>
 
 
That template is all that's required to render the HTML for the nav page!  You can see it [http://www.dreamwidth.org/nav live on Dreamwidth].
 
 
== Standard tricks ==
 
 
=== Inserting variables into translation strings ===
 
 
The example above sneaked in the way to use the translation system from within templates, by doing:
 
 
<source lang="html4strict">[%- IF cat; sections.title = cat_title; ELSE; sections.title = <strong>'.title' | ml</strong>; END -%]</source>
 
 
The relevant part above is bolded. But what if you needed to insert something, such as the sitename or a username, into a string? Here's a fragment that does the latter, drawn from [http://hg.dwscoalition.org/dw-free/file/tip/views/misc/pubkey.tt views/misc/pubkey.tt]:
 
 
<source lang="html4strict">[% '.label' | ml(user = u.ljuser_display) %]</source>
 
 
== References ==
 
 
* [http://template-toolkit.org/docs/index.html Template Toolkit Documentation]
 
* [http://hack.dreamwidth.net/dev/docs/DW/Routing.html DW::Routing API Documentation]
 
* [http://hack.dreamwidth.net/dev/docs/DW/Template.html DW::Template API Documentation]
 
 
=== Code Modules ===
 
 
* [http://hg.dwscoalition.org/dw-free/file/tip/cgi-bin/DW/Controller.pm DW::Controller]
 
* [http://hg.dwscoalition.org/dw-free/file/tip/cgi-bin/DW/Request.pm DW::Request]
 
* [http://hg.dwscoalition.org/dw-free/file/tip/cgi-bin/DW/Routing.pm DW::Routing]
 
* [http://hg.dwscoalition.org/dw-free/file/tip/cgi-bin/DW/Template.pm DW::Template]
 
  
 
[[Category: Development]]
 
[[Category: Development]]

Latest revision as of 21:11, 24 February 2013

Dreamwidth is currently in the process of moving away from BML and towards a system of routing that employs Template Toolkit for templates.

More information on how to use this system can be found in: