PageRenderTime 73ms CodeModel.GetById 3ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/pluginlib.php

http://github.com/moodle/moodle
PHP | 1690 lines | 893 code | 251 blank | 546 comment | 131 complexity | 6cbc46e95e666de47f05e5b97a2e09dd MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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/>.
  16
  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 */
  29
  30defined('MOODLE_INTERNAL') || die();
  31
  32/**
  33 * Singleton class providing general plugins management functionality
  34 */
  35class plugin_manager {
  36
  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';
  41
  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';
  56
  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;
  63
  64    /**
  65     * Direct initiation not allowed, use the factory method {@link self::instance()}
  66     *
  67     * @todo we might want to specify just a single plugin type to work with
  68     */
  69    protected function __construct() {
  70        $this->get_plugins(true);
  71    }
  72
  73    /**
  74     * Sorry, this is singleton
  75     */
  76    protected function __clone() {
  77    }
  78
  79    /**
  80     * Factory method for this class
  81     *
  82     * @return plugin_manager the singleton instance
  83     */
  84    public static function instance() {
  85        global $CFG;
  86
  87        if (is_null(self::$singletoninstance)) {
  88            self::$singletoninstance = new self();
  89        }
  90        return self::$singletoninstance;
  91    }
  92
  93    /**
  94     * Returns a tree of known plugins and information about them
  95     *
  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.
 100     */
 101    public function get_plugins($disablecache=false) {
 102
 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');
 109                }
 110                if (class_exists('plugintype_' . $plugintype)) {
 111                    $plugintypeclass = 'plugintype_' . $plugintype;
 112                } else {
 113                    $plugintypeclass = 'plugintype_general';
 114                }
 115                if (!in_array('plugin_information', class_implements($plugintypeclass))) {
 116                    throw new coding_exception('Class ' . $plugintypeclass . ' must implement plugin_information');
 117                }
 118                $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass);
 119                $this->pluginsinfo[$plugintype] = $plugins;
 120            }
 121        }
 122
 123        return $this->pluginsinfo;
 124    }
 125
 126    /**
 127     * Returns list of plugins that define their subplugins and the information
 128     * about them from the db/subplugins.php file.
 129     *
 130     * At the moment, only activity modules can define subplugins.
 131     *
 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.
 135     */
 136    public function get_subplugins($disablecache=false) {
 137
 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;
 150                    }
 151                $this->subpluginsinfo['mod_' . $mod] = $modsubplugins;
 152                }
 153            }
 154        }
 155
 156        return $this->subpluginsinfo;
 157    }
 158
 159    /**
 160     * Returns the name of the plugin that defines the given subplugin type
 161     *
 162     * If the given subplugin type is not actually a subplugin, returns false.
 163     *
 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
 166     */
 167    public function get_parent_of_subplugin($subplugintype) {
 168
 169        $parent = false;
 170        foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
 171            if (isset($subplugintypes[$subplugintype])) {
 172                $parent = $pluginname;
 173                break;
 174            }
 175        }
 176
 177        return $parent;
 178    }
 179
 180    /**
 181     * Returns a localized name of a given plugin
 182     *
 183     * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
 184     * @return string
 185     */
 186    public function plugin_name($plugin) {
 187        list($type, $name) = normalize_component($plugin);
 188        return $this->pluginsinfo[$type][$name]->displayname;
 189    }
 190
 191    /**
 192     * Returns a localized name of a plugin type in plural form
 193     *
 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.
 197     *
 198     * @param string $type the type of the plugin, e.g. mod or workshopform
 199     * @return string
 200     */
 201    public function plugintype_name_plural($type) {
 202
 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');
 206
 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);
 211            } else {
 212                return $this->plugin_name($parent) . ' / ' . $type;
 213            }
 214
 215        } else {
 216            return $type;
 217        }
 218    }
 219
 220    /**
 221     * @param string $component frankenstyle component name.
 222     * @return plugin_information|null the corresponding plugin information.
 223     */
 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];
 229        } else {
 230            return null;
 231        }
 232    }
 233
 234    /**
 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.
 238     */
 239    public function other_plugins_that_require($component) {
 240        $others = array();
 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;
 246                }
 247            }
 248        }
 249        return $others;
 250    }
 251
 252    /**
 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.
 256     */
 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)) {
 261                return false;
 262            }
 263
 264            if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
 265                return false;
 266            }
 267        }
 268
 269        return true;
 270    }
 271
 272    /**
 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.
 276     */
 277    public function all_plugins_ok($moodleversion) {
 278        foreach ($this->get_plugins() as $type => $plugins) {
 279            foreach ($plugins as $plugin) {
 280
 281                if (!empty($plugin->versionrequires) && $plugin->versionrequires > $moodleversion) {
 282                    return false;
 283                }
 284
 285                if (!$this->are_dependencies_satisfied($plugin->get_other_required_plugins())) {
 286                    return false;
 287                }
 288            }
 289        }
 290
 291        return true;
 292    }
 293
 294    /**
 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.
 297     *
 298     * The main purpose of this list is to hide missing plugins during upgrade.
 299     *
 300     * @param string $type plugin type
 301     * @param string $name plugin name
 302     * @return bool
 303     */
 304    public static function is_deleted_standard_plugin($type, $name) {
 305        static $plugins = array(
 306            // do not add 1.9-2.2 plugin removals here
 307        );
 308
 309        if (!isset($plugins[$type])) {
 310            return false;
 311        }
 312        return in_array($name, $plugins[$type]);
 313    }
 314
 315    /**
 316     * Defines a white list of all plugins shipped in the standard Moodle distribution
 317     *
 318     * @param string $type
 319     * @return false|array array of standard plugins or false if the type is unknown
 320     */
 321    public static function standard_plugins_list($type) {
 322        static $standard_plugins = array(
 323
 324            'assignment' => array(
 325                'offline', 'online', 'upload', 'uploadsingle'
 326            ),
 327
 328            'auth' => array(
 329                'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
 330                'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
 331                'shibboleth', 'webservice'
 332            ),
 333
 334            'block' => array(
 335                'activity_modules', 'admin_bookmarks', 'blog_menu',
 336                'blog_recent', 'blog_tags', 'calendar_month',
 337                'calendar_upcoming', 'comments', 'community',
 338                'completionstatus', 'course_list', 'course_overview',
 339                'course_summary', 'feedback', 'glossary_random', 'html',
 340                'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile',
 341                'navigation', 'news_items', 'online_users', 'participants',
 342                'private_files', 'quiz_results', 'recent_activity',
 343                'rss_client', 'search_forums', 'section_links',
 344                'selfcompletion', 'settings', 'site_main_menu',
 345                'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
 346            ),
 347
 348            'coursereport' => array(
 349                //deprecated!
 350            ),
 351
 352            'datafield' => array(
 353                'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
 354                'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
 355            ),
 356
 357            'datapreset' => array(
 358                'imagegallery'
 359            ),
 360
 361            'editor' => array(
 362                'textarea', 'tinymce'
 363            ),
 364
 365            'enrol' => array(
 366                'authorize', 'category', 'cohort', 'database', 'flatfile',
 367                'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
 368                'paypal', 'self'
 369            ),
 370
 371            'filter' => array(
 372                'activitynames', 'algebra', 'censor', 'emailprotect',
 373                'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
 374                'urltolink', 'data', 'glossary'
 375            ),
 376
 377            'format' => array(
 378                'scorm', 'social', 'topics', 'weeks'
 379            ),
 380
 381            'gradeexport' => array(
 382                'ods', 'txt', 'xls', 'xml'
 383            ),
 384
 385            'gradeimport' => array(
 386                'csv', 'xml'
 387            ),
 388
 389            'gradereport' => array(
 390                'grader', 'outcomes', 'overview', 'user'
 391            ),
 392
 393            'gradingform' => array(
 394                'rubric'
 395            ),
 396
 397            'local' => array(
 398            ),
 399
 400            'message' => array(
 401                'email', 'jabber', 'popup'
 402            ),
 403
 404            'mnetservice' => array(
 405                'enrol'
 406            ),
 407
 408            'mod' => array(
 409                'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
 410                'forum', 'glossary', 'imscp', 'label', 'lesson', 'lti', 'page',
 411                'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
 412            ),
 413
 414            'plagiarism' => array(
 415            ),
 416
 417            'portfolio' => array(
 418                'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
 419            ),
 420
 421            'profilefield' => array(
 422                'checkbox', 'datetime', 'menu', 'text', 'textarea'
 423            ),
 424
 425            'qbehaviour' => array(
 426                'adaptive', 'adaptivenopenalty', 'deferredcbm',
 427                'deferredfeedback', 'immediatecbm', 'immediatefeedback',
 428                'informationitem', 'interactive', 'interactivecountback',
 429                'manualgraded', 'missing'
 430            ),
 431
 432            'qformat' => array(
 433                'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
 434                'learnwise', 'missingword', 'multianswer', 'webct',
 435                'xhtml', 'xml'
 436            ),
 437
 438            'qtype' => array(
 439                'calculated', 'calculatedmulti', 'calculatedsimple',
 440                'description', 'essay', 'match', 'missingtype', 'multianswer',
 441                'multichoice', 'numerical', 'random', 'randomsamatch',
 442                'shortanswer', 'truefalse'
 443            ),
 444
 445            'quiz' => array(
 446                'grading', 'overview', 'responses', 'statistics'
 447            ),
 448
 449            'quizaccess' => array(
 450                'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate',
 451                'password', 'safebrowser', 'securewindow', 'timelimit'
 452            ),
 453
 454            'report' => array(
 455                'backups', 'completion', 'configlog', 'courseoverview',
 456                'log', 'loglive', 'outline', 'participation', 'progress', 'questioninstances', 'security', 'stats'
 457            ),
 458
 459            'repository' => array(
 460                'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
 461                'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
 462                'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
 463                'wikimedia', 'youtube'
 464            ),
 465
 466            'scormreport' => array(
 467                'basic',
 468                'interactions'
 469            ),
 470
 471            'theme' => array(
 472                'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
 473                'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
 474                'fusion', 'leatherbound', 'magazine', 'mymobile', 'nimble',
 475                'nonzero', 'overlay', 'serenity', 'sky_high', 'splash',
 476                'standard', 'standardold'
 477            ),
 478
 479            'tool' => array(
 480                'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
 481                'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
 482                'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
 483                'uploaduser', 'unsuproles', 'xmldb'
 484            ),
 485
 486            'webservice' => array(
 487                'amf', 'rest', 'soap', 'xmlrpc'
 488            ),
 489
 490            'workshopallocation' => array(
 491                'manual', 'random'
 492            ),
 493
 494            'workshopeval' => array(
 495                'best'
 496            ),
 497
 498            'workshopform' => array(
 499                'accumulative', 'comments', 'numerrors', 'rubric'
 500            )
 501        );
 502
 503        if (isset($standard_plugins[$type])) {
 504            return $standard_plugins[$type];
 505
 506        } else {
 507            return false;
 508        }
 509    }
 510}
 511
 512/**
 513 * Interface for making information about a plugin available.
 514 *
 515 * Note that most of the useful information is made available in pubic fields,
 516 * which cannot be documented in this interface. See the field definitions on
 517 * {@link plugintype_base} to find out what information is available.
 518 *
 519 * @property-read string component the component name, type_name
 520 */
 521interface plugin_information {
 522
 523    /**
 524     * Gathers and returns the information about all plugins of the given type
 525     *
 526     * Passing the parameter $typeclass allows us to reach the same effect as with the
 527     * late binding in PHP 5.3. Once PHP 5.3 is required, we can refactor this to use
 528     * {@example $plugin = new static();} instead of {@example $plugin = new $typeclass()}
 529     *
 530     * @param string $type the name of the plugintype, eg. mod, auth or workshopform
 531     * @param string $typerootdir full path to the location of the plugin dir
 532     * @param string $typeclass the name of the actually called class
 533     * @return array of plugintype classes, indexed by the plugin name
 534     */
 535    public static function get_plugins($type, $typerootdir, $typeclass);
 536
 537    /**
 538     * Sets $displayname property to a localized name of the plugin
 539     *
 540     * @return void
 541     */
 542    public function init_display_name();
 543
 544    /**
 545     * Sets $versiondisk property to a numerical value representing the
 546     * version of the plugin's source code.
 547     *
 548     * If the value is null after calling this method, either the plugin
 549     * does not use versioning (typically does not have any database
 550     * data) or is missing from disk.
 551     *
 552     * @return void
 553     */
 554    public function load_disk_version();
 555
 556    /**
 557     * Sets $versiondb property to a numerical value representing the
 558     * currently installed version of the plugin.
 559     *
 560     * If the value is null after calling this method, either the plugin
 561     * does not use versioning (typically does not have any database
 562     * data) or has not been installed yet.
 563     *
 564     * @return void
 565     */
 566    public function load_db_version();
 567
 568    /**
 569     * Sets $versionrequires property to a numerical value representing
 570     * the version of Moodle core that this plugin requires.
 571     *
 572     * @return void
 573     */
 574    public function load_required_main_version();
 575
 576    /**
 577     * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
 578     * constants.
 579     *
 580     * If the property's value is null after calling this method, then
 581     * the type of the plugin has not been recognized and you should throw
 582     * an exception.
 583     *
 584     * @return void
 585     */
 586    public function init_is_standard();
 587
 588    /**
 589     * Returns true if the plugin is shipped with the official distribution
 590     * of the current Moodle version, false otherwise.
 591     *
 592     * @return bool
 593     */
 594    public function is_standard();
 595
 596    /**
 597     * Returns the status of the plugin
 598     *
 599     * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
 600     */
 601    public function get_status();
 602
 603    /**
 604     * Get the list of other plugins that this plugin requires ot be installed.
 605     * @return array with keys the frankenstyle plugin name, and values either
 606     *      a version string (like '2011101700') or the constant ANY_VERSION.
 607     */
 608    public function get_other_required_plugins();
 609
 610    /**
 611     * Returns the information about plugin availability
 612     *
 613     * True means that the plugin is enabled. False means that the plugin is
 614     * disabled. Null means that the information is not available, or the
 615     * plugin does not support configurable availability or the availability
 616     * can not be changed.
 617     *
 618     * @return null|bool
 619     */
 620    public function is_enabled();
 621
 622    /**
 623     * Returns the URL of the plugin settings screen
 624     *
 625     * Null value means that the plugin either does not have the settings screen
 626     * or its location is not available via this library.
 627     *
 628     * @return null|moodle_url
 629     */
 630    public function get_settings_url();
 631
 632    /**
 633     * Returns the URL of the screen where this plugin can be uninstalled
 634     *
 635     * Visiting that URL must be safe, that is a manual confirmation is needed
 636     * for actual uninstallation of the plugin. Null value means that the
 637     * plugin either does not support uninstallation, or does not require any
 638     * database cleanup or the location of the screen is not available via this
 639     * library.
 640     *
 641     * @return null|moodle_url
 642     */
 643    public function get_uninstall_url();
 644
 645    /**
 646     * Returns relative directory of the plugin with heading '/'
 647     *
 648     * @example /mod/workshop
 649     * @return string
 650     */
 651    public function get_dir();
 652
 653    /**
 654     * Return the full path name of a file within the plugin.
 655     * No check is made to see if the file exists.
 656     * @param string $relativepath e.g. 'version.php'.
 657     * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
 658     */
 659    public function full_path($relativepath);
 660}
 661
 662/**
 663 * Defines public properties that all plugintype classes must have
 664 * and provides default implementation of required methods.
 665 *
 666 * @property-read string component the component name, type_name
 667 */
 668abstract class plugintype_base {
 669
 670    /** @var string the plugintype name, eg. mod, auth or workshopform */
 671    public $type;
 672    /** @var string full path to the location of all the plugins of this type */
 673    public $typerootdir;
 674    /** @var string the plugin name, eg. assignment, ldap */
 675    public $name;
 676    /** @var string the localized plugin name */
 677    public $displayname;
 678    /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
 679    public $source;
 680    /** @var fullpath to the location of this plugin */
 681    public $rootdir;
 682    /** @var int|string the version of the plugin's source code */
 683    public $versiondisk;
 684    /** @var int|string the version of the installed plugin */
 685    public $versiondb;
 686    /** @var int|float|string required version of Moodle core  */
 687    public $versionrequires;
 688    /** @var array other plugins that this one depends on.
 689     *  Lazy-loaded by {@link get_other_required_plugins()} */
 690    public $dependencies = null;
 691    /** @var int number of instances of the plugin - not supported yet */
 692    public $instances;
 693    /** @var int order of the plugin among other plugins of the same type - not supported yet */
 694    public $sortorder;
 695
 696    /**
 697     * @see plugin_information::get_plugins()
 698     */
 699    public static function get_plugins($type, $typerootdir, $typeclass) {
 700
 701        // get the information about plugins at the disk
 702        $plugins = get_plugin_list($type);
 703        $ondisk = array();
 704        foreach ($plugins as $pluginname => $pluginrootdir) {
 705            $plugin                 = new $typeclass();
 706            $plugin->type           = $type;
 707            $plugin->typerootdir    = $typerootdir;
 708            $plugin->name           = $pluginname;
 709            $plugin->rootdir        = $pluginrootdir;
 710
 711            $plugin->init_display_name();
 712            $plugin->load_disk_version();
 713            $plugin->load_db_version();
 714            $plugin->load_required_main_version();
 715            $plugin->init_is_standard();
 716
 717            $ondisk[$pluginname] = $plugin;
 718        }
 719        return $ondisk;
 720    }
 721
 722    /**
 723     * @see plugin_information::init_display_name()
 724     */
 725    public function init_display_name() {
 726        if (!get_string_manager()->string_exists('pluginname', $this->component)) {
 727            $this->displayname = '[pluginname,' . $this->component . ']';
 728        } else {
 729            $this->displayname = get_string('pluginname', $this->component);
 730        }
 731    }
 732
 733    /**
 734     * Magic method getter, redirects to read only values.
 735     * @param string $name
 736     * @return mixed
 737     */
 738    public function __get($name) {
 739        switch ($name) {
 740            case 'component': return $this->type . '_' . $this->name;
 741
 742            default:
 743                debugging('Invalid plugin property accessed! '.$name);
 744                return null;
 745        }
 746    }
 747
 748    /**
 749     * @see plugin_information::full_path()
 750     */
 751    public function full_path($relativepath) {
 752        if (empty($this->rootdir)) {
 753            return '';
 754        }
 755        return $this->rootdir . '/' . $relativepath;
 756    }
 757
 758    /**
 759     * Load the data from version.php.
 760     * @return object the data object defined in version.php.
 761     */
 762    protected function load_version_php() {
 763        $versionfile = $this->full_path('version.php');
 764
 765        $plugin = new stdClass();
 766        if (is_readable($versionfile)) {
 767            include($versionfile);
 768        }
 769        return $plugin;
 770    }
 771
 772    /**
 773     * @see plugin_information::load_disk_version()
 774     */
 775    public function load_disk_version() {
 776        $plugin = $this->load_version_php();
 777        if (isset($plugin->version)) {
 778            $this->versiondisk = $plugin->version;
 779        }
 780    }
 781
 782    /**
 783     * @see plugin_information::load_required_main_version()
 784     */
 785    public function load_required_main_version() {
 786        $plugin = $this->load_version_php();
 787        if (isset($plugin->requires)) {
 788            $this->versionrequires = $plugin->requires;
 789        }
 790    }
 791
 792    /**
 793     * Initialise {@link $dependencies} to the list of other plugins (in any)
 794     * that this one requires to be installed.
 795     */
 796    protected function load_other_required_plugins() {
 797        $plugin = $this->load_version_php();
 798        if (!empty($plugin->dependencies)) {
 799            $this->dependencies = $plugin->dependencies;
 800        } else {
 801            $this->dependencies = array(); // By default, no dependencies.
 802        }
 803    }
 804
 805    /**
 806     * @see plugin_information::get_other_required_plugins()
 807     */
 808    public function get_other_required_plugins() {
 809        if (is_null($this->dependencies)) {
 810            $this->load_other_required_plugins();
 811        }
 812        return $this->dependencies;
 813    }
 814
 815    /**
 816     * @see plugin_information::load_db_version()
 817     */
 818    public function load_db_version() {
 819
 820        if ($ver = self::get_version_from_config_plugins($this->component)) {
 821            $this->versiondb = $ver;
 822        }
 823    }
 824
 825    /**
 826     * @see plugin_information::init_is_standard()
 827     */
 828    public function init_is_standard() {
 829
 830        $standard = plugin_manager::standard_plugins_list($this->type);
 831
 832        if ($standard !== false) {
 833            $standard = array_flip($standard);
 834            if (isset($standard[$this->name])) {
 835                $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD;
 836            } else if (!is_null($this->versiondb) and is_null($this->versiondisk)
 837                    and plugin_manager::is_deleted_standard_plugin($this->type, $this->name)) {
 838                $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD; // to be deleted
 839            } else {
 840                $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
 841            }
 842        }
 843    }
 844
 845    /**
 846     * @see plugin_information::is_standard()
 847     */
 848    public function is_standard() {
 849        return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
 850    }
 851
 852    /**
 853     * @see plugin_information::get_status()
 854     */
 855    public function get_status() {
 856
 857        if (is_null($this->versiondb) and is_null($this->versiondisk)) {
 858            return plugin_manager::PLUGIN_STATUS_NODB;
 859
 860        } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
 861            return plugin_manager::PLUGIN_STATUS_NEW;
 862
 863        } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
 864            if (plugin_manager::is_deleted_standard_plugin($this->type, $this->name)) {
 865                return plugin_manager::PLUGIN_STATUS_DELETE;
 866            } else {
 867                return plugin_manager::PLUGIN_STATUS_MISSING;
 868            }
 869
 870        } else if ((string)$this->versiondb === (string)$this->versiondisk) {
 871            return plugin_manager::PLUGIN_STATUS_UPTODATE;
 872
 873        } else if ($this->versiondb < $this->versiondisk) {
 874            return plugin_manager::PLUGIN_STATUS_UPGRADE;
 875
 876        } else if ($this->versiondb > $this->versiondisk) {
 877            return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
 878
 879        } else {
 880            // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
 881            throw new coding_exception('Unable to determine plugin state, check the plugin versions');
 882        }
 883    }
 884
 885    /**
 886     * @see plugin_information::is_enabled()
 887     */
 888    public function is_enabled() {
 889        return null;
 890    }
 891
 892    /**
 893     * @see plugin_information::get_settings_url()
 894     */
 895    public function get_settings_url() {
 896        return null;
 897    }
 898
 899    /**
 900     * @see plugin_information::get_uninstall_url()
 901     */
 902    public function get_uninstall_url() {
 903        return null;
 904    }
 905
 906    /**
 907     * @see plugin_information::get_dir()
 908     */
 909    public function get_dir() {
 910        global $CFG;
 911
 912        return substr($this->rootdir, strlen($CFG->dirroot));
 913    }
 914
 915    /**
 916     * Provides access to plugin versions from {config_plugins}
 917     *
 918     * @param string $plugin plugin name
 919     * @param double $disablecache optional, defaults to false
 920     * @return int|false the stored value or false if not found
 921     */
 922    protected function get_version_from_config_plugins($plugin, $disablecache=false) {
 923        global $DB;
 924        static $pluginversions = null;
 925
 926        if (is_null($pluginversions) or $disablecache) {
 927            try {
 928                $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
 929            } catch (dml_exception $e) {
 930                // before install
 931                $pluginversions = array();
 932            }
 933        }
 934
 935        if (!array_key_exists($plugin, $pluginversions)) {
 936            return false;
 937        }
 938
 939        return $pluginversions[$plugin];
 940    }
 941}
 942
 943/**
 944 * General class for all plugin types that do not have their own class
 945 */
 946class plugintype_general extends plugintype_base implements plugin_information {
 947
 948}
 949
 950/**
 951 * Class for page side blocks
 952 */
 953class plugintype_block extends plugintype_base implements plugin_information {
 954
 955    /**
 956     * @see plugin_information::get_plugins()
 957     */
 958    public static function get_plugins($type, $typerootdir, $typeclass) {
 959
 960        // get the information about blocks at the disk
 961        $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
 962
 963        // add blocks missing from disk
 964        $blocksinfo = self::get_blocks_info();
 965        foreach ($blocksinfo as $blockname => $blockinfo) {
 966            if (isset($blocks[$blockname])) {
 967                continue;
 968            }
 969            $plugin                 = new $typeclass();
 970            $plugin->type           = $type;
 971            $plugin->typerootdir    = $typerootdir;
 972            $plugin->name           = $blockname;
 973            $plugin->rootdir        = null;
 974            $plugin->displayname    = $blockname;
 975            $plugin->versiondb      = $blockinfo->version;
 976            $plugin->init_is_standard();
 977
 978            $blocks[$blockname]   = $plugin;
 979        }
 980
 981        return $blocks;
 982    }
 983
 984    /**
 985     * @see plugin_information::init_display_name()
 986     */
 987    public function init_display_name() {
 988
 989        if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
 990            $this->displayname = get_string('pluginname', 'block_' . $this->name);
 991
 992        } else if (($block = block_instance($this->name)) !== false) {
 993            $this->displayname = $block->get_title();
 994
 995        } else {
 996            parent::init_display_name();
 997        }
 998    }
 999
