Plugin API December 29, 2003
Notes:

- This document should contain enough information to write basic plugins. If you have more questions, see the Plugin Development Forum
- Methods and events introduced after BLOG:CMS v1.5 was released have the BLOG:CMS version in which they were introduced listed. If you use any of those functions, don't forget to adapt `getMinNucleusVersion` appropriately.

# Introduction

Back to the developer docs index

This document contains information on how you can write your own BLOG:CMS plugins

# Table Of Contents

- Introduction
- Writing your first plugin
- Overview of the `NucleusPlugin` class
- The `<%plugin(...)%>` skinvar
- The `<%plugin(...)%>` templatevar
- Actions using `action.php`
- Events, and subscribing to them
- Saving options
- Database tables
- Providing an admin area

# Introduction

BLOG:CMS plugins allow just about anyone to extend the functionality that BLOG:CMS offers, without having to alter the PHP code itself. Plugins are simple php scripts that must implement certain methods, and can easily be exchanged between BLOG:CMS users. Installing goes as easy as adding the plugin file to the plugin directory and letting BLOG:CMS know it's there.

Some advantages of plugins are listed below:

- Extra functionality can easily be added to the BLOG:CMS framework, without having to know much details about the implementation.
- You can install only the plugins you need, saving on the time needed to generate a page

All plugin files should be placed in the directory that is listed in `config.php`. Commonly, this will be `/your/path/nucleus/plugins/`. Plugin files can be recognized by their form: `NP_name.php`. Some plugins require a subdirectory with the same name to store extra files or their admin area.

Note: the names are case-sensitive, so they should start with `NP_`, not `Np_` or `np_`. Also note that when the plugin uses a subdirectory, the name of that directory should be all lowercase.

# Writing your first plugin

Ok, lets start by writing a simple plugin. Basically, each plugin is a PHP class that inherits from the predefined class `NucleusPlugin`. Below is an example of a `HelloWorld`-plugin:

    <?

    class NP_HelloWorld extends NucleusPlugin {

    	// name of plugin
    	function getName() {
    		return 'Hello World';
    	}

    	// author of plugin
    	function getAuthor()  {
    		return 'Wouter Demuynck';
    	}

    	// an URL to the plugin website
    	// can also be of the form mailto:foo@bar.com
    	function getURL()
    	{
    		return 'http://nucleuscms.org/';
    	}

    	// version of the plugin
    	function getVersion() {
    		return '1.0';
    	}

    	// a description to be shown on the installed plugins listing
    	function getDescription() {
    		return 'Just a sample plugin.';
    	}

    	function doSkinVar($skinType) {
    		echo 'Hello World!';
    	}

    }
    ?>

1. Copy this code in a file called `NP_HelloWorld.php`, and put it in your plugins directory. Make sure that there are no spaces after the last `?>` or before the first `<?`.. By the way, NP stands for "Nucleus Plugin" :-)
2. Open the BLOG:CMS Administration area and go into Manage Plugins
3. You'll find out that there's a HelloWorld plugin you can install. Do this. If everything worked out correctly, you'll see that your plugin is now listed in the list of installed plugins.
4. Now edit some skin, and insert the following statement at a place of which you know where it will show up on the actual page.

       <%plugin(HelloWorld)%>

   Some notes: the name (HelloWorld) is case sensitive!
5. Now visit a page that uses the skin you edited: notice the "Hello World" on the location where you added the `plugin`-skinvar

So, that wasn't so hard after all. Read on to find out more.

# The class NucleusPlugin

All BLOG:CMS plugins must inherit from the PHP class `NucleusPlugin`. If this sounds complicated, don't worry, it isn't. It even makes your life easier, allowing you to only implement the methods that your plugin needs, and giving access to some auxiliary functions.

Below is an overview of the methods that the `NucleusPlugin` offers, and that you can re-implement in your own plugin. If you want to see the source of the class itsself, it's located at `nucleus/libs/PLUGIN.php`

