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