hacker has asked for the wisdom of the Perl Monks concerning the following question:

Good afternoon fellow monks. Today's problem is with HTML::Template. I have a website which is driven primarily by HTML::Template, and on the front page of the website is a small row of recent articles. Each article's title/date/author are bordered in a small table, and the first few lines of the article are shown below it. Clicking on the title will bring you to a full page of the full article content. This part works.

My problem though, is that I'd like to only show the top 'n' most-recent articles, not all of them. I have the articles in a file, templates/articles.tmpl, which contains the an article header (example below). In the header is a link to the full article, which is in it's own template as well.

Article Header example:

<br /> <TMPL_VAR NAME=ARTICLE_HDR_FRAME> <tr> <td><TMPL_VAR NAME=ARTICLE_HDR_BODY> <tr> <td align="left"> <img src="<TMPL_VAR NAME=IMAGES>/new_package.gif" alt="09- +22-2001" width="12" height="12" border="0" />&nbsp; <a href="<TMPL_VAR NAME=SCRIPT>/article015"><TMPL_VAR NAME +=FONTFACE><font color="blue"><b>My Article Title</b></font></a> </td> <td align="right"><i>Saturday, September 22nd, 2001</i></td> </tr> </table> </td> </tr> </table> <table cellspacing="0" cellpadding="1" border="0" bgcolor="#ffffff" wi +dth="100%"> <tr> <td><TMPL_VAR NAME=FONTFACE>A small blurb of the article, a 'tea +ser', which is the first few sentences of the full article.</font></t +d> </tr> </table>
(some of the HTML tags may look disjointed, but they do match, since this is encapsulated in a larger piece of HTML)

My code for loading articles.tmpl looks like this:

sub front_page_articles { my $fp_article_template = HTML::Template->new(filename => 'templates/a +rticles.tmpl'); $fp_article_template->param( ARTICLE_HDR_FRAME => $article_hdr_frame, ARTICLE_HDR_BODY => $article_hdr_body, IMAGES => $images, FONTFACE => "$fontface", SCRIPT => $script, SITE => $site, ); print $fp_article_template->output; }
I can't seem to figure out a way, without reading in the file with 'open(ART, "templates/articles.tmpl") or dir "Foo!" {...}' and then splitting at the 'nth' blank line between article constructs, to limit the front page to display the top 5 most recent articles, for example. When the user clicks on the 'All Articles' link, the full article list will be shown, from 0..n.

How can I do this? Am I using the wrong tool?

Edit ar0n -- fixed title

Replies are listed 'Best First'.
(jeffa) Re: HTML::Template 'splicing' help
by jeffa (Bishop) on Dec 22, 2001 at 22:41 UTC
    What i am hearing is that you have N numbers of thingies you want display in one place 5 times, and in another the full N. Well, sounds like you need a loop, and i don't see a single <tmpl_loop> tag in your template. That's a big problem, because that tag is precisely what is needed to make this work. Here's how i would handle it (warning: theorectical ideas ahead):

    Somehow you are going to have a datastructure that holds each article's body and it's title, as well as other possible 'attributes'. I'll assume 'title' and 'body' are plenty. You will need to somehow (great place for map, BTW) munge you orginal datastructure into this form:

    $VAR1 = [ { title => 'foo', article => 'bar', }, { title => 'baz', article => 'qux', }, # etc. ];
    A list of hashes. This is going to be fed to the <tmpl_loop> tag in your template. For more info on loops and corresponding datastructes, read up on HTML::Template or check out HTML::Template Tutorial. Onward!

    Now, at this point you have a data structure ready to be looped, but, the home page is different that the actual artice page. Whether you decided to have two complete different sets of code or one is up to you, my example below will use the later (one):

    Run the following script first with no arguments to see what how the 'article' page renders, and then again with any argument to see how the 'home' page renders. The following code attempts to merely drive home some concepts, you will have to modify it to meet your real needs:


    use strict; use HTML::Template; use Data::Dumper; # just a simple way to try both test cases my $page = (shift) ? 'home' : 'articles'; my $html = do { local $/; <DATA> }; my $template = HTML::Template->new(scalarref => \$html); my $articles = [ { title => q|Title 1|, body => q|blah blah blah, blah blah blah blah|, }, { title => q|Title 2|, body => q|blah blah blah, blah blah blah blah|, }, { title => q|Title 3|, body => q|blah blah blah, blah blah blah blah|, }, { title => q|Title 4|, body => q|blah blah blah, blah blah blah blah|, }, ]; $template->param( class => $page, articles => ('home' eq $page) ? [(@$articles)[0..1]] : $articles, ); print $template->output(); __DATA__ <table class="<tmpl_var name=class>"> <tmpl_loop name="articles"> <tr> <td><tmpl_var name="title"></td> <td><tmpl_var name="body"></td> </tr> </tmpl_loop>

    This line probably deserves some elaboration:
    articles => ('home' eq $page) ? [(@$articles)[0..1]] : $articles,
    If we are serving the 'home' page, then whe only grab the first 2 elements of @$articles and package them back in an array reference. You might also choose to truncate the values of each 'body' key from the elements. I chose not to here out of simplicity, and well, mostly laziness. Finally, the class="" attribute in the <table> tag can be used to format the resulting table differently.

    Good luck, and feel free to ask me further questions.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    F--F--F--F--F--F--F--F--
    (the triplet paradiddle)