Web DevCenter
oreilly.comSafari Books Online.Conferences.
MySQL Conference and Expo April 14-17, 2008, Santa Clara, CA

Sponsored Developer Resources

Web Columns
Adobe GoLive
Essential JavaScript
Megnut

Web Topics
All Articles
Browsers
ColdFusion
CSS
Database
Flash
Graphics
HTML/XHTML/DHTML
Scripting Languages
Tools
Weblogs

Atom 1.0 Feed RSS 1.0 Feed RSS 2.0 Feed

Learning Lab






Developing Movable Type Plug-ins
Pages: 1, 2, 3

The Stash

Hooking into MT's template processing starts with the stash method provided by the MT::Template::Context module. Through stash, we can retrieve the current information MT is processing templates with at that moment. We can also use stash to store our own information for later use by other associated tags. Here is a quick example of its use:



# This line stores $value in the current context with a key of 'foo.'
$ctx->stash('foo',$value);

# This line retrieves the value of foo and assigns it to $value
my $value = $ctx->stash('foo');

As MT works its way recursively through the tags it encounters while processing templates, it is constantly adding, retrieving, and clearing values from the stash. Here are some of the current keys MT will use during template processing.

blogA reference to the current MT::Blog instance in context.
blog_idAn integer value of the current weblog's ID. The equivalent of MT::Blog->id stored for convenience.
entriesA reference to an array of MT::Entry objects representing the entries being currently processed.
entryA reference to the current MT::Entry instance in context.
categoryA reference to the current MT::Placement instance in context.
commentA reference to the current MT::Comment instance in context.
pingA reference to the current MT::Ping (TrackBack ping) instance in context.
tagA string representing the current tag name being processed. The MT prefix is omitted.

Other than tag, these stashed references provide access to content that has been retrieved by MT from the database into memory, based on the template processing's current context that has been determined by the template type or by another tag.

As we'll see in the examples that follow, this information in the stash is quite handy to developing our own plugins. Now let's return to the rest of the MT plugin framework.

Container Tags

As we've discussed, variable tags alone are not terribly interesting. Another construct supported by MT is the container tag. As its name implies, this type of tag contains additional markup and template tags within start and end tags. Container tags allow us to process a block of template code and/or create a context from which other tags can draw their data. Here is a simple example from MT's built-in tags of a container (MTEntries) that creates a list of weblog entry titles inserted into the template by MTEntryTitle.

<MTEntries>
   <MTEntryTitle /><br />
</MTEntries>

Programming container tags requires a bit more consideration, because it is likely that the contents of the container tag require further processing. Let's review an example of a simple container with an associated variable tag.

package MT::Plugin::SimpleLoop;
use MT::Template::Context;
MT::Template::Context->add_container_tag(SimpleLoop => \&loop );
MT::Template::Context->add_tag(SimpleLoopIndex => \&loop_index );
 
sub loop {
  my $ctx = shift;
  my $args = shift;
  my $content = '';
  my $builder = $ctx->stash('builder');
  my $tokens = $ctx->stash('tokens');
  for my $i(1..$args->{loops}) {
    $ctx->stash('loop_index',$i);
    my $out = $builder->build($ctx, $tokens);
    $content .= $out;
  }
}
 
sub loop_index {
  my $ctx = shift;	
  return $ctx->stash('loop_index');
}

1;

With this plugin implemented, we can create a list of integers in our template like this:

<MTSimpleLoop loops="10">
  <MTSimpleLoopIndex/><br />
</MTSimpleLoop>

Looking back at the example code, things start off like our variable tag replacements. I declare a package and the use of MT::Template::Context class before registering one container tag and one variable tag with their associated subroutines. Moving on to the first subroutine of loop, we begin as before by assigning the Context class and tag argument hash references to variables.

As mentioned, container tags differ from their variable counterparts in that other template tags are assumed to be within them. This means that at some point, we have to pass the container tags' contents back into the template-processing engine before ending our subroutine. Here in our example, we retrieve a reference to the template builder class (MT::Builder) that has been stashed by the system and store it in $builder. We also get a reference to the collection of processing tokens it has created from the stash and store it in $tokens.

With everything in place, we start loop. First we stash the current index of the loop in loop_index. Next we pass the current context and the processing tokens back to the builder, eventually storing the result of that processing in $out. We concatenate this result to previous results in $content and loop again.

To see how we use the loop_index value we stashed, we go to the second subroutine, where we retrieve that value and return it as a string.

This is an excellent example of how tags work together using stash, and begins to demonstrate the possibilities of extending MT's operation with plugins. Let's press on.

Conditional Tags

We'll only take a cursory look at the conditional tag, since it's just a specialized container tag that has been added to the API for convenience.

Subroutines registered as conditional tags need only return a true or false value. MT automatically handles whether the conditional tag's contents should be passed on for further processing or be stripped from the template's output. In other words, there is a need to wrap the builder object in a conditional. Here is a simple implementation of two conditional tags.

package MT::Plugin::ConditionalExample
use MT::Template::Context;
MT::Template::Context->add_conditional_tag(IncludeThis => sub { return 1 });
MT::Template::Context->add_conditional_tag(Excludethis => sub { return 0 });

With this plugin implemented, we could use the following markup into our template:

<MTIncludeThis>This text will appear.</MTIncludeThis>
<MTExcludeThis>This text will be stripped.</MTExcludeThis>

Only the phrase: "This text will appear." would remain, since that conditional tag's subroutine returns a true value to MT's template builder.

Global Filters

Global Filters are not tags, but arguments that can be added to any Movable Type template tag. Global filter arguments take a single value (quite often just a "1" to signify "on") and invoke a filter that is applied to the tag's content right before insertion. MT's native global filters include routines for stripping markup tags, encoding XML, and converting text to lower case.

Global filters can be a sophisticated as you like, but generally they tend to be quite simple. Here is an example of a global filter that will strip out blank lines.

package MT::Plugin::StripBlanks
use MT::Template::Context;
MT::Template::Context->add_global_filter(strip_blanks => sub { &strip_blanks });
sub strip_blanks { 
  my $text = shift;
  my $arg_value = shift;
  my $ctx = shift;
  $text=~s/^\s*?\n//gm if ($arg_value); 
  return $text
}
1;

Once again, I've written this example out "longhand" for clarity. Note that the values passed to a global filter routine are different than those in its tag-based counterparts. Global filter routines are passed a scalar with the text to be processed, a scalar of the value of the argument, and finally, a reference to the Context class instance.

In our example, the argument value stored in $arg_value and the context object stored in $ctx are not of any use, so we just ignore them. We apply a simple regular expression to the value of $text and return it.

With this plugin implemented we can do …

<MTEntries strip_blanks="1">
  ...
</MTEntries>

… and all blank lines will be stripped out of the result's template markup within the MTEntries tagset.

Pages: 1, 2, 3

Next Pagearrow