# Loop.pl # Movable Type plugin tags for looping through a list of values # by Kevin Shay # http://www.staggernation.com/mtplugins/ # last modified July 12, 2004 package MT::Plugin::Loop; use strict; use vars qw( $VERSION ); $VERSION = '1.1'; use MT; use MT::Template::Context; eval{ require MT::Plugin }; unless ($@) { my $plugin = { name => "Loop $VERSION", description => 'Loop through a list of values (either hardcoded or generated by other MT tags), repeating a section of template code for each value.', doc_link => 'http://www.staggernation.com/mtplugins/Loop' }; MT->add_plugin(new MT::Plugin($plugin)); } MT::Template::Context->add_container_tag('Loop' => sub{&_hdlr_loop;}); MT::Template::Context->add_tag('LoopValue' => sub{&_hdlr_loop_value;}); MT::Template::Context->add_tag('LoopN' => sub{&_hdlr_loop_n;}); sub _hdlr_loop { # handler for MT container tag to loop through a given list of values # args to tag: # values: delimited list of values to loop through # delimiter (optional): indicates what string has been used to delimit # the list of values (default is semicolon) # sort (optional): pass "alpha" or "num" to sort the values my ($ctx, $args, $cond) = @_; # store initial builder and tokens so we can repeat them my $builder = $ctx->stash('builder'); my $tokens = $ctx->stash('tokens'); defined(my $values = $args->{'values'}) || return $ctx->error('No values passed'); my $delim = defined($args->{'delimiter'}) ? $args->{'delimiter'} : ','; my $text = ''; # get the non-tokenized contents of this tag my $uncompiled = $ctx->stash('uncompiled'); # if they're using [MTLoopValue/N] (i.e. passing the value or N as an # argument to a tag), we'll need to recompile on each pass, subbing # in the value my $recompile = ($uncompiled =~ /\[MTLoop/) ? 1 : 0; # and now, a brief foray into the Backslash Jungle. # within the values argument, you can use MT tags, but with # square brackets instead of angle brackets and single quotes # instead of double quotes; literal square brackets and single # quotes must be escaped with a backslash # convert non-escaped []' $values =~ s/(?/g; $values =~ s/(?compile($ctx, $values); $values = $builder->build($ctx, $tok, $cond); return $ctx->error($builder->errstr) unless defined($values); } my @vals = split(/$delim/, $values); if (defined($args->{'sort'})) { if ($args->{'sort'} eq 'alpha') { @vals = sort {$a cmp $b} @vals; } elsif ($args->{'sort'} eq 'num') { @vals = sort {$a <=> $b} @vals; } } my $n = 0; for (@vals) { $n++; if ($recompile) { my $copy = $uncompiled; $copy =~ s/\[MTLoopValue\]/$_/g; $copy =~ s/\[MTLoopN\]/$n/g; $tokens = $builder->compile($ctx, $copy); } $ctx->stash('loop_n', $n); $ctx->stash('loop_value', $_); defined(my $iter = $builder->build($ctx, $tokens)) || return $ctx->error($ctx->errstr); $text .= $iter; } return $text; } sub _hdlr_loop_value { # handler for MT tag that prints the current value within the loop my ($ctx, $args) = @_; defined(my $val = $ctx->stash('loop_value')) || return $ctx->error('Not called from within MTLoop container'); return $val; } sub _hdlr_loop_n { # handler for MT tag that prints the number of the loop iteration my ($ctx, $args) = @_; defined(my $n = $ctx->stash('loop_n')) || return $ctx->error('Not called from within MTLoop container'); return $n; } 1;