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'
358 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
359 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
360 'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero',
361 'overlay', 'serenity', 'sky_high', 'splash', 'standard',
365 'webservice' => array(
366 'amf', 'rest', 'soap', 'xmlrpc'
369 'workshopallocation' => array(
373 'workshopeval' => array(
377 'workshopform' => array(
378 'accumulative', 'comments', 'numerrors', 'rubric'
382 if (isset($standard_plugins[$type])) {
383 return $standard_plugins[$type];
392 * All classes that represent a plugin of some type must implement this interface
394 interface plugintype_interface
{
397 * Gathers and returns the information about all plugins of the given type
399 * Passing the parameter $typeclass allows us to reach the same effect as with the
400 * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
401 * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
403 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
404 * @param string $typerootdir full path to the location of the plugin dir
405 * @param string $typeclass the name of the actually called class
406 * @return array of plugintype classes, indexed by the plugin name
408 public static function get_plugins($type, $typerootdir, $typeclass);
411 * Sets $displayname property to a localized name of the plugin
415 public function set_display_name();
418 * Sets $versiondisk property to a numerical value representing the
419 * version of the plugin's source code.
421 * If the value is null after calling this method, either the plugin
422 * does not use versioning (typically does not have any database
423 * data) or is missing from disk.
427 public function set_version_disk();
430 * Sets $versiondb property to a numerical value representing the
431 * currently installed version of the plugin.
433 * If the value is null after calling this method, either the plugin
434 * does not use versioning (typically does not have any database
435 * data) or has not been installed yet.
439 public function set_version_db();
442 * Sets $versionrequires property to a numerical value representing
443 * the version of Moodle core that this plugin requires.
447 public function set_version_requires();
450 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
453 * If the property's value is null after calling this method, then
454 * the type of the plugin has not been recognized and you should throw
459 public function set_source();
462 * Returns true if the plugin is shipped with the official distribution
463 * of the current Moodle version, false otherwise.
467 public function is_standard();
470 * Returns the status of the plugin
472 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
474 public function get_status();
477 * Returns the information about plugin availability
479 * True means that the plugin is enabled. False means that the plugin is
480 * disabled. Null means that the information is not available, or the
481 * plugin does not support configurable availability or the availability
482 * can not be changed.
486 public function is_enabled();
489 * Returns the URL of the plugin settings screen
491 * Null value means that the plugin either does not have the settings screen
492 * or its location is not available via this library.
494 * @return null|moodle_url
496 public function get_settings_url();
499 * Returns the URL of the screen where this plugin can be uninstalled
501 * Visiting that URL must be safe, that is a manual confirmation is needed
502 * for actual uninstallation of the plugin. Null value means that the
503 * plugin either does not support uninstallation, or does not require any
504 * database cleanup or the location of the screen is not available via this
507 * @return null|moodle_url
509 public function get_uninstall_url();
512 * Returns relative directory of the plugin with heading '/'
514 * @example /mod/workshop
517 public function get_dir();
521 * Defines public properties that all plugintype classes must have
522 * and provides default implementation of required methods.
524 abstract class plugintype_base
{
526 /** @var string the plugintype name, eg. mod, auth or workshopform */
528 /** @var string full path to the location of all the plugins of this type */
530 /** @var string the plugin name, eg. assignment, ldap */
532 /** @var string the localized plugin name */
534 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
536 /** @var fullpath to the location of this plugin */
538 /** @var int|string the version of the plugin's source code */
540 /** @var int|string the version of the installed plugin */
542 /** @var int|float|string required version of Moodle core */
543 public $versionrequires;
544 /** @var int number of instances of the plugin - not supported yet */
546 /** @var int order of the plugin among other plugins of the same type - not supported yet */
550 * @see plugintype_interface::get_plugins()
552 public static function get_plugins($type, $typerootdir, $typeclass) {
554 // get the information about plugins at the disk
555 $plugins = get_plugin_list($type);
557 foreach ($plugins as $pluginname => $pluginrootdir) {
558 $plugin = new $typeclass();
559 $plugin->type
= $type;
560 $plugin->typerootdir
= $typerootdir;
561 $plugin->name
= $pluginname;
562 $plugin->rootdir
= $pluginrootdir;
564 $plugin->set_display_name();
565 $plugin->set_version_disk();
566 $plugin->set_version_db();
567 $plugin->set_version_requires();
568 $plugin->set_source();
570 $ondisk[$pluginname] = $plugin;
576 * @see plugintype_interface::set_display_name()
578 public function set_display_name() {
579 if (! get_string_manager()->string_exists('pluginname', $this->type
. '_' . $this->name
)) {
580 $this->displayname
= '[pluginname,' . $this->type
. '_' . $this->name
. ']';
582 $this->displayname
= get_string('pluginname', $this->type
. '_' . $this->name
);
587 * @see plugintype_interface::set_version_disk()
589 public function set_version_disk() {
591 if (empty($this->rootdir
)) {
595 $versionfile = $this->rootdir
. '/version.php';
597 if (is_readable($versionfile)) {
598 include($versionfile);
599 if (isset($plugin->version
)) {
600 $this->versiondisk
= $plugin->version
;
606 * @see plugintype_interface::set_version_db()
608 public function set_version_db() {
610 if ($ver = self
::get_version_from_config_plugins($this->type
. '_' . $this->name
)) {
611 $this->versiondb
= $ver;
616 * @see plugintype_interface::set_version_requires()
618 public function set_version_requires() {
620 if (empty($this->rootdir
)) {
624 $versionfile = $this->rootdir
. '/version.php';
626 if (is_readable($versionfile)) {
627 include($versionfile);
628 if (isset($plugin->requires
)) {
629 $this->versionrequires
= $plugin->requires
;
635 * @see plugintype_interface::set_source()
637 public function set_source() {
639 $standard = plugin_manager
::standard_plugins_list($this->type
);
641 if ($standard !== false) {
642 $standard = array_flip($standard);
643 if (isset($standard[$this->name
])) {
644 $this->source
= plugin_manager
::PLUGIN_SOURCE_STANDARD
;
646 $this->source
= plugin_manager
::PLUGIN_SOURCE_EXTENSION
;
652 * @see plugintype_interface::is_standard()
654 public function is_standard() {
655 return $this->source
=== plugin_manager
::PLUGIN_SOURCE_STANDARD
;
659 * @see plugintype_interface::get_status()
661 public function get_status() {
663 if (is_null($this->versiondb
) and is_null($this->versiondisk
)) {
664 return plugin_manager
::PLUGIN_STATUS_NODB
;
666 } else if (is_null($this->versiondb
) and !is_null($this->versiondisk
)) {
667 return plugin_manager
::PLUGIN_STATUS_NEW
;
669 } else if (!is_null($this->versiondb
) and is_null($this->versiondisk
)) {
670 return plugin_manager
::PLUGIN_STATUS_MISSING
;
672 } else if ((string)$this->versiondb
=== (string)$this->versiondisk
) {
673 return plugin_manager
::PLUGIN_STATUS_UPTODATE
;
675 } else if ($this->versiondb
< $this->versiondisk
) {
676 return plugin_manager
::PLUGIN_STATUS_UPGRADE
;
678 } else if ($this->versiondb
> $this->versiondisk
) {
679 return plugin_manager
::PLUGIN_STATUS_DOWNGRADE
;
682 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
683 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
688 * @see plugintype_interface::is_enabled()
690 public function is_enabled() {
695 * @see plugintype_interface::get_settings_url()
697 public function get_settings_url() {
702 * @see plugintype_interface::get_uninstall_url()
704 public function get_uninstall_url() {
709 * @see plugintype_interface::get_dir()
711 public function get_dir() {
714 return substr($this->rootdir
, strlen($CFG->dirroot
));
718 * Provides access to plugin versions from {config_plugins}
720 * @param string $plugin plugin name
721 * @param double $disablecache optional, defaults to false
722 * @return int|false the stored value or false if not found
724 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
726 static $pluginversions = null;
728 if (is_null($pluginversions) or $disablecache) {
729 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
732 if (!array_key_exists($plugin, $pluginversions)) {
736 return $pluginversions[$plugin];
741 * General class for all plugin types that do not have their own class
743 class plugintype_general
extends plugintype_base
implements plugintype_interface
{
748 * Class for page side blocks
750 class plugintype_block
extends plugintype_base
implements plugintype_interface
{
753 * @see plugintype_interface::get_plugins()
755 public static function get_plugins($type, $typerootdir, $typeclass) {
757 // get the information about blocks at the disk
758 $blocks = parent
::get_plugins($type, $typerootdir, $typeclass);
760 // add blocks missing from disk
761 $blocksinfo = self
::get_blocks_info();
762 foreach ($blocksinfo as $blockname => $blockinfo) {
763 if (isset($blocks[$blockname])) {
766 $plugin = new $typeclass();
767 $plugin->type
= $type;
768 $plugin->typerootdir
= $typerootdir;
769 $plugin->name
= $blockname;
770 $plugin->rootdir
= null;
771 $plugin->displayname
= $blockname;
772 $plugin->versiondb
= $blockinfo->version
;
773 $plugin->set_source();
775 $blocks[$blockname] = $plugin;
782 * @see plugintype_interface::set_display_name()
784 public function set_display_name() {
786 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name
)) {
787 $this->displayname
= get_string('pluginname', 'block_' . $this->name
);
789 } else if (($block = block_instance($this->name
)) !== false) {
790 $this->displayname
= $block->get_title();
793 parent
::set_display_name();
798 * @see plugintype_interface::set_version_db()
800 public function set_version_db() {
803 $blocksinfo = self
::get_blocks_info();
804 if (isset($blocksinfo[$this->name
]->version
)) {
805 $this->versiondb
= $blocksinfo[$this->name
]->version
;
810 * @see plugintype_interface::is_enabled()
812 public function is_enabled() {
814 $blocksinfo = self
::get_blocks_info();
815 if (isset($blocksinfo[$this->name
]->visible
)) {
816 if ($blocksinfo[$this->name
]->visible
) {
822 return parent
::is_enabled();
827 * @see plugintype_interface::get_settings_url()
829 public function get_settings_url() {
831 if (($block = block_instance($this->name
)) === false) {
832 return parent
::get_settings_url();
834 } else if ($block->has_config()) {
835 if (!empty($this->rootdir
) and file_exists($this->rootdir
. '/settings.php')) {
836 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name
));
838 $blocksinfo = self
::get_blocks_info();
839 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name
]->id
));
843 return parent
::get_settings_url();
848 * @see plugintype_interface::get_uninstall_url()
850 public function get_uninstall_url() {
852 $blocksinfo = self
::get_blocks_info();
853 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name
]->id
, 'sesskey' => sesskey()));
857 * Provides access to the records in {block} table
859 * @param bool $disablecache do not use internal static cache
860 * @return array array of stdClasses
862 protected static function get_blocks_info($disablecache=false) {
864 static $blocksinfocache = null;
866 if (is_null($blocksinfocache) or $disablecache) {
867 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
870 return $blocksinfocache;
875 * Class for text filters
877 class plugintype_filter
extends plugintype_base
implements plugintype_interface
{
880 * @see plugintype_interface::get_plugins()
882 public static function get_plugins($type, $typerootdir, $typeclass) {
887 // get the list of filters from both /filter and /mod location
888 $installed = filter_get_all_installed();
890 foreach ($installed as $filterlegacyname => $displayname) {
891 $plugin = new $typeclass();
892 $plugin->type
= $type;
893 $plugin->typerootdir
= $typerootdir;
894 $plugin->name
= self
::normalize_legacy_name($filterlegacyname);
895 $plugin->rootdir
= $CFG->dirroot
. '/' . $filterlegacyname;
896 $plugin->displayname
= $displayname;
898 $plugin->set_version_disk();
899 $plugin->set_version_db();
900 $plugin->set_version_requires();
901 $plugin->set_source();
903 $filters[$plugin->name
] = $plugin;
906 $globalstates = self
::get_global_states();
908 if ($DB->get_manager()->table_exists('filter_active')) {
909 // if we're upgrading from 1.9, the table does not exist yet
910 // if it does, make sure that all installed filters are registered
911 $needsreload = false;
912 foreach (array_keys($installed) as $filterlegacyname) {
913 if (!isset($globalstates[self
::normalize_legacy_name($filterlegacyname)])) {
914 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED
);
919 $globalstates = self
::get_global_states(true);
923 // make sure that all registered filters are installed, just in case
924 foreach ($globalstates as $name => $info) {
925 if (!isset($filters[$name])) {
926 // oops, there is a record in filter_active but the filter is not installed
927 $plugin = new $typeclass();
928 $plugin->type
= $type;
929 $plugin->typerootdir
= $typerootdir;
930 $plugin->name
= $name;
931 $plugin->rootdir
= $CFG->dirroot
. '/' . $info->legacyname
;
932 $plugin->displayname
= $info->legacyname
;
934 $plugin->set_version_db();
936 if (is_null($plugin->versiondb
)) {
937 // this is a hack to stimulate 'Missing from disk' error
938 // because $plugin->versiondisk will be null !== false
939 $plugin->versiondb
= false;
942 $filters[$plugin->name
] = $plugin;
950 * @see plugintype_interface::set_display_name()
952 public function set_display_name() {
953 // do nothing, the name is set in self::get_plugins()
957 * @see plugintype_interface::set_version_disk()
959 public function set_version_disk() {
961 if (strpos($this->name
, 'mod_') === 0) {
962 // filters bundled with modules do not use versioning
966 return parent
::set_version_disk();
970 * @see plugintype_interface::set_version_requires()
972 public function set_version_requires() {
974 if (strpos($this->name
, 'mod_') === 0) {
975 // filters bundled with modules do not use versioning
979 return parent
::set_version_requires();
983 * @see plugintype_interface::is_enabled()
985 public function is_enabled() {
987 $globalstates = self
::get_global_states();
989 foreach ($globalstates as $filterlegacyname => $info) {
990 $name = self
::normalize_legacy_name($filterlegacyname);
991 if ($name === $this->name
) {
992 if ($info->active
== TEXTFILTER_DISABLED
) {
995 // it may be 'On' or 'Off, but available'
1005 * @see plugintype_interface::get_settings_url()
1007 public function get_settings_url() {
1009 $globalstates = self
::get_global_states();
1010 $legacyname = $globalstates[$this->name
]->legacyname
;
1011 if (filter_has_global_settings($legacyname)) {
1012 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1019 * @see plugintype_interface::get_uninstall_url()
1021 public function get_uninstall_url() {
1023 if (strpos($this->name
, 'mod_') === 0) {
1026 $globalstates = self
::get_global_states();
1027 $legacyname = $globalstates[$this->name
]->legacyname
;
1028 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1033 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1035 * @param string $legacyfiltername legacy filter name
1036 * @return string frankenstyle-like name
1038 protected static function normalize_legacy_name($legacyfiltername) {
1040 $name = str_replace('/', '_', $legacyfiltername);
1041 if (strpos($name, 'filter_') === 0) {
1042 $name = substr($name, 7);
1044 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1052 * Provides access to the results of {@link filter_get_global_states()}
1053 * but indexed by the normalized filter name
1055 * The legacy filter name is available as ->legacyname property.
1057 * @param bool $disablecache
1060 protected static function get_global_states($disablecache=false) {
1062 static $globalstatescache = null;
1064 if ($disablecache or is_null($globalstatescache)) {
1066 if (!$DB->get_manager()->table_exists('filter_active')) {
1067 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1068 // does not exist yet
1069 $globalstatescache = array();
1072 foreach (filter_get_global_states() as $legacyname => $info) {
1073 $name = self
::normalize_legacy_name($legacyname);
1074 $filterinfo = new stdClass();
1075 $filterinfo->legacyname
= $legacyname;
1076 $filterinfo->active
= $info->active
;
1077 $filterinfo->sortorder
= $info->sortorder
;
1078 $globalstatescache[$name] = $filterinfo;
1083 return $globalstatescache;
1088 * Class for activity modules
1090 class plugintype_mod
extends plugintype_base
implements plugintype_interface
{
1093 * @see plugintype_interface::get_plugins()
1095 public static function get_plugins($type, $typerootdir, $typeclass) {
1097 // get the information about plugins at the disk
1098 $modules = parent
::get_plugins($type, $typerootdir, $typeclass);
1100 // add modules missing from disk
1101 $modulesinfo = self
::get_modules_info();
1102 foreach ($modulesinfo as $modulename => $moduleinfo) {
1103 if (isset($modules[$modulename])) {
1106 $plugin = new $typeclass();
1107 $plugin->type
= $type;
1108 $plugin->typerootdir
= $typerootdir;
1109 $plugin->name
= $modulename;
1110 $plugin->rootdir
= null;
1111 $plugin->displayname
= $modulename;
1112 $plugin->versiondb
= $moduleinfo->version
;
1113 $plugin->set_source();
1115 $modules[$modulename] = $plugin;
1122 * @see plugintype_interface::set_display_name()
1124 public function set_display_name() {
1125 if (get_string_manager()->string_exists('pluginname', $this->type
. '_' . $this->name
)) {
1126 $this->displayname
= get_string('pluginname', $this->type
. '_' . $this->name
);
1128 $this->displayname
= get_string('modulename', $this->type
. '_' . $this->name
);
1133 * @see plugintype_interface::set_version_disk()
1135 public function set_version_disk() {
1137 if (empty($this->rootdir
)) {
1141 $versionfile = $this->rootdir
. '/version.php';
1143 if (is_readable($versionfile)) {
1144 include($versionfile);
1145 if (isset($module->version
)) {
1146 $this->versiondisk
= $module->version
;
1152 * @see plugintype_interface::set_version_db()
1154 public function set_version_db() {
1157 $modulesinfo = self
::get_modules_info();
1158 if (isset($modulesinfo[$this->name
]->version
)) {
1159 $this->versiondb
= $modulesinfo[$this->name
]->version
;
1164 * @see plugintype_interface::set_version_requires()
1166 public function set_version_requires() {
1168 if (empty($this->rootdir
)) {
1172 $versionfile = $this->rootdir
. '/version.php';
1174 if (is_readable($versionfile)) {
1175 include($versionfile);
1176 if (isset($module->requires
)) {
1177 $this->versionrequires
= $module->requires
;
1183 * @see plugintype_interface::is_enabled()
1185 public function is_enabled() {
1187 $modulesinfo = self
::get_modules_info();
1188 if (isset($modulesinfo[$this->name
]->visible
)) {
1189 if ($modulesinfo[$this->name
]->visible
) {
1195 return parent
::is_enabled();
1200 * @see plugintype_interface::get_settings_url()
1202 public function get_settings_url() {
1204 if (!empty($this->rootdir
) and (file_exists($this->rootdir
. '/settings.php') or file_exists($this->rootdir
. '/settingstree.php'))) {
1205 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name
));
1207 return parent
::get_settings_url();
1212 * @see plugintype_interface::get_uninstall_url()
1214 public function get_uninstall_url() {
1216 if ($this->name
!== 'forum') {
1217 return new moodle_url('/admin/modules.php', array('delete' => $this->name
, 'sesskey' => sesskey()));
1224 * Provides access to the records in {modules} table
1226 * @param bool $disablecache do not use internal static cache
1227 * @return array array of stdClasses
1229 protected static function get_modules_info($disablecache=false) {
1231 static $modulesinfocache = null;
1233 if (is_null($modulesinfocache) or $disablecache) {
1234 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1237 return $modulesinfocache;
1242 * Class for question types
1244 class plugintype_qtype
extends plugintype_base
implements plugintype_interface
{
1247 * @see plugintype_interface::set_display_name()
1249 public function set_display_name() {
1250 $this->displayname
= get_string($this->name
, 'qtype_' . $this->name
);
1255 * Class for question formats
1257 class plugintype_qformat
extends plugintype_base
implements plugintype_interface
{
1260 * @see plugintype_interface::set_display_name()
1262 public function set_display_name() {
1263 $this->displayname
= get_string($this->name
, 'qformat_' . $this->name
);
1268 * Class for authentication plugins
1270 class plugintype_auth
extends plugintype_base
implements plugintype_interface
{
1273 * @see plugintype_interface::is_enabled()
1275 public function is_enabled() {
1277 /** @var null|array list of enabled authentication plugins */
1278 static $enabled = null;
1280 if (in_array($this->name
, array('nologin', 'manual'))) {
1281 // these two are always enabled and can't be disabled
1285 if (is_null($enabled)) {
1286 $enabled = explode(',', $CFG->auth
);
1289 return isset($enabled[$this->name
]);
1293 * @see plugintype_interface::get_settings_url()
1295 public function get_settings_url() {
1297 if (!empty($this->rootdir
) and file_exists($this->rootdir
. '/settings.php')) {
1298 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name
));
1300 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name
));
1306 * Class for enrolment plugins
1308 class plugintype_enrol
extends plugintype_base
implements plugintype_interface
{
1311 * We do not actually need whole enrolment classes here so we do not call
1312 * {@link enrol_get_plugins()}. Note that this may produce slightly different
1313 * results, for example if the enrolment plugin does not contain lib.php
1314 * but it is listed in $CFG->enrol_plugins_enabled
1316 * @see plugintype_interface::is_enabled()
1318 public function is_enabled() {
1320 /** @var null|array list of enabled enrolment plugins */
1321 static $enabled = null;
1323 if (is_null($enabled)) {
1324 $enabled = explode(',', $CFG->enrol_plugins_enabled
);
1327 return isset($enabled[$this->name
]);
1331 * @see plugintype_interface::get_settings_url()
1333 public function get_settings_url() {
1335 if ($this->is_enabled() or (!empty($this->rootdir
) and file_exists($this->rootdir
. '/settings.php'))) {
1336 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name
));
1338 return parent
::get_settings_url();
1343 * @see plugintype_interface::get_uninstall_url()
1345 public function get_uninstall_url() {
1346 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name
, 'sesskey' => sesskey()));
1351 * Class for messaging processors
1353 class plugintype_message
extends plugintype_base
implements plugintype_interface
{
1356 * @see plugintype_interface::get_settings_url()
1358 public function get_settings_url() {
1360 if ($this->name
=== 'jabber') {
1361 return new moodle_url('/admin/settings.php', array('section' => 'jabber'));
1364 if ($this->name
=== 'email') {
1365 return new moodle_url('/admin/settings.php', array('section' => 'mail'));
1372 * Class for repositories
1374 class plugintype_repository
extends plugintype_base
implements plugintype_interface
{
1377 * @see plugintype_interface::is_enabled()
1379 public function is_enabled() {
1381 $enabled = self
::get_enabled_repositories();
1383 return isset($enabled[$this->name
]);
1387 * @see plugintype_interface::get_settings_url()
1389 public function get_settings_url() {
1391 if ($this->is_enabled()) {
1392 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name
));
1394 return parent
::get_settings_url();
1399 * Provides access to the records in {repository} table
1401 * @param bool $disablecache do not use internal static cache
1402 * @return array array of stdClasses
1404 protected static function get_enabled_repositories($disablecache=false) {
1406 static $repositories = null;
1408 if (is_null($repositories) or $disablecache) {
1409 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1412 return $repositories;
1417 * Class for portfolios
1419 class plugintype_portfolio
extends plugintype_base
implements plugintype_interface
{
1422 * @see plugintype_interface::is_enabled()
1424 public function is_enabled() {
1426 $enabled = self
::get_enabled_portfolios();
1428 return isset($enabled[$this->name
]);
1432 * Provides access to the records in {portfolio_instance} table
1434 * @param bool $disablecache do not use internal static cache
1435 * @return array array of stdClasses
1437 protected static function get_enabled_portfolios($disablecache=false) {
1439 static $portfolios = null;
1441 if (is_null($portfolios) or $disablecache) {
1442 $portfolios = array();
1443 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
1444 foreach ($instances as $instance) {
1445 if (isset($portfolios[$instance->plugin
])) {
1446 if ($instance->visible
) {
1447 $portfolios[$instance->plugin
]->visible
= $instance->visible
;
1450 $portfolios[$instance->plugin
] = $instance;
1462 class plugintype_theme
extends plugintype_base
implements plugintype_interface
{
1465 * @see plugintype_interface::is_enabled()
1467 public function is_enabled() {
1470 if ((!empty($CFG->theme
) and $CFG->theme
=== $this->name
) or
1471 (!empty($CFG->themelegacy
) and $CFG->themelegacy
=== $this->name
)) {
1474 return parent
::is_enabled();
1480 * Class representing an MNet service
1482 class plugintype_mnetservice
extends plugintype_base
implements plugintype_interface
{
1485 * @see plugintype_interface::is_enabled()
1487 public function is_enabled() {
1490 if (empty($CFG->mnet_dispatcher_mode
) ||
$CFG->mnet_dispatcher_mode
!== 'strict') {
1493 return parent
::is_enabled();