Plugin Architecture

In this post I’m going to talk about how the RedFeather plugin system is implemented. ¬†I’m also give a rough overview on how the rest of RedFeather works and provide a rationale for the decisions I made regarding the design.

Given that the primary motivation behind RedFeather is the desire for simplicity I hat to make sure that the plugin system was just as simple. ¬†The first thing to consider was installation. ¬†This is a particular problem with EPrints, where a plugin is usually composed of many files which need to be individually copied in the correct directories in the source in order to work. ¬†While, the Bazaar package manager goes a long way towards making it easier for the user, it increases the complexity of both the plugin itself and the EPrints platform. ¬†With RedFeather each plugin is composed of a single php file which is can be installed by simply copying it into the webspace alongside the main repository. ¬†Depending on the complexity of the plugin, a small amount of additional configuration may be required but ultimately that’s all it takes. ¬†This simplicity also translates to the loading process; plugins are activated by appending their content to the main RedFeather file using a PHP include – this means that there is no need to parse or process the plugins in any way. ¬†It also maintains consistency within the system since there is absolutely no difference between a plugin and the RedFeather core itself. ¬†In effect, the main RedFeather source is really just a collection of the most useful plugins.

So, how does RedFeather work?  Function pointers.

Literally everything in RedFeather is achieved via the clever use of functions: page loading, toolbar rendering, and workflow management. ¬†Furthermore, ever single function can be overridden in a plugin, allowing you to change the behaviour of almost any part of RedFeather. ¬†If you don’t like the layout of the browse page you can simply create a plugin that replaces that part of the system. ¬†The challenge then becomes one of code factorisation – how do I best break down the code into functions in order to facilitate code reuse? ¬†In the example where we’re editing the browse page there existing code is already broken down into various chunks; there is code to render individual fields, individual resources, the complete list of resources and the entire browse page itself. ¬†If you wanted to adjust the browse page to include a description at the top, you would only have to edit the “page_browse” function (which only consists of a few lines). ¬†Likewise, if you wanted to add an icon to each resource you might edit “generate_resource”.

This leads us onto a problem: too many functions reduces the readability of the code – made worse due to the fact that, as of the time of writing, the core RedFeather source code alone contains over 70 function. ¬†There is where good code structure and consistant naming conventions become critical. ¬†For example, functions prefixed with “render_” always write to the output buffer whereas functions beginning with “generate_” return html. ¬†However, functions names also serve a far more useful purpose in controlling program flow. ¬†The most obvious example is the prefix “page_”, which is used to denote the entry point for any URL access (for example, the url “index.html?page=resource_manager” will automatically call the function ‘page_resource_manager”). ¬†This makes it very easy to write plugins to define new pages, since all one must do is define a single function. ¬†I’ll talk more about function pointers in a future post.

In addition to the cunning function pointers there are also a number of global variables which are used to pass information around the program. ¬†The $BODY and $TITLE variables are used as output buffers for the html body and title when rendering a page using a inbuilt template system (which I will talk about in a future post). ¬†The $DATA variable is pre-loaded on every page with the metadata of all the resources in the repository and can be written back to persistance using the “save_data” function. ¬†The $CONF variable is the most important of all and contains all the configuration information – from the repository name itself, to the width of the resource preview widget. ¬†While it is generally considered “bad practise” to use global variables I felt they actually improved code readability in this case because they provide a consistent way to access frequently used values. ¬†Also, passing them around would just add unnecessary overhead to every single function call.

I think that’s enough background on the RedFeather implementation for now – I’ll go into more detail in future posts.

Leave a Reply

Your email address will not be published. Required fields are marked *