moodlelib.php
1 |
<?php // $Id$ |
---|---|
2 |
|
3 |
///////////////////////////////////////////////////////////////////////////
|
4 |
// //
|
5 |
// NOTICE OF COPYRIGHT //
|
6 |
// //
|
7 |
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
|
8 |
// http://moodle.org //
|
9 |
// //
|
10 |
// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
|
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 |
///////////////////////////////////////////////////////////////////////////
|
25 |
|
26 |
/**
|
27 |
* moodlelib.php - Moodle main library
|
28 |
*
|
29 |
* Main library file of miscellaneous general-purpose Moodle functions.
|
30 |
* Other main libraries:
|
31 |
* - weblib.php - functions that produce web output
|
32 |
* - datalib.php - functions that access the database
|
33 |
* @author Martin Dougiamas
|
34 |
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
35 |
* @package moodlecore
|
36 |
*/
|
37 |
|
38 |
/// CONSTANTS (Encased in phpdoc proper comments)/////////////////////////
|
39 |
|
40 |
/**
|
41 |
* Used by some scripts to check they are being called by Moodle
|
42 |
*/
|
43 |
define('MOODLE_INTERNAL', true); |
44 |
|
45 |
/// Date and time constants ///
|
46 |
/**
|
47 |
* Time constant - the number of seconds in a year
|
48 |
*/
|
49 |
|
50 |
define('YEARSECS', 31536000); |
51 |
|
52 |
/**
|
53 |
* Time constant - the number of seconds in a week
|
54 |
*/
|
55 |
define('WEEKSECS', 604800); |
56 |
|
57 |
/**
|
58 |
* Time constant - the number of seconds in a day
|
59 |
*/
|
60 |
define('DAYSECS', 86400); |
61 |
|
62 |
/**
|
63 |
* Time constant - the number of seconds in an hour
|
64 |
*/
|
65 |
define('HOURSECS', 3600); |
66 |
|
67 |
/**
|
68 |
* Time constant - the number of seconds in a minute
|
69 |
*/
|
70 |
define('MINSECS', 60); |
71 |
|
72 |
/**
|
73 |
* Time constant - the number of minutes in a day
|
74 |
*/
|
75 |
define('DAYMINS', 1440); |
76 |
|
77 |
/**
|
78 |
* Time constant - the number of minutes in an hour
|
79 |
*/
|
80 |
define('HOURMINS', 60); |
81 |
|
82 |
/// Parameter constants - every call to optional_param(), required_param() ///
|
83 |
/// or clean_param() should have a specified type of parameter. //////////////
|
84 |
|
85 |
/**
|
86 |
* PARAM_RAW specifies a parameter that is not cleaned/processed in any way;
|
87 |
* originally was 0, but changed because we need to detect unknown
|
88 |
* parameter types and swiched order in clean_param().
|
89 |
*/
|
90 |
define('PARAM_RAW', 666); |
91 |
|
92 |
/**
|
93 |
* PARAM_CLEAN - obsoleted, please try to use more specific type of parameter.
|
94 |
* It was one of the first types, that is why it is abused so much ;-)
|
95 |
*/
|
96 |
define('PARAM_CLEAN', 0x0001); |
97 |
|
98 |
/**
|
99 |
* PARAM_INT - integers only, use when expecting only numbers.
|
100 |
*/
|
101 |
define('PARAM_INT', 0x0002); |
102 |
|
103 |
/**
|
104 |
* PARAM_INTEGER - an alias for PARAM_INT
|
105 |
*/
|
106 |
define('PARAM_INTEGER', 0x0002); |
107 |
|
108 |
/**
|
109 |
* PARAM_NUMBER - a real/floating point number.
|
110 |
*/
|
111 |
define('PARAM_NUMBER', 0x000a); |
112 |
|
113 |
/**
|
114 |
* PARAM_ALPHA - contains only english letters.
|
115 |
*/
|
116 |
define('PARAM_ALPHA', 0x0004); |
117 |
|
118 |
/**
|
119 |
* PARAM_ACTION - an alias for PARAM_ALPHA, use for various actions in formas and urls
|
120 |
* @TODO: should we alias it to PARAM_ALPHANUM ?
|
121 |
*/
|
122 |
define('PARAM_ACTION', 0x0004); |
123 |
|
124 |
/**
|
125 |
* PARAM_FORMAT - an alias for PARAM_ALPHA, use for names of plugins, formats, etc.
|
126 |
* @TODO: should we alias it to PARAM_ALPHANUM ?
|
127 |
*/
|
128 |
define('PARAM_FORMAT', 0x0004); |
129 |
|
130 |
/**
|
131 |
* PARAM_NOTAGS - all html tags are stripped from the text. Do not abuse this type.
|
132 |
*/
|
133 |
define('PARAM_NOTAGS', 0x0008); |
134 |
|
135 |
/**
|
136 |
* PARAM_MULTILANG - alias of PARAM_TEXT.
|
137 |
*/
|
138 |
define('PARAM_MULTILANG', 0x0009); |
139 |
|
140 |
/**
|
141 |
* PARAM_TEXT - general plain text compatible with multilang filter, no other html tags.
|
142 |
*/
|
143 |
define('PARAM_TEXT', 0x0009); |
144 |
|
145 |
/**
|
146 |
* PARAM_FILE - safe file name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
|
147 |
*/
|
148 |
define('PARAM_FILE', 0x0010); |
149 |
|
150 |
/**
|
151 |
* PARAM_TAG - one tag (interests, blogs, etc.) - mostly international alphanumeric with spaces
|
152 |
*/
|
153 |
define('PARAM_TAG', 0x0011); |
154 |
|
155 |
/**
|
156 |
* PARAM_TAGLIST - list of tags separated by commas (interests, blogs, etc.)
|
157 |
*/
|
158 |
define('PARAM_TAGLIST', 0x0012); |
159 |
|
160 |
/**
|
161 |
* PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
|
162 |
* note: the leading slash is not removed, window drive letter is not allowed
|
163 |
*/
|
164 |
define('PARAM_PATH', 0x0020); |
165 |
|
166 |
/**
|
167 |
* PARAM_HOST - expected fully qualified domain name (FQDN) or an IPv4 dotted quad (IP address)
|
168 |
*/
|
169 |
define('PARAM_HOST', 0x0040); |
170 |
|
171 |
/**
|
172 |
* PARAM_URL - expected properly formatted URL. Please note that domain part is required, http://localhost/ is not acceppted but http://localhost.localdomain/ is ok.
|
173 |
*/
|
174 |
define('PARAM_URL', 0x0080); |
175 |
|
176 |
/**
|
177 |
* 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!)
|
178 |
*/
|
179 |
define('PARAM_LOCALURL', 0x0180); |
180 |
|
181 |
/**
|
182 |
* PARAM_CLEANFILE - safe file name, all dangerous and regional chars are removed,
|
183 |
* use when you want to store a new file submitted by students
|
184 |
*/
|
185 |
define('PARAM_CLEANFILE',0x0200); |
186 |
|
187 |
/**
|
188 |
* PARAM_ALPHANUM - expected numbers and letters only.
|
189 |
*/
|
190 |
define('PARAM_ALPHANUM', 0x0400); |
191 |
|
192 |
/**
|
193 |
* PARAM_BOOL - converts input into 0 or 1, use for switches in forms and urls.
|
194 |
*/
|
195 |
define('PARAM_BOOL', 0x0800); |
196 |
|
197 |
/**
|
198 |
* PARAM_CLEANHTML - cleans submitted HTML code and removes slashes
|
199 |
* note: do not forget to addslashes() before storing into database!
|
200 |
*/
|
201 |
define('PARAM_CLEANHTML',0x1000); |
202 |
|
203 |
/**
|
204 |
* PARAM_ALPHAEXT the same contents as PARAM_ALPHA plus the chars in quotes: "/-_" allowed,
|
205 |
* suitable for include() and require()
|
206 |
* @TODO: should we rename this function to PARAM_SAFEDIRS??
|
207 |
*/
|
208 |
define('PARAM_ALPHAEXT', 0x2000); |
209 |
|
210 |
/**
|
211 |
* PARAM_SAFEDIR - safe directory name, suitable for include() and require()
|
212 |
*/
|
213 |
define('PARAM_SAFEDIR', 0x4000); |
214 |
|
215 |
/**
|
216 |
* PARAM_SEQUENCE - expects a sequence of numbers like 8 to 1,5,6,4,6,8,9. Numbers and comma only.
|
217 |
*/
|
218 |
define('PARAM_SEQUENCE', 0x8000); |
219 |
|
220 |
/**
|
221 |
* PARAM_PEM - Privacy Enhanced Mail format
|
222 |
*/
|
223 |
define('PARAM_PEM', 0x10000); |
224 |
|
225 |
/**
|
226 |
* PARAM_BASE64 - Base 64 encoded format
|
227 |
*/
|
228 |
define('PARAM_BASE64', 0x20000); |
229 |
|
230 |
|
231 |
/// Page types ///
|
232 |
/**
|
233 |
* PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php.
|
234 |
*/
|
235 |
define('PAGE_COURSE_VIEW', 'course-view'); |
236 |
|
237 |
/// Debug levels ///
|
238 |
/** no warnings at all */
|
239 |
define ('DEBUG_NONE', 0); |
240 |
/** E_ERROR | E_PARSE */
|
241 |
define ('DEBUG_MINIMAL', 5); |
242 |
/** E_ERROR | E_PARSE | E_WARNING | E_NOTICE */
|
243 |
define ('DEBUG_NORMAL', 15); |
244 |
/** E_ALL without E_STRICT for now, do show recoverable fatal errors */
|
245 |
define ('DEBUG_ALL', 6143); |
246 |
/** DEBUG_ALL with extra Moodle debug messages - (DEBUG_ALL | 32768) */
|
247 |
define ('DEBUG_DEVELOPER', 38911); |
248 |
|
249 |
/**
|
250 |
* Blog access level constant declaration
|
251 |
*/
|
252 |
define ('BLOG_USER_LEVEL', 1); |
253 |
define ('BLOG_GROUP_LEVEL', 2); |
254 |
define ('BLOG_COURSE_LEVEL', 3); |
255 |
define ('BLOG_SITE_LEVEL', 4); |
256 |
define ('BLOG_GLOBAL_LEVEL', 5); |
257 |
|
258 |
/**
|
259 |
* Tag constanst
|
260 |
*/
|
261 |
//To prevent problems with multibytes strings, this should not exceed the
|
262 |
//length of "varchar(255) / 3 (bytes / utf-8 character) = 85".
|
263 |
define('TAG_MAX_LENGTH', 50); |
264 |
|
265 |
/**
|
266 |
* Password policy constants
|
267 |
*/
|
268 |
define ('PASSWORD_LOWER', 'abcdefghijklmnopqrstuvwxyz'); |
269 |
define ('PASSWORD_UPPER', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); |
270 |
define ('PASSWORD_DIGITS', '0123456789'); |
271 |
define ('PASSWORD_NONALPHANUM', '.,;:!?_-+/*@#&$'); |
272 |
|
273 |
if (!defined('SORT_LOCALE_STRING')) { // PHP < 4.4.0 - TODO: remove in 2.0 |
274 |
define('SORT_LOCALE_STRING', SORT_STRING); |
275 |
} |
276 |
|
277 |
|
278 |
/// PARAMETER HANDLING ////////////////////////////////////////////////////
|
279 |
|
280 |
/**
|
281 |
* Returns a particular value for the named variable, taken from
|
282 |
* POST or GET. If the parameter doesn't exist then an error is
|
283 |
* thrown because we require this variable.
|
284 |
*
|
285 |
* This function should be used to initialise all required values
|
286 |
* in a script that are based on parameters. Usually it will be
|
287 |
* used like this:
|
288 |
* $id = required_param('id');
|
289 |
*
|
290 |
* @param string $parname the name of the page parameter we want
|
291 |
* @param int $type expected type of parameter
|
292 |
* @return mixed
|
293 |
*/
|
294 |
function required_param($parname, $type=PARAM_CLEAN) { |
295 |
|
296 |
// detect_unchecked_vars addition
|
297 |
global $CFG; |
298 |
if (!empty($CFG->detect_unchecked_vars)) { |
299 |
global $UNCHECKED_VARS; |
300 |
unset ($UNCHECKED_VARS->vars[$parname]); |
301 |
} |
302 |
|
303 |
if (isset($_POST[$parname])) { // POST has precedence |
304 |
$param = $_POST[$parname]; |
305 |
} else if (isset($_GET[$parname])) { |
306 |
$param = $_GET[$parname]; |
307 |
} else {
|
308 |
error('A required parameter ('.$parname.') was missing'); |
309 |
} |
310 |
|
311 |
return clean_param($param, $type); |
312 |
} |
313 |
|
314 |
/**
|
315 |
* Returns a particular value for the named variable, taken from
|
316 |
* POST or GET, otherwise returning a given default.
|
317 |
*
|
318 |
* This function should be used to initialise all optional values
|
319 |
* in a script that are based on parameters. Usually it will be
|
320 |
* used like this:
|
321 |
* $name = optional_param('name', 'Fred');
|
322 |
*
|
323 |
* @param string $parname the name of the page parameter we want
|
324 |
* @param mixed $default the default value to return if nothing is found
|
325 |
* @param int $type expected type of parameter
|
326 |
* @return mixed
|
327 |
*/
|
328 |
function optional_param($parname, $default=NULL, $type=PARAM_CLEAN) { |
329 |
|
330 |
// detect_unchecked_vars addition
|
331 |
global $CFG; |
332 |
if (!empty($CFG->detect_unchecked_vars)) { |
333 |
global $UNCHECKED_VARS; |
334 |
unset ($UNCHECKED_VARS->vars[$parname]); |
335 |
} |
336 |
|
337 |
if (isset($_POST[$parname])) { // POST has precedence |
338 |
$param = $_POST[$parname]; |
339 |
} else if (isset($_GET[$parname])) { |
340 |
$param = $_GET[$parname]; |
341 |
} else {
|
342 |
return $default; |
343 |
} |
344 |
|
345 |
return clean_param($param, $type); |
346 |
} |
347 |
|
348 |
/**
|
349 |
* Used by {@link optional_param()} and {@link required_param()} to
|
350 |
* clean the variables and/or cast to specific types, based on
|
351 |
* an options field.
|
352 |
* <code>
|
353 |
* $course->format = clean_param($course->format, PARAM_ALPHA);
|
354 |
* $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN);
|
355 |
* </code>
|
356 |
*
|
357 |
* @uses $CFG
|
358 |
* @uses PARAM_RAW
|
359 |
* @uses PARAM_CLEAN
|
360 |
* @uses PARAM_CLEANHTML
|
361 |
* @uses PARAM_INT
|
362 |
* @uses PARAM_NUMBER
|
363 |
* @uses PARAM_ALPHA
|
364 |
* @uses PARAM_ALPHANUM
|
365 |
* @uses PARAM_ALPHAEXT
|
366 |
* @uses PARAM_SEQUENCE
|
367 |
* @uses PARAM_BOOL
|
368 |
* @uses PARAM_NOTAGS
|
369 |
* @uses PARAM_TEXT
|
370 |
* @uses PARAM_SAFEDIR
|
371 |
* @uses PARAM_CLEANFILE
|
372 |
* @uses PARAM_FILE
|
373 |
* @uses PARAM_PATH
|
374 |
* @uses PARAM_HOST
|
375 |
* @uses PARAM_URL
|
376 |
* @uses PARAM_LOCALURL
|
377 |
* @uses PARAM_PEM
|
378 |
* @uses PARAM_BASE64
|
379 |
* @uses PARAM_TAG
|
380 |
* @uses PARAM_SEQUENCE
|
381 |
* @param mixed $param the variable we are cleaning
|
382 |
* @param int $type expected format of param after cleaning.
|
383 |
* @return mixed
|
384 |
*/
|
385 |
function clean_param($param, $type) { |
386 |
|
387 |
global $CFG; |
388 |
|
389 |
if (is_array($param)) { // Let's loop |
390 |
$newparam = array(); |
391 |
foreach ($param as $key => $value) { |
392 |
$newparam[$key] = clean_param($value, $type); |
393 |
} |
394 |
return $newparam; |
395 |
} |
396 |
|
397 |
switch ($type) { |
398 |
case PARAM_RAW: // no cleaning at all |
399 |
return $param; |
400 |
|
401 |
case PARAM_CLEAN: // General HTML cleaning, try to use more specific type if possible |
402 |
if (is_numeric($param)) { |
403 |
return $param; |
404 |
} |
405 |
$param = stripslashes($param); // Needed for kses to work fine |
406 |
$param = clean_text($param); // Sweep for scripts, etc |
407 |
return addslashes($param); // Restore original request parameter slashes |
408 |
|
409 |
case PARAM_CLEANHTML: // prepare html fragment for display, do not store it into db!! |
410 |
$param = stripslashes($param); // Remove any slashes |
411 |
$param = clean_text($param); // Sweep for scripts, etc |
412 |
return trim($param); |
413 |
|
414 |
case PARAM_INT: |
415 |
return (int)$param; // Convert to integer |
416 |
|
417 |
case PARAM_NUMBER: |
418 |
return (float)$param; // Convert to integer |
419 |
|
420 |
case PARAM_ALPHA: // Remove everything not a-z |
421 |
return eregi_replace('[^a-zA-Z]', '', $param); |
422 |
|
423 |
case PARAM_ALPHANUM: // Remove everything not a-zA-Z0-9 |
424 |
return eregi_replace('[^A-Za-z0-9]', '', $param); |
425 |
|
426 |
case PARAM_ALPHAEXT: // Remove everything not a-zA-Z/_- |
427 |
return eregi_replace('[^a-zA-Z/_-]', '', $param); |
428 |
|
429 |
case PARAM_SEQUENCE: // Remove everything not 0-9, |
430 |
return eregi_replace('[^0-9,]', '', $param); |
431 |
|
432 |
case PARAM_BOOL: // Convert to 1 or 0 |
433 |
$tempstr = strtolower($param); |
434 |
if ($tempstr == 'on' or $tempstr == 'yes' ) { |
435 |
$param = 1; |
436 |
} else if ($tempstr == 'off' or $tempstr == 'no') { |
437 |
$param = 0; |
438 |
} else {
|
439 |
$param = empty($param) ? 0 : 1; |
440 |
} |
441 |
return $param; |
442 |
|
443 |
case PARAM_NOTAGS: // Strip all tags |
444 |
return strip_tags($param); |
445 |
|
446 |
case PARAM_TEXT: // leave only tags needed for multilang |
447 |
return clean_param(strip_tags($param, '<lang><span>'), PARAM_CLEAN); |
448 |
|
449 |
case PARAM_SAFEDIR: // Remove everything not a-zA-Z0-9_- |
450 |
return eregi_replace('[^a-zA-Z0-9_-]', '', $param); |
451 |
|
452 |
case PARAM_CLEANFILE: // allow only safe characters |
453 |
return clean_filename($param); |
454 |
|
455 |
case PARAM_FILE: // Strip all suspicious characters from filename |
456 |
$param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param); |
457 |
$param = ereg_replace('\.\.+', '', $param); |
458 |
if($param == '.') { |
459 |
$param = ''; |
460 |
} |
461 |
return $param; |
462 |
|
463 |
case PARAM_PATH: // Strip all suspicious characters from file path |
464 |
$param = str_replace('\\\'', '\'', $param); |
465 |
$param = str_replace('\\"', '"', $param); |
466 |
$param = str_replace('\\', '/', $param); |
467 |
$param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param); |
468 |
$param = ereg_replace('\.\.+', '', $param); |
469 |
$param = ereg_replace('//+', '/', $param); |
470 |
return ereg_replace('/(\./)+', '/', $param); |
471 |
|
472 |
case PARAM_HOST: // allow FQDN or IPv4 dotted quad |
473 |
$param = preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars |
474 |
// match ipv4 dotted quad
|
475 |
if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){ |
476 |
// confirm values are ok
|
477 |
if ( $match[0] > 255 |
478 |
|| $match[1] > 255 |
479 |
|| $match[3] > 255 |
480 |
|| $match[4] > 255 ) { |
481 |
// hmmm, what kind of dotted quad is this?
|
482 |
$param = ''; |
483 |
} |
484 |
} elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers |
485 |
&& !preg_match('/^[\.-]/', $param) // no leading dots/hyphens |
486 |
&& !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens |
487 |
) { |
488 |
// all is ok - $param is respected
|
489 |
} else {
|
490 |
// all is not ok...
|
491 |
$param=''; |
492 |
} |
493 |
return $param; |
494 |
|
495 |
case PARAM_URL: // allow safe ftp, http, mailto urls |
496 |
include_once($CFG->dirroot . '/lib/validateurlsyntax.php'); |
497 |
if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) { |
498 |
// all is ok, param is respected
|
499 |
} else {
|
500 |
$param =''; // not really ok |
501 |
} |
502 |
return $param; |
503 |
|
504 |
case PARAM_LOCALURL: // allow http absolute, root relative and relative URLs within wwwroot |
505 |
$param = clean_param($param, PARAM_URL); |
506 |
if (!empty($param)) { |
507 |
if (preg_match(':^/:', $param)) { |
508 |
// root-relative, ok!
|
509 |
} elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) { |
510 |
// absolute, and matches our wwwroot
|
511 |
} else {
|
512 |
// relative - let's make sure there are no tricks
|
513 |
if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) { |
514 |
// looks ok.
|
515 |
} else {
|
516 |
$param = ''; |
517 |
} |
518 |
} |
519 |
} |
520 |
return $param; |
521 |
|
522 |
case PARAM_PEM: |
523 |
$param = trim($param); |
524 |
// PEM formatted strings may contain letters/numbers and the symbols
|
525 |
// forward slash: /
|
526 |
// plus sign: +
|
527 |
// equal sign: =
|
528 |
// , surrounded by BEGIN and END CERTIFICATE prefix and suffixes
|
529 |
if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) { |
530 |
list($wholething, $body) = $matches; |
531 |
unset($wholething, $matches); |
532 |
$b64 = clean_param($body, PARAM_BASE64); |
533 |
if (!empty($b64)) { |
534 |
return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n"; |
535 |
} else {
|
536 |
return ''; |
537 |
} |
538 |
} |
539 |
return ''; |
540 |
|
541 |
case PARAM_BASE64: |
542 |
if (!empty($param)) { |
543 |
// PEM formatted strings may contain letters/numbers and the symbols
|
544 |
// forward slash: /
|
545 |
// plus sign: +
|
546 |
// equal sign: =
|
547 |
if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) { |
548 |
return ''; |
549 |
} |
550 |
$lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY); |
551 |
// Each line of base64 encoded data must be 64 characters in
|
552 |
// length, except for the last line which may be less than (or
|
553 |
// equal to) 64 characters long.
|
554 |
for ($i=0, $j=count($lines); $i < $j; $i++) { |
555 |
if ($i + 1 == $j) { |
556 |
if (64 < strlen($lines[$i])) { |
557 |
return ''; |
558 |
} |
559 |
continue;
|
560 |
} |
561 |
|
562 |
if (64 != strlen($lines[$i])) { |
563 |
return ''; |
564 |
} |
565 |
} |
566 |
return implode("\n",$lines); |
567 |
} else {
|
568 |
return ''; |
569 |
} |
570 |
|
571 |
case PARAM_TAG: |
572 |
//as long as magic_quotes_gpc is used, a backslash will be a
|
573 |
//problem, so remove *all* backslash.
|
574 |
$param = str_replace('\\', '', $param); |
575 |
//convert many whitespace chars into one
|
576 |
$param = preg_replace('/\s+/', ' ', $param); |
577 |
$textlib = textlib_get_instance();
|
578 |
$param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH); |
579 |
return $param; |
580 |
|
581 |
|
582 |
case PARAM_TAGLIST: |
583 |
$tags = explode(',', $param); |
584 |
$result = array(); |
585 |
foreach ($tags as $tag) { |
586 |
$res = clean_param($tag, PARAM_TAG); |
587 |
if ($res != '') { |
588 |
$result[] = $res; |
589 |
} |
590 |
} |
591 |
if ($result) { |
592 |
return implode(',', $result); |
593 |
} else {
|
594 |
return ''; |
595 |
} |
596 |
|
597 |
default: // throw error, switched parameters in optional_param or another serious problem |
598 |
error("Unknown parameter type: $type");
|
599 |
} |
600 |
} |
601 |
|
602 |
/**
|
603 |
* Return true if given value is integer or string with integer value
|
604 |
*
|
605 |
* @param mixed $value String or Int
|
606 |
* @return bool true if number, false if not
|
607 |
*/
|
608 |
function is_number($value) { |
609 |
if (is_int($value)) { |
610 |
return true; |
611 |
} else if (is_string($value)) { |
612 |
return ((string)(int)$value) === $value; |
613 |
} else {
|
614 |
return false; |
615 |
} |
616 |
} |
617 |
|
618 |
/**
|
619 |
* This function is useful for testing whether something you got back from
|
620 |
* the HTML editor actually contains anything. Sometimes the HTML editor
|
621 |
* appear to be empty, but actually you get back a <br> tag or something.
|
622 |
*
|
623 |
* @param string $string a string containing HTML.
|
624 |
* @return boolean does the string contain any actual content - that is text,
|
625 |
* images, objcts, etc.
|
626 |
*/
|
627 |
function html_is_blank($string) { |
628 |
return trim(strip_tags($string, '<img><object><applet><input><select><textarea><hr>')) == ''; |
629 |
} |
630 |
|
631 |
/**
|
632 |
* Set a key in global configuration
|
633 |
*
|
634 |
* Set a key/value pair in both this session's {@link $CFG} global variable
|
635 |
* and in the 'config' database table for future sessions.
|
636 |
*
|
637 |
* Can also be used to update keys for plugin-scoped configs in config_plugin table.
|
638 |
* In that case it doesn't affect $CFG.
|
639 |
*
|
640 |
* A NULL value will delete the entry.
|
641 |
*
|
642 |
* @param string $name the key to set
|
643 |
* @param string $value the value to set (without magic quotes)
|
644 |
* @param string $plugin (optional) the plugin scope
|
645 |
* @uses $CFG
|
646 |
* @return bool
|
647 |
*/
|
648 |
function set_config($name, $value, $plugin=NULL) { |
649 |
/// No need for get_config because they are usually always available in $CFG
|
650 |
|
651 |
global $CFG; |
652 |
|
653 |
if (empty($plugin)) { |
654 |
if (!array_key_exists($name, $CFG->config_php_settings)) { |
655 |
// So it's defined for this invocation at least
|
656 |
if (is_null($value)) { |
657 |
unset($CFG->$name); |
658 |
} else {
|
659 |
$CFG->$name = (string)$value; // settings from db are always strings |
660 |
} |
661 |
} |
662 |
|
663 |
if (get_field('config', 'name', 'name', $name)) { |
664 |
if ($value===null) { |
665 |
return delete_records('config', 'name', $name); |
666 |
} else {
|
667 |
return set_field('config', 'value', addslashes($value), 'name', $name); |
668 |
} |
669 |
} else {
|
670 |
if ($value===null) { |
671 |
return true; |
672 |
} |
673 |
$config = new object(); |
674 |
$config->name = $name; |
675 |
$config->value = addslashes($value); |
676 |
return insert_record('config', $config); |
677 |
} |
678 |
} else { // plugin scope |
679 |
if ($id = get_field('config_plugins', 'id', 'name', $name, 'plugin', $plugin)) { |
680 |
if ($value===null) { |
681 |
return delete_records('config_plugins', 'name', $name, 'plugin', $plugin); |
682 |
} else {
|
683 |
return set_field('config_plugins', 'value', addslashes($value), 'id', $id); |
684 |
} |
685 |
} else {
|
686 |
if ($value===null) { |
687 |
return true; |
688 |
} |
689 |
$config = new object(); |
690 |
$config->plugin = addslashes($plugin); |
691 |
$config->name = $name; |
692 |
$config->value = addslashes($value); |
693 |
return insert_record('config_plugins', $config); |
694 |
} |
695 |
} |
696 |
} |
697 |
|
698 |
/**
|
699 |
* Get configuration values from the global config table
|
700 |
* or the config_plugins table.
|
701 |
*
|
702 |
* If called with no parameters it will do the right thing
|
703 |
* generating $CFG safely from the database without overwriting
|
704 |
* existing values.
|
705 |
*
|
706 |
* If called with 2 parameters it will return a $string single
|
707 |
* value or false of the value is not found.
|
708 |
*
|
709 |
* @param string $plugin
|
710 |
* @param string $name
|
711 |
* @uses $CFG
|
712 |
* @return hash-like object or single value
|
713 |
*
|
714 |
*/
|
715 |
function get_config($plugin=NULL, $name=NULL) { |
716 |
|
717 |
global $CFG; |
718 |
|
719 |
if (!empty($name)) { // the user is asking for a specific value |
720 |
if (!empty($plugin)) { |
721 |
return get_field('config_plugins', 'value', 'plugin' , $plugin, 'name', $name); |
722 |
} else {
|
723 |
return get_field('config', 'value', 'name', $name); |
724 |
} |
725 |
} |
726 |
|
727 |
// the user is after a recordset
|
728 |
if (!empty($plugin)) { |
729 |
if ($configs=get_records('config_plugins', 'plugin', $plugin, '', 'name,value')) { |
730 |
$configs = (array)$configs; |
731 |
$localcfg = array(); |
732 |
foreach ($configs as $config) { |
733 |
$localcfg[$config->name] = $config->value; |
734 |
} |
735 |
return (object)$localcfg; |
736 |
} else {
|
737 |
return false; |
738 |
} |
739 |
} else {
|
740 |
// this was originally in setup.php
|
741 |
if ($configs = get_records('config')) { |
742 |
$localcfg = (array)$CFG; |
743 |
foreach ($configs as $config) { |
744 |
if (!isset($localcfg[$config->name])) { |
745 |
$localcfg[$config->name] = $config->value; |
746 |
} |
747 |
// do not complain anymore if config.php overrides settings from db
|
748 |
} |
749 |
|
750 |
$localcfg = (object)$localcfg; |
751 |
return $localcfg; |
752 |
} else {
|
753 |
// preserve $CFG if DB returns nothing or error
|
754 |
return $CFG; |
755 |
} |
756 |
|
757 |
} |
758 |
} |
759 |
|
760 |
/**
|
761 |
* Removes a key from global configuration
|
762 |
*
|
763 |
* @param string $name the key to set
|
764 |
* @param string $plugin (optional) the plugin scope
|
765 |
* @uses $CFG
|
766 |
* @return bool
|
767 |
*/
|
768 |
function unset_config($name, $plugin=NULL) { |
769 |
|
770 |
global $CFG; |
771 |
|
772 |
unset($CFG->$name); |
773 |
|
774 |
if (empty($plugin)) { |
775 |
return delete_records('config', 'name', $name); |
776 |
} else {
|
777 |
return delete_records('config_plugins', 'name', $name, 'plugin', $plugin); |
778 |
} |
779 |
} |
780 |
|
781 |
/**
|
782 |
* Get volatile flags
|
783 |
*
|
784 |
* @param string $type
|
785 |
* @param int $changedsince
|
786 |
* @return records array
|
787 |
*
|
788 |
*/
|
789 |
function get_cache_flags($type, $changedsince=NULL) { |
790 |
|
791 |
$type = addslashes($type); |
792 |
|
793 |
$sqlwhere = 'flagtype=\'' . $type . '\' AND expiry >= ' . time(); |
794 |
if ($changedsince !== NULL) { |
795 |
$changedsince = (int)$changedsince; |
796 |
$sqlwhere .= ' AND timemodified > ' . $changedsince; |
797 |
} |
798 |
$cf = array(); |
799 |
if ($flags=get_records_select('cache_flags', $sqlwhere, '', 'name,value')) { |
800 |
foreach ($flags as $flag) { |
801 |
$cf[$flag->name] = $flag->value; |
802 |
} |
803 |
} |
804 |
return $cf; |
805 |
} |
806 |
|
807 |
/**
|
808 |
* Use this funciton to get a list of users from a config setting of type admin_setting_users_with_capability.
|
809 |
* @param string $value the value of the config setting.
|
810 |
* @param string $capability the capability - must match the one passed to the admin_setting_users_with_capability constructor.
|
811 |
* @return array of user objects.
|
812 |
*/
|
813 |
function get_users_from_config($value, $capability) { |
814 |
global $CFG; |
815 |
if ($value == '$@ALL@$') { |
816 |
$users = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM), $capability); |
817 |
} else if ($value) { |
818 |
$usernames = explode(',', $value); |
819 |
$users = get_records_select('user', "username IN ('" . implode("','", $usernames) . "') AND mnethostid = " . $CFG->mnet_localhost_id); |
820 |
} else {
|
821 |
$users = array(); |
822 |
} |
823 |
return $users; |
824 |
} |
825 |
|
826 |
/**
|
827 |
* Get volatile flags
|
828 |
*
|
829 |
* @param string $type
|
830 |
* @param string $name
|
831 |
* @param int $changedsince
|
832 |
* @return records array
|
833 |
*
|
834 |
*/
|
835 |
function get_cache_flag($type, $name, $changedsince=NULL) { |
836 |
|
837 |
$type = addslashes($type); |
838 |
$name = addslashes($name); |
839 |
|
840 |
$sqlwhere = 'flagtype=\'' . $type . '\' AND name=\'' . $name . '\' AND expiry >= ' . time(); |
841 |
if ($changedsince !== NULL) { |
842 |
$changedsince = (int)$changedsince; |
843 |
$sqlwhere .= ' AND timemodified > ' . $changedsince; |
844 |
} |
845 |
return get_field_select('cache_flags', 'value', $sqlwhere); |
846 |
} |
847 |
|
848 |
/**
|
849 |
* Set a volatile flag
|
850 |
*
|
851 |
* @param string $type the "type" namespace for the key
|
852 |
* @param string $name the key to set
|
853 |
* @param string $value the value to set (without magic quotes) - NULL will remove the flag
|
854 |
* @param int $expiry (optional) epoch indicating expiry - defaults to now()+ 24hs
|
855 |
* @return bool
|
856 |
*/
|
857 |
function set_cache_flag($type, $name, $value, $expiry=NULL) { |
858 |
|
859 |
|
860 |
$timemodified = time(); |
861 |
if ($expiry===NULL || $expiry < $timemodified) { |
862 |
$expiry = $timemodified + 24 * 60 * 60; |
863 |
} else {
|
864 |
$expiry = (int)$expiry; |
865 |
} |
866 |
|
867 |
if ($value === NULL) { |
868 |
return unset_cache_flag($type,$name); |
869 |
} |
870 |
|
871 |
$type = addslashes($type); |
872 |
$name = addslashes($name); |
873 |
if ($f = get_record('cache_flags', 'name', $name, 'flagtype', $type)) { // this is a potentail problem in DEBUG_DEVELOPER |
874 |
if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) { |
875 |
return true; //no need to update; helps rcache too |
876 |
} |
877 |
$f->value = addslashes($value); |
878 |
$f->expiry = $expiry; |
879 |
$f->timemodified = $timemodified; |
880 |
return update_record('cache_flags', $f); |
881 |
} else {
|
882 |
$f = new object(); |
883 |
$f->flagtype = $type; |
884 |
$f->name = $name; |
885 |
$f->value = addslashes($value); |
886 |
$f->expiry = $expiry; |
887 |
$f->timemodified = $timemodified; |
888 |
return (bool)insert_record('cache_flags', $f); |
889 |
} |
890 |
} |
891 |
|
892 |
/**
|
893 |
* Removes a single volatile flag
|
894 |
*
|
895 |
* @param string $type the "type" namespace for the key
|
896 |
* @param string $name the key to set
|
897 |
* @uses $CFG
|
898 |
* @return bool
|
899 |
*/
|
900 |
function unset_cache_flag($type, $name) { |
901 |
|
902 |
return delete_records('cache_flags', |
903 |
'name', addslashes($name), |
904 |
'flagtype', addslashes($type)); |
905 |
} |
906 |
|
907 |
/**
|
908 |
* Garbage-collect volatile flags
|
909 |
*
|
910 |
*/
|
911 |
function gc_cache_flags() { |
912 |
return delete_records_select('cache_flags', 'expiry < ' . time()); |
913 |
} |
914 |
|
915 |
/**
|
916 |
* Refresh current $USER session global variable with all their current preferences.
|
917 |
* @uses $USER
|
918 |
*/
|
919 |
function reload_user_preferences() { |
920 |
|
921 |
global $USER; |
922 |
|
923 |
//reset preference
|
924 |
$USER->preference = array(); |
925 |
|
926 |
if (!isloggedin() or isguestuser()) { |
927 |
// no permanent storage for not-logged-in user and guest
|
928 |
|
929 |
} else if ($preferences = get_records('user_preferences', 'userid', $USER->id)) { |
930 |
foreach ($preferences as $preference) { |
931 |
$USER->preference[$preference->name] = $preference->value; |
932 |
} |
933 |
} |
934 |
|
935 |
return true; |
936 |
} |
937 |
|
938 |
/**
|
939 |
* Sets a preference for the current user
|
940 |
* Optionally, can set a preference for a different user object
|
941 |
* @uses $USER
|
942 |
* @todo Add a better description and include usage examples. Add inline links to $USER and user functions in above line.
|
943 |
|
944 |
* @param string $name The key to set as preference for the specified user
|
945 |
* @param string $value The value to set forthe $name key in the specified user's record
|
946 |
* @param int $otheruserid A moodle user ID
|
947 |
* @return bool
|
948 |
*/
|
949 |
function set_user_preference($name, $value, $otheruserid=NULL) { |
950 |
|
951 |
global $USER; |
952 |
|
953 |
if (!isset($USER->preference)) { |
954 |
reload_user_preferences(); |
955 |
} |
956 |
|
957 |
if (empty($name)) { |
958 |
return false; |
959 |
} |
960 |
|
961 |
$nostore = false; |
962 |
|
963 |
if (empty($otheruserid)){ |
964 |
if (!isloggedin() or isguestuser()) { |
965 |
$nostore = true; |
966 |
} |
967 |
$userid = $USER->id; |
968 |
} else {
|
969 |
if (isguestuser($otheruserid)) { |
970 |
$nostore = true; |
971 |
} |
972 |
$userid = $otheruserid; |
973 |
} |
974 |
|
975 |
$return = true; |
976 |
if ($nostore) { |
977 |
// no permanent storage for not-logged-in user and guest
|
978 |
|
979 |
} else if ($preference = get_record('user_preferences', 'userid', $userid, 'name', addslashes($name))) { |
980 |
if ($preference->value === $value) { |
981 |
return true; |
982 |
} |
983 |
if (!set_field('user_preferences', 'value', addslashes((string)$value), 'id', $preference->id)) { |
984 |
$return = false; |
985 |
} |
986 |
|
987 |
} else {
|
988 |
$preference = new object(); |
989 |
$preference->userid = $userid; |
990 |
$preference->name = addslashes($name); |
991 |
$preference->value = addslashes((string)$value); |
992 |
if (!insert_record('user_preferences', $preference)) { |
993 |
$return = false; |
994 |
} |
995 |
} |
996 |
|
997 |
// update value in USER session if needed
|
998 |
if ($userid == $USER->id) { |
999 |
$USER->preference[$name] = (string)$value; |
1000 |
} |
1001 |
|
1002 |
return $return; |
1003 |
} |
1004 |
|
1005 |
/**
|
1006 |
* Unsets a preference completely by deleting it from the database
|
1007 |
* Optionally, can set a preference for a different user id
|
1008 |
* @uses $USER
|
1009 |
* @param string $name The key to unset as preference for the specified user
|
1010 |
* @param int $otheruserid A moodle user ID
|
1011 |
*/
|
1012 |
function unset_user_preference($name, $otheruserid=NULL) { |
1013 |
|
1014 |
global $USER; |
1015 |
|
1016 |
if (!isset($USER->preference)) { |
1017 |
reload_user_preferences(); |
1018 |
} |
1019 |
|
1020 |
if (empty($otheruserid)){ |
1021 |
$userid = $USER->id; |
1022 |
} else {
|
1023 |
$userid = $otheruserid; |
1024 |
} |
1025 |
|
1026 |
//Delete the preference from $USER if needed
|
1027 |
if ($userid == $USER->id) { |
1028 |
unset($USER->preference[$name]); |
1029 |
} |
1030 |
|
1031 |
//Then from DB
|
1032 |
return delete_records('user_preferences', 'userid', $userid, 'name', addslashes($name)); |
1033 |
} |
1034 |
|
1035 |
|
1036 |
/**
|
1037 |
* Sets a whole array of preferences for the current user
|
1038 |
* @param array $prefarray An array of key/value pairs to be set
|
1039 |
* @param int $otheruserid A moodle user ID
|
1040 |
* @return bool
|
1041 |
*/
|
1042 |
function set_user_preferences($prefarray, $otheruserid=NULL) { |
1043 |
|
1044 |
if (!is_array($prefarray) or empty($prefarray)) { |
1045 |
return false; |
1046 |
} |
1047 |
|
1048 |
$return = true; |
1049 |
foreach ($prefarray as $name => $value) { |
1050 |
// The order is important; test for return is done first
|
1051 |
$return = (set_user_preference($name, $value, $otheruserid) && $return); |
1052 |
} |
1053 |
return $return; |
1054 |
} |
1055 |
|
1056 |
/**
|
1057 |
* If no arguments are supplied this function will return
|
1058 |
* all of the current user preferences as an array.
|
1059 |
* If a name is specified then this function
|
1060 |
* attempts to return that particular preference value. If
|
1061 |
* none is found, then the optional value $default is returned,
|
1062 |
* otherwise NULL.
|
1063 |
* @param string $name Name of the key to use in finding a preference value
|
1064 |
* @param string $default Value to be returned if the $name key is not set in the user preferences
|
1065 |
* @param int $otheruserid A moodle user ID
|
1066 |
* @uses $USER
|
1067 |
* @return string
|
1068 |
*/
|
1069 |
function get_user_preferences($name=NULL, $default=NULL, $otheruserid=NULL) { |
1070 |
global $USER; |
1071 |
|
1072 |
if (!isset($USER->preference)) { |
1073 |
reload_user_preferences(); |
1074 |
} |
1075 |
|
1076 |
if (empty($otheruserid)){ |
1077 |
$userid = $USER->id; |
1078 |
} else {
|
1079 |
$userid = $otheruserid; |
1080 |
} |
1081 |
|
1082 |
if ($userid == $USER->id) { |
1083 |
$preference = $USER->preference; |
1084 |
|
1085 |
} else {
|
1086 |
$preference = array(); |
1087 |
if ($prefdata = get_records('user_preferences', 'userid', $userid)) { |
1088 |
foreach ($prefdata as $pref) { |
1089 |
$preference[$pref->name] = $pref->value; |
1090 |
} |
1091 |
} |
1092 |
} |
1093 |
|
1094 |
if (empty($name)) { |
1095 |
return $preference; // All values |
1096 |
|
1097 |
} else if (array_key_exists($name, $preference)) { |
1098 |
return $preference[$name]; // The single value |
1099 |
|
1100 |
} else {
|
1101 |
return $default; // Default value (or NULL) |
1102 |
} |
1103 |
} |
1104 |
|
1105 |
|
1106 |
/// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
|
1107 |
|
1108 |
/**
|
1109 |
* Given date parts in user time produce a GMT timestamp.
|
1110 |
*
|
1111 |
* @param int $year The year part to create timestamp of
|
1112 |
* @param int $month The month part to create timestamp of
|
1113 |
* @param int $day The day part to create timestamp of
|
1114 |
* @param int $hour The hour part to create timestamp of
|
1115 |
* @param int $minute The minute part to create timestamp of
|
1116 |
* @param int $second The second part to create timestamp of
|
1117 |
* @param float $timezone ?
|
1118 |
* @param bool $applydst ?
|
1119 |
* @return int timestamp
|
1120 |
* @todo Finish documenting this function
|
1121 |
*/
|
1122 |
function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) { |
1123 |
|
1124 |
$strtimezone = NULL; |
1125 |
if (!is_numeric($timezone)) { |
1126 |
$strtimezone = $timezone; |
1127 |
} |
1128 |
|
1129 |
$timezone = get_user_timezone_offset($timezone); |
1130 |
|
1131 |
if (abs($timezone) > 13) { |
1132 |
$time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year); |
1133 |
} else {
|
1134 |
$time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year); |
1135 |
$time = usertime($time, $timezone); |
1136 |
if($applydst) { |
1137 |
$time -= dst_offset_on($time, $strtimezone); |
1138 |
} |
1139 |
} |
1140 |
|
1141 |
return $time; |
1142 |
|
1143 |
} |
1144 |
|
1145 |
/**
|
1146 |
* Given an amount of time in seconds, returns string
|
1147 |
* formatted nicely as weeks, days, hours etc as needed
|
1148 |
*
|
1149 |
* @uses MINSECS
|
1150 |
* @uses HOURSECS
|
1151 |
* @uses DAYSECS
|
1152 |
* @uses YEARSECS
|
1153 |
* @param int $totalsecs ?
|
1154 |
* @param array $str ?
|
1155 |
* @return string
|
1156 |
*/
|
1157 |
function format_time($totalsecs, $str=NULL) { |
1158 |
|
1159 |
$totalsecs = abs($totalsecs); |
1160 |
|
1161 |
if (!$str) { // Create the str structure the slow way |
1162 |
$str->day = get_string('day'); |
1163 |
$str->days = get_string('days'); |
1164 |
$str->hour = get_string('hour'); |
1165 |
$str->hours = get_string('hours'); |
1166 |
$str->min = get_string('min'); |
1167 |
$str->mins = get_string('mins'); |
1168 |
$str->sec = get_string('sec'); |
1169 |
$str->secs = get_string('secs'); |
1170 |
$str->year = get_string('year'); |
1171 |
$str->years = get_string('years'); |
1172 |
} |
1173 |
|
1174 |
|
1175 |
$years = floor($totalsecs/YEARSECS); |
1176 |
$remainder = $totalsecs - ($years*YEARSECS); |
1177 |
$days = floor($remainder/DAYSECS); |
1178 |
$remainder = $totalsecs - ($days*DAYSECS); |
1179 |
$hours = floor($remainder/HOURSECS); |
1180 |
$remainder = $remainder - ($hours*HOURSECS); |
1181 |
$mins = floor($remainder/MINSECS); |
1182 |
$secs = $remainder - ($mins*MINSECS); |
1183 |
|
1184 |
$ss = ($secs == 1) ? $str->sec : $str->secs; |
1185 |
$sm = ($mins == 1) ? $str->min : $str->mins; |
1186 |
$sh = ($hours == 1) ? $str->hour : $str->hours; |
1187 |
$sd = ($days == 1) ? $str->day : $str->days; |
1188 |
$sy = ($years == 1) ? $str->year : $str->years; |
1189 |
|
1190 |
$oyears = ''; |
1191 |
$odays = ''; |
1192 |
$ohours = ''; |
1193 |
$omins = ''; |
1194 |
$osecs = ''; |
1195 |
|
1196 |
if ($years) $oyears = $years .' '. $sy; |
1197 |
if ($days) $odays = $days .' '. $sd; |
1198 |
if ($hours) $ohours = $hours .' '. $sh; |
1199 |
if ($mins) $omins = $mins .' '. $sm; |
1200 |
if ($secs) $osecs = $secs .' '. $ss; |
1201 |
|
1202 |
if ($years) return trim($oyears .' '. $odays); |
1203 |
if ($days) return trim($odays .' '. $ohours); |
1204 |
if ($hours) return trim($ohours .' '. $omins); |
1205 |
if ($mins) return trim($omins .' '. $osecs); |
1206 |
if ($secs) return $osecs; |
1207 |
return get_string('now'); |
1208 |
} |
1209 |
|
1210 |
/**
|
1211 |
* Returns a formatted string that represents a date in user time
|
1212 |
* <b>WARNING: note that the format is for strftime(), not date().</b>
|
1213 |
* Because of a bug in most Windows time libraries, we can't use
|
1214 |
* the nicer %e, so we have to use %d which has leading zeroes.
|
1215 |
* A lot of the fuss in the function is just getting rid of these leading
|
1216 |
* zeroes as efficiently as possible.
|
1217 |
*
|
1218 |
* If parameter fixday = true (default), then take off leading
|
1219 |
* zero from %d, else mantain it.
|
1220 |
*
|
1221 |
* @uses HOURSECS
|
1222 |
* @param int $date timestamp in GMT
|
1223 |
* @param string $format strftime format
|
1224 |
* @param float $timezone
|
1225 |
* @param bool $fixday If true (default) then the leading
|
1226 |
* zero from %d is removed. If false then the leading zero is mantained.
|
1227 |
* @return string
|
1228 |
*/
|
1229 |
function userdate($date, $format='', $timezone=99, $fixday = true) { |
1230 |
|
1231 |
global $CFG; |
1232 |
|
1233 |
$strtimezone = NULL; |
1234 |
if (!is_numeric($timezone)) { |
1235 |
$strtimezone = $timezone; |
1236 |
} |
1237 |
|
1238 |
if (empty($format)) { |
1239 |
$format = get_string('strftimedaydatetime'); |
1240 |
} |
1241 |
|
1242 |
if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed. |
1243 |
$fixday = false; |
1244 |
} else if ($fixday) { |
1245 |
$formatnoday = str_replace('%d', 'DD', $format); |
1246 |
$fixday = ($formatnoday != $format); |
1247 |
} |
1248 |
|
1249 |
$date += dst_offset_on($date, $strtimezone); |
1250 |
|
1251 |
$timezone = get_user_timezone_offset($timezone); |
1252 |
|
1253 |
if (abs($timezone) > 13) { /// Server time |
1254 |
if ($fixday) { |
1255 |
$datestring = strftime($formatnoday, $date); |
1256 |
$daystring = str_replace(' 0', '', strftime(' %d', $date)); |
1257 |
$datestring = str_replace('DD', $daystring, $datestring); |
1258 |
} else {
|
1259 |
$datestring = strftime($format, $date); |
1260 |
} |
1261 |
} else {
|
1262 |
$date += (int)($timezone * 3600); |
1263 |
if ($fixday) { |
1264 |
$datestring = gmstrftime($formatnoday, $date); |
1265 |
$daystring = str_replace(' 0', '', gmstrftime(' %d', $date)); |
1266 |
$datestring = str_replace('DD', $daystring, $datestring); |
1267 |
} else {
|
1268 |
$datestring = gmstrftime($format, $date); |
1269 |
} |
1270 |
} |
1271 |
|
1272 |
/// If we are running under Windows convert from windows encoding to UTF-8
|
1273 |
/// (because it's impossible to specify UTF-8 to fetch locale info in Win32)
|
1274 |
|
1275 |
if ($CFG->ostype == 'WINDOWS') { |
1276 |
if ($localewincharset = get_string('localewincharset')) { |
1277 |
$textlib = textlib_get_instance();
|
1278 |
$datestring = $textlib->convert($datestring, $localewincharset, 'utf-8'); |
1279 |
} |
1280 |
} |
1281 |
|
1282 |
return $datestring; |
1283 |
} |
1284 |
|
1285 |
/**
|
1286 |
* Given a $time timestamp in GMT (seconds since epoch),
|
1287 |
* returns an array that represents the date in user time
|
1288 |
*
|
1289 |
* @uses HOURSECS
|
1290 |
* @param int $time Timestamp in GMT
|
1291 |
* @param float $timezone ?
|
1292 |
* @return array An array that represents the date in user time
|
1293 |
* @todo Finish documenting this function
|
1294 |
*/
|
1295 |
function usergetdate($time, $timezone=99) { |
1296 |
|
1297 |
$strtimezone = NULL; |
1298 |
if (!is_numeric($timezone)) { |
1299 |
$strtimezone = $timezone; |
1300 |
} |
1301 |
|
1302 |
$timezone = get_user_timezone_offset($timezone); |
1303 |
|
1304 |
if (abs($timezone) > 13) { // Server time |
1305 |
return getdate($time); |
1306 |
} |
1307 |
|
1308 |
// There is no gmgetdate so we use gmdate instead
|
1309 |
$time += dst_offset_on($time, $strtimezone); |
1310 |
$time += intval((float)$timezone * HOURSECS); |
1311 |
|
1312 |
$datestring = gmstrftime('%B_%A_%j_%Y_%m_%w_%d_%H_%M_%S', $time); |
1313 |
|
1314 |
//be careful to ensure the returned array matches that produced by getdate() above
|
1315 |
list(
|
1316 |
$getdate['month'], |
1317 |
$getdate['weekday'], |
1318 |
$getdate['yday'], |
1319 |
$getdate['year'], |
1320 |
$getdate['mon'], |
1321 |
$getdate['wday'], |
1322 |
$getdate['mday'], |
1323 |
$getdate['hours'], |
1324 |
$getdate['minutes'], |
1325 |
$getdate['seconds'] |
1326 |
) = explode('_', $datestring); |
1327 |
|
1328 |
return $getdate; |
1329 |
} |
1330 |
|
1331 |
/**
|
1332 |
* Given a GMT timestamp (seconds since epoch), offsets it by
|
1333 |
* the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds
|
1334 |
*
|
1335 |
* @uses HOURSECS
|
1336 |
* @param int $date Timestamp in GMT
|
1337 |
* @param float $timezone
|
1338 |
* @return int
|
1339 |
*/
|
1340 |
function usertime($date, $timezone=99) { |
1341 |
|
1342 |
$timezone = get_user_timezone_offset($timezone); |
1343 |
|
1344 |
if (abs($timezone) > 13) { |
1345 |
return $date; |
1346 |
} |
1347 |
return $date - (int)($timezone * HOURSECS); |
1348 |
} |
1349 |
|
1350 |
/**
|
1351 |
* Given a time, return the GMT timestamp of the most recent midnight
|
1352 |
* for the current user.
|
1353 |
*
|
1354 |
* @param int $date Timestamp in GMT
|
1355 |
* @param float $timezone ?
|
1356 |
* @return ?
|
1357 |
*/
|
1358 |
function usergetmidnight($date, $timezone=99) { |
1359 |
|
1360 |
$userdate = usergetdate($date, $timezone); |
1361 |
|
1362 |
// Time of midnight of this user's day, in GMT
|
1363 |
return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone); |
1364 |
|
1365 |
} |
1366 |
|
1367 |
/**
|
1368 |
* Returns a string that prints the user's timezone
|
1369 |
*
|
1370 |
* @param float $timezone The user's timezone
|
1371 |
* @return string
|
1372 |
*/
|
1373 |
function usertimezone($timezone=99) { |
1374 |
|
1375 |
$tz = get_user_timezone($timezone); |
1376 |
|
1377 |
if (!is_float($tz)) { |
1378 |
return $tz; |
1379 |
} |
1380 |
|
1381 |
if(abs($tz) > 13) { // Server time |
1382 |
return get_string('serverlocaltime'); |
1383 |
} |
1384 |
|
1385 |
if($tz == intval($tz)) { |
1386 |
// Don't show .0 for whole hours
|
1387 |
$tz = intval($tz); |
1388 |
} |
1389 |
|
1390 |
if($tz == 0) { |
1391 |
return 'UTC'; |
1392 |
} |
1393 |
else if($tz > 0) { |
1394 |
return 'UTC+'.$tz; |
1395 |
} |
1396 |
else {
|
1397 |
return 'UTC'.$tz; |
1398 |
} |
1399 |
|
1400 |
} |
1401 |
|
1402 |
/**
|
1403 |
* Returns a float which represents the user's timezone difference from GMT in hours
|
1404 |
* Checks various settings and picks the most dominant of those which have a value
|
1405 |
*
|
1406 |
* @uses $CFG
|
1407 |
* @uses $USER
|
1408 |
* @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
|
1409 |
* @return int
|
1410 |
*/
|
1411 |
function get_user_timezone_offset($tz = 99) { |
1412 |
|
1413 |
global $USER, $CFG; |
1414 |
|
1415 |
$tz = get_user_timezone($tz); |
1416 |
|
1417 |
if (is_float($tz)) { |
1418 |
return $tz; |
1419 |
} else {
|
1420 |
$tzrecord = get_timezone_record($tz); |
1421 |
if (empty($tzrecord)) { |
1422 |
return 99.0; |
1423 |
} |
1424 |
return (float)$tzrecord->gmtoff / HOURMINS; |
1425 |
} |
1426 |
} |
1427 |
|
1428 |
/**
|
1429 |
* Returns an int which represents the systems's timezone difference from GMT in seconds
|
1430 |
* @param mixed $tz timezone
|
1431 |
* @return int if found, false is timezone 99 or error
|
1432 |
*/
|
1433 |
function get_timezone_offset($tz) { |
1434 |
global $CFG; |
1435 |
|
1436 |
if ($tz == 99) { |
1437 |
return false; |
1438 |
} |
1439 |
|
1440 |
if (is_numeric($tz)) { |
1441 |
return intval($tz * 60*60); |
1442 |
} |
1443 |
|
1444 |
if (!$tzrecord = get_timezone_record($tz)) { |
1445 |
return false; |
1446 |
} |
1447 |
return intval($tzrecord->gmtoff * 60); |
1448 |
} |
1449 |
|
1450 |
/**
|
1451 |
* Returns a float or a string which denotes the user's timezone
|
1452 |
* 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)
|
1453 |
* means that for this timezone there are also DST rules to be taken into account
|
1454 |
* Checks various settings and picks the most dominant of those which have a value
|
1455 |
*
|
1456 |
* @uses $USER
|
1457 |
* @uses $CFG
|
1458 |
* @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
|
1459 |
* @return mixed
|
1460 |
*/
|
1461 |
function get_user_timezone($tz = 99) { |
1462 |
global $USER, $CFG; |
1463 |
|
1464 |
$timezones = array( |
1465 |
$tz,
|
1466 |
isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99, |
1467 |
isset($USER->timezone) ? $USER->timezone : 99, |
1468 |
isset($CFG->timezone) ? $CFG->timezone : 99, |
1469 |
); |
1470 |
|
1471 |
$tz = 99; |
1472 |
|
1473 |
while(($tz == '' || $tz == 99 || $tz == NULL) && $next = each($timezones)) { |
1474 |
$tz = $next['value']; |
1475 |
} |
1476 |
|
1477 |
return is_numeric($tz) ? (float) $tz : $tz; |
1478 |
} |
1479 |
|
1480 |
/**
|
1481 |
* ?
|
1482 |
*
|
1483 |
* @uses $CFG
|
1484 |
* @uses $db
|
1485 |
* @param string $timezonename ?
|
1486 |
* @return object
|
1487 |
*/
|
1488 |
function get_timezone_record($timezonename) { |
1489 |
global $CFG, $db; |
1490 |
static $cache = NULL; |
1491 |
|
1492 |
if ($cache === NULL) { |
1493 |
$cache = array(); |
1494 |
} |
1495 |
|
1496 |
if (isset($cache[$timezonename])) { |
1497 |
return $cache[$timezonename]; |
1498 |
} |
1499 |
|
1500 |
return $cache[$timezonename] = get_record_sql('SELECT * FROM '.$CFG->prefix.'timezone |
1501 |
WHERE name = '.$db->qstr($timezonename).' ORDER BY year DESC', true); |
1502 |
} |
1503 |
|
1504 |
/**
|
1505 |
* ?
|
1506 |
*
|
1507 |
* @uses $CFG
|
1508 |
* @uses $USER
|
1509 |
* @param ? $fromyear ?
|
1510 |
* @param ? $to_year ?
|
1511 |
* @return bool
|
1512 |
*/
|
1513 |
function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) { |
1514 |
global $CFG, $SESSION; |
1515 |
|
1516 |
$usertz = get_user_timezone($strtimezone); |
1517 |
|
1518 |
if (is_float($usertz)) { |
1519 |
// Trivial timezone, no DST
|
1520 |
return false; |
1521 |
} |
1522 |
|
1523 |
if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) { |
1524 |
// We have precalculated values, but the user's effective TZ has changed in the meantime, so reset
|
1525 |
unset($SESSION->dst_offsets); |
1526 |
unset($SESSION->dst_range); |
1527 |
} |
1528 |
|
1529 |
if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) { |
1530 |
// Repeat calls which do not request specific year ranges stop here, we have already calculated the table
|
1531 |
// This will be the return path most of the time, pretty light computationally
|
1532 |
return true; |
1533 |
} |
1534 |
|
1535 |
// Reaching here means we either need to extend our table or create it from scratch
|
1536 |
|
1537 |
// Remember which TZ we calculated these changes for
|
1538 |
$SESSION->dst_offsettz = $usertz; |
1539 |
|
1540 |
if(empty($SESSION->dst_offsets)) { |
1541 |
// If we 're creating from scratch, put the two guard elements in there
|
1542 |
$SESSION->dst_offsets = array(1 => NULL, 0 => NULL); |
1543 |
} |
1544 |
if(empty($SESSION->dst_range)) { |
1545 |
// If creating from scratch
|
1546 |
$from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971); |
1547 |
$to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035); |
1548 |
|
1549 |
// Fill in the array with the extra years we need to process
|
1550 |
$yearstoprocess = array(); |
1551 |
for($i = $from; $i <= $to; ++$i) { |
1552 |
$yearstoprocess[] = $i; |
1553 |
} |
1554 |
|
1555 |
// Take note of which years we have processed for future calls
|
1556 |
$SESSION->dst_range = array($from, $to); |
1557 |
} |
1558 |
else {
|
1559 |
// If needing to extend the table, do the same
|
1560 |
$yearstoprocess = array(); |
1561 |
|
1562 |
$from = max((empty($from_year) ? $SESSION->dst_range[0] : $from_year), 1971); |
1563 |
$to = min((empty($to_year) ? $SESSION->dst_range[1] : $to_year), 2035); |
1564 |
|
1565 |
if($from < $SESSION->dst_range[0]) { |
1566 |
// Take note of which years we need to process and then note that we have processed them for future calls
|
1567 |
for($i = $from; $i < $SESSION->dst_range[0]; ++$i) { |
1568 |
$yearstoprocess[] = $i; |
1569 |
} |
1570 |
$SESSION->dst_range[0] = $from; |
1571 |
} |
1572 |
if($to > $SESSION->dst_range[1]) { |
1573 |
// Take note of which years we need to process and then note that we have processed them for future calls
|
1574 |
for($i = $SESSION->dst_range[1] + 1; $i <= $to; ++$i) { |
1575 |
$yearstoprocess[] = $i; |
1576 |
} |
1577 |
$SESSION->dst_range[1] = $to; |
1578 |
} |
1579 |
} |
1580 |
|
1581 |
if(empty($yearstoprocess)) { |
1582 |
// This means that there was a call requesting a SMALLER range than we have already calculated
|
1583 |
return true; |
1584 |
} |
1585 |
|
1586 |
// From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need
|
1587 |
// Also, the array is sorted in descending timestamp order!
|
1588 |
|
1589 |
// Get DB data
|
1590 |
|
1591 |
static $presets_cache = array(); |
1592 |
if (!isset($presets_cache[$usertz])) { |
1593 |
$presets_cache[$usertz] = 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'); |
1594 |
} |
1595 |
if(empty($presets_cache[$usertz])) { |
1596 |
return false; |
1597 |
} |
1598 |
|
1599 |
// Remove ending guard (first element of the array)
|
1600 |
reset($SESSION->dst_offsets); |
1601 |
unset($SESSION->dst_offsets[key($SESSION->dst_offsets)]); |
1602 |
|
1603 |
// Add all required change timestamps
|
1604 |
foreach($yearstoprocess as $y) { |
1605 |
// Find the record which is in effect for the year $y
|
1606 |
foreach($presets_cache[$usertz] as $year => $preset) { |
1607 |
if($year <= $y) { |
1608 |
break;
|
1609 |
} |
1610 |
} |
1611 |
|
1612 |
$changes = dst_changes_for_year($y, $preset); |
1613 |
|
1614 |
if($changes === NULL) { |
1615 |
continue;
|
1616 |
} |
1617 |
if($changes['dst'] != 0) { |
1618 |
$SESSION->dst_offsets[$changes['dst']] = $preset->dstoff * MINSECS; |
1619 |
} |
1620 |
if($changes['std'] != 0) { |
1621 |
$SESSION->dst_offsets[$changes['std']] = 0; |
1622 |
} |
1623 |
} |
1624 |
|
1625 |
// Put in a guard element at the top
|
1626 |
$maxtimestamp = max(array_keys($SESSION->dst_offsets)); |
1627 |
$SESSION->dst_offsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any "small" number will do |
1628 |
|
1629 |
// Sort again
|
1630 |
krsort($SESSION->dst_offsets); |
1631 |
|
1632 |
return true; |
1633 |
} |
1634 |
|
1635 |
function dst_changes_for_year($year, $timezone) { |
1636 |
|
1637 |
if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) { |
1638 |
return NULL; |
1639 |
} |
1640 |
|
1641 |
$monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year); |
1642 |
$monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year); |
1643 |
|
1644 |
list($dst_hour, $dst_min) = explode(':', $timezone->dst_time); |
1645 |
list($std_hour, $std_min) = explode(':', $timezone->std_time); |
1646 |
|
1647 |
$timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false); |
1648 |
$timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false); |
1649 |
|
1650 |
// Instead of putting hour and minute in make_timestamp(), we add them afterwards.
|
1651 |
// This has the advantage of being able to have negative values for hour, i.e. for timezones
|
1652 |
// where GMT time would be in the PREVIOUS day than the local one on which DST changes.
|
1653 |
|
1654 |
$timedst += $dst_hour * HOURSECS + $dst_min * MINSECS; |
1655 |
$timestd += $std_hour * HOURSECS + $std_min * MINSECS; |
1656 |
|
1657 |
return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd); |
1658 |
} |
1659 |
|
1660 |
// $time must NOT be compensated at all, it has to be a pure timestamp
|
1661 |
function dst_offset_on($time, $strtimezone = NULL) { |
1662 |
global $SESSION; |
1663 |
|
1664 |
if(!calculate_user_dst_table(NULL, NULL, $strtimezone) || empty($SESSION->dst_offsets)) { |
1665 |
return 0; |
1666 |
} |
1667 |
|
1668 |
reset($SESSION->dst_offsets); |
1669 |
while(list($from, $offset) = each($SESSION->dst_offsets)) { |
1670 |
if($from <= $time) { |
1671 |
break;
|
1672 |
} |
1673 |
} |
1674 |
|
1675 |
// This is the normal return path
|
1676 |
if($offset !== NULL) { |
1677 |
return $offset; |
1678 |
} |
1679 |
|
1680 |
// Reaching this point means we haven't calculated far enough, do it now:
|
1681 |
// Calculate extra DST changes if needed and recurse. The recursion always
|
1682 |
// moves toward the stopping condition, so will always end.
|
1683 |
|
1684 |
if($from == 0) { |
1685 |
// We need a year smaller than $SESSION->dst_range[0]
|
1686 |
if($SESSION->dst_range[0] == 1971) { |
1687 |
return 0; |
1688 |
} |
1689 |
calculate_user_dst_table($SESSION->dst_range[0] - 5, NULL, $strtimezone); |
1690 |
return dst_offset_on($time, $strtimezone); |
1691 |
} |
1692 |
else {
|
1693 |
// We need a year larger than $SESSION->dst_range[1]
|
1694 |
if($SESSION->dst_range[1] == 2035) { |
1695 |
return 0; |
1696 |
} |
1697 |
calculate_user_dst_table(NULL, $SESSION->dst_range[1] + 5, $strtimezone); |
1698 |
return dst_offset_on($time, $strtimezone); |
1699 |
} |
1700 |
} |
1701 |
|
1702 |
function find_day_in_month($startday, $weekday, $month, $year) { |
1703 |
|
1704 |
$daysinmonth = days_in_month($month, $year); |
1705 |
|
1706 |
if($weekday == -1) { |
1707 |
// Don't care about weekday, so return:
|
1708 |
// abs($startday) if $startday != -1
|
1709 |
// $daysinmonth otherwise
|
1710 |
return ($startday == -1) ? $daysinmonth : abs($startday); |
1711 |
} |
1712 |
|
1713 |
// From now on we 're looking for a specific weekday
|
1714 |
|
1715 |
// Give "end of month" its actual value, since we know it
|
1716 |
if($startday == -1) { |
1717 |
$startday = -1 * $daysinmonth; |
1718 |
} |
1719 |
|
1720 |
// Starting from day $startday, the sign is the direction
|
1721 |
|
1722 |
if($startday < 1) { |
1723 |
|
1724 |
$startday = abs($startday); |
1725 |
$lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0)); |
1726 |
|
1727 |
// This is the last such weekday of the month
|
1728 |
$lastinmonth = $daysinmonth + $weekday - $lastmonthweekday; |
1729 |
if($lastinmonth > $daysinmonth) { |
1730 |
$lastinmonth -= 7; |
1731 |
} |
1732 |
|
1733 |
// Find the first such weekday <= $startday
|
1734 |
while($lastinmonth > $startday) { |
1735 |
$lastinmonth -= 7; |
1736 |
} |
1737 |
|
1738 |
return $lastinmonth; |
1739 |
|
1740 |
} |
1741 |
else {
|
1742 |
|
1743 |
$indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year, 0)); |
1744 |
|
1745 |
$diff = $weekday - $indexweekday; |
1746 |
if($diff < 0) { |
1747 |
$diff += 7; |
1748 |
} |
1749 |
|
1750 |
// This is the first such weekday of the month equal to or after $startday
|
1751 |
$firstfromindex = $startday + $diff; |
1752 |
|
1753 |
return $firstfromindex; |
1754 |
|
1755 |
} |
1756 |
} |
1757 |
|
1758 |
/**
|
1759 |
* Calculate the number of days in a given month
|
1760 |
*
|
1761 |
* @param int $month The month whose day count is sought
|
1762 |
* @param int $year The year of the month whose day count is sought
|
1763 |
* @return int
|
1764 |
*/
|
1765 |
function days_in_month($month, $year) { |
1766 |
return intval(date('t', mktime(12, 0, 0, $month, 1, $year, 0))); |
1767 |
} |
1768 |
|
1769 |
/**
|
1770 |
* Calculate the position in the week of a specific calendar day
|
1771 |
*
|
1772 |
* @param int $day The day of the date whose position in the week is sought
|
1773 |
* @param int $month The month of the date whose position in the week is sought
|
1774 |
* @param int $year The year of the date whose position in the week is sought
|
1775 |
* @return int
|
1776 |
*/
|
1777 |
function dayofweek($day, $month, $year) { |
1778 |
// I wonder if this is any different from
|
1779 |
// strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
|
1780 |
return intval(date('w', mktime(12, 0, 0, $month, $day, $year, 0))); |
1781 |
} |
1782 |
|
1783 |
/// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////
|
1784 |
|
1785 |
/**
|
1786 |
* Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
|
1787 |
* if one does not already exist, but does not overwrite existing sesskeys. Returns the
|
1788 |
* sesskey string if $USER exists, or boolean false if not.
|
1789 |
*
|
1790 |
* @uses $USER
|
1791 |
* @return string
|
1792 |
*/
|
1793 |
function sesskey() { |
1794 |
global $USER; |
1795 |
|
1796 |
if(!isset($USER)) { |
1797 |
return false; |
1798 |
} |
1799 |
|
1800 |
if (empty($USER->sesskey)) { |
1801 |
$USER->sesskey = random_string(10); |
1802 |
} |
1803 |
|
1804 |
return $USER->sesskey; |
1805 |
} |
1806 |
|
1807 |
|
1808 |
/**
|
1809 |
* For security purposes, this function will check that the currently
|
1810 |
* given sesskey (passed as a parameter to the script or this function)
|
1811 |
* matches that of the current user.
|
1812 |
*
|
1813 |
* @param string $sesskey optionally provided sesskey
|
1814 |
* @return bool
|
1815 |
*/
|
1816 |
function confirm_sesskey($sesskey=NULL) { |
1817 |
global $USER; |
1818 |
|
1819 |
if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) { |
1820 |
return true; |
1821 |
} |
1822 |
|
1823 |
if (empty($sesskey)) { |
1824 |
$sesskey = required_param('sesskey', PARAM_RAW); // Check script parameters |
1825 |
} |
1826 |
|
1827 |
if (!isset($USER->sesskey)) { |
1828 |
return false; |
1829 |
} |
1830 |
|
1831 |
return ($USER->sesskey === $sesskey); |
1832 |
} |
1833 |
|
1834 |
/**
|
1835 |
* Check the session key using {@link confirm_sesskey()},
|
1836 |
* and cause a fatal error if it does not match.
|
1837 |
*/
|
1838 |
function require_sesskey() { |
1839 |
if (!confirm_sesskey()) {
|
1840 |
print_error('invalidsesskey');
|
1841 |
} |
1842 |
} |
1843 |
|
1844 |
/**
|
1845 |
* Setup all global $CFG course variables, set locale and also themes
|
1846 |
* This function can be used on pages that do not require login instead of require_login()
|
1847 |
*
|
1848 |
* @param mixed $courseorid id of the course or course object
|
1849 |
*/
|
1850 |
function course_setup($courseorid=0) { |
1851 |
global $COURSE, $CFG, $SITE; |
1852 |
|
1853 |
/// Redefine global $COURSE if needed
|
1854 |
if (empty($courseorid)) { |
1855 |
// no change in global $COURSE - for backwards compatibiltiy
|
1856 |
// if require_rogin() used after require_login($courseid);
|
1857 |
} else if (is_object($courseorid)) { |
1858 |
$COURSE = clone($courseorid); |
1859 |
} else {
|
1860 |
global $course; // used here only to prevent repeated fetching from DB - may be removed later |
1861 |
if ($courseorid == SITEID) { |
1862 |
$COURSE = clone($SITE); |
1863 |
} else if (!empty($course->id) and $course->id == $courseorid) { |
1864 |
$COURSE = clone($course); |
1865 |
} else {
|
1866 |
if (!$COURSE = get_record('course', 'id', $courseorid)) { |
1867 |
error('Invalid course ID');
|
1868 |
} |
1869 |
} |
1870 |
} |
1871 |
|
1872 |
/// set locale and themes
|
1873 |
moodle_setlocale(); |
1874 |
theme_setup(); |
1875 |
|
1876 |
} |
1877 |
|
1878 |
/**
|
1879 |
* This function checks that the current user is logged in and has the
|
1880 |
* required privileges
|
1881 |
*
|
1882 |
* This function checks that the current user is logged in, and optionally
|
1883 |
* whether they are allowed to be in a particular course and view a particular
|
1884 |
* course module.
|
1885 |
* If they are not logged in, then it redirects them to the site login unless
|
1886 |
* $autologinguest is set and {@link $CFG}->autologinguests is set to 1 in which
|
1887 |
* case they are automatically logged in as guests.
|
1888 |
* If $courseid is given and the user is not enrolled in that course then the
|
1889 |
* user is redirected to the course enrolment page.
|
1890 |
* If $cm is given and the coursemodule is hidden and the user is not a teacher
|
1891 |
* in the course then the user is redirected to the course home page.
|
1892 |
*
|
1893 |
* @uses $CFG
|
1894 |
* @uses $SESSION
|
1895 |
* @uses $USER
|
1896 |
* @uses $FULLME
|
1897 |
* @uses SITEID
|
1898 |
* @uses $COURSE
|
1899 |
* @param mixed $courseorid id of the course or course object
|
1900 |
* @param bool $autologinguest
|
1901 |
* @param object $cm course module object
|
1902 |
* @param bool $setwantsurltome Define if we want to set $SESSION->wantsurl, defaults to
|
1903 |
* true. Used to avoid (=false) some scripts (file.php...) to set that variable,
|
1904 |
* in order to keep redirects working properly. MDL-14495
|
1905 |
*/
|
1906 |
function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsurltome=true) { |
1907 |
|
1908 |
global $CFG, $SESSION, $USER, $COURSE, $FULLME; |
1909 |
|
1910 |
/// setup global $COURSE, themes, language and locale
|
1911 |
course_setup($courseorid);
|
1912 |
|
1913 |
/// If the user is not even logged in yet then make sure they are
|
1914 |
if (!isloggedin()) {
|
1915 |
//NOTE: $USER->site check was obsoleted by session test cookie,
|
1916 |
// $USER->confirmed test is in login/index.php
|
1917 |
if ($setwantsurltome) { |
1918 |
$SESSION->wantsurl = $FULLME; |
1919 |
} |
1920 |
if (!empty($_SERVER['HTTP_REFERER'])) { |
1921 |
$SESSION->fromurl = $_SERVER['HTTP_REFERER']; |
1922 |
} |
1923 |
if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests) and ($COURSE->id == SITEID or $COURSE->guest) ) { |
1924 |
$loginguest = '?loginguest=true'; |
1925 |
} else {
|
1926 |
$loginguest = ''; |
1927 |
} |
1928 |
if (empty($CFG->loginhttps) or $loginguest) { //do not require https for guest logins |
1929 |
redirect($CFG->wwwroot .'/login/index.php'. $loginguest); |
1930 |
} else {
|
1931 |
$wwwroot = str_replace('http:','https:', $CFG->wwwroot); |
1932 |
redirect($wwwroot .'/login/index.php'); |
1933 |
} |
1934 |
exit;
|
1935 |
} |
1936 |
|
1937 |
/// loginas as redirection if needed
|
1938 |
if ($COURSE->id != SITEID and !empty($USER->realuser)) { |
1939 |
if ($USER->loginascontext->contextlevel == CONTEXT_COURSE) { |
1940 |
if ($USER->loginascontext->instanceid != $COURSE->id) { |
1941 |
print_error('loginasonecourse', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid); |
1942 |
} |
1943 |
} |
1944 |
} |
1945 |
|
1946 |
/// check whether the user should be changing password (but only if it is REALLY them)
|
1947 |
if (get_user_preferences('auth_forcepasswordchange') && empty($USER->realuser)) { |
1948 |
$userauth = get_auth_plugin($USER->auth); |
1949 |
if ($userauth->can_change_password()) { |
1950 |
$SESSION->wantsurl = $FULLME; |
1951 |
if ($changeurl = $userauth->change_password_url()) { |
1952 |
//use plugin custom url
|
1953 |
redirect($changeurl);
|
1954 |
} else {
|
1955 |
//use moodle internal method
|
1956 |
if (empty($CFG->loginhttps)) { |
1957 |
redirect($CFG->wwwroot .'/login/change_password.php'); |
1958 |
} else {
|
1959 |
$wwwroot = str_replace('http:','https:', $CFG->wwwroot); |
1960 |
redirect($wwwroot .'/login/change_password.php'); |
1961 |
} |
1962 |
} |
1963 |
} else {
|
1964 |
print_error('nopasswordchangeforced', 'auth'); |
1965 |
} |
1966 |
} |
1967 |
|
1968 |
/// Check that the user account is properly set up
|
1969 |
if (user_not_fully_set_up($USER)) { |
1970 |
$SESSION->wantsurl = $FULLME; |
1971 |
redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&course='. SITEID); |
1972 |
} |
1973 |
|
1974 |
/// Make sure current IP matches the one for this session (if required)
|
1975 |
if (!empty($CFG->tracksessionip)) { |
1976 |
if ($USER->sessionIP != md5(getremoteaddr())) { |
1977 |
print_error('sessionipnomatch', 'error'); |
1978 |
} |
1979 |
} |
1980 |
|
1981 |
/// Make sure the USER has a sesskey set up. Used for checking script parameters.
|
1982 |
sesskey(); |
1983 |
|
1984 |
// Check that the user has agreed to a site policy if there is one
|
1985 |
if (!empty($CFG->sitepolicy)) { |
1986 |
if (!$USER->policyagreed) { |
1987 |
$SESSION->wantsurl = $FULLME; |
1988 |
redirect($CFG->wwwroot .'/user/policy.php'); |
1989 |
} |
1990 |
} |
1991 |
|
1992 |
// Fetch the system context, we are going to use it a lot.
|
1993 |
$sysctx = get_context_instance(CONTEXT_SYSTEM); |
1994 |
|
1995 |
/// If the site is currently under maintenance, then print a message
|
1996 |
if (!has_capability('moodle/site:config', $sysctx)) { |
1997 |
if (file_exists($CFG->dataroot.'/'.SITEID.'/maintenance.html')) { |
1998 |
print_maintenance_message(); |
1999 |
exit;
|
2000 |
} |
2001 |
} |
2002 |
|
2003 |
/// groupmembersonly access control
|
2004 |
if (!empty($CFG->enablegroupings) and $cm and $cm->groupmembersonly and !has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) { |
2005 |
if (isguestuser() or !groups_has_membership($cm)) { |
2006 |
print_error('groupmembersonlyerror', 'group', $CFG->wwwroot.'/course/view.php?id='.$cm->course); |
2007 |
} |
2008 |
} |
2009 |
|
2010 |
// Fetch the course context, and prefetch its child contexts
|
2011 |
if (!isset($COURSE->context)) { |
2012 |
if ( ! $COURSE->context = get_context_instance(CONTEXT_COURSE, $COURSE->id) ) { |
2013 |
print_error('nocontext');
|
2014 |
} |
2015 |
} |
2016 |
if (!empty($cm) && !isset($cm->context)) { |
2017 |
if ( ! $cm->context = get_context_instance(CONTEXT_MODULE, $cm->id) ) { |
2018 |
print_error('nocontext');
|
2019 |
} |
2020 |
} |
2021 |
if ($COURSE->id == SITEID) { |
2022 |
/// Eliminate hidden site activities straight away
|
2023 |
if (!empty($cm) && !$cm->visible |
2024 |
&& !has_capability('moodle/course:viewhiddenactivities', $cm->context)) { |
2025 |
redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden')); |
2026 |
} |
2027 |
user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times |
2028 |
return;
|
2029 |
|
2030 |
} else {
|
2031 |
|
2032 |
/// Check if the user can be in a particular course
|
2033 |
if (empty($USER->access['rsw'][$COURSE->context->path])) { |
2034 |
//
|
2035 |
// MDL-13900 - If the course or the parent category are hidden
|
2036 |
// and the user hasn't the 'course:viewhiddencourses' capability, prevent access
|
2037 |
//
|
2038 |
if ( !($COURSE->visible && course_parent_visible($COURSE)) && |
2039 |
!has_capability('moodle/course:viewhiddencourses', $COURSE->context)) { |
2040 |
print_header_simple(); |
2041 |
notice(get_string('coursehidden'), $CFG->wwwroot .'/'); |
2042 |
} |
2043 |
} |
2044 |
|
2045 |
/// Non-guests who don't currently have access, check if they can be allowed in as a guest
|
2046 |
|
2047 |
if ($USER->username != 'guest' and !has_capability('moodle/course:view', $COURSE->context)) { |
2048 |
if ($COURSE->guest == 1) { |
2049 |
// Temporarily assign them guest role for this context, if it fails later user is asked to enrol
|
2050 |
$USER->access = load_temp_role($COURSE->context, $CFG->guestroleid, $USER->access); |
2051 |
} |
2052 |
} |
2053 |
|
2054 |
/// If the user is a guest then treat them according to the course policy about guests
|
2055 |
|
2056 |
if (has_capability('moodle/legacy:guest', $COURSE->context, NULL, false)) { |
2057 |
if (has_capability('moodle/site:doanything', $sysctx)) { |
2058 |
// administrators must be able to access any course - even if somebody gives them guest access
|
2059 |
user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times |
2060 |
return;
|
2061 |
} |
2062 |
|
2063 |
switch ($COURSE->guest) { /// Check course policy about guest access |
2064 |
|
2065 |
case 1: /// Guests always allowed |
2066 |
if (!has_capability('moodle/course:view', $COURSE->context)) { // Prohibited by capability |
2067 |
print_header_simple(); |
2068 |
notice(get_string('guestsnotallowed', '', format_string($COURSE->fullname)), "$CFG->wwwroot/login/index.php"); |
2069 |
} |
2070 |
if (!empty($cm) and !$cm->visible) { // Not allowed to see module, send to course page |
2071 |
redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, |
2072 |
get_string('activityiscurrentlyhidden'));
|
2073 |
} |
2074 |
|
2075 |
user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times |
2076 |
return; // User is allowed to see this course |
2077 |
|
2078 |
break;
|
2079 |
|
2080 |
case 2: /// Guests allowed with key |
2081 |
if (!empty($USER->enrolkey[$COURSE->id])) { // Set by enrol/manual/enrol.php |
2082 |
user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times |
2083 |
return true; |
2084 |
} |
2085 |
// otherwise drop through to logic below (--> enrol.php)
|
2086 |
break;
|
2087 |
|
2088 |
default: /// Guests not allowed |
2089 |
$strloggedinasguest = get_string('loggedinasguest'); |
2090 |
print_header_simple('', '', |
2091 |
build_navigation(array(array('name' => $strloggedinasguest, 'link' => null, 'type' => 'misc')))); |
2092 |
if (empty($USER->access['rsw'][$COURSE->context->path])) { // Normal guest |
2093 |
notice(get_string('guestsnotallowed', '', format_string($COURSE->fullname)), "$CFG->wwwroot/login/index.php"); |
2094 |
} else {
|
2095 |
notify(get_string('guestsnotallowed', '', format_string($COURSE->fullname))); |
2096 |
echo '<div class="notifyproblem">'.switchroles_form($COURSE->id).'</div>'; |
2097 |
print_footer($COURSE);
|
2098 |
exit;
|
2099 |
} |
2100 |
break;
|
2101 |
} |
2102 |
|
2103 |
/// For non-guests, check if they have course view access
|
2104 |
|
2105 |
} else if (has_capability('moodle/course:view', $COURSE->context)) { |
2106 |
if (!empty($USER->realuser)) { // Make sure the REAL person can also access this course |
2107 |
if (!has_capability('moodle/course:view', $COURSE->context, $USER->realuser)) { |
2108 |
print_header_simple(); |
2109 |
notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/'); |
2110 |
} |
2111 |
} |
2112 |
|
2113 |
/// Make sure they can read this activity too, if specified
|
2114 |
|
2115 |
if (!empty($cm) && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $cm->context)) { |
2116 |
redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden')); |
2117 |
} |
2118 |
user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times |
2119 |
return; // User is allowed to see this course |
2120 |
|
2121 |
} |
2122 |
|
2123 |
|
2124 |
/// Currently not enrolled in the course, so see if they want to enrol
|
2125 |
$SESSION->wantsurl = $FULLME; |
2126 |
redirect($CFG->wwwroot .'/course/enrol.php?id='. $COURSE->id); |
2127 |
die;
|
2128 |
} |
2129 |
} |
2130 |
|
2131 |
|
2132 |
|
2133 |
/**
|
2134 |
* This function just makes sure a user is logged out.
|
2135 |
*
|
2136 |
* @uses $CFG
|
2137 |
* @uses $USER
|
2138 |
*/
|
2139 |
function require_logout() { |
2140 |
|
2141 |
global $USER, $CFG, $SESSION; |
2142 |
|
2143 |
if (isloggedin()) {
|
2144 |
add_to_log(SITEID, "user", "logout", "view.php?id=$USER->id&course=".SITEID, $USER->id, 0, $USER->id); |
2145 |
|
2146 |
$authsequence = get_enabled_auth_plugins(); // auths, in sequence |
2147 |
foreach($authsequence as $authname) { |
2148 |
$authplugin = get_auth_plugin($authname); |
2149 |
$authplugin->prelogout_hook();
|
2150 |
} |
2151 |
} |
2152 |
|
2153 |
if (ini_get_bool("register_globals") and check_php_version("4.3.0")) { |
2154 |
// This method is just to try to avoid silly warnings from PHP 4.3.0
|
2155 |
session_unregister("USER");
|
2156 |
session_unregister("SESSION");
|
2157 |
} |
2158 |
|
2159 |
// Initialize variable to pass-by-reference to headers_sent(&$file, &$line)
|
2160 |
$file = $line = null; |
2161 |
if (headers_sent($file, $line)) { |
2162 |
error_log('MoodleSessionTest cookie could not be set in moodlelib.php:'.__LINE__); |
2163 |
error_log('Headers were already sent in file: '.$file.' on line '.$line); |
2164 |
} else {
|
2165 |
if (check_php_version('5.2.0')) { |
2166 |
setcookie('MoodleSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure, $CFG->cookiehttponly); |
2167 |
} else {
|
2168 |
setcookie('MoodleSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure); |
2169 |
} |
2170 |
} |
2171 |
|
2172 |
unset($_SESSION['USER']); |
2173 |
unset($_SESSION['SESSION']); |
2174 |
|
2175 |
unset($SESSION); |
2176 |
unset($USER); |
2177 |
|
2178 |
} |
2179 |
|
2180 |
/**
|
2181 |
* This is a weaker version of {@link require_login()} which only requires login
|
2182 |
* when called from within a course rather than the site page, unless
|
2183 |
* the forcelogin option is turned on.
|
2184 |
*
|
2185 |
* @uses $CFG
|
2186 |
* @param mixed $courseorid The course object or id in question
|
2187 |
* @param bool $autologinguest Allow autologin guests if that is wanted
|
2188 |
* @param object $cm Course activity module if known
|
2189 |
* @param bool $setwantsurltome Define if we want to set $SESSION->wantsurl, defaults to
|
2190 |
* true. Used to avoid (=false) some scripts (file.php...) to set that variable,
|
2191 |
* in order to keep redirects working properly. MDL-14495
|
2192 |
*/
|
2193 |
function require_course_login($courseorid, $autologinguest=true, $cm=null, $setwantsurltome=true) { |
2194 |
global $CFG; |
2195 |
if (!empty($CFG->forcelogin)) { |
2196 |
// login required for both SITE and courses
|
2197 |
require_login($courseorid, $autologinguest, $cm, $setwantsurltome); |
2198 |
|
2199 |
} else if (!empty($cm) and !$cm->visible) { |
2200 |
// always login for hidden activities
|
2201 |
require_login($courseorid, $autologinguest, $cm, $setwantsurltome); |
2202 |
|
2203 |
} else if ((is_object($courseorid) and $courseorid->id == SITEID) |
2204 |
or (!is_object($courseorid) and $courseorid == SITEID)) { |
2205 |
//login for SITE not required
|
2206 |
if ($cm and empty($cm->visible)) { |
2207 |
// hidden activities are not accessible without login
|
2208 |
require_login($courseorid, $autologinguest, $cm, $setwantsurltome); |
2209 |
} else if ($cm and !empty($CFG->enablegroupings) and $cm->groupmembersonly) { |
2210 |
// not-logged-in users do not have any group membership
|
2211 |
require_login($courseorid, $autologinguest, $cm, $setwantsurltome); |
2212 |
} else {
|
2213 |
user_accesstime_log(SITEID);
|
2214 |
return;
|
2215 |
} |
2216 |
|
2217 |
} else {
|
2218 |
// course login always required
|
2219 |
require_login($courseorid, $autologinguest, $cm, $setwantsurltome); |
2220 |
} |
2221 |
} |
2222 |
|
2223 |
/**
|
2224 |
* Require key login. Function terminates with error if key not found or incorrect.
|
2225 |
* @param string $script unique script identifier
|
2226 |
* @param int $instance optional instance id
|
2227 |
*/
|
2228 |
function require_user_key_login($script, $instance=null) { |
2229 |
global $nomoodlecookie, $USER, $SESSION, $CFG; |
2230 |
|
2231 |
if (empty($nomoodlecookie)) { |
2232 |
error('Incorrect use of require_key_login() - session cookies must be disabled!');
|
2233 |
} |
2234 |
|
2235 |
/// extra safety
|
2236 |
@session_write_close();
|
2237 |
|
2238 |
$keyvalue = required_param('key', PARAM_ALPHANUM); |
2239 |
|
2240 |
if (!$key = get_record('user_private_key', 'script', $script, 'value', $keyvalue, 'instance', $instance)) { |
2241 |
error('Incorrect key');
|
2242 |
} |
2243 |
|
2244 |
if (!empty($key->validuntil) and $key->validuntil < time()) { |
2245 |
error('Expired key');
|
2246 |
} |
2247 |
|
2248 |
if ($key->iprestriction) { |
2249 |
$remoteaddr = getremoteaddr();
|
2250 |
if ($remoteaddr == '' or !address_in_subnet($remoteaddr, $key->iprestriction)) { |
2251 |
error('Client IP address mismatch');
|
2252 |
} |
2253 |
} |
2254 |
|
2255 |
if (!$user = get_record('user', 'id', $key->userid)) { |
2256 |
error('Incorrect user record');
|
2257 |
} |
2258 |
|
2259 |
/// emulate normal session
|
2260 |
$SESSION = new object(); |
2261 |
$USER = $user; |
2262 |
|
2263 |
/// note we are not using normal login
|
2264 |
if (!defined('USER_KEY_LOGIN')) { |
2265 |
define('USER_KEY_LOGIN', true); |
2266 |
} |
2267 |
|
2268 |
load_all_capabilities(); |
2269 |
|
2270 |
/// return isntance id - it might be empty
|
2271 |
return $key->instance; |
2272 |
} |
2273 |
|
2274 |
/**
|
2275 |
* Creates a new private user access key.
|
2276 |
* @param string $script unique target identifier
|
2277 |
* @param int $userid
|
2278 |
* @param instance $int optional instance id
|
2279 |
* @param string $iprestriction optional ip restricted access
|
2280 |
* @param timestamp $validuntil key valid only until given data
|
2281 |
* @return string access key value
|
2282 |
*/
|
2283 |
function create_user_key($script, $userid, $instance=null, $iprestriction=null, $validuntil=null) { |
2284 |
$key = new object(); |
2285 |
$key->script = $script; |
2286 |
$key->userid = $userid; |
2287 |
$key->instance = $instance; |
2288 |
$key->iprestriction = $iprestriction; |
2289 |
$key->validuntil = $validuntil; |
2290 |
$key->timecreated = time(); |
2291 |
|
2292 |
$key->value = md5($userid.'_'.time().random_string(40)); // something long and unique |
2293 |
while (record_exists('user_private_key', 'value', $key->value)) { |
2294 |
// must be unique
|
2295 |
$key->value = md5($userid.'_'.time().random_string(40)); |
2296 |
} |
2297 |
|
2298 |
if (!insert_record('user_private_key', $key)) { |
2299 |
error('Can not insert new key');
|
2300 |
} |
2301 |
|
2302 |
return $key->value; |
2303 |
} |
2304 |
|
2305 |
/**
|
2306 |
* Modify the user table by setting the currently logged in user's
|
2307 |
* last login to now.
|
2308 |
*
|
2309 |
* @uses $USER
|
2310 |
* @return bool
|
2311 |
*/
|
2312 |
function update_user_login_times() { |
2313 |
global $USER; |
2314 |
|
2315 |
$user = new object(); |
2316 |
$USER->lastlogin = $user->lastlogin = $USER->currentlogin; |
2317 |
$USER->currentlogin = $user->lastaccess = $user->currentlogin = time(); |
2318 |
|
2319 |
$user->id = $USER->id; |
2320 |
|
2321 |
return update_record('user', $user); |
2322 |
} |
2323 |
|
2324 |
/**
|
2325 |
* Determines if a user has completed setting up their account.
|
2326 |
*
|
2327 |
* @param user $user A {@link $USER} object to test for the existance of a valid name and email
|
2328 |
* @return bool
|
2329 |
*/
|
2330 |
function user_not_fully_set_up($user) { |
2331 |
return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user))); |
2332 |
} |
2333 |
|
2334 |
function over_bounce_threshold($user) { |
2335 |
|
2336 |
global $CFG; |
2337 |
|
2338 |
if (empty($CFG->handlebounces)) { |
2339 |
return false; |
2340 |
} |
2341 |
|
2342 |
if (empty($user->id)) { /// No real (DB) user, nothing to do here. |
2343 |
return false; |
2344 |
} |
2345 |
|
2346 |
// set sensible defaults
|
2347 |
if (empty($CFG->minbounces)) { |
2348 |
$CFG->minbounces = 10; |
2349 |
} |
2350 |
if (empty($CFG->bounceratio)) { |
2351 |
$CFG->bounceratio = .20; |
2352 |
} |
2353 |
$bouncecount = 0; |
2354 |
$sendcount = 0; |
2355 |
if ($bounce = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) { |
2356 |
$bouncecount = $bounce->value; |
2357 |
} |
2358 |
if ($send = get_record('user_preferences','userid',$user->id,'name','email_send_count')) { |
2359 |
$sendcount = $send->value; |
2360 |
} |
2361 |
return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio); |
2362 |
} |
2363 |
|
2364 |
/**
|
2365 |
* @param $user - object containing an id
|
2366 |
* @param $reset - will reset the count to 0
|
2367 |
*/
|
2368 |
function set_send_count($user,$reset=false) { |
2369 |
|
2370 |
if (empty($user->id)) { /// No real (DB) user, nothing to do here. |
2371 |
return;
|
2372 |
} |
2373 |
|
2374 |
if ($pref = get_record('user_preferences','userid',$user->id,'name','email_send_count')) { |
2375 |
$pref->value = (!empty($reset)) ? 0 : $pref->value+1; |
2376 |
update_record('user_preferences',$pref); |
2377 |
} |
2378 |
else if (!empty($reset)) { // if it's not there and we're resetting, don't bother. |
2379 |
// make a new one
|
2380 |
$pref->name = 'email_send_count'; |
2381 |
$pref->value = 1; |
2382 |
$pref->userid = $user->id; |
2383 |
insert_record('user_preferences',$pref, false); |
2384 |
} |
2385 |
} |
2386 |
|
2387 |
/**
|
2388 |
* @param $user - object containing an id
|
2389 |
* @param $reset - will reset the count to 0
|
2390 |
*/
|
2391 |
function set_bounce_count($user,$reset=false) { |
2392 |
if ($pref = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) { |
2393 |
$pref->value = (!empty($reset)) ? 0 : $pref->value+1; |
2394 |
update_record('user_preferences',$pref); |
2395 |
} |
2396 |
else if (!empty($reset)) { // if it's not there and we're resetting, don't bother. |
2397 |
// make a new one
|
2398 |
$pref->name = 'email_bounce_count'; |
2399 |
$pref->value = 1; |
2400 |
$pref->userid = $user->id; |
2401 |
insert_record('user_preferences',$pref, false); |
2402 |
} |
2403 |
} |
2404 |
|
2405 |
/**
|
2406 |
* Keeps track of login attempts
|
2407 |
*
|
2408 |
* @uses $SESSION
|
2409 |
*/
|
2410 |
function update_login_count() { |
2411 |
|
2412 |
global $SESSION; |
2413 |
|
2414 |
$max_logins = 10; |
2415 |
|
2416 |
if (empty($SESSION->logincount)) { |
2417 |
$SESSION->logincount = 1; |
2418 |
} else {
|
2419 |
$SESSION->logincount++;
|
2420 |
} |
2421 |
|
2422 |
if ($SESSION->logincount > $max_logins) { |
2423 |
unset($SESSION->wantsurl); |
2424 |
print_error('errortoomanylogins');
|
2425 |
} |
2426 |
} |
2427 |
|
2428 |
/**
|
2429 |
* Resets login attempts
|
2430 |
*
|
2431 |
* @uses $SESSION
|
2432 |
*/
|
2433 |
function reset_login_count() { |
2434 |
global $SESSION; |
2435 |
|
2436 |
$SESSION->logincount = 0; |
2437 |
} |
2438 |
|
2439 |
function sync_metacourses() { |
2440 |
|
2441 |
global $CFG; |
2442 |
|
2443 |
if (!$courses = get_records('course', 'metacourse', 1)) { |
2444 |
return;
|
2445 |
} |
2446 |
|
2447 |
foreach ($courses as $course) { |
2448 |
sync_metacourse($course);
|
2449 |
} |
2450 |
} |
2451 |
|
2452 |
/**
|
2453 |
* Goes through all enrolment records for the courses inside the metacourse and sync with them.
|
2454 |
*
|
2455 |
* @param mixed $course the metacourse to synch. Either the course object itself, or the courseid.
|
2456 |
*/
|
2457 |
function sync_metacourse($course) { |
2458 |
global $CFG; |
2459 |
|
2460 |
// Check the course is valid.
|
2461 |
if (!is_object($course)) { |
2462 |
if (!$course = get_record('course', 'id', $course)) { |
2463 |
return false; // invalid course id |
2464 |
} |
2465 |
} |
2466 |
|
2467 |
// Check that we actually have a metacourse.
|
2468 |
if (empty($course->metacourse)) { |
2469 |
return false; |
2470 |
} |
2471 |
|
2472 |
// Get a list of roles that should not be synced.
|
2473 |
if (!empty($CFG->nonmetacoursesyncroleids)) { |
2474 |
$roleexclusions = 'ra.roleid NOT IN (' . $CFG->nonmetacoursesyncroleids . ') AND'; |
2475 |
} else {
|
2476 |
$roleexclusions = ''; |
2477 |
} |
2478 |
|
2479 |
// Get the context of the metacourse.
|
2480 |
$context = get_context_instance(CONTEXT_COURSE, $course->id); // SITEID can not be a metacourse |
2481 |
|
2482 |
// We do not ever want to unassign the list of metacourse manager, so get a list of them.
|
2483 |
if ($users = get_users_by_capability($context, 'moodle/course:managemetacourse')) { |
2484 |
$managers = array_keys($users); |
2485 |
} else {
|
2486 |
$managers = array(); |
2487 |
} |
2488 |
|
2489 |
// Get assignments of a user to a role that exist in a child course, but
|
2490 |
// not in the meta coure. That is, get a list of the assignments that need to be made.
|
2491 |
if (!$assignments = get_records_sql(" |
2492 |
SELECT
|
2493 |
ra.id, ra.roleid, ra.userid, ra.hidden
|
2494 |
FROM
|
2495 |
{$CFG->prefix}role_assignments ra,
|
2496 |
{$CFG->prefix}context con,
|
2497 |
{$CFG->prefix}course_meta cm
|
2498 |
WHERE
|
2499 |
ra.contextid = con.id AND
|
2500 |
con.contextlevel = " . CONTEXT_COURSE . " AND |
2501 |
con.instanceid = cm.child_course AND
|
2502 |
cm.parent_course = {$course->id} AND
|
2503 |
$roleexclusions
|
2504 |
NOT EXISTS (
|
2505 |
SELECT 1 FROM
|
2506 |
{$CFG->prefix}role_assignments ra2
|
2507 |
WHERE
|
2508 |
ra2.userid = ra.userid AND
|
2509 |
ra2.roleid = ra.roleid AND
|
2510 |
ra2.contextid = {$context->id}
|
2511 |
)
|
2512 |
")) {
|
2513 |
$assignments = array(); |
2514 |
} |
2515 |
|
2516 |
// Get assignments of a user to a role that exist in the meta course, but
|
2517 |
// not in any child courses. That is, get a list of the unassignments that need to be made.
|
2518 |
if (!$unassignments = get_records_sql(" |
2519 |
SELECT
|
2520 |
ra.id, ra.roleid, ra.userid
|
2521 |
FROM
|
2522 |
{$CFG->prefix}role_assignments ra
|
2523 |
WHERE
|
2524 |
ra.contextid = {$context->id} AND
|
2525 |
$roleexclusions
|
2526 |
NOT EXISTS (
|
2527 |
SELECT 1 FROM
|
2528 |
{$CFG->prefix}role_assignments ra2,
|
2529 |
{$CFG->prefix}context con2,
|
2530 |
{$CFG->prefix}course_meta cm
|
2531 |
WHERE
|
2532 |
ra2.userid = ra.userid AND
|
2533 |
ra2.roleid = ra.roleid AND
|
2534 |
ra2.contextid = con2.id AND
|
2535 |
con2.contextlevel = " . CONTEXT_COURSE . " AND |
2536 |
con2.instanceid = cm.child_course AND
|
2537 |
cm.parent_course = {$course->id}
|
2538 |
)
|
2539 |
")) {
|
2540 |
$unassignments = array(); |
2541 |
} |
2542 |
|
2543 |
$success = true; |
2544 |
|
2545 |
// Make the unassignments, if they are not managers.
|
2546 |
foreach ($unassignments as $unassignment) { |
2547 |
if (!in_array($unassignment->userid, $managers)) { |
2548 |
$success = role_unassign($unassignment->roleid, $unassignment->userid, 0, $context->id) && $success; |
2549 |
} |
2550 |
} |
2551 |
|
2552 |
// Make the assignments.
|
2553 |
foreach ($assignments as $assignment) { |
2554 |
$success = role_assign($assignment->roleid, $assignment->userid, 0, $context->id, 0, 0, $assignment->hidden) && $success; |
2555 |
} |
2556 |
|
2557 |
return $success; |
2558 |
|
2559 |
// TODO: finish timeend and timestart
|
2560 |
// maybe we could rely on cron job to do the cleaning from time to time
|
2561 |
} |
2562 |
|
2563 |
/**
|
2564 |
* Adds a record to the metacourse table and calls sync_metacoures
|
2565 |
*/
|
2566 |
function add_to_metacourse ($metacourseid, $courseid) { |
2567 |
|
2568 |
if (!$metacourse = get_record("course","id",$metacourseid)) { |
2569 |
return false; |
2570 |
} |
2571 |
|
2572 |
if (!$course = get_record("course","id",$courseid)) { |
2573 |
return false; |
2574 |
} |
2575 |
|
2576 |
if (!$record = get_record("course_meta","parent_course",$metacourseid,"child_course",$courseid)) { |
2577 |
$rec = new object(); |
2578 |
$rec->parent_course = $metacourseid; |
2579 |
$rec->child_course = $courseid; |
2580 |
if (!insert_record('course_meta',$rec)) { |
2581 |
return false; |
2582 |
} |
2583 |
return sync_metacourse($metacourseid); |
2584 |
} |
2585 |
return true; |
2586 |
|
2587 |
} |
2588 |
|
2589 |
/**
|
2590 |
* Removes the record from the metacourse table and calls sync_metacourse
|
2591 |
*/
|
2592 |
function remove_from_metacourse($metacourseid, $courseid) { |
2593 |
|
2594 |
if (delete_records('course_meta','parent_course',$metacourseid,'child_course',$courseid)) { |
2595 |
return sync_metacourse($metacourseid); |
2596 |
} |
2597 |
return false; |
2598 |
} |
2599 |
|
2600 |
|
2601 |
/**
|
2602 |
* Determines if a user is currently logged in
|
2603 |
*
|
2604 |
* @uses $USER
|
2605 |
* @return bool
|
2606 |
*/
|
2607 |
function isloggedin() { |
2608 |
global $USER; |
2609 |
|
2610 |
return (!empty($USER->id)); |
2611 |
} |
2612 |
|
2613 |
/**
|
2614 |
* Determines if a user is logged in as real guest user with username 'guest'.
|
2615 |
* This function is similar to original isguest() in 1.6 and earlier.
|
2616 |
* Current isguest() is deprecated - do not use it anymore.
|
2617 |
*
|
2618 |
* @param $user mixed user object or id, $USER if not specified
|
2619 |
* @return bool true if user is the real guest user, false if not logged in or other user
|
2620 |
*/
|
2621 |
function isguestuser($user=NULL) { |
2622 |
global $USER; |
2623 |
if ($user === NULL) { |
2624 |
$user = $USER; |
2625 |
} else if (is_numeric($user)) { |
2626 |
$user = get_record('user', 'id', $user, '', '', '', '', 'id, username'); |
2627 |
} |
2628 |
|
2629 |
if (empty($user->id)) { |
2630 |
return false; // not logged in, can not be guest |
2631 |
} |
2632 |
|
2633 |
return ($user->username == 'guest'); |
2634 |
} |
2635 |
|
2636 |
/**
|
2637 |
* Determines if the currently logged in user is in editing mode.
|
2638 |
* Note: originally this function had $userid parameter - it was not usable anyway
|
2639 |
*
|
2640 |
* @uses $USER, $PAGE
|
2641 |
* @return bool
|
2642 |
*/
|
2643 |
function isediting() { |
2644 |
global $USER, $PAGE; |
2645 |
|
2646 |
if (empty($USER->editing)) { |
2647 |
return false; |
2648 |
} elseif (is_object($PAGE) && method_exists($PAGE,'user_allowed_editing')) { |
2649 |
return $PAGE->user_allowed_editing(); |
2650 |
} |
2651 |
return true;//false; |
2652 |
} |
2653 |
|
2654 |
/**
|
2655 |
* Determines if the logged in user is currently moving an activity
|
2656 |
*
|
2657 |
* @uses $USER
|
2658 |
* @param int $courseid The id of the course being tested
|
2659 |
* @return bool
|
2660 |
*/
|
2661 |
function ismoving($courseid) { |
2662 |
global $USER; |
2663 |
|
2664 |
if (!empty($USER->activitycopy)) { |
2665 |
return ($USER->activitycopycourse == $courseid); |
2666 |
} |
2667 |
return false; |
2668 |
} |
2669 |
|
2670 |
/**
|
2671 |
* Given an object containing firstname and lastname
|
2672 |
* values, this function returns a string with the
|
2673 |
* full name of the person.
|
2674 |
* The result may depend on system settings
|
2675 |
* or language. 'override' will force both names
|
2676 |
* to be used even if system settings specify one.
|
2677 |
*
|
2678 |
* @uses $CFG
|
2679 |
* @uses $SESSION
|
2680 |
* @param object $user A {@link $USER} object to get full name of
|
2681 |
* @param bool $override If true then the name will be first name followed by last name rather than adhering to fullnamedisplay setting.
|
2682 |
*/
|
2683 |
function fullname($user, $override=false) { |
2684 |
|
2685 |
global $CFG, $SESSION; |
2686 |
|
2687 |
if (!isset($user->firstname) and !isset($user->lastname)) { |
2688 |
return ''; |
2689 |
} |
2690 |
|
2691 |
if (!$override) { |
2692 |
if (!empty($CFG->forcefirstname)) { |
2693 |
$user->firstname = $CFG->forcefirstname; |
2694 |
} |
2695 |
if (!empty($CFG->forcelastname)) { |
2696 |
$user->lastname = $CFG->forcelastname; |
2697 |
} |
2698 |
} |
2699 |
|
2700 |
if (!empty($SESSION->fullnamedisplay)) { |
2701 |
$CFG->fullnamedisplay = $SESSION->fullnamedisplay; |
2702 |
} |
2703 |
|
2704 |
if ($CFG->fullnamedisplay == 'firstname lastname') { |
2705 |
return $user->firstname .' '. $user->lastname; |
2706 |
|
2707 |
} else if ($CFG->fullnamedisplay == 'lastname firstname') { |
2708 |
return $user->lastname .' '. $user->firstname; |
2709 |
|
2710 |
} else if ($CFG->fullnamedisplay == 'firstname') { |
2711 |
if ($override) { |
2712 |
return get_string('fullnamedisplay', '', $user); |
2713 |
} else {
|
2714 |
return $user->firstname; |
2715 |
} |
2716 |
} |
2717 |
|
2718 |
return get_string('fullnamedisplay', '', $user); |
2719 |
} |
2720 |
|
2721 |
/**
|
2722 |
* Sets a moodle cookie with an encrypted string
|
2723 |
*
|
2724 |
* @uses $CFG
|
2725 |
* @uses DAYSECS
|
2726 |
* @uses HOURSECS
|
2727 |
* @param string $thing The string to encrypt and place in a cookie
|
2728 |
*/
|
2729 |
function set_moodle_cookie($thing) { |
2730 |
global $CFG; |
2731 |
|
2732 |
if ($thing == 'guest') { // Ignore guest account |
2733 |
return;
|
2734 |
} |
2735 |
|
2736 |
$cookiename = 'MOODLEID_'.$CFG->sessioncookie; |
2737 |
|
2738 |
$days = 60; |
2739 |
$seconds = DAYSECS*$days; |
2740 |
|
2741 |
setCookie($cookiename, '', time() - HOURSECS, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure); |
2742 |
setCookie($cookiename, rc4encrypt($thing), time()+$seconds, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure); |
2743 |
} |
2744 |
|
2745 |
/**
|
2746 |
* Gets a moodle cookie with an encrypted string
|
2747 |
*
|
2748 |
* @uses $CFG
|
2749 |
* @return string
|
2750 |
*/
|
2751 |
function get_moodle_cookie() { |
2752 |
global $CFG; |
2753 |
|
2754 |
$cookiename = 'MOODLEID_'.$CFG->sessioncookie; |
2755 |
|
2756 |
if (empty($_COOKIE[$cookiename])) { |
2757 |
return ''; |
2758 |
} else {
|
2759 |
$thing = rc4decrypt($_COOKIE[$cookiename]); |
2760 |
return ($thing == 'guest') ? '': $thing; // Ignore guest account |
2761 |
} |
2762 |
} |
2763 |
|
2764 |
/**
|
2765 |
* Returns whether a given authentication plugin exists.
|
2766 |
*
|
2767 |
* @uses $CFG
|
2768 |
* @param string $auth Form of authentication to check for. Defaults to the
|
2769 |
* global setting in {@link $CFG}.
|
2770 |
* @return boolean Whether the plugin is available.
|
2771 |
*/
|
2772 |
function exists_auth_plugin($auth) { |
2773 |
global $CFG; |
2774 |
|
2775 |
if (file_exists("{$CFG->dirroot}/auth/$auth/auth.php")) { |
2776 |
return is_readable("{$CFG->dirroot}/auth/$auth/auth.php"); |
2777 |
} |
2778 |
return false; |
2779 |
} |
2780 |
|
2781 |
/**
|
2782 |
* Checks if a given plugin is in the list of enabled authentication plugins.
|
2783 |
*
|
2784 |
* @param string $auth Authentication plugin.
|
2785 |
* @return boolean Whether the plugin is enabled.
|
2786 |
*/
|
2787 |
function is_enabled_auth($auth) { |
2788 |
if (empty($auth)) { |
2789 |
return false; |
2790 |
} |
2791 |
|
2792 |
$enabled = get_enabled_auth_plugins();
|
2793 |
|
2794 |
return in_array($auth, $enabled); |
2795 |
} |
2796 |
|
2797 |
/**
|
2798 |
* Returns an authentication plugin instance.
|
2799 |
*
|
2800 |
* @uses $CFG
|
2801 |
* @param string $auth name of authentication plugin
|
2802 |
* @return object An instance of the required authentication plugin.
|
2803 |
*/
|
2804 |
function get_auth_plugin($auth) { |
2805 |
global $CFG; |
2806 |
|
2807 |
// check the plugin exists first
|
2808 |
if (! exists_auth_plugin($auth)) { |
2809 |
error("Authentication plugin '$auth' not found.");
|
2810 |
} |
2811 |
|
2812 |
// return auth plugin instance
|
2813 |
require_once "{$CFG->dirroot}/auth/$auth/auth.php"; |
2814 |
$class = "auth_plugin_$auth"; |
2815 |
return new $class; |
2816 |
} |
2817 |
|
2818 |
/**
|
2819 |
* Returns array of active auth plugins.
|
2820 |
*
|
2821 |
* @param bool $fix fix $CFG->auth if needed
|
2822 |
* @return array
|
2823 |
*/
|
2824 |
function get_enabled_auth_plugins($fix=false) { |
2825 |
global $CFG; |
2826 |
|
2827 |
$default = array('manual', 'nologin'); |
2828 |
|
2829 |
if (empty($CFG->auth)) { |
2830 |
$auths = array(); |
2831 |
} else {
|
2832 |
$auths = explode(',', $CFG->auth); |
2833 |
} |
2834 |
|
2835 |
if ($fix) { |
2836 |
$auths = array_unique($auths); |
2837 |
foreach($auths as $k=>$authname) { |
2838 |
if (!exists_auth_plugin($authname) or in_array($authname, $default)) { |
2839 |
unset($auths[$k]); |
2840 |
} |
2841 |
} |
2842 |
$newconfig = implode(',', $auths); |
2843 |
if (!isset($CFG->auth) or $newconfig != $CFG->auth) { |
2844 |
set_config('auth', $newconfig); |
2845 |
} |
2846 |
} |
2847 |
|
2848 |
return (array_merge($default, $auths)); |
2849 |
} |
2850 |
|
2851 |
/**
|
2852 |
* Returns true if an internal authentication method is being used.
|
2853 |
* if method not specified then, global default is assumed
|
2854 |
*
|
2855 |
* @uses $CFG
|
2856 |
* @param string $auth Form of authentication required
|
2857 |
* @return bool
|
2858 |
*/
|
2859 |
function is_internal_auth($auth) { |
2860 |
$authplugin = get_auth_plugin($auth); // throws error if bad $auth |
2861 |
return $authplugin->is_internal(); |
2862 |
} |
2863 |
|
2864 |
/**
|
2865 |
* Returns true if the user is a 'restored' one
|
2866 |
*
|
2867 |
* Used in the login process to inform the user
|
2868 |
* and allow him/her to reset the password
|
2869 |
*
|
2870 |
* @uses $CFG
|
2871 |
* @param string $username username to be checked
|
2872 |
* @return bool
|
2873 |
*/
|
2874 |
function is_restored_user($username) { |
2875 |
global $CFG; |
2876 |
|
2877 |
return record_exists('user', 'username', $username, 'mnethostid', $CFG->mnet_localhost_id, 'password', 'restored'); |
2878 |
} |
2879 |
|
2880 |
/**
|
2881 |
* Returns an array of user fields
|
2882 |
*
|
2883 |
* @uses $CFG
|
2884 |
* @uses $db
|
2885 |
* @return array User field/column names
|
2886 |
*/
|
2887 |
function get_user_fieldnames() { |
2888 |
|
2889 |
global $CFG, $db; |
2890 |
|
2891 |
$fieldarray = $db->MetaColumnNames($CFG->prefix.'user'); |
2892 |
unset($fieldarray['ID']); |
2893 |
|
2894 |
return $fieldarray; |
2895 |
} |
2896 |
|
2897 |
/**
|
2898 |
* Creates the default "guest" user. Used both from
|
2899 |
* admin/index.php and login/index.php
|
2900 |
* @return mixed user object created or boolean false if the creation has failed
|
2901 |
*/
|
2902 |
function create_guest_record() { |
2903 |
|
2904 |
global $CFG; |
2905 |
|
2906 |
$guest = new stdClass(); |
2907 |
$guest->auth = 'manual'; |
2908 |
$guest->username = 'guest'; |
2909 |
$guest->password = hash_internal_user_password('guest'); |
2910 |
$guest->firstname = addslashes(get_string('guestuser')); |
2911 |
$guest->lastname = ' '; |
2912 |
$guest->email = 'root@localhost'; |
2913 |
$guest->description = addslashes(get_string('guestuserinfo')); |
2914 |
$guest->mnethostid = $CFG->mnet_localhost_id; |
2915 |
$guest->confirmed = 1; |
2916 |
$guest->lang = $CFG->lang; |
2917 |
$guest->timemodified= time(); |
2918 |
|
2919 |
if (! $guest->id = insert_record("user", $guest)) { |
2920 |
return false; |
2921 |
} |
2922 |
|
2923 |
return $guest; |
2924 |
} |
2925 |
|
2926 |
/**
|
2927 |
* Creates a bare-bones user record
|
2928 |
*
|
2929 |
* @uses $CFG
|
2930 |
* @param string $username New user's username to add to record
|
2931 |
* @param string $password New user's password to add to record
|
2932 |
* @param string $auth Form of authentication required
|
2933 |
* @return object A {@link $USER} object
|
2934 |
* @todo Outline auth types and provide code example
|
2935 |
*/
|
2936 |
function create_user_record($username, $password, $auth='manual') { |
2937 |
global $CFG; |
2938 |
|
2939 |
//just in case check text case
|
2940 |
$username = trim(moodle_strtolower($username)); |
2941 |
|
2942 |
$authplugin = get_auth_plugin($auth); |
2943 |
|
2944 |
if ($newinfo = $authplugin->get_userinfo($username)) { |
2945 |
$newinfo = truncate_userinfo($newinfo); |
2946 |
foreach ($newinfo as $key => $value){ |
2947 |
$newuser->$key = addslashes($value); |
2948 |
} |
2949 |
} |
2950 |
|
2951 |
if (!empty($newuser->email)) { |
2952 |
if (email_is_not_allowed($newuser->email)) { |
2953 |
unset($newuser->email); |
2954 |
} |
2955 |
} |
2956 |
|
2957 |
if (!isset($newuser->city)) { |
2958 |
$newuser->city = ''; |
2959 |
} |
2960 |
|
2961 |
$newuser->auth = $auth; |
2962 |
$newuser->username = $username; |
2963 |
|
2964 |
// fix for MDL-8480
|
2965 |
// user CFG lang for user if $newuser->lang is empty
|
2966 |
// or $user->lang is not an installed language
|
2967 |
$sitelangs = array_keys(get_list_of_languages()); |
2968 |
if (empty($newuser->lang) || !in_array($newuser->lang, $sitelangs)) { |
2969 |
$newuser -> lang = $CFG->lang; |
2970 |
} |
2971 |
$newuser->confirmed = 1; |
2972 |
$newuser->lastip = getremoteaddr();
|
2973 |
$newuser->timemodified = time(); |
2974 |
$newuser->mnethostid = $CFG->mnet_localhost_id; |
2975 |
|
2976 |
if (insert_record('user', $newuser)) { |
2977 |
$user = get_complete_user_data('username', $newuser->username); |
2978 |
if(!empty($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'})){ |
2979 |
set_user_preference('auth_forcepasswordchange', 1, $user->id); |
2980 |
} |
2981 |
update_internal_user_password($user, $password); |
2982 |
return $user; |
2983 |
} |
2984 |
return false; |
2985 |
} |
2986 |
|
2987 |
/**
|
2988 |
* Will update a local user record from an external source
|
2989 |
*
|
2990 |
* @uses $CFG
|
2991 |
* @param string $username New user's username to add to record
|
2992 |
* @return user A {@link $USER} object
|
2993 |
*/
|
2994 |
function update_user_record($username, $authplugin) { |
2995 |
$username = trim(moodle_strtolower($username)); /// just in case check text case |
2996 |
|
2997 |
$oldinfo = get_record('user', 'username', $username, '','','','', 'username, auth'); |
2998 |
$userauth = get_auth_plugin($oldinfo->auth); |
2999 |
|
3000 |
if ($newinfo = $userauth->get_userinfo($username)) { |
3001 |
$newinfo = truncate_userinfo($newinfo); |
3002 |
foreach ($newinfo as $key => $value){ |
3003 |
if ($key === 'username') { |
3004 |
// 'username' is not a mapped updateable/lockable field, so skip it.
|
3005 |
continue;
|
3006 |
} |
3007 |
$confval = $userauth->config->{'field_updatelocal_' . $key}; |
3008 |
$lockval = $userauth->config->{'field_lock_' . $key}; |
3009 |
if (empty($confval) || empty($lockval)) { |
3010 |
continue;
|
3011 |
} |
3012 |
if ($confval === 'onlogin') { |
3013 |
$value = addslashes($value); |
3014 |
// MDL-4207 Don't overwrite modified user profile values with
|
3015 |
// empty LDAP values when 'unlocked if empty' is set. The purpose
|
3016 |
// of the setting 'unlocked if empty' is to allow the user to fill
|
3017 |
// in a value for the selected field _if LDAP is giving
|
3018 |
// nothing_ for this field. Thus it makes sense to let this value
|
3019 |
// stand in until LDAP is giving a value for this field.
|
3020 |
if (!(empty($value) && $lockval === 'unlockedifempty')) { |
3021 |
set_field('user', $key, $value, 'username', $username) |
3022 |
|| error_log("Error updating $key for $username"); |
3023 |
} |
3024 |
} |
3025 |
} |
3026 |
} |
3027 |
|
3028 |
return get_complete_user_data('username', $username); |
3029 |
} |
3030 |
|
3031 |
function truncate_userinfo($info) { |
3032 |
/// will truncate userinfo as it comes from auth_get_userinfo (from external auth)
|
3033 |
/// which may have large fields
|
3034 |
|
3035 |
// define the limits
|
3036 |
$limit = array( |
3037 |
'username' => 100, |
3038 |
'idnumber' => 255, |
3039 |
'firstname' => 100, |
3040 |
'lastname' => 100, |
3041 |
'email' => 100, |
3042 |
'icq' => 15, |
3043 |
'phone1' => 20, |
3044 |
'phone2' => 20, |
3045 |
'institution' => 40, |
3046 |
'department' => 30, |
3047 |
'address' => 70, |
3048 |
'city' => 20, |
3049 |
'country' => 2, |
3050 |
'url' => 255, |
3051 |
); |
3052 |
|
3053 |
// apply where needed
|
3054 |
foreach (array_keys($info) as $key) { |
3055 |
if (!empty($limit[$key])) { |
3056 |
$info[$key] = trim(substr($info[$key],0, $limit[$key])); |
3057 |
} |
3058 |
} |
3059 |
|
3060 |
return $info; |
3061 |
} |
3062 |
|
3063 |
/**
|
3064 |
* Marks user deleted in internal user database and notifies the auth plugin.
|
3065 |
* Also unenrols user from all roles and does other cleanup.
|
3066 |
* @param object $user Userobject before delete (without system magic quotes)
|
3067 |
* @return boolean success
|
3068 |
*/
|
3069 |
function delete_user($user) { |
3070 |
global $CFG; |
3071 |
require_once($CFG->libdir.'/grouplib.php'); |
3072 |
require_once($CFG->libdir.'/gradelib.php'); |
3073 |
require_once($CFG->dirroot.'/message/lib.php'); |
3074 |
|
3075 |
begin_sql(); |
3076 |
|
3077 |
// delete all grades - backup is kept in grade_grades_history table
|
3078 |
if ($grades = grade_grade::fetch_all(array('userid'=>$user->id))) { |
3079 |
foreach ($grades as $grade) { |
3080 |
$grade->delete('userdelete'); |
3081 |
} |
3082 |
} |
3083 |
|
3084 |
//move unread messages from this user to read
|
3085 |
message_move_userfrom_unread2read($user->id);
|
3086 |
|
3087 |
// remove from all groups
|
3088 |
delete_records('groups_members', 'userid', $user->id); |
3089 |
|
3090 |
// unenrol from all roles in all contexts
|
3091 |
role_unassign(0, $user->id); // this might be slow but it is really needed - modules might do some extra cleanup! |
3092 |
|
3093 |
// now do a final accesslib cleanup - removes all role assingments in user context and context itself
|
3094 |
delete_context(CONTEXT_USER, $user->id); |
3095 |
|
3096 |
require_once($CFG->dirroot.'/tag/lib.php'); |
3097 |
tag_set('user', $user->id, array()); |
3098 |
|
3099 |
// workaround for bulk deletes of users with the same email address
|
3100 |
$delname = addslashes("$user->email.".time()); |
3101 |
while (record_exists('user', 'username', $delname)) { // no need to use mnethostid here |
3102 |
$delname++;
|
3103 |
} |
3104 |
|
3105 |
// mark internal user record as "deleted"
|
3106 |
$updateuser = new object(); |
3107 |
$updateuser->id = $user->id; |
3108 |
$updateuser->deleted = 1; |
3109 |
$updateuser->username = $delname; // Remember it just in case |
3110 |
$updateuser->email = md5($user->username);// Store hash of username, useful importing/restoring users |
3111 |
$updateuser->idnumber = ''; // Clear this field to free it up |
3112 |
$updateuser->timemodified = time(); |
3113 |
|
3114 |
if (update_record('user', $updateuser)) { |
3115 |
commit_sql(); |
3116 |
// notify auth plugin - do not block the delete even when plugin fails
|
3117 |
$authplugin = get_auth_plugin($user->auth); |
3118 |
$authplugin->user_delete($user); |
3119 |
|
3120 |
events_trigger('user_deleted', $user); |
3121 |
return true; |
3122 |
|
3123 |
} else {
|
3124 |
rollback_sql(); |
3125 |
return false; |
3126 |
} |
3127 |
} |
3128 |
|
3129 |
/**
|
3130 |
* Retrieve the guest user object
|
3131 |
*
|
3132 |
* @uses $CFG
|
3133 |
* @return user A {@link $USER} object
|
3134 |
*/
|
3135 |
function guest_user() { |
3136 |
global $CFG; |
3137 |
|
3138 |
if ($newuser = get_record('user', 'username', 'guest', 'mnethostid', $CFG->mnet_localhost_id)) { |
3139 |
$newuser->confirmed = 1; |
3140 |
$newuser->lang = $CFG->lang; |
3141 |
$newuser->lastip = getremoteaddr();
|
3142 |
} |
3143 |
|
3144 |
return $newuser; |
3145 |
} |
3146 |
|
3147 |
/**
|
3148 |
* Given a username and password, this function looks them
|
3149 |
* up using the currently selected authentication mechanism,
|
3150 |
* and if the authentication is successful, it returns a
|
3151 |
* valid $user object from the 'user' table.
|
3152 |
*
|
3153 |
* Uses auth_ functions from the currently active auth module
|
3154 |
*
|
3155 |
* After authenticate_user_login() returns success, you will need to
|
3156 |
* log that the user has logged in, and call complete_user_login() to set
|
3157 |
* the session up.
|
3158 |
*
|
3159 |
* @uses $CFG
|
3160 |
* @param string $username User's username (with system magic quotes)
|
3161 |
* @param string $password User's password (with system magic quotes)
|
3162 |
* @return user|flase A {@link $USER} object or false if error
|
3163 |
*/
|
3164 |
function authenticate_user_login($username, $password) { |
3165 |
|
3166 |
global $CFG; |
3167 |
|
3168 |
$authsenabled = get_enabled_auth_plugins();
|
3169 |
|
3170 |
if ($user = get_complete_user_data('username', $username)) { |
3171 |
$auth = empty($user->auth) ? 'manual' : $user->auth; // use manual if auth not set |
3172 |
if ($auth=='nologin' or !is_enabled_auth($auth)) { |
3173 |
add_to_log(0, 'login', 'error', 'index.php', $username); |
3174 |
error_log('[client '.getremoteaddr()."] $CFG->wwwroot Disabled Login: $username ".$_SERVER['HTTP_USER_AGENT']); |
3175 |
return false; |
3176 |
} |
3177 |
$auths = array($auth); |
3178 |
|
3179 |
} else {
|
3180 |
// check if there's a deleted record (cheaply)
|
3181 |
if (get_field('user', 'id', 'username', $username, 'deleted', 1, '')) { |
3182 |
error_log('[client '.$_SERVER['REMOTE_ADDR']."] $CFG->wwwroot Deleted Login: $username ".$_SERVER['HTTP_USER_AGENT']); |
3183 |
return false; |
3184 |
} |
3185 |
|
3186 |
$auths = $authsenabled; |
3187 |
$user = new object(); |
3188 |
$user->id = 0; // User does not exist |
3189 |
} |
3190 |
|
3191 |
foreach ($auths as $auth) { |
3192 |
$authplugin = get_auth_plugin($auth); |
3193 |
|
3194 |
// on auth fail fall through to the next plugin
|
3195 |
if (!$authplugin->user_login($username, $password)) { |
3196 |
continue;
|
3197 |
} |
3198 |
|
3199 |
// successful authentication
|
3200 |
if ($user->id) { // User already exists in database |
3201 |
if (empty($user->auth)) { // For some reason auth isn't set yet |
3202 |
set_field('user', 'auth', $auth, 'username', $username); |
3203 |
$user->auth = $auth; |
3204 |
} |
3205 |
if (empty($user->firstaccess)) { //prevent firstaccess from remaining 0 for manual account that never required confirmation |
3206 |
set_field('user','firstaccess', $user->timemodified, 'id', $user->id); |
3207 |
$user->firstaccess = $user->timemodified; |
3208 |
} |
3209 |
|
3210 |
update_internal_user_password($user, $password); // just in case salt or encoding were changed (magic quotes too one day) |
3211 |
|
3212 |
if (!$authplugin->is_internal()) { // update user record from external DB |
3213 |
$user = update_user_record($username, get_auth_plugin($user->auth)); |
3214 |
} |
3215 |
} else {
|
3216 |
// if user not found, create him
|
3217 |
$user = create_user_record($username, $password, $auth); |
3218 |
} |
3219 |
|
3220 |
$authplugin->sync_roles($user); |
3221 |
|
3222 |
foreach ($authsenabled as $hau) { |
3223 |
$hauth = get_auth_plugin($hau); |
3224 |
$hauth->user_authenticated_hook($user, $username, $password); |
3225 |
} |
3226 |
|
3227 |
/// Log in to a second system if necessary
|
3228 |
/// NOTICE: /sso/ will be moved to auth and deprecated soon; use user_authenticated_hook() instead
|
3229 |
if (!empty($CFG->sso)) { |
3230 |
include_once($CFG->dirroot .'/sso/'. $CFG->sso .'/lib.php'); |
3231 |
if (function_exists('sso_user_login')) { |
3232 |
if (!sso_user_login($username, $password)) { // Perform the signon process |
3233 |
notify('Second sign-on failed');
|
3234 |
} |
3235 |
} |
3236 |
} |
3237 |
|
3238 |
if ($user->id===0) { |
3239 |
return false; |
3240 |
} |
3241 |
return $user; |
3242 |
} |
3243 |
|
3244 |
// failed if all the plugins have failed
|
3245 |
add_to_log(0, 'login', 'error', 'index.php', $username); |
3246 |
if (debugging('', DEBUG_ALL)) { |
3247 |
error_log('[client '.getremoteaddr()."] $CFG->wwwroot Failed Login: $username ".$_SERVER['HTTP_USER_AGENT']); |
3248 |
} |
3249 |
return false; |
3250 |
} |
3251 |
|
3252 |
/**
|
3253 |
* Call to complete the user login process after authenticate_user_login()
|
3254 |
* has succeeded. It will setup the $USER variable and other required bits
|
3255 |
* and pieces.
|
3256 |
*
|
3257 |
* NOTE:
|
3258 |
* - It will NOT log anything -- up to the caller to decide what to log.
|
3259 |
*
|
3260 |
*
|
3261 |
*
|
3262 |
* @uses $CFG, $USER
|
3263 |
* @param string $user obj
|
3264 |
* @return user|flase A {@link $USER} object or false if error
|
3265 |
*/
|
3266 |
function complete_user_login($user) { |
3267 |
global $CFG, $USER; |
3268 |
|
3269 |
$USER = $user; // this is required because we need to access preferences here! |
3270 |
|
3271 |
if (!empty($CFG->regenloginsession)) { |
3272 |
// please note this setting may break some auth plugins
|
3273 |
session_regenerate_id(); |
3274 |
} |
3275 |
|
3276 |
reload_user_preferences(); |
3277 |
|
3278 |
update_user_login_times(); |
3279 |
if (empty($CFG->nolastloggedin)) { |
3280 |
set_moodle_cookie($USER->username);
|
3281 |
} else {
|
3282 |
// do not store last logged in user in cookie
|
3283 |
// auth plugins can temporarily override this from loginpage_hook()
|
3284 |
// do not save $CFG->nolastloggedin in database!
|
3285 |
set_moodle_cookie('nobody');
|
3286 |
} |
3287 |
set_login_session_preferences(); |
3288 |
|
3289 |
// Call enrolment plugins
|
3290 |
check_enrolment_plugins($user);
|
3291 |
|
3292 |
/// This is what lets the user do anything on the site :-)
|
3293 |
load_all_capabilities(); |
3294 |
|
3295 |
/// Select password change url
|
3296 |
$userauth = get_auth_plugin($USER->auth); |
3297 |
|
3298 |
/// check whether the user should be changing password
|
3299 |
if (get_user_preferences('auth_forcepasswordchange', false)){ |
3300 |
if ($userauth->can_change_password()) { |
3301 |
if ($changeurl = $userauth->change_password_url()) { |
3302 |
redirect($changeurl);
|
3303 |
} else {
|
3304 |
redirect($CFG->httpswwwroot.'/login/change_password.php'); |
3305 |
} |
3306 |
} else {
|
3307 |
print_error('nopasswordchangeforced', 'auth'); |
3308 |
} |
3309 |
} |
3310 |
return $USER; |
3311 |
} |
3312 |
|
3313 |
/**
|
3314 |
* Compare password against hash stored in internal user table.
|
3315 |
* If necessary it also updates the stored hash to new format.
|
3316 |
*
|
3317 |
* @param object user
|
3318 |
* @param string plain text password
|
3319 |
* @return bool is password valid?
|
3320 |
*/
|
3321 |
function validate_internal_user_password(&$user, $password) { |
3322 |
global $CFG; |
3323 |
|
3324 |
if (!isset($CFG->passwordsaltmain)) { |
3325 |
$CFG->passwordsaltmain = ''; |
3326 |
} |
3327 |
|
3328 |
$validated = false; |
3329 |
|
3330 |
// get password original encoding in case it was not updated to unicode yet
|
3331 |
$textlib = textlib_get_instance();
|
3332 |
$convpassword = $textlib->convert($password, 'utf-8', get_string('oldcharset')); |
3333 |
|
3334 |
if ($user->password == md5($password.$CFG->passwordsaltmain) or $user->password == md5($password) |
3335 |
or $user->password == md5($convpassword.$CFG->passwordsaltmain) or $user->password == md5($convpassword)) { |
3336 |
$validated = true; |
3337 |
} else {
|
3338 |
for ($i=1; $i<=20; $i++) { //20 alternative salts should be enough, right? |
3339 |
$alt = 'passwordsaltalt'.$i; |
3340 |
if (!empty($CFG->$alt)) { |
3341 |
if ($user->password == md5($password.$CFG->$alt) or $user->password == md5($convpassword.$CFG->$alt)) { |
3342 |
$validated = true; |
3343 |
break;
|
3344 |
} |
3345 |
} |
3346 |
} |
3347 |
} |
3348 |
|
3349 |
if ($validated) { |
3350 |
// force update of password hash using latest main password salt and encoding if needed
|
3351 |
update_internal_user_password($user, $password); |
3352 |
} |
3353 |
|
3354 |
return $validated; |
3355 |
} |
3356 |
|
3357 |
/**
|
3358 |
* Calculate hashed value from password using current hash mechanism.
|
3359 |
*
|
3360 |
* @param string password
|
3361 |
* @return string password hash
|
3362 |
*/
|
3363 |
function hash_internal_user_password($password) { |
3364 |
global $CFG; |
3365 |
|
3366 |
if (isset($CFG->passwordsaltmain)) { |
3367 |
return md5($password.$CFG->passwordsaltmain); |
3368 |
} else {
|
3369 |
return md5($password); |
3370 |
} |
3371 |
} |
3372 |
|
3373 |
/**
|
3374 |
* Update pssword hash in user object.
|
3375 |
*
|
3376 |
* @param object user
|
3377 |
* @param string plain text password
|
3378 |
* @param bool store changes also in db, default true
|
3379 |
* @return true if hash changed
|
3380 |
*/
|
3381 |
function update_internal_user_password(&$user, $password) { |
3382 |
global $CFG; |
3383 |
|
3384 |
$authplugin = get_auth_plugin($user->auth); |
3385 |
if ($authplugin->prevent_local_passwords()) { |
3386 |
$hashedpassword = 'not cached'; |
3387 |
} else {
|
3388 |
$hashedpassword = hash_internal_user_password($password); |
3389 |
} |
3390 |
|
3391 |
return set_field('user', 'password', $hashedpassword, 'id', $user->id); |
3392 |
} |
3393 |
|
3394 |
/**
|
3395 |
* Get a complete user record, which includes all the info
|
3396 |
* in the user record
|
3397 |
* Intended for setting as $USER session variable
|
3398 |
*
|
3399 |
* @uses $CFG
|
3400 |
* @uses SITEID
|
3401 |
* @param string $field The user field to be checked for a given value.
|
3402 |
* @param string $value The value to match for $field.
|
3403 |
* @return user A {@link $USER} object.
|
3404 |
*/
|
3405 |
function get_complete_user_data($field, $value, $mnethostid=null) { |
3406 |
|
3407 |
global $CFG; |
3408 |
|
3409 |
if (!$field || !$value) { |
3410 |
return false; |
3411 |
} |
3412 |
|
3413 |
/// Build the WHERE clause for an SQL query
|
3414 |
|
3415 |
$constraints = $field .' = \''. $value .'\' AND deleted <> \'1\''; |
3416 |
|
3417 |
// If we are loading user data based on anything other than id,
|
3418 |
// we must also restrict our search based on mnet host.
|
3419 |
if ($field != 'id') { |
3420 |
if (empty($mnethostid)) { |
3421 |
// if empty, we restrict to local users
|
3422 |
$mnethostid = $CFG->mnet_localhost_id; |
3423 |
} |
3424 |
} |
3425 |
if (!empty($mnethostid)) { |
3426 |
$mnethostid = (int)$mnethostid; |
3427 |
$constraints .= ' AND mnethostid = ' . $mnethostid; |
3428 |
} |
3429 |
|
3430 |
/// Get all the basic user data
|
3431 |
|
3432 |
if (! $user = get_record_select('user', $constraints)) { |
3433 |
return false; |
3434 |
} |
3435 |
|
3436 |
/// Get various settings and preferences
|
3437 |
|
3438 |
if ($displays = get_records('course_display', 'userid', $user->id)) { |
3439 |
foreach ($displays as $display) { |
3440 |
$user->display[$display->course] = $display->display; |
3441 |
} |
3442 |
} |
3443 |
|
3444 |
$user->preference = get_user_preferences(null, null, $user->id); |
3445 |
|
3446 |
$user->lastcourseaccess = array(); // during last session |
3447 |
$user->currentcourseaccess = array(); // during current session |
3448 |
if ($lastaccesses = get_records('user_lastaccess', 'userid', $user->id)) { |
3449 |
foreach ($lastaccesses as $lastaccess) { |
3450 |
$user->lastcourseaccess[$lastaccess->courseid] = $lastaccess->timeaccess; |
3451 |
} |
3452 |
} |
3453 |
|
3454 |
$sql = "SELECT g.id, g.courseid |
3455 |
FROM {$CFG->prefix}groups g, {$CFG->prefix}groups_members gm
|
3456 |
WHERE gm.groupid=g.id AND gm.userid={$user->id}";
|
3457 |
|
3458 |
// this is a special hack to speedup calendar display
|
3459 |
$user->groupmember = array(); |
3460 |
if ($groups = get_records_sql($sql)) { |
3461 |
foreach ($groups as $group) { |
3462 |
if (!array_key_exists($group->courseid, $user->groupmember)) { |
3463 |
$user->groupmember[$group->courseid] = array(); |
3464 |
} |
3465 |
$user->groupmember[$group->courseid][$group->id] = $group->id; |
3466 |
} |
3467 |
} |
3468 |
|
3469 |
/// Add the custom profile fields to the user record
|
3470 |
include_once($CFG->dirroot.'/user/profile/lib.php'); |
3471 |
$customfields = (array)profile_user_record($user->id); |
3472 |
foreach ($customfields as $cname=>$cvalue) { |
3473 |
if (!isset($user->$cname)) { // Don't overwrite any standard fields |
3474 |
$user->$cname = $cvalue; |
3475 |
} |
3476 |
} |
3477 |
|
3478 |
/// Rewrite some variables if necessary
|
3479 |
if (!empty($user->description)) { |
3480 |
$user->description = true; // No need to cart all of it around |
3481 |
} |
3482 |
if ($user->username == 'guest') { |
3483 |
$user->lang = $CFG->lang; // Guest language always same as site |
3484 |
$user->firstname = get_string('guestuser'); // Name always in current language |
3485 |
$user->lastname = ' '; |
3486 |
} |
3487 |
|
3488 |
if (isset($_SERVER['REMOTE_ADDR'])) { |
3489 |
$user->sesskey = random_string(10); |
3490 |
$user->sessionIP = md5(getremoteaddr()); // Store the current IP in the session |
3491 |
} |
3492 |
|
3493 |
return $user; |
3494 |
} |
3495 |
|
3496 |
/**
|
3497 |
* @uses $CFG
|
3498 |
* @param string $password the password to be checked agains the password policy
|
3499 |
* @param string $errmsg the error message to display when the password doesn't comply with the policy.
|
3500 |
* @return bool true if the password is valid according to the policy. false otherwise.
|
3501 |
*/
|
3502 |
function check_password_policy($password, &$errmsg) { |
3503 |
global $CFG; |
3504 |
|
3505 |
if (empty($CFG->passwordpolicy)) { |
3506 |
return true; |
3507 |
} |
3508 |
|
3509 |
$textlib = textlib_get_instance();
|
3510 |
$errmsg = ''; |
3511 |
if ($textlib->strlen($password) < $CFG->minpasswordlength) { |
3512 |
$errmsg .= '<div>'. get_string('errorminpasswordlength', 'auth', $CFG->minpasswordlength) .'</div>'; |
3513 |
|
3514 |
} |
3515 |
if (preg_match_all('/[[:digit:]]/u', $password, $matches) < $CFG->minpassworddigits) { |
3516 |
$errmsg .= '<div>'. get_string('errorminpassworddigits', 'auth', $CFG->minpassworddigits) .'</div>'; |
3517 |
|
3518 |
} |
3519 |
if (preg_match_all('/[[:lower:]]/u', $password, $matches) < $CFG->minpasswordlower) { |
3520 |
$errmsg .= '<div>'. get_string('errorminpasswordlower', 'auth', $CFG->minpasswordlower) .'</div>'; |
3521 |
|
3522 |
} |
3523 |
if (preg_match_all('/[[:upper:]]/u', $password, $matches) < $CFG->minpasswordupper) { |
3524 |
$errmsg .= '<div>'. get_string('errorminpasswordupper', 'auth', $CFG->minpasswordupper) .'</div>'; |
3525 |
|
3526 |
} |
3527 |
if (preg_match_all('/[^[:upper:][:lower:][:digit:]]/u', $password, $matches) < $CFG->minpasswordnonalphanum) { |
3528 |
$errmsg .= '<div>'. get_string('errorminpasswordnonalphanum', 'auth', $CFG->minpasswordnonalphanum) .'</div>'; |
3529 |
} |
3530 |
|
3531 |
if ($errmsg == '') { |
3532 |
return true; |
3533 |
} else {
|
3534 |
return false; |
3535 |
} |
3536 |
} |
3537 |
|
3538 |
|
3539 |
/**
|
3540 |
* When logging in, this function is run to set certain preferences
|
3541 |
* for the current SESSION
|
3542 |
*/
|
3543 |
function set_login_session_preferences() { |
3544 |
global $SESSION, $CFG; |
3545 |
|
3546 |
$SESSION->justloggedin = true; |
3547 |
|
3548 |
unset($SESSION->lang); |
3549 |
|
3550 |
// Restore the calendar filters, if saved
|
3551 |
if (intval(get_user_preferences('calendar_persistflt', 0))) { |
3552 |
include_once($CFG->dirroot.'/calendar/lib.php'); |
3553 |
calendar_set_filters_status(get_user_preferences('calendar_savedflt', 0xff)); |
3554 |
} |
3555 |
} |
3556 |
|
3557 |
|
3558 |
/**
|
3559 |
* Delete a course, including all related data from the database,
|
3560 |
* and any associated files from the moodledata folder.
|
3561 |
*
|
3562 |
* @param mixed $courseorid The id of the course or course object to delete.
|
3563 |
* @param bool $showfeedback Whether to display notifications of each action the function performs.
|
3564 |
* @return bool true if all the removals succeeded. false if there were any failures. If this
|
3565 |
* method returns false, some of the removals will probably have succeeded, and others
|
3566 |
* failed, but you have no way of knowing which.
|
3567 |
*/
|
3568 |
function delete_course($courseorid, $showfeedback = true) { |
3569 |
global $CFG; |
3570 |
$result = true; |
3571 |
|
3572 |
if (is_object($courseorid)) { |
3573 |
$courseid = $courseorid->id; |
3574 |
$course = $courseorid; |
3575 |
} else {
|
3576 |
$courseid = $courseorid; |
3577 |
if (!$course = get_record('course', 'id', $courseid)) { |
3578 |
return false; |
3579 |
} |
3580 |
} |
3581 |
|
3582 |
// frontpage course can not be deleted!!
|
3583 |
if ($courseid == SITEID) { |
3584 |
return false; |
3585 |
} |
3586 |
|
3587 |
if (!remove_course_contents($courseid, $showfeedback)) { |
3588 |
if ($showfeedback) { |
3589 |
notify("An error occurred while deleting some of the course contents.");
|
3590 |
} |
3591 |
$result = false; |
3592 |
} |
3593 |
|
3594 |
if (!delete_records("course", "id", $courseid)) { |
3595 |
if ($showfeedback) { |
3596 |
notify("An error occurred while deleting the main course record.");
|
3597 |
} |
3598 |
$result = false; |
3599 |
} |
3600 |
|
3601 |
/// Delete all roles and overiddes in the course context
|
3602 |
if (!delete_context(CONTEXT_COURSE, $courseid)) { |
3603 |
if ($showfeedback) { |
3604 |
notify("An error occurred while deleting the main course context.");
|
3605 |
} |
3606 |
$result = false; |
3607 |
} |
3608 |
|
3609 |
if (!fulldelete($CFG->dataroot.'/'.$courseid)) { |
3610 |
if ($showfeedback) { |
3611 |
notify("An error occurred while deleting the course files.");
|
3612 |
} |
3613 |
$result = false; |
3614 |
} |
3615 |
|
3616 |
if ($result) { |
3617 |
//trigger events
|
3618 |
events_trigger('course_deleted', $course); |
3619 |
} |
3620 |
|
3621 |
return $result; |
3622 |
} |
3623 |
|
3624 |
/**
|
3625 |
* Clear a course out completely, deleting all content
|
3626 |
* but don't delete the course itself
|
3627 |
*
|
3628 |
* @uses $CFG
|
3629 |
* @param int $courseid The id of the course that is being deleted
|
3630 |
* @param bool $showfeedback Whether to display notifications of each action the function performs.
|
3631 |
* @return bool true if all the removals succeeded. false if there were any failures. If this
|
3632 |
* method returns false, some of the removals will probably have succeeded, and others
|
3633 |
* failed, but you have no way of knowing which.
|
3634 |
*/
|
3635 |
function remove_course_contents($courseid, $showfeedback=true) { |
3636 |
|
3637 |
global $CFG; |
3638 |
require_once($CFG->libdir.'/questionlib.php'); |
3639 |
require_once($CFG->libdir.'/gradelib.php'); |
3640 |
|
3641 |
$result = true; |
3642 |
|
3643 |
if (! $course = get_record('course', 'id', $courseid)) { |
3644 |
error('Course ID was incorrect (can\'t find it)');
|
3645 |
} |
3646 |
|
3647 |
$strdeleted = get_string('deleted'); |
3648 |
|
3649 |
/// Clean up course formats (iterate through all formats in the even the course format was ever changed)
|
3650 |
$formats = get_list_of_plugins('course/format'); |
3651 |
foreach ($formats as $format) { |
3652 |
$formatdelete = $format.'_course_format_delete_course'; |
3653 |
$formatlib = "$CFG->dirroot/course/format/$format/lib.php"; |
3654 |
if (file_exists($formatlib)) { |
3655 |
include_once($formatlib); |
3656 |
if (function_exists($formatdelete)) { |
3657 |
if ($showfeedback) { |
3658 |
notify($strdeleted.' '.$format); |
3659 |
} |
3660 |
$formatdelete($course->id); |
3661 |
} |
3662 |
} |
3663 |
} |
3664 |
|
3665 |
/// Delete every instance of every module
|
3666 |
|
3667 |
if ($allmods = get_records('modules') ) { |
3668 |
foreach ($allmods as $mod) { |
3669 |
$modname = $mod->name; |
3670 |
$modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php'; |
3671 |
$moddelete = $modname .'_delete_instance'; // Delete everything connected to an instance |
3672 |
$moddeletecourse = $modname .'_delete_course'; // Delete other stray stuff (uncommon) |
3673 |
$count=0; |
3674 |
if (file_exists($modfile)) { |
3675 |
include_once($modfile); |
3676 |
if (function_exists($moddelete)) { |
3677 |
if ($instances = get_records($modname, 'course', $course->id)) { |
3678 |
foreach ($instances as $instance) { |
3679 |
if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) { |
3680 |
/// Delete activity context questions and question categories
|
3681 |
question_delete_activity($cm, $showfeedback); |
3682 |
} |
3683 |
if ($moddelete($instance->id)) { |
3684 |
$count++;
|
3685 |
|
3686 |
} else {
|
3687 |
notify('Could not delete '. $modname .' instance '. $instance->id .' ('. format_string($instance->name) .')'); |
3688 |
$result = false; |
3689 |
} |
3690 |
if ($cm) { |
3691 |
// delete cm and its context in correct order
|
3692 |
delete_records('course_modules', 'id', $cm->id); |
3693 |
delete_context(CONTEXT_MODULE, $cm->id); |
3694 |
} |
3695 |
} |
3696 |
} |
3697 |
} else {
|
3698 |
notify('Function '.$moddelete.'() doesn\'t exist!'); |
3699 |
$result = false; |
3700 |
} |
3701 |
|
3702 |
if (function_exists($moddeletecourse)) { |
3703 |
$moddeletecourse($course, $showfeedback); |
3704 |
} |
3705 |
} |
3706 |
if ($showfeedback) { |
3707 |
notify($strdeleted .' '. $count .' x '. $modname); |
3708 |
} |
3709 |
} |
3710 |
} else {
|
3711 |
error('No modules are installed!');
|
3712 |
} |
3713 |
|
3714 |
/// Give local code a chance to delete its references to this course.
|
3715 |
require_once($CFG->libdir.'/locallib.php'); |
3716 |
notify_local_delete_course($courseid, $showfeedback); |
3717 |
|
3718 |
/// Delete course blocks
|
3719 |
|
3720 |
if ($blocks = get_records_sql("SELECT * |
3721 |
FROM {$CFG->prefix}block_instance
|
3722 |
WHERE pagetype = '".PAGE_COURSE_VIEW."' |
3723 |
AND pageid = $course->id")) {
|
3724 |
if (delete_records('block_instance', 'pagetype', PAGE_COURSE_VIEW, 'pageid', $course->id)) { |
3725 |
if ($showfeedback) { |
3726 |
notify($strdeleted .' block_instance'); |
3727 |
} |
3728 |
|
3729 |
require_once($CFG->libdir.'/blocklib.php'); |
3730 |
foreach ($blocks as $block) { /// Delete any associated contexts for this block |
3731 |
|
3732 |
delete_context(CONTEXT_BLOCK, $block->id); |
3733 |
|
3734 |
// fix for MDL-7164
|
3735 |
// Get the block object and call instance_delete()
|
3736 |
if (!$record = blocks_get_record($block->blockid)) { |
3737 |
$result = false; |
3738 |
continue;
|
3739 |
} |
3740 |
if (!$obj = block_instance($record->name, $block)) { |
3741 |
$result = false; |
3742 |
continue;
|
3743 |
} |
3744 |
// Return value ignored, in core mods this does not do anything, but just in case
|
3745 |
// third party blocks might have stuff to clean up
|
3746 |
// we execute this anyway
|
3747 |
$obj->instance_delete();
|
3748 |
|
3749 |
} |
3750 |
} else {
|
3751 |
$result = false; |
3752 |
} |
3753 |
} |
3754 |
|
3755 |
/// Delete any groups, removing members and grouping/course links first.
|
3756 |
require_once($CFG->dirroot.'/group/lib.php'); |
3757 |
groups_delete_groupings($courseid, $showfeedback); |
3758 |
groups_delete_groups($courseid, $showfeedback); |
3759 |
|
3760 |
/// Delete all related records in other tables that may have a courseid
|
3761 |
/// This array stores the tables that need to be cleared, as
|
3762 |
/// table_name => column_name that contains the course id.
|
3763 |
|
3764 |
$tablestoclear = array( |
3765 |
'event' => 'courseid', // Delete events |
3766 |
'log' => 'course', // Delete logs |
3767 |
'course_sections' => 'course', // Delete any course stuff |
3768 |
'course_modules' => 'course', |
3769 |
'backup_courses' => 'courseid', // Delete scheduled backup stuff |
3770 |
'user_lastaccess' => 'courseid', |
3771 |
'backup_log' => 'courseid' |
3772 |
); |
3773 |
foreach ($tablestoclear as $table => $col) { |
3774 |
if (delete_records($table, $col, $course->id)) { |
3775 |
if ($showfeedback) { |
3776 |
notify($strdeleted . ' ' . $table); |
3777 |
} |
3778 |
} else {
|
3779 |
$result = false; |
3780 |
} |
3781 |
} |
3782 |
|
3783 |
|
3784 |
/// Clean up metacourse stuff
|
3785 |
|
3786 |
if ($course->metacourse) { |
3787 |
delete_records("course_meta","parent_course",$course->id); |
3788 |
sync_metacourse($course->id); // have to do it here so the enrolments get nuked. sync_metacourses won't find it without the id. |
3789 |
if ($showfeedback) { |
3790 |
notify("$strdeleted course_meta");
|
3791 |
} |
3792 |
} else {
|
3793 |
if ($parents = get_records("course_meta","child_course",$course->id)) { |
3794 |
foreach ($parents as $parent) { |
3795 |
remove_from_metacourse($parent->parent_course,$parent->child_course); // this will do the unenrolments as well. |
3796 |
} |
3797 |
if ($showfeedback) { |
3798 |
notify("$strdeleted course_meta");
|
3799 |
} |
3800 |
} |
3801 |
} |
3802 |
|
3803 |
/// Delete questions and question categories
|
3804 |
question_delete_course($course, $showfeedback); |
3805 |
|
3806 |
/// Remove all data from gradebook
|
3807 |
$context = get_context_instance(CONTEXT_COURSE, $courseid); |
3808 |
remove_course_grades($courseid, $showfeedback); |
3809 |
remove_grade_letters($context, $showfeedback); |
3810 |
|
3811 |
return $result; |
3812 |
} |
3813 |
|
3814 |
/**
|
3815 |
* Change dates in module - used from course reset.
|
3816 |
* @param strin $modname forum, assignent, etc
|
3817 |
* @param array $fields array of date fields from mod table
|
3818 |
* @param int $timeshift time difference
|
3819 |
* @return success
|
3820 |
*/
|
3821 |
function shift_course_mod_dates($modname, $fields, $timeshift, $courseid) { |
3822 |
global $CFG; |
3823 |
include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php'); |
3824 |
|
3825 |
$return = true; |
3826 |
foreach ($fields as $field) { |
3827 |
$updatesql = "UPDATE {$CFG->prefix}$modname |
3828 |
SET $field = $field + ($timeshift)
|
3829 |
WHERE course=$courseid AND $field<>0 AND $field<>0";
|
3830 |
$return = execute_sql($updatesql, false) && $return; |
3831 |
} |
3832 |
|
3833 |
$refreshfunction = $modname.'_refresh_events'; |
3834 |
if (function_exists($refreshfunction)) { |
3835 |
$refreshfunction($courseid); |
3836 |
} |
3837 |
|
3838 |
return $return; |
3839 |
} |
3840 |
|
3841 |
/**
|
3842 |
* This function will empty a course of user data.
|
3843 |
* It will retain the activities and the structure of the course.
|
3844 |
* @param object $data an object containing all the settings including courseid (without magic quotes)
|
3845 |
* @return array status array of array component, item, error
|
3846 |
*/
|
3847 |
function reset_course_userdata($data) { |
3848 |
global $CFG, $USER; |
3849 |
require_once($CFG->libdir.'/gradelib.php'); |
3850 |
require_once($CFG->dirroot.'/group/lib.php'); |
3851 |
|
3852 |
$data->courseid = $data->id; |
3853 |
$context = get_context_instance(CONTEXT_COURSE, $data->courseid); |
3854 |
|
3855 |
// calculate the time shift of dates
|
3856 |
if (!empty($data->reset_start_date)) { |
3857 |
// time part of course startdate should be zero
|
3858 |
$data->timeshift = $data->reset_start_date - usergetmidnight($data->reset_start_date_old); |
3859 |
} else {
|
3860 |
$data->timeshift = 0; |
3861 |
} |
3862 |
|
3863 |
// result array: component, item, error
|
3864 |
$status = array(); |
3865 |
|
3866 |
// start the resetting
|
3867 |
$componentstr = get_string('general'); |
3868 |
|
3869 |
// move the course start time
|
3870 |
if (!empty($data->reset_start_date) and $data->timeshift) { |
3871 |
// change course start data
|
3872 |
set_field('course', 'startdate', $data->reset_start_date, 'id', $data->courseid); |
3873 |
// update all course and group events - do not move activity events
|
3874 |
$updatesql = "UPDATE {$CFG->prefix}event |
3875 |
SET timestart = timestart + ({$data->timeshift})
|
3876 |
WHERE courseid={$data->courseid} AND instance=0";
|
3877 |
execute_sql($updatesql, false); |
3878 |
|
3879 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false); |
3880 |
} |
3881 |
|
3882 |
if (!empty($data->reset_logs)) { |
3883 |
delete_records('log', 'course', $data->courseid); |
3884 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deletelogs'), 'error'=>false); |
3885 |
} |
3886 |
|
3887 |
if (!empty($data->reset_events)) { |
3888 |
delete_records('event', 'courseid', $data->courseid); |
3889 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteevents', 'calendar'), 'error'=>false); |
3890 |
} |
3891 |
|
3892 |
if (!empty($data->reset_notes)) { |
3893 |
require_once($CFG->dirroot.'/notes/lib.php'); |
3894 |
note_delete_all($data->courseid);
|
3895 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotes', 'notes'), 'error'=>false); |
3896 |
} |
3897 |
|
3898 |
$componentstr = get_string('roles'); |
3899 |
|
3900 |
if (!empty($data->reset_roles_overrides)) { |
3901 |
$children = get_child_contexts($context); |
3902 |
foreach ($children as $child) { |
3903 |
delete_records('role_capabilities', 'contextid', $child->id); |
3904 |
} |
3905 |
delete_records('role_capabilities', 'contextid', $context->id); |
3906 |
//force refresh for logged in users
|
3907 |
mark_context_dirty($context->path);
|
3908 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deletecourseoverrides', 'role'), 'error'=>false); |
3909 |
} |
3910 |
|
3911 |
if (!empty($data->reset_roles_local)) { |
3912 |
$children = get_child_contexts($context); |
3913 |
foreach ($children as $child) { |
3914 |
role_unassign(0, 0, 0, $child->id); |
3915 |
} |
3916 |
//force refresh for logged in users
|
3917 |
mark_context_dirty($context->path);
|
3918 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deletelocalroles', 'role'), 'error'=>false); |
3919 |
} |
3920 |
|
3921 |
// First unenrol users - this cleans some of related user data too, such as forum subscriptions, tracking, etc.
|
3922 |
$data->unenrolled = array(); |
3923 |
if (!empty($data->reset_roles)) { |
3924 |
foreach($data->reset_roles as $roleid) { |
3925 |
if ($users = get_role_users($roleid, $context, false, 'u.id', 'u.id ASC')) { |
3926 |
foreach ($users as $user) { |
3927 |
role_unassign($roleid, $user->id, 0, $context->id); |
3928 |
if (!has_capability('moodle/course:view', $context, $user->id)) { |
3929 |
$data->unenrolled[$user->id] = $user->id; |
3930 |
} |
3931 |
} |
3932 |
} |
3933 |
} |
3934 |
} |
3935 |
if (!empty($data->unenrolled)) { |
3936 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('unenrol').' ('.count($data->unenrolled).')', 'error'=>false); |
3937 |
} |
3938 |
|
3939 |
|
3940 |
$componentstr = get_string('groups'); |
3941 |
|
3942 |
// remove all group members
|
3943 |
if (!empty($data->reset_groups_members)) { |
3944 |
groups_delete_group_members($data->courseid);
|
3945 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('removegroupsmembers', 'group'), 'error'=>false); |
3946 |
} |
3947 |
|
3948 |
// remove all groups
|
3949 |
if (!empty($data->reset_groups_remove)) { |
3950 |
groups_delete_groups($data->courseid, false); |
3951 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallgroups', 'group'), 'error'=>false); |
3952 |
} |
3953 |
|
3954 |
// remove all grouping members
|
3955 |
if (!empty($data->reset_groupings_members)) { |
3956 |
groups_delete_groupings_groups($data->courseid, false); |
3957 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('removegroupingsmembers', 'group'), 'error'=>false); |
3958 |
} |
3959 |
|
3960 |
// remove all groupings
|
3961 |
if (!empty($data->reset_groupings_remove)) { |
3962 |
groups_delete_groupings($data->courseid, false); |
3963 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallgroupings', 'group'), 'error'=>false); |
3964 |
} |
3965 |
|
3966 |
// Look in every instance of every module for data to delete
|
3967 |
$unsupported_mods = array(); |
3968 |
if ($allmods = get_records('modules') ) { |
3969 |
foreach ($allmods as $mod) { |
3970 |
$modname = $mod->name; |
3971 |
if (!count_records($modname, 'course', $data->courseid)) { |
3972 |
continue; // skip mods with no instances |
3973 |
} |
3974 |
$modfile = $CFG->dirroot.'/mod/'. $modname.'/lib.php'; |
3975 |
$moddeleteuserdata = $modname.'_reset_userdata'; // Function to delete user data |
3976 |
if (file_exists($modfile)) { |
3977 |
include_once($modfile); |
3978 |
if (function_exists($moddeleteuserdata)) { |
3979 |
$modstatus = $moddeleteuserdata($data); |
3980 |
if (is_array($modstatus)) { |
3981 |
$status = array_merge($status, $modstatus); |
3982 |
} else {
|
3983 |
debugging('Module '.$modname.' returned incorrect staus - must be an array!'); |
3984 |
} |
3985 |
} else {
|
3986 |
$unsupported_mods[] = $mod; |
3987 |
} |
3988 |
} else {
|
3989 |
debugging('Missing lib.php in '.$modname.' module!'); |
3990 |
} |
3991 |
} |
3992 |
} |
3993 |
|
3994 |
// mention unsupported mods
|
3995 |
if (!empty($unsupported_mods)) { |
3996 |
foreach($unsupported_mods as $mod) { |
3997 |
$status[] = array('component'=>get_string('modulenameplural', $mod->name), 'item'=>'', 'error'=>get_string('resetnotimplemented')); |
3998 |
} |
3999 |
} |
4000 |
|
4001 |
|
4002 |
$componentstr = get_string('gradebook', 'grades'); |
4003 |
// reset gradebook
|
4004 |
if (!empty($data->reset_gradebook_items)) { |
4005 |
remove_course_grades($data->courseid, false); |
4006 |
grade_grab_course_grades($data->courseid);
|
4007 |
grade_regrade_final_grades($data->courseid);
|
4008 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('removeallcourseitems', 'grades'), 'error'=>false); |
4009 |
|
4010 |
} else if (!empty($data->reset_gradebook_grades)) { |
4011 |
grade_course_reset($data->courseid);
|
4012 |
$status[] = array('component'=>$componentstr, 'item'=>get_string('removeallcoursegrades', 'grades'), 'error'=>false); |
4013 |
} |
4014 |
|
4015 |
return $status; |
4016 |
} |
4017 |
|
4018 |
function generate_email_processing_address($modid,$modargs) { |
4019 |
global $CFG; |
4020 |
|
4021 |
$header = $CFG->mailprefix . substr(base64_encode(pack('C',$modid)),0,2).$modargs; |
4022 |
return $header . substr(md5($header.get_site_identifier()),0,16).'@'.$CFG->maildomain; |
4023 |
} |
4024 |
|
4025 |
function moodle_process_email($modargs,$body) { |
4026 |
// the first char should be an unencoded letter. We'll take this as an action
|
4027 |
switch ($modargs{0}) { |
4028 |
case 'B': { // bounce |
4029 |
list(,$userid) = unpack('V',base64_decode(substr($modargs,1,8))); |
4030 |
if ($user = get_record_select("user","id=$userid","id,email")) { |
4031 |
// check the half md5 of their email
|
4032 |
$md5check = substr(md5($user->email),0,16); |
4033 |
if ($md5check == substr($modargs, -16)) { |
4034 |
set_bounce_count($user);
|
4035 |
} |
4036 |
// else maybe they've already changed it?
|
4037 |
} |
4038 |
} |
4039 |
break;
|
4040 |
// maybe more later?
|
4041 |
} |
4042 |
} |
4043 |
|
4044 |
/// CORRESPONDENCE ////////////////////////////////////////////////
|
4045 |
|
4046 |
/**
|
4047 |
* Get mailer instance, enable buffering, flush buffer or disable buffering.
|
4048 |
* @param $action string 'get', 'buffer', 'close' or 'flush'
|
4049 |
* @return reference to mailer instance if 'get' used or nothing
|
4050 |
*/
|
4051 |
function &get_mailer($action='get') { |
4052 |
global $CFG; |
4053 |
|
4054 |
static $mailer = null; |
4055 |
static $counter = 0; |
4056 |
|
4057 |
if (!isset($CFG->smtpmaxbulk)) { |
4058 |
$CFG->smtpmaxbulk = 1; |
4059 |
} |
4060 |
|
4061 |
if ($action == 'get') { |
4062 |
$prevkeepalive = false; |
4063 |
|
4064 |
if (isset($mailer) and $mailer->Mailer == 'smtp') { |
4065 |
if ($counter < $CFG->smtpmaxbulk and empty($mailer->error_count)) { |
4066 |
$counter++;
|
4067 |
// reset the mailer
|
4068 |
$mailer->Priority = 3; |
4069 |
$mailer->CharSet = 'UTF-8'; // our default |
4070 |
$mailer->ContentType = "text/plain"; |
4071 |
|