User preferences were not getting set!
[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 = NULL;  // Continue with the current user below
426         } else {
427             return false;    // No-one to set!
428         }
429     }
431     $return = true;
432     foreach ($prefarray as $name => $value) {
433         // The order is important; if the test for return is done first, then
434         // if one function call fails all the remaining ones will be "optimized away"
435         $return = set_user_preference($name, $value, $userid) and $return;
436     }
437     return $return;
440 /**
441  * If no arguments are supplied this function will return
442  * all of the current user preferences as an array.
443  * If a name is specified then this function
444  * attempts to return that particular preference value.  If
445  * none is found, then the optional value $default is returned,
446  * otherwise NULL.
447  * @param string $name Name of the key to use in finding a preference value
448  * @param string $default Value to be returned if the $name key is not set in the user preferences
449  * @param int $userid A moodle user ID
450  * @uses $USER
451  * @return string
452  */
453 function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {
455     global $USER;
457     if (empty($userid)) {   // assume current user
458         if (empty($USER->preference)) {
459             return $default;              // Default value (or NULL)
460         }
461         if (empty($name)) {
462             return $USER->preference;     // Whole array
463         }
464         if (!isset($USER->preference[$name])) {
465             return $default;              // Default value (or NULL)
466         }
467         return $USER->preference[$name];  // The single value
469     } else {
470         $preference = get_records_menu('user_preferences', 'userid', $userid, 'name', 'name,value');
472         if (empty($name)) {
473             return $preference;
474         }
475         if (!isset($preference[$name])) {
476             return $default;              // Default value (or NULL)
477         }
478         return $preference[$name];        // The single value
479     }
483 /// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
485 /**
486  * Given date parts in user time produce a GMT timestamp.
487  *
488  * @param int $year The year part to create timestamp of.
489  * @param int $month The month part to create timestamp of.
490  * @param int $day The day part to create timestamp of.
491  * @param int $hour The hour part to create timestamp of.
492  * @param int $minute The minute part to create timestamp of.
493  * @param int $second The second part to create timestamp of.
494  * @param int $timezone ?
495  * @return ?
496  * @todo Finish documenting this function
497  */
498 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
500     $timezone = get_user_timezone($timezone);
502     if (abs($timezone) > 13) {
503         $time = mktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year, 0);
504     } else {
505         $time = gmmktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year, 0);
506         $time = usertime($time, $timezone);  // This is GMT
507     }
509     if(!$applydst) {
510         return $time;
511     }
513     return $time;
515     /*
516     // WARNING: BUG: TODO: This is buggy, but it will do for testing purposes
517     if(($dstid = get_user_preferences('calendar_dstpreset')) !== NULL) {
518         $preset = get_record('dst_preset', 'id', $dstid);
519         if($time > $preset->last_change && $time < $preset->next_change) {
520             return $time;
521         }
523         // We need to find out what's going on...
524         $nowuserdate = usergetdate($time);
526         $changes = calendar_dst_changes_for_year($year, $preset);
527         if($time < $changes['activate'] || $time > $changes['deactivate']) {
528             // DST will be off at that time
529             if($preset->current_offset != 0) {
530                 print_object('Uncompensated time was:');
531                 print_object(usergetdate($time));
532                 $time += $preset->apply_offset * 60;
533                 print_object('Compensated time is:');
534                 print_object(usergetdate($time));
535             }
536         }
537         else {
538             // DST will be on at that time
539             if($preset->current_offset == 0) {
540                 print_object('Uncompensated time was:');
541                 print_object(usergetdate($time));
542                 $time -= $preset->apply_offset * 60;
543                 print_object('Compensated time is:');
544                 print_object(usergetdate($time));
545             }
546         }
547         
548         return $time;
549     }
550     */
553 /**
554  * Given an amount of time in seconds, returns string
555  * formatted nicely as months, days, hours etc as needed
556  *
557  * @uses MINSECS
558  * @uses HOURSECS
559  * @uses DAYSECS
560  * @param int $totalsecs ?
561  * @param array $str ?
562  * @return string
563  * @todo Finish documenting this function
564  */
565  function format_time($totalsecs, $str=NULL) {
567     $totalsecs = abs($totalsecs);
569     if (!$str) {  // Create the str structure the slow way
570         $str->day   = get_string('day');
571         $str->days  = get_string('days');
572         $str->hour  = get_string('hour');
573         $str->hours = get_string('hours');
574         $str->min   = get_string('min');
575         $str->mins  = get_string('mins');
576         $str->sec   = get_string('sec');
577         $str->secs  = get_string('secs');
578     }
580     $days      = floor($totalsecs/DAYSECS);
581     $remainder = $totalsecs - ($days*DAYSECS);
582     $hours     = floor($remainder/HOURSECS);
583     $remainder = $remainder - ($hours*HOURSECS);
584     $mins      = floor($remainder/MINSECS);
585     $secs      = $remainder - ($mins*MINSECS);
587     $ss = ($secs == 1)  ? $str->sec  : $str->secs;
588     $sm = ($mins == 1)  ? $str->min  : $str->mins;
589     $sh = ($hours == 1) ? $str->hour : $str->hours;
590     $sd = ($days == 1)  ? $str->day  : $str->days;
592     $odays = '';
593     $ohours = '';
594     $omins = '';
595     $osecs = '';
597     if ($days)  $odays  = $days .' '. $sd;
598     if ($hours) $ohours = $hours .' '. $sh;
599     if ($mins)  $omins  = $mins .' '. $sm;
600     if ($secs)  $osecs  = $secs .' '. $ss;
602     if ($days)  return $odays .' '. $ohours;
603     if ($hours) return $ohours .' '. $omins;
604     if ($mins)  return $omins .' '. $osecs;
605     if ($secs)  return $osecs;
606     return get_string('now');
609 /**
610  * Returns a formatted string that represents a date in user time
611  * <b>WARNING: note that the format is for strftime(), not date().</b>
612  * Because of a bug in most Windows time libraries, we can't use
613  * the nicer %e, so we have to use %d which has leading zeroes.
614  * A lot of the fuss in the function is just getting rid of these leading
615  * zeroes as efficiently as possible.
616  *
617  * If parameter fixday = true (default), then take off leading
618  * zero from %d, else mantain it.
619  *
620  * @uses HOURSECS
621  * @param  int $date ?
622  * @param string $format ?
623  * @param int $timezone ?
624  * @param boolean $fixday If true (default) then the leading
625  * zero from %d is removed. If false then the leading zero is mantained.
626  * @return string
627  * @todo Finish documenting this function
628  */
629 function userdate($date, $format='', $timezone=99, $fixday = true) {
631     if ($format == '') {
632         $format = get_string('strftimedaydatetime');
633     }
635     $formatnoday = str_replace('%d', 'DD', $format);
636     if ($fixday) {
637         $fixday = ($formatnoday != $format);
638     }
640     $timezone = get_user_timezone($timezone);
642     if (abs($timezone) > 13) {
643         if ($fixday) {
644             $datestring = strftime($formatnoday, $date);
645             $daystring  = str_replace(' 0', '', strftime(" %d", $date));
646             $datestring = str_replace('DD', $daystring, $datestring);
647         } else {
648             $datestring = strftime($format, $date);
649         }
650     } else {
651         $date = $date + (int)($timezone * HOURSECS);
652         if ($fixday) {
653             $datestring = gmstrftime($formatnoday, $date);
654             $daystring  = str_replace(' 0', '', gmstrftime(" %d", $date));
655             $datestring = str_replace('DD', $daystring, $datestring);
656         } else {
657             $datestring = gmstrftime($format, $date);
658         }
659     }
661     return $datestring;
664 /**
665  * Given a $date timestamp in GMT (seconds since epoch),
666  * returns an array that represents the date in user time
667  *
668  * @uses HOURSECS
669  * @param  int $date Timestamp in GMT
670  * @param int $timezone ?
671  * @return array An array that represents the date in user time
672  * @todo Finish documenting this function
673  */
674 function usergetdate($date, $timezone=99) {
676     $timezone = get_user_timezone($timezone);
678     if (abs($timezone) > 13) {
679         return getdate($date);
680     }
681     //There is no gmgetdate so I have to fake it...
682     $date = $date + (int)($timezone * HOURSECS);
684     // This is independent of the server's TZ settings,
685     // unlike gmstrftime. It's also a bit faster this way.
686     list(
687         $getdate['seconds'],
688         $getdate['minutes'],
689         $getdate['hours'],
690         $getdate['mday'],
691         $getdate['mon'],
692         $getdate['year'],
693         $getdate['wday'],
694         $getdate['yday'],
695         $getdate['weekday'],
696         $getdate['month']
697     ) = explode(' ', gmdate('s i H d m Y w z l F', $date));
699     return $getdate;
702 /**
703  * Given a GMT timestamp (seconds since epoch), offsets it by
704  * the timezone.  eg 3pm in India is 3pm GMT - 7 * 3600 seconds
705  *
706  * @uses HOURSECS
707  * @param  int $date Timestamp in GMT
708  * @param int $timezone ?
709  * @return int
710  * @todo Finish documenting this function
711  */
712 function usertime($date, $timezone=99) {
714     $timezone = get_user_timezone($timezone);
715     if (abs($timezone) > 13) {
716         return $date;
717     }
718     return $date - (int)($timezone * HOURSECS);
721 /**
722  * Given a time, return the GMT timestamp of the most recent midnight
723  * for the current user.
724  *
725  * @param  int $date Timestamp in GMT
726  * @param int $timezone ?
727  * @return ?
728  * @todo Finish documenting this function. Is timezone an int or float?
729  */
730 function usergetmidnight($date, $timezone=99) {
732     $timezone = get_user_timezone($timezone);
733     $userdate = usergetdate($date, $timezone);
735     if (abs($timezone) > 13) {
736         return mktime(0, 0, 0, $userdate['mon'], $userdate['mday'], $userdate['year']);
737     }
739     $timemidnight = gmmktime (0, 0, 0, $userdate['mon'], $userdate['mday'], $userdate['year']);
740     return usertime($timemidnight, $timezone); // Time of midnight of this user's day, in GMT
744 /**
745  * Returns a string that prints the user's timezone
746  *
747  * @param float $timezone The user's timezone
748  * @return string
749  * @todo is $timezone an int or a float?
750  */
751 function usertimezone($timezone=99) {
753     $timezone = get_user_timezone($timezone);
755     if (abs($timezone) > 13) {
756         return 'server time';
757     }
758     if (abs($timezone) < 0.5) {
759         return 'GMT';
760     }
761     if ($timezone > 0) {
762         return 'GMT+'. $timezone;
763     } else {
764         return 'GMT'. $timezone;
765     }
768 /**
769  * Returns a float which represents the user's timezone difference from GMT in hours
770  * Checks various settings and picks the most dominant of those which have a value
771  *
772  * @uses $CFG
773  * @uses $USER
774  * @param int $tz The user's timezone
775  * @return int
776  * @todo is $tz an int or a float?
777  */
778 function get_user_timezone($tz = 99) {
780     // Variables declared explicitly global here so that if we add
781     // something later we won't forget to global it...
782     $timezones = array(
783         isset($GLOBALS['USER']->timezone) ? $GLOBALS['USER']->timezone : 99,
784         isset($GLOBALS['CFG']->timezone) ? $GLOBALS['CFG']->timezone : 99,
785         );
786     while($tz == 99 && $next = each($timezones)) {
787         $tz = (float)$next['value'];
788     }
790     return $tz;
793 /// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////
795 // Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
796 // if one does not already exist, but does not overwrite existing sesskeys. Returns the
797 // sesskey string if $USER exists, or boolean false if not.
798 function set_user_sesskey() {
799     global $USER;
801     if(!isset($USER)) {
802         return false;
803     }
805     if (empty($USER->sesskey)) {
806         $USER->sesskey = random_string(10);
807     }
809     return $USER->sesskey;
812 /**
813  * This function checks that the current user is logged in, and optionally
814  * whether they are "logged in" or allowed to be in a particular course.
815  * If not, then it redirects them to the site login or course enrolment.
816  * $autologinguest determines whether visitors should automatically be
817  * logged in as guests provide {@link $CFG}->autologinguests is set to 1
818  *
819  * @uses $CFG
820  * @uses $SESSION
821  * @uses $USER
822  * @uses $FULLME
823  * @uses SITEID
824  * @uses $MoodleSession
825  * @param int $courseid The course in question
826  * @param boolean $autologinguest ?
827  * @todo Finish documenting this function
828  */
829 function require_login($courseid=0, $autologinguest=true) {
831     global $CFG, $SESSION, $USER, $FULLME, $MoodleSession;
833     // First check that the user is logged in to the site.
834     if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
835         $SESSION->wantsurl = $FULLME;
836         if (!empty($_SERVER['HTTP_REFERER'])) {
837             $SESSION->fromurl  = $_SERVER['HTTP_REFERER'];
838         }
839         $USER = NULL;
840         if ($autologinguest and $CFG->autologinguests and $courseid and get_field('course','guest','id',$courseid)) {
841             $loginguest = '?loginguest=true';
842         } else {
843             $loginguest = '';
844         }
845         if (empty($CFG->loginhttps)) {
846             redirect($CFG->wwwroot .'/login/index.php'. $loginguest);
847         } else {
848             $wwwroot = str_replace('http','https', $CFG->wwwroot);
849             redirect($wwwroot .'/login/index.php'. $loginguest);
850         }
851         die;
852     }
854     // check whether the user should be changing password
855     // reload_user_preferences();    // Why is this necessary?  Seems wasteful.  - MD
856     if (!empty($USER->preference['auth_forcepasswordchange'])){
857         if (is_internal_auth() || $CFG->{'auth_'.$USER->auth.'_stdchangepassword'}){
858             redirect($CFG->wwwroot .'/login/change_password.php');
859         } elseif($CFG->changepassword) {
860             redirect($CFG->changepassword);
861         } else {
862             error('You cannot proceed without changing your password.
863                    However there is no available page for changing it.
864                    Please contact your Moodle Administrator.');
865         }
866     }
868     // Check that the user account is properly set up
869     if (user_not_fully_set_up($USER)) {
870         redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
871         die;
872     }
874     // Make sure current IP matches the one for this session (if required)
875     if (!empty($CFG->tracksessionip)) {
876         if ($USER->sessionIP != md5(getremoteaddr())) {
877             error(get_string('sessionipnomatch', 'error'));
878         }
879     }
881     // Make sure the USER has a sesskey set up.  Used for checking script parameters.
882     set_user_sesskey();
884     // Check that the user has agreed to a site policy if there is one
885     if (!empty($CFG->sitepolicy)) {
886         if (!$USER->policyagreed) {
887             $SESSION->wantsurl = $FULLME;
888             redirect($CFG->wwwroot .'/user/policy.php');
889             die;
890         }
891     }
893     // Next, check if the user can be in a particular course
894     if ($courseid) {
895         if ($courseid == SITEID) {
896             return;   // Anyone can be in the site course
897         }
898         if (!empty($USER->student[$courseid]) or !empty($USER->teacher[$courseid]) or !empty($USER->admin)) {
899             if (isset($USER->realuser)) {   // Make sure the REAL person can also access this course
900                 if (!isteacher($courseid, $USER->realuser)) {
901                     print_header();
902                     notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
903                 }
904             }
905             return;   // user is a member of this course.
906         }
907         if (! $course = get_record('course', 'id', $courseid)) {
908             error('That course doesn\'t exist');
909         }
910         if (!$course->visible) {
911             print_header();
912             notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
913         }
914         if ($USER->username == 'guest') {
915             switch ($course->guest) {
916                 case 0: // Guests not allowed
917                     print_header();
918                     notice(get_string('guestsnotallowed', '', $course->fullname));
919                     break;
920                 case 1: // Guests allowed
921                     return;
922                 case 2: // Guests allowed with key (drop through)
923                     break;
924             }
925         }
927         // Currently not enrolled in the course, so see if they want to enrol
928         $SESSION->wantsurl = $FULLME;
929         redirect($CFG->wwwroot .'/course/enrol.php?id='. $courseid);
930         die;
931     }
934 /**
935  * This is a weaker version of {@link require_login()} which only requires login
936  * when called from within a course rather than the site page, unless
937  * the forcelogin option is turned on.
938  *
939  * @uses $CFG
940  * @param int $courseid The course in question
941  * @param boolean $autologinguest ?
942  * @todo Finish documenting this function
943  */
944 function require_course_login($course, $autologinguest=true) {
945     global $CFG;
946     if ($CFG->forcelogin) {
947       require_login();
948     }
949     if ($course->category) {
950       require_login($course->id, $autologinguest);
951     }
954 /**
955  * Modify the user table by setting the currently logged in user's
956  * last login to now.
957  *
958  * @uses $USER
959  * @return boolean
960  */
961 function update_user_login_times() {
962     global $USER;
964     $USER->lastlogin = $user->lastlogin = $USER->currentlogin;
965     $USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
967     $user->id = $USER->id;
969     return update_record('user', $user);
972 /**
973  * Determines if a user has completed setting up their account.
974  *
975  * @param user $user A {@link $USER} object to test for the existance of a valid name and email
976  * @return boolean
977  */
978 function user_not_fully_set_up($user) {
979     return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email)));
982 /**
983  * Keeps track of login attempts
984  *
985  * @uses $SESSION
986  */
987 function update_login_count() {
989     global $SESSION;
991     $max_logins = 10;
993     if (empty($SESSION->logincount)) {
994         $SESSION->logincount = 1;
995     } else {
996         $SESSION->logincount++;
997     }
999     if ($SESSION->logincount > $max_logins) {
1000         unset($SESSION->wantsurl);
1001         error(get_string('errortoomanylogins'));
1002     }
1005 /**
1006  * Resets login attempts
1007  *
1008  * @uses $SESSION
1009  */
1010 function reset_login_count() {
1011     global $SESSION;
1013     $SESSION->logincount = 0;
1016 /**
1017  * check_for_restricted_user
1018  *
1019  * @uses $CFG
1020  * @uses $USER
1021  * @param string $username ?
1022  * @param string $redirect ?
1023  * @todo Finish documenting this function
1024  */
1025 function check_for_restricted_user($username=NULL, $redirect='') {
1026     global $CFG, $USER;
1028     if (!$username) {
1029         if (!empty($USER->username)) {
1030             $username = $USER->username;
1031         } else {
1032             return false;
1033         }
1034     }
1036     if (!empty($CFG->restrictusers)) {
1037         $names = explode(',', $CFG->restrictusers);
1038         if (in_array($username, $names)) {
1039             error(get_string('restricteduser', 'error', fullname($USER)), $redirect);
1040         }
1041     }
1044 /**
1045  * Determines if a user an admin
1046  *
1047  * @uses $USER
1048  * @param int $userid The id of the user as is found in the 'user' table
1049  * @staticvar array $admin ?
1050  * @staticvar array $nonadmins ?
1051  * @return boolean
1052  * @todo Complete documentation for this function
1053  */
1054 function isadmin($userid=0) {
1055     global $USER;
1056     static $admins = array();
1057     static $nonadmins = array();
1059     if (!$userid){
1060         if (empty($USER->id)) {
1061             return false;
1062         }
1063         $userid = $USER->id;
1064     }
1066     if (in_array($userid, $admins)) {
1067         return true;
1068     } else if (in_array($userid, $nonadmins)) {
1069         return false;
1070     } else if (record_exists('user_admins', 'userid', $userid)){
1071         $admins[] = $userid;
1072         return true;
1073     } else {
1074         $nonadmins[] = $userid;
1075         return false;
1076     }
1079 /**
1080  * Determines if a user is a teacher or an admin
1081  *
1082   * @uses $USER
1083  * @param int $courseid The id of the course that is being viewed, if any
1084  * @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.
1085  * @param boolean $includeadmin If true this function will return true when it encounters an admin user.
1086  * @return boolean
1087  * @todo Finish documenting this function
1088  */
1089 function isteacher($courseid=0, $userid=0, $includeadmin=true) {
1090     global $USER;
1092     if ($includeadmin and isadmin($userid)) {  // admins can do anything the teacher can
1093         return true;
1094     }
1096     if (!$userid) {
1097         if ($courseid) {
1098             return !empty($USER->teacher[$courseid]);
1099         }
1100         if (!isset($USER->id)) {
1101             return false;
1102         }
1103         $userid = $USER->id;
1104     }
1106     if (!$courseid) {
1107         return record_exists('user_teachers', 'userid', $userid);
1108     }
1110     return record_exists('user_teachers', 'userid', $userid, 'course', $courseid);
1113 /**
1114  * Determines if a user is allowed to edit a given course
1115  *
1116  * @uses $USER
1117  * @param int $courseid The id of the course that is being edited
1118  * @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.
1119  * @return boolean
1120  */
1121 function isteacheredit($courseid, $userid=0) {
1122     global $USER;
1124     if (isadmin($userid)) {  // admins can do anything
1125         return true;
1126     }
1128     if (!$userid) {
1129         return !empty($USER->teacheredit[$courseid]);
1130     }
1132     return get_field('user_teachers', 'editall', 'userid', $userid, 'course', $courseid);
1135 /**
1136  * Determines if a user can create new courses
1137  *
1138  * @uses $USER
1139  * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
1140  * @return boolean
1141  */
1142 function iscreator ($userid=0) {
1143     global $USER;
1144     if (empty($USER->id)) {
1145         return false;
1146     }
1147     if (isadmin($userid)) {  // admins can do anything
1148         return true;
1149     }
1150     if (empty($userid)) {
1151         return record_exists('user_coursecreators', 'userid', $USER->id);
1152     }
1154     return record_exists('user_coursecreators', 'userid', $userid);
1157 /**
1158  * Determines if a user is a student in the specified course
1159  *
1160  * If the course id specifies the site then the function determines
1161  * if the user is a confirmed and valid user of this site.
1162  *
1163  * @uses $USER
1164  * @uses $CFG
1165  * @uses SITEID
1166  * @param int $courseid The id of the course being tested
1167  * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
1168  * @return boolean
1169  */
1170 function isstudent($courseid, $userid=0) {
1171     global $USER, $CFG;
1173     if (empty($USER->id) and !$userid) {
1174         return false;
1175     }
1177     if ($courseid == SITEID) {
1178         if (!$userid) {
1179             $userid = $USER->id;
1180         }
1181         if (isguest($userid)) {
1182             return false;
1183         }
1184         // a site teacher can never be a site student
1185         if (isteacher($courseid, $userid)) {
1186             return false;
1187         }
1188         if ($CFG->allusersaresitestudents) {
1189             return record_exists('user', 'id', $userid);
1190         } else {
1191             return (record_exists('user_students', 'userid', $userid)
1192                      or record_exists('user_teachers', 'userid', $userid));
1193         }
1194     }
1196     if (!$userid) {
1197         return !empty($USER->student[$courseid]);
1198     }
1200   //  $timenow = time();   // todo:  add time check below
1202     return record_exists('user_students', 'userid', $userid, 'course', $courseid);
1205 /**
1206  * Determines if the specified user is logged in as guest.
1207  *
1208  * @uses $USER
1209  * @param int $userid The user being tested. You can set this to 0 or leave it blank to test the currently logged in user.
1210  * @return boolean
1211  */
1212 function isguest($userid=0) {
1213     global $USER;
1215     if (!$userid) {
1216         if (empty($USER->username)) {
1217             return false;
1218         }
1219         return ($USER->username == 'guest');
1220     }
1222     return record_exists('user', 'id', $userid, 'username', 'guest');
1225 /**
1226  * Determines if the currently logged in user is in editing mode
1227  *
1228  * @uses $USER
1229  * @param int $courseid The id of the course being tested
1230  * @param user $user A {@link $USER} object. If null then the currently logged in user is used.
1231  * @return boolean
1232  */
1233 function isediting($courseid, $user=NULL) {
1234     global $USER;
1235     if (!$user){
1236         $user = $USER;
1237     }
1238     if (empty($user->editing)) {
1239         return false;
1240     }
1241     return ($user->editing and isteacher($courseid, $user->id));
1244 /**
1245  * Determines if the logged in user is currently moving an activity
1246  *
1247  * @uses $USER
1248  * @param int $courseid The id of the course being tested
1249  * @return boolean
1250  */
1251 function ismoving($courseid) {
1252     global $USER;
1254     if (!empty($USER->activitycopy)) {
1255         return ($USER->activitycopycourse == $courseid);
1256     }
1257     return false;
1260 /**
1261  * Given an object containing firstname and lastname
1262  * values, this function returns a string with the
1263  * full name of the person.
1264  * The result may depend on system settings
1265  * or language.  'override' will force both names
1266  * to be used even if system settings specify one.
1267  * @uses $CFG
1268  * @uses $SESSION
1269  * @param    type description
1270  * @todo Finish documenting this function
1271  */
1272 function fullname($user, $override=false) {
1274     global $CFG, $SESSION;
1276     if (!isset($user->firstname) and !isset($user->lastname)) {
1277         return '';
1278     }
1280     if (!empty($SESSION->fullnamedisplay)) {
1281         $CFG->fullnamedisplay = $SESSION->fullnamedisplay;
1282     }
1284     if ($CFG->fullnamedisplay == 'firstname lastname') {
1285         return $user->firstname .' '. $user->lastname;
1287     } else if ($CFG->fullnamedisplay == 'lastname firstname') {
1288         return $user->lastname .' '. $user->firstname;
1290     } else if ($CFG->fullnamedisplay == 'firstname') {
1291         if ($override) {
1292             return get_string('fullnamedisplay', '', $user);
1293         } else {
1294             return $user->firstname;
1295         }
1296     }
1298     return get_string('fullnamedisplay', '', $user);
1301 /**
1302  * Sets a moodle cookie with an encrypted string
1303  *
1304  * @uses $CFG
1305  * @uses DAYSECS
1306  * @uses HOURSECS
1307  * @param string $thing The string to encrypt and place in a cookie
1308  */
1309 function set_moodle_cookie($thing) {
1310     global $CFG;
1312     $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
1314     $days = 60;
1315     $seconds = DAYSECS*$days;
1317     setCookie($cookiename, '', time() - HOURSECS, '/');
1318     setCookie($cookiename, rc4encrypt($thing), time()+$seconds, '/');
1321 /**
1322  * Gets a moodle cookie with an encrypted string
1323  *
1324  * @uses $CFG
1325  * @return string
1326  */
1327 function get_moodle_cookie() {
1328     global $CFG;
1330     $cookiename = 'MOODLEID_'.$CFG->sessioncookie;
1332     if (empty($_COOKIE[$cookiename])) {
1333         return '';
1334     } else {
1335         return rc4decrypt($_COOKIE[$cookiename]);
1336     }
1339 /**
1340  * Returns true if an internal authentication method is being used.
1341  * if method not specified then, global default is assumed
1342  *
1343  * @uses $CFG
1344  * @param string $auth Form of authentication required
1345  * @return boolean
1346  * @todo Outline auth types and provide code example
1347  */
1348 function is_internal_auth($auth='') {
1349 /// Returns true if an internal authentication method is being used.
1350 /// If auth not specified then global default is assumed
1352     global $CFG;
1354     if (empty($auth)) {
1355         $auth = $CFG->auth;
1356     }
1358     return ($auth == "email" || $auth == "none" || $auth == "manual");
1361 /**
1362  * Returns an array of user fields
1363  *
1364  * @uses $CFG
1365  * @uses $db
1366  * @return array User field/column names
1367  * @todo Finish documenting this function
1368  */
1369 function get_user_fieldnames() {
1371     global $CFG, $db;
1373     $fieldarray = $db->MetaColumnNames($CFG->prefix.'user');
1374     unset($fieldarray['ID']);
1376     return $fieldarray;
1379 /**
1380  * Creates a bare-bones user record
1381  *
1382  * @uses $CFG
1383  * @param string $username New user's username to add to record
1384  * @param string $password New user's password to add to record
1385  * @param string $auth Form of authentication required
1386  * @return user A {@link $USER} object
1387  * @todo Outline auth types and provide code example
1388  */
1389 function create_user_record($username, $password, $auth='') {
1390     global $CFG;
1392     //just in case check text case
1393     $username = trim(moodle_strtolower($username));
1395     if (function_exists('auth_get_userinfo')) {
1396         if ($newinfo = auth_get_userinfo($username)) {
1397             $newinfo = truncate_userinfo($newinfo);
1398             foreach ($newinfo as $key => $value){
1399                 $newuser->$key = addslashes(stripslashes($value)); // Just in case
1400             }
1401         }
1402     }
1404     if (!empty($newuser->email)) {
1405         if (email_is_not_allowed($newuser->email)) {
1406             unset($newuser->email);
1407         }
1408     }
1410     $newuser->auth = (empty($auth)) ? $CFG->auth : $auth;
1411     $newuser->username = $username;
1412     $newuser->password = md5($password);
1413     $newuser->lang = $CFG->lang;
1414     $newuser->confirmed = 1;
1415     $newuser->lastIP = getremoteaddr();
1416     $newuser->timemodified = time();
1418     if (insert_record('user', $newuser)) {
1419          $user = get_user_info_from_db('username', $newuser->username);
1420          if($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'}){
1421              set_user_preference('auth_forcepasswordchange', 1, $user);
1422          }
1423          return $user;
1424     }
1425     return false;
1428 /**
1429  * Will update a local user record from an external source
1430  *
1431  * @uses $CFG
1432  * @param string $username New user's username to add to record
1433  * @return user A {@link $USER} object
1434  */
1435 function update_user_record($username) {
1436     global $CFG;
1438     if (function_exists('auth_get_userinfo')) {
1439         $username = trim(moodle_strtolower($username)); /// just in case check text case
1441         if ($newinfo = auth_get_userinfo($username)) {
1442             foreach ($newinfo as $key => $value){
1443                 if (!empty($CFG->{'auth_user_' . $key. '_updatelocal'})) {
1444                     $value = addslashes(stripslashes($value));   // Just in case
1445                     set_field('user', $key, $value, 'username', $username);
1446                 }
1447             }
1448         }
1449     }
1450     return get_user_info_from_db('username', $username);
1453 function truncate_userinfo($info) {
1454 /// will truncate userinfo as it comes from auth_get_userinfo (from external auth)
1455 /// which may have large fields
1457     // define the limits
1458     $limit = array(
1459                     'username'    => 100,
1460                     'idnumber'    =>  12,
1461                     'firstname'   =>  20,
1462                     'lastname'    =>  20,
1463                     'email'       => 100,
1464                     'icq'         =>  15,
1465                     'phone1'      =>  20,
1466                     'phone2'      =>  20,
1467                     'institution' =>  40,
1468                     'department'  =>  30,
1469                     'address'     =>  70,
1470                     'city'        =>  20,
1471                     'country'     =>   2,
1472                     'url'         => 255,
1473                     );
1475     // apply where needed
1476     foreach (array_keys($info) as $key) {
1477         if (!empty($limit[$key])) {
1478             $info[$key] = substr($info[$key],0, $limit[$key]);
1479         }
1480     }
1482     return $info;
1485 /**
1486  * Retrieve the guest user object
1487  *
1488  * @uses $CFG
1489  * @return user A {@link $USER} object
1490  */
1491 function guest_user() {
1492     global $CFG;
1494     if ($newuser = get_record('user', 'username', 'guest')) {
1495         $newuser->loggedin = true;
1496         $newuser->confirmed = 1;
1497         $newuser->site = $CFG->wwwroot;
1498         $newuser->lang = $CFG->lang;
1499         $newuser->lastIP = getremoteaddr();
1500     }
1502     return $newuser;
1505 /**
1506  * Given a username and password, this function looks them
1507  * up using the currently selected authentication mechanism,
1508  * and if the authentication is successful, it returns a
1509  * valid $user object from the 'user' table.
1510  *
1511  * Uses auth_ functions from the currently active auth module
1512  *
1513  * @uses $CFG
1514  * @param string $username  User's username
1515  * @param string $password  User's password
1516  * @return user|flase A {@link $USER} object or false if error
1517  */
1518 function authenticate_user_login($username, $password) {
1520     global $CFG;
1522     $md5password = md5($password);
1524     // First try to find the user in the database
1526     $user = get_user_info_from_db('username', $username);
1528     // Sort out the authentication method we are using.
1530     if (empty($CFG->auth)) {
1531         $CFG->auth = 'manual';     // Default authentication module
1532     }
1534     if (empty($user->auth)) {      // For some reason it isn't set yet
1535         if (isadmin($user->id) or isguest($user->id)) {
1536             $auth = 'manual';    // Always assume these guys are internal
1537         } else {
1538             $auth = $CFG->auth;  // Normal users default to site method
1539         }
1540         // update user record from external DB
1541         if ($user->auth != 'manual' && $user->auth != 'email') {
1542             $user = update_user_record($username);
1543         }
1544     } else {
1545         $auth = $user->auth;
1546     }
1548     if (detect_munged_arguments($auth, 0)) {   // For safety on the next require
1549         return false;
1550     }
1552     if (!file_exists($CFG->dirroot .'/auth/'. $auth .'/lib.php')) {
1553         $auth = 'manual';    // Can't find auth module, default to internal
1554     }
1556     require_once($CFG->dirroot .'/auth/'. $auth .'/lib.php');
1558     if (auth_user_login($username, $password)) {  // Successful authentication
1559         if ($user) {                              // User already exists in database
1560             if (empty($user->auth)) {             // For some reason auth isn't set yet
1561                 set_field('user', 'auth', $auth, 'username', $username);
1562             }
1563             if ($md5password <> $user->password) {   // Update local copy of password for reference
1564                 set_field('user', 'password', $md5password, 'username', $username);
1565             }
1566             if (!is_internal_auth()) {            // update user record from external DB
1567                 $user = update_user_record($username);
1568             }
1569         } else {
1570             $user = create_user_record($username, $password, $auth);
1571         }
1573         if (function_exists('auth_iscreator')) {    // Check if the user is a creator
1574             $useriscreator = auth_iscreator($username);
1575             if (!is_null($useriscreator)) {
1576                 if ($useriscreator) {
1577                     if (! record_exists('user_coursecreators', 'userid', $user->id)) {
1578                         $cdata->userid = $user->id;
1579                         if (! insert_record('user_coursecreators', $cdata)) {
1580                             error('Cannot add user to course creators.');
1581                         }
1582                     }
1583                 } else {
1584                     if (record_exists('user_coursecreators', 'userid', $user->id)) {
1585                         if (! delete_records('user_coursecreators', 'userid', $user->id)) {
1586                             error('Cannot remove user from course creators.');
1587                         }
1588                     }
1589                 }
1590             }
1591         }
1592         $user->sessionIP = md5(getremoteaddr());   // Store the current IP in the session
1593         return $user;
1595     } else {
1596         add_to_log(0, 'login', 'error', $_SERVER['HTTP_REFERER'], $username);
1597         error_log('[client '.$_SERVER['REMOTE_ADDR']."]\t$CFG->wwwroot\tFailed Login:\t$username\t".$_SERVER['HTTP_USER_AGENT']);
1598         return false;
1599     }
1602 /**
1603  * Enrols (or re-enrols) a student in a given course
1604  *
1605  * @param int $courseid The id of the course that is being viewed
1606  * @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.
1607  * @param int $timestart ?
1608  * @param int $timeend ?
1609  * @return boolean
1610  * @todo Finish documenting this function
1611  */
1612 function enrol_student($userid, $courseid, $timestart=0, $timeend=0, $enrol='manual') {
1614     global $CFG;
1616     if (!$course = get_record('course', 'id', $courseid)) {  // Check course
1617         return false;
1618     }
1619     if (!$user = get_record('user', 'id', $userid)) {        // Check user
1620         return false;
1621     }
1622     if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
1623         $student->timestart = $timestart;
1624         $student->timeend = $timeend;
1625         $student->time = time();
1626         $student->enrol = $enrol;
1627         return update_record('user_students', $student);
1629     } else {
1630         require_once("$CFG->dirroot/mod/forum/lib.php");
1631         forum_add_user($userid, $courseid);
1633         $student->userid = $userid;
1634         $student->course = $courseid;
1635         $student->timestart = $timestart;
1636         $student->timeend = $timeend;
1637         $student->time = time();
1638         $student->enrol = $enrol;
1639         return insert_record('user_students', $student);
1640     }
1643 /**
1644  * Unenrols a student from a given course
1645  *
1646  * @param int $courseid The id of the course that is being viewed, if any
1647  * @param int $userid The id of the user that is being tested against.
1648  * @return boolean
1649  */
1650 function unenrol_student($userid, $courseid=0) {
1652     if ($courseid) {
1653         /// First delete any crucial stuff that might still send mail
1654         if ($forums = get_records('forum', 'course', $courseid)) {
1655             foreach ($forums as $forum) {
1656                 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
1657             }
1658         }
1659         if ($groups = get_groups($courseid, $userid)) {
1660             foreach ($groups as $group) {
1661                 delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
1662             }
1663         }
1664         return delete_records('user_students', 'userid', $userid, 'course', $courseid);
1666     } else {
1667         delete_records('forum_subscriptions', 'userid', $userid);
1668         delete_records('groups_members', 'userid', $userid);
1669         return delete_records('user_students', 'userid', $userid);
1670     }
1673 /**
1674  * Add a teacher to a given course
1675  *
1676   * @uses $USER
1677  * @param int $courseid The id of the course that is being viewed, if any
1678  * @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.
1679  * @param int $editall ?
1680  * @param string $role ?
1681  * @param int $timestart ?
1682  * @param int $timeend ?
1683  * @return boolean
1684  * @todo Finish documenting this function
1685  */
1686 function add_teacher($userid, $courseid, $editall=1, $role='', $timestart=0, $timeend=0, $enrol='manual') {
1687     global $CFG;
1689     if ($teacher = get_record('user_teachers', 'userid', $userid, 'course', $courseid)) {
1690         $newteacher = NULL;
1691         $newteacher->id = $teacher->id;
1692         $newteacher->editall = $editall;
1693         $newteacher->enrol = $enrol;
1694         if ($role) {
1695             $newteacher->role = $role;
1696         }
1697         if ($timestart) {
1698             $newteacher->timestart = $timestart;
1699         }
1700         if ($timeend) {
1701             $newteacher->timeend = $timeend;
1702         }
1703         return update_record('user_teachers', $newteacher);
1704     }
1706     if (!record_exists('user', 'id', $userid)) {
1707         return false;   // no such user
1708     }
1710     if (!record_exists('course', 'id', $courseid)) {
1711         return false;   // no such course
1712     }
1714     $teacher = NULL;
1715     $teacher->userid  = $userid;
1716     $teacher->course  = $courseid;
1717     $teacher->editall = $editall;
1718     $teacher->role    = $role;
1719     $teacher->timemodified = time();
1720     $newteacher->timestart = $timestart;
1721     $newteacher->timeend = $timeend;
1722     if ($student = get_record('user_students', 'userid', $userid, 'course', $courseid)) {
1723         $teacher->timestart = $student->timestart;
1724         $teacher->timeend = $student->timeend;
1725         $teacher->timeaccess = $student->timeaccess;
1726     }
1728     if (record_exists('user_teachers', 'course', $courseid)) {
1729         $teacher->authority = 2;
1730     } else {
1731         $teacher->authority = 1;
1732     }
1733     delete_records('user_students', 'userid', $userid, 'course', $courseid); // Unenrol as student
1735     /// Add forum subscriptions for new users
1736     require_once('../mod/forum/lib.php');
1737     forum_add_user($userid, $courseid);
1739     return insert_record('user_teachers', $teacher);
1743 /**
1744  * Removes a teacher from a given course (or ALL courses)
1745  * Does not delete the user account
1746  *
1747  * @param int $courseid The id of the course that is being viewed, if any
1748  * @param int $userid The id of the user that is being tested against.
1749  * @return boolean
1750  */
1751 function remove_teacher($userid, $courseid=0) {
1752     if ($courseid) {
1753         /// First delete any crucial stuff that might still send mail
1754         if ($forums = get_records('forum', 'course', $courseid)) {
1755             foreach ($forums as $forum) {
1756                 delete_records('forum_subscriptions', 'forum', $forum->id, 'userid', $userid);
1757             }
1758         }
1760         /// Next if the teacher is not registered as a student, but is
1761         /// a member of a group, remove them from the group.
1762         if (!isstudent($courseid, $userid)) {
1763             if ($groups = get_groups($courseid, $userid)) {
1764                 foreach ($groups as $group) {
1765                     delete_records('groups_members', 'groupid', $group->id, 'userid', $userid);
1766                 }
1767             }
1768         }
1770         return delete_records('user_teachers', 'userid', $userid, 'course', $courseid);
1771     } else {
1772         delete_records('forum_subscriptions', 'userid', $userid);
1773         return delete_records('user_teachers', 'userid', $userid);
1774     }
1777 /**
1778  * Add a creator to the site
1779  *
1780  * @param int $userid The id of the user that is being tested against.
1781  * @return boolean
1782  */
1783 function add_creator($userid) {
1785     if (!record_exists('user_admins', 'userid', $userid)) {
1786         if (record_exists('user', 'id', $userid)) {
1787             $creator->userid = $userid;
1788             return insert_record('user_coursecreators', $creator);
1789         }
1790         return false;
1791     }
1792     return true;
1795 /**
1796  * Remove a creator from a site
1797  *
1798   * @uses $db
1799  * @param int $userid The id of the user that is being tested against.
1800  * @return boolean
1801  */
1802 function remove_creator($userid) {
1803     global $db;
1805     return delete_records('user_coursecreators', 'userid', $userid);
1808 /**
1809  * Add an admin to a site
1810  *
1811  * @uses SITEID
1812  * @param int $userid The id of the user that is being tested against.
1813  * @return boolean
1814  */
1815 function add_admin($userid) {
1817     if (!record_exists('user_admins', 'userid', $userid)) {
1818         if (record_exists('user', 'id', $userid)) {
1819             $admin->userid = $userid;
1821             // any admin is also a teacher on the site course
1822             if (!record_exists('user_teachers', 'course', SITEID, 'userid', $userid)) {
1823                 if (!add_teacher($userid, SITEID)) {
1824                     return false;
1825                 }
1826             }
1828             return insert_record('user_admins', $admin);
1829         }
1830         return false;
1831     }
1832     return true;
1835 /**
1836  * Removes an admin from a site
1837  *
1838   * @uses $db
1839   * @uses SITEID
1840  * @param int $userid The id of the user that is being tested against.
1841  * @return boolean
1842  */
1843 function remove_admin($userid) {
1844     global $db;
1846     // remove also from the list of site teachers
1847     remove_teacher($userid, SITEID);
1849     return delete_records('user_admins', 'userid', $userid);
1852 /**
1853  * Clear a course out completely, deleting all content
1854  * but don't delete the course itself
1855  *
1856  * @uses $USER
1857  * @uses $SESSION
1858  * @uses $CFG
1859  * @param int $courseid The id of the course that is being viewed
1860  * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
1861  * @return boolean
1862  */
1863 function remove_course_contents($courseid, $showfeedback=true) {
1865     global $CFG, $THEME, $USER, $SESSION;
1867     $result = true;
1869     if (! $course = get_record('course', 'id', $courseid)) {
1870         error('Course ID was incorrect (can\'t find it)');
1871     }
1873     $strdeleted = get_string('deleted');
1875     // First delete every instance of every module
1877     if ($allmods = get_records('modules') ) {
1878         foreach ($allmods as $mod) {
1879             $modname = $mod->name;
1880             $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
1881             $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
1882             $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
1883             $count=0;
1884             if (file_exists($modfile)) {
1885                 include_once($modfile);
1886                 if (function_exists($moddelete)) {
1887                     if ($instances = get_records($modname, 'course', $course->id)) {
1888                         foreach ($instances as $instance) {
1889                             if ($moddelete($instance->id)) {
1890                                 $count++;
1891                             } else {
1892                                 notify('Could not delete '. $modname .' instance '. $instance->id .' ('. $instance->name .')');
1893                                 $result = false;
1894                             }
1895                         }
1896                     }
1897                 } else {
1898                     notify('Function '. $moddelete() .'doesn\'t exist!');
1899                     $result = false;
1900                 }
1902                 if (function_exists($moddeletecourse)) {
1903                     $moddeletecourse($course);
1904                 }
1905             }
1906             if ($showfeedback) {
1907                 notify($strdeleted .' '. $count .' x '. $modname);
1908             }
1909         }
1910     } else {
1911         error('No modules are installed!');
1912     }
1914     // Delete any user stuff
1916     if (delete_records('user_students', 'course', $course->id)) {
1917         if ($showfeedback) {
1918             notify($strdeleted .' user_students');
1919         }
1920     } else {
1921         $result = false;
1922     }
1924     if (delete_records('user_teachers', 'course', $course->id)) {
1925         if ($showfeedback) {
1926             notify($strdeleted .' user_teachers');
1927         }
1928     } else {
1929         $result = false;
1930     }
1932     // Delete any groups
1934     if ($groups = get_records('groups', 'courseid', $course->id)) {
1935         foreach ($groups as $group) {
1936             if (delete_records('groups_members', 'groupid', $group->id)) {
1937                 if ($showfeedback) {
1938                     notify($strdeleted .' groups_members');
1939                 }
1940             } else {
1941                 $result = false;
1942             }
1943             if (delete_records('groups', 'id', $group->id)) {
1944                 if ($showfeedback) {
1945                     notify($strdeleted .' groups');
1946                 }
1947             } else {
1948                 $result = false;
1949             }
1950         }
1951     }
1953     // Delete events
1955     if (delete_records('event', 'courseid', $course->id)) {
1956         if ($showfeedback) {
1957             notify($strdeleted .' event');
1958         }
1959     } else {
1960         $result = false;
1961     }
1963     // Delete logs
1965     if (delete_records('log', 'course', $course->id)) {
1966         if ($showfeedback) {
1967             notify($strdeleted .' log');
1968         }
1969     } else {
1970         $result = false;
1971     }
1973     // Delete any course stuff
1975     if (delete_records('course_sections', 'course', $course->id)) {
1976         if ($showfeedback) {
1977             notify($strdeleted .' course_sections');
1978         }
1979     } else {
1980         $result = false;
1981     }
1983     if (delete_records('course_modules', 'course', $course->id)) {
1984         if ($showfeedback) {
1985             notify($strdeleted .' course_modules');
1986         }
1987     } else {
1988         $result = false;
1989     }
1991     return $result;
1995 /**
1996  * This function will empty a course of USER data as much as
1997 /// possible. It will retain the activities and the structure
1998 /// of the course.
1999  *
2000  * @uses $USER
2001  * @uses $THEME
2002  * @uses $SESSION
2003  * @uses $CFG
2004  * @param int $courseid The id of the course that is being viewed
2005  * @param boolean $showfeedback Set this to false to suppress notifications from being printed as the functions performs its steps.
2006  * @param boolean $removestudents ?
2007  * @param boolean $removeteachers ?
2008  * @param boolean $removegroups ?
2009  * @param boolean $removeevents ?
2010  * @param boolean $removelogs ?
2011  * @return boolean
2012  * @todo Finish documenting this function
2013  */
2014 function remove_course_userdata($courseid, $showfeedback=true,
2015                                 $removestudents=true, $removeteachers=false, $removegroups=true,
2016                                 $removeevents=true, $removelogs=false) {
2018     global $CFG, $THEME, $USER, $SESSION;
2020     $result = true;
2022     if (! $course = get_record('course', 'id', $courseid)) {
2023         error('Course ID was incorrect (can\'t find it)');
2024     }
2026     $strdeleted = get_string('deleted');
2028     // Look in every instance of every module for data to delete
2030     if ($allmods = get_records('modules') ) {
2031         foreach ($allmods as $mod) {
2032             $modname = $mod->name;
2033             $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
2034             $moddeleteuserdata = $modname .'_delete_userdata';   // Function to delete user data
2035             $count=0;
2036             if (file_exists($modfile)) {
2037                 @include_once($modfile);
2038                 if (function_exists($moddeleteuserdata)) {
2039                     $moddeleteuserdata($course, $showfeedback);
2040                 }
2041             }
2042         }
2043     } else {
2044         error('No modules are installed!');
2045     }
2047     // Delete other stuff
2049     if ($removestudents) {
2050         /// Delete student enrolments
2051         if (delete_records('user_students', 'course', $course->id)) {
2052             if ($showfeedback) {
2053                 notify($strdeleted .' user_students');
2054             }
2055         } else {
2056             $result = false;
2057         }
2058         /// Delete group members (but keep the groups)
2059         if ($groups = get_records('groups', 'courseid', $course->id)) {
2060             foreach ($groups as $group) {
2061                 if (delete_records('groups_members', 'groupid', $group->id)) {
2062                     if ($showfeedback) {
2063                         notify($strdeleted .' groups_members');
2064                     }
2065                 } else {
2066                     $result = false;
2067                 }
2068             }
2069         }
2070     }
2072     if ($removeteachers) {
2073         if (delete_records('user_teachers', 'course', $course->id)) {
2074             if ($showfeedback) {
2075                 notify($strdeleted .' user_teachers');
2076             }
2077         } else {
2078             $result = false;
2079         }
2080     }
2082     if ($removegroups) {
2083         if ($groups = get_records('groups', 'courseid', $course->id)) {
2084             foreach ($groups as $group) {
2085                 if (delete_records('groups', 'id', $group->id)) {
2086                     if ($showfeedback) {
2087                         notify($strdeleted .' groups');
2088                     }
2089                 } else {
2090                     $result = false;
2091                 }
2092             }
2093         }
2094     }
2096     if ($removeevents) {
2097         if (delete_records('event', 'courseid', $course->id)) {
2098             if ($showfeedback) {
2099                 notify($strdeleted .' event');
2100             }
2101         } else {
2102             $result = false;
2103         }
2104     }
2106     if ($removelogs) {
2107         if (delete_records('log', 'course', $course->id)) {
2108             if ($showfeedback) {
2109                 notify($strdeleted .' log');
2110             }
2111         } else {
2112             $result = false;
2113         }
2114     }
2116     return $result;
2122 /// GROUPS /////////////////////////////////////////////////////////
2125 /**
2126 * Returns a boolean: is the user a member of the given group?
2128 * @param    type description
2129  * @todo Finish documenting this function
2130 */
2131 function ismember($groupid, $userid=0) {
2132     global $USER;
2134     if (!$groupid) {   // No point doing further checks
2135         return false;
2136     }
2138     if (!$userid) {
2139         if (empty($USER->groupmember)) {
2140             return false;
2141         }
2142         foreach ($USER->groupmember as $courseid => $mgroupid) {
2143             if ($mgroupid == $groupid) {
2144                 return true;
2145             }
2146         }
2147         return false;
2148     }
2150     return record_exists('groups_members', 'groupid', $groupid, 'userid', $userid);
2153 /**
2154  * Add a user to a group, return true upon success or if user already a group member
2155  *
2156  * @param groupid  The group id
2157  * @param userid   The user id
2158  * @todo Finish documenting this function
2159  */
2160 function add_user_to_group ($groupid, $userid) {
2161     if (ismember($groupid, $userid)) return true;
2162     $record->groupid = $groupid;
2163     $record->userid = $userid;
2164     $record->timeadded = time(); 
2165     return (insert_record('groups_members', $record) !== false);
2169 /**
2170  * Returns the group ID of the current user in the given course
2171  *
2172  * @uses $USER
2173  * @param int $courseid The course being examined - relates to id field in 'course' table.
2174  * @todo Finish documenting this function
2175  */
2176 function mygroupid($courseid) {
2177     global $USER;
2179     if (empty($USER->groupmember[$courseid])) {
2180         return 0;
2181     } else {
2182         return $USER->groupmember[$courseid];
2183     }
2186 /**
2187  * For a given course, and possibly course module, determine
2188  * what the current default groupmode is:
2189  * NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2190  *
2191  * @param course $course A {@link $COURSE} object
2192  * @param array? $cm A course module object
2193  * @return int A group mode (NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS)
2194  */
2195 function groupmode($course, $cm=null) {
2197     if ($cm and !$course->groupmodeforce) {
2198         return $cm->groupmode;
2199     }
2200     return $course->groupmode;
2204 /**
2205  * Sets the current group in the session variable
2206  *
2207  * @uses $SESSION
2208  * @param int $courseid The course being examined - relates to id field in 'course' table.
2209  * @param int $groupid The group being examined.
2210  * @return int Current group id which was set by this function
2211  * @todo Finish documenting this function
2212  */
2213 function set_current_group($courseid, $groupid) {
2214     global $SESSION;
2216     return $SESSION->currentgroup[$courseid] = $groupid;
2220 /**
2221  * Gets the current group for the current user as an id or an object
2222  *
2223  * @uses $CFG
2224  * @uses $SESSION
2225  * @param int $courseid The course being examined - relates to id field in 'course' table.
2226  * @param boolean $full If true, the return value is a full record object. If false, just the id of the record.
2227  * @todo Finish documenting this function
2228  */
2229 function get_current_group($courseid, $full=false) {
2230     global $SESSION, $USER;
2232     if (!isset($SESSION->currentgroup[$courseid])) {
2233         if (empty($USER->groupmember[$courseid])) {
2234             return 0;
2235         } else {
2236             $SESSION->currentgroup[$courseid] = $USER->groupmember[$courseid];
2237         }
2238     }
2240     if ($full) {
2241         return get_record('groups', 'id', $SESSION->currentgroup[$courseid]);
2242     } else {
2243         return $SESSION->currentgroup[$courseid];
2244     }
2247 /**
2248  * A combination function to make it easier for modules
2249  * to set up groups.
2250  *
2251  * It will use a given "groupid" parameter and try to use
2252  * that to reset the current group for the user.
2253  *
2254  * @uses VISIBLEGROUPS
2255  * @param course $course A {@link $COURSE} object
2256  * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2257  * @param int $groupid Will try to use this optional parameter to
2258  *            reset the current group for the user
2259  * @return int|false Returns the current group id or false if error.
2260  * @todo Finish documenting this function
2261  */
2262 function get_and_set_current_group($course, $groupmode, $groupid=-1) {
2264     if (!$groupmode) {   // Groups don't even apply
2265         return false;
2266     }
2268     $currentgroupid = get_current_group($course->id);
2270     if ($groupid < 0) {  // No change was specified
2271         return $currentgroupid;
2272     }
2274     if ($groupid) {      // Try to change the current group to this groupid
2275         if ($group = get_record('groups', 'id', $groupid, 'courseid', $course->id)) { // Exists
2276             if (isteacheredit($course->id)) {          // Sets current default group
2277                 $currentgroupid = set_current_group($course->id, $group->id);
2279             } else if ($groupmode == VISIBLEGROUPS) {  // All groups are visible
2280                 $currentgroupid = $group->id;
2281             }
2282         }
2283     } else {             // When groupid = 0 it means show ALL groups
2284         if (isteacheredit($course->id)) {          // Sets current default group
2285             $currentgroupid = set_current_group($course->id, 0);
2287         } else if ($groupmode == VISIBLEGROUPS) {  // All groups are visible
2288             $currentgroupid = 0;
2289         }
2290     }
2292     return $currentgroupid;
2296 /**
2297  * A big combination function to make it easier for modules
2298  * to set up groups.
2299  *
2300  * Terminates if the current user shouldn't be looking at this group
2301  * Otherwise returns the current group if there is one
2302  * Otherwise returns false if groups aren't relevant
2303  *
2304  * @uses SEPARATEGROUPS
2305  * @uses VISIBLEGROUPS
2306  * @param course $course A {@link $COURSE} object
2307  * @param int $groupmode Either NOGROUPS, SEPARATEGROUPS or VISIBLEGROUPS
2308  * @param string $urlroot ?
2309  * @todo Finish documenting this function
2310  */
2311 function setup_and_print_groups($course, $groupmode, $urlroot) {
2313     if (isset($_GET['group'])) {
2314         $changegroup = $_GET['group'];  /// 0 or higher
2315     } else {
2316         $changegroup = -1;              /// This means no group change was specified
2317     }
2319     $currentgroup = get_and_set_current_group($course, $groupmode, $changegroup);
2321     if ($currentgroup === false) {
2322         return false;
2323     }
2325     if ($groupmode == SEPARATEGROUPS and !isteacheredit($course->id) and !$currentgroup) {
2326         print_heading(get_string('notingroup'));
2327         print_footer($course);
2328         exit;
2329     }
2331     if ($groupmode == VISIBLEGROUPS or ($groupmode and isteacheredit($course->id))) {
2332         if ($groups = get_records_menu('groups', 'courseid', $course->id, 'name ASC', 'id,name')) {
2333             echo '<div align="center">';
2334             print_group_menu($groups, $groupmode, $currentgroup, $urlroot);
2335             echo '</div>';
2336         }
2337     }
2339     return $currentgroup;
2344 /// CORRESPONDENCE  ////////////////////////////////////////////////
2346 /**
2347  * Send an email to a specified user
2348  *
2349  * @uses $CFG
2350  * @uses $_SERVER
2351  * @uses SITEID
2352  * @param user $user  A {@link $USER} object
2353  * @param user $from A {@link $USER} object
2354  * @param string $subject plain text subject line of the email
2355  * @param string $messagetext plain text version of the message
2356  * @param string $messagehtml complete html version of the message (optional)
2357  * @param string $attachment a file on the filesystem, relative to $CFG->dataroot
2358  * @param string $attachname the name of the file (extension indicates MIME)
2359  * @param boolean $usetrueaddress determines whether $from email address should
2360  *          be sent out. Will be overruled by user profile setting for maildisplay
2361  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2362  *          was blocked by user and "false" if there was another sort of error.
2363  */
2364 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true) {
2366     global $CFG, $FULLME;
2368     global $course;                // This is a bit of an ugly hack to be gotten rid of later
2369     if (!empty($course->lang)) {   // Course language is defined
2370         $CFG->courselang = $course->lang;
2371     }
2373     include_once($CFG->libdir .'/phpmailer/class.phpmailer.php');
2375     if (empty($user)) {
2376         return false;
2377     }
2379     if (!empty($user->emailstop)) {
2380         return 'emailstop';
2381     }
2383     $mail = new phpmailer;
2385     $mail->Version = 'Moodle '. $CFG->version;           // mailer version
2386     $mail->PluginDir = $CFG->libdir .'/phpmailer/';      // plugin directory (eg smtp plugin)
2389     if (current_language() != 'en') {
2390         $mail->CharSet = get_string('thischarset');
2391     }
2393     if ($CFG->smtphosts == 'qmail') {
2394         $mail->IsQmail();                              // use Qmail system
2396     } else if (empty($CFG->smtphosts)) {
2397         $mail->IsMail();                               // use PHP mail() = sendmail
2399     } else {
2400         $mail->IsSMTP();                               // use SMTP directly
2401         if ($CFG->debug > 7) {
2402             echo '<pre>' . "\n";
2403             $mail->SMTPDebug = true;
2404         }
2405         $mail->Host = $CFG->smtphosts;               // specify main and backup servers
2407         if ($CFG->smtpuser) {                          // Use SMTP authentication
2408             $mail->SMTPAuth = true;
2409             $mail->Username = $CFG->smtpuser;
2410             $mail->Password = $CFG->smtppass;
2411         }
2412     }
2414     $adminuser = get_admin();
2416     $mail->Sender   = $adminuser->email;
2418     if (is_string($from)) { // So we can pass whatever we want if there is need
2419         $mail->From     = $CFG->noreplyaddress;
2420         $mail->FromName = $from;
2421     } else if ($usetrueaddress and $from->maildisplay) {
2422         $mail->From     = $from->email;
2423         $mail->FromName = fullname($from);
2424     } else {
2425         $mail->From     = $CFG->noreplyaddress;
2426         $mail->FromName = fullname($from);
2427     }
2428     $mail->Subject  =  stripslashes($subject);
2430     $mail->AddAddress($user->email, fullname($user) );
2432     $mail->WordWrap = 79;                               // set word wrap
2434     if (!empty($from->customheaders)) {                 // Add custom headers
2435         if (is_array($from->customheaders)) {
2436             foreach ($from->customheaders as $customheader) {
2437                 $mail->AddCustomHeader($customheader);
2438             }
2439         } else {
2440             $mail->AddCustomHeader($from->customheaders);
2441         }
2442     }
2444     if ($messagehtml) {
2445         $mail->IsHTML(true);
2446         $mail->Encoding = 'quoted-printable';           // Encoding to use
2447         $mail->Body    =  $messagehtml;
2448         $mail->AltBody =  "\n$messagetext\n";
2449     } else {
2450         $mail->IsHTML(false);
2451         $mail->Body =  "\n$messagetext\n";
2452     }
2454     if ($attachment && $attachname) {
2455         if (ereg( "\\.\\." ,$attachment )) {    // Security check for ".." in dir path
2456             $mail->AddAddress($adminuser->email, fullname($adminuser) );
2457             $mail->AddStringAttachment('Error in attachment.  User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain');
2458         } else {
2459             include_once($CFG->dirroot .'/files/mimetypes.php');
2460             $mimetype = mimeinfo('type', $attachname);
2461             $mail->AddAttachment($CFG->dataroot .'/'. $attachment, $attachname, 'base64', $mimetype);
2462         }
2463     }
2465     if ($mail->Send()) {
2466         return true;
2467     } else {
2468         mtrace('ERROR: '. $mail->ErrorInfo);
2469         add_to_log(SITEID, 'library', 'mailer', $FULLME, 'ERROR: '. $mail->ErrorInfo);
2470         return false;
2471     }
2474 /**
2475  * Resets specified user's password and send the new password to the user via email.
2476  *
2477  * @uses $CFG
2478  * @param user $user A {@link $USER} object
2479  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2480  *          was blocked by user and "false" if there was another sort of error.
2481  */
2482 function reset_password_and_mail($user) {
2484     global $CFG;
2486     $site  = get_site();
2487     $from = get_admin();
2489     $newpassword = generate_password();
2491     if (! set_field('user', 'password', md5($newpassword), 'id', $user->id) ) {
2492         error('Could not set user password!');
2493     }
2495     $a->firstname = $user->firstname;
2496     $a->sitename = $site->fullname;
2497     $a->username = $user->username;
2498     $a->newpassword = $newpassword;
2499     $a->link = $CFG->wwwroot .'/login/change_password.php';
2500     $a->signoff = fullname($from, true).' ('. $from->email .')';
2502     $message = get_string('newpasswordtext', '', $a);
2504     $subject  = $site->fullname .': '. get_string('changedpassword');
2506     return email_to_user($user, $from, $subject, $message);
2510 /**
2511  * Send email to specified user with confirmation text and activation link.
2512  *
2513  * @uses $CFG
2514  * @param user $user A {@link $USER} object
2515  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2516  *          was blocked by user and "false" if there was another sort of error.
2517  */
2518  function send_confirmation_email($user) {
2520     global $CFG;
2522     $site = get_site();
2523     $from = get_admin();
2525     $data->firstname = $user->firstname;
2526     $data->sitename = $site->fullname;
2527     $data->link = $CFG->wwwroot .'/login/confirm.php?p='. $user->secret .'&amp;s='. $user->username;
2528     $data->admin = fullname($from) .' ('. $from->email .')';
2530     $message = get_string('emailconfirmation', '', $data);
2531     $subject = get_string('emailconfirmationsubject', '', $site->fullname);
2533     $messagehtml = text_to_html($message, false, false, true);
2535     return email_to_user($user, $from, $subject, $message, $messagehtml);
2539 /**
2540  * send_password_change_confirmation_email.
2541  *
2542  * @uses $CFG
2543  * @param user $user A {@link $USER} object
2544  * @return boolean|string Returns "true" if mail was sent OK, "emailstop" if email
2545  *          was blocked by user and "false" if there was another sort of error.
2546  * @todo Finish documenting this function
2547  */
2548 function send_password_change_confirmation_email($user) {
2550     global $CFG;
2552     $site = get_site();
2553     $from = get_admin();
2555     $data->firstname = $user->firstname;
2556     $data->sitename = $site->fullname;
2557     $data->link = $CFG->wwwroot .'/login/forgot_password.php?p='. $user->secret .'&amp;s='. $user->username;
2558     $data->admin = fullname($from).' ('. $from->email .')';
2560     $message = get_string('emailpasswordconfirmation', '', $data);
2561     $subject = get_string('emailpasswordconfirmationsubject', '', $site->fullname);
2563     return email_to_user($user, $from, $subject, $message);
2567 /**
2568  * Check that an email is allowed.  It returns an error message if there
2569  * was a problem.
2570  *
2571  * @param    type description
2572  * @todo Finish documenting this function
2573  */
2574 function email_is_not_allowed($email) {
2576     global $CFG;
2578     if (!empty($CFG->allowemailaddresses)) {
2579         $allowed = explode(' ', $CFG->allowemailaddresses);
2580         foreach ($allowed as $allowedpattern) {
2581             $allowedpattern = trim($allowedpattern);
2582             if (!$allowedpattern) {
2583                 continue;
2584             }
2585             if (strpos($email, $allowedpattern) !== false) {  // Match!
2586                 return false;
2587             }
2588         }
2589         return get_string('emailonlyallowed', '', $CFG->allowemailaddresses);
2591     } else if (!empty($CFG->denyemailaddresses)) {
2592         $denied = explode(' ', $CFG->denyemailaddresses);
2593         foreach ($denied as $deniedpattern) {
2594             $deniedpattern = trim($deniedpattern);
2595             if (!$deniedpattern) {
2596                 continue;
2597             }
2598             if (strpos($email, $deniedpattern) !== false) {   // Match!
2599                 return get_string('emailnotallowed', '', $CFG->denyemailaddresses);
2600             }
2601         }
2602     }
2604     return false;
2608 /// FILE HANDLING  /////////////////////////////////////////////
2610 /**
2611  * Create a directory.
2612  *
2613  * @uses $CFG
2614  * @param string $directory  a string of directory names under $CFG->dataroot eg  stuff/assignment/1
2615  * param boolean $shownotices If true then notification messages will be printed out on error.
2616  * @return string|false Returns full path to directory if successful, false if not
2617  */
2618 function make_upload_directory($directory, $shownotices=true) {
2620     global $CFG;
2622     $currdir = $CFG->dataroot;
2624     umask(0000);
2626     if (!file_exists($currdir)) {
2627         if (! mkdir($currdir, $CFG->directorypermissions)) {
2628             if ($shownotices) {
2629                 notify('ERROR: You need to create the directory '. $currdir .' with web server write access');
2630             }
2631             return false;
2632         }
2633         if ($handle = fopen($currdir.'/.htaccess', 'w')) {   // For safety
2634             @fwrite($handle, "deny from all\r\n");
2635             @fclose($handle); 
2636         }
2637     }
2639     $dirarray = explode('/', $directory);
2641     foreach ($dirarray as $dir) {
2642         $currdir = $currdir .'/'. $dir;
2643         if (! file_exists($currdir)) {
2644             if (! mkdir($currdir, $CFG->directorypermissions)) {
2645                 if ($shownotices) {
2646                     notify('ERROR: Could not find or create a directory ('. $currdir .')');
2647                 }
2648                 return false;
2649             }
2650             //@chmod($currdir, $CFG->directorypermissions);  // Just in case mkdir didn't do it
2651         }
2652     }
2654     return $currdir;
2657 /**
2658  * Makes an upload directory for a particular module.
2659  *
2660  * @uses $CFG
2661  * @param int $courseid The id of the course in question - maps to id field of 'course' table.
2662  * @return string|false Returns full path to directory if successful, false if not
2663  * @todo Finish documenting this function
2664  */
2665 function make_mod_upload_directory($courseid) {
2666     global $CFG;
2668     if (! $moddata = make_upload_directory($courseid .'/'. $CFG->moddata)) {
2669         return false;
2670     }
2672     $strreadme = get_string('readme');
2674     if (file_exists($CFG->dirroot .'/lang/'. $CFG->lang .'/docs/module_files.txt')) {
2675         copy($CFG->dirroot .'/lang/'. $CFG->lang .'/docs/module_files.txt', $moddata .'/'. $strreadme .'.txt');
2676     } else {
2677         copy($CFG->dirroot .'/lang/en/docs/module_files.txt', $moddata .'/'. $strreadme .'.txt');
2678     }
2679     return $moddata;
2682 /**
2683  * Returns current name of file on disk if it exists.
2684  *
2685  * @param string $newfile File to be verified
2686  * @return string Current name of file on disk if true
2687  * @todo Finish documenting this function
2688  */
2689 function valid_uploaded_file($newfile) {
2690     if (empty($newfile)) {
2691         return '';
2692     }
2693     if (is_uploaded_file($newfile['tmp_name']) and $newfile['size'] > 0) {
2694         return $newfile['tmp_name'];
2695     } else {
2696         return '';
2697     }
2700 /**
2701  * Returns the maximum size for uploading files.
2702  *
2703  * There are seven possible upload limits:
2704  * 1. in Apache using LimitRequestBody (no way of checking or changing this)
2705  * 2. in php.ini for 'upload_max_filesize' (can not be changed inside PHP)
2706  * 3. in .htaccess for 'upload_max_filesize' (can not be changed inside PHP)
2707  * 4. in php.ini for 'post_max_size' (can not be changed inside PHP)
2708  * 5. by the Moodle admin in $CFG->maxbytes
2709  * 6. by the teacher in the current course $course->maxbytes
2710  * 7. by the teacher for the current module, eg $assignment->maxbytes
2711  *
2712  * These last two are passed to this function as arguments (in bytes).
2713  * Anything defined as 0 is ignored.
2714  * The smallest of all the non-zero numbers is returned.
2715  *
2716  * @param int $sizebytes ?
2717  * @param int $coursebytes Current course $course->maxbytes (in bytes)
2718  * @param int $modulebytes Current module ->maxbytes (in bytes)
2719  * @return int The maximum size for uploading files.
2720  * @todo Finish documenting this function
2721  */
2722 function get_max_upload_file_size($sitebytes=0, $coursebytes=0, $modulebytes=0) {
2724     if (! $filesize = ini_get('upload_max_filesize')) {
2725         $filesize = '5M';
2726     }
2727     $minimumsize = get_real_size($filesize);
2729     if ($postsize = ini_get('post_max_size')) {
2730         $postsize = get_real_size($postsize);
2731         if ($postsize < $minimumsize) {
2732             $minimumsize = $postsize;
2733         }
2734     }
2736     if ($sitebytes and $sitebytes < $minimumsize) {
2737         $minimumsize = $sitebytes;
2738     }
2740     if ($coursebytes and $coursebytes < $minimumsize) {
2741         $minimumsize = $coursebytes;
2742     }
2744     if ($modulebytes and $modulebytes < $minimumsize) {
2745         $minimumsize = $modulebytes;
2746     }
2748     return $minimumsize;
2751 /**
2752  * Related to the above function - this function returns an
2753  * array of possible sizes in an array, translated to the
2754  * local language.
2755  *
2756  * @uses SORT_NUMERIC
2757  * @param int $sizebytes ?
2758  * @param int $coursebytes Current course $course->maxbytes (in bytes)
2759  * @param int $modulebytes Current module ->maxbytes (in bytes)
2760  * @return int
2761  * @todo Finish documenting this function
2762  */
2763 function get_max_upload_sizes($sitebytes=0, $coursebytes=0, $modulebytes=0) {
2765     if (!$maxsize = get_max_upload_file_size($sitebytes, $coursebytes, $modulebytes)) {
2766         return array();
2767     }
2769     $filesize[$maxsize] = display_size($maxsize);
2771     $sizelist = array(10240, 51200, 102400, 512000, 1048576, 2097152,
2772                       5242880, 10485760, 20971520, 52428800, 104857600);
2774     foreach ($sizelist as $sizebytes) {
2775        if ($sizebytes < $maxsize) {
2776            $filesize[$sizebytes] = display_size($sizebytes);
2777        }
2778     }
2780     krsort($filesize, SORT_NUMERIC);
2782     return $filesize;
2785 /**
2786  * If there has been an error uploading a file, print the appropriate error message
2787  * Numerical constants used as constant definitions not added until PHP version 4.2.0
2788  *
2789  * $filearray is a 1-dimensional sub-array of the $_FILES array
2790  * eg $filearray = $_FILES['userfile1']
2791  * If left empty then the first element of the $_FILES array will be used
2792  *
2793  * @uses $_FILES
2794  * @param array $filearray  A 1-dimensional sub-array of the $_FILES array
2795  * @param boolean $returnerror ?
2796  * @return boolean
2797  * @todo Finish documenting this function
2798  */
2799 function print_file_upload_error($filearray = '', $returnerror = false) {
2801     if ($filearray == '' or !isset($filearray['error'])) {
2803         if (empty($_FILES)) return false;
2805         $files = $_FILES; /// so we don't mess up the _FILES array for subsequent code
2806         $filearray = array_shift($files); /// use first element of array
2807     }
2809     switch ($filearray['error']) {
2811         case 0: // UPLOAD_ERR_OK
2812             if ($filearray['size'] > 0) {
2813                 $errmessage = get_string('uploadproblem', $filearray['name']);
2814             } else {
2815                 $errmessage = get_string('uploadnofilefound'); /// probably a dud file name
2816             }
2817             break;
2819         case 1: // UPLOAD_ERR_INI_SIZE
2820             $errmessage = get_string('uploadserverlimit');
2821             break;
2823         case 2: // UPLOAD_ERR_FORM_SIZE
2824             $errmessage = get_string('uploadformlimit');
2825             break;
2827         case 3: // UPLOAD_ERR_PARTIAL
2828             $errmessage = get_string('uploadpartialfile');
2829             break;
2831         case 4: // UPLOAD_ERR_NO_FILE
2832             $errmessage = get_string('uploadnofilefound');
2833             break;
2835         default:
2836             $errmessage = get_string('uploadproblem', $filearray['name']);
2837     }
2839     if ($returnerror) {
2840         return $errmessage;
2841     } else {
2842         notify($errmessage);
2843         return true;
2844     }
2848 /**
2849  * Returns an array with all the filenames in
2850  * all subdirectories, relative to the given rootdir.
2851  * If excludefile is defined, then that file/directory is ignored
2852  * If getdirs is true, then (sub)directories are included in the output
2853  * If getfiles is true, then files are included in the output
2854  * (at least one of these must be true!)
2855  *
2856  * @param string $rootdir  ?
2857  * @param string $excludefile  If defined then the specified file/directory is ignored
2858  * @param boolean $descend  ?
2859  * @param boolean $getdirs  If true then (sub)directories are included in the output
2860  * @param boolean $getfiles  If true then files are included in the output
2861  * @return array An array with all the filenames in
2862  * all subdirectories, relative to the given rootdir
2863  * @todo Finish documenting this function. Add examples of $excludefile usage.
2864  */
2865 function get_directory_list($rootdir, $excludefile='', $descend=true, $getdirs=false, $getfiles=true) {
2867     $dirs = array();
2869     if (!$getdirs and !$getfiles) {   // Nothing to show
2870         return $dirs;
2871     }
2873     if (!is_dir($rootdir)) {          // Must be a directory
2874         return $dirs;
2875     }
2877     if (!$dir = opendir($rootdir)) {  // Can't open it for some reason
2878         return $dirs;
2879     }
2881     while (false !== ($file = readdir($dir))) {
2882         $firstchar = substr($file, 0, 1);
2883         if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
2884             continue;
2885         }
2886         $fullfile = $rootdir .'/'. $file;
2887         if (filetype($fullfile) == 'dir') {
2888             if ($getdirs) {
2889                 $dirs[] = $file;
2890             }
2891             if ($descend) {
2892                 $subdirs = get_directory_list($fullfile, $excludefile, $descend, $getdirs, $getfiles);
2893                 foreach ($subdirs as $subdir) {
2894                     $dirs[] = $file .'/'. $subdir;
2895                 }
2896             }
2897         } else if ($getfiles) {
2898             $dirs[] = $file;
2899         }
2900     }
2901     closedir($dir);
2903     asort($dirs);
2905     return $dirs;
2908 /**
2909  * Adds up all the files in a directory and works out the size.
2910  *
2911  * @param string $rootdir  ?
2912  * @param string $excludefile  ?
2913  * @return array
2914  * @todo Finish documenting this function
2915  */
2916 function get_directory_size($rootdir, $excludefile='') {
2918     $size = 0;
2920     if (!is_dir($rootdir)) {          // Must be a directory
2921         return $dirs;
2922     }
2924     if (!$dir = @opendir($rootdir)) {  // Can't open it for some reason
2925         return $dirs;
2926     }
2928     while (false !== ($file = readdir($dir))) {
2929         $firstchar = substr($file, 0, 1);
2930         if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
2931             continue;
2932         }
2933         $fullfile = $rootdir .'/'. $file;
2934         if (filetype($fullfile) == 'dir') {
2935             $size += get_directory_size($fullfile, $excludefile);
2936         } else {
2937             $size += filesize($fullfile);
2938         }
2939     }
2940     closedir($dir);
2942     return $size;
2945 /**
2946  * Converts numbers like 10M into bytes.
2947  *
2948  * @param mixed $size The size to be converted
2949  * @return mixed
2950  */
2951 function get_real_size($size=0) {
2952     if (!$size) {
2953         return 0;
2954     }
2955     $scan['MB'] = 1048576;
2956     $scan['Mb'] = 1048576;
2957     $scan['M'] = 1048576;
2958     $scan['m'] = 1048576;
2959     $scan['KB'] = 1024;
2960     $scan['Kb'] = 1024;
2961     $scan['K'] = 1024;
2962     $scan['k'] = 1024;
2964     while (list($key) = each($scan)) {
2965         if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
2966             $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
2967             break;
2968         }
2969     }
2970     return $size;
2973 /**
2974  * Converts bytes into display form
2975  *
2976  * @param string $size  ?
2977  * @return string
2978  * @staticvar string $gb Localized string for size in gigabytes
2979  * @staticvar string $mb Localized string for size in megabytes
2980  * @staticvar string $kb Localized string for size in kilobytes
2981  * @staticvar string $b Localized string for size in bytes
2982  * @todo Finish documenting this function. Verify return type.
2983  */
2984 function display_size($size) {
2986     static $gb, $mb, $kb, $b;
2988     if (empty($gb)) {
2989         $gb = get_string('sizegb');
2990         $mb = get_string('sizemb');
2991         $kb = get_string('sizekb');
2992         $b  = get_string('sizeb');
2993     }
2995     if ($size >= 1073741824) {
2996         $size = round($size / 1073741824 * 10) / 10 . $gb;
2997     } else if ($size >= 1048576) {
2998         $size = round($size / 1048576 * 10) / 10 . $mb;
2999     } else if ($size >= 1024) {
3000         $size = round($size / 1024 * 10) / 10 . $kb;
3001     } else {
3002         $size = $size .' '. $b;
3003     }
3004     return $size;
3007 /**
3008  * Cleans a given filename by removing suspicious or troublesome characters
3009  * Only these are allowed:
3010  *    alphanumeric _ - .
3011  *
3012  * @param string $string  ?
3013  * @return string
3014  * @todo Finish documenting this function
3015  */
3016 function clean_filename($string) {
3017     $string = eregi_replace("\.\.+", '', $string);
3018     $string = preg_replace('/[^\.a-zA-Z\d\_-]/','_', $string ); // only allowed chars
3019     $string = eregi_replace("_+", '_', $string);
3020     return $string;
3024 /// STRING TRANSLATION  ////////////////////////////////////////
3026 /**
3027  * Returns the code for the current language
3028  *
3029  * @uses $CFG
3030  * @param $USER
3031  * @param $SESSION
3032  * @return string
3033  */
3034 function current_language() {
3035     global $CFG, $USER, $SESSION;
3037     if (!empty($CFG->courselang)) {    // Course language can override all other settings for this page
3038         return $CFG->courselang;
3040     } else if (!empty($SESSION->lang)) {    // Session language can override other settings
3041         return $SESSION->lang;
3043     } else if (!empty($USER->lang)) {    // User language can override site language
3044         return $USER->lang;
3046     } else {
3047         return $CFG->lang;
3048     }
3051 /**
3052  * Prints out a translated string.
3053  *
3054  * Prints out a translated string using the return value from the {@link get_string()} function.
3055  *
3056  * Example usage of this function when the string is in the moodle.php file:<br>
3057  * <code>
3058  * echo '<strong>';
3059  * print_string('wordforstudent');
3060  * echo '</strong>';
3061  * </code>
3062  *
3063  * Example usage of this function when the string is not in the moodle.php file:<br>
3064  * <code>
3065  * echo '<h1>';
3066  * print_string('typecourse', 'calendar');
3067  * echo '</h1>';
3068  * </code>
3069  *
3070  * @param string $identifier The key identifier for the localized string
3071  * @param string $module The module where the key identifier is stored. If none is specified then moodle.php is used.
3072  * @param mixed $a An object, string or number that can be used
3073  * within translation strings
3074  */
3075 function print_string($identifier, $module='', $a=NULL) {
3076     echo get_string($identifier, $module, $a);
3079 /**
3080  * Returns a localized string.
3081  *
3082  * Returns the translated string specified by $identifier as
3083  * for $module.  Uses the same format files as STphp.
3084  * $a is an object, string or number that can be used
3085  * within translation strings
3086  *
3087  * eg "hello \$a->firstname \$a->lastname"
3088  * or "hello \$a"
3089  *
3090  * If you would like to directly echo the localized string use
3091  * the function {@link print_string()}
3092  *
3093  * Example usage of this function involves finding the string you would
3094  * like a local equivalent of and using its identifier and module information
3095  * to retrive it.<br>
3096  * If you open moodle/lang/en/moodle.php and look near line 1031
3097  * you will find a string to prompt a user for their word for student
3098  * <code>
3099  * $string['wordforstudent'] = 'Your word for Student';
3100  * </code>
3101  * So if you want to display the string 'Your word for student'
3102  * in any language that supports it on your site
3103  * you just need to use the identifier 'wordforstudent'
3104  * <code>
3105  * $mystring = '<strong>'. get_string('wordforstudent') .'</strong>';
3106 or
3107  * </code>
3108  * If the string you want is in another file you'd take a slightly
3109  * different approach. Looking in moodle/lang/en/calendar.php you find
3110  * around line 75:
3111  * <code>
3112  * $string['typecourse'] = 'Course event';
3113  * </code>
3114  * If you want to display the string "Course event" in any language
3115  * supported you would use the identifier 'typecourse' and the module 'calendar'
3116  * (because it is in the file calendar.php):
3117  * <code>
3118  * $mystring = '<h1>'. get_string('typecourse', 'calendar') .'</h1>';
3119  * </code>
3120  *
3121  * As a last resort, should the identifier fail to map to a string
3122  * the returned string will be [[ $identifier ]]
3123  *
3124  * @uses $CFG
3125  * @param string $identifier The key identifier for the localized string
3126  * @param string $module The module where the key identifier is stored. If none is specified then moodle.php is used.
3127  * @param mixed $a An object, string or number that can be used
3128  * within translation strings
3129  * @return string The localized string.
3130  */
3131 function get_string($identifier, $module='', $a=NULL) {
3133     global $CFG;
3135     global $course;     /// Not a nice hack, but quick
3136     if (empty($CFG->courselang)) {
3137         if (!empty($course->lang)) {
3138             $CFG->courselang = $course->lang;
3139         }
3140     }
3142     $lang = current_language();
3144     if ($module == '') {
3145         $module = 'moodle';
3146     }
3148 /// Define the two or three major locations of language strings for this module
3150     $locations = array( $CFG->dataroot.'/lang/',  $CFG->dirroot.'/lang/' );
3151     if ($module != 'moodle') {
3152         $locations[] =  $CFG->dirroot .'/mod/'.$module.'/lang/';
3153     }
3155 /// First check all the normal locations for the string in the current language
3157     foreach ($locations as $location) {
3158         $langfile = $location.$lang.'/'.$module.'.php';
3159         if (file_exists($langfile)) {
3160             if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
3161                 eval($result);
3162                 return $resultstring;
3163             }
3164         }
3165     }
3167 /// If the preferred language was English we can abort now
3168     if ($lang == 'en') {
3169         return '[['. $identifier .']]';
3170     }
3172 /// Is a parent language defined?  If so, try to find this string in a parent language file
3174     foreach ($locations as $location) {
3175         $langfile = $location.$lang.'/moodle.php';
3176         if (file_exists($langfile)) {
3177             if ($result = get_string_from_file('parentlanguage', $langfile, "\$parentlang")) {
3178                 eval($result);
3179                 if (!empty($parentlang)) {   // found it!
3180                     $langfile = $location.$parentlang.'/'.$module.'.php';
3181                     if (file_exists($langfile)) {
3182                         if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
3183                             eval($result);
3184                             return $resultstring;
3185                         }
3186                     }
3187                 }
3188             }
3189         }
3190     }
3192 /// Our only remaining option is to try English
3194     foreach ($locations as $location) {
3195         $langfile = $location.'en/'.$module.'.php';
3197         if (file_exists($langfile)) {
3198             if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
3199                 eval($result);
3200                 return $resultstring;
3201             }
3202         }
3203     }
3205     return '[['.$identifier.']]';  // Last resort
3208 /**
3209  * This function is only used from {@link get_string()}.
3210  *
3211  * @internal Only used from get_string, not meant to be public API
3212  * @param string $identifier ?
3213  * @param string $langfile ?
3214  * @param string $destination ?
3215  * @return string|false ?
3216  * @staticvar array $strings Localized strings
3217  * @access private
3218  * @todo Finish documenting this function.
3219  */
3220 function get_string_from_file($identifier, $langfile, $destination) {
3222     static $strings;    // Keep the strings cached in memory.
3224     if (empty($strings[$langfile])) {
3225         $string = array();
3226         include ($langfile);
3227         $strings[$langfile] = $string;
3228     } else {
3229         $string = &$strings[$langfile];
3230     }
3232     if (!isset ($string[$identifier])) {
3233         return false;
3234     }
3236     return $destination .'= sprintf("'. $string[$identifier] .'");';
3239 /**
3240  * Converts an array of strings to their localized value.
3241  *
3242  * @param array $array An array of strings
3243  * @param string $module The language module that these strings can be found in.
3244  * @return string
3245  */
3246 function get_strings($array, $module='') {
3248    $string = NULL;
3249    foreach ($array as $item) {
3250        $string->$item = get_string($item, $module);
3251    }
3252    return $string;
3255 /**
3256  * Returns a list of language codes and their full names
3257  *
3258  * @uses $CFG
3259  * @return array An associative array with contents in the form of LanguageCode => LanguageName
3260  * @todo Finish documenting this function
3261  */
3262 function get_list_of_languages() {
3263     global $CFG;
3265     $languages = array();
3267     if (!empty($CFG->langlist)) {       // use admin's list of languages
3268         $langlist = explode(',', $CFG->langlist);
3269         foreach ($langlist as $lang) {
3270             if (file_exists($CFG->dirroot .'/lang/'. $lang .'/moodle.php')) {
3271                 include($CFG->dirroot .'/lang/'. $lang .'/moodle.php');
3272                 $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
3273                 unset($string);
3274             }
3275         }
3276     } else {
3277         if (!$langdirs = get_list_of_plugins('lang')) {
3278             return false;
3279         }
3280         foreach ($langdirs as $lang) {
3281             @include($CFG->dirroot .'/lang/'. $lang .'/moodle.php');
3282             $languages[$lang] = $string['thislanguage'] .' ('. $lang .')';
3283             unset($string);
3284         }
3285     }
3287     return $languages;
3290 /**
3291  * Returns a list of country names in the current language
3292  *
3293  * @uses $CFG
3294  * @uses $USER
3295  * @return string?
3296  * @todo Finish documenting this function.
3297  */
3298 function get_list_of_countries() {
3299     global $CFG, $USER;
3301     $lang = current_language();
3303     if (!file_exists($CFG->dirroot .'/lang/'. $lang .'/countries.php')) {
3304         if ($parentlang = get_string('parentlanguage')) {
3305             if (file_exists($CFG->dirroot .'/lang/'. $parentlang .'/countries.php')) {
3306                 $lang = $parentlang;
3307             } else {
3308                 $lang = 'en';  // countries.php must exist in this pack
3309             }
3310         } else {
3311             $lang = 'en';  // countries.php must exist in this pack
3312         }
3313     }
3315     include($CFG->dirroot .'/lang/'. $lang .'/countries.php');
3317     if (!empty($string)) {
3318         asort($string);
3319     }
3321     return $string;
3324 /**
3325  * Returns a list of picture names in the current language
3326  *
3327  * @uses $CFG
3328  * @return string?
3329  * @todo Finish documenting this function.
3330  */
3331 function get_list_of_pixnames() {
3332     global $CFG;
3334     $lang = current_language();
3336     if (!file_exists($CFG->dirroot .'/lang/'. $lang .'/pix.php')) {
3337         if ($parentlang = get_string('parentlanguage')) {
3338             if (file_exists($CFG->dirroot .'/lang/'. $parentlang .'/pix.php')) {
3339                 $lang = $parentlang;
3340             } else {
3341                 $lang = 'en';  // countries.php must exist in this pack
3342             }
3343         } else {
3344             $lang = 'en';  // countries.php must exist in this pack
3345         }
3346     }
3348     include_once($CFG->dirroot .'/lang/'. $lang .'/pix.php');
3350     return $string;
3353 /**
3354  * Can include a given document file (depends on second
3355  * parameter) or just return info about it.
3356  *
3357  * @uses $CFG
3358  * @param string $file ?
3359  * @param boolean $include ?
3360  * @return ?
3361  * @todo Finish documenting this function
3362  */
3363 function document_file($file, $include=true) {
3364     global $CFG;
3366     $file = clean_filename($file);
3368     if (empty($file)) {
3369         return false;
3370     }
3372     $langs = array(current_language(), get_string('parentlanguage'), 'en');
3374     foreach ($langs as $lang) {
3375         $info->filepath = $CFG->dirroot .'/lang/'. $lang .'/docs/'. $file;
3376         $info->urlpath  = $CFG->wwwroot .'/lang/'. $lang .'/docs/'. $file;
3378         if (file_exists($info->filepath)) {
3379             if ($include) {
3380                 include($info->filepath);
3381             }
3382             return $info;
3383         }
3384     }
3386     return false;
3389 /**
3390 * Function to raise the memory limit to a new value.
3391 * Will respect the memory limit if it is higher, thus allowing
3392 * settings in php.ini, apache conf or command line switches
3393 * to override it
3395 * The memory limit should be expressed with a string (eg:'64M')
3397 * Return boolean
3399 * @param    value    string with the new memory limit
3400 */
3401 function raise_memory_limit ($newlimit) {
3403     if (empty($newlimit)) {
3404         return false;
3405     }
3407     $cur = @ini_get('memory_limit');
3408     if (empty($cur)) {
3409         // if php is compiled without --enable-memory-limits
3410         // apparently memory_limit is set to ''
3411         $cur=0;
3412     } else {
3413         if ($cur == -1){
3414             return true; // unlimited mem!
3415         }
3416       $cur = get_real_size($cur);
3417     }
3419     $new = get_real_size($newlimit);
3420     if ($new > $cur) {
3421         ini_set('memory_limit', $newlimit);
3422         return true;
3423     }
3424     return false;
3427 /// ENCRYPTION  ////////////////////////////////////////////////
3429 /**
3430  * rc4encrypt
3431  *
3432  * @param string $data ?
3433  * @return string
3434  * @todo Finish documenting this function
3435  */
3436 function rc4encrypt($data) {
3437     $password = 'nfgjeingjk';
3438     return endecrypt($password, $data, '');
3441 /**
3442  * rc4decrypt
3443  *
3444  * @param string $data ?
3445  * @return string
3446  * @todo Finish documenting this function
3447  */
3448 function rc4decrypt($data) {
3449     $password = 'nfgjeingjk';
3450     return endecrypt($password, $data, 'de');
3453 /**
3454  * Based on a class by Mukul Sabharwal [mukulsabharwal @ yahoo.com]
3455  *
3456  * @param string $pwd ?
3457  * @param string $data ?
3458  * @param string $case ?
3459  * @return string
3460  * @todo Finish documenting this function
3461  */
3462 function endecrypt ($pwd, $data, $case) {
3464     if ($case == 'de') {
3465         $data = urldecode($data);
3466     }
3468     $key[] = '';
3469     $box[] = '';
3470     $temp_swap = '';
3471     $pwd_length = 0;
3473     $pwd_length = strlen($pwd);
3475     for ($i = 0; $i <= 255; $i++) {
3476         $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1));
3477         $box[$i] = $i;
3478     }
3480     $x = 0;
3482     for ($i = 0; $i <= 255; $i++) {
3483         $x = ($x + $box[$i] + $key[$i]) % 256;
3484         $temp_swap = $box[$i];
3485         $box[$i] = $box[$x];
3486         $box[$x] = $temp_swap;
3487     }
3489     $temp = '';
3490     $k = '';
3492     $cipherby = '';
3493     $cipher = '';
3495     $a = 0;
3496     $j = 0;
3498     for ($i = 0; $i < strlen($data); $i++) {
3499         $a = ($a + 1) % 256;
3500         $j = ($j + $box[$a]) % 256;
3501         $temp = $box[$a];
3502         $box[$a] = $box[$j];
3503         $box[$j] = $temp;
3504         $k = $box[(($box[$a] + $box[$j]) % 256)];
3505         $cipherby = ord(substr($data, $i, 1)) ^ $k;
3506         $cipher .= chr($cipherby);
3507     }
3509     if ($case == 'de') {
3510         $cipher = urldecode(urlencode($cipher));
3511     } else {
3512         $cipher = urlencode($cipher);
3513     }
3515     return $cipher;
3519 /// CALENDAR MANAGEMENT  ////////////////////////////////////////////////////////////////
3522 /**
3523  * Call this function to add an event to the calendar table
3524  *  and to call any calendar plugins
3525  *
3526  * @uses $CFG
3527  * @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:
3528  *  <ul>
3529  *    <li><b>$event->name</b> - Name for the event
3530  *    <li><b>$event->description</b> - Description of the event (defaults to '')
3531  *    <li><b>$event->format</b> - Format for the description (using formatting types defined at the top of weblib.php)
3532  *    <li><b>$event->courseid</b> - The id of the course this event belongs to (0 = all courses)
3533  *    <li><b>$event->groupid</b> - The id of the group this event belongs to (0 = no group)
3534  *    <li><b>$event->userid</b> - The id of the user this event belongs to (0 = no user)
3535  *    <li><b>$event->modulename</b> - Name of the module that creates this event
3536  *    <li><b>$event->instance</b> - Instance of the module that owns this event
3537  *    <li><b>$event->eventtype</b> - The type info together with the module info could
3538  *             be used by calendar plugins to decide how to display event
3539  *    <li><b>$event->timestart</b>- Timestamp for start of event
3540  *    <li><b>$event->timeduration</b> - Duration (defaults to zero)
3541  *    <li><b>$event->visible</b> - 0 if the event should be hidden (e.g. because the activity that created it is hidden)
3542  *  </ul>
3543  * @return int The id number of the resulting record
3544  * @todo Finish documenting this function
3545  */
3546  function add_event($event) {
3548     global $CFG;
3550     $event->timemodified = time();
3552     if (!$event->id = insert_record('event', $event)) {
3553         return false;
3554     }
3556     if (!empty($CFG->calendar)) { // call the add_event function of the selected calendar
3557         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3558             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3559             $calendar_add_event = $CFG->calendar.'_add_event';
3560             if (function_exists($calendar_add_event)) {
3561                 $calendar_add_event($event);
3562             }
3563         }
3564     }
3566     return $event->id;
3569 /**
3570  * Call this function to update an event in the calendar table
3571  * the event will be identified by the id field of the $event object.
3572  *
3573  * @uses $CFG
3574  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field.
3575  * @return boolean
3576  * @todo Finish documenting this function
3577  */
3578 function update_event($event) {
3580     global $CFG;
3582     $event->timemodified = time();
3584     if (!empty($CFG->calendar)) { // call the update_event function of the selected calendar
3585         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3586             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3587             $calendar_update_event = $CFG->calendar.'_update_event';
3588             if (function_exists($calendar_update_event)) {
3589                 $calendar_update_event($event);
3590             }
3591         }
3592     }
3593     return update_record('event', $event);
3596 /**
3597  * Call this function to delete the event with id $id from calendar table.
3598  *
3599   * @uses $CFG
3600  * @param int $id The id of an event from the 'calendar' table.
3601  * @return array An associative array with the results from the SQL call.
3602  * @todo Verify return type
3603  */
3604 function delete_event($id) {
3606     global $CFG;
3608     if (!empty($CFG->calendar)) { // call the delete_event function of the selected calendar
3609         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3610             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3611             $calendar_delete_event = $CFG->calendar.'_delete_event';
3612             if (function_exists($calendar_delete_event)) {
3613                 $calendar_delete_event($id);
3614             }
3615         }
3616     }
3617     return delete_records('event', 'id', $id);
3620 /**
3621  * Call this function to hide an event in the calendar table
3622  * the event will be identified by the id field of the $event object.
3623  *
3624  * @uses $CFG
3625  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field.
3626  * @return array An associative array with the results from the SQL call.
3627  * @todo Verify return type
3628  */
3629 function hide_event($event) {
3630     global $CFG;
3632     if (!empty($CFG->calendar)) { // call the update_event function of the selected calendar
3633         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3634             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3635             $calendar_hide_event = $CFG->calendar.'_hide_event';
3636             if (function_exists($calendar_hide_event)) {
3637                 $calendar_hide_event($event);
3638             }
3639         }
3640     }
3641     return set_field('event', 'visible', 0, 'id', $event->id);
3644 /**
3645  * Call this function to unhide an event in the calendar table
3646  * the event will be identified by the id field of the $event object.
3647  *
3648  * @uses $CFG
3649  * @param array $event An associative array representing an event from the calendar table. The event will be identified by the id field.
3650  * @return array An associative array with the results from the SQL call.
3651  * @todo Verify return type
3652  */
3653 function show_event($event) {
3654     global $CFG;
3656     if (!empty($CFG->calendar)) { // call the update_event function of the selected calendar
3657         if (file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
3658             include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
3659             $calendar_show_event = $CFG->calendar.'_show_event';
3660             if (function_exists($calendar_show_event)) {
3661                 $calendar_show_event($event);
3662             }
3663         }
3664     }
3665     return set_field('event', 'visible', 1, 'id', $event->id);
3669 /// ENVIRONMENT CHECKING  ////////////////////////////////////////////////////////////
3671 /**
3672  * Lists plugin directories within some directory
3673  *
3674  * @uses $CFG
3675  * @param string $plugin ?
3676  * @param string $exclude ?
3677  * @return array
3678  * @todo Finish documenting this function
3679  */
3680 function get_list_of_plugins($plugin='mod', $exclude='') {
3682     global $CFG;
3684     $basedir = opendir($CFG->dirroot .'/'. $plugin);
3685     while ($dir = readdir($basedir)) {
3686         $firstchar = substr($dir, 0, 1);
3687         if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf' or $dir == $exclude) {
3688             continue;
3689         }
3690         if (filetype($CFG->dirroot .'/'. $plugin .'/'. $dir) != 'dir') {
3691             continue;
3692         }
3693         $plugins[] = $dir;
3694     }
3695     if ($plugins) {
3696         asort($plugins);
3697     }
3698     return $plugins;
3701 /**
3702  * Returns true if the current version of PHP is greater that the specified one.
3703  *
3704  * @param string $version The version of php being tested.
3705  * @return boolean
3706  * @todo Finish documenting this function
3707  */
3708 function check_php_version($version='4.1.0') {
3709     $minversion = intval(str_replace('.', '', $version));
3710     $curversion = intval(str_replace('.', '', phpversion()));
3711     return ($curversion >= $minversion);
3715 /**
3716  * Checks to see if is a browser matches the specified
3717  * brand and is equal or better version.
3718  *
3719  * @uses $_SERVER
3720  * @param string $brand The browser identifier being tested
3721  * @param int $version The version of the browser
3722  * @return boolean
3723  * @todo Finish documenting this function
3724  */
3725  function check_browser_version($brand='MSIE', $version=5.5) {
3726     $agent = $_SERVER['HTTP_USER_AGENT'];
3728     if (empty($agent)) {
3729         return false;
3730     }
3732     switch ($brand) {
3734       case 'Gecko':   /// Gecko based browsers
3736           if (substr_count($agent, 'Camino')) {
3737               // MacOS X Camino support
3738               $version = 20041110;
3739           }
3741           // the proper string - Gecko/CCYYMMDD Vendor/Version
3742           if (ereg("^([a-zA-Z]+)/([0-9]+\.[0-9]+) \((.*)\) (.*)$", $agent, $match)) {
3743               if (ereg("^([Gecko]+)/([0-9]+)",$match[4], $reldate)) {
3744                   if ($reldate[2] > $version) {
3745                       return true;
3746                   }
3747               }
3748           }
3749           break;
3752       case 'MSIE':   /// Internet Explorer
3754           if (strpos($agent, 'Opera')) {     // Reject Opera
3755               return false;
3756           }
3757           $string = explode(';', $agent);
3758           if (!isset($string[1])) {
3759               return false;
3760           }
3761           $string = explode(' ', trim($string[1]));
3762           if (!isset($string[0]) and !isset($string[1])) {
3763               return false;
3764           }
3765           if ($string[0] == $brand and (float)$string[1] >= $version ) {
3766               return true;
3767           }
3768           break;
3770     }
3772     return false;
3775 /**
3776  * This function makes the return value of ini_get consistent if you are
3777  * setting server directives through the .htaccess file in apache.
3778  * Current behavior for value set from php.ini On = 1, Off = [blank]
3779  * Current behavior for value set from .htaccess On = On, Off = Off
3780  * Contributed by jdell @ unr.edu
3781  *
3782  * @param string $ini_get_arg ?
3783  * @return boolean
3784  * @todo Finish documenting this function
3785  */
3786 function ini_get_bool($ini_get_arg) {
3787     $temp = ini_get($ini_get_arg);
3789     if ($temp == '1' or strtolower($temp) == 'on') {
3790         return true;
3791     }
3792     return false;
3795 /**
3796  * Compatibility stub to provide backward compatibility
3797  *
3798  * Determines if the HTML editor is enabled.
3799  * @deprecated Use {@link can_use_html_editor()} instead.
3800  */
3801  function can_use_richtext_editor() {
3802     return can_use_html_editor();