Merge branch 'master' of ssh://git.ikiwiki.info
[ikiwiki] / doc / todo / allow_TMPL__95__LOOP_in_template_directives.mdwn
1 [[!tag patch todo]]
2
3 [[!template id="note" text="""
4 Simply copied this from my website
5 [[http://www.camco.ie/code/ikiwiki,3.20120202,20120313a/]]
6 feel free to reformat / delete"""]]
7
8 The following re-write allows for multiple definitions of the
9 same tag value in a template definition.  This, in turn, allows
10 us to use TMPL_LOOPS in our template directives; all-be-it in a
11 rather limited way.
12
13 I would, personally, only use this feature for very basic loops
14 and, although nested loops *might* be possible (with a little
15 more tinkering) it think any attempt would be better served by
16 [[Kathyrn Anderson's|http://www.katspace.org/]] [[field et
17 al.|http://ikiwiki.info/plugins/contrib/field/]] plugin.
18
19 It *is* (primarily) intended to allow insertion of organised CSS
20 blocks (i.e. `<div>`) through template directives (since i can't
21 seem to get HTML and Markup to mix the way I want).
22
23 [[!template id="note" text="""
24 Apologies for the re-write.  I struggle reading perl code that
25 I didn't write and (probably too often) re-format to reduce my
26 head-aches.  Anyway it didn't make sense to post the patch since
27 everything's changed now.
28 """]]
29
30 NB: this *should* be 100% backwards compatible.
31
32 # `lib/perl5/IkiWiki/Plugin/template.pm`
33
34 [[!format perl """
35
36         #!/usr/bin/perl
37         # Structured template plugin.
38         package IkiWiki::Plugin::template ;
39
40         use warnings ;
41         use strict ;
42         use IkiWiki 3.00 ;
43         use Encode ;
44
45         sub mktmpl_hash( $ ; $ ; @ ) ;
46                                 # declare to supress warning in recursive call
47         sub mktmpl_hash( $ ; $ ; @ )
48                                 # make hash for the template, filling
49                                 # values from the supplied params
50         {
51                 my $template = shift( @_ )
52                                 || error( "mktmpl_hash: no template provided" ) ;
53                 my $param_src = shift( @_ )
54                                 || error( "mktmpl_hash: no parameters" ) ;
55
56                 my $path ;
57                 if( $#_ > 0 )
58                 {
59                         $path = [ @_ ] ;
60                 } else {
61                         $path = shift(@_) || [] ;
62                 } ;
63
64                 my %params ;
65
66                 my @path_vars ;
67                 if( $#{$path} < 0 )
68                 {
69                         @path_vars = $template->query() ;
70                 } else {
71                         @path_vars = $template->query( loop => $path ) ;
72                 } ;
73
74                 foreach my $var ( @path_vars )
75                 {
76                         push( @{$path}, $var ) ;
77                         my $param_type = $template->query( name => $path ) ;
78                         if( $param_type eq 'VAR' )
79                         {
80                                 my @var_path = split( /_/, $var ) ;
81                                 if( $var_path[0] ne '' )
82                                 {
83                                         $path->[-1] = join( '_', @var_path[1..$#var_path] )
84                                                 if( $var_path[0] eq 'raw' ) ;
85                                         $params{$var} = shift( @{$param_src->{$path->[-1]}} )
86                                                         || return(undef) ;
87                                 } ;
88                         } elsif( $param_type eq 'LOOP' )
89                         {
90                                 $params{$var} = [] ;
91                                 push( @{$params{$var}}, $_ )
92                                         while( $_ = mktmpl_hash($template,$param_src,$path) ) ;
93                         } ;
94                         pop( @{$path} ) ;
95                 } ; 
96                 return( \%params ) ;
97         } ;
98
99         sub proc_tmpl_hash( $ ; $ ; $ ; $ ) ;
100                                 # declare to supress warning in recursive call
101         sub proc_tmpl_hash( $ ; $ ; $ ; $ )
102                                 # walk the hash, preprocess and
103                                 # convert to html
104         {
105                 my $tmpl_hash = shift( @_ ) ;
106                 my $page = shift( @_ ) ;
107                 my $destpage = shift( @_ ) ;
108                 my $scan = shift( @_ ) ;
109                 foreach my $key ( keys(%{$tmpl_hash}) )
110                 {
111                         unless( ref($tmpl_hash->{$key}) )
112                                                 # here we assume that
113                                                 # any reference is an
114                                                 # array and allow it to
115                                                 # fail if that's false
116                         {
117                                 $tmpl_hash->{$key} =
118                                                 IkiWiki::preprocess(
119                                                                 $page,
120                                                                 $destpage,
121                                                                 $tmpl_hash->{$key},
122                                                                 $scan ) ;
123                                 my @key_path = split( /_/, $key ) ;
124                                 $tmpl_hash->{$key} =
125                                                 IkiWiki::htmlize(
126                                                                 $page,
127                                                                 $destpage,
128                                                                 pagetype($pagesources{$page}),
129                                                                 $tmpl_hash->{$key}, )
130                                         unless( $key_path[0] eq 'raw' ) ;
131                         } else {
132                                 proc_tmpl_hash( $_, $page, $destpage, $scan )
133                                         foreach( @{$tmpl_hash->{$key}} ) ;
134                         } ;
135                 } ;
136         } ;
137
138         # "standard" ikiwiki definitions / hooks
139
140         sub import
141         {
142                 hook( type => "getsetup",
143                                 id => "template",
144                                 call => \&getsetup ) ;
145                 hook( type => "preprocess",
146                                 id => "template",
147                                 call => \&preprocess,
148                                 scan => 1 ) ;
149         } ;
150
151         sub getsetup()
152         {
153                 return(
154                                 plugin => {
155                                         safe => 1,
156                                         rebuild => undef,
157                                         section => "widget",
158                                 }, ) ;
159         } ;
160
161         sub preprocess( @ )
162         {
163         # first process arguments into arrays of values
164                 my %params ;
165
166                 my( $key, $value ) ;
167                 while( ($key,$value)=splice(@_,0,2) )
168                 {
169                         if( exists($params{$key}) )
170                         {
171                                 push( @{$params{$key}}, $value ) ;
172                         } else {
173                                 $params{$key} = [ $value ] ;
174                         } ;
175                 } ;
176
177         # set context
178                 my $scan = ! defined( wantarray() ) ;
179                                         # This needs to run even in scan
180                                         # mode, in order to process links
181                                         # and other metadata included via
182                                         # the template.
183
184         # check for critical values
185                 if( ! exists($params{id}) )
186                 {
187                         error( gettext("missing id parameter") ) ;
188                 } ;
189
190         # set some convenience variables
191                 my $id = $params{id}->[$#{$params{id}}] ;
192                 my $page = $params{page}->[$#{$params{page}}] ;
193                 my $destpage = $params{destpage}->[$#{$params{destpage}}] ;
194         # ... and an essential one for the production pass
195                 $params{basename} = [ IkiWiki::basename($page) ] ;
196
197         # load the template
198                 my $template ;
199                 eval {
200                         $template =
201                                         template_depends( $id, $page,
202                                                         blind_cache=>1 ) ;
203                                                 # The bare id is used, so
204                                                 # a page templates/$id can
205                                                 # be used as the template.
206                 } ;
207                 if( $@ )
208                 {
209                         error(
210                                         sprintf(
211                                                         gettext("failed to process template %s"),
212                                                         htmllink(
213                                                                         $page,
214                                                                         $destpage,
215                                                                         "/templates/$id")
216                                                         )." $@"
217                                         ) ;
218                 } ;
219
220         # create and process the parameters
221                 my $tmpl_hash = mktmpl_hash( $template, \%params ) ;
222                 proc_tmpl_hash( $tmpl_hash, $page, $destpage, $scan ) ;
223         # ... and load the template with the values
224                 $template->param( $tmpl_hash ) ;
225
226         # return the processed page chunk
227                 return( IkiWiki::preprocess($page,
228                                                 $destpage,
229                                                 $template->output(),$scan)
230                                 ) ;
231         } ;
232
233         1 ;
234
235 """]]
236
237 ## sample template
238
239         # <TMPL_VAR HEADER0>
240
241         <table>
242         <TMPL_LOOP TEST0>
243         <tr>
244                 <td><TMPL_VAR DATA0></td>
245                 <td><TMPL_VAR DATA1></td>
246         </tr>
247         </TMPL_LOOP>
248         </table>
249
250 ## sample iki page
251
252         \[[!meta title="this is my loops page"]]
253
254         \[[!template id="loops"
255         header0="this is a table"
256         data0="cell0:0"
257         data1="cell0:1"
258         data0="cell1:0"
259         data1="cell1:1"
260         ]]