The last plugin I’m going to talk about during this initial period of RedFeather development is the keywords plugin. While the ability to add keywords to a resource is desirable enough to be a candidate for inclusion in the RedFeather core itself, I’ve decided to keep it separate for the time being since it serves as such an excellent demonstration of the plugin architecture. It acts as an example implementation of many of the different features a plugin might provide, in this case:
- Modifying the metadata schema.
- Adding field to input workflow.
- Adding field to output views.
- Adding a new page.
- Adding a new toolbar item.
So, let’s start by addressing the metadata schema. This is stored in the Advanced Configuration section of the RedFeather source and is defined in the $CONF as a simple string array called ‘fields’. However, this list by itself has no function and is merely a contract that defines what fields RedFeather will look for when it tries to perform actions involving metadata. In other words, it is the responsibility of a RedFeather plugin to provide support for each field specified – fields themselves provide literally zero functionality on their own. This is implemented using function pointers (like I mentioned in an earlier blog post, EVERYTHING is function pointers in RedFeather). All a plugin has to do is loop through the fields and call a different handler function for each field in the schema. In order to maintain code readability, each of these functions should be given a descriptive name following the RedFeather conventions.
The RDF plugin for example, provides individual functions that understand how to convert the field data into triples. This means that the function “generate_rdf_field_title” should return an RDF:description involving dc:title whereas the handler for processing the uploaded file needs to return both dct:hasPart and rdf:type. This perfectly illustrates exactly why the metadata definition is entirely abstract – there is literally no way to automatically determine how to process any given field, and the additional code required to implement a system flexible enough to support this level of description outweigh any possible benefit it may give.
In the case of the keywords plugin, this means implementing additional functions for the existing core modules: resource manager, browse/view, rdf and rss. The resource manager module is responsible for generating the edit workflow so this needs to be supported first. The workflow generator automatically looks for functions in the form ‘generate_input_field_xxx’, which it concatenates to the bottom of the form. There are no explicit restrictions on what these functions return as long as they include the required <input> elements for that field. In simple cases, such as for the title, this leads to an incredibly simple, single-line definition. However, the keywords field is actually a multifield (in that it can have more than one value) so is slightly more complicated. Fortunately, RedFeather also provides a simple way to automatically support multifields, which is also utilised in the creators field.
The browse/view module is responsible for actually displaying this data, since it provides the implementation for the metadata generator. This is implemented in a very similar way to the resource manager but instead looks for functions of the form ‘generate_output_field_xxx’. This consistency when dealing with plugins and fields exists throughout RedFeather and is vital in maintaining code readability in a swamp of functions. Output fields are much simpler than input fields and just have to return the html in the form of two-column table rows, which are combined to form the metadata table. The RSS and RDF field implementations are also impressively compact so I will move on.
That is all that is needed to implement the ‘data’ aspect of the keywords plugin. Appropriate input boxes now appear on the workflow and the fields manifest in the browse view, resource view, and alternative data views (RDF, RSS and JSON). However, no keyword system is complete without a pretty tagcloud to go with it. Implementing one requires us to create a whole new page (for the tagcloud itself) and a way to get to it from the main repository (in this case, a simple menu item).
As I mentioned in my earlier post on the RedFeather architecture, pages are implemented within RedFeather using function pointers. In this case, all we need to do is write a new function in the form ‘page_xxx’ (which is then automatically accessible at index.html?page=xxx). Page functions are used by RedFeather to output data or html to the web; this can be done either manually, or using the RedFeather templating system (which I talked about in great detail in a previous post). The tagcloud is intended to be normal page within the site, so it should use the template. The system then accesses the global resource data and builds the tagcloud by writing html to the $BODY global variable; this is then written to user by calling “render_template”.
Adding a menu item to allow users to access the tagcloud from the browse page is a case of simply writing a new function in the correct format. This is then processed by the toolbar renderer and added to the menu.
This consistent, concise architecture allows even intermediately skilled programmers to very quickly create exciting new plugins and features for RedFeather.