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