HowToAddRESTAndMacroHandlersToContribs

The Foswiki plugins architecture allows you to hook into the Foswiki flows at a very deep level. To support this, plugins are automatically loaded at startup and "stitched in" to the flow at the points dictated by the handlers they implement. Because of the extra startup processing they require, plugins are distinguished from other forms of extension by existing in their own "Plugins" namespace.

However sometimes you have an existing contrib that does not need to hook into the flow, but you do just want to add a REST or macro handler to it. But that's a problem, because unlike plugins, Contrib modules are not automatically loaded when Foswiki starts up.

Consider how Contribs work. Let's say we have a really simple extension that defines a function, 'fnord', that is designed to be called from other extensions. The Contrib module will look something like this:

package Foswiki::Contrib::FnordContrib;
use strict;
our $RELEASE = '2.9.4';
use version 0.77; our $VERSION = version->declare('v2.9.4');

sub fnord {
   ...
1;

To use this contrib, callers will have something like this in the code:

   require Foswiki::Contrib::FnordContrib;
   Foswiki::Contrib::FnordContrib::fnord();

So far so good; the caller takes responsibility for ensuring the contrib code is loaded if they want to call the function. However a problem arises when the Contrib isn't explicitly loaded by any other auto-loaded code, but we want to register a REST handler that will allow fnord to be called via an HTTP request, or a macro handler that supports the %FNORD macro. We could always implement a companion plugin to perform the relevant calls to Foswiki::Func::registerRESTHandler and Foswiki::Func::registerTagHandler.

But there's a simpler approach; just explain quietly to Foswiki that the Contrib is also a plugin. That way, Foswiki will call the initPlugin method in the Contrib, which can be used to declare the desired handlers.

First, we implement a standard initPlugin method in the same package, and use Foswiki::Func to add the handlers:

package Foswiki::Contrib::FnordContrib;
use strict;
our $RELEASE = '2.9.5';
use version 0.77; our $VERSION = version->declare('v2.9.5');

sub initPlugin {
   Foswiki::Func::registerRestHandler('fnord', \&_restFnord);
   Foswiki::Func::registerTagHandler('FNORD', \&_FNORD);
   # The details of how to write REST and macro tag handlers are out of scope here
   return 1;
}

Now we edit the Config.spec for the contrib and add:

# **STRING EXPERT**
# Plugin module path for handler registration
$Foswiki::cfg{Plugins}{FnordContrib}{Module} = 'Foswiki::Contrib::FnordContrib';
# **BOOLEAN EXPERT**
# Enable plugin module for handler registration
$Foswiki::cfg{Plugins}{FnordContrib}{Enabled} = 1;

Next time we run configure with this contrib installed, and save something, these settings will be added to LocalSite.cfg. Then, when Foswiki runs, it enumerates the $Foswiki::cfg{Plugins} hash and tries to call initPlugin on each module it finds - which of course includes our contrib.

Of course it's not a true plugin, and won't get treated as such by support modules such as configure and the installers, but for the purposes of adding REST and macro handlers this works just fine. This approach should work with any Foswiki release.

One final note; be warned that the contrib package is treated internally as a plugin, and flow handler functions will be picked up from the it. But don't assume this is a cheap way to convert your contrib into a plugin! If everyone starts declaring Contribs that are actually plugins, the support tools that rely on being able to distinguish them will fail. If you want to do any more than simple registration of REST and macro handlers, then convert your contrib into the Plugins namespace, or create a companion plugin.

-- CrawfordCurrie
Topic revision: r3 - 15 Nov 2012, GeorgeClark
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy