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