1000    /**
1001     * @see plugin_information::load_db_version()
1002     */
1003    public function load_db_version() {
1004        global $DB;
1005
1006        $blocksinfo = self::get_blocks_info();
1007        if (isset($blocksinfo[$this->name]->version)) {
1008            $this->versiondb = $blocksinfo[$this->name]->version;
1009        }
1010    }
1011
1012    /**
1013     * @see plugin_information::is_enabled()
1014     */
1015    public function is_enabled() {
1016
1017        $blocksinfo = self::get_blocks_info();
1018        if (isset($blocksinfo[$this->name]->visible)) {
1019            if ($blocksinfo[$this->name]->visible) {
1020                return true;
1021            } else {
1022                return false;
1023            }
1024        } else {
1025            return parent::is_enabled();
1026        }
1027    }
1028
1029    /**
1030     * @see plugin_information::get_settings_url()
1031     */
1032    public function get_settings_url() {
1033
1034        if (($block = block_instance($this->name)) === false) {
1035            return parent::get_settings_url();
1036
1037        } else if ($block->has_config()) {
1038            if (file_exists($this->full_path('settings.php'))) {
1039                return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
1040            } else {
1041                $blocksinfo = self::get_blocks_info();
1042                return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
1043            }
1044
1045        } else {
1046            return parent::get_settings_url();
1047        }
1048    }
1049
1050    /**
1051     * @see plugin_information::get_uninstall_url()
1052     */
1053    public function get_uninstall_url() {
1054
1055        $blocksinfo = self::get_blocks_info();
1056        return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
1057    }
1058
1059    /**
1060     * Provides access to the records in {block} table
1061     *
1062     * @param bool $disablecache do not use internal static cache
1063     * @return array array of stdClasses
1064     */
1065    protected static function get_blocks_info($disablecache=false) {
1066        global $DB;
1067        static $blocksinfocache = null;
1068
1069        if (is_null($blocksinfocache) or $disablecache) {
1070            try {
1071                $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
1072            } catch (dml_exception $e) {
1073                // before install
1074                $blocksinfocache = array();
1075            }
1076        }
1077
1078        return $blocksinfocache;
1079    }
1080}
1081
1082/**
1083 * Class for text filters
1084 */
1085class plugintype_filter extends plugintype_base implements plugin_information {
1086
1087    /**
1088     * @see plugin_information::get_plugins()
1089     */
1090    public static function get_plugins($type, $typerootdir, $typeclass) {
1091        global $CFG, $DB;
1092
1093        $filters = array();
1094
1095        // get the list of filters from both /filter and /mod location
1096        $installed = filter_get_all_installed();
1097
1098        foreach ($installed as $filterlegacyname => $displayname) {
1099            $plugin                 = new $typeclass();
1100            $plugin->type           = $type;
1101            $plugin->typerootdir    = $typerootdir;
1102            $plugin->name           = self::normalize_legacy_name($filterlegacyname);
1103            $plugin->rootdir        = $CFG->dirroot . '/' . $filterlegacyname;
1104            $plugin->displayname    = $displayname;
1105
1106            $plugin->load_disk_version();
1107            $plugin->load_db_version();
1108            $plugin->load_required_main_version();
1109            $plugin->init_is_standard();
1110
1111            $filters[$plugin->name] = $plugin;
1112        }
1113
1114        $globalstates = self::get_global_states();
1115
1116        if ($DB->get_manager()->table_exists('filter_active')) {
1117            // if we're upgrading from 1.9, the table does not exist yet
1118            // if it does, make sure that all installed filters are registered
1119            $needsreload  = false;
1120            foreach (array_keys($installed) as $filterlegacyname) {
1121                if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
1122                    filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
1123                    $needsreload = true;
1124                }
1125            }
1126            if ($needsreload) {
1127                $globalstates = self::get_global_states(true);
1128            }
1129        }
1130
1131        // make sure that all registered filters are installed, just in case
1132        foreach ($globalstates as $name => $info) {
1133            if (!isset($filters[$name])) {
1134                // oops, there is a record in filter_active but the filter is not installed
1135                $plugin                 = new $typeclass();
1136                $plugin->type           = $type;
1137                $plugin->typerootdir    = $typerootdir;
1138                $plugin->name           = $name;
1139                $plugin->rootdir        = $CFG->dirroot . '/' . $info->legacyname;
1140                $plugin->displayname    = $info->legacyname;
1141
1142                $plugin->load_db_version();
1143
1144                if (is_null($plugin->versiondb)) {
1145                    // this is a hack to stimulate 'Missing from disk' error
1146                    // because $plugin->versiondisk will be null !== false
1147                    $plugin->versiondb = false;
1148                }
1149
1150                $filters[$plugin->name] = $plugin;
1151            }
1152        }
1153
1154        return $filters;
1155    }
1156
1157    /**
1158     * @see plugin_information::init_display_name()
1159     */
1160    public function init_display_name() {
1161        // do nothing, the name is set in self::get_plugins()
1162    }
1163
1164    /**
1165     * @see plugintype_base::load_version_php().
1166     */
1167    protected function load_version_php() {
1168        if (strpos($this->name, 'mod_') === 0) {
1169            // filters bundled with modules do not have a version.php and so
1170            // do not provide their own versioning information.
1171            return new stdClass();
1172        }
1173        return parent::load_version_php();
1174    }
1175
1176    /**
1177     * @see plugin_information::is_enabled()
1178     */
1179    public function is_enabled() {
1180
1181        $globalstates = self::get_global_states();
1182
1183        foreach ($globalstates as $filterlegacyname => $info) {
1184            $name = self::normalize_legacy_name($filterlegacyname);
1185            if ($name === $this->name) {
1186                if ($info->active == TEXTFILTER_DISABLED) {
1187                    return false;
1188                } else {
1189                    // it may be 'On' or 'Off, but available'
1190                    return null;
1191                }
1192            }
1193        }
1194
1195        return null;
1196    }
1197
1198    /**
1199     * @see plugin_information::get_settings_url()
1200     */
1201    public function get_settings_url() {
1202
1203        $globalstates = self::get_global_states();
1204        $legacyname = $globalstates[$this->name]->legacyname;
1205        if (filter_has_global_settings($legacyname)) {
1206            return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1207        } else {
1208            return null;
1209        }
1210    }
1211
1212    /**
1213     * @see plugin_information::get_uninstall_url()
1214     */
1215    public function get_uninstall_url() {
1216
1217        if (strpos($this->name, 'mod_') === 0) {
1218            return null;
1219        } else {
1220            $globalstates = self::get_global_states();
1221            $legacyname = $globalstates[$this->name]->legacyname;
1222            return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1223        }
1224    }
1225
1226    /**
1227     * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1228     *
1229     * @param string $legacyfiltername legacy filter name
1230     * @return string frankenstyle-like name
1231     */
1232    protected static function normalize_legacy_name($legacyfiltername) {
1233
1234        $name = str_replace('/', '_', $legacyfiltername);
1235        if (strpos($name, 'filter_') === 0) {
1236            $name = substr($name, 7);
1237            if (empty($name)) {
1238                throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1239            }
1240        }
1241
1242        return $name;
1243    }
1244
1245    /**
1246     * Provides access to the results of {@link filter_get_global_states()}
1247     * but indexed by the normalized filter name
1248     *
1249     * The legacy filter name is available as ->legacyname property.
1250     *
1251     * @param bool $disablecache
1252     * @return array
1253     */
1254    protected static function get_global_states($disablecache=false) {
1255        global $DB;
1256        static $globalstatescache = null;
1257
1258        if ($disablecache or is_null($globalstatescache)) {
1259
1260            if (!$DB->get_manager()->table_exists('filter_active')) {
1261                // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1262                // does not exist yet
1263                $globalstatescache = array();
1264
1265            } else {
1266                foreach (filter_get_global_states() as $legacyname => $info) {
1267                    $name                       = self::normalize_legacy_name($legacyname);
1268                    $filterinfo                 = new stdClass();
1269                    $filterinfo->legacyname     = $legacyname;
1270                    $filterinfo->active         = $info->active;
1271                    $filterinfo->sortorder      = $info->sortorder;
1272                    $globalstatescache[$name]   = $filterinfo;
1273                }
1274            }
1275        }
1276
1277        return $globalstatescache;
1278    }
1279}
1280
1281/**
1282 * Class for activity modules
1283 */
1284class plugintype_mod extends plugintype_base implements plugin_information {
1285
1286    /**
1287     * @see plugin_information::get_plugins()
1288     */
1289    public static function get_plugins($type, $typerootdir, $typeclass) {
1290
1291        // get the information about plugins at the disk
1292        $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1293
1294        // add modules missing from disk
1295        $modulesinfo = self::get_modules_info();
1296        foreach ($modulesinfo as $modulename => $moduleinfo) {
1297            if (isset($modules[$modulename])) {
1298                continue;
1299            }
1300            $plugin                 = new $typeclass();
1301            $plugin->type           = $type;
1302            $plugin->typerootdir    = $typerootdir;
1303            $plugin->name           = $modulename;
1304            $plugin->rootdir        = null;
1305            $plugin->displayname    = $modulename;
1306            $plugin->versiondb      = $moduleinfo->version;
1307            $plugin->init_is_standard();
1308
1309            $modules[$modulename]   = $plugin;
1310        }
1311
1312        return $modules;
1313    }
1314
1315    /**
1316     * @see plugin_information::init_display_name()
1317     */
1318    public function init_display_name() {
1319        if (get_string_manager()->string_exists('pluginname', $this->component)) {
1320            $this->displayname = get_string('pluginname', $this->component);
1321        } else {
1322            $this->displayname = get_string('modulename', $this->component);
1323        }
1324    }
1325
1326    /**
1327     * Load the data from version.php.
1328     * @return object the data object defined in version.php.
1329     */
1330    protected function load_version_php() {
1331        $versionfile = $this->full_path('version.php');
1332
1333        $module = new stdClass();
1334        if (is_readable($versionfile)) {
1335            include($versionfile);
1336        }
1337        return $module;
1338    }
1339
1340    /**
1341     * @see plugin_information::load_db_version()
1342     */
1343    public function load_db_version() {
1344        global $DB;
1345
1346        $modulesinfo = self::get_modules_info();
1347        if (isset($modulesinfo[$this->name]->version)) {
1348            $this->versiondb = $modulesinfo[$this->name]->version;
1349        }
1350    }
1351
1352    /**
1353     * @see plugin_information::is_enabled()
1354     */
1355    public function is_enabled() {
1356
1357        $modulesinfo = self::get_modules_info();
1358        if (isset($modulesinfo[$this->name]->visible)) {
1359            if ($modulesinfo[$this->name]->visible) {
1360                return true;
1361            } else {
1362                return false;
1363            }
1364        } else {
1365            return parent::is_enabled();
1366        }
1367    }
1368
1369    /**
1370     * @see plugin_information::get_settings_url()
1371     */
1372    public function get_settings_url() {
1373
1374        if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
1375            return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
1376        } else {
1377            return parent::get_settings_url();
1378        }
1379    }
1380
1381    /**
1382     * @see plugin_information::get_uninstall_url()
1383     */
1384    public function get_uninstall_url() {
1385
1386        if ($this->name !== 'forum') {
1387            return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1388        } else {
1389            return null;
1390        }
1391    }
1392
1393    /**
1394     * Provides access to the records in {modules} table
1395     *
1396     * @param bool $disablecache do not use internal static cache
1397     * @return array array of stdClasses
1398     */
1399    protected static function get_modules_info($disablecache=false) {
1400        global $DB;
1401        static $modulesinfocache = null;
1402
1403        if (is_null($modulesinfocache) or $disablecache) {
1404            try {
1405                $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1406            } catch (dml_exception $e) {
1407                // before install
1408                $modulesinfocache = array();
1409            }
1410        }
1411
1412        return $modulesinfocache;
1413    }
1414}
1415
1416
1417/**
1418 * Class for question behaviours.
1419 */
1420class plugintype_qbehaviour extends plugintype_base implements plugin_information {
1421    /**
1422     * @see plugin_information::get_uninstall_url()
1423     */
1424    public function get_uninstall_url() {
1425        return new moodle_url('/admin/qbehaviours.php',
1426                array('delete' => $this->name, 'sesskey' => sesskey()));
1427    }
1428}
1429
1430
1431/**
1432 * Class for question types
1433 */
1434class plugintype_qtype extends plugintype_base implements plugin_information {
1435    /**
1436    * @see plugin_information::get_uninstall_url()
1437    */
1438    public function get_uninstall_url() {
1439        return new moodle_url('/admin/qtypes.php',
1440                array('delete' => $this->name, 'sesskey' => sesskey()));
1441    }
1442}
1443
1444
1445/**
1446 * Class for authentication plugins
1447 */
1448class plugintype_auth extends plugintype_base implements plugin_information {
1449
1450    /**
1451     * @see plugin_information::is_enabled()
1452     */
1453    public function is_enabled() {
1454        global $CFG;
1455        /** @var null|array list of enabled authentication plugins */
1456        static $enabled = null;
1457
1458        if (in_array($this->name, array('nologin', 'manual'))) {
1459            // these two are always enabled and can't be disabled
1460            return null;
1461        }
1462
1463        if (is_null($enabled)) {
1464            $enabled = explode(',', $CFG->auth);
1465        }
1466
1467        return isset($enabled[$this->name]);
1468    }
1469
1470    /**
1471     * @see plugin_information::get_settings_url()
1472     */
1473    public function get_settings_url() {
1474        if (file_exists($this->full_path('settings.php'))) {
1475            return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
1476        } else {
1477            return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1478        }
1479    }
1480}
1481
1482/**
1483 * Class for enrolment plugins
1484 */
1485class plugintype_enrol extends plugintype_base implements plugin_information {
1486
1487    /**
1488     * We do not actually need whole enrolment classes here so we do not call
1489     * {@link enrol_get_plugins()}. Note that this may produce slightly different
1490     * results, for example if the enrolment plugin does not contain lib.php
1491     * but it is listed in $CFG->enrol_plugins_enabled
1492     *
1493     * @see plugin_information::is_enabled()
1494     */
1495    public function is_enabled() {
1496        global $CFG;
1497        /** @var null|array list of enabled enrolment plugins */
1498        static $enabled = null;
1499
1500      

Large files files are truncated, but you can click here to view the full file