wiclear-2007-07-19/inc/classes/plugin_manager.class.php
<?php
# ***** BEGIN LICENSE BLOCK *****
# This file is part of WiClear.
# Copyright (c) 2004-2007 David Jobet. All rights
# reserved.
#
# WiClear is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# WiClear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with DotClear; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
# ***** END LICENSE BLOCK *****
/**
* \brief manages plugin
*
* enable to load plugin on demand when they are really used.
* This avoid consuming too much memory/cpu for code that exist but won't be necessary used.
*/
class PluginManager
{
// constant
var $rootDir;
var $toolsBaseDir = 'tools/'; //!< tools base dir
// plugin hooks
var $contentBoxHandle = array(); //!< array(mode)=>array(plugin_name, callback)
var $userActionHandle = array(); //!< array(mode)=>array(plugin_name, callback)
var $browserTitlePageHandle = array(); //!< array(mode)=>array(plugin_name, callback)
var $javascriptLibs = array(); //!< array(mode)=>array(javascript lib)
// current plugin we're loading hooks from
var $current_plugin = ''; //!< used when importing plugins to properly store plugins info
var $loaded_plugins = array(); //!< list of already loaded plugins
var $loaded_i18n = array(); //!< list of already loaded i18n
// general callback for plugins
var $pluginCallbacks = array();
var $onPluginCallbacks = array();
var $validatePluginCallbacks = array();
// ACL hooks for plugins
var $pluginACLs = array(); //!< array[acl_type=>description]
function PluginManager($rootDir)
{
$this->rootDir = $rootDir;
}
/**
* \brief allow a plugin to add an handle to contentBox area
*
* \param mode the url mode
* \param callback the callback function to callback when mode is used in url
*/
function addContentBoxHandle($mode, $callback)
{
$this->contentBoxHandle[$mode] = array($this->current_plugin, $callback);
}
/**
* \brief allow a plugin to add an handle when reacting to user submission
*
* \param mode the url mode
* \param callback the callback function to callback when mode is used in url
*/
function addUserActionHandle($mode, $callback)
{
$this->userActionHandle[$mode] = array($this->current_plugin, $callback);
}
/**
* \brief allow a plugin to customize browser title
*
* \param mode the url mode
* \param callback the callback function to callback when mode is used in url
*/
function addBrowserTitlePageHandle($mode, $callback)
{
$this->browserTitlePageHandle[$mode] = array($this->current_plugin, $callback);
}
/**
* \brief allow a plugin to add a javascript lib
*
* \param mode the url mode
* \param $jsLib the javascript library to add when plugin is used
*/
function addJavascriptLibs($mode, $jsLib)
{
if (!array_key_exists($mode, $this->javascriptLibs))
{
$this->javascriptLibs[$mode] = array();
}
$this->javascriptLibs[$mode][] = $jsLib;
}
/**
* \brief allow a plugin to add a callback from core code known hooks
*
* \param $clb the new callback to register
*/
function addPluginCallback($clb)
{
$this->pluginCallbacks[] = $clb;
}
/**
* \brief allow the main site to callback a plugin
*
* \param where is an identifier that can be exploited by a plugin to know which part of the site is calling back
* \param extra is an extra parameter that can be of any type, depending of which part of the site is calling back
*/
function pluginCallback($where, &$extra)
{
foreach ($this->pluginCallbacks as $clb)
{
$clb($where, $extra);
}
}
/**
* \brief allow a plugin to define a new type of acl
*
* \param acl_type acl_type of ACL
* \param desc string describing acl
*/
function addPluginACL($acl_type, $desc)
{
if ($acl_type >= acl_plugin_start && !array_key_exists($acl_type, $this->pluginACLs))
{
$this->pluginACLs[$acl_type] = $desc;
}
}
/**
* \brief retrieve all plugin defined ACLs
*
* \return array<acl_type=>description>
*/
function getPluginACLs()
{
return $this->pluginACLs;
}
/**
* \brief allow a plugin to add a callback from core code known hooks
*
* \param $clb the new callback to register
*/
function addOnPluginCallback($clb)
{
$this->onPluginCallbacks[] = $clb;
}
/**
* \brief allow the main site to callback a plugin when reacting to user
*
* \param where is an identifier that can be exploited by a plugin to know which part of the site is calling back
* \param data is an array of data that may be of interest to plugin
*/
function onPluginCallback($where, $data)
{
foreach ($this->onPluginCallbacks as $clb)
{
$clb($where, $data);
}
}
/**
* \brief allow a plugin to validate content from core code known hooks
*
* \param $clb the new callback to register
*/
function addValidatePluginCallback($clb)
{
$this->validatePluginCallbacks[] = $clb;
}
/**
* \brief allow the main site to callback a plugin when reacting to user
*
* \param where is an identifier that can be exploited by a plugin to know which part of the site is calling back
* \param data is an array of data that may be of interest to plugin
* \param reason inout parameter indicating the reason of the validation reject
* \return true if content was validated, false otherwise. In the latter case, variable 'reason' indicates the reason.
*/
function validatePluginCallback($where, $data, &$reason, $debug = false)
{
foreach ($this->validatePluginCallbacks as $clb)
{
if ($clb($where, $data, $reason, $debug) === false)
{
return false;
}
}
return true;
}
/**
* \brief called in prepend.inc.php when setuping world : it imports metadata from plugins
*
* This function is used when setuping world. It lists all plugins found in plugin
* directory, verify mandatory files are presend (hooks.inc.php and index.php), then
* require hooks.inc.php so that plugin can callback methods
* <ul>
* <li>addContentBoxHandle
* <li>addUserActionHandle
* <li>addBrowserTitlePageHandle
* <li>addJavascriptLibs
* </ul>
*/
function importPlugins()
{
$pluginsDir = $this->rootDir.$this->toolsBaseDir;
// import plugins
if (file_exists($pluginsDir))
{
$h = opendir($pluginsDir);
while (($file = readdir($h)) !== false)
{
if ($file != '..' && is_dir($pluginsDir.$file))
{
$this->current_plugin = $file;
// this is a valid tool dir, check required, mandatory files
if (file_exists($pluginsDir.$file.'/hooks.inc.php') &&
file_exists($pluginsDir.$file.'/index.php')
)
{
require $pluginsDir.$file.'/hooks.inc.php';
}
}
}
closedir($h);
}
$this->current_plugin = '';
}
/**
* \brief load a plugin only once. THis is based on plugin name.
*
* It is called when the PluginManager detects a plugin us being used, or if the
* plugin wants to load the complete plugin.
*/
function loadPlugin($plugin, $mode = '')
{
// inject javascript in global javascript structure
if (array_key_exists($mode, $this->javascriptLibs))
{
$jsLibsToLoad = $this->javascriptLibs[$mode];
global $javascriptLibs;
foreach ($jsLibsToLoad as $jsLibToLoad)
{
if (in_array($jsLibToLoad, $javascriptLibs) == false)
{
$javascriptLibs[] = $jsLibToLoad;
}
}
}
if (in_array($plugin, $this->loaded_plugins))
{
// already loaded, nothing to do
return;
}
// load plugin
$pluginsDir = $this->rootDir.$this->toolsBaseDir;
require $pluginsDir.$plugin.'/index.php';
$this->loadI18N($plugin);
$this->loaded_plugins[] = $plugin;
}
/**
* \brief load i18N of plugin only once. THis is based on plugin name.
*
* It is called when the PluginManager detects a plugin us being used, or if the
* plugin wants to load i18N.
*/
function loadI18N($plugin)
{
if (in_array($plugin, $this->loaded_i18n))
{
// already loaded, nothing to do
return;
}
// load i18n file if any
global $ui_lang;
global $translation;
$pluginsDir = $this->rootDir.$this->toolsBaseDir;
if (!empty($translation) && file_exists($pluginsDir.$plugin.'/i18n') && $ui_lang != 'en')
{
$translation->completeFrom($pluginsDir.$plugin.'/i18n', $ui_lang, 'en', '.lang');
}
$this->loaded_i18n[] = $plugin;
}
/**
* \brief internal function that detects when a plugin is being used, load it, and handle callback
*/
function handleMode($mode, &$hooks)
{
if (array_key_exists($mode, $hooks))
{
$plugin = $hooks[$mode][0];
$callback = $hooks[$mode][1];
$this->loadPlugin($plugin, $mode);
return $callback();
}
}
/**
* \brief called when handling contentBox by boxes.lib.php, load plugin before handling callback
*/
function contentBoxHandle($mode)
{
return $this->handleMode($mode, $this->contentBoxHandle);
}
/**
* \brief called when reacting to user input, load plugin before handling callback
*/
function handleUserAction($mode)
{
$this->handleMode($mode, $this->userActionHandle);
}
/**
* \brief called when customizing browser title, load plugin before handling callback
*/
function browserTitlePageHandle($mode)
{
return $this->handleMode($mode, $this->browserTitlePageHandle);
}
}
?>