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