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',
369 'webservice' => array(
370 'amf', 'rest', 'soap', 'xmlrpc'
373 'workshopallocation' => array(
377 'workshopeval' => array(
381 'workshopform' => array(
382 'accumulative', 'comments', 'numerrors', 'rubric'
386 if (isset($standard_plugins[$type])) {
387 return $standard_plugins[$type];
396 * All classes that represent a plugin of some type must implement this interface
398 interface plugintype_interface {
401 * Gathers and returns the information about all plugins of the given type
403 * Passing the parameter $typeclass allows us to reach the same effect as with the
404 * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
405 * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
407 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
408 * @param string $typerootdir full path to the location of the plugin dir
409 * @param string $typeclass the name of the actually called class
410 * @return array of plugintype classes, indexed by the plugin name
412 public static function get_plugins($type, $typerootdir, $typeclass);
415 * Sets $displayname property to a localized name of the plugin
419 public function set_display_name();
422 * Sets $versiondisk property to a numerical value representing the
423 * version of the plugin's source code.
425 * If the value is null after calling this method, either the plugin
426 * does not use versioning (typically does not have any database
427 * data) or is missing from disk.
431 public function set_version_disk();
434 * Sets $versiondb property to a numerical value representing the
435 * currently installed version of the plugin.
437 * If the value is null after calling this method, either the plugin
438 * does not use versioning (typically does not have any database
439 * data) or has not been installed yet.
443 public function set_version_db();
446 * Sets $versionrequires property to a numerical value representing
447 * the version of Moodle core that this plugin requires.
451 public function set_version_requires();
454 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
457 * If the property's value is null after calling this method, then
458 * the type of the plugin has not been recognized and you should throw
463 public function set_source();
466 * Returns true if the plugin is shipped with the official distribution
467 * of the current Moodle version, false otherwise.
471 public function is_standard();
474 * Returns the status of the plugin
476 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
478 public function get_status();
481 * Returns the information about plugin availability
483 * True means that the plugin is enabled. False means that the plugin is
484 * disabled. Null means that the information is not available, or the
485 * plugin does not support configurable availability or the availability
486 * can not be changed.
490 public function is_enabled();
493 * Returns the URL of the plugin settings screen
495 * Null value means that the plugin either does not have the settings screen
496 * or its location is not available via this library.
498 * @return null|moodle_url
500 public function get_settings_url();
503 * Returns the URL of the screen where this plugin can be uninstalled
505 * Visiting that URL must be safe, that is a manual confirmation is needed
506 * for actual uninstallation of the plugin. Null value means that the
507 * plugin either does not support uninstallation, or does not require any
508 * database cleanup or the location of the screen is not available via this
511 * @return null|moodle_url
513 public function get_uninstall_url();
516 * Returns relative directory of the plugin with heading '/'
518 * @example /mod/workshop
521 public function get_dir();
525 * Defines public properties that all plugintype classes must have
526 * and provides default implementation of required methods.
528 abstract class plugintype_base {
530 /** @var string the plugintype name, eg. mod, auth or workshopform */
532 /** @var string full path to the location of all the plugins of this type */
534 /** @var string the plugin name, eg. assignment, ldap */
536 /** @var string the localized plugin name */
538 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
540 /** @var fullpath to the location of this plugin */
542 /** @var int|string the version of the plugin's source code */
544 /** @var int|string the version of the installed plugin */
546 /** @var int|float|string required version of Moodle core */
547 public $versionrequires;
548 /** @var int number of instances of the plugin - not supported yet */
550 /** @var int order of the plugin among other plugins of the same type - not supported yet */
554 * @see plugintype_interface::get_plugins()
556 public static function get_plugins($type, $typerootdir, $typeclass) {
558 // get the information about plugins at the disk
559 $plugins = get_plugin_list($type);
561 foreach ($plugins as $pluginname => $pluginrootdir) {
562 $plugin = new $typeclass();
563 $plugin->type = $type;
564 $plugin->typerootdir = $typerootdir;
565 $plugin->name = $pluginname;
566 $plugin->rootdir = $pluginrootdir;
568 $plugin->set_display_name();
569 $plugin->set_version_disk();
570 $plugin->set_version_db();
571 $plugin->set_version_requires();
572 $plugin->set_source();
574 $ondisk[$pluginname] = $plugin;
580 * @see plugintype_interface::set_display_name()
582 public function set_display_name() {
583 if (! get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
584 $this->displayname = '[pluginname,' . $this->type . '_' . $this->name . ']';
586 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
591 * @see plugintype_interface::set_version_disk()
593 public function set_version_disk() {
595 if (empty($this->rootdir)) {
599 $versionfile = $this->rootdir . '/version.php';
601 if (is_readable($versionfile)) {
602 include($versionfile);
603 if (isset($plugin->version)) {
604 $this->versiondisk = $plugin->version;
610 * @see plugintype_interface::set_version_db()
612 public function set_version_db() {
614 if ($ver = self::get_version_from_config_plugins($this->type . '_' . $this->name)) {
615 $this->versiondb = $ver;
620 * @see plugintype_interface::set_version_requires()
622 public function set_version_requires() {
624 if (empty($this->rootdir)) {
628 $versionfile = $this->rootdir . '/version.php';
630 if (is_readable($versionfile)) {
631 include($versionfile);
632 if (isset($plugin->requires)) {
633 $this->versionrequires = $plugin->requires;
639 * @see plugintype_interface::set_source()
641 public function set_source() {
643 $standard = plugin_manager::standard_plugins_list($this->type);
645 if ($standard !== false) {
646 $standard = array_flip($standard);
647 if (isset($standard[$this->name])) {
648 $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD;
650 $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
656 * @see plugintype_interface::is_standard()
658 public function is_standard() {
659 return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
663 * @see plugintype_interface::get_status()
665 public function get_status() {
667 if (is_null($this->versiondb) and is_null($this->versiondisk)) {
668 return plugin_manager::PLUGIN_STATUS_NODB;
670 } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
671 return plugin_manager::PLUGIN_STATUS_NEW;
673 } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
674 return plugin_manager::PLUGIN_STATUS_MISSING;
676 } else if ((string)$this->versiondb === (string)$this->versiondisk) {
677 return plugin_manager::PLUGIN_STATUS_UPTODATE;
679 } else if ($this->versiondb < $this->versiondisk) {
680 return plugin_manager::PLUGIN_STATUS_UPGRADE;
682 } else if ($this->versiondb > $this->versiondisk) {
683 return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
686 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
687 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
692 * @see plugintype_interface::is_enabled()
694 public function is_enabled() {
699 * @see plugintype_interface::get_settings_url()
701 public function get_settings_url() {
706 * @see plugintype_interface::get_uninstall_url()
708 public function get_uninstall_url() {
713 * @see plugintype_interface::get_dir()
715 public function get_dir() {
718 return substr($this->rootdir, strlen($CFG->dirroot));
722 * Provides access to plugin versions from {config_plugins}
724 * @param string $plugin plugin name
725 * @param double $disablecache optional, defaults to false
726 * @return int|false the stored value or false if not found
728 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
730 static $pluginversions = null;
732 if (is_null($pluginversions) or $disablecache) {
733 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
736 if (!array_key_exists($plugin, $pluginversions)) {
740 return $pluginversions[$plugin];
745 * General class for all plugin types that do not have their own class
747 class plugintype_general extends plugintype_base implements plugintype_interface {
752 * Class for page side blocks
754 class plugintype_block extends plugintype_base implements plugintype_interface {
757 * @see plugintype_interface::get_plugins()
759 public static function get_plugins($type, $typerootdir, $typeclass) {
761 // get the information about blocks at the disk
762 $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
764 // add blocks missing from disk
765 $blocksinfo = self::get_blocks_info();
766 foreach ($blocksinfo as $blockname => $blockinfo) {
767 if (isset($blocks[$blockname])) {
770 $plugin = new $typeclass();
771 $plugin->type = $type;
772 $plugin->typerootdir = $typerootdir;
773 $plugin->name = $blockname;
774 $plugin->rootdir = null;
775 $plugin->displayname = $blockname;
776 $plugin->versiondb = $blockinfo->version;
777 $plugin->set_source();
779 $blocks[$blockname] = $plugin;
786 * @see plugintype_interface::set_display_name()
788 public function set_display_name() {
790 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
791 $this->displayname = get_string('pluginname', 'block_' . $this->name);
793 } else if (($block = block_instance($this->name)) !== false) {
794 $this->displayname = $block->get_title();
797 parent::set_display_name();
802 * @see plugintype_interface::set_version_db()
804 public function set_version_db() {
807 $blocksinfo = self::get_blocks_info();
808 if (isset($blocksinfo[$this->name]->version)) {
809 $this->versiondb = $blocksinfo[$this->name]->version;
814 * @see plugintype_interface::is_enabled()
816 public function is_enabled() {
818 $blocksinfo = self::get_blocks_info();
819 if (isset($blocksinfo[$this->name]->visible)) {
820 if ($blocksinfo[$this->name]->visible) {
826 return parent::is_enabled();
831 * @see plugintype_interface::get_settings_url()
833 public function get_settings_url() {
835 if (($block = block_instance($this->name)) === false) {
836 return parent::get_settings_url();
838 } else if ($block->has_config()) {
839 if (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php')) {
840 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
842 $blocksinfo = self::get_blocks_info();
843 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
847 return parent::get_settings_url();
852 * @see plugintype_interface::get_uninstall_url()
854 public function get_uninstall_url() {
856 $blocksinfo = self::get_blocks_info();
857 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
861 * Provides access to the records in {block} table
863 * @param bool $disablecache do not use internal static cache
864 * @return array array of stdClasses
866 protected static function get_blocks_info($disablecache=false) {
868 static $blocksinfocache = null;
870 if (is_null($blocksinfocache) or $disablecache) {
871 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
874 return $blocksinfocache;
879 * Class for text filters
881 class plugintype_filter extends plugintype_base implements plugintype_interface {
884 * @see plugintype_interface::get_plugins()
886 public static function get_plugins($type, $typerootdir, $typeclass) {
891 // get the list of filters from both /filter and /mod location
892 $installed = filter_get_all_installed();
894 foreach ($installed as $filterlegacyname => $displayname) {
895 $plugin = new $typeclass();
896 $plugin->type = $type;
897 $plugin->typerootdir = $typerootdir;
898 $plugin->name = self::normalize_legacy_name($filterlegacyname);
899 $plugin->rootdir = $CFG->dirroot . '/' . $filterlegacyname;
900 $plugin->displayname = $displayname;
902 $plugin->set_version_disk();
903 $plugin->set_version_db();
904 $plugin->set_version_requires();
905 $plugin->set_source();
907 $filters[$plugin->name] = $plugin;
910 $globalstates = self::get_global_states();
912 if ($DB->get_manager()->table_exists('filter_active')) {
913 // if we're upgrading from 1.9, the table does not exist yet
914 // if it does, make sure that all installed filters are registered
915 $needsreload = false;
916 foreach (array_keys($installed) as $filterlegacyname) {
917 if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
918 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
923 $globalstates = self::get_global_states(true);
927 // make sure that all registered filters are installed, just in case
928 foreach ($globalstates as $name => $info) {
929 if (!isset($filters[$name])) {
930 // oops, there is a record in filter_active but the filter is not installed
931 $plugin = new $typeclass();
932 $plugin->type = $type;
933 $plugin->typerootdir = $typerootdir;
934 $plugin->name = $name;
935 $plugin->rootdir = $CFG->dirroot . '/' . $info->legacyname;
936 $plugin->displayname = $info->legacyname;
938 $plugin->set_version_db();
940 if (is_null($plugin->versiondb)) {
941 // this is a hack to stimulate 'Missing from disk' error
942 // because $plugin->versiondisk will be null !== false
943 $plugin->versiondb = false;
946 $filters[$plugin->name] = $plugin;
954 * @see plugintype_interface::set_display_name()
956 public function set_display_name() {
957 // do nothing, the name is set in self::get_plugins()
961 * @see plugintype_interface::set_version_disk()
963 public function set_version_disk() {
965 if (strpos($this->name, 'mod_') === 0) {
966 // filters bundled with modules do not use versioning
970 return parent::set_version_disk();
974 * @see plugintype_interface::set_version_requires()
976 public function set_version_requires() {
978 if (strpos($this->name, 'mod_') === 0) {
979 // filters bundled with modules do not use versioning
983 return parent::set_version_requires();
987 * @see plugintype_interface::is_enabled()
989 public function is_enabled() {
991 $globalstates = self::get_global_states();
993 foreach ($globalstates as $filterlegacyname => $info) {
994 $name = self::normalize_legacy_name($filterlegacyname);
995 if ($name === $this->name) {
996 if ($info->active == TEXTFILTER_DISABLED) {
999 // it may be 'On' or 'Off, but available'
1009 * @see plugintype_interface::get_settings_url()
1011 public function get_settings_url() {
1013 $globalstates = self::get_global_states();
1014 $legacyname = $globalstates[$this->name]->legacyname;
1015 if (filter_has_global_settings($legacyname)) {
1016 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1023 * @see plugintype_interface::get_uninstall_url()
1025 public function get_uninstall_url() {
1027 if (strpos($this->name, 'mod_') === 0) {
1030 $globalstates = self::get_global_states();
1031 $legacyname = $globalstates[$this->name]->legacyname;
1032 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1037 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1039 * @param string $legacyfiltername legacy filter name
1040 * @return string frankenstyle-like name
1042 protected static function normalize_legacy_name($legacyfiltername) {
1044 $name = str_replace('/', '_', $legacyfiltername);
1045 if (strpos($name, 'filter_') === 0) {
1046 $name = substr($name, 7);
1048 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1056 * Provides access to the results of {@link filter_get_global_states()}
1057 * but indexed by the normalized filter name
1059 * The legacy filter name is available as ->legacyname property.
1061 * @param bool $disablecache
1064 protected static function get_global_states($disablecache=false) {
1066 static $globalstatescache = null;
1068 if ($disablecache or is_null($globalstatescache)) {
1070 if (!$DB->get_manager()->table_exists('filter_active')) {
1071 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1072 // does not exist yet
1073 $globalstatescache = array();
1076 foreach (filter_get_global_states() as $legacyname => $info) {
1077 $name = self::normalize_legacy_name($legacyname);
1078 $filterinfo = new stdClass();
1079 $filterinfo->legacyname = $legacyname;
1080 $filterinfo->active = $info->active;
1081 $filterinfo->sortorder = $info->sortorder;
1082 $globalstatescache[$name] = $filterinfo;
1087 return $globalstatescache;
1092 * Class for activity modules
1094 class plugintype_mod extends plugintype_base implements plugintype_interface {
1097 * @see plugintype_interface::get_plugins()
1099 public static function get_plugins($type, $typerootdir, $typeclass) {
1101 // get the information about plugins at the disk
1102 $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1104 // add modules missing from disk
1105 $modulesinfo = self::get_modules_info();
1106 foreach ($modulesinfo as $modulename => $moduleinfo) {
1107 if (isset($modules[$modulename])) {
1110 $plugin = new $typeclass();
1111 $plugin->type = $type;
1112 $plugin->typerootdir = $typerootdir;
1113 $plugin->name = $modulename;
1114 $plugin->rootdir = null;
1115 $plugin->displayname = $modulename;
1116 $plugin->versiondb = $moduleinfo->version;
1117 $plugin->set_source();
1119 $modules[$modulename] = $plugin;
1126 * @see plugintype_interface::set_display_name()
1128 public function set_display_name() {
1129 if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1130 $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1132 $this->displayname = get_string('modulename', $this->type . '_' . $this->name);
1137 * @see plugintype_interface::set_version_disk()
1139 public function set_version_disk() {
1141 if (empty($this->rootdir)) {
1145 $versionfile = $this->rootdir . '/version.php';
1147 if (is_readable($versionfile)) {
1148 include($versionfile);
1149 if (isset($module->version)) {
1150 $this->versiondisk = $module->version;
1156 * @see plugintype_interface::set_version_db()
1158 public function set_version_db() {
1161 $modulesinfo = self::get_modules_info();
1162 if (isset($modulesinfo[$this->name]->version)) {
1163 $this->versiondb = $modulesinfo[$this->name]->version;
1168 * @see plugintype_interface::set_version_requires()
1170 public function set_version_requires() {
1172 if (empty($this->rootdir)) {
1176 $versionfile = $this->rootdir . '/version.php';
1178 if (is_readable($versionfile)) {
1179 include($versionfile);
1180 if (isset($module->requires)) {
1181 $this->versionrequires = $module->requires;
1187 * @see plugintype_interface::is_enabled()
1189 public function is_enabled() {
1191 $modulesinfo = self::get_modules_info();
1192 if (isset($modulesinfo[$this->name]->visible)) {
1193 if ($modulesinfo[$this->name]->visible) {
1199 return parent::is_enabled();
1204 * @see plugintype_interface::get_settings_url()
1206 public function get_settings_url() {
1208 if (!empty($this->rootdir) and (file_exists($this->rootdir . '/settings.php') or file_exists($this->rootdir . '/settingstree.php'))) {
1209 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
1211 return parent::get_settings_url();
1216 * @see plugintype_interface::get_uninstall_url()
1218 public function get_uninstall_url() {
1220 if ($this->name !== 'forum') {
1221 return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1228 * Provides access to the records in {modules} table
1230 * @param bool $disablecache do not use internal static cache
1231 * @return array array of stdClasses
1233 protected static function get_modules_info($disablecache=false) {
1235 static $modulesinfocache = null;
1237 if (is_null($modulesinfocache) or $disablecache) {
1238 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1241 return $modulesinfocache;
1246 * Class for question types
1248 class plugintype_qtype extends plugintype_base implements plugintype_interface {
1251 * @see plugintype_interface::set_display_name()
1253 public function set_display_name() {
1254 $this->displayname = get_string($this->name, 'qtype_' . $this->name);
1259 * Class for question formats
1261 class plugintype_qformat extends plugintype_base implements plugintype_interface {
1264 * @see plugintype_interface::set_display_name()
1266 public function set_display_name() {
1267 $this->displayname = get_string($this->name, 'qformat_' . $this->name);
1272 * Class for authentication plugins
1274 class plugintype_auth extends plugintype_base implements plugintype_interface {
1277 * @see plugintype_interface::is_enabled()
1279 public function is_enabled() {
1281 /** @var null|array list of enabled authentication plugins */
1282 static $enabled = null;
1284 if (in_array($this->name, array('nologin', 'manual'))) {
1285 // these two are always enabled and can't be disabled
1289 if (is_null($enabled)) {
1290 $enabled = explode(',', $CFG->auth);
1293 return isset($enabled[$this->name]);
1297 * @see plugintype_interface::get_settings_url()
1299 public function get_settings_url() {
1301 if (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php')) {
1302 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
1304 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1310 * Class for enrolment plugins
1312 class plugintype_enrol extends plugintype_base implements plugintype_interface {
1315 * We do not actually need whole enrolment classes here so we do not call
1316 * {@link enrol_get_plugins()}. Note that this may produce slightly different
1317 * results, for example if the enrolment plugin does not contain lib.php
1318 * but it is listed in $CFG->enrol_plugins_enabled
1320 * @see plugintype_interface::is_enabled()
1322 public function is_enabled() {
1324 /** @var null|array list of enabled enrolment plugins */
1325 static $enabled = null;
1327 if (is_null($enabled)) {
1328 $enabled = explode(',', $CFG->enrol_plugins_enabled);
1331 return isset($enabled[$this->name]);
1335 * @see plugintype_interface::get_settings_url()
1337 public function get_settings_url() {
1339 if ($this->is_enabled() or (!empty($this->rootdir) and file_exists($this->rootdir . '/settings.php'))) {
1340 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name));
1342 return parent::get_settings_url();
1347 * @see plugintype_interface::get_uninstall_url()
1349 public function get_uninstall_url() {
1350 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey()));
1355 * Class for messaging processors
1357 class plugintype_message extends plugintype_base implements plugintype_interface {
1360 * @see plugintype_interface::get_settings_url()
1362 public function get_settings_url() {
1364 if ($this->name === 'jabber') {
1365 return new moodle_url('/admin/settings.php', array('section' => 'jabber'));
1368 if ($this->name === 'email') {
1369 return new moodle_url('/admin/settings.php', array('section' => 'mail'));
1376 * Class for repositories
1378 class plugintype_repository extends plugintype_base implements plugintype_interface {
1381 * @see plugintype_interface::is_enabled()
1383 public function is_enabled() {
1385 $enabled = self::get_enabled_repositories();
1387 return isset($enabled[$this->name]);
1391 * @see plugintype_interface::get_settings_url()
1393 public function get_settings_url() {
1395 if ($this->is_enabled()) {
1396 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
1398 return parent::get_settings_url();
1403 * Provides access to the records in {repository} table
1405 * @param bool $disablecache do not use internal static cache
1406 * @return array array of stdClasses
1408 protected static function get_enabled_repositories($disablecache=false) {
1410 static $repositories = null;
1412 if (is_null($repositories) or $disablecache) {
1413 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1416 return $repositories;
1421 * Class for portfolios
1423 class plugintype_portfolio extends plugintype_base implements plugintype_interface {
1426 * @see plugintype_interface::is_enabled()
1428 public function is_enabled() {
1430 $enabled = self::get_enabled_portfolios();
1432 return isset($enabled[$this->name]);
1436 * Provides access to the records in {portfolio_instance} table
1438 * @param bool $disablecache do not use internal static cache
1439 * @return array array of stdClasses
1441 protected static function get_enabled_portfolios($disablecache=false) {
1443 static $portfolios = null;
1445 if (is_null($portfolios) or $disablecache) {
1446 $portfolios = array();
1447 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
1448 foreach ($instances as $instance) {
1449 if (isset($portfolios[$instance->plugin])) {
1450 if ($instance->visible) {
1451 $portfolios[$instance->plugin]->visible = $instance->visible;
1454 $portfolios[$instance->plugin] = $instance;
1466 class plugintype_theme extends plugintype_base implements plugintype_interface {
1469 * @see plugintype_interface::is_enabled()
1471 public function is_enabled() {
1474 if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
1475 (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
1478 return parent::is_enabled();
1484 * Class representing an MNet service
1486 class plugintype_mnetservice extends plugintype_base implements plugintype_interface {
1489 * @see plugintype_interface::is_enabled()
1491 public function is_enabled() {
1494 if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
1497 return parent::is_enabled();