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