Adding tinymce editor choice.
[moodle.git] / lib / moodlelib.php
CommitLineData
ef1e97c7 1<?php // $Id$
f9903ed0 2
9fa49e22 3///////////////////////////////////////////////////////////////////////////
4// //
5// NOTICE OF COPYRIGHT //
6// //
7// Moodle - Modular Object-Oriented Dynamic Learning Environment //
abc3b857 8// http://moodle.org //
9fa49e22 9// //
abc3b857 10// Copyright (C) 1999-2004 Martin Dougiamas http://dougiamas.com //
9fa49e22 11// //
12// This program is free software; you can redistribute it and/or modify //
13// it under the terms of the GNU General Public License as published by //
14// the Free Software Foundation; either version 2 of the License, or //
15// (at your option) any later version. //
16// //
17// This program is distributed in the hope that it will be useful, //
18// but WITHOUT ANY WARRANTY; without even the implied warranty of //
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20// GNU General Public License for more details: //
21// //
22// http://www.gnu.org/copyleft/gpl.html //
23// //
24///////////////////////////////////////////////////////////////////////////
65ccdd8c 25
7cf1c7bd 26/**
89dcb99d 27 * moodlelib.php - Moodle main library
7cf1c7bd 28 *
29 * Main library file of miscellaneous general-purpose Moodle functions.
30 * Other main libraries:
8c3dba73 31 * - weblib.php - functions that produce web output
32 * - datalib.php - functions that access the database
7cf1c7bd 33 * @author Martin Dougiamas
34 * @version $Id$
89dcb99d 35 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
7cf1c7bd 36 * @package moodlecore
37 */
e1ecf0a0 38
bbd3f2c4 39/// CONSTANTS (Encased in phpdoc proper comments)/////////////////////////
f374fb10 40
6b94a807 41/**
42 * Used by some scripts to check they are being called by Moodle
43 */
44define('MOODLE_INTERNAL', true);
45
7cf1c7bd 46/**
47 * No groups used?
48 */
d8ba183c 49define('NOGROUPS', 0);
7cf1c7bd 50
51/**
52 * Groups used?
53 */
f374fb10 54define('SEPARATEGROUPS', 1);
7cf1c7bd 55
56/**
57 * Groups visible?
58 */
f374fb10 59define('VISIBLEGROUPS', 2);
60
bbd3f2c4 61/// Date and time constants ///
7a5672c9 62/**
2f87145b 63 * Time constant - the number of seconds in a week
7a5672c9 64 */
361855e6 65define('WEEKSECS', 604800);
2f87145b 66
67/**
68 * Time constant - the number of seconds in a day
69 */
7a5672c9 70define('DAYSECS', 86400);
2f87145b 71
72/**
73 * Time constant - the number of seconds in an hour
74 */
7a5672c9 75define('HOURSECS', 3600);
2f87145b 76
77/**
78 * Time constant - the number of seconds in a minute
79 */
7a5672c9 80define('MINSECS', 60);
2f87145b 81
82/**
83 * Time constant - the number of minutes in a day
84 */
7a5672c9 85define('DAYMINS', 1440);
2f87145b 86
87/**
88 * Time constant - the number of minutes in an hour
89 */
7a5672c9 90define('HOURMINS', 60);
f9903ed0 91
c59733ef 92/// Parameter constants - every call to optional_param(), required_param() ///
93/// or clean_param() should have a specified type of parameter. //////////////
94
e0d346ff 95/**
c59733ef 96 * PARAM_RAW specifies a parameter that is not cleaned/processed in any way.
e0d346ff 97 */
2ae28153 98define('PARAM_RAW', 0x0000);
bbd3f2c4 99
100/**
c59733ef 101 * PARAM_CLEAN - obsoleted, please try to use more specific type of parameter.
102 * It was one of the first types, that is why it is abused so much ;-)
bbd3f2c4 103 */
2ae28153 104define('PARAM_CLEAN', 0x0001);
bbd3f2c4 105
106/**
c59733ef 107 * PARAM_INT - integers only, use when expecting only numbers.
bbd3f2c4 108 */
2ae28153 109define('PARAM_INT', 0x0002);
bbd3f2c4 110
111/**
112 * PARAM_INTEGER - an alias for PARAM_INT
113 */
114define('PARAM_INTEGER', 0x0002);
115
116/**
c59733ef 117 * PARAM_ALPHA - contains only english letters.
bbd3f2c4 118 */
2ae28153 119define('PARAM_ALPHA', 0x0004);
bbd3f2c4 120
121/**
c59733ef 122 * PARAM_ACTION - an alias for PARAM_ALPHA, use for various actions in formas and urls
123 * @TODO: should we alias it to PARAM_ALPHANUM ?
bbd3f2c4 124 */
125define('PARAM_ACTION', 0x0004);
126
127/**
c59733ef 128 * PARAM_FORMAT - an alias for PARAM_ALPHA, use for names of plugins, formats, etc.
129 * @TODO: should we alias it to PARAM_ALPHANUM ?
bbd3f2c4 130 */
131define('PARAM_FORMAT', 0x0004);
132
133/**
c59733ef 134 * PARAM_NOTAGS - all html tags are stripped from the text. Do not abuse this type.
bbd3f2c4 135 */
2ae28153 136define('PARAM_NOTAGS', 0x0008);
bbd3f2c4 137
138/**
c59733ef 139 * PARAM_FILE - safe file name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
bbd3f2c4 140 */
2ae28153 141define('PARAM_FILE', 0x0010);
bbd3f2c4 142
143/**
c59733ef 144 * PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
145 * note: the leading slash is not removed, window drive letter is not allowed
bbd3f2c4 146 */
2ae28153 147define('PARAM_PATH', 0x0020);
bbd3f2c4 148
149/**
c59733ef 150 * PARAM_HOST - expected fully qualified domain name (FQDN) or an IPv4 dotted quad (IP address)
bbd3f2c4 151 */
152define('PARAM_HOST', 0x0040);
153
154/**
c59733ef 155 * PARAM_URL - expected properly formatted URL.
bbd3f2c4 156 */
2ae28153 157define('PARAM_URL', 0x0080);
bbd3f2c4 158
159/**
c59733ef 160 * PARAM_LOCALURL - expected properly formatted URL as well as one that refers to the local server itself. (NOT orthogonal to the others! Implies PARAM_URL!)
bbd3f2c4 161 */
162define('PARAM_LOCALURL', 0x0180);
163
164/**
c59733ef 165 * PARAM_CLEANFILE - safe file name, all dangerous and regional chars are removed,
166 * use when you want to store a new file submitted by students
bbd3f2c4 167 */
14d6c233 168define('PARAM_CLEANFILE',0x0200);
e0d346ff 169
8bd3fad3 170/**
c59733ef 171 * PARAM_ALPHANUM - expected numbers and letters only.
bbd3f2c4 172 */
173define('PARAM_ALPHANUM', 0x0400);
174
175/**
c59733ef 176 * PARAM_BOOL - converts input into 0 or 1, use for switches in forms and urls.
bbd3f2c4 177 */
178define('PARAM_BOOL', 0x0800);
179
180/**
c59733ef 181 * PARAM_CLEANHTML - cleans submitted HTML code and removes slashes
182 * note: do not forget to addslashes() before storing into database!
bbd3f2c4 183 */
184define('PARAM_CLEANHTML',0x1000);
185
186/**
c59733ef 187 * PARAM_ALPHAEXT the same contents as PARAM_ALPHA plus the chars in quotes: "/-_" allowed,
188 * suitable for include() and require()
189 * @TODO: should we rename this function to PARAM_SAFEDIRS??
bbd3f2c4 190 */
191define('PARAM_ALPHAEXT', 0x2000);
192
193/**
c59733ef 194 * PARAM_SAFEDIR - safe directory name, suitable for include() and require()
bbd3f2c4 195 */
196define('PARAM_SAFEDIR', 0x4000);
197
198/// Page types ///
199/**
200 * PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php.
8bd3fad3 201 */
202define('PAGE_COURSE_VIEW', 'course-view');
8bd3fad3 203
bbd3f2c4 204
9fa49e22 205/// PARAMETER HANDLING ////////////////////////////////////////////////////
6b174680 206
e0d346ff 207/**
361855e6 208 * Returns a particular value for the named variable, taken from
209 * POST or GET. If the parameter doesn't exist then an error is
e0d346ff 210 * thrown because we require this variable.
211 *
361855e6 212 * This function should be used to initialise all required values
213 * in a script that are based on parameters. Usually it will be
e0d346ff 214 * used like this:
215 * $id = required_param('id');
216 *
217 * @param string $varname the name of the parameter variable we want
bbd3f2c4 218 * @param int $options a bit field that specifies any cleaning needed
e0d346ff 219 * @return mixed
220 */
221function required_param($varname, $options=PARAM_CLEAN) {
e0d346ff 222
5d7a9f56 223 // detect_unchecked_vars addition
224 global $CFG;
225 if (!empty($CFG->detect_unchecked_vars)) {
226 global $UNCHECKED_VARS;
227 unset ($UNCHECKED_VARS->vars[$varname]);
228 }
229
e0d346ff 230 if (isset($_POST[$varname])) { // POST has precedence
231 $param = $_POST[$varname];
232 } else if (isset($_GET[$varname])) {
233 $param = $_GET[$varname];
234 } else {
3af57507 235 error('A required parameter ('.$varname.') was missing');
e0d346ff 236 }
237
238 return clean_param($param, $options);
239}
240
241/**
361855e6 242 * Returns a particular value for the named variable, taken from
e0d346ff 243 * POST or GET, otherwise returning a given default.
244 *
361855e6 245 * This function should be used to initialise all optional values
246 * in a script that are based on parameters. Usually it will be
e0d346ff 247 * used like this:
248 * $name = optional_param('name', 'Fred');
249 *
250 * @param string $varname the name of the parameter variable we want
251 * @param mixed $default the default value to return if nothing is found
bbd3f2c4 252 * @param int $options a bit field that specifies any cleaning needed
e0d346ff 253 * @return mixed
254 */
255function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {
e0d346ff 256
5d7a9f56 257 // detect_unchecked_vars addition
258 global $CFG;
259 if (!empty($CFG->detect_unchecked_vars)) {
260 global $UNCHECKED_VARS;
261 unset ($UNCHECKED_VARS->vars[$varname]);
262 }
263
e0d346ff 264 if (isset($_POST[$varname])) { // POST has precedence
265 $param = $_POST[$varname];
266 } else if (isset($_GET[$varname])) {
267 $param = $_GET[$varname];
268 } else {
269 return $default;
270 }
271
272 return clean_param($param, $options);
273}
274
7a530277 275/**
4d7c4f7f 276 * HACK ALERT! Do not use this function, it will be removed soon!!
f5ecb599 277 * @todo Remove this function soon!
7a530277 278 */
279function isset_param($varname) {
280 if (isset($_GET[$varname])) {
281 return true;
282 }
283 if (isset($_POST[$varname])) {
284 return true;
285 }
286 return false;
287}
288
e0d346ff 289/**
361855e6 290 * Used by {@link optional_param()} and {@link required_param()} to
291 * clean the variables and/or cast to specific types, based on
e0d346ff 292 * an options field.
bbd3f2c4 293 * <code>
294 * $course->format = clean_param($course->format, PARAM_ALPHA);
295 * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN);
296 * </code>
e0d346ff 297 *
bbd3f2c4 298 * @uses $CFG
299 * @uses PARAM_CLEAN
300 * @uses PARAM_INT
301 * @uses PARAM_INTEGER
302 * @uses PARAM_ALPHA
303 * @uses PARAM_ALPHANUM
304 * @uses PARAM_NOTAGS
305 * @uses PARAM_ALPHATEXT
306 * @uses PARAM_BOOL
307 * @uses PARAM_SAFEDIR
308 * @uses PARAM_CLEANFILE
309 * @uses PARAM_FILE
310 * @uses PARAM_PATH
311 * @uses PARAM_HOST
312 * @uses PARAM_URL
313 * @uses PARAM_LOCALURL
314 * @uses PARAM_CLEANHTML
e0d346ff 315 * @param mixed $param the variable we are cleaning
bbd3f2c4 316 * @param int $options a bit field that specifies the cleaning needed. This field is specified by combining PARAM_* definitions together with a logical or.
e0d346ff 317 * @return mixed
318 */
319function clean_param($param, $options) {
e0d346ff 320
7744ea12 321 global $CFG;
322
80bfd470 323 if (is_array($param)) { // Let's loop
324 $newparam = array();
325 foreach ($param as $key => $value) {
326 $newparam[$key] = clean_param($value, $options);
327 }
328 return $newparam;
329 }
330
3af57507 331 if (!$options) {
332 return $param; // Return raw value
333 }
334
7228f796 335 if ((string)$param == (string)(int)$param) { // It's just an integer
e0d346ff 336 return (int)$param;
337 }
338
339 if ($options & PARAM_CLEAN) {
a8936222 340 $param = stripslashes($param); // Needed by kses to work fine
e0d346ff 341 $param = clean_text($param); // Sweep for scripts, etc
a8936222 342 $param = addslashes($param); // Restore original request parameter slashes
e0d346ff 343 }
344
345 if ($options & PARAM_INT) {
346 $param = (int)$param; // Convert to integer
347 }
348
3af57507 349 if ($options & PARAM_ALPHA) { // Remove everything not a-z
01accf3e 350 $param = eregi_replace('[^a-zA-Z]', '', $param);
3af57507 351 }
352
f24148ef 353 if ($options & PARAM_ALPHANUM) { // Remove everything not a-zA-Z0-9
354 $param = eregi_replace('[^A-Za-z0-9]', '', $param);
355 }
356
0ed442f8 357 if ($options & PARAM_ALPHAEXT) { // Remove everything not a-zA-Z/_-
358 $param = eregi_replace('[^a-zA-Z/_-]', '', $param);
359 }
360
f24148ef 361 if ($options & PARAM_BOOL) { // Convert to 1 or 0
5d7a9f56 362 $tempstr = strtolower($param);
363 if ($tempstr == 'on') {
364 $param = 1;
365 } else if ($tempstr == 'off') {
366 $param = 0;
367 } else {
368 $param = empty($param) ? 0 : 1;
369 }
f24148ef 370 }
371
3af57507 372 if ($options & PARAM_NOTAGS) { // Strip all tags completely
373 $param = strip_tags($param);
374 }
375
95bfd207 376 if ($options & PARAM_SAFEDIR) { // Remove everything not a-zA-Z0-9_-
377 $param = eregi_replace('[^a-zA-Z0-9_-]', '', $param);
378 }
379
14d6c233 380 if ($options & PARAM_CLEANFILE) { // allow only safe characters
381 $param = clean_filename($param);
382 }
383
3af57507 384 if ($options & PARAM_FILE) { // Strip all suspicious characters from filename
14d6c233 385 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param);
386 $param = ereg_replace('\.\.+', '', $param);
fd05dffe 387 if($param == '.') {
388 $param = '';
389 }
3af57507 390 }
391
392 if ($options & PARAM_PATH) { // Strip all suspicious characters from file path
d52d5a8e 393 $param = str_replace('\\\'', '\'', $param);
394 $param = str_replace('\\"', '"', $param);
7e6b7f8d 395 $param = str_replace('\\', '/', $param);
14d6c233 396 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
7e6b7f8d 397 $param = ereg_replace('\.\.+', '', $param);
d52d5a8e 398 $param = ereg_replace('//+', '/', $param);
fd05dffe 399 $param = ereg_replace('/(\./)+', '/', $param);
3af57507 400 }
401
371a2ed0 402 if ($options & PARAM_HOST) { // allow FQDN or IPv4 dotted quad
d2a9f7cc 403 preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
4bd2e69a 404 // match ipv4 dotted quad
371a2ed0 405 if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
406 // confirm values are ok
407 if ( $match[0] > 255
408 || $match[1] > 255
d2a9f7cc 409 || $match[3] > 255
371a2ed0 410 || $match[4] > 255 ) {
411 // hmmm, what kind of dotted quad is this?
412 $param = '';
413 }
414 } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
415 && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens
416 && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens
417 ) {
418 // all is ok - $param is respected
419 } else {
420 // all is not ok...
d2a9f7cc 421 $param='';
422 }
371a2ed0 423 }
424
7744ea12 425 if ($options & PARAM_URL) { // allow safe ftp, http, mailto urls
426
427 include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
428
429 //
430 // Parameters to validateurlsyntax()
431 //
432 // s? scheme is optional
433 // H? http optional
434 // S? https optional
435 // F? ftp optional
436 // E? mailto optional
437 // u- user section not allowed
438 // P- password not allowed
439 // a? address optional
440 // I? Numeric IP address optional (can use IP or domain)
441 // p- port not allowed -- restrict to default port
442 // f? "file" path section optional
443 // q? query section optional
444 // r? fragment (anchor) optional
445 //
446 if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p-f?q?r?')) {
447 // all is ok, param is respected
448 } else {
449 $param =''; // not really ok
450 }
31686aea 451 $options ^= PARAM_URL; // Turn off the URL bit so that simple PARAM_URLs don't test true for PARAM_LOCALURL
7744ea12 452 }
453
d2a9f7cc 454 if ($options & PARAM_LOCALURL) {
7744ea12 455 // assume we passed the PARAM_URL test...
456 // allow http absolute, root relative and relative URLs within wwwroot
457 if (!empty($param)) {
d2a9f7cc 458 if (preg_match(':^/:', $param)) {
7744ea12 459 // root-relative, ok!
60ecca3a 460 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
7744ea12 461 // absolute, and matches our wwwroot
d2a9f7cc 462 } else {
7744ea12 463 // relative - let's make sure there are no tricks
464 if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) {
465 // looks ok.
466 } else {
467 $param = '';
d2a9f7cc 468 }
7744ea12 469 }
470 }
471 }
7744ea12 472
2ae28153 473 if ($options & PARAM_CLEANHTML) {
e1ecf0a0 474 $param = stripslashes($param); // Remove any slashes
2ae28153 475 $param = clean_text($param); // Sweep for scripts, etc
476 $param = trim($param); // Sweep for scripts, etc
477 }
478
e0d346ff 479 return $param;
480}
481
7cf1c7bd 482/**
7228f796 483 * For security purposes, this function will check that the currently
484 * given sesskey (passed as a parameter to the script or this function)
485 * matches that of the current user.
7cf1c7bd 486 *
7228f796 487 * @param string $sesskey optionally provided sesskey
bbd3f2c4 488 * @return bool
7228f796 489 */
490function confirm_sesskey($sesskey=NULL) {
491 global $USER;
492
c6a4e7e5 493 if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) {
089e9eae 494 return true;
495 }
496
7228f796 497 if (empty($sesskey)) {
498 $sesskey = required_param('sesskey'); // Check script parameters
499 }
500
501 if (!isset($USER->sesskey)) {
502 return false;
503 }
504
505 return ($USER->sesskey === $sesskey);
506}
507
3a61ccbe 508
7228f796 509/**
510 * Ensure that a variable is set
511 *
512 * If $var is undefined throw an error, otherwise return $var.
513 * This function will soon be made obsolete by {@link required_param()}
7cf1c7bd 514 *
7228f796 515 * @param mixed $var the variable which may be unset
516 * @param mixed $default the value to return if $var is unset
7cf1c7bd 517 */
9fa49e22 518function require_variable($var) {
373da1d4 519 global $CFG;
520 if (!empty($CFG->disableglobalshack)) {
521 error( 'The require_variable() function is deprecated.' );
522 }
9fa49e22 523 if (! isset($var)) {
b0ccd3fb 524 error('A required parameter was missing');
6b174680 525 }
526}
527
7cf1c7bd 528
529/**
530 * Ensure that a variable is set
531 *
532 * If $var is undefined set it (by reference), otherwise return $var.
7cf1c7bd 533 *
534 * @param mixed $var the variable which may be unset
535 * @param mixed $default the value to return if $var is unset
536 */
9fa49e22 537function optional_variable(&$var, $default=0) {
373da1d4 538 global $CFG;
539 if (!empty($CFG->disableglobalshack)) {
7a530277 540 error( "The optional_variable() function is deprecated ($var, $default)." );
373da1d4 541 }
9fa49e22 542 if (! isset($var)) {
543 $var = $default;
6b174680 544 }
545}
546
7a530277 547
7cf1c7bd 548/**
549 * Set a key in global configuration
550 *
89dcb99d 551 * Set a key/value pair in both this session's {@link $CFG} global variable
7cf1c7bd 552 * and in the 'config' database table for future sessions.
e1ecf0a0 553 *
554 * Can also be used to update keys for plugin-scoped configs in config_plugin table.
555 * In that case it doesn't affect $CFG.
7cf1c7bd 556 *
557 * @param string $name the key to set
558 * @param string $value the value to set
a4080313 559 * @param string $plugin (optional) the plugin scope
7cf1c7bd 560 * @uses $CFG
561 * @return bool
562 */
a4080313 563function set_config($name, $value, $plugin=NULL) {
9fa49e22 564/// No need for get_config because they are usually always available in $CFG
70812e39 565
42282810 566 global $CFG;
567
a4080313 568 if (empty($plugin)) {
569 $CFG->$name = $value; // So it's defined for this invocation at least
e1ecf0a0 570
a4080313 571 if (get_field('config', 'name', 'name', $name)) {
572 return set_field('config', 'value', $value, 'name', $name);
573 } else {
574 $config->name = $name;
575 $config->value = $value;
576 return insert_record('config', $config);
577 }
578 } else { // plugin scope
579 if ($id = get_field('config_plugins', 'id', 'name', $name, 'plugin', $plugin)) {
580 return set_field('config_plugins', 'value', $value, 'id', $id);
581 } else {
582 $config->plugin = $plugin;
583 $config->name = $name;
584 $config->value = $value;
585 return insert_record('config_plugins', $config);
586 }
587 }
588}
589
590/**
e1ecf0a0 591 * Get configuration values from the global config table
a4080313 592 * or the config_plugins table.
593 *
594 * If called with no parameters it will do the right thing
595 * generating $CFG safely from the database without overwriting
e1ecf0a0 596 * existing values.
a4080313 597 *
e1ecf0a0 598 * @param string $plugin
599 * @param string $name
a4080313 600 * @uses $CFG
601 * @return hash-like object or single value
602 *
603 */
604function get_config($plugin=NULL, $name=NULL) {
7cf1c7bd 605
a4080313 606 global $CFG;
dfc9ba9b 607
a4080313 608 if (!empty($name)) { // the user is asking for a specific value
609 if (!empty($plugin)) {
610 return get_record('config_plugins', 'plugin' , $plugin, 'name', $name);
611 } else {
612 return get_record('config', 'name', $name);
613 }
614 }
615
616 // the user is after a recordset
617 if (!empty($plugin)) {
618 if ($configs=get_records('config_plugins', 'plugin', $plugin, '', 'name,value')) {
619 $configs = (array)$configs;
620 $localcfg = array();
621 foreach ($configs as $config) {
622 $localcfg[$config->name] = $config->value;
623 }
624 return (object)$localcfg;
625 } else {
626 return false;
627 }
d897cae4 628 } else {
a4080313 629 // this was originally in setup.php
630 if ($configs = get_records('config')) {
631 $localcfg = (array)$CFG;
632 foreach ($configs as $config) {
633 if (!isset($localcfg[$config->name])) {
634 $localcfg[$config->name] = $config->value;
635 } else {
e1ecf0a0 636 if ($localcfg[$config->name] != $config->value ) {
a4080313 637 // complain if the DB has a different
638 // value than config.php does
639 error_log("\$CFG->{$config->name} in config.php ({$localcfg[$config->name]}) overrides database setting ({$config->value})");
640 }
641 }
642 }
e1ecf0a0 643
a4080313 644 $localcfg = (object)$localcfg;
645 return $localcfg;
646 } else {
647 // preserve $CFG if DB returns nothing or error
648 return $CFG;
649 }
e1ecf0a0 650
39917a09 651 }
39917a09 652}
653
a4080313 654
7cf1c7bd 655/**
656 * Refresh current $USER session global variable with all their current preferences.
657 * @uses $USER
658 */
70812e39 659function reload_user_preferences() {
70812e39 660
661 global $USER;
662
070e2616 663 if(empty($USER) || empty($USER->id)) {
664 return false;
665 }
666
d8ba183c 667 unset($USER->preference);
70812e39 668
669 if ($preferences = get_records('user_preferences', 'userid', $USER->id)) {
670 foreach ($preferences as $preference) {
671 $USER->preference[$preference->name] = $preference->value;
672 }
4586d60c 673 } else {
674 //return empty preference array to hold new values
675 $USER->preference = array();
c6d15803 676 }
70812e39 677}
678
7cf1c7bd 679/**
680 * Sets a preference for the current user
681 * Optionally, can set a preference for a different user object
682 * @uses $USER
68fbd8e1 683 * @todo Add a better description and include usage examples. Add inline links to $USER and user functions in above line.
684
7cf1c7bd 685 * @param string $name The key to set as preference for the specified user
686 * @param string $value The value to set forthe $name key in the specified user's record
c6d15803 687 * @param int $userid A moodle user ID
bbd3f2c4 688 * @return bool
7cf1c7bd 689 */
13af52a6 690function set_user_preference($name, $value, $otheruser=NULL) {
70812e39 691
692 global $USER;
693
13af52a6 694 if (empty($otheruser)){
695 if (!empty($USER) && !empty($USER->id)) {
070e2616 696 $userid = $USER->id;
13af52a6 697 } else {
070e2616 698 return false;
699 }
13af52a6 700 } else {
701 $userid = $otheruser;
d35757eb 702 }
703
70812e39 704 if (empty($name)) {
705 return false;
706 }
707
a3f1f815 708 if ($preference = get_record('user_preferences', 'userid', $userid, 'name', $name)) {
b0ccd3fb 709 if (set_field('user_preferences', 'value', $value, 'id', $preference->id)) {
13af52a6 710 if (empty($otheruser) and !empty($USER)) {
070e2616 711 $USER->preference[$name] = $value;
712 }
066af654 713 return true;
714 } else {
715 return false;
716 }
70812e39 717
718 } else {
a3f1f815 719 $preference->userid = $userid;
70812e39 720 $preference->name = $name;
721 $preference->value = (string)$value;
066af654 722 if (insert_record('user_preferences', $preference)) {
13af52a6 723 if (empty($otheruser) and !empty($USER)) {
070e2616 724 $USER->preference[$name] = $value;
725 }
70812e39 726 return true;
727 } else {
728 return false;
729 }
730 }
731}
732
6eb3e776 733/**
734 * Unsets a preference completely by deleting it from the database
735 * Optionally, can set a preference for a different user id
736 * @uses $USER
737 * @param string $name The key to unset as preference for the specified user
c6d15803 738 * @param int $userid A moodle user ID
bbd3f2c4 739 * @return bool
6eb3e776 740 */
741function unset_user_preference($name, $userid=NULL) {
742
743 global $USER;
744
361855e6 745 if (empty($userid)){
070e2616 746 if(!empty($USER) && !empty($USER->id)) {
747 $userid = $USER->id;
748 }
749 else {
750 return false;
751 }
6eb3e776 752 }
753
49d005ee 754 //Delete the preference from $USER
755 if (isset($USER->preference[$name])) {
756 unset($USER->preference[$name]);
757 }
e1ecf0a0 758
49d005ee 759 //Then from DB
6eb3e776 760 return delete_records('user_preferences', 'userid', $userid, 'name', $name);
761}
762
763
7cf1c7bd 764/**
765 * Sets a whole array of preferences for the current user
766 * @param array $prefarray An array of key/value pairs to be set
c6d15803 767 * @param int $userid A moodle user ID
bbd3f2c4 768 * @return bool
7cf1c7bd 769 */
a3f1f815 770function set_user_preferences($prefarray, $userid=NULL) {
771
772 global $USER;
70812e39 773
774 if (!is_array($prefarray) or empty($prefarray)) {
775 return false;
776 }
777
361855e6 778 if (empty($userid)){
108adee2 779 if (!empty($USER) && !empty($USER->id)) {
780 $userid = NULL; // Continue with the current user below
781 } else {
782 return false; // No-one to set!
070e2616 783 }
a3f1f815 784 }
785
70812e39 786 $return = true;
787 foreach ($prefarray as $name => $value) {
070e2616 788 // The order is important; if the test for return is done first, then
789 // if one function call fails all the remaining ones will be "optimized away"
a3f1f815 790 $return = set_user_preference($name, $value, $userid) and $return;
70812e39 791 }
792 return $return;
793}
794
7cf1c7bd 795/**
796 * If no arguments are supplied this function will return
361855e6 797 * all of the current user preferences as an array.
7cf1c7bd 798 * If a name is specified then this function
799 * attempts to return that particular preference value. If
800 * none is found, then the optional value $default is returned,
801 * otherwise NULL.
802 * @param string $name Name of the key to use in finding a preference value
803 * @param string $default Value to be returned if the $name key is not set in the user preferences
c6d15803 804 * @param int $userid A moodle user ID
7cf1c7bd 805 * @uses $USER
806 * @return string
807 */
a3f1f815 808function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {
70812e39 809
810 global $USER;
811
a3f1f815 812 if (empty($userid)) { // assume current user
813 if (empty($USER->preference)) {
814 return $default; // Default value (or NULL)
815 }
816 if (empty($name)) {
817 return $USER->preference; // Whole array
818 }
819 if (!isset($USER->preference[$name])) {
820 return $default; // Default value (or NULL)
821 }
822 return $USER->preference[$name]; // The single value
823
824 } else {
825 $preference = get_records_menu('user_preferences', 'userid', $userid, 'name', 'name,value');
826
827 if (empty($name)) {
828 return $preference;
829 }
830 if (!isset($preference[$name])) {
831 return $default; // Default value (or NULL)
832 }
833 return $preference[$name]; // The single value
70812e39 834 }
70812e39 835}
836
837
9fa49e22 838/// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
39917a09 839
7cf1c7bd 840/**
c6d15803 841 * Given date parts in user time produce a GMT timestamp.
7cf1c7bd 842 *
68fbd8e1 843 * @param int $year The year part to create timestamp of
844 * @param int $month The month part to create timestamp of
845 * @param int $day The day part to create timestamp of
846 * @param int $hour The hour part to create timestamp of
847 * @param int $minute The minute part to create timestamp of
848 * @param int $second The second part to create timestamp of
849 * @param float $timezone ?
850 * @param bool $applydst ?
e34d817e 851 * @return int timestamp
7cf1c7bd 852 * @todo Finish documenting this function
853 */
9f1f6daf 854function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
39917a09 855
dddb014a 856 $timezone = get_user_timezone_offset($timezone);
857
94e34118 858 if (abs($timezone) > 13) {
68fbd8e1 859 $time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
03c17ddf 860 } else {
68fbd8e1 861 $time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
196f2619 862 $time = usertime($time, $timezone);
28c66824 863 if($applydst) {
864 $time -= dst_offset_on($time);
865 }
9f1f6daf 866 }
867
196f2619 868 return $time;
85cafb3e 869
39917a09 870}
871
7cf1c7bd 872/**
873 * Given an amount of time in seconds, returns string
874 * formatted nicely as months, days, hours etc as needed
875 *
2f87145b 876 * @uses MINSECS
877 * @uses HOURSECS
878 * @uses DAYSECS
c6d15803 879 * @param int $totalsecs ?
880 * @param array $str ?
89dcb99d 881 * @return string
7cf1c7bd 882 * @todo Finish documenting this function
883 */
884 function format_time($totalsecs, $str=NULL) {
c7e3ac2a 885
6b174680 886 $totalsecs = abs($totalsecs);
c7e3ac2a 887
8dbed6be 888 if (!$str) { // Create the str structure the slow way
b0ccd3fb 889 $str->day = get_string('day');
890 $str->days = get_string('days');
891 $str->hour = get_string('hour');
892 $str->hours = get_string('hours');
893 $str->min = get_string('min');
894 $str->mins = get_string('mins');
895 $str->sec = get_string('sec');
896 $str->secs = get_string('secs');
8dbed6be 897 }
898
7a5672c9 899 $days = floor($totalsecs/DAYSECS);
900 $remainder = $totalsecs - ($days*DAYSECS);
901 $hours = floor($remainder/HOURSECS);
902 $remainder = $remainder - ($hours*HOURSECS);
903 $mins = floor($remainder/MINSECS);
904 $secs = $remainder - ($mins*MINSECS);
8dbed6be 905
906 $ss = ($secs == 1) ? $str->sec : $str->secs;
907 $sm = ($mins == 1) ? $str->min : $str->mins;
908 $sh = ($hours == 1) ? $str->hour : $str->hours;
909 $sd = ($days == 1) ? $str->day : $str->days;
910
b0ccd3fb 911 $odays = '';
912 $ohours = '';
913 $omins = '';
914 $osecs = '';
9c9f7d77 915
b0ccd3fb 916 if ($days) $odays = $days .' '. $sd;
917 if ($hours) $ohours = $hours .' '. $sh;
918 if ($mins) $omins = $mins .' '. $sm;
919 if ($secs) $osecs = $secs .' '. $ss;
6b174680 920
b0ccd3fb 921 if ($days) return $odays .' '. $ohours;
922 if ($hours) return $ohours .' '. $omins;
923 if ($mins) return $omins .' '. $osecs;
924 if ($secs) return $osecs;
925 return get_string('now');
6b174680 926}
f9903ed0 927
7cf1c7bd 928/**
929 * Returns a formatted string that represents a date in user time
930 * <b>WARNING: note that the format is for strftime(), not date().</b>
931 * Because of a bug in most Windows time libraries, we can't use
932 * the nicer %e, so we have to use %d which has leading zeroes.
933 * A lot of the fuss in the function is just getting rid of these leading
934 * zeroes as efficiently as possible.
361855e6 935 *
8c3dba73 936 * If parameter fixday = true (default), then take off leading
7cf1c7bd 937 * zero from %d, else mantain it.
938 *
2f87145b 939 * @uses HOURSECS
e34d817e 940 * @param int $date timestamp in GMT
941 * @param string $format strftime format
d2a9f7cc 942 * @param float $timezone
bbd3f2c4 943 * @param bool $fixday If true (default) then the leading
c6d15803 944 * zero from %d is removed. If false then the leading zero is mantained.
945 * @return string
7cf1c7bd 946 */
b0ccd3fb 947function userdate($date, $format='', $timezone=99, $fixday = true) {
7a302afc 948
1ac7ee24 949 global $CFG;
950
951 static $strftimedaydatetime;
102dc313 952
b0ccd3fb 953 if ($format == '') {
1ac7ee24 954 if (empty($strftimedaydatetime)) {
955 $strftimedaydatetime = get_string('strftimedaydatetime');
956 }
957 $format = $strftimedaydatetime;
5fa51a39 958 }
035cdbff 959
c3a3c5b8 960 if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed.
961 $fixday = false;
962 } else if ($fixday) {
963 $formatnoday = str_replace('%d', 'DD', $format);
61ae5d36 964 $fixday = ($formatnoday != $format);
965 }
dcde9f02 966
88ec5b7c 967 $date += dst_offset_on($date);
85351042 968
494b9296 969 $timezone = get_user_timezone_offset($timezone);
102dc313 970
971 if (abs($timezone) > 13) { /// Server time
d2a9f7cc 972 if ($fixday) {
102dc313 973 $datestring = strftime($formatnoday, $date);
974 $daystring = str_replace(' 0', '', strftime(' %d', $date));
975 $datestring = str_replace('DD', $daystring, $datestring);
976 } else {
977 $datestring = strftime($format, $date);
978 }
88ec5b7c 979 } else {
102dc313 980 $date += (int)($timezone * 3600);
981 if ($fixday) {
982 $datestring = gmstrftime($formatnoday, $date);
983 $daystring = str_replace(' 0', '', gmstrftime(' %d', $date));
984 $datestring = str_replace('DD', $daystring, $datestring);
985 } else {
986 $datestring = gmstrftime($format, $date);
987 }
88ec5b7c 988 }
102dc313 989
035cdbff 990 return $datestring;
873960de 991}
992
7cf1c7bd 993/**
196f2619 994 * Given a $time timestamp in GMT (seconds since epoch),
c6d15803 995 * returns an array that represents the date in user time
7cf1c7bd 996 *
2f87145b 997 * @uses HOURSECS
196f2619 998 * @param int $time Timestamp in GMT
68fbd8e1 999 * @param float $timezone ?
c6d15803 1000 * @return array An array that represents the date in user time
7cf1c7bd 1001 * @todo Finish documenting this function
1002 */
196f2619 1003function usergetdate($time, $timezone=99) {
6b174680 1004
494b9296 1005 $timezone = get_user_timezone_offset($timezone);
a36166d3 1006
e34d817e 1007 if (abs($timezone) > 13) { // Server time
ed1f69b0 1008 return getdate($time);
d2a9f7cc 1009 }
1010
e34d817e 1011 // There is no gmgetdate so we use gmdate instead
02f0527d 1012 $time += dst_offset_on($time);
e34d817e 1013 $time += intval((float)$timezone * HOURSECS);
3bba1e6e 1014
1015 $datestring = gmstrftime('%S_%M_%H_%d_%m_%Y_%w_%j_%A_%B', $time);
02f0527d 1016
9f1f6daf 1017 list(
1018 $getdate['seconds'],
1019 $getdate['minutes'],
1020 $getdate['hours'],
1021 $getdate['mday'],
1022 $getdate['mon'],
1023 $getdate['year'],
1024 $getdate['wday'],
1025 $getdate['yday'],
1026 $getdate['weekday'],
1027 $getdate['month']
3bba1e6e 1028 ) = explode('_', $datestring);
9f1f6daf 1029
d2d6171f 1030 return $getdate;
d552ead0 1031}
1032
7cf1c7bd 1033/**
1034 * Given a GMT timestamp (seconds since epoch), offsets it by
1035 * the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds
1036 *
2f87145b 1037 * @uses HOURSECS
c6d15803 1038 * @param int $date Timestamp in GMT
e34d817e 1039 * @param float $timezone
c6d15803 1040 * @return int
7cf1c7bd 1041 */
d552ead0 1042function usertime($date, $timezone=99) {
a36166d3 1043
494b9296 1044 $timezone = get_user_timezone_offset($timezone);
2665e47a 1045
0431bd7c 1046 if (abs($timezone) > 13) {
d552ead0 1047 return $date;
1048 }
7a5672c9 1049 return $date - (int)($timezone * HOURSECS);
d552ead0 1050}
1051
8c3dba73 1052/**
1053 * Given a time, return the GMT timestamp of the most recent midnight
1054 * for the current user.
1055 *
e34d817e 1056 * @param int $date Timestamp in GMT
1057 * @param float $timezone ?
c6d15803 1058 * @return ?
8c3dba73 1059 */
edf7fe8c 1060function usergetmidnight($date, $timezone=99) {
edf7fe8c 1061
494b9296 1062 $timezone = get_user_timezone_offset($timezone);
edf7fe8c 1063 $userdate = usergetdate($date, $timezone);
4606d9bb 1064
02f0527d 1065 // Time of midnight of this user's day, in GMT
1066 return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone);
edf7fe8c 1067
1068}
1069
7cf1c7bd 1070/**
1071 * Returns a string that prints the user's timezone
1072 *
1073 * @param float $timezone The user's timezone
1074 * @return string
1075 */
d552ead0 1076function usertimezone($timezone=99) {
d552ead0 1077
0c244315 1078 $tz = get_user_timezone($timezone);
f30fe8d0 1079
0c244315 1080 if (!is_float($tz)) {
1081 return $tz;
d552ead0 1082 }
0c244315 1083
1084 if(abs($tz) > 13) { // Server time
1085 return get_string('serverlocaltime');
1086 }
1087
1088 if($tz == intval($tz)) {
1089 // Don't show .0 for whole hours
1090 $tz = intval($tz);
1091 }
1092
1093 if($tz == 0) {
b0ccd3fb 1094 return 'GMT';
d552ead0 1095 }
0c244315 1096 else if($tz > 0) {
1097 return 'GMT+'.$tz;
1098 }
1099 else {
1100 return 'GMT'.$tz;
d552ead0 1101 }
e1ecf0a0 1102
f9903ed0 1103}
1104
7cf1c7bd 1105/**
1106 * Returns a float which represents the user's timezone difference from GMT in hours
1107 * Checks various settings and picks the most dominant of those which have a value
1108 *
7cf1c7bd 1109 * @uses $CFG
1110 * @uses $USER
b2b68362 1111 * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked
c6d15803 1112 * @return int
7cf1c7bd 1113 */
494b9296 1114function get_user_timezone_offset($tz = 99) {
f30fe8d0 1115
43b59916 1116 global $USER, $CFG;
1117
e8904995 1118 $tz = get_user_timezone($tz);
c9e55a25 1119
7b9e355e 1120 if (is_float($tz)) {
1121 return $tz;
1122 } else {
e8904995 1123 $tzrecord = get_timezone_record($tz);
7b9e355e 1124 if (empty($tzrecord)) {
e8904995 1125 return 99.0;
1126 }
4f2dbde9 1127 return (float)$tzrecord->gmtoff / HOURMINS;
e8904995 1128 }
1129}
1130
bbd3f2c4 1131/**
b2b68362 1132 * Returns a float or a string which denotes the user's timezone
1133 * A float value means that a simple offset from GMT is used, while a string (it will be the name of a timezone in the database)
1134 * means that for this timezone there are also DST rules to be taken into account
1135 * Checks various settings and picks the most dominant of those which have a value
bbd3f2c4 1136 *
1137 * @uses $USER
1138 * @uses $CFG
b2b68362 1139 * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked
1140 * @return mixed
bbd3f2c4 1141 */
e8904995 1142function get_user_timezone($tz = 99) {
1143 global $USER, $CFG;
43b59916 1144
f30fe8d0 1145 $timezones = array(
e8904995 1146 $tz,
1147 isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99,
43b59916 1148 isset($USER->timezone) ? $USER->timezone : 99,
1149 isset($CFG->timezone) ? $CFG->timezone : 99,
f30fe8d0 1150 );
43b59916 1151
e8904995 1152 $tz = 99;
43b59916 1153
e8904995 1154 while(($tz == '' || $tz == 99) && $next = each($timezones)) {
1155 $tz = $next['value'];
43b59916 1156 }
e8904995 1157
1158 return is_numeric($tz) ? (float) $tz : $tz;
43b59916 1159}
1160
bbd3f2c4 1161/**
1162 * ?
1163 *
1164 * @uses $CFG
1165 * @uses $db
1166 * @param string $timezonename ?
1167 * @return object
1168 */
43b59916 1169function get_timezone_record($timezonename) {
1170 global $CFG, $db;
1171 static $cache = NULL;
1172
8edffd15 1173 if ($cache === NULL) {
43b59916 1174 $cache = array();
1175 }
1176
8edffd15 1177 if (isset($cache[$timezonename])) {
43b59916 1178 return $cache[$timezonename];
f30fe8d0 1179 }
1180
e1ecf0a0 1181 return get_record_sql('SELECT * FROM '.$CFG->prefix.'timezone
e84a246a 1182 WHERE name = '.$db->qstr($timezonename).' ORDER BY year DESC', true);
f30fe8d0 1183}
f9903ed0 1184
bbd3f2c4 1185/**
1186 * ?
1187 *
1188 * @uses $CFG
1189 * @uses $USER
1190 * @param ? $fromyear ?
1191 * @param ? $to_year ?
1192 * @return bool
1193 */
830a2bbd 1194function calculate_user_dst_table($from_year = NULL, $to_year = NULL) {
2280ecf5 1195 global $CFG, $SESSION;
85cafb3e 1196
989585e9 1197 $usertz = get_user_timezone();
7cb29a3d 1198
989585e9 1199 if (is_float($usertz)) {
1200 // Trivial timezone, no DST
1201 return false;
1202 }
1203
2280ecf5 1204 if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) {
989585e9 1205 // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset
2280ecf5 1206 unset($SESSION->dst_offsets);
1207 unset($SESSION->dst_range);
830a2bbd 1208 }
1209
2280ecf5 1210 if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) {
830a2bbd 1211 // Repeat calls which do not request specific year ranges stop here, we have already calculated the table
1212 // This will be the return path most of the time, pretty light computationally
1213 return true;
85cafb3e 1214 }
1215
830a2bbd 1216 // Reaching here means we either need to extend our table or create it from scratch
989585e9 1217
1218 // Remember which TZ we calculated these changes for
2280ecf5 1219 $SESSION->dst_offsettz = $usertz;
989585e9 1220
2280ecf5 1221 if(empty($SESSION->dst_offsets)) {
830a2bbd 1222 // If we 're creating from scratch, put the two guard elements in there
2280ecf5 1223 $SESSION->dst_offsets = array(1 => NULL, 0 => NULL);
830a2bbd 1224 }
2280ecf5 1225 if(empty($SESSION->dst_range)) {
830a2bbd 1226 // If creating from scratch
1227 $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971);
1228 $to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035);
1229
1230 // Fill in the array with the extra years we need to process
1231 $yearstoprocess = array();
1232 for($i = $from; $i <= $to; ++$i) {
1233 $yearstoprocess[] = $i;
1234 }
1235
1236 // Take note of which years we have processed for future calls
2280ecf5 1237 $SESSION->dst_range = array($from, $to);
830a2bbd 1238 }
1239 else {
1240 // If needing to extend the table, do the same
1241 $yearstoprocess = array();
1242
2280ecf5 1243 $from = max((empty($from_year) ? $SESSION->dst_range[0] : $from_year), 1971);
1244 $to = min((empty($to_year) ? $SESSION->dst_range[1] : $to_year), 2035);
830a2bbd 1245
2280ecf5 1246 if($from < $SESSION->dst_range[0]) {
830a2bbd 1247 // Take note of which years we need to process and then note that we have processed them for future calls
2280ecf5 1248 for($i = $from; $i < $SESSION->dst_range[0]; ++$i) {
830a2bbd 1249 $yearstoprocess[] = $i;
1250 }
2280ecf5 1251 $SESSION->dst_range[0] = $from;
830a2bbd 1252 }
2280ecf5 1253 if($to > $SESSION->dst_range[1]) {
830a2bbd 1254 // Take note of which years we need to process and then note that we have processed them for future calls
2280ecf5 1255 for($i = $SESSION->dst_range[1] + 1; $i <= $to; ++$i) {
830a2bbd 1256 $yearstoprocess[] = $i;
1257 }
2280ecf5 1258 $SESSION->dst_range[1] = $to;
830a2bbd 1259 }
1260 }
1261
1262 if(empty($yearstoprocess)) {
1263 // This means that there was a call requesting a SMALLER range than we have already calculated
1264 return true;
1265 }
1266
1267 // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need
1268 // Also, the array is sorted in descending timestamp order!
1269
1270 // Get DB data
989585e9 1271 $presetrecords = get_records('timezone', 'name', $usertz, 'year DESC', 'year, gmtoff, dstoff, dst_month, dst_startday, dst_weekday, dst_skipweeks, dst_time, std_month, std_startday, std_weekday, std_skipweeks, std_time');
e789650d 1272 if(empty($presetrecords)) {
1273 return false;
1274 }
57f1191c 1275
830a2bbd 1276 // Remove ending guard (first element of the array)
2280ecf5 1277 reset($SESSION->dst_offsets);
1278 unset($SESSION->dst_offsets[key($SESSION->dst_offsets)]);
830a2bbd 1279
1280 // Add all required change timestamps
1281 foreach($yearstoprocess as $y) {
1282 // Find the record which is in effect for the year $y
1283 foreach($presetrecords as $year => $preset) {
1284 if($year <= $y) {
1285 break;
c9e72798 1286 }
830a2bbd 1287 }
1288
1289 $changes = dst_changes_for_year($y, $preset);
1290
1291 if($changes === NULL) {
1292 continue;
1293 }
1294 if($changes['dst'] != 0) {
2280ecf5 1295 $SESSION->dst_offsets[$changes['dst']] = $preset->dstoff * MINSECS;
830a2bbd 1296 }
1297 if($changes['std'] != 0) {
2280ecf5 1298 $SESSION->dst_offsets[$changes['std']] = 0;
c9e72798 1299 }
85cafb3e 1300 }
42d36497 1301
830a2bbd 1302 // Put in a guard element at the top
2280ecf5 1303 $maxtimestamp = max(array_keys($SESSION->dst_offsets));
1304 $SESSION->dst_offsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any "small" number will do
830a2bbd 1305
1306 // Sort again
2280ecf5 1307 krsort($SESSION->dst_offsets);
830a2bbd 1308
e789650d 1309 return true;
1310}
42d36497 1311
e789650d 1312function dst_changes_for_year($year, $timezone) {
7cb29a3d 1313
e789650d 1314 if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) {
1315 return NULL;
42d36497 1316 }
7cb29a3d 1317
e789650d 1318 $monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year);
1319 $monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year);
1320
1321 list($dst_hour, $dst_min) = explode(':', $timezone->dst_time);
1322 list($std_hour, $std_min) = explode(':', $timezone->std_time);
d2a9f7cc 1323
6dc8dddc 1324 $timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false);
1325 $timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false);
830a2bbd 1326
1327 // Instead of putting hour and minute in make_timestamp(), we add them afterwards.
1328 // This has the advantage of being able to have negative values for hour, i.e. for timezones
1329 // where GMT time would be in the PREVIOUS day than the local one on which DST changes.
1330
1331 $timedst += $dst_hour * HOURSECS + $dst_min * MINSECS;
1332 $timestd += $std_hour * HOURSECS + $std_min * MINSECS;
42d36497 1333
e789650d 1334 return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd);
42d36497 1335}
1336
02f0527d 1337// $time must NOT be compensated at all, it has to be a pure timestamp
1338function dst_offset_on($time) {
2280ecf5 1339 global $SESSION;
02f0527d 1340
2280ecf5 1341 if(!calculate_user_dst_table() || empty($SESSION->dst_offsets)) {
c9e72798 1342 return 0;
85cafb3e 1343 }
1344
2280ecf5 1345 reset($SESSION->dst_offsets);
1346 while(list($from, $offset) = each($SESSION->dst_offsets)) {
59556d48 1347 if($from <= $time) {
c9e72798 1348 break;
1349 }
1350 }
1351
830a2bbd 1352 // This is the normal return path
1353 if($offset !== NULL) {
1354 return $offset;
02f0527d 1355 }
02f0527d 1356
830a2bbd 1357 // Reaching this point means we haven't calculated far enough, do it now:
1358 // Calculate extra DST changes if needed and recurse. The recursion always
1359 // moves toward the stopping condition, so will always end.
1360
1361 if($from == 0) {
2280ecf5 1362 // We need a year smaller than $SESSION->dst_range[0]
1363 if($SESSION->dst_range[0] == 1971) {
830a2bbd 1364 return 0;
1365 }
2280ecf5 1366 calculate_user_dst_table($SESSION->dst_range[0] - 5, NULL);
830a2bbd 1367 return dst_offset_on($time);
1368 }
1369 else {
2280ecf5 1370 // We need a year larger than $SESSION->dst_range[1]
1371 if($SESSION->dst_range[1] == 2035) {
830a2bbd 1372 return 0;
1373 }
2280ecf5 1374 calculate_user_dst_table(NULL, $SESSION->dst_range[1] + 5);
830a2bbd 1375 return dst_offset_on($time);
1376 }
85cafb3e 1377}
02f0527d 1378
28902d99 1379function find_day_in_month($startday, $weekday, $month, $year) {
8dc3f6cf 1380
1381 $daysinmonth = days_in_month($month, $year);
1382
42d36497 1383 if($weekday == -1) {
28902d99 1384 // Don't care about weekday, so return:
1385 // abs($startday) if $startday != -1
1386 // $daysinmonth otherwise
1387 return ($startday == -1) ? $daysinmonth : abs($startday);
8dc3f6cf 1388 }
1389
1390 // From now on we 're looking for a specific weekday
8dc3f6cf 1391
28902d99 1392 // Give "end of month" its actual value, since we know it
1393 if($startday == -1) {
1394 $startday = -1 * $daysinmonth;
1395 }
1396
1397 // Starting from day $startday, the sign is the direction
8dc3f6cf 1398
28902d99 1399 if($startday < 1) {
8dc3f6cf 1400
28902d99 1401 $startday = abs($startday);
8dc3f6cf 1402 $lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
1403
1404 // This is the last such weekday of the month
1405 $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday;
1406 if($lastinmonth > $daysinmonth) {
1407 $lastinmonth -= 7;
42d36497 1408 }
8dc3f6cf 1409
28902d99 1410 // Find the first such weekday <= $startday
1411 while($lastinmonth > $startday) {
8dc3f6cf 1412 $lastinmonth -= 7;
42d36497 1413 }
8dc3f6cf 1414
1415 return $lastinmonth;
e1ecf0a0 1416
42d36497 1417 }
1418 else {
42d36497 1419
28902d99 1420 $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year, 0));
42d36497 1421
8dc3f6cf 1422 $diff = $weekday - $indexweekday;
1423 if($diff < 0) {
1424 $diff += 7;
42d36497 1425 }
42d36497 1426
28902d99 1427 // This is the first such weekday of the month equal to or after $startday
1428 $firstfromindex = $startday + $diff;
42d36497 1429
8dc3f6cf 1430 return $firstfromindex;
1431
1432 }
42d36497 1433}
1434
bbd3f2c4 1435/**
1436 * Calculate the number of days in a given month
1437 *
1438 * @param int $month The month whose day count is sought
1439 * @param int $year The year of the month whose day count is sought
1440 * @return int
1441 */
42d36497 1442function days_in_month($month, $year) {
1443 return intval(date('t', mktime(12, 0, 0, $month, 1, $year, 0)));
1444}
1445
bbd3f2c4 1446/**
1447 * Calculate the position in the week of a specific calendar day
1448 *
1449 * @param int $day The day of the date whose position in the week is sought
1450 * @param int $month The month of the date whose position in the week is sought
1451 * @param int $year The year of the date whose position in the week is sought
1452 * @return int
1453 */
8dc3f6cf 1454function dayofweek($day, $month, $year) {
1455 // I wonder if this is any different from
1456 // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
1457 return intval(date('w', mktime(12, 0, 0, $month, $day, $year, 0)));
1458}
1459
9fa49e22 1460/// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////
f9903ed0 1461
bbd3f2c4 1462/**
1463 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
1464 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
1465 * sesskey string if $USER exists, or boolean false if not.
1466 *
1467 * @uses $USER
1468 * @return string
1469 */
04280e85 1470function sesskey() {
1a33f699 1471 global $USER;
1472
1473 if(!isset($USER)) {
1474 return false;
1475 }
1476
1477 if (empty($USER->sesskey)) {
1478 $USER->sesskey = random_string(10);
1479 }
1480
1481 return $USER->sesskey;
1482}
1483
0302c52f 1484/* this function forces a user to log out */
1485
1486function require_logout() {
527ec51a 1487 global $USER, $CFG;
0302c52f 1488 if (isset($USER) and isset($USER->id)) {
1489 add_to_log(SITEID, "user", "logout", "view.php?id=$USER->id&course=".SITEID, $USER->id, 0, $USER->id);
1490
1491 if ($USER->auth == 'cas' && !empty($CFG->cas_enabled)) {
1492 require($CFG->dirroot.'/auth/cas/logout.php');
1493 }
1494 }
1495
1496 if (ini_get_bool("register_globals") and check_php_version("4.3.0")) {
1497 // This method is just to try to avoid silly warnings from PHP 4.3.0
1498 session_unregister("USER");
1499 session_unregister("SESSION");
1500 }
1501
1502 setcookie('MoodleSessionTest'.$CFG->sessioncookie, '', time() - 3600, '/');
1503 unset($_SESSION['USER']);
1504 unset($_SESSION['SESSION']);
1505
1506 unset($SESSION);
1507 unset($USER);
1508
1509}
7cf1c7bd 1510/**
ec81373f 1511 * This function checks that the current user is logged in and has the
1512 * required privileges
1513 *
7cf1c7bd 1514 * This function checks that the current user is logged in, and optionally
ec81373f 1515 * whether they are allowed to be in a particular course and view a particular
1516 * course module.
1517 * If they are not logged in, then it redirects them to the site login unless
d2a9f7cc 1518 * $autologinguest is set and {@link $CFG}->autologinguests is set to 1 in which
ec81373f 1519 * case they are automatically logged in as guests.
1520 * If $courseid is given and the user is not enrolled in that course then the
1521 * user is redirected to the course enrolment page.
1522 * If $cm is given and the coursemodule is hidden and the user is not a teacher
1523 * in the course then the user is redirected to the course home page.
7cf1c7bd 1524 *
7cf1c7bd 1525 * @uses $CFG
c6d15803 1526 * @uses $SESSION
7cf1c7bd 1527 * @uses $USER
1528 * @uses $FULLME
c6d15803 1529 * @uses SITEID
7cf1c7bd 1530 * @uses $MoodleSession
ec81373f 1531 * @param int $courseid id of the course
bbd3f2c4 1532 * @param bool $autologinguest
1533 * @param object $cm course module object
7cf1c7bd 1534 */
ec81373f 1535function require_login($courseid=0, $autologinguest=true, $cm=null) {
f9903ed0 1536
73047f2f 1537 global $CFG, $SESSION, $USER, $FULLME, $MoodleSession;
d8ba183c 1538
da5c172a 1539 // First check that the user is logged in to the site.
c21c671d 1540 if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
f9903ed0 1541 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1542 if (!empty($_SERVER['HTTP_REFERER'])) {
1543 $SESSION->fromurl = $_SERVER['HTTP_REFERER'];
9f44d972 1544 }
c21c671d 1545 $USER = NULL;
8f23e33b 1546 if ($autologinguest and $CFG->autologinguests and $courseid and ($courseid == SITEID or get_field('course','guest','id',$courseid)) ) {
8e8d0524 1547 $loginguest = '?loginguest=true';
1548 } else {
1549 $loginguest = '';
a2ebe6a5 1550 }
8a33e371 1551 if (empty($CFG->loginhttps)) {
b0ccd3fb 1552 redirect($CFG->wwwroot .'/login/index.php'. $loginguest);
8a33e371 1553 } else {
b0ccd3fb 1554 $wwwroot = str_replace('http','https', $CFG->wwwroot);
1555 redirect($wwwroot .'/login/index.php'. $loginguest);
8a33e371 1556 }
20fde7b1 1557 exit;
f9903ed0 1558 }
808a3baa 1559
d35757eb 1560 // check whether the user should be changing password
027a1604 1561 // reload_user_preferences(); // Why is this necessary? Seems wasteful. - MD
a3f1f815 1562 if (!empty($USER->preference['auth_forcepasswordchange'])){
d35757eb 1563 if (is_internal_auth() || $CFG->{'auth_'.$USER->auth.'_stdchangepassword'}){
20fde7b1 1564 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1565 redirect($CFG->wwwroot .'/login/change_password.php');
d35757eb 1566 } elseif($CFG->changepassword) {
1567 redirect($CFG->changepassword);
1568 } else {
361855e6 1569 error('You cannot proceed without changing your password.
d35757eb 1570 However there is no available page for changing it.
b0ccd3fb 1571 Please contact your Moodle Administrator.');
d35757eb 1572 }
1573 }
808a3baa 1574 // Check that the user account is properly set up
1575 if (user_not_fully_set_up($USER)) {
20fde7b1 1576 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1577 redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
808a3baa 1578 }
d8ba183c 1579
366dfa60 1580 // Make sure current IP matches the one for this session (if required)
361855e6 1581 if (!empty($CFG->tracksessionip)) {
366dfa60 1582 if ($USER->sessionIP != md5(getremoteaddr())) {
1583 error(get_string('sessionipnomatch', 'error'));
1584 }
1585 }
6d8f47d6 1586
1587 // Make sure the USER has a sesskey set up. Used for checking script parameters.
04280e85 1588 sesskey();
366dfa60 1589
027a1604 1590 // Check that the user has agreed to a site policy if there is one
1591 if (!empty($CFG->sitepolicy)) {
1592 if (!$USER->policyagreed) {
957b5198 1593 $SESSION->wantsurl = $FULLME;
027a1604 1594 redirect($CFG->wwwroot .'/user/policy.php');
027a1604 1595 }
1695b680 1596 }
1597
1598 // If the site is currently under maintenance, then print a message
1599 if (!isadmin()) {
eeefd0b0 1600 if (file_exists($CFG->dataroot.'/'.SITEID.'/maintenance.html')) {
1695b680 1601 print_maintenance_message();
20fde7b1 1602 exit;
1695b680 1603 }
027a1604 1604 }
1605
da5c172a 1606 // Next, check if the user can be in a particular course
1607 if ($courseid) {
ec81373f 1608 if ($courseid == SITEID) { // Anyone can be in the site course
1609 if (isset($cm) and !$cm->visible and !isteacher(SITEID)) { // Not allowed to see module, send to course page
1610 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1611 }
d2a9f7cc 1612 return;
e3512050 1613 }
7fb0fec7 1614 if (! $course = get_record('course', 'id', $courseid)) {
1615 error('That course doesn\'t exist');
1616 }
1617 if (!(isteacher($courseid) || !empty($USER->admin)) && (!$course->visible || !course_parent_visible($course))) {
1618 print_header();
1619 notice(get_string('coursehidden'), $CFG->wwwroot .'/');
1620 }
9c9f7d77 1621 if (!empty($USER->student[$courseid]) or !empty($USER->teacher[$courseid]) or !empty($USER->admin)) {
cb909d74 1622 if (isset($USER->realuser)) { // Make sure the REAL person can also access this course
1623 if (!isteacher($courseid, $USER->realuser)) {
1624 print_header();
b0ccd3fb 1625 notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
cb909d74 1626 }
3ce2f1e0 1627 }
ec81373f 1628 if (isset($cm) and !$cm->visible and !isteacher($courseid)) { // Not allowed to see module, send to course page
1629 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1630 }
da5c172a 1631 return; // user is a member of this course.
1632 }
b0ccd3fb 1633 if ($USER->username == 'guest') {
7363ff91 1634 switch ($course->guest) {
1635 case 0: // Guests not allowed
1636 print_header();
ea971152 1637 notice(get_string('guestsnotallowed', '', $course->fullname), "$CFG->wwwroot/login/index.php");
7363ff91 1638 break;
1639 case 1: // Guests allowed
ec81373f 1640 if (isset($cm) and !$cm->visible) { // Not allowed to see module, send to course page
1641 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1642 }
7363ff91 1643 return;
1644 case 2: // Guests allowed with key (drop through)
1645 break;
1646 }
da5c172a 1647 }
f9903ed0 1648
9ca3b4f3 1649 //User is not enrolled in the course, wants to access course content
1650 //as a guest, and course setting allow unlimited guest access
1651 //Code cribbed from course/loginas.php
1652 if (strstr($FULLME,"username=guest") && ($course->guest==1)) {
b56ccdd9 1653 $realuser = $USER->id;
1654 $realname = fullname($USER, true);
1655 $USER = guest_user();
1656 $USER->loggedin = true;
1657 $USER->site = $CFG->wwwroot;
1658 $USER->realuser = $realuser;
5f357fb6 1659 $USER->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
1660 if (isset($SESSION->currentgroup)) { // Remember current cache setting for later
1661 $SESSION->oldcurrentgroup = $SESSION->currentgroup;
1662 unset($SESSION->currentgroup);
b56ccdd9 1663 }
1664 $guest_name = fullname($USER, true);
1665 add_to_log($course->id, "course", "loginas", "../user/view.php?id=$course->id&$USER->id$", "$realname -> $guest_name");
ec81373f 1666 if (isset($cm) and !$cm->visible) { // Not allowed to see module, send to course page
1667 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1668 }
b56ccdd9 1669 return;
9ca3b4f3 1670 }
1671
7363ff91 1672 // Currently not enrolled in the course, so see if they want to enrol
da5c172a 1673 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1674 redirect($CFG->wwwroot .'/course/enrol.php?id='. $courseid);
da5c172a 1675 die;
1676 }
f9903ed0 1677}
1678
7cf1c7bd 1679/**
1680 * This is a weaker version of {@link require_login()} which only requires login
1681 * when called from within a course rather than the site page, unless
1682 * the forcelogin option is turned on.
1683 *
1684 * @uses $CFG
4febb58f 1685 * @param object $course The course object in question
bbd3f2c4 1686 * @param bool $autologinguest Allow autologin guests if that is wanted
4febb58f 1687 * @param object $cm Course activity module if known
7cf1c7bd 1688 */
ec81373f 1689function require_course_login($course, $autologinguest=true, $cm=null) {
f950af3c 1690 global $CFG;
1596edff 1691 if (!empty($CFG->forcelogin)) {
b56ccdd9 1692 require_login();
f950af3c 1693 }
4febb58f 1694 if ($course->id != SITEID) {
ec81373f 1695 require_login($course->id, $autologinguest, $cm);
f950af3c 1696 }
1697}
1698
7cf1c7bd 1699/**
1700 * Modify the user table by setting the currently logged in user's
1701 * last login to now.
1702 *
1703 * @uses $USER
bbd3f2c4 1704 * @return bool
7cf1c7bd 1705 */
1d881d92 1706function update_user_login_times() {
1707 global $USER;
1708
1709 $USER->lastlogin = $user->lastlogin = $USER->currentlogin;
2a2f5f11 1710 $USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
1d881d92 1711
1712 $user->id = $USER->id;
1713
b0ccd3fb 1714 return update_record('user', $user);
1d881d92 1715}
1716
7cf1c7bd 1717/**
1718 * Determines if a user has completed setting up their account.
1719 *
89dcb99d 1720 * @param user $user A {@link $USER} object to test for the existance of a valid name and email
bbd3f2c4 1721 * @return bool
7cf1c7bd 1722 */
808a3baa 1723function user_not_fully_set_up($user) {
bb64b51a 1724 return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user)));
1725}
1726
1727function over_bounce_threshold($user) {
d2a9f7cc 1728
bb64b51a 1729 global $CFG;
d2a9f7cc 1730
bb64b51a 1731 if (empty($CFG->handlebounces)) {
1732 return false;
1733 }
1734 // set sensible defaults
1735 if (empty($CFG->minbounces)) {
1736 $CFG->minbounces = 10;
1737 }
1738 if (empty($CFG->bounceratio)) {
1739 $CFG->bounceratio = .20;
1740 }
1741 $bouncecount = 0;
1742 $sendcount = 0;
1743 if ($bounce = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) {
1744 $bouncecount = $bounce->value;
1745 }
1746 if ($send = get_record('user_preferences','userid',$user->id,'name','email_send_count')) {
1747 $sendcount = $send->value;
1748 }
1749 return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio);
1750}
1751
d2a9f7cc 1752/**
bb64b51a 1753 * @param $user - object containing an id
1754 * @param $reset - will reset the count to 0
1755 */
1756function set_send_count($user,$reset=false) {
d2a9f7cc 1757 if ($pref = get_record('user_preferences','userid',$user->id,'name','email_send_count')) {
bb64b51a 1758 $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
1759 update_record('user_preferences',$pref);
1760 }
1761 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
1762 // make a new one
1763 $pref->name = 'email_send_count';
1764 $pref->value = 1;
1765 $pref->userid = $user->id;
06ba0b04 1766 insert_record('user_preferences',$pref, false);
bb64b51a 1767 }
1768}
1769
d2a9f7cc 1770/**
bb64b51a 1771* @param $user - object containing an id
1772 * @param $reset - will reset the count to 0
1773 */
1774function set_bounce_count($user,$reset=false) {
d2a9f7cc 1775 if ($pref = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) {
bb64b51a 1776 $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
1777 update_record('user_preferences',$pref);
1778 }
1779 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
1780 // make a new one
1781 $pref->name = 'email_bounce_count';
1782 $pref->value = 1;
1783 $pref->userid = $user->id;
06ba0b04 1784 insert_record('user_preferences',$pref, false);
bb64b51a 1785 }
808a3baa 1786}
f9903ed0 1787
7cf1c7bd 1788/**
1789 * Keeps track of login attempts
1790 *
1791 * @uses $SESSION
1792 */
f9903ed0 1793function update_login_count() {
9fa49e22 1794
f9903ed0 1795 global $SESSION;
1796
1797 $max_logins = 10;
1798
1799 if (empty($SESSION->logincount)) {
1800 $SESSION->logincount = 1;
1801 } else {
1802 $SESSION->logincount++;
1803 }
1804
1805 if ($SESSION->logincount > $max_logins) {
9fa49e22 1806 unset($SESSION->wantsurl);
b0ccd3fb 1807 error(get_string('errortoomanylogins'));
d578afc8 1808 }
1809}
1810
7cf1c7bd 1811/**
1812 * Resets login attempts
1813 *
1814 * @uses $SESSION
1815 */
9fa49e22 1816function reset_login_count() {
9fa49e22 1817 global $SESSION;
d578afc8 1818
9fa49e22 1819 $SESSION->logincount = 0;
d578afc8 1820}
1821
7cf1c7bd 1822/**
1823 * check_for_restricted_user
1824 *
89dcb99d 1825 * @uses $CFG
1826 * @uses $USER
1827 * @param string $username ?
1828 * @param string $redirect ?
7cf1c7bd 1829 * @todo Finish documenting this function
1830 */
b0ccd3fb 1831function check_for_restricted_user($username=NULL, $redirect='') {
cb98d312 1832 global $CFG, $USER;
1833
1834 if (!$username) {
1835 if (!empty($USER->username)) {
1836 $username = $USER->username;
1837 } else {
1838 return false;
1839 }
1840 }
1841
1842 if (!empty($CFG->restrictusers)) {
1843 $names = explode(',', $CFG->restrictusers);
1844 if (in_array($username, $names)) {
b0ccd3fb 1845 error(get_string('restricteduser', 'error', fullname($USER)), $redirect);
cb98d312 1846 }
1847 }
1848}
1849
a266c904 1850function is_restricted_user($username){
1851 global $CFG;
e1ecf0a0 1852
a266c904 1853 if (!empty($CFG->restrictusers)) {
1854 $names = explode(',', $CFG->restrictusers);
1855 if (in_array($username, $names)) {
1856 return true;
1857 }
1858 }
1859 return false;
1860}
1861
b61efafb 1862function sync_metacourses() {
1863
1864 global $CFG;
1865
5f37b628 1866 if (!$courses = get_records_sql("SELECT DISTINCT parent_course,1 FROM {$CFG->prefix}course_meta")) {
b61efafb 1867 return;
1868 }
d2a9f7cc 1869
b61efafb 1870 foreach ($courses as $course) {
1871 sync_metacourse($course->parent_course);
1872 }
1873}
1874
1875
1876/**
1877 * Goes through all enrolment records for the courses inside the metacourse and sync with them.
d2a9f7cc 1878 */
b61efafb 1879
1880function sync_metacourse($metacourseid) {
1881
755c8d58 1882 global $CFG;
b61efafb 1883
1884 if (!$metacourse = get_record("course","id",$metacourseid)) {
1885 return false;
1886 }
1887
755c8d58 1888 if (count_records('course_meta','parent_course',$metacourseid) == 0) {
1889 // if there are no child courses for this meta course, nuke the enrolments
b61efafb 1890 if ($enrolments = get_records('user_students','course',$metacourseid,'','userid,1')) {
1891 foreach ($enrolments as $enrolment) {
1892 unenrol_student($enrolment->userid,$metacourseid);
1893 }
1894 }
1895 return true;
1896 }
1897
755c8d58 1898 // first get the list of child courses
1899 $c_courses = get_records('course_meta','parent_course',$metacourseid);
1900 $instr = '';
1901 foreach ($c_courses as $c) {
1902 $instr .= $c->child_course.',';
1903 }
1904 $instr = substr($instr,0,-1);
87671466 1905
755c8d58 1906 // now get the list of valid enrolments in the child courses
1907 $sql = 'SELECT DISTINCT userid,1 FROM '.$CFG->prefix.'user_students WHERE course IN ('.$instr.')';
1908 $enrolments = get_records_sql($sql);
e1ecf0a0 1909
755c8d58 1910 // put it into a nice array we can happily use array_diff on.
1911 $ce = array();
1912 if (!empty($enrolments)) {
1913 foreach ($enrolments as $en) {
1914 $ce[] = $en->userid;
b61efafb 1915 }
1916 }
1917
755c8d58 1918 // now get the list of current enrolments in the meta course.
1919 $sql = 'SELECT userid,1 FROM '.$CFG->prefix.'user_students WHERE course = '.$metacourseid;
1920 $enrolments = get_records_sql($sql);
b61efafb 1921
755c8d58 1922 $me = array();
1923 if (!empty($enrolments)) {
1924 foreach ($enrolments as $en) {
1925 $me[] = $en->userid;
b61efafb 1926 }
1927 }
d2a9f7cc 1928
755c8d58 1929 $enrolmentstodelete = array_diff($me,$ce);
1930 $userstoadd = array_diff($ce,$me);
1931
1932 foreach ($enrolmentstodelete as $userid) {
1933 unenrol_student($userid,$metacourseid);
1934 }
1935 foreach ($userstoadd as $userid) {
1936 enrol_student($userid,$metacourseid,0,0,'metacourse');
1937 }
e1ecf0a0 1938
b61efafb 1939 // and next make sure that we have the right start time and end time (ie max and min) for them all.
1940 if ($enrolments = get_records('user_students','course',$metacourseid,'','id,userid')) {
1941 foreach ($enrolments as $enrol) {
1942 if ($maxmin = get_record_sql("SELECT min(timestart) AS timestart, max(timeend) AS timeend
755c8d58 1943 FROM {$CFG->prefix}user_students u,
e1ecf0a0 1944 {$CFG->prefix}course_meta mc
1945 WHERE u.course = mc.child_course
755c8d58 1946 AND userid = $enrol->userid
b61efafb 1947 AND mc.parent_course = $metacourseid")) {
1948 $enrol->timestart = $maxmin->timestart;
1949 $enrol->timeend = $maxmin->timeend;
755c8d58 1950 $enrol->enrol = 'metacourse'; // just in case it wasn't there earlier.
b61efafb 1951 update_record('user_students',$enrol);
1952 }
1953 }
1954 }
1955 return true;
755c8d58 1956
b61efafb 1957}
1958
d2a9f7cc 1959/**
b61efafb 1960 * Adds a record to the metacourse table and calls sync_metacoures
1961 */
1962function add_to_metacourse ($metacourseid, $courseid) {
d2a9f7cc 1963
b61efafb 1964 if (!$metacourse = get_record("course","id",$metacourseid)) {
1965 return false;
1966 }
d2a9f7cc 1967
b61efafb 1968 if (!$course = get_record("course","id",$courseid)) {
1969 return false;
1970 }
1971
5f37b628 1972 if (!$record = get_record("course_meta","parent_course",$metacourseid,"child_course",$courseid)) {
b61efafb 1973 $rec->parent_course = $metacourseid;
1974 $rec->child_course = $courseid;
5f37b628 1975 if (!insert_record('course_meta',$rec)) {
b61efafb 1976 return false;
1977 }
1978 return sync_metacourse($metacourseid);
1979 }
1980 return true;
d2a9f7cc 1981
b61efafb 1982}
1983
d2a9f7cc 1984/**
b61efafb 1985 * Removes the record from the metacourse table and calls sync_metacourse
1986 */
1987function remove_from_metacourse($metacourseid, $courseid) {
1988
5f37b628 1989 if (delete_records('course_meta','parent_course',$metacourseid,'child_course',$courseid)) {
b61efafb 1990 return sync_metacourse($metacourseid);
1991 }
1992 return false;
1993}
1994
1995
7c12949d 1996/**
1997 * Determines if a user is currently logged in
1998 *
1999 * @uses $USER
bbd3f2c4 2000 * @return bool
7c12949d 2001 */
2002function isloggedin() {
2003 global $USER;
2004
2005 return (!empty($USER->id));
2006}
2007
2008
7cf1c7bd 2009/**
2010 * Determines if a user an admin
2011 *
2012 * @uses $USER
c6d15803 2013 * @param int $userid The id of the user as is found in the 'user' table
bbd3f2c4 2014 * @staticvar array $admins List of users who have been found to be admins by user id
2015 * @staticvar array $nonadmins List of users who have been found not to be admins by user id
2016 * @return bool
7cf1c7bd 2017 */
581d7b49 2018function isadmin($userid=0) {
f9903ed0 2019 global $USER;
5e04ee0c 2020 static $admins, $nonadmins;
2021
2022 if (!isset($admins)) {
2023 $admins = array();
2024 $nonadmins = array();
2025 }
f9903ed0 2026
581d7b49 2027 if (!$userid){
2028 if (empty($USER->id)) {
2029 return false;
2030 }
2031 $userid = $USER->id;
9bd2c874 2032 }
2033
dcc17b63 2034 if (!empty($USER->id) and ($userid == $USER->id)) { // Check session cache
2035 return !empty($USER->admin);
2036 }
2037
581d7b49 2038 if (in_array($userid, $admins)) {
aa095969 2039 return true;
581d7b49 2040 } else if (in_array($userid, $nonadmins)) {
aa095969 2041 return false;
b0ccd3fb 2042 } else if (record_exists('user_admins', 'userid', $userid)){
581d7b49 2043 $admins[] = $userid;
aa095969 2044 return true;
2045 } else {
581d7b49 2046 $nonadmins[] = $userid;
aa095969 2047 return false;
f9903ed0 2048 }
f9903ed0 2049}
2050
7cf1c7bd 2051/**
5e04ee0c 2052 * Determines if a user is a teacher (or better)
7cf1c7bd 2053 *
9407d456 2054 * @uses $USER
bbd3f2c4 2055 * @uses $CFG
c6d15803 2056 * @param int $courseid The id of the course that is being viewed, if any
2057 * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
bbd3f2c4 2058 * @param bool $includeadmin If true this function will return true when it encounters an admin user.
2059 * @return bool
7cf1c7bd 2060 */
fb830a1b 2061function isteacher($courseid=0, $userid=0, $includeadmin=true) {
5e04ee0c 2062/// Is the user able to access this course as a teacher?
fb830a1b 2063 global $USER, $CFG;
f9903ed0 2064
5e04ee0c 2065 if (empty($userid)) { // we are relying on $USER
2066 if (empty($USER) or empty($USER->id)) { // not logged in so can't be a teacher
2067 return false;
2068 }
57bc53c6 2069 if (!empty($USER->studentview)) {
2070 return false;
2071 }
dcc17b63 2072 if (!empty($USER->teacher) and $courseid) { // look in session cache
2073 if (!empty($USER->teacher[$courseid])) { // Explicitly a teacher, good
57bc53c6 2074 return true;
dcc17b63 2075 }
5e04ee0c 2076 }
dcc17b63 2077 $userid = $USER->id; // we need to make further checks
5e04ee0c 2078 }
2079
dcc17b63 2080 if ($includeadmin and isadmin($userid)) { // admins can do anything the teacher can
d115a57f 2081 return true;
2082 }
2083
dcc17b63 2084 if (empty($courseid)) { // should not happen, but we handle it
fb830a1b 2085 if (isadmin() or $CFG->debug > 7) {
dcc17b63 2086 notify('Coding error: isteacher() should not be used without a valid course id '.
2087 'as argument. Please notify the developer for this module.');
fb830a1b 2088 }
9407d456 2089 return isteacherinanycourse($userid, $includeadmin);
2090 }
2091
dcc17b63 2092/// Last resort, check the database
2093
9407d456 2094 return record_exists('user_teachers', 'userid', $userid, 'course', $courseid);
2095}
2096
2097/**
2098 * Determines if a user is a teacher in any course, or an admin
2099 *
2100 * @uses $USER
2101 * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
bbd3f2c4 2102 * @param bool $includeadmin If true this function will return true when it encounters an admin user.
2103 * @return bool
9407d456 2104 */
5e04ee0c 2105function isteacherinanycourse($userid=0, $includeadmin=true) {
fddbcf9c 2106 global $USER;
2107
5e04ee0c 2108 if (empty($userid)) {
2109 if (empty($USER) or empty($USER->id)) {
9407d456 2110 return false;
2111 }
dcc17b63 2112 if (!empty($USER->teacher)) { // look in session cache
2113 return true;
2114 }
9407d456 2115 $userid = $USER->id;
9d3c795c 2116 }
2117
5e04ee0c 2118 if ($includeadmin and isadmin($userid)) { // admins can do anything
fddbcf9c 2119 return true;
2120 }
2121
9407d456 2122 return record_exists('user_teachers', 'userid', $userid);
f9903ed0 2123}
2124
7cf1c7bd 2125/**
2126 * Determines if a user is allowed to edit a given course
2127 *
2128 * @uses $USER
c6d15803 2129 * @param int $courseid The id of the course that is being edited
2130 * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
367b8413 2131 * @param bool $ignorestudentview true = don't do check for studentview mode
bbd3f2c4 2132 * @return boo
7cf1c7bd 2133 */
367b8413 2134function isteacheredit($courseid, $userid=0, $ignorestudentview=false) {
73047f2f 2135 global $USER;
2136
367b8413 2137 // we can't edit in studentview
2138 if (!empty($USER->studentview) and !$ignorestudentview) {
2139 return false;
2140 }
2141
d8ba183c 2142 if (isadmin($userid)) { // admins can do anything
73047f2f 2143 return true;
2144 }
2145
2146 if (!$userid) {
ddd7a47a 2147 if (empty($USER) or empty($USER->id)) { // not logged in so can't be a teacher
2148 return false;
2149 }
2150 if (empty($USER->teacheredit)) { // we are relying on session cache
2151 return false;
2152 }
73047f2f 2153 return !empty($USER->teacheredit[$courseid]);
2154 }
2155
b0ccd3fb 2156 return get_field('user_teachers', 'editall', 'userid', $userid, 'course', $courseid);
73047f2f 2157}
2158
7cf1c7bd 2159/**
2160 * Determines if a user can create new courses
2161 *
2162 * @uses $USER
361855e6 2163 * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
bbd3f2c4 2164 * @return bool
7cf1c7bd 2165 */
1924074c 2166function iscreator ($userid=0) {
1924074c 2167 global $USER;
8a205861 2168 if (empty($USER->id)) {
2169 return false;
2170 }
1924074c 2171 if (isadmin($userid)) { // admins can do anything
2172 return true;
2173 }
8a205861 2174 if (empty($userid)) {
b0ccd3fb 2175 return record_exists('user_coursecreators', 'userid', $USER->id);
1924074c 2176 }
2177
b0ccd3fb 2178 return record_exists('user_coursecreators', 'userid', $userid);
1924074c 2179}
2180
7cf1c7bd 2181/**
2182 * Determines if a user is a student in the specified course
361855e6 2183 *
7cf1c7bd 2184 * If the course id specifies the site then the function determines
2185 * if the user is a confirmed and valid user of this site.
2186 *
2187 * @uses $USER
2188 * @uses $CFG
c6d15803 2189 * @uses SITEID
2190 * @param int $courseid The id of the course being tested
361855e6 2191 * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
bbd3f2c4 2192 * @return bool
7cf1c7bd 2193 */
8a9e3fd7 2194function isstudent($courseid, $userid=0) {
71f9abf9 2195 global $USER, $CFG;
f9903ed0 2196
2700d113 2197 if (empty($USER->id) and !$userid) {
7064e18f 2198 return false;
2199 }
2200
222ac91b 2201 if ($courseid == SITEID) {
2cc72e84 2202 if (!$userid) {
2203 $userid = $USER->id;
2204 }
2205 if (isguest($userid)) {
2206 return false;
2207 }
71f9abf9 2208 // a site teacher can never be a site student
2209 if (isteacher($courseid, $userid)) {
2210 return false;
2211 }
2700d113 2212 if ($CFG->allusersaresitestudents) {
2213 return record_exists('user', 'id', $userid);
2214 } else {
2215 return (record_exists('user_students', 'userid', $userid)
71f9abf9 2216 or record_exists('user_teachers', 'userid', $userid));
2700d113 2217 }
8f0cd6ef 2218 }
2cc72e84 2219
f9903ed0 2220 if (!$userid) {
1c349f7f 2221 if (empty($USER->studentview)) {
2222 return (!empty($USER->student[$courseid]));
2223 } else {
2224 return(!empty($USER->teacher[$courseid]) or isadmin());
2225 }
f9903ed0 2226 }
2227
ebc3bd2b 2228 // $timenow = time(); // todo: add time check below
f9903ed0 2229
b0ccd3fb 2230 return record_exists('user_students', 'userid', $userid, 'course', $courseid);
f9903ed0 2231}
2232
7cf1c7bd 2233/**
2234 * Determines if the specified user is logged in as guest.
2235 *
2236 * @uses $USER
361855e6 2237 * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
bbd3f2c4 2238 * @return bool
7cf1c7bd 2239 */
da5c172a 2240function isguest($userid=0) {
2241 global $USER;
2242
2243 if (!$userid) {
b35e8568 2244 if (empty($USER->username)) {
2245 return false;
2246 }
b0ccd3fb 2247 return ($USER->username == 'guest');
da5c172a 2248 }
2249
b0ccd3fb 2250 return record_exists('user', 'id', $userid, 'username', 'guest');
da5c172a 2251}
2252
7cf1c7bd 2253/**
2254 * Determines if the currently logged in user is in editing mode
2255 *
2256 * @uses $USER
c6d15803 2257 * @param int $courseid The id of the course being tested
89dcb99d 2258 * @param user $user A {@link $USER} object. If null then the currently logged in user is used.
bbd3f2c4 2259 * @return bool
7cf1c7bd 2260 */
2c309dc2 2261function isediting($courseid, $user=NULL) {
3cf4fa0c 2262 global $USER;
2263 if (!$user) {
2264 $user = $USER;
2c309dc2 2265 }
9c9f7d77 2266 if (empty($user->editing)) {
2267 return false;
2268 }
2c309dc2 2269 return ($user->editing and isteacher($courseid, $user->id));
2270}
2271
7cf1c7bd 2272/**
2273 * Determines if the logged in user is currently moving an activity
2274 *
2275 * @uses $USER
c6d15803 2276 * @param int $courseid The id of the course being tested
bbd3f2c4 2277 * @return bool
7cf1c7bd 2278 */
7977cffd 2279function ismoving($courseid) {
7977cffd 2280 global $USER;
2281
2282 if (!empty($USER->activitycopy)) {
2283 return ($USER->activitycopycourse == $courseid);
2284 }
2285 return false;
2286}
2287
7cf1c7bd 2288/**
2289 * Given an object containing firstname and lastname
2290 * values, this function returns a string with the
2291 * full name of the person.
2292 * The result may depend on system settings
2293 * or language. 'override' will force both names
361855e6 2294 * to be used even if system settings specify one.
68fbd8e1 2295 *
7cf1c7bd 2296 * @uses $CFG
2297 * @uses $SESSION
68fbd8e1 2298 * @param object $user A {@link $USER} object to get full name of
2299 * @param bool $override If true then the name will be first name followed by last name rather than adhering to fullnamedisplay setting.
7cf1c7bd 2300 */
e2cd5065 2301function fullname($user, $override=false) {
b5cbb64d 2302
f374fb10 2303 global $CFG, $SESSION;
2304
6527c077 2305 if (!isset($user->firstname) and !isset($user->lastname)) {
2306 return '';
2307 }
2308
4c202228 2309 if (!$override) {
2310 if (!empty($CFG->forcefirstname)) {
2311 $user->firstname = $CFG->forcefirstname;
2312 }
2313 if (!empty($CFG->forcelastname)) {
2314 $user->lastname = $CFG->forcelastname;
2315 }
2316 }
2317
f374fb10 2318 if (!empty($SESSION->fullnamedisplay)) {
2319 $CFG->fullnamedisplay = $SESSION->fullnamedisplay;
2320 }
e2cd5065 2321
b5cbb64d 2322 if ($CFG->fullnamedisplay == 'firstname lastname') {
b0ccd3fb 2323 return $user->firstname .' '. $user->lastname;
b5cbb64d 2324
2325 } else if ($CFG->fullnamedisplay == 'lastname firstname') {
b0ccd3fb 2326 return $user->lastname .' '. $user->firstname;
e2cd5065 2327
b5cbb64d 2328 } else if ($CFG->fullnamedisplay == 'firstname') {
2329 if ($override) {
2330 return get_string('fullnamedisplay', '', $user);
2331 } else {
2332 return $user->firstname;
2333 }
2334 }
e2cd5065 2335
b5cbb64d 2336 return get_string('fullnamedisplay', '', $user);
e2cd5065 2337}
2338
7cf1c7bd 2339/**
2340 * Sets a moodle cookie with an encrypted string
2341 *
2342 * @uses $CFG
2f87145b 2343 * @uses DAYSECS
2344 * @uses HOURSECS
7cf1c7bd 2345 * @param string $thing The string to encrypt and place in a cookie
2346 */
f9903ed0 2347function set_moodle_cookie($thing) {
7185e073 2348 global $CFG;
482b6e6e 2349
7cbe6afe 2350 if ($thing == 'guest') { // Ignore guest account
2351 return;
2352 }
2353
482b6e6e 2354 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
f9903ed0 2355
2356 $days = 60;
7a5672c9 2357 $seconds = DAYSECS*$days;
f9903ed0 2358
7a5672c9 2359 setCookie($cookiename, '', time() - HOURSECS, '/');
b0ccd3fb 2360 setCookie($cookiename, rc4encrypt($thing), time()+$seconds, '/');
f9903ed0 2361}
2362
7cf1c7bd 2363/**
2364 * Gets a moodle cookie with an encrypted string
2365 *
2366 * @uses $CFG
2367 * @return string
2368 */
f9903ed0 2369function get_moodle_cookie() {
7185e073 2370 global $CFG;
2371
482b6e6e 2372 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
7185e073 2373
1079c8a8 2374 if (empty($_COOKIE[$cookiename])) {
b0ccd3fb 2375 return '';
1079c8a8 2376 } else {
7cbe6afe 2377 $thing = rc4decrypt($_COOKIE[$cookiename]);
2378 return ($thing == 'guest') ? '': $thing; // Ignore guest account
1079c8a8 2379 }
f9903ed0 2380}
2381
7cf1c7bd 2382/**
2383 * Returns true if an internal authentication method is being used.
2384 * if method not specified then, global default is assumed
2385 *
2386 * @uses $CFG
2387 * @param string $auth Form of authentication required
bbd3f2c4 2388 * @return bool
7cf1c7bd 2389 * @todo Outline auth types and provide code example
2390 */
39a5a35d 2391function is_internal_auth($auth='') {
ba7166c3 2392/// Returns true if an internal authentication method is being used.
a3f1f815 2393/// If auth not specified then global default is assumed
ba7166c3 2394
2395 global $CFG;
2396
a3f1f815 2397 if (empty($auth)) {
2398 $auth = $CFG->auth;
39a5a35d 2399 }
2400
a3f1f815 2401 return ($auth == "email" || $auth == "none" || $auth == "manual");
2402}
2403
8c3dba73 2404/**
2405 * Returns an array of user fields
2406 *
c6d15803 2407 * @uses $CFG
2408 * @uses $db
2409 * @return array User field/column names
8c3dba73 2410 */
a3f1f815 2411function get_user_fieldnames() {
a3f1f815 2412
2413 global $CFG, $db;
2414
2415 $fieldarray = $db->MetaColumnNames($CFG->prefix.'user');
2416 unset($fieldarray['ID']);
2417
2418 return $fieldarray;
ba7166c3 2419}
f9903ed0 2420
7cf1c7bd 2421/**
2422 * Creates a bare-bones user record
2423 *
2424 * @uses $CFG
7cf1c7bd 2425 * @param string $username New user's username to add to record
2426 * @param string $password New user's password to add to record
2427 * @param string $auth Form of authentication required
68fbd8e1 2428 * @return object A {@link $USER} object
7cf1c7bd 2429 * @todo Outline auth types and provide code example
2430 */
71f9abf9 2431function create_user_record($username, $password, $auth='') {
366dfa60 2432 global $CFG;
71f9abf9 2433
1e22bc9c 2434 //just in case check text case
2435 $username = trim(moodle_strtolower($username));
71f9abf9 2436
3271b70f 2437 if (function_exists('auth_get_userinfo')) {
e858f9da 2438 if ($newinfo = auth_get_userinfo($username)) {
b36a8fc4 2439 $newinfo = truncate_userinfo($newinfo);
34daec9b 2440 foreach ($newinfo as $key => $value){
9f44d972 2441 $newuser->$key = addslashes(stripslashes($value)); // Just in case
e858f9da 2442 }
2443 }
2444 }
f9903ed0 2445
85a1d4c9 2446 if (!empty($newuser->email)) {
2447 if (email_is_not_allowed($newuser->email)) {
2448 unset($newuser->email);
2449 }
2450 }
2451
71f9abf9 2452 $newuser->auth = (empty($auth)) ? $CFG->auth : $auth;
faebaf0f 2453 $newuser->username = $username;
244b4e8f 2454 if(empty($CFG->{$newuser->auth.'_preventpassindb'})){ //Prevent passwords in Moodle's DB
2455 $newuser->password = md5($password);
2456 } else {
2457 $newuser->password = 'not cached'; //Unusable password
2458 }
a0bac19d 2459 $newuser->lang = $CFG->lang;
faebaf0f 2460 $newuser->confirmed = 1;
59619427 2461 $newuser->lastIP = getremoteaddr();
faebaf0f 2462 $newuser->timemodified = time();
f9903ed0 2463
b0ccd3fb 2464 if (insert_record('user', $newuser)) {
7c12949d 2465 $user = get_complete_user_data('username', $newuser->username);
d35757eb 2466 if($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'}){
4b598ff4 2467 set_user_preference('auth_forcepasswordchange', 1, $user->id);
d35757eb 2468 }
2469 return $user;
faebaf0f 2470 }
2471 return false;
2472}
2473
7cf1c7bd 2474/**
2475 * Will update a local user record from an external source
2476 *
2477 * @uses $CFG
2478 * @param string $username New user's username to add to record
89dcb99d 2479 * @return user A {@link $USER} object
7cf1c7bd 2480 */
d35757eb 2481function update_user_record($username) {
d35757eb 2482 global $CFG;
2483
2484 if (function_exists('auth_get_userinfo')) {
2485 $username = trim(moodle_strtolower($username)); /// just in case check text case
2486
b78fbdac 2487 $oldinfo = get_record('user', 'username', $username, '','','','', 'username, auth');
2488 $authconfig = get_config('auth/' . $oldinfo->auth);
2489
d35757eb 2490 if ($newinfo = auth_get_userinfo($username)) {
4b598ff4 2491 $newinfo = truncate_userinfo($newinfo);
d35757eb 2492 foreach ($newinfo as $key => $value){
b78fbdac 2493 $confkey = 'field_updatelocal_' . $key;
2494 if (!empty($authconfig->$confkey) && $authconfig->$confkey === 'onlogin') {
d35757eb 2495 $value = addslashes(stripslashes($value)); // Just in case
e1ecf0a0 2496 set_field('user', $key, $value, 'username', $username)
4b598ff4 2497 || error_log("Error updating $key for $username");
d35757eb 2498 }
2499 }
2500 }
2501 }
7c12949d 2502 return get_complete_user_data('username', $username);
d35757eb 2503}
0609562b 2504
b36a8fc4 2505function truncate_userinfo($info) {
2506/// will truncate userinfo as it comes from auth_get_userinfo (from external auth)
2507/// which may have large fields
2508
2509 // define the limits
2510 $limit = array(
2511 'username' => 100,
1c66bf59 2512 'idnumber' => 64,
b36a8fc4 2513 'firstname' => 20,
2514 'lastname' => 20,
2515 'email' => 100,
2516 'icq' => 15,
2517 'phone1' => 20,
2518 'phone2' => 20,
2519 'institution' => 40,
2520 'department' => 30,
2521 'address' => 70,
2522 'city' => 20,
2523 'country' => 2,
2524 'url' => 255,
2525 );
361855e6 2526
b36a8fc4 2527 // apply where needed
2528 foreach (array_keys($info) as $key) {
2529 if (!empty($limit[$key])) {
adfc03f9 2530 $info[$key] = trim(substr($info[$key],0, $limit[$key]));
361855e6 2531 }
b36a8fc4 2532 }
361855e6 2533
b36a8fc4 2534 return $info;
2535}
2536
7cf1c7bd 2537/**
2538 * Retrieve the guest user object
2539 *
2540 * @uses $CFG
89dcb99d 2541 * @return user A {@link $USER} object
7cf1c7bd 2542 */
0609562b 2543function guest_user() {
2544 global $CFG;
2545
b0ccd3fb 2546 if ($newuser = get_record('user', 'username', 'guest')) {
0609562b 2547 $newuser->loggedin = true;
2548 $newuser->confirmed = 1;
2549 $newuser->site = $CFG->wwwroot;
2550 $newuser->lang = $CFG->lang;
366dfa60 2551 $newuser->lastIP = getremoteaddr();
0609562b 2552 }
2553
2554 return $newuser;
2555}
2556
7cf1c7bd 2557/**
2558 * Given a username and password, this function looks them
2559 * up using the currently selected authentication mechanism,
2560 * and if the authentication is successful, it returns a
2561 * valid $user object from the 'user' table.
361855e6 2562 *
7cf1c7bd 2563 * Uses auth_ functions from the currently active auth module
2564 *
2565 * @uses $CFG
361855e6 2566 * @param string $username User's username
2567 * @param string $password User's password
89dcb99d 2568 * @return user|flase A {@link $USER} object or false if error
7cf1c7bd 2569 */
faebaf0f 2570function authenticate_user_login($username, $password) {
faebaf0f 2571
2572 global $CFG;
2573
466558e3 2574 $md5password = md5($password);
2575
27286aeb 2576 // First try to find the user in the database
466558e3 2577
7c12949d 2578 if (!$user = get_complete_user_data('username', $username)) {
18f16d61 2579 $user->id = 0; // Not a user
2580 $user->auth = $CFG->auth;
2581 }
39a5a35d 2582
27286aeb 2583 // Sort out the authentication method we are using.
39a5a35d 2584
27286aeb 2585 if (empty($CFG->auth)) {
b0ccd3fb 2586 $CFG->auth = 'manual'; // Default authentication module
27286aeb 2587 }
39a5a35d 2588
27286aeb 2589 if (empty($user->auth)) { // For some reason it isn't set yet
ccb3585f 2590 if (!empty($user->id) && (isadmin($user->id) || isguest($user->id))) {
71f9abf9 2591 $auth = 'manual'; // Always assume these guys are internal
27286aeb 2592 } else {
71f9abf9 2593 $auth = $CFG->auth; // Normal users default to site method
27286aeb 2594 }
d35757eb 2595 // update user record from external DB
2596 if ($user->auth != 'manual' && $user->auth != 'email') {
2597 $user = update_user_record($username);
2598 }
71f9abf9 2599 } else {
2600 $auth = $user->auth;
27286aeb 2601 }
8f0cd6ef 2602
ce791f88 2603 if (detect_munged_arguments($auth, 0)) { // For safety on the next require
2604 return false;
2605 }
2606
b0ccd3fb 2607 if (!file_exists($CFG->dirroot .'/auth/'. $auth .'/lib.php')) {
2608 $auth = 'manual'; // Can't find auth module, default to internal
466558e3 2609 }
2610
b0ccd3fb 2611 require_once($CFG->dirroot .'/auth/'. $auth .'/lib.php');
faebaf0f 2612
2613 if (auth_user_login($username, $password)) { // Successful authentication
d613daf0 2614 if ($user->id) { // User already exists in database
71f9abf9 2615 if (empty($user->auth)) { // For some reason auth isn't set yet
2616 set_field('user', 'auth', $auth, 'username', $username);
2617 }
01d6bd42 2618 if (empty($CFG->{$user->auth.'_preventpassindb'})){ //Calculate the password to update
2619 $passfield = $md5password;
2620 } else {
2621 $passfield = 'not cached';
2622 }
2623 if ($passfield <> $user->password) { // Update local copy of password for reference
2624 set_field('user', 'password', $passfield, 'username', $username); //Update password
faebaf0f 2625 }
366dfa60 2626 if (!is_internal_auth()) { // update user record from external DB
d35757eb 2627 $user = update_user_record($username);
2628 }
faebaf0f 2629 } else {
71f9abf9 2630 $user = create_user_record($username, $password, $auth);
faebaf0f 2631 }
89b54325 2632
e582b65e 2633 if (function_exists('auth_iscreator')) { // Check if the user is a creator
f894a791 2634 $useriscreator = auth_iscreator($username);
2635 if (!is_null($useriscreator)) {
2636 if ($useriscreator) {
2637 if (! record_exists('user_coursecreators', 'userid', $user->id)) {
2638 $cdata->userid = $user->id;
2639 if (! insert_record('user_coursecreators', $cdata)) {
2640 error('Cannot add user to course creators.');
2641 }
39a5a35d 2642 }
f894a791 2643 } else {
2644 if (record_exists('user_coursecreators', 'userid', $user->id)) {
2645 if (! delete_records('user_coursecreators', 'userid', $user->id)) {
2646 error('Cannot remove user from course creators.');
2647 }
39a5a35d 2648 }
2649 }
361855e6 2650 }
39a5a35d 2651 }
01af6da6 2652
2653 /// Log in to a second system if necessary
2654 if (!empty($CFG->sso)) {
2655 include_once($CFG->dirroot .'/sso/'. $CFG->sso .'/lib.php');
2656 if (function_exists('sso_user_login')) {
2657 if (!sso_user_login($username, $password)) { // Perform the signon process
2658 notify('Second sign-on failed');
2659 }
2660 }
2661 }
2662
e582b65e 2663 return $user;
9d3c795c 2664
e582b65e 2665 } else {
f64c1ef6 2666 add_to_log(0, 'login', 'error', 'index.php', $username);
f52d48db 2667 error_log('[client '.$_SERVER['REMOTE_ADDR']."] $CFG->wwwroot Failed Login: $username ".$_SERVER['HTTP_USER_AGENT']);
e582b65e 2668 return false;
2669 }
f9903ed0 2670}
2671
7c12949d 2672/**
2673 * Get a complete user record, which includes all the info
2674 * in the user record, as well as membership information
2675 * Intended for setting as $USER session variable
2676 *
2677 * @uses $CFG
2678 * @uses SITEID
e1ecf0a0 2679 * @param string $field The user field to be checked for a given value.
7c12949d 2680 * @param string $value The value to match for $field.
2681 * @return user A {@link $USER} object.
2682 */
2683function get_complete_user_data($field, $value) {
2684
2685 global $CFG;
2686
2687 if (!$field || !$value) {
2688 return false;
2689 }
2690
2691/// Get all the basic user data
2692
2693 if (! $user = get_record_select('user', $field .' = \''. $value .'\' AND deleted <> \'1\'')) {
2694 return false;
2695 }
2696
2697/// Add membership information
2698
2699 if ($admins = get_records('user_admins', 'userid', $user->id)) {
2700 $user->admin = true;
2701 }
2702
2703 $user->student[SITEID] = isstudent(SITEID, $user->id);
2704
2705/// Determine enrolments based on current enrolment module
2706
2707 require_once($CFG->dirroot .'/enrol/'. $CFG->enrol .'/enrol.php');
2708 $enrol = new enrolment_plugin();
2709 $enrol->get_student_courses($user);
2710 $enrol->get_teacher_courses($user);
2711
2712/// Get various settings and preferences
2713
2714 if ($displays = get_records('course_display', 'userid', $user->id)) {
2715 foreach ($displays as $display) {
2716 $user->display[$display->course] = $display->display;
2717 }
2718 }
2719
2720 if ($preferences = get_records('user_preferences', 'userid', $user->id)) {
2721 foreach ($preferences as $preference) {
2722 $user->preference[$preference->name] = $preference->value;
2723 }
2724 }
2725
2726 if ($groups = get_records('groups_members', 'userid', $user->id)) {
2727 foreach ($groups as $groupmember) {
2728 $courseid = get_field('groups', 'courseid', 'id', $groupmember->groupid);
fa22fd5f 2729 //change this to 2D array so we can put multiple groups in a course
2730 $user->groupmember[$courseid][] = $groupmember->groupid;
7c12949d 2731 }
2732 }
2733
2734/// Rewrite some variables if necessary
2735 if (!empty($user->description)) {
2736 $user->description = true; // No need to cart all of it around
2737 }
2738 if ($user->username == 'guest') {
2739 $user->lang = $CFG->lang; // Guest language always same as site
2740 $user->firstname = get_string('guestuser'); // Name always in current language
2741 $user->lastname = ' ';
2742 }
2743
2744 $user->loggedin = true;
2745 $user->site = $CFG->wwwroot; // for added security, store the site in the session
2746 $user->sesskey = random_string(10);
2747 $user->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
2748
2749 return $user;
2750
2751}
2752
2753function get_user_info_from_db($field, $value) { // For backward compatibility
2754 return get_complete_user_data($field, $value);
2755}
2756
e1ecf0a0 2757/*
7c12949d 2758 * When logging in, this function is run to set certain preferences
2759 * for the current SESSION
2760 */
2761function set_login_session_preferences() {
7c7ca1b5 2762 global $SESSION, $CFG;
7c12949d 2763
2764 $SESSION->justloggedin = true;
2765
2766 unset($SESSION->lang);
2767 unset($SESSION->encoding);
6aaa17c7 2768 $SESSION->encoding = current_charset();
7c12949d 2769
2770 // Restore the calendar filters, if saved
2771 if (intval(get_user_preferences('calendar_persistflt', 0))) {
2772 include_once($CFG->dirroot.'/calendar/lib.php');
a266c904 2773 calendar_set_filters_status(get_user_preferences('calendav_savedflt', 0xff));
7c12949d 2774 }
2775}
2776
2777
7cf1c7bd 2778/**
2779 * Enrols (or re-enrols) a student in a given course
2780 *
e1ecf0a0 2781 * NOTE: Defaults to 'manual' enrolment - enrolment plugins
a6d114e6 2782 * must set it explicitly.
2783 *
bbd3f2c4 2784 * @uses $CFG
c6d15803 2785 * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
bbd3f2c4 2786 * @param int $courseid The id of the course that is being viewed
c6d15803 2787 * @param int $timestart ?
2788 * @param int $timeend ?
bbd3f2c4 2789 * @param string $enrol ?
2790 * @return bool
7cf1c7bd 2791 * @todo Finish documenting this function
2792 */
a6d114e6 2793function enrol_student($userid, $courseid, $timestart=0, $timeend=0, $enrol='manual') {
b40bc478 2794
0a0bcdd2 2795 global $CFG, $USER;
75169b06 2796
b0ccd3fb 2797 if (!$course = get_record('course', 'id', $courseid)) { // Check course
3041b0f8 2798 return false;
4d312bbe 2799 }
b0ccd3fb 2800 if (!$user = get_record('user', 'id', $userid)) { // Check user
631cf796 2801 return false;
2802 }
b61efafb 2803 // enrol the student in any parent meta courses...
bbd3f2c4 2804 if ($parents = get_records('course_meta', 'child_course', $courseid)) {
b61efafb 2805 foreach ($parents as $parent) {
755c8d58 2806 enrol_student($userid, $parent->parent_course, $timestart, $timeend,'metacourse');
0a0bcdd2 2807 // if we're enrolling ourselves in the child course, add the parent courses to USER too
2808 // otherwise they'll have to logout and in again to get it
2809 // http://moodle.org/mod/forum/post.php?reply=185699
2810 if (!empty($USER) && $userid == $USER->id) {
2811 $USER->student[$parent->parent_course] = true;
2812 }
b61efafb 2813 }
2814 }
92318548 2815
b0ccd3fb 2816 if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
631cf796 2817 $student->timestart = $timestart;
2818 $student->timeend = $timeend;
2819 $student->time = time();
6e8ca983 2820 $student->enrol = $enrol;
b0ccd3fb 2821 return update_record('user_students', $student);
361855e6 2822
631cf796 2823 } else {
75169b06 2824 require_once("$CFG->dirroot/mod/forum/lib.php");
2f3b54ae 2825 forum_add_user($userid, $courseid);
2826
631cf796 2827 $student->userid = $userid;
2828 $student->course = $courseid;
2829 $student->timestart = $timestart;
2830 $student->timeend = $timeend;
2831 $student->time = time();
6e8ca983 2832 $student->enrol = $enrol;
b0ccd3fb 2833 return insert_record('user_students', $student);
631cf796 2834 }
d7facad8 2835}
2836
7cf1c7bd 2837/**
2838 * Unenrols a student from a given course
2839 *
c6d15803 2840 * @param int $courseid The id of the course that is being viewed, if any
2841 * @param int $userid The id of the user that is being tested against.
bbd3f2c4 2842 * @return bool
7cf1c7bd 2843 */
9fa62805 2844function unenrol_student($userid, $courseid=0) {
79a10294 2845 global $CFG;
d7facad8 2846
9fa62805 2847 if ($courseid) {
9fa49e22 2848 /// First delete any crucial stuff that might still send mail
b0ccd3fb 2849 if ($forums = get_records('forum', 'course', $courseid)) {
9fa49e22 2850 foreach ($forums as $forum) {
b0ccd3fb 2851 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
9fa62805 2852 }
2853 }
2854 if ($groups = get_groups($courseid, $userid)) {
2855 foreach ($groups as $group) {
b0ccd3fb 2856 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
bb09fb11 2857 }
f9903ed0 2858 }
79a10294 2859 // unenrol the student from any parent meta courses...
5f37b628 2860 if ($parents = get_records('course_meta','child_course',$courseid)) {
b61efafb 2861 foreach ($parents as $parent) {
79a10294 2862 if (!record_exists_sql('SELECT us.id FROM '.$CFG->prefix.'user_students us, '
2863 .$CFG->prefix.'course_meta cm WHERE cm.child_course = us.course
2864 AND us.userid = '.$userid .' AND us.course != '.$courseid)) {
2865 unenrol_student($userid, $parent->parent_course);
2866 }
b61efafb 2867 }
2868 }
b0ccd3fb 2869 return delete_records('user_students', 'userid', $userid, 'course', $courseid);
9fa49e22 2870
f9903ed0 2871 } else {
b0ccd3fb 2872 delete_records('forum_subscriptions', 'userid', $userid);
2873 delete_records('groups_members', 'userid', $userid);
2874 return delete_records('user_students', 'userid', $userid);
f9903ed0 2875 }
2876}
2877
7cf1c7bd 2878/**
2879 * Add a teacher to a given course
2880 *
bbd3f2c4 2881 * @uses $USER
c6d15803 2882 * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
bbd3f2c4 2883 * @param int $courseid The id of the course that is being viewed, if any
c6d15803 2884 * @param int $editall ?
7cf1c7bd 2885 * @param string $role ?
c6d15803 2886 * @param int $timestart ?
2887 * @param int $timeend ?
bbd3f2c4 2888 * @param string $enrol ?
2889 * @return bool
7cf1c7bd 2890 * @todo Finish documenting this function
2891 */
6e8ca983 2892function add_teacher($userid, $courseid, $editall=1, $role='', $timestart=0, $timeend=0, $enrol='manual') {
7b5944cd 2893 global $CFG;
3041b0f8 2894
61451a36 2895 if ($teacher = get_record('user_teachers', 'userid', $userid, 'course', $courseid)) {
b40bc478 2896 $newteacher = NULL;
2897 $newteacher->id = $teacher->id;
2898 $newteacher->editall = $editall;
6e8ca983 2899 $newteacher->enrol = $enrol;
b40bc478 2900 if ($role) {
2901 $newteacher->role = $role;
2902 }
2903 if ($timestart) {
2904 $newteacher->timestart = $timestart;
3041b0f8 2905 }
b40bc478 2906 if ($timeend) {
2907 $newteacher->timeend = $timeend;
2908 }
2909 return update_record('user_teachers', $newteacher);
3041b0f8 2910 }
61451a36 2911
b0ccd3fb 2912 if (!record_exists('user', 'id', $userid)) {
61451a36 2913 return false; // no such user
2914 }
2915
b0ccd3fb 2916 if (!record_exists('course', 'id', $courseid)) {
61451a36 2917 return false; // no such course
2918 }
2919
2920 $teacher = NULL;
2921 $teacher->userid = $userid;
2922 $teacher->course = $courseid;
2923 $teacher->editall = $editall;
2924 $teacher->role = $role;
aef46c08 2925 $teacher->enrol = $enrol;
5a2dea02 2926 $teacher->timemodified = time();
aef46c08 2927 $teacher->timestart = $timestart;
2928 $teacher->timeend = $timeend;
b0ccd3fb 2929 if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
5a2dea02 2930 $teacher->timestart = $student->timestart;
2931 $teacher->timeend = $student->timeend;
2932 $teacher->timeaccess = $student->timeaccess;
2933 }
61451a36 2934
b0ccd3fb 2935 if (record_exists('user_teachers', 'course', $courseid)) {
61451a36 2936 $teacher->authority = 2;
2937 } else {
2938 $teacher->authority = 1;
2939 }
b0ccd3fb 2940 delete_records('user_students', 'userid', $userid, 'course', $courseid); // Unenrol as student
8f0cd6ef 2941
709f0ec8 2942 /// Add forum subscriptions for new users
463a92db 2943 require_once($CFG->dirroot.'/mod/forum/lib.php');
7b5944cd 2944 forum_add_user($userid, $courseid);
61451a36 2945
b0ccd3fb 2946 return insert_record('user_teachers', $teacher);
61451a36 2947
3041b0f8 2948}
2949
7cf1c7bd 2950/**
2951 * Removes a teacher from a given course (or ALL courses)
2952 * Does not delete the user account
2953 *
c6d15803 2954 * @param int $courseid The id of the course that is being viewed, if any
361855e6 2955 * @param int $userid The id of the user that is being tested against.
bbd3f2c4 2956 * @return bool
7cf1c7bd 2957 */
3041b0f8 2958function remove_teacher($userid, $courseid=0) {
3041b0f8 2959 if ($courseid) {
9fa49e22 2960 /// First delete any crucial stuff that might still send mail
b0ccd3fb 2961 if ($forums = get_records('forum', 'course', $courseid)) {
9fa49e22 2962 foreach ($forums as $forum) {
b0ccd3fb 2963 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
9fa49e22 2964 }
2965 }
b02193e6 2966
2967 /// Next if the teacher is not registered as a student, but is
2968 /// a member of a group, remove them from the group.
2969 if (!isstudent($courseid, $userid)) {
2970 if ($groups = get_groups($courseid, $userid)) {
2971 foreach ($groups as $group) {
b0ccd3fb 2972 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
b02193e6 2973 }
2974 }
2975 }
2976
b0ccd3fb 2977 return delete_records('user_teachers', 'userid', $userid, 'course', $courseid);
57507290 2978 } else {
b0ccd3fb 2979 delete_records('forum_subscriptions', 'userid', $userid);
2980 return delete_records('user_teachers', 'userid', $userid);
57507290 2981 }
f9903ed0 2982}
2983
7cf1c7bd 2984/**
2985 * Add a creator to the site
2986 *
361855e6 2987 * @param int $userid The id of the user that is being tested against.
bbd3f2c4 2988 * @return bool
7cf1c7bd 2989 */
3041b0f8 2990function add_creator($userid) {
3041b0f8 2991
b0ccd3fb 2992 if (!record_exists('user_admins', 'userid', $userid)) {
2993 if (record_exists('user', 'id', $userid)) {
3041b0f8 2994 $creator->userid = $userid;
b0ccd3fb 2995 return insert_record('user_coursecreators', $creator);
3041b0f8 2996 }
2997 return false;
2998 }
2999 return true;
3000}
3001
7cf1c7bd 3002/**
3003 * Remove a creator from a site
3004 *
bbd3f2c4 3005 * @uses $db
c6d15803 3006 * @param int $userid The id of the user that is being tested against.
bbd3f2c4 3007 * @return bool
7cf1c7bd 3008 */
3041b0f8 3009function remove_creator($userid) {
3041b0f8 3010 global $db;
3011
b0ccd3fb 3012 return delete_records('user_coursecreators', 'userid', $userid);
3041b0f8 3013}
3014
7cf1c7bd 3015/**
3016 * Add an admin to a site
3017 *
3018 * @uses SITEID
c6d15803 3019 * @param int $userid The id of the user that is being tested against.
bbd3f2c4 3020 * @return bool
7cf1c7bd 3021 */
3041b0f8 3022function add_admin($userid) {
3041b0f8 3023
b0ccd3fb 3024 if (!record_exists('user_admins', 'userid', $userid)) {
3025 if (record_exists('user', 'id', $userid)) {
3041b0f8 3026 $admin->userid = $userid;
361855e6 3027
f950af3c 3028 // any admin is also a teacher on the site course
222ac91b 3029 if (!record_exists('user_teachers', 'course', SITEID, 'userid', $userid)) {
3030 if (!add_teacher($userid, SITEID)) {
f950af3c 3031 return false;
3032 }
3033 }
361855e6 3034
b0ccd3fb 3035 return insert_record('user_admins', $admin);
3041b0f8 3036 }
3037 return false;
3038 }
3039 return true;
3040}
3041
7cf1c7bd 3042/**
3043 * Removes an admin from a site
3044 *
bbd3f2c4 3045 * @uses $db
3046 * @uses SITEID
c6d15803 3047 * @param int $userid The id of the user that is being tested against.
bbd3f2c4 3048 * @return bool
7cf1c7bd 3049 */
3041b0f8 3050function remove_admin($userid) {
9fa49e22 3051 global $db;
f9903ed0 3052
f950af3c 3053 // remove also from the list of site teachers
222ac91b 3054 remove_teacher($userid, SITEID);
f950af3c 3055
b0ccd3fb 3056 return delete_records('user_admins', 'userid', $userid);
f9903ed0 3057}
3058
b97c4164 3059/**
3060 * Delete a course, including all related data from the database,
3061 * and any associated files from the moodledata folder.
e1ecf0a0 3062 *
b97c4164 3063 * @param int $courseid The id of the course to delete.
3064 * @param bool $showfeedback Whether to display notifications of each action the function performs.
3065 * @return bool true if all the removals succeeded. false if there were any failures. If this
3066 * method returns false, some of the removals will probably have succeeded, and others
3067 * failed, but you have no way of knowing which.
3068 */
3069function delete_course($courseid, $showfeedback = true) {
3070 global $CFG;
3071 $result = true;
e1ecf0a0 3072
b97c4164 3073 if (!remove_course_contents($courseid, $showfeedback)) {
3074 if ($showfeedback) {
3075 notify("An error occurred while deleting some of the course contents.");
3076 }
3077 $result = false;
3078 }
3079
3080 if (!delete_records("course", "id", $courseid)) {
3081 if ($showfeedback) {
3082 notify("An error occurred while deleting the main course record.");
3083 }
3084 $result = false;
3085 }
3086
3087 if (!fulldelete($CFG->dataroot.'/'.$courseid)) {
3088 if ($showfeedback) {
3089 notify("An error occurred while deleting the course files.");
3090 }
3091 $result = false;
3092 }
e1ecf0a0 3093
b97c4164 3094 return $result;
3095}
3096
7cf1c7bd 3097/**
3098 * Clear a course out completely, deleting all content
3099 * but don't delete the course itself
3100 *
7cf1c7bd 3101 * @uses $CFG
1f8ede91 3102 * @param int $courseid The id of the course that is being deleted
3103 * @param bool $showfeedback Whether to display notifications of each action the function performs.
b97c4164 3104 * @return bool true if all the removals succeeded. false if there were any failures. If this
3105 * method returns false, some of the removals will probably have succeeded, and others
3106 * failed, but you have no way of knowing which.
7cf1c7bd 3107 */
07aeb7b0 3108function remove_course_contents($courseid, $showfeedback=true) {
07aeb7b0 3109
1f8ede91 3110 global $CFG;
07aeb7b0 3111
3112 $result = true;
3113
b0ccd3fb 3114 if (! $course = get_record('course', 'id', $courseid)) {
3115 error('Course ID was incorrect (can\'t find it)');
07aeb7b0 3116 }
3117
b0ccd3fb 3118 $strdeleted = get_string('deleted');
07aeb7b0 3119
3120 // First delete every instance of every module
d8ba183c 3121
b0ccd3fb 3122 if ($allmods = get_records('modules') ) {
07aeb7b0 3123 foreach ($allmods as $mod) {
3124 $modname = $mod->name;
b0ccd3fb 3125 $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
3126 $moddelete = $modname .'_delete_instance'; // Delete everything connected to an instance
3127 $moddeletecourse = $modname .'_delete_course'; // Delete other stray stuff (uncommon)
07aeb7b0 3128 $count=0;
3129 if (file_exists($modfile)) {
3130 include_once($modfile);
3131 if (function_exists($moddelete)) {
b0ccd3fb 3132 if ($instances = get_records($modname, 'course', $course->id)) {
07aeb7b0 3133 foreach ($instances as $instance) {
3134 if ($moddelete($instance->id)) {
3135 $count++;
3136 } else {
7eec3390 3137 notify('Could not delete '. $modname .' instance '. $instance->id .' ('. format_string($instance->name) .')');
07aeb7b0 3138 $result = false;
3139 }
3140 }
3141 }
3142 } else {
b0ccd3fb 3143 notify('Function '. $moddelete() .'doesn\'t exist!');
07aeb7b0 3144 $result = false;
3145 }
3146
ca952b03 3147 if (function_exists($moddeletecourse)) {
3148 $moddeletecourse($course);
3149 }
07aeb7b0 3150 }
3151 if ($showfeedback) {
b0ccd3fb 3152 notify($strdeleted .' '. $count .' x '. $modname);
07aeb7b0 3153 }
3154 }
3155 } else {
b0ccd3fb 3156 error('No modules are installed!');
07aeb7b0 3157 }
3158
251af423 3159 // Delete course blocks
3160 if (delete_records('block_instance', 'pagetype', PAGE_COURSE_VIEW, 'pageid', $course->id)) {
3161 if ($showfeedback) {
3162 notify($strdeleted .' block_instance');
3163 }
3164 } else {
3165 $result = false;
3166 }
3167
07aeb7b0 3168 // Delete any user stuff
3169
b0ccd3fb 3170 if (delete_records('user_students', 'course', $course->id)) {
07aeb7b0 3171 if ($showfeedback) {
b0ccd3fb 3172 notify($strdeleted .' user_students');
07aeb7b0 3173 }
3174 } else {
3175 $result = false;
3176 }
3177
b0ccd3fb 3178 if (delete_records('user_teachers', 'course', $course->id)) {
07aeb7b0 3179 if ($showfeedback) {
b0ccd3fb 3180 notify($strdeleted .' user_teachers');
07aeb7b0 3181 }
3182 } else {
3183 $result = false;
3184 }
3185
082e3ebc 3186 // Delete any groups
3187
b0ccd3fb 3188 if ($groups = get_records('groups', 'courseid', $course->id)) {
082e3ebc 3189 foreach ($groups as $group) {
b0ccd3fb 3190 if (delete_records('groups_members', 'groupid', $group->id)) {
082e3ebc 3191 if ($showfeedback) {
b0ccd3fb 3192 notify($strdeleted .' groups_members');
082e3ebc 3193 }
3194 } else {
3195 $result = false;
3196 }
b0ccd3fb 3197 if (delete_records('groups', 'id', $group->id)) {
082e3ebc 3198 if ($showfeedback) {
b0ccd3fb 3199 notify($strdeleted .' groups');
082e3ebc 3200 }
3201 } else {
3202 $result = false;
3203 }
3204 }
3205 }
3206
3207 // Delete events
3208
b0ccd3fb 3209 if (delete_records('event', 'courseid', $course->id)) {
082e3ebc 3210 if ($showfeedback) {
b0ccd3fb 3211 notify($strdeleted .' event');
082e3ebc 3212 }
3213 } else {
3214 $result = false;
3215 }
3216
07aeb7b0 3217 // Delete logs
3218
b0ccd3fb 3219 if (delete_records('log', 'course', $course->id)) {
07aeb7b0 3220 if ($showfeedback) {
b0ccd3fb 3221