|
|
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
| |
− |
| |
− | The URL(s) and handler function(s) go into the controller file, which will be somewhere in <tt>$LJHOME/cgi-bin/DW/Controller</tt>.
| |
− |
| |
− | The template(s) will go into <tt>$LJHOME/views</tt>.
| |
− |
| |
− | === Defining a URL ===
| |
− |
| |
− | First, you need to define a URL in a controller file in <tt>$LJHOME/cgi-bin/DW/Controller</tt>. This will tell the system what handler function to use for a given URL pattern.
| |
− |
| |
− | 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>
| |
− |
| |
− | ==== Defining an index page ====
| |
− |
| |
− | <source lang="perl">
| |
− | # This is all you have to do
| |
− | DW::Routing->register_string( '/something/index', \&index_handler,
| |
− | app => 1 );
| |
− | </source>
| |
− |
| |
− | This registers all of /something, /something/ and /something/index
| |
− |
| |
− | === Creating a handler function ===
| |
− |
| |
− | The handler function tells the system what to do with the browser request it is receiving. It's also defined in the controller file you register the URL in, in <tt>$LJHOME/cgi-bin/DW/Controller</tt>. The handler function will look up things, define variables we need for the page, and pass them onto the template.
| |
− |
| |
− | 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, subpatterns, and request:
| |
− |
| |
− | <source lang="perl"># handles menu nav pages
| |
− | sub nav_handler {
| |
− | my ( $opts, $cat ) = @_;
| |
− | 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
| |
− | $cat ||= $r->get_args->{cat} || '';</source>
| |
− |
| |
− | Here, I'm doing error checking. If I don't get back the array of menu hashes, 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 = '.title' | ml; END -%]</source>
| |
− |
| |
− | 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>
| |
− |
| |
− | === Including a file with a temporary ML scope change ===
| |
− |
| |
− | <source lang="html4strict">
| |
− | [% dw.scoped_include('stats/site.tt'); %]
| |
− | </source>
| |
− |
| |
− | === Specifying needed CSS/JS files ===
| |
− |
| |
− | <source lang="html4strict">
| |
− | [%- CALL dw.set_active_resource_group( 'jquery' ) -%]
| |
− | [%- dw.need_res( 'stc/kitten.css' ) -%]
| |
− | [%- dw.need_res( 'js/ponies.js' ) -%]
| |
− | [%- dw.need_res( ( group => 'jquery' ), 'js/sparkle.js' ) -%]
| |
− | </source>
| |
− |
| |
− | Note: ignore the first and last if your page doesn't use jQuery.
| |
− |
| |
− | For CSS, remember to used the [http://www.dreamwidth.org/dev/classes standardized classes] as much as possible.
| |
− |
| |
− | === Adding code to the head ===
| |
− |
| |
− | Use this section code to add things to the head of the page.
| |
− |
| |
− | <source lang="html4strict">
| |
− | [% sections.head = BLOCK %]
| |
− | <style type="text/css">
| |
− | dt { font-size: bigger; font-weight: bold; margin-bottom: .2em; }
| |
− | dd { margin-bottom: 1em; }
| |
− | </style>
| |
− | [% END # sections.head %]
| |
− | </source>
| |
− |
| |
− | === Dumping variables for debugging ===
| |
− |
| |
− | If you're trying to debug problems with your template, you might find the [http://template-toolkit.org/docs/modules/Template/Plugin/Dumper.html Dumper] plugin useful to peek inside the contents of your variables.
| |
− |
| |
− | <source lang="html4strict">
| |
− | [% USE Dumper %]
| |
− | [% Dumper.dump(variable) %]
| |
− | </source>
| |
− |
| |
− | == References ==
| |
− |
| |
− | * [http://template-toolkit.org/docs/index.html Template Toolkit Documentation]
| |
− | * [http://docs.dreamwidth.net/DW/Request.html DW::Request API Documentation]
| |
− | * [http://docs.dreamwidth.net/DW/Routing.html DW::Routing API Documentation]
| |
− | * [http://docs.dreamwidth.net/DW/Template.html DW::Template API Documentation]
| |
− | * [http://docs.dreamwidth.net/DW/Template/Plugin.html Plugin Documentation]
| |
− | * [http://docs.dreamwidth.net/DW/Template/Filters.html Filter Documentation]
| |
− |
| |
− | === Code Modules ===
| |
− |
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Controller.pm DW::Controller]
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Request.pm DW::Request]
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Routing.pm DW::Routing]
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Template.pm DW::Template]
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Template/Plugin.pm DW::Template::Plugin]
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Template/Filters.pm DW::Template::Filters]
| |
− | * [https://github.com/dreamwidth/dw-free/blob/develop/cgi-bin/DW/Template/Plugin/FormHTML.pm DW::Template::Plugin::FormHTML]
| |
| | | |
| [[Category: Development]] | | [[Category: Development]] |