Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
require_once(INCLUDE_DIR.'/class.config.php');
class PluginConfig extends Config {
var $table = CONFIG_TABLE;
var $form;
function __construct($name) {
// Use parent constructor to place configurable information into the
// central config table in a namespace of "plugin.<id>"
parent::Config("plugin.$name");
}
/* abstract */
function getOptions() {
return array();
}
/**
* Retreive a Form instance for the configurable options offered in
* ::getOptions
*/
function getForm() {
if (!isset($this->form)) {
$this->form = new Form($this->getOptions());
if ($_SERVER['REQUEST_METHOD'] != 'POST')
$this->form->data($this->getInfo());
}
return $this->form;
}
/**
* commit
*
* Used in the POST request of the configuration process. The
* ::getForm() method should be used to retrieve a configuration form
* for this plugin. That form should be submitted via a POST request,
* and this method should be called in that request. The data from the
* POST request will be interpreted and will adjust the configuration of
* this field
*
* Parameters:
* errors - (OUT array) receives validation errors of the parsed
* configuration form
*
* Returns:
* (bool) true if the configuration was updated, false if there were
* errors. If false, the errors were written into the received errors
* array.
*/
function commit(&$errors=array()) {
$f = $this->getForm();
if ($f->isValid()) {
$config = $f->getClean();
$commit = $this->pre_save($config, $errors);
if ($commit && count($errors) === 0)
return $this->updateAll($config);
return false;
}
/**
* Pre-save hook to check configuration for errors (other than obvious
* validation errors) prior to saving. Add an error to the errors list
* or return boolean FALSE if the config commit should be aborted.
}
/**
* Remove all configuration for this plugin -- used when the plugin is
* uninstalled
*/
function purge() {
$sql = 'DELETE FROM '.$this->table
.' WHERE `namespace`='.db_input($this->getNamespace());
return (db_query($sql) && db_affected_rows());
}
}
class PluginManager {
static private $plugin_info = array();
static private $plugin_list = array();
/**
* boostrap
*
* Used to bootstrap the plugin subsystem and initialize all the plugins
* currently enabled.
*/
function bootstrap() {
foreach ($this->allActive() as $p)
$p->bootstrap();
}
/**
* allActive
*
* Scans the plugin registry to find all installed and active plugins.
* Those plugins are included, instanciated, and cached in a list.
*
* Returns:
* Array<Plugin> a cached list of instanciated plugins for all installed
* and active plugins
*/
static function allInstalled() {
if (static::$plugin_list)
return static::$plugin_list;
$sql = 'SELECT * FROM '.PLUGIN_TABLE;
if (!($res = db_query($sql)))
return static::$plugin_list;
$infos = static::allInfos();
while ($ht = db_fetch_array($res)) {
// XXX: Only read active plugins here. allInfos() will
// read all plugins
if (isset($infos[$ht['install_path']])) {
$info = $infos[$ht['install_path']];
if ($ht['isactive']) {
list($path, $class) = explode(':', $info['plugin']);
if (!$class)
$class = $path;
else
require_once(INCLUDE_DIR . $ht['install_path']
. '/' . $path);
static::$plugin_list[$ht['install_path']]
= new $class($ht['id']);
static::$plugin_list[$ht['install_path']] = $ht;
return static::$plugin_list;
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
}
static function allActive() {
$plugins = array();
foreach (static::allInstalled() as $p)
if ($p instanceof Plugin && $p->isActive())
$plugins[] = $p;
return $plugins;
}
/**
* allInfos
*
* Scans the plugin folders for installed plugins. For each one, the
* plugin.php file is included and the info array returned in added to
* the list returned.
*
* Returns:
* Information about all available plugins. The registry will have to be
* queried to determine if the plugin is installed
*/
static function allInfos() {
static $defaults = array(
'include' => 'include/',
'stream' => false,
);
if (static::$plugin_info)
return static::$plugin_info;
foreach (glob(INCLUDE_DIR . 'plugins/*', GLOB_ONLYDIR) as $p) {
if (!is_file($p . '/plugin.php'))
// Invalid plugin -- must define "/plugin.php"
continue;
// plugin.php is require to return an array of informaiton about
// the plugin.
$info = array_merge($defaults, (include $p . '/plugin.php'));
$info['install_path'] = str_replace(INCLUDE_DIR, '', $p);
// XXX: Ensure 'id' key isset
static::$plugin_info[$info['install_path']] = $info;
}
return static::$plugin_info;
}
static function getInfoForPath($path) {
$infos = static::allInfos();
if (isset($infos[$path]))
return $infos[$path];
return null;
}
function getInstance($path) {
static $instances = array();
if (!isset($instances[$path])
&& ($ps = static::allInstalled())
&& ($ht = $ps[$path])
&& ($info = static::getInfoForPath($path))) {
// $ht may be the plugin instance
if ($ht instanceof Plugin)
return $ht;
// Usually this happens when the plugin is being enabled
list($path, $class) = explode(':', $info['plugin']);
if (!$class)
$class = $path;
else
require_once(INCLUDE_DIR . $info['install_path'] . '/' . $path);
$instances[$path] = new $class($ht['id']);
}
return $instances[$path];
}
/**
* install
*
* Used to install a plugin that is in-place on the filesystem, but not
* registered in the plugin registry -- the %plugin table.
*/
function install($path) {
if (!($info = $this->getInfoForPath($path)))
return false;
$sql='INSERT INTO '.PLUGIN_TABLE.' SET installed=NOW() '
.', install_path='.db_input($path)
.', name='.db_input($info['name']);
if (!db_query($sql) || !db_affected_rows())
return false;
static::$plugin_list = array();
return true;
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
}
}
/**
* Class: Plugin (abstract)
*
* Base class for plugins. Plugins should inherit from this class and define
* the useful pieces of the
*/
class Plugin {
/**
* Configuration manager for the plugin. Should be the name of a class
* that inherits from PluginConfig. This is abstract and must be defined
* by the plugin subclass.
*/
var $config_class = null;
var $id;
var $info;
function Plugin($id) {
$this->id = $id;
$this->load();
}
function load() {
$sql = 'SELECT * FROM '.PLUGIN_TABLE.' WHERE
`id`='.db_input($this->id);
if (($res = db_query($sql)) && ($ht=db_fetch_array($res)))
$this->ht = $ht;
$this->info = PluginManager::getInfoForPath($this->ht['install_path']);
}
function getId() { return $this->id; }
function getName() { return $this->info['name']; }
function isActive() { return $this->ht['isactive']; }
function isPhar() { return $this->ht['isphar']; }
function getInstallDate() { return $this->ht['installed']; }
function getIncludePath() {
return realpath(INCLUDE_DIR . $this->info['install_path'] . '/'
. $this->info['include_path']) . '/';
}
/**
* uninstall
*
* Removes the plugin from the plugin registry. The files remain on the
* filesystem which would allow the plugin to be reinstalled. The
* configuration for the plugin is also removed. If the plugin is
* reinstalled, it will have to be reconfigured.
*/
function uninstall() {
if ($this->pre_uninstall() === false)
return false;
$sql = 'DELETE FROM '.PLUGIN_TABLE
.' WHERE id='.db_input($this->getId());
if (db_query($sql) && db_affected_rows())
return $this->getConfig()->purge();
return false;
}
/**
* pre_uninstall
*
* Hook function to veto the uninstallation request. Return boolean
* FALSE if the uninstall operation should be aborted.
* TODO: Recommend a location to stash an error message if aborting
*/
function pre_uninstall() {
return true;
}
function enable() {
$sql = 'UPDATE '.PLUGIN_TABLE
.' SET isactive=1 WHERE id='.db_input($this->getId());
return (db_query($sql) && db_affected_rows());
}
function disable() {
$sql = 'UPDATE '.PLUGIN_TABLE
.' SET isactive=0 WHERE id='.db_input($this->getId());
return (db_query($sql) && db_affected_rows());
}
/**
* upgrade
*
* Upgrade the plugin. This is used to migrate the database pieces of
* the plugin using the database migration stream packaged with the
* plugin.
*/
function upgrade() {
}
function getConfig() {
static $config = null;
if ($config === null && $this->config_class)
$config = new $this->config_class($this->getId());
return $config;
}
function source($what) {
$what = str_replace('\\', '/', $what);
if ($what && $what[0] != '/')
$what = $this->getIncludePath() . $what;
include_once $what;
}
static function lookup($id) { //Assuming local ID is the only lookup used!
$path = false;
if ($id && is_numeric($id)) {
$sql = 'SELECT install_path FROM '.PLUGIN_TABLE
.' WHERE id='.db_input($id);
$path = db_result(db_query($sql));
}
if ($path)
return PluginManager::getInstance($path);
}
}
?>