Writing Your Own Plugins

Nearly all modications for ResourceSpace should be done within your own plugin. You can access portions of code through hooks and you can add styling, configuration, language strings and all manner of content through a plugin. They are also incredibly simple to build and add to ResourceSpace.

What is a Plugin?

A plugin in ResourceSpace is a collection of PHP code, language files, graphics, CSS files and other related files that are structured to conform to the ResourceSpace plugin architecture. For deployment, the files that constitute a plugin are packaged together into a self-contained file called a ResourceSpace Plugin (.RSP) file.

To put a plugin into production, a site administrator uploads, activates, and, if necessary, configures the plugin using the Team Centre > Manage Plugins page.

Once activated, the base ResourceSpace code automatically calls functions that make up the plugin's PHP code and utilizes the plugin's other files as needed to put the plugin's changes and extensions into effect.

The Structure of a Plugin

All of the files that constitute a plug-in reside in a single folder, called the plug-in's root folder, or in subfolders under it. The root folder has the same name as the plug-in (which means plug-in names need to conform to folder naming conventions). In a running system, the installed plugin root folders are subfolders of the ResourceSpace "plugins" folder (which means that installed plugins must have unique names).

A Complete plugin can have any of the following in it's root but requires only the first item which is what enables it to be activated in ResourceSpace.

  1. plugin_name.yaml - This is the description of the plugin. When creating your you must follow the guidance set out in the section below. The YAML file must also have exactly the same name as your plugin folder with the ".yaml" extension.
  2. config - contains config.php This file sets the default values for the plugin's configuration variables.
  3. css - contains the plugin's style sheets. If a file called 'style.css' is present it will automatically be included in all pages.
  4. dbstruct - contains database definition files that let a plugin define additional columns or entirely new tables.
  5. gfx - contains any graphics files needed by the plugin but is also accessbile to any other customisations so you can link to it like any other folder location.
  6. hooks - contains the PHP hook function files. These files contain the functions that implement the plugin logic.
  7. languages - contains plugin-specific language files. These extend or modify the base ResourceSpace language files.
  8. pages - contains any new PHP pages specific to the plugin, including the plugin's configuration page, setup.php, if needed.

In addition, the following files may be included in the root folder:

  • license.txt - your license details.
  • install.txt - installation instructions.
  • about[plugin_name].pdf - plugin documentation.
Plugins need not be complex. A really simple plugin might consist of just two files and one subfolder: the [plugin_name].yaml file in the root and a hook folder containing one hook file. In fact, a useful plugin need not contain program logic at all. For example, by including a style.css stylesheet in the 'css' folder and graphics in 'gfx' a 'colour theme' plugin can be created that changes the colour theme of the whole system.

The Plugin YAML file

name: [plugin_name]

author: Your Name

version: [version number]

desc: Short project description

info_url: http://url.to.your/info_pages/ (Link to web page with additional information about the plugin)

config_url: /plugins/[plugin_name]/pages/setup.php (Path to the plugin configuration page.)

default_priority: [three-digit number] (Sets plugin hook execution priority. Low numbers are high priority. Default: 999.)

Packaging Your Plugin

While it is still possible to manually install a plugin on your server (see Before the Plugin Manager, below), the preferred method is to package all of the needed files into a ResourceSpace Plugin (.rsp) file and to use the Plugin Manager.

To create a ResourceSpace Plugin file simply tar and gzip your plugin (.tar.gz) then rename it to "[plugin name].rsp".

Once you have done this you can upload, install, and activate it using the Team Centre > System Setup > Manage Plugins page.

The .rsp packaging is also the preferred way to distribute plugins to other ResourceSpace users. You may also wish to consider contributing your plugin to the community at large by placing it in the ResourceSpace Plugin Repository.

Hooks and Hook Functions

"Hooks" are named points in the base code of ResourceSpace at which "plugin hook functions" are called. In the base code of Resource space hooks have the form:

hook('hook_name'[, additional parameters]...);

where hook_name is the name of the hook. As indicated, a hook may or may not have additional parameters. The code in which a hook is embedded may or may not require the hook to return a value. When it does, the return value is often a boolean. In that case the typical form is:

if (hook('hook_name'[, additional parameters]...))
    {
    // Case when no hook function alters the base behavior
    }

If a plugin wishes to execute code at the point of a hook, it implements a plugin hook function that corresponds to that hook. When the hook is executed in the base code, ResourceSpace calls the corresponding hook function in each active plugin that implements it. When multiple active plugins use the same hook, they are called in priority order. The hook function for the lowest priority plugin is executed first, and the highest priority one last so that the highest priority plugin gets the final say about the ultimate outcome. The execution order for plugins with equal priority is indeterminate. The priority of a plugin is set in its yaml file. (See above.)