Overview of the class `NucleusPlugin` (redefinable methods)
Method Signature | Explanation
--- | ---
`getName()` | Returns the name of the plugin. This will be the name that will show up on the list of installed plugins. You should definately redefine this method, since the default implementation returns Undefined
`getAuthor()` | Returns the name of author of the plugin. This name will show up on the list of installed plugins. You should definately redefine this method, since the default implementation returns Undefined
`getURL()` | Returns the URL of the site where the plugin can be downloaded, or where additional information about the plugin can be found. If no such site exists, a `mailto:`-link with the authors email address is appropriate. You should definately redefine this method, since the default implementation returns Undefined
`getDescription()` | Returns a longer description of the plugin. This will show up on the list of installed plugins. The default implementation returns Undefined
`getVersion()` | Returns the current version of the plugin. Returns 0.0 by default
`getMinNucleusVersion()` | (v2.0b) Returns the minimum required BLOG:CMS version. By default, this returns `155` (v1.55). If you are using plugin features that were introduced later, please implement this function (e.g. v2.0 => 200). Please note that BLOG:CMS v1.55 does not use this function at all, so it remains possible to install newer plugins there, even if they use newer features.
`init()` | Initializes the plugin. This method gets called immediately after the plugin object is created and the `plugid`-attribute has been set. By default, this method does nothing.
`doSkinVar($skinType)` | When plugins are called using the `<%plugin(...)%>`-skinvar, this method will be called. the `$skinType` parameter contains the type of skin (`item`, `archive`, ...) from where the plugin is called. Don't get confused by the fact that there's only one parameter. Multiple parameters can still be passed. More info on implementing the `doSkinVar` method. By default, this method does no output at all.
`doTemplateVar(&$item)` | Basically the same as `doSkinVar`, but this time for calls of the <%plugin(...)%>-var in templates (item header/body/footer and dateheader/footer). By default, this method forwards the handling to the `doSkinVar`-method, using `template` as skintype. More information on implementing the `doTemplateVar` method
`doTemplateCommentsVar(&$item, &$comment)` | (v2.0b) Basically the same as `doSkinVar`, but this time for calls of the <%plugin(...)%>-var in templates (comments-related parts). By default, this method forwards the handling to the `doSkinVar`-method, using `template` as skintype. More information on implementing the `doTemplateCommentsVar` method
`doAction($type)` | When a plugin wants to allow user interaction, it can allow actions through `action.php`. This is the script that BLOG:CMS uses itself to handle new comments and karma votes. Called with the correct parameters, the `doAction`-method from a plugin can be called. The `$type` contains an optional message type. Extra variables from the request can be accessed from within the `doAction` method. By default, this method returns a string No Such Action which will trigger an error message. More info on `doAction`.
`install()` | This method gets called on the moment the plugin is installed. It can perform initializing actions, such as the creation of database tables, the creation of plugin options, etc... By default, this method does nothing.
`unInstall()` | Called when the plugin is uninstalled. It's a good thing to clean up information your plugin created in the database at this point. By default, this method does nothing.
`getEventList()` | Plugins can subscribe to events. Events get generated whenever BLOG:CMS performs a certain action. An `AddItem` event for example, will call all plugins that subscribed to this event. The called method will be `event_AddItem($params)`. The `$params`-parameter is an associative array containing several fields of information, like the itemid for `AddItem`. Returns an empty array by default, indicating that the plugin does not subscribe to any event at all. More information about events.
`getTableList()` | This method should return an array of database tables that the plugin has created. It's used in the backup feature that BLOG:CMS offers, so plugin tables are also included in the backup. By default, returns an empty array.
`hasAdminArea()` | Should return `1` if the plugin has an admin area of its own, and `0` if it doesn't. By default, `0` is returned.

Next to the methods that can be implemented, the class `NucleusPlugin` offers some extra methods which you should not implement yourself. They can be called from within your plugin using the `$this->functionName()` syntax.

Overview of the class `NucleusPlugin` (non-redefinable methods)
Method Signature | Explanation
--- | ---
`createOption(...)` `createBlogOption(...)`(v2.2) `createCategoryOption(...)`(v2.2) `createMemberOption(...)`(v2.2) | Creates a new option
`deleteOption(...)` `deleteBlogOption(...)`(v2.2) `deleteCategoryOption(...)`(v2.2) `deleteMemberOption(...)`(v2.2) | Deletes an option
`setOption(...)` `setBlogOption(...)`(v2.2) `setCategoryOption(...)`(v2.2) `setMemberOption(...)`(v2.2) | Sets the value of an option
`getOption(...)` `getBlogOption(...)`(v2.2) `getCategoryOption(...)`(v2.2) `getMemberOption(...)`(v2.2) | Retrieves the value of an option
`getAllBlogOptions(...)`(v2.2) `getAllCategoryOptions(...)`(v2.2) `getAllMemberOptions(...)`(v2.2) | For a given option, returns an associative of all values (one value per context)
`getID()` | Returns the ID for this plugin (this is the ID internally used inside BLOG:CMS)
`getAdminURL()` | Returns the URL of where the admin area of the plugin is located (if there is no such admin area, this information is invalid)
`getDirectory()` | Returns the path in the servers filesystem where the extra files for the plugin are stored (if there are no such files, this information makes no sense). The result is something like "`.../nucleus/plugins/plugname/`"
`getShortName()` | Returns the part of the plugin classname without the "`NP_`"-part, and in all-lowercase. This information is used in the functions `getAdminURL` and `getDirectory`

