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