From b838050e5828b5cbf32b9e82ce11c9cc54592809 Mon Sep 17 00:00:00 2001
From: Piyush Mishra <me@piyushmishra.com>
Date: Sat, 20 Aug 2011 19:11:52 +0530
Subject: [PATCH] added new plugins config cascade and added plugin.info.txt

---
 .gitignore                              |   1 +
 conf/plugins.protected.php              |   6 +
 inc/config_cascade.php                  |   5 +
 inc/infoutils.php                       |   4 +-
 inc/lang/en/lang.php                    |   2 +-
 inc/plugincontroller.class.php          | 167 ++++++++++++++++++++----
 inc/pluginutils.php                     |  33 ++++-
 lib/plugins/acl/admin.php               |  14 --
 lib/plugins/acl/plugin.info.txt         |   6 +
 lib/plugins/config/admin.php            |  15 ---
 lib/plugins/config/plugin.info.txt      |   6 +
 lib/plugins/info/plugin.info.txt        |   6 +
 lib/plugins/revert/admin.php            |  14 --
 lib/plugins/revert/plugin.info.txt      |   6 +
 lib/plugins/usermanager/admin.php       |  14 --
 lib/plugins/usermanager/plugin.info.txt |   6 +
 16 files changed, 216 insertions(+), 89 deletions(-)
 create mode 100644 conf/plugins.protected.php
 create mode 100644 lib/plugins/acl/plugin.info.txt
 create mode 100644 lib/plugins/config/plugin.info.txt
 create mode 100644 lib/plugins/info/plugin.info.txt
 create mode 100644 lib/plugins/revert/plugin.info.txt
 create mode 100644 lib/plugins/usermanager/plugin.info.txt

diff --git a/.gitignore b/.gitignore
index 6ad14d206..f70efa665 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
 /conf/words.aspell
 /conf/lang/*
 /conf/plugin_lang/*
+/conf/plugins.local.*
 .htaccess
 *.swp
 *.bak
diff --git a/conf/plugins.protected.php b/conf/plugins.protected.php
new file mode 100644
index 000000000..bdfe821c5
--- /dev/null
+++ b/conf/plugins.protected.php
@@ -0,0 +1,6 @@
+<?php
+$plugins['acl']         = 1;
+$plugins['plugin']      = 1;
+$plugins['config']      = 1;
+$plugins['usermanager'] = 1;
+$plugins['revert']      = 1;
diff --git a/inc/config_cascade.php b/inc/config_cascade.php
index 48ed5a000..c01778e99 100644
--- a/inc/config_cascade.php
+++ b/inc/config_cascade.php
@@ -64,6 +64,11 @@ $config_cascade = array_merge(
         'plainauth.users' => array(
             'default' => DOKU_CONF.'users.auth.php',
             ),
+        
+        'plugins' => array(
+            'local'     => array(DOKU_CONF.'plugins.local.php'),
+            'protected' => array(DOKU_CONF.'plugins.protected.php'),
+            ),
         ),
         $config_cascade
 );
diff --git a/inc/infoutils.php b/inc/infoutils.php
index 786661d01..f1deec66b 100644
--- a/inc/infoutils.php
+++ b/inc/infoutils.php
@@ -66,8 +66,8 @@ function getVersionData(){
             $chunk = fread($fh,2000);
             fclose($fh);
             $chunk = trim($chunk);
-            $chunk = array_pop(explode("\n",$chunk));   //last log line
-            $chunk = array_shift(explode("\t",$chunk)); //strip commit msg
+            $chunk = @array_pop(explode("\n",$chunk));   //last log line
+            $chunk = @array_shift(explode("\t",$chunk)); //strip commit msg
             $chunk = explode(" ",$chunk);
             array_pop($chunk); //strip timezone
             $date = date('Y-m-d',array_pop($chunk));
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index 95356f7b4..02dff5cb7 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -318,5 +318,5 @@ $lang['seconds'] = '%d seconds ago';
 
 $lang['wordblock'] = 'Your change was not saved because it contains blocked text (spam).';
 
-
+$lang['plugin_insterr'] = "Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.";
 //Setup VIM: ex: et ts=2 :
diff --git a/inc/plugincontroller.class.php b/inc/plugincontroller.class.php
index ea5725d47..6dcdcdfff 100644
--- a/inc/plugincontroller.class.php
+++ b/inc/plugincontroller.class.php
@@ -11,11 +11,18 @@ if(!defined('DOKU_PLUGIN'))  define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
 
 class Doku_Plugin_Controller {
 
-    var $list_enabled = array();
-    var $list_disabled = array();
     var $list_bytype = array();
+    var $tmp_plugins = array();
+    var $plugin_cascade = array('default'=>array(),'local'=>array(),'protected'=>array());
+    var $last_local = '';
+    //backup of tmp_plugins needed for write check
+    var $tmp_bak =array();
 
-    function Doku_Plugin_Controller() {
+    /**
+     * Populates the master list of plugins
+     */
+    function __construct() {
+        $this->loadConfig();
         $this->_populateMasterList();
     }
 