# Skinvars

## Description

You can create your own skinvars, and call them using `<%plugin(PlugName,parameters)%>` or `<%PlugName(parameters)%>` (when this does not conflict with an existing skinvar). Parameters are comma-separated.

To handle skinvars, you'll need to implement the `doSkinVar` method. Some samples of signatures are given below:

    function doSkinVar($skinType)
    function doSkinVar($skinType, $param1, $param2)
    function doSkinVar($skinType, $skinVar, $param1, $param2)
    function doSkinVar($skinType, $skinVar, $param1 = 'default value')

- The `$skinType` parameter will be one of 'index', 'item', 'archive', 'archivelist', 'member', 'error', 'search', 'imagepopup' or 'template'
- The `$skinVar` is actually the first parameter that's being interpreted as a type of skinvar (e.g. `<%plugin(PlugName,VarType)%>`)
- You can use `doSkinVar()` (no parameters) and retrieve the parameters using the PHP function `func_get_args()`. Could be handy if you have different types of skinvars with different numbers of arguments

## Notes

- (v2.0b) You can find the name of the skin that's currently being parsed in the global variable `$currentSkinName`

# Template variables

## Description

Template plugin variables work in the same way as skin plugin vars. There are two differences:

1. They are called from within templates instead of from within skins
2. They don't take a `$skinType` parameter. Instead, they take extra parameters with info on the item and comment that is currently being parsed:
   - The `doTemplateVar`-method gets a `&$item` parameter.
   - The `doTemplateCommentsVar`-method gets an `&$item` parameter as well as a `&$comment` parameter.
   Note the ampersands!

Template variables are called in exactly the same way as skinvars (using `<%plugin(PlugName,parameters)%>` or `<%PlugName(parameters)%>`)

By default, all template variables are passed on to the `doSkinVar`-method, using '`template`' as `skinType`-parameter.

If you want to provide your own implementation, you'll need to redefine the method `doTemplateVar` and/or `doTemplateCommentsVar`. It works in the same way as `doSkinVar`, except that now the `skinType`-parameter is missing.

    function doTemplateVar(&$item)
    function doTemplateVar(&$item, $param1, $param2)
    function doTemplateVar(&$item, $type, $param1, $param2)
    function doTemplateVar(&$item, $type, $param1 = 'default value')
    function doTemplateCommentsVar(&$item, &$comment)
    function doTemplateCommentsVar(&$item, &$comment, $param1, $param2)
    function doTemplateCommentsVar(&$item, &$comment, $type, $param1, $param2)
    function doTemplateCommentsVar(&$item, &$comment, $type, $param1 = 'default value')

## Notes

- (v2.0b) You can find the name of the template that's currently being used inside the global variable `$currentTemplateName`

# Actions

Plugins can perform actions through `action.php`, the same script that's being used to receive comments and karma votes. You can call it using both GET and POST methods. Required parameters are `action` (should be 'plugin'), `name` (name of the plugin) and `type` (type of requested action)

To enable these actions, you should implement the `doAction($actionType)` method in your plugin. Extra parameters from the request can be received using `requestVar('name')` (`requestVar` takes care of magic_quotes_gpc that PHP might have added)

When your `doAction` method returns a string, it will be interpreted as an error, and an error message will be shown.

# Events

BLOG:CMS Plugins can subscribe to events that occur whenever something important happens. The plugin can then execute some actions, or output some text.

## Example

Below is an example of how a plugin subscribes to the `PreAddComment`-event, an event that is generated immediately before a comment is added to a blog.

    class NP_Acronyms extends NucleusPlugin {
      ...
      function getEventList() { return array('PreAddComment'); }
      ...
      function event_PreAddComment(&$data) {
        // replace acronym HTML
        $data['comment']['body'] =
            strreplace('HTML',
                       '<acronym title="HyperText Markup Language">HTML</acronym>',
                       $data['comment']['body']);
      }
    }

This plugin replaces the text `HTML` in each comment by the text `<acronym title="HyperText Markup Language">HTML</acronym>`. The `acronym`-tag is a HTML-tag that allows authors to provide extra information on acronyms.

## Subscribing to events

Here's the steps you need to take to subscribe to an event:

1. Add the event name to the array returned by the `getEventList`-method
2. Create a method with signature `event_EventName($data)`, in which the handling of the event is done

Multiple plugins can subscribe to the same event. The order in which these plugins are notified is the same order as the ordening in the plugin list of the admin area. Plugins higher in the list get notified earlier on.

## Parameters

