2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Defines classes used for plugins management
20 * This library provides a unified interface to various plugin types in
21 * Moodle. It is mainly used by the plugins management admin page and the
22 * plugins check page during the upgrade.
26 * @copyright 2011 David Mudrak <david@moodle.com>
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30 defined('MOODLE_INTERNAL') ||
die();
33 * Singleton class providing general plugins management functionality
35 class plugin_manager
{
37 /** the plugin is shipped with standard Moodle distribution */
38 const PLUGIN_SOURCE_STANDARD
= 'std';
39 /** the plugin is added extension */
40 const PLUGIN_SOURCE_EXTENSION
= 'ext';
42 /** the plugin uses neither database nor capabilities, no versions */
43 const PLUGIN_STATUS_NODB
= 'nodb';
44 /** the plugin is up-to-date */
45 const PLUGIN_STATUS_UPTODATE
= 'uptodate';
46 /** the plugin is about to be installed */
47 const PLUGIN_STATUS_NEW
= 'new';
48 /** the plugin is about to be upgraded */
49 const PLUGIN_STATUS_UPGRADE
= 'upgrade';
50 /** the standard plugin is about to be deleted */
51 const PLUGIN_STATUS_DELETE
= 'delete';
52 /** the version at the disk is lower than the one already installed */
53 const PLUGIN_STATUS_DOWNGRADE
= 'downgrade';
54 /** the plugin is installed but missing from disk */
55 const PLUGIN_STATUS_MISSING
= 'missing';
57 /** @var plugin_manager holds the singleton instance */
58 protected static $singletoninstance;
59 /** @var array of raw plugins information */
60 protected $pluginsinfo = null;
61 /** @var array of raw subplugins information */
62 protected $subpluginsinfo = null;
65 * Direct initiation not allowed, use the factory method {@link self::instance()}
67 * @todo we might want to specify just a single plugin type to work with
69 protected function __construct() {
70 $this->get_plugins(true);
74 * Sorry, this is singleton
76 protected function __clone() {
80 * Factory method for this class
82 * @return plugin_manager the singleton instance
84 public static function instance() {
87 if (is_null(self
::$singletoninstance)) {
88 self
::$singletoninstance = new self();
90 return self
::$singletoninstance;
94 * Returns a tree of known plugins and information about them
96 * @param bool $disablecache force reload, cache can be used otherwise
97 * @return array 2D array. The first keys are plugin type names (e.g. qtype);
98 * the second keys are the plugin local name (e.g. multichoice); and
99 * the values are the corresponding {@link plugin_information} objects.
101 public function get_plugins($disablecache=false) {
103 if ($disablecache or is_null($this->pluginsinfo
)) {
104 $this->pluginsinfo
= array();
105 $plugintypes = get_plugin_types();
106 foreach ($plugintypes as $plugintype => $plugintyperootdir) {
107 if (in_array($plugintype, array('base', 'general'))) {
108 throw new coding_exception('Illegal usage of reserved word for plugin type');
110 if (class_exists('plugintype_' . $plugintype)) {
111 $plugintypeclass = 'plugintype_' . $plugintype;
113 $plugintypeclass = 'plugintype_general';
115 if (!in_array('plugin_information', class_implements($plugintypeclass))) {
116 throw new coding_exception('Class ' . $plugintypeclass . ' must implement plugin_information');
118 $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass);
119 $this->pluginsinfo
[$plugintype] = $plugins;
123 return $this->pluginsinfo
;
127 * Returns list of plugins that define their subplugins and the information
128 * about them from the db/subplugins.php file.
130 * At the moment, only activity modules can define subplugins.
132 * @param bool $disablecache force reload, cache can be used otherwise
133 * @return array with keys like 'mod_quiz', and values the data from the
134 * corresponding db/subplugins.php file.
136 public function get_subplugins($disablecache=false) {
138 if ($disablecache or is_null($this->subpluginsinfo
)) {
139 $this->subpluginsinfo
= array();
140 $mods = get_plugin_list('mod');
141 foreach ($mods as $mod => $moddir) {
142 $modsubplugins = array();
143 if (file_exists($moddir . '/db/subplugins.php')) {
144 include($moddir . '/db/subplugins.php');
145 foreach ($subplugins as $subplugintype => $subplugintyperootdir) {
146 $subplugin = new stdClass();
147 $subplugin->type
= $subplugintype;
148 $subplugin->typerootdir
= $subplugintyperootdir;
149 $modsubplugins[$subplugintype] = $subplugin;
151 $this->subpluginsinfo
['mod_' . $mod] = $modsubplugins;
156 return $this->subpluginsinfo
;
160 * Returns the name of the plugin that defines the given subplugin type
162 * If the given subplugin type is not actually a subplugin, returns false.
164 * @param string $subplugintype the name of subplugin type, eg. workshopform or quiz
165 * @return false|string the name of the parent plugin, eg. mod_workshop
167 public function get_parent_of_subplugin($subplugintype) {
170 foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
171 if (isset($subplugintypes[$subplugintype])) {
172 $parent = $pluginname;
181 * Returns a localized name of a given plugin
183 * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
186 public function plugin_name($plugin) {
187 list($type, $name) = normalize_component($plugin);
188 return $this->pluginsinfo
[$type][$name]->displayname
;
192 * Returns a localized name of a plugin type in plural form
194 * Most plugin types define their names in core_plugin lang file. In case of subplugins,
195 * we try to ask the parent plugin for the name. In the worst case, we will return
196 * the value of the passed $type parameter.
198 * @param string $type the type of the plugin, e.g. mod or workshopform
201 public function plugintype_name_plural($type) {
203 if (get_string_manager()->string_exists('type_' . $type . '_plural', 'core_plugin')) {
204 // for most plugin types, their names are defined in core_plugin lang file
205 return get_string('type_' . $type . '_plural', 'core_plugin');
207 } else if ($parent = $this->get_parent_of_subplugin($type)) {
208 // if this is a subplugin, try to ask the parent plugin for the name
209 if (get_string_manager()->string_exists('subplugintype_' . $type . '_plural', $parent)) {
210 return $this->plugin_name($parent) . ' / ' . get_string('subplugintype_' . $type . '_plural', $parent);
212 return $this->plugin_name($parent) . ' / ' . $type;
221 * @param string $component frankenstyle component name.
222 * @return plugin_information|null the corresponding plugin information.
224 public function get_plugin_info($component) {
225 list($type, $name) = normalize_component($component);
226 $plugins = $this->get_plugins();
227 if (isset($plugins[$type][$name])) {
228 return $plugins[$type][$name];
235 * Get a list of any other pluings that require this one.
236 * @param string $component frankenstyle component name.
237 * @return array of frankensyle component names that require this one.
239 public function other_plugins_that_require($component) {
241 foreach ($this->get_plugins() as $type => $plugins) {
242 foreach ($plugins as $plugin) {
243 $required = $plugin->get_other_required_plugins();
244 if (isset($required[$component])) {
245 $others[] = $plugin->component
;
253 * Check a dependencies list against the list of installed plugins.
254 * @param array $dependencies compenent name to required version or ANY_VERSION.
255 * @return bool true if all the dependencies are satisfied.
257 public function are_dependencies_satisfied($dependencies) {
258 foreach ($dependencies as $component => $requiredversion) {
259 $otherplugin = $this->get_plugin_info($component);
260 if (is_null($otherplugin)) {
264 if ($requiredversion != ANY_VERSION
and $otherplugin->versiondisk
< $requiredversion) {
273 * Checks all dependencies for all installed plugins. Used by install and upgrade.
274 * @param int $moodleversion the version from version.php.
275 * @return bool true if all the dependencies are satisfied for all plugins.
277 public function all_plugins_ok($moodleversion) {
278 foreach ($this->get_plugins() as $type => $plugins) {
279 foreach ($plugins as $plugin) {
281 if (!empty($plugin->versionrequires
) && $plugin->versionrequires
> $moodleversion) {
285 if (!$this->are_dependencies_satisfied($plugin->get_other_required_plugins())) {
295 * Defines a list of all plugins that were originally shipped in the standard Moodle distribution,
296 * but are not anymore and are deleted during upgrades.
298 * The main purpose of this list is to hide missing plugins during upgrade.
300 * @param string $type plugin type
301 * @param string $name plugin name
304 public static function is_deleted_standard_plugin($type, $name) {
305 static $plugins = array(
306 'block' => array('admin', 'admin_tree', 'loancalc', 'search'),
307 'filter' => array('mod_data', 'mod_glossary'),
310 if (!isset($plugins[$type])) {
313 return in_array($name, $plugins[$type]);
317 * Defines a white list of all plugins shipped in the standard Moodle distribution
319 * @param string $type
320 * @return false|array array of standard plugins or false if the type is unknown
322 public static function standard_plugins_list($type) {
323 static $standard_plugins = array(
325 'assignment' => array(
326 'offline', 'online', 'upload', 'uploadsingle'
330 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
331 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
332 'shibboleth', 'webservice'
336 'activity_modules', 'admin_bookmarks', 'blog_menu',
337 'blog_recent', 'blog_tags', 'calendar_month',
338 'calendar_upcoming', 'comments', 'community',
339 'completionstatus', 'course_list', 'course_overview',
340 'course_summary', 'feedback', 'glossary_random', 'html',
341 'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile',
342 'navigation', 'news_items', 'online_users', 'participants',
343 'private_files', 'quiz_results', 'recent_activity',
344 'rss_client', 'search_forums', 'section_links',
345 'selfcompletion', 'settings', 'site_main_menu',
346 'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
349 'coursereport' => array(
353 'datafield' => array(
354 'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
355 'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
358 'datapreset' => array(
363 'textarea', 'tinymce'
367 'authorize', 'category', 'cohort', 'database', 'flatfile',
368 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
373 'activitynames', 'algebra', 'censor', 'emailprotect',
374 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
375 'urltolink', 'data', 'glossary'
379 'scorm', 'social', 'topics', 'weeks'
382 'gradeexport' => array(
383 'ods', 'txt', 'xls', 'xml'
386 'gradeimport' => array(
390 'gradereport' => array(
391 'grader', 'outcomes', 'overview', 'user'
394 'gradingform' => array(
402 'email', 'jabber', 'popup'
405 'mnetservice' => array(
410 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
411 'forum', 'glossary', 'imscp', 'label', 'lesson', 'lti', 'page',
412 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
415 'plagiarism' => array(
418 'portfolio' => array(
419 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
422 'profilefield' => array(
423 'checkbox', 'datetime', 'menu', 'text', 'textarea'
426 'qbehaviour' => array(
427 'adaptive', 'adaptivenopenalty', 'deferredcbm',
428 'deferredfeedback', 'immediatecbm', 'immediatefeedback',
429 'informationitem', 'interactive', 'interactivecountback',
430 'manualgraded', 'missing'
434 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
435 'learnwise', 'missingword', 'multianswer', 'webct',
440 'calculated', 'calculatedmulti', 'calculatedsimple',
441 'description', 'essay', 'match', 'missingtype', 'multianswer',
442 'multichoice', 'numerical', 'random', 'randomsamatch',
443 'shortanswer', 'truefalse'
447 'grading', 'overview', 'responses', 'statistics'
450 'quizaccess' => array(
451 'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate',
452 'password', 'safebrowser', 'securewindow', 'timelimit'
456 'backups', 'completion', 'configlog', 'courseoverview',
457 'log', 'loglive', 'outline', 'participation', 'progress', 'questioninstances', 'security', 'stats'
460 'repository' => array(
461 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
462 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
463 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
464 'wikimedia', 'youtube'
467 'scormreport' => array(
473 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
474 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
475 'fusion', 'leatherbound', 'magazine', 'mymobile', 'nimble',
476 'nonzero', 'overlay', 'serenity', 'sky_high', 'splash',
477 'standard', 'standardold'
481 'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
482 'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
483 'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
484 'uploaduser', 'unsuproles', 'xmldb'
487 'webservice' => array(
488 'amf', 'rest', 'soap', 'xmlrpc'
491 'workshopallocation' => array(
495 'workshopeval' => array(
499 'workshopform' => array(
500 'accumulative', 'comments', 'numerrors', 'rubric'
504 if (isset($standard_plugins[$type])) {
505 return $standard_plugins[$type];
514 * Interface for making information about a plugin available.
516 * Note that most of the useful information is made available in pubic fields,
517 * which cannot be documented in this interface. See the field definitions on
518 * {@link plugintype_base} to find out what information is available.
520 * @property-read string component the component name, type_name
522 interface plugin_information
{
525 * Gathers and returns the information about all plugins of the given type
527 * Passing the parameter $typeclass allows us to reach the same effect as with the
528 * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
529 * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
531 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
532 * @param string $typerootdir full path to the location of the plugin dir
533 * @param string $typeclass the name of the actually called class
534 * @return array of plugintype classes, indexed by the plugin name
536 public static function get_plugins($type, $typerootdir, $typeclass);
539 * Sets $displayname property to a localized name of the plugin
543 public function init_display_name();
546 * Sets $versiondisk property to a numerical value representing the
547 * version of the plugin's source code.
549 * If the value is null after calling this method, either the plugin
550 * does not use versioning (typically does not have any database
551 * data) or is missing from disk.
555 public function load_disk_version();
558 * Sets $versiondb property to a numerical value representing the
559 * currently installed version of the plugin.
561 * If the value is null after calling this method, either the plugin
562 * does not use versioning (typically does not have any database
563 * data) or has not been installed yet.
567 public function load_db_version();
570 * Sets $versionrequires property to a numerical value representing
571 * the version of Moodle core that this plugin requires.
575 public function load_required_main_version();
578 * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
581 * If the property's value is null after calling this method, then
582 * the type of the plugin has not been recognized and you should throw
587 public function init_is_standard();
590 * Returns true if the plugin is shipped with the official distribution
591 * of the current Moodle version, false otherwise.
595 public function is_standard();
598 * Returns the status of the plugin
600 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
602 public function get_status();
605 * Get the list of other plugins that this plugin requires ot be installed.
606 * @return array with keys the frankenstyle plugin name, and values either
607 * a version string (like '2011101700') or the constant ANY_VERSION.
609 public function get_other_required_plugins();
612 * Returns the information about plugin availability
614 * True means that the plugin is enabled. False means that the plugin is
615 * disabled. Null means that the information is not available, or the
616 * plugin does not support configurable availability or the availability
617 * can not be changed.
621 public function is_enabled();
624 * Returns the URL of the plugin settings screen
626 * Null value means that the plugin either does not have the settings screen
627 * or its location is not available via this library.
629 * @return null|moodle_url
631 public function get_settings_url();
634 * Returns the URL of the screen where this plugin can be uninstalled
636 * Visiting that URL must be safe, that is a manual confirmation is needed
637 * for actual uninstallation of the plugin. Null value means that the
638 * plugin either does not support uninstallation, or does not require any
639 * database cleanup or the location of the screen is not available via this
642 * @return null|moodle_url
644 public function get_uninstall_url();
647 * Returns relative directory of the plugin with heading '/'
649 * @example /mod/workshop
652 public function get_dir();
655 * Return the full path name of a file within the plugin.
656 * No check is made to see if the file exists.
657 * @param string $relativepath e.g. 'version.php'.
658 * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
660 public function full_path($relativepath);
664 * Defines public properties that all plugintype classes must have
665 * and provides default implementation of required methods.
667 * @property-read string component the component name, type_name
669 abstract class plugintype_base
{
671 /** @var string the plugintype name, eg. mod, auth or workshopform */
673 /** @var string full path to the location of all the plugins of this type */
675 /** @var string the plugin name, eg. assignment, ldap */
677 /** @var string the localized plugin name */
679 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
681 /** @var fullpath to the location of this plugin */
683 /** @var int|string the version of the plugin's source code */
685 /** @var int|string the version of the installed plugin */
687 /** @var int|float|string required version of Moodle core */
688 public $versionrequires;
689 /** @var array other plugins that this one depends on.
690 * Lazy-loaded by {@link get_other_required_plugins()} */
691 public $dependencies = null;
692 /** @var int number of instances of the plugin - not supported yet */
694 /** @var int order of the plugin among other plugins of the same type - not supported yet */
698 * @see plugin_information::get_plugins()
700 public static function get_plugins($type, $typerootdir, $typeclass) {
702 // get the information about plugins at the disk
703 $plugins = get_plugin_list($type);
705 foreach ($plugins as $pluginname => $pluginrootdir) {
706 $plugin = new $typeclass();
707 $plugin->type
= $type;
708 $plugin->typerootdir
= $typerootdir;
709 $plugin->name
= $pluginname;
710 $plugin->rootdir
= $pluginrootdir;
712 $plugin->init_display_name();
713 $plugin->load_disk_version();
714 $plugin->load_db_version();
715 $plugin->load_required_main_version();
716 $plugin->init_is_standard();
718 $ondisk[$pluginname] = $plugin;
724 * @see plugin_information::init_display_name()
726 public function init_display_name() {
727 if (!get_string_manager()->string_exists('pluginname', $this->component
)) {
728 $this->displayname
= '[pluginname,' . $this->component
. ']';
730 $this->displayname
= get_string('pluginname', $this->component
);
735 * Magic method getter, redirects to read only values.
736 * @param string $name
739 public function __get($name) {
741 case 'component': return $this->type
. '_' . $this->name
;
744 debugging('Invalid plugin property accessed! '.$name);
750 * @see plugin_information::full_path()
752 public function full_path($relativepath) {
753 if (empty($this->rootdir
)) {
756 return $this->rootdir
. '/' . $relativepath;
760 * Load the data from version.php.
761 * @return object the data object defined in version.php.
763 protected function load_version_php() {
764 $versionfile = $this->full_path('version.php');
766 $plugin = new stdClass();
767 if (is_readable($versionfile)) {
768 include($versionfile);
774 * @see plugin_information::load_disk_version()
776 public function load_disk_version() {
777 $plugin = $this->load_version_php();
778 if (isset($plugin->version
)) {
779 $this->versiondisk
= $plugin->version
;
784 * @see plugin_information::load_required_main_version()
786 public function load_required_main_version() {
787 $plugin = $this->load_version_php();
788 if (isset($plugin->requires
)) {
789 $this->versionrequires
= $plugin->requires
;
794 * Initialise {@link $dependencies} to the list of other plugins (in any)
795 * that this one requires to be installed.
797 protected function load_other_required_plugins() {
798 $plugin = $this->load_version_php();
799 if (!empty($plugin->dependencies
)) {
800 $this->dependencies
= $plugin->dependencies
;
802 $this->dependencies
= array(); // By default, no dependencies.
807 * @see plugin_information::get_other_required_plugins()
809 public function get_other_required_plugins() {
810 if (is_null($this->dependencies
)) {
811 $this->load_other_required_plugins();
813 return $this->dependencies
;
817 * @see plugin_information::load_db_version()
819 public function load_db_version() {
821 if ($ver = self
::get_version_from_config_plugins($this->component
)) {
822 $this->versiondb
= $ver;
827 * @see plugin_information::init_is_standard()
829 public function init_is_standard() {
831 $standard = plugin_manager
::standard_plugins_list($this->type
);
833 if ($standard !== false) {
834 $standard = array_flip($standard);
835 if (isset($standard[$this->name
])) {
836 $this->source
= plugin_manager
::PLUGIN_SOURCE_STANDARD
;
837 } else if (!is_null($this->versiondb
) and is_null($this->versiondisk
)
838 and plugin_manager
::is_deleted_standard_plugin($this->type
, $this->name
)) {
839 $this->source
= plugin_manager
::PLUGIN_SOURCE_STANDARD
; // to be deleted
841 $this->source
= plugin_manager
::PLUGIN_SOURCE_EXTENSION
;
847 * @see plugin_information::is_standard()
849 public function is_standard() {
850 return $this->source
=== plugin_manager
::PLUGIN_SOURCE_STANDARD
;
854 * @see plugin_information::get_status()
856 public function get_status() {
858 if (is_null($this->versiondb
) and is_null($this->versiondisk
)) {
859 return plugin_manager
::PLUGIN_STATUS_NODB
;
861 } else if (is_null($this->versiondb
) and !is_null($this->versiondisk
)) {
862 return plugin_manager
::PLUGIN_STATUS_NEW
;
864 } else if (!is_null($this->versiondb
) and is_null($this->versiondisk
)) {
865 if (plugin_manager
::is_deleted_standard_plugin($this->type
, $this->name
)) {
866 return plugin_manager
::PLUGIN_STATUS_DELETE
;
868 return plugin_manager
::PLUGIN_STATUS_MISSING
;
871 } else if ((string)$this->versiondb
=== (string)$this->versiondisk
) {
872 return plugin_manager
::PLUGIN_STATUS_UPTODATE
;
874 } else if ($this->versiondb
< $this->versiondisk
) {
875 return plugin_manager
::PLUGIN_STATUS_UPGRADE
;
877 } else if ($this->versiondb
> $this->versiondisk
) {
878 return plugin_manager
::PLUGIN_STATUS_DOWNGRADE
;
881 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
882 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
887 * @see plugin_information::is_enabled()
889 public function is_enabled() {
894 * @see plugin_information::get_settings_url()
896 public function get_settings_url() {
901 * @see plugin_information::get_uninstall_url()
903 public function get_uninstall_url() {
908 * @see plugin_information::get_dir()
910 public function get_dir() {
913 return substr($this->rootdir
, strlen($CFG->dirroot
));
917 * Provides access to plugin versions from {config_plugins}
919 * @param string $plugin plugin name
920 * @param double $disablecache optional, defaults to false
921 * @return int|false the stored value or false if not found
923 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
925 static $pluginversions = null;
927 if (is_null($pluginversions) or $disablecache) {
929 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
930 } catch (dml_exception
$e) {
932 $pluginversions = array();
936 if (!array_key_exists($plugin, $pluginversions)) {
940 return $pluginversions[$plugin];
945 * General class for all plugin types that do not have their own class
947 class plugintype_general
extends plugintype_base
implements plugin_information
{
952 * Class for page side blocks
954 class plugintype_block
extends plugintype_base
implements plugin_information
{
957 * @see plugin_information::get_plugins()
959 public static function get_plugins($type, $typerootdir, $typeclass) {
961 // get the information about blocks at the disk
962 $blocks = parent
::get_plugins($type, $typerootdir, $typeclass);
964 // add blocks missing from disk
965 $blocksinfo = self
::get_blocks_info();
966 foreach ($blocksinfo as $blockname => $blockinfo) {
967 if (isset($blocks[$blockname])) {
970 $plugin = new $typeclass();
971 $plugin->type
= $type;
972 $plugin->typerootdir
= $typerootdir;
973 $plugin->name
= $blockname;
974 $plugin->rootdir
= null;
975 $plugin->displayname
= $blockname;
976 $plugin->versiondb
= $blockinfo->version
;
977 $plugin->init_is_standard();
979 $blocks[$blockname] = $plugin;
986 * @see plugin_information::init_display_name()
988 public function init_display_name() {
990 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name
)) {
991 $this->displayname
= get_string('pluginname', 'block_' . $this->name
);
993 } else if (($block = block_instance($this->name
)) !== false) {
994 $this->displayname
= $block->get_title();
997 parent
::init_display_name();
1002 * @see plugin_information::load_db_version()
1004 public function load_db_version() {
1007 $blocksinfo = self
::get_blocks_info();
1008 if (isset($blocksinfo[$this->name
]->version
)) {
1009 $this->versiondb
= $blocksinfo[$this->name
]->version
;
1014 * @see plugin_information::is_enabled()
1016 public function is_enabled() {
1018 $blocksinfo = self
::get_blocks_info();
1019 if (isset($blocksinfo[$this->name
]->visible
)) {
1020 if ($blocksinfo[$this->name
]->visible
) {
1026 return parent
::is_enabled();
1031 * @see plugin_information::get_settings_url()
1033 public function get_settings_url() {
1035 if (($block = block_instance($this->name
)) === false) {
1036 return parent
::get_settings_url();
1038 } else if ($block->has_config()) {
1039 if (file_exists($this->full_path('settings.php'))) {
1040 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name
));
1042 $blocksinfo = self
::get_blocks_info();
1043 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name
]->id
));
1047 return parent
::get_settings_url();
1052 * @see plugin_information::get_uninstall_url()
1054 public function get_uninstall_url() {
1056 $blocksinfo = self
::get_blocks_info();
1057 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name
]->id
, 'sesskey' => sesskey()));
1061 * Provides access to the records in {block} table
1063 * @param bool $disablecache do not use internal static cache
1064 * @return array array of stdClasses
1066 protected static function get_blocks_info($disablecache=false) {
1068 static $blocksinfocache = null;
1070 if (is_null($blocksinfocache) or $disablecache) {
1072 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
1073 } catch (dml_exception
$e) {
1075 $blocksinfocache = array();
1079 return $blocksinfocache;
1084 * Class for text filters
1086 class plugintype_filter
extends plugintype_base
implements plugin_information
{
1089 * @see plugin_information::get_plugins()
1091 public static function get_plugins($type, $typerootdir, $typeclass) {
1096 // get the list of filters from both /filter and /mod location
1097 $installed = filter_get_all_installed();
1099 foreach ($installed as $filterlegacyname => $displayname) {
1100 $plugin = new $typeclass();
1101 $plugin->type
= $type;
1102 $plugin->typerootdir
= $typerootdir;
1103 $plugin->name
= self
::normalize_legacy_name($filterlegacyname);
1104 $plugin->rootdir
= $CFG->dirroot
. '/' . $filterlegacyname;
1105 $plugin->displayname
= $displayname;
1107 $plugin->load_disk_version();
1108 $plugin->load_db_version();
1109 $plugin->load_required_main_version();
1110 $plugin->init_is_standard();
1112 $filters[$plugin->name
] = $plugin;
1115 $globalstates = self
::get_global_states();
1117 if ($DB->get_manager()->table_exists('filter_active')) {
1118 // if we're upgrading from 1.9, the table does not exist yet
1119 // if it does, make sure that all installed filters are registered
1120 $needsreload = false;
1121 foreach (array_keys($installed) as $filterlegacyname) {
1122 if (!isset($globalstates[self
::normalize_legacy_name($filterlegacyname)])) {
1123 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED
);
1124 $needsreload = true;
1128 $globalstates = self
::get_global_states(true);
1132 // make sure that all registered filters are installed, just in case
1133 foreach ($globalstates as $name => $info) {
1134 if (!isset($filters[$name])) {
1135 // oops, there is a record in filter_active but the filter is not installed
1136 $plugin = new $typeclass();
1137 $plugin->type
= $type;
1138 $plugin->typerootdir
= $typerootdir;
1139 $plugin->name
= $name;
1140 $plugin->rootdir
= $CFG->dirroot
. '/' . $info->legacyname
;
1141 $plugin->displayname
= $info->legacyname
;
1143 $plugin->load_db_version();
1145 if (is_null($plugin->versiondb
)) {
1146 // this is a hack to stimulate 'Missing from disk' error
1147 // because $plugin->versiondisk will be null !== false
1148 $plugin->versiondb
= false;
1151 $filters[$plugin->name
] = $plugin;
1159 * @see plugin_information::init_display_name()
1161 public function init_display_name() {
1162 // do nothing, the name is set in self::get_plugins()
1166 * @see plugintype_base::load_version_php().
1168 protected function load_version_php() {
1169 if (strpos($this->name
, 'mod_') === 0) {
1170 // filters bundled with modules do not have a version.php and so
1171 // do not provide their own versioning information.
1172 return new stdClass();
1174 return parent
::load_version_php();
1178 * @see plugin_information::is_enabled()
1180 public function is_enabled() {
1182 $globalstates = self
::get_global_states();
1184 foreach ($globalstates as $filterlegacyname => $info) {
1185 $name = self
::normalize_legacy_name($filterlegacyname);
1186 if ($name === $this->name
) {
1187 if ($info->active
== TEXTFILTER_DISABLED
) {
1190 // it may be 'On' or 'Off, but available'
1200 * @see plugin_information::get_settings_url()
1202 public function get_settings_url() {
1204 $globalstates = self
::get_global_states();
1205 $legacyname = $globalstates[$this->name
]->legacyname
;
1206 if (filter_has_global_settings($legacyname)) {
1207 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1214 * @see plugin_information::get_uninstall_url()
1216 public function get_uninstall_url() {
1218 if (strpos($this->name
, 'mod_') === 0) {
1221 $globalstates = self
::get_global_states();
1222 $legacyname = $globalstates[$this->name
]->legacyname
;
1223 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1228 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1230 * @param string $legacyfiltername legacy filter name
1231 * @return string frankenstyle-like name
1233 protected static function normalize_legacy_name($legacyfiltername) {
1235 $name = str_replace('/', '_', $legacyfiltername);
1236 if (strpos($name, 'filter_') === 0) {
1237 $name = substr($name, 7);
1239 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1247 * Provides access to the results of {@link filter_get_global_states()}
1248 * but indexed by the normalized filter name
1250 * The legacy filter name is available as ->legacyname property.
1252 * @param bool $disablecache
1255 protected static function get_global_states($disablecache=false) {
1257 static $globalstatescache = null;
1259 if ($disablecache or is_null($globalstatescache)) {
1261 if (!$DB->get_manager()->table_exists('filter_active')) {
1262 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1263 // does not exist yet
1264 $globalstatescache = array();
1267 foreach (filter_get_global_states() as $legacyname => $info) {
1268 $name = self
::normalize_legacy_name($legacyname);
1269 $filterinfo = new stdClass();
1270 $filterinfo->legacyname
= $legacyname;
1271 $filterinfo->active
= $info->active
;
1272 $filterinfo->sortorder
= $info->sortorder
;
1273 $globalstatescache[$name] = $filterinfo;
1278 return $globalstatescache;
1283 * Class for activity modules
1285 class plugintype_mod
extends plugintype_base
implements plugin_information
{
1288 * @see plugin_information::get_plugins()
1290 public static function get_plugins($type, $typerootdir, $typeclass) {
1292 // get the information about plugins at the disk
1293 $modules = parent
::get_plugins($type, $typerootdir, $typeclass);
1295 // add modules missing from disk
1296 $modulesinfo = self
::get_modules_info();
1297 foreach ($modulesinfo as $modulename => $moduleinfo) {
1298 if (isset($modules[$modulename])) {
1301 $plugin = new $typeclass();
1302 $plugin->type
= $type;
1303 $plugin->typerootdir
= $typerootdir;
1304 $plugin->name
= $modulename;
1305 $plugin->rootdir
= null;
1306 $plugin->displayname
= $modulename;
1307 $plugin->versiondb
= $moduleinfo->version
;
1308 $plugin->init_is_standard();
1310 $modules[$modulename] = $plugin;
1317 * @see plugin_information::init_display_name()
1319 public function init_display_name() {
1320 if (get_string_manager()->string_exists('pluginname', $this->component
)) {
1321 $this->displayname
= get_string('pluginname', $this->component
);
1323 $this->displayname
= get_string('modulename', $this->component
);
1328 * Load the data from version.php.
1329 * @return object the data object defined in version.php.
1331 protected function load_version_php() {
1332 $versionfile = $this->full_path('version.php');
1334 $module = new stdClass();
1335 if (is_readable($versionfile)) {
1336 include($versionfile);
1342 * @see plugin_information::load_db_version()
1344 public function load_db_version() {
1347 $modulesinfo = self
::get_modules_info();
1348 if (isset($modulesinfo[$this->name
]->version
)) {
1349 $this->versiondb
= $modulesinfo[$this->name
]->version
;
1354 * @see plugin_information::is_enabled()
1356 public function is_enabled() {
1358 $modulesinfo = self
::get_modules_info();
1359 if (isset($modulesinfo[$this->name
]->visible
)) {
1360 if ($modulesinfo[$this->name
]->visible
) {
1366 return parent
::is_enabled();
1371 * @see plugin_information::get_settings_url()
1373 public function get_settings_url() {
1375 if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1376 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name
));
1378 return parent
::get_settings_url();
1383 * @see plugin_information::get_uninstall_url()
1385 public function get_uninstall_url() {
1387 if ($this->name
!== 'forum') {
1388 return new moodle_url('/admin/modules.php', array('delete' => $this->name
, 'sesskey' => sesskey()));
1395 * Provides access to the records in {modules} table
1397 * @param bool $disablecache do not use internal static cache
1398 * @return array array of stdClasses
1400 protected static function get_modules_info($disablecache=false) {
1402 static $modulesinfocache = null;
1404 if (is_null($modulesinfocache) or $disablecache) {
1406 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1407 } catch (dml_exception
$e) {
1409 $modulesinfocache = array();
1413 return $modulesinfocache;
1419 * Class for question behaviours.
1421 class plugintype_qbehaviour
extends plugintype_base
implements plugin_information
{
1423 * @see plugin_information::get_uninstall_url()
1425 public function get_uninstall_url() {
1426 return new moodle_url('/admin/qbehaviours.php',
1427 array('delete' => $this->name
, 'sesskey' => sesskey()));
1433 * Class for question types
1435 class plugintype_qtype
extends plugintype_base
implements plugin_information
{
1437 * @see plugin_information::get_uninstall_url()
1439 public function get_uninstall_url() {
1440 return new moodle_url('/admin/qtypes.php',
1441 array('delete' => $this->name
, 'sesskey' => sesskey()));
1447 * Class for authentication plugins
1449 class plugintype_auth
extends plugintype_base
implements plugin_information
{
1452 * @see plugin_information::is_enabled()
1454 public function is_enabled() {
1456 /** @var null|array list of enabled authentication plugins */
1457 static $enabled = null;
1459 if (in_array($this->name
, array('nologin', 'manual'))) {
1460 // these two are always enabled and can't be disabled
1464 if (is_null($enabled)) {
1465 $enabled = array_flip(explode(',', $CFG->auth
));
1468 return isset($enabled[$this->name
]);
1472 * @see plugin_information::get_settings_url()
1474 public function get_settings_url() {
1475 if (file_exists($this->full_path('settings.php'))) {
1476 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name
));
1478 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name
));
1484 * Class for enrolment plugins
1486 class plugintype_enrol
extends plugintype_base
implements plugin_information
{
1489 * We do not actually need whole enrolment classes here so we do not call
1490 * {@link enrol_get_plugins()}. Note that this may produce slightly different
1491 * results, for example if the enrolment plugin does not contain lib.php
1492 * but it is listed in $CFG->enrol_plugins_enabled
1494 * @see plugin_information::is_enabled()
1496 public function is_enabled() {
1498 /** @var null|array list of enabled enrolment plugins */
1499 static $enabled = null;
1501 if (is_null($enabled)) {
1502 $enabled = array_flip(explode(',', $CFG->enrol_plugins_enabled
));
1505 return isset($enabled[$this->name
]);
1509 * @see plugin_information::get_settings_url()
1511 public function get_settings_url() {
1513 if ($this->is_enabled() or file_exists($this->full_path('settings.php'))) {
1514 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name
));
1516 return parent
::get_settings_url();
1521 * @see plugin_information::get_uninstall_url()
1523 public function get_uninstall_url() {
1524 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name
, 'sesskey' => sesskey()));
1529 * Class for messaging processors
1531 class plugintype_message
extends plugintype_base
implements plugin_information
{
1534 * @see plugin_information::get_settings_url()
1536 public function get_settings_url() {
1538 if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1539 return new moodle_url('/admin/settings.php', array('section' => 'messagesetting' . $this->name
));
1541 return parent
::get_settings_url();
1547 * Class for repositories
1549 class plugintype_repository
extends plugintype_base
implements plugin_information
{
1552 * @see plugin_information::is_enabled()
1554 public function is_enabled() {
1556 $enabled = self
::get_enabled_repositories();
1558 return isset($enabled[$this->name
]);
1562 * @see plugin_information::get_settings_url()
1564 public function get_settings_url() {
1566 if ($this->is_enabled()) {
1567 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name
));
1569 return parent
::get_settings_url();
1574 * Provides access to the records in {repository} table
1576 * @param bool $disablecache do not use internal static cache
1577 * @return array array of stdClasses
1579 protected static function get_enabled_repositories($disablecache=false) {
1581 static $repositories = null;
1583 if (is_null($repositories) or $disablecache) {
1584 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1587 return $repositories;
1592 * Class for portfolios
1594 class plugintype_portfolio
extends plugintype_base
implements plugin_information
{
1597 * @see plugin_information::is_enabled()
1599 public function is_enabled() {
1601 $enabled = self
::get_enabled_portfolios();
1603 return isset($enabled[$this->name
]);
1607 * Provides access to the records in {portfolio_instance} table
1609 * @param bool $disablecache do not use internal static cache
1610 * @return array array of stdClasses
1612 protected static function get_enabled_portfolios($disablecache=false) {
1614 static $portfolios = null;
1616 if (is_null($portfolios) or $disablecache) {
1617 $portfolios = array();
1618 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
1619 foreach ($instances as $instance) {
1620 if (isset($portfolios[$instance->plugin
])) {
1621 if ($instance->visible
) {
1622 $portfolios[$instance->plugin
]->visible
= $instance->visible
;
1625 $portfolios[$instance->plugin
] = $instance;
1637 class plugintype_theme
extends plugintype_base
implements plugin_information
{
1640 * @see plugin_information::is_enabled()
1642 public function is_enabled() {
1645 if ((!empty($CFG->theme
) and $CFG->theme
=== $this->name
) or
1646 (!empty($CFG->themelegacy
) and $CFG->themelegacy
=== $this->name
)) {
1649 return parent
::is_enabled();
1655 * Class representing an MNet service
1657 class plugintype_mnetservice
extends plugintype_base
implements plugin_information
{
1660 * @see plugin_information::is_enabled()
1662 public function is_enabled() {
1665 if (empty($CFG->mnet_dispatcher_mode
) ||
$CFG->mnet_dispatcher_mode
!== 'strict') {
1668 return parent
::is_enabled();
1674 * Class for admin tool plugins
1676 class plugintype_tool
extends plugintype_base
implements plugin_information
{
1678 public function get_uninstall_url() {
1679 return new moodle_url('/admin/tools.php', array('delete' => $this->name
, 'sesskey' => sesskey()));
1684 * Class for admin tool plugins
1686 class plugintype_report
extends plugintype_base
implements plugin_information
{
1688 public function get_uninstall_url() {
1689 return new moodle_url('/admin/reports.php', array('delete' => $this->name
, 'sesskey' => sesskey()));