2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
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.
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();
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;
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);
72 * Sorry, this is singleton
74 protected function __clone() {
78 * Factory method for this class
80 * @return plugin_manager the singleton instance
82 public static function instance() {
85 if (is_null(self::$singletoninstance)) {
86 self::$singletoninstance = new self();
88 return self::$singletoninstance;
92 * Returns a tree of known plugins and information about them
94 * @param bool $disablecache force reload, cache can be used otherwise
97 public function get_plugins($disablecache=false) {
99 if ($disablecache or is_null($this->pluginsinfo)) {
100 $this->pluginsinfo = array();
101 $plugintypes = get_plugin_types();
102 foreach ($plugintypes as $plugintype => $plugintyperootdir) {
103 if (in_array($plugintype, array('base', 'general'))) {
104 throw new coding_exception('Illegal usage of reserved word for plugin type');
106 if (class_exists('plugintype_' . $plugintype)) {
107 $plugintypeclass = 'plugintype_' . $plugintype;
109 $plugintypeclass = 'plugintype_general';
111 if (!in_array('plugintype_interface', class_implements($plugintypeclass))) {
112 throw new coding_exception('Class ' . $plugintypeclass . ' must implement plugintype_interface');
114 $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass);
115 $this->pluginsinfo[$plugintype] = $plugins;
119 return $this->pluginsinfo;
123 * Returns list of plugins that define their subplugins and information about them
125 * At the moment, only activity modules can define subplugins.
127 * @param double $disablecache force reload, cache can be used otherwise
130 public function get_subplugins($disablecache=false) {
132 if ($disablecache or is_null($this->subpluginsinfo)) {
133 $this->subpluginsinfo = array();
134 $mods = get_plugin_list('mod');
135 foreach ($mods as $mod => $moddir) {
136 $modsubplugins = array();
137 if (file_exists($moddir . '/db/subplugins.php')) {
138 include($moddir . '/db/subplugins.php');
139 foreach ($subplugins as $subplugintype => $subplugintyperootdir) {
140 $subplugin = new stdClass();
141 $subplugin->type = $subplugintype;
142 $subplugin->typerootdir = $subplugintyperootdir;
143 $modsubplugins[$subplugintype] = $subplugin;
145 $this->subpluginsinfo['mod_' . $mod] = $modsubplugins;
150 return $this->subpluginsinfo;
154 * Returns the name of the plugin that defines the given subplugin type
156 * If the given subplugin type is not actually a subplugin, returns false.
158 * @param string $subplugintype the name of subplugin type, eg. workshopform or quiz
159 * @return false|string the name of the parent plugin, eg. mod_workshop
161 public function get_parent_of_subplugin($subplugintype) {
164 foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
165 if (isset($subplugintypes[$subplugintype])) {
166 $parent = $pluginname;
175 * Returns a localized name of a given plugin
177 * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
180 public function plugin_name($plugin) {
181 list($type, $name) = normalize_component($plugin);
182 return $this->pluginsinfo[$type][$name]->displayname;
186 * Returns a localized name of a plugin type in plural form
188 * Most plugin types define their names in core_plugin lang file. In case of subplugins,
189 * we try to ask the parent plugin for the name. In the worst case, we will return
190 * the value of the passed $type parameter.
192 * @param string $type the type of the plugin, e.g. mod or workshopform
195 public function plugintype_name_plural($type) {
197 if (get_string_manager()->string_exists('type_' . $type . '_plural', 'core_plugin')) {
198 // for most plugin types, their names are defined in core_plugin lang file
199 return get_string('type_' . $type . '_plural', 'core_plugin');
201 } else if ($parent = $this->get_parent_of_subplugin($type)) {
202 // if this is a subplugin, try to ask the parent plugin for the name
203 if (get_string_manager()->string_exists('subplugintype_' . $type . '_plural', $parent)) {
204 return $this->plugin_name($parent) . ' / ' . get_string('subplugintype_' . $type . '_plural', $parent);
206 return $this->plugin_name($parent) . ' / ' . $type;
215 * Defines a white list of all plugins shipped in the standard Moodle distribution
217 * @return false|array array of standard plugins or false if the type is unknown
219 public static function standard_plugins_list($type) {
220 static $standard_plugins = array(
222 'assignment' => array(
223 'offline', 'online', 'upload', 'uploadsingle'
227 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
228 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
229 'shibboleth', 'webservice'
233 'activity_modules', 'admin_bookmarks', 'blog_menu',
234 'blog_recent', 'blog_tags', 'calendar_month',
235 'calendar_upcoming', 'comments', 'community',
236 'completionstatus', 'course_list', 'course_overview',
237 'course_summary', 'feedback', 'glossary_random', 'html',
238 'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile',
239 'navigation', 'news_items', 'online_users', 'participants',
240 'private_files', 'quiz_results', 'recent_activity',
241 'rss_client', 'search', 'search_forums', 'section_links',
242 'selfcompletion', 'settings', 'site_main_menu',
243 'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
246 'coursereport' => array(
247 'completion', 'log', 'outline', 'participation', 'progress', 'stats'
250 'datafield' => array(
251 'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
252 'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
255 'datapreset' => array(
260 'textarea', 'tinymce'
264 'authorize', 'category', 'cohort', 'database', 'flatfile',
265 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
270 'activitynames', 'algebra', 'censor', 'emailprotect',
271 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
272 'urltolink', 'mod_data', 'mod_glossary'
276 'scorm', 'social', 'topics', 'weeks'
279 'gradeexport' => array(
280 'ods', 'txt', 'xls', 'xml'
283 'gradeimport' => array(
287 'gradereport' => array(
288 'grader', 'outcomes', 'overview', 'user'
296 'email', 'jabber', 'popup'
299 'mnetservice' => array(
304 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
305 'forum', 'glossary', 'imscp', 'label', 'lesson', 'page',
306 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
309 'plagiarism' => array(
312 'portfolio' => array(
313 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
316 'profilefield' => array(
317 'checkbox', 'datetime', 'menu', 'text', 'textarea'
320 'qbehaviour' => array(
321 'adaptive', 'adaptivenopenalty', 'deferredcbm',
322 'deferredfeedback', 'immediatecbm', 'immediatefeedback',
323 'informationitem', 'interactive', 'interactivecountback',
324 'manualgraded', 'missing'
328 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
329 'learnwise', 'missingword', 'multianswer', 'qti_two', 'webct',
334 'calculated', 'calculatedmulti', 'calculatedsimple',
335 'description', 'essay', 'match', 'missingtype', 'multianswer',
336 'multichoice', 'numerical', 'random', 'randomsamatch',
337 'shortanswer', 'truefalse'
341 'grading', 'overview', 'responses', 'statistics'
345 'backups', 'capability', 'configlog', 'courseoverview',
346 'customlang', 'log', 'profiling', 'questioninstances',
347 'security', 'spamcleaner', 'stats', 'unittest', 'unsuproles'
350 'repository' => array(
351 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
352 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
353 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
354 'wikimedia', 'youtube'
357 'scormreport' => array(
362 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
363 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
364 'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero',
365 'overlay', 'serenity', 'sky_high', 'splash', 'standard',
372 'webservice' => array(
373 'amf', 'rest', 'soap', 'xmlrpc'
376 'workshopallocation' => array(
380 'workshopeval' => array(
384 'workshopform' => array(
385 'accumulative', 'comments', 'numerrors', 'rubric'
389 if (isset($standard_plugins[$type])) {
390 return $standard_plugins[$type];
399 * All classes that represent a plugin of some type must implement this interface
401 interface plugintype_interface {
404 * Gathers and returns the information about all plugins of the given type
406 * Passing the parameter $typeclass allows us to reach the same effect as with the
407 * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
408 * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
410 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
411 * @param string $typerootdir full path to the location of the plugin dir
412 * @param string $typeclass the name of the actually called class
413 * @return array of plugintype classes, indexed by the plugin name
415 public static function get_plugins($type, $typerootdir, $typeclass);
418 * Sets $displayname property to a localized name of the plugin
422 public function set_display_name();
425 * Sets $versiondisk property to a numerical value representing the
426 * version of the plugin's source code.
428 * If the value is null after calling this method, either the plugin
429 * does not use versioning (typically does not have any database
430 * data) or is missing from disk.
434 public function set_version_disk();
437 * Sets $versiondb property to a numerical value representing the
438 * currently installed version of the plugin.
440 * If the value is null after calling this method, either the plugin
441 * does not use versioning (typically does not have any database
442 * data) or has not been installed yet.
446 public function set_version_db();
449 * Sets $versionrequires property to a numerical value representing
450 * the version of Moodle core that this plugin requires.
454 public function set_version_requires();
457 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
460 * If the property's value is null after calling this method, then
461 * the type of the plugin has not been recognized and you should throw
466 public function set_source();
469 * Returns true if the plugin is shipped with the official distribution
470 * of the current Moodle version, false otherwise.
474 public function is_standard();
477 * Returns the status of the plugin
479 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
481 public function get_status();
484 * Returns the information about plugin availability
486 * True means that the plugin is enabled. False means that the plugin is
487 * disabled. Null means that the information is not available, or the
488 * plugin does not support configurable availability or the availability
489 * can not be changed.
493 public function is_enabled();
496 * Returns the URL of the plugin settings screen
498 * Null value means that the plugin either does not have the settings screen
499 * or its location is not available via this library.
501 * @return null|moodle_url
503 public function get_settings_url();
506 * Returns the URL of the screen where this plugin can be uninstalled
508 * Visiting that URL must be safe, that is a manual confirmation is needed
509 * for actual uninstallation of the plugin. Null value means that the
510 * plugin either does not support uninstallation, or does not require any
511 * database cleanup or the location of the screen is not available via this
514 * @return null|moodle_url
516 public function get_uninstall_url();
519 * Returns relative directory of the plugin with heading '/'
521 * @example /mod/workshop
524 public function get_dir();
528 * Defines public properties that all plugintype classes must have
529 * and provides default implementation of required methods.
531 abstract class plugintype_base {
533 /** @var string the plugintype name, eg. mod, auth or workshopform */
535 /** @var string full path to the location of all the plugins of this type */
537 /** @var string the plugin name, eg. assignment, ldap */
539 /** @var string the localized plugin name */
541 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
543 /** @var fullpath to the location of this plugin */
545 /** @var int|string the version of the plugin's source code */
547 /** @var int|string the version of the installed plugin */
549 /** @var int|float|string required version of Moodle core */
550 public $versionrequires;
551 /** @var int number of instances of the plugin - not supported yet */
553 /** @var int order of the plugin among other plugins of the same type - not supported yet */
557 * @see plugintype_interface::get_plugins()
559 public static function get_plugins($type, $typerootdir, $typeclass) {
561 // get the information about plugins at the disk
562 $plugins = get_plugin_list($type);
564 foreach ($plugins as $pluginname => $pluginrootdir) {
565 $plugin = new $typeclass();
566 $plugin->type = $type;
567 $plugin->typerootdir = $typerootdir;
568 $plugin->name = $pluginname;
569 $plugin->rootdir = $pluginrootdir;
571 $plugin->set_display_name();
572 $plugin->set_version_disk();
573 $plugin->set_version_db();
574 $plugin->set_version_requires();
575 $plugin->set_source();
577 $ondisk[$pluginname] = $plugin;
583 * @see plugintype_interface::set_display_name()
585 public function set_display_name() {
586 if (! get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
587 $this->displayname = '[pluginname,' . $this->type . '_' . $this->name . ']';
589 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
594 * @see plugintype_interface::set_version_disk()
596 public function set_version_disk() {
598 if (empty($this->rootdir)) {
602 $versionfile = $this->rootdir . '/version.php';
604 if (is_readable($versionfile)) {
605 include($versionfile);
606 if (isset($plugin->version)) {
607 $this->versiondisk = $plugin->version;
613 * @see plugintype_interface::set_version_db()
615 public function set_version_db() {
617 if ($ver = self::get_version_from_config_plugins($this->type . '_' . $this->name)) {
618 $this->versiondb = $ver;
623 * @see plugintype_interface::set_version_requires()
625 public function set_version_requires() {
627 if (empty($this->rootdir)) {
631 $versionfile = $this->rootdir . '/version.php';
633 if (is_readable($versionfile)) {
634 include($versionfile);
635 if (isset($plugin->requires)) {
636 $this->versionrequires = $plugin->requires;
642 * @see plugintype_interface::set_source()
644 public function set_source() {
646 $standard = plugin_manager::standard_plugins_list($this->type);
648 if ($standard !== false) {
649 $standard = array_flip($standard);
650 if (isset($standard[$this->name])) {
651 $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD;
653 $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
659 * @see plugintype_interface::is_standard()
661 public function is_standard() {
662 return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
666 * @see plugintype_interface::get_status()
668 public function get_status() {
670 if (is_null($this->versiondb) and is_null($this->versiondisk)) {
671 return plugin_manager::PLUGIN_STATUS_NODB;
673 } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
674 return plugin_manager::PLUGIN_STATUS_NEW;
676 } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
677 return plugin_manager::PLUGIN_STATUS_MISSING;
679 } else if ((string)$this->versiondb === (string)$this->versiondisk) {
680 return plugin_manager::PLUGIN_STATUS_UPTODATE;
682 } else if ($this->versiondb < $this->versiondisk) {
683 return plugin_manager::PLUGIN_STATUS_UPGRADE;
685 } else if ($this->versiondb > $this->versiondisk) {
686 return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
689 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
690 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
695 * @see plugintype_interface::is_enabled()
697 public function is_enabled() {
702 * @see plugintype_interface::get_settings_url()
704 public function get_settings_url() {
709 * @see plugintype_interface::get_uninstall_url()
711 public function get_uninstall_url() {
716 * @see plugintype_interface::get_dir()
718 public function get_dir() {
721 return substr($this->rootdir, strlen($CFG->dirroot));
725 * Provides access to plugin versions from {config_plugins}
727 * @param string $plugin plugin name
728 * @param double $disablecache optional, defaults to false
729 * @return int|false the stored value or false if not found
731 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
733 static $pluginversions = null;
735 if (is_null($pluginversions) or $disablecache) {
736 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
739 if (!array_key_exists($plugin, $pluginversions)) {
743 return $pluginversions[$plugin];
748 * General class for all plugin types that do not have their own class
750 class plugintype_general extends plugintype_base implements plugintype_interface {
755 * Class for page side blocks
757 class plugintype_block extends plugintype_base implements plugintype_interface {
760 * @see plugintype_interface::get_plugins()
762 public static function get_plugins($type, $typerootdir, $typeclass) {
764 // get the information about blocks at the disk
765 $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
767 // add blocks missing from disk
768 $blocksinfo = self::get_blocks_info();
769 foreach ($blocksinfo as $blockname => $blockinfo) {
770 if (isset($blocks[$blockname])) {
773 $plugin = new $typeclass();
774 $plugin->type = $type;
775 $plugin->typerootdir = $typerootdir;
776 $plugin->name = $blockname;
777 $plugin->rootdir = null;
778 $plugin->displayname = $blockname;
779 $plugin->versiondb = $blockinfo->version;
780 $plugin->set_source();
782 $blocks[$blockname] = $plugin;
789 * @see plugintype_interface::set_display_name()
791 public function set_display_name() {
793 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
794 $this->displayname = get_string('pluginname', 'block_' . $this->name);
796 } else if (($block = block_instance($this->name)) !== false) {
797 $this->displayname = $block->get_title();
800 parent::set_display_name();
805 * @see plugintype_interface::set_version_db()
807 public function set_version_db() {
810 $blocksinfo = self::get_blocks_info();
811 if (isset($blocksinfo[$this->name]->version)) {
812 $this->versiondb = $blocksinfo[$this->name]->version;
817 * @see plugintype_interface::is_enabled()
819 public function is_enabled() {
821 $blocksinfo = self::get_blocks_info();
822 if (isset($blocksinfo[$this->name]->visible)) {
823 if ($blocksinfo[$this->name]->visible) {
829 return parent::is_enabled();
834 * @see plugintype_interface::get_settings_url()
836 public function get_settings_url() {
838 if (($block = block_instance($this->name)) === false) {
839 return parent::get_settings_url();
841 } else if ($block->has_config()) {
842 if (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php')) {
843 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
845 $blocksinfo = self::get_blocks_info();
846 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
850 return parent::get_settings_url();
855 * @see plugintype_interface::get_uninstall_url()
857 public function get_uninstall_url() {
859 $blocksinfo = self::get_blocks_info();
860 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
864 * Provides access to the records in {block} table
866 * @param bool $disablecache do not use internal static cache
867 * @return array array of stdClasses
869 protected static function get_blocks_info($disablecache=false) {
871 static $blocksinfocache = null;
873 if (is_null($blocksinfocache) or $disablecache) {
874 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
877 return $blocksinfocache;
882 * Class for text filters
884 class plugintype_filter extends plugintype_base implements plugintype_interface {
887 * @see plugintype_interface::get_plugins()
889 public static function get_plugins($type, $typerootdir, $typeclass) {
894 // get the list of filters from both /filter and /mod location
895 $installed = filter_get_all_installed();
897 foreach ($installed as $filterlegacyname => $displayname) {
898 $plugin = new $typeclass();
899 $plugin->type = $type;
900 $plugin->typerootdir = $typerootdir;
901 $plugin->name = self::normalize_legacy_name($filterlegacyname);
902 $plugin->rootdir = $CFG->dirroot . '/' . $filterlegacyname;
903 $plugin->displayname = $displayname;
905 $plugin->set_version_disk();
906 $plugin->set_version_db();
907 $plugin->set_version_requires();
908 $plugin->set_source();
910 $filters[$plugin->name] = $plugin;
913 $globalstates = self::get_global_states();
915 if ($DB->get_manager()->table_exists('filter_active')) {
916 // if we're upgrading from 1.9, the table does not exist yet
917 // if it does, make sure that all installed filters are registered
918 $needsreload = false;
919 foreach (array_keys($installed) as $filterlegacyname) {
920 if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
921 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
926 $globalstates = self::get_global_states(true);
930 // make sure that all registered filters are installed, just in case
931 foreach ($globalstates as $name => $info) {
932 if (!isset($filters[$name])) {
933 // oops, there is a record in filter_active but the filter is not installed
934 $plugin = new $typeclass();
935 $plugin->type = $type;
936 $plugin->typerootdir = $typerootdir;
937 $plugin->name = $name;
938 $plugin->rootdir = $CFG->dirroot . '/' . $info->legacyname;
939 $plugin->displayname = $info->legacyname;
941 $plugin->set_version_db();
943 if (is_null($plugin->versiondb)) {
944 // this is a hack to stimulate 'Missing from disk' error
945 // because $plugin->versiondisk will be null !== false
946 $plugin->versiondb = false;
949 $filters[$plugin->name] = $plugin;
957 * @see plugintype_interface::set_display_name()
959 public function set_display_name() {
960 // do nothing, the name is set in self::get_plugins()
964 * @see plugintype_interface::set_version_disk()
966 public function set_version_disk() {
968 if (strpos($this->name, 'mod_') === 0) {
969 // filters bundled with modules do not use versioning
973 return parent::set_version_disk();
977 * @see plugintype_interface::set_version_requires()
979 public function set_version_requires() {
981 if (strpos($this->name, 'mod_') === 0) {
982 // filters bundled with modules do not use versioning
986 return parent::set_version_requires();
990 * @see plugintype_interface::is_enabled()
992 public function is_enabled() {
994 $globalstates = self::get_global_states();
996 foreach ($globalstates as $filterlegacyname => $info) {
997 $name = self::normalize_legacy_name($filterlegacyname);
998 if ($name === $this->name) {
999 if ($info->active == TEXTFILTER_DISABLED) {
1002 // it may be 'On' or 'Off, but available'
1012 * @see plugintype_interface::get_settings_url()
1014 public function get_settings_url() {
1016 $globalstates = self::get_global_states();
1017 $legacyname = $globalstates[$this->name]->legacyname;
1018 if (filter_has_global_settings($legacyname)) {
1019 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1026 * @see plugintype_interface::get_uninstall_url()
1028 public function get_uninstall_url() {
1030 if (strpos($this->name, 'mod_') === 0) {
1033 $globalstates = self::get_global_states();
1034 $legacyname = $globalstates[$this->name]->legacyname;
1035 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1040 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1042 * @param string $legacyfiltername legacy filter name
1043 * @return string frankenstyle-like name
1045 protected static function normalize_legacy_name($legacyfiltername) {
1047 $name = str_replace('/', '_', $legacyfiltername);
1048 if (strpos($name, 'filter_') === 0) {
1049 $name = substr($name, 7);
1051 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1059 * Provides access to the results of {@link filter_get_global_states()}
1060 * but indexed by the normalized filter name
1062 * The legacy filter name is available as ->legacyname property.
1064 * @param bool $disablecache
1067 protected static function get_global_states($disablecache=false) {
1069 static $globalstatescache = null;
1071 if ($disablecache or is_null($globalstatescache)) {
1073 if (!$DB->get_manager()->table_exists('filter_active')) {
1074 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1075 // does not exist yet
1076 $globalstatescache = array();
1079 foreach (filter_get_global_states() as $legacyname => $info) {
1080 $name = self::normalize_legacy_name($legacyname);
1081 $filterinfo = new stdClass();
1082 $filterinfo->legacyname = $legacyname;
1083 $filterinfo->active = $info->active;
1084 $filterinfo->sortorder = $info->sortorder;
1085 $globalstatescache[$name] = $filterinfo;
1090 return $globalstatescache;
1095 * Class for activity modules
1097 class plugintype_mod extends plugintype_base implements plugintype_interface {
1100 * @see plugintype_interface::get_plugins()
1102 public static function get_plugins($type, $typerootdir, $typeclass) {
1104 // get the information about plugins at the disk
1105 $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1107 // add modules missing from disk
1108 $modulesinfo = self::get_modules_info();
1109 foreach ($modulesinfo as $modulename => $moduleinfo) {
1110 if (isset($modules[$modulename])) {
1113 $plugin = new $typeclass();
1114 $plugin->type = $type;
1115 $plugin->typerootdir = $typerootdir;
1116 $plugin->name = $modulename;
1117 $plugin->rootdir = null;
1118 $plugin->displayname = $modulename;
1119 $plugin->versiondb = $moduleinfo->version;
1120 $plugin->set_source();
1122 $modules[$modulename] = $plugin;
1129 * @see plugintype_interface::set_display_name()
1131 public function set_display_name() {
1132 if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1133 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1135 $this->displayname = get_string('modulename', $this->type . '_' . $this->name);
1140 * @see plugintype_interface::set_version_disk()
1142 public function set_version_disk() {
1144 if (empty($this->rootdir)) {
1148 $versionfile = $this->rootdir . '/version.php';
1150 if (is_readable($versionfile)) {
1151 include($versionfile);
1152 if (isset($module->version)) {
1153 $this->versiondisk = $module->version;
1159 * @see plugintype_interface::set_version_db()
1161 public function set_version_db() {
1164 $modulesinfo = self::get_modules_info();
1165 if (isset($modulesinfo[$this->name]->version)) {
1166 $this->versiondb = $modulesinfo[$this->name]->version;
1171 * @see plugintype_interface::set_version_requires()
1173 public function set_version_requires() {
1175 if (empty($this->rootdir)) {
1179 $versionfile = $this->rootdir . '/version.php';
1181 if (is_readable($versionfile)) {
1182 include($versionfile);
1183 if (isset($module->requires)) {
1184 $this->versionrequires = $module->requires;
1190 * @see plugintype_interface::is_enabled()
1192 public function is_enabled() {
1194 $modulesinfo = self::get_modules_info();
1195 if (isset($modulesinfo[$this->name]->visible)) {
1196 if ($modulesinfo[$this->name]->visible) {
1202 return parent::is_enabled();
1207 * @see plugintype_interface::get_settings_url()
1209 public function get_settings_url() {
1211 if (!empty($this->rootdir) and (file_exists($this->rootdir . '/settings.php') or file_exists($this->rootdir . '/settingstree.php'))) {
1212 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
1214 return parent::get_settings_url();
1219 * @see plugintype_interface::get_uninstall_url()
1221 public function get_uninstall_url() {
1223 if ($this->name !== 'forum') {
1224 return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1231 * Provides access to the records in {modules} table
1233 * @param bool $disablecache do not use internal static cache
1234 * @return array array of stdClasses
1236 protected static function get_modules_info($disablecache=false) {
1238 static $modulesinfocache = null;
1240 if (is_null($modulesinfocache) or $disablecache) {
1241 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1244 return $modulesinfocache;
1249 * Class for question types
1251 class plugintype_qtype extends plugintype_base implements plugintype_interface {
1254 * @see plugintype_interface::set_display_name()
1256 public function set_display_name() {
1257 $this->displayname = get_string($this->name, 'qtype_' . $this->name);
1262 * Class for question formats
1264 class plugintype_qformat extends plugintype_base implements plugintype_interface {
1267 * @see plugintype_interface::set_display_name()
1269 public function set_display_name() {
1270 $this->displayname = get_string($this->name, 'qformat_' . $this->name);
1275 * Class for authentication plugins
1277 class plugintype_auth extends plugintype_base implements plugintype_interface {
1280 * @see plugintype_interface::is_enabled()
1282 public function is_enabled() {
1284 /** @var null|array list of enabled authentication plugins */
1285 static $enabled = null;
1287 if (in_array($this->name, array('nologin', 'manual'))) {
1288 // these two are always enabled and can't be disabled
1292 if (is_null($enabled)) {
1293 $enabled = explode(',', $CFG->auth);
1296 return isset($enabled[$this->name]);
1300 * @see plugintype_interface::get_settings_url()
1302 public function get_settings_url() {
1304 if (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php')) {
1305 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
1307 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1313 * Class for enrolment plugins
1315 class plugintype_enrol extends plugintype_base implements plugintype_interface {
1318 * We do not actually need whole enrolment classes here so we do not call
1319 * {@link enrol_get_plugins()}. Note that this may produce slightly different
1320 * results, for example if the enrolment plugin does not contain lib.php
1321 * but it is listed in $CFG->enrol_plugins_enabled
1323 * @see plugintype_interface::is_enabled()
1325 public function is_enabled() {
1327 /** @var null|array list of enabled enrolment plugins */
1328 static $enabled = null;
1330 if (is_null($enabled)) {
1331 $enabled = explode(',', $CFG->enrol_plugins_enabled);
1334 return isset($enabled[$this->name]);
1338 * @see plugintype_interface::get_settings_url()
1340 public function get_settings_url() {
1342 if ($this->is_enabled() or (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php'))) {
1343 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name));
1345 return parent::get_settings_url();
1350 * @see plugintype_interface::get_uninstall_url()
1352 public function get_uninstall_url() {
1353 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey()));
1358 * Class for messaging processors
1360 class plugintype_message extends plugintype_base implements plugintype_interface {
1363 * @see plugintype_interface::get_settings_url()
1365 public function get_settings_url() {
1367 if ($this->name === 'jabber') {
1368 return new moodle_url('/admin/settings.php', array('section' => 'jabber'));
1371 if ($this->name === 'email') {
1372 return new moodle_url('/admin/settings.php', array('section' => 'mail'));
1379 * Class for repositories
1381 class plugintype_repository extends plugintype_base implements plugintype_interface {
1384 * @see plugintype_interface::is_enabled()
1386 public function is_enabled() {
1388 $enabled = self::get_enabled_repositories();
1390 return isset($enabled[$this->name]);
1394 * @see plugintype_interface::get_settings_url()
1396 public function get_settings_url() {
1398 if ($this->is_enabled()) {
1399 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
1401 return parent::get_settings_url();
1406 * Provides access to the records in {repository} table
1408 * @param bool $disablecache do not use internal static cache
1409 * @return array array of stdClasses
1411 protected static function get_enabled_repositories($disablecache=false) {
1413 static $repositories = null;
1415 if (is_null($repositories) or $disablecache) {
1416 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1419 return $repositories;
1424 * Class for portfolios
1426 class plugintype_portfolio extends plugintype_base implements plugintype_interface {
1429 * @see plugintype_interface::is_enabled()
1431 public function is_enabled() {
1433 $enabled = self::get_enabled_portfolios();
1435 return isset($enabled[$this->name]);
1439 * Provides access to the records in {portfolio_instance} table
1441 * @param bool $disablecache do not use internal static cache
1442 * @return array array of stdClasses
1444 protected static function get_enabled_portfolios($disablecache=false) {
1446 static $portfolios = null;
1448 if (is_null($portfolios) or $disablecache) {
1449 $portfolios = array();
1450 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
1451 foreach ($instances as $instance) {
1452 if (isset($portfolios[$instance->plugin])) {
1453 if ($instance->visible) {
1454 $portfolios[$instance->plugin]->visible = $instance->visible;
1457 $portfolios[$instance->plugin] = $instance;
1469 class plugintype_theme extends plugintype_base implements plugintype_interface {
1472 * @see plugintype_interface::is_enabled()
1474 public function is_enabled() {
1477 if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
1478 (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
1481 return parent::is_enabled();
1487 * Class representing an MNet service
1489 class plugintype_mnetservice extends plugintype_base implements plugintype_interface {
1492 * @see plugintype_interface::is_enabled()
1494 public function is_enabled() {
1497 if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
1500 return parent::is_enabled();