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