Developing Movable Type Interface Plugins With BigPAPI
The BigPAPI plugin for Movable Type allows you to create plugins that can easily add features to Movable Type's interface. This article explains the background behind the develpment of BigPAPI, and walks through the creation of a simple working plugin.
Why BigPAPI?
With each new version, Movable Type has become friendlier to plugin development. The earliest plugins could do little more than add tags to the platform's templating system. Now (as of version 3.2), the Plugin API allows plugins to define not only template tags but text filters, conditional tags, global tag attributes, object callbacks, application callbacks, object actions, itemset actions, and junk filters. And a plugin can contain its own CGI scripts that are tightly integrated with Movable Type, and easy to develop, thanks to the constantly improving API.
There's always been one missing element, though. A plugin has never been able to incorporate itself directly into Movable Type's own interface. While plugins can have their own interfaces that coexist with the core system, or define certain types of actions that will show up on some of the system's screens, developers have never had the ability to do things like add a field to an editing screen, or reorder the items in a menu, without making users hack some of the Movable Type program files.
Since Movable Type 3.16, alternate application templates have been an option, but they're somewhat limited; you can use an alternate template to add static HTML elements to the interface, but a plugin doesn't really have a way to interact with those elements. Also, using alternate templates creates problems when a new version of the application is released with changes to the original templates.
I created BigPAPI as a way around these limitations. It's a fairly simple plugin that doesn't do anything to the Movable Type interface by itself. Once it's installed, though, other plugins can augment or alter any page of the application in any way they see fitno hacking necessary. (BigPAPI expands the Plugin API, hence the name.)
The Approach
BigPAPI takes advantage of the fact that Movable Type's codebase is neatly organized into modules, and the fact that Perl allows you to override a subroutine within a module.
The plugin essentially inserts itself at a couple of points so that it can run some callbacks before the MT::App module displays any application page. This means that in order to modify an application template before it's displayed to the user, a plugin simply needs to define the appropriate callbacks.
There are two types of callbacks, template and param callbacks. These are described fully in the BigPAPI documentation, so we won't go into much detail here. Instead, let's dive into an example.
HTML::Template
Before working with BigPAPI, you should have some familiarity with the Perl module HTML::Template. This is the module MT uses to build the templates for its interface. (Please note that these templates have nothing to do with the templates you create and edit within Movable Type, which are used to publish content.)
The templates for the application interface are in the directory tmpl/cms within your Movable Type installation. You may want to browse through some of these files to get an idea of the type of template code you'll be working with when writing a BigPAPI plugin.
A BigPAPI Plugin From Start to Finish
To illustrate the process of creating a BigPAPI-based plugin, let's take a simple piece of information that might be handy to have displayed in the application interface. The Categories listing shows the title of each category in a particular weblog. Let's suppose we want this page to display each category's description as well.
Getting Started
First we need to determine which of the templates in the cms directory Movable Type is using to build this part of the interface. You can usually figure this out based on the names of the templates, and by perusing the code of a likely template to see if it contains the expected interface elements
for the screen in question. If that doesn't work, try following these steps:
- In Movable Type, navigate to the screen you want to modify.
- Look for the
__modeparameter in the URL. In this case, it'slist_cat. - Open
lib/MT/App/CMS.pm. - In the big
$app->add_methods()statement toward the top of the module, locate the mode. In this case, we see that'list_cat'maps to thelist_categories()subroutine. - Find the subroutine in the module.
- At the end of the subroutine, look for a
build_pagestatement. In this case, we find:$app->build_page('edit_categories.tmpl', \%param);
Thus, the template we want is edit_categories.tmpl.
This approach doesn't work in all cases. But by poking around in CMS.pm and the template files themselves, you should be able to figure out which template you're dealing with.
Now that we know which template to target, let's begin writing some code. First, we need to define our plugin with some basic code that should be familiar if you've written, or examined, other types of plugins:
require
MT::Plugin;
require MT;
my $plugin = MT::Plugin->new({
name
=> "CatListDescriptions",
description => 'Display descriptions
on the Categories
listing.'
});
MT->add_plugin($plugin);
This gives us a plugin object we can pass when adding callbacks, and also registers the plugin so it'll show up in Movable Type's listing of installed plugins.
Tweaking the Template
In order to modify the category listing template, we first define a BigPAPI template callback:
MT->add_callback('bigpapi::template::edit_categories',
9, $plugin, \&_template);
This means that whenever the system is about to build the edit_categories.tmpl template, it will first call the subroutine _template() in our plugin, and pass it a reference to the text of the template. Now we need to figure out what to do to that text. The best way to do that is to open the template, tmpl/cms/edit_categories.tmpl.
We want to add each category's description after its label. After looking through the template code, it becomes clear that the actual category listing
is generated within a loop called CATEGORY_LOOP, and this is what displays the linked label:
<span
style="margin-left: <TMPL_VAR
NAME=CATEGORY_PIXEL_DEPTH>px;"><a href="<TMPL_VAR
NAME=SCRIPT_URL>?__mode=view&_type=category&blog_id=<TMPL_VAR
NAME=BLOG_ID>&id=<TMPL_VAR
NAME=CATEGORY_ID>"><TMPL_VAR
NAME=CATEGORY_LABEL></a></span>
But the more text we search for with a regular expression, the more prone our plugin will be to break with a future version of the template. Let's narrow it down to just the label itself, plus the closing link tag (we don't want the description to be linked):
<TMPL_VAR
NAME=CATEGORY_LABEL></a>
The important thing here is to confirm that this text doesn't appear anywhere else on the template; we need a string that's as short as possible, but also unique, so that the change is applied in the right place.
So here's the code we'll use in our callback subroutine:
my $old = qq{<TMPL_VAR
NAME=CATEGORY_LABEL></a>};
$old =
quotemeta($old);
It's always a good idea to use Perl's quotemeta() function on a piece of HTML you're going to use in a regular expression, in case it contains characters thathave a special meaning within a regex.
For the replacement text, we want to retain the original string, and add a new HTML::Template variable:
my $new =
<<HTML;
<TMPL_VAR
NAME=CATEGORY_LABEL></a>
<TMPL_VAR
NAME=CATLISTDESCRIPTIONS_DESCRIPTION>
HTML
$$template =~
s/$old/$new/;
That'll do it for the template callback. Our subroutine doesn't need to return anything, because $template is a reference, so changes made to $$template will apply to the original variable in memory.
Setting Parameters
Now we need to populate the variable we added to the template. For that, we define a param callback:
MT->add_callback('bigpapi::param::edit_categories',
9, $plugin, \&_param);
This means that before Movable Type builds the category listing page, it will call our _param() subroutine, passing it a reference to a hash containing all the HTML::Template parameters that the system itself has already assigned.
Looking at the template again, we see that each row of the CATEGORY_LOOP has an element called CATEGORY_ID. We can use that to load the MT::Category object for each category. Then it's just a matter of putting the description into an appropriately named element in the row:
require MT::Category;
for my $row
(@{$param->{'category_loop'}}) {
my $cat =
MT::Category->load({ 'id' => $row->{'category_id'} });
$row->{'catlistdescriptions_description'} =
$cat->description;
}
Note that we've named the parameter with our plugin's name as a prefix, so it's unlikely to conflict with any of Movable Type's parameters, or anything any other plugin might want to place on the template.
We're Done
That's it! Here's the complete plugin:
use strict;
package
MT::Plugin::CatListDescriptions;
require MT::Plugin;
require MT;
my
$plugin = MT::Plugin->new({
name => "CatListDescriptions",
description => 'Display descriptions on the Categories
listing.'
});
MT->add_plugin($plugin);
MT->add_callback('bigpapi::template::edit_categories',
9, $plugin,
\&_template);
MT->add_callback('bigpapi::param::edit_categories',
9, $plugin, \&_param);
sub _template {
my ($cb, $app,
$template) = @_;
my $old = qq{<TMPL_VAR
NAME=CATEGORY_LABEL></a>};
$old = quotemeta($old);
my $new = <<HTML;
<TMPL_VAR
NAME=CATEGORY_LABEL></a>
<TMPL_VAR
NAME=CATLISTDESCRIPTIONS_DESCRIPTION>
HTML
$$template =~
s/$old/$new/;
}
sub _param {
my ($cb, $app, $param) = @_;
require MT::Category;
for my $row
(@{$param->{'category_loop'}}) {
my $cat =
MT::Category->load({ 'id' => $row->{'category_id'} });
$row->{'catlistdescriptions_description'} =
$cat->description;
}
}
1;
This brief piece of code is all it took to dynamically add a useful, if trivial, element to Movable Type's interface. And now any user can make the same addition simply by uploading CatListDescriptions.pl
to their plugins directory (in addition to BigPAPI.pl). With Movable Type 3.2, you can easily add code so users can configure your interface changes not to appear, or to appear with different settings, on specified weblogs.
Scratching the Surface
The plugin we've just implemented is hardly earth-shattering, but hopefully it's given you an inkling of the possibilities. I've already released a few slightly more substantial BigPAPI plugins: LinkEntryToFile, which applies Movable Type's "Link this template to a file" functionality to the text of entries; MainMenuRecent, which displays the latest entries for each weblog on your Main Menu; UpdateAuthoredOn, which lets you set the Authored On timestamp of an entry by clicking a button; and WeblogsActionMenu, which lets you jump directly to somewhere other than the menu in a different weblog.
But there's much more to come, and the real potential for this type of plugin will be realized when it's used as one component of larger-scale plugin applicationsa plugin might have its own CGI script interface, but also use BigPAPI to integrate itself into the main Mpvable Type interface.
I'll be updating the documentation with more plugins as they become available. If you come up with a plugin based on BigPAPI, please let me know, and I'll add it to the list. I look forward to seeing what the Movable Type developer community can do with this tool.



1 Comments
Brilliant idea. btw. I really enjoyed reading all of your posts. It’s interesting to read ideas, and observations from someone else’s point of view… makes you think more. Greetings