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