--- /dev/null
+[[!tag patch]]
+
+[[!templte id="note" text="""
+Simply copied this from my website
+[[http://www.camco.ie/code/ikiwiki,3.20120202,20120313a/]]
+feel free to reformat / delete"""]]
+
+The following re-write allows for multiple definitions of the
+same tag value in a template definition. This, in turn, allows
+us to use TMPL_LOOPS in our template directives; all-be-it in a
+rather limited way.
+
+I would, personally, only use this feature for very basic loops
+and, although nested loops *might* be possible (with a little
+more tinkering) it think any attempt would be better served by
+[[Kathyrn Anderson's|http://www.katspace.org/]] [[field et
+al.|http://ikiwiki.info/plugins/contrib/field/]] plugin.
+
+It *is* (primarily) intended to allow insertion of organised CSS
+blocks (i.e. `<div>`) through template directives (since i can't
+seem to get HTML and Markup to mix the way I want).
+
+[[!template id="note" text="""
+Apologies for the re-write. I struggle reading perl code that
+I didn't write and (probably too often) re-format to reduce my
+head-aches. Anyway it didn't make sense to post the patch since
+everything's changed now.
+"""]]
+
+# `lib/perl5/IkiWiki/Plugin/template.pm`
+
+[[!format perl """
+
+ #!/usr/bin/perl
+ # Structured template plugin.
+ package IkiWiki::Plugin::template ;
+
+ use warnings ;
+ use strict ;
+ use IkiWiki 3.00 ;
+ use Encode ;
+
+ sub mktmpl_hash( $ ; $ ; @ ) ;
+ # declare to supress warning in recursive call
+ sub mktmpl_hash( $ ; $ ; @ )
+ # make hash for the template, filling
+ # values from the supplied params
+ {
+ my $template = shift( @_ )
+ || error( "mktmpl_hash: no template provided" ) ;
+ my $param_src = shift( @_ )
+ || error( "mktmpl_hash: no parameters" ) ;
+
+ my $path ;
+ if( $#_ > 0 )
+ {
+ $path = [ @_ ] ;
+ } else {
+ $path = shift(@_) || [] ;
+ } ;
+
+ my %params ;
+
+ my @path_vars ;
+ if( $#{$path} < 0 )
+ {
+ @path_vars = $template->query() ;
+ } else {
+ @path_vars = $template->query( loop => $path ) ;
+ } ;
+
+ foreach my $var ( @path_vars )
+ {
+ push( @{$path}, $var ) ;
+ my $param_type = $template->query( name => $path ) ;
+ if( $param_type eq 'VAR' )
+ {
+ my @var_path = split( /_/, $var ) ;
+ if( $var_path[0] ne '' )
+ {
+ $path->[-1] = join( '_', @var_path[1..$#var_path] )
+ if( $var_path[0] eq 'raw' ) ;
+ $params{$var} = shift( @{$param_src->{$path->[-1]}} )
+ || return(undef) ;
+ } ;
+ } elsif( $param_type eq 'LOOP' )
+ {
+ $params{$var} = [] ;
+ push( @{$params{$var}}, $_ )
+ while( $_ = mktmpl_hash($template,$param_src,$path) ) ;
+ } ;
+ pop( @{$path} ) ;
+ } ;
+ return( \%params ) ;
+ } ;
+
+ sub proc_tmpl_hash( $ ; $ ; $ ; $ ) ;
+ # declare to supress warning in recursive call
+ sub proc_tmpl_hash( $ ; $ ; $ ; $ )
+ # walk the hash, preprocess and
+ # convert to html
+ {
+ my $tmpl_hash = shift( @_ ) ;
+ my $page = shift( @_ ) ;
+ my $destpage = shift( @_ ) ;
+ my $scan = shift( @_ ) ;
+ foreach my $key ( keys(%{$tmpl_hash}) )
+ {
+ unless( ref($tmpl_hash->{$key}) )
+ # here we assume that
+ # any reference is an
+ # array and allow it to
+ # fail if that's false
+ {
+ $tmpl_hash->{$key} =
+ IkiWiki::preprocess(
+ $page,
+ $destpage,
+ $tmpl_hash->{$key},
+ $scan ) ;
+ my @key_path = split( /_/, $key ) ;
+ $tmpl_hash->{$key} =
+ IkiWiki::htmlize(
+ $page,
+ $destpage,
+ pagetype($pagesources{$page}),
+ $tmpl_hash->{$key}, )
+ unless( $key_path[0] eq 'raw' ) ;
+ } else {
+ proc_tmpl_hash( $_, $page, $destpage, $scan )
+ foreach( @{$tmpl_hash->{$key}} ) ;
+ } ;
+ } ;
+ } ;
+
+ # "standard" ikiwiki definitions / hooks
+
+ sub import
+ {
+ hook( type => "getsetup",
+ id => "template",
+ call => \&getsetup ) ;
+ hook( type => "preprocess",
+ id => "template",
+ call => \&preprocess,
+ scan => 1 ) ;
+ } ;
+
+ sub getsetup()
+ {
+ return(
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ section => "widget",
+ }, ) ;
+ } ;
+
+ sub preprocess( @ )
+ {
+ # first process arguments into arrays of values
+ my %params ;
+
+ my( $key, $value ) ;
+ while( ($key,$value)=splice(@_,0,2) )
+ {
+ if( exists($params{$key}) )
+ {
+ push( @{$params{$key}}, $value ) ;
+ } else {
+ $params{$key} = [ $value ] ;
+ } ;
+ } ;
+
+ # set context
+ my $scan = ! defined( wantarray() ) ;
+ # This needs to run even in scan
+ # mode, in order to process links
+ # and other metadata included via
+ # the template.
+
+ # check for critical values
+ if( ! exists($params{id}) )
+ {
+ error( gettext("missing id parameter") ) ;
+ } ;
+
+ # set some convenience variables
+ my $id = $params{id}->[$#{$params{id}}] ;
+ my $page = $params{page}->[$#{$params{page}}] ;
+ my $destpage = $params{destpage}->[$#{$params{destpage}}] ;
+ # ... and an essential one for the production pass
+ $params{basename} = IkiWiki::basename($page) ;
+
+ # load the template
+ my $template ;
+ eval {
+ $template =
+ template_depends( $id, $page,
+ blind_cache=>1 ) ;
+ # The bare id is used, so
+ # a page templates/$id can
+ # be used as the template.
+ } ;
+ if( $@ )
+ {
+ error(
+ sprintf(
+ gettext("failed to process template %s"),
+ htmllink(
+ $page,
+ $destpage,
+ "/templates/$id")
+ )." $@"
+ ) ;
+ } ;
+
+ # create and process the parameters
+ my $tmpl_hash = mktmpl_hash( $template, \%params ) ;
+ proc_tmpl_hash( $tmpl_hash, $page, $destpage, $scan ) ;
+ # ... and load the template with the values
+ $template->param( $tmpl_hash ) ;
+
+ # return the processed page chunk
+ return( IkiWiki::preprocess($page,
+ $destpage,
+ $template->output(),$scan)
+ ) ;
+ } ;
+
+ 1 ;
+
+"""]]
+
+## sample template
+
+ # <TMPL_VAR HEADER0>
+
+ <table>
+ <TMPL_LOOP TEST0>
+ <tr>
+ <td><TMPL_VAR DATA0></td>
+ <td><TMPL_VAR DATA1></td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+
+## sample iki page
+
+ \[[!meta title="this is my loops page"]]
+
+ \[[!template id="loops"
+ header0="this is a table"
+ data0="cell0:0"
+ data1="cell0:1"
+ data0="cell1:0"
+ data0="cell1:1"
+ ]]