PageRenderTime 4ms CodeModel.GetById 17ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/moodle/lib/pluginlib.php

#
PHP | 1691 lines | 895 code | 251 blank | 545 comment | 131 complexity | 4f4e3f6b13ee08cb68714e1c124e28b3 MD5 | raw 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            'block' => array('admin', 'admin_tree', 'loancalc', 'search'),
 307            'filter' => array('mod_data', 'mod_glossary'),
 308        );
 309
 310        if (!isset($plugins[$type])) {
 311            return false;
 312        }
 313        return in_array($name, $plugins[$type]);
 314    }
 315
 316    /**
 317     * Defines a white list of all plugins shipped in the standard Moodle distribution
 318     *
 319     * @param string $type
 320     * @return false|array array of standard plugins or false if the type is unknown
 321     */
 322    public static function standard_plugins_list($type) {
 323        static $standard_plugins = array(
 324
 325            'assignment' => array(
 326                'offline', 'online', 'upload', 'uploadsingle'
 327            ),
 328
 329            'auth' => array(
 330                'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
 331                'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
 332                'shibboleth', 'webservice'
 333            ),
 334
 335            'block' => array(
 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'
 347            ),
 348
 349            'coursereport' => array(
 350                //deprecated!
 351            ),
 352
 353            'datafield' => array(
 354                'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
 355                'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
 356            ),
 357
 358            'datapreset' => array(
 359                'imagegallery'
 360            ),
 361
 362            'editor' => array(
 363                'textarea', 'tinymce'
 364            ),
 365
 366            'enrol' => array(
 367                'authorize', 'category', 'cohort', 'database', 'flatfile',
 368                'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
 369                'paypal', 'self'
 370            ),
 371
 372            'filter' => array(
 373                'activitynames', 'algebra', 'censor', 'emailprotect',
 374                'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
 375                'urltolink', 'data', 'glossary'
 376            ),
 377
 378            'format' => array(
 379                'scorm', 'social', 'topics', 'weeks'
 380            ),
 381
 382            'gradeexport' => array(
 383                'ods', 'txt', 'xls', 'xml'
 384            ),
 385
 386            'gradeimport' => array(
 387                'csv', 'xml'
 388            ),
 389
 390            'gradereport' => array(
 391                'grader', 'outcomes', 'overview', 'user'
 392            ),
 393
 394            'gradingform' => array(
 395                'rubric'
 396            ),
 397
 398            'local' => array(
 399            ),
 400
 401            'message' => array(
 402                'email', 'jabber', 'popup'
 403            ),
 404
 405            'mnetservice' => array(
 406                'enrol'
 407            ),
 408
 409            'mod' => array(
 410                'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
 411                'forum', 'glossary', 'imscp', 'label', 'lesson', 'lti', 'page',
 412                'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
 413            ),
 414
 415            'plagiarism' => array(
 416            ),
 417
 418            'portfolio' => array(
 419                'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
 420            ),
 421
 422            'profilefield' => array(
 423                'checkbox', 'datetime', 'menu', 'text', 'textarea'
 424            ),
 425
 426            'qbehaviour' => array(
 427                'adaptive', 'adaptivenopenalty', 'deferredcbm',
 428                'deferredfeedback', 'immediatecbm', 'immediatefeedback',
 429                'informationitem', 'interactive', 'interactivecountback',
 430                'manualgraded', 'missing'
 431            ),
 432
 433            'qformat' => array(
 434                'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
 435                'learnwise', 'missingword', 'multianswer', 'webct',
 436                'xhtml', 'xml'
 437            ),
 438
 439            'qtype' => array(
 440                'calculated', 'calculatedmulti', 'calculatedsimple',
 441                'description', 'essay', 'match', 'missingtype', 'multianswer',
 442                'multichoice', 'numerical', 'random', 'randomsamatch',
 443                'shortanswer', 'truefalse'
 444            ),
 445
 446            'quiz' => array(
 447                'grading', 'overview', 'responses', 'statistics'
 448            ),
 449
 450            'quizaccess' => array(
 451                'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate',
 452                'password', 'safebrowser', 'securewindow', 'timelimit'
 453            ),
 454
 455            'report' => array(
 456                'backups', 'completion', 'configlog', 'courseoverview',
 457                'log', 'loglive', 'outline', 'participation', 'progress', 'questioninstances', 'security', 'stats'
 458            ),
 459
 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'
 465            ),
 466
 467            'scormreport' => array(
 468                'basic',
 469                'interactions'
 470            ),
 471
 472            'theme' => 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'
 478            ),
 479
 480            'tool' => array(
 481                'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
 482                'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
 483                'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
 484                'uploaduser', 'unsuproles', 'xmldb'
 485            ),
 486
 487            'webservice' => array(
 488                'amf', 'rest', 'soap', 'xmlrpc'
 489            ),
 490
 491            'workshopallocation' => array(
 492                'manual', 'random'
 493            ),
 494
 495            'workshopeval' => array(
 496                'best'
 497            ),
 498
 499            'workshopform' => array(
 500                'accumulative', 'comments', 'numerrors', 'rubric'
 501            )
 502        );
 503
 504        if (isset($standard_plugins[$type])) {
 505            return $standard_plugins[$type];
 506
 507        } else {
 508            return false;
 509        }
 510    }
 511}
 512
 513/**
 514 * Interface for making information about a plugin available.
 515 *
 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.
 519 *
 520 * @property-read string component the component name, type_name
 521 */
 522interface plugin_information {
 523
 524    /**
 525     * Gathers and returns the information about all plugins of the given type
 526     *
 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()}
 530     *
 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
 535     */
 536    public static function get_plugins($type, $typerootdir, $typeclass);
 537
 538    /**
 539     * Sets $displayname property to a localized name of the plugin
 540     *
 541     * @return void
 542     */
 543    public function init_display_name();
 544
 545    /**
 546     * Sets $versiondisk property to a numerical value representing the
 547     * version of the plugin's source code.
 548     *
 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.
 552     *
 553     * @return void
 554     */
 555    public function load_disk_version();
 556
 557    /**
 558     * Sets $versiondb property to a numerical value representing the
 559     * currently installed version of the plugin.
 560     *
 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.
 564     *
 565     * @return void
 566     */
 567    public function load_db_version();
 568
 569    /**
 570     * Sets $versionrequires property to a numerical value representing
 571     * the version of Moodle core that this plugin requires.
 572     *
 573     * @return void
 574     */
 575    public function load_required_main_version();
 576
 577    /**
 578     * Sets $source property to one of plugin_manager::PLUGIN_SOURCE_xxx
 579     * constants.
 580     *
 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
 583     * an exception.
 584     *
 585     * @return void
 586     */
 587    public function init_is_standard();
 588
 589    /**
 590     * Returns true if the plugin is shipped with the official distribution
 591     * of the current Moodle version, false otherwise.
 592     *
 593     * @return bool
 594     */
 595    public function is_standard();
 596
 597    /**
 598     * Returns the status of the plugin
 599     *
 600     * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
 601     */
 602    public function get_status();
 603
 604    /**
 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.
 608     */
 609    public function get_other_required_plugins();
 610
 611    /**
 612     * Returns the information about plugin availability
 613     *
 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.
 618     *
 619     * @return null|bool
 620     */
 621    public function is_enabled();
 622
 623    /**
 624     * Returns the URL of the plugin settings screen
 625     *
 626     * Null value means that the plugin either does not have the settings screen
 627     * or its location is not available via this library.
 628     *
 629     * @return null|moodle_url
 630     */
 631    public function get_settings_url();
 632
 633    /**
 634     * Returns the URL of the screen where this plugin can be uninstalled
 635     *
 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
 640     * library.
 641     *
 642     * @return null|moodle_url
 643     */
 644    public function get_uninstall_url();
 645
 646    /**
 647     * Returns relative directory of the plugin with heading '/'
 648     *
 649     * @example /mod/workshop
 650     * @return string
 651     */
 652    public function get_dir();
 653
 654    /**
 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'.
 659     */
 660    public function full_path($relativepath);
 661}
 662
 663/**
 664 * Defines public properties that all plugintype classes must have
 665 * and provides default implementation of required methods.
 666 *
 667 * @property-read string component the component name, type_name
 668 */
 669abstract class plugintype_base {
 670
 671    /** @var string the plugintype name, eg. mod, auth or workshopform */
 672    public $type;
 673    /** @var string full path to the location of all the plugins of this type */
 674    public $typerootdir;
 675    /** @var string the plugin name, eg. assignment, ldap */
 676    public $name;
 677    /** @var string the localized plugin name */
 678    public $displayname;
 679    /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
 680    public $source;
 681    /** @var fullpath to the location of this plugin */
 682    public $rootdir;
 683    /** @var int|string the version of the plugin's source code */
 684    public $versiondisk;
 685    /** @var int|string the version of the installed plugin */
 686    public $versiondb;
 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 */
 693    public $instances;
 694    /** @var int order of the plugin among other plugins of the same type - not supported yet */
 695    public $sortorder;
 696
 697    /**
 698     * @see plugin_information::get_plugins()
 699     */
 700    public static function get_plugins($type, $typerootdir, $typeclass) {
 701
 702        // get the information about plugins at the disk
 703        $plugins = get_plugin_list($type);
 704        $ondisk = array();
 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;
 711
 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();
 717
 718            $ondisk[$pluginname] = $plugin;
 719        }
 720        return $ondisk;
 721    }
 722
 723    /**
 724     * @see plugin_information::init_display_name()
 725     */
 726    public function init_display_name() {
 727        if (!get_string_manager()->string_exists('pluginname', $this->component)) {
 728            $this->displayname = '[pluginname,' . $this->component . ']';
 729        } else {
 730            $this->displayname = get_string('pluginname', $this->component);
 731        }
 732    }
 733
 734    /**
 735     * Magic method getter, redirects to read only values.
 736     * @param string $name
 737     * @return mixed
 738     */
 739    public function __get($name) {
 740        switch ($name) {
 741            case 'component': return $this->type . '_' . $this->name;
 742
 743            default:
 744                debugging('Invalid plugin property accessed! '.$name);
 745                return null;
 746        }
 747    }
 748
 749    /**
 750     * @see plugin_information::full_path()
 751     */
 752    public function full_path($relativepath) {
 753        if (empty($this->rootdir)) {
 754            return '';
 755        }
 756        return $this->rootdir . '/' . $relativepath;
 757    }
 758
 759    /**
 760     * Load the data from version.php.
 761     * @return object the data object defined in version.php.
 762     */
 763    protected function load_version_php() {
 764        $versionfile = $this->full_path('version.php');
 765
 766        $plugin = new stdClass();
 767        if (is_readable($versionfile)) {
 768            include($versionfile);
 769        }
 770        return $plugin;
 771    }
 772
 773    /**
 774     * @see plugin_information::load_disk_version()
 775     */
 776    public function load_disk_version() {
 777        $plugin = $this->load_version_php();
 778        if (isset($plugin->version)) {
 779            $this->versiondisk = $plugin->version;
 780        }
 781    }
 782
 783    /**
 784     * @see plugin_information::load_required_main_version()
 785     */
 786    public function load_required_main_version() {
 787        $plugin = $this->load_version_php();
 788        if (isset($plugin->requires)) {
 789            $this->versionrequires = $plugin->requires;
 790        }
 791    }
 792
 793    /**
 794     * Initialise {@link $dependencies} to the list of other plugins (in any)
 795     * that this one requires to be installed.
 796     */
 797    protected function load_other_required_plugins() {
 798        $plugin = $this->load_version_php();
 799        if (!empty($plugin->dependencies)) {
 800            $this->dependencies = $plugin->dependencies;
 801        } else {
 802            $this->dependencies = array(); // By default, no dependencies.
 803        }
 804    }
 805
 806    /**
 807     * @see plugin_information::get_other_required_plugins()
 808     */
 809    public function get_other_required_plugins() {
 810        if (is_null($this->dependencies)) {
 811            $this->load_other_required_plugins();
 812        }
 813        return $this->dependencies;
 814    }
 815
 816    /**
 817     * @see plugin_information::load_db_version()
 818     */
 819    public function load_db_version() {
 820
 821        if ($ver = self::get_version_from_config_plugins($this->component)) {
 822            $this->versiondb = $ver;
 823        }
 824    }
 825
 826    /**
 827     * @see plugin_information::init_is_standard()
 828     */
 829    public function init_is_standard() {
 830
 831        $standard = plugin_manager::standard_plugins_list($this->type);
 832
 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
 840            } else {
 841                $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
 842            }
 843        }
 844    }
 845
 846    /**
 847     * @see plugin_information::is_standard()
 848     */
 849    public function is_standard() {
 850        return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
 851    }
 852
 853    /**
 854     * @see plugin_information::get_status()
 855     */
 856    public function get_status() {
 857
 858        if (is_null($this->versiondb) and is_null($this->versiondisk)) {
 859            return plugin_manager::PLUGIN_STATUS_NODB;
 860
 861        } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
 862            return plugin_manager::PLUGIN_STATUS_NEW;
 863
 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;
 867            } else {
 868                return plugin_manager::PLUGIN_STATUS_MISSING;
 869            }
 870
 871        } else if ((string)$this->versiondb === (string)$this->versiondisk) {
 872            return plugin_manager::PLUGIN_STATUS_UPTODATE;
 873
 874        } else if ($this->versiondb < $this->versiondisk) {
 875            return plugin_manager::PLUGIN_STATUS_UPGRADE;
 876
 877        } else if ($this->versiondb > $this->versiondisk) {
 878            return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
 879
 880        } else {
 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');
 883        }
 884    }
 885
 886    /**
 887     * @see plugin_information::is_enabled()
 888     */
 889    public function is_enabled() {
 890        return null;
 891    }
 892
 893    /**
 894     * @see plugin_information::get_settings_url()
 895     */
 896    public function get_settings_url() {
 897        return null;
 898    }
 899
 900    /**
 901     * @see plugin_information::get_uninstall_url()
 902     */
 903    public function get_uninstall_url() {
 904        return null;
 905    }
 906
 907    /**
 908     * @see plugin_information::get_dir()
 909     */
 910    public function get_dir() {
 911        global $CFG;
 912
 913        return substr($this->rootdir, strlen($CFG->dirroot));
 914    }
 915
 916    /**
 917     * Provides access to plugin versions from {config_plugins}
 918     *
 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
 922     */
 923    protected function get_version_from_config_plugins($plugin, $disablecache=false) {
 924        global $DB;
 925        static $pluginversions = null;
 926
 927        if (is_null($pluginversions) or $disablecache) {
 928            try {
 929                $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
 930            } catch (dml_exception $e) {
 931                // before install
 932                $pluginversions = array();
 933            }
 934        }
 935
 936        if (!array_key_exists($plugin, $pluginversions)) {
 937            return false;
 938        }
 939
 940        return $pluginversions[$plugin];
 941    }
 942}
 943
 944/**
 945 * General class for all plugin types that do not have their own class
 946 */
 947class plugintype_general extends plugintype_base implements plugin_information {
 948
 949}
 950
 951/**
 952 * Class for page side blocks
 953 */
 954class plugintype_block extends plugintype_base implements plugin_information {
 955
 956    /**
 957     * @see plugin_information::get_plugins()
 958     */
 959    public static function get_plugins($type, $typerootdir, $typeclass) {
 960
 961        // get the information about blocks at the disk
 962        $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
 963
 964        // add blocks missing from disk
 965        $blocksinfo = self::get_blocks_info();
 966        foreach ($blocksinfo as $blockname => $blockinfo) {
 967            if (isset($blocks[$blockname])) {
 968                continue;
 969            }
 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();
 978
 979            $blocks[$blockname]   = $plugin;
 980        }
 981
 982        return $blocks;
 983    }
 984
 985    /**
 986     * @see plugin_information::init_display_name()
 987     */
 988    public function init_display_name() {
 989
 990        if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
 991            $this->displayname = get_string('pluginname', 'block_' . $this->name);
 992
 993        } else if (($block = block_instance($this->name)) !== false) {
 994            $this->displayname = $block->get_title();
 995
 996        } else {
 997            parent::init_display_name();
 998        }
 999    }
