Skip to content

Writing Office Plugins

Andy Theuninck edited this page Apr 6, 2015 · 1 revision

Office's plugin system is object oriented. Each plugin consists of a directory containing one or more class definitions. When a plugin is enabled, CORE will automatically discover the classes within that plugin and slot them into appropriate places based on the API class they inherit from. See Common Architecture for notes on autoloading and Coding Standards for class & file naming conventions.

Creating a Plugin

User-defined plugins go in fannie/modules/plugins2.0. To get started, make a new directory there. This will contain your plugin. Inside that directory create a new PHP file to define your plugin. This definition tells Office a little information about your plugin. To do so, it needs to define a class extending FanniePlugin like this:

include_once(dirname(__FILE__).'/../../../config.php');
if (!class_exists('FannieAPI')) {
    include($FANNIE_ROOT.'classlib2.0/FannieAPI.php');
}
class CalendarPlugin extends FanniePlugin 
{
}

This first include line Office's global configuration file. It's almost always a good idea to do this first so all user-defined settings are accessible. Note that the path does not assume the software is in a particular directory like /var/www. This is important for compatibility with various systems. Using "dirname(FILE)" is preferred over using "DIR" since the latter is only available in relatively new versions of PHP.

The next includes all of Office's API classes if needed. The variable $FANNIE_ROOT is provided by the global configuration file. It points to the directory containing the software and can be used to reference paths without using lots of "../" strings. The class_exists check should always be used on this type of include.

Finally, define the plugin class. The name of the subclass should match the name of the PHP file. So, for example, in this case the file should be named CalendarPlugin.php. As a best practice, this applies to any PHP file that defines a class (see Coding Standards

FanniePlugin Class Details

At this point, the plugin definition is technically complete. Possible additions include:

A description of your plugin is generally a good idea. FanniePlugin has a public property description.

public $plugin_description = 'Plugin for calendars';

If your plugin has any user-configurable settings, you can define them in the settings array. Each setting needs a name, default value, description, and label. Settings in the plugin_settings property will be saved in the global configuration variable $FANNIE_PLUGIN_SETTINGS via the name key. An example setting like this:

public $plugin_settings = array(
    'CalendarDatabase' => array(
        'default'=>'core_calendar',
        'label'=>'Database',
    	'description'=>'Database to calendars. Can
            be one of the default CORE databases or a 
            separate one.',
    ),
);

would be saved as $FANNIE_PLUGIN_SETTINGS['CalendarDatabase'].

The FanniePlugin class includes methods named plugin_enable(), plugin_disable(), and setting_change(). These are called when the plugin is enabled, disabled, or its settings are updated (respectively).

Adding a Page to a Plugin

User-facing pages can be constructed easily using the FanniePage class (or one of its specialized subclasses). A basic page definition looks like this:

include_once(dirname(__FILE__).'/../../../config.php');
if (!class_exists('FannieAPI')) {
    include($FANNIE_ROOT.'classlib2.0/FannieAPI.php');
}
class CalendarMainPage extends FanniePage 
{
    public function bodyContent()
    {
        return '<p>Hello World!</p>';
    }
}

FannieDispatch::conditionalExec();

As in the plugin definition, the global configuration file and API class definition are included then a subclass is defined. In compliance with naming conventions this particular file should be named CalendarMainPage.php. The FannieDispatch method will print the page only if this file is called directly. This makes it safe for other developers to include() this file if they want to reuse, extend, or test our class.

FanniePage is designed to let developers easily maintain consistent look and feel without repeating lots of code as well as tie into lots of different sections of the page. A subclass may override any of the following:

  • Properties
    • title [string] is displayed in the window's title bar.
    • header [string] at the top of a page's main, righthand section.
    • themed [boolean] use the newer Bootstrap based theme. Setting this to true is highly recommended.
    • description [string] is a short explanation of what the page does.
  • Methods
    • preprocess() [returns bool] is the very first method called. If it returns True, the rest of the page will be drawn. If it returns False, nothing else runs. This function is typically used to process form input.
    • bodyContent() [returns string] draws the main content of the page.
    • javascriptContent() [returns string] defines any custom javascript. <script> tags should not be included; they're added automatically.
    • css_content() [returns string] defines any custom CSS. <style> tags should not be included; they're added automatically.

FanniePage also includes some helper methods. They are not meant to be overriden but can be useful in the above methods or any additional custom methods.

  • addScript($file_url [string], $type [string]) include a script file. The $type argument is optional. If omitted, the type is assumed to be "text/javascript".
    • jQuery is almost always included automatically. Do not add it again as that can cause problems. jQuery-UI is also almost always available as is Bootstrap is the page is themed.
  • addCssFile($file_url [string]) include a stylesheet file.
    • Similarly, CSS files for jQuery-UI and Bootstrap are already included in most cases
  • addOnloadCommand($str [string]) runs the javascript command $str when the page loads. The method can be called multiple times. The commands will all run in the order that they're added.

URL best practices: like filesystem paths, no assumptions should be made about where the software is installed. To get a URL to somewhere within your plugin, create an instance of your plugin definition class then call its plugin_url() method. This returns the URL of the directory containing your plugin. To reference a URL somewhere outside the plugin, use the global configuration variable $FANNIE_URL. It points to the root directory of Office.

Database Access & Use

All database interactions are conducted via the SQLManager class. A full list of its methods is beyond the scope of this document, but its primary purpose is to allow for different DBMSes (e.g., MySQL, Postgres, etc) without altering PHP code. In general, its methods correspond to PHP's MySQL functions with the "mysql_" removed (e.g., query(), fetch_row(), num_rows(), error(), etc). Learning all the classes' methods is encouraged but probably overkill when just getting started. See Common Architecture

Request a database connection using the FannieDB class. It contains a static method, get($db_name [string]) where $db_name is the name of a database. The global configuration variables $FANNIE_OP_DB, $FANNIE_TRANS_DB, and $FANNIE_ARCHIVE_DB contain the names of Office's three default databases. These are user-customizable so don't hardcode common defaults like "core_op" or "is4c_trans".

If your plugin needs additional tables and/or views, include a database name option in the plugins' settings so the user has the option of keeping the plugins' tables/views in a separate database. This reduces the chances of running into name problems where two plugins want to use the same name for a table/view and makes it easier for the user to manage data related to the plugin. Deleting tables or views when the plugin is disabled is generally a bad idea unless that data can be easily recreated. Losing data due to a mistaken configuration change is bad. Clearly keeping the plugins' tables/views separate makes it more reasonable for the user to decide what is safe to manually delete.

Clone this wiki locally