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