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