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