Merge branch 'wip-MDL-25454-master' of git://github.com/marinaglancy/moodle
[moodle.git] / lib / pluginlib.php
blob9cdc83a4f04cd62bfad00fd366653c9529897e9f
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * Defines classes used for plugins management
20 * This library provides a unified interface to various plugin types in
21 * Moodle. It is mainly used by the plugins management admin page and the
22 * plugins check page during the upgrade.
24 * @package core
25 * @subpackage admin
26 * @copyright 2011 David Mudrak <david@moodle.com>
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30 defined('MOODLE_INTERNAL') || die();
32 /**
33 * Singleton class providing general plugins management functionality
35 class plugin_manager {
37 /** the plugin is shipped with standard Moodle distribution */
38 const PLUGIN_SOURCE_STANDARD = 'std';
39 /** the plugin is added extension */
40 const PLUGIN_SOURCE_EXTENSION = 'ext';
42 /** the plugin uses neither database nor capabilities, no versions */
43 const PLUGIN_STATUS_NODB = 'nodb';
44 /** the plugin is up-to-date */
45 const PLUGIN_STATUS_UPTODATE = 'uptodate';
46 /** the plugin is about to be installed */
47 const PLUGIN_STATUS_NEW = 'new';
48 /** the plugin is about to be upgraded */
49 const PLUGIN_STATUS_UPGRADE = 'upgrade';
50 /** the version at the disk is lower than the one already installed */
51 const PLUGIN_STATUS_DOWNGRADE = 'downgrade';
52 /** the plugin is installed but missing from disk */
53 const PLUGIN_STATUS_MISSING = 'missing';
55 /** @var plugin_manager holds the singleton instance */
56 protected static $singletoninstance;
57 /** @var array of raw plugins information */
58 protected $pluginsinfo = null;
59 /** @var array of raw subplugins information */
60 protected $subpluginsinfo = null;
62 /**
63 * Direct initiation not allowed, use the factory method {@link self::instance()}
65 * @todo we might want to specify just a single plugin type to work with
67 protected function __construct() {
68 $this->get_plugins(true);
71 /**
72 * Sorry, this is singleton
74 protected function __clone() {
77 /**
78 * Factory method for this class
80 * @return plugin_manager the singleton instance
82 public static function instance() {
83 global $CFG;
85 if (is_null(self::$singletoninstance)) {
86 self::$singletoninstance = new self();
88 return self::$singletoninstance;
91 /**
92 * Returns a tree of known plugins and information about them
94 * @param bool $disablecache force reload, cache can be used otherwise
95 * @return array
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;
108 } else {
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
128 * @return array
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) {
163 $parent = false;
164 foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
165 if (isset($subplugintypes[$subplugintype])) {
166 $parent = $pluginname;
167 break;
171 return $parent;
175 * Returns a localized name of a given plugin
177 * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
178 * @return string
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
193 * @return string
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);
205 } else {
206 return $this->plugin_name($parent) . ' / ' . $type;
209 } else {
210 return $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'
226 'auth' => array(
227 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
228 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
229 'shibboleth', 'webservice'
232 'block' => array(
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(
256 'imagegallery'
259 'editor' => array(
260 'textarea', 'tinymce'
263 'enrol' => array(
264 'authorize', 'category', 'cohort', 'database', 'flatfile',
265 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
266 'paypal', 'self'
269 'filter' => array(
270 'activitynames', 'algebra', 'censor', 'emailprotect',
271 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
272 'urltolink', 'mod_data', 'mod_glossary'
275 'format' => array(
276 'scorm', 'social', 'topics', 'weeks'
279 'gradeexport' => array(
280 'ods', 'txt', 'xls', 'xml'
283 'gradeimport' => array(
284 'csv', 'xml'
287 'gradereport' => array(
288 'grader', 'outcomes', 'overview', 'user'
291 'local' => array(
294 'message' => array(
295 'email', 'jabber', 'popup'
298 'mnetservice' => array(
299 'enrol'
302 'mod' => 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'
326 'qformat' => array(
327 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
328 'learnwise', 'missingword', 'multianswer', 'qti_two', 'webct',
329 'xhtml', 'xml'
332 'qtype' => array(
333 'calculated', 'calculatedmulti', 'calculatedsimple',
334 'description', 'essay', 'match', 'missingtype', 'multianswer',
335 'multichoice', 'numerical', 'random', 'randomsamatch',
336 'shortanswer', 'truefalse'
339 'quiz' => array(
340 'grading', 'overview', 'responses', 'statistics'
343 'report' => array(
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(
356 'basic'
359 'theme' => 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',
364 'standardold'
367 'tool' => array(
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(
379 'manual', 'random'
382 'workshopeval' => array(
383 'best'
386 'workshopform' => array(
387 'accumulative', 'comments', 'numerrors', 'rubric'
391 if (isset($standard_plugins[$type])) {
392 return $standard_plugins[$type];
394 } else {
395 return false;
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
422 * @return void
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.
434 * @return void
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.
446 * @return void
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.
454 * @return void
456 public function set_version_requires();
459 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
460 * constants.
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
464 * an exception.
466 * @return void
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.
474 * @return bool
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.
493 * @return null|bool
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
514 * library.
516 * @return null|moodle_url
518 public function get_uninstall_url();
521 * Returns relative directory of the plugin with heading '/'
523 * @example /mod/workshop
524 * @return string
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 */
536 public $type;
537 /** @var string full path to the location of all the plugins of this type */
538 public $typerootdir;
539 /** @var string the plugin name, eg. assignment, ldap */
540 public $name;
541 /** @var string the localized plugin name */
542 public $displayname;
543 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
544 public $source;
545 /** @var fullpath to the location of this plugin */
546 public $rootdir;
547 /** @var int|string the version of the plugin's source code */
548 public $versiondisk;
549 /** @var int|string the version of the installed plugin */
550 public $versiondb;
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 */
554 public $instances;
555 /** @var int order of the plugin among other plugins of the same type - not supported yet */
556 public $sortorder;
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);
565 $ondisk = array();
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;
581 return $ondisk;
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 . ']';
590 } else {
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)) {
601 return;
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)) {
630 return;
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;
654 } else {
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;
690 } else {
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() {
700 return null;
704 * @see plugintype_interface::get_settings_url()
706 public function get_settings_url() {
707 return null;
711 * @see plugintype_interface::get_uninstall_url()
713 public function get_uninstall_url() {
714 return null;
718 * @see plugintype_interface::get_dir()
720 public function get_dir() {
721 global $CFG;
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) {
734 global $DB;
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)) {
742 return false;
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])) {
773 continue;
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;
787 return $blocks;
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();
801 } else {
802 parent::set_display_name();
807 * @see plugintype_interface::set_version_db()
809 public function set_version_db() {
810 global $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) {
826 return true;
827 } else {
828 return false;
830 } else {
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));
846 } else {
847 $blocksinfo = self::get_blocks_info();
848 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
851 } else {
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) {
872 global $DB;
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) {
892 global $CFG, $DB;
894 $filters = array();
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);
924 $needsreload = true;
927 if ($needsreload) {
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;
955 return $filters;
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
972 return;
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
985 return;
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) {
1002 return false;
1003 } else {
1004 // it may be 'On' or 'Off, but available'
1005 return null;
1010 return null;
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)));
1022 } else {
1023 return null;
1028 * @see plugintype_interface::get_uninstall_url()
1030 public function get_uninstall_url() {
1032 if (strpos($this->name, 'mod_') === 0) {
1033 return null;
1034 } else {
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);
1052 if (empty($name)) {
1053 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1057 return $name;
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
1067 * @return array
1069 protected static function get_global_states($disablecache=false) {
1070 global $DB;
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();
1080 } else {
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])) {
1113 continue;
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;
1127 return $modules;
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);
1136 } else {
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)) {
1147 return;
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() {
1164 global $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)) {
1178 return;
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) {
1199 return true;
1200 } else {
1201 return false;
1203 } else {
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));
1215 } else {
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()));
1227 } else {
1228 return null;
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) {
1239 global $DB;
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() {
1285 global $CFG;
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
1291 return null;
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));
1308 } else {
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() {
1328 global $CFG;
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));
1346 } else {
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));
1402 } else {
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) {
1414 global $DB;
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) {
1447 global $DB;
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;
1458 } else {
1459 $portfolios[$instance->plugin] = $instance;
1464 return $portfolios;
1469 * Class for themes
1471 class plugintype_theme extends plugintype_base implements plugintype_interface {
1474 * @see plugintype_interface::is_enabled()
1476 public function is_enabled() {
1477 global $CFG;
1479 if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
1480 (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
1481 return true;
1482 } else {
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() {
1497 global $CFG;
1499 if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
1500 return false;
1501 } else {
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()));