wbTeamPro Plugin Architecture
System Plugins allow you to integrate code directly into wbTeamPro via a set of events triggered during runtime. Included with your installation package is a plugin example that can be used as reference while learning to develop within wbTeamPro.
Plugin File Structure
Plugins are stored within the wbTeamPro addons folder /modules/addons/wbteampro/plugins
, with each plugin being entirely contained within a sub-folder.
The plugin model requires that the following files be included (where "example" is the unique plugin key):
-
example/example.php
This is the core plugin file that will be loaded and examined for plugin events. -
example/example.xml
This is the plugin package information. -
example/lang/english.php
This is the default language file for the plugin.
Plugin Information XML
The plugin package XML defines several important aspects of a plugin to the wbTeamPro system. Plugins may include parameter fields that can be used to allow customization of the plugin once installed. The example.xml
file shows how the XML file should be constructed, and examples of the available parameter fields.
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" version="1.0" zone="all" method="install">
<name>plugin_example.xml_name</name>
<author>Webuddha, Holodyn, Inc.</author>
<created>2013-01-01</created>
<version>3.0.0</version>
<description>plugin_example.xml_description</description>
<files>
<folder>lang</folder>
<folder>widgets</folder>
<file>example.php</file>
<file>example.xml</file>
</files>
<params>
<param
type="text"
name="example_text"
default="0"
label="plugin_example.params.example_text-def"
description="plugin_example.params.example_text-desc"
/>
<param
type="selectlist"
name="example_selectlist"
default="0"
label="plugin_example.params.example_selectlist-def"
description="plugin_example.params.example_selectlist-desc"
>
<option value="0">plugin_example.params.opt_no</option>
<option value="1">plugin_example.params.opt_yes</option>
</param>
<param
type="radiolist"
name="example_radiolist"
default="0"
label="plugin_example.params.example_radiolist-def"
description="plugin_example.params.example_radiolist-desc"
>
<option value="0">plugin_example.params.opt_no</option>
<option value="1">plugin_example.params.opt_yes</option>
</param>
<param
type="checklist"
name="example_checklist"
default="1"
label="plugin_example.params.example_checklist-def"
description="plugin_example.params.example_checklist-desc"
>
<option value="0">plugin_example.params.opt_no</option>
<option value="1">plugin_example.params.opt_yes</option>
</param>
<param
type="textarea"
name="example_textarea"
default=""
label="plugin_example.params.example_textarea-def"
description="plugin_example.params.example_textarea-desc"
/>
</params>
</extension>
Plugin Event PHP
The plugin package Core PHP file defines the event methods that will be triggered during runtime.
- Plugins are zoned for either client, admin, or all and are loaded upon wbTeamPro initialization
- Every plugin must have a unique plugin "key" name, such as "example" for the Example plugin
- The plugin class is defined by appending the plugin key to the prefix "wbTeamPro_Plugin_"
- Plugin events are methods named for the appropriate event trigger
- All event methods will receive the same two parameters ( $data, $params )
The example.php
file shows how to to properly construct the plugin.
<?php
/**
Webuddha wbTeamPro
(c)2010 Webuddha.com, The Holodyn Corporation - All Rights Reserved
**/
// ********************************************************************
// Check Valid Access
// ********************************************************************
defined('WHMCS_ADMIN') or defined('WHMCS_CLIENT') or die('Invalid Access');
// ********************************************************************
// Plugin Class
// ********************************************************************
class wbTeamPro_Plugin_Example extends wbTeamPro_Plugin {
/******************************************************************************
PUBLIC FUNCTIONS
*******************************************************************************/
//---------------------------------------------------------------------------
// Class Constructor
//---------------------------------------------------------------------------
public function __construct( $extension ){
// Import Plugin Language
wbTeamPro_Lang::import( dirname(__FILE__).DS.'lang'.DS );
// Initialize Plugin
parent::__construct( $extension );
}
//---------------------------------------------------------------------------
// System Event Functions
//---------------------------------------------------------------------------
public function system_onRunCron( &$data, &$params ){}
public function system_onAfterUpgrade( &$data, &$params ){
$previous_version = $data['previous_version']; // Before Upgrade - ie: 3.0.1
$current_version = $data['current_version']; // After Upgrade - ie: 3.0.2
wbTeamPro_Common::IsOlderVersion($current_version, '3.0.2'); // Is older than 3.0.2 (returns 1 if major, 2 if minor, 3 if build, 4 if stage)
}
//---------------------------------------------------------------------------
// APIv1 Event Functions
//---------------------------------------------------------------------------
public function apiv1_getResourceList( &$data, &$params ){
$resourceList = &$data['resourceList'];
$resourceList['plg_example'] = array(
'label' => 'Example Plugin',
'methods' => array(
'plgExampleFindConfigs' => array(
'synopsis' => 'Return a Example Plugin Config Option List',
'action' => 'plg_example.configs',
'http_method' => 'get',
'params' => array()
),
'plgExampleGetConfig' => array(
'synopsis' => 'Return a Example Plugin Config Option',
'action' => 'plg_example.config.{option}',
'http_method' => 'get',
'params' => array(
'option' => array('type' => 'text', 'required' => 1),
'example_string' => array('type' => 'string', 'default' => 'example', 'note' => 'This is a String'),
'example_int' => array('type' => "int"),
'example_enum' => array('type' => "enum('a','b')")
)
),
'plgExampleGetUser' => array(
'synopsis' => 'Return an Admin user',
'action' => 'plg_example.user.{id}',
'http_method' => 'get',
'params' => array(
'id' => array('type' => 'int')
)
),
'plgExampleFindActions' => array(
'synopsis' => 'Return a Custom Action List',
'action' => 'plg_example.actions',
'http_method' => 'get',
'params' => array(
'limit' => array('type' => 'int', 'default' => wbTeamPro_Config::_('page_listing_limit')),
'page' => array('type' => 'int', 'default' => 1),
'offset' => array('type' => 'int', 'default' => 0),
'order' => array('type' => 'string', 'default' => 'project.project_name.asc'),
'search_string' => array('type' => 'string'),
'project_id' => array('type' => 'int')
)
),
),
'fields' => array()
);
}
function apiv1_processAction( &$data, &$params ){
$request =& $data['request'];
$route = explode('/', $request['action']);
if (reset($route) == 'plg_example')
{
$api =& $data['api'];
$response =& $data['response'];
array_shift($route);
switch (reset($route)) {
case 'configs':
$request['code'] = 200;
$response['config'] = $this->extension->extension_params;
break;
case 'config':
$filter = new wbTeamPro_Params($api->parseMethodParams('plgExampleGetConfig'));
if (!isset($this->extension->extension_params[ $filter->get('option') ])) {
$request['code'] = 400;
$request['message'] = 'Invalid Configuration Value';
}
else {
$request['code'] = 200;
$response['config'] = array(
'option' => $option,
'value' => $this->extension->extension_params[ $filter->get('option') ]
);
}
break;
case 'user':
// Request from Database
$filter = new wbTeamPro_Params($api->parseMethodParams('plgExampleGetUser'));
if ((int)$filter->get('id')) {
$dbh = wbDatabase::getInstance();
$record = $dbh->runQuery("
SELECT id, firstname
FROM `tbladmins`
WHERE `id` = '". $dbh->getEscaped($filter->get('id')) ."'
")->getRow();
}
// Response
if (@$record) {
$data['request']['code'] = 200;
$data['response']['data']['filters'] = (array)$filter;
$data['response']['client'] = $record;
}
else {
$data['request']['code'] = 404;
$data['request']['message'] = 'Not Found';
}
break;
case 'actions':
// Request from Model
$model = wbTeamPro_Model::getInstance('action');
$filter = $api->parseMethodParams('plgExampleFindActions');
$order = preg_replace('/^(.*)\.(asc|desc)$/','$1 $2',wbTeamPro_Request::get('order','action.action_complete.asc'));
$page = wbTeamPro_Request::get('page',1);
$limitstart = wbTeamPro_Request::get('offset',0);
$limit = wbTeamPro_Request::get('limit',wbTeamPro_Config::_('page_listing_limit'));
$result = $model->getList(array(
'filter' => (array)$filter,
'order' => $order,
'page' => $page,
'limitstart' => $limitstart,
'limit' => $limit
));
// Response
$request['code'] = 200;
$response['data']['total'] = $result['total'];
$response['data']['page'] = $result['page'];
$response['data']['pages'] = $result['pages'];
$response['data']['limit'] = $result['limit'];
$response['data']['offset'] = $result['limitstart'];
$response['data']['filters'] = $result['filters'];
$response['actions'] = &$result['records'];
break;
}
}
}
//---------------------------------------------------------------------------
// Table Event Functions
//---------------------------------------------------------------------------
public function table_onAfterStore_Project( &$data, &$params ){
// Return TRUE to continue (default)
return true;
// You can inspect the class & data
wbTeamPro_Common::inspect('table_onAfterStore_Project Event', $this, $data, $params); die('ABORT');
// You can modify the recently stored object
$openProject =& $data['openProject'];
if( strpos($openProject->project_name, '*Example Plugin*') === false ){
// Set the active object for use by following operations
$openProject->project_name = $openProject->project_name . ' ** Example Plugin';
// Store your changes (Within the store function you must save directly)
$openProject->_dbh->runQuery("
UPDATE `".$openProject->_tbl."`
SET `project_name` = '". $openProject->_dbh->getEscaped($openProject->project_name) ."'
WHERE `project_id` = '". $openAction->_dbh->getEscaped($openProject->project_id) ."'
LIMIT 1
");
}
}
public function table_onBeforeDelete_Project( &$data, &$params ){
// Return TRUE to continue (default)
return true;
// You can inspect the class & data
wbTeamPro_Common::inspect('table_onBeforeDelete_Project Event', $this, $data, $params); die('ABORT');
// Assign a Message and Return FALSE to abort
$data['openProject']->setError( wbTeamPro_Lang::_('plugin_example.deleteCancel','Delete Cancelled by Example Plugin') );
return false;
}
public function table_onAfterCopy_Project( &$data, &$params ){}
public function table_onAfterStore_Action( &$data, &$params ){}
public function table_onBeforeDelete_Action( &$data, &$params ){}
public function table_onAfterStore_Topic( &$data, &$params ){}
public function table_onBeforeDelete_Topic( &$data, &$params ){}
public function table_onAfterStore_File( &$data, &$params ){}
public function table_onBeforeDelete_File( &$data, &$params ){}
public function table_onAfterStore_TicketXref( &$data, &$params ){}
public function table_onBeforeDelete_TicketXref ( &$data, &$params ){}
public function table_onAfterStore_Timelog( &$data, &$params ){}
public function table_onBeforeDelete_Timelog( &$data, &$params ){}
//---------------------------------------------------------------------------
// View Event Functions
// view_onBeforeRender_Path_File
//---------------------------------------------------------------------------
public function view_onBeforeRender_Blocks_Menu( &$data, &$params ){
// Get Menu
$menu = wbTeamPro_Menu::getMenu('mainmenu');
// Primary Menu
$custom = $menu->addChild('menu.custom_plugin', array(
'label' => wbTeamPro_Lang::_('menu.custom_plugin', 'Custom Plugin'),
'uri' => 'wbteampro.php?call=plugin&target=example'
)
);
$custom->addChild('menu.custom_plugin_link', array(
'label' => wbTeamPro_Lang::_('menu.custom_plugin_link', 'Custom Plugin Link'),
'uri' => 'wbteampro.php?call=plugin&target=example'
));
}
public function view_onBeforeRender_Blocks_Sidebar( &$data, &$params ){
// Get Menu
$sidebar = wbTeamPro_Menu::getMenu('sidebar');
// Primary Menu
$custom = $sidebar->addChild(
'menu.custom_plugin',
array(
'label' => '<i class="fa fa-cogs"></i> ' . wbTeamPro_Lang::_('menu.custom_plugin', 'Custom Plugin'),
'extras' => array('safe_label' => true),
'attributes' => array('class' => 'nav-setupmanager'),
)
);
$custom->addChild('menu.custom_plugin_link', array(
'label' => wbTeamPro_Lang::_('menu.custom_plugin_link', 'Custom Plugin Link'),
'uri' => 'wbteampro.php?call=plugin&target=example'
));
}
//---------------------------------------------------------------------------
// Client Event Functions
//---------------------------------------------------------------------------
public function client_onInitialize( &$data, &$params ){}
public function client_onCallPlugin( &$data, &$params ){
// Examples of Direct Call
// wbteampro.php?call=plugin&target=example&view=html
// wbteampro.php?call=plugin&target=example&view=raw
echo "Process & Produce Client Output...";
}
public function client_onPrepareView( &$data, &$params ){}
public function client_onBeforeRender_ProjectEditTabs( &$data, &$params ){
// Example of Tab Injection
/*
$data['pageTabs']['unique_taskid'] =
array(
'task' => 'blank',
'link' => 'wbteampro.php?task=blank',
'label' => wbTeamPro_Lang::_('tabs.label','Example Tab'),
);
*/
}
public function client_onBeforeLoadTask( &$data, &$params ){}
public function client_onAfterLoadTask( &$data, &$params ){}
public function client_onFinalizeView( &$data, &$params ){}
public function client_onBeforeHeaderOutput( &$data, &$params ){}
public function client_onHeaderOutput( &$data, &$params ){}
public function client_onAfterStore_Action( &$data, &$params ){}
public function client_onAfterStore_File( &$data, &$params ){}
public function client_onAfterStore_Topic( &$data, &$params ){}
//---------------------------------------------------------------------------
// Admin Event Functions
//---------------------------------------------------------------------------
public function admin_onInitialize( &$data, &$params ){}
public function admin_onCallPlugin( &$data, &$params ){
// Examples of Direct Call
// wbteampro.php?call=plugin&target=example&view=html
// wbteampro.php?call=plugin&target=example&view=raw
echo "Process & Produce Admin Output...";
}
public function admin_onPrepareView( &$data, &$params ){}
public function admin_onBeforeLoadTask( &$data, &$params ){}
public function admin_onAfterLoadTask( &$data, &$params ){}
public function admin_onFinalizeView( &$data, &$params ){}
public function admin_onBeforeHeaderOutput( &$data, &$params ){}
public function admin_onHeaderOutput( &$data, &$params ){}
public function admin_onBeforeRender_ProjectEditTabs( &$data, &$params ){
// Example of Tab Injection
/*
$data['pageTabs']['unique_taskid'] =
array(
'task' => 'blank',
'link' => 'wbteampro.php?task=blank',
'label' => wbTeamPro_Lang::_('tabs.label','Example Tab'),
);
*/
}
public function admin_onDuringRender_ProjectEditForm( &$data, &$params ){}
public function admin_onBeforeRender_ActionEditTabs( &$data, &$params ){
// Example of Tab Injection
/*
$data['subTabs']['unique_tabkey'] = array(
'label' => 'Tab Label', // tab label
'title' => 'Tab Title', // tab link title
'css' => '', // tab css rules
'onclick' => '', // tab onclick js code
'content' => '', // tab content
'src' => '', // url to call for tab content
// Future Consideration - not in use
'event' => array(
'click' => null // tab onclick js code
)
'content' => array( // use one of the three options
'include' => null, // path to include for tab innerHTML
'html' => null, // html content for tab innerHTML
'raw' => null, // raw code to replace tab wrapper
'src' => null // url to load on tab click
)
);
*/
}
public function admin_onDuringRender_TimelogEditForm( &$data, &$params ){}
public function admin_onAfterStore_Project( &$data, &$params ){}
public function admin_onAfterStore_Action( &$data, &$params ){}
public function admin_onAfterStore_File( &$data, &$params ){}
public function admin_onAfterStore_Topic( &$data, &$params ){}
public function admin_onRenderTicketProjectTabForm( &$data, &$params ){}
public function admin_onAfterStore_TicketXref( &$data, &$params ){}
//---------------------------------------------------------------------------
// Plugin Management Functions
//---------------------------------------------------------------------------
public function admin_onDuringRender_PluginEditForm( &$data, &$params ){
// Localize the Extension Data
$ext_data = $data['extension']['extension_data'];
// Render a form on the Plugin Edit Form
?>
<h2><?php echo wbTeamPro_Lang::_('plugin_example.dataStorage.header') ?></h2>
<table class="form" width="100%" border="0" cellspacing="2" cellpadding="3">
<tr>
<td class=fieldlabel width="15%"><?php echo wbTeamPro_Lang::_('plugin_example.dataStorage.example1-def') ?>:</td>
<td class=fieldarea><input type="text" name="extension_data[example1]" value="<?php echo htmlspecialchars($ext_data['example1']) ?>" style="width:99%;" ></td>
<td class="fieldlabel desc"><?php echo wbTeamPro_Lang::_('plugin_example.dataStorage.example1-desc') ?></td>
</tr>
<tr>
<td class=fieldlabel width="15%"><?php echo wbTeamPro_Lang::_('plugin_example.dataStorage.example2-def') ?></td>
<td class=fieldarea width="35%"><input type="text" name="extension_data[example2]" value="<?php echo htmlspecialchars($ext_data['example2']) ?>" style="width:99%;" ></td>
<td class="fieldlabel desc"><?php echo wbTeamPro_Lang::_('plugin_example.dataStorage.example1-desc') ?></td>
</tr>
</table>
<?php
}
public function admin_onAfterSave_PluginEditForm( &$data, &$params ){
// Overwrite the Extension Data
$this->extension->extension_data->example1 = 'Test Value Overwrite';
// Store the extension data changes
$this->extension->store();
}
//---------------------------------------------------------------------------
// Plugin Management Functions
//---------------------------------------------------------------------------
public function plugin_onBeforeUninstall( &$data, &$params ){
// Allow Uninstall
return true;
// Example of how to prevent uninstall
$data['openExtension']->setError('Cannot Uninstall Example Plugin');
return false;
}
}
Plugin Language PHP
The plugin package optionally allows for language translation files to be included. Language files should each be named after the language options available in WHMCS.
<?php
/**
Webuddha wbTeamPro
(c)2010 Webuddha.com, The Holodyn Corporation - All Rights Reserved
CHANGELOG ************
**/
defined('WHMCS_CLIENT') or defined('WHMCS_ADMIN') or die('Invalid Access');
/************** SHARED LANGUAGE */
$_PLUGINLANG["wbteampro"]["plugin_example"]["xml_name"] = 'Example Plugin';
$_PLUGINLANG["wbteampro"]["plugin_example"]["xml_description"] = 'This example plugin can be used to learn about wbTeamPro development';
$_PLUGINLANG["wbteampro"]["plugin_example"]["widget_title"] = 'Example Widget';
/************** ADMIN LANGUAGE */
if( defined('WHMCS_ADMIN') ){
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_text-def"] = 'Example Input';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_text-desc"] = 'TEXT Input Field';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_selectlist-def"] = 'Select Input';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_selectlist-desc"] = 'SELECT Options Field';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_radiolist-def"] = 'Radio Input';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_radiolist-desc"] = 'RADIO Input Single Select List';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_checklist-def"] = 'Checkbox Input';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_checklist-desc"] = 'CHECKBOX Input Multiple Select List';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_textarea-def"] = 'Textarea Input';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["example_textarea-desc"] = 'Textarea Block Field';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["opt_yes"] = 'Yes';
$_ADMINLANG["wbteampro"]["plugin_example"]["params"]["opt_no"] = 'No';
$_ADMINLANG["wbteampro"]["plugin_example"]["welcome"] = 'This is a Language Example';
$_ADMINLANG["wbteampro"]["plugin_example"]["deleteCancel"] = 'Delete Cancelled by Example Plugin';
$_ADMINLANG["wbteampro"]["plugin_example"]["dataStorage"]["header"] = 'Custom Plugin Form / Extension Data Storage';
$_ADMINLANG["wbteampro"]["plugin_example"]["dataStorage"]["example1-def"] = 'Date Example 1';
$_ADMINLANG["wbteampro"]["plugin_example"]["dataStorage"]["example1-desc"] = 'Custom Description Example';
$_ADMINLANG["wbteampro"]["plugin_example"]["dataStorage"]["example2-def"] = 'Date Example 2';
$_ADMINLANG["wbteampro"]["plugin_example"]["dataStorage"]["example2-desc"] = 'Custom Description Example';
}
/************** CLIENT LANGUAGE */
else {
$_LANG["wbteampro"]["plugin_example"]["welcome"] = "This is a Client Language";
}
Plugin Event Reference
The following is a complete list of the plugin event triggers currently available.
system_onRunCron - During cron after validating session
system_onAfterUpgrade - After performing a wbTeamPro upgrade
apiv1_getResourceList - Inject API resources
table_onAfterCopy_Project -> After performing copy function
table_onAfterStore_Action -> After storing database record
table_onAfterStore_File -> After storing database record
table_onAfterStore_Project -> After storing database record
table_onAfterStore_TicketXref -> After storing database record
table_onAfterStore_Timelog -> After storing database record
table_onAfterStore_Topic -> After storing database record
table_onBeforeDelete_Action -> Before deleting database record
table_onBeforeDelete_File -> Before deleting database record
table_onBeforeDelete_Project -> Before deleting database record
table_onBeforeDelete_TicketXref -> Before deleting database record
table_onBeforeDelete_Timelog -> Before deleting database record
table_onBeforeDelete_Topic -> Before deleting database record
plugin_onAfterInstall -> After plugin installed
plugin_onBeforeUninstall -> Before plugin un-installed
plugin_onAfterEnable -> After plugin enabled
plugin_onBeforeDisable -> Before plugin disabled
admin_onCallPlugin - Injection point for custom plugin events
admin_onAfterLoadTask - During controller after loading task
admin_onBeforeLoadTask - During controller before loading task
admin_onFinalizeView - During controller after collecting output
admin_onHeaderOutput - Executes after output of html header
admin_onInitialize - During controller startup after startup
admin_onPrepareView - During controller before preparing ouput
admin_onBeforeHeaderOutput - Before rendering header ouput
admin_onDuringRender_PluginEditForm -> During plugin detail form display
admin_onAfterSave_PluginEditForm -> After saving plugin detail form
admin_onBeforeRender_ActionEditTabs - Before action edit tabs display
admin_onBeforeRender_ProjectEditTabs - Before project edit tabs display
admin_onDuringRender_ProjectEditForm - During project detail form display
admin_onDuringRender_TicketProjectTabForm - During project tab form on Support Ticket display
admin_onDuringRender_TimelogEditForm - During timelog edit form display
admin_onAfterStore_Action - After saving before rendering
admin_onAfterStore_File - After saving before rendering
admin_onAfterStore_Project - After saving before rendering
admin_onAfterStore_TicketXref - After saving before rendering
admin_onAfterStore_Topic - After saving before rendering
admin_onAfterStore_Timelog - After saving before rendering
client_onAfterLoadTask - During controller after loading task
client_onBeforeLoadTask - During controller before loading task
client_onFinalizeView - During controller after collecting output
client_onHeaderOutput - Executes after output of html header
client_onInitialize - During controller startup after startup
client_onPrepareView - During controller before preparing ouput
client_onBeforeRender_ProjectEditTabs - During project edit tabs display (added v3.0.16)
client_onAfterStore_Action - After saving before rendering
client_onAfterStore_File - After saving before rendering
client_onAfterStore_Topic - After saving before rendering
client_onBeforeHeaderOutput - Before rendering header ouput
client_onCallPlugin - Injection point for custom plugin events