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