The `event_EventName`-method gets only one parameter, `$data`, of which the contents differs depending on the event. It is an associative array with data. Objects and arrays that are passed in this array, are passed by reference, so the changes you make there will be remembered.

The event list below uses some colors to indicate if changes in the parameters will be seen by BLOG:CMS or not:

- pass-by-reference: when changes are made to this kind of parameters, they will be seen by BLOG:CMS.
- pass-by-value: a copy of the value is made before it is sent to the plugin event handler. Changes in the contents of these variables will be discarded automatically.

Objects that are passed as parameters are indicates as follows: object. Most objects are also passed by reference, making them look like object by ref

## Event List

Events on which a plugin can subscribe
Name | When | Parameters
--- | --- | ---
PreSkinParse | Immediately before the parsing of a skin begins | skin The `SKIN`-object that is handling the parse type Type of skinpart (one of 'index', 'item', 'archive', 'archivelist', 'member', 'error', 'search', 'imagepopup', 'fileparser')
PostSkinParse | Immediately after parsing a skin | skin The `SKIN`-object that is handling the parse type Type of skinpart (one of 'index', 'item', 'archive', 'archivelist', 'member', 'error', 'search', 'imagepopup', 'fileparser')
PreItem | Before an item is parsed, but after the item header has been placed | blog `BLOG` object item object containing item data
PostItem | After an item has been parsed, but before the item footer has been parsed | blog `BLOG` object item object containing item data
PreComment | Before a comment is shown | comment associative array containing comment data
PostComment | After a comment has been shown | comment associative array containing comment data
PreDateHead | Before a date header is shown | blog `BLOG` object timestamp Timestamp for date header
PostDateHead | After a date header has been parsed | blog `BLOG` object timestamp Timestamp for date header
PreDateFoot | Before a date footer is parsed | blog `BLOG` object timestamp Timestamp for day that is closed
PostDateFoot | After a date footer has been parsed | blog `BLOG` object timestamp Timestamp for day that is closed
LoginSuccess | After a successful login | member `MEMBER` object
PostChangePassword | After a successful password change | member `MEMBER` object
LoginFailed | After a failed login | username login name that was used in the login attempt
Logout | After logout | username name of the user that logged out
PreBlogContent | Before blog content has been inserted through a skinvar | blog `BLOG` object type Type of skinvar that's being called ('blog', 'otherblog', 'archive', 'archivelist', 'item', 'searchresults', 'othersearchresults', 'categorylist', 'otherarchive', 'otherarchivelist')
PostBlogContent | After blog content has been inserted through a skinvar | blog `BLOG` object type Type of skinvar that's being called ('blog', 'otherblog', 'archive', 'archivelist', 'item', 'searchresults', 'othersearchresults', 'categorylist', 'otherarchive', 'otherarchivelist')
PreAddComment | Before adding a comment to the database | comment comment data (associative array)
PostAddComment | After adding a comment to the database | comment comment data (associative array) commentid comment ID
PostRegister | After a new user has registered | member New `MEMBER` object
PostAddItem | After an item has been added to the database | itemid new itemid in database
PreAddItem | Immediately before an item is added to the database | title title body body text more extended text blog `BLOG` object authorid ID of author timestamp UNIX timestamp closed 1 (no comments allowed) or 0 (comments allowed) draft 1 (draft) or 0 (not draft) catid ID fo category
PreUpdateItem | Immediately before an item gets updates in the database | itemid item ID title title body body text more extended text blog `BLOG` closed 1 (no comments allowed) or 0 (comments allowed) catid ID fo category
PrepareItemForEdit | Called after getting an item from the database, and before presenting it to the user to be edited. | item associative array containing item data
PreUpdateComment | Immediately before a comment is updated and saved into the database | body Comment body
PrepareCommentForEdit | After a comment is retrieved from the database, and before it is presented to the user to be edited. | comment comment data (associative array)
PrepareCommentForPreview | Subscribe to this event to print Comment Preview data. | body comment preview data (associative array) cnumber comment preview id number
PrePluginOptionsEdit | (v2.0b) before the 'edit plugin options' form is created. (v2.2) extra parameters | context (v2.2) `global`, `blog`, `member` or `category` options Array with for each option an associative array, having the following indices: `name`, `value`, `oid`, `description`, `type`, `typeinfo`, `contextid`. Extra options can be added here (if you want to do something with them, you'll need to subscribe to PostPluginOptionsUpdate as well) plugid plugin ID (compare with `GetID()` to find out if this concerns you) (only present when context is global) contextid context ID (blogid, memberid, catid depending on context)
PostPluginOptionsUpdate | (v2.0b) After the options for a plugin have been updated. (v2.2) Different parameters depending on context | context (v2.2) `global`, `member`, `blog` or `category` plugid plugin ID (compare with `GetID()` to find out if this concerns you) (global context) blogid (v2.2) blog ID (blog context) blog (v2.2) BLOG object (blog context) memberid (v2.2) member ID (member context) member (v2.2) MEMBER object (member context) catid (v2.2) category ID (category context)
PostAuthentication | (v2.0b) After the login procedure has been completed. This occurs on each page request. | loggedIn result of `$member->isLoggedIn()`
PreAddItemForm | (v2.0b) Immediately before an add item form (bookmarklet or admin area) is created. | contents reference to an associative array, in which the values 'title', 'body' and 'more' can be filled with initial values for the formfields. To avoid multiple plugins to alter these values, set the 'hasBeenSet' value to 1 when you're done (and check for it before starting) blog reference to a `BLOG` object
AddItemFormExtras | (v2.0b) Somewhere inside the add item page or bookmarklet. Here, plugins can add their custom fields without having to alter one of the `.template` files. | blog reference to a `BLOG` object
EditItemFormExtras | (v2.0b) Somewhere inside the edit item page or bookmarklet. Here, plugins can add their custom fields without having to alter one of the `.template` files. Don't add too much data, and please generate valid XHTML, looking like this:
    <h3>plugin name</h3>
    <p>your stuff</p>
This way, multiple plugins can add options here while things keep a good structure. Also try to use prefixes for your fieldnames, in order to avoid nameclashes (e.g. `plug_tb_url`) | blog reference to a `BLOG` object variables (read-only) An associative array containing all sorts of information on the item that's being edited: 'itemid', 'draft', 'closed', 'title', 'body', 'more', 'author', 'authorid', 'timestamp', 'karmapos', 'karmaneg', 'catid' itemid shortcut to the item ID
BlogSettingsFormExtras | (v2.0) On the blog settings page. You can add your own forms here. Don't add too much data, and please generate valid XHTML, looking like this:
    <h4>plugin name</h4>
    <form method="post" action="..."><p>
    your stuff
    </p></form>
This way, multiple plugins can add options here while things keep a good structure. Also try to use prefixes for your fieldnames, in order to avoid nameclashes (e.g. `plug_tb_url`) | blog reference to a `BLOG` object
PreDeleteItem | (v2.0) Immediately before an item gets deleted in the database | itemid id of the item that will be deleted
PostDeleteItem | (v2.0) Immediately after an item has been deleted in the database | itemid id of the deleted item
PreDeleteCategory | (v2.0) Immediately before a category gets deleted from the database | catid category ID
PostDeleteCategory | (v2.0) Immediately after a category has been deleted from the database | catid category ID
PreDeleteBlog | (v2.0) Immediately before a blog gets deleted from the database | blogid ID of the blog that will be deleted
PostDeleteBlog | (v2.0) Immediately after a blog has been deleted from the database | blogid ID of the blog that was deleted from the database
PreDeleteMember | (v2.0) Immediately before a member gets deleted from the database | member reference to the `MEMBER` object associated with the member that needs to be deleted
PostDeleteMember | (v2.0) Immediately after a member has been deleted from the database | member reference to the `MEMBER` object associated with the member that has been deleted
PreDeleteTeamMember | (v2.0) Immediately before a member gets deleted from a weblog team | member reference to the `MEMBER` object blogid ID of the blog
PostDeleteTeamMember | (v2.0) Immediately after a member has been deleted from a weblog team | member reference to the `MEMBER` object blogid ID of the blog
PreDeleteComment | (v2.0) Immediately before a comment gets deleted from the database | commentid ID of the comment that will be deleted
PostDeleteComment | (v2.0) Immediately after a comment has been deleted from the database | commentid ID of the deleted comment
ActionLogCleared | (v2.0) After the action log has been cleared | None
PreDeleteTemplate | (v2.0) Immediately before a template gets deleted from the database | templateid ID of the template that will be deleted
PostDeleteTemplate | (v2.0) Immediately after a template has been deleted from the database | templateid ID of the deleted template
PreDeleteSkin | (v2.0) Immediately before a skin gets deleted from the database | skinid ID of the skin that will be deleted
PostDeleteSkin | (v2.0) Immediately after a skin has been deleted from the database | skinid ID of the deleted skin
PreDeletePlugin | (v2.0) Immediately before a plugin gets deleted from the database | plugid ID of the plugin that will be deleted
PostDeletePlugin | (v2.0) Immediately after a plugin has been deleted from the database | plugid ID of the deleted plugin
PreDeleteBan | (v2.0) Immediately before an IP ban gets deleted from the database | blogid ID of the blog for which the ban will be deleted iprange banned IP range
PostDeleteBan | (v2.0) Immediately after an IP ban has been deleted from the database | blogid ID of the blog for which the ban has been deleted iprange banned IP range
PreAddCategory | (v2.0) Immediately before a new category is created in the database | blog reference to `BLOG` object name name of new category description description of new category
PostAddCategory | (v2.0) Immediately after a new category has been created in the database | blog reference to `BLOG` object name name of new category description description of new category catid New category ID
PreAddBlog | (v2.0) Immediately before a new blog is created | name name of new blog shortname shortname of new blog timeoffset time offset of new blog description description of new blog defaultskin ID of default skin for new blog
PostAddBlog | (v2.0) Immediately after a new blog has been created | blog new `BLOG` object
PreAddPlugin | (v2.0) Immediately before a plugin is added | file filename of the new plugin
PostAddPlugin | (v2.0) Immediately after a plugin has been added | plugin An object of the newly added plugin
PreAddTeamMember | (v2.0) Immediately before a member gets added to a blog team | blog `BLOG` object member `MEMBER` object admin boolean indicating if the newly added member will have blog admin rights or not
PostAddTeamMember | (v2.0) Immediately after a member has been added to a blog team | blog `BLOG` object member `MEMBER` object admin boolean indicating if the newly added member has admin rights or not
PreAddTemplate | (v2.0) Immediately before a new template is created (note: this one also gets called when a template is cloned)) | name name of the new template description description of the new template
PostAddTemplate | (v2.0) Immediately after a new template has been created | name name of the new template description description of the new template templateid ID of the new template
PreAddSkin | (v2.0) Immediately before a new skin is created (note: this one also gets called when a skin is cloned)) | name name of the new skin description description of the new skin type content type of the skin includeMode includeMode of the new skin includePrefix includePrefix of the new skin
PostAddSkin | (v2.0) Immediately after a new skin has been created | name name of the new skin description description of the new skin type content type of the skin includeMode includeMode of the new skin includePrefix includePrefix of the new skin skinid ID of the new skin
PreAddBan | (v2.0) Immediately before a new ban is added to a weblog | blogid ID of the blog iprange IP range to be banned reason textual message describing the reason for the ban
PostAddBan | (v2.0) Immediately after a new ban has been added | blogid ID of the blog iprange IP range to be banned reason textual message describing the reason for the ban
PreMoveItem | (v2.0) Immediately before an item is moved to another blog/category | itemid ID of the item destblogid ID of the destination blog destcatid ID of the destination category
PostMoveItem | (v2.0) Immediately after an item has been moved to another blog/category | itemid ID of the item destblogid ID of the new blog destcatid ID of the new category
PreMoveCategory | (v2.0) Immediately before a catgeory is moved to another blog | catid ID of the catgeory sourceblog source `BLOG` object destblog destination `BLOG` object
PostMoveCategory | (v2.0) Immediately after a catgeory has been moved to another blog | catid ID of the catgeory sourceblog source `BLOG` object destblog destination `BLOG` object
MemberSettingsFormExtras | (v2.0) On the member settings page. You can add your own forms here. Don't add too much data, and please generate valid XHTML, looking like this:
    <h4>plugin name</h4>
    <form method="post" action="..."><p>
    your stuff
    </p></form>
