1 # Blosxom Plugin: interpolate_fancy
2 # Author: Rael Dornfest <rael@oreilly.com>,
3 # Modified by: Matthijs Kooijman <m.kooijman@student.utwente.nl>
5 # Documentation: See the bottom of this file or type:
6 # perldoc interpolate_fancy
8 package interpolate_fancy;
10 # --- Configurable variables -----
12 # Do you want me to recursively interpolate into the story $title
13 # and $body? Consider and reconsider carefully before turning this
14 # on as if anyone other than you has the ability to post stories,
15 # there's a chance of some tomfoolery, exposing variables and calling
16 # actions/subroutines you might not want called.
17 # 0 = No (default), 1 = Yes
18 our $recurse_into_story = 0;
20 # --------------------------------
27 # (Recursively) resolve conditinal includes <?...>...</?>.
28 # Expects two arguments: The (part of the) template we are expanding and
29 # wether or not this part will be displayed. When this last argument is false,
30 # this part of the template will not be displayed, but is still parsed to
31 # properly resolve nested conditions.
33 # This will resolve and substitute all conditionals on the "current level".
34 # This effectively means, the routine will search for and resolve all tags it
35 # finds (with proper nesting), until it finds a closing tag (</?>) for which
36 # it has not seen the opening tag, or there are no more opening tags in the
39 # This function will return the template with all conditionals on the current
40 # level resolved. This will guarantee that if there are any opening or closing
41 # tags left in the template, the first one will be a closing tag (which can
42 # then be resolved in the upper level).
48 if ($template !~ /(.*?)<\?(\!?\$\w+(?:::)?\w*)(?:\s+?(.+?))?>(.*)/s) {
49 return $template; # No open tags, normal text
57 if ($head =~ /<\/\?>/s) {
58 return $template; # There is a closing tag before the first open tag,
62 $displayitem = $display;
63 if ($displayitem) { # Don't care about these tests if we're not displayed anyway.
64 my $negated = ($var =~ s/^\!//); # Remove negation mark
65 if ($negated || !$attr) {
67 warn("<?!$var $attr>: Negated expression can't have attributes, ignoring them.");
69 $displayitem = eval("defined $var");
70 if ($negated) { $displayitem = !$displayitem; }
72 $displayitem = _test(eval($var), $attr);
77 $tail = _resolve_nested($tail, $displayitem);
79 if ($tail !~ /<\/\?>/s) { # Is there no closing tag?
80 warn("Invalid nesting: <?$var $attr> missing closing tag.");
84 $tail =~ s/<\/\?>//s; # Remove the closing tag
86 $tail =~ s/.*?<\/\?>//s; # Remove up to the closing tag
88 $template = $head . $tail;
99 # Halt recursive interpolation of story $body
100 # by mangling interpolation tags (to be unmangled in a moment)
101 unless ($recurse_into_story) {
102 $blosxom::title =~ s/<(\@|\??\$)/<#INTERPOLATE_FANCY_DEFANG#$1/gsi;
103 $blosxom::body =~ s/<(\@|\??\$)/<#INTERPOLATE_FANCY_DEFANG#$1/gsi;
106 my $template = shift;
108 # Backward Compatibility with core Blosxom style interpolation
109 $template =~ s#(?<!<)(?<!<\?)(?<!<\?!)(\$\w+(?:::)?\w*)#<$1 />#gs;
112 # Conditional inclusion
114 # e.g. <?$path>stuff</?>
115 $template = interpolate_fancy::_resolve_nested($template, 1);
121 while( $template =~ s/<\$([a-zA-Z?]\w+(?:::)?\w*)\s+?\/>/"defined \$$1 ? \$$1 : undef"/gsee ) { }
126 # e.g. <@plugin.subroutine arg1="a" output="no" />
127 # e.g. <@plugin.subroutine encoding="Latin1" output="yes">pass content</@>
128 $template =~ s#<\@(\w+?)\.(\w+?)\s+?(.+?)?(?:>(.*?)<\/\@\1\.\2>|\s+?\/>)#&interpolate_fancy::_action($1,$2,$3,$4)#gse;
130 # Unmangle mangled interpolation tags in story $title and $body
131 # (by now in the template itself)
132 unless ($recurse_into_story) {
133 $template =~ s/<#INTERPOLATE_FANCY_DEFANG#(\@|\??\$)/<$1/gsi;
142 my($variable, $attr) = @_;
143 my $attributes = interpolate_fancy::_attributes($attr);
145 defined $attributes->{eq} and return $variable eq $attributes->{eq};
146 defined $attributes->{ne} and return $variable ne $attributes->{ne};
147 defined $attributes->{gt} and return $variable > $attributes->{gt};
148 defined $attributes->{lt} and return $variable < $attributes->{lt};
149 defined $attributes->{like} and return $variable =~ /$attributes->{like}/;
150 defined $attributes->{unlike} and return $variable !~ /$attributes->{unlike}/;
156 my($plugin, $action, $attr, $content) = @_;
158 $content =~ s#<\@(\w+?)\.(\w+?)\s+?(.+?)?(?:>(.*?)<\/\@\1\.\2>|\s+?\/>)#&interpolate_fancy::_action($1,$2,$3,$4)#gse;
160 my $attributes = interpolate_fancy::_attributes($attr);
162 $blosxom::plugins{$plugin}
163 and $plugin->can($action)
164 and $result = $plugin->$action($attributes, $content);
166 return $attributes->{'output'} =~ /yes/i ? $result : undef;
173 while ( $attr =~ /\b(\w+?)\s*?=\s*?(["'])(.*?)\2/g ) {
174 $attributes->{$1} = $3;
186 Blosxom Plug-in: interpolate_fancy
190 Overrides Blosxom's far simpler to use, but more limited, default interpolate()
193 Include bits of text and template variable values in templates, either
194 conditionally or unconditionally:
196 Perform actions (i.e. call plug-in subroutines) at any point in your page,
197 whether to act on current content and return results or no.
199 =head2 Variable expansion
200 This syntax will expand to the value of the referenced variable and can be
201 used to include dynamic content in your pages.
203 * Unconditionally and recursively
205 e.g. include a link to the story's path using various template variables.
207 <a href="<$url /><$path />"><$path /></a>
209 Limited by the $recurse_into_story configuration directive (see
210 the CONFIGURATION below).
212 * Unconditionally and recursively (backward compatibility with Blosxom's standard interpolation)
214 e.g. include a link to the story's path using various template variables.
216 <a href="$url$path">$path</a>
218 Limited by the $recurse_into_story configuration directive (see
219 the CONFIGURATION below).
221 ==head2 Conditional inclusion
222 These tags will each have a certain condition attached to them, depending on
223 what syntax is used exactly. All text between the opening and closing tags
224 will only be included in the final output of the page if the given condition
225 is true, it will be discarded otherwise.
227 Note that as of 2006-01-11 conditional tags can be nested, which effectively
228 allows you to specify the logical and of two (or more) conditions. There is
229 no way yet to specify the logical or, though. If you're using nested
230 conditions and they won't work properly, check your error log, since the
231 plugin puts warnings about incorrect nestings there.
233 * The template variable has a value (i.e. is defined)
235 e.g. include a hyperlink to the story's path if it has a path (i.e.
238 <?$path><a href="<$url /><$path />"><$path /></a></?>
240 * The template variable doesn't have a value (i.e. is NOT defined)
242 e.g. include a hyperlink to home if path is undefined.
244 <?!$path><a href="<$url />">home</a></?>
246 * The template variable is equal (=) to a particular value
248 e.g. include "1 writeback" (singular) if the value of writeback count is 1
250 <$writeback::count /> <?$writeback::count eq="1">writeback</?>
252 * The template variable is not equal (!=) to a particular value
254 e.g. include "x writebacks" (plural) if the value of writeback
257 <$writeback::count /> <?$writeback::count ne="1">writebacks</?>
259 * The template variable is less than (<) a particular value
261 e.g. include "no writebacks" if the value of writeback count is < 1
263 <?$writeback::count lt="1">no writebacks</?>
265 * The template variable is greater than (>) a particular value
267 e.g. include "oodles of writebacks" if the value of writeback count is > 50
269 <?$writeback::count gt="50">oodles of writebacks</?>
271 * The template variable is like (=~) a particular regular expression
273 e.g. Greet a visitor properly, depending on their courtesy title
276 <?$user::courtesy like="^(Mr\.?|Sir)$">Sir</?>
277 <?$user::courtesy like="^(Mr?s\.?|Miss)$">M'am</?>
279 * The template variable is unlike (!~) a particular regular expression
281 e.g. The posting is neither a film nor a book
283 <?$path unlike="/(Film|Literature)">no review</?>
287 Perform an action (i.e. call a plug-in's subroutine) at any point in your page.
288 Optionally pass arguments as key/value pairs stated as XML-style attributes.
291 <@plugin.subroutine arg1="a" arg2="bee" />
293 calls &plugin::subroutine( {'arg1'=>'a', 'arg2'=>'bee' } ).
295 Specify that results should be sent to the browser using the output="yes"
298 <@plugin.subroutine arg1="a" arg2="bee" output="yes" />
300 Otherwise, subroutines will still have their effect, but the results will
303 Content wrapped in the action call is sent as another argument to the
306 <@plugin.subroutine encoding="Latin1" output="yes">
308 </@plugin.subroutine>
310 This calls &plugin::subroutine( {'encoding'=>'Latin1', 'output'=>'yes' }, "pass this content" ).
312 Actions are recursive, meaning that you can embed an action inside another,
313 and so on and so on and so on. Actions are unfolded from the inside out,
314 with the most deeply embedded first, second deepest second, and so forth until
315 the outermost action is performed last.
317 Recursion is limited by the $recurse_into_story configuration directive (see
318 the CONFIGURATION below).
322 For those of you interested in writing plugin actions or using some of the
323 more advanced features in your Blosxom blog templates, here are a couple of
328 # For the sake of this example, assume these actions live in a "myplugin"
331 # This action strips HTML from its content
333 my($self, $attributes, $content) = @_;
334 $content =~ s!</?.+?>!!g;
338 # This action foreshortens its content to a length specified in the call to
339 # action's length attribute
341 my($self, $attributes, $content) = @_;
342 my $default_length = 144;
343 return substr($content, 0, $attributes{'length'}||$default_length);
348 Calling these individually in a Blosxom flavour template looks something like:
350 The following bit of text is devoid of HTML:
351 <@myplugin.strip_html output="Yes">
352 Silly <a href="http://www.raelity.org/">me</a>, I plumb
353 <em>forgot</em> to remove the HTML.
354 </@myplugin.strip_html>
356 The following bit of text is only 20 characters in length:
357 <@myplugin.foreshorten output="Yes">
358 This text is far longer than 20 characters on the page,
359 but will only appear as "This text is far lon" in the
361 </@myplugin.foreshorten>
363 And in combination, stripping the HTML _before_ foreshortening (notice
364 the strip_html action is embedded inside the foreshorten action and
367 The following bit of text is only 20 characters in length and devoid of HTML:
368 <@myplugin.foreshorten output="Yes">
369 <@myplugin.strip_html output="Yes">
370 Silly <a href="http://www.raelity.org/">me</a>, I plumb
371 <em>forgot</em> to remove the HTML.
372 This text is far longer than 20 characters on the page,
373 but will only appear as "This text is far lon" in the
375 </@myplugin.strip_html>
376 </@myplugin.foreshorten>
380 Drop the interpolate_fancy plug-in into your Blosxom plugins folder.
384 None necessary; interpolate_fancy will run out of the box with no need
385 of additional configuration or fiddling on your part (caveat: see
386 BACKWARD COMPATILITY below).
388 The interpolate_fancy plugin does sport one configuration directive
389 which you should very much consider leaving alone.
391 # 0 = No (default), 1 = Yes
392 my $recurse_into_story = 0;
394 $recurse_into_story decides whether or not the interpolation engine
395 should respect and interpolate (swap for the associated value)
396 variables and actions embedded in story $title and $body themselves.
398 Off by default, you should consider and reconsider carefully before
399 turning this on as if anyone other than you has the ability to post
400 stories to your blog, there's a chance of some tomfoolery, exposing
401 variables and calling actions/subroutines you might not want called.
403 =head1 BACKWARD COMPATIBILITY
405 If you've been using core Blosxom's interpolation style
406 (e.g. $title), this plugin will provide backward compatibility,
407 requiring no template rewriting on your part.
409 If you've been using the interpolate_conditional plugin,
410 the conditional bits won't be respected by default. You should
411 run your templates through the interpolate2fancy utility
412 [http://www.blosxom.com/downloads/utilities/interpolate2fancy.py].
420 Rael Dornfest <rael@oreilly.com>, http://www.raelity.org/
421 Modified by Matthijs Kooijman <m.kooijman@student.utwente.nl>, http://katherina.student.utwente.nl/~matthijs/blog
425 Blosxom Home/Docs/Licensing: http://www.raelity.org/apps/blosxom/
427 Blosxom Plugin Docs: http://www.raelity.org/apps/blosxom/plugin.shtml
431 Address bug reports and comments to the Blosxom mailing list
432 [http://www.yahoogroups.com/group/blosxom].
436 Blosxom and this Blosxom Plug-in
437 Copyright 2003, Rael Dornfest
439 Permission is hereby granted, free of charge, to any person obtaining a
440 copy of this software and associated documentation files (the "Software"),
441 to deal in the Software without restriction, including without limitation
442 the rights to use, copy, modify, merge, publish, distribute, sublicense,
443 and/or sell copies of the Software, and to permit persons to whom the
444 Software is furnished to do so, subject to the following conditions:
446 The above copyright notice and this permission notice shall be included
447 in all copies or substantial portions of the Software.
449 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
450 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
451 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
452 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
453 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
454 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
455 OTHER DEALINGS IN THE SOFTWARE.