MDL-20438 Initial support for cron based notifications
[moodle.git] / lib / pluginlib.php
CommitLineData
b9934a17 1<?php
b6ad8594 2
b9934a17
DM
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Defines classes used for plugins management
20 *
21 * This library provides a unified interface to various plugin types in
22 * Moodle. It is mainly used by the plugins management admin page and the
23 * plugins check page during the upgrade.
24 *
25 * @package core
26 * @subpackage admin
27 * @copyright 2011 David Mudrak <david@moodle.com>
28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 */
30
31defined('MOODLE_INTERNAL') || die();
32
f9286139
DM
33require_once($CFG->libdir.'/filelib.php'); // curl class needed here
34
b9934a17
DM
35/**
36 * Singleton class providing general plugins management functionality
37 */
38class plugin_manager {
39
40 /** the plugin is shipped with standard Moodle distribution */
41 const PLUGIN_SOURCE_STANDARD = 'std';
42 /** the plugin is added extension */
43 const PLUGIN_SOURCE_EXTENSION = 'ext';
44
45 /** the plugin uses neither database nor capabilities, no versions */
46 const PLUGIN_STATUS_NODB = 'nodb';
47 /** the plugin is up-to-date */
48 const PLUGIN_STATUS_UPTODATE = 'uptodate';
49 /** the plugin is about to be installed */
50 const PLUGIN_STATUS_NEW = 'new';
51 /** the plugin is about to be upgraded */
52 const PLUGIN_STATUS_UPGRADE = 'upgrade';
ec8935f5
PS
53 /** the standard plugin is about to be deleted */
54 const PLUGIN_STATUS_DELETE = 'delete';
b9934a17
DM
55 /** the version at the disk is lower than the one already installed */
56 const PLUGIN_STATUS_DOWNGRADE = 'downgrade';
57 /** the plugin is installed but missing from disk */
58 const PLUGIN_STATUS_MISSING = 'missing';
59
60 /** @var plugin_manager holds the singleton instance */
61 protected static $singletoninstance;
62 /** @var array of raw plugins information */
63 protected $pluginsinfo = null;
64 /** @var array of raw subplugins information */
65 protected $subpluginsinfo = null;
66
67 /**
68 * Direct initiation not allowed, use the factory method {@link self::instance()}
b9934a17
DM
69 */
70 protected function __construct() {
b9934a17
DM
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() {
b9934a17
DM
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
e61aaece
TH
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
b6ad8594 97 * the values are the corresponding objects extending {@link plugininfo_base}
b9934a17
DM
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();
4ed26680 104 $plugintypes = $this->reorder_plugin_types($plugintypes);
b9934a17
DM
105 foreach ($plugintypes as $plugintype => $plugintyperootdir) {
106 if (in_array($plugintype, array('base', 'general'))) {
107 throw new coding_exception('Illegal usage of reserved word for plugin type');
108 }
b6ad8594
DM
109 if (class_exists('plugininfo_' . $plugintype)) {
110 $plugintypeclass = 'plugininfo_' . $plugintype;
b9934a17 111 } else {
b6ad8594 112 $plugintypeclass = 'plugininfo_general';
b9934a17 113 }
b6ad8594
DM
114 if (!in_array('plugininfo_base', class_parents($plugintypeclass))) {
115 throw new coding_exception('Class ' . $plugintypeclass . ' must extend plugininfo_base');
b9934a17
DM
116 }
117 $plugins = call_user_func(array($plugintypeclass, 'get_plugins'), $plugintype, $plugintyperootdir, $plugintypeclass);
118 $this->pluginsinfo[$plugintype] = $plugins;
119 }
dd119e21
DM
120
121 // append the information about available updates provided by {@link available_update_checker()}
122 $provider = available_update_checker::instance();
123 foreach ($this->pluginsinfo as $plugintype => $plugins) {
124 foreach ($plugins as $plugininfoholder) {
7d8de6d8 125 $plugininfoholder->check_available_updates($provider);
dd119e21
DM
126 }
127 }
b9934a17
DM
128 }
129
130 return $this->pluginsinfo;
131 }
132
133 /**
0242bdc7
TH
134 * Returns list of plugins that define their subplugins and the information
135 * about them from the db/subplugins.php file.
b9934a17
DM
136 *
137 * At the moment, only activity modules can define subplugins.
138 *
0242bdc7
TH
139 * @param bool $disablecache force reload, cache can be used otherwise
140 * @return array with keys like 'mod_quiz', and values the data from the
141 * corresponding db/subplugins.php file.
b9934a17
DM
142 */
143 public function get_subplugins($disablecache=false) {
144
145 if ($disablecache or is_null($this->subpluginsinfo)) {
146 $this->subpluginsinfo = array();
147 $mods = get_plugin_list('mod');
148 foreach ($mods as $mod => $moddir) {
149 $modsubplugins = array();
150 if (file_exists($moddir . '/db/subplugins.php')) {
151 include($moddir . '/db/subplugins.php');
152 foreach ($subplugins as $subplugintype => $subplugintyperootdir) {
153 $subplugin = new stdClass();
154 $subplugin->type = $subplugintype;
155 $subplugin->typerootdir = $subplugintyperootdir;
156 $modsubplugins[$subplugintype] = $subplugin;
157 }
158 $this->subpluginsinfo['mod_' . $mod] = $modsubplugins;
159 }
160 }
161 }
162
163 return $this->subpluginsinfo;
164 }
165
166 /**
167 * Returns the name of the plugin that defines the given subplugin type
168 *
169 * If the given subplugin type is not actually a subplugin, returns false.
170 *
171 * @param string $subplugintype the name of subplugin type, eg. workshopform or quiz
172 * @return false|string the name of the parent plugin, eg. mod_workshop
173 */
174 public function get_parent_of_subplugin($subplugintype) {
175
176 $parent = false;
177 foreach ($this->get_subplugins() as $pluginname => $subplugintypes) {
178 if (isset($subplugintypes[$subplugintype])) {
179 $parent = $pluginname;
180 break;
181 }
182 }
183
184 return $parent;
185 }
186
187 /**
188 * Returns a localized name of a given plugin
189 *
190 * @param string $plugin name of the plugin, eg mod_workshop or auth_ldap
191 * @return string
192 */
193 public function plugin_name($plugin) {
194 list($type, $name) = normalize_component($plugin);
195 return $this->pluginsinfo[$type][$name]->displayname;
196 }
197
198 /**
199 * Returns a localized name of a plugin type in plural form
200 *
201 * Most plugin types define their names in core_plugin lang file. In case of subplugins,
202 * we try to ask the parent plugin for the name. In the worst case, we will return
203 * the value of the passed $type parameter.
204 *
205 * @param string $type the type of the plugin, e.g. mod or workshopform
206 * @return string
207 */
208 public function plugintype_name_plural($type) {
209
210 if (get_string_manager()->string_exists('type_' . $type . '_plural', 'core_plugin')) {
211 // for most plugin types, their names are defined in core_plugin lang file
212 return get_string('type_' . $type . '_plural', 'core_plugin');
213
214 } else if ($parent = $this->get_parent_of_subplugin($type)) {
215 // if this is a subplugin, try to ask the parent plugin for the name
216 if (get_string_manager()->string_exists('subplugintype_' . $type . '_plural', $parent)) {
217 return $this->plugin_name($parent) . ' / ' . get_string('subplugintype_' . $type . '_plural', $parent);
218 } else {
219 return $this->plugin_name($parent) . ' / ' . $type;
220 }
221
222 } else {
223 return $type;
224 }
225 }
226
e61aaece
TH
227 /**
228 * @param string $component frankenstyle component name.
b6ad8594 229 * @return plugininfo_base|null the corresponding plugin information.
e61aaece
TH
230 */
231 public function get_plugin_info($component) {
232 list($type, $name) = normalize_component($component);
233 $plugins = $this->get_plugins();
234 if (isset($plugins[$type][$name])) {
235 return $plugins[$type][$name];
236 } else {
237 return null;
238 }
239 }
240
828788f0 241 /**
b6ad8594 242 * Get a list of any other plugins that require this one.
828788f0
TH
243 * @param string $component frankenstyle component name.
244 * @return array of frankensyle component names that require this one.
245 */
246 public function other_plugins_that_require($component) {
247 $others = array();
248 foreach ($this->get_plugins() as $type => $plugins) {
249 foreach ($plugins as $plugin) {
250 $required = $plugin->get_other_required_plugins();
251 if (isset($required[$component])) {
252 $others[] = $plugin->component;
253 }
254 }
255 }
256 return $others;
257 }
258
e61aaece 259 /**
777781d1
TH
260 * Check a dependencies list against the list of installed plugins.
261 * @param array $dependencies compenent name to required version or ANY_VERSION.
262 * @return bool true if all the dependencies are satisfied.
e61aaece 263 */
777781d1
TH
264 public function are_dependencies_satisfied($dependencies) {
265 foreach ($dependencies as $component => $requiredversion) {
e61aaece
TH
266 $otherplugin = $this->get_plugin_info($component);
267 if (is_null($otherplugin)) {
0242bdc7
TH
268 return false;
269 }
270
3f123d92 271 if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
0242bdc7
TH
272 return false;
273 }
274 }
275
276 return true;
277 }
278
faadd326 279 /**
777781d1 280 * Checks all dependencies for all installed plugins. Used by install and upgrade.
faadd326 281 * @param int $moodleversion the version from version.php.
777781d1 282 * @return bool true if all the dependencies are satisfied for all plugins.
faadd326
TH
283 */
284 public function all_plugins_ok($moodleversion) {
285 foreach ($this->get_plugins() as $type => $plugins) {
286 foreach ($plugins as $plugin) {
287
288 if (!empty($plugin->versionrequires) && $plugin->versionrequires > $moodleversion) {
289 return false;
290 }
291
777781d1 292 if (!$this->are_dependencies_satisfied($plugin->get_other_required_plugins())) {
faadd326
TH
293 return false;
294 }
295 }
296 }
297
298 return true;
299 }
300
ec8935f5
PS
301 /**
302 * Defines a list of all plugins that were originally shipped in the standard Moodle distribution,
303 * but are not anymore and are deleted during upgrades.
304 *
305 * The main purpose of this list is to hide missing plugins during upgrade.
306 *
307 * @param string $type plugin type
308 * @param string $name plugin name
309 * @return bool
310 */
311 public static function is_deleted_standard_plugin($type, $name) {
312 static $plugins = array(
34c72803 313 // do not add 1.9-2.2 plugin removals here
ec8935f5
PS
314 );
315
316 if (!isset($plugins[$type])) {
317 return false;
318 }
319 return in_array($name, $plugins[$type]);
320 }
321
b9934a17
DM
322 /**
323 * Defines a white list of all plugins shipped in the standard Moodle distribution
324 *
ec8935f5 325 * @param string $type
b9934a17
DM
326 * @return false|array array of standard plugins or false if the type is unknown
327 */
328 public static function standard_plugins_list($type) {
329 static $standard_plugins = array(
330
331 'assignment' => array(
332 'offline', 'online', 'upload', 'uploadsingle'
333 ),
334
335 'auth' => array(
336 'cas', 'db', 'email', 'fc', 'imap', 'ldap', 'manual', 'mnet',
337 'nntp', 'nologin', 'none', 'pam', 'pop3', 'radius',
338 'shibboleth', 'webservice'
339 ),
340
341 'block' => array(
342 'activity_modules', 'admin_bookmarks', 'blog_menu',
343 'blog_recent', 'blog_tags', 'calendar_month',
344 'calendar_upcoming', 'comments', 'community',
345 'completionstatus', 'course_list', 'course_overview',
346 'course_summary', 'feedback', 'glossary_random', 'html',
347 'login', 'mentees', 'messages', 'mnet_hosts', 'myprofile',
348 'navigation', 'news_items', 'online_users', 'participants',
349 'private_files', 'quiz_results', 'recent_activity',
f68cef22 350 'rss_client', 'search_forums', 'section_links',
b9934a17
DM
351 'selfcompletion', 'settings', 'site_main_menu',
352 'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
353 ),
354
355 'coursereport' => array(
a2a444ab 356 //deprecated!
b9934a17
DM
357 ),
358
359 'datafield' => array(
360 'checkbox', 'date', 'file', 'latlong', 'menu', 'multimenu',
361 'number', 'picture', 'radiobutton', 'text', 'textarea', 'url'
362 ),
363
364 'datapreset' => array(
365 'imagegallery'
366 ),
367
368 'editor' => array(
369 'textarea', 'tinymce'
370 ),
371
372 'enrol' => array(
373 'authorize', 'category', 'cohort', 'database', 'flatfile',
374 'guest', 'imsenterprise', 'ldap', 'manual', 'meta', 'mnet',
375 'paypal', 'self'
376 ),
377
378 'filter' => array(
379 'activitynames', 'algebra', 'censor', 'emailprotect',
380 'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
87783982 381 'urltolink', 'data', 'glossary'
b9934a17
DM
382 ),
383
384 'format' => array(
385 'scorm', 'social', 'topics', 'weeks'
386 ),
387
388 'gradeexport' => array(
389 'ods', 'txt', 'xls', 'xml'
390 ),
391
392 'gradeimport' => array(
393 'csv', 'xml'
394 ),
395
396 'gradereport' => array(
397 'grader', 'outcomes', 'overview', 'user'
398 ),
399
f59f488a
DM
400 'gradingform' => array(
401 'rubric'
402 ),
403
b9934a17
DM
404 'local' => array(
405 ),
406
407 'message' => array(
408 'email', 'jabber', 'popup'
409 ),
410
411 'mnetservice' => array(
412 'enrol'
413 ),
414
415 'mod' => array(
416 'assignment', 'chat', 'choice', 'data', 'feedback', 'folder',
7fdee5b6 417 'forum', 'glossary', 'imscp', 'label', 'lesson', 'lti', 'page',
b9934a17
DM
418 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop'
419 ),
420
421 'plagiarism' => array(
422 ),
423
424 'portfolio' => array(
425 'boxnet', 'download', 'flickr', 'googledocs', 'mahara', 'picasa'
426 ),
427
428 'profilefield' => array(
429 'checkbox', 'datetime', 'menu', 'text', 'textarea'
430 ),
431
d1c77ac3
DM
432 'qbehaviour' => array(
433 'adaptive', 'adaptivenopenalty', 'deferredcbm',
434 'deferredfeedback', 'immediatecbm', 'immediatefeedback',
435 'informationitem', 'interactive', 'interactivecountback',
436 'manualgraded', 'missing'
437 ),
438
b9934a17
DM
439 'qformat' => array(
440 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
2dc54611 441 'learnwise', 'missingword', 'multianswer', 'webct',
b9934a17
DM
442 'xhtml', 'xml'
443 ),
444
445 'qtype' => array(
446 'calculated', 'calculatedmulti', 'calculatedsimple',
447 'description', 'essay', 'match', 'missingtype', 'multianswer',
448 'multichoice', 'numerical', 'random', 'randomsamatch',
449 'shortanswer', 'truefalse'
450 ),
451
452 'quiz' => array(
453 'grading', 'overview', 'responses', 'statistics'
454 ),
455
c999d841
TH
456 'quizaccess' => array(
457 'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate',
458 'password', 'safebrowser', 'securewindow', 'timelimit'
459 ),
460
b9934a17 461 'report' => array(
13fdaaac 462 'backups', 'completion', 'configlog', 'courseoverview',
8a8f29c2 463 'log', 'loglive', 'outline', 'participation', 'progress', 'questioninstances', 'security', 'stats'
b9934a17
DM
464 ),
465
466 'repository' => array(
467 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'filesystem',
468 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot',
469 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav',
470 'wikimedia', 'youtube'
471 ),
472
99e86561 473 'scormreport' => array(
8f1a0d21 474 'basic',
e61a7137
AKA
475 'interactions',
476 'graphs'
99e86561
PS
477 ),
478
b9934a17 479 'theme' => array(
bef9ad95
DM
480 'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
481 'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
98ca9e84
EL
482 'fusion', 'leatherbound', 'magazine', 'mymobile', 'nimble',
483 'nonzero', 'overlay', 'serenity', 'sky_high', 'splash',
484 'standard', 'standardold'
b9934a17
DM
485 ),
486
11b24ce7 487 'tool' => array(
b703861f 488 'bloglevelupgrade', 'capability', 'customlang', 'dbtransfer', 'generator',
cff8fc8d 489 'health', 'innodb', 'langimport', 'multilangupgrade', 'profiling',
fab6f7b7 490 'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport', 'unittest',
9597e00b 491 'uploaduser', 'unsuproles', 'xmldb'
11b24ce7
PS
492 ),
493
b9934a17
DM
494 'webservice' => array(
495 'amf', 'rest', 'soap', 'xmlrpc'
496 ),
497
498 'workshopallocation' => array(
499 'manual', 'random'
500 ),
501
502 'workshopeval' => array(
503 'best'
504 ),
505
506 'workshopform' => array(
507 'accumulative', 'comments', 'numerrors', 'rubric'
508 )
509 );
510
511 if (isset($standard_plugins[$type])) {
512 return $standard_plugins[$type];
513
514 } else {
515 return false;
516 }
517 }
4ed26680
DM
518
519 /**
520 * Reordes plugin types into a sequence to be displayed
521 *
522 * For technical reasons, plugin types returned by {@link get_plugin_types()} are
523 * in a certain order that does not need to fit the expected order for the display.
524 * Particularly, activity modules should be displayed first as they represent the
525 * real heart of Moodle. They should be followed by other plugin types that are
526 * used to build the courses (as that is what one expects from LMS). After that,
527 * other supportive plugin types follow.
528 *
529 * @param array $types associative array
530 * @return array same array with altered order of items
531 */
532 protected function reorder_plugin_types(array $types) {
533 $fix = array(
534 'mod' => $types['mod'],
535 'block' => $types['block'],
536 'qtype' => $types['qtype'],
537 'qbehaviour' => $types['qbehaviour'],
538 'qformat' => $types['qformat'],
539 'filter' => $types['filter'],
540 'enrol' => $types['enrol'],
541 );
542 foreach ($types as $type => $path) {
543 if (!isset($fix[$type])) {
544 $fix[$type] = $path;
545 }
546 }
547 return $fix;
548 }
b9934a17
DM
549}
550
b9934a17 551
cd0bb55f
DM
552/**
553 * General exception thrown by the {@link available_update_checker} class
554 */
555class available_update_checker_exception extends moodle_exception {
556
557 /**
558 * @param string $errorcode exception description identifier
559 * @param mixed $debuginfo debugging data to display
560 */
561 public function __construct($errorcode, $debuginfo=null) {
562 parent::__construct($errorcode, 'core_plugin', '', null, print_r($debuginfo, true));
563 }
564}
565
566
567/**
568 * Singleton class that handles checking for available updates
569 */
570class available_update_checker {
571
572 /** @var available_update_checker holds the singleton instance */
573 protected static $singletoninstance;
7d8de6d8
DM
574 /** @var null|int the timestamp of when the most recent response was fetched */
575 protected $recentfetch = null;
576 /** @var null|array the recent response from the update notification provider */
577 protected $recentresponse = null;
55585f3a
DM
578 /** @var null|string the numerical version of the local Moodle code */
579 protected $currentversion = null;
580 /** @var null|string branch of the local Moodle code */
581 protected $currentbranch = null;
582 /** @var array of (string)frankestyle => (string)version list of additional plugins deployed at this site */
583 protected $currentplugins = array();
cd0bb55f
DM
584
585 /**
586 * Direct initiation not allowed, use the factory method {@link self::instance()}
587 */
588 protected function __construct() {
cd0bb55f
DM
589 }
590
591 /**
592 * Sorry, this is singleton
593 */
594 protected function __clone() {
595 }
596
597 /**
598 * Factory method for this class
599 *
600 * @return available_update_checker the singleton instance
601 */
602 public static function instance() {
603 if (is_null(self::$singletoninstance)) {
604 self::$singletoninstance = new self();
605 }
606 return self::$singletoninstance;
607 }
608
cd0bb55f
DM
609 /**
610 * Returns the timestamp of the last execution of {@link fetch()}
611 *
612 * @return int|null null if it has never been executed or we don't known
613 */
614 public function get_last_timefetched() {
7d8de6d8
DM
615
616 $this->restore_response();
617
618 if (!empty($this->recentfetch)) {
619 return $this->recentfetch;
620
cd0bb55f 621 } else {
7d8de6d8 622 return null;
cd0bb55f
DM
623 }
624 }
625
626 /**
627 * Fetches the available update status from the remote site
628 *
629 * @throws available_update_checker_exception
630 */
631 public function fetch() {
7d8de6d8 632 $response = $this->get_response();
cd0bb55f 633 $this->validate_response($response);
7d8de6d8 634 $this->store_response($response);
cd0bb55f
DM
635 }
636
55585f3a
DM
637 /**
638 * Returns the available update information for Moodle core
639 *
640 * The returned structure is an array of available_update_info objects or null
641 * if there no such info available.
642 *
643 * @param int $minmaturity minimal maturity level to return, returns all by default
644 * @return null|stdClass null or array of available_update_info objects
645 */
646 public function get_core_update_info($minmaturity = 0) {
647
648 $this->load_current_environment();
649
650 $updates = $this->get_update_info('core');
651 if (empty($updates)) {
652 return null;
653 }
654 $return = array();
655 foreach ($updates as $update) {
656 if (isset($update->maturity) and ($update->maturity < $minmaturity)) {
657 continue;
658 }
659 if ($update->version > $this->currentversion) {
660 $return[] = $update;
661 }
662 }
663
664 if (empty($return)) {
665 return null;
666 }
667
668 return $return;
669 }
670
cd0bb55f
DM
671 /**
672 * Returns the available update information for the given component
673 *
674 * This method returns null if the most recent response does not contain any information
7d8de6d8
DM
675 * about it. The returned structure is an array of available updates for the given
676 * component. Each update info is an object with at least one property called
677 * 'version'. Other possible properties are 'release', 'maturity', 'url' and 'downloadurl'.
cd0bb55f
DM
678 *
679 * @param string $component frankenstyle
55585f3a 680 * @return null|stdClass null or array of available_update_info objects
cd0bb55f
DM
681 */
682 public function get_update_info($component) {
683
7d8de6d8 684 $this->restore_response();
cd0bb55f 685
7d8de6d8
DM
686 if (!empty($this->recentresponse['updates'][$component])) {
687 $updates = array();
688 foreach ($this->recentresponse['updates'][$component] as $info) {
689 $updates[] = new available_update_info($component, $info);
690 }
691 return $updates;
cd0bb55f
DM
692 } else {
693 return null;
694 }
695 }
696
be378880
DM
697 /**
698 * The method being run via cron.php
699 */
700 public function cron() {
701 global $CFG;
702
703 if (!$this->cron_autocheck_enabled()) {
704 $this->cron_mtrace('Automatic check for available updates not enabled, skipping.');
705 return;
706 }
707
708 $now = $this->cron_current_timestamp();
709
710 if ($this->cron_has_fresh_fetch($now)) {
711 $this->cron_mtrace('Recently fetched info about available updates is still fresh enough, skipping.');
712 return;
713 }
714
715 if ($this->cron_has_outdated_fetch($now)) {
716 $this->cron_mtrace('Outdated or missing info about available updates, forced fetching ... ', '');
717 $this->cron_execute();
718 return;
719 }
720
721 $offset = $this->cron_execution_offset();
722 $start = mktime(1, 0, 0, date('n', $now), date('j', $now), date('Y', $now)); // 01:00 AM today local time
723 if ($now > $start + $offset) {
724 $this->cron_mtrace('Regular daily check for available updates ... ', '');
725 $this->cron_execute();
726 return;
727 }
728 }
729
730 /// end of public API //////////////////////////////////////////////////////
731
cd0bb55f 732 /**
7d8de6d8 733 * Makes cURL request to get data from the remote site
cd0bb55f 734 *
7d8de6d8 735 * @return string raw request result
cd0bb55f
DM
736 * @throws available_update_checker_exception
737 */
7d8de6d8 738 protected function get_response() {
cd0bb55f
DM
739 $curl = new curl(array('proxy' => true));
740 $response = $curl->post($this->prepare_request_url(), $this->prepare_request_params());
741 $curlinfo = $curl->get_info();
742 if ($curlinfo['http_code'] != 200) {
743 throw new available_update_checker_exception('err_response_http_code', $curlinfo['http_code']);
744 }
cd0bb55f
DM
745 return $response;
746 }
747
748 /**
749 * Makes sure the response is valid, has correct API format etc.
750 *
7d8de6d8 751 * @param string $response raw response as returned by the {@link self::get_response()}
cd0bb55f
DM
752 * @throws available_update_checker_exception
753 */
7d8de6d8
DM
754 protected function validate_response($response) {
755
756 $response = $this->decode_response($response);
cd0bb55f
DM
757
758 if (empty($response)) {
759 throw new available_update_checker_exception('err_response_empty');
760 }
761
7d8de6d8
DM
762 if (empty($response['status']) or $response['status'] !== 'OK') {
763 throw new available_update_checker_exception('err_response_status', $response['status']);
764 }
765
766 if (empty($response['apiver']) or $response['apiver'] !== '1.0') {
767 throw new available_update_checker_exception('err_response_format_version', $response['apiver']);
cd0bb55f
DM
768 }
769
7d8de6d8
DM
770 if (empty($response['forbranch']) or $response['forbranch'] !== moodle_major_version(true)) {
771 throw new available_update_checker_exception('err_response_target_version', $response['target']);
cd0bb55f
DM
772 }
773 }
774
775 /**
7d8de6d8 776 * Decodes the raw string response from the update notifications provider
cd0bb55f 777 *
7d8de6d8
DM
778 * @param string $response as returned by {@link self::get_response()}
779 * @return array decoded response structure
cd0bb55f 780 */
7d8de6d8
DM
781 protected function decode_response($response) {
782 return json_decode($response, true);
cd0bb55f
DM
783 }
784
785 /**
7d8de6d8
DM
786 * Stores the valid fetched response for later usage
787 *
788 * This implementation uses the config_plugins table as the permanent storage.
cd0bb55f 789 *
7d8de6d8 790 * @param string $response raw valid data returned by {@link self::get_response()}
cd0bb55f 791 */
7d8de6d8
DM
792 protected function store_response($response) {
793
794 set_config('recentfetch', time(), 'core_plugin');
795 set_config('recentresponse', $response, 'core_plugin');
796
797 $this->restore_response(true);
cd0bb55f
DM
798 }
799
800 /**
7d8de6d8
DM
801 * Loads the most recent raw response record we have fetched
802 *
803 * This implementation uses the config_plugins table as the permanent storage.
cd0bb55f 804 *
7d8de6d8 805 * @param bool $forcereload reload even if it was already loaded
cd0bb55f 806 */
7d8de6d8
DM
807 protected function restore_response($forcereload = false) {
808
809 if (!$forcereload and !is_null($this->recentresponse)) {
810 // we already have it, nothing to do
811 return;
cd0bb55f
DM
812 }
813
7d8de6d8
DM
814 $config = get_config('core_plugin');
815
816 if (!empty($config->recentresponse) and !empty($config->recentfetch)) {
817 try {
818 $this->validate_response($config->recentresponse);
819 $this->recentfetch = $config->recentfetch;
820 $this->recentresponse = $this->decode_response($config->recentresponse);
821 }
822 catch (available_update_checker_exception $e) {
823 // do not set recentresponse if the validation fails
824 }
825
cd0bb55f 826 } else {
7d8de6d8 827 $this->recentresponse = array();
cd0bb55f
DM
828 }
829 }
830
831 /**
832 * Returns the URL to send update requests to
833 *
834 * During the development or testing, you can set $CFG->alternativeupdateproviderurl
835 * to a custom URL that will be used. Otherwise the standard URL will be returned.
836 *
837 * @return string URL
838 */
839 protected function prepare_request_url() {
840 global $CFG;
841
842 if (!empty($CFG->alternativeupdateproviderurl)) {
843 return $CFG->alternativeupdateproviderurl;
844 } else {
845 return 'http://download.moodle.org/api/1.0/updates.php';
846 }
847 }
848
55585f3a
DM
849 /**
850 * Sets the properties currentversion, currentbranch and currentplugins
851 *
852 * @param bool $forcereload
853 */
854 protected function load_current_environment($forcereload=false) {
855 global $CFG;
856
857 if (!is_null($this->currentversion) and !$forcereload) {
858 // nothing to do
859 return;
860 }
861
862 require($CFG->dirroot.'/version.php');
863 $this->currentversion = $version;
864
865 $this->currentbranch = moodle_major_version(true);
866
867 $pluginman = plugin_manager::instance();
868 foreach ($pluginman->get_plugins() as $type => $plugins) {
869 foreach ($plugins as $plugin) {
870 if (!$plugin->is_standard()) {
871 $this->currentplugins[$plugin->component] = $plugin->versiondisk;
872 }
873 }
874 }
875 }
876
cd0bb55f
DM
877 /**
878 * Returns the list of HTTP params to be sent to the updates provider URL
879 *
880 * @return array of (string)param => (string)value
881 */
882 protected function prepare_request_params() {
883 global $CFG;
884
55585f3a 885 $this->load_current_environment();
7d8de6d8
DM
886 $this->restore_response();
887
cd0bb55f
DM
888 $params = array();
889 $params['format'] = 'json';
890
7d8de6d8
DM
891 if (isset($this->recentresponse['ticket'])) {
892 $params['ticket'] = $this->recentresponse['ticket'];
cd0bb55f
DM
893 }
894
55585f3a
DM
895 if (isset($this->currentversion)) {
896 $params['version'] = $this->currentversion;
897 } else {
898 throw new coding_exception('Main Moodle version must be already known here');
cd0bb55f
DM
899 }
900
55585f3a
DM
901 if (isset($this->currentbranch)) {
902 $params['branch'] = $this->currentbranch;
903 } else {
904 throw new coding_exception('Moodle release must be already known here');
905 }
906
907 $plugins = array();
908 foreach ($this->currentplugins as $plugin => $version) {
909 $plugins[] = $plugin.'@'.$version;
910 }
911 if (!empty($plugins)) {
912 $params['plugins'] = implode(',', $plugins);
cd0bb55f
DM
913 }
914
cd0bb55f
DM
915 return $params;
916 }
be378880
DM
917
918 /**
919 * Returns the current timestamp
920 *
921 * @return int the timestamp
922 */
923 protected function cron_current_timestamp() {
924 return time();
925 }
926
927 /**
928 * Output cron debugging info
929 *
930 * @see mtrace()
931 * @param string $msg output message
932 * @param string $eol end of line
933 */
934 protected function cron_mtrace($msg, $eol = PHP_EOL) {
935 mtrace($msg, $eol);
936 }
937
938 /**
939 * Decide if the autocheck feature is disabled in the server setting
940 *
941 * @return bool true if autocheck enabled, false if disabled
942 */
943 protected function cron_autocheck_enabled() {
944 if (empty($CFG->updateautocheck)) {
945 return false;
946 } else {
947 return true;
948 }
949 }
950
951 /**
952 * Decide if the recently fetched data are still fresh enough
953 *
954 * @param int $now current timestamp
955 * @return bool true if no need to re-fetch, false otherwise
956 */
957 protected function cron_has_fresh_fetch($now) {
958 $recent = $this->get_last_timefetched();
959
960 if (empty($recent)) {
961 return false;
962 }
963
964 if ($now < $recent) {
965 $this->cron_mtrace('The most recent fetch is reported to be in the future, this is weird!');
966 return true;
967 }
968
969 if ($now - $recent > HOURSECS) {
970 return false;
971 }
972
973 return true;
974 }
975
976 /**
977 * Decide if the fetch is outadated or even missing
978 *
979 * @param int $now current timestamp
980 * @return bool false if no need to re-fetch, true otherwise
981 */
982 protected function cron_has_outdated_fetch($now) {
983 $recent = $this->get_last_timefetched();
984
985 if (empty($recent)) {
986 return true;
987 }
988
989 if ($now < $recent) {
990 $this->cron_mtrace('The most recent fetch is reported to be in the future, this is weird!');
991 return false;
992 }
993
994 if ($now - $recent > 48 * HOURSECS) {
995 return true;
996 }
997
998 return false;
999 }
1000
1001 /**
1002 * Returns the cron execution offset for this site
1003 *
1004 * The main {@link self::cron()} is supposed to run every night in some random time
1005 * between 01:00 and 06:00 AM (local time). The exact moment is defined by so called
1006 * execution offset, that is the amount of time after 01:00 AM. The offset value is
1007 * initially generated randomly and then used consistently at the site. This way, the
1008 * regular checks against the download.moodle.org server are spread in time.
1009 *
1010 * @return int the offset number of seconds from range 1 sec to 5 hours
1011 */
1012 protected function cron_execution_offset() {
1013 global $CFG;
1014
1015 if (empty($CFG->updatecronoffset)) {
1016 set_config('updatecronoffset', rand(1, 5 * HOURSECS));
1017 }
1018
1019 return $CFG->updatecronoffset;
1020 }
1021
1022 /**
1023 * Fetch available updates info and eventually send notification to site admins
1024 */
1025 protected function cron_execute() {
1026 // todo
1027 }
cd0bb55f
DM
1028}
1029
1030
7d8de6d8
DM
1031/**
1032 * Defines the structure of objects returned by {@link available_update_checker::get_update_info()}
1033 */
1034class available_update_info {
1035
1036 /** @var string frankenstyle component name */
1037 public $component;
1038 /** @var int the available version of the component */
1039 public $version;
1040 /** @var string|null optional release name */
1041 public $release = null;
1042 /** @var int|null optional maturity info, eg {@link MATURITY_STABLE} */
1043 public $maturity = null;
1044 /** @var string|null optional URL of a page with more info about the update */
1045 public $url = null;
1046 /** @var string|null optional URL of a ZIP package that can be downloaded and installed */
1047 public $download = null;
1048
1049 /**
1050 * Creates new instance of the class
1051 *
1052 * The $info array must provide at least the 'version' value and optionally all other
1053 * values to populate the object's properties.
1054 *
1055 * @param string $name the frankenstyle component name
1056 * @param array $info associative array with other properties
1057 */
1058 public function __construct($name, array $info) {
1059 $this->component = $name;
1060 foreach ($info as $k => $v) {
1061 if (property_exists('available_update_info', $k) and $k != 'component') {
1062 $this->$k = $v;
1063 }
1064 }
1065 }
1066}
1067
1068
00ef3c3e
DM
1069/**
1070 * Factory class producing required subclasses of {@link plugininfo_base}
1071 */
1072class plugininfo_default_factory {
1073
1074 /**
1075 * Makes a new instance of the plugininfo class
1076 *
1077 * @param string $type the plugin type, eg. 'mod'
1078 * @param string $typerootdir full path to the location of all the plugins of this type
1079 * @param string $name the plugin name, eg. 'workshop'
1080 * @param string $namerootdir full path to the location of the plugin
1081 * @param string $typeclass the name of class that holds the info about the plugin
1082 * @return plugininfo_base the instance of $typeclass
1083 */
1084 public static function make($type, $typerootdir, $name, $namerootdir, $typeclass) {
1085 $plugin = new $typeclass();
1086 $plugin->type = $type;
1087 $plugin->typerootdir = $typerootdir;
1088 $plugin->name = $name;
1089 $plugin->rootdir = $namerootdir;
1090
1091 $plugin->init_display_name();
1092 $plugin->load_disk_version();
1093 $plugin->load_db_version();
1094 $plugin->load_required_main_version();
1095 $plugin->init_is_standard();
1096
1097 return $plugin;
1098 }
1099}
1100
1101
b9934a17 1102/**
b6ad8594 1103 * Base class providing access to the information about a plugin
828788f0
TH
1104 *
1105 * @property-read string component the component name, type_name
b9934a17 1106 */
b6ad8594 1107abstract class plugininfo_base {
b9934a17
DM
1108
1109 /** @var string the plugintype name, eg. mod, auth or workshopform */
1110 public $type;
1111 /** @var string full path to the location of all the plugins of this type */
1112 public $typerootdir;
1113 /** @var string the plugin name, eg. assignment, ldap */
1114 public $name;
1115 /** @var string the localized plugin name */
1116 public $displayname;
1117 /** @var string the plugin source, one of plugin_manager::PLUGIN_SOURCE_xxx constants */
1118 public $source;
1119 /** @var fullpath to the location of this plugin */
1120 public $rootdir;
1121 /** @var int|string the version of the plugin's source code */
1122 public $versiondisk;
1123 /** @var int|string the version of the installed plugin */
1124 public $versiondb;
1125 /** @var int|float|string required version of Moodle core */
1126 public $versionrequires;
b6ad8594
DM
1127 /** @var array other plugins that this one depends on, lazy-loaded by {@link get_other_required_plugins()} */
1128 public $dependencies;
b9934a17
DM
1129 /** @var int number of instances of the plugin - not supported yet */
1130 public $instances;
1131 /** @var int order of the plugin among other plugins of the same type - not supported yet */
1132 public $sortorder;
7d8de6d8
DM
1133 /** @var array|null array of {@link available_update_info} for this plugin */
1134 public $availableupdates;
b9934a17
DM
1135
1136 /**
b6ad8594
DM
1137 * Gathers and returns the information about all plugins of the given type
1138 *
b6ad8594
DM
1139 * @param string $type the name of the plugintype, eg. mod, auth or workshopform
1140 * @param string $typerootdir full path to the location of the plugin dir
1141 * @param string $typeclass the name of the actually called class
1142 * @return array of plugintype classes, indexed by the plugin name
b9934a17
DM
1143 */
1144 public static function get_plugins($type, $typerootdir, $typeclass) {
1145
1146 // get the information about plugins at the disk
1147 $plugins = get_plugin_list($type);
1148 $ondisk = array();
1149 foreach ($plugins as $pluginname => $pluginrootdir) {
00ef3c3e
DM
1150 $ondisk[$pluginname] = plugininfo_default_factory::make($type, $typerootdir,
1151 $pluginname, $pluginrootdir, $typeclass);
b9934a17
DM
1152 }
1153 return $ondisk;
1154 }
1155
1156 /**
b6ad8594 1157 * Sets {@link $displayname} property to a localized name of the plugin
b9934a17 1158 */
b8343e68 1159 public function init_display_name() {
828788f0
TH
1160 if (!get_string_manager()->string_exists('pluginname', $this->component)) {
1161 $this->displayname = '[pluginname,' . $this->component . ']';
b9934a17 1162 } else {
828788f0
TH
1163 $this->displayname = get_string('pluginname', $this->component);
1164 }
1165 }
1166
1167 /**
1168 * Magic method getter, redirects to read only values.
b6ad8594 1169 *
828788f0
TH
1170 * @param string $name
1171 * @return mixed
1172 */
1173 public function __get($name) {
1174 switch ($name) {
1175 case 'component': return $this->type . '_' . $this->name;
1176
1177 default:
1178 debugging('Invalid plugin property accessed! '.$name);
1179 return null;
b9934a17
DM
1180 }
1181 }
1182
1183 /**
b6ad8594
DM
1184 * Return the full path name of a file within the plugin.
1185 *
1186 * No check is made to see if the file exists.
1187 *
1188 * @param string $relativepath e.g. 'version.php'.
1189 * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
b9934a17 1190 */
473289a0 1191 public function full_path($relativepath) {
b9934a17 1192 if (empty($this->rootdir)) {
473289a0 1193 return '';
b9934a17 1194 }
473289a0
TH
1195 return $this->rootdir . '/' . $relativepath;
1196 }
b9934a17 1197
473289a0
TH
1198 /**
1199 * Load the data from version.php.
b6ad8594
DM
1200 *
1201 * @return stdClass the object called $plugin defined in version.php
473289a0
TH
1202 */
1203 protected function load_version_php() {
1204 $versionfile = $this->full_path('version.php');
b9934a17 1205
473289a0 1206 $plugin = new stdClass();
b9934a17
DM
1207 if (is_readable($versionfile)) {
1208 include($versionfile);
b9934a17 1209 }
473289a0 1210 return $plugin;
b9934a17
DM
1211 }
1212
1213 /**
b6ad8594
DM
1214 * Sets {@link $versiondisk} property to a numerical value representing the
1215 * version of the plugin's source code.
1216 *
1217 * If the value is null after calling this method, either the plugin
1218 * does not use versioning (typically does not have any database
1219 * data) or is missing from disk.
b9934a17 1220 */
473289a0
TH
1221 public function load_disk_version() {
1222 $plugin = $this->load_version_php();
1223 if (isset($plugin->version)) {
1224 $this->versiondisk = $plugin->version;
b9934a17
DM
1225 }
1226 }
1227
1228 /**
b6ad8594
DM
1229 * Sets {@link $versionrequires} property to a numerical value representing
1230 * the version of Moodle core that this plugin requires.
b9934a17 1231 */
b8343e68 1232 public function load_required_main_version() {
473289a0
TH
1233 $plugin = $this->load_version_php();
1234 if (isset($plugin->requires)) {
1235 $this->versionrequires = $plugin->requires;
b9934a17 1236 }
473289a0 1237 }
b9934a17 1238
0242bdc7 1239 /**
777781d1 1240 * Initialise {@link $dependencies} to the list of other plugins (in any)
0242bdc7
TH
1241 * that this one requires to be installed.
1242 */
1243 protected function load_other_required_plugins() {
1244 $plugin = $this->load_version_php();
777781d1
TH
1245 if (!empty($plugin->dependencies)) {
1246 $this->dependencies = $plugin->dependencies;
0242bdc7 1247 } else {
777781d1 1248 $this->dependencies = array(); // By default, no dependencies.
0242bdc7
TH
1249 }
1250 }
1251
1252 /**
b6ad8594
DM
1253 * Get the list of other plugins that this plugin requires to be installed.
1254 *
1255 * @return array with keys the frankenstyle plugin name, and values either
1256 * a version string (like '2011101700') or the constant ANY_VERSION.
0242bdc7
TH
1257 */
1258 public function get_other_required_plugins() {
777781d1 1259 if (is_null($this->dependencies)) {
0242bdc7
TH
1260 $this->load_other_required_plugins();
1261 }
777781d1 1262 return $this->dependencies;
0242bdc7
TH
1263 }
1264
473289a0 1265 /**
b6ad8594
DM
1266 * Sets {@link $versiondb} property to a numerical value representing the
1267 * currently installed version of the plugin.
1268 *
1269 * If the value is null after calling this method, either the plugin
1270 * does not use versioning (typically does not have any database
1271 * data) or has not been installed yet.
473289a0
TH
1272 */
1273 public function load_db_version() {
828788f0 1274 if ($ver = self::get_version_from_config_plugins($this->component)) {
473289a0 1275 $this->versiondb = $ver;
b9934a17
DM
1276 }
1277 }
1278
1279 /**
b6ad8594
DM
1280 * Sets {@link $source} property to one of plugin_manager::PLUGIN_SOURCE_xxx
1281 * constants.
1282 *
1283 * If the property's value is null after calling this method, then
1284 * the type of the plugin has not been recognized and you should throw
1285 * an exception.
b9934a17 1286 */
b8343e68 1287 public function init_is_standard() {
b9934a17
DM
1288
1289 $standard = plugin_manager::standard_plugins_list($this->type);
1290
1291 if ($standard !== false) {
1292 $standard = array_flip($standard);
1293 if (isset($standard[$this->name])) {
1294 $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD;
ec8935f5
PS
1295 } else if (!is_null($this->versiondb) and is_null($this->versiondisk)
1296 and plugin_manager::is_deleted_standard_plugin($this->type, $this->name)) {
1297 $this->source = plugin_manager::PLUGIN_SOURCE_STANDARD; // to be deleted
b9934a17
DM
1298 } else {
1299 $this->source = plugin_manager::PLUGIN_SOURCE_EXTENSION;
1300 }
1301 }
1302 }
1303
1304 /**
b6ad8594
DM
1305 * Returns true if the plugin is shipped with the official distribution
1306 * of the current Moodle version, false otherwise.
1307 *
1308 * @return bool
b9934a17
DM
1309 */
1310 public function is_standard() {
1311 return $this->source === plugin_manager::PLUGIN_SOURCE_STANDARD;
1312 }
1313
1314 /**
b6ad8594
DM
1315 * Returns the status of the plugin
1316 *
1317 * @return string one of plugin_manager::PLUGIN_STATUS_xxx constants
b9934a17
DM
1318 */
1319 public function get_status() {
1320
1321 if (is_null($this->versiondb) and is_null($this->versiondisk)) {
1322 return plugin_manager::PLUGIN_STATUS_NODB;
1323
1324 } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
1325 return plugin_manager::PLUGIN_STATUS_NEW;
1326
1327 } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
ec8935f5
PS
1328 if (plugin_manager::is_deleted_standard_plugin($this->type, $this->name)) {
1329 return plugin_manager::PLUGIN_STATUS_DELETE;
1330 } else {
1331 return plugin_manager::PLUGIN_STATUS_MISSING;
1332 }
b9934a17
DM
1333
1334 } else if ((string)$this->versiondb === (string)$this->versiondisk) {
1335 return plugin_manager::PLUGIN_STATUS_UPTODATE;
1336
1337 } else if ($this->versiondb < $this->versiondisk) {
1338 return plugin_manager::PLUGIN_STATUS_UPGRADE;
1339
1340 } else if ($this->versiondb > $this->versiondisk) {
1341 return plugin_manager::PLUGIN_STATUS_DOWNGRADE;
1342
1343 } else {
1344 // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
1345 throw new coding_exception('Unable to determine plugin state, check the plugin versions');
1346 }
1347 }
1348
1349 /**
b6ad8594
DM
1350 * Returns the information about plugin availability
1351 *
1352 * True means that the plugin is enabled. False means that the plugin is
1353 * disabled. Null means that the information is not available, or the
1354 * plugin does not support configurable availability or the availability
1355 * can not be changed.
1356 *
1357 * @return null|bool
b9934a17
DM
1358 */
1359 public function is_enabled() {
1360 return null;
1361 }
1362
dd119e21 1363 /**
7d8de6d8 1364 * Populates the property {@link $availableupdates} with the information provided by
dd119e21
DM
1365 * available update checker
1366 *
1367 * @param available_update_checker $provider the class providing the available update info
1368 */
7d8de6d8
DM
1369 public function check_available_updates(available_update_checker $provider) {
1370 $this->availableupdates = $provider->get_update_info($this->component);
dd119e21
DM
1371 }
1372
d26f3ddd 1373 /**
7d8de6d8 1374 * If there are updates for this plugin available, returns them.
d26f3ddd 1375 *
7d8de6d8
DM
1376 * Returns array of {@link available_update_info} objects, if some update
1377 * is available. Returns null if there is no update available or if the update
1378 * availability is unknown.
d26f3ddd 1379 *
7d8de6d8 1380 * @return array|null
d26f3ddd 1381 */
7d8de6d8 1382 public function available_updates() {
9bdedf32 1383 global $CFG;
dd119e21 1384
7d8de6d8 1385 if (empty($this->availableupdates) or !is_array($this->availableupdates)) {
dd119e21
DM
1386 return null;
1387 }
1388
9bdedf32
DM
1389 if (!isset($CFG->updateminmaturity)) {
1390 // this may happen during the very first upgrade to 2.3
1391 $CFG->updateminmaturity = MATURITY_STABLE;
1392 }
1393
7d8de6d8
DM
1394 $updates = array();
1395
1396 foreach ($this->availableupdates as $availableupdate) {
9bdedf32
DM
1397 if (isset($availableupdate->maturity) and $availableupdate->maturity < $CFG->updateminmaturity) {
1398 continue;
1399 }
7d8de6d8
DM
1400 if ($availableupdate->version > $this->versiondisk) {
1401 $updates[] = $availableupdate;
1402 }
1403 }
1404
1405 if (empty($updates)) {
1406 return null;
dd119e21
DM
1407 }
1408
7d8de6d8 1409 return $updates;
d26f3ddd
DM
1410 }
1411
b9934a17 1412 /**
b6ad8594
DM
1413 * Returns the URL of the plugin settings screen
1414 *
1415 * Null value means that the plugin either does not have the settings screen
1416 * or its location is not available via this library.
1417 *
1418 * @return null|moodle_url
b9934a17
DM
1419 */
1420 public function get_settings_url() {
1421 return null;
1422 }
1423
1424 /**
b6ad8594
DM
1425 * Returns the URL of the screen where this plugin can be uninstalled
1426 *
1427 * Visiting that URL must be safe, that is a manual confirmation is needed
1428 * for actual uninstallation of the plugin. Null value means that the
1429 * plugin either does not support uninstallation, or does not require any
1430 * database cleanup or the location of the screen is not available via this
1431 * library.
1432 *
1433 * @return null|moodle_url
b9934a17
DM
1434 */
1435 public function get_uninstall_url() {
1436 return null;
1437 }
1438
1439 /**
b6ad8594
DM
1440 * Returns relative directory of the plugin with heading '/'
1441 *
1442 * @return string
b9934a17
DM
1443 */
1444 public function get_dir() {
1445 global $CFG;
1446
1447 return substr($this->rootdir, strlen($CFG->dirroot));
1448 }
1449
1450 /**
1451 * Provides access to plugin versions from {config_plugins}
1452 *
1453 * @param string $plugin plugin name
1454 * @param double $disablecache optional, defaults to false
1455 * @return int|false the stored value or false if not found
1456 */
1457 protected function get_version_from_config_plugins($plugin, $disablecache=false) {
1458 global $DB;
1459 static $pluginversions = null;
1460
1461 if (is_null($pluginversions) or $disablecache) {
f433088d
PS
1462 try {
1463 $pluginversions = $DB->get_records_menu('config_plugins', array('name' => 'version'), 'plugin', 'plugin,value');
1464 } catch (dml_exception $e) {
1465 // before install
1466 $pluginversions = array();
1467 }
b9934a17
DM
1468 }
1469
1470 if (!array_key_exists($plugin, $pluginversions)) {
1471 return false;
1472 }
1473
1474 return $pluginversions[$plugin];
1475 }
1476}
1477
b6ad8594 1478
b9934a17
DM
1479/**
1480 * General class for all plugin types that do not have their own class
1481 */
b6ad8594 1482class plugininfo_general extends plugininfo_base {
b9934a17
DM
1483}
1484
b6ad8594 1485
b9934a17
DM
1486/**
1487 * Class for page side blocks
1488 */
b6ad8594 1489class plugininfo_block extends plugininfo_base {
b9934a17 1490
b9934a17
DM
1491 public static function get_plugins($type, $typerootdir, $typeclass) {
1492
1493 // get the information about blocks at the disk
1494 $blocks = parent::get_plugins($type, $typerootdir, $typeclass);
1495
1496 // add blocks missing from disk
1497 $blocksinfo = self::get_blocks_info();
1498 foreach ($blocksinfo as $blockname => $blockinfo) {
1499 if (isset($blocks[$blockname])) {
1500 continue;
1501 }
1502 $plugin = new $typeclass();
1503 $plugin->type = $type;
1504 $plugin->typerootdir = $typerootdir;
1505 $plugin->name = $blockname;
1506 $plugin->rootdir = null;
1507 $plugin->displayname = $blockname;
1508 $plugin->versiondb = $blockinfo->version;
b8343e68 1509 $plugin->init_is_standard();
b9934a17
DM
1510
1511 $blocks[$blockname] = $plugin;
1512 }
1513
1514 return $blocks;
1515 }
1516
b8343e68 1517 public function init_display_name() {
b9934a17
DM
1518
1519 if (get_string_manager()->string_exists('pluginname', 'block_' . $this->name)) {
1520 $this->displayname = get_string('pluginname', 'block_' . $this->name);
1521
1522 } else if (($block = block_instance($this->name)) !== false) {
1523 $this->displayname = $block->get_title();
1524
1525 } else {
b8343e68 1526 parent::init_display_name();
b9934a17
DM
1527 }
1528 }
1529
b8343e68 1530 public function load_db_version() {
b9934a17
DM
1531 global $DB;
1532
1533 $blocksinfo = self::get_blocks_info();
1534 if (isset($blocksinfo[$this->name]->version)) {
1535 $this->versiondb = $blocksinfo[$this->name]->version;
1536 }
1537 }
1538
b9934a17
DM
1539 public function is_enabled() {
1540
1541 $blocksinfo = self::get_blocks_info();
1542 if (isset($blocksinfo[$this->name]->visible)) {
1543 if ($blocksinfo[$this->name]->visible) {
1544 return true;
1545 } else {
1546 return false;
1547 }
1548 } else {
1549 return parent::is_enabled();
1550 }
1551 }
1552
b9934a17
DM
1553 public function get_settings_url() {
1554
1555 if (($block = block_instance($this->name)) === false) {
1556 return parent::get_settings_url();
1557
1558 } else if ($block->has_config()) {
6740c605 1559 if (file_exists($this->full_path('settings.php'))) {
b9934a17
DM
1560 return new moodle_url('/admin/settings.php', array('section' => 'blocksetting' . $this->name));
1561 } else {
1562 $blocksinfo = self::get_blocks_info();
1563 return new moodle_url('/admin/block.php', array('block' => $blocksinfo[$this->name]->id));
1564 }
1565
1566 } else {
1567 return parent::get_settings_url();
1568 }
1569 }
1570
b9934a17
DM
1571 public function get_uninstall_url() {
1572
1573 $blocksinfo = self::get_blocks_info();
1574 return new moodle_url('/admin/blocks.php', array('delete' => $blocksinfo[$this->name]->id, 'sesskey' => sesskey()));
1575 }
1576
1577 /**
1578 * Provides access to the records in {block} table
1579 *
1580 * @param bool $disablecache do not use internal static cache
1581 * @return array array of stdClasses
1582 */
1583 protected static function get_blocks_info($disablecache=false) {
1584 global $DB;
1585 static $blocksinfocache = null;
1586
1587 if (is_null($blocksinfocache) or $disablecache) {
f433088d
PS
1588 try {
1589 $blocksinfocache = $DB->get_records('block', null, 'name', 'name,id,version,visible');
1590 } catch (dml_exception $e) {
1591 // before install
1592 $blocksinfocache = array();
1593 }
b9934a17
DM
1594 }
1595
1596 return $blocksinfocache;
1597 }
1598}
1599
b6ad8594 1600
b9934a17
DM
1601/**
1602 * Class for text filters
1603 */
b6ad8594 1604class plugininfo_filter extends plugininfo_base {
b9934a17 1605
b9934a17 1606 public static function get_plugins($type, $typerootdir, $typeclass) {
7c9b837e 1607 global $CFG, $DB;
b9934a17
DM
1608
1609 $filters = array();
1610
1611 // get the list of filters from both /filter and /mod location
1612 $installed = filter_get_all_installed();
1613
1614 foreach ($installed as $filterlegacyname => $displayname) {
1615 $plugin = new $typeclass();
1616 $plugin->type = $type;
1617 $plugin->typerootdir = $typerootdir;
1618 $plugin->name = self::normalize_legacy_name($filterlegacyname);
1619 $plugin->rootdir = $CFG->dirroot . '/' . $filterlegacyname;
1620 $plugin->displayname = $displayname;
1621
b8343e68
TH
1622 $plugin->load_disk_version();
1623 $plugin->load_db_version();
1624 $plugin->load_required_main_version();
1625 $plugin->init_is_standard();
b9934a17
DM
1626
1627 $filters[$plugin->name] = $plugin;
1628 }
1629
b9934a17 1630 $globalstates = self::get_global_states();
7c9b837e
DM
1631
1632 if ($DB->get_manager()->table_exists('filter_active')) {
1633 // if we're upgrading from 1.9, the table does not exist yet
1634 // if it does, make sure that all installed filters are registered
1635 $needsreload = false;
1636 foreach (array_keys($installed) as $filterlegacyname) {
1637 if (!isset($globalstates[self::normalize_legacy_name($filterlegacyname)])) {
1638 filter_set_global_state($filterlegacyname, TEXTFILTER_DISABLED);
1639 $needsreload = true;
1640 }
1641 }
1642 if ($needsreload) {
1643 $globalstates = self::get_global_states(true);
b9934a17 1644 }
b9934a17
DM
1645 }
1646
1647 // make sure that all registered filters are installed, just in case
1648 foreach ($globalstates as $name => $info) {
1649 if (!isset($filters[$name])) {
1650 // oops, there is a record in filter_active but the filter is not installed
1651 $plugin = new $typeclass();
1652 $plugin->type = $type;
1653 $plugin->typerootdir = $typerootdir;
1654 $plugin->name = $name;
1655 $plugin->rootdir = $CFG->dirroot . '/' . $info->legacyname;
1656 $plugin->displayname = $info->legacyname;
1657
b8343e68 1658 $plugin->load_db_version();
b9934a17
DM
1659
1660 if (is_null($plugin->versiondb)) {
1661 // this is a hack to stimulate 'Missing from disk' error
1662 // because $plugin->versiondisk will be null !== false
1663 $plugin->versiondb = false;
1664 }
1665
1666 $filters[$plugin->name] = $plugin;
1667 }
1668 }
1669
1670 return $filters;
1671 }
1672
b8343e68 1673 public function init_display_name() {
b9934a17
DM
1674 // do nothing, the name is set in self::get_plugins()
1675 }
1676
1677 /**
b6ad8594 1678 * @see load_version_php()
b9934a17 1679 */
473289a0 1680 protected function load_version_php() {
b9934a17 1681 if (strpos($this->name, 'mod_') === 0) {
473289a0
TH
1682 // filters bundled with modules do not have a version.php and so
1683 // do not provide their own versioning information.
1684 return new stdClass();
b9934a17 1685 }
473289a0 1686 return parent::load_version_php();
b9934a17
DM
1687 }
1688
b9934a17
DM
1689 public function is_enabled() {
1690
1691 $globalstates = self::get_global_states();
1692
1693 foreach ($globalstates as $filterlegacyname => $info) {
1694 $name = self::normalize_legacy_name($filterlegacyname);
1695 if ($name === $this->name) {
1696 if ($info->active == TEXTFILTER_DISABLED) {
1697 return false;
1698 } else {
1699 // it may be 'On' or 'Off, but available'
1700 return null;
1701 }
1702 }
1703 }
1704
1705 return null;
1706 }
1707
b9934a17
DM
1708 public function get_settings_url() {
1709
1710 $globalstates = self::get_global_states();
1711 $legacyname = $globalstates[$this->name]->legacyname;
1712 if (filter_has_global_settings($legacyname)) {
1713 return new moodle_url('/admin/settings.php', array('section' => 'filtersetting' . str_replace('/', '', $legacyname)));
1714 } else {
1715 return null;
1716 }
1717 }
1718
b9934a17
DM
1719 public function get_uninstall_url() {
1720
1721 if (strpos($this->name, 'mod_') === 0) {
1722 return null;
1723 } else {
1724 $globalstates = self::get_global_states();
1725 $legacyname = $globalstates[$this->name]->legacyname;
1726 return new moodle_url('/admin/filters.php', array('sesskey' => sesskey(), 'filterpath' => $legacyname, 'action' => 'delete'));
1727 }
1728 }
1729
1730 /**
1731 * Convert legacy filter names like 'filter/foo' or 'mod/bar' into frankenstyle
1732 *
1733 * @param string $legacyfiltername legacy filter name
1734 * @return string frankenstyle-like name
1735 */
1736 protected static function normalize_legacy_name($legacyfiltername) {
1737
1738 $name = str_replace('/', '_', $legacyfiltername);
1739 if (strpos($name, 'filter_') === 0) {
1740 $name = substr($name, 7);
1741 if (empty($name)) {
1742 throw new coding_exception('Unable to determine filter name: ' . $legacyfiltername);
1743 }
1744 }
1745
1746 return $name;
1747 }
1748
1749 /**
1750 * Provides access to the results of {@link filter_get_global_states()}
1751 * but indexed by the normalized filter name
1752 *
1753 * The legacy filter name is available as ->legacyname property.
1754 *
1755 * @param bool $disablecache
1756 * @return array
1757 */
1758 protected static function get_global_states($disablecache=false) {
1759 global $DB;
1760 static $globalstatescache = null;
1761
1762 if ($disablecache or is_null($globalstatescache)) {
1763
1764 if (!$DB->get_manager()->table_exists('filter_active')) {
1765 // we're upgrading from 1.9 and the table used by {@link filter_get_global_states()}
1766 // does not exist yet
1767 $globalstatescache = array();
1768
1769 } else {
1770 foreach (filter_get_global_states() as $legacyname => $info) {
1771 $name = self::normalize_legacy_name($legacyname);
1772 $filterinfo = new stdClass();
1773 $filterinfo->legacyname = $legacyname;
1774 $filterinfo->active = $info->active;
1775 $filterinfo->sortorder = $info->sortorder;
1776 $globalstatescache[$name] = $filterinfo;
1777 }
1778 }
1779 }
1780
1781 return $globalstatescache;
1782 }
1783}
1784
b6ad8594 1785
b9934a17
DM
1786/**
1787 * Class for activity modules
1788 */
b6ad8594 1789class plugininfo_mod extends plugininfo_base {
b9934a17 1790
b9934a17
DM
1791 public static function get_plugins($type, $typerootdir, $typeclass) {
1792
1793 // get the information about plugins at the disk
1794 $modules = parent::get_plugins($type, $typerootdir, $typeclass);
1795
1796 // add modules missing from disk
1797 $modulesinfo = self::get_modules_info();
1798 foreach ($modulesinfo as $modulename => $moduleinfo) {
1799 if (isset($modules[$modulename])) {
1800 continue;
1801 }
1802 $plugin = new $typeclass();
1803 $plugin->type = $type;
1804 $plugin->typerootdir = $typerootdir;
1805 $plugin->name = $modulename;
1806 $plugin->rootdir = null;
1807 $plugin->displayname = $modulename;
1808 $plugin->versiondb = $moduleinfo->version;
b8343e68 1809 $plugin->init_is_standard();
b9934a17
DM
1810
1811 $modules[$modulename] = $plugin;
1812 }
1813
1814 return $modules;
1815 }
1816
b8343e68 1817 public function init_display_name() {
828788f0
TH
1818 if (get_string_manager()->string_exists('pluginname', $this->component)) {
1819 $this->displayname = get_string('pluginname', $this->component);
b9934a17 1820 } else {
828788f0 1821 $this->displayname = get_string('modulename', $this->component);
b9934a17
DM
1822 }
1823 }
1824
1825 /**
473289a0
TH
1826 * Load the data from version.php.
1827 * @return object the data object defined in version.php.
b9934a17 1828 */
473289a0
TH
1829 protected function load_version_php() {
1830 $versionfile = $this->full_path('version.php');
b9934a17 1831
473289a0 1832 $module = new stdClass();
b9934a17
DM
1833 if (is_readable($versionfile)) {
1834 include($versionfile);
b9934a17 1835 }
473289a0 1836 return $module;
b9934a17
DM
1837 }
1838
b8343e68 1839 public function load_db_version() {
b9934a17
DM
1840 global $DB;
1841
1842 $modulesinfo = self::get_modules_info();
1843 if (isset($modulesinfo[$this->name]->version)) {
1844 $this->versiondb = $modulesinfo[$this->name]->version;
1845 }
1846 }
1847
b9934a17
DM
1848 public function is_enabled() {
1849
1850 $modulesinfo = self::get_modules_info();
1851 if (isset($modulesinfo[$this->name]->visible)) {
1852 if ($modulesinfo[$this->name]->visible) {
1853 return true;
1854 } else {
1855 return false;
1856 }
1857 } else {
1858 return parent::is_enabled();
1859 }
1860 }
1861
b9934a17
DM
1862 public function get_settings_url() {
1863
6740c605 1864 if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
b9934a17
DM
1865 return new moodle_url('/admin/settings.php', array('section' => 'modsetting' . $this->name));
1866 } else {
1867 return parent::get_settings_url();
1868 }
1869 }
1870
b9934a17
DM
1871 public function get_uninstall_url() {
1872
1873 if ($this->name !== 'forum') {
1874 return new moodle_url('/admin/modules.php', array('delete' => $this->name, 'sesskey' => sesskey()));
1875 } else {
1876 return null;
1877 }
1878 }
1879
1880 /**
1881 * Provides access to the records in {modules} table
1882 *
1883 * @param bool $disablecache do not use internal static cache
1884 * @return array array of stdClasses
1885 */
1886 protected static function get_modules_info($disablecache=false) {
1887 global $DB;
1888 static $modulesinfocache = null;
1889
1890 if (is_null($modulesinfocache) or $disablecache) {
f433088d
PS
1891 try {
1892 $modulesinfocache = $DB->get_records('modules', null, 'name', 'name,id,version,visible');
1893 } catch (dml_exception $e) {
1894 // before install
1895 $modulesinfocache = array();
1896 }
b9934a17
DM
1897 }
1898
1899 return $modulesinfocache;
1900 }
1901}
1902
0242bdc7
TH
1903
1904/**
1905 * Class for question behaviours.
1906 */
b6ad8594
DM
1907class plugininfo_qbehaviour extends plugininfo_base {
1908
828788f0
TH
1909 public function get_uninstall_url() {
1910 return new moodle_url('/admin/qbehaviours.php',
1911 array('delete' => $this->name, 'sesskey' => sesskey()));
1912 }
0242bdc7
TH
1913}
1914
1915
b9934a17
DM
1916/**
1917 * Class for question types
1918 */
b6ad8594
DM
1919class plugininfo_qtype extends plugininfo_base {
1920
828788f0
TH
1921 public function get_uninstall_url() {
1922 return new moodle_url('/admin/qtypes.php',
1923 array('delete' => $this->name, 'sesskey' => sesskey()));
1924 }
b9934a17
DM
1925}
1926
b9934a17
DM
1927
1928/**
1929 * Class for authentication plugins
1930 */
b6ad8594 1931class plugininfo_auth extends plugininfo_base {
b9934a17 1932
b9934a17
DM
1933 public function is_enabled() {
1934 global $CFG;
1935 /** @var null|array list of enabled authentication plugins */
1936 static $enabled = null;
1937
1938 if (in_array($this->name, array('nologin', 'manual'))) {
1939 // these two are always enabled and can't be disabled
1940 return null;
1941 }
1942
1943 if (is_null($enabled)) {
d5d181f5 1944 $enabled = array_flip(explode(',', $CFG->auth));
b9934a17
DM
1945 }
1946
1947 return isset($enabled[$this->name]);
1948 }
1949
b9934a17 1950 public function get_settings_url() {
6740c605 1951 if (file_exists($this->full_path('settings.php'))) {
b9934a17
DM
1952 return new moodle_url('/admin/settings.php', array('section' => 'authsetting' . $this->name));
1953 } else {
1954 return new moodle_url('/admin/auth_config.php', array('auth' => $this->name));
1955 }
1956 }
1957}
1958
b6ad8594 1959
b9934a17
DM
1960/**
1961 * Class for enrolment plugins
1962 */
b6ad8594 1963class plugininfo_enrol extends plugininfo_base {
b9934a17 1964
b9934a17
DM
1965 public function is_enabled() {
1966 global $CFG;
1967 /** @var null|array list of enabled enrolment plugins */
1968 static $enabled = null;
1969
b6ad8594
DM
1970 // We do not actually need whole enrolment classes here so we do not call
1971 // {@link enrol_get_plugins()}. Note that this may produce slightly different
1972 // results, for example if the enrolment plugin does not contain lib.php
1973 // but it is listed in $CFG->enrol_plugins_enabled
1974
b9934a17 1975 if (is_null($enabled)) {
d5d181f5 1976 $enabled = array_flip(explode(',', $CFG->enrol_plugins_enabled));
b9934a17
DM
1977 }
1978
1979 return isset($enabled[$this->name]);
1980 }
1981
b9934a17
DM
1982 public function get_settings_url() {
1983
6740c605 1984 if ($this->is_enabled() or file_exists($this->full_path('settings.php'))) {
b9934a17
DM
1985 return new moodle_url('/admin/settings.php', array('section' => 'enrolsettings' . $this->name));
1986 } else {
1987 return parent::get_settings_url();
1988 }
1989 }
1990
b9934a17
DM
1991 public function get_uninstall_url() {
1992 return new moodle_url('/admin/enrol.php', array('action' => 'uninstall', 'enrol' => $this->name, 'sesskey' => sesskey()));
1993 }
1994}
1995
b6ad8594 1996
b9934a17
DM
1997/**
1998 * Class for messaging processors
1999 */
b6ad8594 2000class plugininfo_message extends plugininfo_base {
b9934a17 2001
b9934a17
DM
2002 public function get_settings_url() {
2003
6740c605
TH
2004 if (file_exists($this->full_path('settings.php')) or file_exists($this->full_path('settingstree.php'))) {
2005 return new moodle_url('/admin/settings.php', array('section' => 'messagesetting' . $this->name));
2006 } else {
2007 return parent::get_settings_url();
b9934a17 2008 }
b9934a17
DM
2009 }
2010}
2011
b6ad8594 2012
b9934a17
DM
2013/**
2014 * Class for repositories
2015 */
b6ad8594 2016class plugininfo_repository extends plugininfo_base {
b9934a17 2017
b9934a17
DM
2018 public function is_enabled() {
2019
2020 $enabled = self::get_enabled_repositories();
2021
2022 return isset($enabled[$this->name]);
2023 }
2024
b9934a17
DM
2025 public function get_settings_url() {
2026
2027 if ($this->is_enabled()) {
2028 return new moodle_url('/admin/repository.php', array('sesskey' => sesskey(), 'action' => 'edit', 'repos' => $this->name));
2029 } else {
2030 return parent::get_settings_url();
2031 }
2032 }
2033
2034 /**
2035 * Provides access to the records in {repository} table
2036 *
2037 * @param bool $disablecache do not use internal static cache
2038 * @return array array of stdClasses
2039 */
2040 protected static function get_enabled_repositories($disablecache=false) {
2041 global $DB;
2042 static $repositories = null;
2043
2044 if (is_null($repositories) or $disablecache) {
2045 $repositories = $DB->get_records('repository', null, 'type', 'type,visible,sortorder');
2046 }
2047
2048 return $repositories;
2049 }
2050}
2051
b6ad8594 2052
b9934a17
DM
2053/**
2054 * Class for portfolios
2055 */
b6ad8594 2056class plugininfo_portfolio extends plugininfo_base {
b9934a17 2057
b9934a17
DM
2058 public function is_enabled() {
2059
2060 $enabled = self::get_enabled_portfolios();
2061
2062 return isset($enabled[$this->name]);
2063 }
2064
2065 /**
2066 * Provides access to the records in {portfolio_instance} table
2067 *
2068 * @param bool $disablecache do not use internal static cache
2069 * @return array array of stdClasses
2070 */
2071 protected static function get_enabled_portfolios($disablecache=false) {
2072 global $DB;
2073 static $portfolios = null;
2074
2075 if (is_null($portfolios) or $disablecache) {
2076 $portfolios = array();
2077 $instances = $DB->get_recordset('portfolio_instance', null, 'plugin');
2078 foreach ($instances as $instance) {
2079 if (isset($portfolios[$instance->plugin])) {
2080 if ($instance->visible) {
2081 $portfolios[$instance->plugin]->visible = $instance->visible;
2082 }
2083 } else {
2084 $portfolios[$instance->plugin] = $instance;
2085 }
2086 }
2087 }
2088
2089 return $portfolios;
2090 }
2091}
2092
b6ad8594 2093
b9934a17
DM
2094/**
2095 * Class for themes
2096 */
b6ad8594 2097class plugininfo_theme extends plugininfo_base {
b9934a17 2098
b9934a17
DM
2099 public function is_enabled() {
2100 global $CFG;
2101
2102 if ((!empty($CFG->theme) and $CFG->theme === $this->name) or
2103 (!empty($CFG->themelegacy) and $CFG->themelegacy === $this->name)) {
2104 return true;
2105 } else {
2106 return parent::is_enabled();
2107 }
2108 }
2109}
2110
b6ad8594 2111
b9934a17
DM
2112/**
2113 * Class representing an MNet service
2114 */
b6ad8594 2115class plugininfo_mnetservice extends plugininfo_base {
b9934a17 2116
b9934a17
DM
2117 public function is_enabled() {
2118 global $CFG;
2119
2120 if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
2121 return false;
2122 } else {
2123 return parent::is_enabled();
2124 }
2125 }
2126}
3cdfaeef 2127
b6ad8594 2128
3cdfaeef
PS
2129/**
2130 * Class for admin tool plugins
2131 */
b6ad8594 2132class plugininfo_tool extends plugininfo_base {
3cdfaeef
PS
2133
2134 public function get_uninstall_url() {
2135 return new moodle_url('/admin/tools.php', array('delete' => $this->name, 'sesskey' => sesskey()));
2136 }
2137}
4f6bba20 2138
b6ad8594 2139
4f6bba20
PS
2140/**
2141 * Class for admin tool plugins
2142 */
b6ad8594 2143class plugininfo_report extends plugininfo_base {
4f6bba20
PS
2144
2145 public function get_uninstall_url() {
2146 return new moodle_url('/admin/reports.php', array('delete' => $this->name, 'sesskey' => sesskey()));
2147 }
2148}