This way, multiple plugins can add options here while things keep a good structure. Also try to use prefixes for your fieldnames, in order to avoid nameclashes (e.g. `plug_tb_url`) | member reference to a `MEMBER` object
GeneralSettingsFormExtras | (v2.0) On the general settings page. You can add your own forms here. Don't add too much data, and please generate valid XHTML, looking like this:
    <h4>plugin name</h4>
    <form method="post" action="..."><p>
    your stuff
    </p></form>
This way, multiple plugins can add options here while things keep a good structure. Also try to use prefixes for your fieldnames, in order to avoid nameclashes (e.g. `plug_tb_url`) | None
AdminPrePageHead | (v2.5) On admin area pages, immediately before the page head is printed. This event could be used to add extra script/css includes in the head section | extrahead Extra information to be included in the head section of the HTML page. Append your extras here. action Currently executed action or pagetype
AdminPrePageFoot | (v2.5) On admin area pages, immediately before the page footer is printed. | action Currently executed action or pagetype
PreSendContentType | (v2.5) Immediately before a content type is being set in the HTTP header | contentType content type (e.g. `application/xhtml+xml`) charset Character set pageType String indicating which type of page we're displaying: `skin` (skinparts), `media` (media library), `admin-action` (admin area), `bookmarklet-action` (bookmarklet)
QuickMenu | (v2.5) At the end of the Admin Area quick menu. This can be used to add extra plugin entries. To add entries, push associative arrays on the `options` array. An example can be found in the section about creating a plugin admin area. | options Array
BookmarkletExtraHead | (v2.5) Somewhere inside the `head` section of the bookmarklet XHTML code. | extrahead Information to be included in the `head` section of the XHTML code. Add your extras here.
FormExtra | (v3.1+) Called from within comment, member mail and account activation forms. The event allows to insert extra form fields, such as e.g. captcha images. | type Indicates in which form the event was called: `commentform-loggedin`, `commentform-notloggedin`, `membermailform-loggedin`, `membermail-notloggedin`, `additemform` or `activation`. All values besided `activation` refer to the respective forms in `nucleus/forms`. member `MEMBER` object. Only present when `type` is `activation`. Contains the information about the site member that is (re)activating his account.
ValidateForm | (v3.1+) Called when validating comment forms, member mail forms and account activation forms. | type One of `comment`, `membermail` or `activation` error Reference to an empty string. When your plugin decides the formdata is invalid and should lead to e.g. a comment not being allowed, a descriptive text should be placed inside this string. This is the error that will be displayed to the user. member `MEMBER` object. Only present when `type` is `activation`. Contains the information about the site member that is (re)activating his account. comment Only present when `type` is `comment`. It contains an associative array with the comment data.

