Skip to content
Snippets Groups Projects
class.plugin.php 9.78 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jared Hancock's avatar
    Jared Hancock committed
    <?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();
                $this->pre_save($config, $errors);
            }
            $errors += $f->errors();
            if (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
         */
        function pre_save($config, &$errors) {
            return;
        }
    
        /**
         * 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();
    
        /**
         * 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() {
            static $plugins = null;
            if ($plugins !== null)
                return $plugins;
    
            $plugins = array();
            $sql = 'SELECT * FROM '.PLUGIN_TABLE;
            if (!($res = db_query($sql)))
                return $plugins;
    
            $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']);
                        require_once(INCLUDE_DIR . '/' . $ht['install_path'] . '/' . $path);
                        $plugins[$ht['install_path']] = new $class($ht['id']);
                    }
                    else {
                        $plugins[$ht['install_path']] = $ht;
                    }
                }
            }
            return $plugins;
        }
    
        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']);
                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']);
            return (db_query($sql) && db_affected_rows());
        }
    }
    
    /**
     * 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() {
            $sql = 'DELETE FROM '.PLUGIN_TABLE
                .' WHERE id='.db_input($this->getId());
            if (db_query($sql) && db_affected_rows())
                return $this->getConfig()->purge();
            return false;
        }
    
        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)
                $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);
        }
    }
    
    ?>