mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 08:55:15 +02:00
MDL-39854 reimplement Frankenstyle support and enable classloader
Better performance, more reliable, completely self contained, more validation and full backwards compatibility. This will also allow us to implement ignoring of plugins.
This commit is contained in:
parent
3d164e1ef4
commit
9e19a0f08b
@ -56,22 +56,18 @@ class tool_customlang_utils {
|
||||
|
||||
$list['moodle'] = 'core';
|
||||
|
||||
$coresubsystems = get_core_subsystems();
|
||||
$coresubsystems = core_component::get_core_subsystems();
|
||||
ksort($coresubsystems); // should be but just in case
|
||||
foreach ($coresubsystems as $name => $location) {
|
||||
if ($name != 'moodle.org') {
|
||||
$list[$name] = 'core_'.$name;
|
||||
}
|
||||
$list[$name] = 'core_'.$name;
|
||||
}
|
||||
|
||||
$plugintypes = get_plugin_types();
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
foreach ($plugintypes as $type => $location) {
|
||||
$pluginlist = get_plugin_list($type);
|
||||
$pluginlist = core_component::get_plugin_list($type);
|
||||
foreach ($pluginlist as $name => $ununsed) {
|
||||
if ($type == 'mod') {
|
||||
if (array_key_exists($name, $list)) {
|
||||
throw new Exception('Activity module and core subsystem name collision');
|
||||
}
|
||||
// Plugin names are now automatically validated.
|
||||
$list[$name] = $type.'_'.$name;
|
||||
} else {
|
||||
$list[$type.'_'.$name] = $type.'_'.$name;
|
||||
|
@ -575,7 +575,7 @@ class tool_installaddon_installer {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($plugintype, $pluginname) = normalize_component($data->component);
|
||||
list($plugintype, $pluginname) = core_component::normalize_component($data->component);
|
||||
|
||||
if ($plugintype === 'core') {
|
||||
return false;
|
||||
@ -585,6 +585,15 @@ class tool_installaddon_installer {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!core_component::is_valid_plugin_name($plugintype, $pluginname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
if (!isset($plugintypes[$plugintype])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep this regex in sync with the one used by the download.moodle.org/api/x.y/pluginfo.php
|
||||
if (!preg_match('/^[0-9]+$/', $data->version)) {
|
||||
return false;
|
||||
|
@ -124,6 +124,13 @@ class tool_installaddon_installer_test extends advanced_testcase {
|
||||
'version' => 2012123199,
|
||||
)));
|
||||
$this->assertSame(false, $installer->testable_decode_remote_request($request));
|
||||
|
||||
$request = base64_encode(json_encode(array(
|
||||
'name' => 'Bogus module name',
|
||||
'component' => 'mod_xxx_yyy',
|
||||
'version' => 2012123190,
|
||||
)));
|
||||
$this->assertSame(false, $installer->testable_decode_remote_request($request));
|
||||
}
|
||||
|
||||
public function test_move_directory() {
|
||||
|
@ -215,23 +215,28 @@ class XMLDBAction {
|
||||
* @return string PHP code to be used to mark a reached savepoint
|
||||
*/
|
||||
function upgrade_savepoint_php($structure) {
|
||||
global $CFG;
|
||||
|
||||
// NOTE: $CFG->admin !== 'admin' is not supported in XMLDB editor, sorry.
|
||||
|
||||
$path = $structure->getPath();
|
||||
$plugintype = 'error';
|
||||
|
||||
// Trim "db" from path
|
||||
$path = dirname($path);
|
||||
|
||||
// Get pluginname, plugindir and plugintype
|
||||
$pluginname = basename($path);
|
||||
if ($path == 'lib') { // exception for lib (not proper plugin)
|
||||
$plugindir = 'lib';
|
||||
if ($path === 'lib/db') {
|
||||
$plugintype = 'lib';
|
||||
} else { // rest of plugins
|
||||
// TODO: this is not nice and may fail, plugintype should be passed around somehow instead
|
||||
$plugintypes = get_plugin_types(false);
|
||||
$plugindir = dirname($path);
|
||||
$plugindir = str_replace('\\', '/', $plugindir);
|
||||
$plugintype = array_search($plugindir, $plugintypes);
|
||||
$pluginname = null;
|
||||
|
||||
} else {
|
||||
$path = dirname($path);
|
||||
$pluginname = basename($path);
|
||||
$path = dirname($path);
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
foreach ($plugintypes as $type => $fulldir) {
|
||||
if ($CFG->dirroot.'/'.$path === $fulldir) {
|
||||
$plugintype = $type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
@ -1697,12 +1697,12 @@ function matching_page_type_patterns($pagetype) {
|
||||
* @return array an array of all the page type patterns that might match this page type.
|
||||
*/
|
||||
function generate_page_type_patterns($pagetype, $parentcontext = null, $currentcontext = null) {
|
||||
global $CFG;
|
||||
global $CFG; // Required for includes bellow.
|
||||
|
||||
$bits = explode('-', $pagetype);
|
||||
|
||||
$core = get_core_subsystems();
|
||||
$plugins = get_plugin_types();
|
||||
$core = core_component::get_core_subsystems();
|
||||
$plugins = core_component::get_plugin_types();
|
||||
|
||||
//progressively strip pieces off the page type looking for a match
|
||||
$componentarray = null;
|
||||
@ -1712,7 +1712,7 @@ function generate_page_type_patterns($pagetype, $parentcontext = null, $currentc
|
||||
|
||||
// Check to see if the component is a core component
|
||||
if (array_key_exists($possiblecomponent, $core) && !empty($core[$possiblecomponent])) {
|
||||
$libfile = $CFG->dirroot.'/'.$core[$possiblecomponent].'/lib.php';
|
||||
$libfile = $core[$possiblecomponent].'/lib.php';
|
||||
if (file_exists($libfile)) {
|
||||
require_once($libfile);
|
||||
$function = $possiblecomponent.'_page_type_list';
|
||||
@ -1730,7 +1730,7 @@ function generate_page_type_patterns($pagetype, $parentcontext = null, $currentc
|
||||
//We've found a plugin type. Look for a plugin name by getting the next section of page type
|
||||
if (count($bits) > $i) {
|
||||
$pluginname = $bits[$i];
|
||||
$directory = get_plugin_directory($possiblecomponent, $pluginname);
|
||||
$directory = core_component::get_plugin_directory($possiblecomponent, $pluginname);
|
||||
if (!empty($directory)){
|
||||
$libfile = $directory.'/lib.php';
|
||||
if (file_exists($libfile)) {
|
||||
@ -1750,16 +1750,14 @@ function generate_page_type_patterns($pagetype, $parentcontext = null, $currentc
|
||||
|
||||
//we'll only get to here if we still don't have any patterns
|
||||
//the plugin type may have a callback
|
||||
$directory = get_plugin_directory($possiblecomponent, null);
|
||||
if (!empty($directory)){
|
||||
$libfile = $directory.'/lib.php';
|
||||
if (file_exists($libfile)) {
|
||||
require_once($libfile);
|
||||
$function = $possiblecomponent.'_page_type_list';
|
||||
if (function_exists($function)) {
|
||||
if ($patterns = $function($pagetype, $parentcontext, $currentcontext)) {
|
||||
break;
|
||||
}
|
||||
$directory = $plugins[$possiblecomponent];
|
||||
$libfile = $directory.'/lib.php';
|
||||
if (file_exists($libfile)) {
|
||||
require_once($libfile);
|
||||
$function = $possiblecomponent.'_page_type_list';
|
||||
if (function_exists($function)) {
|
||||
if ($patterns = $function($pagetype, $parentcontext, $currentcontext)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
682
lib/classes/component.php
Normal file
682
lib/classes/component.php
Normal file
@ -0,0 +1,682 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Components (core subsystems + plugins) related code.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collection of components related methods.
|
||||
*/
|
||||
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');
|
||||
|
||||
/** @var null cache of plugin types */
|
||||
protected static $plugintypes = null;
|
||||
/** @var null cache of plugin locations */
|
||||
protected static $plugins = null;
|
||||
/** @var null cache of core subsystems */
|
||||
protected static $subsystems = null;
|
||||
/** @var null list of all known classes that can be autoloaded */
|
||||
protected static $classmap = null;
|
||||
|
||||
/**
|
||||
* Class loader for Frankenstyle named classes in standard locations.
|
||||
* Frankenstyle namespaces are supported.
|
||||
*
|
||||
* The expected location for core classes is:
|
||||
* 1/ core_xx_yy_zz ---> lib/classes/xx_yy_zz.php
|
||||
* 2/ \core\xx_yy_zz ---> lib/classes/xx_yy_zz.php
|
||||
* 3/ \core\xx\yy_zz ---> lib/classes/xx/yy_zz.php
|
||||
*
|
||||
* The expected location for plugin classes is:
|
||||
* 1/ mod_name_xx_yy_zz ---> mod/name/classes/xx_yy_zz.php
|
||||
* 2/ \mod_name\xx_yy_zz ---> mod/name/classes/xx_yy_zz.php
|
||||
* 3/ \mod_name\xx\yy_zz ---> mod/name/classes/xx/yy_zz.php
|
||||
*
|
||||
* @param string $classname
|
||||
*/
|
||||
public static function classloader($classname) {
|
||||
self::init();
|
||||
|
||||
if (isset(self::$classmap[$classname])) {
|
||||
// Global $CFG is expected in included scripts.
|
||||
global $CFG;
|
||||
// Function include would be faster, but for BC it is better to include only once.
|
||||
include_once(self::$classmap[$classname]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise caches, always call before accessing self:: caches.
|
||||
*/
|
||||
protected static function init() {
|
||||
global $CFG;
|
||||
|
||||
// Init only once per request/CLI execution, we ignore changes done afterwards.
|
||||
if (isset(self::$plugintypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PHPUNIT_TEST or !empty($CFG->early_install_lang)) {
|
||||
// 1/ Do not bother storing the file for unit tests,
|
||||
// we need fresh copy for each execution and
|
||||
// later we keep it in memory.
|
||||
// 2/ We can not write to dataroot in installer yet.
|
||||
self::fill_all_caches();
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: cachedir MUST be shared by all servers in a cluster, sorry guys...
|
||||
// MUC should use classloading, we can not depend on it here.
|
||||
$cachefile = "$CFG->cachedir/core_component.php";
|
||||
|
||||
if (!CACHE_DISABLE_ALL and !self::is_developer()) {
|
||||
// 1/ Use the cache only outside of install and upgrade.
|
||||
// 2/ Let developers add/remove classes in developer mode.
|
||||
if (is_readable($cachefile)) {
|
||||
$cache = false;
|
||||
include($cachefile);
|
||||
if (!is_array($cache)) {
|
||||
// Something is very wrong.
|
||||
} else if (!isset($cache['plugintypes']) or !isset($cache['plugins']) or !isset($cache['subsystems']) or !isset($cache['classmap'])) {
|
||||
// Something is very wrong.
|
||||
} else if ($cache['plugintypes']['mod'] !== "$CFG->dirroot/mod") {
|
||||
// Dirroot was changed.
|
||||
} else {
|
||||
// The cache looks ok, let's use it.
|
||||
self::$plugintypes = $cache['plugintypes'];
|
||||
self::$plugins = $cache['plugins'];
|
||||
self::$subsystems = $cache['subsystems'];
|
||||
self::$classmap = $cache['classmap'];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cachedir = dirname($cachefile);
|
||||
if (!is_dir($cachedir)) {
|
||||
mkdir($cachedir, $CFG->directorypermissions, true);
|
||||
}
|
||||
|
||||
if (!isset(self::$plugintypes)) {
|
||||
self::fill_all_caches();
|
||||
|
||||
// This needs to be atomic and self-fixing as much as possible.
|
||||
|
||||
$content = self::get_cache_content();
|
||||
if (file_exists($cachefile)) {
|
||||
if (sha1_file($cachefile) === sha1($content)) {
|
||||
return;
|
||||
}
|
||||
unlink($cachefile);
|
||||
}
|
||||
|
||||
if ($fp = @fopen($cachefile.'.tmp', 'xb')) {
|
||||
fwrite($fp, $content);
|
||||
fclose($fp);
|
||||
@rename($cachefile.'.tmp', $cachefile);
|
||||
@chmod($cachefile, $CFG->filepermissions);
|
||||
}
|
||||
@unlink($cachefile.'.tmp'); // Just in case anything fails (race condition).
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we in developer debug mode?
|
||||
*
|
||||
* Note: You need to set "$CFG->debug = (E_ALL | E_STRICT);" in config.php,
|
||||
* the reason is we need to use this before we setup DB connection or caches for CFG.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_developer() {
|
||||
global $CFG;
|
||||
|
||||
if (!isset($CFG->config_php_settings['debug'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$debug = (int)$CFG->config_php_settings['debug'];
|
||||
if ($debug & E_ALL and $debug & E_STRICT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create cache file content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_cache_content() {
|
||||
$cache = array(
|
||||
'subsystems' => var_export(self::$subsystems, true),
|
||||
'plugintypes' => var_export(self::$plugintypes, true),
|
||||
'plugins' => var_export(self::$plugins, true),
|
||||
'classmap' => var_export(self::$classmap, true),
|
||||
);
|
||||
|
||||
return '<?php
|
||||
$cache = '.var_export($cache, true).';
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all caches.
|
||||
*/
|
||||
protected static function fill_all_caches() {
|
||||
self::$subsystems = self::fetch_subsystems();
|
||||
|
||||
self::$plugintypes = self::fetch_plugintypes();
|
||||
|
||||
self::$plugins = array();
|
||||
foreach (self::$plugintypes as $type => $fulldir) {
|
||||
self::$plugins[$type] = self::fetch_plugins($type, $fulldir);
|
||||
}
|
||||
|
||||
self::fill_classmap_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of core subsystems.
|
||||
* @return array
|
||||
*/
|
||||
protected static function fetch_subsystems() {
|
||||
global $CFG;
|
||||
|
||||
// NOTE: Any additions here must be verified to not collide with existing add-on modules and subplugins!!!
|
||||
|
||||
$info = array(
|
||||
'access' => null,
|
||||
'admin' => $CFG->dirroot.'/'.$CFG->admin,
|
||||
'auth' => $CFG->dirroot.'/auth',
|
||||
'backup' => $CFG->dirroot.'/backup/util/ui',
|
||||
'badges' => $CFG->dirroot.'/badges',
|
||||
'block' => $CFG->dirroot.'/blocks',
|
||||
'blog' => $CFG->dirroot.'/blog',
|
||||
'bulkusers' => null,
|
||||
'cache' => $CFG->dirroot.'/cache',
|
||||
'calendar' => $CFG->dirroot.'/calendar',
|
||||
'cohort' => $CFG->dirroot.'/cohort',
|
||||
'condition' => null,
|
||||
'completion' => null,
|
||||
'countries' => null,
|
||||
'course' => $CFG->dirroot.'/course',
|
||||
'currencies' => null,
|
||||
'dbtransfer' => null,
|
||||
'debug' => null,
|
||||
'dock' => null,
|
||||
'editor' => $CFG->dirroot.'/lib/editor',
|
||||
'edufields' => null,
|
||||
'enrol' => $CFG->dirroot.'/enrol',
|
||||
'error' => null,
|
||||
'filepicker' => null,
|
||||
'files' => $CFG->dirroot.'/files',
|
||||
'filters' => null,
|
||||
//'fonts' => null, // Bogus.
|
||||
'form' => $CFG->dirroot.'/lib/form',
|
||||
'grades' => $CFG->dirroot.'/grade',
|
||||
'grading' => $CFG->dirroot.'/grade/grading',
|
||||
'group' => $CFG->dirroot.'/group',
|
||||
'help' => null,
|
||||
'hub' => null,
|
||||
'imscc' => null,
|
||||
'install' => null,
|
||||
'iso6392' => null,
|
||||
'langconfig' => null,
|
||||
'license' => null,
|
||||
'mathslib' => null,
|
||||
'media' => null,
|
||||
'message' => $CFG->dirroot.'/message',
|
||||
'mimetypes' => null,
|
||||
'mnet' => $CFG->dirroot.'/mnet',
|
||||
//'moodle.org' => null, // Not used any more.
|
||||
'my' => $CFG->dirroot.'/my',
|
||||
'notes' => $CFG->dirroot.'/notes',
|
||||
'pagetype' => null,
|
||||
'pix' => null,
|
||||
'plagiarism' => $CFG->dirroot.'/plagiarism',
|
||||
'plugin' => null,
|
||||
'portfolio' => $CFG->dirroot.'/portfolio',
|
||||
'publish' => $CFG->dirroot.'/course/publish',
|
||||
'question' => $CFG->dirroot.'/question',
|
||||
'rating' => $CFG->dirroot.'/rating',
|
||||
'register' => $CFG->dirroot.'/'.$CFG->admin.'/registration', // Broken badly if $CFG->admin changed.
|
||||
'repository' => $CFG->dirroot.'/repository',
|
||||
'rss' => $CFG->dirroot.'/rss',
|
||||
'role' => $CFG->dirroot.'/'.$CFG->admin.'/roles',
|
||||
'search' => null,
|
||||
'table' => null,
|
||||
'tag' => $CFG->dirroot.'/tag',
|
||||
'timezones' => null,
|
||||
'user' => $CFG->dirroot.'/user',
|
||||
'userkey' => null,
|
||||
'webservice' => $CFG->dirroot.'/webservice',
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of known plugin types.
|
||||
* @return array
|
||||
*/
|
||||
protected static function fetch_plugintypes() {
|
||||
global $CFG;
|
||||
|
||||
$types = array(
|
||||
'qtype' => $CFG->dirroot.'/question/type',
|
||||
'mod' => $CFG->dirroot.'/mod',
|
||||
'auth' => $CFG->dirroot.'/auth',
|
||||
'enrol' => $CFG->dirroot.'/enrol',
|
||||
'message' => $CFG->dirroot.'/message/output',
|
||||
'block' => $CFG->dirroot.'/blocks',
|
||||
'filter' => $CFG->dirroot.'/filter',
|
||||
'editor' => $CFG->dirroot.'/lib/editor',
|
||||
'format' => $CFG->dirroot.'/course/format',
|
||||
'profilefield' => $CFG->dirroot.'/user/profile/field',
|
||||
'report' => $CFG->dirroot.'/report',
|
||||
'coursereport' => $CFG->dirroot.'/course/report', // Must be after system reports.
|
||||
'gradeexport' => $CFG->dirroot.'/grade/export',
|
||||
'gradeimport' => $CFG->dirroot.'/grade/import',
|
||||
'gradereport' => $CFG->dirroot.'/grade/report',
|
||||
'gradingform' => $CFG->dirroot.'/grade/grading/form',
|
||||
'mnetservice' => $CFG->dirroot.'/mnet/service',
|
||||
'webservice' => $CFG->dirroot.'/webservice',
|
||||
'repository' => $CFG->dirroot.'/repository',
|
||||
'portfolio' => $CFG->dirroot.'/portfolio',
|
||||
'qbehaviour' => $CFG->dirroot.'/question/behaviour',
|
||||
'qformat' => $CFG->dirroot.'/question/format',
|
||||
'plagiarism' => $CFG->dirroot.'/plagiarism',
|
||||
'tool' => $CFG->dirroot.'/'.$CFG->admin.'/tool',
|
||||
'cachestore' => $CFG->dirroot.'/cache/stores',
|
||||
'cachelock' => $CFG->dirroot.'/cache/locks',
|
||||
|
||||
);
|
||||
|
||||
if (!empty($CFG->themedir) and is_dir($CFG->themedir) ) {
|
||||
$types['theme'] = $CFG->themedir;
|
||||
} else {
|
||||
$types['theme'] = $CFG->dirroot.'/theme';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Local is always last!
|
||||
$types['local'] = $CFG->dirroot.'/local';
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of plugins of given type in given directory.
|
||||
* @param string $plugintype
|
||||
* @param string $fulldir
|
||||
* @return array
|
||||
*/
|
||||
protected static function fetch_plugins($plugintype, $fulldir) {
|
||||
global $CFG;
|
||||
|
||||
$fulldirs = (array)$fulldir;
|
||||
if ($plugintype === 'theme') {
|
||||
if (realpath($fulldir) !== realpath($CFG->dirroot.'/theme')) {
|
||||
// Include themes in standard location too.
|
||||
array_unshift($fulldirs, $CFG->dirroot.'/theme');
|
||||
}
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($fulldirs as $fulldir) {
|
||||
if (!is_dir($fulldir)) {
|
||||
continue;
|
||||
}
|
||||
$items = new \DirectoryIterator($fulldir);
|
||||
foreach ($items as $item) {
|
||||
if ($item->isDot() or !$item->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$pluginname = $item->getFilename();
|
||||
if ($plugintype === 'auth' and $pluginname === 'db') {
|
||||
// Special exception for this wrong plugin name.
|
||||
} else if (isset(self::$ignoreddirs[$pluginname])) {
|
||||
continue;
|
||||
}
|
||||
if (!self::is_valid_plugin_name($plugintype, $pluginname)) {
|
||||
// Always ignore plugins with problematic names here.
|
||||
continue;
|
||||
}
|
||||
$result[$pluginname] = $fulldir.'/'.$pluginname;
|
||||
unset($item);
|
||||
}
|
||||
unset($items);
|
||||
}
|
||||
|
||||
ksort($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all classes that can be autoloaded including frankenstyle namespaces.
|
||||
*/
|
||||
protected static function fill_classmap_cache() {
|
||||
global $CFG;
|
||||
|
||||
self::$classmap = array();
|
||||
|
||||
self::load_classes('core', "$CFG->dirroot/lib/classes");
|
||||
|
||||
foreach (self::$subsystems as $subsystem => $fulldir) {
|
||||
self::load_classes('core_'.$subsystem, "$fulldir/classes");
|
||||
}
|
||||
|
||||
foreach (self::$plugins as $plugintype => $plugins) {
|
||||
foreach ($plugins as $pluginname => $fulldir) {
|
||||
self::load_classes($plugintype.'_'.$pluginname, "$fulldir/classes");
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Add a few extra legacy classes here if necessary.
|
||||
//self::$classmap['textlib'] = "$CFG->dirroot/lib/textlib.class.php";
|
||||
//self::$classmap['collatorlib'] = "$CFG->dirroot/lib/textlib.class.php";
|
||||
}
|
||||
|
||||
/**
|
||||
* Find classes in directory and recurse to subdirs.
|
||||
* @param string $component
|
||||
* @param string $fulldir
|
||||
* @param string $namespace
|
||||
*/
|
||||
protected static function load_classes($component, $fulldir, $namespace = '') {
|
||||
if (!is_dir($fulldir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$items = new \DirectoryIterator($fulldir);
|
||||
foreach ($items as $item) {
|
||||
if ($item->isDot()) {
|
||||
continue;
|
||||
}
|
||||
if ($item->isDir()) {
|
||||
$dirname = $item->getFilename();
|
||||
self::load_classes($component, "$fulldir/$dirname", $namespace.'\\'.$dirname);
|
||||
continue;
|
||||
}
|
||||
|
||||
$filename = $item->getFilename();
|
||||
$classname = preg_replace('/\.php$/', '', $filename);
|
||||
|
||||
if ($filename === $classname) {
|
||||
// Not a php file.
|
||||
continue;
|
||||
}
|
||||
if ($namespace === '') {
|
||||
// Legacy long frankenstyle class name.
|
||||
self::$classmap[$component.'_'.$classname] = "$fulldir/$filename";
|
||||
}
|
||||
// New namespaced classes.
|
||||
self::$classmap[$component.$namespace.'\\'.$classname] = "$fulldir/$filename";
|
||||
}
|
||||
unset($item);
|
||||
unset($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all core subsystems and their location
|
||||
*
|
||||
* This is a whitelist of components that are part of the core and their
|
||||
* language strings are defined in /lang/en/<<subsystem>>.php. If a given
|
||||
* plugin is not listed here and it does not have proper plugintype prefix,
|
||||
* then it is considered as course activity module.
|
||||
*
|
||||
* The location is absolute file path to dir. NULL means there is no special
|
||||
* directory for this subsystem. If the location is set, the subsystem's
|
||||
* renderer.php is expected to be there.
|
||||
*
|
||||
* @return array of (string)name => (string|null)full dir location
|
||||
*/
|
||||
public static function get_core_subsystems() {
|
||||
self::init();
|
||||
return self::$subsystems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available plugin types together with their location.
|
||||
*
|
||||
* @return array as (string)plugintype => (string)fulldir
|
||||
*/
|
||||
public static function get_plugin_types() {
|
||||
self::init();
|
||||
return self::$plugintypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of plugins of given type.
|
||||
*
|
||||
* @param string $plugintype
|
||||
* @return array as (string)pluginname => (string)fulldir
|
||||
*/
|
||||
public static function get_plugin_list($plugintype) {
|
||||
self::init();
|
||||
|
||||
if (!isset(self::$plugins[$plugintype])) {
|
||||
return array();
|
||||
}
|
||||
return self::$plugins[$plugintype];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all the plugins of a given type that define a certain class
|
||||
* in a certain file. The plugin component names and class names are returned.
|
||||
*
|
||||
* @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
|
||||
* @param string $class the part of the name of the class after the
|
||||
* frankenstyle prefix. e.g 'thing' if you are looking for classes with
|
||||
* names like report_courselist_thing. If you are looking for classes with
|
||||
* the same name as the plugin name (e.g. qtype_multichoice) then pass ''.
|
||||
* Frankenstyle namespaces are also supported.
|
||||
* @param string $file the name of file within the plugin that defines the class.
|
||||
* @return array with frankenstyle plugin names as keys (e.g. 'report_courselist', 'mod_forum')
|
||||
* and the class names as values (e.g. 'report_courselist_thing', 'qtype_multichoice').
|
||||
*/
|
||||
public static function get_plugin_list_with_class($plugintype, $class, $file = null) {
|
||||
global $CFG; // Necessary in case it is referenced by included PHP scripts.
|
||||
|
||||
if ($class) {
|
||||
$suffix = '_' . $class;
|
||||
} else {
|
||||
$suffix = '';
|
||||
}
|
||||
|
||||
$pluginclasses = array();
|
||||
$plugins = self::get_plugin_list($plugintype);
|
||||
foreach ($plugins as $plugin => $fulldir) {
|
||||
// Try class in frankenstyle namespace.
|
||||
if ($class) {
|
||||
$classname = '\\' . $plugintype . '_' . $plugin . '\\' . $class;
|
||||
if (class_exists($classname, true)) {
|
||||
$pluginclasses[$plugintype . '_' . $plugin] = $classname;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Try autoloading of class with frankenstyle prefix.
|
||||
$classname = $plugintype . '_' . $plugin . $suffix;
|
||||
if (class_exists($classname, true)) {
|
||||
$pluginclasses[$plugintype . '_' . $plugin] = $classname;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fall back to old file location and class name.
|
||||
if ($file and file_exists("$fulldir/$file")) {
|
||||
include_once("$fulldir/$file");
|
||||
if (class_exists($classname, false)) {
|
||||
$pluginclasses[$plugintype . '_' . $plugin] = $classname;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pluginclasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact absolute path to plugin directory.
|
||||
*
|
||||
* @param string $plugintype type of plugin
|
||||
* @param string $pluginname name of the plugin
|
||||
* @return string full path to plugin directory; null if not found
|
||||
*/
|
||||
public static function get_plugin_directory($plugintype, $pluginname) {
|
||||
if (empty($pluginname)) {
|
||||
// Invalid plugin name, sorry.
|
||||
return null;
|
||||
}
|
||||
|
||||
self::init();
|
||||
|
||||
if (!isset(self::$plugins[$plugintype][$pluginname])) {
|
||||
return null;
|
||||
}
|
||||
return self::$plugins[$plugintype][$pluginname];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact absolute path to plugin directory.
|
||||
*
|
||||
* @param string $subsystem type of core subsystem
|
||||
* @return string full path to subsystem directory; null if not found
|
||||
*/
|
||||
public static function get_subsystem_directory($subsystem) {
|
||||
self::init();
|
||||
|
||||
if (!isset(self::$subsystems[$subsystem])) {
|
||||
return null;
|
||||
}
|
||||
return self::$subsystems[$subsystem];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates a plug name. It is much faster than calling clean_param.
|
||||
*
|
||||
* @param string $plugintype type of plugin
|
||||
* @param string $pluginname a string that might be a plugin name.
|
||||
* @return bool if this string is a valid plugin name.
|
||||
*/
|
||||
public static function is_valid_plugin_name($plugintype, $pluginname) {
|
||||
if ($plugintype === 'mod') {
|
||||
// Modules must not have the same name as core subsystems.
|
||||
if (!isset(self::$subsystems)) {
|
||||
// Watch out, this is called from init!
|
||||
self::init();
|
||||
}
|
||||
if (isset(self::$subsystems[$pluginname])) {
|
||||
return false;
|
||||
}
|
||||
// Modules MUST NOT have any underscores,
|
||||
// component normalisation would break very badly otherwise!
|
||||
return (bool)preg_match('/^[a-z][a-z0-9]*$/', $pluginname);
|
||||
|
||||
} else {
|
||||
return (bool)preg_match('/^[a-z](?:[a-z0-9_](?!__))*[a-z0-9]$/', $pluginname);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the component name using the "frankenstyle" rules.
|
||||
*
|
||||
* Note: this does not verify the validity of plugin or type names.
|
||||
*
|
||||
* @param string $component
|
||||
* @return array as (string)$type => (string)$plugin
|
||||
*/
|
||||
public static function normalize_component($component) {
|
||||
if ($component === 'moodle' or $component === 'core' or $component === '') {
|
||||
return array('core', null);
|
||||
}
|
||||
|
||||
if (strpos($component, '_') === false) {
|
||||
self::init();
|
||||
if (array_key_exists($component, self::$subsystems)) {
|
||||
$type = 'core';
|
||||
$plugin = $component;
|
||||
} else {
|
||||
// Everything else without underscore is a module.
|
||||
$type = 'mod';
|
||||
$plugin = $component;
|
||||
}
|
||||
|
||||
} else {
|
||||
list($type, $plugin) = explode('_', $component, 2);
|
||||
if ($type === 'moodle') {
|
||||
$type = 'core';
|
||||
}
|
||||
// Any unknown type must be a subplugin.
|
||||
}
|
||||
|
||||
return array($type, $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return exact absolute path to a plugin directory.
|
||||
*
|
||||
* @param string $component name such as 'moodle', 'mod_forum'
|
||||
* @return string full path to component directory; NULL if not found
|
||||
*/
|
||||
public static function get_component_directory($component) {
|
||||
global $CFG;
|
||||
|
||||
list($type, $plugin) = self::normalize_component($component);
|
||||
|
||||
if ($type === 'core') {
|
||||
if ($plugin === null) {
|
||||
return $path = $CFG->libdir;
|
||||
}
|
||||
return self::get_subsystem_directory($plugin);
|
||||
}
|
||||
|
||||
return self::get_plugin_directory($type, $plugin);
|
||||
}
|
||||
}
|
@ -120,29 +120,6 @@ $definitions = array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
),
|
||||
|
||||
// Cache for the list of known plugin and subplugin types - {@see get_plugin_types()}.
|
||||
// Contains two arrays of (string)pluginname => (string)location. The first array with
|
||||
// the key 0 contains locations relative to $CFG->dirroot. The second array with the
|
||||
// key 1 contains absolute paths.
|
||||
'plugintypes' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true, // 0 or 1 depending on the requested location type.
|
||||
'simpledata' => true, // Array of strings.
|
||||
'persistent' => true, // Likely there will be a couple of calls to this.
|
||||
'persistmaxsize' => 2, // Both arrays should stay loaded in memory.
|
||||
),
|
||||
|
||||
// Cache for the list of installed plugins - {@see get_plugin_list()}.
|
||||
// The key consists of the plugin type string (e.g. mod, block, enrol etc).
|
||||
// The value is an associative array of plugin name => plugin location.
|
||||
'pluginlist' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'simpledata' => true,
|
||||
'persistent' => true,
|
||||
'persistentmaxsize' => 2,
|
||||
),
|
||||
|
||||
// Cache used by the {@link plugininfo_base} class.
|
||||
'plugininfo_base' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
|
@ -30,6 +30,176 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* List all core subsystems and their location
|
||||
*
|
||||
* This is a whitelist of components that are part of the core and their
|
||||
* language strings are defined in /lang/en/<<subsystem>>.php. If a given
|
||||
* plugin is not listed here and it does not have proper plugintype prefix,
|
||||
* then it is considered as course activity module.
|
||||
*
|
||||
* The location is optionally dirroot relative path. NULL means there is no special
|
||||
* directory for this subsystem. If the location is set, the subsystem's
|
||||
* renderer.php is expected to be there.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::get_core_subsystems()
|
||||
*
|
||||
* @param bool $fullpaths false means relative paths from dirroot, use true for performance reasons
|
||||
* @return array of (string)name => (string|null)location
|
||||
*/
|
||||
function get_core_subsystems($fullpaths = false) {
|
||||
global $CFG;
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
|
||||
if ($fullpaths) {
|
||||
return $subsystems;
|
||||
}
|
||||
|
||||
debugging('Short paths are deprecated when using get_core_subsystems(), please fix the code to use fullpaths instead.', DEBUG_DEVELOPER);
|
||||
|
||||
$dlength = strlen($CFG->dirroot);
|
||||
|
||||
foreach ($subsystems as $k => $v) {
|
||||
if ($v === null) {
|
||||
continue;
|
||||
}
|
||||
$subsystems[$k] = substr($v, $dlength+1);
|
||||
}
|
||||
|
||||
return $subsystems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all plugin types.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::get_plugin_types()
|
||||
*
|
||||
* @param bool $fullpaths false means relative paths from dirroot
|
||||
* @return array Array of strings - name=>location
|
||||
*/
|
||||
function get_plugin_types($fullpaths = true) {
|
||||
global $CFG;
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
$types = core_component::get_plugin_types();
|
||||
|
||||
if ($fullpaths) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
debugging('Short paths are deprecated when using get_plugin_types(), please fix the code to use fullpaths instead.', DEBUG_DEVELOPER);
|
||||
|
||||
$dlength = strlen($CFG->dirroot);
|
||||
|
||||
foreach ($types as $k => $v) {
|
||||
if ($k === 'theme') {
|
||||
$types[$k] = 'theme';
|
||||
continue;
|
||||
}
|
||||
$types[$k] = substr($v, $dlength+1);
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use when listing real plugins of one type.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::get_plugin_list()
|
||||
*
|
||||
* @param string $plugintype type of plugin
|
||||
* @return array name=>fulllocation pairs of plugins of given type
|
||||
*/
|
||||
function get_plugin_list($plugintype) {
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
if ($plugintype === '') {
|
||||
$plugintype = 'mod';
|
||||
}
|
||||
|
||||
return core_component::get_plugin_list($plugintype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all the plugins of a given type that define a certain class
|
||||
* in a certain file. The plugin component names and class names are returned.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::get_plugin_list_with_class()
|
||||
*
|
||||
* @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
|
||||
* @param string $class the part of the name of the class after the
|
||||
* frankenstyle prefix. e.g 'thing' if you are looking for classes with
|
||||
* names like report_courselist_thing. If you are looking for classes with
|
||||
* the same name as the plugin name (e.g. qtype_multichoice) then pass ''.
|
||||
* @param string $file the name of file within the plugin that defines the class.
|
||||
* @return array with frankenstyle plugin names as keys (e.g. 'report_courselist', 'mod_forum')
|
||||
* and the class names as values (e.g. 'report_courselist_thing', 'qtype_multichoice').
|
||||
*/
|
||||
function get_plugin_list_with_class($plugintype, $class, $file) {
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
return core_component::get_plugin_list_with_class($plugintype, $class, $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact absolute path to plugin directory.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::get_plugin_directory()
|
||||
*
|
||||
* @param string $plugintype type of plugin
|
||||
* @param string $name name of the plugin
|
||||
* @return string full path to plugin directory; NULL if not found
|
||||
*/
|
||||
function get_plugin_directory($plugintype, $name) {
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
if ($plugintype === '') {
|
||||
$plugintype = 'mod';
|
||||
}
|
||||
|
||||
return core_component::get_plugin_directory($plugintype, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the component name using the "frankenstyle" names.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::normalize_component()
|
||||
*
|
||||
* @param string $component
|
||||
* @return array as (string)$type => (string)$plugin
|
||||
*/
|
||||
function normalize_component($component) {
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
return core_component::normalize_component($component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return exact absolute path to a plugin directory.
|
||||
*
|
||||
* @deprecated since 2.6, use core_component::normalize_component()
|
||||
*
|
||||
* @param string $component name such as 'moodle', 'mod_forum'
|
||||
* @return string full path to component directory; NULL if not found
|
||||
*/
|
||||
function get_component_directory($component) {
|
||||
|
||||
// NOTE: do not add any other debugging here, keep forever.
|
||||
|
||||
return core_component::get_component_directory($component);
|
||||
}
|
||||
|
||||
|
||||
// === Deprecated before 2.6.0 ===
|
||||
|
||||
/**
|
||||
* Hack to find out the GD version by parsing phpinfo output
|
||||
*
|
||||
|
@ -4830,7 +4830,7 @@ function remove_course_contents($courseid, $showfeedback = true, array $options
|
||||
|
||||
// Delete every instance of every module,
|
||||
// this has to be done before deleting of course level stuff
|
||||
$locations = get_plugin_list('mod');
|
||||
$locations = core_component::get_plugin_list('mod');
|
||||
foreach ($locations as $modname=>$moddir) {
|
||||
if ($modname === 'NEWMODULE') {
|
||||
continue;
|
||||
@ -6733,7 +6733,7 @@ class core_string_manager implements string_manager {
|
||||
public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) {
|
||||
global $CFG;
|
||||
|
||||
list($plugintype, $pluginname) = normalize_component($component);
|
||||
list($plugintype, $pluginname) = core_component::normalize_component($component);
|
||||
if ($plugintype == 'core' and is_null($pluginname)) {
|
||||
$component = 'core';
|
||||
} else {
|
||||
@ -6781,7 +6781,7 @@ class core_string_manager implements string_manager {
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!$location = get_plugin_directory($plugintype, $pluginname) or !is_dir($location)) {
|
||||
if (!$location = core_component::get_plugin_directory($plugintype, $pluginname) or !is_dir($location)) {
|
||||
return array();
|
||||
}
|
||||
if ($plugintype === 'mod') {
|
||||
@ -6912,13 +6912,13 @@ class core_string_manager implements string_manager {
|
||||
}
|
||||
if (!isset($string[$identifier])) {
|
||||
// the string is still missing - should be fixed by developer
|
||||
list($plugintype, $pluginname) = normalize_component($component);
|
||||
list($plugintype, $pluginname) = core_component::normalize_component($component);
|
||||
if ($plugintype == 'core') {
|
||||
$file = "lang/en/{$component}.php";
|
||||
} else if ($plugintype == 'mod') {
|
||||
$file = "mod/{$pluginname}/lang/en/{$pluginname}.php";
|
||||
} else {
|
||||
$path = get_plugin_directory($plugintype, $pluginname);
|
||||
$path = core_component::get_plugin_directory($plugintype, $pluginname);
|
||||
$file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php";
|
||||
}
|
||||
debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " .
|
||||
@ -7703,7 +7703,7 @@ function get_list_of_themes() {
|
||||
if (!empty($CFG->themelist)) { // use admin's list of themes
|
||||
$themelist = explode(',', $CFG->themelist);
|
||||
} else {
|
||||
$themelist = array_keys(get_plugin_list("theme"));
|
||||
$themelist = array_keys(core_component::get_plugin_list("theme"));
|
||||
}
|
||||
|
||||
foreach ($themelist as $key => $themename) {
|
||||
@ -8039,374 +8039,14 @@ function endecrypt ($pwd, $data, $case) {
|
||||
|
||||
/// ENVIRONMENT CHECKING ////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Returns the exact absolute path to plugin directory.
|
||||
*
|
||||
* @param string $plugintype type of plugin
|
||||
* @param string $name name of the plugin
|
||||
* @return string full path to plugin directory; NULL if not found
|
||||
*/
|
||||
function get_plugin_directory($plugintype, $name) {
|
||||
global $CFG;
|
||||
|
||||
if ($plugintype === '') {
|
||||
$plugintype = 'mod';
|
||||
}
|
||||
|
||||
$types = get_plugin_types(true);
|
||||
if (!array_key_exists($plugintype, $types)) {
|
||||
return NULL;
|
||||
}
|
||||
$name = clean_param($name, PARAM_SAFEDIR); // just in case ;-)
|
||||
|
||||
if (!empty($CFG->themedir) and $plugintype === 'theme') {
|
||||
if (!is_dir($types['theme'] . '/' . $name)) {
|
||||
// ok, so the theme is supposed to be in the $CFG->themedir
|
||||
return $CFG->themedir . '/' . $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $types[$plugintype].'/'.$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return exact absolute path to a plugin directory.
|
||||
*
|
||||
* @param string $component name such as 'moodle', 'mod_forum'
|
||||
* @return string full path to component directory; NULL if not found
|
||||
*/
|
||||
function get_component_directory($component) {
|
||||
global $CFG;
|
||||
|
||||
list($type, $plugin) = normalize_component($component);
|
||||
|
||||
if ($type === 'core') {
|
||||
if ($plugin === NULL ) {
|
||||
$path = $CFG->libdir;
|
||||
} else {
|
||||
$subsystems = get_core_subsystems();
|
||||
if (isset($subsystems[$plugin])) {
|
||||
$path = $CFG->dirroot.'/'.$subsystems[$plugin];
|
||||
} else {
|
||||
$path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$path = get_plugin_directory($type, $plugin);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the component name using the "frankenstyle" names.
|
||||
* @param string $component
|
||||
* @return array $type+$plugin elements
|
||||
*/
|
||||
function normalize_component($component) {
|
||||
if ($component === 'moodle' or $component === 'core') {
|
||||
$type = 'core';
|
||||
$plugin = NULL;
|
||||
|
||||
} else if (strpos($component, '_') === false) {
|
||||
$subsystems = get_core_subsystems();
|
||||
if (array_key_exists($component, $subsystems)) {
|
||||
$type = 'core';
|
||||
$plugin = $component;
|
||||
} else {
|
||||
// everything else is a module
|
||||
$type = 'mod';
|
||||
$plugin = $component;
|
||||
}
|
||||
|
||||
} else {
|
||||
list($type, $plugin) = explode('_', $component, 2);
|
||||
$plugintypes = get_plugin_types(false);
|
||||
if ($type !== 'core' and !array_key_exists($type, $plugintypes)) {
|
||||
$type = 'mod';
|
||||
$plugin = $component;
|
||||
}
|
||||
}
|
||||
|
||||
return array($type, $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all core subsystems and their location
|
||||
*
|
||||
* This is a whitelist of components that are part of the core and their
|
||||
* language strings are defined in /lang/en/<<subsystem>>.php. If a given
|
||||
* plugin is not listed here and it does not have proper plugintype prefix,
|
||||
* then it is considered as course activity module.
|
||||
*
|
||||
* The location is dirroot relative path. NULL means there is no special
|
||||
* directory for this subsystem. If the location is set, the subsystem's
|
||||
* renderer.php is expected to be there.
|
||||
*
|
||||
* @return array of (string)name => (string|null)location
|
||||
*/
|
||||
function get_core_subsystems() {
|
||||
global $CFG;
|
||||
|
||||
static $info = null;
|
||||
|
||||
if (!$info) {
|
||||
$info = array(
|
||||
'access' => NULL,
|
||||
'admin' => $CFG->admin,
|
||||
'auth' => 'auth',
|
||||
'backup' => 'backup/util/ui',
|
||||
'badges' => 'badges',
|
||||
'block' => 'blocks',
|
||||
'blog' => 'blog',
|
||||
'bulkusers' => NULL,
|
||||
'cache' => 'cache',
|
||||
'calendar' => 'calendar',
|
||||
'cohort' => 'cohort',
|
||||
'condition' => NULL,
|
||||
'completion' => NULL,
|
||||
'countries' => NULL,
|
||||
'course' => 'course',
|
||||
'currencies' => NULL,
|
||||
'dbtransfer' => NULL,
|
||||
'debug' => NULL,
|
||||
'dock' => NULL,
|
||||
'editor' => 'lib/editor',
|
||||
'edufields' => NULL,
|
||||
'enrol' => 'enrol',
|
||||
'error' => NULL,
|
||||
'filepicker' => NULL,
|
||||
'files' => 'files',
|
||||
'filters' => NULL,
|
||||
'fonts' => NULL,
|
||||
'form' => 'lib/form',
|
||||
'grades' => 'grade',
|
||||
'grading' => 'grade/grading',
|
||||
'group' => 'group',
|
||||
'help' => NULL,
|
||||
'hub' => NULL,
|
||||
'imscc' => NULL,
|
||||
'install' => NULL,
|
||||
'iso6392' => NULL,
|
||||
'langconfig' => NULL,
|
||||
'license' => NULL,
|
||||
'mathslib' => NULL,
|
||||
'media' => 'media',
|
||||
'message' => 'message',
|
||||
'mimetypes' => NULL,
|
||||
'mnet' => 'mnet',
|
||||
'moodle.org' => NULL, // the dot is nasty, watch out! should be renamed to moodleorg
|
||||
'my' => 'my',
|
||||
'notes' => 'notes',
|
||||
'pagetype' => NULL,
|
||||
'pix' => NULL,
|
||||
'plagiarism' => 'plagiarism',
|
||||
'plugin' => NULL,
|
||||
'portfolio' => 'portfolio',
|
||||
'publish' => 'course/publish',
|
||||
'question' => 'question',
|
||||
'rating' => 'rating',
|
||||
'register' => 'admin/registration', //TODO: this is wrong, unfortunately we would need to modify hub code to pass around the correct url
|
||||
'repository' => 'repository',
|
||||
'rss' => 'rss',
|
||||
'role' => $CFG->admin.'/role',
|
||||
'search' => 'search',
|
||||
'table' => NULL,
|
||||
'tag' => 'tag',
|
||||
'timezones' => NULL,
|
||||
'user' => 'user',
|
||||
'userkey' => NULL,
|
||||
'webservice' => 'webservice',
|
||||
);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all plugin types
|
||||
* @param bool $fullpaths false means relative paths from dirroot
|
||||
* @return array Array of strings - name=>location
|
||||
*/
|
||||
function get_plugin_types($fullpaths=true) {
|
||||
global $CFG;
|
||||
|
||||
$cache = cache::make('core', 'plugintypes');
|
||||
|
||||
if ($fullpaths) {
|
||||
// First confirm that dirroot and the stored dirroot match.
|
||||
if ($CFG->dirroot === $cache->get('dirroot')) {
|
||||
// They match we can use it.
|
||||
$cached = $cache->get(1);
|
||||
} else {
|
||||
// Oops they didn't match. The moodle directory has been moved on us.
|
||||
$cached = false;
|
||||
}
|
||||
} else {
|
||||
$cached = $cache->get(0);
|
||||
}
|
||||
|
||||
if ($cached !== false) {
|
||||
return $cached;
|
||||
|
||||
} else {
|
||||
$info = array('qtype' => 'question/type',
|
||||
'mod' => 'mod',
|
||||
'auth' => 'auth',
|
||||
'enrol' => 'enrol',
|
||||
'message' => 'message/output',
|
||||
'block' => 'blocks',
|
||||
'filter' => 'filter',
|
||||
'editor' => 'lib/editor',
|
||||
'format' => 'course/format',
|
||||
'profilefield' => 'user/profile/field',
|
||||
'report' => 'report',
|
||||
'coursereport' => 'course/report', // must be after system reports
|
||||
'gradeexport' => 'grade/export',
|
||||
'gradeimport' => 'grade/import',
|
||||
'gradereport' => 'grade/report',
|
||||
'gradingform' => 'grade/grading/form',
|
||||
'mnetservice' => 'mnet/service',
|
||||
'webservice' => 'webservice',
|
||||
'repository' => 'repository',
|
||||
'portfolio' => 'portfolio',
|
||||
'qbehaviour' => 'question/behaviour',
|
||||
'qformat' => 'question/format',
|
||||
'plagiarism' => 'plagiarism',
|
||||
'tool' => $CFG->admin.'/tool',
|
||||
'cachestore' => 'cache/stores',
|
||||
'cachelock' => 'cache/locks',
|
||||
'theme' => 'theme', // this is a bit hacky, themes may be in $CFG->themedir too
|
||||
);
|
||||
|
||||
$subpluginowners = array_merge(array_values(get_plugin_list('mod')),
|
||||
array_values(get_plugin_list('editor')));
|
||||
foreach ($subpluginowners as $ownerdir) {
|
||||
if (file_exists("$ownerdir/db/subplugins.php")) {
|
||||
$subplugins = array();
|
||||
include("$ownerdir/db/subplugins.php");
|
||||
foreach ($subplugins as $subtype=>$dir) {
|
||||
$info[$subtype] = $dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// local is always last!
|
||||
$info['local'] = 'local';
|
||||
|
||||
$fullinfo = array();
|
||||
foreach ($info as $type => $dir) {
|
||||
$fullinfo[$type] = $CFG->dirroot.'/'.$dir;
|
||||
}
|
||||
|
||||
$cache->set(0, $info);
|
||||
$cache->set(1, $fullinfo);
|
||||
// We cache the dirroot as well so that we can compare it when we
|
||||
// retrieve full info from the cache.
|
||||
$cache->set('dirroot', $CFG->dirroot);
|
||||
|
||||
return ($fullpaths ? $fullinfo : $info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates a plug name. It is much faster than calling clean_param.
|
||||
* @param string $name a string that might be a plugin name.
|
||||
* @return bool if this string is a valid plugin name.
|
||||
*/
|
||||
function is_valid_plugin_name($name) {
|
||||
return (bool) preg_match('/^[a-z](?:[a-z0-9_](?!__))*[a-z0-9]$/', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of get_list_of_plugins()
|
||||
* @param string $plugintype type of plugin
|
||||
* @return array name=>fulllocation pairs of plugins of given type
|
||||
*/
|
||||
function get_plugin_list($plugintype) {
|
||||
global $CFG;
|
||||
|
||||
// We use the dirroot as an identifier here because if it has changed the whole cache
|
||||
// can be considered invalid.
|
||||
$cache = cache::make('core', 'pluginlist', array('dirroot' => $CFG->dirroot));
|
||||
$cached = $cache->get($plugintype);
|
||||
if ($cached !== false) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests');
|
||||
if ($plugintype == 'auth') {
|
||||
// Historically we have had an auth plugin called 'db', so allow a special case.
|
||||
$key = array_search('db', $ignored);
|
||||
if ($key !== false) {
|
||||
unset($ignored[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($plugintype === '') {
|
||||
$plugintype = 'mod';
|
||||
}
|
||||
|
||||
$fulldirs = array();
|
||||
|
||||
if ($plugintype === 'mod') {
|
||||
// mod is an exception because we have to call this function from get_plugin_types()
|
||||
$fulldirs[] = $CFG->dirroot.'/mod';
|
||||
|
||||
} else if ($plugintype === 'editor') {
|
||||
// Exception also needed for editor for same reason.
|
||||
$fulldirs[] = $CFG->dirroot . '/lib/editor';
|
||||
|
||||
} else if ($plugintype === 'theme') {
|
||||
$fulldirs[] = $CFG->dirroot.'/theme';
|
||||
// themes are special because they may be stored also in separate directory
|
||||
if (!empty($CFG->themedir) and file_exists($CFG->themedir) and is_dir($CFG->themedir) ) {
|
||||
$fulldirs[] = $CFG->themedir;
|
||||
}
|
||||
|
||||
} else {
|
||||
$types = get_plugin_types(true);
|
||||
if (!array_key_exists($plugintype, $types)) {
|
||||
$cache->set($plugintype, array());
|
||||
return array();
|
||||
}
|
||||
$fulldir = $types[$plugintype];
|
||||
if (!file_exists($fulldir)) {
|
||||
$cache->set($plugintype, array());
|
||||
return array();
|
||||
}
|
||||
$fulldirs[] = $fulldir;
|
||||
}
|
||||
$result = array();
|
||||
|
||||
foreach ($fulldirs as $fulldir) {
|
||||
if (!is_dir($fulldir)) {
|
||||
continue;
|
||||
}
|
||||
$items = new DirectoryIterator($fulldir);
|
||||
foreach ($items as $item) {
|
||||
if ($item->isDot() or !$item->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$pluginname = $item->getFilename();
|
||||
if (in_array($pluginname, $ignored)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_valid_plugin_name($pluginname)) {
|
||||
// Better ignore plugins with problematic names here.
|
||||
continue;
|
||||
}
|
||||
$result[$pluginname] = $fulldir.'/'.$pluginname;
|
||||
unset($item);
|
||||
}
|
||||
unset($items);
|
||||
}
|
||||
|
||||
//TODO: implement better sorting once we migrated all plugin names to 'pluginname', ksort does not work for unicode, that is why we have to sort by the dir name, not the strings!
|
||||
ksort($result);
|
||||
$cache->set($plugintype, $result);
|
||||
return $result;
|
||||
// This does not work for 'mod', bad luck, use any other type.
|
||||
return core_component::is_valid_plugin_name('tool', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -8423,7 +8063,7 @@ function get_plugin_list_with_file($plugintype, $file, $include = false) {
|
||||
|
||||
$plugins = array();
|
||||
|
||||
foreach(get_plugin_list($plugintype) as $plugin => $dir) {
|
||||
foreach (core_component::get_plugin_list($plugintype) as $plugin => $dir) {
|
||||
$path = $dir . '/' . $file;
|
||||
if (file_exists($path)) {
|
||||
if ($include) {
|
||||
@ -8471,37 +8111,6 @@ function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php'
|
||||
return $pluginfunctions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all the plugins of a given type that define a certain class
|
||||
* in a certain file. The plugin component names and class names are returned.
|
||||
*
|
||||
* @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
|
||||
* @param string $class the part of the name of the class after the
|
||||
* frankenstyle prefix. e.g 'thing' if you are looking for classes with
|
||||
* names like report_courselist_thing. If you are looking for classes with
|
||||
* the same name as the plugin name (e.g. qtype_multichoice) then pass ''.
|
||||
* @param string $file the name of file within the plugin that defines the class.
|
||||
* @return array with frankenstyle plugin names as keys (e.g. 'report_courselist', 'mod_forum')
|
||||
* and the class names as values (e.g. 'report_courselist_thing', 'qtype_multichoice').
|
||||
*/
|
||||
function get_plugin_list_with_class($plugintype, $class, $file) {
|
||||
if ($class) {
|
||||
$suffix = '_' . $class;
|
||||
} else {
|
||||
$suffix = '';
|
||||
}
|
||||
|
||||
$pluginclasses = array();
|
||||
foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) {
|
||||
$classname = $plugintype . '_' . $plugin . $suffix;
|
||||
if (class_exists($classname)) {
|
||||
$pluginclasses[$plugintype . '_' . $plugin] = $classname;
|
||||
}
|
||||
}
|
||||
|
||||
return $pluginclasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists plugin-like directories within specified directory
|
||||
*
|
||||
@ -8527,6 +8136,17 @@ function get_list_of_plugins($directory='mod', $exclude='', $basedir='') {
|
||||
$basedir = $basedir .'/'. $directory;
|
||||
}
|
||||
|
||||
if (empty($exclude) and debugging('', DEBUG_DEVELOPER)) {
|
||||
// Make sure devs do not use this to list normal plugins,
|
||||
// this is intended for general directories that are not plugins!
|
||||
|
||||
$subtypes = core_component::get_plugin_types();
|
||||
if (in_array($basedir, $subtypes)) {
|
||||
debugging('get_list_of_plugins() should not be used to list real plugins, use core_component::get_plugin_list() instead!', DEBUG_DEVELOPER);
|
||||
}
|
||||
unset($subtypes);
|
||||
}
|
||||
|
||||
if (file_exists($basedir) && filetype($basedir) == 'dir') {
|
||||
if (!$dirhandle = opendir($basedir)) {
|
||||
debugging("Directory permission error for plugin ({$directory}). Directory exists but cannot be read.", DEBUG_DEVELOPER);
|
||||
@ -8534,7 +8154,7 @@ function get_list_of_plugins($directory='mod', $exclude='', $basedir='') {
|
||||
}
|
||||
while (false !== ($dir = readdir($dirhandle))) {
|
||||
$firstchar = substr($dir, 0, 1);
|
||||
if ($firstchar === '.' or $dir === 'CVS' or $dir === '_vti_cnf' or $dir === 'simpletest' or $dir === 'yui' or $dir === 'phpunit' or $dir === $exclude) {
|
||||
if ($firstchar === '.' or $dir === 'CVS' or $dir === '_vti_cnf' or $dir === 'simpletest' or $dir === 'yui' or $dir === 'tests' or $dir === 'classes' or $dir === $exclude) {
|
||||
continue;
|
||||
}
|
||||
if (filetype($basedir .'/'. $dir) != 'dir') {
|
||||
@ -8585,13 +8205,13 @@ function component_callback($component, $function, array $params = array(), $def
|
||||
}
|
||||
$component = $cleancomponent;
|
||||
|
||||
list($type, $name) = normalize_component($component);
|
||||
list($type, $name) = core_component::normalize_component($component);
|
||||
$component = $type . '_' . $name;
|
||||
|
||||
$oldfunction = $name.'_'.$function;
|
||||
$function = $component.'_'.$function;
|
||||
|
||||
$dir = get_component_directory($component);
|
||||
$dir = core_component::get_component_directory($component);
|
||||
if (empty($dir)) {
|
||||
throw new coding_exception('Invalid component used in plugin/component_callback():' . $component);
|
||||
}
|
||||
@ -8660,7 +8280,7 @@ function plugin_supports($type, $name, $feature, $default = NULL) {
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!$path = get_plugin_directory($type, $name)) {
|
||||
if (!$path = core_component::get_plugin_directory($type, $name)) {
|
||||
// non existent plugin type
|
||||
return false;
|
||||
}
|
||||
@ -9157,8 +8777,6 @@ function moodle_needs_upgrading() {
|
||||
|
||||
// We have to purge plugin related caches now to be sure we have fresh data
|
||||
// and new plugins can be detected.
|
||||
cache::make('core', 'plugintypes')->purge();
|
||||
cache::make('core', 'pluginlist')->purge();
|
||||
cache::make('core', 'plugininfo_base')->purge();
|
||||
cache::make('core', 'plugininfo_mod')->purge();
|
||||
cache::make('core', 'plugininfo_block')->purge();
|
||||
@ -9174,7 +8792,7 @@ function moodle_needs_upgrading() {
|
||||
}
|
||||
|
||||
// modules
|
||||
$mods = get_plugin_list('mod');
|
||||
$mods = core_component::get_plugin_list('mod');
|
||||
$installed = $DB->get_records('modules', array(), '', 'name, version');
|
||||
foreach ($mods as $mod => $fullmod) {
|
||||
if ($mod === 'NEWMODULE') { // Someone has unzipped the template, ignore it
|
||||
@ -9198,7 +8816,7 @@ function moodle_needs_upgrading() {
|
||||
unset($installed);
|
||||
|
||||
// blocks
|
||||
$blocks = get_plugin_list('block');
|
||||
$blocks = core_component::get_plugin_list('block');
|
||||
$installed = $DB->get_records('block', array(), '', 'name, version');
|
||||
require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
|
||||
foreach ($blocks as $blockname=>$fullblock) {
|
||||
@ -9220,13 +8838,13 @@ function moodle_needs_upgrading() {
|
||||
unset($installed);
|
||||
|
||||
// now the rest of plugins
|
||||
$plugintypes = get_plugin_types();
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
unset($plugintypes['mod']);
|
||||
unset($plugintypes['block']);
|
||||
|
||||
$versions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin, value');
|
||||
foreach ($plugintypes as $type=>$unused) {
|
||||
$plugs = get_plugin_list($type);
|
||||
$plugs = core_component::get_plugin_list($type);
|
||||
foreach ($plugs as $plug=>$fullplug) {
|
||||
$component = $type.'_'.$plug;
|
||||
if (!is_readable($fullplug.'/version.php')) {
|
||||
|
@ -183,13 +183,15 @@ abstract class renderer_factory_base implements renderer_factory {
|
||||
}
|
||||
|
||||
} else if (!empty($subtype)) {
|
||||
$coresubsystems = get_core_subsystems();
|
||||
if (!isset($coresubsystems[$subtype])) {
|
||||
$coresubsystems = core_component::get_core_subsystems();
|
||||
if (!array_key_exists($subtype, $coresubsystems)) { // There may be nulls.
|
||||
throw new coding_exception('Invalid core subtype "' . $subtype . '" in renderer request');
|
||||
}
|
||||
$rendererfile = $CFG->dirroot . '/' . $coresubsystems[$subtype] . '/renderer.php';
|
||||
if (file_exists($rendererfile)) {
|
||||
include_once($rendererfile);
|
||||
if ($coresubsystems[$subtype]) {
|
||||
$rendererfile = $coresubsystems[$subtype] . '/renderer.php';
|
||||
if (file_exists($rendererfile)) {
|
||||
include_once($rendererfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1662,19 +1662,18 @@ class YUI_config {
|
||||
}
|
||||
|
||||
// Handle other core subsystems.
|
||||
$subsystems = get_core_subsystems();
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
foreach ($subsystems as $subsystem => $path) {
|
||||
if (is_null($path)) {
|
||||
continue;
|
||||
}
|
||||
$path = get_component_directory($subsystem);
|
||||
if ($module = $this->get_moodle_path_metadata($path)) {
|
||||
$moodlemodules = array_merge($moodlemodules, $module);
|
||||
}
|
||||
}
|
||||
|
||||
// And finally the plugins.
|
||||
$plugintypes = get_plugin_types();
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
foreach ($plugintypes as $plugintype => $pathroot) {
|
||||
$pluginlist = get_plugin_list($plugintype);
|
||||
foreach ($pluginlist as $plugin => $path) {
|
||||
|
@ -289,6 +289,8 @@ umask(0000);
|
||||
$CFG->yui2version = '2.9.0';
|
||||
$CFG->yui3version = '3.9.1';
|
||||
|
||||
// core_component can be used in any scripts, it does not need anything else.
|
||||
require_once($CFG->libdir .'/classes/component.php');
|
||||
|
||||
// special support for highly optimised scripts that do not need libraries and DB connection
|
||||
if (defined('ABORT_AFTER_CONFIG')) {
|
||||
@ -504,6 +506,13 @@ ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include
|
||||
//please note zend library is supposed to be used only from web service protocol classes, it may be removed in future
|
||||
ini_set('include_path', $CFG->libdir.'/zend' . PATH_SEPARATOR . ini_get('include_path'));
|
||||
|
||||
// Register our classloader, in theory somebody might want to replace it to load other hacked core classes.
|
||||
if (defined('COMPONENT_CLASSLOADER')) {
|
||||
spl_autoload_register(COMPONENT_CLASSLOADER);
|
||||
} else {
|
||||
spl_autoload_register('core_component::classloader');
|
||||
}
|
||||
|
||||
// Load up standard libraries
|
||||
require_once($CFG->libdir .'/textlib.class.php'); // Functions to handle multibyte strings
|
||||
require_once($CFG->libdir .'/filterlib.php'); // Functions for filtering test as it is output
|
||||
|
@ -93,7 +93,7 @@ class tests_finder {
|
||||
|
||||
$subsystemswithtests = array();
|
||||
|
||||
$subsystems = get_core_subsystems();
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
|
||||
// Hack the list a bit to cover some well-known ones
|
||||
$subsystems['backup'] = 'backup';
|
||||
@ -101,11 +101,10 @@ class tests_finder {
|
||||
$subsystems['db-ddl'] = 'lib/ddl';
|
||||
|
||||
ksort($subsystems);
|
||||
foreach ($subsystems as $subsys => $relsubsys) {
|
||||
if ($relsubsys === null) {
|
||||
foreach ($subsystems as $subsys => $fullsubsys) {
|
||||
if ($fullsubsys === null) {
|
||||
continue;
|
||||
}
|
||||
$fullsubsys = $CFG->dirroot . '/' . $relsubsys;
|
||||
if (!is_dir($fullsubsys)) {
|
||||
continue;
|
||||
}
|
||||
|
329
lib/tests/component_test.php
Normal file
329
lib/tests/component_test.php
Normal file
@ -0,0 +1,329 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* core_component related tests.
|
||||
*
|
||||
* @package core
|
||||
* @category phpunit
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Class core_component_testcase.
|
||||
*/
|
||||
class core_component_testcase extends advanced_testcase {
|
||||
|
||||
// To be changed if number of subsystems increases/decreases,
|
||||
// this is defined here to annoy devs that try to add more without any thinking,
|
||||
// always verify that it does not collide with any existing add-on modules and subplugins!!!
|
||||
const SUBSYSTEMCOUNT = 63;
|
||||
|
||||
public function test_get_core_subsystems() {
|
||||
global $CFG;
|
||||
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
|
||||
$this->assertCount(self::SUBSYSTEMCOUNT, $subsystems, 'Oh, somebody added or removed a core subsystem, think twice before doing that!');
|
||||
|
||||
// Make sure all paths are full/null, exist and are inside dirroot.
|
||||
foreach($subsystems as $subsystem => $fulldir) {
|
||||
$this->assertFalse(strpos($subsystem, '_'), 'Core subsystems must be one work without underscores');
|
||||
if ($fulldir === null) {
|
||||
if ($subsystem === 'dock' or $subsystem === 'filepicker' or $subsystem === 'help') {
|
||||
// Arrgghh, let's not introduce more subsystems for no real reason...
|
||||
} else {
|
||||
// Lang strings.
|
||||
$this->assertFileExists("$CFG->dirroot/lang/en/$subsystem.php", 'Core subsystems without fulldir are usually used for lang strings.');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$this->assertFileExists($fulldir);
|
||||
// Check that base uses realpath() separators and "/" in the subdirs.
|
||||
$this->assertStringStartsWith($CFG->dirroot.'/', $fulldir);
|
||||
$reldir = substr($fulldir, strlen($CFG->dirroot)+1);
|
||||
$this->assertFalse(strpos($reldir, '\\'));
|
||||
}
|
||||
|
||||
// Make sure all core language files are also subsystems!
|
||||
$items = new DirectoryIterator("$CFG->dirroot/lang/en");
|
||||
foreach ($items as $item) {
|
||||
if ($item->isDot() or $item->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$file = $item->getFilename();
|
||||
if ($file === 'moodle.php') {
|
||||
// Do not add new lang strings unless really necessary!!!
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($file, -4) !== '.php') {
|
||||
continue;
|
||||
}
|
||||
$file = substr($file, 0, strlen($file)-4);
|
||||
$this->assertArrayHasKey($file, $subsystems, 'All core lang files should be subsystems, think twice before adding anything!');
|
||||
}
|
||||
unset($item);
|
||||
unset($items);
|
||||
|
||||
}
|
||||
|
||||
public function test_deprecated_get_core_subsystems() {
|
||||
global $CFG;
|
||||
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
|
||||
$this->assertSame($subsystems, get_core_subsystems(true));
|
||||
|
||||
$realsubsystems = get_core_subsystems();
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertSame($realsubsystems, get_core_subsystems(false));
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
$this->assertEquals(count($subsystems), count($realsubsystems));
|
||||
|
||||
foreach ($subsystems as $subsystem => $fulldir) {
|
||||
$this->assertArrayHasKey($subsystem, $realsubsystems);
|
||||
if ($fulldir === null) {
|
||||
$this->assertNull($realsubsystems[$subsystem]);
|
||||
continue;
|
||||
}
|
||||
$this->assertSame($fulldir, $CFG->dirroot.'/'.$realsubsystems[$subsystem]);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_get_plugin_types() {
|
||||
global $CFG;
|
||||
|
||||
$this->assertTrue(empty($CFG->themedir), 'Non-empty $CFG->themedir is not covered by any tests yet, you need to disable it.');
|
||||
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
foreach($plugintypes as $plugintype => $fulldir) {
|
||||
$this->assertStringStartsWith("$CFG->dirroot/", $fulldir);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_deprecated_get_plugin_types() {
|
||||
global $CFG;
|
||||
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
$this->assertSame($plugintypes, get_plugin_types());
|
||||
$this->assertSame($plugintypes, get_plugin_types(true));
|
||||
|
||||
$realplugintypes = get_plugin_types(false);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
foreach($plugintypes as $plugintype => $fulldir) {
|
||||
$this->assertSame($fulldir, $CFG->dirroot.'/'.$realplugintypes[$plugintype]);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_get_plugin_list() {
|
||||
global $CFG;
|
||||
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
foreach ($plugintypes as $plugintype => $fulldir) {
|
||||
$plugins = core_component::get_plugin_list($plugintype);
|
||||
foreach($plugins as $pluginname => $plugindir) {
|
||||
$this->assertStringStartsWith("$CFG->dirroot/", $plugindir);
|
||||
}
|
||||
if ($plugintype !== 'auth') {
|
||||
// Let's crosscheck it with independent implementation (auth/db is an exception).
|
||||
$reldir = substr($fulldir, strlen($CFG->dirroot)+1);
|
||||
$dirs = get_list_of_plugins($reldir);
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertSame($dirs, array_keys($plugins));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_deprecated_get_plugin_list() {
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
foreach ($plugintypes as $plugintype => $fulldir) {
|
||||
$plugins = core_component::get_plugin_list($plugintype);
|
||||
$this->assertSame($plugins, get_plugin_list($plugintype));
|
||||
}
|
||||
}
|
||||
|
||||
public function test_get_plugin_directory() {
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
foreach ($plugintypes as $plugintype => $fulldir) {
|
||||
$plugins = core_component::get_plugin_list($plugintype);
|
||||
foreach($plugins as $pluginname => $plugindir) {
|
||||
$this->assertSame($plugindir, core_component::get_plugin_directory($plugintype, $pluginname));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_deprecated_get_plugin_directory() {
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
foreach ($plugintypes as $plugintype => $fulldir) {
|
||||
$plugins = core_component::get_plugin_list($plugintype);
|
||||
foreach($plugins as $pluginname => $plugindir) {
|
||||
$this->assertSame(core_component::get_plugin_directory($plugintype, $pluginname), get_plugin_directory($plugintype, $pluginname));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_get_subsystem_directory() {
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
foreach ($subsystems as $subsystem => $fulldir) {
|
||||
$this->assertSame($fulldir, core_component::get_subsystem_directory($subsystem));
|
||||
}
|
||||
}
|
||||
|
||||
public function test_is_valid_plugin_name() {
|
||||
$this->assertTrue(core_component::is_valid_plugin_name('mod', 'example1'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', '1example'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', 'example.xx'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', '.example'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', '_example'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', 'example_x1'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', 'example-x1'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('mod', 'role'));
|
||||
|
||||
$this->assertTrue(core_component::is_valid_plugin_name('tool', 'example1'));
|
||||
$this->assertTrue(core_component::is_valid_plugin_name('tool', 'example_x1'));
|
||||
$this->assertTrue(core_component::is_valid_plugin_name('tool', 'example_x1_xxx'));
|
||||
$this->assertTrue(core_component::is_valid_plugin_name('tool', 'role'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('tool', '1example'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('tool', 'example.xx'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('tool', 'example-xx'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('tool', '.example'));
|
||||
$this->assertFalse(core_component::is_valid_plugin_name('tool', '_example'));
|
||||
}
|
||||
|
||||
public function test_normalize_component() {
|
||||
global $CFG;
|
||||
|
||||
// Moodle core.
|
||||
$this->assertSame(array('core', null), core_component::normalize_component('core'));
|
||||
$this->assertSame(array('core', null), core_component::normalize_component('moodle'));
|
||||
$this->assertSame(array('core', null), core_component::normalize_component(''));
|
||||
|
||||
// Moodle core subsystems.
|
||||
$this->assertSame(array('core', 'admin'), core_component::normalize_component('admin'));
|
||||
$this->assertSame(array('core', 'admin'), core_component::normalize_component('core_admin'));
|
||||
$this->assertSame(array('core', 'admin'), core_component::normalize_component('moodle_admin'));
|
||||
|
||||
// Activity modules and their subplugins.
|
||||
$this->assertSame(array('mod', 'workshop'), core_component::normalize_component('workshop'));
|
||||
$this->assertSame(array('mod', 'workshop'), core_component::normalize_component('mod_workshop'));
|
||||
$this->assertSame(array('workshopform', 'accumulative'), core_component::normalize_component('workshopform_accumulative'));
|
||||
$this->assertSame(array('mod', 'quiz'), core_component::normalize_component('quiz'));
|
||||
$this->assertSame(array('quiz', 'grading'), core_component::normalize_component('quiz_grading'));
|
||||
$this->assertSame(array('mod', 'data'), core_component::normalize_component('data'));
|
||||
$this->assertSame(array('datafield', 'checkbox'), core_component::normalize_component('datafield_checkbox'));
|
||||
|
||||
// Other plugin types.
|
||||
$this->assertSame(array('auth', 'mnet'), core_component::normalize_component('auth_mnet'));
|
||||
$this->assertSame(array('enrol', 'self'), core_component::normalize_component('enrol_self'));
|
||||
$this->assertSame(array('block', 'html'), core_component::normalize_component('block_html'));
|
||||
$this->assertSame(array('block', 'mnet_hosts'), core_component::normalize_component('block_mnet_hosts'));
|
||||
$this->assertSame(array('local', 'amos'), core_component::normalize_component('local_amos'));
|
||||
$this->assertSame(array('local', 'admin'), core_component::normalize_component('local_admin'));
|
||||
|
||||
// Unknown words without underscore are supposed to be activity modules.
|
||||
$this->assertSame(array('mod', 'whothefuckwouldcomewithsuchastupidnameofcomponent'),
|
||||
core_component::normalize_component('whothefuckwouldcomewithsuchastupidnameofcomponent'));
|
||||
// Module names can not contain underscores, this must be a subplugin.
|
||||
$this->assertSame(array('whothefuck', 'wouldcomewithsuchastupidnameofcomponent'),
|
||||
core_component::normalize_component('whothefuck_wouldcomewithsuchastupidnameofcomponent'));
|
||||
$this->assertSame(array('whothefuck', 'would_come_withsuchastupidnameofcomponent'),
|
||||
core_component::normalize_component('whothefuck_would_come_withsuchastupidnameofcomponent'));
|
||||
}
|
||||
|
||||
public function test_deprecated_normalize_component() {
|
||||
global $CFG;
|
||||
|
||||
// Moodle core.
|
||||
$this->assertSame(array('core', null), normalize_component('core'));
|
||||
$this->assertSame(array('core', null), normalize_component(''));
|
||||
$this->assertSame(array('core', null), normalize_component('moodle'));
|
||||
|
||||
// Moodle core subsystems.
|
||||
$this->assertSame(array('core', 'admin'), normalize_component('admin'));
|
||||
$this->assertSame(array('core', 'admin'), normalize_component('core_admin'));
|
||||
$this->assertSame(array('core', 'admin'), normalize_component('moodle_admin'));
|
||||
|
||||
// Activity modules and their subplugins.
|
||||
$this->assertSame(array('mod', 'workshop'), normalize_component('workshop'));
|
||||
$this->assertSame(array('mod', 'workshop'), normalize_component('mod_workshop'));
|
||||
$this->assertSame(array('workshopform', 'accumulative'), normalize_component('workshopform_accumulative'));
|
||||
$this->assertSame(array('mod', 'quiz'), normalize_component('quiz'));
|
||||
$this->assertSame(array('quiz', 'grading'), normalize_component('quiz_grading'));
|
||||
$this->assertSame(array('mod', 'data'), normalize_component('data'));
|
||||
$this->assertSame(array('datafield', 'checkbox'), normalize_component('datafield_checkbox'));
|
||||
|
||||
// Other plugin types.
|
||||
$this->assertSame(array('auth', 'mnet'), normalize_component('auth_mnet'));
|
||||
$this->assertSame(array('enrol', 'self'), normalize_component('enrol_self'));
|
||||
$this->assertSame(array('block', 'html'), normalize_component('block_html'));
|
||||
$this->assertSame(array('block', 'mnet_hosts'), normalize_component('block_mnet_hosts'));
|
||||
$this->assertSame(array('local', 'amos'), normalize_component('local_amos'));
|
||||
$this->assertSame(array('local', 'admin'), normalize_component('local_admin'));
|
||||
|
||||
// Unknown words without underscore are supposed to be activity modules.
|
||||
$this->assertSame(array('mod', 'whothefuckwouldcomewithsuchastupidnameofcomponent'),
|
||||
normalize_component('whothefuckwouldcomewithsuchastupidnameofcomponent'));
|
||||
// Module names can not contain underscores, this must be a subplugin.
|
||||
$this->assertSame(array('whothefuck', 'wouldcomewithsuchastupidnameofcomponent'),
|
||||
normalize_component('whothefuck_wouldcomewithsuchastupidnameofcomponent'));
|
||||
$this->assertSame(array('whothefuck', 'would_come_withsuchastupidnameofcomponent'),
|
||||
normalize_component('whothefuck_would_come_withsuchastupidnameofcomponent'));
|
||||
}
|
||||
|
||||
|
||||
public function test_get_component_directory() {
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
foreach ($plugintypes as $plugintype => $fulldir) {
|
||||
$plugins = core_component::get_plugin_list($plugintype);
|
||||
foreach($plugins as $pluginname => $plugindir) {
|
||||
$this->assertSame($plugindir, core_component::get_component_directory(($plugintype.'_'.$pluginname)));
|
||||
}
|
||||
}
|
||||
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
foreach($subsystems as $subsystem => $fulldir) {
|
||||
$this->assertSame($fulldir, core_component::get_component_directory(('core_'.$subsystem)));
|
||||
}
|
||||
}
|
||||
|
||||
public function test_deprecated_get_component_directory() {
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
foreach ($plugintypes as $plugintype => $fulldir) {
|
||||
$plugins = core_component::get_plugin_list($plugintype);
|
||||
foreach($plugins as $pluginname => $plugindir) {
|
||||
$this->assertSame($plugindir, get_component_directory(($plugintype.'_'.$pluginname)));
|
||||
}
|
||||
}
|
||||
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
foreach($subsystems as $subsystem => $fulldir) {
|
||||
$this->assertSame($fulldir, get_component_directory(('core_'.$subsystem)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1319,41 +1319,6 @@ class moodlelib_testcase extends advanced_testcase {
|
||||
$USER = $olduser;
|
||||
}
|
||||
|
||||
public function test_normalize_component() {
|
||||
|
||||
// moodle core
|
||||
$this->assertEquals(normalize_component('moodle'), array('core', null));
|
||||
$this->assertEquals(normalize_component('core'), array('core', null));
|
||||
|
||||
// moodle core subsystems
|
||||
$this->assertEquals(normalize_component('admin'), array('core', 'admin'));
|
||||
$this->assertEquals(normalize_component('core_admin'), array('core', 'admin'));
|
||||
|
||||
// activity modules and their subplugins
|
||||
$this->assertEquals(normalize_component('workshop'), array('mod', 'workshop'));
|
||||
$this->assertEquals(normalize_component('mod_workshop'), array('mod', 'workshop'));
|
||||
$this->assertEquals(normalize_component('workshopform_accumulative'), array('workshopform', 'accumulative'));
|
||||
$this->assertEquals(normalize_component('quiz'), array('mod', 'quiz'));
|
||||
$this->assertEquals(normalize_component('quiz_grading'), array('quiz', 'grading'));
|
||||
$this->assertEquals(normalize_component('data'), array('mod', 'data'));
|
||||
$this->assertEquals(normalize_component('datafield_checkbox'), array('datafield', 'checkbox'));
|
||||
|
||||
// other plugin types
|
||||
$this->assertEquals(normalize_component('auth_mnet'), array('auth', 'mnet'));
|
||||
$this->assertEquals(normalize_component('enrol_self'), array('enrol', 'self'));
|
||||
$this->assertEquals(normalize_component('block_html'), array('block', 'html'));
|
||||
$this->assertEquals(normalize_component('block_mnet_hosts'), array('block', 'mnet_hosts'));
|
||||
$this->assertEquals(normalize_component('local_amos'), array('local', 'amos'));
|
||||
|
||||
// unknown components are supposed to be activity modules
|
||||
$this->assertEquals(normalize_component('whothefuckwouldcomewithsuchastupidnameofcomponent'),
|
||||
array('mod', 'whothefuckwouldcomewithsuchastupidnameofcomponent'));
|
||||
$this->assertEquals(normalize_component('whothefuck_wouldcomewithsuchastupidnameofcomponent'),
|
||||
array('mod', 'whothefuck_wouldcomewithsuchastupidnameofcomponent'));
|
||||
$this->assertEquals(normalize_component('whothefuck_would_come_withsuchastupidnameofcomponent'),
|
||||
array('mod', 'whothefuck_would_come_withsuchastupidnameofcomponent'));
|
||||
}
|
||||
|
||||
protected function get_fake_preference_test_userid() {
|
||||
global $DB;
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 2.6 ===
|
||||
* Use new core_component::* plugin listing and component normalisation methods.
|
||||
|
||||
|
||||
=== 2.5.1 ===
|
||||
|
||||
* New get_course() function for use when obtaining the course record from database. Will
|
||||
|
Loading…
x
Reference in New Issue
Block a user