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