Parent Directory
|
Revision Log
This commit was manufactured by cvs2svn to create branch 'rel-2-4-patches'.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK/Template.pm,v 1.3 2006/01/25 23:13:51 sh002i Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK::Template; 18 use base qw(Exporter); 19 20 =head1 NAME 21 22 WeBWorK::Template - apply a template to a ContentGenerator. 23 24 =head1 SYNOPSIS 25 26 use WeBWorK::Template qw/template/; 27 28 my $templateFile = "default.template"; 29 my $cg = WeBWorK::ContentGenerator::SomeSubclass->new($r); 30 31 template($templateFile, $cg); 32 33 =head1 DESCRIPTION 34 35 WeBWorK uses templates to customize the presentation of pages. A template is a 36 complete HTML document, containing normal HTML code and special escape 37 sequences. 38 39 =head2 ESCAPE SEQUENCES 40 41 Escape sequences have a format similar to that of server-side includes (SSI). 42 The format is as follows: 43 44 <!--#NAME ARG1="VALUE1" ARG2="VALUE2" ...--> 45 46 An escape's C<NAME> and arguments (C<ARG1>, C<ARG2>, etc.) are case-sensitive, 47 the argument values may or may not be depending on the particular escape. Most 48 escapes have case-sensitive values. 49 50 Escape equences are replaced by dynamically generated content from WeBWorK's 51 content generation system, WeBWorK::ContentGenerator. When a template escape 52 C<NAME> is encountered in the document, the template processor checks for a 53 method of the same name in the current content generator. If found, that method 54 is invoked as follows: 55 56 @result = $contentGenerator->NAME(\%escapeArguments); 57 58 where %escapeArguments contains the key/value pairs of arguments in the escape 59 sequence (like C<ARG1="VALUE1" ARG2="VALUE2">). The method may print() output 60 directly to the client or return a result. If the method returns a non-empty 61 value, it is sent to the client. 62 63 =head2 CONDITIONAL PROCESSING 64 65 In addition to the normal escape sequences above, the escape sequences C<#if>, 66 C<#else>, and C<#endif> are reserved and used to conditionally include portions 67 of the template in the output. 68 69 The C<#if> escape sequence has the following form: 70 71 <!--#if PRED1="VALUE1" PRED2="VALUE2" ...-> 72 73 When an C<#if> escape is evaluated, each predicate (C<PRED1>, C<PRED2>, etc.) is 74 evaluated by calling a method named C<if_PRED> in the current content generator 75 with the predicate's value as the sole argument. If no such method exists, the 76 predicate is false. If the method returns a true value, the predicate is true. 77 78 If any predicate is true, the code between the C<#if> escape and a matching 79 C<#else> or C<#endif> escape is included in the output. C<#if> statements can be 80 nested. 81 82 For example: 83 84 <!--#if loggedin="1"--> 85 <!--#if can="loginstatus"--> 86 <div class="LoginStatus"> 87 <!--#loginstatus--> 88 </div> 89 <!--#endif--> 90 <!--#endif--> 91 <!--#if can="path"--> 92 <div class="Path"> 93 <!--#path style="text" image="/webwork2_files/images/right_arrow.png" text=" > "--> 94 </div> 95 <!--#endif--> 96 97 Several predicate functions are defined in WeBWorK::ContentGenerator. 98 99 =cut 100 101 use strict; 102 use warnings; 103 use WeBWorK::Utils qw(readFile); 104 105 our @EXPORT = (); 106 our @EXPORT_OK = qw( 107 template 108 ); 109 110 =head1 FUNCTIONS 111 112 =over 113 114 =item template($templatePath, $cg) 115 116 Process the template file $templatePath. Methods from $cg, an instance of 117 WeBWorK::ContentGenerator, are called to handle escape sequences in the 118 template. 119 120 =cut 121 122 sub template { 123 my ($templatePath, $cg) = @_; 124 125 # the truth value of the top of this stack determines if we're printing output or not. 126 # we want to start off in printing mode. 127 # say $ifstack[-1] to get the result of the last <#!--if--> 128 my @ifstack = (1); 129 130 my @template = split /\n/, readFile($templatePath); 131 132 foreach my $line (@template) { 133 # This is incremental regex processing. 134 # the /c is so that pos($line) doesn't die when the regex fails. 135 while ($line =~ m/\G(.*?)<!--#(\w*)((?:\s+.*?)?)-->/gc) { 136 my ($before, $function, $raw_args) = ($1, $2, $3); 137 my @args = ($raw_args =~ /\S/) ? cook_args($raw_args) : (); 138 139 if ($ifstack[-1]) { 140 print $before; 141 } 142 143 if ($function eq "if") { 144 # a predicate can only be true if everything else on the ifstack is already true, for ANDing 145 push @ifstack, (if_handler($cg, [@args]) && $ifstack[-1]); 146 } elsif ($function eq "else" and @ifstack > 1) { 147 $ifstack[-1] = not $ifstack[-1]; 148 } elsif ($function eq "endif" and @ifstack > 1) { 149 pop @ifstack; 150 } elsif ($ifstack[-1]) { 151 if ($cg->can($function)) { 152 my @result = $cg->$function({@args}); 153 if (@result) { 154 print @result; 155 } else { 156 warn "Template escape $function returned an empty list."; 157 } 158 } 159 } 160 } 161 162 if ($ifstack[-1]) { 163 print substr($line, (defined pos $line) ? pos $line : 0), "\n"; 164 } 165 } 166 } 167 168 # cook_args(STRING) - parses a string of the form ARG1="FOO" ARG2="BAR". Returns 169 # a list which pairs into key/values and fits nicely in {}s. 170 # 171 sub cook_args($) { # ... also used by bin/wwdb, so watch out 172 my ($raw_args) = @_; 173 my @args = (); 174 175 # Boy I love m//g in scalar context! Go read the camel book, heathen. 176 # First, get the whole token with the quotes on both ends... 177 while ($raw_args =~ m/\G\s*(\w*)="((?:[^"\\]|\\.)*)"/g) { 178 my ($key, $value) = ($1, $2); 179 # ... then, rip out all the protecty backspaces 180 $value =~ s/\\(.)/$1/g; 181 push @args, $key => $value; 182 } 183 184 return @args; 185 } 186 187 # This is different. It probably shouldn't print anything (except in debugging cases) 188 # and it should return a boolean, not a string. &if is called in a nonstandard way 189 # by &template, with $args as an arrayref instead of a hashref. this is a hack! yay! 190 # 191 # OK, this is a pluggin architecture. it iterates through attributes of the "if" tag, 192 # and for each predicate $p, it calls &if_$p in an object-oriented way, continuing the 193 # grand templating theme of an object-oriented pluggable architecture using ->can($). 194 # 195 sub if_handler { 196 my ($cg, $args) = @_; 197 # A single if "or"s it's components. Nesting produces "and". 198 199 my @args = @$args; # Hahahahaha, get it?! 200 201 if (@args % 2 != 0) { 202 # flip out and kill people, but do not commit seppuku 203 print '<!--&if recieved an uneven number of arguments. This shouldn\'t happen, but I\'ll let it slide.-->\n'; 204 } 205 206 while (@args > 1) { 207 my ($key, $value) = (shift @args, shift @args); 208 209 # a non-existent &if_$key is the same as a false result, but we're ORing, so it's OK 210 my $sub = "if_$key"; # perl doesn't like it when you try to construct a string right in a method invocation 211 if ($cg->can("if_$key") and $cg->$sub("$value")) { 212 return 1; 213 } 214 } 215 216 return 0; 217 } 218 219 =back 220 221 =cut 222 223 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |