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