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

Web Topics
All Articles
Scripting Languages

Atom 1.0 Feed RSS 1.0 Feed RSS 2.0 Feed

Learning Lab

Developing Movable Type Plug-ins

by Timothy Appnel

One of the most significant things to come out of the weblog movement is the availability of low-cost and relatively easy-to-use tools for publishing. One of the most sophisticated and powerful is Movable Type, developed by Ben and Mena Trott of Six Apart. Movable Type's features are so rich that the tool's uses have begun to transcend weblogging.

Written in a highly modular Perl object-oriented (OO) style, Movable Type (MT) has an open code base (it's not open source—an important distinction) that makes the browser-based tool quite flexible and easily modifiable to adapt to any number of publishing applications. In recent releases, extending MT has become easier and more elegant with the introduction of a plugin framework that continues to be enhanced with each new release.

In this article I will cover the MT plugin framework, its complete API, and the basics of hooking into the core systems operation and its data persistence service. It's assumed that you're somewhat knowledgeable with Perl and familiar with its OO style. (See Simon Cozens' article on Object-Oriented Perl for a quick primer.) With the richness of Perl and the MT system, a whole book could be written on the subject. We'll just cover the basics in this whirlwind tour.

The Movable Type Plugin Framework

Related Reading

Essential Blogging
Selecting and Using Weblog Tools
By Cory Doctorow, Rael Dornfest, J. Scott Johnson, Shelley Powers, Benjamin Trott, Mena G. Trott

As I've mentioned, Movable Type provides developers with a framework to easily and elegantly extend the base functionality of the system using OO Perl. Generally speaking, plugins are limited to MT's template processing engine, to add variable, container, and conditional tags in addition to global filters. In version 2.6, the plugin framework began to branch out from template processing by introducing an API for hooking in text-formatting engines and access to MT's data persistence mechanisms. We'll go into the specifics of what these can do for you throughout the article.

Installing plugins are quite easy. Simply place the code in a subdirectory named plugins in the main Movable Type directory where mt.cgi resides. (If you are running an older version of MT and have not installed a plugin before, you may have to create this directory.) MT will attempt to load any file in the plugins directory ending with .pl as a plugin to the system. Plugins are available to all weblogs hosted by this instance of MT.

In addition to the plugin framework, MT provides an extensive and well-documented array of functionality through its OO Perl modules, giving developers the ability to create command-line utilities to their own extension applications. We won't be able to cover this topic in this article, but the documentation is available here.

Let's dive into our first plugin.

MTHelloWorld: Your First Movable Type Plugin

Let's start with a basic variable tag that simply inserts a value into a template when in encountered.

package MT::Plugin::HelloWorld;
use MT::Template::Context;
MT::Template::Context->add_tag(HelloWorld => sub { return 'Hello World.'; } );

Save this code to a file named mt-helloworld.pl in the plugins directory as we discussed, and presto! — <MTHelloWorld /> in your templates will be replaced with the string Hello World. Let's review what our first plugin is doing.

In the first line I declared a package of MT::Plugin::HelloWorld. This declaration is optional, but is good form to avoid namespace collisions with other plugins or MT. Using MT::Plugin:: is another good practice for clarity and similar reasons.

In the second line we call into service MT::Template::Context, the module that contains the majority of the plugin magic and is the main workhorse during content generation.

Finally we register our HelloWorld tag by passing in a single key-value hash. The key is the tag name (case matters) you are registering. The MT prefix is assumed and will be appended by the system during processing. The value is a subroutine that will be called when the tag registered is incurred during processing. Here I placed the subroutine directly in the hash because it was so simple, but it's generally good form to use the anonymous subroutine to call an external named subroutine. This practice makes your code easier to read and affords you the advanced practice of reusing the subroutine with multiple tags.

Let's add a bit more sophistication to this plugin. Suppose we want to be able to say "Hello" to a specific world, and define that world in our template markup. Here is what our code may look like:

package MT::Plugin::HelloWorld;
use MT::Template::Context;
MT::Template::Context->add_tag(HelloWorld => \&hello_world); 
sub hello_world {
  my $ctx = shift;
  my $args = shift;
  return "Hello " defined($args->{world})?$args->{world}:'World';

Now we can use <MTHelloWorld world="Krypton"/> and Hello Krypton will be inserted in our template. If we don't declare a world argument, we still get Hello World. Let's break down what's different.

Since our tag's routine is getting a bit more sophisticated, in the third line we've moved it to a named subroutine that's external to this command. Moving to that subroutine, we see in the first two lines that MT passes template tag plugins two references for use in our plugin routine. I've coded them longhand for clarity, but you can use whatever brand of Perl kung fu you practice to work with these references.

The first reference declared as $ctx is the current instance of MT::Template::Context. This object instance that carries all of the information to the template processor's current state is essential to almost any type of plugin processing. We'll delve into some of the common contents of this object instance in a later section.

The second reference declared as $args is a hash of any arguments that were defined in the template. In the third and final line of the subroutine, we use this hash to check if an argument of world has been defined and create our return string accordingly, using World as a default. In good Perl form, all other arguments are ignored. (Perhaps they're for the global filter handler.)

NOTE: Our particular example would always insert something into a template. However, in some instances you may decide to insert nothing, based on some condition. In these cases you must return a null string and not an undefined value. An undefined value returned from a plugin is treated as an error and will stop all processing.

Variable replacement isn't really that interesting. The real power of plugins is when they hook into the system and are used in combination with other tags. Before continuing on to the rest of the plugin framework, we'll review some of the common ways we can hook into MT's processing.

Pages: 1, 2, 3

Next Pagearrow