9a7b27a809ef16daedc0a4cc5441ec756a89a965
[moodle.git] / lib / moodlelib.php
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 //                                                                       //
5 // NOTICE OF COPYRIGHT                                                   //
6 //                                                                       //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment         //
8 //          http://moodle.org                                            //
9 //                                                                       //
10 // Copyright (C) 1999-2004  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 ///////////////////////////////////////////////////////////////////////////
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  * @version $Id$
35  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
36  * @package moodlecore
37  */
38 /// CONSTANTS /////////////////////////////////////////////////////////////
40 /**
41  * Used by some scripts to check they are being called by Moodle
42  */
43 define('MOODLE_INTERNAL', true);
46 /**
47  * No groups used?
48  */
49 define('NOGROUPS', 0);
51 /**
52  * Groups used?
53  */
54 define('SEPARATEGROUPS', 1);
56 /**
57  * Groups visible?
58  */
59 define('VISIBLEGROUPS', 2);
61 /**
62  * Time constant - the number of seconds in a week
63  */
64 define('WEEKSECS', 604800);
66 /**
67  * Time constant - the number of seconds in a day
68  */
69 define('DAYSECS', 86400);
71 /**
72  * Time constant - the number of seconds in an hour
73  */
74 define('HOURSECS', 3600);
76 /**
77  * Time constant - the number of seconds in a minute
78  */
79 define('MINSECS', 60);
81 /**
82  * Time constant - the number of minutes in a day
83  */
84 define('DAYMINS', 1440);
86 /**
87  * Time constant - the number of minutes in an hour
88  */
89 define('HOURMINS', 60);
91 /**
92  * Parameter constants - if set then the parameter is cleaned of scripts etc
93  */
94 define('PARAM_RAW',     0x00);
95 define('PARAM_CLEAN',   0x01);
96 define('PARAM_INT',     0x02);
97 define('PARAM_INTEGER', 0x02);  // Alias for PARAM_INT
98 define('PARAM_ALPHA',   0x04);
99 define('PARAM_ACTION',  0x04);  // Alias for PARAM_ALPHA
100 define('PARAM_FORMAT',  0x04);  // Alias for PARAM_ALPHA
101 define('PARAM_NOTAGS',  0x08);
102 define('PARAM_FILE',    0x10);
103 define('PARAM_PATH',    0x20);
106 /// PARAMETER HANDLING ////////////////////////////////////////////////////
108 /**
109  * Returns a particular value for the named variable, taken from
110  * POST or GET.  If the parameter doesn't exist then an error is
111  * thrown because we require this variable.
112  *
113  * This function should be used to initialise all required values
114  * in a script that are based on parameters.  Usually it will be
115  * used like this:
116  *    $id = required_param('id');
117  *
118  * @param string $varname the name of the parameter variable we want
119  * @param integer $options a bit field that specifies any cleaning needed
120  * @return mixed
121  */
122 function required_param($varname, $options=PARAM_CLEAN) {
124     if (isset($_POST[$varname])) {       // POST has precedence
125         $param = $_POST[$varname];
126     } else if (isset($_GET[$varname])) {
127         $param = $_GET[$varname];
128     } else {
129         error('A required parameter ('.$varname.') was missing');
130     }
132     return clean_param($param, $options);
135 /**
136  * Returns a particular value for the named variable, taken from
137  * POST or GET, otherwise returning a given default.
138  *
139  * This function should be used to initialise all optional values
140  * in a script that are based on parameters.  Usually it will be
141  * used like this:
142  *    $name = optional_param('name', 'Fred');
143  *
144  * @param string $varname the name of the parameter variable we want
145  * @param mixed  $default the default value to return if nothing is found
146  * @param integer $options a bit field that specifies any cleaning needed
147  * @return mixed
148  */
149 function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {
151     if (isset($_POST[$varname])) {       // POST has precedence
152         $param = $_POST[$varname];
153     } else if (isset($_GET[$varname])) {
154         $param = $_GET[$varname];
155     } else {
156         return $default;
157     }
159     return clean_param($param, $options);
162 /**
163  * Used by {@link optional_param()} and {@link required_param()} to
164  * clean the variables and/or cast to specific types, based on
165  * an options field.
166  *
167  * @param mixed $param the variable we are cleaning
168  * @param integer $options a bit field that specifies the cleaning needed
169  * @return mixed
170  */
171 function clean_param($param, $options) {
173     if (!$options) {
174         return $param;                   // Return raw value
175     }
177     if ((string)$param == (string)(int)$param) {  // It's just an integer
178         return (int)$param;
179     }
181     if ($options & PARAM_CLEAN) {
182         $param = clean_text($param);     // Sweep for scripts, etc
183     }
185     if ($options & PARAM_INT) {
186         $param = (int)$param;            // Convert to integer
187     }
189     if ($options & PARAM_ALPHA) {        // Remove everything not a-z
190         $param = eregi_replace('[^a-z]', '', $param);
191     }
193     if ($options & PARAM_NOTAGS) {       // Strip all tags completely
194         $param = strip_tags($param);
195     }
197     if ($options & PARAM_FILE) {         // Strip all suspicious characters from filename
198         $param = clean_param($param, PARAM_PATH);
199         $pos = strrpos($param,'/');
200         if ($pos !== FALSE) {
201             $param = substr($param, $pos+1);
202         }
203         if ($param === '.' or $param === ' ') {
204             $param = '';
205         }
206     }
208     if ($options & PARAM_PATH) {         // Strip all suspicious characters from file path
209         $param = str_replace('\\\'', '\'', $param);
210         $param = str_replace('\\"', '"', $param);
211         $param = str_replace('\\', '/', $param);
212         $param = ereg_replace('[[:cntrl:]]|[<>"`\|\']', '', $param);
213         $param = ereg_replace('\.\.+', '', $param);
214         $param = ereg_replace('//+', '/', $param);
215     }
217     return $param;
220 /**
221  * For security purposes, this function will check that the currently
222  * given sesskey (passed as a parameter to the script or this function)
223  * matches that of the current user.
224  *
225  * @param string $sesskey optionally provided sesskey
226  * @return boolean
227  */
228 function confirm_sesskey($sesskey=NULL) {
229     global $USER;
231     if (!empty($USER->ignoresesskey)) {
232         return true;
233     }
235     if (empty($sesskey)) {
236         $sesskey = required_param('sesskey');  // Check script parameters
237     }
239     if (!isset($USER->sesskey)) {
240         return false;
241     }
243     return ($USER->sesskey === $sesskey);
247 /**
248  * Ensure that a variable is set
249  *
250  * If $var is undefined throw an error, otherwise return $var.
251  * This function will soon be made obsolete by {@link required_param()}
252  *
253  * @param mixed $var the variable which may be unset
254  * @param mixed $default the value to return if $var is unset
255  */
256 function require_variable($var) {
257     if (! isset($var)) {
258         error('A required parameter was missing');
259     }
263 /**
264  * Ensure that a variable is set
265  *
266  * If $var is undefined set it (by reference), otherwise return $var.
267  * This function will soon be made obsolete by {@link optional_param()}
268  *
269  * @param mixed $var the variable which may be unset
270  * @param mixed $default the value to return if $var is unset
271  */
272 function optional_variable(&$var, $default=0) {
273     if (! isset($var)) {
274         $var = $default;
275     }
278 /**
279  * Set a key in global configuration
280  *
281  * Set a key/value pair in both this session's {@link $CFG} global variable
282  * and in the 'config' database table for future sessions.
283  *
284  * @param string $name the key to set
285  * @param string $value the value to set
286  * @uses $CFG
287  * @return bool
288  */
289 function set_config($name, $value) {
290 /// No need for get_config because they are usually always available in $CFG
292     global $CFG;
295     $CFG->$name = $value;  // So it's defined for this invocation at least
297     if (get_field('config', 'name', 'name', $name)) {
298         return set_field('config', 'value', $value, 'name', $name);
299     } else {
300         $config->name = $name;
301         $config->value = $value;
302         return insert_record('config', $config);
303     }
306 /**
307  * Refresh current $USER session global variable with all their current preferences.
308  * @uses $USER
309  */
310 function reload_user_preferences() {
312     global $USER;
314     if(empty($USER) || empty($USER->id)) {
315         return false;
316     }
318     unset($USER->preference);
320     if ($preferences = get_records('user_preferences', 'userid', $USER->id)) {
321         foreach ($preferences as $preference) {
322             $USER->preference[$preference->name] = $preference->value;
323         }
324     } else {
325             //return empty preference array to hold new values
326             $USER->preference = array();
327     }
330 /**
331  * Sets a preference for the current user
332  * Optionally, can set a preference for a different user object
333  * @uses $USER
334  * @todo Add a better description and include usage examples.
335  * @param string $name The key to set as preference for the specified user
336  * @param string $value The value to set forthe $name key in the specified user's record
337  * @param int $userid A moodle user ID
338  * @todo Add inline links to $USER and user functions in above line.
339  * @return boolean
340  */
341 function set_user_preference($name, $value, $otheruser=NULL) {
343     global $USER;
345     if (empty($otheruser)){
346         if (!empty($USER) && !empty($USER->id)) {
347             $userid = $USER->id;
348         } else {
349             return false;
350         }
351     } else {
352         $userid = $otheruser;
353     }
355     if (empty($name)) {
356         return false;
357     }
359     if ($preference = get_record('user_preferences', 'userid', $userid, 'name', $name)) {
360         if (set_field('user_preferences', 'value', $value, 'id', $preference->id)) {
361             if (empty($otheruser) and !empty($USER)) {
362                 $USER->preference[$name] = $value;
363             }
364             return true;
365         } else {
366             return false;
367         }
369     } else {
370         $preference->userid = $userid;
371         $preference->name   = $name;
372         $preference->value  = (string)$value;
373         if (insert_record('user_preferences', $preference)) {
374             if (empty($otheruser) and !empty($USER)) {
375                 $USER->preference[$name] = $value;
376             }
377             return true;
378         } else {
379             return false;
380         }
381     }
384 /**
385  * Unsets a preference completely by deleting it from the database
386  * Optionally, can set a preference for a different user id
387  * @uses $USER
388  * @param string  $name The key to unset as preference for the specified user
389  * @param int $userid A moodle user ID
390  * @return boolean
391  */
392 function unset_user_preference($name, $userid=NULL) {
394     global $USER;
396     if (empty($userid)){
397         if(!empty($USER) && !empty($USER->id)) {
398             $userid = $USER->id;
399         }
400         else {
401             return false;
402         }
403     }
405     return delete_records('user_preferences', 'userid', $userid, 'name', $name);
409 /**
410  * Sets a whole array of preferences for the current user
411  * @param array $prefarray An array of key/value pairs to be set
412  * @param int $userid A moodle user ID
413  * @return boolean
414  */
415 function set_user_preferences($prefarray, $userid=NULL) {
417     global $USER;
419     if (!is_array($prefarray) or empty($prefarray)) {
420         return false;
421     }
423     if (empty($userid)){
424         if(!empty($USER) && !empty($USER->id)) {
425             $userid = $USER->id;
426         }
427         else {
428             return false;
429         }
430     }
432     $return = true;
433     foreach ($prefarray as $name => $value) {
434         // The order is important; if the test for return is done first, then
435         // if one function call fails all the remaining ones will be "optimized away"
436         $return = set_user_preference($name, $value, $userid) and $return;
437     }
438     return $return;
441 /**
442  * If no arguments are supplied this function will return
443  * all of the current user preferences as an array.
444  * If a name is specified then this function
445  * attempts to return that particular preference value.  If
446  * none is found, then the optional value $default is returned,
447  * otherwise NULL.
448  * @param string $name Name of the key to use in finding a preference value
449  * @param string $default Value to be returned if the $name key is not set in the user preferences
450  * @param int $userid A moodle user ID
451  * @uses $USER
452  * @return string
453  */
454 function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {
456     global $USER;
458     if (empty($userid)) {   // assume current user
459         if (empty($USER->preference)) {
460             return $default;              // Default value (or NULL)
461         }
462         if (empty($name)) {
463             return $USER->preference;     // Whole array
464         }
465         if (!isset($USER->preference[$name])) {
466             return $default;              // Default value (or NULL)
467         }
468         return $USER->preference[$name];  // The single value
470     } else {
471         $preference = get_records_menu('user_preferences', 'userid', $userid, 'name', 'name,value');
473         if (empty($name)) {
474             return $preference;
475         }
476         if (!isset($preference[$name])) {
477             return $default;              // Default value (or NULL)
478         }
479         return $preference[$name];        // The single value
480     }
484 /// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
486 /**
487  * Given date parts in user time produce a GMT timestamp.
488  *
489  * @param int $year The year part to create timestamp of.
490  * @param int $month The month part to create timestamp of.
491  * @param int $day The day part to create timestamp of.
492  * @param int $hour The hour part to create timestamp of.
493  * @param int $minute The minute part to create timestamp of.
494  * @param int $second The second part to create timestamp of.
495  * @param int $timezone ?
496  * @return ?
497  * @todo Finish documenting this function
498  */
499 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
501     $timezone = get_user_timezone($timezone);
503     if (abs($timezone) > 13) {
504         $time = mktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year, 0);
505     } else {
506         $time = gmmktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year, 0);
507         $time = usertime($time, $timezone);  // This is GMT
508     }
510     if(!$applydst) {
511         return $time;
512     }
514     return $time;
516     /*
517     // WARNING: BUG: TODO: This is buggy, but it will do for testing purposes
518     if(($dstid = get_user_preferences('calendar_dstpreset')) !== NULL) {
519         $preset = get_record('dst_preset', 'id', $dstid);
520         if($time > $preset->last_change && $time < $preset->next_change) {
521             return $time;
522         }
524         // We need to find out what's going on...
525         $nowuserdate = usergetdate($time);
527         $changes = calendar_dst_changes_for_year($year, $preset);
528         if($time < $changes['activate'] || $time > $changes['deactivate']) {
529             // DST will be off at that time
530             if($preset->current_offset != 0) {
531                 print_object('Uncompensated time was:');
532                 print_object(usergetdate($time));
533                 $time += $preset->apply_offset * 60;
534                 print_object('Compensated time is:');
535                 print_object(usergetdate($time));
536             }
537         }
538         else {
539             // DST will be on at that time
540             if($preset->current_offset == 0) {
541                 print_object('Uncompensated time was:');
542                 print_object(usergetdate($time));
543                 $time -= $preset->apply_offset * 60;
544                 print_object('Compensated time is:');
545                 print_object(usergetdate($time));
546             }
547         }
548         
549         return $time;
550     }
551     */
554 /**
555  * Given an amount of time in seconds, returns string
556  * formatted nicely as months, days, hours etc as needed
557  *
558  * @uses MINSECS
559  * @uses HOURSECS
560  * @uses DAYSECS
561  * @param int $totalsecs ?
562  * @param array $str ?
563  * @return string
564  * @todo Finish documenting this function
565  */
566  function format_time($totalsecs, $str=NULL) {
568     $totalsecs = abs($totalsecs);
570     if (!$str) {  // Create the str structure the slow way
571         $str->day   = get_string('day');
572         $str->days  = get_string('days');
573         $str->hour  = get_string('hour');
574         $str->hours = get_string('hours');
575         $str->min   = get_string('min');
576         $str->mins  = get_string('mins');
577         $str->sec   = get_string('sec');
578         $str->secs  = get_string('secs');
579     }
581     $days      = floor($totalsecs/DAYSECS);
582     $remainder = $totalsecs - ($days*DAYSECS);
583     $hours     = floor($remainder/HOURSECS);
584     $remainder = $remainder - ($hours*HOURSECS);
585     $mins      = floor($remainder/MINSECS);
586     $secs      = $remainder - ($mins*MINSECS);
588     $ss = ($secs == 1)  ? $str->sec  : $str->secs;
589     $sm = ($mins == 1)  ? $str->min  : $str->mins;
590     $sh = ($hours == 1) ? $str->hour : $str->hours;
591     $sd = ($days == 1)  ? $str->day  : $str->days;
593     $odays = '';
594     $ohours = '';
595     $omins = '';
596     $osecs = '';
598     if ($days)  $odays  = $days .' '. $sd;
599     if ($hours) $ohours = $hours .' '. $sh;
600     if ($mins)  $omins  = $mins .' '. $sm;
601     if ($secs)  $osecs  = $secs .' '. $ss;
603     if ($days)  return $odays .' '. $ohours;
604     if ($hours) return $ohours .' '. $omins;
605     if ($mins)  return $omins .' '. $osecs;
606     if ($secs)  return $osecs;
607     return get_string('now');
610 /**
611  * Returns a formatted string that represents a date in user time
612  * <b>WARNING: note that the format is for strftime(), not date().</b>
613  * Because of a bug in most Windows time libraries, we can't use
614  * the nicer %e, so we have to use %d which has leading zeroes.
615  * A lot of the fuss in the function is just getting rid of these leading
616  * zeroes as efficiently as possible.
617  *
618  * If parameter fixday = true (default), then take off leading
619  * zero from %d, else mantain it.
620  *
621  * @uses HOURSECS
622  * @param  int $date ?
623  * @param string $format ?
624  * @param int $timezone ?
625  * @param boolean $fixday If true (default) then the leading
626  * zero from %d is removed. If false then the leading zero is mantained.
627  * @return string
628  * @todo Finish documenting this function
629  */
630 function userdate($date, $format='', $timezone=99, $fixday = true) {
632     if ($format == '') {
633         $format = get_string('strftimedaydatetime');
634     }
636     $formatnoday = str_replace('%d', 'DD', $format);
637     if ($fixday) {
638         $fixday = ($formatnoday != $format);
639     }
641     $timezone = get_user_timezone($timezone);
643     if (abs($timezone) > 13) {
644         if ($fixday) {
645             $datestring = strftime($formatnoday, $date);
646             $daystring  = str_replace(' 0', '', strftime(" %d", $date));
647             $datestring = str_replace('DD', $daystring, $datestring);
648         } else {
649             $datestring = strftime($format, $date);
650         }
651     } else {
652         $date = $date + (int)($timezone * HOURSECS);
653         if ($fixday) {
654             $datestring = gmstrftime($formatnoday, $date);
655             $daystring  = str_replace(' 0', '', gmstrftime(" %d", $date));
656             $datestring = str_replace('DD', $daystring, $datestring);
657         } else {
658             $datestring = gmstrftime($format, $date);
659         }
660     }
662     return $datestring;
665 /**
666  * Given a $date timestamp in GMT (seconds since epoch),
667  * returns an array that represents the date in user time
668  *
669  * @uses HOURSECS
670  * @param  int $date Timestamp in GMT
671  * @param int $timezone ?
672  * @return array An array that represents the date in user time
673  * @todo Finish documenting this function
674  */
675 function usergetdate($date, $timezone=99) {
677     $timezone = get_user_timezone($timezone);
679     if (abs($timezone) > 13) {
680         return getdate($date);
681     }
682     //There is no gmgetdate so I have to fake it...
683     $date = $date + (int)($timezone * HOURSECS);
685     // This is independent of the server's TZ settings,
686     // unlike gmstrftime. It's also a bit faster this way.
687     list(
688         $getdate['seconds'],
689         $getdate['minutes'],
690         $getdate['hours'],
691         $getdate['mday'],
692         $getdate['mon'],
693         $getdate['year'],
694         $getdate['wday'],
695         $getdate['yday'],
696         $getdate['weekday'],
697         $getdate['month']
698     ) = explode(' ', gmdate('s i H d m Y w z l F', $date));
700     return $getdate;
703 /**
704  * Given a GMT timestamp (seconds since epoch), offsets it by
705  * the timezone.  eg 3pm in India is 3pm GMT - 7 * 3600 seconds
706  *
707  * @uses HOURSECS
708  * @param  int $date Timestamp in GMT
709  * @param int $timezone ?
710  * @return int
711  * @todo Finish documenting this function
712  */
713 function usertime($date, $timezone=99) {
715     $timezone = get_user_timezone($timezone);
716     if (abs($timezone) > 13) {
717         return $date;
718     }
719     return $date - (int)($timezone * HOURSECS);
722 /**
723  * Given a time, return the GMT timestamp of the most recent midnight
724  * for the current user.
725  *
726  * @param  int $date Timestamp in GMT
727  * @param int $timezone ?
728  * @return ?
729  * @todo Finish documenting this function. Is timezone an int or float?
730  */
731 function usergetmidnight($date, $timezone=99) {
733     $timezone = get_user_timezone($timezone);
734     $userdate = usergetdate($date, $timezone);
736     if (abs($timezone) > 13) {
737         return mktime(0, 0, 0, $userdate['mon'], $userdate['mday'], $userdate['year']);
738     }
740     $timemidnight = gmmktime (0, 0, 0, $userdate['mon'], $userdate['mday'], $userdate['year']);
741     return usertime($timemidnight, $timezone); // Time of midnight of this user's day, in GMT
745 /**
746  * Returns a string that prints the user's timezone
747  *
748  * @param float $timezone The user's timezone
749  * @return string
750  * @todo is $timezone an int or a float?
751  */
752 function usertimezone($timezone=99) {
754     $timezone = get_user_timezone($timezone);
756     if (abs($timezone) > 13) {
757         return 'server time';
758     }
759     if (abs($timezone) < 0.5) {
760         return 'GMT';
761     }
762     if ($timezone > 0) {
763         return 'GMT+'. $timezone;
764     } else {
765         return 'GMT'. $timezone;
766     }
769 /**
770  * Returns a float which represents the user's timezone difference from GMT in hours
771  * Checks various settings and picks the most dominant of those which have a value
772  *
773  * @uses $CFG
774  * @uses $USER
775  * @param int $tz The user's timezone
776  * @return int
777  * @todo is $tz an int or a float?
778  */
779 function get_user_timezone($tz = 99) {
781     // Variables declared explicitly global here so that if we add
782     // something later we won't forget to global it...
783     $timezones = array(
784         isset($GLOBALS['USER']->timezone) ? $GLOBALS['USER']->timezone : 99,
785         isset($GLOBALS['CFG']->timezone) ? $GLOBALS['CFG']->timezone : 99,
786         );
787     while($tz == 99 && $next = each($timezones)) {
788         $tz = (float)$next['value'];
789     }
791     return $tz;
794 /// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////
796 // Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
797 // if one does not already exist, but does not overwrite existing sesskeys. Returns the
798 // sesskey string if $USER exists, or boolean false if not.
799 function set_user_sesskey() {
800     global $USER;
802     if(!isset($USER)) {
803         return false;
804     }
806     if (empty($USER->sesskey)) {
807         $USER->sesskey = random_string(10);
808     }
810     return $USER->sesskey;
813 /**
814  * This function checks that the current user is logged in, and optionally
815  * whether they are "logged in" or allowed to be in a particular course.
816  * If not, then it redirects them to the site login or course enrolment.
817  * $autologinguest determines whether visitors should automatically be
818  * logged in as guests provide {@link $CFG}->autologinguests is set to 1
819  *
820  * @uses $CFG
821  * @uses $SESSION
822  * @uses $USER
823  * @uses $FULLME
824  * @uses SITEID
825  * @uses $MoodleSession
826  * @param int $courseid The course in question
827  * @param boolean $autologinguest ?
828  * @todo Finish documenting this function
829  */
830 function require_login($courseid=0, $autologinguest=true) {
832     global $CFG, $SESSION, $USER, $FULLME, $MoodleSession;
834     // First check that the user is logged in to the site.
835     if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
836         $SESSION->wantsurl = $FULLME;
837         if (!empty($_SERVER['HTTP_REFERER'])) {
838             $SESSION->fromurl  = $_SERVER['HTTP_REFERER'];
839         }
840         $USER = NULL;
841         if ($autologinguest and $CFG->autologinguests and $courseid and get_field('course','guest','id',$courseid)) {
842             $loginguest = '?loginguest=true';
843         } else {
844             $loginguest = '';
845         }
846         if (empty($CFG->loginhttps)) {
847             redirect($CFG->wwwroot .'/login/index.php'. $loginguest);
848         } else {
849             $wwwroot = str_replace('http','https', $CFG->wwwroot);
850             redirect($wwwroot .'/login/index.php'. $loginguest);
851         }
852         die;
853     }
855     // check whether the user should be changing password
856     // reload_user_preferences();    // Why is this necessary?  Seems wasteful.  - MD
857     if (!empty($USER->preference['auth_forcepasswordchange'])){
858         if (is_internal_auth() || $CFG->{'auth_'.$USER->auth.'_stdchangepassword'}){
859             redirect($CFG->wwwroot .'/login/change_password.php');
860         } elseif($CFG->changepassword) {
861             redirect($CFG->changepassword);
862         } else {
863             error('You cannot proceed without changing your password.
864                    However there is no available page for changing it.
865                    Please contact your Moodle Administrator.');
866         }
867     }
869     // Check that the user account is properly set up
870     if (user_not_fully_set_up($USER)) {
871         redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
872         die;
873     }
875     // Make sure current IP matches the one for this session (if required)
876     if (!empty($CFG->tracksessionip)) {
877         if ($USER->sessionIP != md5(getremoteaddr())) {
878             error(get_string('sessionipnomatch', 'error'));
879         }
880     }
882     // Make sure the USER has a sesskey set up.  Used for checking script parameters.
883     set_user_sesskey();
885     // Check that the user has agreed to a site policy if there is one
886     if (!empty($CFG->sitepolicy)) {
887         if (!$USER->policyagreed) {
888             $SESSION->wantsurl = $FULLME;
889             redirect($CFG->wwwroot .'/user/policy.php');
890             die;
891         }
892     }
894     // Next, check if the user can be in a particular course
895     if ($courseid) {
896         if ($courseid == SITEID) {
897             return;   // Anyone can be in the site course
898         }
899         if (!empty($USER->student[$courseid]) or !empty($USER->teacher[$courseid]) or !empty($USER->admin)) {
900             if (isset($USER->realuser)) {   // Make sure the REAL person can also access this course
901                 if (!isteacher($courseid, $USER->realuser)) {
902                     print_header();
903                     notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
904                 }
905             }
906             return;   // user is a member of this course.
907         }
908         if (! $course = get_record('course', 'id', $courseid)) {
909             error('That course doesn\'t exist');
910         }
911         if (!$course->visible) {
912             print_header();
913             notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
914         }
915         if ($USER->username == 'guest') {
916             switch ($course->guest) {
917                 case 0: // Guests not allowed
918                     print_header();
919                     notice(get_string('guestsnotallowed', '', $course->fullname));
920                     break;
921                 case 1: // Guests allowed
922                     return;
923                 case 2: // Guests allowed with key (drop through)
924                     break;
925             }
926         }
928         // Currently not enrolled in the course, so see if they want to enrol
929         $SESSION->wantsurl = $FULLME;
930         redirect($CFG->wwwroot .'/course/enrol.php?id='. $courseid);
931         die;
932     }
935 /**
936  * This is a weaker version of {@link require_login()} which only requires login
937  * when called from within a course rather than the site page, unless
938  * the forcelogin option is turned on.
939  *
940  * @uses $CFG
941  * @param int $courseid The course in question
942  * @param boolean $autologinguest ?
943  * @todo Finish documenting this function
944  */
945 function require_course_login($course, $autologinguest=true) {
946     global $CFG;
947     if ($CFG->forcelogin) {
948       require_login();
949     }
950     if ($course->category) {
951       require_login($course->id, $autologinguest);
952     }
955 /**
956  * Modify the user table by setting the currently logged in user's
957  * last login to now.
958  *
959  * @uses $USER
960  * @return boolean
961  */
962 function update_user_login_times() {
963     global $USER;
965     $USER->lastlogin = $user->lastlogin = $USER->currentlogin;
966     $USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
968     $user->id = $USER->id;
970     return update_record('user', $user);
973 /**
974  * Determines if a user has completed setting up their account.
975  *
976  * @param user $user A {@link $USER} object to test for the existance of a valid name and email
977  * @return boolean
978  */
979 function user_not_fully_set_up($user) {
980     return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email)));
983 /**
984  * Keeps track of login attempts
985  *
986  * @uses $SESSION
987  */
988 function update_login_count() {
990     global $SESSION;
992     $max_logins = 10;
994     if (empty($SESSION->logincount)) {
995         $SESSION->logincount = 1;
996     } else {
997         $SESSION->logincount++;
998     }
1000     if ($SESSION->logincount > $max_logins) {
1001         unset($SESSION->wantsurl);
1002         error(get_string('errortoomanylogins'));
1003     }
1006 /**
1007  * Resets login attempts
1008  *
1009  * @uses $SESSION
1010  */
1011 function reset_login_count() {
1012     global $SESSION;
1014     $SESSION->logincount = 0;
1017 /**
1018  * check_for_restricted_user
1019  *
1020  * @uses $CFG
1021  * @uses $USER
1022  * @param string $username ?
1023  * @param string $redirect ?
1024  * @todo Finish documenting this function
1025  */
1026 function check_for_restricted_user($username=NULL, $redirect='') {
1027     global $CFG, $USER;
1029     if (!$username) {
1030         if (!empty($USER->username)) {
1031             $username = $USER->username;
1032         } else {
1033             return false;
1034         }
1035     }
1037     if (!empty($CFG->restrictusers)) {
1038         $names = explode(',', $CFG->restrictusers);
1039         if (in_array($username, $names)) {
1040             error(get_string('restricteduser', 'error', fullname($USER)), $redirect);
1041         }
1042     }
1045 /**
1046  * Determines if a user an admin
1047  *
1048  * @uses $USER
1049  * @param int $userid The id of the user as is found in the 'user' table
1050  * @staticvar array $admin ?
1051  * @staticvar array $nonadmins ?
1052  * @return boolean
1053  * @todo Complete documentation for this function
1054  */
1055 function isadmin($userid=0) {
1056     global $USER;
1057     static $admins = array();
1058     static $nonadmins = array();
1060     if (!$userid){
1061         if (empty($USER->id)) {
1062             return false;
1063         }
1064         $userid = $USER->id;
1065     }
1067     if (in_array($userid, $admins)) {
1068         return true;
1069     } else if (in_array($userid, $nonadmins)) {
1070         return false;
1071     } else if (record_exists('user_admins', 'userid', $userid)){
1072         $admins[] = $userid;
1073         return true;
1074     } else {
1075         $nonadmins[] = $userid;
1076         return false;
1077     }
1080 /**
1081  * Determines if a user is a teacher or an admin
1082  *
1083   * @uses $USER
1084  * @param int $courseid The id of the course that is being viewed, if any
1085  * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
1086  * @param boolean $includeadmin If true this function will return true when it encounters an admin user.
1087  * @return boolean
1088  * @todo Finish documenting this function
1089  */
1090 function isteacher($courseid=0, $userid=0, $includeadmin=true) {
1091     global $USER;
1093     if ($includeadmin and isadmin($userid)) {  // admins can do anything the teacher can
1094         return true;
1095     }
1097     if (!$userid) {
1098         if ($courseid) {
1099             return !empty($USER->teacher[$courseid]);
1100         }
1101         if (!isset($USER->id)) {
1102             return false;
1103         }
1104         $userid = $USER->id;
1105     }
1107     if (!$courseid) {
1108         return record_exists('user_teachers', 'userid', $userid);
1109     }
1111     return record_exists('user_teachers', 'userid', $userid, 'course', $courseid);
1114 /**
1115  * Determines if a user is allowed to edit a given course
1116  *
1117  * @uses $USER
1118  * @param int $courseid The id of the course that is being edited
1119  * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
1120  * @return boolean
1121  */
1122 function isteacheredit($courseid, $userid=0) {
1123     global $USER;
1125     if (isadmin($userid)) {  // admins can do anything
1126         return true;
1127     }
1129     if (!$userid) {
1130         return !empty($USER->teacheredit[$courseid]);
1131     }
1133     return get_field('user_teachers', 'editall', 'userid', $userid, 'course', $courseid);
1136 /**
1137  * Determines if a user can create new courses
1138  *
1139  * @uses $USER
1140  * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
1141  * @return boolean
1142  */
1143 function iscreator ($userid=0) {
1144     global $USER;
1145     if (empty($USER->id)) {
1146         return false;
1147     }
1148     if (isadmin($userid)) {  // admins can do anything
1149         return true;
1150     }
1151     if (empty($userid)) {
1152         return record_exists('user_coursecreators', 'userid', $USER->id);
1153     }
1155     return record_exists('user_coursecreators', 'userid', $userid);
1158 /**
1159  * Determines if a user is a student in the specified course
1160  *
1161  * If the course id specifies the site then the function determines
1162  * if the user is a confirmed and valid user of this site.
1163  *
1164  * @uses $USER
1165  * @uses $CFG
1166  * @uses SITEID
1167  * @param int $courseid The id of the course being tested
1168  * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
1169  * @return boolean
1170  */
1171 function isstudent($courseid, $userid=0) {
1172     global $USER, $CFG;
1174     if (empty($USER->id) and !$userid) {
1175         return false;
1176     }
1178     if ($courseid == SITEID) {
1179         if (!$userid) {
1180             $userid = $USER->id;
1181         }
1182         if (isguest($userid)) {
1183             return false;
1184         }
1185         // a site teacher can never be a site student
1186         if (isteacher($courseid, $userid)) {
1187             return false;
1188         }
1189         if ($CFG->allusersaresitestudents) {
1190             return record_exists('user', 'id', $userid);
1191         } else {
1192             return (record_exists('user_students', 'userid', $userid)
1193                      or record_exists('user_teachers', 'userid', $userid));
1194         }
1195     }
1197     if (!$userid) {
1198         return !empty($USER->student[$courseid]);
1199     }
1201   //  $timenow = time();   // todo:  add time check below
1203     return record_exists('user_students', 'userid', $userid, 'course', $courseid);
1206 /**
1207  * Determines if the specified user is logged in as guest.
1208  *
1209  * @uses $USER
1210  * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
1211  * @return boolean
1212  */
1213 function isguest($userid=0) {
1214     global $USER;
1216     if (!$userid) {
1217         if (empty($USER->username)) {
1218             return false;
1219         }
1220         return ($USER->username == 'guest');
1221     }
1223     return record_exists('user', 'id', $userid, 'username', 'guest');
1226 /**
1227  * Determines if the currently logged in user is in editing mode
1228  *
1229  * @uses $USER
1230  * @param int $courseid The id of the course being tested
1231  * @param user $user A {@link $USER} object. If null then the currently logged in user is used.
1232  * @return boolean
1233  */
1234 function isediting($courseid, $user=NULL) {
1235     global $USER;
1236     if (!$user){
1237         $user = $USER;
1238     }
1239     if (empty($user->editing)) {
1240         return false;
1241     }
1242     return ($user->editing and isteacher($courseid, $user->id));
1245 /**
1246  * Determines if the logged in user is currently moving an activity
1247  *
1248  * @uses $USER
1249  * @param int $courseid The id of the course being tested
1250  * @return boolean
1251  */
1252 function ismoving($courseid) {
1253     global $USER;
1255     if (!empty($USER->activitycopy)) {
1256         return ($USER->activitycopycourse == $courseid);
1257     }
1258     return false;
1261 /**
1262  * Given an object containing firstname and lastname
1263  * values, this function returns a string with the
1264  * full name of the person.
1265  * The result may depend on system settings
1266  * or language.  'override' will force both names
1267  * to be used even if system settings specify one.
1268  * @uses $CFG
1269  * @uses $SESSION
1270  * @param    type description
1271  * @todo Finish documenting this function
1272  */
1273 function fullname($user, $override=false) {
1275     global $CFG, $SESSION;
1277     if (!isset($user->firstname) and !isset($user->lastname)) {
1278         return '';
1279     }
1281     if (!empty($SESSION->fullnamedisplay)) {
1282         $CFG->fullnamedisplay = $SESSION->fullnamedisplay;
1283     }
1285     if ($CFG->fullnamedisplay == 'firstname lastname') {
1286         return $user->firstname .' '. $user->lastname;
1288     } else if ($CFG->fullnamedisplay == 'lastname firstname') {
1289         return $user->lastname .' '. $user->firstname;
1291     } else if ($CFG->fullnamedisplay == 'firstname') {
1292         if ($override) {
1293             return get_string('fullnamedisplay', '', $user);
1294         } else {
1295             return $user->firstname;
1296         }
1297     }
1299     return get_string('fullnamedisplay', '', $user);
1302 /**
1303  * Sets a moodle cookie with an encrypted string
1304  *
1305  * @uses $CFG
1306  * @uses DAYSECS
1307  * @uses HOURSECS
1308  * @param string $thing The string to encrypt and place in a cookie
1309  */
1310 function set_moodle_cookie($thing) {
1311     global $CFG;
1313     $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
1315     $days = 60;
1316     $seconds = DAYSECS*$days;
1318     setCookie($cookiename, '', time() - HOURSECS, '/');
1319     setCookie($cookiename, rc4encrypt($thing), time()+$seconds, '/');
1322 /**
1323  * Gets a moodle cookie with an encrypted string
1324  *
1325  * @uses $CFG
1326  * @return string
1327  */
1328 function get_moodle_cookie() {
1329     global $CFG;
1331     $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
1333     if (empty($_COOKIE[$cookiename])) {
1334         return '';
1335     } else {
1336         return rc4decrypt($_COOKIE[$cookiename]);
1337     }
1340 /**
1341  * Returns true if an internal authentication method is being used.
1342  * if method not specified then, global default is assumed
1343  *
1344  * @uses $CFG
1345  * @param string $auth Form of authentication required
1346  * @return boolean
1347  * @todo Outline auth types and provide code example
1348  */
1349 function is_internal_auth($auth='') {
1350 /// Returns true if an internal authentication method is being used.
1351 /// If auth not specified then global default is assumed
1353     global $CFG;
1355     if (empty($auth)) {
1356         $auth = $CFG->auth;
1357     }
1359     return ($auth == "email" || $auth == "none" || $auth == "manual");
1362 /**
1363  * Returns an array of user fields
1364  *
1365  * @uses $CFG
1366  * @uses $db
1367  * @return array User field/column names
1368  * @todo Finish documenting this function
1369  */
1370 function get_user_fieldnames() {
1372     global $CFG, $db;
1374     $fieldarray = $db->MetaColumnNames($CFG->prefix.'user');
1375     unset($fieldarray['ID']);
1377     return $fieldarray;
1380 /**
1381  * Creates a bare-bones user record
1382  *
1383  * @uses $CFG
1384  * @param string $username New user's username to add to record
1385  * @param string $password New user's password to add to record
1386  * @param string $auth Form of authentication required
1387  * @return user A {@link $USER} object
1388  * @todo Outline auth types and provide code example
1389  */
1390 function create_user_record($username, $password, $auth='') {
1391     global $CFG;
1393     //just in case check text case
1394     $username = trim(moodle_strtolower($username));
1396     if (function_exists('auth_get_userinfo')) {
1397         if ($newinfo = auth_get_userinfo($username)) {
1398             $newinfo = truncate_userinfo($newinfo);
1399             foreach ($newinfo as $key => $value){
1400                 $newuser->$key = addslashes(stripslashes($value)); // Just in case
1401             }
1402         }
1403     }
1405     if (!empty($newuser->email)) {
1406         if (email_is_not_allowed($newuser->email)) {
1407             unset($newuser->email);
1408         }
1409     }
1411     $newuser->auth = (empty($auth)) ? $CFG->auth : $auth;
1412     $newuser->username = $username;
1413     $newuser->password = md5($password);
1414     $newuser->lang = $CFG->lang;
1415     $newuser->confirmed = 1;
1416     $newuser->lastIP = getremoteaddr();
1417     $newuser->timemodified = time();
1419     if (insert_record('user', $newuser)) {
1420          $user = get_user_info_from_db('username', $newuser->username);
1421          if($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'}){
1422              set_user_preference('auth_forcepasswordchange', 1, $user);
1423          }
1424          return $user;
1425     }
1426     return false;
1429 /**
1430  * Will update a local user record from an external source
1431  *
1432  * @uses $CFG
1433  * @param string $username New user's username to add to record
1434  * @return user A {@link $USER} object
1435  */
1436 function update_user_record($username) {
1437     global $CFG;
1439     if (function_exists('auth_get_userinfo')) {
1440         $username = trim(moodle_strtolower($username)); /// just in case check text case
1442         if ($newinfo = auth_get_userinfo($username)) {
1443             foreach ($newinfo as $key => $value){
1444                 if (!empty($CFG->{'auth_user_' . $key. '_updatelocal'})) {
1445                     $value = addslashes(stripslashes($value));   // Just in case
1446                     set_field('user', $key, $value, 'username', $username);
1447                 }
1448             }
1449         }
1450     }
1451     return get_user_info_from_db('username', $username);
1454 function truncate_userinfo($info) {
1455 /// will truncate userinfo as it comes from auth_get_userinfo (from external auth)
1456 /// which may have large fields
1458     // define the limits
1459     $limit = array(
1460                     'username'    => 100,
1461                     'idnumber'    =>  12,
1462                     'firstname'   =>  20,
1463                     'lastname'    =>  20,
1464                     'email'       => 100,
1465                     'icq'         =>  15,
1466                     'phone1'      =>  20,
1467                     'phone2'      =>  20,
1468                     'institution' =>  40,
1469                     'department'  =>  30,
1470                     'address'     =>  70,
1471                     'city'        =>  20,
1472                     'country'     =>   2,
1473                     'url'         => 255,
1474                     );
1476     // apply where needed
1477     foreach (array_keys($info) as $key) {
1478         if (!empty($limit[$key])) {
1479             $info[$key] = substr($info[$key],0, $limit[$key]);
1480         }
1481     }
1483     return $info;
1486 /**
1487  * Retrieve the guest user object
1488  *
1489  * @uses $CFG
1490  * @return user A {@link $USER} object
1491  */
1492 function guest_user() {
1493     global $CFG;
1495     if ($newuser = get_record('user', 'username', 'guest')) {
1496         $newuser->loggedin = true;
1497         $newuser->confirmed = 1;
1498         $newuser->site = $CFG->wwwroot;
1499         $newuser->lang = $CFG->lang;
1500         $newuser->lastIP = getremoteaddr();
1501     }
1503     return $newuser;
1506 /**
1507  * Given a username and password, this function looks them
1508  * up using the currently selected authentication mechanism,
1509  * and if the authentication is successful, it returns a
1510  * valid $user object from the 'user' table.
1511  *
1512  * Uses auth_ functions from the currently active auth module
1513  *
1514  * @uses $CFG
1515  * @param string $username  User's username
1516  * @param string $password  User's password
1517  * @return user|flase A {@link $USER} object or false if error
1518  */
1519 function authenticate_user_login($username, $password) {
1521     global $CFG;
1523     $md5password = md5($password);
1525     // First try to find the user in the database
1527     $user = get_user_info_from_db('username', $username);
1529     // Sort out the authentication method we are using.
1531     if (empty($CFG->auth)) {
1532         $CFG->auth = 'manual';     // Default authentication module
1533     }
1535     if (empty($user->auth)) {      // For some reason it isn't set yet
1536         if (isadmin($user->id) or isguest($user->id)) {
1537             $auth = 'manual';    // Always assume these guys are internal
1538         } else {
1539             $auth = $CFG->auth;  // Normal users default to site method
1540         }
1541         // update user record from external DB
1542         if ($user->auth != 'manual' && $user->auth != 'email') {
1543             $user = update_user_record($username);
1544         }
1545     } else {
1546         $auth = $user->auth;
1547     }
1549     if (detect_munged_arguments($auth, 0)) {   // For safety on the next require
1550         return false;
1551     }
1553     if (!file_exists($CFG->dirroot .'/auth/'. $auth .'/lib.php')) {
1554         $auth = 'manual';    // Can't find auth module, default to internal
1555     }
1557     require_once($CFG->dirroot .'/auth/'. $auth .'/lib.php');
1559     if (auth_user_login($username, $password)) {  // Successful authentication
1560         if ($user) {                              // User already exists in database
1561             if (empty($user->auth)) {             // For some reason auth isn't set yet
1562                 set_field('user', 'auth', $auth, 'username', $username);
1563             }
1564             if ($md5password <> $user->password) {   // Update local copy of password for reference
1565                 set_field('user', 'password', $md5password, 'username', $username);
1566             }
1567             if (!is_internal_auth()) {            // update user record from external DB
1568                 $user = update_user_record($username);
1569             }
1570         } else {
1571             $user = create_user_record($username, $password, $auth);
1572         }
1574         if (function_exists('auth_iscreator')) {    // Check if the user is a creator
1575             $useriscreator = auth_iscreator($username);
1576             if (!is_null($useriscreator)) {
1577                 if ($useriscreator) {
1578                     if (! record_exists('user_coursecreators', 'userid', $user->id)) {
1579                         $cdata->userid = $user->id;
1580                         if (! insert_record('user_coursecreators', $cdata)) {
1581                             error('Cannot add user to course creators.');
1582                         }
1583                     }
1584                 } else {
1585                     if (record_exists('user_coursecreators', 'userid', $user->id)) {
1586                         if (! delete_records('user_coursecreators', 'userid', $user->id)) {
1587                             error('Cannot remove user from course creators.');
1588                         }
1589                     }
1590                 }
1591             }
1592         }
1593         $user->sessionIP = md5(getremoteaddr());   // Store the current IP in the session
1594         return $user;
1596     } else {
1597         add_to_log(0, 'login', 'error', $_SERVER['HTTP_REFERER'], $username);
1598         error_log('[client '.$_SERVER['REMOTE_ADDR']."]\t$CFG->wwwroot\tFailed Login:\t$username\t".$_SERVER['HTTP_USER_AGENT']);
1599         return false;
1600     }
1603 /**
1604  * Enrols (or re-enrols) a student in a given course
1605  *
1606  * @param int $courseid The id of the course that is being viewed
1607  * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
1608  * @param int $timestart ?
1609  * @param int $timeend ?
1610  * @return boolean
1611  * @todo Finish documenting this function
1612  */
1613 function enrol_student($userid, $courseid, $timestart=0, $timeend=0, $enrol='manual') {
1615     global $CFG;
1617     if (!$course = get_record('course', 'id', $courseid)) {  // Check course
1618         return false;
1619     }
1620     if (!$user = get_record('user', 'id', $userid)) {        // Check user
1621         return false;
1622     }
1623     if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
1624         $student->timestart = $timestart;
1625         $student->timeend = $timeend;
1626         $student->time = time();
1627         $student->enrol = $enrol;
1628         return update_record('user_students', $student);
1630     } else {
1631         require_once("$CFG->dirroot/mod/forum/lib.php");
1632         forum_add_user($userid, $courseid);
1634         $student->userid = $userid;
1635         $student->course = $courseid;
1636         $student->timestart = $timestart;
1637         $student->timeend = $timeend;
1638         $student->time = time();
1639         $student->enrol = $enrol;
1640         return insert_record('user_students', $student);
1641     }
1644 /**
1645  * Unenrols a student from a given course
1646  *
1647  * @param int $courseid The id of the course that is being viewed, if any
1648  * @param int $userid The id of the user that is being tested against.
1649  * @return boolean
1650  */
1651 function unenrol_student($userid, $courseid=0) {
1653     if ($courseid) {
1654         /// First delete any crucial stuff that might still send mail
1655         if ($forums = get_records('forum', 'course', $courseid)) {
1656             foreach ($forums as $forum) {
1657                 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
1658             }
1659         }
1660         if ($groups = get_groups($courseid, $userid)) {
1661             foreach ($groups as $group) {
1662                 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
1663             }
1664         }
1665         return delete_records('user_students', 'userid', $userid, 'course', $courseid);
1667     } else {
1668         delete_records('forum_subscriptions', 'userid', $userid);
1669         delete_records('groups_members', 'userid', $userid);
1670         return delete_records('user_students', 'userid', $userid);
1671     }
1674 /**
1675  * Add a teacher to a given course
1676  *
1677   * @uses $USER
1678  * @param int $courseid The id of the course that is being viewed, if any
1679  * @param int $userid The id of the user that is being tested against. Set this to 0 if you would just like to test against the currently logged in user.
1680  * @param int $editall ?
1681  * @param string $role ?
1682  * @param int $timestart ?
1683  * @param int $timeend ?
1684  * @return boolean
1685  * @todo Finish documenting this function
1686  */
1687 function add_teacher($userid, $courseid, $editall=1, $role='', $timestart=0, $timeend=0, $enrol='manual') {
1688     global $CFG;
1690     if ($teacher = get_record('user_teachers', 'userid', $userid, 'course', $courseid)) {
1691         $newteacher = NULL;
1692         $newteacher->id = $teacher->id;
1693         $newteacher->editall = $editall;
1694         $newteacher->enrol = $enrol;
1695         if ($role) {
1696             $newteacher->role = $role;
1697         }
1698         if ($timestart) {
1699             $newteacher->timestart = $timestart;
1700         }
1701         if ($timeend) {
1702             $newteacher->timeend = $timeend;
1703         }
1704         return update_record('user_teachers', $newteacher);
1705     }
1707     if (!record_exists('user', 'id', $userid)) {
1708         return false;   // no such user
1709     }
1711     if (!record_exists('course', 'id', $courseid)) {
1712         return false;   // no such course
1713     }
1715     $teacher = NULL;
1716     $teacher->userid  = $userid;
1717     $teacher->course  = $courseid;
1718     $teacher->editall = $editall;
1719     $teacher->role    = $role;
1720     $teacher->timemodified = time();
1721     $newteacher->timestart = $timestart;
1722     $newteacher->timeend = $timeend;
1723     if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
1724         $teacher->timestart = $student->timestart;
1725         $teacher->timeend = $student->timeend;
1726         $teacher->timeaccess = $student->timeaccess;
1727     }
1729     if (record_exists('user_teachers', 'course', $courseid)) {
1730         $teacher->authority = 2;
1731     } else {
1732         $teacher->authority = 1;
1733     }
1734     delete_records('user_students', 'userid', $userid, 'course', $courseid); // Unenrol as student
1736     /// Add forum subscriptions for new users
1737     require_once('../mod/forum/lib.php');
1738     forum_add_user($userid, $courseid);
1740     return insert_record('user_teachers', $teacher);
1744 /**
1745  * Removes a teacher from a given course (or ALL courses)
1746  * Does not delete the user account
1747  *
1748  * @param int $courseid The id of the course that is being viewed, if any
1749  * @param int $userid The id of the user that is being tested against.
1750  * @return boolean
1751  */
1752 function remove_teacher($userid, $courseid=0) {
1753     if ($courseid) {
1754         /// First delete any crucial stuff that might still send mail
1755         if ($forums = get_records('forum', 'course', $courseid)) {
1756             foreach ($forums as $forum) {
1757                 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
1758             }
1759         }
1761         /// Next if the teacher is not registered as a student, but is
1762         /// a member of a group, remove them from the group.
1763         if (!isstudent($courseid, $userid)) {
1764             if ($groups = get_groups($courseid, $userid)) {
1765                 foreach ($groups as $group) {
1766                     delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
1767                 }
1768             }
1769         }
1771         return delete_records('user_teachers', 'userid', $userid, 'course', $courseid);
1772     } else {
1773         delete_records('forum_subscriptions', 'userid', $userid);
1774         return delete_records('user_teachers', 'userid', $userid);
1775     }
1778 /**
1779  * Add a creator to the site
1780  *
1781  * @param int $userid The id of the user that is being tested against.
1782  * @return boolean
1783  */
1784 function add_creator($userid) {
1786     if (!record_exists('user_admins', 'userid', $userid)) {
1787         if (record_exists('user', 'id', $userid)) {
1788             $creator->userid = $userid;
1789             return insert_record('user_coursecreators', $creator);
1790         }
1791         return false;
1792     }
1793     return true;
1796 /**
1797  * Remove a creator from a site
1798  *
1799   * @uses $db
1800  * @param int $userid The id of the user that is being tested against.
1801  * @return boolean
1802  */
1803 function remove_creator($userid) {
1804     global $db;
1806     return delete_records('user_coursecreators', 'userid', $userid);
1809 /**
1810  * Add an admin to a site
1811  *
1812  * @uses SITEID
1813  * @param int $userid The id of the user that is being tested against.
1814  * @return boolean
1815  */
1816 function add_admin($userid) {
1818     if (!record_exists('user_admins', 'userid', $userid)) {
1819         if (record_exists('user', 'id', $userid)) {
1820             $admin->userid = $userid;
1822             // any admin is also a teacher on the site course
1823             if (!record_exists('user_teachers', 'course', SITEID, 'userid', $userid)) {
1824                 if (!add_teacher($userid, SITEID)) {
1825                     return false;
1826                 }
1827             }
1829             return insert_record('user_admins', $admin);
1830         }
1831         return false;
1832     }
1833     return true;
1836 /**
1837  * Removes an admin from a site
1838  *
1839   * @uses $db
1840   * @uses SITEID
1841  * @param int $userid The id of the user that is being tested against.
1842  * @return boolean
1843  */
1844 function remove_admin($userid) {
1845     global $db;
1847     // remove also from the list of site teachers
1848     remove_teacher($userid, SITEID);
1850     return delete_records('user_admins', 'userid', $userid);
1853 /**
1854  * Clear a course out completely, deleting all content
1855  * but don't delete the course itself
1856  *
1857  * @uses $USER
1858  * @uses $SESSION
1859  * @uses $CFG
1860  * @param int $courseid The id of the course that is being viewed
1861  * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
1862  * @return boolean
1863  */
1864 function remove_course_contents($courseid, $showfeedback=true) {
1866     global $CFG, $THEME, $USER, $SESSION;
1868     $result = true;
1870     if (! $course = get_record('course', 'id', $courseid)) {
1871         error('Course ID was incorrect (can\'t find it)');
1872     }
1874     $strdeleted = get_string('deleted');
1876     // First delete every instance of every module
1878     if ($allmods = get_records('modules') ) {
1879         foreach ($allmods as $mod) {
1880             $modname = $mod->name;
1881             $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
1882             $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
1883             $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
1884             $count=0;
1885             if (file_exists($modfile)) {
1886                 include_once($modfile);
1887                 if (function_exists($moddelete)) {
1888                     if ($instances = get_records($modname, 'course', $course->id)) {
1889                         foreach ($instances as $instance) {
1890                             if ($moddelete($instance->id)) {
1891                                 $count++;
1892                             } else {
1893                                 notify('Could not delete '. $modname .' instance '. $instance->id .' ('. $instance->name .')');
1894                                 $result = false;
1895                             }
1896                         }
1897                     }
1898                 } else {
1899                     notify('Function '. $moddelete() .'doesn\'t exist!');
1900                     $result = false;
1901                 }
1903                 if (function_exists($moddeletecourse)) {
1904                     $moddeletecourse($course);
1905                 }
1906             }
1907             if ($showfeedback) {
1908                 notify($strdeleted .' '. $count .' x '. $modname);
1909             }
1910         }
1911     } else {
1912         error('No modules are installed!');
1913     }
1915     // Delete any user stuff
1917     if (delete_records('user_students', 'course', $course->id)) {
1918         if ($showfeedback) {
1919             notify($strdeleted .' user_students');
1920         }
1921     } else {
1922         $result = false;
1923     }
1925     if (delete_records('user_teachers', 'course', $course->id)) {
1926         if ($showfeedback) {
1927             notify($strdeleted .' user_teachers');
1928         }
1929     } else {
1930         $result = false;
1931     }
1933     // Delete any groups
1935     if ($groups = get_records('groups', 'courseid', $course->id)) {
1936         foreach ($groups as $group) {
1937             if (delete_records('groups_members', 'groupid', $group->id)) {
1938                 if ($showfeedback) {
1939                     notify($strdeleted .' groups_members');
1940                 }
1941             } else {
1942                 $result = false;
1943             }
1944             if (delete_records('groups', 'id', $group->id)) {
1945                 if ($showfeedback) {
1946                     notify($strdeleted .' groups');
1947                 }
1948             } else {
1949                 $result = false;
1950             }
1951         }
1952     }
1954     // Delete events
1956     if (delete_records('event', 'courseid', $course->id)) {
1957         if ($showfeedback) {
1958             notify($strdeleted .' event');
1959         }
1960     } else {
1961         $result = false;
1962     }
1964     // Delete logs
1966     if (delete_records('log', 'course', $course->id)) {
1967         if ($showfeedback) {
1968             notify($strdeleted .' log');
1969         }
1970     } else {
1971         $result = false;
1972     }
1974     // Delete any course stuff
1976     if (delete_records('course_sections', 'course', $course->id)) {
1977         if ($showfeedback) {
1978             notify($strdeleted .' course_sections');
1979         }
1980     } else {
1981         $result = false;
1982     }
1984     if (delete_records('course_modules', 'course', $course->id)) {
1985         if ($showfeedback) {
1986             notify($strdeleted .' course_modules');
1987         }
1988     } else {
1989         $result = false;
1990     }
1992     return $result;
1996 /**
1997  * This function will empty a course of USER data as much as
1998 /// possible. It will retain the activities and the structure
1999 /// of the course.
2000  *
2001  * @uses $USER
2002  * @uses $THEME
2003  * @uses $SESSION
2004  * @uses $CFG
2005  * @param int $courseid The id of the course that is being viewed
2006  * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
2007  * @param boolean $removestudents ?
2008  * @param boolean $removeteachers ?
2009  * @param boolean $removegroups ?
2010  * @param boolean $removeevents ?
2011  * @param boolean $removelogs ?
2012  * @return boolean
2013  * @todo Finish documenting this function
2014  */
2015 function remove_course_userdata($courseid, $showfeedback=true,
2016                                 $removestudents=true, $removeteachers=false, $removegroups=true,
2017                                 $removeevents=true, $removelogs=false) {
2019     global $CFG, $THEME, $USER, $SESSION;
2021     $result = true;
2023     if (! $course = get_record('course', 'id', $courseid)) {
2024         error('Course ID was incorrect (can\'t find it)');
2025     }
2027     $strdeleted = get_string('deleted');
2029     // Look in every instance of every module for data to delete
2031     if ($allmods = get_records('modules') ) {
2032         foreach ($allmods as $mod) {
2033             $modname = $mod->name;
2034             $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
2035             $moddeleteuserdata = $modname .'_delete_userdata';   // Function to delete user data
2036             $count=0;
2037             if (file_exists($modfile)) {
2038                 @include_once($modfile);
2039                 if (function_exists($moddeleteuserdata)) {
2040                     $moddeleteuserdata($course, $showfeedback);
2041                 }
2042             }
2043         }
2044     } else {
2045         error('No modules are installed!');
2046     }
2048     // Delete other stuff
2050     if ($removestudents) {
2051         /// Delete student enrolments
2052         if (delete_records('user_students', 'course', $course->id)) {
2053             if ($showfeedback) {
2054                 notify($strdeleted .' user_students');
2055             }
2056         } else {
2057             $result = false;
2058         }
2059         /// Delete group members (but keep the groups)
2060         if ($groups = get_records('groups', 'courseid', $course->id)) {
2061             foreach ($groups as $group) {
2062                 if (delete_records('groups_members', 'groupid', $group->id)) {
2063                     if ($showfeedback) {
2064                         notify($strdeleted .' groups_members');
2065                     }
2066                 } else {
2067                     $result = false;
2068                 }
2069             }
2070         }
2071     }
2073     if ($removeteachers) {
2074         if (delete_records('user_teachers', 'course', $course->id)) {
2075             if ($showfeedback) {
2076                 notify($strdeleted .' user_teachers');
2077             }
2078         } else {
2079             $result = false;
2080         }
2081     }
2083     if ($removegroups) {
2084         if ($groups = get_records('groups', 'courseid', $course->id)) {
2085             foreach ($groups as $group) {
2086                 if (delete_records('groups', 'id', $group->id)) {
2087                     if ($showfeedback) {
2088                         notify($strdeleted .' groups');
2089                     }
2090                 } else {
2091                     $result = false;
2092                 }
2093             }
2094         }
2095     }
2097     if ($removeevents) {
2098         if (delete_records('event', 'courseid', $course->id)) {
2099             if ($showfeedback) {
2100                 notify($strdeleted .' event');
2101             }
2102         } else {
2103             $result = false;
2104         }
2105     }
2107     if ($removelogs) {
2108         if (delete_records('log', 'course', $course->id)) {
2109             if ($showfeedback) {
2110                 notify($strdeleted .' log');
2111             }
2112         } else {
2113             $result = false;
2114         }
2115     }
2117     return $result;
2123 /// GROUPS /////////////////////////////////////////////////////////
2126 /**
2127 * Returns a boolean: is the user a member of the given group?
2129 * @param    type description
2130  * @todo Finish documenting this function
2131 */
2132 function ismember($groupid, $userid=0) {
2133     global $USER;
2135     if (!$groupid) {   // No point doing further checks
2136         return false;
2137     }
2139     if (!$userid) {
2140         if (empty($USER->groupmember)) {
2141             return false;
2142         }
2143         foreach ($USER->groupmember as $courseid => $mgroupid) {
2144             if ($mgroupid == $groupid) {
2145                 return true;
2146             }
2147         }
2148         return false;
2149     }
2151     return record_exists('groups_members', 'groupid', $groupid, 'userid', $userid);
2154 /**
2155  * Add a user to a group, return true upon success or if user already a group member
2156  *
2157  * @param groupid  The group id
2158  * @param userid   The user id
2159  * @todo Finish documenting this function
2160  */
2161 function add_user_to_group ($groupid, $userid) {
2162     if (ismember($groupid, $userid)) return true;
2163     $record->groupid = $groupid;
2164     $record->userid = $userid;
2165     $record->timeadded = time(); 
2166     return (insert_record('groups_members', $record) !== false);
2170 /**
2171  * Returns the group ID of the current user in the given course
2172  *
2173  * @uses $USER
2174  * @param int $courseid The course being examined - relates to id field in 'course' table.
2175  * @todo Finish documenting this function
2176  */
2177 function mygroupid($courseid) {
2178     global $USER;
2180     if (empty($USER->groupmember[$courseid])) {
2181         return 0;
2182     } else {
2183         return $USER->groupmember[$courseid];
2184     }
2187 /**
2188  * For a given course, and possibly course module, determine
2189  * what the current default groupmode is:
2190  * NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2191  *
2192  * @param course $course A {@link $COURSE} object
2193  * @param array? $cm A course module object
2194  * @return int A group mode (NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS)
2195  */
2196 function groupmode($course, $cm=null) {
2198     if ($cm and !$course->groupmodeforce) {
2199         return $cm->groupmode;
2200     }
2201     return $course->groupmode;
2205 /**
2206  * Sets the current group in the session variable
2207  *
2208  * @uses $SESSION
2209  * @param int $courseid The course being examined - relates to id field in 'course' table.
2210  * @param int $groupid The group being examined.
2211  * @return int Current group id which was set by this function
2212  * @todo Finish documenting this function
2213  */
2214 function set_current_group($courseid, $groupid) {
2215     global $SESSION;
2217     return $SESSION->currentgroup[$courseid] = $groupid;
2221 /**
2222  * Gets the current group for the current user as an id or an object
2223  *
2224  * @uses $CFG
2225  * @uses $SESSION
2226  * @param int $courseid The course being examined - relates to id field in 'course' table.
2227  * @param boolean $full If true, the return value is a full record object. If false, just the id of the record.
2228  * @todo Finish documenting this function
2229  */
2230 function get_current_group($courseid, $full=false) {
2231     global $SESSION, $USER;
2233     if (!isset($SESSION->currentgroup[$courseid])) {
2234         if (empty($USER->groupmember[$courseid])) {
2235             return 0;
2236         } else {
2237             $SESSION->currentgroup[$courseid] = $USER->groupmember[$courseid];
2238         }
2239     }
2241     if ($full) {
2242         return get_record('groups', 'id', $SESSION->currentgroup[$courseid]);
2243     } else {
2244         return $SESSION->currentgroup[$courseid];
2245     }
2248 /**
2249  * A combination function to make it easier for modules
2250  * to set up groups.
2251  *
2252  * It will use a given "groupid" parameter and try to use
2253  * that to reset the current group for the user.
2254  *
2255  * @uses VISIBLEGROUPS
2256  * @param course $course A {@link $COURSE} object
2257  * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2258  * @param int $groupid Will try to use this optional parameter to
2259  *            reset the current group for the user
2260  * @return int|false Returns the current group id or false if error.
2261  * @todo Finish documenting this function
2262  */
2263 function get_and_set_current_group($course, $groupmode, $groupid=-1) {
2265     if (!$groupmode) {   // Groups don't even apply
2266         return false;
2267     }
2269     $currentgroupid = get_current_group($course->id);
2271     if ($groupid < 0) {  // No change was specified
2272         return $currentgroupid;
2273     }
2275     if ($groupid) {      // Try to change the current group to this groupid
2276         if ($group = get_record('groups', 'id', $groupid, 'courseid', $course->id)) { // Exists
2277             if (isteacheredit($course->id)) {          // Sets current default group
2278                 $currentgroupid = set_current_group($course->id, $group->id);
2280             } else if ($groupmode == VISIBLEGROUPS) {  // All groups are visible
2281                 $currentgroupid = $group->id;
2282             }
2283         }
2284     } else {             // When groupid = 0 it means show ALL groups
2285         if (isteacheredit($course->id)) {          // Sets current default group
2286             $currentgroupid = set_current_group($course->id, 0);
2288         } else if ($groupmode == VISIBLEGROUPS) {  // All groups are visible
2289             $currentgroupid = 0;
2290         }
2291     }
2293     return $currentgroupid;
2297 /**
2298  * A big combination function to make it easier for modules
2299  * to set up groups.
2300  *
2301  * Terminates if the current user shouldn't be looking at this group
2302  * Otherwise returns the current group if there is one
2303  * Otherwise returns false if groups aren't relevant
2304  *
2305  * @uses SEPARATEGROUPS
2306  * @uses VISIBLEGROUPS
2307  * @param course $course A {@link $COURSE} object
2308  * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2309  * @param string $urlroot ?
2310  * @todo Finish documenting this function
2311  */
2312 function setup_and_print_groups($course, $groupmode, $urlroot) {
2314     if (isset($_GET['group'])) {
2315         $changegroup = $_GET['group'];  /// 0 or higher
2316     } else {
2317         $changegroup = -1;              /// This means no group change was specified
2318     }
2320     $currentgroup = get_and_set_current_group($course, $groupmode, $changegroup);
2322     if ($currentgroup === false) {
2323         return false;
2324     }
2326     if ($groupmode == SEPARATEGROUPS and !isteacheredit($course->id) and !$currentgroup) {
2327         print_heading(get_string('notingroup'));
2328         print_footer($course);
2329         exit;
2330     }
2332     if ($groupmode == VISIBLEGROUPS or ($groupmode and isteacheredit($course->id))) {
2333         if ($groups = get_records_menu('groups', 'courseid', $course->id, 'name ASC', 'id,name')) {
2334             echo '<div align="center">';
2335             print_group_menu($groups, $groupmode, $currentgroup, $urlroot);
2336             echo '</div>';
2337         }
2338     }
2340     return $currentgroup;
2345 /// CORRESPONDENCE  ////////////////////////////////////////////////
2347 /**
2348  * Send an email to a specified user
2349  *
2350  * @uses $CFG
2351  * @uses $_SERVER
2352  * @uses SITEID
2353  * @param user $user  A {@link $USER} object
2354  * @param user $from A {@link $USER} object
2355  * @param string $subject plain text subject line of the email
2356  * @param string $messagetext plain text version of the message
2357  * @param string $messagehtml complete html version of the message (optional)
2358  * @param string $attachment a file on the filesystem, relative to $CFG->dataroot
2359  * @param string $attachname the name of the file (extension indicates MIME)
2360  * @param boolean $usetrueaddress determines whether $from email address should
2361  *          be sent out. Will be overruled by user profile setting for maildisplay
2362  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2363  *          was blocked by user and "false" if there was another sort of error.
2364  */
2365 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true) {
2367     global $CFG, $FULLME;
2369     global $course;                // This is a bit of an ugly hack to be gotten rid of later
2370     if (!empty($course->lang)) {   // Course language is defined
2371         $CFG->courselang = $course->lang;
2372     }
2374     include_once($CFG->libdir .'/phpmailer/class.phpmailer.php');
2376     if (empty($user)) {
2377         return false;
2378     }
2380     if (!empty($user->emailstop)) {
2381         return 'emailstop';
2382     }
2384     $mail = new phpmailer;
2386     $mail->Version = 'Moodle '. $CFG->version;           // mailer version
2387     $mail->PluginDir = $CFG->libdir .'/phpmailer/';      // plugin directory (eg smtp plugin)
2390     if (current_language() != 'en') {
2391         $mail->CharSet = get_string('thischarset');
2392     }
2394     if ($CFG->smtphosts == 'qmail') {
2395         $mail->IsQmail();                              // use Qmail system
2397     } else if (empty($CFG->smtphosts)) {
2398         $mail->IsMail();                               // use PHP mail() = sendmail
2400     } else {
2401         $mail->IsSMTP();                               // use SMTP directly
2402         if ($CFG->debug > 7) {
2403             echo '<pre>' . "\n";
2404             $mail->SMTPDebug = true;
2405         }
2406         $mail->Host = $CFG->smtphosts;               // specify main and backup servers
2408         if ($CFG->smtpuser) {                          // Use SMTP authentication
2409             $mail->SMTPAuth = true;
2410             $mail->Username = $CFG->smtpuser;
2411             $mail->Password = $CFG->smtppass;
2412         }
2413     }
2415     $adminuser = get_admin();
2417     $mail->Sender   = $adminuser->email;
2419     if (is_string($from)) { // So we can pass whatever we want if there is need
2420         $mail->From     = $CFG->noreplyaddress;
2421         $mail->FromName = $from;
2422     } else if ($usetrueaddress and $from->maildisplay) {
2423         $mail->From     = $from->email;
2424         $mail->FromName = fullname($from);
2425     } else {
2426         $mail->From     = $CFG->noreplyaddress;
2427         $mail->FromName = fullname($from);
2428     }
2429     $mail->Subject  =  stripslashes($subject);
2431     $mail->AddAddress($user->email, fullname($user) );
2433     $mail->WordWrap = 79;                               // set word wrap
2435     if (!empty($from->customheaders)) {                 // Add custom headers
2436         if (is_array($from->customheaders)) {
2437             foreach ($from->customheaders as $customheader) {
2438                 $mail->AddCustomHeader($customheader);
2439             }
2440         } else {
2441             $mail->AddCustomHeader($from->customheaders);
2442         }
2443     }
2445     if ($messagehtml) {
2446         $mail->IsHTML(true);
2447         $mail->Encoding = 'quoted-printable';           // Encoding to use
2448         $mail->Body    =  $messagehtml;
2449         $mail->AltBody =  "\n$messagetext\n";
2450     } else {
2451         $mail->IsHTML(false);
2452         $mail->Body =  "\n$messagetext\n";
2453     }
2455     if ($attachment && $attachname) {
2456         if (ereg( "\\.\\." ,$attachment )) {    // Security check for ".." in dir path
2457             $mail->AddAddress($adminuser->email, fullname($adminuser) );
2458             $mail->AddStringAttachment('Error in attachment.  User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain');
2459         } else {
2460             include_once($CFG->dirroot .'/files/mimetypes.php');
2461             $mimetype = mimeinfo('type', $attachname);
2462             $mail->AddAttachment($CFG->dataroot .'/'. $attachment, $attachname, 'base64', $mimetype);
2463         }
2464     }
2466     if ($mail->Send()) {
2467         return true;
2468     } else {
2469         mtrace('ERROR: '. $mail->ErrorInfo);
2470         add_to_log(SITEID, 'library', 'mailer', $FULLME, 'ERROR: '. $mail->ErrorInfo);
2471         return false;
2472     }
2475 /**
2476  * Resets specified user's password and send the new password to the user via email.
2477  *
2478  * @uses $CFG
2479  * @param user $user A {@link $USER} object
2480  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2481  *          was blocked by user and "false" if there was another sort of error.
2482  */
2483 function reset_password_and_mail($user) {
2485     global $CFG;
2487     $site  = get_site();
2488     $from = get_admin();
2490     $newpassword = generate_password();
2492     if (! set_field('user', 'password', md5($newpassword), 'id', $user->id) ) {
2493         error('Could not set user password!');
2494     }
2496     $a->firstname = $user->firstname;
2497     $a->sitename = $site->fullname;
2498     $a->username = $user->username;
2499     $a->newpassword = $newpassword;
2500     $a->link = $CFG->wwwroot .'/login/change_password.php';
2501     $a->signoff = fullname($from, true).' ('. $from->email .')';
2503     $message = get_string('newpasswordtext', '', $a);
2505     $subject  = $site->fullname .': '. get_string('changedpassword');
2507     return email_to_user($user, $from, $subject, $message);
2511 /**
2512  * Send email to specified user with confirmation text and activation link.
2513  *
2514  * @uses $CFG
2515  * @param user $user A {@link $USER} object
2516  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2517  *          was blocked by user and "false" if there was another sort of error.
2518  */
2519  function send_confirmation_email($user) {
2521     global $CFG;
2523     $site = get_site();
2524     $from = get_admin();
2526     $data->firstname = $user->firstname;
2527     $data->sitename = $site->fullname;
2528     $data->link = $CFG->wwwroot .'/login/confirm.php?p='. $user->secret .'&amp;s='. $user->username;
2529     $data->admin = fullname($from) .' ('. $from->email .')';
2531     $message = get_string('emailconfirmation', '', $data);
2532     $subject = get_string('emailconfirmationsubject', '', $site->fullname);
2534     $messagehtml = text_to_html($message, false, false, true);
2536     return email_to_user($user, $from, $subject, $message, $messagehtml);
2540 /**
2541  * send_password_change_confirmation_email.
2542  *
2543  * @uses $CFG
2544  * @param user $user A {@link $USER} object
2545  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2546  *          was blocked by user and "false" if there was another sort of error.
2547  * @todo Finish documenting this function
2548  */
2549 function send_password_change_confirmation_email($user) {
2551     global $CFG;
2553     $site = get_site();
2554     $from = get_admin();
2556     $data->firstname = $user->firstname;
2557     $data->sitename = $site->fullname;
2558     $data->link = $CFG->wwwroot .'/login/forgot_password.php?p='. $user->secret .'&amp;s='. $user->username;
2559     $data->admin = fullname($from).' ('. $from->email .')';
2561     $message = get_string('emailpasswordconfirmation', '', $data);
2562     $subject = get_string('emailpasswordconfirmationsubject', '', $site->fullname);
2564     return email_to_user($user, $from, $subject, $message);
2568 /**
2569  * Check that an email is allowed.  It returns an error message if there
2570  * was a problem.
2571  *
2572  * @param    type description
2573  * @todo Finish documenting this function
2574  */
2575 function email_is_not_allowed($email) {
2577     global $CFG;
2579     if (!empty($CFG->allowemailaddresses)) {
2580         $allowed = explode(' ', $CFG->allowemailaddresses);
2581         foreach ($allowed as $allowedpattern) {
2582             $allowedpattern = trim($allowedpattern);
2583             if (!$allowedpattern) {
2584                 continue;
2585             }
2586             if (strpos($email, $allowedpattern) !== false) {  // Match!
2587                 return false;
2588             }
2589         }
2590         return get_string('emailonlyallowed', '', $CFG->allowemailaddresses);
2592     } else if (!empty($CFG->denyemailaddresses)) {
2593         $denied = explode(' ', $CFG->denyemailaddresses);
2594         foreach ($denied as $deniedpattern) {
2595             $deniedpattern = trim($deniedpattern);
2596             if (!$deniedpattern) {
2597                 continue;
2598             }
2599             if (strpos($email, $deniedpattern) !== false) {   // Match!
2600                 return get_string('emailnotallowed', '', $CFG->denyemailaddresses);
2601             }
2602         }
2603     }
2605     return false;
2609 /// FILE HANDLING  /////////////////////////////////////////////
2611 /**
2612  * Create a directory.
2613  *
2614  * @uses $CFG
2615  * @param string $directory  a string of directory names under $CFG->dataroot eg  stuff/assignment/1
2616  * param boolean $shownotices If true then notification messages will be printed out on error.
2617  * @return string|false Returns full path to directory if successful, false if not
2618  */
2619 function make_upload_directory($directory, $shownotices=true) {
2621     global $CFG;
2623     $currdir = $CFG->dataroot;
2625     umask(0000);
2627     if (!file_exists($currdir)) {
2628         if (! mkdir($currdir, $CFG->directorypermissions)) {
2629             if ($shownotices) {
2630                 notify('ERROR: You need to create the directory '. $currdir .' with web server write access');
2631             }
2632             return false;
2633         }
2634         if ($handle = fopen($currdir.'/.htaccess', 'w')) {   // For safety
2635             @fwrite($handle, "deny from all\r\n");
2636             @fclose($handle); 
2637         }
2638     }
2640     $dirarray = explode('/', $directory);
2642     foreach ($dirarray as $dir) {
2643         $currdir = $currdir .'/'. $dir;
2644         if (! file_exists($currdir)) {
2645             if (! mkdir($currdir, $CFG->directorypermissions)) {
2646                 if ($shownotices) {
2647                     notify('ERROR: Could not find or create a directory ('. $currdir .')');
2648                 }
2649                 return false;
2650             }
2651             //@chmod($currdir, $CFG->directorypermissions);  // Just in case mkdir didn't do it
2652         }
2653     }
2655     return $currdir;
2658 /**
2659  * Makes an upload directory for a particular module.
2660  *
2661  * @uses $CFG
2662  * @param int $courseid The id of the course in question - maps to id field of 'course' table.
2663  * @return string|false Returns full path to directory if successful, false if not
2664  * @todo Finish documenting this function
2665  */
2666 function make_mod_upload_directory($courseid) {
2667     global $CFG;
2669     if (! $moddata = make_upload_directory($courseid .'/'. $CFG->moddata)) {
2670         return false;
2671     }
2673     $strreadme = get_string('readme');
2675     if (file_exists($CFG->dirroot .'/lang/'. $CFG->lang .'/docs/module_files.txt')) {
2676         copy($CFG->dirroot .'/lang/'. $CFG->lang .'/docs/module_files.txt', $moddata .'/'. $strreadme .'.txt');
2677     } else {
2678         copy($CFG->dirroot .'/lang/en/docs/module_files.txt', $moddata .'/'. $strreadme .'.txt');
2679     }
2680     return $moddata;
2683 /**
2684  * Returns current name of file on disk if it exists.
2685  *
2686  * @param string $newfile File to be verified
2687  * @return string Current name of file on disk if true
2688  * @todo Finish documenting this function
2689  */
2690 function valid_uploaded_file($newfile) {
2691     if (empty($newfile)) {
2692         return '';
2693     }
2694     if (is_uploaded_file($newfile['tmp_name']) and $newfile['size'] > 0) {
2695         return $newfile['tmp_name'];
2696     } else {
2697         return '';
2698     }
2701 /**
2702  * Returns the maximum size for uploading files.
2703  *
2704  * There are seven possible upload limits:
2705  * 1. in Apache using LimitRequestBody (no way of checking or changing this)
2706  * 2. in php.ini for 'upload_max_filesize' (can not be changed inside PHP)
2707  * 3. in .htaccess for 'upload_max_filesize' (can not be changed inside PHP)
2708  * 4. in php.ini for 'post_max_size' (can not be changed inside PHP)
2709  * 5. by the Moodle admin in $CFG->maxbytes
2710  * 6. by the teacher in the current course $course->maxbytes
2711  * 7. by the teacher for the current module, eg $assignment->maxbytes
2712  *
2713  * These last two are passed to this function as arguments (in bytes).
2714  * Anything defined as 0 is ignored.
2715  * The smallest of all the non-zero numbers is returned.
2716  *
2717  * @param int $sizebytes ?
2718  * @param int $coursebytes Current course $course->maxbytes (in bytes)
2719  * @param int $modulebytes Current module ->maxbytes (in bytes)
2720  * @return int The maximum size for uploading files.
2721  * @todo Finish documenting this function
2722  */
2723 function get_max_upload_file_size($sitebytes=0, $coursebytes=0, $modulebytes=0) {
2725     if (! $filesize = ini_get('upload_max_filesize')) {
2726         $filesize = '5M';
2727     }
2728     $minimumsize = get_real_size($filesize);
2730     if ($postsize = ini_get('post_max_size')) {
2731         $postsize = get_real_size($postsize);
2732         if ($postsize < $minimumsize) {
2733             $minimumsize = $postsize;
2734         }
2735     }
2737     if ($sitebytes and $sitebytes < $minimumsize) {
2738         $minimumsize = $sitebytes;
2739     }
2741     if ($coursebytes and $coursebytes < $minimumsize) {
2742         $minimumsize = $coursebytes;
2743     }
2745     if ($modulebytes and $modulebytes < $minimumsize) {
2746         $minimumsize = $modulebytes;
2747     }
2749     return $minimumsize;
2752 /**
2753  * Related to the above function - this function returns an
2754  * array of possible sizes in an array, translated to the
2755  * local language.
2756  *
2757  * @uses SORT_NUMERIC
2758  * @param int $sizebytes ?
2759  * @param int $coursebytes Current course $course->maxbytes (in bytes)
2760  * @param int $modulebytes Current module ->maxbytes (in bytes)
2761  * @return int
2762  * @todo Finish documenting this function
2763  */
2764 function get_max_upload_sizes($sitebytes=0, $coursebytes=0, $modulebytes=0) {
2766     if (!$maxsize = get_max_upload_file_size($sitebytes, $coursebytes, $modulebytes)) {
2767         return array();
2768     }
2770     $filesize[$maxsize] = display_size($maxsize);
2772     $sizelist = array(10240, 51200, 102400, 512000, 1048576, 2097152,
2773                       5242880, 10485760, 20971520, 52428800, 104857600);
2775     foreach ($sizelist as $sizebytes) {
2776        if ($sizebytes < $maxsize) {
2777            $filesize[$sizebytes] = display_size($sizebytes);
2778        }
2779     }
2781     krsort($filesize, SORT_NUMERIC);
2783     return $filesize;
2786 /**
2787  * If there has been an error uploading a file, print the appropriate error message
2788  * Numerical constants used as constant definitions not added until PHP version 4.2.0
2789  *
2790  * $filearray is a 1-dimensional sub-array of the $_FILES array
2791  * eg $filearray = $_FILES['userfile1']
2792  * If left empty then the first element of the $_FILES array will be used
2793  *
2794  * @uses $_FILES
2795  * @param array $filearray  A 1-dimensional sub-array of the $_FILES array
2796  * @param boolean $returnerror ?
2797  * @return boolean
2798  * @todo Finish documenting this function
2799  */
2800 function print_file_upload_error($filearray = '', $returnerror = false) {
2802     if ($filearray == '' or !isset($filearray['error'])) {
2804         if (empty($_FILES)) return false;
2806         $files = $_FILES; /// so we don't mess up the _FILES array for subsequent code
2807         $filearray = array_shift($files); /// use first element of array
2808     }
2810     switch ($filearray['error']) {
2812         case 0: // UPLOAD_ERR_OK
2813             if ($filearray['size'] > 0) {
2814                 $errmessage = get_string('uploadproblem', $filearray['name']);
2815             } else {
2816                 $errmessage = get_string('uploadnofilefound'); /// probably a dud file name
2817             }
2818             break;
2820         case 1: // UPLOAD_ERR_INI_SIZE
2821             $errmessage = get_string('uploadserverlimit');
2822             break;
2824         case 2: // UPLOAD_ERR_FORM_SIZE
2825             $errmessage = get_string('uploadformlimit');
2826             break;
2828         case 3: // UPLOAD_ERR_PARTIAL
2829             $errmessage = get_string('uploadpartialfile');
2830             break;
2832         case 4: // UPLOAD_ERR_NO_FILE
2833             $errmessage = get_string('uploadnofilefound');
2834             break;
2836         default:
2837             $errmessage = get_string('uploadproblem', $filearray['name']);
2838     }
2840     if ($returnerror) {
2841         return $errmessage;
2842     } else {
2843         notify($errmessage);
2844         return true;
2845     }
2849 /**
2850  * Returns an array with all the filenames in
2851  * all subdirectories, relative to the given rootdir.
2852  * If excludefile is defined, then that file/directory is ignored
2853  * If getdirs is true, then (sub)directories are included in the output
2854  * If getfiles is true, then files are included in the output
2855  * (at least one of these must be true!)
2856  *
2857  * @param string $rootdir  ?
2858  * @param string $excludefile  If defined then the specified file/directory is ignored
2859  * @param boolean $descend  ?
2860  * @param boolean $getdirs  If true then (sub)directories are included in the output
2861  * @param boolean $getfiles  If true then files are included in the output
2862  * @return array An array with all the filenames in
2863  * all subdirectories, relative to the given rootdir
2864  * @todo Finish documenting this function. Add examples of $excludefile usage.
2865  */
2866 function get_directory_list($rootdir, $excludefile='', $descend=true, $getdirs=false, $getfiles=true) {
2868     $dirs = array();
2870     if (!$getdirs and !$getfiles) {   // Nothing to show
2871         return $dirs;
2872     }
2874     if (!is_dir($rootdir)) {          // Must be a directory
2875         return $dirs;
2876     }
2878     if (!$dir = opendir($rootdir)) {  // Can't open it for some reason
2879         return $dirs;
2880     }
2882     while (false !== ($file = readdir($dir))) {
2883         $firstchar = substr($file, 0, 1);
2884         if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
2885             continue;
2886         }
2887         $fullfile = $rootdir .'/'. $file;
2888         if (filetype($fullfile) == 'dir') {
2889             if ($getdirs) {
2890                 $dirs[] = $file;
2891             }
2892             if ($descend) {
2893                 $subdirs = get_directory_list($fullfile, $excludefile, $descend, $getdirs, $getfiles);
2894                 foreach ($subdirs as $subdir) {
2895                     $dirs[] = $file .'/'. $subdir;
2896                 }
2897             }
2898         } else if ($getfiles) {
2899             $dirs[] = $file;
2900         }
2901     }
2902     closedir($dir);
2904     asort($dirs);
2906     return $dirs;
2909 /**
2910  * Adds up all the files in a directory and works out the size.
2911  *
2912  * @param string $rootdir  ?
2913  * @param string $excludefile  ?
2914  * @return array
2915  * @todo Finish documenting this function
2916  */
2917 function get_directory_size($rootdir, $excludefile='') {
2919     $size = 0;
2921     if (!is_dir($rootdir)) {          // Must be a directory
2922         return $dirs;
2923     }
2925     if (!$dir = @opendir($rootdir)) {  // Can't open it for some reason
2926         return $dirs;
2927     }
2929     while (false !== ($file = readdir($dir))) {
2930         $firstchar = substr($file, 0, 1);
2931         if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
2932             continue;
2933         }
2934         $fullfile = $rootdir .'/'. $file;
2935         if (filetype($fullfile) == 'dir') {
2936             $size += get_directory_size($fullfile, $excludefile);
2937         } else {
2938             $size += filesize($fullfile);
2939         }
2940     }
2941     closedir($dir);
2943     return $size;
2946 /**
2947  * Converts numbers like 10M into bytes.
2948  *
2949  * @param mixed $size The size to be converted
2950  * @return mixed
2951  */
2952 function get_real_size($size=0) {
2953     if (!$size) {
2954         return 0;
2955     }
2956     $scan['MB'] = 1048576;
2957     $scan['Mb'] = 1048576;
2958     $scan['M'] = 1048576;
2959     $scan['m'] = 1048576;
2960     $scan['KB'] = 1024;
2961     $scan['Kb'] = 1024;
2962     $scan['K'] = 1024;
2963     $scan['k'] = 1024;
2965     while (list($key) = each($scan)) {
2966         if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
2967             $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
2968             break;
2969         }
2970     }
2971     return $size;
2974 /**
2975  * Converts bytes into display form
2976  *
2977  * @param string $size  ?
2978  * @return string
2979  * @staticvar string $gb Localized string for size in gigabytes
2980  * @staticvar string $mb Localized string for size in megabytes
2981  * @staticvar string $kb Localized string for size in kilobytes
2982  * @staticvar string $b Localized string for size in bytes
2983  * @todo Finish documenting this function. Verify return type.
2984  */
2985 function display_size($size) {
2987     static $gb, $mb, $kb, $b;
2989     if (empty($gb)) {
2990         $gb = get_string('sizegb');
2991         $mb = get_string('sizemb');
2992         $kb = get_string('sizekb');
2993         $b  = get_string('sizeb');
2994     }
2996     if ($size >= 1073741824) {
2997         $size = round($size / 1073741824 * 10) / 10 . $gb;
2998     } else if ($size >= 1048576) {
2999         $size = round($size / 1048576 * 10) / 10 . $mb;
3000     } else if ($size >= 1024) {
3001         $size = round($size / 1024 * 10) / 10 . $kb;
3002     } else {
3003         $size = $size .' '. $b;
3004     }
3005     return $size;
3008 /**
3009  * Cleans a given filename by removing suspicious or troublesome characters
3010  * Only these are allowed:
3011  *    alphanumeric _ - .
3012  *
3013  * @param string $string  ?
3014  * @return string
3015  * @todo Finish documenting this function
3016  */
3017 function clean_filename($string) {
3018     $string = eregi_replace("\.\.+", '', $string);
3019     $string = preg_replace('/[^\.a-zA-Z\d\_-]/','_', $string ); // only allowed chars
3020     $string = eregi_replace("_+", '_', $string);
3021     return $string;
3025 /// STRING TRANSLATION  ////////////////////////////////////////
3027 /**
3028  * Returns the code for the current language
3029  *
3030  * @uses $CFG
3031  * @param $USER
3032  * @param $SESSION
3033  * @return string
3034  */
3035 function current_language() {
3036     global $CFG, $USER, $SESSION;
3038     if (!empty($CFG->courselang)) {    // Course language can override all other settings for this page
3039         return $CFG->courselang;
3041     } else if (!empty($SESSION->lang)) {    // Session language can override other settings
3042         return $SESSION->lang;
3044     } else if (!empty($USER->lang)) {    // User language can override site language
3045         return $USER->lang;
3047     } else {
3048         return $CFG->lang;
3049     }
3052 /**
3053  * Prints out a translated string.
3054  *
3055  * Prints out a translated string using the return value from the {@link get_string()} function.
3056  *
3057  * Example usage of this function when the string is in the moodle.php file:<br>
3058  * <code>
3059  * echo '<strong>';
3060  * print_string('wordforstudent');
3061  * echo '</strong>';
3062  * </code>
3063  *
3064  * Example usage of this function when the string is not in the moodle.php file:<br>
3065  * <code>
3066  * echo '<h1>';
3067  * print_string('typecourse', 'calendar');
3068  * echo '</h1>';
3069  * </code>
3070  *
3071  * @param string $identifier The key identifier for the localized string
3072  * @param string $module The module where the key identifier is stored. If none is specified then moodle.php is used.
3073  * @param mixed $a An object, string or number that can be used
3074  * within translation strings
3075  */
3076 function print_string($identifier, $module='', $a=NULL) {
3077     echo get_string($identifier, $module, $a);
3080 /**
3081  * Returns a localized string.
3082  *
3083  * Returns the translated string specified by $identifier as
3084  * for $module.  Uses the same format files as STphp.
3085  * $a is an object, string or number that can be used
3086  * within translation strings
3087  *
3088  * eg "hello \$a->firstname \$a->lastname"
3089  * or "hello \$a"
3090  *
3091  * If you would like to directly echo the localized string use
3092  * the function {@link print_string()}
3093  *
3094  * Example usage of this function involves finding the string you would
3095  * like a local equivalent of and using its identifier and module information
3096  * to retrive it.<br>
3097  * If you open moodle/lang/en/moodle.php and look near line 1031
3098  * you will find a string to prompt a user for their word for student
3099  * <code>
3100  * $string['wordforstudent'] = 'Your word for Student';
3101  * </code>
3102  * So if you want to display the string 'Your word for student'
3103  * in any language that supports it on your site
3104  * you just need to use the identifier 'wordforstudent'
3105  * <code>
3106  * $mystring = '<strong>'. get_string('wordforstudent') .'</strong>';
3107 or
3108  * </code>
3109  * If the string you want is in another file you'd take a slightly
3110  * different approach. Looking in moodle/lang/en/calendar.php you find
3111  * around line 75:
3112  * <code>
3113  * $string['typecourse'] = 'Course event';
3114  * </code>
3115  * If you want to display the string "Course event" in any language
3116  * supported you would use the identifier 'typecourse' and the module 'calendar'
3117  * (because it is in the file calendar.php):
3118  * <code>
3119  * $mystring = '<h1>'. get_string('typecourse', 'calendar') .'</h1>';
3120  * </code>
3121  *
3122  * As a last resort, should the identifier fail to map to a string
3123  * the returned string will be [[ $identifier ]]
3124  *
3125  * @uses $CFG
3126  * @param string $identifier The key identifier for the localized string
3127  * @param string $module The module where the key identifier is stored. If none is specified then moodle.php is used.
3128  * @param mixed $a An object, string or number that can be used
3129  * within translation strings
3130  * @return string The localized string.
3131  */
3132 function get_string($identifier, $module='', $a=NULL) {
3134     global $CFG;
3136     global $course;     /// Not a nice hack, but quick
3137     if (empty($CFG->courselang)) {
3138         if (!empty($course->lang)) {
3139             $CFG->courselang = $course->lang;
3140         }
3141     }
3143     $lang = current_language();
3145     if ($module == '') {
3146         $module = 'moodle';
3147     }
3149 /// Define the two or three major locations of language strings for this module
3151     $locations = array( $CFG->dataroot.'/lang/',  $CFG->dirroot.'/lang/' );
3152     if ($module != 'moodle') {
3153         $locations[] =  $CFG->dirroot .'/mod/'.$module.'/lang/';
3154     }
3156 /// First check all the normal locations for the string in the current language
3158     foreach ($locations as $location) {
3159         $langfile = $location.$lang.'/'.$module.'.php';
3160         if (file_exists($langfile)) {
3161             if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
3162                 eval($result);
3163                 return $resultstring;
3164             }
3165         }
3166     }
3168 /// If the preferred language was English we can abort now
3169     if ($lang == 'en') {
3170         return '[['. $identifier .']]';
3171     }
3173 /// Is a parent language defined?  If so, try to find this string in a parent language file
3175     foreach ($locations as $location) {
3176         $langfile = $location.$lang.'/moodle.php';
3177         if (file_exists($langfile)) {
3178             if ($result = get_string_from_file('parentlanguage', $langfile, "\$parentlang")) {
3179                 eval($result);
3180                 if (!empty($parentlang)) {   // found it!
3181                     $langfile = $location.$parentlang.'/'.$module.'.php';
3182                     if (file_exists($langfile)) {
3183                         if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
3184                             eval($result);
3185                             return $resultstring;
3186                         }
3187                     }
3188                 }
3189             }
3190         }
3191     }
3193 /// Our only remaining option is to try English
3195     foreach ($locations as $location) {
3196         $langfile = $location.'en/'.$module.'.php';
3198         if (file_exists($langfile)) {
3199             if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
3200                 eval($result);
3201                 return $resultstring;
3202             }
3203         }
3204     }
3206     return '[['.$identifier.']]';  // Last resort
3209 /**
3210  * This function is only used from {@link get_string()}.
3211  *
3212  * @internal Only used from get_string, not meant to be public API
3213  * @param string $identifier ?
3214  * @param string $langfile ?
3215  * @param string $destination ?
3216  * @return string|false ?
3217  * @staticvar array $strings Localized strings
3218  * @access private
3219  * @todo Finish documenting this function.
3220  */
3221 function get_string_from_file($identifier, $langfile, $destination) {
3223     static $strings;    // Keep the strings cached in memory.
3225     if (empty($strings[$langfile])) {
3226         $string = array();
3227         include ($langfile);
3228         $strings[$langfile] = $string;
3229     } else {
3230         $string = &$strings[$langfile];
3231     }
3233     if (!isset ($string[$identifier])) {
3234         return false;
3235     }
3237     return $destination .'= sprintf("'. $string[$identifier] .'");';
3240 /**
3241  * Converts an array of strings to their localized value.
3242  *
3243  * @param array $array An array of strings
3244  * @param string $module The language module that these strings can be found in.
3245  * @return string
3246  */
3247 function get_strings($array, $module='') {
3249    $string = NULL;
3250    foreach ($array as $item) {
3251        $string->$item = get_string($item, $module);
3252    }
3253    return $string;
3256 /**
3257  * Returns a list of language codes and their full names
3258  *
3259  * @uses $CFG
3260  * @return array An associative array with contents in the form of LanguageCode => LanguageName
3261  * @todo Finish documenting this function
3262  */
3263 function get_list_of_languages() {
3264     global $CFG;
3266     $languages = array();
3268     if (!empty($CFG->langlist)) {       // use admin's list of languages
3269         $langlist = explode(',', $CFG->langlist);
3270         foreach ($langlist as $lang) {
3271             if (file_exists($CFG->dirroot .'/lang/'. $lang .'/moodle.php')) {
3272                 include($CFG->dirroot .'/lang/'. $lang .'/moodle.php');
3273                 $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
3274                 unset($string);
3275             }
3276         }
3277     } else {
3278         if (!$langdirs = get_list_of_plugins('lang')) {
3279             return false;
3280         }
3281         foreach ($langdirs as $lang) {
3282             @include($CFG->dirroot .'/lang/'. $lang .'/moodle.php');
3283             $languages[$lang] = $string['thislanguage'] .' ('. $lang .')';
3284             unset($string);
3285         }
3286     }
3288     return $languages;
3291 /**
3292  * Returns a list of country names in the current language
3293  *
3294  * @uses $CFG
3295  * @uses $USER
3296  * @return string?
3297  * @todo Finish documenting this function.
3298  */
3299 function get_list_of_countries() {
3300     global $CFG, $USER;
3302     $lang = current_language();
3304     if (!file_exists($CFG->dirroot .'/lang/'. $lang .'/countries.php')) {
3305         if ($parentlang = get_string('parentlanguage')) {
3306             if (file_exists($CFG->dirroot .'/lang/'. $parentlang .'/countries.php')) {
3307                 $lang = $parentlang;
3308             } else {
3309                 $lang = 'en';  // countries.php must exist in this pack
3310             }
3311         } else {
3312             $lang = 'en';  // countries.php must exist in this pack
3313         }
3314     }
3316     include($CFG->dirroot .'/lang/'. $lang .'/countries.php');
3318     if (!empty($string)) {
3319         asort($string);
3320     }
3322     return $string;
3325 /**
3326  * Returns a list of picture names in the current language
3327  *
3328  * @uses $CFG
3329  * @return string?
3330  * @todo Finish documenting this function.
3331  */
3332 function get_list_of_pixnames() {
3333     global $CFG;
3335     $lang = current_language();
3337     if (!file_exists($CFG->dirroot .'/lang/'. $lang .'/pix.php')) {
3338         if ($parentlang = get_string('parentlanguage')) {
3339             if (file_exists($CFG->dirroot .'/lang/'. $parentlang .'/pix.php')) {
3340                 $lang = $parentlang;
3341             } else {
3342                 $lang = 'en';  // countries.php must exist in this pack
3343             }
3344         } else {
3345             $lang = 'en';  // countries.php must exist in this pack
3346         }
3347     }
3349     include_once($CFG->dirroot .'/lang/'. $lang .'/pix.php');
3351     return $string;
3354 /**
3355  * Can include a given document file (depends on second
3356  * parameter) or just return info about it.
3357  *
3358  * @uses $CFG
3359  * @param string $file ?
3360  * @param boolean $include ?
3361  * @return ?
3362  * @todo Finish documenting this function
3363  */
3364 function document_file($file, $include=true) {
3365     global $CFG;
3367     $file = clean_filename($file);
3369     if (empty($file)) {
3370         return false;
3371     }
3373     $langs = array(current_language(), get_string('parentlanguage'), 'en');
3375     foreach ($langs as $lang) {
3376         $info->filepath = $CFG->dirroot .'/lang/'. $lang .'/docs/'. $file;
3377         $info->urlpath  = $CFG->wwwroot .'/lang/'. $lang .'/docs/'. $file;
3379         if (file_exists($info->filepath)) {
3380             if ($include) {
3381                 include($info->filepath);
3382             }
3383             return $info;
3384         }
3385     }
3387     return false;
3390 /**
3391 * Function to raise the memory limit to a new value.
3392 * Will respect the memory limit if it is higher, thus allowing
3393 * settings in php.ini, apache conf or command line switches
3394 * to override it
3396 * The memory limit should be expressed with a string (eg:'64M')
3398 * Return boolean
3400 * @param    value    string with the new memory limit
3401 */
3402 function raise_memory_limit ($newlimit) {
3404     if (empty($newlimit)) {
3405         return false;
3406     }
3408     $cur = @ini_get('memory_limit');
3409     if (empty($cur)) {
3410         // if php is compiled without --enable-memory-limits
3411         // apparently memory_limit is set to ''
3412         $cur=0;
3413     } else {
3414         if ($cur == -1){
3415             return true; // unlimited mem!
3416         }
3417       $cur = get_real_size($cur);
3418     }
3420     $new = get_real_size($newlimit);
3421     if ($new > $cur) {
3422         ini_set('memory_limit', $newlimit);
3423         return true;
3424     }
3425     return false;
3428 /// ENCRYPTION  ////////////////////////////////////////////////
3430 /**
3431  * rc4encrypt
3432  *
3433  * @param string $data ?
3434  * @return string
3435  * @todo Finish documenting this function
3436  */
3437 function rc4encrypt($data) {
3438     $password = 'nfgjeingjk';
3439     return endecrypt($password, $data, '');
3442 /**
3443  * rc4decrypt
3444  *
3445  * @param string $data ?
3446  * @return string
3447  * @todo Finish documenting this function
3448  */
3449 function rc4decrypt($data) {
3450     $password = 'nfgjeingjk';
3451     return endecrypt($password, $data, 'de');
3454 /**
3455  * Based on a class by Mukul Sabharwal [mukulsabharwal @ yahoo.com]
3456  *
3457  * @param string $pwd ?
3458  * @param string $data ?
3459  * @param string $case ?
3460  * @return string
3461  * @todo Finish documenting this function
3462  */
3463 function endecrypt ($pwd, $data, $case) {
3465     if ($case == 'de') {
3466         $data = urldecode($data);
3467     }
3469     $key[] = '';
3470     $box[] = '';
3471     $temp_swap = '';
3472     $pwd_length = 0;
3474     $pwd_length = strlen($pwd);
3476     for ($i = 0; $i <= 255; $i++) {
3477         $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1));
3478         $box[$i] = $i;
3479     }
3481     $x = 0;
3483     for ($i = 0; $i <= 255; $i++) {
3484         $x = ($x + $box[$i] + $key[$i]) % 256;
3485         $temp_swap = $box[$i];
3486         $box[$i] = $box[$x];
3487         $box[$x] = $temp_swap;
3488     }
3490     $temp = '';
3491     $k = '';
3493     $cipherby = '';
3494     $cipher = '';
3496     $a = 0;
3497     $j = 0;
3499     for ($i = 0; $i < strlen($data); $i++) {
3500         $a = ($a + 1) % 256;
3501         $j = ($j + $box[$a]) % 256;
3502         $temp = $box[$a];
3503         $box[$a] = $box[$j];
3504         $box[$j] = $temp;
3505         $k = $box[(($box[$a] + $box[$j]) % 256)];
3506         $cipherby = ord(substr($data, $i, 1)) ^ $k;
3507         $cipher .= chr($cipherby);
3508     }
3510     if ($case == 'de') {
3511         $cipher = urldecode(urlencode($cipher));
3512     } else {
3513         $cipher = urlencode($cipher);
3514     }
3516     return $cipher;
3520 /// CALENDAR MANAGEMENT  ////////////////////////////////////////////////////////////////
3523 /**
3524  * Call this function to add an event to the calendar table
3525  *  and to call any calendar plugins
3526  *
3527  * @uses $CFG
3528  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field. The object event should include the following:
3529  *  <ul>
3530  *    <li><b>$event->name</b> - Name for the event
3531  *    <li><b>$event->description</b> - Description of the event (defaults to '')
3532  *    <li><b>$event->format</b> - Format for the description (using formatting types defined at the top of weblib.php)
3533  *    <li><b>$event->courseid</b> - The id of the course this event belongs to (0 = all courses)
3534  *    <li><b>$event->groupid</b> - The id of the group this event belongs to (0 = no group)
3535  *    <li><b>$event->userid</b> - The id of the user this event belongs to (0 = no user)
3536  *    <li><b>$event->modulename</b> - Name of the module that creates this event
3537  *    <li><b>$event->instance</b> - Instance of the module that owns this event
3538  *    <li><b>$event->eventtype</b> - The type info together with the module info could
3539  *             be used by calendar plugins to decide how to display event
3540  *    <li><b>$event->timestart</b>- Timestamp for start of event
3541  *    <li><b>$event->timeduration</b> - Duration (defaults to zero)
3542  *    <li><b>$event->visible</b> - 0 if the event should be hidden (e.g. because the activity that created it is hidden)
3543  *  </ul>
3544  * @return int The id number of the resulting record
3545  * @todo Finish documenting this function
3546  */
3547  function add_event($event) {
3549     global $CFG;
3551     $event->timemodified = time();
3553     if (!$event->id = insert_record('event', $event)) {
3554         return false;
3555     }
3557     if (!empty($CFG->calendar)) { // call the add_event function of the selected calendar
3558         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3559             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3560             $calendar_add_event = $CFG->calendar.'_add_event';
3561             if (function_exists($calendar_add_event)) {
3562                 $calendar_add_event($event);
3563             }
3564         }
3565     }
3567     return $event->id;
3570 /**
3571  * Call this function to update an event in the calendar table
3572  * the event will be identified by the id field of the $event object.
3573  *
3574  * @uses $CFG
3575  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field.
3576  * @return boolean
3577  * @todo Finish documenting this function
3578  */
3579 function update_event($event) {
3581     global $CFG;
3583     $event->timemodified = time();
3585     if (!empty($CFG->calendar)) { // call the update_event function of the selected calendar
3586         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3587             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3588             $calendar_update_event = $CFG->calendar.'_update_event';
3589             if (function_exists($calendar_update_event)) {
3590                 $calendar_update_event($event);
3591             }
3592         }
3593     }
3594     return update_record('event', $event);
3597 /**
3598  * Call this function to delete the event with id $id from calendar table.
3599  *
3600   * @uses $CFG
3601  * @param int $id The id of an event from the 'calendar' table.
3602  * @return array An associative array with the results from the SQL call.
3603  * @todo Verify return type
3604  */
3605 function delete_event($id) {
3607     global $CFG;
3609     if (!empty($CFG->calendar)) { // call the delete_event function of the selected calendar
3610         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3611             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3612             $calendar_delete_event = $CFG->calendar.'_delete_event';
3613             if (function_exists($calendar_delete_event)) {
3614                 $calendar_delete_event($id);
3615             }
3616         }
3617     }
3618     return delete_records('event', 'id', $id);
3621 /**
3622  * Call this function to hide an event in the calendar table
3623  * the event will be identified by the id field of the $event object.
3624  *
3625  * @uses $CFG
3626  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field.
3627  * @return array An associative array with the results from the SQL call.
3628  * @todo Verify return type
3629  */
3630 function hide_event($event) {
3631     global $CFG;
3633     if (!empty($CFG->calendar)) { // call the update_event function of the selected calendar
3634         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3635             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3636             $calendar_hide_event = $CFG->calendar.'_hide_event';
3637             if (function_exists($calendar_hide_event)) {
3638                 $calendar_hide_event($event);
3639             }
3640         }
3641     }
3642     return set_field('event', 'visible', 0, 'id', $event->id);
3645 /**
3646  * Call this function to unhide an event in the calendar table
3647  * the event will be identified by the id field of the $event object.
3648  *
3649  * @uses $CFG
3650  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field.
3651  * @return array An associative array with the results from the SQL call.
3652  * @todo Verify return type
3653  */
3654 function show_event($event) {
3655     global $CFG;
3657     if (!empty($CFG->calendar)) { // call the update_event function of the selected calendar
3658         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3659             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3660             $calendar_show_event = $CFG->calendar.'_show_event';
3661             if (function_exists($calendar_show_event)) {
3662                 $calendar_show_event($event);
3663             }
3664         }
3665     }
3666     return set_field('event', 'visible', 1, 'id', $event->id);
3670 /// ENVIRONMENT CHECKING  ////////////////////////////////////////////////////////////
3672 /**
3673  * Lists plugin directories within some directory
3674  *
3675  * @uses $CFG
3676  * @param string $plugin ?
3677  * @param string $exclude ?
3678  * @return array
3679  * @todo Finish documenting this function
3680  */
3681 function get_list_of_plugins($plugin='mod', $exclude='') {
3683     global $CFG;
3685     $basedir = opendir($CFG->dirroot .'/'. $plugin);
3686     while ($dir = readdir($basedir)) {
3687         $firstchar = substr($dir, 0, 1);
3688         if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf' or $dir == $exclude) {
3689             continue;
3690         }
3691         if (filetype($CFG->dirroot .'/'. $plugin .'/'. $dir) != 'dir') {
3692             continue;
3693         }
3694         $plugins[] = $dir;
3695     }
3696     if ($plugins) {
3697         asort($plugins);
3698     }
3699     return $plugins;
3702 /**
3703  * Returns true if the current version of PHP is greater that the specified one.
3704  *
3705  * @param string $version The version of php being tested.
3706  * @return boolean
3707  * @todo Finish documenting this function
3708  */
3709 function check_php_version($version='4.1.0') {
3710     $minversion = intval(str_replace('.', '', $version));
3711     $curversion = intval(str_replace('.', '', phpversion()));
3712     return ($curversion >= $minversion);
3716 /**
3717  * Checks to see if is a browser matches the specified
3718  * brand and is equal or better version.
3719  *
3720  * @uses $_SERVER
3721  * @param string $brand The browser identifier being tested
3722  * @param int $version The version of the browser
3723  * @return boolean
3724  * @todo Finish documenting this function
3725  */
3726  function check_browser_version($brand='MSIE', $version=5.5) {
3727     $agent = $_SERVER['HTTP_USER_AGENT'];
3729     if (empty($agent)) {
3730         return false;
3731     }
3733     switch ($brand) {
3735       case 'Gecko':   /// Gecko based browsers
3737           if (substr_count($agent, 'Camino')) {
3738               // MacOS X Camino support
3739               $version = 20041110;
3740           }
3742           // the proper string - Gecko/CCYYMMDD Vendor/Version
3743           if (ereg("^([a-zA-Z]+)/([0-9]+\.[0-9]+) \((.*)\) (.*)$", $agent, $match)) {
3744               if (ereg("^([Gecko]+)/([0-9]+)",$match[4], $reldate)) {
3745                   if ($reldate[2] > $version) {
3746                       return true;
3747                   }
3748               }
3749           }
3750           break;
3753       case 'MSIE':   /// Internet Explorer
3755           if (strpos($agent, 'Opera')) {     // Reject Opera
3756               return false;
3757           }
3758           $string = explode(';', $agent);
3759           if (!isset($string[1])) {
3760               return false;
3761           }
3762           $string = explode(' ', trim($string[1]));
3763           if (!isset($string[0]) and !isset($string[1])) {
3764               return false;
3765           }
3766           if ($string[0] == $brand and (float)$string[1] >= $version ) {
3767               return true;
3768           }
3769           break;
3771     }
3773     return false;
3776 /**
3777  * This function makes the return value of ini_get consistent if you are
3778  * setting server directives through the .htaccess file in apache.
3779  * Current behavior for value set from php.ini On = 1, Off = [blank]
3780  * Current behavior for value set from .htaccess On = On, Off = Off
3781  * Contributed by jdell @ unr.edu
3782  *
3783  * @param string $ini_get_arg ?
3784  * @return boolean
3785  * @todo Finish documenting this function
3786  */
3787 function ini_get_bool($ini_get_arg) {
3788     $temp = ini_get($ini_get_arg);
3790     if ($temp == '1' or strtolower($temp) == 'on') {
3791         return true;
3792     }
3793     return false;
3796 /**
3797  * Compatibility stub to provide backward compatibility
3798  *
3799  * Determines if the HTML editor is enabled.
3800  * @deprecated Use {@link can_use_html_editor()} instead.
3801  */
3802  function can_use_richtext_editor() {
3803     return can_use_html_editor();
3806 /**
3807  * Determines if the HTML editor is enabled.
3808  *
3809  * This depends on site and user
3810  * settings, as well as the current browser being used.
3811  *
3812  * @return string|false Returns false if editor is not being used, otherwise
3813  * returns 'MSIE' or 'Gecko'.
3814  * @todo Finish documenting this function
3815  */
3816  function can_use_html_editor() {
3817     global $USER, $CFG;
3819     if (!empty($USER->htmleditor) and !empty($CFG->htmleditor)) {
3820         if (check_browser_version('MSIE', 5.5)) {
3821             return 'MSIE';
3822         } else if (check_browser_version('Gecko', 20030516)) {
3823             return 'Gecko';
3824         }
3825     }
3826     return false;
3829 /**
3830  * Hack to find out the GD version by parsing phpinfo output
3831  *
3832  * @return int GD version (1, 2, or 0)
3833  */
3834 function check_gd_version() {
3835     $gdversion = 0;
3837     if (function_exists('gd_info')){
3838         $gd_info = gd_info();
3839         if (substr_count($gd_info['GD Version'], '2.')) {
3840             $gdversion = 2;
3841         } else if (substr_count($gd_info['GD Version'], '1.')) {
3842             $gdversion = 1;
3843         }
3845     } else {
3846         ob_start();
3847         phpinfo(8);
3848         $phpinfo = ob_get_contents();
3849         ob_end_clean();
3851         $phpinfo = explode("\n", $phpinfo);
3854         foreach ($phpinfo as $text) {
3855             $parts = explode('</td>', $text);
3856             foreach ($parts as $key => $val) {
3857                 $parts[$key] = trim(strip_tags($val));
3858             }
3859             if ($parts[0] == 'GD Version') {
3860                 if (substr_count($parts[1], '2.0')) {
3861                     $parts[1] = '2.0';
3862                 }
3863                 $gdversion = intval($parts[1]);
3864             }
3865         }
3866     }
3868     return $gdversion;   // 1, 2 or 0
3871 /**
3872  * Determine if moodle installation requires update
3873  *
3874  * Checks version numbers of main code and all modules to see
3875  * if there are any mismatches
3876  *
3877  * @uses $CFG
3878  * @return boolean
3879  * @todo Finish documenting this function
3880  */
3881 function moodle_needs_upgrading() {
3882     global $CFG;
3884     include_once($CFG->dirroot .'/version.php');  # defines $version and upgrades
3885     if ($CFG->version) {
3886         if ($version > $CFG->version) {
3887             return true;
3888         }
3889         if ($mods = get_list_of_plugins('mod')) {
3890             foreach ($mods as $mod) {
3891                 $fullmod = $CFG->dirroot .'/mod/'. $mod;
3892                 unset($module);
3893                 if (!is_readable($fullmod .'/version.php')) {
3894                     notify('Module "'. $mod .'" is not readable - check permissions');
3895                     continue;
3896                 }
3897                 include_once($fullmod .'/version.php');  # defines $module with version etc
3898                 if ($currmodule = get_record('modules', 'name', $mod)) {
3899                     if ($module->version > $currmodule->version) {
3900                         return true;
3901                     }
3902                 }
3903             }
3904         }
3905     } else {
3906         return true;
3907     }
3908     return false;
3912 /// MISCELLANEOUS ////////////////////////////////////////////////////////////////////
3914 /**
3915  * Notify admin users or admin user of any failed logins (since last notification).
3916  *
3917  * @uses $CFG
3918  * @uses $db
3919  * @uses HOURSECS
3920  * @todo Finish documenting this function. Add long description with more detail on what it does.
3921  */
3922 function notify_login_failures() {
3923     global $CFG, $db;
3925     switch ($CFG->notifyloginfailures) {
3926         case 'mainadmin' :
3927             $recip = array(get_admin());
3928             break;
3929         case 'alladmins':
3930             $recip = get_admins();
3931             break;
3932     }
3934     if (empty($CFG->lastnotifyfailure)) {
3935         $CFG->lastnotifyfailure=0;
3936     }
3938     // we need to deal with the threshold stuff first.
3939     if (empty($CFG->notifyloginthreshold)) {
3940         $CFG->notifyloginthreshold = 10; // default to something sensible.
3941     }
3943     $notifyipsrs = $db->Execute('SELECT ip FROM '. $CFG->prefix .'log WHERE time > '. $CFG->lastnotifyfailure .'
3944                           AND module=\'login\' AND action=\'error\' GROUP BY ip HAVING count(*) > '. $CFG->notifyloginthreshold);
3946     $notifyusersrs = $db->Execute('SELECT info FROM '. $CFG->prefix .'log WHERE time > '. $CFG->lastnotifyfailure .'
3947                           AND module=\'login\' AND action=\'error\' GROUP BY info HAVING count(*) > '. $CFG->notifyloginthreshold);
3949     if ($notifyipsrs) {
3950         $ipstr = '';
3951         while ($row = $notifyipsrs->FetchRow()) {
3952             $ipstr .= "'". $row['ip'] ."',";
3953         }
3954         $ipstr = substr($ipstr,0,strlen($ipstr)-1);
3955     }
3956     if ($notifyusersrs) {
3957         $userstr = '';
3958         while ($row = $notifyusersrs->FetchRow()) {
3959             $userstr .= "'". $row['info'] ."',";
3960         }
3961         $userstr = substr($userstr,0,strlen($userstr)-1);
3962     }
3964     if (strlen($userstr) > 0 || strlen($ipstr) > 0) {
3965         $count = 0;
3966         $logs = get_logs('time > '. $CFG->lastnotifyfailure .' AND module=\'login\' AND action=\'error\' '
3967                  .((strlen($ipstr) > 0 && strlen($userstr) > 0) ? ' AND ( ip IN ('. $ipstr .') OR info IN ('. $userstr .') ) '
3968                  : ((strlen($ipstr) != 0) ? ' AND ip IN ('. $ipstr .') ' : ' AND info IN ('. $userstr .') ')), 'l.time DESC', '', '', $count);
3970         // if we haven't run in the last hour and we have something useful to report and we are actually supposed to be reporting to somebody
3971         if (is_array($recip) and count($recip) > 0 and ((time() - HOURSECS) > $CFG->lastnotifyfailure)
3972             and is_array($logs) and count($logs) > 0) {
3974             $message = '';
3975             $site = get_site();
3976             $subject = get_string('notifyloginfailuressubject', '', $site->fullname);
3977             $message .= get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot)
3978                  .(($CFG->lastnotifyfailure != 0) ? '('.userdate($CFG->lastnotifyfailure).')' : '')."\n\n";
3979             foreach ($logs as $log) {
3980                 $log->time = userdate($log->time);
3981                 $message .= get_string('notifyloginfailuresmessage','',$log)."\n";
3982             }
3983             $message .= "\n\n".get_string('notifyloginfailuresmessageend','',$CFG->wwwroot)."\n\n";
3984             foreach ($recip as $admin) {
3985                 mtrace('Emailing '. $admin->username .' about '. count($logs) .' failed login attempts');
3986                 email_to_user($admin,get_admin(),$subject,$message);
3987             }
3988             $conf->name = 'lastnotifyfailure';
3989             $conf->value = time();
3990             if ($current = get_record('config', 'name', 'lastnotifyfailure')) {
3991                 $conf->id = $current->id;
3992                 if (! update_record('config', $conf)) {
3993                     mtrace('Could not update last notify time');
3994                 }
3996             } else if (! insert_record('config', $conf)) {
3997                 mtrace('Could not set last notify time');
3998             }
3999         }
4000     }
4003 /**
4004  * moodle_setlocale
4005  *
4006  * @uses $CFG
4007  * @uses $USER
4008  * @uses $SESSION
4009  * @param string $locale ?
4010  * @todo Finish documenting this function
4011  */
4012 function moodle_setlocale($locale='') {
4014     global $SESSION, $USER, $CFG;
4016     if ($locale) {
4017         $CFG->locale = $locale;
4018     } else if (!empty($CFG->courselang) and ($CFG->courselang != $CFG->lang) ) {
4019         $CFG->locale = get_string('locale');
4020     } else if (!empty($SESSION->lang) and ($SESSION->lang != $CFG->lang) ) {
4021         $CFG->locale = get_string('locale');
4022     } else if (!empty($USER->lang) and ($USER->lang != $CFG->lang) ) {
4023         $CFG->locale = get_string('locale');
4024     } else if (empty($CFG->locale)) {
4025         $CFG->locale = get_string('locale');
4026         set_config('locale', $CFG->locale);   // cache it to save lookups in future
4027     }
4028     setlocale (LC_TIME, $CFG->locale);
4029     setlocale (LC_COLLATE, $CFG->locale);
4031     if ($CFG->locale != 'tr_TR') {            // To workaround a well-known PHP bug with Turkish
4032         setlocale (LC_CTYPE, $CFG->locale);
4033     }
4036 /**
4037  * Converts string to lowercase using most compatible function available.
4038  *
4039  * @param string $string The string to convert to all lowercase characters.
4040  * @param string $encoding The encoding on the string.
4041  * @return string
4042  * @todo Add examples of calling this function with/without encoding types
4043  */
4044 function moodle_strtolower ($string, $encoding='') {
4045     if (function_exists('mb_strtolower')) {
4046         if($encoding===''){
4047            return mb_strtolower($string);          //use multibyte support with default encoding
4048         } else {
4049            return mb_strtolower($string, $encoding); //use given encoding
4050         }
4051     } else {
4052         return strtolower($string);                // use common function what rely on current locale setting
4053     }
4056 /**
4057  * Count words in a string.
4058  *
4059  * Words are defined as things between whitespace.
4060  *
4061  * @param string $string The text to be searched for words.
4062  * @return int The count of words in the specified string
4063  */
4064 function count_words($string) {
4065     $string = strip_tags($string);
4066     return count(preg_split("/\w\b/", $string)) - 1;
4069 /**
4070  * Generate and return a random string of the specified length.
4071  *
4072  * @param int $length The length of the string to be created.
4073  * @return string
4074  */
4075 function random_string ($length=15) {
4076     $pool  = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
4077     $pool .= 'abcdefghijklmnopqrstuvwxyz';
4078     $pool .= '0123456789';
4079     $poollen = strlen($pool);
4080     mt_srand ((double) microtime() * 1000000);
4081     $string = '';
4082     for ($i = 0; $i < $length; $i++) {
4083         $string .= substr($pool, (mt_rand()%($poollen)), 1);
4084     }
4085     return $string;
4088 /**
4089  * Given dates in seconds, how many weeks is the date from startdate
4090  * The first week is 1, the second 2 etc ...
4091  *
4092  * @uses WEEKSECS
4093  * @param ? $startdate ?
4094  * @param ? $thedate ?
4095  * @return string
4096  * @todo Finish documenting this function
4097  */
4098 function getweek ($startdate, $thedate) {
4099     if ($thedate < $startdate) {   // error
4100         return 0;
4101     }
4103     return floor(($thedate - $startdate) / WEEKSECS) + 1;
4106 /**
4107  * returns a randomly generated password of length $maxlen.  inspired by
4108  * {@link http://www.phpbuilder.com/columns/jesus19990502.php3}
4109  *
4110  * @param int $maxlength  The maximum size of the password being generated.
4111  * @return string
4112  * @todo Finish documenting this function
4113  */
4114 function generate_password($maxlen=10) {
4115     global $CFG;
4117     $fillers = '1234567890!$-+';
4118     $wordlist = file($CFG->wordlist);
4120     srand((double) microtime() * 1000000);
4121     $word1 = trim($wordlist[rand(0, count($wordlist) - 1)]);
4122     $word2 = trim($wordlist[rand(0, count($wordlist) - 1)]);
4123     $filler1 = $fillers[rand(0, strlen($fillers) - 1)];
4125     return substr($word1 . $filler1 . $word2, 0, $maxlen);
4128 /**
4129  * Given a float, prints it nicely
4130  *
4131  * @param float $num The float to print
4132  * @param int $places The number of decimal places to print.
4133  * @return string
4134  */
4135 function format_float($num, $places=1) {
4136     return sprintf("%.$places"."f", $num);
4139 /**
4140  * Given a simple array, this shuffles it up just like shuffle()
4141  * Unlike PHP's shuffle() ihis function works on any machine.
4142  *
4143  * @param array $array The array to be rearranged
4144  * @return array
4145  */
4146 function swapshuffle($array) {
4148     srand ((double) microtime() * 10000000);
4149     $last = count($array) - 1;
4150     for ($i=0;$i<=$last;$i++) {
4151         $from = rand(0,$last);
4152         $curr = $array[$i];
4153         $array[$i] = $array[$from];
4154         $array[$from] = $curr;
4155     }
4156     return $array;
4159 /**
4160  * Like {@link swapshuffle()}, but works on associative arrays
4161  *
4162  * @param array $array The associative array to be rearranged
4163  * @return array
4164  */
4165 function swapshuffle_assoc($array) {
4166 ///
4168     $newkeys = swapshuffle(array_keys($array));
4169     foreach ($newkeys as $newkey) {
4170         $newarray[$newkey] = $array[$newkey];
4171     }
4172     return $newarray;
4175 /**
4176  * Given an arbitrary array, and a number of draws,
4177  * this function returns an array with that amount
4178  * of items.  The indexes are retained.
4179  *
4180  * @param array $array ?
4181  * @param ? $draws ?
4182  * @return ?
4183  * @todo Finish documenting this function
4184  */
4185 function draw_rand_array($array, $draws) {
4186     srand ((double) microtime() * 10000000);
4188     $return = array();
4190     $last = count($array);
4192     if ($draws > $last) {
4193         $draws = $last;
4194     }
4196     while ($draws > 0) {
4197         $last--;
4199         $keys = array_keys($array);
4200         $rand = rand(0, $last);
4202         $return[$keys[$rand]] = $array[$keys[$rand]];
4203         unset($array[$keys[$rand]]);
4205         $draws--;
4206     }
4208     return $return;
4211 /**
4212  * microtime_diff
4213  *
4214  * @param string $a ?
4215  * @param string $b ?
4216  * @return string
4217  * @todo Finish documenting this function
4218  */
4219 function microtime_diff($a, $b) {
4220     list($a_dec, $a_sec) = explode(' ', $a);
4221     list($b_dec, $b_sec) = explode(' ', $b);
4222     return $b_sec - $a_sec + $b_dec - $a_dec;
4225 /**
4226  * Given a list (eg a,b,c,d,e) this function returns
4227  * an array of 1->a, 2->b, 3->c etc
4228  *
4229  * @param array $list ?
4230  * @param string $separator ?
4231  * @todo Finish documenting this function
4232  */
4233 function make_menu_from_list($list, $separator=',') {
4235     $array = array_reverse(explode($separator, $list), true);
4236     foreach ($array as $key => $item) {
4237         $outarray[$key+1] = trim($item);
4238     }
4239     return $outarray;
4242 /**
4243  * Creates an array that represents all the current grades that
4244  * can be chosen using the given grading type.  Negative numbers
4245  * are scales, zero is no grade, and positive numbers are maximum
4246  * grades.
4247  *
4248  * @param int $gradingtype ?
4249  * return int
4250  * @todo Finish documenting this function
4251  */
4252 function make_grades_menu($gradingtype) {
4253     $grades = array();
4254     if ($gradingtype < 0) {
4255         if ($scale = get_record('scale', 'id', - $gradingtype)) {
4256             return make_menu_from_list($scale->scale);
4257         }
4258     } else if ($gradingtype > 0) {
4259         for ($i=$gradingtype; $i>=0; $i--) {
4260             $grades[$i] = $i .' / '. $gradingtype;
4261         }
4262         return $grades;
4263     }
4264     return $grades;
4267 /**
4268  * This function returns the nummber of activities
4269  * using scaleid in a courseid
4270  *
4271  * @param int $courseid ?
4272  * @param int $scaleid ?
4273  * @return int
4274  * @todo Finish documenting this function
4275  */
4276 function course_scale_used($courseid, $scaleid) {
4278     global $CFG;
4280     $return = 0;
4282     if (!empty($scaleid)) {
4283         if ($cms = get_course_mods($courseid)) {
4284             foreach ($cms as $cm) {
4285                 //Check cm->name/lib.php exists
4286                 if (file_exists($CFG->dirroot.'/mod/'.$cm->modname.'/lib.php')) {
4287                     include_once($CFG->dirroot.'/mod/'.$cm->modname.'/lib.php');
4288                     $function_name = $cm->modname.'_scale_used';
4289                     if (function_exists($function_name)) {
4290                         if ($function_name($cm->instance,$scaleid)) {
4291                             $return++;
4292                         }
4293                     }
4294                 }
4295             }
4296         }
4297     }
4298     return $return;
4301 /**
4302  * This function returns the nummber of activities
4303  * using scaleid in the entire site
4304  *
4305  * @param int $scaleid ?
4306  * @return int
4307  * @todo Finish documenting this function. Is return type correct?
4308  */
4309 function site_scale_used($scaleid,&$courses) {
4311     global $CFG;
4313     $return = 0;
4315     if (!is_array($courses) || count($courses) == 0) {
4316         $courses = get_courses("all",false,"c.id,c.shortname");
4317     }
4319     if (!empty($scaleid)) {
4320         if (is_array($courses) && count($courses) > 0) {
4321             foreach ($courses as $course) {
4322                 $return += course_scale_used($course->id,$scaleid);
4323             }
4324         }
4325     }
4326     return $return;
4329 /**
4330  * make_unique_id_code
4331  *
4332  * @param string $extra ?
4333  * @return string
4334  * @todo Finish documenting this function
4335  */
4336 function make_unique_id_code($extra='') {
4338     $hostname = 'unknownhost';
4339     if (!empty($_SERVER['HTTP_HOST'])) {
4340         $hostname = $_SERVER['HTTP_HOST'];
4341     } else if (!empty($_ENV['HTTP_HOST'])) {
4342         $hostname = $_ENV['HTTP_HOST'];
4343     } else if (!empty($_SERVER['SERVER_NAME'])) {
4344         $hostname = $_SERVER['SERVER_NAME'];
4345     } else if (!empty($_ENV['SERVER_NAME'])) {
4346         $hostname = $_ENV['SERVER_NAME'];
4347     }
4349     $date = gmdate("ymdHis");
4351     $random =  random_string(6);
4353     if ($extra) {
4354         return $hostname .'+'. $date .'+'. $random .'+'. $extra;
4355     } else {
4356         return $hostname .'+'. $date .'+'. $random;
4357     }
4361 /**
4362  * Function to check the passed address is within the passed subnet
4363  *
4364  * The parameter is a comma separated string of subnet definitions.
4365  * Subnet strings can be in one of two formats:
4366  *   1: xxx.xxx.xxx.xxx/xx
4367  *   2: xxx.xxx
4368  * Code for type 1 modified from user posted comments by mediator at
4369  * {@link http://au.php.net/manual/en/function.ip2long.php}
4370  *
4371  * @param string $addr    The address you are checking
4372  * @param string $subnetstr    The string of subnet addresses
4373  * @return boolean
4374  */
4375 function address_in_subnet($addr, $subnetstr) {
4377     $subnets = explode(',', $subnetstr);
4378     $found = false;
4379     $addr = trim($addr);
4381     foreach ($subnets as $subnet) {
4382         $subnet = trim($subnet);
4383         if (strpos($subnet, '/') !== false) { /// type 1
4385             list($ip, $mask) = explode('/', $subnet);
4386             $mask = 0xffffffff << (32 - $mask);
4387             $found = ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
4389         } else { /// type 2
4390             $found = (strpos($addr, $subnet) === 0);
4391         }
4393         if ($found) {
4394             continue;
4395         }
4396     }
4398     return $found;
4401 /**
4402  * For outputting debugging info
4403  *
4404  * @uses STDOUT
4405  * @param string $string ?
4406  * @param string $eol ?
4407  * @todo Finish documenting this function
4408  */
4409 function mtrace($string, $eol="\n") {
4411     if (defined('STDOUT')) {
4412         fwrite(STDOUT, $string.$eol);
4413     } else {
4414         echo $string . $eol;
4415     }
4417     flush();
4420 //Replace 1 or more slashes or backslashes to 1 slash
4421 function cleardoubleslashes ($path) {
4422     return preg_replace('/(\/|\\\){1,}/','/',$path);
4425 function zip_files ($originalfiles, $destination) {
4426 //Zip an array of files/dirs to a destination zip file
4427 //Both parameters must be FULL paths to the files/dirs
4429     global $CFG;
4431     //Extract everything from destination
4432     $path_parts = pathinfo(cleardoubleslashes($destination));
4433     $destpath = $path_parts["dirname"];       //The path of the zip file
4434     $destfilename = $path_parts["basename"];  //The name of the zip file
4435     $extension = $path_parts["extension"];    //The extension of the file
4437     //If no file, error
4438     if (empty($destfilename)) {
4439         return false;
4440     }
4442     //If no extension, add it
4443     if (empty($extension)) {
4444         $extension = 'zip';
4445         $destfilename = $destfilename.'.'.$extension;
4446     }
4448     //Check destination path exists
4449     if (!is_dir($destpath)) {
4450         return false;
4451     }
4453     //Check destination path is writable. TODO!!
4455     //Clean destination filename
4456     $destfilename = clean_filename($destfilename);
4458     //Now check and prepare every file
4459     $files = array();
4460     $origpath = NULL;
4462     foreach ($originalfiles as $file) {  //Iterate over each file
4463         //Check for every file
4464         $tempfile = cleardoubleslashes($file); // no doubleslashes!
4465         //Calculate the base path for all files if it isn't set
4466         if ($origpath === NULL) {
4467             $origpath = rtrim(cleardoubleslashes(dirname($tempfile)), "/");
4468         }
4469         //See if the file is readable
4470         if (!is_readable($tempfile)) {  //Is readable
4471             continue;
4472         }
4473         //See if the file/dir is in the same directory than the rest
4474         if (rtrim(cleardoubleslashes(dirname($tempfile)), "/") != $origpath) {
4475             continue;
4476         }
4477         //Add the file to the array
4478         $files[] = $tempfile;
4479     }
4481     //Everything is ready:
4482     //    -$origpath is the path where ALL the files to be compressed reside (dir).
4483     //    -$destpath is the destination path where the zip file will go (dir).
4484     //    -$files is an array of files/dirs to compress (fullpath)
4485     //    -$destfilename is the name of the zip file (without path)
4487     //print_object($files);                  //Debug
4489     if (empty($CFG->zip)) {    // Use built-in php-based zip function
4491         include_once("$CFG->libdir/pclzip/pclzip.lib.php");
4492         $archive = new PclZip(cleardoubleslashes("$destpath/$destfilename"));
4493         if (($list = $archive->create($files, PCLZIP_OPT_REMOVE_PATH,$origpath) == 0)) {
4494             notice($archive->errorInfo(true));
4495             return false;
4496         }
4498     } else {                   // Use external zip program
4500         $filestozip = "";
4501         foreach ($files as $filetozip) {
4502             $filestozip .= escapeshellarg(basename($filetozip));
4503             $filestozip .= " ";
4504         }
4505         //Construct the command
4506         $separator = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? ' &' : ' ;';
4507         $command = 'cd '.escapeshellarg($origpath).$separator.
4508                     escapeshellarg($CFG->zip).' -r '.
4509                     escapeshellarg(cleardoubleslashes("$destpath/$destfilename")).' '.$filestozip;
4510         //All converted to backslashes in WIN
4511         if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
4512             $command = str_replace('/','\\',$command);
4513         }
4514         Exec($command);
4515     }
4516     return true;
4519 function unzip_file ($zipfile, $destination = '', $showstatus = true) {
4520 //Unzip one zip file to a destination dir
4521 //Both parameters must be FULL paths
4522 //If destination isn't specified, it will be the
4523 //SAME directory where the zip file resides.
4525     global $CFG;
4527     //Extract everything from zipfile
4528     $path_parts = pathinfo(cleardoubleslashes($zipfile));
4529     $zippath = $path_parts["dirname"];       //The path of the zip file
4530     $zipfilename = $path_parts["basename"];  //The name of the zip file
4531     $extension = $path_parts["extension"];    //The extension of the file
4533     //If no file, error
4534     if (empty($zipfilename)) {
4535         return false;
4536     }