MDL-23308, fixed course files tree file download link
[moodle.git] / lib / setuplib.php
CommitLineData
b37eac91 1<?php
d3f9f1f8 2
b37eac91 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/**
20 * These functions are required very early in the Moodle
21 * setup process, before any of the main libraries are
22 * loaded.
30fa50d0 23 *
78bfb562
PS
24 * @package core
25 * @subpackage lib
26 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
b37eac91 28 */
d3f9f1f8 29
78bfb562
PS
30defined('MOODLE_INTERNAL') || die();
31
c84a2dbe 32/// Debug levels ///
33/** no warnings at all */
34define ('DEBUG_NONE', 0);
35/** E_ERROR | E_PARSE */
36define ('DEBUG_MINIMAL', 5);
37/** E_ERROR | E_PARSE | E_WARNING | E_NOTICE */
38define ('DEBUG_NORMAL', 15);
39/** E_ALL without E_STRICT for now, do show recoverable fatal errors */
40define ('DEBUG_ALL', 6143);
41/** DEBUG_ALL with extra Moodle debug messages - (DEBUG_ALL | 32768) */
42define ('DEBUG_DEVELOPER', 38911);
43
6a525ce2 44/**
b08b3569 45 * Simple class. It is usually used instead of stdClass because it looks
1387fcdd 46 * more familiar to Java developers ;-) Do not use for type checking of
b08b3569 47 * function parameters.
b37eac91 48 *
47d2216e
PS
49 * @package core
50 * @subpackage lib
51 * @copyright 2009 Petr Skoda {@link http://skodak.org}
52 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6a525ce2 53 */
b08b3569 54class object extends stdClass {};
6a525ce2 55
251387d0 56/**
57 * Base Moodle Exception class
b37eac91 58 *
c84a2dbe 59 * Although this class is defined here, you cannot throw a moodle_exception until
60 * after moodlelib.php has been included (which will happen very soon).
61 *
47d2216e
PS
62 * @package core
63 * @subpackage lib
64 * @copyright 2008 Petr Skoda {@link http://skodak.org}
65 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
251387d0 66 */
67class moodle_exception extends Exception {
68 public $errorcode;
69 public $module;
70 public $a;
71 public $link;
eee5d9bb 72 public $debuginfo;
251387d0 73
74 /**
75 * Constructor
76 * @param string $errorcode The name of the string from error.php to print
77 * @param string $module name of module
78 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
79 * @param object $a Extra words and phrases that might be required in the error string
eee5d9bb 80 * @param string $debuginfo optional debugging information
251387d0 81 */
5ca18631 82 function __construct($errorcode, $module='', $link='', $a=NULL, $debuginfo=null) {
83 if (empty($module) || $module == 'moodle' || $module == 'core') {
251387d0 84 $module = 'error';
85 }
86
5ca18631 87 $this->errorcode = $errorcode;
88 $this->module = $module;
89 $this->link = $link;
90 $this->a = $a;
91 $this->debuginfo = $debuginfo;
251387d0 92
4f6be42c
DM
93 if (get_string_manager()->string_exists($errorcode, $module)) {
94 $message = get_string($errorcode, $module, $a);
95 } else {
96 $message = $module . '/' . $errorcode;
97 }
251387d0 98
99 parent::__construct($message, 0);
100 }
101}
102
df997f84
PS
103/**
104 * Course/activity access exception.
105 *
106 * This exception is thrown from require_login()
47d2216e
PS
107 *
108 * @package core
109 * @subpackage lib
110 * @copyright 2010 Petr Skoda {@link http://skodak.org}
111 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
112 */
113class require_login_exception extends moodle_exception {
114 function __construct($debuginfo) {
115 parent::__construct('requireloginerror', 'error', '', NULL, $debuginfo);
116 }
117}
118
559a5dbd 119/**
120 * Web service parameter exception class
121 *
122 * This exception must be thrown to the web service client when a web service parameter is invalid
123 * The error string is gotten from webservice.php
124 */
125class webservice_parameter_exception extends moodle_exception {
126 /**
127 * Constructor
128 * @param string $errorcode The name of the string from webservice.php to print
129 * @param string $a The name of the parameter
130 */
131 function __construct($errorcode=null, $a = '') {
132 parent::__construct($errorcode, 'webservice', '', $a, null);
133 }
134}
135
9a0df45a 136/**
137 * Exceptions indicating user does not have permissions to do something
138 * and the execution can not continue.
47d2216e
PS
139 *
140 * @package core
141 * @subpackage lib
142 * @copyright 2009 Petr Skoda {@link http://skodak.org}
143 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9a0df45a 144 */
145class required_capability_exception extends moodle_exception {
146 function __construct($context, $capability, $errormessage, $stringfile) {
147 $capabilityname = get_capability_string($capability);
148 parent::__construct($errormessage, $stringfile, get_context_url($context), $capabilityname);
149 }
150}
151
655bbf51 152/**
cce1b0b9 153 * Exception indicating programming error, must be fixed by a programer. For example
154 * a core API might throw this type of exception if a plugin calls it incorrectly.
b37eac91 155 *
47d2216e
PS
156 * @package core
157 * @subpackage lib
158 * @copyright 2008 Petr Skoda {@link http://skodak.org}
159 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
655bbf51 160 */
161class coding_exception extends moodle_exception {
655bbf51 162 /**
163 * Constructor
164 * @param string $hint short description of problem
165 * @param string $debuginfo detailed information how to fix problem
166 */
167 function __construct($hint, $debuginfo=null) {
168 parent::__construct('codingerror', 'debug', '', $hint, $debuginfo);
a3f7cbf6 169 }
170}
171
172/**
173 * Exception indicating malformed parameter problem.
174 * This exception is not supposed to be thrown when processing
175 * user submitted data in forms. It is more suitable
176 * for WS and other low level stuff.
47d2216e
PS
177 *
178 * @package core
179 * @subpackage lib
180 * @copyright 2009 Petr Skoda {@link http://skodak.org}
181 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
a3f7cbf6 182 */
183class invalid_parameter_exception extends moodle_exception {
184 /**
185 * Constructor
186 * @param string $debuginfo some detailed information
187 */
188 function __construct($debuginfo=null) {
189 parent::__construct('invalidparameter', 'debug', '', null, $debuginfo);
655bbf51 190 }
191}
192
d07ff72d 193/**
194 * Exception indicating malformed response problem.
195 * This exception is not supposed to be thrown when processing
196 * user submitted data in forms. It is more suitable
197 * for WS and other low level stuff.
198 */
199class invalid_response_exception extends moodle_exception {
200 /**
201 * Constructor
202 * @param string $debuginfo some detailed information
203 */
204 function __construct($debuginfo=null) {
205 parent::__construct('invalidresponse', 'debug', '', null, $debuginfo);
206 }
207}
208
cce1b0b9 209/**
1387fcdd 210 * An exception that indicates something really weird happened. For example,
cce1b0b9 211 * if you do switch ($context->contextlevel), and have one case for each
212 * CONTEXT_... constant. You might throw an invalid_state_exception in the
f630a546 213 * default case, to just in case something really weird is going on, and
8dc0ae8f 214 * $context->contextlevel is invalid - rather than ignoring this possibility.
b37eac91 215 *
47d2216e
PS
216 * @package core
217 * @subpackage lib
218 * @copyright 2009 onwards Martin Dougiamas {@link http://moodle.com}
219 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
cce1b0b9 220 */
221class invalid_state_exception extends moodle_exception {
222 /**
223 * Constructor
224 * @param string $hint short description of problem
225 * @param string $debuginfo optional more detailed information
226 */
227 function __construct($hint, $debuginfo=null) {
228 parent::__construct('invalidstatedetected', 'debug', '', $hint, $debuginfo);
229 }
230}
231
4031f6a2
PS
232/**
233 * An exception that indicates incorrect permissions in $CFG->dataroot
234 *
235 * @package core
236 * @subpackage lib
237 * @copyright 2010 Petr Skoda {@link http://skodak.org}
238 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
239 */
240class invalid_dataroot_permissions extends moodle_exception {
241 /**
242 * Constructor
243 * @param string $debuginfo optional more detailed information
244 */
245 function __construct($debuginfo = NULL) {
246 parent::__construct('invaliddatarootpermissions', 'error', '', NULL, $debuginfo);
247 }
248}
249
251387d0 250/**
1387fcdd 251 * Default exception handler, uncaught exceptions are equivalent to error() in 1.9 and earlier
fd1a792e 252 *
30fa50d0 253 * @param Exception $ex
c19bc39c 254 * @return void -does not return. Terminates execution!
251387d0 255 */
c19bc39c 256function default_exception_handler($ex) {
695c5ec4 257 global $DB, $OUTPUT;
1fbdf76d 258
259 // detect active db transactions, rollback and log as error
695c5ec4 260 abort_all_db_transactions();
1fe1d104 261
c19bc39c 262 $info = get_exception_info($ex);
34a2777c 263
c19bc39c 264 if (debugging('', DEBUG_MINIMAL)) {
2e9b772f 265 $logerrmsg = "Default exception handler: ".$info->message.' Debug: '.$info->debuginfo."\n".format_backtrace($info->backtrace, true);
695c5ec4 266 error_log($logerrmsg);
fd1a792e 267 }
268
c19bc39c
PS
269 if (is_early_init($info->backtrace)) {
270 echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
b7009474 271 } else {
3c1ea58b 272 try {
a56c457e
PS
273 if ($DB) {
274 // If you enable db debugging and exception is thrown, the print footer prints a lot of rubbish
275 $DB->set_debug(0);
276 }
3c1ea58b
PS
277 echo $OUTPUT->fatal_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
278 } catch (Exception $out_ex) {
279 // default exception handler MUST not throw any exceptions!!
2e9b772f 280 // the problem here is we do not know if page already started or not, we only know that somebody messed up in outputlib or theme
3c1ea58b 281 // so we just print at least something instead of "Exception thrown without a stack frame in Unknown on line 0":-(
1adaa404
PS
282 if (CLI_SCRIPT or AJAX_SCRIPT) {
283 // just ignore the error and send something back using the safest method
284 echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
285 } else {
286 echo bootstrap_renderer::early_error_content($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo);
287 $outinfo = get_exception_info($out_ex);
288 echo bootstrap_renderer::early_error_content($outinfo->message, $outinfo->moreinfourl, $outinfo->link, $outinfo->backtrace, $outinfo->debuginfo);
289 }
3c1ea58b 290 }
7544d13c 291 }
292
34a2777c 293 exit(1); // General error code
294}
295
85ba1e78
PS
296/**
297 * Default error handler, prevents some white screens.
298 * @param int $errno
299 * @param string $errstr
300 * @param string $errfile
301 * @param int $errline
302 * @param array $errcontext
303 * @return bool false means use default error handler
304 */
305function default_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
306 if ($errno == 4096) {
307 //fatal catchable error
308 throw new coding_exception('PHP catchable fatal error', $errstr);
309 }
310 return false;
311}
312
695c5ec4
PS
313/**
314 * Unconditionally abort all database transactions, this function
315 * should be called from exception handlers only.
316 * @return void
317 */
318function abort_all_db_transactions() {
319 global $CFG, $DB, $SCRIPT;
320
d5a8d9aa 321 // default exception handler MUST not throw any exceptions!!
03221650 322
695c5ec4
PS
323 if ($DB && $DB->is_transaction_started()) {
324 error_log('Database transaction aborted automatically in ' . $CFG->dirroot . $SCRIPT);
d5a8d9aa
PS
325 // note: transaction blocks should never change current $_SESSION
326 $DB->force_transaction_rollback();
695c5ec4
PS
327 }
328}
329
b7009474 330/**
50764d37
PS
331 * This function encapsulates the tests for whether an exception was thrown in
332 * early init -- either during setup.php or during init of $OUTPUT.
b7009474 333 *
334 * If another exception is thrown then, and if we do not take special measures,
335 * we would just get a very cryptic message "Exception thrown without a stack
336 * frame in Unknown on line 0". That makes debugging very hard, so we do take
337 * special measures in default_exception_handler, with the help of this function.
338 *
339 * @param array $backtrace the stack trace to analyse.
340 * @return boolean whether the stack trace is somewhere in output initialisation.
341 */
50764d37 342function is_early_init($backtrace) {
b7009474 343 $dangerouscode = array(
344 array('function' => 'header', 'type' => '->'),
345 array('class' => 'bootstrap_renderer'),
50764d37 346 array('file' => dirname(__FILE__).'/setup.php'),
b7009474 347 );
348 foreach ($backtrace as $stackframe) {
349 foreach ($dangerouscode as $pattern) {
350 $matches = true;
351 foreach ($pattern as $property => $value) {
352 if (!isset($stackframe[$property]) || $stackframe[$property] != $value) {
353 $matches = false;
354 }
355 }
356 if ($matches) {
357 return true;
358 }
359 }
360 }
361 return false;
362}
363
34a2777c 364/**
cbf05caa
PS
365 * Abort execution by throwing of a general exception,
366 * default exception handler displays the error message in most cases.
34a2777c 367 *
368 * @param string $errorcode The name of the language string containing the error message.
369 * Normally this should be in the error.php lang file.
370 * @param string $module The language file to get the error message from.
371 * @param string $link The url where the user will be prompted to continue.
372 * If no url is provided the user will be directed to the site index page.
373 * @param object $a Extra words and phrases that might be required in the error string
cbf05caa
PS
374 * @param string $debuginfo optional debugging information
375 * @return void, always throws exception!
34a2777c 376 */
cbf05caa
PS
377function print_error($errorcode, $module = 'error', $link = '', $a = null, $debuginfo = null) {
378 throw new moodle_exception($errorcode, $module, $link, $a, $debuginfo);
34a2777c 379}
380
381/**
c19bc39c
PS
382 * Returns detailed information about specified exception.
383 * @param exception $ex
384 * @return object
34a2777c 385 */
c19bc39c 386function get_exception_info($ex) {
34a2777c 387 global $CFG, $DB, $SESSION;
388
c19bc39c
PS
389 if ($ex instanceof moodle_exception) {
390 $errorcode = $ex->errorcode;
391 $module = $ex->module;
392 $a = $ex->a;
393 $link = $ex->link;
394 $debuginfo = $ex->debuginfo;
395 } else {
396 $errorcode = 'generalexceptionmessage';
397 $module = 'error';
398 $a = $ex->getMessage();
399 $link = '';
400 $debuginfo = null;
34a2777c 401 }
402
c19bc39c
PS
403 $backtrace = $ex->getTrace();
404 $place = array('file'=>$ex->getFile(), 'line'=>$ex->getLine(), 'exception'=>get_class($ex));
405 array_unshift($backtrace, $place);
406
c84a2dbe 407 // Be careful, no guarantee moodlelib.php is loaded.
34a2777c 408 if (empty($module) || $module == 'moodle' || $module == 'core') {
409 $module = 'error';
410 }
4f6be42c
DM
411 if (function_exists('get_string_manager')) {
412 if (get_string_manager()->string_exists($errorcode, $module)) {
413 $message = get_string($errorcode, $module, $a);
414 } elseif ($module == 'error' && get_string_manager()->string_exists($errorcode, 'moodle')) {
c84a2dbe 415 // Search in moodle file if error specified - needed for backwards compatibility
416 $message = get_string($errorcode, 'moodle', $a);
4f6be42c
DM
417 } else {
418 $message = $module . '/' . $errorcode;
c84a2dbe 419 }
420 } else {
421 $message = $module . '/' . $errorcode;
422 }
423
424 // Be careful, no guarantee weblib.php is loaded.
425 if (function_exists('clean_text')) {
426 $message = clean_text($message);
427 } else {
428 $message = htmlspecialchars($message);
34a2777c 429 }
34a2777c 430
431 if (!empty($CFG->errordocroot)) {
432 $errordocroot = $CFG->errordocroot;
433 } else if (!empty($CFG->docroot)) {
434 $errordocroot = $CFG->docroot;
435 } else {
436 $errordocroot = 'http://docs.moodle.org';
437 }
438 if ($module === 'error') {
439 $modulelink = 'moodle';
0ae8f5fc 440 } else {
34a2777c 441 $modulelink = $module;
251387d0 442 }
34a2777c 443 $moreinfourl = $errordocroot . '/en/error/' . $modulelink . '/' . $errorcode;
444
d4a03c00 445 if (empty($link)) {
34a2777c 446 if (!empty($SESSION->fromurl)) {
447 $link = $SESSION->fromurl;
448 unset($SESSION->fromurl);
449 } else {
450 $link = $CFG->wwwroot .'/';
451 }
452 }
453
c19bc39c
PS
454 $info = new object();
455 $info->message = $message;
456 $info->errorcode = $errorcode;
457 $info->backtrace = $backtrace;
458 $info->link = $link;
459 $info->moreinfourl = $moreinfourl;
460 $info->a = $a;
461 $info->debuginfo = $debuginfo;
30fa50d0 462
c19bc39c 463 return $info;
34a2777c 464}
465
34a2777c 466/**
467 * Formats a backtrace ready for output.
468 *
469 * @param array $callers backtrace array, as returned by debug_backtrace().
470 * @param boolean $plaintext if false, generates HTML, if true generates plain text.
471 * @return string formatted backtrace, ready for output.
472 */
473function format_backtrace($callers, $plaintext = false) {
a0394be4 474 // do not use $CFG->dirroot because it might not be available in destructors
34a2777c 475 $dirroot = dirname(dirname(__FILE__));
30fa50d0 476
34a2777c 477 if (empty($callers)) {
478 return '';
479 }
480
481 $from = $plaintext ? '' : '<ul style="text-align: left">';
482 foreach ($callers as $caller) {
483 if (!isset($caller['line'])) {
484 $caller['line'] = '?'; // probably call_user_func()
485 }
486 if (!isset($caller['file'])) {
487 $caller['file'] = 'unknownfile'; // probably call_user_func()
488 }
489 $from .= $plaintext ? '* ' : '<li>';
490 $from .= 'line ' . $caller['line'] . ' of ' . str_replace($dirroot, '', $caller['file']);
491 if (isset($caller['function'])) {
492 $from .= ': call to ';
493 if (isset($caller['class'])) {
494 $from .= $caller['class'] . $caller['type'];
495 }
496 $from .= $caller['function'] . '()';
497 } else if (isset($caller['exception'])) {
498 $from .= ': '.$caller['exception'].' thrown';
499 }
500 $from .= $plaintext ? "\n" : '</li>';
501 }
502 $from .= $plaintext ? '' : '</ul>';
503
504 return $from;
251387d0 505}
6a525ce2 506
fbf2c91e 507/**
508 * This function verifies the sanity of PHP configuration
509 * and stops execution if anything critical found.
510 */
511function setup_validate_php_configuration() {
512 // this must be very fast - no slow checks here!!!
513
514 if (ini_get_bool('register_globals')) {
515 print_error('globalswarning', 'admin');
516 }
517 if (ini_get_bool('session.auto_start')) {
518 print_error('sessionautostartwarning', 'admin');
519 }
520 if (ini_get_bool('magic_quotes_runtime')) {
521 print_error('fatalmagicquotesruntime', 'admin');
522 }
523}
524
12bb0c3e
PS
525/**
526 * Initialise global $CFG variable
527 * @return void
528 */
529function initialise_cfg() {
530 global $CFG, $DB;
8b8aa060 531
12bb0c3e
PS
532 try {
533 if ($DB) {
534 $localcfg = $DB->get_records_menu('config', array(), '', 'name,value');
535 foreach ($localcfg as $name=>$value) {
536 if (property_exists($CFG, $name)) {
537 // config.php settings always take precedence
538 continue;
539 }
540 $CFG->{$name} = $value;
541 }
542 }
543 } catch (dml_read_exception $e) {
544 // most probably empty db, going to install soon
545 }
546}
547
11e7b506 548/**
75781f87 549 * Initialises $FULLME and friends. Private function. Should only be called from
550 * setup.php.
11e7b506 551 */
552function initialise_fullme() {
553 global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT;
554
75781f87 555 // Detect common config error.
84b88cfd 556 if (substr($CFG->wwwroot, -1) == '/') {
557 print_error('wwwrootslash', 'error');
558 }
559
75781f87 560 if (CLI_SCRIPT) {
561 initialise_fullme_cli();
562 return;
37ccf1fe 563 }
11e7b506 564
75781f87 565 $wwwroot = parse_url($CFG->wwwroot);
566 if (!isset($wwwroot['path'])) {
567 $wwwroot['path'] = '';
568 }
569 $wwwroot['path'] .= '/';
570
571 $rurl = setup_get_remote_url();
11e7b506 572
75781f87 573 // Check that URL is under $CFG->wwwroot.
574 if (strpos($rurl['path'], $wwwroot['path']) === 0) {
575 $SCRIPT = substr($rurl['path'], strlen($wwwroot['path'])-1);
576 } else {
577 // Probably some weird external script
578 $SCRIPT = $FULLSCRIPT = $FULLME = $ME = null;
11e7b506 579 return;
580 }
581
75781f87 582 // $CFG->sslproxy specifies if external SSL appliance is used
583 // (That is, the Moodle server uses http, with an external box translating everything to https).
584 if (empty($CFG->sslproxy)) {
585 if ($rurl['scheme'] == 'http' and $wwwroot['scheme'] == 'https') {
586 print_error('sslonlyaccess', 'error');
587 }
588 }
589
590 // $CFG->reverseproxy specifies if reverse proxy server used.
591 // Used in load balancing scenarios.
592 // Do not abuse this to try to solve lan/wan access problems!!!!!
593 if (empty($CFG->reverseproxy)) {
594 if (($rurl['host'] != $wwwroot['host']) or
595 (!empty($wwwroot['port']) and $rurl['port'] != $wwwroot['port'])) {
68a45361 596 // Explain the problem and redirect them to the right URL
df92ba9a
PS
597 if (!defined('NO_MOODLE_COOKIES')) {
598 define('NO_MOODLE_COOKIES', true);
599 }
68a45361 600 redirect($CFG->wwwroot, get_string('wwwrootmismatch', 'error', $CFG->wwwroot), 3);
75781f87 601 }
602 }
603
604 // hopefully this will stop all those "clever" admins trying to set up moodle
605 // with two different addresses in intranet and Internet
606 if (!empty($CFG->reverseproxy) && $rurl['host'] == $wwwroot['host']) {
607 print_error('reverseproxyabused', 'error');
608 }
609
610 $hostandport = $rurl['scheme'] . '://' . $wwwroot['host'];
611 if (!empty($wwwroot['port'])) {
612 $hostandport .= ':'.$wwwroot['port'];
613 }
614
615 $FULLSCRIPT = $hostandport . $rurl['path'];
616 $FULLME = $hostandport . $rurl['fullpath'];
617 $ME = $rurl['fullpath'];
618 $rurl['path'] = $rurl['fullpath'];
619}
620
621/**
622 * Initialises $FULLME and friends for command line scripts.
623 * This is a private method for use by initialise_fullme.
624 */
625function initialise_fullme_cli() {
80e30f25 626 global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT;
627
75781f87 628 // Urls do not make much sense in CLI scripts
629 $backtrace = debug_backtrace();
630 $topfile = array_pop($backtrace);
631 $topfile = realpath($topfile['file']);
632 $dirroot = realpath($CFG->dirroot);
633
634 if (strpos($topfile, $dirroot) !== 0) {
635 // Probably some weird external script
636 $SCRIPT = $FULLSCRIPT = $FULLME = $ME = null;
637 } else {
638 $relativefile = substr($topfile, strlen($dirroot));
639 $relativefile = str_replace('\\', '/', $relativefile); // Win fix
640 $SCRIPT = $FULLSCRIPT = $relativefile;
641 $FULLME = $ME = null;
642 }
643}
644
645/**
646 * Get the URL that PHP/the web server thinks it is serving. Private function
647 * used by initialise_fullme. In your code, use $PAGE->url, $SCRIPT, etc.
648 * @return array in the same format that parse_url returns, with the addition of
649 * a 'fullpath' element, which includes any slasharguments path.
650 */
651function setup_get_remote_url() {
11e7b506 652 $rurl = array();
75781f87 653 list($rurl['host']) = explode(':', $_SERVER['HTTP_HOST']);
11e7b506 654 $rurl['port'] = $_SERVER['SERVER_PORT'];
75781f87 655 $rurl['path'] = $_SERVER['SCRIPT_NAME']; // Script path without slash arguments
11e7b506 656
657 if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
658 //Apache server
659 $rurl['scheme'] = empty($_SERVER['HTTPS']) ? 'http' : 'https';
660 $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
661
662 } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) {
199c1981 663 //lighttpd - not officially supported
11e7b506 664 $rurl['scheme'] = empty($_SERVER['HTTPS']) ? 'http' : 'https';
665 $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
666
7e13a265 667 } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
199c1981 668 //nginx - not officially supported
7e13a265 669 $rurl['scheme'] = empty($_SERVER['HTTPS']) ? 'http' : 'https';
670 $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
671
11e7b506 672 } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) {
199c1981 673 //IIS - needs a lot of tweaking to make it work
11e7b506 674 $rurl['scheme'] = ($_SERVER['HTTPS'] == 'off') ? 'http' : 'https';
675 $rurl['fullpath'] = $_SERVER['SCRIPT_NAME'];
676
677 // NOTE: ignore PATH_INFO because it is incorrectly encoded using 8bit filesystem legacy encoding in IIS
678 // since 2.0 we rely on iis rewrite extenssion like Helicon ISAPI_rewrite
21517960 679 // example rule: RewriteRule ^([^\?]+?\.php)(\/.+)$ $1\?file=$2 [QSA]
11e7b506 680
681 if ($_SERVER['QUERY_STRING'] != '') {
991ec2ee 682 $rurl['fullpath'] .= '?'.$_SERVER['QUERY_STRING'];
11e7b506 683 }
684 $_SERVER['REQUEST_URI'] = $rurl['fullpath']; // extra IIS compatibility
685
199c1981
PS
686 } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'cherokee') !== false) {
687 //cherokee - not officially supported
688 $rurl['scheme'] = ($_SERVER['HTTPS'] == 'off') ? 'http' : 'https';
689 $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
690
8b8aa060
PS
691 } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'zeus') !== false) {
692 //zeus - not officially supported
693 $rurl['scheme'] = ($_SERVER['HTTPS'] == 'off') ? 'http' : 'https';
694 $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded
695
696 } else {
75781f87 697 throw new moodle_exception('unsupportedwebserver', 'error', '', $_SERVER['SERVER_SOFTWARE']);
11e7b506 698 }
75781f87 699 return $rurl;
11e7b506 700}
701
d3f9f1f8 702/**
703 * Initializes our performance info early.
704 *
705 * Pairs up with get_performance_info() which is actually
251387d0 706 * in moodlelib.php. This function is here so that we can
707 * call it before all the libs are pulled in.
d3f9f1f8 708 *
709 * @uses $PERF
710 */
711function init_performance_info() {
712
6fc4ad72 713 global $PERF, $CFG, $USER;
251387d0 714
ab130a0b 715 $PERF = new object();
d3f9f1f8 716 $PERF->logwrites = 0;
717 if (function_exists('microtime')) {
718 $PERF->starttime = microtime();
c84a2dbe 719 }
d3f9f1f8 720 if (function_exists('memory_get_usage')) {
721 $PERF->startmemory = memory_get_usage();
722 }
723 if (function_exists('posix_times')) {
251387d0 724 $PERF->startposixtimes = posix_times();
d3f9f1f8 725 }
b65567f4 726 if (function_exists('apd_set_pprof_trace')) {
727 // APD profiling
6fc4ad72 728 if ($USER->id > 0 && $CFG->perfdebug >= 15) {
251387d0 729 $tempdir = $CFG->dataroot . '/temp/profile/' . $USER->id;
6fc4ad72 730 mkdir($tempdir);
731 apd_set_pprof_trace($tempdir);
732 $PERF->profiling = true;
733 }
b65567f4 734 }
d3f9f1f8 735}
736
31a99877 737/**
738 * Indicates whether we are in the middle of the initial Moodle install.
739 *
740 * Very occasionally it is necessary avoid running certain bits of code before the
741 * Moodle installation has completed. The installed flag is set in admin/index.php
742 * after Moodle core and all the plugins have been installed, but just before
743 * the person doing the initial install is asked to choose the admin password.
744 *
745 * @return boolean true if the initial install is not complete.
746 */
747function during_initial_install() {
748 global $CFG;
749 return empty($CFG->rolesactive);
750}
751
76f3815b 752/**
753 * Function to raise the memory limit to a new value.
754 * Will respect the memory limit if it is higher, thus allowing
755 * settings in php.ini, apache conf or command line switches
756 * to override it
757 *
758 * The memory limit should be expressed with a string (eg:'64M')
759 *
760 * @param string $newlimit the new memory limit
761 * @return bool
762 */
11e7b506 763function raise_memory_limit($newlimit) {
76f3815b 764
765 if (empty($newlimit)) {
766 return false;
767 }
768
769 $cur = @ini_get('memory_limit');
770 if (empty($cur)) {
771 // if php is compiled without --enable-memory-limits
772 // apparently memory_limit is set to ''
773 $cur=0;
774 } else {
775 if ($cur == -1){
776 return true; // unlimited mem!
777 }
778 $cur = get_real_size($cur);
779 }
780
781 $new = get_real_size($newlimit);
782 if ($new > $cur) {
783 ini_set('memory_limit', $newlimit);
7022dd39 784 return true;
785 }
786 return false;
787}
788
789/**
790 * Function to reduce the memory limit to a new value.
791 * Will respect the memory limit if it is lower, thus allowing
792 * settings in php.ini, apache conf or command line switches
793 * to override it
794 *
795 * The memory limit should be expressed with a string (eg:'64M')
796 *
797 * @param string $newlimit the new memory limit
798 * @return bool
799 */
f630a546 800function reduce_memory_limit($newlimit) {
7022dd39 801 if (empty($newlimit)) {
802 return false;
803 }
804 $cur = @ini_get('memory_limit');
805 if (empty($cur)) {
806 // if php is compiled without --enable-memory-limits
807 // apparently memory_limit is set to ''
808 $cur=0;
809 } else {
810 if ($cur == -1){
811 return true; // unlimited mem!
812 }
813 $cur = get_real_size($cur);
814 }
815
816 $new = get_real_size($newlimit);
817 // -1 is smaller, but it means unlimited
818 if ($new < $cur && $new != -1) {
819 ini_set('memory_limit', $newlimit);
76f3815b 820 return true;
821 }
822 return false;
823}
824
825/**
826 * Converts numbers like 10M into bytes.
827 *
828 * @param mixed $size The size to be converted
829 * @return mixed
830 */
831function get_real_size($size=0) {
832 if (!$size) {
833 return 0;
834 }
11e7b506 835 $scan = array();
76f3815b 836 $scan['MB'] = 1048576;
837 $scan['Mb'] = 1048576;
838 $scan['M'] = 1048576;
839 $scan['m'] = 1048576;
840 $scan['KB'] = 1024;
841 $scan['Kb'] = 1024;
842 $scan['K'] = 1024;
843 $scan['k'] = 1024;
844
845 while (list($key) = each($scan)) {
846 if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
847 $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
848 break;
849 }
850 }
851 return $size;
852}
853
5e39d7aa 854/**
855 * Check whether a major upgrade is needed. That is defined as an upgrade that
856 * changes something really fundamental in the database, so nothing can possibly
857 * work until the database has been updated, and that is defined by the hard-coded
858 * version number in this function.
859 */
860function redirect_if_major_upgrade_required() {
861 global $CFG;
64f93798 862 $lastmajordbchanges = 2010070300;
5e39d7aa 863 if (empty($CFG->version) or (int)$CFG->version < $lastmajordbchanges or
864 during_initial_install() or !empty($CFG->adminsetuppending)) {
865 try {
866 @session_get_instance()->terminate_current();
867 } catch (Exception $e) {
868 // Ignore any errors, redirect to upgrade anyway.
869 }
a38c7a10 870 $url = $CFG->wwwroot . '/' . $CFG->admin . '/index.php';
5e39d7aa 871 @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other');
a38c7a10
PS
872 @header('Location: ' . $url);
873 echo bootstrap_renderer::plain_redirect_message(htmlspecialchars($url));
5e39d7aa 874 exit;
875 }
876}
877
d3f9f1f8 878/**
4031f6a2
PS
879 * Function to check if a directory exists and by default create it if not exists.
880 *
881 * Previously this was accepting paths only from dataroot, but we now allow
882 * files outside of dataroot if you supply custom paths for some settings in config.php.
883 * This function does not verify that the directory is writable.
d3f9f1f8 884 *
4031f6a2
PS
885 * @param string $dir absolute directory path
886 * @param boolean $create directory if does not exist
887 * @param boolean $recursive create directory recursively
888 * @return boolean true if directory exists or created, false otherwise
d3f9f1f8 889 */
4031f6a2 890function check_dir_exists($dir, $create = true, $recursive = true) {
d3f9f1f8 891 global $CFG;
892
4031f6a2 893 umask(0000); // just in case some evil code changed it
d3f9f1f8 894
4031f6a2
PS
895 if (is_dir($dir)) {
896 return true;
897 }
d3f9f1f8 898
4031f6a2
PS
899 if (!$create) {
900 return false;
d100b8a0 901 }
902
4031f6a2
PS
903 return mkdir($dir, $CFG->directorypermissions, $recursive);
904}
905
906/**
907 * Create a directory in dataroot and make sure it is writable.
908 *
909 * @param string $directory a string of directory names under $CFG->dataroot eg temp/something
910 * @param bool $exceptiononerror throw exception if error encountered
911 * @return string|false Returns full path to directory if successful, false if not; may throw exception
912 */
913function make_upload_directory($directory, $exceptiononerror = true) {
914 global $CFG;
915
916 // Make sure a .htaccess file is here, JUST IN CASE the files area is in the open and .htaccess is supported
917 if (!file_exists("$CFG->dataroot/.htaccess")) {
918 if ($handle = fopen("$CFG->dataroot/.htaccess", 'w')) { // For safety
edaf04d4 919 @fwrite($handle, "deny from all\r\nAllowOverride None\r\nNote: this file is broken intentionally, we do not want anybody to undo it in subdirectory!\r\n");
d3f9f1f8 920 @fclose($handle);
921 }
922 }
923
4031f6a2 924 $dir = "$CFG->dataroot/$directory";
d3f9f1f8 925
4031f6a2
PS
926 if (file_exists($dir) and !is_dir($dir)) {
927 if ($exceptiononerror) {
928 throw new coding_exception($dir.' directory can not be created, file with the same name already exists.');
929 } else {
930 return false;
931 }
932 }
933
934 umask(0000); // just in case some evil code changed it
935
936 if (!file_exists($dir)) {
937 if (!mkdir($dir, $CFG->directorypermissions, true)) {
938 if ($exceptiononerror) {
939 throw new invalid_dataroot_permissions($dir.' can not be created, check permissions.');
940 } else {
d3f9f1f8 941 return false;
942 }
d3f9f1f8 943 }
944 }
945
4031f6a2
PS
946 if (!is_writable($dir)) {
947 if ($exceptiononerror) {
948 throw new invalid_dataroot_permissions($dir.' is not writable, check permissions.');
949 } else {
950 return false;
951 }
952 }
953
954 return $dir;
d3f9f1f8 955}
956
419e1d93 957function init_memcached() {
958 global $CFG, $MCACHE;
959
f917d0ea 960 include_once($CFG->libdir . '/memcached.class.php');
961 $MCACHE = new memcached;
962 if ($MCACHE->status()) {
963 return true;
251387d0 964 }
f917d0ea 965 unset($MCACHE);
251387d0 966 return false;
419e1d93 967}
968
2142d492 969function init_eaccelerator() {
970 global $CFG, $MCACHE;
971
972 include_once($CFG->libdir . '/eaccelerator.class.php');
973 $MCACHE = new eaccelerator;
f917d0ea 974 if ($MCACHE->status()) {
2142d492 975 return true;
251387d0 976 }
2142d492 977 unset($MCACHE);
978 return false;
979}
980
981
c84a2dbe 982/**
983 * This class solves the problem of how to initialise $OUTPUT.
984 *
985 * The problem is caused be two factors
986 * <ol>
987 * <li>On the one hand, we cannot be sure when output will start. In particular,
30fa50d0 988 * an error, which needs to be displayed, could be thrown at any time.</li>
c84a2dbe 989 * <li>On the other hand, we cannot be sure when we will have all the information
990 * necessary to correctly initialise $OUTPUT. $OUTPUT depends on the theme, which
991 * (potentially) depends on the current course, course categories, and logged in user.
992 * It also depends on whether the current page requires HTTPS.</li>
993 * </ol>
994 *
995 * So, it is hard to find a single natural place during Moodle script execution,
996 * which we can guarantee is the right time to initialise $OUTPUT. Instead we
997 * adopt the following strategy
998 * <ol>
999 * <li>We will initialise $OUTPUT the first time it is used.</li>
1000 * <li>If, after $OUTPUT has been initialised, the script tries to change something
7e0d6675 1001 * that $OUTPUT depends on, we throw an exception making it clear that the script
c84a2dbe 1002 * did something wrong.
1003 * </ol>
1004 *
1005 * The only problem with that is, how do we initialise $OUTPUT on first use if,
1006 * it is going to be used like $OUTPUT->somthing(...)? Well that is where this
1007 * class comes in. Initially, we set up $OUTPUT = new bootstrap_renderer(). Then,
1008 * when any method is called on that object, we initialise $OUTPUT, and pass the call on.
1009 *
1010 * Note that this class is used before lib/outputlib.php has been loaded, so we
1387fcdd 1011 * must be careful referring to classes/functions from there, they may not be
c84a2dbe 1012 * defined yet, and we must avoid fatal errors.
1013 *
1014 * @copyright 2009 Tim Hunt
1015 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1016 * @since Moodle 2.0
1017 */
1018class bootstrap_renderer {
1019 /**
1020 * Handles re-entrancy. Without this, errors or debugging output that occur
1021 * during the initialisation of $OUTPUT, cause infinite recursion.
1022 * @var boolean
1023 */
1024 protected $initialising = false;
1025
b7009474 1026 /**
1027 * Have we started output yet?
1028 * @return boolean true if the header has been printed.
1029 */
1030 public function has_started() {
1031 return false;
1032 }
1033
c84a2dbe 1034 public function __call($method, $arguments) {
b7009474 1035 global $OUTPUT, $PAGE;
c84a2dbe 1036
87b6851c 1037 $recursing = false;
1038 if ($method == 'notification') {
1387fcdd 1039 // Catch infinite recursion caused by debugging output during print_header.
87b6851c 1040 $backtrace = debug_backtrace();
1041 array_shift($backtrace);
1042 array_shift($backtrace);
50764d37 1043 $recursing = is_early_init($backtrace);
87b6851c 1044 }
1045
eb5bdb35
PS
1046 $earlymethods = array(
1047 'fatal_error' => 'early_error',
1048 'notification' => 'early_notification',
1049 );
1050
c84a2dbe 1051 // If lib/outputlib.php has been loaded, call it.
87b6851c 1052 if (!empty($PAGE) && !$recursing) {
eb5bdb35
PS
1053 if (array_key_exists($method, $earlymethods)) {
1054 //prevent PAGE->context warnings - exceptions might appear before we set any context
1055 $PAGE->set_context(null);
1056 }
b7009474 1057 $PAGE->initialise_theme_and_output();
1058 return call_user_func_array(array($OUTPUT, $method), $arguments);
c84a2dbe 1059 }
2142d492 1060
c84a2dbe 1061 $this->initialising = true;
eb5bdb35 1062
c84a2dbe 1063 // Too soon to initialise $OUTPUT, provide a couple of key methods.
c84a2dbe 1064 if (array_key_exists($method, $earlymethods)) {
1065 return call_user_func_array(array('bootstrap_renderer', $earlymethods[$method]), $arguments);
1066 }
1067
1068 throw new coding_exception('Attempt to start output before enough information is known to initialise the theme.');
1069 }
1070
1071 /**
1387fcdd 1072 * Returns nicely formatted error message in a div box.
30fa50d0 1073 * @return string
c84a2dbe 1074 */
3c1ea58b 1075 public static function early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
b7009474 1076 global $CFG;
1077
3c1ea58b
PS
1078 $content = '<div style="margin-top: 6em; margin-left:auto; margin-right:auto; color:#990000; text-align:center; font-size:large; border-width:1px;
1079border-color:black; background-color:#ffffee; border-style:solid; border-radius: 20px; border-collapse: collapse;
1080width: 80%; -moz-border-radius: 20px; padding: 15px">
1081' . $message . '
1082</div>';
1083 if (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER) {
1084 if (!empty($debuginfo)) {
c5d18164
PS
1085 $debuginfo = s($debuginfo); // removes all nasty JS
1086 $debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines
1087 $content .= '<div class="notifytiny">Debug info: ' . $debuginfo . '</div>';
3c1ea58b
PS
1088 }
1089 if (!empty($backtrace)) {
1090 $content .= '<div class="notifytiny">Stack trace: ' . format_backtrace($backtrace, false) . '</div>';
1091 }
1092 }
1093
1094 return $content;
1095 }
1096
1097 /**
1098 * This function should only be called by this class, or from exception handlers
1099 * @return string
1100 */
1101 public static function early_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
1adaa404
PS
1102 global $CFG;
1103
1104 if (CLI_SCRIPT) {
1105 echo "!!! $message !!!\n";
1106 if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
1107 if (!empty($debuginfo)) {
1108 echo "\nDebug info: $debuginfo";
1109 }
1110 if (!empty($backtrace)) {
1111 echo "\nStack trace: " . format_backtrace($backtrace, true);
1112 }
1113 }
1114 return;
1115
1116 } else if (AJAX_SCRIPT) {
1117 $e = new stdClass();
1118 $e->error = $message;
1119 $e->stacktrace = NULL;
1120 $e->debuginfo = NULL;
1121 if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
1122 if (!empty($debuginfo)) {
1123 $e->debuginfo = $debuginfo;
1124 }
1125 if (!empty($backtrace)) {
1126 $e->stacktrace = format_backtrace($backtrace, true);
1127 }
1128 }
6db3eee0 1129 @header('Content-Type: application/json');
1adaa404
PS
1130 echo json_encode($e);
1131 return;
1132 }
1133
c84a2dbe 1134 // In the name of protocol correctness, monitoring and performance
1387fcdd 1135 // profiling, set the appropriate error headers for machine consumption
c84a2dbe 1136 if (isset($_SERVER['SERVER_PROTOCOL'])) {
1137 // Avoid it with cron.php. Note that we assume it's HTTP/1.x
1387fcdd 1138 // The 503 ode here means our Moodle does not work at all, the error happened too early
c84a2dbe 1139 @header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable');
1140 }
1141
1142 // better disable any caching
1143 @header('Content-Type: text/html; charset=utf-8');
1144 @header('Cache-Control: no-store, no-cache, must-revalidate');
1145 @header('Cache-Control: post-check=0, pre-check=0', false);
1146 @header('Pragma: no-cache');
1147 @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
1148 @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
1149
5e39d7aa 1150 if (function_exists('get_string')) {
c84a2dbe 1151 $strerror = get_string('error');
1152 } else {
c84a2dbe 1153 $strerror = 'Error';
1154 }
1155
3c1ea58b 1156 $content = self::early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo);
5e39d7aa 1157
1158 return self::plain_page($strerror, $content);
c84a2dbe 1159 }
1160
1161 public static function early_notification($message, $classes = 'notifyproblem') {
1162 return '<div class="' . $classes . '">' . $message . '</div>';
1163 }
5e39d7aa 1164
1165 public static function plain_redirect_message($encodedurl) {
1166 $message = '<p>' . get_string('pageshouldredirect') . '</p><p><a href="'.
1167 $encodedurl .'">'. get_string('continue') .'</a></p>';
a0a268d5 1168 return self::plain_page(get_string('redirect'), $message);
5e39d7aa 1169 }
1170
1171 protected static function plain_page($title, $content) {
1172 if (function_exists('get_string') && function_exists('get_html_lang')) {
1173 $htmllang = get_html_lang();
1174 } else {
1175 $htmllang = '';
1176 }
1177
1178 return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1179<html xmlns="http://www.w3.org/1999/xhtml" ' . $htmllang . '>
1180<head>
1181<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1182<title>' . $title . '</title>
1183</head><body>' . $content . '</body></html>';
1184 }
c84a2dbe 1185}