All ResourceSpace pages contain certain standard hooks, and most have additional hooks specific to that page. For example, the ResourceSpace file search.php — which implements the search page — contains quite a few hooks. Among them is one named 'resultsbottomtoolbar'. It is positioned at the end of the code that creates the links near the bottom of the search results page. This hook lets a plugin add additional links to the standard ones by implementing a plugin hook function.

A plugin's hook functions for any of the hooks in a given ResourceSpace page reside in that plugin's "hooks" subfolder in a file with the same name as the ResourceSpace PHP file that implements the page. For example, the search page is implemented by search.php. This means a plugin's hook functions for the search page are in the file hooks/search.php.

Not all hooks are in files that implement pages. Some are in files that page-implementing files bring in using the PHP include statement. For example, /include/general.php contains a number of hooks, but doesn't by itself implement a RespourceSpace page. Instead, page-implementing files like /pages/search.php bring it in with the include statement. In such cases, the hook is considered to be a part of the file that does the include and hook functions are placed accordingly. So when a plugin uses a hook defined in general.php in the context of the search page, the hook function is placed in the plugin's hooks/search.php.

It's also possible to create hook functions that trigger on every page that defines a named hook. Typically, the corresponding hooks are physically in files that are included by files that implement pages. To define a hook function that is triggered this way, use the page name 'all'. In other words, place a hook function that is to be triggered on any page that defines the corresponding hook in the file hooks/all.php.

The plugin hook functions themselves are named using the convention HookPluginnamePagenameHookname. So, for example, for the plugin Myplugin, the hook function for the resultsbottomtoolbar hook in the ResourceSpace search page is defined this way:

function HookMypluginSearchResultsbottomtoolbar()
    {
    # Hook function code goes here

    return true;
    }

Note: The capitalisation convention for the hook function name is required. The plugin name, page name and hook name always start with an upper case letter. The other characters in the function name may not be upper case letters.

This is also true for the hook functions in a hooks/all.php file — the page name is 'all'. So, you would add the function HookMypluginAllInitialise to create a function for the initialise hook that is present at the top of every page.

As noted above, whether a hook function is passed parameters — and what those parameters are — is defined by the hook definition in the base code. Similarly, some hooks require a value to be returned while others do not. To understand the required function signature and the return value (if any) you will need to understand the the base code in which the hook is defined.

Most hooks follow one of a number of common patterns and, after inspecting the code in which a few hooks appear, you'll recognize them. The two most common patterns are noted in the discussion about hooks, above. But there are others so be sure to take the time to understand the context for any hook you plan to use.

One final note: Please be sure to read the Coding Standards. Plugins should follow them. If you're new to writing code for Web applications in PHP, pay particular attention to how you handle data that comes in from the "outside." It's all too easy to introduce security vulnerabilities if you're not careful.

Finding or Making the Hooks You Need

To develop a plugin, you'll need a copy of the ResourceSpace base code. By far the best way to get a copy and keep it current—which as a developer you will certainly want to do—is to use Subversion ("svn") to grab a current copy and keep it up to date. You'll also need proper development tools and a working knowledge of the technologies used in ResourceSpace (principally Subversion, PHP, HTML, CSS, MySQL, and, to some extent, Javascript) to do plugin development.

As you analyse the base code with an eye toward implementing the changes or additions you envision for your new plugin, you'll come across many places in the code that contain hooks. These are the places in the code at which you can add or replace function in ResourceSpace. If an existing hook meets your need, feel free to use it by writing a hook function for your plugin. If, as may well be the case, your analysis shows the need to add hooks, that can be done, too, using the detailed recipe, below. Unless you've done something pretty outrageous, the process of getting your hooks into the base should be quick and easy.

As with any design work that extends an existing system, you'll find designing hook functions much easier if you spend a little time looking at how existing designs. The easiest way to do this is to look at some of the plugins that are a part of the base code—the ones that come "preinstalled" in the /plugins folder—when you got it from svn. They contain many examples of hook and hook function design and will give you a good idea of the range of things you can do with plugins.

How to Add a New Hook

You may require further hooks when developing a plugin. Here's how to do it:

  • Run "svn update" to bring your copy of the ResourceSpace code to the HEAD revision.
  • Add the hook(s) to your copy of the base code so you can proceed with plugin development.
  • Get your plugin working and debugged so that you're sure you have the right hook(s) in the right place(s).
  • Generate "diffs" text using "svn diff" to show what you have done to add your hook(s).
  • Submit the diff(s) to the Google Groups forum under the topic "New Hooks."
  • The hooks will be added to the latest code in the Subversion repository by one of the developers with write access.
  • Run "svn update" to fetch the latest code, overwriting your own changed files.

