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