# This is MTAgain. Original code written by Lummox JR, with new additions by # Tony Boyd. This version has only been tested on MT 3.34, and has a minimum # requirement of MT 3.0. # # CHANGELOG: # # Version 1.2 (2008-May-17): # * Added the MTAgainBreak tag. # * Added the MT 3 plugin interface. # # Version 1.1 (2003-Sep-20): # * Added the MTAgainStop tag. # # Version 1.0: # * Initial release. package MT::Plugin::Again; use MT; use MT::Plugin; use MT::Template::Context; use vars qw( $VERSION ); $VERSION = '1.2'; my $plugin = MT::Plugin->new({ name => 'Again', description => "This provides the MTAgain tag and friends, which allows for recursive loops.", doc_link => 'http://forums.sixapart.com/index.php?showtopic=23599', author_name => 'Lummox JR & Tony Boyd', author_link => 'http://www.outshine.com/', version => $VERSION, config_template => \&again_template }); MT->add_plugin($plugin); MT::Template::Context->add_container_tag(Again => \&mtagain); MT::Template::Context->add_tag(AgainHere => \&mtagainhere); MT::Template::Context->add_tag(AgainBreak => \&mtagainbreak); MT::Template::Context->add_container_tag(AgainStop => \&mtagainstop); sub mtagain { my($ctx, $args, $cond) = @_; my $res = ''; my $limit=$args->{limit}; $limit=1000 unless defined $limit; $limit=undef if ($limit =~ m/^no/i); my $builder = $ctx->stash('builder'); my $tokens = $ctx->stash('tokens'); my $oldtokens=$ctx->stash('again_tokens'); $ctx->stash('again_tokens',$tokens); my $oldlimit=$ctx->stash('again_limit'); $ctx->stash('again_limit',(defined $limit)?$limit-1:undef); defined(my $out = $builder->build($ctx, $tokens, $cond)) or return $ctx->error( $builder->errstr ); $ctx->stash('again_tokens',$oldtokens); $ctx->stash('again_limit',$oldlimit); $res .= $out; $res; } sub mtagainhere { my($ctx, $args, $cond) = @_; my $limit=$ctx->stash('again_limit'); return '' if (defined $limit && $limit<=0); my $res = ''; my $tokens = $ctx->stash('again_tokens'); defined $tokens or return $ctx->error(MT->translate( "[_1] is not valid outside of [_2].", "", "" )); $ctx->stash('again_limit',($limit>1)?($limit-1):0) if defined $limit; my $builder = $ctx->stash('builder'); defined(my $out = $builder->build($ctx, $tokens, $cond)) or return $ctx->error( $builder->errstr ); $res .= $out; $res; } sub mtagainbreak { my($ctx, $args, $cond) = @_; my $limit=$ctx->stash('again_limit'); return '' if (defined $limit && $limit<=0); my $tokens = $ctx->stash('again_tokens'); defined $tokens or return $ctx->error(MT->translate( "[_1] is not valid outside of [_2].", "", "" )); $ctx->stash('again_limit',-1); my $res = ''; $res; } sub mtagainstop { my($ctx, $args, $cond) = @_; my $limit=$ctx->stash('again_limit'); return '' if (defined $limit && $limit<0); my $res = ''; my $atokens = $ctx->stash('again_tokens'); defined $atokens or return $ctx->error(MT->translate( "[_1] is not valid outside of [_2].", "", "" )); my $builder = $ctx->stash('builder'); my $out; # set a flag that this was actually called $ctx->stash('again_stopped',1) if(defined $ctx->stash('again_stopped')); if(!defined($limit) || $limit>0) { $ctx->stash('again_limit',($limit>1)?($limit-1):0) if defined $limit; local $ctx->{__stash}{again_stopped}=0; defined($out = $builder->build($ctx, $atokens, $cond)) or return $ctx->error( $builder->errstr ); if($ctx->{__stash}{again_stopped}) {$res.=$out; return $res;} } # build this tag's contents if at the end of the limited recursion, or # if the "any" argument is used and there's nothing left to recurse if(!$limit || $limit<1 || $args->{any}) { defined($out = $builder->build($ctx, $ctx->stash('tokens'), $cond)) or return $ctx->error( $builder->errstr ); $res .= $out; } $res; } sub again_template { my $tmpl = <<'EOT';

This provides the following tags:

<MTAgain limit="100"> is a container tag that you wrap around the code you want to repeat. The limit attribute is optional. However, I suggest you add it. The old MTAgain plugin had a hard-coded limit of 50, so it was impossible to lock up a server with endless looping. That's great for safety, but it also makes it impossible to loop through thousands of blog entries. Since I wanted to do exactly that, this version of MTAgain has a limit of 1000. If you don't need it looping that deeply, you should set a limit.

<MTAgainHere> is a token that you place in the spot where you want the code to repeat. This needs to be inside the MTAgain tags. This is a difficult concept for some. You are putting the code inside of itself. But it isn't that hard. Consider this code:

<MTAgain limit="3">
<MTEntryPrevious><$MTEntryTitle$><MTAgainHere></MTEntryPrevious>
</MTAgain>

You've put the MTEntryPrevious tags inside the MTAgain tags, so the MTEntryPrevious tags are going to repeat. Where will they repeat? Where the MTAgainHere tag is -- inside the MTEntryPrevious tag. This means it shows the name of the previous entry, then shows the name of the entry previous to that and so on. See?

<MTAgainStop any="1"> is a container tag that you wrap around the code you want to appear at the end of the loop. The "any" attribute is optional. Since there are two ways to end the loop, the any attribute determines which ending it responds to. The first way a loop can end is that it peters out, never hitting your hard limit. For example, if you limit the loop to running 5 times but there are only 3 items to loop through, you never hit the hard limit of 5. In such a case, you want <MTAgainStop any="1">. However, if you need code for when the hard limit IS reached, then you want <MTAgainStop>. That will not run if it peters out, but it will run if the hard limit is reached and the loop is forced to stop.

<MTAgainBreak> is a token that you use to kill a loop, even if the limit hasn't been reached. Used with normal MT tags it almost doesn't make sense -- wherever you put it, it'll kill the looping immediately, so what's the point? Well, if you're using conditional tags, it gets much more interesting. Consider what happens when you couple this with the <MTIfEqual> tag from the Compare Plugin:

<MTAgain>
<MTEntryPrevious>
<MTEntryTags>
<MTIfEqual a="[MTTagName]" b="rocknroll">
<a href="<$MTEntryPermalink$>"><$MTEntryTitle$></a><MTAgainBreak>
</MTIfEqual>
</MTEntryTags>
<MTAgainHere>
</MTEntryPrevious>
</MTAgain>

What does that do? That keeps going backwards through previous entries -- that's what MTAgain & MTAgainHere gets you. But notice the MTIfEqual tag -- it's watching for a specific tagged entry. When it finds it, it prints out a link, followed by MTAgainBreak, which ends the loop. Thus, you can have a link to the "previous entry tagged as rocknroll" or whatever.

EOT } 1;