upgraded some.
[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 965function find_day_in_month($index, $weekday, $month, $year) {
8dc3f6cf 966
967 $daysinmonth = days_in_month($month, $year);
968
42d36497 969 if($weekday == -1) {
8dc3f6cf 970 // Don't care about weekday, so return either $index or $daysinmonth
971 return ($index == -1) ? $daysinmonth : $index;
972 }
973
974 // From now on we 're looking for a specific weekday
975 $numweeks = intval($weekday / 7); // 0 for first weekday, 1 for second etc.
976 $weekday = $weekday % 7;
977
978 // Starting from day $index, -1 == last day of month
979
980 if($index == -1) {
981
982 $lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
983
984 // This is the last such weekday of the month
985 $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday;
986 if($lastinmonth > $daysinmonth) {
987 $lastinmonth -= 7;
42d36497 988 }
8dc3f6cf 989
990 // Skip the required number of weeks and return
991 while($numweeks--) {
992 $lastinmonth -= 7;
42d36497 993 }
8dc3f6cf 994
995 return $lastinmonth;
996
42d36497 997 }
998 else {
42d36497 999
8dc3f6cf 1000 $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $index, $year, 0));
42d36497 1001
8dc3f6cf 1002 $diff = $weekday - $indexweekday;
1003 if($diff < 0) {
1004 $diff += 7;
42d36497 1005 }
42d36497 1006
8dc3f6cf 1007 // This is the first such weekday of the month equal to or after $index
1008 $firstfromindex = $index + $diff;
1009
1010 // Skip the required number of weeks and return
1011 while($numweeks--) {
1012 $firstfromindex += 7;
42d36497 1013 }
42d36497 1014
8dc3f6cf 1015 return $firstfromindex;
1016
1017 }
42d36497 1018}
1019
1020function days_in_month($month, $year) {
1021 return intval(date('t', mktime(12, 0, 0, $month, 1, $year, 0)));
1022}
1023
8dc3f6cf 1024function dayofweek($day, $month, $year) {
1025 // I wonder if this is any different from
1026 // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
1027 return intval(date('w', mktime(12, 0, 0, $month, $day, $year, 0)));
1028}
1029
9fa49e22 1030/// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////
f9903ed0 1031
1a33f699 1032// Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
1033// if one does not already exist, but does not overwrite existing sesskeys. Returns the
1034// sesskey string if $USER exists, or boolean false if not.
04280e85 1035function sesskey() {
1a33f699 1036 global $USER;
1037
1038 if(!isset($USER)) {
1039 return false;
1040 }
1041
1042 if (empty($USER->sesskey)) {
1043 $USER->sesskey = random_string(10);
1044 }
1045
1046 return $USER->sesskey;
1047}
1048
7cf1c7bd 1049/**
ec81373f 1050 * This function checks that the current user is logged in and has the
1051 * required privileges
1052 *
7cf1c7bd 1053 * This function checks that the current user is logged in, and optionally
ec81373f 1054 * whether they are allowed to be in a particular course and view a particular
1055 * course module.
1056 * If they are not logged in, then it redirects them to the site login unless
d2a9f7cc 1057 * $autologinguest is set and {@link $CFG}->autologinguests is set to 1 in which
ec81373f 1058 * case they are automatically logged in as guests.
1059 * If $courseid is given and the user is not enrolled in that course then the
1060 * user is redirected to the course enrolment page.
1061 * If $cm is given and the coursemodule is hidden and the user is not a teacher
1062 * in the course then the user is redirected to the course home page.
7cf1c7bd 1063 *
7cf1c7bd 1064 * @uses $CFG
c6d15803 1065 * @uses $SESSION
7cf1c7bd 1066 * @uses $USER
1067 * @uses $FULLME
c6d15803 1068 * @uses SITEID
7cf1c7bd 1069 * @uses $MoodleSession
ec81373f 1070 * @param int $courseid id of the course
d2a9f7cc 1071 * @param boolean $autologinguest
ec81373f 1072 * @param $cm course module object
7cf1c7bd 1073 */
ec81373f 1074function require_login($courseid=0, $autologinguest=true, $cm=null) {
f9903ed0 1075
73047f2f 1076 global $CFG, $SESSION, $USER, $FULLME, $MoodleSession;
d8ba183c 1077
da5c172a 1078 // First check that the user is logged in to the site.
c21c671d 1079 if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
f9903ed0 1080 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1081 if (!empty($_SERVER['HTTP_REFERER'])) {
1082 $SESSION->fromurl = $_SERVER['HTTP_REFERER'];
9f44d972 1083 }
c21c671d 1084 $USER = NULL;
8e8d0524 1085 if ($autologinguest and $CFG->autologinguests and $courseid and get_field('course','guest','id',$courseid)) {
1086 $loginguest = '?loginguest=true';
1087 } else {
1088 $loginguest = '';
a2ebe6a5 1089 }
8a33e371 1090 if (empty($CFG->loginhttps)) {
b0ccd3fb 1091 redirect($CFG->wwwroot .'/login/index.php'. $loginguest);
8a33e371 1092 } else {
b0ccd3fb 1093 $wwwroot = str_replace('http','https', $CFG->wwwroot);
1094 redirect($wwwroot .'/login/index.php'. $loginguest);
8a33e371 1095 }
20fde7b1 1096 exit;
f9903ed0 1097 }
808a3baa 1098
d35757eb 1099 // check whether the user should be changing password
027a1604 1100 // reload_user_preferences(); // Why is this necessary? Seems wasteful. - MD
a3f1f815 1101 if (!empty($USER->preference['auth_forcepasswordchange'])){
d35757eb 1102 if (is_internal_auth() || $CFG->{'auth_'.$USER->auth.'_stdchangepassword'}){
20fde7b1 1103 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1104 redirect($CFG->wwwroot .'/login/change_password.php');
d35757eb 1105 } elseif($CFG->changepassword) {
1106 redirect($CFG->changepassword);
1107 } else {
361855e6 1108 error('You cannot proceed without changing your password.
d35757eb 1109 However there is no available page for changing it.
b0ccd3fb 1110 Please contact your Moodle Administrator.');
d35757eb 1111 }
1112 }
808a3baa 1113 // Check that the user account is properly set up
1114 if (user_not_fully_set_up($USER)) {
20fde7b1 1115 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1116 redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
808a3baa 1117 }
d8ba183c 1118
366dfa60 1119 // Make sure current IP matches the one for this session (if required)
361855e6 1120 if (!empty($CFG->tracksessionip)) {
366dfa60 1121 if ($USER->sessionIP != md5(getremoteaddr())) {
1122 error(get_string('sessionipnomatch', 'error'));
1123 }
1124 }
6d8f47d6 1125
1126 // Make sure the USER has a sesskey set up. Used for checking script parameters.
04280e85 1127 sesskey();
366dfa60 1128
027a1604 1129 // Check that the user has agreed to a site policy if there is one
1130 if (!empty($CFG->sitepolicy)) {
1131 if (!$USER->policyagreed) {
957b5198 1132 $SESSION->wantsurl = $FULLME;
027a1604 1133 redirect($CFG->wwwroot .'/user/policy.php');
027a1604 1134 }
1695b680 1135 }
1136
1137 // If the site is currently under maintenance, then print a message
1138 if (!isadmin()) {
1139 if (file_exists($CFG->dataroot.'/1/maintenance.html')) {
1140 print_maintenance_message();
20fde7b1 1141 exit;
1695b680 1142 }
027a1604 1143 }
1144
da5c172a 1145 // Next, check if the user can be in a particular course
1146 if ($courseid) {
ec81373f 1147 if ($courseid == SITEID) { // Anyone can be in the site course
1148 if (isset($cm) and !$cm->visible and !isteacher(SITEID)) { // Not allowed to see module, send to course page
1149 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1150 }
d2a9f7cc 1151 return;
e3512050 1152 }
9c9f7d77 1153 if (!empty($USER->student[$courseid]) or !empty($USER->teacher[$courseid]) or !empty($USER->admin)) {
cb909d74 1154 if (isset($USER->realuser)) { // Make sure the REAL person can also access this course
1155 if (!isteacher($courseid, $USER->realuser)) {
1156 print_header();
b0ccd3fb 1157 notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
cb909d74 1158 }
3ce2f1e0 1159 }
ec81373f 1160 if (isset($cm) and !$cm->visible and !isteacher($courseid)) { // Not allowed to see module, send to course page
1161 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1162 }
da5c172a 1163 return; // user is a member of this course.
1164 }
b0ccd3fb 1165 if (! $course = get_record('course', 'id', $courseid)) {
1166 error('That course doesn\'t exist');
da5c172a 1167 }
1efa27fd 1168 if (!$course->visible) {
1169 print_header();
4bd2e69a 1170 notice(get_string('coursehidden'), $CFG->wwwroot .'/');
1efa27fd 1171 }
b0ccd3fb 1172 if ($USER->username == 'guest') {
7363ff91 1173 switch ($course->guest) {
1174 case 0: // Guests not allowed
1175 print_header();
ea971152 1176 notice(get_string('guestsnotallowed', '', $course->fullname), "$CFG->wwwroot/login/index.php");
7363ff91 1177 break;
1178 case 1: // Guests allowed
ec81373f 1179 if (isset($cm) and !$cm->visible) { // Not allowed to see module, send to course page
1180 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1181 }
7363ff91 1182 return;
1183 case 2: // Guests allowed with key (drop through)
1184 break;
1185 }
da5c172a 1186 }
f9903ed0 1187
9ca3b4f3 1188 //User is not enrolled in the course, wants to access course content
1189 //as a guest, and course setting allow unlimited guest access
1190 //Code cribbed from course/loginas.php
1191 if (strstr($FULLME,"username=guest") && ($course->guest==1)) {
b56ccdd9 1192 $realuser = $USER->id;
1193 $realname = fullname($USER, true);
1194 $USER = guest_user();
1195 $USER->loggedin = true;
1196 $USER->site = $CFG->wwwroot;
1197 $USER->realuser = $realuser;
5f357fb6 1198 $USER->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
1199 if (isset($SESSION->currentgroup)) { // Remember current cache setting for later
1200 $SESSION->oldcurrentgroup = $SESSION->currentgroup;
1201 unset($SESSION->currentgroup);
b56ccdd9 1202 }
1203 $guest_name = fullname($USER, true);
1204 add_to_log($course->id, "course", "loginas", "../user/view.php?id=$course->id&$USER->id$", "$realname -> $guest_name");
ec81373f 1205 if (isset($cm) and !$cm->visible) { // Not allowed to see module, send to course page
1206 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1207 }
b56ccdd9 1208 return;
9ca3b4f3 1209 }
1210
7363ff91 1211 // Currently not enrolled in the course, so see if they want to enrol
da5c172a 1212 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1213 redirect($CFG->wwwroot .'/course/enrol.php?id='. $courseid);
da5c172a 1214 die;
1215 }
f9903ed0 1216}
1217
7cf1c7bd 1218/**
1219 * This is a weaker version of {@link require_login()} which only requires login
1220 * when called from within a course rather than the site page, unless
1221 * the forcelogin option is turned on.
1222 *
1223 * @uses $CFG
c6d15803 1224 * @param int $courseid The course in question
b56ccdd9 1225 * @param boolean $autologinguest Allow autologin guests if that is wanted
7cf1c7bd 1226 */
ec81373f 1227function require_course_login($course, $autologinguest=true, $cm=null) {
f950af3c 1228 global $CFG;
1229 if ($CFG->forcelogin) {
b56ccdd9 1230 require_login();
f950af3c 1231 }
1232 if ($course->category) {
ec81373f 1233 require_login($course->id, $autologinguest, $cm);
f950af3c 1234 }
1235}
1236
7cf1c7bd 1237/**
1238 * Modify the user table by setting the currently logged in user's
1239 * last login to now.
1240 *
1241 * @uses $USER
1242 * @return boolean
1243 */
1d881d92 1244function update_user_login_times() {
1245 global $USER;
1246
1247 $USER->lastlogin = $user->lastlogin = $USER->currentlogin;
2a2f5f11 1248 $USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
1d881d92 1249
1250 $user->id = $USER->id;
1251
b0ccd3fb 1252 return update_record('user', $user);
1d881d92 1253}
1254
7cf1c7bd 1255/**
1256 * Determines if a user has completed setting up their account.
1257 *
89dcb99d 1258 * @param user $user A {@link $USER} object to test for the existance of a valid name and email
7cf1c7bd 1259 * @return boolean
1260 */
808a3baa 1261function user_not_fully_set_up($user) {
bb64b51a 1262 return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user)));
1263}
1264
1265function over_bounce_threshold($user) {
d2a9f7cc 1266
bb64b51a 1267 global $CFG;
d2a9f7cc 1268
bb64b51a 1269 if (empty($CFG->handlebounces)) {
1270 return false;
1271 }
1272 // set sensible defaults
1273 if (empty($CFG->minbounces)) {
1274 $CFG->minbounces = 10;
1275 }
1276 if (empty($CFG->bounceratio)) {
1277 $CFG->bounceratio = .20;
1278 }
1279 $bouncecount = 0;
1280 $sendcount = 0;
1281 if ($bounce = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) {
1282 $bouncecount = $bounce->value;
1283 }
1284 if ($send = get_record('user_preferences','userid',$user->id,'name','email_send_count')) {
1285 $sendcount = $send->value;
1286 }
1287 return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio);
1288}
1289
d2a9f7cc 1290/**
bb64b51a 1291 * @param $user - object containing an id
1292 * @param $reset - will reset the count to 0
1293 */
1294function set_send_count($user,$reset=false) {
d2a9f7cc 1295 if ($pref = get_record('user_preferences','userid',$user->id,'name','email_send_count')) {
bb64b51a 1296 $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
1297 update_record('user_preferences',$pref);
1298 }
1299 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
1300 // make a new one
1301 $pref->name = 'email_send_count';
1302 $pref->value = 1;
1303 $pref->userid = $user->id;
1304 insert_record('user_preferences',$pref);
1305 }
1306}
1307
d2a9f7cc 1308/**
bb64b51a 1309* @param $user - object containing an id
1310 * @param $reset - will reset the count to 0
1311 */
1312function set_bounce_count($user,$reset=false) {
d2a9f7cc 1313 if ($pref = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) {
bb64b51a 1314 $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
1315 update_record('user_preferences',$pref);
1316 }
1317 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
1318 // make a new one
1319 $pref->name = 'email_bounce_count';
1320 $pref->value = 1;
1321 $pref->userid = $user->id;
1322 insert_record('user_preferences',$pref);
1323 }
808a3baa 1324}
f9903ed0 1325
7cf1c7bd 1326/**
1327 * Keeps track of login attempts
1328 *
1329 * @uses $SESSION
1330 */
f9903ed0 1331function update_login_count() {
9fa49e22 1332
f9903ed0 1333 global $SESSION;
1334
1335 $max_logins = 10;
1336
1337 if (empty($SESSION->logincount)) {
1338 $SESSION->logincount = 1;
1339 } else {
1340 $SESSION->logincount++;
1341 }
1342
1343 if ($SESSION->logincount > $max_logins) {
9fa49e22 1344 unset($SESSION->wantsurl);
b0ccd3fb 1345 error(get_string('errortoomanylogins'));
d578afc8 1346 }
1347}
1348
7cf1c7bd 1349/**
1350 * Resets login attempts
1351 *
1352 * @uses $SESSION
1353 */
9fa49e22 1354function reset_login_count() {
9fa49e22 1355 global $SESSION;
d578afc8 1356
9fa49e22 1357 $SESSION->logincount = 0;
d578afc8 1358}
1359
7cf1c7bd 1360/**
1361 * check_for_restricted_user
1362 *
89dcb99d 1363 * @uses $CFG
1364 * @uses $USER
1365 * @param string $username ?
1366 * @param string $redirect ?
7cf1c7bd 1367 * @todo Finish documenting this function
1368 */
b0ccd3fb 1369function check_for_restricted_user($username=NULL, $redirect='') {
cb98d312 1370 global $CFG, $USER;
1371
1372 if (!$username) {
1373 if (!empty($USER->username)) {
1374 $username = $USER->username;
1375 } else {
1376 return false;
1377 }
1378 }
1379
1380 if (!empty($CFG->restrictusers)) {
1381 $names = explode(',', $CFG->restrictusers);
1382 if (in_array($username, $names)) {
b0ccd3fb 1383 error(get_string('restricteduser', 'error', fullname($USER)), $redirect);
cb98d312 1384 }
1385 }
1386}
1387
b61efafb 1388function sync_metacourses() {
1389
1390 global $CFG;
1391
5f37b628 1392 if (!$courses = get_records_sql("SELECT DISTINCT parent_course,1 FROM {$CFG->prefix}course_meta")) {
b61efafb 1393 return;
1394 }
d2a9f7cc 1395
b61efafb 1396 foreach ($courses as $course) {
1397 sync_metacourse($course->parent_course);
1398 }
1399}
1400
1401
1402/**
1403 * Goes through all enrolment records for the courses inside the metacourse and sync with them.
d2a9f7cc 1404 */
b61efafb 1405
1406function sync_metacourse($metacourseid) {
1407
87671466 1408 global $CFG,$db;
b61efafb 1409
1410 if (!$metacourse = get_record("course","id",$metacourseid)) {
1411 return false;
1412 }
1413
1414
5f37b628 1415 if (count_records('course_meta','parent_course',$metacourseid) == 0) { // if there are no child courses for this meta course, nuke the enrolments
b61efafb 1416 if ($enrolments = get_records('user_students','course',$metacourseid,'','userid,1')) {
1417 foreach ($enrolments as $enrolment) {
1418 unenrol_student($enrolment->userid,$metacourseid);
1419 }
1420 }
1421 return true;
1422 }
1423
b61efafb 1424 // this will return a list of userids from user_student for enrolments in the metacourse that shouldn't be there.
d2a9f7cc 1425 $sql = "SELECT parent.userid,max(child.course) as course
87671466 1426 FROM {$CFG->prefix}course_meta meta
d2a9f7cc 1427 JOIN {$CFG->prefix}user_students parent
87671466 1428 ON meta.parent_course = parent.course
d2a9f7cc 1429 LEFT OUTER JOIN {$CFG->prefix}user_students child
1430 ON child.course = meta.child_course
ee1bef90 1431 AND child.userid = parent.userid
87671466 1432 WHERE meta.parent_course = $metacourseid
d2a9f7cc 1433 GROUP BY child.course,parent.userid
87671466 1434 ORDER BY parent.userid,child.course";
1435
1436 $res = $db->Execute($sql);
b61efafb 1437
87671466 1438 //iterate results
1439 $enrolmentstodelete = array();
1440 while( !$res->EOF && isset($res->fields) ) {
1441 $enrolmentstodelete[] = $res->fields;
1442 $res->MoveNext();
1443 }
1444
1445 if (!empty($enrolmentstodelete)) {
1446 $last->id = 0;
1447 $last->course = 0;
b61efafb 1448 foreach ($enrolmentstodelete as $enrolment) {
87671466 1449 $enrolment = (object)$enrolment;
1450 if (count($enrolmentstodelete) == 1 && empty($enrolment->course)) {
1451 unenrol_student($enrolment->userid,$metacourseid);
1452 break;
1453 }
1454 if ($last->id != $enrolment->userid) { // we've changed
1455 if (empty($last->course) && !empty($last->id)) {
1456 unenrol_student($last->id,$metacourseid); // doing it this way for forum subscriptions etc.
1457 }
1458 $last->course = 0;
1459 $last->id = $enrolment->userid;
1460 }
1461
1462 if (!empty($enrolment->course)) {
1463 $last->course = $enrolment->course;
1464 }
1465 }
1466 if (!empty($last->id) && empty($last->course)) {
1467 unenrol_student($last->id,$metacourseid); // doing it this way for forum subscriptions etc.
b61efafb 1468 }
1469 }
1470
1471
1472 // this will return a list of userids that need to be enrolled in the metacourse
d2a9f7cc 1473 $sql = "SELECT DISTINCT child.userid,1
1474 FROM {$CFG->prefix}course_meta meta
1475 JOIN {$CFG->prefix}user_students child
1476 ON meta.child_course = child.course
1477 LEFT OUTER JOIN {$CFG->prefix}user_students parent
1478 ON meta.parent_course = parent.course
ee1bef90 1479 AND parent.userid = child.userid
d2a9f7cc 1480 WHERE parent.course IS NULL
ee1bef90 1481 AND meta.parent_course = $metacourseid";
b61efafb 1482
1483 if ($userstoadd = get_records_sql($sql)) {
1484 foreach ($userstoadd as $user) {
1485 enrol_student($user->userid,$metacourseid);
1486 }
1487 }
d2a9f7cc 1488
b61efafb 1489 // and next make sure that we have the right start time and end time (ie max and min) for them all.
1490 if ($enrolments = get_records('user_students','course',$metacourseid,'','id,userid')) {
1491 foreach ($enrolments as $enrol) {
1492 if ($maxmin = get_record_sql("SELECT min(timestart) AS timestart, max(timeend) AS timeend
0bedb187 1493 FROM {$CFG->prefix}user_students u JOIN {$CFG->prefix}course_meta mc ON u.course = mc.child_course WHERE userid = $enrol->userid
b61efafb 1494 AND mc.parent_course = $metacourseid")) {
1495 $enrol->timestart = $maxmin->timestart;
1496 $enrol->timeend = $maxmin->timeend;
1497 update_record('user_students',$enrol);
1498 }
1499 }
1500 }
1501 return true;
1502}
1503
d2a9f7cc 1504/**
b61efafb 1505 * Adds a record to the metacourse table and calls sync_metacoures
1506 */
1507function add_to_metacourse ($metacourseid, $courseid) {
d2a9f7cc 1508
b61efafb 1509 if (!$metacourse = get_record("course","id",$metacourseid)) {
1510 return false;
1511 }
d2a9f7cc 1512
b61efafb 1513 if (!$course = get_record("course","id",$courseid)) {
1514 return false;
1515 }
1516
5f37b628 1517 if (!$record = get_record("course_meta","parent_course",$metacourseid,"child_course",$courseid)) {
b61efafb 1518 $rec->parent_course = $metacourseid;
1519 $rec->child_course = $courseid;
5f37b628 1520 if (!insert_record('course_meta',$rec)) {
b61efafb 1521 return false;
1522 }
1523 return sync_metacourse($metacourseid);
1524 }
1525 return true;
d2a9f7cc 1526
b61efafb 1527}
1528
d2a9f7cc 1529/**
b61efafb 1530 * Removes the record from the metacourse table and calls sync_metacourse
1531 */
1532function remove_from_metacourse($metacourseid, $courseid) {
1533
5f37b628 1534 if (delete_records('course_meta','parent_course',$metacourseid,'child_course',$courseid)) {
b61efafb 1535 return sync_metacourse($metacourseid);
1536 }
1537 return false;
1538}
1539
1540
7cf1c7bd 1541/**
1542 * Determines if a user an admin
1543 *
1544 * @uses $USER
c6d15803 1545 * @param int $userid The id of the user as is found in the 'user' table
89dcb99d 1546 * @staticvar array $admin ?
1547 * @staticvar array $nonadmins ?
7cf1c7bd 1548 * @return boolean
89dcb99d 1549 * @todo Complete documentation for this function
7cf1c7bd 1550 */
581d7b49 1551function isadmin($userid=0) {
f9903ed0 1552 global $USER;
aa095969 1553 static $admins = array();
1554 static $nonadmins = array();
f9903ed0 1555
581d7b49 1556 if (!$userid){
1557 if (empty($USER->id)) {
1558 return false;
1559 }
1560 $userid = $USER->id;
9bd2c874 1561 }
1562
581d7b49 1563 if (in_array($userid, $admins)) {
aa095969 1564 return true;
581d7b49 1565 } else if (in_array($userid, $nonadmins)) {
aa095969 1566 return false;
b0ccd3fb 1567 } else if (record_exists('user_admins', 'userid', $userid)){
581d7b49 1568 $admins[] = $userid;
aa095969 1569 return true;
1570 } else {
581d7b49 1571 $nonadmins[] = $userid;
aa095969 1572 return false;
f9903ed0 1573 }
f9903ed0 1574}
1575
7cf1c7bd 1576/**
1577 * Determines if a user is a teacher or an admin
1578 *
9407d456 1579 * @uses $USER
c6d15803 1580 * @param int $courseid The id of the course that is being viewed, if any
1581 * @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 1582 * @param boolean $includeadmin If true this function will return true when it encounters an admin user.
1583 * @return boolean
1584 * @todo Finish documenting this function
1585 */
fb830a1b 1586function isteacher($courseid=0, $userid=0, $includeadmin=true) {
1587/// Is the user a teacher or admin?
1588 global $USER, $CFG;
f9903ed0 1589
9788367b 1590 if ($includeadmin and isadmin($userid)) { // admins can do anything the teacher can
d115a57f 1591 return true;
1592 }
1593
9407d456 1594 if (empty($courseid)) {
fb830a1b 1595 if (isadmin() or $CFG->debug > 7) {
1596 notify('Coding error: isteacher() should not be used without a valid course id as argument. Please notify a developer.');
1597 }
9407d456 1598 return isteacherinanycourse($userid, $includeadmin);
1599 }
1600
f9903ed0 1601 if (!$userid) {
71f9abf9 1602 if ($courseid) {
1603 return !empty($USER->teacher[$courseid]);
1604 }
1605 if (!isset($USER->id)) {
1606 return false;
1607 }
1608 $userid = $USER->id;
f9903ed0 1609 }
1610
9407d456 1611 return record_exists('user_teachers', 'userid', $userid, 'course', $courseid);
1612}
1613
1614/**
1615 * Determines if a user is a teacher in any course, or an admin
1616 *
1617 * @uses $USER
1618 * @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.
1619 * @param boolean $includeadmin If true this function will return true when it encounters an admin user.
1620 * @return boolean
1621 * @todo Finish documenting this function
1622 */
1623function isteacherinanycourse($userid = 0, $includeadmin = true) {
fddbcf9c 1624 global $USER;
1625
9407d456 1626 if(empty($userid)) {
1627 if(empty($USER) || empty($USER->id)) {
1628 return false;
1629 }
1630 $userid = $USER->id;
9d3c795c 1631 }
1632
fddbcf9c 1633 if (isadmin($userid) && $includeadmin) { // admins can do anything
1634 return true;
1635 }
1636
9407d456 1637 return record_exists('user_teachers', 'userid', $userid);
f9903ed0 1638}
1639
7cf1c7bd 1640/**
1641 * Determines if a user is allowed to edit a given course
1642 *
1643 * @uses $USER
c6d15803 1644 * @param int $courseid The id of the course that is being edited
1645 * @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 1646 * @return boolean
1647 */
73047f2f 1648function isteacheredit($courseid, $userid=0) {
73047f2f 1649 global $USER;
1650
d8ba183c 1651 if (isadmin($userid)) { // admins can do anything
73047f2f 1652 return true;
1653 }
1654
1655 if (!$userid) {
1656 return !empty($USER->teacheredit[$courseid]);
1657 }
1658
b0ccd3fb 1659 return get_field('user_teachers', 'editall', 'userid', $userid, 'course', $courseid);
73047f2f 1660}
1661
7cf1c7bd 1662/**
1663 * Determines if a user can create new courses
1664 *
1665 * @uses $USER
361855e6 1666 * @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 1667 * @return boolean
1668 */
1924074c 1669function iscreator ($userid=0) {
1924074c 1670 global $USER;
8a205861 1671 if (empty($USER->id)) {
1672 return false;
1673 }
1924074c 1674 if (isadmin($userid)) { // admins can do anything
1675 return true;
1676 }
8a205861 1677 if (empty($userid)) {
b0ccd3fb 1678 return record_exists('user_coursecreators', 'userid', $USER->id);
1924074c 1679 }
1680
b0ccd3fb 1681 return record_exists('user_coursecreators', 'userid', $userid);
1924074c 1682}
1683
7cf1c7bd 1684/**
1685 * Determines if a user is a student in the specified course
361855e6 1686 *
7cf1c7bd 1687 * If the course id specifies the site then the function determines
1688 * if the user is a confirmed and valid user of this site.
1689 *
1690 * @uses $USER
1691 * @uses $CFG
c6d15803 1692 * @uses SITEID
1693 * @param int $courseid The id of the course being tested
361855e6 1694 * @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 1695 * @return boolean
1696 */
8a9e3fd7 1697function isstudent($courseid, $userid=0) {
71f9abf9 1698 global $USER, $CFG;
f9903ed0 1699
2700d113 1700 if (empty($USER->id) and !$userid) {
7064e18f 1701 return false;
1702 }
1703
222ac91b 1704 if ($courseid == SITEID) {
2cc72e84 1705 if (!$userid) {
1706 $userid = $USER->id;
1707 }
1708 if (isguest($userid)) {
1709 return false;
1710 }
71f9abf9 1711 // a site teacher can never be a site student
1712 if (isteacher($courseid, $userid)) {
1713 return false;
1714 }
2700d113 1715 if ($CFG->allusersaresitestudents) {
1716 return record_exists('user', 'id', $userid);
1717 } else {
1718 return (record_exists('user_students', 'userid', $userid)
71f9abf9 1719 or record_exists('user_teachers', 'userid', $userid));
2700d113 1720 }
8f0cd6ef 1721 }
2cc72e84 1722
f9903ed0 1723 if (!$userid) {
346b1a24 1724 return !empty($USER->student[$courseid]);
f9903ed0 1725 }
1726
ebc3bd2b 1727 // $timenow = time(); // todo: add time check below
f9903ed0 1728
b0ccd3fb 1729 return record_exists('user_students', 'userid', $userid, 'course', $courseid);
f9903ed0 1730}
1731
7cf1c7bd 1732/**
1733 * Determines if the specified user is logged in as guest.
1734 *
1735 * @uses $USER
361855e6 1736 * @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 1737 * @return boolean
1738 */
da5c172a 1739function isguest($userid=0) {
1740 global $USER;
1741
1742 if (!$userid) {
b35e8568 1743 if (empty($USER->username)) {
1744 return false;
1745 }
b0ccd3fb 1746 return ($USER->username == 'guest');
da5c172a 1747 }
1748
b0ccd3fb 1749 return record_exists('user', 'id', $userid, 'username', 'guest');
da5c172a 1750}
1751
7cf1c7bd 1752/**
1753 * Determines if the currently logged in user is in editing mode
1754 *
1755 * @uses $USER
c6d15803 1756 * @param int $courseid The id of the course being tested
89dcb99d 1757 * @param user $user A {@link $USER} object. If null then the currently logged in user is used.
7cf1c7bd 1758 * @return boolean
1759 */
2c309dc2 1760function isediting($courseid, $user=NULL) {
1761 global $USER;
1762 if (!$user){
1763 $user = $USER;
1764 }
9c9f7d77 1765 if (empty($user->editing)) {
1766 return false;
1767 }
2c309dc2 1768 return ($user->editing and isteacher($courseid, $user->id));
1769}
1770
7cf1c7bd 1771/**
1772 * Determines if the logged in user is currently moving an activity
1773 *
1774 * @uses $USER
c6d15803 1775 * @param int $courseid The id of the course being tested
7cf1c7bd 1776 * @return boolean
1777 */
7977cffd 1778function ismoving($courseid) {
7977cffd 1779 global $USER;
1780
1781 if (!empty($USER->activitycopy)) {
1782 return ($USER->activitycopycourse == $courseid);
1783 }
1784 return false;
1785}
1786
7cf1c7bd 1787/**
1788 * Given an object containing firstname and lastname
1789 * values, this function returns a string with the
1790 * full name of the person.
1791 * The result may depend on system settings
1792 * or language. 'override' will force both names
361855e6 1793 * to be used even if system settings specify one.
7cf1c7bd 1794 * @uses $CFG
1795 * @uses $SESSION
1796 * @param type description
1797 * @todo Finish documenting this function
1798 */
e2cd5065 1799function fullname($user, $override=false) {
b5cbb64d 1800
f374fb10 1801 global $CFG, $SESSION;
1802
6527c077 1803 if (!isset($user->firstname) and !isset($user->lastname)) {
1804 return '';
1805 }
1806
f374fb10 1807 if (!empty($SESSION->fullnamedisplay)) {
1808 $CFG->fullnamedisplay = $SESSION->fullnamedisplay;
1809 }
e2cd5065 1810
b5cbb64d 1811 if ($CFG->fullnamedisplay == 'firstname lastname') {
b0ccd3fb 1812 return $user->firstname .' '. $user->lastname;
b5cbb64d 1813
1814 } else if ($CFG->fullnamedisplay == 'lastname firstname') {
b0ccd3fb 1815 return $user->lastname .' '. $user->firstname;
e2cd5065 1816
b5cbb64d 1817 } else if ($CFG->fullnamedisplay == 'firstname') {
1818 if ($override) {
1819 return get_string('fullnamedisplay', '', $user);
1820 } else {
1821 return $user->firstname;
1822 }
1823 }
e2cd5065 1824
b5cbb64d 1825 return get_string('fullnamedisplay', '', $user);
e2cd5065 1826}
1827
7cf1c7bd 1828/**
1829 * Sets a moodle cookie with an encrypted string
1830 *
1831 * @uses $CFG
2f87145b 1832 * @uses DAYSECS
1833 * @uses HOURSECS
7cf1c7bd 1834 * @param string $thing The string to encrypt and place in a cookie
1835 */
f9903ed0 1836function set_moodle_cookie($thing) {
7185e073 1837 global $CFG;
482b6e6e 1838
1839 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
f9903ed0 1840
1841 $days = 60;
7a5672c9 1842 $seconds = DAYSECS*$days;
f9903ed0 1843
7a5672c9 1844 setCookie($cookiename, '', time() - HOURSECS, '/');
b0ccd3fb 1845 setCookie($cookiename, rc4encrypt($thing), time()+$seconds, '/');
f9903ed0 1846}
1847
7cf1c7bd 1848/**
1849 * Gets a moodle cookie with an encrypted string
1850 *
1851 * @uses $CFG
1852 * @return string
1853 */
f9903ed0 1854function get_moodle_cookie() {
7185e073 1855 global $CFG;
1856
482b6e6e 1857 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
7185e073 1858
1079c8a8 1859 if (empty($_COOKIE[$cookiename])) {
b0ccd3fb 1860 return '';
1079c8a8 1861 } else {
1862 return rc4decrypt($_COOKIE[$cookiename]);
1863 }
f9903ed0 1864}
1865
7cf1c7bd 1866/**
1867 * Returns true if an internal authentication method is being used.
1868 * if method not specified then, global default is assumed
1869 *
1870 * @uses $CFG
1871 * @param string $auth Form of authentication required
1872 * @return boolean
1873 * @todo Outline auth types and provide code example
1874 */
39a5a35d 1875function is_internal_auth($auth='') {
ba7166c3 1876/// Returns true if an internal authentication method is being used.
a3f1f815 1877/// If auth not specified then global default is assumed
ba7166c3 1878
1879 global $CFG;
1880
a3f1f815 1881 if (empty($auth)) {
1882 $auth = $CFG->auth;
39a5a35d 1883 }
1884
a3f1f815 1885 return ($auth == "email" || $auth == "none" || $auth == "manual");
1886}
1887
8c3dba73 1888/**
1889 * Returns an array of user fields
1890 *
c6d15803 1891 * @uses $CFG
1892 * @uses $db
1893 * @return array User field/column names
8c3dba73 1894 * @todo Finish documenting this function
1895 */
a3f1f815 1896function get_user_fieldnames() {
a3f1f815 1897
1898 global $CFG, $db;
1899
1900 $fieldarray = $db->MetaColumnNames($CFG->prefix.'user');
1901 unset($fieldarray['ID']);
1902
1903 return $fieldarray;
ba7166c3 1904}
f9903ed0 1905
7cf1c7bd 1906/**
1907 * Creates a bare-bones user record
1908 *
1909 * @uses $CFG
7cf1c7bd 1910 * @param string $username New user's username to add to record
1911 * @param string $password New user's password to add to record
1912 * @param string $auth Form of authentication required
89dcb99d 1913 * @return user A {@link $USER} object
7cf1c7bd 1914 * @todo Outline auth types and provide code example
1915 */
71f9abf9 1916function create_user_record($username, $password, $auth='') {
366dfa60 1917 global $CFG;
71f9abf9 1918
1e22bc9c 1919 //just in case check text case
1920 $username = trim(moodle_strtolower($username));
71f9abf9 1921
3271b70f 1922 if (function_exists('auth_get_userinfo')) {
e858f9da 1923 if ($newinfo = auth_get_userinfo($username)) {
b36a8fc4 1924 $newinfo = truncate_userinfo($newinfo);
34daec9b 1925 foreach ($newinfo as $key => $value){
9f44d972 1926 $newuser->$key = addslashes(stripslashes($value)); // Just in case
e858f9da 1927 }
1928 }
1929 }
f9903ed0 1930
85a1d4c9 1931 if (!empty($newuser->email)) {
1932 if (email_is_not_allowed($newuser->email)) {
1933 unset($newuser->email);
1934 }
1935 }
1936
71f9abf9 1937 $newuser->auth = (empty($auth)) ? $CFG->auth : $auth;
faebaf0f 1938 $newuser->username = $username;
1939 $newuser->password = md5($password);
a0bac19d 1940 $newuser->lang = $CFG->lang;
faebaf0f 1941 $newuser->confirmed = 1;
59619427 1942 $newuser->lastIP = getremoteaddr();
faebaf0f 1943 $newuser->timemodified = time();
f9903ed0 1944
b0ccd3fb 1945 if (insert_record('user', $newuser)) {
1946 $user = get_user_info_from_db('username', $newuser->username);
d35757eb 1947 if($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'}){
1948 set_user_preference('auth_forcepasswordchange', 1, $user);
1949 }
1950 return $user;
faebaf0f 1951 }
1952 return false;
1953}
1954
7cf1c7bd 1955/**
1956 * Will update a local user record from an external source
1957 *
1958 * @uses $CFG
1959 * @param string $username New user's username to add to record
89dcb99d 1960 * @return user A {@link $USER} object
7cf1c7bd 1961 */
d35757eb 1962function update_user_record($username) {
d35757eb 1963 global $CFG;
1964
1965 if (function_exists('auth_get_userinfo')) {
1966 $username = trim(moodle_strtolower($username)); /// just in case check text case
1967
1968 if ($newinfo = auth_get_userinfo($username)) {
1969 foreach ($newinfo as $key => $value){
1970 if (!empty($CFG->{'auth_user_' . $key. '_updatelocal'})) {
1971 $value = addslashes(stripslashes($value)); // Just in case
1972 set_field('user', $key, $value, 'username', $username);
1973 }
1974 }
1975 }
1976 }
b0ccd3fb 1977 return get_user_info_from_db('username', $username);
d35757eb 1978}
0609562b 1979
b36a8fc4 1980function truncate_userinfo($info) {
1981/// will truncate userinfo as it comes from auth_get_userinfo (from external auth)
1982/// which may have large fields
1983
1984 // define the limits
1985 $limit = array(
1986 'username' => 100,
1c66bf59 1987 'idnumber' => 64,
b36a8fc4 1988 'firstname' => 20,
1989 'lastname' => 20,
1990 'email' => 100,
1991 'icq' => 15,
1992 'phone1' => 20,
1993 'phone2' => 20,
1994 'institution' => 40,
1995 'department' => 30,
1996 'address' => 70,
1997 'city' => 20,
1998 'country' => 2,
1999 'url' => 255,
2000 );
361855e6 2001
b36a8fc4 2002 // apply where needed
2003 foreach (array_keys($info) as $key) {
2004 if (!empty($limit[$key])) {
adfc03f9 2005 $info[$key] = trim(substr($info[$key],0, $limit[$key]));
361855e6 2006 }
b36a8fc4 2007 }
361855e6 2008
b36a8fc4 2009 return $info;
2010}
2011
7cf1c7bd 2012/**
2013 * Retrieve the guest user object
2014 *
2015 * @uses $CFG
89dcb99d 2016 * @return user A {@link $USER} object
7cf1c7bd 2017 */
0609562b 2018function guest_user() {
2019 global $CFG;
2020
b0ccd3fb 2021 if ($newuser = get_record('user', 'username', 'guest')) {
0609562b 2022 $newuser->loggedin = true;
2023 $newuser->confirmed = 1;
2024 $newuser->site = $CFG->wwwroot;
2025 $newuser->lang = $CFG->lang;
366dfa60 2026 $newuser->lastIP = getremoteaddr();
0609562b 2027 }
2028
2029 return $newuser;
2030}
2031
7cf1c7bd 2032/**
2033 * Given a username and password, this function looks them
2034 * up using the currently selected authentication mechanism,
2035 * and if the authentication is successful, it returns a
2036 * valid $user object from the 'user' table.
361855e6 2037 *
7cf1c7bd 2038 * Uses auth_ functions from the currently active auth module
2039 *
2040 * @uses $CFG
361855e6 2041 * @param string $username User's username
2042 * @param string $password User's password
89dcb99d 2043 * @return user|flase A {@link $USER} object or false if error
7cf1c7bd 2044 */
faebaf0f 2045function authenticate_user_login($username, $password) {
faebaf0f 2046
2047 global $CFG;
2048
466558e3 2049 $md5password = md5($password);
2050
27286aeb 2051 // First try to find the user in the database
466558e3 2052
18f16d61 2053 if (!$user = get_user_info_from_db('username', $username)) {
2054 $user->id = 0; // Not a user
2055 $user->auth = $CFG->auth;
2056 }
39a5a35d 2057
27286aeb 2058 // Sort out the authentication method we are using.
39a5a35d 2059
27286aeb 2060 if (empty($CFG->auth)) {
b0ccd3fb 2061 $CFG->auth = 'manual'; // Default authentication module
27286aeb 2062 }
39a5a35d 2063
27286aeb 2064 if (empty($user->auth)) { // For some reason it isn't set yet
ccb3585f 2065 if (!empty($user->id) && (isadmin($user->id) || isguest($user->id))) {
71f9abf9 2066 $auth = 'manual'; // Always assume these guys are internal
27286aeb 2067 } else {
71f9abf9 2068 $auth = $CFG->auth; // Normal users default to site method
27286aeb 2069 }
d35757eb 2070 // update user record from external DB
2071 if ($user->auth != 'manual' && $user->auth != 'email') {
2072 $user = update_user_record($username);
2073 }
71f9abf9 2074 } else {
2075 $auth = $user->auth;
27286aeb 2076 }
8f0cd6ef 2077
ce791f88 2078 if (detect_munged_arguments($auth, 0)) { // For safety on the next require
2079 return false;
2080 }
2081
b0ccd3fb 2082 if (!file_exists($CFG->dirroot .'/auth/'. $auth .'/lib.php')) {
2083 $auth = 'manual'; // Can't find auth module, default to internal
466558e3 2084 }
2085
b0ccd3fb 2086 require_once($CFG->dirroot .'/auth/'. $auth .'/lib.php');
faebaf0f 2087
2088 if (auth_user_login($username, $password)) { // Successful authentication
d613daf0 2089 if ($user->id) { // User already exists in database
71f9abf9 2090 if (empty($user->auth)) { // For some reason auth isn't set yet
2091 set_field('user', 'auth', $auth, 'username', $username);
2092 }
92710226 2093 if ($md5password <> $user->password) { // Update local copy of password for reference
71f9abf9 2094 set_field('user', 'password', $md5password, 'username', $username);
faebaf0f 2095 }
366dfa60 2096 if (!is_internal_auth()) { // update user record from external DB
d35757eb 2097 $user = update_user_record($username);
2098 }
faebaf0f 2099 } else {
71f9abf9 2100 $user = create_user_record($username, $password, $auth);
faebaf0f 2101 }
89b54325 2102
e582b65e 2103 if (function_exists('auth_iscreator')) { // Check if the user is a creator
f894a791 2104 $useriscreator = auth_iscreator($username);
2105 if (!is_null($useriscreator)) {
2106 if ($useriscreator) {
2107 if (! record_exists('user_coursecreators', 'userid', $user->id)) {
2108 $cdata->userid = $user->id;
2109 if (! insert_record('user_coursecreators', $cdata)) {
2110 error('Cannot add user to course creators.');
2111 }
39a5a35d 2112 }
f894a791 2113 } else {
2114 if (record_exists('user_coursecreators', 'userid', $user->id)) {
2115 if (! delete_records('user_coursecreators', 'userid', $user->id)) {
2116 error('Cannot remove user from course creators.');
2117 }
39a5a35d 2118 }
2119 }
361855e6 2120 }
39a5a35d 2121 }
d613daf0 2122 if ($user) {
2123 $user->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
2124 }
e582b65e 2125 return $user;
9d3c795c 2126
e582b65e 2127 } else {
b0ccd3fb 2128 add_to_log(0, 'login', 'error', $_SERVER['HTTP_REFERER'], $username);
3af57507 2129 error_log('[client '.$_SERVER['REMOTE_ADDR']."]\t$CFG->wwwroot\tFailed Login:\t$username\t".$_SERVER['HTTP_USER_AGENT']);
e582b65e 2130 return false;
2131 }
f9903ed0 2132}
2133
7cf1c7bd 2134/**
2135 * Enrols (or re-enrols) a student in a given course
2136 *
c6d15803 2137 * @param int $courseid The id of the course that is being viewed
2138 * @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.
2139 * @param int $timestart ?
2140 * @param int $timeend ?
7cf1c7bd 2141 * @return boolean
2142 * @todo Finish documenting this function
2143 */
92318548 2144function enrol_student($userid, $courseid, $timestart=0, $timeend=0, $enrol='') {
b40bc478 2145
75169b06 2146 global $CFG;
2147
b0ccd3fb 2148 if (!$course = get_record('course', 'id', $courseid)) { // Check course
3041b0f8 2149 return false;
4d312bbe 2150 }
b0ccd3fb 2151 if (!$user = get_record('user', 'id', $userid)) { // Check user
631cf796 2152 return false;
2153 }
b61efafb 2154 // enrol the student in any parent meta courses...
5f37b628 2155 if ($parents = get_records('course_meta','child_course',$courseid)) {
b61efafb 2156 foreach ($parents as $parent) {
2157 enrol_student($userid, $parent->parent_course,$timestart,$timeend,$enrol);
2158 }
2159 }
92318548 2160
2161 if (empty($enrol)) {
2162 $enrol = $CFG->enrol; // Default current method
2163 }
b0ccd3fb 2164 if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
631cf796 2165 $student->timestart = $timestart;
2166 $student->timeend = $timeend;
2167 $student->time = time();
6e8ca983 2168 $student->enrol = $enrol;
b0ccd3fb 2169 return update_record('user_students', $student);
361855e6 2170
631cf796 2171 } else {
75169b06 2172 require_once("$CFG->dirroot/mod/forum/lib.php");
2f3b54ae 2173 forum_add_user($userid, $courseid);
2174
631cf796 2175 $student->userid = $userid;
2176 $student->course = $courseid;
2177 $student->timestart = $timestart;
2178 $student->timeend = $timeend;
2179 $student->time = time();
6e8ca983 2180 $student->enrol = $enrol;
b0ccd3fb 2181 return insert_record('user_students', $student);
631cf796 2182 }
d7facad8 2183}
2184
7cf1c7bd 2185/**
2186 * Unenrols a student from a given course
2187 *
c6d15803 2188 * @param int $courseid The id of the course that is being viewed, if any
2189 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2190 * @return boolean
2191 */
9fa62805 2192function unenrol_student($userid, $courseid=0) {
d7facad8 2193
9fa62805 2194 if ($courseid) {
9fa49e22 2195 /// First delete any crucial stuff that might still send mail
b0ccd3fb 2196 if ($forums = get_records('forum', 'course', $courseid)) {
9fa49e22 2197 foreach ($forums as $forum) {
b0ccd3fb 2198 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
9fa62805 2199 }
2200 }
2201 if ($groups = get_groups($courseid, $userid)) {
2202 foreach ($groups as $group) {
b0ccd3fb 2203 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
bb09fb11 2204 }
f9903ed0 2205 }
b61efafb 2206 // enrol the student in any parent meta courses...
5f37b628 2207 if ($parents = get_records('course_meta','child_course',$courseid)) {
b61efafb 2208 foreach ($parents as $parent) {
2209 unenrol_student($userid, $parent->parent_course);
2210 }
2211 }
b0ccd3fb 2212 return delete_records('user_students', 'userid', $userid, 'course', $courseid);
9fa49e22 2213
f9903ed0 2214 } else {
b0ccd3fb 2215 delete_records('forum_subscriptions', 'userid', $userid);
2216 delete_records('groups_members', 'userid', $userid);
2217 return delete_records('user_students', 'userid', $userid);
f9903ed0 2218 }
2219}
2220
7cf1c7bd 2221/**
2222 * Add a teacher to a given course
2223 *
2224 * @uses $USER
c6d15803 2225 * @param int $courseid The id of the course that is being viewed, if any
2226 * @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.
2227 * @param int $editall ?
7cf1c7bd 2228 * @param string $role ?
c6d15803 2229 * @param int $timestart ?
2230 * @param int $timeend ?
7cf1c7bd 2231 * @return boolean
2232 * @todo Finish documenting this function
2233 */
6e8ca983 2234function add_teacher($userid, $courseid, $editall=1, $role='', $timestart=0, $timeend=0, $enrol='manual') {
7b5944cd 2235 global $CFG;
3041b0f8 2236
61451a36 2237 if ($teacher = get_record('user_teachers', 'userid', $userid, 'course', $courseid)) {
b40bc478 2238 $newteacher = NULL;
2239 $newteacher->id = $teacher->id;
2240 $newteacher->editall = $editall;
6e8ca983 2241 $newteacher->enrol = $enrol;
b40bc478 2242 if ($role) {
2243 $newteacher->role = $role;
2244 }
2245 if ($timestart) {
2246 $newteacher->timestart = $timestart;
3041b0f8 2247 }
b40bc478 2248 if ($timeend) {
2249 $newteacher->timeend = $timeend;
2250 }
2251 return update_record('user_teachers', $newteacher);
3041b0f8 2252 }
61451a36 2253
b0ccd3fb 2254 if (!record_exists('user', 'id', $userid)) {
61451a36 2255 return false; // no such user
2256 }
2257
b0ccd3fb 2258 if (!record_exists('course', 'id', $courseid)) {
61451a36 2259 return false; // no such course
2260 }
2261
2262 $teacher = NULL;
2263 $teacher->userid = $userid;
2264 $teacher->course = $courseid;
2265 $teacher->editall = $editall;
2266 $teacher->role = $role;
5a2dea02 2267 $teacher->timemodified = time();
2268 $newteacher->timestart = $timestart;
2269 $newteacher->timeend = $timeend;
b0ccd3fb 2270 if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
5a2dea02 2271 $teacher->timestart = $student->timestart;
2272 $teacher->timeend = $student->timeend;
2273 $teacher->timeaccess = $student->timeaccess;
2274 }
61451a36 2275
b0ccd3fb 2276 if (record_exists('user_teachers', 'course', $courseid)) {
61451a36 2277 $teacher->authority = 2;
2278 } else {
2279 $teacher->authority = 1;
2280 }
b0ccd3fb 2281 delete_records('user_students', 'userid', $userid, 'course', $courseid); // Unenrol as student
8f0cd6ef 2282
709f0ec8 2283 /// Add forum subscriptions for new users
7b5944cd 2284 require_once('../mod/forum/lib.php');
2285 forum_add_user($userid, $courseid);
61451a36 2286
b0ccd3fb 2287 return insert_record('user_teachers', $teacher);
61451a36 2288
3041b0f8 2289}
2290
7cf1c7bd 2291/**
2292 * Removes a teacher from a given course (or ALL courses)
2293 * Does not delete the user account
2294 *
c6d15803 2295 * @param int $courseid The id of the course that is being viewed, if any
361855e6 2296 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2297 * @return boolean
2298 */
3041b0f8 2299function remove_teacher($userid, $courseid=0) {
3041b0f8 2300 if ($courseid) {
9fa49e22 2301 /// First delete any crucial stuff that might still send mail
b0ccd3fb 2302 if ($forums = get_records('forum', 'course', $courseid)) {
9fa49e22 2303 foreach ($forums as $forum) {
b0ccd3fb 2304 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
9fa49e22 2305 }
2306 }
b02193e6 2307
2308 /// Next if the teacher is not registered as a student, but is
2309 /// a member of a group, remove them from the group.
2310 if (!isstudent($courseid, $userid)) {
2311 if ($groups = get_groups($courseid, $userid)) {
2312 foreach ($groups as $group) {
b0ccd3fb 2313 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
b02193e6 2314 }
2315 }
2316 }
2317
b0ccd3fb 2318 return delete_records('user_teachers', 'userid', $userid, 'course', $courseid);
57507290 2319 } else {
b0ccd3fb 2320 delete_records('forum_subscriptions', 'userid', $userid);
2321 return delete_records('user_teachers', 'userid', $userid);
57507290 2322 }
f9903ed0 2323}
2324
7cf1c7bd 2325/**
2326 * Add a creator to the site
2327 *
361855e6 2328 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2329 * @return boolean
2330 */
3041b0f8 2331function add_creator($userid) {
3041b0f8 2332
b0ccd3fb 2333 if (!record_exists('user_admins', 'userid', $userid)) {
2334 if (record_exists('user', 'id', $userid)) {
3041b0f8 2335 $creator->userid = $userid;
b0ccd3fb 2336 return insert_record('user_coursecreators', $creator);
3041b0f8 2337 }
2338 return false;
2339 }
2340 return true;
2341}
2342
7cf1c7bd 2343/**
2344 * Remove a creator from a site
2345 *
2346 * @uses $db
c6d15803 2347 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2348 * @return boolean
2349 */
3041b0f8 2350function remove_creator($userid) {
3041b0f8 2351 global $db;
2352
b0ccd3fb 2353 return delete_records('user_coursecreators', 'userid', $userid);
3041b0f8 2354}
2355
7cf1c7bd 2356/**
2357 * Add an admin to a site
2358 *
2359 * @uses SITEID
c6d15803 2360 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2361 * @return boolean
2362 */
3041b0f8 2363function add_admin($userid) {
3041b0f8 2364
b0ccd3fb 2365 if (!record_exists('user_admins', 'userid', $userid)) {
2366 if (record_exists('user', 'id', $userid)) {
3041b0f8 2367 $admin->userid = $userid;
361855e6 2368
f950af3c 2369 // any admin is also a teacher on the site course
222ac91b 2370 if (!record_exists('user_teachers', 'course', SITEID, 'userid', $userid)) {
2371 if (!add_teacher($userid, SITEID)) {
f950af3c 2372 return false;
2373 }
2374 }
361855e6 2375
b0ccd3fb 2376 return insert_record('user_admins', $admin);
3041b0f8 2377 }
2378 return false;
2379 }
2380 return true;
2381}
2382
7cf1c7bd 2383/**
2384 * Removes an admin from a site
2385 *
2386 * @uses $db
2387 * @uses SITEID
c6d15803 2388 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2389 * @return boolean
2390 */
3041b0f8 2391function remove_admin($userid) {
9fa49e22 2392 global $db;
f9903ed0 2393
f950af3c 2394 // remove also from the list of site teachers
222ac91b 2395 remove_teacher($userid, SITEID);
f950af3c 2396
b0ccd3fb 2397 return delete_records('user_admins', 'userid', $userid);
f9903ed0 2398}
2399
7cf1c7bd 2400/**
2401 * Clear a course out completely, deleting all content
2402 * but don't delete the course itself
2403 *
2404 * @uses $USER
2405 * @uses $SESSION
2406 * @uses $CFG
c6d15803 2407 * @param int $courseid The id of the course that is being viewed
7cf1c7bd 2408 * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
2409 * @return boolean
2410 */
07aeb7b0 2411function remove_course_contents($courseid, $showfeedback=true) {
07aeb7b0 2412
538a2210 2413 global $CFG, $USER, $SESSION;
07aeb7b0 2414
2415 $result = true;
2416
b0ccd3fb 2417 if (! $course = get_record('course', 'id', $courseid)) {
2418 error('Course ID was incorrect (can\'t find it)');
07aeb7b0 2419 }
2420
b0ccd3fb 2421 $strdeleted = get_string('deleted');
07aeb7b0 2422
2423 // First delete every instance of every module
d8ba183c 2424
b0ccd3fb 2425 if ($allmods = get_records('modules') ) {
07aeb7b0 2426 foreach ($allmods as $mod) {
2427 $modname = $mod->name;
b0ccd3fb 2428 $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
2429 $moddelete = $modname .'_delete_instance'; // Delete everything connected to an instance
2430 $moddeletecourse = $modname .'_delete_course'; // Delete other stray stuff (uncommon)
07aeb7b0 2431 $count=0;
2432 if (file_exists($modfile)) {
2433 include_once($modfile);
2434 if (function_exists($moddelete)) {
b0ccd3fb 2435 if ($instances = get_records($modname, 'course', $course->id)) {
07aeb7b0 2436 foreach ($instances as $instance) {
2437 if ($moddelete($instance->id)) {
2438 $count++;
2439 } else {
b0ccd3fb 2440 notify('Could not delete '. $modname .' instance '. $instance->id .' ('. $instance->name .')');
07aeb7b0 2441 $result = false;
2442 }
2443 }
2444 }
2445 } else {
b0ccd3fb 2446 notify('Function '. $moddelete() .'doesn\'t exist!');
07aeb7b0 2447 $result = false;
2448 }
2449
ca952b03 2450 if (function_exists($moddeletecourse)) {
2451 $moddeletecourse($course);
2452 }
07aeb7b0 2453 }
2454 if ($showfeedback) {
b0ccd3fb 2455 notify($strdeleted .' '. $count .' x '. $modname);
07aeb7b0 2456 }
2457 }
2458 } else {
b0ccd3fb 2459 error('No modules are installed!');
07aeb7b0 2460 }
2461
251af423 2462 // Delete course blocks
2463 if (delete_records('block_instance', 'pagetype', PAGE_COURSE_VIEW, 'pageid', $course->id)) {
2464 if ($showfeedback) {
2465 notify($strdeleted .' block_instance');
2466 }
2467 } else {
2468 $result = false;
2469 }
2470
07aeb7b0 2471 // Delete any user stuff
2472
b0ccd3fb 2473 if (delete_records('user_students', 'course', $course->id)) {
07aeb7b0 2474 if ($showfeedback) {
b0ccd3fb 2475 notify($strdeleted .' user_students');
07aeb7b0 2476 }
2477 } else {
2478 $result = false;
2479 }
2480
b0ccd3fb 2481 if (delete_records('user_teachers', 'course', $course->id)) {
07aeb7b0 2482 if ($showfeedback) {
b0ccd3fb 2483 notify($strdeleted .' user_teachers');
07aeb7b0 2484 }
2485 } else {
2486 $result = false;
2487 }
2488
082e3ebc 2489 // Delete any groups
2490
b0ccd3fb 2491 if ($groups = get_records('groups', 'courseid', $course->id)) {
082e3ebc 2492 foreach ($groups as $group) {
b0ccd3fb 2493 if (delete_records('groups_members', 'groupid', $group->id)) {
082e3ebc 2494 if ($showfeedback) {
b0ccd3fb 2495 notify($strdeleted .' groups_members');
082e3ebc 2496 }
2497 } else {
2498 $result = false;
2499 }
b0ccd3fb 2500 if (delete_records('groups', 'id', $group->id)) {
082e3ebc 2501 if ($showfeedback) {
b0ccd3fb 2502 notify($strdeleted .' groups');
082e3ebc 2503 }
2504 } else {
2505 $result = false;
2506 }
2507 }
2508 }
2509
2510 // Delete events
2511
b0ccd3fb 2512 if (delete_records('event', 'courseid', $course->id)) {
082e3ebc 2513 if ($showfeedback) {
b0ccd3fb 2514 notify($strdeleted .' event');
082e3ebc 2515 }
2516 } else {
2517 $result = false;
2518 }
2519
07aeb7b0 2520 // Delete logs
2521
b0ccd3fb 2522 if (delete_records('log', 'course', $course->id)) {
07aeb7b0 2523 if ($showfeedback) {
b0ccd3fb 2524 notify($strdeleted .' log');
07aeb7b0 2525 }
2526 } else {
2527 $result = false;
2528 }
2529
2530 // Delete any course stuff
2531
b0ccd3fb 2532 if (delete_records('course_sections', 'course', $course->id)) {
07aeb7b0 2533 if ($showfeedback) {
b0ccd3fb 2534 notify($strdeleted .' course_sections');
07aeb7b0 2535 }
2536 } else {
2537 $result = false;
2538 }
2539
b0ccd3fb 2540 if (delete_records('course_modules', 'course', $course->id)) {
07aeb7b0 2541 if ($showfeedback) {
b0ccd3fb 2542 notify($strdeleted .' course_modules');
07aeb7b0 2543 }
2544 } else {
2545 $result = false;
2546 }
2547
5f37b628 2548 if ($course->metacourse) {
2549 delete_records("course_meta","parent_course",$course->id);
b61efafb 2550 sync_metacourse($course->id); // have to do it here so the enrolments get nuked. sync_metacourses won't find it without the id.
2551 if ($showfeedback) {
5f37b628 2552 notify("$strdeleted course_meta");
b61efafb 2553 }
2554 }
2555 else {
5f37b628 2556 if ($parents = get_records("course_meta","child_course",$course->id)) {
b61efafb 2557 foreach ($parents as $parent) {
2558 remove_from_metacourse($parent->parent_course,$parent->child_course); // this will do the unenrolments as well.
2559 }
2560 if ($showfeedback) {
5f37b628 2561 notify("$strdeleted course_meta");
b61efafb 2562 }
2563 }
2564 }
2565
07aeb7b0 2566 return $result;
2567
2568}
2569
7cf1c7bd 2570/**
2571 * This function will empty a course of USER data as much as
2572/// possible. It will retain the activities and the structure
2573/// of the course.
2574 *
2575 * @uses $USER
7cf1c7bd 2576 * @uses $SESSION
2577 * @uses $CFG
c6d15803 2578 * @param int $courseid The id of the course that is being viewed
7cf1c7bd 2579 * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
2580 * @param boolean $removestudents ?
2581 * @param boolean $removeteachers ?
2582 * @param boolean $removegroups ?
2583 * @param boolean $removeevents ?
2584 * @param boolean $removelogs ?
2585 * @return boolean
2586 * @todo Finish documenting this function
2587 */
3831de52 2588function remove_course_userdata($courseid, $showfeedback=true,
2589 $removestudents=true, $removeteachers=false, $removegroups=true,
2590 $removeevents=true, $removelogs=false) {
3831de52 2591
538a2210 2592 global $CFG, $USER, $SESSION;
3831de52 2593
2594 $result = true;
2595
b0ccd3fb 2596 if (! $course = get_record('course', 'id', $courseid)) {
2597 error('Course ID was incorrect (can\'t find it)');
3831de52 2598 }
2599
b0ccd3fb 2600 $strdeleted = get_string('deleted');
3831de52 2601
2602 // Look in every instance of every module for data to delete
2603
b0ccd3fb 2604 if ($allmods = get_records('modules') ) {
3831de52 2605 foreach ($allmods as $mod) {
2606 $modname = $mod->name;
b0ccd3fb 2607 $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
2608 $moddeleteuserdata = $modname .'_delete_userdata'; // Function to delete user data
3831de52 2609 $count=0;
2610 if (file_exists($modfile)) {
2611 @include_once($modfile);
2612 if (function_exists($moddeleteuserdata)) {
2613 $moddeleteuserdata($course, $showfeedback);
2614 }
2615 }
2616 }
2617 } else {
b0ccd3fb 2618 error('No modules are installed!');
3831de52 2619 }
2620
2621 // Delete other stuff
2622
2623 if ($removestudents) {
2624 /// Delete student enrolments
b0ccd3fb 2625 if (delete_records('user_students', 'course', $course->id)) {
3831de52 2626 if ($showfeedback) {
b0ccd3fb 2627 notify($strdeleted .' user_students');
3831de52 2628 }
2629 } else {
2630 $result = false;
2631 }
2632 /// Delete group members (but keep the groups)
b0ccd3fb 2633 if ($groups = get_records('groups', 'courseid', $course->id)) {
3831de52 2634 foreach ($groups as $group) {
b0ccd3fb 2635 if (delete_records('groups_members', 'groupid', $group->id)) {
3831de52 2636 if ($showfeedback) {
b0ccd3fb 2637 notify($strdeleted .' groups_members');
3831de52 2638 }
2639 } else {
2640 $result = false;
2641 }
2642 }
2643 }
2644 }
2645
2646 if ($removeteachers) {
b0ccd3fb 2647 if (delete_records('user_teachers', 'course', $course->id)) {
3831de52 2648 if ($showfeedback) {
b0ccd3fb 2649 notify($strdeleted .' user_teachers');
3831de52 2650 }
2651 } else {
2652 $result = false;
2653 }
2654 }
2655
2656 if ($removegroups) {
b0ccd3fb 2657 if ($groups = get_records('groups', 'courseid', $course->id)) {
3831de52 2658 foreach ($groups as $group) {
b0ccd3fb 2659 if (delete_records('groups', 'id', $group->id)) {
3831de52 2660 if ($showfeedback) {
b0ccd3fb 2661 notify($strdeleted .' groups');
3831de52 2662 }
2663 } else {
2664 $result = false;
2665 }
2666 }
2667 }
2668 }
2669
2670 if ($removeevents) {
b0ccd3fb 2671 if (delete_records('event', 'courseid', $course->id)) {
3831de52 2672 if ($showfeedback) {
b0ccd3fb 2673 notify($strdeleted .' event');
3831de52 2674 }
2675 } else {
2676 $result = false;
2677 }
2678 }
2679
2680 if ($removelogs) {
b0ccd3fb 2681 if (delete_records('log', 'course', $course->id)) {
3831de52 2682 if ($showfeedback) {
b0ccd3fb 2683 notify($strdeleted .' log');
3831de52 2684 }
2685 } else {
2686 $result = false;
2687 }
2688 }
2689
2690 return $result;
2691
2692}
2693
2694
f9903ed0 2695
f374fb10 2696/// GROUPS /////////////////////////////////////////////////////////
d8ba183c 2697
f374fb10 2698
2699/**
2700* Returns a boolean: is the user a member of the given group?
d8ba183c 2701*
dcd338ff 2702* @param type description
7cf1c7bd 2703 * @todo Finish documenting this function
f374fb10 2704*/
2705function ismember($groupid, $userid=0) {
2706 global $USER;
2707
8a2c9076 2708 if (!$groupid) { // No point doing further checks
2709 return false;
2710 }
2711
f374fb10 2712 if (!$userid) {
0d67c514 2713 if (empty($USER->groupmember)) {
2714 return false;
2715 }
2716 foreach ($USER->groupmember as $courseid => $mgroupid) {
2717 if ($mgroupid == $groupid) {
2718 return true;
2719 }
2720 }
2721 return false;
f374fb10 2722 }
2723
b0ccd3fb 2724 return record_exists('groups_members', 'groupid', $groupid, 'userid', $userid);
f374fb10 2725}
2726
4ed533df 2727/**
2728 * Add a user to a group, return true upon success or if user already a group member
2729 *
2730 * @param groupid The group id
2731 * @param userid The user id
2732 * @todo Finish documenting this function
2733 */
2734function add_user_to_group ($groupid, $userid) {
2735 if (ismember($groupid, $userid)) return true;
2736 $record->groupid = $groupid;
2737 $record->userid = $userid;
d2a9f7cc 2738 $record->timeadded = time();
4ed533df 2739 return (insert_record('groups_members', $record) !== false);
2740}
2741
2742
0d67c514 2743/**
c6d15803 2744 * Returns the group ID of the current user in the given course
2745 *
2746 * @uses $USER
2747 * @param int $courseid The course being examined - relates to id field in 'course' table.
7cf1c7bd 2748 * @todo Finish documenting this function
c6d15803 2749 */
0d67c514 2750function mygroupid($courseid) {
2751 global $USER;
2752
2753 if (empty($USER->groupmember[$courseid])) {
2754 return 0;
2755 } else {
2756 return $USER->groupmember[$courseid];
2757 }
2758}
2759
f374fb10 2760/**
c6d15803 2761 * For a given course, and possibly course module, determine
2762 * what the current default groupmode is:
2763 * NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2764 *
89dcb99d 2765 * @param course $course A {@link $COURSE} object
2766 * @param array? $cm A course module object
c6d15803 2767 * @return int A group mode (NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS)
2768 */
f374fb10 2769function groupmode($course, $cm=null) {
2770
2771 if ($cm and !$course->groupmodeforce) {
2772 return $cm->groupmode;
2773 }
2774 return $course->groupmode;
2775}
2776
2777
2778/**
c6d15803 2779 * Sets the current group in the session variable
2780 *
2781 * @uses $SESSION
2782 * @param int $courseid The course being examined - relates to id field in 'course' table.
2783 * @param int $groupid The group being examined.
2784 * @return int Current group id which was set by this function
7cf1c7bd 2785 * @todo Finish documenting this function
c6d15803 2786 */
f374fb10 2787function set_current_group($courseid, $groupid) {
2788 global $SESSION;
2789
2790 return $SESSION->currentgroup[$courseid] = $groupid;
2791}
2792
2793
2794/**
c6d15803 2795 * Gets the current group for the current user as an id or an object
2796 *
2797 * @uses $CFG
2798 * @uses $SESSION
2799 * @param int $courseid The course being examined - relates to id field in 'course' table.
9f1f6daf 2800 * @param boolean $full If true, the return value is a full record object. If false, just the id of the record.
7cf1c7bd 2801 * @todo Finish documenting this function
c6d15803 2802 */
f374fb10 2803function get_current_group($courseid, $full=false) {
2804 global $SESSION, $USER;
2805
ce04df6b 2806 if (!isset($SESSION->currentgroup[$courseid])) {
f374fb10 2807 if (empty($USER->groupmember[$courseid])) {
8a2c9076 2808 return 0;
f374fb10 2809 } else {
2810 $SESSION->currentgroup[$courseid] = $USER->groupmember[$courseid];
2811 }
2812 }
2813
2814 if ($full) {
0da33e07 2815 return get_record('groups', 'id', $SESSION->currentgroup[$courseid]);
f374fb10 2816 } else {
2817 return $SESSION->currentgroup[$courseid];
2818 }
2819}
2820
0d67c514 2821/**
c6d15803 2822 * A combination function to make it easier for modules
2823 * to set up groups.
2824 *
2825 * It will use a given "groupid" parameter and try to use
2826 * that to reset the current group for the user.
2827 *
2828 * @uses VISIBLEGROUPS
89dcb99d 2829 * @param course $course A {@link $COURSE} object
c6d15803 2830 * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2831 * @param int $groupid Will try to use this optional parameter to
2832 * reset the current group for the user
89dcb99d 2833 * @return int|false Returns the current group id or false if error.
7cf1c7bd 2834 * @todo Finish documenting this function
c6d15803 2835 */
eb6147a8 2836function get_and_set_current_group($course, $groupmode, $groupid=-1) {
0d67c514 2837
2838 if (!$groupmode) { // Groups don't even apply
d8ba183c 2839 return false;
0d67c514 2840 }
2841
2842 $currentgroupid = get_current_group($course->id);
2843
eb6147a8 2844 if ($groupid < 0) { // No change was specified
2845 return $currentgroupid;
2846 }
2847
2848 if ($groupid) { // Try to change the current group to this groupid
0d67c514 2849 if ($group = get_record('groups', 'id', $groupid, 'courseid', $course->id)) { // Exists
2850 if (isteacheredit($course->id)) { // Sets current default group
2851 $currentgroupid = set_current_group($course->id, $group->id);
2852
2853 } else if ($groupmode == VISIBLEGROUPS) { // All groups are visible
2854 $currentgroupid = $group->id;
2855 }
2856 }
eb6147a8 2857 } else { // When groupid = 0 it means show ALL groups
2858 if (isteacheredit($course->id)) { // Sets current default group
2859 $currentgroupid = set_current_group($course->id, 0);
2860
2861 } else if ($groupmode == VISIBLEGROUPS) { // All groups are visible
2862 $currentgroupid = 0;
2863 }
0d67c514 2864 }
2865
2866 return $currentgroupid;
2867}
2868
2869
c3cbfe7f 2870/**
c6d15803 2871 * A big combination function to make it easier for modules
2872 * to set up groups.
2873 *
2874 * Terminates if the current user shouldn't be looking at this group
2875 * Otherwise returns the current group if there is one
2876 * Otherwise returns false if groups aren't relevant
2877 *
2878 * @uses SEPARATEGROUPS
2879 * @uses VISIBLEGROUPS
89dcb99d 2880 * @param course $course A {@link $COURSE} object
c6d15803 2881 * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2882 * @param string $urlroot ?
7cf1c7bd 2883 * @todo Finish documenting this function
c6d15803 2884 */
c3cbfe7f 2885function setup_and_print_groups($course, $groupmode, $urlroot) {
2886
eb6147a8 2887 if (isset($_GET['group'])) {
2888 $changegroup = $_GET['group']; /// 0 or higher
2889 } else {
2890 $changegroup = -1; /// This means no group change was specified
2891 }
2892
2893 $currentgroup = get_and_set_current_group($course, $groupmode, $changegroup);
c3cbfe7f 2894
eb6147a8 2895 if ($currentgroup === false) {
c3cbfe7f 2896 return false;
2897 }
2898
4b6d8dd5 2899 if ($groupmode == SEPARATEGROUPS and !isteacheredit($course->id) and !$currentgroup) {
2900 print_heading(get_string('notingroup'));
c3cbfe7f 2901 print_footer($course);
2902 exit;
2903 }
2904
2905 if ($groupmode == VISIBLEGROUPS or ($groupmode and isteacheredit($course->id))) {
b0ccd3fb 2906 if ($groups = get_records_menu('groups', 'courseid', $course->id, 'name ASC', 'id,name')) {
eb6147a8 2907 echo '<div align="center">';
c3cbfe7f 2908 print_group_menu($groups, $groupmode, $currentgroup, $urlroot);
eb6147a8 2909 echo '</div>';
c3cbfe7f 2910 }
2911 }
2912
2913 return $currentgroup;
2914}
0d67c514 2915
bb64b51a 2916function generate_email_processing_address($modid,$modargs) {
2917 global $CFG;
303d0af1 2918
2919 if (empty($CFG->siteidentifier)) { // Unique site identification code
2920 set_config('siteidentifier', random_string(32));
bb64b51a 2921 }
d2a9f7cc 2922
bb64b51a 2923 $header = $CFG->mailprefix . substr(base64_encode(pack('C',$modid)),0,2).$modargs;
2924 return $header . substr(md5($header.$CFG->sitesecret),0,16).'@'.$CFG->maildomain;
2925}
2926
f374fb10 2927
bb64b51a 2928function moodle_process_email($modargs,$body) {
2929 // the first char should be an unencoded letter. We'll take this as an action
2930 switch ($modargs{0}) {
2931 case 'B': { // bounce
2932 list(,$userid) = unpack('V',base64_decode(substr($modargs,1,8)));
2933 if ($user = get_record_select("user","id=$userid","id,email")) {
2934 // check the half md5 of their email
2935 $md5check = substr(md5($user->email),0,16);
2936 if ($md5check = substr($modargs, -16)) {
2937 set_bounce_count($user);
2938 }
2939 // else maybe they've already changed it?
2940 }
2941 }
2942 break;
2943 // maybe more later?
2944 }
2945}
f374fb10 2946
f9903ed0 2947/// CORRESPONDENCE ////////////////////////////////////////////////
2948
7cf1c7bd 2949/**
2950 * Send an email to a specified user
2951 *
7cf1c7bd 2952 * @uses $CFG
2953 * @uses $_SERVER
c6d15803 2954 * @uses SITEID
89dcb99d 2955 * @param user $user A {@link $USER} object
2956 * @param user $from A {@link $USER} object
7cf1c7bd 2957 * @param string $subject plain text subject line of the email
2958 * @param string $messagetext plain text version of the message
2959 * @param string $messagehtml complete html version of the message (optional)
2960 * @param string $attachment a file on the filesystem, relative to $CFG->dataroot
2961 * @param string $attachname the name of the file (extension indicates MIME)
361855e6 2962 * @param boolean $usetrueaddress determines whether $from email address should
c6d15803 2963 * be sent out. Will be overruled by user profile setting for maildisplay
361855e6 2964 * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
c6d15803 2965 * was blocked by user and "false" if there was another sort of error.
7cf1c7bd 2966 */
bb64b51a 2967function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $repyto='', $replytoname='') {
f9903ed0 2968
f9f4d999 2969 global $CFG, $FULLME;
f9903ed0 2970
0cc6fa6a 2971 global $course; // This is a bit of an ugly hack to be gotten rid of later
2972 if (!empty($course->lang)) { // Course language is defined
2973 $CFG->courselang = $course->lang;
2974 }
32e2b302 2975 if (!empty($course->theme)) { // Course language is defined
2976 $CFG->coursetheme = $course->theme;
2977 }
0cc6fa6a 2978
b0ccd3fb 2979 include_once($CFG->libdir .'/phpmailer/class.phpmailer.php');
f9903ed0 2980
cadb96f2 2981 if (empty($user)) {
2982 return false;
2983 }
2984
2985 if (!empty($user->emailstop)) {
579dcca4 2986 return 'emailstop';
f9903ed0 2987 }
d2a9f7cc 2988
bb64b51a 2989 if (over_bounce_threshold($user)) {
2990 error_log("User $user->id (".fullname($user).") is over bounce threshold! Not sending.");
2991 return false;
2992 }
d8ba183c 2993
f9903ed0 2994 $mail = new phpmailer;
2995
b0ccd3fb 2996 $mail->Version = 'Moodle '. $CFG->version; // mailer version
2997 $mail->PluginDir = $CFG->libdir .'/phpmailer/'; // plugin directory (eg smtp plugin)
562bbe90 2998
98c4eae3 2999
b0ccd3fb 3000 if (current_language() != 'en') {
3001 $mail->CharSet = get_string('thischarset');
98c4eae3 3002 }
3003
b0ccd3fb 3004 if ($CFG->smtphosts == 'qmail') {
62740736 3005 $mail->IsQmail(); // use Qmail system
3006
3007 } else if (empty($CFG->smtphosts)) {
3008 $mail->IsMail(); // use PHP mail() = sendmail
3009
3010 } else {
1e411ffc 3011 $mail->IsSMTP(); // use SMTP directly
57ef3480 3012 if ($CFG->debug > 7) {
b0ccd3fb 3013 echo '<pre>' . "\n";
57ef3480 3014 $mail->SMTPDebug = true;
3015 }
b0ccd3fb 3016 $mail->Host = $CFG->smtphosts; // specify main and backup servers
9f58537a 3017
3018 if ($CFG->smtpuser) { // Use SMTP authentication
3019 $mail->SMTPAuth = true;
3020 $mail->Username = $CFG->smtpuser;
3021 $mail->Password = $CFG->smtppass;
3022 }
7f86ce17 3023 }
f9903ed0 3024
2b97bd71 3025 $adminuser = get_admin();
3026
bb64b51a 3027 // make up an email address for handling bounces
3028 if (!empty($CFG->handlebounces)) {
3029 $modargs = 'B'.base64_encode(pack('V',$user->id)).substr(md5($user->email),0,16);
3030 $mail->Sender = generate_email_processing_address(0,$modargs);
3031 }
3032 else {
3033 $mail->Sender = $adminuser->email;
d2a9f7cc 3034 }
2b97bd71 3035
a402bdcb 3036 if (is_string($from)) { // So we can pass whatever we want if there is need
3037 $mail->From = $CFG->noreplyaddress;
0d8a590a 3038 $mail->FromName = $from;
a402bdcb 3039 } else if ($usetrueaddress and $from->maildisplay) {
b0ccd3fb 3040 $mail->From = $from->email;
6e506bf9 3041 $mail->FromName = fullname($from);
3042 } else {
b0ccd3fb 3043 $mail->From = $CFG->noreplyaddress;
0d8a590a 3044 $mail->FromName = fullname($from);
bb64b51a 3045 if (empty($replyto)) {
3046 $mail->AddReplyTo($CFG->noreplyaddress,get_string('noreplyname'));
3047 }
6e506bf9 3048 }
d2a9f7cc 3049
bb64b51a 3050 if (!empty($replyto)) {
3051 $mail->AddReplyTo($replyto,$replytoname);
3052 }
3053
136dabd8 3054 $mail->Subject = stripslashes($subject);
f9903ed0 3055
b0ccd3fb 3056 $mail->AddAddress($user->email, fullname($user) );
f9903ed0 3057
58d24720 3058 $mail->WordWrap = 79; // set word wrap
f9903ed0 3059
857b798b 3060 if (!empty($from->customheaders)) { // Add custom headers
3061 if (is_array($from->customheaders)) {
3062 foreach ($from->customheaders as $customheader) {
3063 $mail->AddCustomHeader($customheader);
3064 }
3065 } else {
3066 $mail->AddCustomHeader($from->customheaders);
3067 }
b68dca19 3068 }
8f0cd6ef 3069
433c8b2e 3070 if (!empty($from->priority)) {
3071 $mail->Priority = $from->priority;
3072 }
3073
756e1823 3074 if ($messagehtml && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it
136dabd8 3075 $mail->IsHTML(true);
b0ccd3fb 3076 $mail->Encoding = 'quoted-printable'; // Encoding to use
136dabd8 3077 $mail->Body = $messagehtml;
78681899 3078 $mail->AltBody = "\n$messagetext\n";
136dabd8 3079 } else {
3080 $mail->IsHTML(false);
78681899 3081 $mail->Body = "\n$messagetext\n";
f9903ed0 3082 }
3083
136dabd8 3084 if ($attachment && $attachname) {
3085 if (ereg( "\\.\\." ,$attachment )) { // Security check for ".." in dir path
b0ccd3fb 3086 $mail->AddAddress($adminuser->email, fullname($adminuser) );
3087 $mail->AddStringAttachment('Error in attachment. User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain');
136dabd8 3088 } else {
b0ccd3fb 3089 include_once($CFG->dirroot .'/files/mimetypes.php');
3090 $mimetype = mimeinfo('type', $attachname);
3091 $mail->AddAttachment($CFG->dataroot .'/'. $attachment, $attachname, 'base64', $mimetype);
136dabd8 3092 }
f9903ed0 3093 }
3094
136dabd8 3095 if ($mail->Send()) {
bb64b51a 3096 set_send_count($user);
136dabd8 3097 return true;
3098 } else {
b0ccd3fb 3099 mtrace('ERROR: '. $mail->ErrorInfo);
f9f4d999 3100 add_to_log(SITEID, 'library', 'mailer', $FULLME, 'ERROR: '. $mail->ErrorInfo);
f9903ed0 3101 return false;
3102 }
f9903ed0 3103}
3104
7cf1c7bd 3105/**
3106 * Resets specified user's password and send the new password to the user via email.
3107 *
3108 * @uses $CFG
89dcb99d 3109 * @param user $user A {@link $USER} object
361855e6 3110 * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
c6d15803 3111 * was blocked by user and "false" if there was another sort of error.
7cf1c7bd 3112 */
1d881d92 3113function reset_password_and_mail($user) {
3114
3115 global $CFG;
3116
3117 $site = get_site();
3118 $from = get_admin();
3119
3120 $newpassword = generate_password();
3121
b0ccd3fb 3122 if (! set_field('user', 'password', md5($newpassword), 'id', $user->id) ) {
3123 error('Could not set user password!');
1d881d92 3124 }
3125
3126 $a->firstname = $user->firstname;
3127 $a->sitename = $site->fullname;
3128 $a->username = $user->username;
3129 $a->newpassword = $newpassword;
b0ccd3fb 3130 $a->link = $CFG->wwwroot .'/login/change_password.php';
3131 $a->signoff = fullname($from, true).' ('. $from->email .')';
1d881d92 3132
b0ccd3fb 3133 $message = get_string('newpasswordtext', '', $a);
1d881d92 3134
b0ccd3fb 3135 $subject = $site->fullname .': '. get_string('changedpassword');
1d881d92 3136
3137 return email_to_user($user, $from, $subject, $message);
3138
3139}
3140
7cf1c7bd 3141/**
3142 * Send email to specified user with confirmation text and activation link.
3143 *
3144 * @uses $CFG
89dcb99d 3145 * @param user $user A {@link $USER} object
361855e6 3146 * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
c6d15803 3147 * was blocked by user and "false" if there was another sort of error.
7cf1c7bd 3148 */
3149 function send_confirmation_email($user) {
1d881d92 3150
3151 global $CFG;
3152
3153 $site = get_site();
3154 $from = get_admin();
3155
865a6f26 3156 $data->firstname = fullname($user);
1d881d92 3157 $data->sitename = $site->fullname;
b0ccd3fb 3158 $data->admin = fullname($from) .' ('. $from->email .')';
1d881d92 3159
b0ccd3fb 3160 $subject = get_string('emailconfirmationsubject', '', $site->fullname);
1d881d92 3161
0afedf68 3162 /// Make the text version a normal link for normal people
3163 $data->link = $CFG->wwwroot .'/login/confirm.php?p='. $user->secret .'&s='. $user->username;
3164 $message = get_string('emailconfirmation', '', $data);
3165
3166 /// Make the HTML version more XHTML happy (&amp;)
3167 $data->link = $CFG->wwwroot .'/login/confirm.php?p='. $user->secret .'&amp;s='. $user->username;
3168 $messagehtml = text_to_html(get_string('emailconfirmation', '', $data), false, false, true);
3169
3170 $user->mailformat = 1; // Always send HTML version as well
58d24720 3171
3172 return email_to_user($user, $from, $subject, $message, $messagehtml);
1d881d92 3173
3174}
3175
7cf1c7bd 3176/**
3177 * send_password_change_confirmation_email.
3178 *
c6d15803 3179 * @uses $CFG
89dcb99d 3180 * @param user $user A {@link $USER} object
361855e6 3181 * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
c6d15803 3182 * was blocked by user and "false" if there was another sort of error.
7cf1c7bd 3183 * @todo Finish documenting this function
3184 */
eb347b6b 3185function send_password_change_confirmation_email($user) {
3186
3187 global $CFG;
3188
3189 $site = get_site();
3190 $from = get_admin();
3191
3192 $data->firstname = $user->firstname;
3193 $data->sitename = $site->fullname;
d2a9f7cc 3194 $data->link = $CFG->wwwroot .'/login/forgot_password.php?p='. $user->secret .'&s='. $user->username;
b0ccd3fb 3195 $data->admin = fullname($from).' ('. $from->email .')';
eb347b6b 3196
b0ccd3fb 3197 $message = get_string('emailpasswordconfirmation', '', $data);
3198 $subject = get_string('emailpasswordconfirmationsubject', '', $site->fullname);
eb347b6b 3199
3200 return email_to_user($user, $from, $subject, $message);
3201
3202}
3203
7cf1c7bd 3204/**
3205 * Check that an email is allowed. It returns an error message if there
3206 * was a problem.
3207 *
3208 * @param type description
3209 * @todo Finish documenting this function
3210 */
85a1d4c9 3211function email_is_not_allowed($email) {
85a1d4c9 3212
3213 global $CFG;
3214
3215 if (!empty($CFG->allowemailaddresses)) {
3216 $allowed = explode(' ', $CFG->allowemailaddresses);
3217 foreach ($allowed as $allowedpattern) {
3218 $allowedpattern = trim($allowedpattern);
3219 if (!$allowedpattern) {
3220 continue;
3221 }
3222 if (strpos($email, $allowedpattern) !== false) { // Match!
3223 return false;
3224 }
3225 }
b0ccd3fb 3226 return get_string('emailonlyallowed', '', $CFG->allowemailaddresses);
85a1d4c9 3227
3228 } else if (!empty($CFG->denyemailaddresses)) {
3229 $denied = explode(' ', $CFG->denyemailaddresses);
3230 foreach ($denied as $deniedpattern) {
3231 $deniedpattern = trim($deniedpattern);
3232 if (!$deniedpattern) {
3233 continue;
3234 }
3235 if (strpos($email, $deniedpattern) !== false) { // Match!
b0ccd3fb 3236 return get_string('emailnotallowed', '', $CFG->denyemailaddresses);
85a1d4c9 3237 }
3238 }
3239 }
3240
3241 return false;
3242}
1d881d92 3243
136dabd8 3244
f9903ed0 3245/// FILE HANDLING /////////////////////////////////////////////
3246
7cf1c7bd 3247/**
c6d15803 3248 * Create a directory.
7cf1c7bd 3249 *
3250 * @uses $CFG
8c3dba73 3251 * @param string $directory a string of directory names under $CFG->dataroot eg stuff/assignment/1
c6d15803 3252 * param boolean $shownotices If true then notification messages will be printed out on error.
3253 * @return string|false Returns full path to directory if successful, false if not