1000
1001    /**
1002     * @see plugin_information::load_db_version()
1003     */
1004    public function load_db_version() {
1005        global $DB;
1006
1007        $blocksinfo = self::get_blocks_info();
1008        if (isset($blocksinfo[$this->name]->version)) {
1009            $this->versiondb = $blocksinfo[$this->name]->version;
1010        }
1011    }
1012
1013    /**
1014     * @see plugin_information::is_enabled()
1015     */
1016    public function is_enabled() {
1017
1018        $blocksinfo = self::get_blocks_info();
1019        if (isset($blocksinfo[$this->name]->visible)) {
1020            if ($blocksinfo[$this->name]->visible) {
1021                return true;
1022            } else {
1023                return false;
1024            }
1025        } else {
1026            return parent::is_enabled();
1027        }
1028    }
1029
1030    /**
1031     * @see plugin_information::get_settings_url()
1032     */
1033    public function get_settings_url() {
1034
1035        if (($block = block_instance($this->name)) === false) {
1036            return parent::get_settings_url();
1037
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));
1041            } else {
1042                $blocksinfo = self::get_blocks_info();
1043                return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
1044            }
1045
1046        } else {
1047            return parent::get_settings_url();
1048        }
1049    }
1050
1051    /**
1052     * @see plugin_information::get_uninstall_url()
1053     */
1054    public function get_uninstall_url() {
1055
1056        $blocksinfo = self::get_blocks_info();
1057        return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
1058    }
1059
1060    /**
1061     * Provides access to the records in {block} table
1062     *
1063     * @param bool $disablecache do not use internal static cache
1064     * @return array array of stdClasses
1065     */
1066    protected static function get_blocks_info($disablecache=false) {
1067        global $DB;
1068        static $blocksinfocache = null;
1069
1070        if (is_null($blocksinfocache) or $disablecache) {
1071            try {
1072                $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
1073            } catch (dml_exception $e) {
1074                // before install
1075                $blocksinfocache = array();
1076            }
1077        }
1078
1079        return $blocksinfocache;
1080    }
1081}
1082
1083/**
1084 * Class for text filters
1085 */
1086class plugintype_filter extends plugintype_base implements plugin_information {
1087
1088    /**
1089     * @see plugin_information::get_plugins()
1090     */
1091    public static function get_plugins($type, $typerootdir, $typeclass) {
1092        global $CFG, $DB;
1093
1094        $filters = array();
1095
1096        // get the list of filters from both /filter and /mod location
1097        $installed = filter_get_all_installed();
1098
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;
1106
1107            $plugin->load_disk_version();
1108            $plugin->load_db_version();
1109            $plugin->load_required_main_version();
1110            $plugin->init_is_standard();
1111
1112            $filters[$plugin->name] = $plugin;
1113        }
1114
1115        $globalstates = self::get_global_states();
1116
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;
1125                }
1126            }
1127            if ($needsreload) {
1128                $globalstates = self::get_global_states(true);
1129            }
1130        }
1131
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;
1142
1143                $plugin->load_db_version();
1144
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;
1149                }
1150
1151                $filters[$plugin->name] = $plugin;
1152            }
1153        }
1154
1155        return $filters;
1156    }
1157
1158    /**
1159     * @see plugin_information::init_display_name()
1160     */
1161    public function init_display_name() {
1162        // do nothing, the name is set in self::get_plugins()
1163    }
1164
1165    /**
1166     * @see plugintype_base::load_version_php().
1167     */
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();
1173        }
1174        return parent::load_version_php();
1175    }
1176
1177    /**
1178     * @see plugin_information::is_enabled()
1179     */
1180    public function is_enabled() {
1181
1182        $globalstates = self::get_global_states();
1183
1184        foreach ($globalstates as $filterlegacyname => $info) {
1185            $name = self::normalize_legacy_name($filterlegacyname);
1186            if ($name === $this->name) {
1187                if ($info->active == TEXTFILTER_DISABLED) {
1188                    return false;
1189                } else {
1190                    // it may be 'On' or 'Off, but available'
1191                    return null;
1192                }
1193            }
1194        }
1195
1196        return null;
1197    }
1198
1199    /**
1200     * @see plugin_information::get_settings_url()
1201     */
1202    public function get_settings_url() {
1203
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)));
1208        } else {
1209            return null;
1210        }
1211    }
1212
1213    /**
1214     * @see plugin_information::get_uninstall_url()
1215     */
1216    public function get_uninstall_url() {
1217
1218        if (strpos($this->name, 'mod_') === 0) {
1219            return null;
1220        } else {
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'));
1224        }
1225    }
1226
1227    /**
1228     * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1229     *
1230     * @param string $legacyfiltername legacy filter name
1231     * @return string frankenstyle-like name
1232     */
1233    protected static function normalize_legacy_name($legacyfiltername) {
1234
1235        $name = str_replace('/', '_', $legacyfiltername);
1236        if (strpos($name, 'filter_') === 0) {
1237            $name = substr($name, 7);
1238            if (empty($name)) {
1239                throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1240            }
1241        }
1242
1243        return $name;
1244    }
1245
1246    /**
1247     * Provides access to the results of {@link filter_get_global_states()}
1248     * but indexed by the normalized filter name
1249     *
1250     * The legacy filter name is available as ->legacyname property.
1251     *
1252     * @param bool $disablecache
1253     * @return array
1254     */
1255    protected static function get_global_states($disablecache=false) {
1256        global $DB;
1257        static $globalstatescache = null;
1258
1259        if ($disablecache or is_null($globalstatescache)) {
1260
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();
1265
1266            } else {
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;
1274                }
1275            }
1276        }
1277
1278        return $globalstatescache;
1279    }
1280}
1281
1282/**
1283 * Class for activity modules
1284 */
1285class plugintype_mod extends plugintype_base implements plugin_information {
1286
1287    /**
1288     * @see plugin_information::get_plugins()
1289     */
1290    public static function get_plugins($type, $typerootdir, $typeclass) {
1291
1292        // get the information about plugins at the disk
1293        $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1294
1295        // add modules missing from disk
1296        $modulesinfo = self::get_modules_info();
1297        foreach ($modulesinfo as $modulename => $moduleinfo) {
1298            if (isset($modules[$modulename])) {
1299                continue;
1300            }
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();
1309
1310            $modules[$modulename]   = $plugin;
1311        }
1312
1313        return $modules;
1314    }
1315
1316    /**
1317     * @see plugin_information::init_display_name()
1318     */
1319    public function init_display_name() {
1320        if (get_string_manager()->string_exists('pluginname', $this->component)) {
1321            $this->displayname = get_string('pluginname', $this->component);
1322        } else {
1323            $this->displayname = get_string('modulename', $this->component);
1324        }
1325    }
1326
1327    /**
1328     * Load the data from version.php.
1329     * @return object the data object defined in version.php.
1330     */
1331    protected function load_version_php() {
1332        $versionfile = $this->full_path('version.php');
1333
1334        $module = new stdClass();
1335        if (is_readable($versionfile)) {
1336            include($versionfile);
1337        }
1338        return $module;
1339    }
1340
1341    /**
1342     * @see plugin_information::load_db_version()
1343     */
1344    public function load_db_version() {
1345        global $DB;
1346
1347        $modulesinfo = self::get_modules_info();
1348        if (isset($modulesinfo[$this->name]->version)) {
1349            $this->versiondb = $modulesinfo[$this->name]->version;
1350        }
1351    }
1352
1353    /**
1354     * @see plugin_information::is_enabled()
1355     */
1356    public function is_enabled() {
1357
1358        $modulesinfo = self::get_modules_info();
1359        if (isset($modulesinfo[$this->name]->visible)) {
1360            if ($modulesinfo[$this->name]->visible) {
1361                return true;
1362            } else {
1363                return false;
1364            }
1365        } else {
1366            return parent::is_enabled();
1367        }
1368    }
1369
1370    /**
1371     * @see plugin_information::get_settings_url()
1372     */
1373    public function get_settings_url() {
1374
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));
1377        } else {
1378            return parent::get_settings_url();
1379        }
1380    }
1381
1382    /**
1383     * @see plugin_information::get_uninstall_url()
1384     */
1385    public function get_uninstall_url() {
1386
1387        if ($this->name !== 'forum') {
1388            return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1389        } else {
1390            return null;
1391        }
1392    }
1393
1394    /**
1395     * Provides access to the records in {modules} table
1396     *
1397     * @param bool $disablecache do not use internal static cache
1398     * @return array array of stdClasses
1399     */
1400    protected static function get_modules_info($disablecache=false) {
1401        global $DB;
1402        static $modulesinfocache = null;
1403
1404        if (is_null($modulesinfocache) or $disablecache) {
1405            try {
1406                $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1407            } catch (dml_exception $e) {
1408                // before install
1409                $modulesinfocache = array();
1410            }
1411        }
1412
1413        return $modulesinfocache;
1414    }
1415}
1416
1417
1418/**
1419 * Class for question behaviours.
1420 */
1421class plugintype_qbehaviour extends plugintype_base implements plugin_information {
1422    /**
1423     * @see plugin_information::get_uninstall_url()
1424     */
1425    public function get_uninstall_url() {
1426        return new moodle_url('/admin/qbehaviours.php',
1427                array('delete' => $this->name, 'sesskey' => sesskey()));
1428    }
1429}
1430
1431
1432/**
1433 * Class for question types
1434 */
1435class plugintype_qtype extends plugintype_base implements plugin_information {
1436    /**
1437    * @see plugin_information::get_uninstall_url()
1438    */
1439    public function get_uninstall_url() {
1440        return new moodle_url('/admin/qtypes.php',
1441                array('delete' => $this->name, 'sesskey' => sesskey()));
1442    }
1443}
1444
1445
1446/**
1447 * Class for authentication plugins
1448 */
1449class plugintype_auth extends plugintype_base implements plugin_information {
1450
1451    /**
1452     * @see plugin_information::is_enabled()
1453     */
1454    public function is_enabled() {
1455        global $CFG;
1456        /** @var null|array list of enabled authentication plugins */
1457        static $enabled = null;
1458
1459        if (in_array($this->name, array('nologin', 'manual'))) {
1460            // these two are always enabled and can't be disabled
1461            return null;
1462        }
1463
1464        if (is_null($enabled)) {
1465            $enabled = array_flip(explode(',', $CFG->auth));
1466        }
1467
1468        return isset($enabled[$this->name]);
1469    }
1470
1471    /**
1472     * @see plugin_information::get_settings_url()
1473     */
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));
1477        } else {
1478            return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1479        }
1480    }
1481}
1482
1483/**
1484 * Class for enrolment plugins
1485 */
1486class plugintype_enrol extends plugintype_base implements plugin_information {
1487
1488    /**
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
1493     *
1494     * @see plugin_information::is_enabled()
1495     */
1496    public function is_enabled() {
1497        global $CFG;
1498        /** @var null|array list of enabled enrolment plugins */
1499        static $enabled = null;
1500
1501        if (is_null($enabled)) {
1502            $enabled = array_flip(explode(',', $CFG->enrol_plugins_enabled));
1503        }
1504
1505        return isset($enabled[$this->name]);
1506    }
1507
1508    /**
1509     * @see plugin_information::get_settings_url()
1510     */
1511    public function get_settings_url() {
1512
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));
1515        } else {
1516            return parent::get_settings_url();
1517        }
1518    }
1519
1520    /**
1521     * @see plugin_information::get_uninstall_url()
1522     */
1523    public function get_uninstall_url() {
1524        return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey()));
1525    }
1526}
1527
1528/**
1529 * Class for messaging processors
1530 */
1531class plugintype_message extends plugintype_base implements plugin_information {
1532
1533    /**
1534     * @see plugin_information::get_settings_url()
1535     */
1536    public function get_settings_url() {
1537
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));
1540        } else {
1541            return parent::get_settings_url();
1542        }
1543    }
1544}
1545
1546/**
1547 * Class for repositories
1548 */
1549class plugintype_repository extends plugintype_base implements plugin_information {
1550
1551    /**
1552     * @see plugin_information::is_enabled()
1553     */
1554    public function is_enabled() {
1555
1556        $enabled = self::get_enabled_repositories();
1557
1558        return isset($enabled[$this->name]);
1559    }
1560
1561    /**
1562     * @see plugin_information::get_settings_url()
1563     */
1564    public function get_settings_url() {
1565
1566        if ($this->is_enabled()) {
1567            return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
1568        } else {
1569            return parent::get_settings_url();
1570        }
1571    }
1572
1573    /**
1574     * Provides access to the records in {repository} table
1575     *
1576     * @param bool $disablecache do not use internal static cache
1577     * @return array array of stdClasses
1578     */
1579    protected static function get_enabled_repositories($disablecache=false) {
1580        global $DB;
1581        static $repositories = null;
1582
1583        if (is_null($repositories) or $disablecache) {
1584            $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
1585        }
1586
1587        return $repositories;
1588    }
1589}
1590
1591/**
1592 * Class for portfolios
1593 */
1594class plugintype_portfolio extends plugintype_base implements plugin_information {
1595
1596    /**
1597     * @see plugin_information::is_enabled()
1598     */
1599    public function is_enabled() {
1600
1601        $enabled = self::get_enabled_portfolios();
1602
1603        return isset($enabled[$this->name]);
1604    }
1605
1606    /**
1607     * Provides access to the records in {portfolio_instance} table
1608     *
1609     * @param bool $disablecache do not use internal static cache
1610     * @return array array of stdClasses
1611     */
1612    protected static function get_enabled_portfolios($disablecache=false) {
1613        global $DB;
1614        static $portfolios = null;
1615
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;
1623                    }
1624                } else {
1625                    $portfolios[$instance->plugin] = $instance;
1626                }
1627            }
1628        }
1629
1630        return $portfolios;
1631    }
1632}
1633
1634/**
1635 * Class for themes
1636 */
1637class plugintype_theme extends plugintype_base implements plugin_information {
1638
1639    /**
1640     * @see plugin_information::is_enabled()
1641     */
1642    public function is_enabled() {
1643        global $CFG;
1644
1645        if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
1646            (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
1647            return true;
1648        } else {
1649            return parent::is_enabled();
1650        }
1651    }
1652}
1653
1654/**
1655 * Class representing an MNet service
1656 */
1657class plugintype_mnetservice extends plugintype_base implements plugin_information {
1658
1659    /**
1660     * @see plugin_information::is_enabled()
1661     */
1662    public function is_enabled() {
1663        global $CFG;
1664
1665        if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
1666            return false;
1667        } else {
1668            return parent::is_enabled();
1669        }
1670    }
1671}
1672
1673/**
1674 * Class for admin tool plugins
1675 */
1676class plugintype_tool extends plugintype_base implements plugin_information {
1677
1678    public function get_uninstall_url() {
1679        return new moodle_url('/admin/tools.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1680    }
1681}
1682
1683/**
1684 * Class for admin tool plugins
1685 */
1686class plugintype_report extends plugintype_base implements plugin_information {
1687
1688    public function get_uninstall_url() {
1689        return new moodle_url('/admin/reports.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1690    }
1691}