# Saving options

A series of methods are offered to make it easy for plugins to set and retrieve options. These options can be directly edited from inside the BLOG:CMS admin area, taking the need away for the plugin to provide an admin area of its own, and avoiding that options need to be set inside the PHP file itself.

Options are available in different contexts:

1. Global options: Editable on the admin area from the plugins section.
2. Blog options: Editable from the blogsettings pages.
3. Category options: Editable from the blogsettings pages (on the 'edit category' page).
4. Member options: Editable on the 'edit member' pages

## Option types

Several types of options are provided

text
  Simple text
yesno
  Either the value '`yes`' or the value '`no`' (on edit, shown as radio button)
password
  Text field (starred on edit)
textarea (v2.2)
  Text field with multiple rows and columns
select (v2.2)
  Drop down menu. Needs extra info in the following form: `Option 1|value1|Option 2|value2|Option 3|value3`

## Restrictions

1. The name of an option can contain a maximum of 20 characters
2. The description of an option can contain a maximum of 255 characters
3. The value for an option has no limit (Prior to v2.5 the limit was 128 characters)

## The methods

### createOption($name, $desc, $type, $defValue = '', $typeExtras = '')

Creates a new option in the global context

parameter | value
--- | ---
$name | Option name
$desc | Textual description, to be shown on the page where options can be edited
$type | Option type (see above)
$defValue | Initial value
$typeExtras | Extra info on option type (see above)

