Copy 'template.pm' re-write from website
[ikiwiki.git] / doc / todo / allow_TMPL__95__LOOP_in_template_directives.mdwn
1 [[!tag patch]]
2
3 [[!templte 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 # `lib/perl5/IkiWiki/Plugin/template.pm`
31
32 [[!format perl """
33
34         #!/usr/bin/perl
35         # Structured template plugin.
36         package IkiWiki::Plugin::template ;
37
38         use warnings ;
39         use strict ;
40         use IkiWiki 3.00 ;
41         use Encode ;
42
43         sub mktmpl_hash( $ ; $ ; @ ) ;
44                                 # declare to supress warning in recursive call
45         sub mktmpl_hash( $ ; $ ; @ )
46                                 # make hash for the template, filling
47                                 # values from the supplied params
48         {
49                 my $template = shift( @_ )
50                                 || error( "mktmpl_hash: no template provided" ) ;
51                 my $param_src = shift( @_ )
52                                 || error( "mktmpl_hash: no parameters" ) ;
53
54                 my $path ;
55                 if( $#_ > 0 )
56                 {
57                         $path = [ @_ ] ;
58                 } else {
59                         $path = shift(@_) || [] ;
60                 } ;
61
62                 my %params ;
63
64                 my @path_vars ;
65                 if( $#{$path} < 0 )
66                 {
67                         @path_vars = $template->query() ;
68                 } else {
69                         @path_vars = $template->query( loop => $path ) ;
70                 } ;
71
72                 foreach my $var ( @path_vars )
73                 {
74                         push( @{$path}, $var ) ;
75                         my $param_type = $template->query( name => $path ) ;
76                         if( $param_type eq 'VAR' )
77                         {
78                                 my @var_path = split( /_/, $var ) ;
79                                 if( $var_path[0] ne '' )
80                                 {
81                                         $path->[-1] = join( '_', @var_path[1..$#var_path] )
82                                                 if( $var_path[0] eq 'raw' ) ;
83                                         $params{$var} = shift( @{$param_src->{$path->[-1]}} )
84                                                         || return(undef) ;
85                                 } ;
86                         } elsif( $param_type eq 'LOOP' )
87                         {
88                                 $params{$var} = [] ;
89                                 push( @{$params{$var}}, $_ )
90                                         while( $_ = mktmpl_hash($template,$param_src,$path) ) ;
91                         } ;
92                         pop( @{$path} ) ;
93                 } ; 
94                 return( \%params ) ;
95         } ;
96
97         sub proc_tmpl_hash( $ ; $ ; $ ; $ ) ;
98                                 # declare to supress warning in recursive call
99         sub proc_tmpl_hash( $ ; $ ; $ ; $ )
100                                 # walk the hash, preprocess and
101                                 # convert to html
102         {
103                 my $tmpl_hash = shift( @_ ) ;
104                 my $page = shift( @_ ) ;
105                 my $destpage = shift( @_ ) ;
106                 my $scan = shift( @_ ) ;
107                 foreach my $key ( keys(%{$tmpl_hash}) )
108                 {
109                         unless( ref($tmpl_hash->{$key}) )
110                                                 # here we assume that
111                                                 # any reference is an
112                                                 # array and allow it to
113                                                 # fail if that's false
114                         {
115                                 $tmpl_hash->{$key} =
116                                                 IkiWiki::preprocess(
117                                                                 $page,
118                                                                 $destpage,
119                                                                 $tmpl_hash->{$key},
120                                                                 $scan ) ;
121                                 my @key_path = split( /_/, $key ) ;
122                                 $tmpl_hash->{$key} =
123                                                 IkiWiki::htmlize(
124                                                                 $page,
125                                                                 $destpage,
126                                                                 pagetype($pagesources{$page}),
127                                                                 $tmpl_hash->{$key}, )
128                                         unless( $key_path[0] eq 'raw' ) ;
129                         } else {
130                                 proc_tmpl_hash( $_, $page, $destpage, $scan )
131                                         foreach( @{$tmpl_hash->{$key}} ) ;
132                         } ;
133                 } ;
134         } ;
135
136         # "standard" ikiwiki definitions / hooks
137
138         sub import
139         {
140                 hook( type => "getsetup",
141                                 id => "template",
142                                 call => \&getsetup ) ;
143                 hook( type => "preprocess",
144                                 id => "template",
145                                 call => \&preprocess,
146                                 scan => 1 ) ;
147         } ;
148
149         sub getsetup()
150         {
151                 return(
152                                 plugin => {
153                                         safe => 1,
154                                         rebuild => undef,
155                                         section => "widget",
156                                 }, ) ;
157         } ;
158
159         sub preprocess( @ )
160         {
161         # first process arguments into arrays of values
162                 my %params ;
163
164                 my( $key, $value ) ;
165                 while( ($key,$value)=splice(@_,0,2) )
166                 {
167                         if( exists($params{$key}) )
168                         {
169                                 push( @{$params{$key}}, $value ) ;
170                         } else {
171                                 $params{$key} = [ $value ] ;
172                         } ;
173                 } ;
174
175         # set context
176                 my $scan = ! defined( wantarray() ) ;
177                                         # This needs to run even in scan
178                                         # mode, in order to process links
179                                         # and other metadata included via
180                                         # the template.
181
182         # check for critical values
183                 if( ! exists($params{id}) )
184                 {
185                         error( gettext("missing id parameter") ) ;
186                 } ;
187
188         # set some convenience variables
189                 my $id = $params{id}->[$#{$params{id}}] ;
190                 my $page = $params{page}->[$#{$params{page}}] ;
191                 my $destpage = $params{destpage}->[$#{$params{destpage}}] ;
192         # ... and an essential one for the production pass
193                 $params{basename} = IkiWiki::basename($page) ;
194
195         # load the template
196                 my $template ;
197                 eval {
198                         $template =
199                                         template_depends( $id, $page,
200                                                         blind_cache=>1 ) ;
201                                                 # The bare id is used, so
202                                                 # a page templates/$id can
203                                                 # be used as the template.
204                 } ;
205                 if( $@ )
206                 {
207                         error(
208                                         sprintf(
209                                                         gettext("failed to process template %s"),
210                                                         htmllink(
211                                                                         $page,
212                                                                         $destpage,
213                                                                         "/templates/$id")
214                                                         )." $@"
215                                         ) ;
216                 } ;
217
218         # create and process the parameters
219                 my $tmpl_hash = mktmpl_hash( $template, \%params ) ;
220                 proc_tmpl_hash( $tmpl_hash, $page, $destpage, $scan ) ;
221         # ... and load the template with the values
222                 $template->param( $tmpl_hash ) ;
223
224         # return the processed page chunk
225                 return( IkiWiki::preprocess($page,
226                                                 $destpage,
227                                                 $template->output(),$scan)
228                                 ) ;
229         } ;
230
231         1 ;
232
233 """]]
234
235 ## sample template
236
237         # <TMPL_VAR HEADER0>
238
239         <table>
240         <TMPL_LOOP TEST0>
241         <tr>
242                 <td><TMPL_VAR DATA0></td>
243                 <td><TMPL_VAR DATA1></td>
244         </tr>
245         </TMPL_LOOP>
246         </table>
247
248 ## sample iki page
249
250         \[[!meta title="this is my loops page"]]
251
252         \[[!template id="loops"
253         header0="this is a table"
254         data0="cell0:0"
255         data1="cell0:1"
256         data0="cell1:0"
257         data0="cell1:1"
258         ]]