PageRenderTime 184ms CodeModel.GetById 16ms app.highlight 132ms RepoModel.GetById 7ms app.codeStats 1ms

/lib/pluginlib.php

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

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