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