### [v2.2] createBlogOption($name, $desc, $type, $defValue = '', $typeExtras = '')

Creates an option in the blog context (see `createOption`)

### [v2.2] createCategoryOption($name, $desc, $type, $defValue = '', $typeExtras = '')

Creates an option in the `category` context (see `createOption`)

### [v2.2] createMemberOption($name, $desc, $type, $defValue = '', $typeExtras = '')

Creates an option in the `member` context (see `createOption`)

### setOption($name, $value)

changes the value of an option that was already in the database

parameter | value
--- | ---
$name | Option name
| $value | New value for option

### [v2.2] setBlogOption($blogid, $name, $value)

Changes the value for a blog option. The `blogid` attribute indicates for which blog the option is valid. (other options: see `setOption`)

### [v2.2] setCategoryOption($catid, $name, $value)

Changes the value for a category option. The `catid` attribute indicates for which category the option is valid. (other options: see `setOption`)

### [v2.2] setMemberOption($memberid, $name, $value)

Changes the value for a member option. The `memberid` attribute indicates for which member the option is valid. (other options: see `setOption`)

### getOption($name)

Returns the value for an option in the database

parameter | value
--- | ---
$name | Option name

### [v2.2] getBlogOption($blogid, $name)

Returns the value for a blog option. `blogid` indicates for which blog a value is requested (other parameters: see `getOption`)

### [v2.2] getCategoryOption($catid, $name)

Returns the value for a category option. `catid` indicates for which category a value is requested (other parameters: see `getOption`)

### [v2.2] getMemberOption($memberid, $name)

Returns the value for a member option. `memberid` indicates for which member a value is requested (other parameters: see `getOption`)

### deleteOption($name)

Deletes an option from the database

parameter | value
--- | ---
$name | Option name

### [v2.2] deleteBlogOption($name)

Deletes a blog option (see `deleteOption`)

### [v2.2] deleteCategoryOption($name)

Deletes a category option (see `deleteOption`)

### [v2.2] deleteMemberOption($name)

