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