Login | Register
My pages Projects Community openCollabNet

The Newsbruiser Plugin System

This document describes the NewsBruiser plugin system, which makes it easy to add features to NewsBruiser.

Plugin Overview

When NewsBruiser starts up, it looks at all the directories under nb/plugins/. If a directory has an __init.py__ file, NewsBruiser runs the code in that file. So the first step in creating a plugin is making a subdirectory of nb/plugins/.

The plugins that come with NewsBruiser are sorted by what they do. For instance, the plugins underneath entry/export deal with notifying things external to NewsBruiser (such as files on the filesystem, or other web sites) about changes to NewsBruiser entries. Each plugin subdirectory has a description of the plugins inside its README file; you can use the existing subdirectories to guide your path, or make your own subdirectories.

What your plugin can do

Create new screens

You can define a new screen for your plugin by simply declaring a subclass of NBCGI and registering it with the NewsBruiser dispatch CGI.

To register your screen, put code like the following in your plugin's __init__.py file (assuming your screens are all kept in MyScreens.py):

from nb.NewsBruiserCGIs import DispatchCGI
import MyScreens

You can put all your NBCGI subclasses in one module (eg. MyScreens.py); DispatchCGI will scan that module for NBCGI subclasses and register them all.

Make sure that your NBCGI subclass defines the NAME field, so that the dispatcher will know when a user is trying to access your screen. Every screen must have one and only one NAME, and no two screens can share a NAME.

Example: the api/XML-RPC plugin defines a new screen that provides a CGI interface to XML-RPC.

Respond to NewsBruiser events

Your plugin can be notified when certain important events happen in NewsBruiser, like an entry being published.

To have a method called when an event of type "Foo" happens, you just define a function in __init__.py called eventFoo.

Right now there are four NewsBruiser events you can listen for:

  • eventEntryPublished(entry): A user has published a notebook entry. It might be a new entry, or it might have previously been a draft.
  • eventEntrySaved(entry): A user has modified an entry or a draft. This will be called instead of eventEntryPublished() the first time a user submits a draft.
  • eventEntryDeleted(entry): A user has deleted an entry.
  • eventFullStaticRewrite(notebook): A user has signalled that all external representations of NewsBruiser artifacts should be rewritten. If you keep representations of NewsBruiser artifacts and listening for Entry{Published,Deleted,Saved} isn't sufficient to keep things in sync, you should respond to this event by recreating everything from scratch.

Examples: The entry/export/ plugin listens for all four events and propagates them to any registered EntryExporter objects.

Define entry filters

Entry filters can be used to automatically change the text of an entry. This is generally used to save some work on the part of the person who's typing in notebook entries by, eg. automatically linking URLs they type.

If you want to define an entry text filter, first define the transformation function. This function takes a NBCGI instance and a text string. It performs some transformation on the text and returns the transformed version.

Now you must register your filter. import the Entry class and call Entry.registerFilter, passing in the type of filter (currently only 'text' is supported) and a reference to the transformation function.

If you define an entry filter you will also want to define a configuration option to turn the filter on and off (see below). Your function should check the value of the option for the given notebook and, if it is not set, return an unaltered version of the text.

Keep in mind that entry filters are cumulative; a user may enable more than one, and the results of one will be fed into the next. There is no guarantee of the order in which the filters will be run.

Examples: This is basically all the entry/filter plugins do.

Add configuration options

You can allow the user to make changes to the way your plugin operates through NewsBruiser's integrated configuration interface. You do this by creating an options.conf file in the plugin directory. You can access the value of an option for a particular notebook by calling getOptionValue on the notebook, and passing in the name of the option.

NewsBruiser's configuration interface is provided by a library called "I Want Options", in nb/lib/IWantOptions.py. It has way too many features to mention here, but it NewsBruiser's main options.conf file exercises almost all of them, the IWO library contains internal documentation, and you can get an overview of IWO from a paper I wrote on configuration frameworks, called Beyond The Config File.

Examples: Each entry/filter plugin has a configuration option to turn it on and off.

Define templates and template directives

See the entry/annotate/Comments plugin for an example.

Define syndication hooks

RSS 1.0 and 2.0 will call any registered hook methods when printing out their feed head (1.0) and their channel descriptor (1.0 and 2.0). See the notebook/annotate/License plugin for an example.

Probably other stuff

There is probably other stuff that I didn't add here when I added it to NewsBruiser. Please see the existing plugins for lots of ideas.

What your plugin can't do (yet)

Right now there is no good way to link to plugin CGIs from NewsBruiser CGIs. The basic NewsBruiser CGIs are full of links to the CGIs provided by the included plugins. I can and plan to add hooks in specific places for plugins to provide HTML snippets, which should work for most purposes.

There are a few places in which a plugin can add form fields and form processing logic to the basic CGIs. Only those neccessary to support the outgoing trackback plugin have been implemented.

Templates defined in the core (like the entry template) sometimes contain template directives defined by plugins (like the comment plugin). Ideally the core template would call out to certain templating system hooks which would have been registered by the components. I know this problem is solvable, but have not yet come up with a satisfactory solution.

Back to the main documentation