From 3601c5f09c17710a476c61ba9e2e3716acfc907c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20S=CC=8Ckoda?= Date: Sun, 30 Jun 2013 09:02:56 +0200 Subject: [PATCH] MDL-26943 implement subplugin support for local plugins --- lib/adminlib.php | 9 ++-- lib/classes/component.php | 88 +++++++++++++++++++++++++++++------- lib/pluginlib.php | 11 ++--- lib/tests/component_test.php | 15 ++++++ 4 files changed, 95 insertions(+), 28 deletions(-) diff --git a/lib/adminlib.php b/lib/adminlib.php index 79d06a8d719..1276b4172b7 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -128,14 +128,15 @@ function uninstall_plugin($type, $name) { // This may take a long time. @set_time_limit(0); - // recursively uninstall all module/editor subplugins first - if ($type === 'mod' || $type === 'editor') { - $base = get_component_directory($type . '_' . $name); + // Recursively uninstall all subplugins first. + $subplugintypes = core_component::get_plugin_types_with_subplugins(); + if (isset($subplugintypes[$type])) { + $base = core_component::get_plugin_directory($type, $name); if (file_exists("$base/db/subplugins.php")) { $subplugins = array(); include("$base/db/subplugins.php"); foreach ($subplugins as $subplugintype=>$dir) { - $instances = get_plugin_list($subplugintype); + $instances = core_component::get_plugin_list($subplugintype); foreach ($instances as $subpluginname => $notusedpluginpath) { uninstall_plugin($subplugintype, $subpluginname); } diff --git a/lib/classes/component.php b/lib/classes/component.php index 84f46a9b0fd..ee723b5acf3 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -29,7 +29,7 @@ class core_component { /** @var array list of ignored directories - watch out for auth/db exception */ protected static $ignoreddirs = array('CVS'=>true, '_vti_cnf'=>true, 'simpletest'=>true, 'db'=>true, 'yui'=>true, 'tests'=>true, 'classes'=>true); /** @var array list plugin types that support subplugins, do not add more here unless absolutely necessary */ - protected static $supportsubplugins = array('mod', 'editor'); + protected static $supportsubplugins = array('mod', 'editor', 'local'); /** @var null cache of plugin types */ protected static $plugintypes = null; @@ -326,29 +326,69 @@ $cache = '.var_export($cache, true).'; } foreach (self::$supportsubplugins as $type) { - $subpluginowners = self::fetch_plugins($type, $types[$type]); - foreach ($subpluginowners as $ownerdir) { - if (file_exists("$ownerdir/db/subplugins.php")) { - $subplugins = array(); - include("$ownerdir/db/subplugins.php"); - foreach ($subplugins as $subtype => $dir) { - if (!preg_match('/^[a-z][a-z0-9]*$/', $subtype)) { - error_log("Invalid subtype '$subtype'' detected in '$ownerdir', invalid characters present."); - continue; - } - if (isset(self::$subsystems[$subtype])) { - error_log("Invalid subtype '$subtype'' detected in '$ownerdir', duplicates core subsystem."); - continue; - } - $types[$subtype] = $CFG->dirroot.'/'.$dir; - } + if ($type === 'local') { + // Local subplugins must be after local plugins. + continue; + } + $subplugins = self::fetch_subplugins($type, $types[$type]); + foreach($subplugins as $subtype => $subplugin) { + if (isset($types[$subtype])) { + error_log("Invalid subtype '$subtype', duplicate detected."); + continue; } + $types[$subtype] = $subplugin; } } // Local is always last! $types['local'] = $CFG->dirroot.'/local'; + if (in_array('local', self::$supportsubplugins)) { + $subplugins = self::fetch_subplugins('local', $types['local']); + foreach($subplugins as $subtype => $subplugin) { + if (isset($types[$subtype])) { + error_log("Invalid subtype '$subtype', duplicate detected."); + continue; + } + $types[$subtype] = $subplugin; + } + } + + return $types; + } + + /** + * Returns list of subtypes defined in given plugin type. + * @param string $type + * @param string $fulldir + * @return array + */ + protected static function fetch_subplugins($type, $fulldir) { + global $CFG; + + $types = array(); + $subpluginowners = self::fetch_plugins($type, $fulldir); + foreach ($subpluginowners as $ownerdir) { + if (file_exists("$ownerdir/db/subplugins.php")) { + $subplugins = array(); + include("$ownerdir/db/subplugins.php"); + foreach ($subplugins as $subtype => $dir) { + if (!preg_match('/^[a-z][a-z0-9]*$/', $subtype)) { + error_log("Invalid subtype '$subtype'' detected in '$ownerdir', invalid characters present."); + continue; + } + if (isset(self::$subsystems[$subtype])) { + error_log("Invalid subtype '$subtype'' detected in '$ownerdir', duplicates core subsystem."); + continue; + } + if (!is_dir("$CFG->dirroot/$dir")) { + error_log("Invalid subtype directory '$dir' detected in '$ownerdir'."); + continue; + } + $types[$subtype] = "$CFG->dirroot/$dir"; + } + } + } return $types; } @@ -681,4 +721,18 @@ $cache = '.var_export($cache, true).'; return self::get_plugin_directory($type, $plugin); } + + /** + * Returns list of plugin types that allow subplugins. + * @return array as (string)plugintype => (string)fulldir + */ + public static function get_plugin_types_with_subplugins() { + self::init(); + + $return = array(); + foreach (self::$supportsubplugins as $type) { + $return[$type] = self::$plugintypes[$type]; + } + return $return; + } } diff --git a/lib/pluginlib.php b/lib/pluginlib.php index 8b0b32506c8..7a1192f6d7a 100644 --- a/lib/pluginlib.php +++ b/lib/pluginlib.php @@ -143,8 +143,8 @@ class plugin_manager { // Hack: include mod and editor subplugin management classes first, // the adminlib.php is supposed to contain extra admin settings too. require_once($CFG->libdir.'/adminlib.php'); - foreach(array('mod', 'editor') as $type) { - foreach (get_plugin_list($type) as $dir) { + foreach (core_component::get_plugin_types_with_subplugins() as $type => $ignored) { + foreach (core_component::get_plugin_list($type) as $dir) { if (file_exists("$dir/adminlib.php")) { include_once("$dir/adminlib.php"); } @@ -221,8 +221,6 @@ class plugin_manager { * Returns list of plugins that define their subplugins and the information * about them from the db/subplugins.php file. * - * At the moment, only activity modules and editors can define subplugins. - * * @param bool $disablecache force reload, cache can be used otherwise * @return array with keys like 'mod_quiz', and values the data from the * corresponding db/subplugins.php file. @@ -231,9 +229,8 @@ class plugin_manager { if ($disablecache or is_null($this->subpluginsinfo)) { $this->subpluginsinfo = array(); - foreach (array('mod', 'editor') as $type) { - $owners = get_plugin_list($type); - foreach ($owners as $component => $ownerdir) { + foreach (core_component::get_plugin_types_with_subplugins() as $type => $ignored) { + foreach (core_component::get_plugin_list($type) as $component => $ownerdir) { $componentsubplugins = array(); if (file_exists($ownerdir . '/db/subplugins.php')) { $subplugins = array(); diff --git a/lib/tests/component_test.php b/lib/tests/component_test.php index afdf3a6765e..3380b404657 100644 --- a/lib/tests/component_test.php +++ b/lib/tests/component_test.php @@ -327,4 +327,19 @@ class core_component_testcase extends advanced_testcase { $this->assertSame($fulldir, get_component_directory(('core_'.$subsystem))); } } + + public function test_get_plugin_types_with_subplugins() { + global $CFG; + + $types = core_component::get_plugin_types_with_subplugins(); + + // Hardcode it here to detect if anybody hacks the code to include more types. + $expected = array( + 'mod' => "$CFG->dirroot/mod", + 'editor' => "$CFG->dirroot/lib/editor", + 'local' => "$CFG->dirroot/local", + ); + + $this->assertSame($expected, $types); + } }