Deletes a member option (see `deleteOption`)

### [v2.2] getAllBlogOptions($name)

Returns all values for a given blog option. The result is an associative array with a value for each existing blogid

### [v2.2] getAllCategoryOptions($name)

Returns all values for a given category option. The result is an associative array with a value for each existing catid

### [v2.2] getAllMemberOptions($name)

Returns all values for a given member option. The result is an associative array with a value for each existing memberid

Note: You can't call these functions from inside constructors of plugin classes. If you want to execute them when the plugin is loaded, place them in the `init()` method instead.

# Database tables

## Access to BLOG:CMS tables

Up to v2.0, accessing the BLOG:CMS tables was just a matter of performing an SQL query on one of the `nucleus_` tables. Since it is possible to use a custom table name in BLOG:CMS versions >2.0, some precautions are needed in plugin development:

1. Instead of using a fixed tablename like `nucleus_item`, use the global function `sql_table('item')` to generate the prefixed tablename
2. Make sure your plugin returns `1` (`true`) when `supportsFeature('SqlTablePrefix')` is called on it. If it doesn't, you won't be able to load the plugin on BLOG:CMS versions > 2.0 when a custom prefix has been set (as a precaution)

Note that the `sql_table` global function in not available in BLOG:CMS versions up to v2.0. If you use this method and want your plugin to work on BLOG:CMS versions <= 2.0, add the following snippet of code on top of your plugin class:

    <?

    // plugin needs to work on BLOG:CMS versions <=2.0 as well
    if (!function_exists('sql_table'))
    {
    	function sql_table($name) {
    		return 'nucleus_' . $name;
    	}
    }

    class NP_HelloWorld extends NucleusPlugin {
    ...
    }

    ?>

## Your own tables

If your plugin needs database tables of it's own, you should create then in the `install` method and remove them in the `unInstall` method.

Some pointers

- Consider using a table name like `nucleus_plug_plugname` to avoid conflicts with other plugins. Generate them through `sql_table('plug_plugname')` to make it work with custom prefixes
- You don't need to make a database connection yourself. You can execute queries using the PHP command `mysql_query()`
- If you do make a database connection yourself, make sure to restore the connection with the BLOG:CMS database afterwards. You can do this by calling `sql_connect()` at the end of your function. It might also be good to do this from the constructor, to avoid reconnecting constantly. You could then save your link identifier in `$this->db` and pass that along with every query.
- Also redefine the `getTableList()` method to make sure your table gets backupped when using the backup function.

# Plugin Admin Area

As of BLOG:CMS v2.5, plugins can create admin area pages that integrate with the BLOG:CMS admin area. These pages can be accessed either from the plugin admin page, or the quickmenu on the left.

## Basics

To provide an admin area, you'll need take these steps:

1. Create a subdirectory of the plugins directory, and name it pluginname if your plugin is `NP_PluginName`. Note that the name should be lowercase!
2. In that directory, create a file called index.php, which looks like this:

       <?php

       	// if your 'plugin' directory is not in the default location,
       	// edit this variable to point to your site directory
       	// (where config.php is)
       	$strRel = '../../../';

       	include($strRel . 'config.php');
       	if (!$member->isLoggedIn())
       		doError('You\'re not logged in.');

       	include($DIR_LIBS . 'PLUGINADMIN.php');

       	// create the admin area page
       	$oPluginAdmin = new PluginAdmin('PluginName');
       	$oPluginAdmin->start();

       	echo '<h2>Plugin Name</h2>';

       	echo '<p>Page contents here<p>';

       	$oPluginAdmin->end();

       ?>

3. Subscribe to the `QuickMenu` event and add this code in your plugin:

       function event_QuickMenu(&$data) {
       		array_push(
       			$data['options'],
       			array(
       				'title' => 'Plugin Name',
       				'url' => $this->getAdminURL(),
       				'tooltip' => 'Tooltip text'
       			)
       		);
       	}

4. Implement this method in your plugin:

       function hasAdminArea()
       {
       	return 1;
       }

## Considerations

- Don't just add an entry to the quick menu because it is possible. Imagine having 100 plugins installed, which all add an entry in the menu. This would result in a real mess. So, even if you add an entry in the menu, please consider adding an option (global or member-level) to enable/disable the quick menu entry.
- The `$strRel` variable in the `index.php` needs to be adapted manually if the plugins directory is not located in `nucleus/plugins/`
- Make sure your admin area outputs valid XHTML. If not, the page will be broken in Gecko-based (Mozilla etc) browsers.

## The PluginAdmin class

The purpose of the `PluginAdmin` is to help you. Once created, you can use `$oPluginAdmin->plugin` to access the instance of your plugin.