Now lesson_pages->title supports filterall
[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 */
2ae28153 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);
2ae28153 108define('PARAM_ALPHANUM', 0x0400); //numbers or letters only
109define('PARAM_BOOL', 0x0800); //convert to value 1 or 0 using empty()
110define('PARAM_CLEANHTML',0x1000); //actual HTML code that you want cleaned and slashes removed
0ed442f8 111define('PARAM_ALPHAEXT', 0x2000); // PARAM_ALPHA plus the chars in quotes: "/-_" allowed
e0d346ff 112
8bd3fad3 113/**
114 * Definition of page types
115 */
116define('PAGE_COURSE_VIEW', 'course-view');
8bd3fad3 117
9fa49e22 118/// PARAMETER HANDLING ////////////////////////////////////////////////////
6b174680 119
e0d346ff 120/**
361855e6 121 * Returns a particular value for the named variable, taken from
122 * POST or GET. If the parameter doesn't exist then an error is
e0d346ff 123 * thrown because we require this variable.
124 *
361855e6 125 * This function should be used to initialise all required values
126 * in a script that are based on parameters. Usually it will be
e0d346ff 127 * used like this:
128 * $id = required_param('id');
129 *
130 * @param string $varname the name of the parameter variable we want
131 * @param integer $options a bit field that specifies any cleaning needed
132 * @return mixed
133 */
134function required_param($varname, $options=PARAM_CLEAN) {
e0d346ff 135
136 if (isset($_POST[$varname])) { // POST has precedence
137 $param = $_POST[$varname];
138 } else if (isset($_GET[$varname])) {
139 $param = $_GET[$varname];
140 } else {
3af57507 141 error('A required parameter ('.$varname.') was missing');
e0d346ff 142 }
143
144 return clean_param($param, $options);
145}
146
147/**
361855e6 148 * Returns a particular value for the named variable, taken from
e0d346ff 149 * POST or GET, otherwise returning a given default.
150 *
361855e6 151 * This function should be used to initialise all optional values
152 * in a script that are based on parameters. Usually it will be
e0d346ff 153 * used like this:
154 * $name = optional_param('name', 'Fred');
155 *
156 * @param string $varname the name of the parameter variable we want
157 * @param mixed $default the default value to return if nothing is found
158 * @param integer $options a bit field that specifies any cleaning needed
159 * @return mixed
160 */
161function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {
e0d346ff 162
163 if (isset($_POST[$varname])) { // POST has precedence
164 $param = $_POST[$varname];
165 } else if (isset($_GET[$varname])) {
166 $param = $_GET[$varname];
167 } else {
168 return $default;
169 }
170
171 return clean_param($param, $options);
172}
173
174/**
361855e6 175 * Used by {@link optional_param()} and {@link required_param()} to
176 * clean the variables and/or cast to specific types, based on
e0d346ff 177 * an options field.
178 *
179 * @param mixed $param the variable we are cleaning
180 * @param integer $options a bit field that specifies the cleaning needed
181 * @return mixed
182 */
183function clean_param($param, $options) {
e0d346ff 184
7744ea12 185 global $CFG;
186
3af57507 187 if (!$options) {
188 return $param; // Return raw value
189 }
190
7228f796 191 if ((string)$param == (string)(int)$param) { // It's just an integer
e0d346ff 192 return (int)$param;
193 }
194
195 if ($options & PARAM_CLEAN) {
196 $param = clean_text($param); // Sweep for scripts, etc
197 }
198
199 if ($options & PARAM_INT) {
200 $param = (int)$param; // Convert to integer
201 }
202
3af57507 203 if ($options & PARAM_ALPHA) { // Remove everything not a-z
01accf3e 204 $param = eregi_replace('[^a-zA-Z]', '', $param);
3af57507 205 }
206
f24148ef 207 if ($options & PARAM_ALPHANUM) { // Remove everything not a-zA-Z0-9
208 $param = eregi_replace('[^A-Za-z0-9]', '', $param);
209 }
210
0ed442f8 211 if ($options & PARAM_ALPHAEXT) { // Remove everything not a-zA-Z/_-
212 $param = eregi_replace('[^a-zA-Z/_-]', '', $param);
213 }
214
f24148ef 215 if ($options & PARAM_BOOL) { // Convert to 1 or 0
216 $param = empty($param) ? 0 : 1;
217 }
218
3af57507 219 if ($options & PARAM_NOTAGS) { // Strip all tags completely
220 $param = strip_tags($param);
221 }
222
14d6c233 223 if ($options & PARAM_CLEANFILE) { // allow only safe characters
224 $param = clean_filename($param);
225 }
226
3af57507 227 if ($options & PARAM_FILE) { // Strip all suspicious characters from filename
14d6c233 228 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param);
229 $param = ereg_replace('\.\.+', '', $param);
fd05dffe 230 if($param == '.') {
231 $param = '';
232 }
3af57507 233 }
234
235 if ($options & PARAM_PATH) { // Strip all suspicious characters from file path
d52d5a8e 236 $param = str_replace('\\\'', '\'', $param);
237 $param = str_replace('\\"', '"', $param);
7e6b7f8d 238 $param = str_replace('\\', '/', $param);
14d6c233 239 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
7e6b7f8d 240 $param = ereg_replace('\.\.+', '', $param);
d52d5a8e 241 $param = ereg_replace('//+', '/', $param);
fd05dffe 242 $param = ereg_replace('/(\./)+', '/', $param);
3af57507 243 }
244
371a2ed0 245 if ($options & PARAM_HOST) { // allow FQDN or IPv4 dotted quad
d2a9f7cc 246 preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
4bd2e69a 247 // match ipv4 dotted quad
371a2ed0 248 if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
249 // confirm values are ok
250 if ( $match[0] > 255
251 || $match[1] > 255
d2a9f7cc 252 || $match[3] > 255
371a2ed0 253 || $match[4] > 255 ) {
254 // hmmm, what kind of dotted quad is this?
255 $param = '';
256 }
257 } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
258 && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens
259 && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens
260 ) {
261 // all is ok - $param is respected
262 } else {
263 // all is not ok...
d2a9f7cc 264 $param='';
265 }
371a2ed0 266 }
267
7744ea12 268 if ($options & PARAM_URL) { // allow safe ftp, http, mailto urls
269
270 include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
271
272 //
273 // Parameters to validateurlsyntax()
274 //
275 // s? scheme is optional
276 // H? http optional
277 // S? https optional
278 // F? ftp optional
279 // E? mailto optional
280 // u- user section not allowed
281 // P- password not allowed
282 // a? address optional
283 // I? Numeric IP address optional (can use IP or domain)
284 // p- port not allowed -- restrict to default port
285 // f? "file" path section optional
286 // q? query section optional
287 // r? fragment (anchor) optional
288 //
289 if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p-f?q?r?')) {
290 // all is ok, param is respected
291 } else {
292 $param =''; // not really ok
293 }
31686aea 294 $options ^= PARAM_URL; // Turn off the URL bit so that simple PARAM_URLs don't test true for PARAM_LOCALURL
7744ea12 295 }
296
d2a9f7cc 297 if ($options & PARAM_LOCALURL) {
7744ea12 298 // assume we passed the PARAM_URL test...
299 // allow http absolute, root relative and relative URLs within wwwroot
300 if (!empty($param)) {
d2a9f7cc 301 if (preg_match(':^/:', $param)) {
7744ea12 302 // root-relative, ok!
60ecca3a 303 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
7744ea12 304 // absolute, and matches our wwwroot
d2a9f7cc 305 } else {
7744ea12 306 // relative - let's make sure there are no tricks
307 if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) {
308 // looks ok.
309 } else {
310 $param = '';
d2a9f7cc 311 }
7744ea12 312 }
313 }
314 }
7744ea12 315
2ae28153 316 if ($options & PARAM_CLEANHTML) {
317 $param = stripslashes($param); // Remove any slashes
318 $param = clean_text($param); // Sweep for scripts, etc
319 $param = trim($param); // Sweep for scripts, etc
320 }
321
e0d346ff 322 return $param;
323}
324
7cf1c7bd 325/**
7228f796 326 * For security purposes, this function will check that the currently
327 * given sesskey (passed as a parameter to the script or this function)
328 * matches that of the current user.
7cf1c7bd 329 *
7228f796 330 * @param string $sesskey optionally provided sesskey
331 * @return boolean
332 */
333function confirm_sesskey($sesskey=NULL) {
334 global $USER;
335
089e9eae 336 if (!empty($USER->ignoresesskey)) {
337 return true;
338 }
339
7228f796 340 if (empty($sesskey)) {
341 $sesskey = required_param('sesskey'); // Check script parameters
342 }
343
344 if (!isset($USER->sesskey)) {
345 return false;
346 }
347
348 return ($USER->sesskey === $sesskey);
349}
350
351
352/**
353 * Ensure that a variable is set
354 *
355 * If $var is undefined throw an error, otherwise return $var.
356 * This function will soon be made obsolete by {@link required_param()}
7cf1c7bd 357 *
7228f796 358 * @param mixed $var the variable which may be unset
359 * @param mixed $default the value to return if $var is unset
7cf1c7bd 360 */
9fa49e22 361function require_variable($var) {
9fa49e22 362 if (! isset($var)) {
b0ccd3fb 363 error('A required parameter was missing');
6b174680 364 }
365}
366
7cf1c7bd 367
368/**
369 * Ensure that a variable is set
370 *
371 * If $var is undefined set it (by reference), otherwise return $var.
7228f796 372 * This function will soon be made obsolete by {@link optional_param()}
7cf1c7bd 373 *
374 * @param mixed $var the variable which may be unset
375 * @param mixed $default the value to return if $var is unset
376 */
9fa49e22 377function optional_variable(&$var, $default=0) {
9fa49e22 378 if (! isset($var)) {
379 $var = $default;
6b174680 380 }
381}
382
7cf1c7bd 383/**
384 * Set a key in global configuration
385 *
89dcb99d 386 * Set a key/value pair in both this session's {@link $CFG} global variable
7cf1c7bd 387 * and in the 'config' database table for future sessions.
388 *
389 * @param string $name the key to set
390 * @param string $value the value to set
391 * @uses $CFG
392 * @return bool
393 */
9fa49e22 394function set_config($name, $value) {
395/// No need for get_config because they are usually always available in $CFG
70812e39 396
42282810 397 global $CFG;
398
7cf1c7bd 399
42282810 400 $CFG->$name = $value; // So it's defined for this invocation at least
dfc9ba9b 401
b0ccd3fb 402 if (get_field('config', 'name', 'name', $name)) {
403 return set_field('config', 'value', $value, 'name', $name);
d897cae4 404 } else {
9fa49e22 405 $config->name = $name;
406 $config->value = $value;
b0ccd3fb 407 return insert_record('config', $config);
39917a09 408 }
39917a09 409}
410
7cf1c7bd 411/**
412 * Refresh current $USER session global variable with all their current preferences.
413 * @uses $USER
414 */
70812e39 415function reload_user_preferences() {
70812e39 416
417 global $USER;
418
070e2616 419 if(empty($USER) || empty($USER->id)) {
420 return false;
421 }
422
d8ba183c 423 unset($USER->preference);
70812e39 424
425 if ($preferences = get_records('user_preferences', 'userid', $USER->id)) {
426 foreach ($preferences as $preference) {
427 $USER->preference[$preference->name] = $preference->value;
428 }
4586d60c 429 } else {
430 //return empty preference array to hold new values
431 $USER->preference = array();
c6d15803 432 }
70812e39 433}
434
7cf1c7bd 435/**
436 * Sets a preference for the current user
437 * Optionally, can set a preference for a different user object
438 * @uses $USER
439 * @todo Add a better description and include usage examples.
440 * @param string $name The key to set as preference for the specified user
441 * @param string $value The value to set forthe $name key in the specified user's record
c6d15803 442 * @param int $userid A moodle user ID
7cf1c7bd 443 * @todo Add inline links to $USER and user functions in above line.
444 * @return boolean
445 */
13af52a6 446function set_user_preference($name, $value, $otheruser=NULL) {
70812e39 447
448 global $USER;
449
13af52a6 450 if (empty($otheruser)){
451 if (!empty($USER) && !empty($USER->id)) {
070e2616 452 $userid = $USER->id;
13af52a6 453 } else {
070e2616 454 return false;
455 }
13af52a6 456 } else {
457 $userid = $otheruser;
d35757eb 458 }
459
70812e39 460 if (empty($name)) {
461 return false;
462 }
463
a3f1f815 464 if ($preference = get_record('user_preferences', 'userid', $userid, 'name', $name)) {
b0ccd3fb 465 if (set_field('user_preferences', 'value', $value, 'id', $preference->id)) {
13af52a6 466 if (empty($otheruser) and !empty($USER)) {
070e2616 467 $USER->preference[$name] = $value;
468 }
066af654 469 return true;
470 } else {
471 return false;
472 }
70812e39 473
474 } else {
a3f1f815 475 $preference->userid = $userid;
70812e39 476 $preference->name = $name;
477 $preference->value = (string)$value;
066af654 478 if (insert_record('user_preferences', $preference)) {
13af52a6 479 if (empty($otheruser) and !empty($USER)) {
070e2616 480 $USER->preference[$name] = $value;
481 }
70812e39 482 return true;
483 } else {
484 return false;
485 }
486 }
487}
488
6eb3e776 489/**
490 * Unsets a preference completely by deleting it from the database
491 * Optionally, can set a preference for a different user id
492 * @uses $USER
493 * @param string $name The key to unset as preference for the specified user
c6d15803 494 * @param int $userid A moodle user ID
6eb3e776 495 * @return boolean
496 */
497function unset_user_preference($name, $userid=NULL) {
498
499 global $USER;
500
361855e6 501 if (empty($userid)){
070e2616 502 if(!empty($USER) && !empty($USER->id)) {
503 $userid = $USER->id;
504 }
505 else {
506 return false;
507 }
6eb3e776 508 }
509
510 return delete_records('user_preferences', 'userid', $userid, 'name', $name);
511}
512
513
7cf1c7bd 514/**
515 * Sets a whole array of preferences for the current user
516 * @param array $prefarray An array of key/value pairs to be set
c6d15803 517 * @param int $userid A moodle user ID
7cf1c7bd 518 * @return boolean
519 */
a3f1f815 520function set_user_preferences($prefarray, $userid=NULL) {
521
522 global $USER;
70812e39 523
524 if (!is_array($prefarray) or empty($prefarray)) {
525 return false;
526 }
527
361855e6 528 if (empty($userid)){
108adee2 529 if (!empty($USER) && !empty($USER->id)) {
530 $userid = NULL; // Continue with the current user below
531 } else {
532 return false; // No-one to set!
070e2616 533 }
a3f1f815 534 }
535
70812e39 536 $return = true;
537 foreach ($prefarray as $name => $value) {
070e2616 538 // The order is important; if the test for return is done first, then
539 // if one function call fails all the remaining ones will be "optimized away"
a3f1f815 540 $return = set_user_preference($name, $value, $userid) and $return;
70812e39 541 }
542 return $return;
543}
544
7cf1c7bd 545/**
546 * If no arguments are supplied this function will return
361855e6 547 * all of the current user preferences as an array.
7cf1c7bd 548 * If a name is specified then this function
549 * attempts to return that particular preference value. If
550 * none is found, then the optional value $default is returned,
551 * otherwise NULL.
552 * @param string $name Name of the key to use in finding a preference value
553 * @param string $default Value to be returned if the $name key is not set in the user preferences
c6d15803 554 * @param int $userid A moodle user ID
7cf1c7bd 555 * @uses $USER
556 * @return string
557 */
a3f1f815 558function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {
70812e39 559
560 global $USER;
561
a3f1f815 562 if (empty($userid)) { // assume current user
563 if (empty($USER->preference)) {
564 return $default; // Default value (or NULL)
565 }
566 if (empty($name)) {
567 return $USER->preference; // Whole array
568 }
569 if (!isset($USER->preference[$name])) {
570 return $default; // Default value (or NULL)
571 }
572 return $USER->preference[$name]; // The single value
573
574 } else {
575 $preference = get_records_menu('user_preferences', 'userid', $userid, 'name', 'name,value');
576
577 if (empty($name)) {
578 return $preference;
579 }
580 if (!isset($preference[$name])) {
581 return $default; // Default value (or NULL)
582 }
583 return $preference[$name]; // The single value
70812e39 584 }
70812e39 585}
586
587
9fa49e22 588/// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
39917a09 589
7cf1c7bd 590/**
c6d15803 591 * Given date parts in user time produce a GMT timestamp.
7cf1c7bd 592 *
c6d15803 593 * @param int $year The year part to create timestamp of.
594 * @param int $month The month part to create timestamp of.
595 * @param int $day The day part to create timestamp of.
596 * @param int $hour The hour part to create timestamp of.
597 * @param int $minute The minute part to create timestamp of.
598 * @param int $second The second part to create timestamp of.
d2a9f7cc 599 * @param float $timezone
e34d817e 600 * @return int timestamp
7cf1c7bd 601 * @todo Finish documenting this function
602 */
9f1f6daf 603function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
39917a09 604
dddb014a 605 $timezone = get_user_timezone_offset($timezone);
606
94e34118 607 if (abs($timezone) > 13) {
9f1f6daf 608 $time = mktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year, 0);
03c17ddf 609 } else {
86f092d2 610 $time = gmmktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year, 0);
196f2619 611 $time = usertime($time, $timezone);
03c17ddf 612 }
9f1f6daf 613
85cafb3e 614 if($applydst) {
615 $time -= dst_offset_on($time);
9f1f6daf 616 }
617
196f2619 618 return $time;
85cafb3e 619
39917a09 620}
621
7cf1c7bd 622/**
623 * Given an amount of time in seconds, returns string
624 * formatted nicely as months, days, hours etc as needed
625 *
2f87145b 626 * @uses MINSECS
627 * @uses HOURSECS
628 * @uses DAYSECS
c6d15803 629 * @param int $totalsecs ?
630 * @param array $str ?
89dcb99d 631 * @return string
7cf1c7bd 632 * @todo Finish documenting this function
633 */
634 function format_time($totalsecs, $str=NULL) {
c7e3ac2a 635
6b174680 636 $totalsecs = abs($totalsecs);
c7e3ac2a 637
8dbed6be 638 if (!$str) { // Create the str structure the slow way
b0ccd3fb 639 $str->day = get_string('day');
640 $str->days = get_string('days');
641 $str->hour = get_string('hour');
642 $str->hours = get_string('hours');
643 $str->min = get_string('min');
644 $str->mins = get_string('mins');
645 $str->sec = get_string('sec');
646 $str->secs = get_string('secs');
8dbed6be 647 }
648
7a5672c9 649 $days = floor($totalsecs/DAYSECS);
650 $remainder = $totalsecs - ($days*DAYSECS);
651 $hours = floor($remainder/HOURSECS);
652 $remainder = $remainder - ($hours*HOURSECS);
653 $mins = floor($remainder/MINSECS);
654 $secs = $remainder - ($mins*MINSECS);
8dbed6be 655
656 $ss = ($secs == 1) ? $str->sec : $str->secs;
657 $sm = ($mins == 1) ? $str->min : $str->mins;
658 $sh = ($hours == 1) ? $str->hour : $str->hours;
659 $sd = ($days == 1) ? $str->day : $str->days;
660
b0ccd3fb 661 $odays = '';
662 $ohours = '';
663 $omins = '';
664 $osecs = '';
9c9f7d77 665
b0ccd3fb 666 if ($days) $odays = $days .' '. $sd;
667 if ($hours) $ohours = $hours .' '. $sh;
668 if ($mins) $omins = $mins .' '. $sm;
669 if ($secs) $osecs = $secs .' '. $ss;
6b174680 670
b0ccd3fb 671 if ($days) return $odays .' '. $ohours;
672 if ($hours) return $ohours .' '. $omins;
673 if ($mins) return $omins .' '. $osecs;
674 if ($secs) return $osecs;
675 return get_string('now');
6b174680 676}
f9903ed0 677
7cf1c7bd 678/**
679 * Returns a formatted string that represents a date in user time
680 * <b>WARNING: note that the format is for strftime(), not date().</b>
681 * Because of a bug in most Windows time libraries, we can't use
682 * the nicer %e, so we have to use %d which has leading zeroes.
683 * A lot of the fuss in the function is just getting rid of these leading
684 * zeroes as efficiently as possible.
361855e6 685 *
8c3dba73 686 * If parameter fixday = true (default), then take off leading
7cf1c7bd 687 * zero from %d, else mantain it.
688 *
2f87145b 689 * @uses HOURSECS
e34d817e 690 * @param int $date timestamp in GMT
691 * @param string $format strftime format
d2a9f7cc 692 * @param float $timezone
c6d15803 693 * @param boolean $fixday If true (default) then the leading
694 * zero from %d is removed. If false then the leading zero is mantained.
695 * @return string
7cf1c7bd 696 */
b0ccd3fb 697function userdate($date, $format='', $timezone=99, $fixday = true) {
7a302afc 698
1ac7ee24 699 global $CFG;
700
701 static $strftimedaydatetime;
102dc313 702
b0ccd3fb 703 if ($format == '') {
1ac7ee24 704 if (empty($strftimedaydatetime)) {
705 $strftimedaydatetime = get_string('strftimedaydatetime');
706 }
707 $format = $strftimedaydatetime;
5fa51a39 708 }
035cdbff 709
b0ccd3fb 710 $formatnoday = str_replace('%d', 'DD', $format);
1ac7ee24 711 if ($fixday and empty($CFG->nofixday)) { // Config.php can force %d not to be fixed.
61ae5d36 712 $fixday = ($formatnoday != $format);
713 }
dcde9f02 714
88ec5b7c 715 $date += dst_offset_on($date);
85351042 716
494b9296 717 $timezone = get_user_timezone_offset($timezone);
102dc313 718
719 if (abs($timezone) > 13) { /// Server time
d2a9f7cc 720 if ($fixday) {
102dc313 721 $datestring = strftime($formatnoday, $date);
722 $daystring = str_replace(' 0', '', strftime(' %d', $date));
723 $datestring = str_replace('DD', $daystring, $datestring);
724 } else {
725 $datestring = strftime($format, $date);
726 }
88ec5b7c 727 } else {
102dc313 728 $date += (int)($timezone * 3600);
729 if ($fixday) {
730 $datestring = gmstrftime($formatnoday, $date);
731 $daystring = str_replace(' 0', '', gmstrftime(' %d', $date));
732 $datestring = str_replace('DD', $daystring, $datestring);
733 } else {
734 $datestring = gmstrftime($format, $date);
735 }
88ec5b7c 736 }
102dc313 737
035cdbff 738 return $datestring;
873960de 739}
740
7cf1c7bd 741/**
196f2619 742 * Given a $time timestamp in GMT (seconds since epoch),
c6d15803 743 * returns an array that represents the date in user time
7cf1c7bd 744 *
2f87145b 745 * @uses HOURSECS
196f2619 746 * @param int $time Timestamp in GMT
d2a9f7cc 747 * @param float $timezone
c6d15803 748 * @return array An array that represents the date in user time
7cf1c7bd 749 * @todo Finish documenting this function
750 */
196f2619 751function usergetdate($time, $timezone=99) {
6b174680 752
494b9296 753 $timezone = get_user_timezone_offset($timezone);
a36166d3 754
e34d817e 755 if (abs($timezone) > 13) { // Server time
ed1f69b0 756 return getdate($time);
d2a9f7cc 757 }
758
e34d817e 759 // There is no gmgetdate so we use gmdate instead
02f0527d 760 $time += dst_offset_on($time);
e34d817e 761 $time += intval((float)$timezone * HOURSECS);
3bba1e6e 762
763 $datestring = gmstrftime('%S_%M_%H_%d_%m_%Y_%w_%j_%A_%B', $time);
02f0527d 764
9f1f6daf 765 list(
766 $getdate['seconds'],
767 $getdate['minutes'],
768 $getdate['hours'],
769 $getdate['mday'],
770 $getdate['mon'],
771 $getdate['year'],
772 $getdate['wday'],
773 $getdate['yday'],
774 $getdate['weekday'],
775 $getdate['month']
3bba1e6e 776 ) = explode('_', $datestring);
9f1f6daf 777
d2d6171f 778 return $getdate;
d552ead0 779}
780
7cf1c7bd 781/**
782 * Given a GMT timestamp (seconds since epoch), offsets it by
783 * the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds
784 *
2f87145b 785 * @uses HOURSECS
c6d15803 786 * @param int $date Timestamp in GMT
e34d817e 787 * @param float $timezone
c6d15803 788 * @return int
7cf1c7bd 789 */
d552ead0 790function usertime($date, $timezone=99) {
a36166d3 791
494b9296 792 $timezone = get_user_timezone_offset($timezone);
2665e47a 793
0431bd7c 794 if (abs($timezone) > 13) {
d552ead0 795 return $date;
796 }
7a5672c9 797 return $date - (int)($timezone * HOURSECS);
d552ead0 798}
799
8c3dba73 800/**
801 * Given a time, return the GMT timestamp of the most recent midnight
802 * for the current user.
803 *
e34d817e 804 * @param int $date Timestamp in GMT
805 * @param float $timezone ?
c6d15803 806 * @return ?
8c3dba73 807 */
edf7fe8c 808function usergetmidnight($date, $timezone=99) {
edf7fe8c 809
494b9296 810 $timezone = get_user_timezone_offset($timezone);
edf7fe8c 811 $userdate = usergetdate($date, $timezone);
4606d9bb 812
02f0527d 813 // Time of midnight of this user's day, in GMT
814 return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone);
edf7fe8c 815
816}
817
7cf1c7bd 818/**
819 * Returns a string that prints the user's timezone
820 *
821 * @param float $timezone The user's timezone
822 * @return string
823 */
d552ead0 824function usertimezone($timezone=99) {
d552ead0 825
494b9296 826 $timezone = get_user_timezone_offset($timezone);
f30fe8d0 827
0431bd7c 828 if (abs($timezone) > 13) {
b0ccd3fb 829 return 'server time';
d552ead0 830 }
831 if (abs($timezone) < 0.5) {
b0ccd3fb 832 return 'GMT';
d552ead0 833 }
834 if ($timezone > 0) {
b0ccd3fb 835 return 'GMT+'. $timezone;
d552ead0 836 } else {
b0ccd3fb 837 return 'GMT'. $timezone;
d552ead0 838 }
f9903ed0 839}
840
7cf1c7bd 841/**
842 * Returns a float which represents the user's timezone difference from GMT in hours
843 * Checks various settings and picks the most dominant of those which have a value
844 *
7cf1c7bd 845 * @uses $CFG
846 * @uses $USER
e34d817e 847 * @param float $tz The user's timezone
c6d15803 848 * @return int
7cf1c7bd 849 */
494b9296 850function get_user_timezone_offset($tz = 99) {
f30fe8d0 851
43b59916 852 global $USER, $CFG;
853
e8904995 854 $tz = get_user_timezone($tz);
c9e55a25 855
7b9e355e 856 if (is_float($tz)) {
857 return $tz;
858 } else {
e8904995 859 $tzrecord = get_timezone_record($tz);
7b9e355e 860 if (empty($tzrecord)) {
e8904995 861 return 99.0;
862 }
4f2dbde9 863 return (float)$tzrecord->gmtoff / HOURMINS;
e8904995 864 }
865}
866
867function get_user_timezone($tz = 99) {
868 global $USER, $CFG;
43b59916 869
f30fe8d0 870 $timezones = array(
e8904995 871 $tz,
872 isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99,
43b59916 873 isset($USER->timezone) ? $USER->timezone : 99,
874 isset($CFG->timezone) ? $CFG->timezone : 99,
f30fe8d0 875 );
43b59916 876
e8904995 877 $tz = 99;
43b59916 878
e8904995 879 while(($tz == '' || $tz == 99) && $next = each($timezones)) {
880 $tz = $next['value'];
43b59916 881 }
e8904995 882
883 return is_numeric($tz) ? (float) $tz : $tz;
43b59916 884}
885
886function get_timezone_record($timezonename) {
887 global $CFG, $db;
888 static $cache = NULL;
889
8edffd15 890 if ($cache === NULL) {
43b59916 891 $cache = array();
892 }
893
8edffd15 894 if (isset($cache[$timezonename])) {
43b59916 895 return $cache[$timezonename];
f30fe8d0 896 }
897
e84a246a 898 return get_record_sql('SELECT * FROM '.$CFG->prefix.'timezone
899 WHERE name = '.$db->qstr($timezonename).' ORDER BY year DESC', true);
f30fe8d0 900}
f9903ed0 901
830a2bbd 902function calculate_user_dst_table($from_year = NULL, $to_year = NULL) {
85cafb3e 903 global $CFG, $USER;
85cafb3e 904
830a2bbd 905 if (empty($USER)) {
906 return false;
85cafb3e 907 }
908
989585e9 909 $usertz = get_user_timezone();
7cb29a3d 910
989585e9 911 if (is_float($usertz)) {
912 // Trivial timezone, no DST
913 return false;
914 }
915
916 if (!empty($USER->dstoffsettz) && $USER->dstoffsettz != $usertz) {
917 // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset
918 unset($USER->dstoffsets);
4f2dbde9 919 unset($USER->dstrange);
830a2bbd 920 }
921
922 if (!empty($USER->dstoffsets) && empty($from_year) && empty($to_year)) {
923 // Repeat calls which do not request specific year ranges stop here, we have already calculated the table
924 // This will be the return path most of the time, pretty light computationally
925 return true;
85cafb3e 926 }
927
830a2bbd 928 // Reaching here means we either need to extend our table or create it from scratch
989585e9 929
930 // Remember which TZ we calculated these changes for
931 $USER->dstoffsettz = $usertz;
932
830a2bbd 933 if(empty($USER->dstoffsets)) {
934 // If we 're creating from scratch, put the two guard elements in there
935 $USER->dstoffsets = array(1 => NULL, 0 => NULL);
936 }
937 if(empty($USER->dstrange)) {
938 // If creating from scratch
939 $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971);
940 $to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035);
941
942 // Fill in the array with the extra years we need to process
943 $yearstoprocess = array();
944 for($i = $from; $i <= $to; ++$i) {
945 $yearstoprocess[] = $i;
946 }
947
948 // Take note of which years we have processed for future calls
949 $USER->dstrange = array($from, $to);
950 }
951 else {
952 // If needing to extend the table, do the same
953 $yearstoprocess = array();
954
955 $from = max((empty($from_year) ? $USER->dstrange[0] : $from_year), 1971);
956 $to = min((empty($to_year) ? $USER->dstrange[1] : $to_year), 2035);
957
958 if($from < $USER->dstrange[0]) {
959 // Take note of which years we need to process and then note that we have processed them for future calls
960 for($i = $from; $i < $USER->dstrange[0]; ++$i) {
961 $yearstoprocess[] = $i;
962 }
963 $USER->dstrange[0] = $from;
964 }
965 if($to > $USER->dstrange[1]) {
966 // Take note of which years we need to process and then note that we have processed them for future calls
967 for($i = $USER->dstrange[1] + 1; $i <= $to; ++$i) {
968 $yearstoprocess[] = $i;
969 }
970 $USER->dstrange[1] = $to;
971 }
972 }
973
974 if(empty($yearstoprocess)) {
975 // This means that there was a call requesting a SMALLER range than we have already calculated
976 return true;
977 }
978
979 // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need
980 // Also, the array is sorted in descending timestamp order!
981
982 // Get DB data
989585e9 983 $presetrecords = get_records('timezone', 'name', $usertz, 'year DESC', 'year, gmtoff, dstoff, dst_month, dst_startday, dst_weekday, dst_skipweeks, dst_time, std_month, std_startday, std_weekday, std_skipweeks, std_time');
e789650d 984 if(empty($presetrecords)) {
985 return false;
986 }
57f1191c 987
830a2bbd 988 // Remove ending guard (first element of the array)
989 reset($USER->dstoffsets);
990 unset($USER->dstoffsets[key($USER->dstoffsets)]);
991
992 // Add all required change timestamps
993 foreach($yearstoprocess as $y) {
994 // Find the record which is in effect for the year $y
995 foreach($presetrecords as $year => $preset) {
996 if($year <= $y) {
997 break;
c9e72798 998 }
830a2bbd 999 }
1000
1001 $changes = dst_changes_for_year($y, $preset);
1002
1003 if($changes === NULL) {
1004 continue;
1005 }
1006 if($changes['dst'] != 0) {
1007 $USER->dstoffsets[$changes['dst']] = $preset->dstoff * MINSECS;
1008 }
1009 if($changes['std'] != 0) {
1010 $USER->dstoffsets[$changes['std']] = 0;
c9e72798 1011 }
85cafb3e 1012 }
42d36497 1013
830a2bbd 1014 // Put in a guard element at the top
1015 $maxtimestamp = max(array_keys($USER->dstoffsets));
1016 $USER->dstoffsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any "small" number will do
1017
1018 // Sort again
1019 krsort($USER->dstoffsets);
1020
e789650d 1021 return true;
1022}
42d36497 1023
e789650d 1024function dst_changes_for_year($year, $timezone) {
7cb29a3d 1025
e789650d 1026 if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) {
1027 return NULL;
42d36497 1028 }
7cb29a3d 1029
e789650d 1030 $monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year);
1031 $monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year);
1032
1033 list($dst_hour, $dst_min) = explode(':', $timezone->dst_time);
1034 list($std_hour, $std_min) = explode(':', $timezone->std_time);
d2a9f7cc 1035
6dc8dddc 1036 $timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false);
1037 $timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false);
830a2bbd 1038
1039 // Instead of putting hour and minute in make_timestamp(), we add them afterwards.
1040 // This has the advantage of being able to have negative values for hour, i.e. for timezones
1041 // where GMT time would be in the PREVIOUS day than the local one on which DST changes.
1042
1043 $timedst += $dst_hour * HOURSECS + $dst_min * MINSECS;
1044 $timestd += $std_hour * HOURSECS + $std_min * MINSECS;
42d36497 1045
e789650d 1046 return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd);
42d36497 1047}
1048
02f0527d 1049// $time must NOT be compensated at all, it has to be a pure timestamp
1050function dst_offset_on($time) {
830a2bbd 1051 global $USER;
1052
e789650d 1053 if(!calculate_user_dst_table()) {
85cafb3e 1054 return 0;
1055 }
02f0527d 1056
0bd7322e 1057 if(empty($USER) || empty($USER->dstoffsets)) {
c9e72798 1058 return 0;
85cafb3e 1059 }
1060
830a2bbd 1061 reset($USER->dstoffsets);
1062 while(list($from, $offset) = each($USER->dstoffsets)) {
59556d48 1063 if($from <= $time) {
c9e72798 1064 break;
1065 }
1066 }
1067
830a2bbd 1068 // This is the normal return path
1069 if($offset !== NULL) {
1070 return $offset;
02f0527d 1071 }
02f0527d 1072
830a2bbd 1073 // Reaching this point means we haven't calculated far enough, do it now:
1074 // Calculate extra DST changes if needed and recurse. The recursion always
1075 // moves toward the stopping condition, so will always end.
1076
1077 if($from == 0) {
1078 // We need a year smaller than $USER->dstrange[0]
1079 if($USER->dstrange[0] == 1971) {
1080 return 0;
1081 }
1082 calculate_user_dst_table($USER->dstrange[0] - 5, NULL);
1083 return dst_offset_on($time);
1084 }
1085 else {
1086 // We need a year larger than $USER->dstrange[1]
1087 if($USER->dstrange[1] == 2035) {
1088 return 0;
1089 }
1090 calculate_user_dst_table(NULL, $USER->dstrange[1] + 5);
1091 return dst_offset_on($time);
1092 }
85cafb3e 1093}
02f0527d 1094
28902d99 1095function find_day_in_month($startday, $weekday, $month, $year) {
8dc3f6cf 1096
1097 $daysinmonth = days_in_month($month, $year);
1098
42d36497 1099 if($weekday == -1) {
28902d99 1100 // Don't care about weekday, so return:
1101 // abs($startday) if $startday != -1
1102 // $daysinmonth otherwise
1103 return ($startday == -1) ? $daysinmonth : abs($startday);
8dc3f6cf 1104 }
1105
1106 // From now on we 're looking for a specific weekday
8dc3f6cf 1107
28902d99 1108 // Give "end of month" its actual value, since we know it
1109 if($startday == -1) {
1110 $startday = -1 * $daysinmonth;
1111 }
1112
1113 // Starting from day $startday, the sign is the direction
8dc3f6cf 1114
28902d99 1115 if($startday < 1) {
8dc3f6cf 1116
28902d99 1117 $startday = abs($startday);
8dc3f6cf 1118 $lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
1119
1120 // This is the last such weekday of the month
1121 $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday;
1122 if($lastinmonth > $daysinmonth) {
1123 $lastinmonth -= 7;
42d36497 1124 }
8dc3f6cf 1125
28902d99 1126 // Find the first such weekday <= $startday
1127 while($lastinmonth > $startday) {
8dc3f6cf 1128 $lastinmonth -= 7;
42d36497 1129 }
8dc3f6cf 1130
1131 return $lastinmonth;
1132
42d36497 1133 }
1134 else {
42d36497 1135
28902d99 1136 $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year, 0));
42d36497 1137
8dc3f6cf 1138 $diff = $weekday - $indexweekday;
1139 if($diff < 0) {
1140 $diff += 7;
42d36497 1141 }
42d36497 1142
28902d99 1143 // This is the first such weekday of the month equal to or after $startday
1144 $firstfromindex = $startday + $diff;
42d36497 1145
8dc3f6cf 1146 return $firstfromindex;
1147
1148 }
42d36497 1149}
1150
1151function days_in_month($month, $year) {
1152 return intval(date('t', mktime(12, 0, 0, $month, 1, $year, 0)));
1153}
1154
8dc3f6cf 1155function dayofweek($day, $month, $year) {
1156 // I wonder if this is any different from
1157 // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
1158 return intval(date('w', mktime(12, 0, 0, $month, $day, $year, 0)));
1159}
1160
9fa49e22 1161/// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////
f9903ed0 1162
1a33f699 1163// Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
1164// if one does not already exist, but does not overwrite existing sesskeys. Returns the
1165// sesskey string if $USER exists, or boolean false if not.
04280e85 1166function sesskey() {
1a33f699 1167 global $USER;
1168
1169 if(!isset($USER)) {
1170 return false;
1171 }
1172
1173 if (empty($USER->sesskey)) {
1174 $USER->sesskey = random_string(10);
1175 }
1176
1177 return $USER->sesskey;
1178}
1179
7cf1c7bd 1180/**
ec81373f 1181 * This function checks that the current user is logged in and has the
1182 * required privileges
1183 *
7cf1c7bd 1184 * This function checks that the current user is logged in, and optionally
ec81373f 1185 * whether they are allowed to be in a particular course and view a particular
1186 * course module.
1187 * If they are not logged in, then it redirects them to the site login unless
d2a9f7cc 1188 * $autologinguest is set and {@link $CFG}->autologinguests is set to 1 in which
ec81373f 1189 * case they are automatically logged in as guests.
1190 * If $courseid is given and the user is not enrolled in that course then the
1191 * user is redirected to the course enrolment page.
1192 * If $cm is given and the coursemodule is hidden and the user is not a teacher
1193 * in the course then the user is redirected to the course home page.
7cf1c7bd 1194 *
7cf1c7bd 1195 * @uses $CFG
c6d15803 1196 * @uses $SESSION
7cf1c7bd 1197 * @uses $USER
1198 * @uses $FULLME
c6d15803 1199 * @uses SITEID
7cf1c7bd 1200 * @uses $MoodleSession
ec81373f 1201 * @param int $courseid id of the course
d2a9f7cc 1202 * @param boolean $autologinguest
ec81373f 1203 * @param $cm course module object
7cf1c7bd 1204 */
ec81373f 1205function require_login($courseid=0, $autologinguest=true, $cm=null) {
f9903ed0 1206
73047f2f 1207 global $CFG, $SESSION, $USER, $FULLME, $MoodleSession;
d8ba183c 1208
da5c172a 1209 // First check that the user is logged in to the site.
c21c671d 1210 if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
f9903ed0 1211 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1212 if (!empty($_SERVER['HTTP_REFERER'])) {
1213 $SESSION->fromurl = $_SERVER['HTTP_REFERER'];
9f44d972 1214 }
c21c671d 1215 $USER = NULL;
8e8d0524 1216 if ($autologinguest and $CFG->autologinguests and $courseid and get_field('course','guest','id',$courseid)) {
1217 $loginguest = '?loginguest=true';
1218 } else {
1219 $loginguest = '';
a2ebe6a5 1220 }
8a33e371 1221 if (empty($CFG->loginhttps)) {
b0ccd3fb 1222 redirect($CFG->wwwroot .'/login/index.php'. $loginguest);
8a33e371 1223 } else {
b0ccd3fb 1224 $wwwroot = str_replace('http','https', $CFG->wwwroot);
1225 redirect($wwwroot .'/login/index.php'. $loginguest);
8a33e371 1226 }
20fde7b1 1227 exit;
f9903ed0 1228 }
808a3baa 1229
d35757eb 1230 // check whether the user should be changing password
027a1604 1231 // reload_user_preferences(); // Why is this necessary? Seems wasteful. - MD
a3f1f815 1232 if (!empty($USER->preference['auth_forcepasswordchange'])){
d35757eb 1233 if (is_internal_auth() || $CFG->{'auth_'.$USER->auth.'_stdchangepassword'}){
20fde7b1 1234 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1235 redirect($CFG->wwwroot .'/login/change_password.php');
d35757eb 1236 } elseif($CFG->changepassword) {
1237 redirect($CFG->changepassword);
1238 } else {
361855e6 1239 error('You cannot proceed without changing your password.
d35757eb 1240 However there is no available page for changing it.
b0ccd3fb 1241 Please contact your Moodle Administrator.');
d35757eb 1242 }
1243 }
808a3baa 1244 // Check that the user account is properly set up
1245 if (user_not_fully_set_up($USER)) {
20fde7b1 1246 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1247 redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
808a3baa 1248 }
d8ba183c 1249
366dfa60 1250 // Make sure current IP matches the one for this session (if required)
361855e6 1251 if (!empty($CFG->tracksessionip)) {
366dfa60 1252 if ($USER->sessionIP != md5(getremoteaddr())) {
1253 error(get_string('sessionipnomatch', 'error'));
1254 }
1255 }
6d8f47d6 1256
1257 // Make sure the USER has a sesskey set up. Used for checking script parameters.
04280e85 1258 sesskey();
366dfa60 1259
027a1604 1260 // Check that the user has agreed to a site policy if there is one
1261 if (!empty($CFG->sitepolicy)) {
1262 if (!$USER->policyagreed) {
957b5198 1263 $SESSION->wantsurl = $FULLME;
027a1604 1264 redirect($CFG->wwwroot .'/user/policy.php');
027a1604 1265 }
1695b680 1266 }
1267
1268 // If the site is currently under maintenance, then print a message
1269 if (!isadmin()) {
1270 if (file_exists($CFG->dataroot.'/1/maintenance.html')) {
1271 print_maintenance_message();
20fde7b1 1272 exit;
1695b680 1273 }
027a1604 1274 }
1275
da5c172a 1276 // Next, check if the user can be in a particular course
1277 if ($courseid) {
ec81373f 1278 if ($courseid == SITEID) { // Anyone can be in the site course
1279 if (isset($cm) and !$cm->visible and !isteacher(SITEID)) { // Not allowed to see module, send to course page
1280 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1281 }
d2a9f7cc 1282 return;
e3512050 1283 }
9c9f7d77 1284 if (!empty($USER->student[$courseid]) or !empty($USER->teacher[$courseid]) or !empty($USER->admin)) {
cb909d74 1285 if (isset($USER->realuser)) { // Make sure the REAL person can also access this course
1286 if (!isteacher($courseid, $USER->realuser)) {
1287 print_header();
b0ccd3fb 1288 notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
cb909d74 1289 }
3ce2f1e0 1290 }
ec81373f 1291 if (isset($cm) and !$cm->visible and !isteacher($courseid)) { // Not allowed to see module, send to course page
1292 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1293 }
da5c172a 1294 return; // user is a member of this course.
1295 }
b0ccd3fb 1296 if (! $course = get_record('course', 'id', $courseid)) {
1297 error('That course doesn\'t exist');
da5c172a 1298 }
1efa27fd 1299 if (!$course->visible) {
1300 print_header();
4bd2e69a 1301 notice(get_string('coursehidden'), $CFG->wwwroot .'/');
1efa27fd 1302 }
b0ccd3fb 1303 if ($USER->username == 'guest') {
7363ff91 1304 switch ($course->guest) {
1305 case 0: // Guests not allowed
1306 print_header();
ea971152 1307 notice(get_string('guestsnotallowed', '', $course->fullname), "$CFG->wwwroot/login/index.php");
7363ff91 1308 break;
1309 case 1: // Guests allowed
ec81373f 1310 if (isset($cm) and !$cm->visible) { // Not allowed to see module, send to course page
1311 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1312 }
7363ff91 1313 return;
1314 case 2: // Guests allowed with key (drop through)
1315 break;
1316 }
da5c172a 1317 }
f9903ed0 1318
9ca3b4f3 1319 //User is not enrolled in the course, wants to access course content
1320 //as a guest, and course setting allow unlimited guest access
1321 //Code cribbed from course/loginas.php
1322 if (strstr($FULLME,"username=guest") && ($course->guest==1)) {
b56ccdd9 1323 $realuser = $USER->id;
1324 $realname = fullname($USER, true);
1325 $USER = guest_user();
1326 $USER->loggedin = true;
1327 $USER->site = $CFG->wwwroot;
1328 $USER->realuser = $realuser;
5f357fb6 1329 $USER->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
1330 if (isset($SESSION->currentgroup)) { // Remember current cache setting for later
1331 $SESSION->oldcurrentgroup = $SESSION->currentgroup;
1332 unset($SESSION->currentgroup);
b56ccdd9 1333 }
1334 $guest_name = fullname($USER, true);
1335 add_to_log($course->id, "course", "loginas", "../user/view.php?id=$course->id&$USER->id$", "$realname -> $guest_name");
ec81373f 1336 if (isset($cm) and !$cm->visible) { // Not allowed to see module, send to course page
1337 redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
1338 }
b56ccdd9 1339 return;
9ca3b4f3 1340 }
1341
7363ff91 1342 // Currently not enrolled in the course, so see if they want to enrol
da5c172a 1343 $SESSION->wantsurl = $FULLME;
b0ccd3fb 1344 redirect($CFG->wwwroot .'/course/enrol.php?id='. $courseid);
da5c172a 1345 die;
1346 }
f9903ed0 1347}
1348
7cf1c7bd 1349/**
1350 * This is a weaker version of {@link require_login()} which only requires login
1351 * when called from within a course rather than the site page, unless
1352 * the forcelogin option is turned on.
1353 *
1354 * @uses $CFG
4febb58f 1355 * @param object $course The course object in question
b56ccdd9 1356 * @param boolean $autologinguest Allow autologin guests if that is wanted
4febb58f 1357 * @param object $cm Course activity module if known
7cf1c7bd 1358 */
ec81373f 1359function require_course_login($course, $autologinguest=true, $cm=null) {
f950af3c 1360 global $CFG;
1596edff 1361 if (!empty($CFG->forcelogin)) {
b56ccdd9 1362 require_login();
f950af3c 1363 }
4febb58f 1364 if ($course->id != SITEID) {
ec81373f 1365 require_login($course->id, $autologinguest, $cm);
f950af3c 1366 }
1367}
1368
7cf1c7bd 1369/**
1370 * Modify the user table by setting the currently logged in user's
1371 * last login to now.
1372 *
1373 * @uses $USER
1374 * @return boolean
1375 */
1d881d92 1376function update_user_login_times() {
1377 global $USER;
1378
1379 $USER->lastlogin = $user->lastlogin = $USER->currentlogin;
2a2f5f11 1380 $USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
1d881d92 1381
1382 $user->id = $USER->id;
1383
b0ccd3fb 1384 return update_record('user', $user);
1d881d92 1385}
1386
7cf1c7bd 1387/**
1388 * Determines if a user has completed setting up their account.
1389 *
89dcb99d 1390 * @param user $user A {@link $USER} object to test for the existance of a valid name and email
7cf1c7bd 1391 * @return boolean
1392 */
808a3baa 1393function user_not_fully_set_up($user) {
bb64b51a 1394 return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user)));
1395}
1396
1397function over_bounce_threshold($user) {
d2a9f7cc 1398
bb64b51a 1399 global $CFG;
d2a9f7cc 1400
bb64b51a 1401 if (empty($CFG->handlebounces)) {
1402 return false;
1403 }
1404 // set sensible defaults
1405 if (empty($CFG->minbounces)) {
1406 $CFG->minbounces = 10;
1407 }
1408 if (empty($CFG->bounceratio)) {
1409 $CFG->bounceratio = .20;
1410 }
1411 $bouncecount = 0;
1412 $sendcount = 0;
1413 if ($bounce = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) {
1414 $bouncecount = $bounce->value;
1415 }
1416 if ($send = get_record('user_preferences','userid',$user->id,'name','email_send_count')) {
1417 $sendcount = $send->value;
1418 }
1419 return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio);
1420}
1421
d2a9f7cc 1422/**
bb64b51a 1423 * @param $user - object containing an id
1424 * @param $reset - will reset the count to 0
1425 */
1426function set_send_count($user,$reset=false) {
d2a9f7cc 1427 if ($pref = get_record('user_preferences','userid',$user->id,'name','email_send_count')) {
bb64b51a 1428 $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
1429 update_record('user_preferences',$pref);
1430 }
1431 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
1432 // make a new one
1433 $pref->name = 'email_send_count';
1434 $pref->value = 1;
1435 $pref->userid = $user->id;
1436 insert_record('user_preferences',$pref);
1437 }
1438}
1439
d2a9f7cc 1440/**
bb64b51a 1441* @param $user - object containing an id
1442 * @param $reset - will reset the count to 0
1443 */
1444function set_bounce_count($user,$reset=false) {
d2a9f7cc 1445 if ($pref = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) {
bb64b51a 1446 $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
1447 update_record('user_preferences',$pref);
1448 }
1449 else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
1450 // make a new one
1451 $pref->name = 'email_bounce_count';
1452 $pref->value = 1;
1453 $pref->userid = $user->id;
1454 insert_record('user_preferences',$pref);
1455 }
808a3baa 1456}
f9903ed0 1457
7cf1c7bd 1458/**
1459 * Keeps track of login attempts
1460 *
1461 * @uses $SESSION
1462 */
f9903ed0 1463function update_login_count() {
9fa49e22 1464
f9903ed0 1465 global $SESSION;
1466
1467 $max_logins = 10;
1468
1469 if (empty($SESSION->logincount)) {
1470 $SESSION->logincount = 1;
1471 } else {
1472 $SESSION->logincount++;
1473 }
1474
1475 if ($SESSION->logincount > $max_logins) {
9fa49e22 1476 unset($SESSION->wantsurl);
b0ccd3fb 1477 error(get_string('errortoomanylogins'));
d578afc8 1478 }
1479}
1480
7cf1c7bd 1481/**
1482 * Resets login attempts
1483 *
1484 * @uses $SESSION
1485 */
9fa49e22 1486function reset_login_count() {
9fa49e22 1487 global $SESSION;
d578afc8 1488
9fa49e22 1489 $SESSION->logincount = 0;
d578afc8 1490}
1491
7cf1c7bd 1492/**
1493 * check_for_restricted_user
1494 *
89dcb99d 1495 * @uses $CFG
1496 * @uses $USER
1497 * @param string $username ?
1498 * @param string $redirect ?
7cf1c7bd 1499 * @todo Finish documenting this function
1500 */
b0ccd3fb 1501function check_for_restricted_user($username=NULL, $redirect='') {
cb98d312 1502 global $CFG, $USER;
1503
1504 if (!$username) {
1505 if (!empty($USER->username)) {
1506 $username = $USER->username;
1507 } else {
1508 return false;
1509 }
1510 }
1511
1512 if (!empty($CFG->restrictusers)) {
1513 $names = explode(',', $CFG->restrictusers);
1514 if (in_array($username, $names)) {
b0ccd3fb 1515 error(get_string('restricteduser', 'error', fullname($USER)), $redirect);
cb98d312 1516 }
1517 }
1518}
1519
b61efafb 1520function sync_metacourses() {
1521
1522 global $CFG;
1523
5f37b628 1524 if (!$courses = get_records_sql("SELECT DISTINCT parent_course,1 FROM {$CFG->prefix}course_meta")) {
b61efafb 1525 return;
1526 }
d2a9f7cc 1527
b61efafb 1528 foreach ($courses as $course) {
1529 sync_metacourse($course->parent_course);
1530 }
1531}
1532
1533
1534/**
1535 * Goes through all enrolment records for the courses inside the metacourse and sync with them.
d2a9f7cc 1536 */
b61efafb 1537
1538function sync_metacourse($metacourseid) {
1539
87671466 1540 global $CFG,$db;
b61efafb 1541
1542 if (!$metacourse = get_record("course","id",$metacourseid)) {
1543 return false;
1544 }
1545
1546
5f37b628 1547 if (count_records('course_meta','parent_course',$metacourseid) == 0) { // if there are no child courses for this meta course, nuke the enrolments
b61efafb 1548 if ($enrolments = get_records('user_students','course',$metacourseid,'','userid,1')) {
1549 foreach ($enrolments as $enrolment) {
1550 unenrol_student($enrolment->userid,$metacourseid);
1551 }
1552 }
1553 return true;
1554 }
1555
b61efafb 1556 // this will return a list of userids from user_student for enrolments in the metacourse that shouldn't be there.
d2a9f7cc 1557 $sql = "SELECT parent.userid,max(child.course) as course
87671466 1558 FROM {$CFG->prefix}course_meta meta
d2a9f7cc 1559 JOIN {$CFG->prefix}user_students parent
87671466 1560 ON meta.parent_course = parent.course
d2a9f7cc 1561 LEFT OUTER JOIN {$CFG->prefix}user_students child
1562 ON child.course = meta.child_course
ee1bef90 1563 AND child.userid = parent.userid
87671466 1564 WHERE meta.parent_course = $metacourseid
d2a9f7cc 1565 GROUP BY child.course,parent.userid
87671466 1566 ORDER BY parent.userid,child.course";
1567
1568 $res = $db->Execute($sql);
b61efafb 1569
87671466 1570 //iterate results
1571 $enrolmentstodelete = array();
1572 while( !$res->EOF && isset($res->fields) ) {
1573 $enrolmentstodelete[] = $res->fields;
1574 $res->MoveNext();
1575 }
1576
1577 if (!empty($enrolmentstodelete)) {
1578 $last->id = 0;
1579 $last->course = 0;
b61efafb 1580 foreach ($enrolmentstodelete as $enrolment) {
87671466 1581 $enrolment = (object)$enrolment;
1582 if (count($enrolmentstodelete) == 1 && empty($enrolment->course)) {
1583 unenrol_student($enrolment->userid,$metacourseid);
1584 break;
1585 }
1586 if ($last->id != $enrolment->userid) { // we've changed
1587 if (empty($last->course) && !empty($last->id)) {
1588 unenrol_student($last->id,$metacourseid); // doing it this way for forum subscriptions etc.
1589 }
1590 $last->course = 0;
1591 $last->id = $enrolment->userid;
1592 }
1593
1594 if (!empty($enrolment->course)) {
1595 $last->course = $enrolment->course;
1596 }
1597 }
1598 if (!empty($last->id) && empty($last->course)) {
1599 unenrol_student($last->id,$metacourseid); // doing it this way for forum subscriptions etc.
b61efafb 1600 }
1601 }
1602
1603
1604 // this will return a list of userids that need to be enrolled in the metacourse
d2a9f7cc 1605 $sql = "SELECT DISTINCT child.userid,1
1606 FROM {$CFG->prefix}course_meta meta
1607 JOIN {$CFG->prefix}user_students child
1608 ON meta.child_course = child.course
1609 LEFT OUTER JOIN {$CFG->prefix}user_students parent
1610 ON meta.parent_course = parent.course
ee1bef90 1611 AND parent.userid = child.userid
d2a9f7cc 1612 WHERE parent.course IS NULL
ee1bef90 1613 AND meta.parent_course = $metacourseid";
b61efafb 1614
1615 if ($userstoadd = get_records_sql($sql)) {
1616 foreach ($userstoadd as $user) {
1617 enrol_student($user->userid,$metacourseid);
1618 }
1619 }
d2a9f7cc 1620
b61efafb 1621 // and next make sure that we have the right start time and end time (ie max and min) for them all.
1622 if ($enrolments = get_records('user_students','course',$metacourseid,'','id,userid')) {
1623 foreach ($enrolments as $enrol) {
1624 if ($maxmin = get_record_sql("SELECT min(timestart) AS timestart, max(timeend) AS timeend
0bedb187 1625 FROM {$CFG->prefix}user_students u JOIN {$CFG->prefix}course_meta mc ON u.course = mc.child_course WHERE userid = $enrol->userid
b61efafb 1626 AND mc.parent_course = $metacourseid")) {
1627 $enrol->timestart = $maxmin->timestart;
1628 $enrol->timeend = $maxmin->timeend;
1629 update_record('user_students',$enrol);
1630 }
1631 }
1632 }
1633 return true;
1634}
1635
d2a9f7cc 1636/**
b61efafb 1637 * Adds a record to the metacourse table and calls sync_metacoures
1638 */
1639function add_to_metacourse ($metacourseid, $courseid) {
d2a9f7cc 1640
b61efafb 1641 if (!$metacourse = get_record("course","id",$metacourseid)) {
1642 return false;
1643 }
d2a9f7cc 1644
b61efafb 1645 if (!$course = get_record("course","id",$courseid)) {
1646 return false;
1647 }
1648
5f37b628 1649 if (!$record = get_record("course_meta","parent_course",$metacourseid,"child_course",$courseid)) {
b61efafb 1650 $rec->parent_course = $metacourseid;
1651 $rec->child_course = $courseid;
5f37b628 1652 if (!insert_record('course_meta',$rec)) {
b61efafb 1653 return false;
1654 }
1655 return sync_metacourse($metacourseid);
1656 }
1657 return true;
d2a9f7cc 1658
b61efafb 1659}
1660
d2a9f7cc 1661/**
b61efafb 1662 * Removes the record from the metacourse table and calls sync_metacourse
1663 */
1664function remove_from_metacourse($metacourseid, $courseid) {
1665
5f37b628 1666 if (delete_records('course_meta','parent_course',$metacourseid,'child_course',$courseid)) {
b61efafb 1667 return sync_metacourse($metacourseid);
1668 }
1669 return false;
1670}
1671
1672
7c12949d 1673/**
1674 * Determines if a user is currently logged in
1675 *
1676 * @uses $USER
1677 * @return boolean
1678 */
1679function isloggedin() {
1680 global $USER;
1681
1682 return (!empty($USER->id));
1683}
1684
1685
7cf1c7bd 1686/**
1687 * Determines if a user an admin
1688 *
1689 * @uses $USER
c6d15803 1690 * @param int $userid The id of the user as is found in the 'user' table
89dcb99d 1691 * @staticvar array $admin ?
1692 * @staticvar array $nonadmins ?
7cf1c7bd 1693 * @return boolean
89dcb99d 1694 * @todo Complete documentation for this function
7cf1c7bd 1695 */
581d7b49 1696function isadmin($userid=0) {
f9903ed0 1697 global $USER;
5e04ee0c 1698 static $admins, $nonadmins;
1699
1700 if (!isset($admins)) {
1701 $admins = array();
1702 $nonadmins = array();
1703 }
f9903ed0 1704
581d7b49 1705 if (!$userid){
1706 if (empty($USER->id)) {
1707 return false;
1708 }
1709 $userid = $USER->id;
9bd2c874 1710 }
1711
dcc17b63 1712 if (!empty($USER->id) and ($userid == $USER->id)) { // Check session cache
1713 return !empty($USER->admin);
1714 }
1715
581d7b49 1716 if (in_array($userid, $admins)) {
aa095969 1717 return true;
581d7b49 1718 } else if (in_array($userid, $nonadmins)) {
aa095969 1719 return false;
b0ccd3fb 1720 } else if (record_exists('user_admins', 'userid', $userid)){
581d7b49 1721 $admins[] = $userid;
aa095969 1722 return true;
1723 } else {
581d7b49 1724 $nonadmins[] = $userid;
aa095969 1725 return false;
f9903ed0 1726 }
f9903ed0 1727}
1728
7cf1c7bd 1729/**
5e04ee0c 1730 * Determines if a user is a teacher (or better)
7cf1c7bd 1731 *
9407d456 1732 * @uses $USER
c6d15803 1733 * @param int $courseid The id of the course that is being viewed, if any
1734 * @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 1735 * @param boolean $includeadmin If true this function will return true when it encounters an admin user.
1736 * @return boolean
1737 * @todo Finish documenting this function
1738 */
fb830a1b 1739function isteacher($courseid=0, $userid=0, $includeadmin=true) {
5e04ee0c 1740/// Is the user able to access this course as a teacher?
fb830a1b 1741 global $USER, $CFG;
f9903ed0 1742
5e04ee0c 1743 if (empty($userid)) { // we are relying on $USER
1744 if (empty($USER) or empty($USER->id)) { // not logged in so can't be a teacher
1745 return false;
1746 }
dcc17b63 1747 if (!empty($USER->teacher) and $courseid) { // look in session cache
1748 if (!empty($USER->teacher[$courseid])) { // Explicitly a teacher, good
1749 return true;
1750 }
5e04ee0c 1751 }
dcc17b63 1752 $userid = $USER->id; // we need to make further checks
5e04ee0c 1753 }
1754
dcc17b63 1755 if ($includeadmin and isadmin($userid)) { // admins can do anything the teacher can
d115a57f 1756 return true;
1757 }
1758
dcc17b63 1759 if (empty($courseid)) { // should not happen, but we handle it
fb830a1b 1760 if (isadmin() or $CFG->debug > 7) {
dcc17b63 1761 notify('Coding error: isteacher() should not be used without a valid course id '.
1762 'as argument. Please notify the developer for this module.');
fb830a1b 1763 }
9407d456 1764 return isteacherinanycourse($userid, $includeadmin);
1765 }
1766
dcc17b63 1767/// Last resort, check the database
1768
9407d456 1769 return record_exists('user_teachers', 'userid', $userid, 'course', $courseid);
1770}
1771
1772/**
1773 * Determines if a user is a teacher in any course, or an admin
1774 *
1775 * @uses $USER
1776 * @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.
1777 * @param boolean $includeadmin If true this function will return true when it encounters an admin user.
1778 * @return boolean
1779 * @todo Finish documenting this function
1780 */
5e04ee0c 1781function isteacherinanycourse($userid=0, $includeadmin=true) {
fddbcf9c 1782 global $USER;
1783
5e04ee0c 1784 if (empty($userid)) {
1785 if (empty($USER) or empty($USER->id)) {
9407d456 1786 return false;
1787 }
dcc17b63 1788 if (!empty($USER->teacher)) { // look in session cache
1789 return true;
1790 }
9407d456 1791 $userid = $USER->id;
9d3c795c 1792 }
1793
5e04ee0c 1794 if ($includeadmin and isadmin($userid)) { // admins can do anything
fddbcf9c 1795 return true;
1796 }
1797
9407d456 1798 return record_exists('user_teachers', 'userid', $userid);
f9903ed0 1799}
1800
7cf1c7bd 1801/**
1802 * Determines if a user is allowed to edit a given course
1803 *
1804 * @uses $USER
c6d15803 1805 * @param int $courseid The id of the course that is being edited
1806 * @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 1807 * @return boolean
1808 */
73047f2f 1809function isteacheredit($courseid, $userid=0) {
73047f2f 1810 global $USER;
1811
d8ba183c 1812 if (isadmin($userid)) { // admins can do anything
73047f2f 1813 return true;
1814 }
1815
1816 if (!$userid) {
ddd7a47a 1817 if (empty($USER) or empty($USER->id)) { // not logged in so can't be a teacher
1818 return false;
1819 }
1820 if (empty($USER->teacheredit)) { // we are relying on session cache
1821 return false;
1822 }
73047f2f 1823 return !empty($USER->teacheredit[$courseid]);
1824 }
1825
b0ccd3fb 1826 return get_field('user_teachers', 'editall', 'userid', $userid, 'course', $courseid);
73047f2f 1827}
1828
7cf1c7bd 1829/**
1830 * Determines if a user can create new courses
1831 *
1832 * @uses $USER
361855e6 1833 * @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 1834 * @return boolean
1835 */
1924074c 1836function iscreator ($userid=0) {
1924074c 1837 global $USER;
8a205861 1838 if (empty($USER->id)) {
1839 return false;
1840 }
1924074c 1841 if (isadmin($userid)) { // admins can do anything
1842 return true;
1843 }
8a205861 1844 if (empty($userid)) {
b0ccd3fb 1845 return record_exists('user_coursecreators', 'userid', $USER->id);
1924074c 1846 }
1847
b0ccd3fb 1848 return record_exists('user_coursecreators', 'userid', $userid);
1924074c 1849}
1850
7cf1c7bd 1851/**
1852 * Determines if a user is a student in the specified course
361855e6 1853 *
7cf1c7bd 1854 * If the course id specifies the site then the function determines
1855 * if the user is a confirmed and valid user of this site.
1856 *
1857 * @uses $USER
1858 * @uses $CFG
c6d15803 1859 * @uses SITEID
1860 * @param int $courseid The id of the course being tested
361855e6 1861 * @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 1862 * @return boolean
1863 */
8a9e3fd7 1864function isstudent($courseid, $userid=0) {
71f9abf9 1865 global $USER, $CFG;
f9903ed0 1866
2700d113 1867 if (empty($USER->id) and !$userid) {
7064e18f 1868 return false;
1869 }
1870
222ac91b 1871 if ($courseid == SITEID) {
2cc72e84 1872 if (!$userid) {
1873 $userid = $USER->id;
1874 }
1875 if (isguest($userid)) {
1876 return false;
1877 }
71f9abf9 1878 // a site teacher can never be a site student
1879 if (isteacher($courseid, $userid)) {
1880 return false;
1881 }
2700d113 1882 if ($CFG->allusersaresitestudents) {
1883 return record_exists('user', 'id', $userid);
1884 } else {
1885 return (record_exists('user_students', 'userid', $userid)
71f9abf9 1886 or record_exists('user_teachers', 'userid', $userid));
2700d113 1887 }
8f0cd6ef 1888 }
2cc72e84 1889
f9903ed0 1890 if (!$userid) {
346b1a24 1891 return !empty($USER->student[$courseid]);
f9903ed0 1892 }
1893
ebc3bd2b 1894 // $timenow = time(); // todo: add time check below
f9903ed0 1895
b0ccd3fb 1896 return record_exists('user_students', 'userid', $userid, 'course', $courseid);
f9903ed0 1897}
1898
7cf1c7bd 1899/**
1900 * Determines if the specified user is logged in as guest.
1901 *
1902 * @uses $USER
361855e6 1903 * @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 1904 * @return boolean
1905 */
da5c172a 1906function isguest($userid=0) {
1907 global $USER;
1908
1909 if (!$userid) {
b35e8568 1910 if (empty($USER->username)) {
1911 return false;
1912 }
b0ccd3fb 1913 return ($USER->username == 'guest');
da5c172a 1914 }
1915
b0ccd3fb 1916 return record_exists('user', 'id', $userid, 'username', 'guest');
da5c172a 1917}
1918
7cf1c7bd 1919/**
1920 * Determines if the currently logged in user is in editing mode
1921 *
1922 * @uses $USER
c6d15803 1923 * @param int $courseid The id of the course being tested
89dcb99d 1924 * @param user $user A {@link $USER} object. If null then the currently logged in user is used.
7cf1c7bd 1925 * @return boolean
1926 */
2c309dc2 1927function isediting($courseid, $user=NULL) {
1928 global $USER;
1929 if (!$user){
1930 $user = $USER;
1931 }
9c9f7d77 1932 if (empty($user->editing)) {
1933 return false;
1934 }
2c309dc2 1935 return ($user->editing and isteacher($courseid, $user->id));
1936}
1937
7cf1c7bd 1938/**
1939 * Determines if the logged in user is currently moving an activity
1940 *
1941 * @uses $USER
c6d15803 1942 * @param int $courseid The id of the course being tested
7cf1c7bd 1943 * @return boolean
1944 */
7977cffd 1945function ismoving($courseid) {
7977cffd 1946 global $USER;
1947
1948 if (!empty($USER->activitycopy)) {
1949 return ($USER->activitycopycourse == $courseid);
1950 }
1951 return false;
1952}
1953
7cf1c7bd 1954/**
1955 * Given an object containing firstname and lastname
1956 * values, this function returns a string with the
1957 * full name of the person.
1958 * The result may depend on system settings
1959 * or language. 'override' will force both names
361855e6 1960 * to be used even if system settings specify one.
7cf1c7bd 1961 * @uses $CFG
1962 * @uses $SESSION
1963 * @param type description
1964 * @todo Finish documenting this function
1965 */
e2cd5065 1966function fullname($user, $override=false) {
b5cbb64d 1967
f374fb10 1968 global $CFG, $SESSION;
1969
6527c077 1970 if (!isset($user->firstname) and !isset($user->lastname)) {
1971 return '';
1972 }
1973
f374fb10 1974 if (!empty($SESSION->fullnamedisplay)) {
1975 $CFG->fullnamedisplay = $SESSION->fullnamedisplay;
1976 }
e2cd5065 1977
b5cbb64d 1978 if ($CFG->fullnamedisplay == 'firstname lastname') {
b0ccd3fb 1979 return $user->firstname .' '. $user->lastname;
b5cbb64d 1980
1981 } else if ($CFG->fullnamedisplay == 'lastname firstname') {
b0ccd3fb 1982 return $user->lastname .' '. $user->firstname;
e2cd5065 1983
b5cbb64d 1984 } else if ($CFG->fullnamedisplay == 'firstname') {
1985 if ($override) {
1986 return get_string('fullnamedisplay', '', $user);
1987 } else {
1988 return $user->firstname;
1989 }
1990 }
e2cd5065 1991
b5cbb64d 1992 return get_string('fullnamedisplay', '', $user);
e2cd5065 1993}
1994
7cf1c7bd 1995/**
1996 * Sets a moodle cookie with an encrypted string
1997 *
1998 * @uses $CFG
2f87145b 1999 * @uses DAYSECS
2000 * @uses HOURSECS
7cf1c7bd 2001 * @param string $thing The string to encrypt and place in a cookie
2002 */
f9903ed0 2003function set_moodle_cookie($thing) {
7185e073 2004 global $CFG;
482b6e6e 2005
2006 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
f9903ed0 2007
2008 $days = 60;
7a5672c9 2009 $seconds = DAYSECS*$days;
f9903ed0 2010
7a5672c9 2011 setCookie($cookiename, '', time() - HOURSECS, '/');
b0ccd3fb 2012 setCookie($cookiename, rc4encrypt($thing), time()+$seconds, '/');
f9903ed0 2013}
2014
7cf1c7bd 2015/**
2016 * Gets a moodle cookie with an encrypted string
2017 *
2018 * @uses $CFG
2019 * @return string
2020 */
f9903ed0 2021function get_moodle_cookie() {
7185e073 2022 global $CFG;
2023
482b6e6e 2024 $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
7185e073 2025
1079c8a8 2026 if (empty($_COOKIE[$cookiename])) {
b0ccd3fb 2027 return '';
1079c8a8 2028 } else {
2029 return rc4decrypt($_COOKIE[$cookiename]);
2030 }
f9903ed0 2031}
2032
7cf1c7bd 2033/**
2034 * Returns true if an internal authentication method is being used.
2035 * if method not specified then, global default is assumed
2036 *
2037 * @uses $CFG
2038 * @param string $auth Form of authentication required
2039 * @return boolean
2040 * @todo Outline auth types and provide code example
2041 */
39a5a35d 2042function is_internal_auth($auth='') {
ba7166c3 2043/// Returns true if an internal authentication method is being used.
a3f1f815 2044/// If auth not specified then global default is assumed
ba7166c3 2045
2046 global $CFG;
2047
a3f1f815 2048 if (empty($auth)) {
2049 $auth = $CFG->auth;
39a5a35d 2050 }
2051
a3f1f815 2052 return ($auth == "email" || $auth == "none" || $auth == "manual");
2053}
2054
8c3dba73 2055/**
2056 * Returns an array of user fields
2057 *
c6d15803 2058 * @uses $CFG
2059 * @uses $db
2060 * @return array User field/column names
8c3dba73 2061 * @todo Finish documenting this function
2062 */
a3f1f815 2063function get_user_fieldnames() {
a3f1f815 2064
2065 global $CFG, $db;
2066
2067 $fieldarray = $db->MetaColumnNames($CFG->prefix.'user');
2068 unset($fieldarray['ID']);
2069
2070 return $fieldarray;
ba7166c3 2071}
f9903ed0 2072
7cf1c7bd 2073/**
2074 * Creates a bare-bones user record
2075 *
2076 * @uses $CFG
7cf1c7bd 2077 * @param string $username New user's username to add to record
2078 * @param string $password New user's password to add to record
2079 * @param string $auth Form of authentication required
89dcb99d 2080 * @return user A {@link $USER} object
7cf1c7bd 2081 * @todo Outline auth types and provide code example
2082 */
71f9abf9 2083function create_user_record($username, $password, $auth='') {
366dfa60 2084 global $CFG;
71f9abf9 2085
1e22bc9c 2086 //just in case check text case
2087 $username = trim(moodle_strtolower($username));
71f9abf9 2088
3271b70f 2089 if (function_exists('auth_get_userinfo')) {
e858f9da 2090 if ($newinfo = auth_get_userinfo($username)) {
b36a8fc4 2091 $newinfo = truncate_userinfo($newinfo);
34daec9b 2092 foreach ($newinfo as $key => $value){
9f44d972 2093 $newuser->$key = addslashes(stripslashes($value)); // Just in case
e858f9da 2094 }
2095 }
2096 }
f9903ed0 2097
85a1d4c9 2098 if (!empty($newuser->email)) {
2099 if (email_is_not_allowed($newuser->email)) {
2100 unset($newuser->email);
2101 }
2102 }
2103
71f9abf9 2104 $newuser->auth = (empty($auth)) ? $CFG->auth : $auth;
faebaf0f 2105 $newuser->username = $username;
2106 $newuser->password = md5($password);
a0bac19d 2107 $newuser->lang = $CFG->lang;
faebaf0f 2108 $newuser->confirmed = 1;
59619427 2109 $newuser->lastIP = getremoteaddr();
faebaf0f 2110 $newuser->timemodified = time();
f9903ed0 2111
b0ccd3fb 2112 if (insert_record('user', $newuser)) {
7c12949d 2113 $user = get_complete_user_data('username', $newuser->username);
d35757eb 2114 if($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'}){
2115 set_user_preference('auth_forcepasswordchange', 1, $user);
2116 }
2117 return $user;
faebaf0f 2118 }
2119 return false;
2120}
2121
7cf1c7bd 2122/**
2123 * Will update a local user record from an external source
2124 *
2125 * @uses $CFG
2126 * @param string $username New user's username to add to record
89dcb99d 2127 * @return user A {@link $USER} object
7cf1c7bd 2128 */
d35757eb 2129function update_user_record($username) {
d35757eb 2130 global $CFG;
2131
2132 if (function_exists('auth_get_userinfo')) {
2133 $username = trim(moodle_strtolower($username)); /// just in case check text case
2134
2135 if ($newinfo = auth_get_userinfo($username)) {
2136 foreach ($newinfo as $key => $value){
2137 if (!empty($CFG->{'auth_user_' . $key. '_updatelocal'})) {
2138 $value = addslashes(stripslashes($value)); // Just in case
2139 set_field('user', $key, $value, 'username', $username);
2140 }
2141 }
2142 }
2143 }
7c12949d 2144 return get_complete_user_data('username', $username);
d35757eb 2145}
0609562b 2146
b36a8fc4 2147function truncate_userinfo($info) {
2148/// will truncate userinfo as it comes from auth_get_userinfo (from external auth)
2149/// which may have large fields
2150
2151 // define the limits
2152 $limit = array(
2153 'username' => 100,
1c66bf59 2154 'idnumber' => 64,
b36a8fc4 2155 'firstname' => 20,
2156 'lastname' => 20,
2157 'email' => 100,
2158 'icq' => 15,
2159 'phone1' => 20,
2160 'phone2' => 20,
2161 'institution' => 40,
2162 'department' => 30,
2163 'address' => 70,
2164 'city' => 20,
2165 'country' => 2,
2166 'url' => 255,
2167 );
361855e6 2168
b36a8fc4 2169 // apply where needed
2170 foreach (array_keys($info) as $key) {
2171 if (!empty($limit[$key])) {
adfc03f9 2172 $info[$key] = trim(substr($info[$key],0, $limit[$key]));
361855e6 2173 }
b36a8fc4 2174 }
361855e6 2175
b36a8fc4 2176 return $info;
2177}
2178
7cf1c7bd 2179/**
2180 * Retrieve the guest user object
2181 *
2182 * @uses $CFG
89dcb99d 2183 * @return user A {@link $USER} object
7cf1c7bd 2184 */
0609562b 2185function guest_user() {
2186 global $CFG;
2187
b0ccd3fb 2188 if ($newuser = get_record('user', 'username', 'guest')) {
0609562b 2189 $newuser->loggedin = true;
2190 $newuser->confirmed = 1;
2191 $newuser->site = $CFG->wwwroot;
2192 $newuser->lang = $CFG->lang;
366dfa60 2193 $newuser->lastIP = getremoteaddr();
0609562b 2194 }
2195
2196 return $newuser;
2197}
2198
7cf1c7bd 2199/**
2200 * Given a username and password, this function looks them
2201 * up using the currently selected authentication mechanism,
2202 * and if the authentication is successful, it returns a
2203 * valid $user object from the 'user' table.
361855e6 2204 *
7cf1c7bd 2205 * Uses auth_ functions from the currently active auth module
2206 *
2207 * @uses $CFG
361855e6 2208 * @param string $username User's username
2209 * @param string $password User's password
89dcb99d 2210 * @return user|flase A {@link $USER} object or false if error
7cf1c7bd 2211 */
faebaf0f 2212function authenticate_user_login($username, $password) {
faebaf0f 2213
2214 global $CFG;
2215
466558e3 2216 $md5password = md5($password);
2217
27286aeb 2218 // First try to find the user in the database
466558e3 2219
7c12949d 2220 if (!$user = get_complete_user_data('username', $username)) {
18f16d61 2221 $user->id = 0; // Not a user
2222 $user->auth = $CFG->auth;
2223 }
39a5a35d 2224
27286aeb 2225 // Sort out the authentication method we are using.
39a5a35d 2226
27286aeb 2227 if (empty($CFG->auth)) {
b0ccd3fb 2228 $CFG->auth = 'manual'; // Default authentication module
27286aeb 2229 }
39a5a35d 2230
27286aeb 2231 if (empty($user->auth)) { // For some reason it isn't set yet
ccb3585f 2232 if (!empty($user->id) && (isadmin($user->id) || isguest($user->id))) {
71f9abf9 2233 $auth = 'manual'; // Always assume these guys are internal
27286aeb 2234 } else {
71f9abf9 2235 $auth = $CFG->auth; // Normal users default to site method
27286aeb 2236 }
d35757eb 2237 // update user record from external DB
2238 if ($user->auth != 'manual' && $user->auth != 'email') {
2239 $user = update_user_record($username);
2240 }
71f9abf9 2241 } else {
2242 $auth = $user->auth;
27286aeb 2243 }
8f0cd6ef 2244
ce791f88 2245 if (detect_munged_arguments($auth, 0)) { // For safety on the next require
2246 return false;
2247 }
2248
b0ccd3fb 2249 if (!file_exists($CFG->dirroot .'/auth/'. $auth .'/lib.php')) {
2250 $auth = 'manual'; // Can't find auth module, default to internal
466558e3 2251 }
2252
b0ccd3fb 2253 require_once($CFG->dirroot .'/auth/'. $auth .'/lib.php');
faebaf0f 2254
2255 if (auth_user_login($username, $password)) { // Successful authentication
d613daf0 2256 if ($user->id) { // User already exists in database
71f9abf9 2257 if (empty($user->auth)) { // For some reason auth isn't set yet
2258 set_field('user', 'auth', $auth, 'username', $username);
2259 }
92710226 2260 if ($md5password <> $user->password) { // Update local copy of password for reference
71f9abf9 2261 set_field('user', 'password', $md5password, 'username', $username);
faebaf0f 2262 }
366dfa60 2263 if (!is_internal_auth()) { // update user record from external DB
d35757eb 2264 $user = update_user_record($username);
2265 }
faebaf0f 2266 } else {
71f9abf9 2267 $user = create_user_record($username, $password, $auth);
faebaf0f 2268 }
89b54325 2269
e582b65e 2270 if (function_exists('auth_iscreator')) { // Check if the user is a creator
f894a791 2271 $useriscreator = auth_iscreator($username);
2272 if (!is_null($useriscreator)) {
2273 if ($useriscreator) {
2274 if (! record_exists('user_coursecreators', 'userid', $user->id)) {
2275 $cdata->userid = $user->id;
2276 if (! insert_record('user_coursecreators', $cdata)) {
2277 error('Cannot add user to course creators.');
2278 }
39a5a35d 2279 }
f894a791 2280 } else {
2281 if (record_exists('user_coursecreators', 'userid', $user->id)) {
2282 if (! delete_records('user_coursecreators', 'userid', $user->id)) {
2283 error('Cannot remove user from course creators.');
2284 }
39a5a35d 2285 }
2286 }
361855e6 2287 }
39a5a35d 2288 }
e582b65e 2289 return $user;
9d3c795c 2290
e582b65e 2291 } else {
b0ccd3fb 2292 add_to_log(0, 'login', 'error', $_SERVER['HTTP_REFERER'], $username);
f52d48db 2293 error_log('[client '.$_SERVER['REMOTE_ADDR']."] $CFG->wwwroot Failed Login: $username ".$_SERVER['HTTP_USER_AGENT']);
e582b65e 2294 return false;
2295 }
f9903ed0 2296}
2297
7c12949d 2298/**
2299 * Get a complete user record, which includes all the info
2300 * in the user record, as well as membership information
2301 * Intended for setting as $USER session variable
2302 *
2303 * @uses $CFG
2304 * @uses SITEID
2305 * @param string $field The user field to be checked for a given value.
2306 * @param string $value The value to match for $field.
2307 * @return user A {@link $USER} object.
2308 */
2309function get_complete_user_data($field, $value) {
2310
2311 global $CFG;
2312
2313 if (!$field || !$value) {
2314 return false;
2315 }
2316
2317/// Get all the basic user data
2318
2319 if (! $user = get_record_select('user', $field .' = \''. $value .'\' AND deleted <> \'1\'')) {
2320 return false;
2321 }
2322
2323/// Add membership information
2324
2325 if ($admins = get_records('user_admins', 'userid', $user->id)) {
2326 $user->admin = true;
2327 }
2328
2329 $user->student[SITEID] = isstudent(SITEID, $user->id);
2330
2331/// Determine enrolments based on current enrolment module
2332
2333 require_once($CFG->dirroot .'/enrol/'. $CFG->enrol .'/enrol.php');
2334 $enrol = new enrolment_plugin();
2335 $enrol->get_student_courses($user);
2336 $enrol->get_teacher_courses($user);
2337
2338/// Get various settings and preferences
2339
2340 if ($displays = get_records('course_display', 'userid', $user->id)) {
2341 foreach ($displays as $display) {
2342 $user->display[$display->course] = $display->display;
2343 }
2344 }
2345
2346 if ($preferences = get_records('user_preferences', 'userid', $user->id)) {
2347 foreach ($preferences as $preference) {
2348 $user->preference[$preference->name] = $preference->value;
2349 }
2350 }
2351
2352 if ($groups = get_records('groups_members', 'userid', $user->id)) {
2353 foreach ($groups as $groupmember) {
2354 $courseid = get_field('groups', 'courseid', 'id', $groupmember->groupid);
2355 $user->groupmember[$courseid] = $groupmember->groupid;
2356 }
2357 }
2358
2359/// Rewrite some variables if necessary
2360 if (!empty($user->description)) {
2361 $user->description = true; // No need to cart all of it around
2362 }
2363 if ($user->username == 'guest') {
2364 $user->lang = $CFG->lang; // Guest language always same as site
2365 $user->firstname = get_string('guestuser'); // Name always in current language
2366 $user->lastname = ' ';
2367 }
2368
2369 $user->loggedin = true;
2370 $user->site = $CFG->wwwroot; // for added security, store the site in the session
2371 $user->sesskey = random_string(10);
2372 $user->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
2373
2374 return $user;
2375
2376}
2377
2378function get_user_info_from_db($field, $value) { // For backward compatibility
2379 return get_complete_user_data($field, $value);
2380}
2381
2382/*
2383 * When logging in, this function is run to set certain preferences
2384 * for the current SESSION
2385 */
2386function set_login_session_preferences() {
2387 global $SESSION;
2388
2389 $SESSION->justloggedin = true;
2390
2391 unset($SESSION->lang);
2392 unset($SESSION->encoding);
2393 $SESSION->encoding = get_string('thischarset');
2394
2395 // Restore the calendar filters, if saved
2396 if (intval(get_user_preferences('calendar_persistflt', 0))) {
2397 include_once($CFG->dirroot.'/calendar/lib.php');
2398 calendar_set_filters_status(get_user_preferences('calendar_savedflt', 0xff));
2399 }
2400}
2401
2402
7cf1c7bd 2403/**
2404 * Enrols (or re-enrols) a student in a given course
2405 *
c6d15803 2406 * @param int $courseid The id of the course that is being viewed
2407 * @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.
2408 * @param int $timestart ?
2409 * @param int $timeend ?
7cf1c7bd 2410 * @return boolean
2411 * @todo Finish documenting this function
2412 */
92318548 2413function enrol_student($userid, $courseid, $timestart=0, $timeend=0, $enrol='') {
b40bc478 2414
75169b06 2415 global $CFG;
2416
b0ccd3fb 2417 if (!$course = get_record('course', 'id', $courseid)) { // Check course
3041b0f8 2418 return false;
4d312bbe 2419 }
b0ccd3fb 2420 if (!$user = get_record('user', 'id', $userid)) { // Check user
631cf796 2421 return false;
2422 }
b61efafb 2423 // enrol the student in any parent meta courses...
5f37b628 2424 if ($parents = get_records('course_meta','child_course',$courseid)) {
b61efafb 2425 foreach ($parents as $parent) {
2426 enrol_student($userid, $parent->parent_course,$timestart,$timeend,$enrol);
2427 }
2428 }
92318548 2429
2430 if (empty($enrol)) {
2431 $enrol = $CFG->enrol; // Default current method
2432 }
b0ccd3fb 2433 if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
631cf796 2434 $student->timestart = $timestart;
2435 $student->timeend = $timeend;
2436 $student->time = time();
6e8ca983 2437 $student->enrol = $enrol;
b0ccd3fb 2438 return update_record('user_students', $student);
361855e6 2439
631cf796 2440 } else {
75169b06 2441 require_once("$CFG->dirroot/mod/forum/lib.php");
2f3b54ae 2442 forum_add_user($userid, $courseid);
2443
631cf796 2444 $student->userid = $userid;
2445 $student->course = $courseid;
2446 $student->timestart = $timestart;
2447 $student->timeend = $timeend;
2448 $student->time = time();
6e8ca983 2449 $student->enrol = $enrol;
b0ccd3fb 2450 return insert_record('user_students', $student);
631cf796 2451 }
d7facad8 2452}
2453
7cf1c7bd 2454/**
2455 * Unenrols a student from a given course
2456 *
c6d15803 2457 * @param int $courseid The id of the course that is being viewed, if any
2458 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2459 * @return boolean
2460 */
9fa62805 2461function unenrol_student($userid, $courseid=0) {
d7facad8 2462
9fa62805 2463 if ($courseid) {
9fa49e22 2464 /// First delete any crucial stuff that might still send mail
b0ccd3fb 2465 if ($forums = get_records('forum', 'course', $courseid)) {
9fa49e22 2466 foreach ($forums as $forum) {
b0ccd3fb 2467 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
9fa62805 2468 }
2469 }
2470 if ($groups = get_groups($courseid, $userid)) {
2471 foreach ($groups as $group) {
b0ccd3fb 2472 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
bb09fb11 2473 }
f9903ed0 2474 }
b61efafb 2475 // enrol the student in any parent meta courses...
5f37b628 2476 if ($parents = get_records('course_meta','child_course',$courseid)) {
b61efafb 2477 foreach ($parents as $parent) {
2478 unenrol_student($userid, $parent->parent_course);
2479 }
2480 }
b0ccd3fb 2481 return delete_records('user_students', 'userid', $userid, 'course', $courseid);
9fa49e22 2482
f9903ed0 2483 } else {
b0ccd3fb 2484 delete_records('forum_subscriptions', 'userid', $userid);
2485 delete_records('groups_members', 'userid', $userid);
2486 return delete_records('user_students', 'userid', $userid);
f9903ed0 2487 }
2488}
2489
7cf1c7bd 2490/**
2491 * Add a teacher to a given course
2492 *
2493 * @uses $USER
c6d15803 2494 * @param int $courseid The id of the course that is being viewed, if any
2495 * @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.
2496 * @param int $editall ?
7cf1c7bd 2497 * @param string $role ?
c6d15803 2498 * @param int $timestart ?
2499 * @param int $timeend ?
7cf1c7bd 2500 * @return boolean
2501 * @todo Finish documenting this function
2502 */
6e8ca983 2503function add_teacher($userid, $courseid, $editall=1, $role='', $timestart=0, $timeend=0, $enrol='manual') {
7b5944cd 2504 global $CFG;
3041b0f8 2505
61451a36 2506 if ($teacher = get_record('user_teachers', 'userid', $userid, 'course', $courseid)) {
b40bc478 2507 $newteacher = NULL;
2508 $newteacher->id = $teacher->id;
2509 $newteacher->editall = $editall;
6e8ca983 2510 $newteacher->enrol = $enrol;
b40bc478 2511 if ($role) {
2512 $newteacher->role = $role;
2513 }
2514 if ($timestart) {
2515 $newteacher->timestart = $timestart;
3041b0f8 2516 }
b40bc478 2517 if ($timeend) {
2518 $newteacher->timeend = $timeend;
2519 }
2520 return update_record('user_teachers', $newteacher);
3041b0f8 2521 }
61451a36 2522
b0ccd3fb 2523 if (!record_exists('user', 'id', $userid)) {
61451a36 2524 return false; // no such user
2525 }
2526
b0ccd3fb 2527 if (!record_exists('course', 'id', $courseid)) {
61451a36 2528 return false; // no such course
2529 }
2530
2531 $teacher = NULL;
2532 $teacher->userid = $userid;
2533 $teacher->course = $courseid;
2534 $teacher->editall = $editall;
2535 $teacher->role = $role;
5a2dea02 2536 $teacher->timemodified = time();
2537 $newteacher->timestart = $timestart;
2538 $newteacher->timeend = $timeend;
b0ccd3fb 2539 if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
5a2dea02 2540 $teacher->timestart = $student->timestart;
2541 $teacher->timeend = $student->timeend;
2542 $teacher->timeaccess = $student->timeaccess;
2543 }
61451a36 2544
b0ccd3fb 2545 if (record_exists('user_teachers', 'course', $courseid)) {
61451a36 2546 $teacher->authority = 2;
2547 } else {
2548 $teacher->authority = 1;
2549 }
b0ccd3fb 2550 delete_records('user_students', 'userid', $userid, 'course', $courseid); // Unenrol as student
8f0cd6ef 2551
709f0ec8 2552 /// Add forum subscriptions for new users
7b5944cd 2553 require_once('../mod/forum/lib.php');
2554 forum_add_user($userid, $courseid);
61451a36 2555
b0ccd3fb 2556 return insert_record('user_teachers', $teacher);
61451a36 2557
3041b0f8 2558}
2559
7cf1c7bd 2560/**
2561 * Removes a teacher from a given course (or ALL courses)
2562 * Does not delete the user account
2563 *
c6d15803 2564 * @param int $courseid The id of the course that is being viewed, if any
361855e6 2565 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2566 * @return boolean
2567 */
3041b0f8 2568function remove_teacher($userid, $courseid=0) {
3041b0f8 2569 if ($courseid) {
9fa49e22 2570 /// First delete any crucial stuff that might still send mail
b0ccd3fb 2571 if ($forums = get_records('forum', 'course', $courseid)) {
9fa49e22 2572 foreach ($forums as $forum) {
b0ccd3fb 2573 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
9fa49e22 2574 }
2575 }
b02193e6 2576
2577 /// Next if the teacher is not registered as a student, but is
2578 /// a member of a group, remove them from the group.
2579 if (!isstudent($courseid, $userid)) {
2580 if ($groups = get_groups($courseid, $userid)) {
2581 foreach ($groups as $group) {
b0ccd3fb 2582 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
b02193e6 2583 }
2584 }
2585 }
2586
b0ccd3fb 2587 return delete_records('user_teachers', 'userid', $userid, 'course', $courseid);
57507290 2588 } else {
b0ccd3fb 2589 delete_records('forum_subscriptions', 'userid', $userid);
2590 return delete_records('user_teachers', 'userid', $userid);
57507290 2591 }
f9903ed0 2592}
2593
7cf1c7bd 2594/**
2595 * Add a creator to the site
2596 *
361855e6 2597 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2598 * @return boolean
2599 */
3041b0f8 2600function add_creator($userid) {
3041b0f8 2601
b0ccd3fb 2602 if (!record_exists('user_admins', 'userid', $userid)) {
2603 if (record_exists('user', 'id', $userid)) {
3041b0f8 2604 $creator->userid = $userid;
b0ccd3fb 2605 return insert_record('user_coursecreators', $creator);
3041b0f8 2606 }
2607 return false;
2608 }
2609 return true;
2610}
2611
7cf1c7bd 2612/**
2613 * Remove a creator from a site
2614 *
2615 * @uses $db
c6d15803 2616 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2617 * @return boolean
2618 */
3041b0f8 2619function remove_creator($userid) {
3041b0f8 2620 global $db;
2621
b0ccd3fb 2622 return delete_records('user_coursecreators', 'userid', $userid);
3041b0f8 2623}
2624
7cf1c7bd 2625/**
2626 * Add an admin to a site
2627 *
2628 * @uses SITEID
c6d15803 2629 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2630 * @return boolean
2631 */
3041b0f8 2632function add_admin($userid) {
3041b0f8 2633
b0ccd3fb 2634 if (!record_exists('user_admins', 'userid', $userid)) {
2635 if (record_exists('user', 'id', $userid)) {
3041b0f8 2636 $admin->userid = $userid;
361855e6 2637
f950af3c 2638 // any admin is also a teacher on the site course
222ac91b 2639 if (!record_exists('user_teachers', 'course', SITEID, 'userid', $userid)) {
2640 if (!add_teacher($userid, SITEID)) {
f950af3c 2641 return false;
2642 }
2643 }
361855e6 2644
b0ccd3fb 2645 return insert_record('user_admins', $admin);
3041b0f8 2646 }
2647 return false;
2648 }
2649 return true;
2650}
2651
7cf1c7bd 2652/**
2653 * Removes an admin from a site
2654 *
2655 * @uses $db
2656 * @uses SITEID
c6d15803 2657 * @param int $userid The id of the user that is being tested against.
7cf1c7bd 2658 * @return boolean
2659 */
3041b0f8 2660function remove_admin($userid) {
9fa49e22 2661 global $db;
f9903ed0 2662
f950af3c 2663 // remove also from the list of site teachers
222ac91b 2664 remove_teacher($userid, SITEID);
f950af3c 2665
b0ccd3fb 2666 return delete_records('user_admins', 'userid', $userid);
f9903ed0 2667}
2668
7cf1c7bd 2669/**
2670 * Clear a course out completely, deleting all content
2671 * but don't delete the course itself
2672 *
2673 * @uses $USER
2674 * @uses $SESSION
2675 * @uses $CFG
c6d15803 2676 * @param int $courseid The id of the course that is being viewed
7cf1c7bd 2677 * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
2678 * @return boolean
2679 */
07aeb7b0 2680function remove_course_contents($courseid, $showfeedback=true) {
07aeb7b0 2681
538a2210 2682 global $CFG, $USER, $SESSION;
07aeb7b0 2683
2684 $result = true;
2685
b0ccd3fb 2686 if (! $course = get_record('course', 'id', $courseid)) {
2687 error('Course ID was incorrect (can\'t find it)');
07aeb7b0 2688 }
2689
b0ccd3fb 2690 $strdeleted = get_string('deleted');
07aeb7b0 2691
2692 // First delete every instance of every module
d8ba183c 2693
b0ccd3fb 2694 if ($allmods = get_records('modules') ) {
07aeb7b0 2695 foreach ($allmods as $mod) {
2696 $modname = $mod->name;
b0ccd3fb 2697 $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
2698 $moddelete = $modname .'_delete_instance'; // Delete everything connected to an instance
2699 $moddeletecourse = $modname .'_delete_course'; // Delete other stray stuff (uncommon)
07aeb7b0 2700 $count=0;
2701 if (file_exists($modfile)) {
2702 include_once($modfile);
2703 if (function_exists($moddelete)) {
b0ccd3fb 2704 if ($instances = get_records($modname, 'course', $course->id)) {
07aeb7b0 2705 foreach ($instances as $instance) {
2706 if ($moddelete($instance->id)) {
2707 $count++;
2708 } else {
b0ccd3fb 2709 notify('Could not delete '. $modname .' instance '. $instance->id .' ('. $instance->name .')');
07aeb7b0 2710 $result = false;
2711 }
2712 }
2713 }
2714 } else {
b0ccd3fb 2715 notify('Function '. $moddelete() .'doesn\'t exist!');
07aeb7b0 2716 $result = false;
2717 }
2718
ca952b03 2719 if (function_exists($moddeletecourse)) {
2720 $moddeletecourse($course);
2721 }
07aeb7b0 2722 }
2723 if ($showfeedback) {
b0ccd3fb 2724 notify($strdeleted .' '. $count .' x '. $modname);
07aeb7b0 2725 }
2726 }
2727 } else {
b0ccd3fb 2728 error('No modules are installed!');
07aeb7b0 2729 }
2730
251af423 2731 // Delete course blocks
2732 if (delete_records('block_instance', 'pagetype', PAGE_COURSE_VIEW, 'pageid', $course->id)) {
2733 if ($showfeedback) {
2734 notify($strdeleted .' block_instance');
2735 }
2736 } else {
2737 $result = false;
2738 }
2739
07aeb7b0 2740 // Delete any user stuff
2741
b0ccd3fb 2742 if (delete_records('user_students', 'course', $course->id)) {
07aeb7b0 2743 if ($showfeedback) {
b0ccd3fb 2744 notify($strdeleted .' user_students');
07aeb7b0 2745 }
2746 } else {
2747 $result = false;
2748 }
2749
b0ccd3fb 2750 if (delete_records('user_teachers', 'course', $course->id)) {
07aeb7b0 2751 if ($showfeedback) {
b0ccd3fb 2752 notify($strdeleted .' user_teachers');
07aeb7b0 2753 }
2754 } else {
2755 $result = false;
2756 }
2757
082e3ebc 2758 // Delete any groups
2759
b0ccd3fb 2760 if ($groups = get_records('groups', 'courseid', $course->id)) {
082e3ebc 2761 foreach ($groups as $group) {
b0ccd3fb 2762 if (delete_records('groups_members', 'groupid', $group->id)) {
082e3ebc 2763 if ($showfeedback) {
b0ccd3fb 2764 notify($strdeleted .' groups_members');
082e3ebc 2765 }
2766 } else {
2767 $result = false;
2768 }
b0ccd3fb 2769 if (delete_records('groups', 'id', $group->id)) {
082e3ebc 2770 if ($showfeedback) {
b0ccd3fb 2771 notify($strdeleted .' groups');
082e3ebc 2772 }
2773 } else {
2774 $result = false;
2775 }
2776 }
2777 }
2778
2779 // Delete events
2780
b0ccd3fb 2781 if (delete_records('event', 'courseid', $course->id)) {
082e3ebc 2782 if ($showfeedback) {
b0ccd3fb 2783 notify($strdeleted .' event');
082e3ebc 2784 }
2785 } else {
2786 $result = false;
2787 }
2788
07aeb7b0 2789 // Delete logs
2790
b0ccd3fb 2791 if (delete_records('log', 'course', $course->id)) {
07aeb7b0 2792 if ($showfeedback) {
b0ccd3fb 2793 notify($strdeleted .' log');
07aeb7b0 2794 }
2795 } else {
2796 $result = false;
2797 }
2798
2799 // Delete any course stuff
2800
b0ccd3fb 2801 if (delete_records('course_sections', 'course', $course->id)) {
07aeb7b0 2802 if ($showfeedback) {
b0ccd3fb 2803 notify($strdeleted .' course_sections');
07aeb7b0 2804 }
2805 } else {
2806 $result = false;
2807 }
2808
b0ccd3fb 2809 if (delete_records('course_modules', 'course', $course->id)) {
07aeb7b0 2810 if ($showfeedback) {
b0ccd3fb 2811 notify($strdeleted .' course_modules');
07aeb7b0 2812 }
2813 } else {
2814 $result = false;
2815 }
2816
7ff9860d 2817 // Delete gradebook stuff
2818
322344bb 2819 if (delete_records("grade_category", "course", $course->id)) {
7ff9860d 2820 if ($showfeedback) {
2821 notify("$strdeleted grade categories");
2822 } else {
2823 $result = false;
2824 }
2825 }
322344bb 2826 if (delete_records("grade_exceptions", "course", $course->id)) {
7ff9860d 2827 if ($showfeedback) {
2828 notify("$strdeleted grade exceptions");
2829 } else {
2830 $result = false;
2831 }
2832 }
322344bb 2833 if (delete_records("grade_item", "course", $course->id)) {
7ff9860d 2834 if ($showfeedback) {
2835 notify("$strdeleted grade items");
2836 } else {
2837 $result = false;
2838 }
2839 }
322344bb 2840 if (delete_records("grade_letter", "course", $course->id)) {
7ff9860d 2841 if ($showfeedback) {
2842 notify("$strdeleted grade letters");
2843 } else {
2844 $result = false;
2845 }
2846 }
322344bb 2847 if (delete_records("grade_preferences", "course", $course->id)) {
7ff9860d 2848 if ($showfeedback) {
2849 notify("$strdeleted grade preferences");
2850 } else {
2851 $result = false;
2852 }
2853 }
2854
2855
5f37b628 2856 if ($course->metacourse) {
2857 delete_records("course_meta","parent_course",$course->id);
b61efafb 2858 sync_metacourse($course->id); // have to do it here so the enrolments get nuked. sync_metacourses won't find it without the id.
2859 if ($showfeedback) {
5f37b628 2860 notify("$strdeleted course_meta");
b61efafb 2861 }
7ff9860d 2862 } else {
5f37b628 2863 if ($parents = get_records("course_meta","child_course",$course->id)) {
b61efafb 2864 foreach ($parents as $parent) {
2865 remove_from_metacourse($parent->parent_course,$parent->child_course); // this will do the unenrolments as well.
2866 }
2867 if ($showfeedback) {
5f37b628 2868 notify("$strdeleted course_meta");
b61efafb 2869 }
2870 }
2871 }
2872
07aeb7b0 2873 return $result;
2874
2875}
2876
7cf1c7bd 2877/**
2878 * This function will empty a course of USER data as much as
2879/// possible. It will retain the activities and the structure
2880/// of the course.
2881 *
2882 * @uses $USER
7cf1c7bd 2883 * @uses $SESSION
2884 * @uses $CFG
c6d15803 2885 * @param int $courseid The id of the course that is being viewed
7cf1c7bd 2886 * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
2887 * @param boolean $removestudents ?
2888 * @param boolean $removeteachers ?
2889 * @param boolean $removegroups ?
2890 * @param boolean $removeevents ?
2891 * @param boolean $removelogs ?
2892 * @return boolean
2893 * @todo Finish documenting this function
2894 */
3831de52 2895function remove_course_userdata($courseid, $showfeedback=true,
2896 $removestudents=true, $removeteachers=false, $removegroups=true,
2897 $removeevents=true, $removelogs=false) {
3831de52 2898
538a2210 2899 global $CFG, $USER, $SESSION;
3831de52 2900
2901 $result = true;
2902
b0ccd3fb 2903 if (! $course = get_record('course', 'id', $courseid)) {
2904 error('Course ID was incorrect (can\'t find it)');
3831de52 2905 }
2906
b0ccd3fb 2907 $strdeleted = get_string('deleted');
3831de52 2908
2909 // Look in every instance of every module for data to delete
2910
b0ccd3fb 2911 if ($allmods = get_records('modules') ) {
3831de52 2912 foreach ($allmods as $mod) {
2913 $modname = $mod->name;
b0ccd3fb 2914 $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
2915 $moddeleteuserdata = $modname .'_delete_userdata'; // Function to delete user data
3831de52 2916 $count=0;
2917 if (file_exists($modfile)) {
2918 @include_once($modfile);
2919 if (function_exists($moddeleteuserdata)) {
2920 $moddeleteuserdata($course, $showfeedback);
2921 }
2922 }
2923 }
2924 } else {
b0ccd3fb 2925 error('No modules are installed!');
3831de52 2926 }
2927
2928 // Delete other stuff
2929
2930 if ($removestudents) {
2931 /// Delete student enrolments
b0ccd3fb 2932 if (delete_records('user_students', 'course', $course->id)) {
3831de52 2933 if ($showfeedback) {
b0ccd3fb 2934 notify($strdeleted .' user_students');
3831de52 2935 }
2936 } else {
2937 $result = false;
2938 }
2939 /// Delete group members (but keep the groups)
b0ccd3fb 2940 if ($groups = get_records('groups', 'courseid', $course->id)) {
3831de52 2941 foreach ($groups as $group) {
b0ccd3fb 2942 if (delete_records('groups_members', 'groupid', $group->id)) {
3831de52 2943 if ($showfeedback) {
b0ccd3fb 2944 notify($strdeleted .' groups_members');
3831de52 2945 }
2946 } else {
2947 $result = false;
2948 }
2949 }
2950 }
2951 }
2952
2953 if ($removeteachers) {
b0ccd3fb 2954 if (delete_records('user_teachers', 'course', $course->id)) {
3831de52 2955 if ($showfeedback) {
b0ccd3fb 2956 notify($strdeleted .' user_teachers');
3831de52 2957 }
2958 } else {
2959 $result = false;
2960 }
2961 }
2962
2963 if ($removegroups) {
b0ccd3fb 2964 if ($groups = get_records('groups', 'courseid', $course->id)) {
3831de52 2965 foreach ($groups as $group) {
b0ccd3fb 2966 if (delete_records('groups', 'id', $group->id)) {
3831de52 2967 if ($showfeedback) {
b0ccd3fb 2968 notify($strdeleted .' groups');
3831de52 2969 }
2970 } else {
2971 $result = false;
2972 }
2973 }
2974 }
2975 }
2976
2977 if ($removeevents) {
b0ccd3fb 2978 if (delete_records('event', 'courseid', $course->id)) {
3831de52 2979 if ($showfeedback) {
b0ccd3fb 2980 notify($strdeleted .' event');
3831de52 2981 }
2982 } else {
2983 $result = false;
2984 }
2985 }
2986
2987 if ($removelogs) {
b0ccd3fb 2988 if (delete_records('log', 'course', $course->id)) {
3831de52 2989 if ($showfeedback) {
b0ccd3fb 2990 notify($strdeleted .' log');
3831de52 2991 }
2992 } else {
2993 $result = false;
2994 }
2995 }
2996
2997 return $result;
2998
2999}
3000
3001
f9903ed0 3002
f374fb10 3003/// GROUPS /////////////////////////////////////////////////////////
d8ba183c 3004
f374fb10 3005
3006/**
3007* Returns a boolean: is the user a member of the given group?
d8ba183c 3008*
dcd338ff 3009* @param type description
7cf1c7bd 3010 * @todo Finish documenting this function
f374fb10 3011*/
3012function ismember($groupid, $userid=0) {
3013 global $USER;
3014
8a2c9076 3015 if (!$groupid) { // No point doing further checks
3016 return false;
3017 }
3018
f374fb10 3019 if (!$userid) {
0d67c514 3020 if (empty($USER->groupmember)) {
3021 return false;
3022 }
3023 foreach ($USER->groupmember as $courseid => $mgroupid) {
3024 if ($mgroupid == $groupid) {
3025 return true;
3026 }
3027 }
3028 return false;
f374fb10 3029 }
3030
b0ccd3fb 3031 return record_exists('groups_members', 'groupid', $groupid, 'userid', $userid);
f374fb10 3032}
3033
4ed533df 3034/**
3035 * Add a user to a group, return true upon success or if user already a group member
3036 *
3037 * @param groupid The group id
3038 * @param userid The user id
3039 * @todo Finish documenting this function
3040 */
3041function add_user_to_group ($groupid, $userid) {
3042 if (ismember($groupid, $userid)) return true;
3043 $record->groupid = $groupid;
3044 $record->userid = $userid;
d2a9f7cc 3045 $record->timeadded = time();
4ed533df 3046 return (insert_record('groups_members', $record) !== false);
3047}
3048
3049
0d67c514 3050/**
c6d15803 3051 * Returns the group ID of the current user in the given course
3052 *
3053 * @uses $USER
3054 * @param int $courseid The course being examined - relates to id field in 'course' table.
7cf1c7bd 3055 * @todo Finish documenting this function
c6d15803 3056 */
0d67c514 3057function mygroupid($courseid) {
3058 global $USER;
3059
3060 if (empty($USER->groupmember[$courseid])) {
3061 return 0;
3062 } else {
3063 return $USER->groupmember[$courseid];
3064 }
3065}
3066
f374fb10 3067/**
c6d15803 3068 * For a given course, and possibly course module, determine
3069 * what the current default groupmode is:
3070 * NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
3071 *
89dcb99d 3072 * @param course $course A {@link $COURSE} object
3073 * @param array? $cm A course module object
c6d15803 3074 * @return int A group mode (NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS)
3075 */
f374fb10 3076function groupmode($course, $cm=null) {
3077
3078 if ($cm and !$course->groupmodeforce) {
3079 return $cm->groupmode;
3080 }
3081 return $course->groupmode;
3082}
3083
3084
3085/**
c6d15803 3086 * Sets the current group in the session variable
3087 *
3088 * @uses $SESSION
3089 * @param int $courseid The course being examined - relates to id field in 'course' table.
3090 * @param int $groupid The group being examined.
3091 * @return int Current group id which was set by this function
7cf1c7bd 3092 * @todo Finish documenting this function
c6d15803 3093 */
f374fb10 3094function set_current_group($courseid, $groupid) {
3095 global $SESSION;
3096
3097 return $SESSION->currentgroup[$courseid] = $groupid;
3098}
3099
3100
3101/**
c6d15803 3102 * Gets the current group for the current user as an id or an object
3103 *
3104 * @uses $CFG
3105 * @uses $SESSION
3106 * @param int $courseid The course being examined - relates to id field in 'course' table.
9f1f6daf 3107 * @param boolean $full If true, the return value is a full record object. If false, just the id of the record.
7cf1c7bd 3108 * @todo Finish documenting this function
c6d15803 3109 */
f374fb10 3110function get_current_group($courseid, $full=false) {
3111 global $SESSION, $USER;
3112
ce04df6b 3113 if (!isset($SESSION->currentgroup[$courseid])) {
0b78f2b1 3114 if (empty($USER->groupmember[$courseid]) or isteacheredit($courseid)) {
8a2c9076 3115 return 0;
f374fb10 3116 } else {
3117 $SESSION->currentgroup[$courseid] = $USER->groupmember[$courseid];
3118 }
3119 }
3120
3121 if ($full) {
0da33e07 3122 return get_record('groups', 'id', $SESSION->currentgroup[$courseid]);
f374fb10 3123 } else {
3124 return $SESSION->currentgroup[$courseid];
3125 }
3126}
3127
0d67c514 3128/**
c6d15803 3129 * A combination function to make it easier for modules
3130 * to set up groups.
3131 *
3132 * It will use a given "groupid" parameter and try to use
3133 * that to reset the current group for the user.
3134 *
3135 * @uses VISIBLEGROUPS
89dcb99d 3136 * @param course $course A {@link $COURSE} object
c6d15803 3137 * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
3138 * @param int $groupid Will try to use this optional parameter to
3139 * reset the current group for the user
89dcb99d 3140 * @return int|false Returns the current group id or false if error.
7cf1c7bd 3141 * @todo Finish documenting this function
c6d15803 3142 */
eb6147a8 3143function get_and_set_current_group($course, $groupmode, $groupid=-1) {
0d67c514 3144
3145 if (!$groupmode) { // Groups don't even apply
d8ba183c 3146 return false;
0d67c514 3147 }
3148
3149 $currentgroupid = get_current_group($course->id);
3150
eb6147a8 3151 if ($groupid < 0) { // No change was specified
3152 return $currentgroupid;
3153 }
3154
3155 if ($groupid) { // Try to change the current group to this groupid
0d67c514 3156 if ($group = get_record('groups', 'id', $groupid, 'courseid', $course->id)) { // Exists
3157 if (isteacheredit($course->id)) { // Sets current default group
3158 $currentgroupid = set_current_group($course->id, $group->id);
3159
3160 } else if ($groupmode == VISIBLEGROUPS) { // All groups are visible
3161 $currentgroupid = $group->id;
3162 }
3163 }
eb6147a8 3164 } else { // When groupid = 0 it means show ALL groups
3165 if (isteacheredit($course->id)) { // Sets current default group
3166 $currentgroupid = set_current_group($course->id, 0);
3167
3168 } else if ($groupmode == VISIBLEGROUPS) { // All groups are visible
3169 $currentgroupid = 0;
3170 }
0d67c514 3171 }
3172
3173 return $currentgroupid;
3174}
3175
3176
c3cbfe7f 3177/**
c6d15803 3178 * A big combination function to make it easier for modules
3179 * to set up groups.
3180 *
3181 * Terminates if the current user shouldn't be looking at this group
3182 * Otherwise returns the current group if there is one
3183 * Otherwise returns false if groups aren't relevant
3184 *
3185 * @uses SEPARATEGROUPS
3186 * @uses VISIBLEGROUPS
89dcb99d 3187 * @param course $course A {@link $COURSE} object
c6d15803 3188 * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
3189 * @param string $urlroot ?
7cf1c7bd 3190 * @todo Finish documenting this function
c6d15803 3191 */
c3cbfe7f 3192function setup_and_print_groups($course, $groupmode, $urlroot) {
3193
eb6147a8 3194 if (isset($_GET['group'])) {
3195 $changegroup = $_GET['group']; /// 0 or higher
3196 } else {
3197 $changegroup = -1; /// This means no group change was specified
3198 }
3199
3200 $currentgroup = get_and_set_current_group($course, $groupmode, $changegroup);
c3cbfe7f 3201
eb6147a8 3202 if ($currentgroup === false) {
c3cbfe7f 3203 return false;
3204 }
3205
4b6d8dd5 3206 if ($groupmode == SEPARATEGROUPS and !isteacheredit($course->id) and !$currentgroup) {
3207 print_heading(get_string('notingroup'));
c3cbfe7f 3208 print_footer($course);
3209 exit;
3210 }
3211
3212 if ($groupmode == VISIBLEGROUPS or ($groupmode and isteacheredit($course->id))) {
b0ccd3fb 3213 if ($groups = get_records_menu('groups', 'courseid', $course->id, 'name ASC', 'id,name')) {
eb6147a8 3214 echo '<div align="center">';
c3cbfe7f 3215 print_group_menu($groups, $groupmode, $currentgroup, $urlroot);
eb6147a8 3216 echo '</div>';
c3cbfe7f 3217 }
3218 }
3219
3220 return $currentgroup;
3221}
0d67c514 3222
bb64b51a 3223function generate_email_processing_address($modid,$modargs) {
3224 global $CFG;
303d0af1 3225
3226 if (empty($CFG->siteidentifier)) { // Unique site identification code
3227 set_config('siteidentifier', random_string(32));
bb64b51a 3228 }
d2a9f7cc 3229
bb64b51a 3230 $header = $CFG->mailprefix . substr(base64_encode(pack('C',$modid)),0,2).$modargs;
3231 return $header . substr(md5($header.$CFG->sitesecret),0,16).'@'.$CFG->maildomain;
3232}
3233
f374fb10 3234
bb64b51a 3235function moodle_process_email($modargs,$body) {
3236 // the first char should be an unencoded letter. We'll take this as an action
3237 switch ($modargs{0}) {
3238 case 'B': { // bounce
3239 list(,$userid) = unpack('V',base64_decode(substr($modargs,1,8)));
3240 if ($user = get_record_select("user","id=$userid","id,email")) {
3241 // check the half md5 of their email
3242 $md5check = substr(md5($user->email),0,16);
3243 if ($md5check = substr($modargs, -16)) {
3244 set_bounce_count($user);
3245 }
3246 // else maybe they've already changed it?
3247 }
3248 }
3249 break;
3250 // maybe more later?
3251 }
3252}
f374fb10 3253
f9903ed0 3254/// CORRESPONDENCE ////////////////////////////////////////////////
3255
7cf1c7bd 3256/**
3257 * Send an email to a specified user
3258 *
7cf1c7bd 3259 * @uses $CFG
3260 * @uses $_SERVER
c6d15803 3261 * @uses SITEID
89dcb99d 3262 * @param user $user A {@link $USER} object
3263 * @param user $from A {@link $USER} object
7cf1c7bd 3264 * @param string $subject plain text subject line of the email
3265 * @param string $messagetext plain text version of the message
3266 * @param string $messagehtml complete html version of the message (optional)
3267 * @param string $attachment a file on the filesystem, relative to $CFG->dataroot
3268 * @param string $attachname the name of the file (extension indicates MIME)
361855e6 3269 * @param boolean $usetrueaddress determines whether $from email address should
c6d15803 3270 * be sent out. Will be overruled by user profile setting for maildisplay
361855e6 3271 * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
c6d15803 3272 * was blocked by user and "false" if there was another sort of error.
7cf1c7bd 3273 */
bb64b51a 3274function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $repyto='', $replytoname='') {
f9903ed0 3275
f9f4d999 3276 global $CFG, $FULLME;
f9903ed0 3277
0cc6fa6a 3278 global $course; // This is a bit of an ugly hack to be gotten rid of later
3279 if (!empty($course->lang)) { // Course language is defined
3280 $CFG->courselang = $course->lang;
3281 }
32e2b302 3282 if (!empty($course->theme)) { // Course language is defined
3283 $CFG->coursetheme = $course->theme;
3284 }