@@ -37,7 +44,7 @@ class Doku_Plugin_Controller {
 
         // request the complete list
         if (!$type) {
-            return $all ? array_merge($this->list_enabled,$this->list_disabled) : $this->list_enabled;
+            return $all ? array_keys($this->tmp_plugins) : array_keys(array_filter($this->tmp_plugins));
         }
 
         if (!isset($this->list_bytype[$type]['enabled'])) {
@@ -61,9 +68,10 @@ class Doku_Plugin_Controller {
      * @param  $disabled bool   true to load even disabled plugins
      * @return objectreference  the plugin object or null on failure
      */
-    function &load($type,$name,$new=false,$disabled=false){
+    function load($type,$name,$new=false,$disabled=false){
+
         //we keep all loaded plugins available in global scope for reuse
-        global $DOKU_PLUGINS;
+        global $DOKU_PLUGINS,$lang;
 
         list($plugin,$component) = $this->_splitName($name);
 
@@ -85,12 +93,12 @@ class Doku_Plugin_Controller {
 
         //construct class and instantiate
         if (!class_exists($class, true)) {
+
             # the plugin might be in the wrong directory
             $dir = $this->get_directory($plugin);
             $inf = confToHash(DOKU_PLUGIN."$dir/plugin.info.txt");
             if($inf['base'] && $inf['base'] != $plugin){
-                msg("Plugin installed incorrectly. Rename plugin directory '".
-                    hsc($plugin)."' to '".hsc($inf['base'])."'.",-1);
+                msg(sprintf($lang['plugin_insterr'],hsc($plugin),hsc($inf['base'])),-1);
             }
             return null;
         }
@@ -100,30 +108,27 @@ class Doku_Plugin_Controller {
     }
 
     function isdisabled($plugin) {
-        return (array_search($plugin, $this->list_enabled) === false);
+        return empty($this->tmp_plugins[$plugin]);
     }
 
-    function enable($plugin) {
-        if (array_search($plugin, $this->list_disabled) !== false) {
-            return @unlink(DOKU_PLUGIN.$plugin.'/disabled');
-        }
-        return false;
+    function disable($plugin) {
+        if(array_key_exists($plugin,$this->plugin_cascade['protected'])) return false;
+        $this->tmp_plugins[$plugin] = 0;
+        return $this->saveList();
     }
 
-    function disable($plugin) {
-        if (array_search($plugin, $this->list_enabled) !== false) {
-            return @touch(DOKU_PLUGIN.$plugin.'/disabled');
-        }
-        return false;
+    function enable($plugin) {
+        if(array_key_exists($plugin,$this->plugin_cascade['protected'])) return false;
+        $this->tmp_plugins[$plugin] = 1;
+        return $this->saveList();
     }
 
     function get_directory($plugin) {
         return $plugin;
     }
 
-    function _populateMasterList() {
-        global $conf;
-        if ($dh = opendir(DOKU_PLUGIN)) {
+    protected function _populateMasterList() {
+        if ($dh = @opendir(DOKU_PLUGIN)) {
             while (false !== ($plugin = readdir($dh))) {
                 if ($plugin[0] == '.') continue;               // skip hidden entries
                 if (is_file(DOKU_PLUGIN.$plugin)) continue;    // skip files, we're only interested in directories
@@ -132,19 +137,123 @@ class Doku_Plugin_Controller {
                     // the plugin was disabled by rc2009-01-26
                     // disabling mechanism was changed back very soon again
                     // to keep everything simple we just skip the plugin completely
-                }elseif(@file_exists(DOKU_PLUGIN.$plugin.'/disabled') ||
+                }elseif(@file_exists(DOKU_PLUGIN.$plugin.'/disabled')) {
+                    if(empty($this->plugin_cascade['local'][$plugin]))
+                        $all_plugins[$plugin] = 0;
+                    else
+                        $all_plugins[$plugin] = 1;
+                    //treat this as a default disabled plugin(over-rideable by the plugin manager)
+                    $this->plugin_cascade['default'][$plugin] = 0;
+                    //TODO take this out before final release, 
+                    //it is here only for other developers to be able to switch branches easily
+                    //@unlink(DOKU_PLUGIN.$plugin.'/disabled');
+                    continue;
+                }
+                elseif((array_key_exists($plugin,$this->tmp_plugins) && $this->tmp_plugins[$plugin] == 0) ||
                         ($plugin === 'plugin' && isset($conf['pluginmanager']) &&
                          !$conf['pluginmanager'])){
-                    $this->list_disabled[] = $plugin;
+                    $all_plugins[$plugin] = 0;
+                } elseif((array_key_exists($plugin,$this->tmp_plugins) && $this->tmp_plugins[$plugin] == 1)) {
+                    $all_plugins[$plugin] = 1;
                 } else {
-                    $this->list_enabled[] = $plugin;
+                    $all_plugins[$plugin] = 1;
                 }
             }
+            $this->tmp_plugins = $all_plugins;
+            $this->saveList();
         }
     }
 
+    protected function checkRequire($files) {
+        $plugins = array();
+        foreach($files as $file) {
+            if(file_exists($file)) {
+                @include_once($file);
+            }
+        }
+        return $plugins;
+    }
+
+    function getCascade() {
+        return $this->plugin_cascade;
+    }
+    /**
+     * Save the current list of plugins
+     */
+    function saveList() {
+        if(!empty($this->tmp_plugins)) {
+            //quick check to ensure rewrite is necessary
+            if($this->tmp_plugins != $this->tmp_bak) {
+                //Rebuild local for better check and saving
+                $local_plugins = $this->rebuildLocal();
+                //only write if the list has changed
+                if($local_plugins != $this->plugin_cascade['local']) {
+                    $file = $this->last_local;
+                    $out = "<?php\n";
+                    foreach ($local_plugins as $plugin => $value) {
+                        $out .= "\$plugins['$plugin'] = $value;\n";
+                    }
+                    // backup current file (remove any existing backup)
+                    if (@file_exists($file)) {
+                        if (@file_exists($file.'.bak')) @unlink($file.'.bak');
+                        if (!io_rename($file, $file.'.bak')) return false;
+                    }
+                    //check if can open for writing, else restore
+                    if (!$fh = @fopen($file, 'wb')) {
+                        io_rename($file.'.bak', $file);// problem opening, restore the backup
+                        return false;
+                    }
+                    @fwrite($fh, $out);
+                    fclose($fh);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Rebuild the set of local plugins
+     * @return array array of plugins to be saved in end($config_cascade['plugins']['local'])
+     */
+    function rebuildLocal() {
+        //assign to local variable to avoid overwriting
+        $backup = $this->tmp_plugins;
+        //Can't do anything about protected one so rule them out completely
+        $local_default = array_diff_key($backup,$this->plugin_cascade['protected']);
+        //Diff between local+default and default
+        //gives us the ones we need to check and save
+        $diffed_ones = array_diff_key($local_default,$this->plugin_cascade['default']);
+        //The ones which we are sure of (list of 0s not in default)
+        $sure_plugins = array_filter($diffed_ones,array($this,'negate'));
+        //the ones in need of diff
+        $conflicts = array_diff_key($local_default,$diffed_ones);
+        //The final list
+        return array_merge($sure_plugins,array_diff_assoc($conflicts,$this->plugin_cascade['default']));
+    }
+
+    /**
+     * Build the list of plugins and cascade
+     * 
+     */
+    function loadConfig() {
+        global $config_cascade;
+        foreach(array('default','protected') as $type) {
+            if(array_key_exists($type,$config_cascade['plugins']))
+                $this->plugin_cascade[$type] = $this->checkRequire($config_cascade['plugins'][$type]);
+        }
+        $local = $config_cascade['plugins']['local'];
+        $this->last_local = array_pop($local);
+        $this->plugin_cascade['local'] = $this->checkRequire(array($this->last_local));
+        if(is_array($local)) {
+            $this->plugin_cascade['default'] = array_merge($this->plugin_cascade['default'],$this->checkRequire($local));
+        }
+        $this->tmp_plugins = array_merge($this->plugin_cascade['default'],$this->plugin_cascade['local'],$this->plugin_cascade['protected']);
+        $this->tmp_bak = $this->tmp_plugins;
+    }
+
     function _getListByType($type, $enabled) {
-        $master_list = $enabled ? $this->list_enabled : $this->list_disabled;
+        $master_list = $enabled ? array_keys(array_filter($this->tmp_plugins)) : array_keys(array_filter($this->tmp_plugins,array($this,'negate')));
 
         $plugins = array();
         foreach ($master_list as $plugin) {
@@ -169,11 +278,13 @@ class Doku_Plugin_Controller {
     }
 
     function _splitName($name) {
-        if (array_search($name, $this->list_enabled + $this->list_disabled) === false) {
+        if (array_search($name, array_keys($this->tmp_plugins)) === false) {
             return explode('_',$name,2);
         }
 
         return array($name,'');
     }
-
+    function negate($input) {
+        return !(bool) $input;
+    }
 }
diff --git a/inc/pluginutils.php b/inc/pluginutils.php
index 85bcaee1e..897020a6c 100644
--- a/inc/pluginutils.php
+++ b/inc/pluginutils.php
@@ -16,7 +16,7 @@ function plugin_list($type='',$all=false) {
     global $plugin_controller;
     return $plugin_controller->getList($type,$all);
 }
-function &plugin_load($type,$name,$new=false,$disabled=false) {
+function plugin_load($type,$name,$new=false,$disabled=false) {
     global $plugin_controller;
     return $plugin_controller->load($type,$name,$new,$disabled);
 }
@@ -36,3 +36,34 @@ function plugin_directory($plugin) {
     global $plugin_controller;
     return $plugin_controller->get_directory($plugin);
 }
+function plugin_getcascade() {
+    global $plugin_controller;
+    return $plugin_controller->getCascade();
+}
+/**
+ * return a list (name & type) of all the component plugins that make up this plugin
+ *
+ */
+function get_plugin_components($plugin) {
+    global $plugin_types;
+    static $plugins;
+    if(empty($plugins[$plugin])) {
+        $components = array();
+        $path = DOKU_PLUGIN.plugin_directory($plugin).'/';
+
+        foreach ($plugin_types as $type) {
+            if (@file_exists($path.$type.'.php')) { $components[] = array('name'=>$plugin, 'type'=>$type); continue; }
+
+            if ($dh = @opendir($path.$type.'/')) {
+                while (false !== ($cp = readdir($dh))) {
+                    if ($cp == '.' || $cp == '..' || strtolower(substr($cp,-4)) != '.php') continue;
+
+                    $components[] = array('name'=>$plugin.'_'.substr($cp, 0, -4), 'type'=>$type);
+                }
+                closedir($dh);
+            }
+        }
+        $plugins[$plugin] = $components;
+    }
+    return $plugins[$plugin];
+}
diff --git a/lib/plugins/acl/admin.php b/lib/plugins/acl/admin.php
index 53f6db0cc..a6b0624bc 100644
--- a/lib/plugins/acl/admin.php
+++ b/lib/plugins/acl/admin.php
@@ -30,20 +30,6 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin {
     var $usersgroups = array();
     var $specials = array();
 
-    /**
-     * return some info
-     */
-    function getInfo(){
-        return array(
-            'author' => 'Andreas Gohr',
-            'email'  => 'andi@splitbrain.org',
-            'date'   => '2011-04-16',
-            'name'   => 'ACL Manager',
-            'desc'   => 'Manage Page Access Control Lists',
-            'url'    => 'http://dokuwiki.org/plugin:acl',
-        );
-    }
-
     /**
      * return prompt for admin menu
      */
diff --git a/lib/plugins/acl/plugin.info.txt b/lib/plugins/acl/plugin.info.txt
new file mode 100644
index 000000000..f108a2390
--- /dev/null
+++ b/lib/plugins/acl/plugin.info.txt
@@ -0,0 +1,6 @@
+author Andreas Gohr
+email  andi@splitbrain.org
+date   2011-04-16
+name   ACL Manager
+desc   Manage Page Access Control Lists
+url    http://dokuwiki.org/plugin:acl
diff --git a/lib/plugins/config/admin.php b/lib/plugins/config/admin.php
index e24f3b87b..64906171d 100644
--- a/lib/plugins/config/admin.php
+++ b/lib/plugins/config/admin.php
@@ -32,21 +32,6 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
     var $_session_started = false;
     var $_localised_prompts = false;
 
-    /**
-     * return some info
-     */
-    function getInfo(){
-
-      return array(
-        'author' => 'Christopher Smith',
-        'email'  => 'chris@jalakai.co.uk',
-        'date'   => '2007-08-05',
-        'name'   => 'Configuration Manager',
-        'desc'   => "Manage Dokuwiki's Configuration Settings",
-        'url'    => 'http://dokuwiki.org/plugin:config',
-      );
-    }
-
     function getMenuSort() { return 100; }
 
     /**
diff --git a/lib/plugins/config/plugin.info.txt b/lib/plugins/config/plugin.info.txt
new file mode 100644
index 000000000..ace4889b6
--- /dev/null
+++ b/lib/plugins/config/plugin.info.txt
@@ -0,0 +1,6 @@
+author Christopher Smith
+email  chris@jalakai.co.uk
+date   2007-08-05
+name   Configuration Manager
+desc   Manage Dokuwiki's Configuration Settings
+url    http://dokuwiki.org/plugin:config
diff --git a/lib/plugins/info/plugin.info.txt b/lib/plugins/info/plugin.info.txt
new file mode 100644
index 000000000..2432225f1
--- /dev/null
+++ b/lib/plugins/info/plugin.info.txt
@@ -0,0 +1,6 @@
+author Andreas Gohr
+email andi@splitbrain.org
+date 2008-09-12
+name Info Plugin
+desc Displays information about various DokuWiki internals
+url http://dokuwiki.org/plugin:info
diff --git a/lib/plugins/revert/admin.php b/lib/plugins/revert/admin.php
index e188e2488..2aaf1395f 100644
--- a/lib/plugins/revert/admin.php
+++ b/lib/plugins/revert/admin.php
@@ -20,20 +20,6 @@ class admin_plugin_revert extends DokuWiki_Admin_Plugin {
         $this->setupLocale();
     }
 
-    /**
-     * return some info
-     */
-    function getInfo(){
-        return array(
-            'author' => 'Andreas Gohr',
-            'email'  => 'andi@splitbrain.org',
-            'date'   => '2008-12-10',
-            'name'   => 'Revert Manager',
-            'desc'   => 'Allows you to mass revert recent edits',
-            'url'    => 'http://dokuwiki.org/plugin:revert',
-        );
-    }
-
     /**
      * access for managers
      */
diff --git a/lib/plugins/revert/plugin.info.txt b/lib/plugins/revert/plugin.info.txt
new file mode 100644
index 000000000..5bb6f3413
--- /dev/null
+++ b/lib/plugins/revert/plugin.info.txt
@@ -0,0 +1,6 @@
+author Andreas Gohr
+email  andi@splitbrain.org
+date   2008-12-10
+name   Revert Manager
+desc   Allows you to mass revert recent edits
+url    http://dokuwiki.org/plugin:revert
diff --git a/lib/plugins/usermanager/admin.php b/lib/plugins/usermanager/admin.php
index e40ee9b7e..8e90be093 100644
--- a/lib/plugins/usermanager/admin.php
+++ b/lib/plugins/usermanager/admin.php
@@ -51,20 +51,6 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
         }
     }
 
-    /**
-     * return some info
-     */
-    function getInfo(){
-
-        return array(
-            'author' => 'Chris Smith',
-            'email'  => 'chris@jalakai.co.uk',
-            'date'   => '2008-09-17',
-            'name'   => 'User Manager',
-            'desc'   => 'Manage users '.$this->disabled,
-            'url'    => 'http://dokuwiki.org/plugin:usermanager',
-        );
-    }
      /**
      * return prompt for admin menu
      */
diff --git a/lib/plugins/usermanager/plugin.info.txt b/lib/plugins/usermanager/plugin.info.txt
new file mode 100644
index 000000000..7ec5fafd5
--- /dev/null
+++ b/lib/plugins/usermanager/plugin.info.txt
@@ -0,0 +1,6 @@
+author Chris Smith
+email  chris@jalakai.co.uk
+date   2008-09-17
+name   User Manager
+desc   Manage users 
+url    http://dokuwiki.org/plugin:usermanager
-- 
GitLab