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