Adding a Configuration Page

If a plugin needs to be configured there are two ways to enable it: You can have people edit the contents of the the plugin's config.php file or you can provide a plugin configuration page and have an administrator do it through a UI. Editing the config.php is straightforward and requires no code, but it does require skill and access to the config.php file on the server. Using a configuration page requires access to the Team Centre, but no coding skill or access to the config.php file. It does require the plugin developer to write some code, but usually not much.

As a plugin writer you can do whatever is needed with the configuration page, but doing configuration management from scratch each time leads to a lot of unnecessary work and, potentially, to inconsistencies in how plugin configuration works. To help with this, ResourceSpace provides a set of functions for building plugin configuration pages. Take a look at the existing plugins to see how this works.

Extending the Database with 'dbstruct'

If you need them, you can define additional columns in existing tables, or entirely new tables just for use with your plugin. To do so, create a 'dbstruct' subfolder in the root folder for your plugin, and then create file(s) named:

table_[table_name].txt

If [table_name] names an existing table, it must contain only the definitions for the columns you wish to add. If it's a new table, you must define all the columns you need. The table_[table_name].txt files are simply CSV versions of the results provided from the 'describe (table)' MySQL command. See the existing files in the ResourceSpace dbstruct folder in the base code for examples of the required format.

You can also create 'index_' and 'data_' files to define indices and default data for your tables, exactly as ResourceSpace itself does using the existing dbstruct folder.

Hint: If you prefix the tables and columns specific to your plugin with the name of your plugin, it will aid manual database clean-up operations later if your plugin is removed. (The tables and columns you add using this mechanism are not automatically removed when your plugin is deactivated or deleted.)

You can create your tables via PhpMyAdmin or other tools and create the 'table_', 'index_', and 'data_' files using the pages/tools/dbstruct_create.php tool, then move the created files into your plugin's dbstruct folder.

Function Overrides

It's possible to override an existing function by simply declaring it in either "hooks/all.php" if you want it to be overridden for every page, or "hooks/[pagename].php" to override a function for a specific page only.

When overriding functions you should check that the function has not already been declared by a previous plugin first.

As an example, to override the 'do_search' function for all pages add the following to 'hooks/all.php'.

if (!function_exists("do_search"))
    {
    function do_search($search,$restypes="",$order_by="relevance",$archive=0,$fetchrows=-1)
        {
        # New function code goes here
        }
    }

To make this work, a function check (like the one above) also needs to be added to the base code. You can request a new function check be added to the base if you need one by following the procedure for requesting a new hook.

CSS Overrides (Theme Plugins)

The preferred way to customise the CSS to create your own colour theme is to create a plugin.

Placing a 'style.css' folder within a 'css' folder in the root of a plugin will allow you to add your own CSS. PHP code is not required within the plugin.

As of r1715 you can add Col-<themename>.css files to the css folder as well, so that plugins can include variable color themes.

Custom Language Changes (Customisation Plugins)

Over the long haul, the easiest way to change the text ResourceSpace displays to users—the strings in the $lang[] array—is to create a plugin. By using a plugin, your changes will carry forward from version to version. If, instead, you simply edit the content of the files in the main ResourceSpace languages folder, you'll have to merge your changes with the base each time it changes.

To make a language customisation plugin create the basic structure of a plugin—the plugin root folder and the plugin yaml file. Then create a language file for each language used in your system. Name the files using the ISO 639-1 code for the language with a suffix for the country if needed (e.g. sv.php, pt-BR.php). Place the files within the 'languages' folder in the root of the plugin. Add only your custom changes to the plugin's language files. (There is no need to redefine all the strings. In fact, it's a bad idea to do so since it would effectively override all the language strings.)

For example, in the standard page footer there is an "About Us" link. If you wished to change this to read "About Our Company" instead, you could do so by creating a plugin consisting of just a root folder, the plugin yaml file and a languages subfolder containing an en.php file consisting of nothing more than the two lines:

<?php
$lang['aboutus'] = 'About Our Company';

If your system uses multiple languages, you would add similar language files for the the additional languages.

Packaging Your Plugin

While it is still possible to manually install a plugin on your server (see Before the Plugin Manager, below), the preferred method is to package all of the needed files into a ResourceSpace Plugin (.rsp) file and to use the Plugin Manager.

To create a ResourceSpace Plugin file simply tar and gzip your plugin (.tar.gz) then rename it to "[plugin name].rsp".

Once you have done this you can upload, install, and activate it using the Admin > Manage Plugins page.

The .rsp packaging is also the preferred way to distribute plugins to other ResourceSpace users.