66424082fdc4585f07610ecd21da79401e22c632
[moodle.git] / lib / pluginlib.php
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.
13 //
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
19  *
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.
23  *
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
28  */
30 defined('MOODLE_INTERNAL') || die();
32 /**
33  * Singleton class providing general plugins management functionality
34  */
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()}
64      *
65      * @todo we might want to specify just a single plugin type to work with
66      */
67     protected function __construct() {
68         $this->get_plugins(true);
69     }
71     /**
72      * Sorry, this is singleton
73      */
74     protected function __clone() {
75     }
77     /**
78      * Factory method for this class
79      *
80      * @return plugin_manager the singleton instance
81      */
82     public static function instance() {
83         global $CFG;
85         if (is_null(self::$singletoninstance)) {
86             self::$singletoninstance = new self();
87         }
88         return self::$singletoninstance;
89     }
91     /**
92      * Returns a tree of known plugins and information about them
93      *
94      * @param bool $disablecache force reload, cache can be used otherwise
95      * @return array 2D array. The first keys are plugin type names (e.g. qtype);
96      *      the second keys are the plugin local name (e.g. multichoice); and
97      *      the values are the corresponding {@link plugin_information} objects.
98      */
99     public function get_plugins($disablecache=false) {
101         if ($disablecache or is_null($this->pluginsinfo)) {
102             $this->pluginsinfo = array();
103             $plugintypes = get_plugin_types();
104             foreach ($plugintypes as $plugintype => $plugintyperootdir) {
105                 if (in_array($plugintype, array('base', 'general'))) {
106                     throw new coding_exception('Illegal usage of reserved word for plugin type');
107                 }
108                 if (class_exists('plugintype_' . $plugintype)) {
109                     $plugintypeclass = 'plugintype_' . $plugintype;
110                 } else {
111                     $plugintypeclass = 'plugintype_general';
112                 }
113                 if (!in_array('plugin_information', class_implements($plugintypeclass))) {
114                     throw new coding_exception('Class ' . $plugintypeclass . ' must implement plugin_information');
115                 }
116                 $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass);
117                 $this->pluginsinfo[$plugintype] = $plugins;
118             }
119         }
121         return $this->pluginsinfo;
122     }
124     /**
125      * Returns list of plugins that define their subplugins and the information
126      * about them from the db/subplugins.php file.
127      *
128      * At the moment, only activity modules can define subplugins.
129      *
130      * @param bool $disablecache force reload, cache can be used otherwise
131      * @return array with keys like 'mod_quiz', and values the data from the
132      *      corresponding db/subplugins.php file.
133      */
134     public function get_subplugins($disablecache=false) {
136         if ($disablecache or is_null($this->subpluginsinfo)) {
137             $this->subpluginsinfo = array();
138             $mods = get_plugin_list('mod');
139             foreach ($mods as $mod => $moddir) {
140                 $modsubplugins = array();
141                 if (file_exists($moddir . '/db/subplugins.php')) {
142                     include($moddir . '/db/subplugins.php');
143                     foreach ($subplugins as $subplugintype => $subplugintyperootdir) {
144                         $subplugin = new stdClass();
145                         $subplugin->type = $subplugintype;
146                         $subplugin->typerootdir = $subplugintyperootdir;
147                         $modsubplugins[$subplugintype] = $subplugin;
148                     }
149                 $this->subpluginsinfo['mod_' . $mod] = $modsubplugins;
150                 }
151             }
152         }
154         return $this->subpluginsinfo;
155     }
157     /**
158      * Returns the name of the plugin that defines the given subplugin type
159      *
160      * If the given subplugin type is not actually a subplugin, returns false.
161      *
162      * @param string $subplugintype the name of subplugin type, eg. workshopform or quiz
163      * @return false|string the name of the parent plugin, eg. mod_workshop
164      */
165     public function get_parent_of_subplugin($subplugintype) {
167         $parent = false;
168         foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
169             if (isset($subplugintypes[$subplugintype])) {
170                 $parent = $pluginname;
171                 break;
172             }
173         }
175         return $parent;
176     }
178     /**
179      * Returns a localized name of a given plugin
180      *
181      * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
182      * @return string
183      */
184     public function plugin_name($plugin) {
185         list($type, $name) = normalize_component($plugin);
186         return $this->pluginsinfo[$type][$name]->displayname;
187     }
189     /**
190      * Returns a localized name of a plugin type in plural form
191      *
192      * Most plugin types define their names in core_plugin lang file. In case of subplugins,
193      * we try to ask the parent plugin for the name. In the worst case, we will return
194      * the value of the passed $type parameter.
195      *
196      * @param string $type the type of the plugin, e.g. mod or workshopform
197      * @return string
198      */
199     public function plugintype_name_plural($type) {
201         if (get_string_manager()->string_exists('type_' . $type . '_plural', 'core_plugin')) {
202             // for most plugin types, their names are defined in core_plugin lang file
203             return get_string('type_' . $type . '_plural', 'core_plugin');
205         } else if ($parent = $this->get_parent_of_subplugin($type)) {
206             // if this is a subplugin, try to ask the parent plugin for the name
207             if (get_string_manager()->string_exists('subplugintype_' . $type . '_plural', $parent)) {
208                 return $this->plugin_name($parent) . ' / ' . get_string('subplugintype_' . $type . '_plural', $parent);
209             } else {
210                 return $this->plugin_name($parent) . ' / ' . $type;
211             }
213         } else {
214             return $type;
215         }
216     }
218     /**
219      * @param string $component frankenstyle component name.
220      * @return plugin_information|null the corresponding plugin information.
221      */
222     public function get_plugin_info($component) {
223         list($type, $name) = normalize_component($component);
224         $plugins = $this->get_plugins();
225         if (isset($plugins[$type][$name])) {
226             return $plugins[$type][$name];
227         } else {
228             return null;
229         }
230     }
232     /**
233      * Check a dependencies list against the list of installed plugins.
234      * @param array $dependencies compenent name to required version or ANY_VERSION.
235      * @return bool true if all the dependencies are satisfied.
236      */
237     public function are_dependencies_satisfied($dependencies) {
238         foreach ($dependencies as $component => $requiredversion) {
239             $otherplugin = $this->get_plugin_info($component);
240             if (is_null($otherplugin)) {
241                 return false;
242             }
244             if ($requiredversion != ANY_VERSION and $otherplugin->versiondb < $requiredversion) {
245                 return false;
246             }
247         }
249         return true;
250     }
252     /**
253      * Checks all dependencies for all installed plugins. Used by install and upgrade.
254      * @param int $moodleversion the version from version.php.
255      * @return bool true if all the dependencies are satisfied for all plugins.
256      */
257     public function all_plugins_ok($moodleversion) {
258         foreach ($this->get_plugins() as $type => $plugins) {
259             foreach ($plugins as $plugin) {
261                 if (!empty($plugin->versionrequires) && $plugin->versionrequires > $moodleversion) {
262                     return false;
263                 }
265                 if (!$this->are_dependencies_satisfied($plugin->get_other_required_plugins())) {
266                     return false;
267                 }
268             }
269         }
271         return true;
272     }
274     /**
275      * Defines a white list of all plugins shipped in the standard Moodle distribution
276      *
277      * @return false|array array of standard plugins or false if the type is unknown
278      */
279     public static function standard_plugins_list($type) {
280         static $standard_plugins = array(
282             'assignment' => array(
283                 'offline', 'online', 'upload', 'uploadsingle'
284             ),
286             'auth' => array(
287                 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
288                 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
289                 'shibboleth', 'webservice'
290             ),
292             'block' => array(
293                 'activity_modules', 'admin_bookmarks', 'blog_menu',
294                 'blog_recent', 'blog_tags', 'calendar_month',
295                 'calendar_upcoming', 'comments', 'community',
296                 'completionstatus', 'course_list', 'course_overview',
297                 'course_summary', 'feedback', 'glossary_random', 'html',
298                 'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile',
299                 'navigation', 'news_items', 'online_users', 'participants',
300                 'private_files', 'quiz_results', 'recent_activity',
301                 'rss_client', 'search', 'search_forums', 'section_links',
302                 'selfcompletion', 'settings', 'site_main_menu',
303                 'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
304             ),
306             'coursereport' => array(
307                 'completion', 'log', 'outline', 'participation', 'progress', 'stats'
308             ),
310             'datafield' => array(
311                 'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
312                 'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
313             ),
315             'datapreset' => array(
316                 'imagegallery'
317             ),
319             'editor' => array(
320                 'textarea', 'tinymce'
321             ),
323             'enrol' => array(
324                 'authorize', 'category', 'cohort', 'database', 'flatfile',
325                 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
326                 'paypal', 'self'
327             ),
329             'filter' => array(
330                 'activitynames', 'algebra', 'censor', 'emailprotect',
331                 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
332                 'urltolink', 'mod_data', 'mod_glossary'
333             ),
335             'format' => array(
336                 'scorm', 'social', 'topics', 'weeks'
337             ),
339             'gradeexport' => array(
340                 'ods', 'txt', 'xls', 'xml'
341             ),
343             'gradeimport' => array(
344                 'csv', 'xml'
345             ),
347             'gradereport' => array(
348                 'grader', 'outcomes', 'overview', 'user'
349             ),
351             'gradingform' => array(
352                 'rubric'
353             ),
355             'local' => array(
356             ),
358             'message' => array(
359                 'email', 'jabber', 'popup'
360             ),
362             'mnetservice' => array(
363                 'enrol'
364             ),
366             'mod' => array(
367                 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
368                 'forum', 'glossary', 'imscp', 'label', 'lesson', 'page',
369                 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
370             ),
372             'plagiarism' => array(
373             ),
375             'portfolio' => array(
376                 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
377             ),
379             'profilefield' => array(
380                 'checkbox', 'datetime', 'menu', 'text', 'textarea'
381             ),
383             'qbehaviour' => array(
384                 'adaptive', 'adaptivenopenalty', 'deferredcbm',
385                 'deferredfeedback', 'immediatecbm', 'immediatefeedback',
386                 'informationitem', 'interactive', 'interactivecountback',
387                 'manualgraded', 'missing'
388             ),
390             'qformat' => array(
391                 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
392                 'learnwise', 'missingword', 'multianswer', 'qti_two', 'webct',
393                 'xhtml', 'xml'
394             ),
396             'qtype' => array(
397                 'calculated', 'calculatedmulti', 'calculatedsimple',
398                 'description', 'essay', 'match', 'missingtype', 'multianswer',
399                 'multichoice', 'numerical', 'random', 'randomsamatch',
400                 'shortanswer', 'truefalse'
401             ),
403             'quiz' => array(
404                 'grading', 'overview', 'responses', 'statistics'
405             ),
407             'quizaccess' => array(
408                 'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate',
409                 'password', 'safebrowser', 'securewindow', 'timelimit'
410             ),
412             'report' => array(
413                 'backups', 'configlog', 'courseoverview',
414                 'log', 'questioninstances', 'security', 'stats'
415             ),
417             'repository' => array(
418                 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
419                 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
420                 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
421                 'wikimedia', 'youtube'
422             ),
424             'scormreport' => array(
425                 'basic'
426             ),
428             'theme' => array(
429                 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
430                 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
431                 'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero',
432                 'overlay', 'serenity', 'sky_high', 'splash', 'standard',
433                 'standardold'
434             ),
436             'tool' => array(
437                 'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
438                 'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
439                 'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
440                 'uploaduser', 'unsuproles', 'xmldb'
441             ),
443             'webservice' => array(
444                 'amf', 'rest', 'soap', 'xmlrpc'
445             ),
447             'workshopallocation' => array(
448                 'manual', 'random'
449             ),
451             'workshopeval' => array(
452                 'best'
453             ),
455             'workshopform' => array(
456                 'accumulative', 'comments', 'numerrors', 'rubric'
457             )
458         );
460         if (isset($standard_plugins[$type])) {
461             return $standard_plugins[$type];
463         } else {
464             return false;
465         }
466     }
469 /**
470  * Interface for making information about a plugin available.
471  *
472  * Note that most of the useful information is made available in pubic fields,
473  * which cannot be documented in this interface. See the field definitions on
474  * {@link plugintype_base} to find out what information is available.
475  */
476 interface plugin_information {
478     /**
479      * Gathers and returns the information about all plugins of the given type
480      *
481      * Passing the parameter $typeclass allows us to reach the same effect as with the
482      * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
483      * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
484      *
485      * @param string $type the name of the plugintype, eg. mod, auth or workshopform
486      * @param string $typerootdir full path to the location of the plugin dir
487      * @param string $typeclass the name of the actually called class
488      * @return array of plugintype classes, indexed by the plugin name
489      */
490     public static function get_plugins($type, $typerootdir, $typeclass);
492     /**
493      * Sets $displayname property to a localized name of the plugin
494      *
495      * @return void
496      */
497     public function init_display_name();
499     /**
500      * Sets $versiondisk property to a numerical value representing the
501      * version of the plugin's source code.
502      *
503      * If the value is null after calling this method, either the plugin
504      * does not use versioning (typically does not have any database
505      * data) or is missing from disk.
506      *
507      * @return void
508      */
509     public function load_disk_version();
511     /**
512      * Sets $versiondb property to a numerical value representing the
513      * currently installed version of the plugin.
514      *
515      * If the value is null after calling this method, either the plugin
516      * does not use versioning (typically does not have any database
517      * data) or has not been installed yet.
518      *
519      * @return void
520      */
521     public function load_db_version();
523     /**
524      * Sets $versionrequires property to a numerical value representing
525      * the version of Moodle core that this plugin requires.
526      *
527      * @return void
528      */
529     public function load_required_main_version();
531     /**
532      * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
533      * constants.
534      *
535      * If the property's value is null after calling this method, then
536      * the type of the plugin has not been recognized and you should throw
537      * an exception.
538      *
539      * @return void
540      */
541     public function init_is_standard();
543     /**
544      * Returns true if the plugin is shipped with the official distribution
545      * of the current Moodle version, false otherwise.
546      *
547      * @return bool
548      */
549     public function is_standard();
551     /**
552      * Returns the status of the plugin
553      *
554      * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
555      */
556     public function get_status();
558     /**
559      * Get the list of other plugins that this plugin requires ot be installed.
560      * @return array with keys the frankenstyle plugin name, and values either
561      *      a version string (like '2011101700') or the constant ANY_VERSION.
562      */
563     public function get_other_required_plugins();
565     /**
566      * Returns the information about plugin availability
567      *
568      * True means that the plugin is enabled. False means that the plugin is
569      * disabled. Null means that the information is not available, or the
570      * plugin does not support configurable availability or the availability
571      * can not be changed.
572      *
573      * @return null|bool
574      */
575     public function is_enabled();
577     /**
578      * Returns the URL of the plugin settings screen
579      *
580      * Null value means that the plugin either does not have the settings screen
581      * or its location is not available via this library.
582      *
583      * @return null|moodle_url
584      */
585     public function get_settings_url();
587     /**
588      * Returns the URL of the screen where this plugin can be uninstalled
589      *
590      * Visiting that URL must be safe, that is a manual confirmation is needed
591      * for actual uninstallation of the plugin. Null value means that the
592      * plugin either does not support uninstallation, or does not require any
593      * database cleanup or the location of the screen is not available via this
594      * library.
595      *
596      * @return null|moodle_url
597      */
598     public function get_uninstall_url();
600     /**
601      * Returns relative directory of the plugin with heading '/'
602      *
603      * @example /mod/workshop
604      * @return string
605      */
606     public function get_dir();
608     /**
609      * Return the full path name of a file within the plugin.
610      * No check is made to see if the file exists.
611      * @param string $relativepath e.g. 'version.php'.
612      * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
613      */
614     public function full_path($relativepath);
617 /**
618  * Defines public properties that all plugintype classes must have
619  * and provides default implementation of required methods.
620  */
621 abstract class plugintype_base {
623     /** @var string the plugintype name, eg. mod, auth or workshopform */
624     public $type;
625     /** @var string full path to the location of all the plugins of this type */
626     public $typerootdir;
627     /** @var string the plugin name, eg. assignment, ldap */
628     public $name;
629     /** @var string the localized plugin name */
630     public $displayname;
631     /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
632     public $source;
633     /** @var fullpath to the location of this plugin */
634     public $rootdir;
635     /** @var int|string the version of the plugin's source code */
636     public $versiondisk;
637     /** @var int|string the version of the installed plugin */
638     public $versiondb;
639     /** @var int|float|string required version of Moodle core  */
640     public $versionrequires;
641     /** @var array other plugins that this one depends on.
642      *  Lazy-loaded by {@link get_other_required_plugins()} */
643     public $dependencies = null;
644     /** @var int number of instances of the plugin - not supported yet */
645     public $instances;
646     /** @var int order of the plugin among other plugins of the same type - not supported yet */
647     public $sortorder;
649     /**
650      * @see plugin_information::get_plugins()
651      */
652     public static function get_plugins($type, $typerootdir, $typeclass) {
654         // get the information about plugins at the disk
655         $plugins = get_plugin_list($type);
656         $ondisk = array();
657         foreach ($plugins as $pluginname => $pluginrootdir) {
658             $plugin                 = new $typeclass();
659             $plugin->type           = $type;
660             $plugin->typerootdir    = $typerootdir;
661             $plugin->name           = $pluginname;
662             $plugin->rootdir        = $pluginrootdir;
664             $plugin->init_display_name();
665             $plugin->load_disk_version();
666             $plugin->load_db_version();
667             $plugin->load_required_main_version();
668             $plugin->init_is_standard();
670             $ondisk[$pluginname] = $plugin;
671         }
672         return $ondisk;
673     }
675     /**
676      * @see plugin_information::init_display_name()
677      */
678     public function init_display_name() {
679         if (! get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
680             $this->displayname = '[pluginname,' . $this->type . '_' . $this->name . ']';
681         } else {
682             $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
683         }
684     }
686     /**
687      * @see plugin_information::full_path()
688      */
689     public function full_path($relativepath) {
690         if (empty($this->rootdir)) {
691             return '';
692         }
693         return $this->rootdir . '/' . $relativepath;
694     }
696     /**
697      * Load the data from version.php.
698      * @return object the data object defined in version.php.
699      */
700     protected function load_version_php() {
701         $versionfile = $this->full_path('version.php');
703         $plugin = new stdClass();
704         if (is_readable($versionfile)) {
705             include($versionfile);
706         }
707         return $plugin;
708     }
710     /**
711      * @see plugin_information::load_disk_version()
712      */
713     public function load_disk_version() {
714         $plugin = $this->load_version_php();
715         if (isset($plugin->version)) {
716             $this->versiondisk = $plugin->version;
717         }
718     }
720     /**
721      * @see plugin_information::load_required_main_version()
722      */
723     public function load_required_main_version() {
724         $plugin = $this->load_version_php();
725         if (isset($plugin->requires)) {
726             $this->versionrequires = $plugin->requires;
727         }
728     }
730     /**
731      * Initialise {@link $dependencies} to the list of other plugins (in any)
732      * that this one requires to be installed.
733      */
734     protected function load_other_required_plugins() {
735         $plugin = $this->load_version_php();
736         if (!empty($plugin->dependencies)) {
737             $this->dependencies = $plugin->dependencies;
738         } else {
739             $this->dependencies = array(); // By default, no dependencies.
740         }
741     }
743     /**
744      * @see plugin_information::get_other_required_plugins()
745      */
746     public function get_other_required_plugins() {
747         if (is_null($this->dependencies)) {
748             $this->load_other_required_plugins();
749         }
750         return $this->dependencies;
751     }
753     /**
754      * @see plugin_information::load_db_version()
755      */
756     public function load_db_version() {
758         if ($ver = self::get_version_from_config_plugins($this->type . '_' . $this->name)) {
759             $this->versiondb = $ver;
760         }
761     }
763     /**
764      * @see plugin_information::init_is_standard()
765      */
766     public function init_is_standard() {
768         $standard = plugin_manager::standard_plugins_list($this->type);
770         if ($standard !== false) {
771             $standard = array_flip($standard);
772             if (isset($standard[$this->name])) {
773                 $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD;
774             } else {
775                 $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
776             }
777         }
778     }
780     /**
781      * @see plugin_information::is_standard()
782      */
783     public function is_standard() {
784         return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
785     }
787     /**
788      * @see plugin_information::get_status()
789      */
790     public function get_status() {
792         if (is_null($this->versiondb) and is_null($this->versiondisk)) {
793             return plugin_manager::PLUGIN_STATUS_NODB;
795         } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
796             return plugin_manager::PLUGIN_STATUS_NEW;
798         } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
799             return plugin_manager::PLUGIN_STATUS_MISSING;
801         } else if ((string)$this->versiondb === (string)$this->versiondisk) {
802             return plugin_manager::PLUGIN_STATUS_UPTODATE;
804         } else if ($this->versiondb < $this->versiondisk) {
805             return plugin_manager::PLUGIN_STATUS_UPGRADE;
807         } else if ($this->versiondb > $this->versiondisk) {
808             return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
810         } else {
811             // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
812             throw new coding_exception('Unable to determine plugin state, check the plugin versions');
813         }
814     }
816     /**
817      * @see plugin_information::is_enabled()
818      */
819     public function is_enabled() {
820         return null;
821     }
823     /**
824      * @see plugin_information::get_settings_url()
825      */
826     public function get_settings_url() {
827         return null;
828     }
830     /**
831      * @see plugin_information::get_uninstall_url()
832      */
833     public function get_uninstall_url() {
834         return null;
835     }
837     /**
838      * @see plugin_information::get_dir()
839      */
840     public function get_dir() {
841         global $CFG;
843         return substr($this->rootdir, strlen($CFG->dirroot));
844     }
846     /**
847      * Provides access to plugin versions from {config_plugins}
848      *
849      * @param string $plugin plugin name
850      * @param double $disablecache optional, defaults to false
851      * @return int|false the stored value or false if not found
852      */
853     protected function get_version_from_config_plugins($plugin, $disablecache=false) {
854         global $DB;
855         static $pluginversions = null;
857         if (is_null($pluginversions) or $disablecache) {
858             try {
859                 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
860             } catch (dml_exception $e) {
861                 // before install
862                 $pluginversions = array();
863             }
864         }
866         if (!array_key_exists($plugin, $pluginversions)) {
867             return false;
868         }
870         return $pluginversions[$plugin];
871     }
874 /**
875  * General class for all plugin types that do not have their own class
876  */
877 class plugintype_general extends plugintype_base implements plugin_information {
881 /**
882  * Class for page side blocks
883  */
884 class plugintype_block extends plugintype_base implements plugin_information {
886     /**
887      * @see plugin_information::get_plugins()
888      */
889     public static function get_plugins($type, $typerootdir, $typeclass) {
891         // get the information about blocks at the disk
892         $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
894         // add blocks missing from disk
895         $blocksinfo = self::get_blocks_info();
896         foreach ($blocksinfo as $blockname => $blockinfo) {
897             if (isset($blocks[$blockname])) {
898                 continue;
899             }
900             $plugin                 = new $typeclass();
901             $plugin->type           = $type;
902             $plugin->typerootdir    = $typerootdir;
903             $plugin->name           = $blockname;
904             $plugin->rootdir        = null;
905             $plugin->displayname    = $blockname;
906             $plugin->versiondb      = $blockinfo->version;
907             $plugin->init_is_standard();
909             $blocks[$blockname]   = $plugin;
910         }
912         return $blocks;
913     }
915     /**
916      * @see plugin_information::init_display_name()
917      */
918     public function init_display_name() {
920         if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
921             $this->displayname = get_string('pluginname', 'block_' . $this->name);
923         } else if (($block = block_instance($this->name)) !== false) {
924             $this->displayname = $block->get_title();
926         } else {
927             parent::init_display_name();
928         }
929     }
931     /**
932      * @see plugin_information::load_db_version()
933      */
934     public function load_db_version() {
935         global $DB;
937         $blocksinfo = self::get_blocks_info();
938         if (isset($blocksinfo[$this->name]->version)) {
939             $this->versiondb = $blocksinfo[$this->name]->version;
940         }
941     }
943     /**
944      * @see plugin_information::is_enabled()
945      */
946     public function is_enabled() {
948         $blocksinfo = self::get_blocks_info();
949         if (isset($blocksinfo[$this->name]->visible)) {
950             if ($blocksinfo[$this->name]->visible) {
951                 return true;
952             } else {
953                 return false;
954             }
955         } else {
956             return parent::is_enabled();
957         }
958     }
960     /**
961      * @see plugin_information::get_settings_url()
962      */
963     public function get_settings_url() {
965         if (($block = block_instance($this->name)) === false) {
966             return parent::get_settings_url();
968         } else if ($block->has_config()) {
969             if (file_exists($this->full_path('settings.php'))) {
970                 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
971             } else {
972                 $blocksinfo = self::get_blocks_info();
973                 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
974             }
976         } else {
977             return parent::get_settings_url();
978         }
979     }
981     /**
982      * @see plugin_information::get_uninstall_url()
983      */
984     public function get_uninstall_url() {
986         $blocksinfo = self::get_blocks_info();
987         return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
988     }
990     /**
991      * Provides access to the records in {block} table
992      *
993      * @param bool $disablecache do not use internal static cache
994      * @return array array of stdClasses
995      */
996     protected static function get_blocks_info($disablecache=false) {
997         global $DB;
998         static $blocksinfocache = null;
1000         if (is_null($blocksinfocache) or $disablecache) {
1001             try {
1002                 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
1003             } catch (dml_exception $e) {
1004                 // before install
1005                 $blocksinfocache = array();
1006             }
1007         }
1009         return $blocksinfocache;
1010     }
1013 /**
1014  * Class for text filters
1015  */
1016 class plugintype_filter extends plugintype_base implements plugin_information {
1018     /**
1019      * @see plugin_information::get_plugins()
1020      */
1021     public static function get_plugins($type, $typerootdir, $typeclass) {
1022         global $CFG, $DB;
1024         $filters = array();
1026         // get the list of filters from both /filter and /mod location
1027         $installed = filter_get_all_installed();
1029         foreach ($installed as $filterlegacyname => $displayname) {
1030             $plugin                 = new $typeclass();
1031             $plugin->type           = $type;
1032             $plugin->typerootdir    = $typerootdir;
1033             $plugin->name           = self::normalize_legacy_name($filterlegacyname);
1034             $plugin->rootdir        = $CFG->dirroot . '/' . $filterlegacyname;
1035             $plugin->displayname    = $displayname;
1037             $plugin->load_disk_version();
1038             $plugin->load_db_version();
1039             $plugin->load_required_main_version();
1040             $plugin->init_is_standard();
1042             $filters[$plugin->name] = $plugin;
1043         }
1045         $globalstates = self::get_global_states();
1047         if ($DB->get_manager()->table_exists('filter_active')) {
1048             // if we're upgrading from 1.9, the table does not exist yet
1049             // if it does, make sure that all installed filters are registered
1050             $needsreload  = false;
1051             foreach (array_keys($installed) as $filterlegacyname) {
1052                 if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
1053                     filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
1054                     $needsreload = true;
1055                 }
1056             }
1057             if ($needsreload) {
1058                 $globalstates = self::get_global_states(true);
1059             }
1060         }
1062         // make sure that all registered filters are installed, just in case
1063         foreach ($globalstates as $name => $info) {
1064             if (!isset($filters[$name])) {
1065                 // oops, there is a record in filter_active but the filter is not installed
1066                 $plugin                 = new $typeclass();
1067                 $plugin->type           = $type;
1068                 $plugin->typerootdir    = $typerootdir;
1069                 $plugin->name           = $name;
1070                 $plugin->rootdir        = $CFG->dirroot . '/' . $info->legacyname;
1071                 $plugin->displayname    = $info->legacyname;
1073                 $plugin->load_db_version();
1075                 if (is_null($plugin->versiondb)) {
1076                     // this is a hack to stimulate 'Missing from disk' error
1077                     // because $plugin->versiondisk will be null !== false
1078                     $plugin->versiondb = false;
1079                 }
1081                 $filters[$plugin->name] = $plugin;
1082             }
1083         }
1085         return $filters;
1086     }
1088     /**
1089      * @see plugin_information::init_display_name()
1090      */
1091     public function init_display_name() {
1092         // do nothing, the name is set in self::get_plugins()
1093     }
1095     /**
1096      * @see plugintype_base::load_version_php().
1097      */
1098     protected function load_version_php() {
1099         if (strpos($this->name, 'mod_') === 0) {
1100             // filters bundled with modules do not have a version.php and so
1101             // do not provide their own versioning information.
1102             return new stdClass();
1103         }
1104         return parent::load_version_php();
1105     }
1107     /**
1108      * @see plugin_information::is_enabled()
1109      */
1110     public function is_enabled() {
1112         $globalstates = self::get_global_states();
1114         foreach ($globalstates as $filterlegacyname => $info) {
1115             $name = self::normalize_legacy_name($filterlegacyname);
1116             if ($name === $this->name) {
1117                 if ($info->active == TEXTFILTER_DISABLED) {
1118                     return false;
1119                 } else {
1120                     // it may be 'On' or 'Off, but available'
1121                     return null;
1122                 }
1123             }
1124         }
1126         return null;
1127     }
1129     /**
1130      * @see plugin_information::get_settings_url()
1131      */
1132     public function get_settings_url() {
1134         $globalstates = self::get_global_states();
1135         $legacyname = $globalstates[$this->name]->legacyname;
1136         if (filter_has_global_settings($legacyname)) {
1137             return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1138         } else {
1139             return null;
1140         }
1141     }
1143     /**
1144      * @see plugin_information::get_uninstall_url()
1145      */
1146     public function get_uninstall_url() {
1148         if (strpos($this->name, 'mod_') === 0) {
1149             return null;
1150         } else {
1151             $globalstates = self::get_global_states();
1152             $legacyname = $globalstates[$this->name]->legacyname;
1153             return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1154         }
1155     }
1157     /**
1158      * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1159      *
1160      * @param string $legacyfiltername legacy filter name
1161      * @return string frankenstyle-like name
1162      */
1163     protected static function normalize_legacy_name($legacyfiltername) {
1165         $name = str_replace('/', '_', $legacyfiltername);
1166         if (strpos($name, 'filter_') === 0) {
1167             $name = substr($name, 7);
1168             if (empty($name)) {
1169                 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1170             }
1171         }
1173         return $name;
1174     }
1176     /**
1177      * Provides access to the results of {@link filter_get_global_states()}
1178      * but indexed by the normalized filter name
1179      *
1180      * The legacy filter name is available as ->legacyname property.
1181      *
1182      * @param bool $disablecache
1183      * @return array
1184      */
1185     protected static function get_global_states($disablecache=false) {
1186         global $DB;
1187         static $globalstatescache = null;
1189         if ($disablecache or is_null($globalstatescache)) {
1191             if (!$DB->get_manager()->table_exists('filter_active')) {
1192                 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1193                 // does not exist yet
1194                 $globalstatescache = array();
1196             } else {
1197                 foreach (filter_get_global_states() as $legacyname => $info) {
1198                     $name                       = self::normalize_legacy_name($legacyname);
1199                     $filterinfo                 = new stdClass();
1200                     $filterinfo->legacyname     = $legacyname;
1201                     $filterinfo->active         = $info->active;
1202                     $filterinfo->sortorder      = $info->sortorder;
1203                     $globalstatescache[$name]   = $filterinfo;
1204                 }
1205             }
1206         }
1208         return $globalstatescache;
1209     }
1212 /**
1213  * Class for activity modules
1214  */
1215 class plugintype_mod extends plugintype_base implements plugin_information {
1217     /**
1218      * @see plugin_information::get_plugins()
1219      */
1220     public static function get_plugins($type, $typerootdir, $typeclass) {
1222         // get the information about plugins at the disk
1223         $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1225         // add modules missing from disk
1226         $modulesinfo = self::get_modules_info();
1227         foreach ($modulesinfo as $modulename => $moduleinfo) {
1228             if (isset($modules[$modulename])) {
1229                 continue;
1230             }
1231             $plugin                 = new $typeclass();
1232             $plugin->type           = $type;
1233             $plugin->typerootdir    = $typerootdir;
1234             $plugin->name           = $modulename;
1235             $plugin->rootdir        = null;
1236             $plugin->displayname    = $modulename;
1237             $plugin->versiondb      = $moduleinfo->version;
1238             $plugin->init_is_standard();
1240             $modules[$modulename]   = $plugin;
1241         }
1243         return $modules;
1244     }
1246     /**
1247      * @see plugin_information::init_display_name()
1248      */
1249     public function init_display_name() {
1250         if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1251             $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1252         } else {
1253             $this->displayname = get_string('modulename', $this->type . '_' . $this->name);
1254         }
1255     }
1257     /**
1258      * Load the data from version.php.
1259      * @return object the data object defined in version.php.
1260      */
1261     protected function load_version_php() {
1262         $versionfile = $this->full_path('version.php');
1264         $module = new stdClass();
1265         if (is_readable($versionfile)) {
1266             include($versionfile);
1267         }
1268         return $module;
1269     }
1271     /**
1272      * @see plugin_information::load_db_version()
1273      */
1274     public function load_db_version() {
1275         global $DB;
1277         $modulesinfo = self::get_modules_info();
1278         if (isset($modulesinfo[$this->name]->version)) {
1279             $this->versiondb = $modulesinfo[$this->name]->version;
1280         }
1281     }
1283     /**
1284      * @see plugin_information::is_enabled()
1285      */
1286     public function is_enabled() {
1288         $modulesinfo = self::get_modules_info();
1289         if (isset($modulesinfo[$this->name]->visible)) {
1290             if ($modulesinfo[$this->name]->visible) {
1291                 return true;
1292             } else {
1293                 return false;
1294             }
1295         } else {
1296             return parent::is_enabled();
1297         }
1298     }
1300     /**
1301      * @see plugin_information::get_settings_url()
1302      */
1303     public function get_settings_url() {
1305         if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1306             return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
1307         } else {
1308             return parent::get_settings_url();
1309         }
1310     }
1312     /**
1313      * @see plugin_information::get_uninstall_url()
1314      */
1315     public function get_uninstall_url() {
1317         if ($this->name !== 'forum') {
1318             return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1319         } else {
1320             return null;
1321         }
1322     }
1324     /**
1325      * Provides access to the records in {modules} table
1326      *
1327      * @param bool $disablecache do not use internal static cache
1328      * @return array array of stdClasses
1329      */
1330     protected static function get_modules_info($disablecache=false) {
1331         global $DB;
1332         static $modulesinfocache = null;
1334         if (is_null($modulesinfocache) or $disablecache) {
1335             try {
1336                 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1337             } catch (dml_exception $e) {
1338                 // before install
1339                 $modulesinfocache = array();
1340             }
1341         }
1343         return $modulesinfocache;
1344     }
1348 /**
1349  * Class for question behaviours.
1350  */
1351 class plugintype_qbehaviour extends plugintype_base implements plugin_information {
1353     /**
1354      * @see plugintype_base::load_other_required_plugins().
1355      */
1356     protected function load_other_required_plugins() {
1357         parent::load_other_required_plugins();
1358         if (!empty($this->dependencies)) {
1359             return;
1360         }
1362         // Standard mechanism did not find anything, so try the legacy way.
1363         global $CFG;
1364         require_once($CFG->libdir . '/questionlib.php');
1365         $required = question_engine::get_behaviour_required_behaviours($this->name);
1366         foreach ($required as $other) {
1367             $this->dependencies['qbehaviour_' . $other] = ANY_VERSION;
1368         }
1369     }
1373 /**
1374  * Class for question types
1375  */
1376 class plugintype_qtype extends plugintype_base implements plugin_information {
1378     /**
1379      * @see plugin_information::init_display_name()
1380      */
1381     public function init_display_name() {
1382         if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1383             $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1384         } else {
1385             $this->displayname = get_string($this->name, 'qtype_' . $this->name);
1386         }
1387     }
1389     /**
1390      * @see plugintype_base::load_other_required_plugins().
1391      */
1392     protected function load_other_required_plugins() {
1393         parent::load_other_required_plugins();
1394         if (!empty($this->dependencies)) {
1395             return;
1396         }
1398         // Standard mechanism did not find anything, so try the legacy way.
1399         global $CFG;
1400         require_once($CFG->libdir . '/questionlib.php');
1401         $required = question_bank::get_qtype($this->name)->requires_qtypes();
1402         foreach ($required as $other) {
1403             $this->dependencies['qtype_' . $other] = ANY_VERSION;
1404         }
1405     }
1408 /**
1409  * Class for question formats
1410  */
1411 class plugintype_qformat extends plugintype_base implements plugin_information {
1413     /**
1414      * @see plugin_information::init_display_name()
1415      */
1416     public function init_display_name() {
1417         if (get_string_manager()->string_exists('pluginname', $this->type . '_' . $this->name)) {
1418             $this->displayname = get_string('pluginname', $this->type . '_' . $this->name);
1419         } else {
1420             $this->displayname = get_string($this->name, 'qformat_' . $this->name);
1421         }
1422     }
1425 /**
1426  * Class for authentication plugins
1427  */
1428 class plugintype_auth extends plugintype_base implements plugin_information {
1430     /**
1431      * @see plugin_information::is_enabled()
1432      */
1433     public function is_enabled() {
1434         global $CFG;
1435         /** @var null|array list of enabled authentication plugins */
1436         static $enabled = null;
1438         if (in_array($this->name, array('nologin', 'manual'))) {
1439             // these two are always enabled and can't be disabled
1440             return null;
1441         }
1443         if (is_null($enabled)) {
1444             $enabled = explode(',', $CFG->auth);
1445         }
1447         return isset($enabled[$this->name]);
1448     }
1450     /**
1451      * @see plugin_information::get_settings_url()
1452      */
1453     public function get_settings_url() {
1454         if (file_exists($this->full_path('settings.php'))) {
1455             return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
1456         } else {
1457             return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1458         }
1459     }
1462 /**
1463  * Class for enrolment plugins
1464  */
1465 class plugintype_enrol extends plugintype_base implements plugin_information {
1467     /**
1468      * We do not actually need whole enrolment classes here so we do not call
1469      * {@link enrol_get_plugins()}. Note that this may produce slightly different
1470      * results, for example if the enrolment plugin does not contain lib.php
1471      * but it is listed in $CFG->enrol_plugins_enabled
1472      *
1473      * @see plugin_information::is_enabled()
1474      */
1475     public function is_enabled() {
1476         global $CFG;
1477         /** @var null|array list of enabled enrolment plugins */
1478         static $enabled = null;
1480         if (is_null($enabled)) {
1481             $enabled = explode(',', $CFG->enrol_plugins_enabled);
1482         }
1484         return isset($enabled[$this->name]);
1485     }
1487     /**
1488      * @see plugin_information::get_settings_url()
1489      */
1490     public function get_settings_url() {
1492         if ($this->is_enabled() or file_exists($this->full_path('settings.php'))) {
1493             return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name));
1494         } else {
1495             return parent::get_settings_url();
1496         }
1497     }
1499     /**
1500      * @see plugin_information::get_uninstall_url()
1501      */
1502     public function get_uninstall_url() {
1503         return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey()));
1504     }
1507 /**
1508  * Class for messaging processors
1509  */
1510 class plugintype_message extends plugintype_base implements plugin_information {
1512     /**
1513      * @see plugin_information::get_settings_url()
1514      */
1515     public function get_settings_url() {
1517         if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1518             return new moodle_url('/admin/settings.php', array('section' => 'messagesetting' . $this->name));
1519         } else {
1520             return parent::get_settings_url();
1521         }
1522     }
1525 /**
1526  * Class for repositories
1527  */
1528 class plugintype_repository extends plugintype_base implements plugin_information {
1530     /**
1531      * @see plugin_information::is_enabled()
1532      */
1533     public function is_enabled() {
1535         $enabled = self::get_enabled_repositories();
1537         return isset($enabled[$this->name]);
1538     }
1540     /**
1541      * @see plugin_information::get_settings_url()
1542      */
1543     public function get_settings_url() {
1545         if ($this->is_enabled()) {
1546             return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
1547         } else {
1548             return parent::get_settings_url();
1549         }
1550     }
1552     /**
1553      * Provides access to the records in {repository} table
1554      *
1555      * @param bool $disablecache do not use internal static cache
1556      * @return array array of stdClasses
1557      */
1558     protected static function get_enabled_repositories($disablecache=false) {
1559         global $DB;
1560         static $repositories = null;
1562         if (is_null($repositories) or $disablecache) {
1563             $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1564         }
1566         return $repositories;
1567     }
1570 /**
1571  * Class for portfolios
1572  */
1573 class plugintype_portfolio extends plugintype_base implements plugin_information {
1575     /**
1576      * @see plugin_information::is_enabled()
1577      */
1578     public function is_enabled() {
1580         $enabled = self::get_enabled_portfolios();
1582         return isset($enabled[$this->name]);
1583     }
1585     /**
1586      * Provides access to the records in {portfolio_instance} table
1587      *
1588      * @param bool $disablecache do not use internal static cache
1589      * @return array array of stdClasses
1590      */
1591     protected static function get_enabled_portfolios($disablecache=false) {
1592         global $DB;
1593         static $portfolios = null;
1595         if (is_null($portfolios) or $disablecache) {
1596             $portfolios = array();
1597             $instances  = $DB->get_recordset('portfolio_instance', null, 'plugin');
1598             foreach ($instances as $instance) {
1599                 if (isset($portfolios[$instance->plugin])) {
1600                     if ($instance->visible) {
1601                         $portfolios[$instance->plugin]->visible = $instance->visible;
1602                     }
1603                 } else {
1604                     $portfolios[$instance->plugin] = $instance;
1605                 }
1606             }
1607         }
1609         return $portfolios;
1610     }
1613 /**
1614  * Class for themes
1615  */
1616 class plugintype_theme extends plugintype_base implements plugin_information {
1618     /**
1619      * @see plugin_information::is_enabled()
1620      */
1621     public function is_enabled() {
1622         global $CFG;
1624         if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
1625             (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
1626             return true;
1627         } else {
1628             return parent::is_enabled();
1629         }
1630     }
1633 /**
1634  * Class representing an MNet service
1635  */
1636 class plugintype_mnetservice extends plugintype_base implements plugin_information {
1638     /**
1639      * @see plugin_information::is_enabled()
1640      */
1641     public function is_enabled() {
1642         global $CFG;
1644         if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
1645             return false;
1646         } else {
1647             return parent::is_enabled();
1648         }
1649     }
1652 /**
1653  * Class for admin tool plugins
1654  */
1655 class plugintype_tool extends plugintype_base implements plugin_information {
1657     public function get_uninstall_url() {
1658         return new moodle_url('/admin/tools.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1659     }