MDL-29030 standardise report_courseoverview
[moodle.git] / lib / pluginlib.php
blobfec3617a97637401d86c54efffe7d429731b530d
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * Defines classes used for plugins management
20 * This library provides a unified interface to various plugin types in
21 * Moodle. It is mainly used by the plugins management admin page and the
22 * plugins check page during the upgrade.
24 * @package core
25 * @subpackage admin
26 * @copyright 2011 David Mudrak <david@moodle.com>
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30 defined('MOODLE_INTERNAL') || die();
32 /**
33 * Singleton class providing general plugins management functionality
35 class plugin_manager {
37 /** the plugin is shipped with standard Moodle distribution */
38 const PLUGIN_SOURCE_STANDARD = 'std';
39 /** the plugin is added extension */
40 const PLUGIN_SOURCE_EXTENSION = 'ext';
42 /** the plugin uses neither database nor capabilities, no versions */
43 const PLUGIN_STATUS_NODB = 'nodb';
44 /** the plugin is up-to-date */
45 const PLUGIN_STATUS_UPTODATE = 'uptodate';
46 /** the plugin is about to be installed */
47 const PLUGIN_STATUS_NEW = 'new';
48 /** the plugin is about to be upgraded */
49 const PLUGIN_STATUS_UPGRADE = 'upgrade';
50 /** the version at the disk is lower than the one already installed */
51 const PLUGIN_STATUS_DOWNGRADE = 'downgrade';
52 /** the plugin is installed but missing from disk */
53 const PLUGIN_STATUS_MISSING = 'missing';
55 /** @var plugin_manager holds the singleton instance */
56 protected static $singletoninstance;
57 /** @var array of raw plugins information */
58 protected $pluginsinfo = null;
59 /** @var array of raw subplugins information */
60 protected $subpluginsinfo = null;
62 /**
63 * Direct initiation not allowed, use the factory method {@link self::instance()}
65 * @todo we might want to specify just a single plugin type to work with
67 protected function __construct() {
68 $this->get_plugins(true);
71 /**
72 * Sorry, this is singleton
74 protected function __clone() {
77 /**
78 * Factory method for this class
80 * @return plugin_manager the singleton instance
82 public static function instance() {
83 global $CFG;
85 if (is_null(self::$singletoninstance)) {
86 self::$singletoninstance = new self();
88 return self::$singletoninstance;
91 /**
92 * Returns a tree of known plugins and information about them
94 * @param bool $disablecache force reload, cache can be used otherwise
95 * @return array 2D array. The first keys are plugin type names (e.g. qtype);
96 * the second keys are the plugin local name (e.g. multichoice); and
97 * the values are the corresponding {@link plugin_information} objects.
99 public function get_plugins($disablecache=false) {
101 if ($disablecache or is_null($this->pluginsinfo)) {
102 $this->pluginsinfo = array();
103 $plugintypes = get_plugin_types();
104 foreach ($plugintypes as $plugintype => $plugintyperootdir) {
105 if (in_array($plugintype, array('base', 'general'))) {
106 throw new coding_exception('Illegal usage of reserved word for plugin type');
108 if (class_exists('plugintype_' . $plugintype)) {
109 $plugintypeclass = 'plugintype_' . $plugintype;
110 } else {
111 $plugintypeclass = 'plugintype_general';
113 if (!in_array('plugin_information', class_implements($plugintypeclass))) {
114 throw new coding_exception('Class ' . $plugintypeclass . ' must implement plugin_information');
116 $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass);
117 $this->pluginsinfo[$plugintype] = $plugins;
121 return $this->pluginsinfo;
125 * Returns list of plugins that define their subplugins and the information
126 * about them from the db/subplugins.php file.
128 * At the moment, only activity modules can define subplugins.
130 * @param bool $disablecache force reload, cache can be used otherwise
131 * @return array with keys like 'mod_quiz', and values the data from the
132 * corresponding db/subplugins.php file.
134 public function get_subplugins($disablecache=false) {
136 if ($disablecache or is_null($this->subpluginsinfo)) {
137 $this->subpluginsinfo = array();
138 $mods = get_plugin_list('mod');
139 foreach ($mods as $mod => $moddir) {
140 $modsubplugins = array();
141 if (file_exists($moddir . '/db/subplugins.php')) {
142 include($moddir . '/db/subplugins.php');
143 foreach ($subplugins as $subplugintype => $subplugintyperootdir) {
144 $subplugin = new stdClass();
145 $subplugin->type = $subplugintype;
146 $subplugin->typerootdir = $subplugintyperootdir;
147 $modsubplugins[$subplugintype] = $subplugin;
149 $this->subpluginsinfo['mod_' . $mod] = $modsubplugins;
154 return $this->subpluginsinfo;
158 * Returns the name of the plugin that defines the given subplugin type
160 * If the given subplugin type is not actually a subplugin, returns false.
162 * @param string $subplugintype the name of subplugin type, eg. workshopform or quiz
163 * @return false|string the name of the parent plugin, eg. mod_workshop
165 public function get_parent_of_subplugin($subplugintype) {
167 $parent = false;
168 foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
169 if (isset($subplugintypes[$subplugintype])) {
170 $parent = $pluginname;
171 break;
175 return $parent;
179 * Returns a localized name of a given plugin
181 * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
182 * @return string
184 public function plugin_name($plugin) {
185 list($type, $name) = normalize_component($plugin);
186 return $this->pluginsinfo[$type][$name]->displayname;
190 * Returns a localized name of a plugin type in plural form
192 * Most plugin types define their names in core_plugin lang file. In case of subplugins,
193 * we try to ask the parent plugin for the name. In the worst case, we will return
194 * the value of the passed $type parameter.
196 * @param string $type the type of the plugin, e.g. mod or workshopform
197 * @return string
199 public function plugintype_name_plural($type) {
201 if (get_string_manager()->string_exists('type_' . $type . '_plural', 'core_plugin')) {
202 // for most plugin types, their names are defined in core_plugin lang file
203 return get_string('type_' . $type . '_plural', 'core_plugin');
205 } else if ($parent = $this->get_parent_of_subplugin($type)) {
206 // if this is a subplugin, try to ask the parent plugin for the name
207 if (get_string_manager()->string_exists('subplugintype_' . $type . '_plural', $parent)) {
208 return $this->plugin_name($parent) . ' / ' . get_string('subplugintype_' . $type . '_plural', $parent);
209 } else {
210 return $this->plugin_name($parent) . ' / ' . $type;
213 } else {
214 return $type;
219 * @param string $component frankenstyle component name.
220 * @return plugin_information|null the corresponding plugin information.
222 public function get_plugin_info($component) {
223 list($type, $name) = normalize_component($component);
224 $plugins = $this->get_plugins();
225 if (isset($plugins[$type][$name])) {
226 return $plugins[$type][$name];
227 } else {
228 return null;
233 * Check a dependencies list against the list of installed plugins.
234 * @param array $dependencies compenent name to required version or ANY_VERSION.
235 * @return bool true if all the dependencies are satisfied.
237 public function are_dependencies_satisfied($dependencies) {
238 foreach ($dependencies as $component => $requiredversion) {
239 $otherplugin = $this->get_plugin_info($component);
240 if (is_null($otherplugin)) {
241 return false;
244 if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
245 return false;
249 return true;
253 * Checks all dependencies for all installed plugins. Used by install and upgrade.
254 * @param int $moodleversion the version from version.php.
255 * @return bool true if all the dependencies are satisfied for all plugins.
257 public function all_plugins_ok($moodleversion) {
258 foreach ($this->get_plugins() as $type => $plugins) {
259 foreach ($plugins as $plugin) {
261 if (!empty($plugin->versionrequires) && $plugin->versionrequires > $moodleversion) {
262 return false;
265 if (!$this->are_dependencies_satisfied($plugin->get_other_required_plugins())) {
266 return false;
271 return true;
275 * Defines a white list of all plugins shipped in the standard Moodle distribution
277 * @return false|array array of standard plugins or false if the type is unknown
279 public static function standard_plugins_list($type) {
280 static $standard_plugins = array(
282 'assignment' => array(
283 'offline', 'online', 'upload', 'uploadsingle'
286 'auth' => array(
287 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
288 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
289 'shibboleth', 'webservice'
292 'block' => array(
293 'activity_modules', 'admin_bookmarks', 'blog_menu',
294 'blog_recent', 'blog_tags', 'calendar_month',
295 'calendar_upcoming', 'comments', 'community',
296 'completionstatus', 'course_list', 'course_overview',
297 'course_summary', 'feedback', 'glossary_random', 'html',
298 'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile',
299 'navigation', 'news_items', 'online_users', 'participants',
300 'private_files', 'quiz_results', 'recent_activity',
301 'rss_client', 'search_forums', 'section_links',
302 'selfcompletion', 'settings', 'site_main_menu',
303 'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
306 'coursereport' => array(
307 'completion', 'log', 'outline', 'participation', 'progress', 'stats'
310 'datafield' => array(
311 'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
312 'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
315 'datapreset' => array(
316 'imagegallery'
319 'editor' => array(
320 'textarea', 'tinymce'
323 'enrol' => array(
324 'authorize', 'category', 'cohort', 'database', 'flatfile',
325 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
326 'paypal', 'self'
329 'filter' => array(
330 'activitynames', 'algebra', 'censor', 'emailprotect',
331 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
332 'urltolink', 'mod_data', 'mod_glossary'
335 'format' => array(
336 'scorm', 'social', 'topics', 'weeks'
339 'gradeexport' => array(
340 'ods', 'txt', 'xls', 'xml'
343 'gradeimport' => array(
344 'csv', 'xml'
347 'gradereport' => array(
348 'grader', 'outcomes', 'overview', 'user'
351 'local' => array(
354 'message' => array(
355 'email', 'jabber', 'popup'
358 'mnetservice' => array(
359 'enrol'
362 'mod' => array(
363 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
364 'forum', 'glossary', 'imscp', 'label', 'lesson', 'page',
365 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
368 'plagiarism' => array(
371 'portfolio' => array(
372 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
375 'profilefield' => array(
376 'checkbox', 'datetime', 'menu', 'text', 'textarea'
379 'qbehaviour' => array(
380 'adaptive', 'adaptivenopenalty', 'deferredcbm',
381 'deferredfeedback', 'immediatecbm', 'immediatefeedback',
382 'informationitem', 'interactive', 'interactivecountback',
383 'manualgraded', 'missing'
386 'qformat' => array(
387 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
388 'learnwise', 'missingword', 'multianswer', 'webct',
389 'xhtml', 'xml'
392 'qtype' => array(
393 'calculated', 'calculatedmulti', 'calculatedsimple',
394 'description', 'essay', 'match', 'missingtype', 'multianswer',
395 'multichoice', 'numerical', 'random', 'randomsamatch',
396 'shortanswer', 'truefalse'
399 'quiz' => array(
400 'grading', 'overview', 'responses', 'statistics'
403 'quizaccess' => array(
404 'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate',
405 'password', 'safebrowser', 'securewindow', 'timelimit'
408 'report' => array(
409 'backups', 'configlog', 'courseoverview',
410 'log', 'questioninstances', 'security', 'stats'
413 'repository' => array(
414 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
415 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
416 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
417 'wikimedia', 'youtube'
420 'scormreport' => array(
421 'basic'
424 'theme' => array(
425 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
426 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
427 'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero',
428 'overlay', 'serenity', 'sky_high', 'splash', 'standard',
429 'standardold'
432 'tool' => array(
433 'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
434 'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
435 'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
436 'uploaduser', 'unsuproles', 'xmldb'
439 'webservice' => array(
440 'amf', 'rest', 'soap', 'xmlrpc'
443 'workshopallocation' => array(
444 'manual', 'random'
447 'workshopeval' => array(
448 'best'
451 'workshopform' => array(
452 'accumulative', 'comments', 'numerrors', 'rubric'
456 if (isset($standard_plugins[$type])) {
457 return $standard_plugins[$type];
459 } else {
460 return false;
466 * Interface for making information about a plugin available.
468 * Note that most of the useful information is made available in pubic fields,
469 * which cannot be documented in this interface. See the field definitions on
470 * {@link plugintype_base} to find out what information is available.
472 interface plugin_information {
475 * Gathers and returns the information about all plugins of the given type
477 * Passing the parameter $typeclass allows us to reach the same effect as with the
478 * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
479 * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
481 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
482 * @param string $typerootdir full path to the location of the plugin dir
483 * @param string $typeclass the name of the actually called class
484 * @return array of plugintype classes, indexed by the plugin name
486 public static function get_plugins($type, $typerootdir, $typeclass);
489 * Sets $displayname property to a localized name of the plugin
491 * @return void
493 public function init_display_name();
496 * Sets $versiondisk property to a numerical value representing the
497 * version of the plugin's source code.
499 * If the value is null after calling this method, either the plugin
500 * does not use versioning (typically does not have any database
501 * data) or is missing from disk.
503 * @return void
505 public function load_disk_version();
508 * Sets $versiondb property to a numerical value representing the
509 * currently installed version of the plugin.
511 * If the value is null after calling this method, either the plugin
512 * does not use versioning (typically does not have any database
513 * data) or has not been installed yet.
515 * @return void
517 public function load_db_version();
520 * Sets $versionrequires property to a numerical value representing
521 * the version of Moodle core that this plugin requires.
523 * @return void
525 public function load_required_main_version();
528 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
529 * constants.
531 * If the property's value is null after calling this method, then
532 * the type of the plugin has not been recognized and you should throw
533 * an exception.
535 * @return void
537 public function init_is_standard();
540 * Returns true if the plugin is shipped with the official distribution
541 * of the current Moodle version, false otherwise.
543 * @return bool
545 public function is_standard();
548 * Returns the status of the plugin
550 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
552 public function get_status();
555 * Get the list of other plugins that this plugin requires ot be installed.
556 * @return array with keys the frankenstyle plugin name, and values either
557 * a version string (like '2011101700') or the constant ANY_VERSION.
559 public function get_other_required_plugins();
562 * Returns the information about plugin availability
564 * True means that the plugin is enabled. False means that the plugin is
565 * disabled. Null means that the information is not available, or the
566 * plugin does not support configurable availability or the availability
567 * can not be changed.
569 * @return null|bool
571 public function is_enabled();
574 * Returns the URL of the plugin settings screen
576 * Null value means that the plugin either does not have the settings screen
577 * or its location is not available via this library.
579 * @return null|moodle_url
581 public function get_settings_url();
584 * Returns the URL of the screen where this plugin can be uninstalled
586 * Visiting that URL must be safe, that is a manual confirmation is needed
587 * for actual uninstallation of the plugin. Null value means that the
588 * plugin either does not support uninstallation, or does not require any
589 * database cleanup or the location of the screen is not available via this
590 * library.
592 * @return null|moodle_url
594 public function get_uninstall_url();
597 * Returns relative directory of the plugin with heading '/'
599 * @example /mod/workshop
600 * @return string
602 public function get_dir();
605 * Return the full path name of a file within the plugin.
606 * No check is made to see if the file exists.
607 * @param string $relativepath e.g. 'version.php'.
608 * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
610 public function full_path($relativepath);
614 * Defines public properties that all plugintype classes must have
615 * and provides default implementation of required methods.
617 abstract class plugintype_base {
619 /** @var string the plugintype name, eg. mod, auth or workshopform */
620 public $type;
621 /** @var string full path to the location of all the plugins of this type */
622 public $typerootdir;
623 /** @var string the plugin name, eg. assignment, ldap */
624 public $name;
625 /** @var string the localized plugin name */
626 public $displayname;
627 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
628 public $source;
629 /** @var fullpath to the location of this plugin */
630 public $rootdir;
631 /** @var int|string the version of the plugin's source code */
632 public $versiondisk;
633 /** @var int|string the version of the installed plugin */
634 public $versiondb;
635 /** @var int|float|string required version of Moodle core */
636 public $versionrequires;
637 /** @var array other plugins that this one depends on.
638 * Lazy-loaded by {@link get_other_required_plugins()} */
639 public $dependencies = null;
640 /** @var int number of instances of the plugin - not supported yet */
641 public $instances;
642 /** @var int order of the plugin among other plugins of the same type - not supported yet */
643 public $sortorder;
646 * @see plugin_information::get_plugins()
648 public static function get_plugins($type, $typerootdir, $typeclass) {
650 // get the information about plugins at the disk
651 $plugins = get_plugin_list($type);
652 $ondisk = array();
653 foreach ($plugins as $pluginname => $pluginrootdir) {
654 $plugin = new $typeclass();
655 $plugin->type = $type;
656 $plugin->typerootdir = $typerootdir;
657 $plugin->name = $pluginname;
658 $plugin->rootdir = $pluginrootdir;
660 $plugin->init_display_name();
661 $plugin->load_disk_version();
662 $plugin->load_db_version();
663 $plugin->load_required_main_version();
664 $plugin->init_is_standard();
666 $ondisk[$pluginname] = $plugin;
668 return $ondisk;
672 * @see plugin_information::init_display_name()
674 public function init_display_name() {
675 if (! get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
676 $this->displayname = '[pluginname,' . $this->type . '_' . $this->name . ']';
677 } else {
678 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
683 * @see plugin_information::full_path()
685 public function full_path($relativepath) {
686 if (empty($this->rootdir)) {
687 return '';
689 return $this->rootdir . '/' . $relativepath;
693 * Load the data from version.php.
694 * @return object the data object defined in version.php.
696 protected function load_version_php() {
697 $versionfile = $this->full_path('version.php');
699 $plugin = new stdClass();
700 if (is_readable($versionfile)) {
701 include($versionfile);
703 return $plugin;
707 * @see plugin_information::load_disk_version()
709 public function load_disk_version() {
710 $plugin = $this->load_version_php();
711 if (isset($plugin->version)) {
712 $this->versiondisk = $plugin->version;
717 * @see plugin_information::load_required_main_version()
719 public function load_required_main_version() {
720 $plugin = $this->load_version_php();
721 if (isset($plugin->requires)) {
722 $this->versionrequires = $plugin->requires;
727 * Initialise {@link $dependencies} to the list of other plugins (in any)
728 * that this one requires to be installed.
730 protected function load_other_required_plugins() {
731 $plugin = $this->load_version_php();
732 if (!empty($plugin->dependencies)) {
733 $this->dependencies = $plugin->dependencies;
734 } else {
735 $this->dependencies = array(); // By default, no dependencies.
740 * @see plugin_information::get_other_required_plugins()
742 public function get_other_required_plugins() {
743 if (is_null($this->dependencies)) {
744 $this->load_other_required_plugins();
746 return $this->dependencies;
750 * @see plugin_information::load_db_version()
752 public function load_db_version() {
754 if ($ver = self::get_version_from_config_plugins($this->type . '_' . $this->name)) {
755 $this->versiondb = $ver;
760 * @see plugin_information::init_is_standard()
762 public function init_is_standard() {
764 $standard = plugin_manager::standard_plugins_list($this->type);
766 if ($standard !== false) {
767 $standard = array_flip($standard);
768 if (isset($standard[$this->name])) {
769 $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD;
770 } else {
771 $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
777 * @see plugin_information::is_standard()
779 public function is_standard() {
780 return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
784 * @see plugin_information::get_status()
786 public function get_status() {
788 if (is_null($this->versiondb) and is_null($this->versiondisk)) {
789 return plugin_manager::PLUGIN_STATUS_NODB;
791 } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
792 return plugin_manager::PLUGIN_STATUS_NEW;
794 } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
795 return plugin_manager::PLUGIN_STATUS_MISSING;
797 } else if ((string)$this->versiondb === (string)$this->versiondisk) {
798 return plugin_manager::PLUGIN_STATUS_UPTODATE;
800 } else if ($this->versiondb < $this->versiondisk) {
801 return plugin_manager::PLUGIN_STATUS_UPGRADE;
803 } else if ($this->versiondb > $this->versiondisk) {
804 return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
806 } else {
807 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
808 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
813 * @see plugin_information::is_enabled()
815 public function is_enabled() {
816 return null;
820 * @see plugin_information::get_settings_url()
822 public function get_settings_url() {
823 return null;
827 * @see plugin_information::get_uninstall_url()
829 public function get_uninstall_url() {
830 return null;
834 * @see plugin_information::get_dir()
836 public function get_dir() {
837 global $CFG;
839 return substr($this->rootdir, strlen($CFG->dirroot));
843 * Provides access to plugin versions from {config_plugins}
845 * @param string $plugin plugin name
846 * @param double $disablecache optional, defaults to false
847 * @return int|false the stored value or false if not found
849 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
850 global $DB;
851 static $pluginversions = null;
853 if (is_null($pluginversions) or $disablecache) {
854 try {
855 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
856 } catch (dml_exception $e) {
857 // before install
858 $pluginversions = array();
862 if (!array_key_exists($plugin, $pluginversions)) {
863 return false;
866 return $pluginversions[$plugin];
871 * General class for all plugin types that do not have their own class
873 class plugintype_general extends plugintype_base implements plugin_information {
878 * Class for page side blocks
880 class plugintype_block extends plugintype_base implements plugin_information {
883 * @see plugin_information::get_plugins()
885 public static function get_plugins($type, $typerootdir, $typeclass) {
887 // get the information about blocks at the disk
888 $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
890 // add blocks missing from disk
891 $blocksinfo = self::get_blocks_info();
892 foreach ($blocksinfo as $blockname => $blockinfo) {
893 if (isset($blocks[$blockname])) {
894 continue;
896 $plugin = new $typeclass();
897 $plugin->type = $type;
898 $plugin->typerootdir = $typerootdir;
899 $plugin->name = $blockname;
900 $plugin->rootdir = null;
901 $plugin->displayname = $blockname;
902 $plugin->versiondb = $blockinfo->version;
903 $plugin->init_is_standard();
905 $blocks[$blockname] = $plugin;
908 return $blocks;
912 * @see plugin_information::init_display_name()
914 public function init_display_name() {
916 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
917 $this->displayname = get_string('pluginname', 'block_' . $this->name);
919 } else if (($block = block_instance($this->name)) !== false) {
920 $this->displayname = $block->get_title();
922 } else {
923 parent::init_display_name();
928 * @see plugin_information::load_db_version()
930 public function load_db_version() {
931 global $DB;
933 $blocksinfo = self::get_blocks_info();
934 if (isset($blocksinfo[$this->name]->version)) {
935 $this->versiondb = $blocksinfo[$this->name]->version;
940 * @see plugin_information::is_enabled()
942 public function is_enabled() {
944 $blocksinfo = self::get_blocks_info();
945 if (isset($blocksinfo[$this->name]->visible)) {
946 if ($blocksinfo[$this->name]->visible) {
947 return true;
948 } else {
949 return false;
951 } else {
952 return parent::is_enabled();
957 * @see plugin_information::get_settings_url()
959 public function get_settings_url() {
961 if (($block = block_instance($this->name)) === false) {
962 return parent::get_settings_url();
964 } else if ($block->has_config()) {
965 if (file_exists($this->full_path('settings.php'))) {
966 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
967 } else {
968 $blocksinfo = self::get_blocks_info();
969 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
972 } else {
973 return parent::get_settings_url();
978 * @see plugin_information::get_uninstall_url()
980 public function get_uninstall_url() {
982 $blocksinfo = self::get_blocks_info();
983 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
987 * Provides access to the records in {block} table
989 * @param bool $disablecache do not use internal static cache
990 * @return array array of stdClasses
992 protected static function get_blocks_info($disablecache=false) {
993 global $DB;
994 static $blocksinfocache = null;
996 if (is_null($blocksinfocache) or $disablecache) {
997 try {
998 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
999 } catch (dml_exception $e) {
1000 // before install
1001 $blocksinfocache = array();
1005 return $blocksinfocache;
1010 * Class for text filters
1012 class plugintype_filter extends plugintype_base implements plugin_information {
1015 * @see plugin_information::get_plugins()
1017 public static function get_plugins($type, $typerootdir, $typeclass) {
1018 global $CFG, $DB;
1020 $filters = array();
1022 // get the list of filters from both /filter and /mod location
1023 $installed = filter_get_all_installed();
1025 foreach ($installed as $filterlegacyname => $displayname) {
1026 $plugin = new $typeclass();
1027 $plugin->type = $type;
1028 $plugin->typerootdir = $typerootdir;
1029 $plugin->name = self::normalize_legacy_name($filterlegacyname);
1030 $plugin->rootdir = $CFG->dirroot . '/' . $filterlegacyname;
1031 $plugin->displayname = $displayname;
1033 $plugin->load_disk_version();
1034 $plugin->load_db_version();
1035 $plugin->load_required_main_version();
1036 $plugin->init_is_standard();
1038 $filters[$plugin->name] = $plugin;
1041 $globalstates = self::get_global_states();
1043 if ($DB->get_manager()->table_exists('filter_active')) {
1044 // if we're upgrading from 1.9, the table does not exist yet
1045 // if it does, make sure that all installed filters are registered
1046 $needsreload = false;
1047 foreach (array_keys($installed) as $filterlegacyname) {
1048 if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
1049 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
1050 $needsreload = true;
1053 if ($needsreload) {
1054 $globalstates = self::get_global_states(true);
1058 // make sure that all registered filters are installed, just in case
1059 foreach ($globalstates as $name => $info) {
1060 if (!isset($filters[$name])) {
1061 // oops, there is a record in filter_active but the filter is not installed
1062 $plugin = new $typeclass();
1063 $plugin->type = $type;
1064 $plugin->typerootdir = $typerootdir;
1065 $plugin->name = $name;
1066 $plugin->rootdir = $CFG->dirroot . '/' . $info->legacyname;
1067 $plugin->displayname = $info->legacyname;
1069 $plugin->load_db_version();
1071 if (is_null($plugin->versiondb)) {
1072 // this is a hack to stimulate 'Missing from disk' error
1073 // because $plugin->versiondisk will be null !== false
1074 $plugin->versiondb = false;
1077 $filters[$plugin->name] = $plugin;
1081 return $filters;
1085 * @see plugin_information::init_display_name()
1087 public function init_display_name() {
1088 // do nothing, the name is set in self::get_plugins()
1092 * @see plugintype_base::load_version_php().
1094 protected function load_version_php() {
1095 if (strpos($this->name, 'mod_') === 0) {
1096 // filters bundled with modules do not have a version.php and so
1097 // do not provide their own versioning information.
1098 return new stdClass();
1100 return parent::load_version_php();
1104 * @see plugin_information::is_enabled()
1106 public function is_enabled() {
1108 $globalstates = self::get_global_states();
1110 foreach ($globalstates as $filterlegacyname => $info) {
1111 $name = self::normalize_legacy_name($filterlegacyname);
1112 if ($name === $this->name) {
1113 if ($info->active == TEXTFILTER_DISABLED) {
1114 return false;
1115 } else {
1116 // it may be 'On' or 'Off, but available'
1117 return null;
1122 return null;
1126 * @see plugin_information::get_settings_url()
1128 public function get_settings_url() {
1130 $globalstates = self::get_global_states();
1131 $legacyname = $globalstates[$this->name]->legacyname;
1132 if (filter_has_global_settings($legacyname)) {
1133 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1134 } else {
1135 return null;
1140 * @see plugin_information::get_uninstall_url()
1142 public function get_uninstall_url() {
1144 if (strpos($this->name, 'mod_') === 0) {
1145 return null;
1146 } else {
1147 $globalstates = self::get_global_states();
1148 $legacyname = $globalstates[$this->name]->legacyname;
1149 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1154 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1156 * @param string $legacyfiltername legacy filter name
1157 * @return string frankenstyle-like name
1159 protected static function normalize_legacy_name($legacyfiltername) {
1161 $name = str_replace('/', '_', $legacyfiltername);
1162 if (strpos($name, 'filter_') === 0) {
1163 $name = substr($name, 7);
1164 if (empty($name)) {
1165 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1169 return $name;
1173 * Provides access to the results of {@link filter_get_global_states()}
1174 * but indexed by the normalized filter name
1176 * The legacy filter name is available as ->legacyname property.
1178 * @param bool $disablecache
1179 * @return array
1181 protected static function get_global_states($disablecache=false) {
1182 global $DB;
1183 static $globalstatescache = null;
1185 if ($disablecache or is_null($globalstatescache)) {
1187 if (!$DB->get_manager()->table_exists('filter_active')) {
1188 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1189 // does not exist yet
1190 $globalstatescache = array();
1192 } else {
1193 foreach (filter_get_global_states() as $legacyname => $info) {
1194 $name = self::normalize_legacy_name($legacyname);
1195 $filterinfo = new stdClass();
1196 $filterinfo->legacyname = $legacyname;
1197 $filterinfo->active = $info->active;
1198 $filterinfo->sortorder = $info->sortorder;
1199 $globalstatescache[$name] = $filterinfo;
1204 return $globalstatescache;
1209 * Class for activity modules
1211 class plugintype_mod extends plugintype_base implements plugin_information {
1214 * @see plugin_information::get_plugins()
1216 public static function get_plugins($type, $typerootdir, $typeclass) {
1218 // get the information about plugins at the disk
1219 $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1221 // add modules missing from disk
1222 $modulesinfo = self::get_modules_info();
1223 foreach ($modulesinfo as $modulename => $moduleinfo) {
1224 if (isset($modules[$modulename])) {
1225 continue;
1227 $plugin = new $typeclass();
1228 $plugin->type = $type;
1229 $plugin->typerootdir = $typerootdir;
1230 $plugin->name = $modulename;
1231 $plugin->rootdir = null;
1232 $plugin->displayname = $modulename;
1233 $plugin->versiondb = $moduleinfo->version;
1234 $plugin->init_is_standard();
1236 $modules[$modulename] = $plugin;
1239 return $modules;
1243 * @see plugin_information::init_display_name()
1245 public function init_display_name() {
1246 if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1247 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1248 } else {
1249 $this->displayname = get_string('modulename', $this->type . '_' . $this->name);
1254 * Load the data from version.php.
1255 * @return object the data object defined in version.php.
1257 protected function load_version_php() {
1258 $versionfile = $this->full_path('version.php');
1260 $module = new stdClass();
1261 if (is_readable($versionfile)) {
1262 include($versionfile);
1264 return $module;
1268 * @see plugin_information::load_db_version()
1270 public function load_db_version() {
1271 global $DB;
1273 $modulesinfo = self::get_modules_info();
1274 if (isset($modulesinfo[$this->name]->version)) {
1275 $this->versiondb = $modulesinfo[$this->name]->version;
1280 * @see plugin_information::is_enabled()
1282 public function is_enabled() {
1284 $modulesinfo = self::get_modules_info();
1285 if (isset($modulesinfo[$this->name]->visible)) {
1286 if ($modulesinfo[$this->name]->visible) {
1287 return true;
1288 } else {
1289 return false;
1291 } else {
1292 return parent::is_enabled();
1297 * @see plugin_information::get_settings_url()
1299 public function get_settings_url() {
1301 if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1302 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
1303 } else {
1304 return parent::get_settings_url();
1309 * @see plugin_information::get_uninstall_url()
1311 public function get_uninstall_url() {
1313 if ($this->name !== 'forum') {
1314 return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1315 } else {
1316 return null;
1321 * Provides access to the records in {modules} table
1323 * @param bool $disablecache do not use internal static cache
1324 * @return array array of stdClasses
1326 protected static function get_modules_info($disablecache=false) {
1327 global $DB;
1328 static $modulesinfocache = null;
1330 if (is_null($modulesinfocache) or $disablecache) {
1331 try {
1332 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1333 } catch (dml_exception $e) {
1334 // before install
1335 $modulesinfocache = array();
1339 return $modulesinfocache;
1345 * Class for question behaviours.
1347 class plugintype_qbehaviour extends plugintype_base implements plugin_information {
1350 * @see plugintype_base::load_other_required_plugins().
1352 protected function load_other_required_plugins() {
1353 parent::load_other_required_plugins();
1354 if (!empty($this->dependencies)) {
1355 return;
1358 // Standard mechanism did not find anything, so try the legacy way.
1359 global $CFG;
1360 require_once($CFG->libdir . '/questionlib.php');
1361 $required = question_engine::get_behaviour_required_behaviours($this->name);
1362 foreach ($required as $other) {
1363 $this->dependencies['qbehaviour_' . $other] = ANY_VERSION;
1370 * Class for question types
1372 class plugintype_qtype extends plugintype_base implements plugin_information {
1375 * @see plugin_information::init_display_name()
1377 public function init_display_name() {
1378 if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1379 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1380 } else {
1381 $this->displayname = get_string($this->name, 'qtype_' . $this->name);
1386 * @see plugintype_base::load_other_required_plugins().
1388 protected function load_other_required_plugins() {
1389 parent::load_other_required_plugins();
1390 if (!empty($this->dependencies)) {
1391 return;
1394 // Standard mechanism did not find anything, so try the legacy way.
1395 global $CFG;
1396 require_once($CFG->libdir . '/questionlib.php');
1397 $required = question_bank::get_qtype($this->name)->requires_qtypes();
1398 foreach ($required as $other) {
1399 $this->dependencies['qtype_' . $other] = ANY_VERSION;
1405 * Class for question formats
1407 class plugintype_qformat extends plugintype_base implements plugin_information {
1410 * @see plugin_information::init_display_name()
1412 public function init_display_name() {
1413 if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1414 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1415 } else {
1416 $this->displayname = get_string($this->name, 'qformat_' . $this->name);
1422 * Class for authentication plugins
1424 class plugintype_auth extends plugintype_base implements plugin_information {
1427 * @see plugin_information::is_enabled()
1429 public function is_enabled() {
1430 global $CFG;
1431 /** @var null|array list of enabled authentication plugins */
1432 static $enabled = null;
1434 if (in_array($this->name, array('nologin', 'manual'))) {
1435 // these two are always enabled and can't be disabled
1436 return null;
1439 if (is_null($enabled)) {
1440 $enabled = explode(',', $CFG->auth);
1443 return isset($enabled[$this->name]);
1447 * @see plugin_information::get_settings_url()
1449 public function get_settings_url() {
1450 if (file_exists($this->full_path('settings.php'))) {
1451 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
1452 } else {
1453 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1459 * Class for enrolment plugins
1461 class plugintype_enrol extends plugintype_base implements plugin_information {
1464 * We do not actually need whole enrolment classes here so we do not call
1465 * {@link enrol_get_plugins()}. Note that this may produce slightly different
1466 * results, for example if the enrolment plugin does not contain lib.php
1467 * but it is listed in $CFG->enrol_plugins_enabled
1469 * @see plugin_information::is_enabled()
1471 public function is_enabled() {
1472 global $CFG;
1473 /** @var null|array list of enabled enrolment plugins */
1474 static $enabled = null;
1476 if (is_null($enabled)) {
1477 $enabled = explode(',', $CFG->enrol_plugins_enabled);
1480 return isset($enabled[$this->name]);
1484 * @see plugin_information::get_settings_url()
1486 public function get_settings_url() {
1488 if ($this->is_enabled() or file_exists($this->full_path('settings.php'))) {
1489 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name));
1490 } else {
1491 return parent::get_settings_url();
1496 * @see plugin_information::get_uninstall_url()
1498 public function get_uninstall_url() {
1499 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey()));
1504 * Class for messaging processors
1506 class plugintype_message extends plugintype_base implements plugin_information {
1509 * @see plugin_information::get_settings_url()
1511 public function get_settings_url() {
1513 if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1514 return new moodle_url('/admin/settings.php', array('section' => 'messagesetting' . $this->name));
1515 } else {
1516 return parent::get_settings_url();
1522 * Class for repositories
1524 class plugintype_repository extends plugintype_base implements plugin_information {
1527 * @see plugin_information::is_enabled()
1529 public function is_enabled() {
1531 $enabled = self::get_enabled_repositories();
1533 return isset($enabled[$this->name]);
1537 * @see plugin_information::get_settings_url()
1539 public function get_settings_url() {
1541 if ($this->is_enabled()) {
1542 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
1543 } else {
1544 return parent::get_settings_url();
1549 * Provides access to the records in {repository} table
1551 * @param bool $disablecache do not use internal static cache
1552 * @return array array of stdClasses
1554 protected static function get_enabled_repositories($disablecache=false) {
1555 global $DB;
1556 static $repositories = null;
1558 if (is_null($repositories) or $disablecache) {
1559 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1562 return $repositories;
1567 * Class for portfolios
1569 class plugintype_portfolio extends plugintype_base implements plugin_information {
1572 * @see plugin_information::is_enabled()
1574 public function is_enabled() {
1576 $enabled = self::get_enabled_portfolios();
1578 return isset($enabled[$this->name]);
1582 * Provides access to the records in {portfolio_instance} table
1584 * @param bool $disablecache do not use internal static cache
1585 * @return array array of stdClasses
1587 protected static function get_enabled_portfolios($disablecache=false) {
1588 global $DB;
1589 static $portfolios = null;
1591 if (is_null($portfolios) or $disablecache) {
1592 $portfolios = array();
1593 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
1594 foreach ($instances as $instance) {
1595 if (isset($portfolios[$instance->plugin])) {
1596 if ($instance->visible) {
1597 $portfolios[$instance->plugin]->visible = $instance->visible;
1599 } else {
1600 $portfolios[$instance->plugin] = $instance;
1605 return $portfolios;
1610 * Class for themes
1612 class plugintype_theme extends plugintype_base implements plugin_information {
1615 * @see plugin_information::is_enabled()
1617 public function is_enabled() {
1618 global $CFG;
1620 if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
1621 (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
1622 return true;
1623 } else {
1624 return parent::is_enabled();
1630 * Class representing an MNet service
1632 class plugintype_mnetservice extends plugintype_base implements plugin_information {
1635 * @see plugin_information::is_enabled()
1637 public function is_enabled() {
1638 global $CFG;
1640 if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
1641 return false;
1642 } else {
1643 return parent::is_enabled();
1649 * Class for admin tool plugins
1651 class plugintype_tool extends plugintype_base implements plugin_information {
1653 public function get_uninstall_url() {
1654 return new moodle_url('/admin/tools.php', array('delete' => $this->name, 'sesskey' => sesskey()));