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'
295 'email', 'jabber', 'popup'
298 'mnetservice' => array(
303 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
304 'forum', 'glossary', 'imscp', 'label', 'lesson', 'page',
305 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
308 'plagiarism' => array(
311 'portfolio' => array(
312 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
315 'profilefield' => array(
316 'checkbox', 'datetime', 'menu', 'text', 'textarea'
319 'qbehaviour' => array(
320 'adaptive', 'adaptivenopenalty', 'deferredcbm',
321 'deferredfeedback', 'immediatecbm', 'immediatefeedback',
322 'informationitem', 'interactive', 'interactivecountback',
323 'manualgraded', 'missing'
327 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
328 'learnwise', 'missingword', 'multianswer', 'qti_two', 'webct',
333 'calculated', 'calculatedmulti', 'calculatedsimple',
334 'description', 'essay', 'match', 'missingtype', 'multianswer',
335 'multichoice', 'numerical', 'random', 'randomsamatch',
336 'shortanswer', 'truefalse'
340 'grading', 'overview', 'responses', 'statistics'
344 'backups', 'configlog', 'courseoverview',
345 'log', 'questioninstances', 'security', 'stats'
348 'repository' => array(
349 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
350 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
351 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
352 'wikimedia', 'youtube'
355 'scormreport' => array(
360 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
361 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
362 'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero',
363 'overlay', 'serenity', 'sky_high', 'splash', 'standard',
368 'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
369 'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
370 'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
371 'uploaduser', 'unsuproles', 'xmldb'
374 'webservice' => array(
375 'amf', 'rest', 'soap', 'xmlrpc'
378 'workshopallocation' => array(
382 'workshopeval' => array(
386 'workshopform' => array(
387 'accumulative', 'comments', 'numerrors', 'rubric'
391 if (isset($standard_plugins[$type])) {
392 return $standard_plugins[$type];
401 * All classes that represent a plugin of some type must implement this interface
403 interface plugintype_interface
{
406 * Gathers and returns the information about all plugins of the given type
408 * Passing the parameter $typeclass allows us to reach the same effect as with the
409 * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
410 * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
412 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
413 * @param string $typerootdir full path to the location of the plugin dir
414 * @param string $typeclass the name of the actually called class
415 * @return array of plugintype classes, indexed by the plugin name
417 public static function get_plugins($type, $typerootdir, $typeclass);
420 * Sets $displayname property to a localized name of the plugin
424 public function set_display_name();
427 * Sets $versiondisk property to a numerical value representing the
428 * version of the plugin's source code.
430 * If the value is null after calling this method, either the plugin
431 * does not use versioning (typically does not have any database
432 * data) or is missing from disk.
436 public function set_version_disk();
439 * Sets $versiondb property to a numerical value representing the
440 * currently installed version of the plugin.
442 * If the value is null after calling this method, either the plugin
443 * does not use versioning (typically does not have any database
444 * data) or has not been installed yet.
448 public function set_version_db();
451 * Sets $versionrequires property to a numerical value representing
452 * the version of Moodle core that this plugin requires.
456 public function set_version_requires();
459 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
462 * If the property's value is null after calling this method, then
463 * the type of the plugin has not been recognized and you should throw
468 public function set_source();
471 * Returns true if the plugin is shipped with the official distribution
472 * of the current Moodle version, false otherwise.
476 public function is_standard();
479 * Returns the status of the plugin
481 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
483 public function get_status();
486 * Returns the information about plugin availability
488 * True means that the plugin is enabled. False means that the plugin is
489 * disabled. Null means that the information is not available, or the
490 * plugin does not support configurable availability or the availability
491 * can not be changed.
495 public function is_enabled();
498 * Returns the URL of the plugin settings screen
500 * Null value means that the plugin either does not have the settings screen
501 * or its location is not available via this library.
503 * @return null|moodle_url
505 public function get_settings_url();
508 * Returns the URL of the screen where this plugin can be uninstalled
510 * Visiting that URL must be safe, that is a manual confirmation is needed
511 * for actual uninstallation of the plugin. Null value means that the
512 * plugin either does not support uninstallation, or does not require any
513 * database cleanup or the location of the screen is not available via this
516 * @return null|moodle_url
518 public function get_uninstall_url();
521 * Returns relative directory of the plugin with heading '/'
523 * @example /mod/workshop
526 public function get_dir();
530 * Defines public properties that all plugintype classes must have
531 * and provides default implementation of required methods.
533 abstract class plugintype_base
{
535 /** @var string the plugintype name, eg. mod, auth or workshopform */
537 /** @var string full path to the location of all the plugins of this type */
539 /** @var string the plugin name, eg. assignment, ldap */
541 /** @var string the localized plugin name */
543 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
545 /** @var fullpath to the location of this plugin */
547 /** @var int|string the version of the plugin's source code */
549 /** @var int|string the version of the installed plugin */
551 /** @var int|float|string required version of Moodle core */
552 public $versionrequires;
553 /** @var int number of instances of the plugin - not supported yet */
555 /** @var int order of the plugin among other plugins of the same type - not supported yet */
559 * @see plugintype_interface::get_plugins()
561 public static function get_plugins($type, $typerootdir, $typeclass) {
563 // get the information about plugins at the disk
564 $plugins = get_plugin_list($type);
566 foreach ($plugins as $pluginname => $pluginrootdir) {
567 $plugin = new $typeclass();
568 $plugin->type
= $type;
569 $plugin->typerootdir
= $typerootdir;
570 $plugin->name
= $pluginname;
571 $plugin->rootdir
= $pluginrootdir;
573 $plugin->set_display_name();
574 $plugin->set_version_disk();
575 $plugin->set_version_db();
576 $plugin->set_version_requires();
577 $plugin->set_source();
579 $ondisk[$pluginname] = $plugin;
585 * @see plugintype_interface::set_display_name()
587 public function set_display_name() {
588 if (! get_string_manager()->string_exists('pluginname', $this->type
. '_' . $this->name
)) {
589 $this->displayname
= '[pluginname,' . $this->type
. '_' . $this->name
. ']';
591 $this->displayname
= get_string('pluginname', $this->type
. '_' . $this->name
);
596 * @see plugintype_interface::set_version_disk()
598 public function set_version_disk() {
600 if (empty($this->rootdir
)) {
604 $versionfile = $this->rootdir
. '/version.php';
606 if (is_readable($versionfile)) {
607 include($versionfile);
608 if (isset($plugin->version
)) {
609 $this->versiondisk
= $plugin->version
;
615 * @see plugintype_interface::set_version_db()
617 public function set_version_db() {
619 if ($ver = self
::get_version_from_config_plugins($this->type
. '_' . $this->name
)) {
620 $this->versiondb
= $ver;
625 * @see plugintype_interface::set_version_requires()
627 public function set_version_requires() {
629 if (empty($this->rootdir
)) {
633 $versionfile = $this->rootdir
. '/version.php';
635 if (is_readable($versionfile)) {
636 include($versionfile);
637 if (isset($plugin->requires
)) {
638 $this->versionrequires
= $plugin->requires
;
644 * @see plugintype_interface::set_source()
646 public function set_source() {
648 $standard = plugin_manager
::standard_plugins_list($this->type
);
650 if ($standard !== false) {
651 $standard = array_flip($standard);
652 if (isset($standard[$this->name
])) {
653 $this->source
= plugin_manager
::PLUGIN_SOURCE_STANDARD
;
655 $this->source
= plugin_manager
::PLUGIN_SOURCE_EXTENSION
;
661 * @see plugintype_interface::is_standard()
663 public function is_standard() {
664 return $this->source
=== plugin_manager
::PLUGIN_SOURCE_STANDARD
;
668 * @see plugintype_interface::get_status()
670 public function get_status() {
672 if (is_null($this->versiondb
) and is_null($this->versiondisk
)) {
673 return plugin_manager
::PLUGIN_STATUS_NODB
;
675 } else if (is_null($this->versiondb
) and !is_null($this->versiondisk
)) {
676 return plugin_manager
::PLUGIN_STATUS_NEW
;
678 } else if (!is_null($this->versiondb
) and is_null($this->versiondisk
)) {
679 return plugin_manager
::PLUGIN_STATUS_MISSING
;
681 } else if ((string)$this->versiondb
=== (string)$this->versiondisk
) {
682 return plugin_manager
::PLUGIN_STATUS_UPTODATE
;
684 } else if ($this->versiondb
< $this->versiondisk
) {
685 return plugin_manager
::PLUGIN_STATUS_UPGRADE
;
687 } else if ($this->versiondb
> $this->versiondisk
) {
688 return plugin_manager
::PLUGIN_STATUS_DOWNGRADE
;
691 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
692 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
697 * @see plugintype_interface::is_enabled()
699 public function is_enabled() {
704 * @see plugintype_interface::get_settings_url()
706 public function get_settings_url() {
711 * @see plugintype_interface::get_uninstall_url()
713 public function get_uninstall_url() {
718 * @see plugintype_interface::get_dir()
720 public function get_dir() {
723 return substr($this->rootdir
, strlen($CFG->dirroot
));
727 * Provides access to plugin versions from {config_plugins}
729 * @param string $plugin plugin name
730 * @param double $disablecache optional, defaults to false
731 * @return int|false the stored value or false if not found
733 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
735 static $pluginversions = null;
737 if (is_null($pluginversions) or $disablecache) {
738 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
741 if (!array_key_exists($plugin, $pluginversions)) {
745 return $pluginversions[$plugin];
750 * General class for all plugin types that do not have their own class
752 class plugintype_general
extends plugintype_base
implements plugintype_interface
{
757 * Class for page side blocks
759 class plugintype_block
extends plugintype_base
implements plugintype_interface
{
762 * @see plugintype_interface::get_plugins()
764 public static function get_plugins($type, $typerootdir, $typeclass) {
766 // get the information about blocks at the disk
767 $blocks = parent
::get_plugins($type, $typerootdir, $typeclass);
769 // add blocks missing from disk
770 $blocksinfo = self
::get_blocks_info();
771 foreach ($blocksinfo as $blockname => $blockinfo) {
772 if (isset($blocks[$blockname])) {
775 $plugin = new $typeclass();
776 $plugin->type
= $type;
777 $plugin->typerootdir
= $typerootdir;
778 $plugin->name
= $blockname;
779 $plugin->rootdir
= null;
780 $plugin->displayname
= $blockname;
781 $plugin->versiondb
= $blockinfo->version
;
782 $plugin->set_source();
784 $blocks[$blockname] = $plugin;
791 * @see plugintype_interface::set_display_name()
793 public function set_display_name() {
795 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name
)) {
796 $this->displayname
= get_string('pluginname', 'block_' . $this->name
);
798 } else if (($block = block_instance($this->name
)) !== false) {
799 $this->displayname
= $block->get_title();
802 parent
::set_display_name();
807 * @see plugintype_interface::set_version_db()
809 public function set_version_db() {
812 $blocksinfo = self
::get_blocks_info();
813 if (isset($blocksinfo[$this->name
]->version
)) {
814 $this->versiondb
= $blocksinfo[$this->name
]->version
;
819 * @see plugintype_interface::is_enabled()
821 public function is_enabled() {
823 $blocksinfo = self
::get_blocks_info();
824 if (isset($blocksinfo[$this->name
]->visible
)) {
825 if ($blocksinfo[$this->name
]->visible
) {
831 return parent
::is_enabled();
836 * @see plugintype_interface::get_settings_url()
838 public function get_settings_url() {
840 if (($block = block_instance($this->name
)) === false) {
841 return parent
::get_settings_url();
843 } else if ($block->has_config()) {
844 if (!empty($this->rootdir
) and file_exists($this->rootdir
. '/settings.php')) {
845 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name
));
847 $blocksinfo = self
::get_blocks_info();
848 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name
]->id
));
852 return parent
::get_settings_url();
857 * @see plugintype_interface::get_uninstall_url()
859 public function get_uninstall_url() {
861 $blocksinfo = self
::get_blocks_info();
862 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name
]->id
, 'sesskey' => sesskey()));
866 * Provides access to the records in {block} table
868 * @param bool $disablecache do not use internal static cache
869 * @return array array of stdClasses
871 protected static function get_blocks_info($disablecache=false) {
873 static $blocksinfocache = null;
875 if (is_null($blocksinfocache) or $disablecache) {
876 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
879 return $blocksinfocache;
884 * Class for text filters
886 class plugintype_filter
extends plugintype_base
implements plugintype_interface
{
889 * @see plugintype_interface::get_plugins()
891 public static function get_plugins($type, $typerootdir, $typeclass) {
896 // get the list of filters from both /filter and /mod location
897 $installed = filter_get_all_installed();
899 foreach ($installed as $filterlegacyname => $displayname) {
900 $plugin = new $typeclass();
901 $plugin->type
= $type;
902 $plugin->typerootdir
= $typerootdir;
903 $plugin->name
= self
::normalize_legacy_name($filterlegacyname);
904 $plugin->rootdir
= $CFG->dirroot
. '/' . $filterlegacyname;
905 $plugin->displayname
= $displayname;
907 $plugin->set_version_disk();
908 $plugin->set_version_db();
909 $plugin->set_version_requires();
910 $plugin->set_source();
912 $filters[$plugin->name
] = $plugin;
915 $globalstates = self
::get_global_states();
917 if ($DB->get_manager()->table_exists('filter_active')) {
918 // if we're upgrading from 1.9, the table does not exist yet
919 // if it does, make sure that all installed filters are registered
920 $needsreload = false;
921 foreach (array_keys($installed) as $filterlegacyname) {
922 if (!isset($globalstates[self
::normalize_legacy_name($filterlegacyname)])) {
923 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED
);
928 $globalstates = self
::get_global_states(true);
932 // make sure that all registered filters are installed, just in case
933 foreach ($globalstates as $name => $info) {
934 if (!isset($filters[$name])) {
935 // oops, there is a record in filter_active but the filter is not installed
936 $plugin = new $typeclass();
937 $plugin->type
= $type;
938 $plugin->typerootdir
= $typerootdir;
939 $plugin->name
= $name;
940 $plugin->rootdir
= $CFG->dirroot
. '/' . $info->legacyname
;
941 $plugin->displayname
= $info->legacyname
;
943 $plugin->set_version_db();
945 if (is_null($plugin->versiondb
)) {
946 // this is a hack to stimulate 'Missing from disk' error
947 // because $plugin->versiondisk will be null !== false
948 $plugin->versiondb
= false;
951 $filters[$plugin->name
] = $plugin;
959 * @see plugintype_interface::set_display_name()
961 public function set_display_name() {
962 // do nothing, the name is set in self::get_plugins()
966 * @see plugintype_interface::set_version_disk()
968 public function set_version_disk() {
970 if (strpos($this->name
, 'mod_') === 0) {
971 // filters bundled with modules do not use versioning
975 return parent
::set_version_disk();
979 * @see plugintype_interface::set_version_requires()
981 public function set_version_requires() {
983 if (strpos($this->name
, 'mod_') === 0) {
984 // filters bundled with modules do not use versioning
988 return parent
::set_version_requires();
992 * @see plugintype_interface::is_enabled()
994 public function is_enabled() {
996 $globalstates = self
::get_global_states();
998 foreach ($globalstates as $filterlegacyname => $info) {
999 $name = self
::normalize_legacy_name($filterlegacyname);
1000 if ($name === $this->name
) {
1001 if ($info->active
== TEXTFILTER_DISABLED
) {
1004 // it may be 'On' or 'Off, but available'
1014 * @see plugintype_interface::get_settings_url()
1016 public function get_settings_url() {
1018 $globalstates = self
::get_global_states();
1019 $legacyname = $globalstates[$this->name
]->legacyname
;
1020 if (filter_has_global_settings($legacyname)) {
1021 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1028 * @see plugintype_interface::get_uninstall_url()
1030 public function get_uninstall_url() {
1032 if (strpos($this->name
, 'mod_') === 0) {
1035 $globalstates = self
::get_global_states();
1036 $legacyname = $globalstates[$this->name
]->legacyname
;
1037 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1042 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1044 * @param string $legacyfiltername legacy filter name
1045 * @return string frankenstyle-like name
1047 protected static function normalize_legacy_name($legacyfiltername) {
1049 $name = str_replace('/', '_', $legacyfiltername);
1050 if (strpos($name, 'filter_') === 0) {
1051 $name = substr($name, 7);
1053 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1061 * Provides access to the results of {@link filter_get_global_states()}
1062 * but indexed by the normalized filter name
1064 * The legacy filter name is available as ->legacyname property.
1066 * @param bool $disablecache
1069 protected static function get_global_states($disablecache=false) {
1071 static $globalstatescache = null;
1073 if ($disablecache or is_null($globalstatescache)) {
1075 if (!$DB->get_manager()->table_exists('filter_active')) {
1076 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1077 // does not exist yet
1078 $globalstatescache = array();
1081 foreach (filter_get_global_states() as $legacyname => $info) {
1082 $name = self
::normalize_legacy_name($legacyname);
1083 $filterinfo = new stdClass();
1084 $filterinfo->legacyname
= $legacyname;
1085 $filterinfo->active
= $info->active
;
1086 $filterinfo->sortorder
= $info->sortorder
;
1087 $globalstatescache[$name] = $filterinfo;
1092 return $globalstatescache;
1097 * Class for activity modules
1099 class plugintype_mod
extends plugintype_base
implements plugintype_interface
{
1102 * @see plugintype_interface::get_plugins()
1104 public static function get_plugins($type, $typerootdir, $typeclass) {
1106 // get the information about plugins at the disk
1107 $modules = parent
::get_plugins($type, $typerootdir, $typeclass);
1109 // add modules missing from disk
1110 $modulesinfo = self
::get_modules_info();
1111 foreach ($modulesinfo as $modulename => $moduleinfo) {
1112 if (isset($modules[$modulename])) {
1115 $plugin = new $typeclass();
1116 $plugin->type
= $type;
1117 $plugin->typerootdir
= $typerootdir;
1118 $plugin->name
= $modulename;
1119 $plugin->rootdir
= null;
1120 $plugin->displayname
= $modulename;
1121 $plugin->versiondb
= $moduleinfo->version
;
1122 $plugin->set_source();
1124 $modules[$modulename] = $plugin;
1131 * @see plugintype_interface::set_display_name()
1133 public function set_display_name() {
1134 if (get_string_manager()->string_exists('pluginname', $this->type
. '_' . $this->name
)) {
1135 $this->displayname
= get_string('pluginname', $this->type
. '_' . $this->name
);
1137 $this->displayname
= get_string('modulename', $this->type
. '_' . $this->name
);
1142 * @see plugintype_interface::set_version_disk()
1144 public function set_version_disk() {
1146 if (empty($this->rootdir
)) {
1150 $versionfile = $this->rootdir
. '/version.php';
1152 if (is_readable($versionfile)) {
1153 include($versionfile);
1154 if (isset($module->version
)) {
1155 $this->versiondisk
= $module->version
;
1161 * @see plugintype_interface::set_version_db()
1163 public function set_version_db() {
1166 $modulesinfo = self
::get_modules_info();
1167 if (isset($modulesinfo[$this->name
]->version
)) {
1168 $this->versiondb
= $modulesinfo[$this->name
]->version
;
1173 * @see plugintype_interface::set_version_requires()
1175 public function set_version_requires() {
1177 if (empty($this->rootdir
)) {
1181 $versionfile = $this->rootdir
. '/version.php';
1183 if (is_readable($versionfile)) {
1184 include($versionfile);
1185 if (isset($module->requires
)) {
1186 $this->versionrequires
= $module->requires
;
1192 * @see plugintype_interface::is_enabled()
1194 public function is_enabled() {
1196 $modulesinfo = self
::get_modules_info();
1197 if (isset($modulesinfo[$this->name
]->visible
)) {
1198 if ($modulesinfo[$this->name
]->visible
) {
1204 return parent
::is_enabled();
1209 * @see plugintype_interface::get_settings_url()
1211 public function get_settings_url() {
1213 if (!empty($this->rootdir
) and (file_exists($this->rootdir
. '/settings.php') or file_exists($this->rootdir
. '/settingstree.php'))) {
1214 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name
));
1216 return parent
::get_settings_url();
1221 * @see plugintype_interface::get_uninstall_url()
1223 public function get_uninstall_url() {
1225 if ($this->name
!== 'forum') {
1226 return new moodle_url('/admin/modules.php', array('delete' => $this->name
, 'sesskey' => sesskey()));
1233 * Provides access to the records in {modules} table
1235 * @param bool $disablecache do not use internal static cache
1236 * @return array array of stdClasses
1238 protected static function get_modules_info($disablecache=false) {
1240 static $modulesinfocache = null;
1242 if (is_null($modulesinfocache) or $disablecache) {
1243 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1246 return $modulesinfocache;
1251 * Class for question types
1253 class plugintype_qtype
extends plugintype_base
implements plugintype_interface
{
1256 * @see plugintype_interface::set_display_name()
1258 public function set_display_name() {
1259 $this->displayname
= get_string($this->name
, 'qtype_' . $this->name
);
1264 * Class for question formats
1266 class plugintype_qformat
extends plugintype_base
implements plugintype_interface
{
1269 * @see plugintype_interface::set_display_name()
1271 public function set_display_name() {
1272 $this->displayname
= get_string($this->name
, 'qformat_' . $this->name
);
1277 * Class for authentication plugins
1279 class plugintype_auth
extends plugintype_base
implements plugintype_interface
{
1282 * @see plugintype_interface::is_enabled()
1284 public function is_enabled() {
1286 /** @var null|array list of enabled authentication plugins */
1287 static $enabled = null;
1289 if (in_array($this->name
, array('nologin', 'manual'))) {
1290 // these two are always enabled and can't be disabled
1294 if (is_null($enabled)) {
1295 $enabled = explode(',', $CFG->auth
);
1298 return isset($enabled[$this->name
]);
1302 * @see plugintype_interface::get_settings_url()
1304 public function get_settings_url() {
1306 if (!empty($this->rootdir
) and file_exists($this->rootdir
. '/settings.php')) {
1307 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name
));
1309 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name
));
1315 * Class for enrolment plugins
1317 class plugintype_enrol
extends plugintype_base
implements plugintype_interface
{
1320 * We do not actually need whole enrolment classes here so we do not call
1321 * {@link enrol_get_plugins()}. Note that this may produce slightly different
1322 * results, for example if the enrolment plugin does not contain lib.php
1323 * but it is listed in $CFG->enrol_plugins_enabled
1325 * @see plugintype_interface::is_enabled()
1327 public function is_enabled() {
1329 /** @var null|array list of enabled enrolment plugins */
1330 static $enabled = null;
1332 if (is_null($enabled)) {
1333 $enabled = explode(',', $CFG->enrol_plugins_enabled
);
1336 return isset($enabled[$this->name
]);
1340 * @see plugintype_interface::get_settings_url()
1342 public function get_settings_url() {
1344 if ($this->is_enabled() or (!empty($this->rootdir
) and file_exists($this->rootdir
. '/settings.php'))) {
1345 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name
));
1347 return parent
::get_settings_url();
1352 * @see plugintype_interface::get_uninstall_url()
1354 public function get_uninstall_url() {
1355 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name
, 'sesskey' => sesskey()));
1360 * Class for messaging processors
1362 class plugintype_message
extends plugintype_base
implements plugintype_interface
{
1365 * @see plugintype_interface::get_settings_url()
1367 public function get_settings_url() {
1369 if ($this->name
=== 'jabber') {
1370 return new moodle_url('/admin/settings.php', array('section' => 'jabber'));
1373 if ($this->name
=== 'email') {
1374 return new moodle_url('/admin/settings.php', array('section' => 'mail'));
1381 * Class for repositories
1383 class plugintype_repository
extends plugintype_base
implements plugintype_interface
{
1386 * @see plugintype_interface::is_enabled()
1388 public function is_enabled() {
1390 $enabled = self
::get_enabled_repositories();
1392 return isset($enabled[$this->name
]);
1396 * @see plugintype_interface::get_settings_url()
1398 public function get_settings_url() {
1400 if ($this->is_enabled()) {
1401 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name
));
1403 return parent
::get_settings_url();
1408 * Provides access to the records in {repository} table
1410 * @param bool $disablecache do not use internal static cache
1411 * @return array array of stdClasses
1413 protected static function get_enabled_repositories($disablecache=false) {
1415 static $repositories = null;
1417 if (is_null($repositories) or $disablecache) {
1418 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1421 return $repositories;
1426 * Class for portfolios
1428 class plugintype_portfolio
extends plugintype_base
implements plugintype_interface
{
1431 * @see plugintype_interface::is_enabled()
1433 public function is_enabled() {
1435 $enabled = self
::get_enabled_portfolios();
1437 return isset($enabled[$this->name
]);
1441 * Provides access to the records in {portfolio_instance} table
1443 * @param bool $disablecache do not use internal static cache
1444 * @return array array of stdClasses
1446 protected static function get_enabled_portfolios($disablecache=false) {
1448 static $portfolios = null;
1450 if (is_null($portfolios) or $disablecache) {
1451 $portfolios = array();
1452 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
1453 foreach ($instances as $instance) {
1454 if (isset($portfolios[$instance->plugin
])) {
1455 if ($instance->visible
) {
1456 $portfolios[$instance->plugin
]->visible
= $instance->visible
;
1459 $portfolios[$instance->plugin
] = $instance;
1471 class plugintype_theme
extends plugintype_base
implements plugintype_interface
{
1474 * @see plugintype_interface::is_enabled()
1476 public function is_enabled() {
1479 if ((!empty($CFG->theme
) and $CFG->theme
=== $this->name
) or
1480 (!empty($CFG->themelegacy
) and $CFG->themelegacy
=== $this->name
)) {
1483 return parent
::is_enabled();
1489 * Class representing an MNet service
1491 class plugintype_mnetservice
extends plugintype_base
implements plugintype_interface
{
1494 * @see plugintype_interface::is_enabled()
1496 public function is_enabled() {
1499 if (empty($CFG->mnet_dispatcher_mode
) ||
$CFG->mnet_dispatcher_mode
!== 'strict') {
1502 return parent
::is_enabled();
1508 * Class for admin tool plugins
1510 class plugintype_tool
extends plugintype_base
implements plugintype_interface
{
1512 public function get_uninstall_url() {
1513 return new moodle_url('/admin/tools.php', array('delete' => $this->name
, 'sesskey' => sesskey()));