Fixes to prevent teachers using loginas to enter other courses as that student
[moodle.git] / lib / moodlelib.php
1 <?PHP // $Id$
3 //
4 // moodlelib.php
5 //
6 // Large collection of useful functions used by many parts of Moodle.
7 //
8 // Martin Dougiamas, 2000
9 //
11 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
13 function print_header ($title="", $heading="", $navigation="", $focus="", $meta="", $cache=true, $button="") {
14 // $title - appears top of window
15 // $heading - appears top of page
16 // $navigation - premade navigation string
17 // $focus - indicates form element eg  inputform.password
18 // $meta - meta tags in the header
19 // $cache - should this page be cacheable?
20 // $button - code for a button in the top-right
21     global $USER, $CFG, $THEME;
23     if (file_exists("$CFG->dirroot/theme/$CFG->theme/styles.css")) {
24         $styles = "$CFG->wwwroot/theme/$CFG->theme/styles.css";
25     } else {
26         $styles = "$CFG->wwwroot/theme/standard/styles.css";
27     }
29     if ($navigation == "home") {
30         $home = true;
31         $navigation = "";
32     }
34     if (!$button and $navigation) {
35         if (isset($USER->id)) {
36             $button = "<FONT SIZE=2><A HREF=\"$CFG->wwwroot/login/logout.php\">".get_string("logout")."</A></FONT>";
37         } else {
38             $button = "<FONT SIZE=2><A HREF=\"$CFG->wwwroot/login/index.php\">".get_string("login")."</A></FONT>";
39         }
40     }
42     // Specify character set ... default is iso-8859-1 but some languages might need something else
43     // Could be optimised by carrying the charset variable around in $USER
44     if (current_language() == "en") {
45         $meta .= "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n";
46     } else {
47         $meta .= "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=".get_string("thischarset")."\">\n";
48     }
50     if ($CFG->langdir == "RTL") {
51         $direction = " DIR=\"RTL\"";
52     } else {
53         $direction = " DIR=\"LTR\"";
54     }
55  
56     if (!$cache) {   // Do everything we can to prevent clients and proxies caching
57         @header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
58         @header("Pragma: no-cache");
59         $meta .= "\n<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">";
60         $meta .= "\n<META HTTP-EQUIV=\"Expires\" CONTENT=\"0\">";
61     }
63     include ("$CFG->dirroot/theme/$CFG->theme/header.html");
64 }
66 function print_footer ($course=NULL) {
67 // Can provide a course object to make the footer contain a link to 
68 // to the course home page, otherwise the link will go to the site home
69     global $USER, $CFG, $THEME;
71     if ($course) {
72         if ($course == "home") {   // special case for site home page - please do not remove
73             $homelink  = "<P ALIGN=center><A TITLE=\"Moodle $CFG->release ($CFG->version)\" HREF=\"http://moodle.com/\">";
74             $homelink .= "<BR><IMG WIDTH=130 HEIGHT=19 SRC=\"pix/madewithmoodle2.gif\" BORDER=0></A></P>";
75         } else {
76             $homelink = "<A TARGET=_top HREF=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</A>";
77         }
78     } else {
79         $homelink = "<A TARGET=_top HREF=\"$CFG->wwwroot\">".get_string("home")."</A>";
80     }
81     if ($USER->realuser) {
82         if ($realuser = get_record("user", "id", $USER->realuser)) {
83             $realuserinfo = " [$realuser->firstname $realuser->lastname] ";
84         }
85     }
86     if ($USER->id) {
87         if ($course) {
88            if ($course == "home") {
89                $site = get_site();
90                $username = "<A HREF=\"$CFG->wwwroot/user/view.php?id=$USER->id&course=$site->id\">$USER->firstname $USER->lastname</A>";
91            } else {
92                $username = "<A HREF=\"$CFG->wwwroot/user/view.php?id=$USER->id&course=$course->id\">$USER->firstname $USER->lastname</A>";
93            }
94         } else {
95            $username = "$USER->firstname $USER->lastname";
96         }
97         $loggedinas = $realuserinfo.get_string("loggedinas", "moodle", "$username").
98                       " (<A HREF=\"$CFG->wwwroot/login/logout.php\">".get_string("logout")."</A>)";
99     } else { 
100         $loggedinas = get_string("loggedinnot", "moodle").
101                       " (<A HREF=\"$CFG->wwwroot/login/index.php\">".get_string("login")."</A>)";
102     } 
104     include ("$CFG->dirroot/theme/$CFG->theme/footer.html");
107 function print_navigation ($navigation) {
108    global $CFG;
110    if ($navigation) {
111        if (! $site = get_site()) {
112            $site->shortname = get_string("home");;
113        }
114        echo "<A TARGET=_top HREF=\"$CFG->wwwroot/\">$site->shortname</A> -> $navigation";
115    }
118 function print_heading($text, $align="CENTER", $size=3) {
119     echo "<P ALIGN=\"$align\"><FONT SIZE=\"$size\"><B>".stripslashes($text)."</B></FONT></P>";
122 function print_heading_with_help($text, $helppage, $module="moodle") {
123 // Centered heading with attached help button (same title text)
124     echo "<P ALIGN=\"CENTER\"><FONT SIZE=\"3\"><B>".stripslashes($text);
125     helpbutton($helppage, $text, $module);
126     echo "</B></FONT></P>";
128     
129 function print_continue($link) {
130     global $HTTP_REFERER;
132     if (!$link) {
133         $link = $HTTP_REFERER;
134     }
136     print_heading("<A HREF=\"$link\">".get_string("continue")."</A>");
140 function print_simple_box($message, $align="", $width="", $color="#FFFFFF", $padding=5, $border=1) {
141     print_simple_box_start($align, $width, $color, $padding, $border);
142     echo "<P>".stripslashes($message)."</P>";
143     print_simple_box_end();
146 function print_simple_box_start($align="", $width="", $color="#FFFFFF", $padding=5, $border=1) {
147     global $THEME;
149     if ($align) {
150         $tablealign = "ALIGN=\"$align\"";
151     }
152     if ($width) {
153         $tablewidth = "WIDTH=\"$width\"";
154         $innertablewidth = "WIDTH=\"100%\"";
155     }
156     echo "<TABLE $tablealign $tablewidth BORDER=0 CELLPADDING=\"$border\" CELLSPACING=0>";
157     echo "<TR><TD BGCOLOR=\"$THEME->borders\">\n";
158     echo "<TABLE $innertablewidth BORDER=0 CELLPADDING=\"$padding\" CELLSPACING=0><TR><TD BGCOLOR=\"$color\">";
161 function print_simple_box_end() {
162     echo "</TD></TR></TABLE>";
163     echo "</TD></TR></TABLE>";
166 function print_single_button($link, $options, $label="OK") {
167     echo "<FORM ACTION=\"$link\" METHOD=GET>";
168     if ($options) {
169         foreach ($options as $name => $value) {
170             echo "<INPUT TYPE=hidden NAME=\"$name\" VALUE=\"$value\">";
171         }
172     }
173     echo "<INPUT TYPE=submit VALUE=\"$label\"></FORM>";
176 function print_spacer($height=1, $width=1, $br=true) {
177     global $CFG;
178     echo "<IMG HEIGHT=\"$height\" WIDTH=\"$width\" SRC=\"$CFG->wwwroot/pix/spacer.gif\" ALT=\"\">";
179     if ($br) {
180         echo "<BR>\n";
181     }
184 function print_file_picture($path, $courseid=0, $height="", $width="", $link="") {
185 // Given the path to a picture file in a course, or a URL,
186 // this function includes the picture in the page.
187     global $CFG;
189     if ($height) {
190         $height = "HEIGHT=\"$height\"";
191     }
192     if ($width) {
193         $width = "WIDTH=\"$width\"";
194     }
195     if ($link) {
196         echo "<A HREF=\"$link\">";
197     }
198     if (substr(strtolower($path), 0, 7) == "http://") {
199         echo "<IMG BORDER=0 $height $width SRC=\"$path\">";
201     } else if ($courseid) {
202         echo "<IMG BORDER=0 $height $width SRC=\"";
203         if ($CFG->slasharguments) {        // Use this method if possible for better caching
204             echo "$CFG->wwwroot/file.php/$courseid/$path";
205         } else {
206             echo "$CFG->wwwroot/file.php?file=$courseid/$path";
207         }
208         echo "\">";
209     } else {
210         echo "Error: must pass URL or course";
211     }
212     if ($link) {
213         echo "</A>";
214     }
217 function print_user_picture($userid, $courseid, $picture, $large=false, $returnstring=false) {
218     global $CFG;
220     $output = "<A HREF=\"$CFG->wwwroot/user/view.php?id=$userid&course=$courseid\">";
221     if ($large) {
222         $file = "f1.jpg";
223         $size = 100;
224     } else {
225         $file = "f2.jpg";
226         $size = 35;
227     }
228     if ($picture) {
229         if ($CFG->slasharguments) {        // Use this method if possible for better caching
230             $output .= "<IMG SRC=\"$CFG->wwwroot/user/pix.php/$userid/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">";
231         } else {
232             $output .= "<IMG SRC=\"$CFG->wwwroot/user/pix.php?file=/$userid/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">";
233         }
234     } else {
235         $output .= "<IMG SRC=\"$CFG->wwwroot/user/default/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">";
236     }
237     $output .= "</A>";
239     if ($returnstring) {
240         return $output;
241     } else {
242         echo $output;
243     }
246 function print_table($table) {
247 // Prints a nicely formatted table.
248 // $table is an object with several properties.
249 //     $table->head      is an array of heading names.
250 //     $table->align     is an array of column alignments
251 //     $table->size      is an array of column sizes
252 //     $table->data[]    is an array of arrays containing the data.
253 //     $table->width     is an percentage of the page
254 //     $table->cellpadding    padding on each cell
255 //     $table->cellspacing    spacing between cells
257     if (isset($table->align)) {
258         foreach ($table->align as $key => $aa) {
259             if ($aa) {
260                 $align[$key] = " ALIGN=\"$aa\"";
261             } else {
262                 $align[$key] = "";
263             }
264         }
265     }
266     if (isset($table->size)) {
267         foreach ($table->size as $key => $ss) {
268             if ($ss) {
269                 $size[$key] = " WIDTH=\"$ss\"";
270             } else {
271                 $size[$key] = "";
272             }
273         }
274     }
276     if (!$table->width) {
277         $table->width = "80%";
278     }
280     if (!$table->cellpadding) {
281         $table->cellpadding = "5";
282     }
284     if (!$table->cellspacing) {
285         $table->cellspacing = "1";
286     }
288     print_simple_box_start("CENTER", "$table->width", "#FFFFFF", 0);
289     echo "<TABLE WIDTH=100% BORDER=0 valign=top align=center ";
290     echo " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\">\n";
292     if ($table->head) {
293         echo "<TR>";
294         foreach ($table->head as $key => $heading) {
295             echo "<TH VALIGN=top ".$align[$key].$size[$key].">$heading</TH>";
296         }
297         echo "</TR>\n";
298     }
300     foreach ($table->data as $row) {
301         echo "<TR VALIGN=TOP>";
302         foreach ($row as $key => $item) {
303             echo "<TD ".$align[$key].$size[$key].">$item</TD>";
304         }
305         echo "</TR>\n";
306     }
307     echo "</TABLE>\n";
308     print_simple_box_end();
310     return true;
313 function print_editing_switch($courseid) {
314     global $CFG, $USER;
316     if (isteacher($courseid)) {
317         if ($USER->editing) {
318             echo "<A HREF=\"$CFG->wwwroot/course/view.php?id=$courseid&edit=off\">Turn editing off</A>";
319         } else {
320             echo "<A HREF=\"$CFG->wwwroot/course/view.php?id=$courseid&edit=on\">Turn editing on</A>";
321         }
322     }
325 function format_float($num, $places=0) {
326     return sprintf("%.$places"."f", $num);
329 function print_textarea($richedit, $rows, $cols, $width, $height, $name, $value="") {
330     global $CFG, $THEME;
332     if ($richedit) {
333         echo "<object id=richedit style=\"BACKGROUND-COLOR: buttonface\"";
334         echo " data=\"$CFG->wwwroot/lib/rte/richedit.html\"";
335         echo " width=\"$width\" height=\"$height\" ";
336         echo " type=\"text/x-scriptlet\" VIEWASTEXT></object>\n";
337         echo "<TEXTAREA style=\"display:none\" NAME=\"$name\" ROWS=1 COLS=1>";
338         p($value);
339         echo "</TEXTAREA>\n";
340     } else {
341         echo "<TEXTAREA name=\"$name\" rows=\"$rows\" cols=\"$cols\" wrap=virtual>";
342         p($value);
343         echo "</TEXTAREA>\n";
344     }
347 function print_richedit_javascript($form, $name, $source="no") {
348     echo "<SCRIPT language=\"JavaScript\" event=\"onload\" for=\"window\">\n";
349     echo "   document.richedit.options = \"history=no;source=$source\";";
350     echo "   document.richedit.docHtml = $form.$name.innerText;";
351     echo "</SCRIPT>";
355 function update_course_icon($courseid) {
356 // Used to be an icon, but it's now a simple form button
357     global $CFG, $USER;
359     if (isteacher($courseid)) {
360         if ($USER->editing) {
361             $string = get_string("turneditingoff");
362             $edit = "off";
363         } else {
364             $string = get_string("turneditingon");
365             $edit = "on";
366         }
367         return "<FORM TARGET=_parent METHOD=GET ACTION=\"$CFG->wwwroot/course/view.php\">".
368                "<INPUT TYPE=hidden NAME=id VALUE=\"$courseid\">".
369                "<INPUT TYPE=hidden NAME=edit VALUE=\"$edit\">".
370                "<INPUT TYPE=submit VALUE=\"$string\"></FORM>";
371     }
374 function update_module_button($moduleid, $courseid, $string) {
375 // Prints the editing button on a module "view" page
376     global $CFG;
378     if (isteacher($courseid)) {
379         $string = get_string("updatethis", "", $string);
380         return "<FORM TARGET=_parent METHOD=GET ACTION=\"$CFG->wwwroot/course/mod.php\">".
381                "<INPUT TYPE=hidden NAME=update VALUE=\"$moduleid\">".
382                "<INPUT TYPE=hidden NAME=return VALUE=\"true\">".
383                "<INPUT TYPE=submit VALUE=\"$string\"></FORM>";
384     }
388 function print_date_selector($day, $month, $year, $currenttime=0) {
389 // Currenttime is a default timestamp in GMT
390 // Prints form items with the names $day, $month and $year
392     if (!$currenttime) {
393         $currenttime = time();
394     }
395     $currentdate = usergetdate($currenttime);
397     for ($i=1; $i<=31; $i++) {
398         $days[$i] = "$i";
399     }
400     for ($i=1; $i<=12; $i++) {
401         $months[$i] = date("F", mktime(0,0,0,$i,1,2000));
402     }
403     for ($i=2000; $i<=2010; $i++) {
404         $years[$i] = $i;
405     }
406     choose_from_menu($days,   $day,   $currentdate[mday], "");
407     choose_from_menu($months, $month, $currentdate[mon],  "");
408     choose_from_menu($years,  $year,  $currentdate[year], "");
411 function print_time_selector($hour, $minute, $currenttime=0) {
412 // Currenttime is a default timestamp in GMT
413 // Prints form items with the names $hour and $minute
415     if (!$currenttime) {
416         $currenttime = time();
417     }
418     $currentdate = usergetdate($currenttime);
419     for ($i=0; $i<=23; $i++) {
420         $hours[$i] = sprintf("%02d",$i);
421     }
422     for ($i=0; $i<=59; $i++) {
423         $minutes[$i] = sprintf("%02d",$i);
424     }
425     choose_from_menu($hours,   $hour,   $currentdate[hours],   "");
426     choose_from_menu($minutes, $minute, $currentdate[minutes], "");
429 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0) {
430 // Given date parts in user time, produce a GMT timestamp
432    return mktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year);
435 function format_time($totalsecs, $str=NULL) {
436 // Given an amount of time in seconds, prints it 
437 // nicely as months, days, hours etc as needed
439     $totalsecs = abs($totalsecs);
441     if (!$str) {  // Create the str structure the slow way
442         $str->day   = get_string("day");
443         $str->days  = get_string("days");
444         $str->hour  = get_string("hour");
445         $str->hours = get_string("hours");
446         $str->min   = get_string("min");
447         $str->mins  = get_string("mins");
448         $str->sec   = get_string("sec");
449         $str->secs  = get_string("secs");
450     }
452     $days      = floor($totalsecs/86400);
453     $remainder = $totalsecs - ($days*86400);
454     $hours     = floor($remainder/3600);
455     $remainder = $remainder - ($hours*3600);
456     $mins      = floor($remainder/60);
457     $secs      = $remainder - ($mins*60);
459     $ss = ($secs == 1)  ? $str->sec  : $str->secs;
460     $sm = ($mins == 1)  ? $str->min  : $str->mins;
461     $sh = ($hours == 1) ? $str->hour : $str->hours;
462     $sd = ($days == 1)  ? $str->day  : $str->days;
464     if ($days)  $odays  = "$days $sd";
465     if ($hours) $ohours = "$hours $sh";
466     if ($mins)  $omins  = "$mins $sm";
467     if ($secs)  $osecs  = "$secs $ss";
469     if ($days)  return "$odays $ohours";
470     if ($hours) return "$ohours $omins";
471     if ($mins)  return "$omins $osecs";
472     if ($secs)  return "$osecs";
473     return get_string("now");
476 function userdate($date, $format="", $timezone=99) {
477 // Returns a formatted string that represents a date in user time
478 // WARNING: note that the format is for strftime(), not date().
480     global $USER;
482     if ($format == "") {
483         $format = "%A, %e %B %Y, %I:%M %p";
484     }
485     if ($timezone == 99) {
486         if (isset($USER->timezone)) {
487             $timezone = (float)$USER->timezone;
488         }
489     }
490     if (abs($timezone) > 12) {
491         return strftime("$format", $date);
492     }
493     return gmstrftime($format, $date + (int)($timezone * 3600));
496 function usergetdate($date, $timezone=99) {
497 // Given a $date timestamp in GMT, returns an array 
498 // that represents the date in user time
500     global $USER;
502     if ($timezone == 99) {
503         $timezone = (float)$USER->timezone;
504     }
505     if (abs($timezone) > 12) {
506         return getdate($date);
507     }
508     //There is no gmgetdate so I have to fake it...
509     $date = $date + (int)($timezone * 3600);
510     $getdate["seconds"] = gmstrftime("%S", $date);
511     $getdate["minutes"] = gmstrftime("%M", $date);
512     $getdate["hours"]   = gmstrftime("%H", $date);
513     $getdate["mday"]    = gmstrftime("%d", $date);
514     $getdate["wday"]    = gmstrftime("%u", $date);
515     $getdate["mon"]     = gmstrftime("%m", $date);
516     $getdate["year"]    = gmstrftime("%Y", $date);
517     $getdate["yday"]    = gmstrftime("%j", $date);
518     $getdate["weekday"] = gmstrftime("%A", $date);
519     $getdate["month"]   = gmstrftime("%B", $date);
520     return $getdate;
523 function usertime($date, $timezone=99) {
524 // Given a GMT timestamp (seconds since epoch), offsets it by 
525 // the timezone.  eg 3pm in India is 3pm GMT - 7 * 3600 seconds
526     global $USER;
528     if ($timezone == 99) {
529         $timezone = (float)$USER->timezone;
530     }
531     if (abs($timezone) > 12) {
532         return $date;
533     }
534     return $date - (int)($timezone * 3600);
537 function usergetmidnight($date, $timezone=99) {
538 // Given a time, return the GMT timestamp of the most recent midnight
539 // for the current user.
540     global $USER;
542     if ($timezone == 99) {
543         $timezone = (float)$USER->timezone;
544     }
546     $userdate = usergetdate($date, $timezone);
548     if (abs($timezone) > 12) {
549         return mktime(0, 0, 0, $userdate["mon"], $userdate["mday"], $userdate["year"]);
550     }
552     $timemidnight = gmmktime (0, 0, 0, $userdate["mon"], $userdate["mday"], $userdate["year"]);
553     return usertime($timemidnight, $timezone); // Time of midnight of this user's day, in GMT
557 function usertimezone($timezone=99) {
558 // returns a string that prints the user's timezone
559     global $USER;
561     if ($timezone == 99) {
562         $timezone = (float)$USER->timezone;
563     }
564     if (abs($timezone) > 12) {
565         return "server time";
566     }
567     if (abs($timezone) < 0.5) {
568         return "GMT";
569     }
570     if ($timezone > 0) {
571         return "GMT+$timezone";
572     } else {
573         return "GMT$timezone";
574     }
578 function error ($message, $link="") {
579     global $CFG, $SESSION;
581     print_header(get_string("error"));
582     echo "<BR>";
583     print_simple_box($message, "center", "", "#FFBBBB");
584    
585     if (!$link) {
586         if ( !empty($SESSION->fromurl) ) {
587             $link = "$SESSION->fromurl";
588             unset($SESSION->fromurl);
589             save_session("SESSION");
590         } else {
591             $link = "$CFG->wwwroot";
592         }
593     }
594     print_continue($link);
595     print_footer();
596     die;
599 function helpbutton ($page, $title="", $module="moodle", $image=true, $text="") {
600     // $page = the keyword that defines a help page
601     // $title = the title of links, rollover tips, alt tags etc
602     // $module = which module is the page defined in
603     // $image = use a help image for the link?  (otherwise uses text)
604     // $text = if defined then this text is used in the page, and 
605     //         the $page variable is ignored.
606     global $CFG;
608     if ($module == "") {
609         $module = "moodle";
610     }
611     if ($image) {
612         $linkobject = "<IMG BORDER=0 HEIGHT=17 WIDTH=22 ALT=\"$title\" SRC=\"$CFG->wwwroot/pix/help.gif\">";
613     } else {
614         $linkobject = $title;
615     }
616     if ($text) {
617         $url = "/help.php?module=$module&text=$text";
618     } else {
619         $url = "/help.php?module=$module&file=$page.html";
620     }
621     link_to_popup_window ($url, "popup", $linkobject, 400, 500, $title);
624 function notice ($message, $link="") {
625     global $THEME, $HTTP_REFERER;
627     if (!$link) {
628         $link = $HTTP_REFERER;
629     }
631     echo "<BR>";
632     print_simple_box($message, "center", "", "$THEME->cellheading");
633     print_heading("<A HREF=\"$link\">".get_string("continue")."</A>");
634     print_footer();
635     die;
638 function notice_yesno ($message, $linkyes, $linkno) {
639     global $THEME;
641     print_simple_box_start("center", "", "$THEME->cellheading");
642     echo "<P ALIGN=CENTER><FONT SIZE=3>$message</FONT></P>";
643     echo "<P ALIGN=CENTER><FONT SIZE=3><B>";
644     echo "<A HREF=\"$linkyes\">".get_string("yes")."</A>";
645     echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
646     echo "<A HREF=\"$linkno\">".get_string("no")."</A>";
647     echo "</B></FONT></P>";
648     print_simple_box_end();
651 function redirect($url, $message="", $delay=0) {
652 // Uses META tags to redirect the user, after printing a notice
654     echo "<META HTTP-EQUIV='Refresh' CONTENT='$delay; URL=$url'>";
656     if (!empty($message)) {
657         print_header();
658         echo "<CENTER>";
659         echo "<P>$message</P>";
660         echo "<P>( <A HREF=\"$url\">".get_string("continue")."</A> )</P>";
661         echo "</CENTER>";
662     }
663     die; 
666 function notify ($message) {
667     echo "<P align=center><B><FONT COLOR=#FF0000>$message</FONT></B></P>\n";
672 /// PARAMETER HANDLING ////////////////////////////////////////////////////
674 function require_variable($var) {
675     if (! isset($var)) {
676         error("A required parameter was missing");
677     }
680 function optional_variable(&$var, $default=0) {
681     if (! isset($var)) {
682         $var = $default;
683     }
689 /// DATABASE HANDLING ////////////////////////////////////////////////
691 function execute_sql($command, $feedback=true) {
692 // Completely general
694     global $db;
695     
696     $result = $db->Execute("$command");
698     if ($result) {
699         if ($feedback) {
700             echo "<P><FONT COLOR=green><B>".get_string("success")."</B></FONT></P>";
701         }
702         return true;
703     } else {
704         if ($feedback) {
705             echo "<P><FONT COLOR=red><B>".get_string("error")."</B></FONT></P>";
706         }
707         return false;
708     }
711 function modify_database($sqlfile) {
712 // Assumes that the input text file consists of a number 
713 // of SQL statements ENDING WITH SEMICOLONS.  The semicolons
714 // MUST be the last character in a line.
715 // Lines that are blank or that start with "#" are ignored.
716 // Only tested with mysql dump files (mysqldump -p -d moodle)
719     if (file_exists($sqlfile)) {
720         $success = true;
721         $lines = file($sqlfile);
722         $command = "";
724         while ( list($i, $line) = each($lines) ) {
725             $line = chop($line);
726             $length = strlen($line);
728             if ($length  &&  substr($line, 0, 1) <> "#") { 
729                 if (substr($line, $length-1, 1) == ";") {
730                     $line = substr($line, 0, $length-1);   // strip ;
731                     $command .= $line;
732                     if (! execute_sql($command)) {
733                         $success = false;
734                     }
735                     $command = "";
736                 } else {
737                     $command .= $line;
738                 }
739             }
740         }
742     } else {
743         $success = false;
744         echo "<P>Tried to modify database, but \"$sqlfile\" doesn't exist!</P>";
745     }
747     return $success;
751 function record_exists($table, $field, $value) {
752     global $db;
754     $rs = $db->Execute("SELECT * FROM $table WHERE $field = '$value' LIMIT 1");
755     if (!$rs) return false;
757     if ( $rs->RecordCount() ) {
758         return true;
759     } else {
760         return false;
761     }
764 function record_exists_sql($sql) {
765     global $db;
767     $rs = $db->Execute($sql);
768     if (!$rs) return false;
770     if ( $rs->RecordCount() ) {
771         return true;
772     } else {
773         return false;
774     }
778 function count_records($table, $selector, $value) {
779 // Get all the records and count them
780     global $db;
782     $rs = $db->Execute("SELECT COUNT(*) FROM $table WHERE $selector = '$value'");
783     if (!$rs) return 0;
785     return $rs->fields[0];
788 function count_records_sql($sql) {
789 // Get all the records and count them
790     global $db;
792     $rs = $db->Execute("$sql");
793     if (!$rs) return 0;
795     return $rs->fields[0];
798 function get_record($table, $selector, $value) {
799 // Get a single record as an object
800     global $db;
802     $rs = $db->Execute("SELECT * FROM $table WHERE $selector = '$value'");
803     if (!$rs) return false;
805     if ( $rs->RecordCount() == 1 ) {
806         return (object)$rs->fields;
807     } else {
808         return false;
809     }
812 function get_record_sql($sql) {
813 // Get a single record as an object
814 // The sql statement is provided as a string.
816     global $db;
818     $rs = $db->Execute("$sql");
819     if (!$rs) return false;
821     if ( $rs->RecordCount() == 1 ) {
822         return (object)$rs->fields;
823     } else {
824         return false;
825     }
828 function get_records($table, $selector, $value, $sort="", $fields="*") {
829 // Get a number of records as an array of objects
830 // Can optionally be sorted eg "time ASC" or "time DESC"
831 // If "fields" is specified, only those fields are returned
832 // The "key" is the first column returned, eg usually "id"
833     global $db;
835     if ($sort) {
836         $sortorder = "ORDER BY $sort";
837     }
838     $sql = "SELECT $fields FROM $table WHERE $selector = '$value' $sortorder";
840     return get_records_sql($sql);
844 function get_records_list($table, $selector, $values, $sort="", $fields="*") {
845 // Get a number of records as an array of objects
846 // Differs from get_records() in that the values variable 
847 // can be a comma-separated list of values eg  "4,5,6,10"
848 // Can optionally be sorted eg "time ASC" or "time DESC"
849 // The "key" is the first column returned, eg usually "id"
850     global $db;
852     if ($sort) {
853         $sortorder = "ORDER BY $sort";
854     }
855     $sql = "SELECT $fields FROM $table WHERE $selector in ($values) $sortorder";
857     return get_records_sql($sql);
861 function get_records_sql($sql) {
862 // Get a number of records as an array of objects
863 // The "key" is the first column returned, eg usually "id"
864 // The sql statement is provided as a string.
866     global $db;
868     $rs = $db->Execute("$sql");
869     if (!$rs) return false;
871     if ( $rs->RecordCount() > 0 ) {
872         if ($records = $rs->GetAssoc(true)) {
873             foreach ($records as $key => $record) {
874                 $objects[$key] = (object) $record;
875             }
876             return $objects;
877         } else {
878             return false;
879         }
880     } else {
881         return false;
882     }
885 function get_records_sql_menu($sql) {
886 // Given an SQL select, this function returns an associative 
887 // array of the first two columns.  This is most useful in 
888 // combination with the choose_from_menu function to create 
889 // a form menu.
891     global $db;
893     $rs = $db->Execute("$sql");
894     if (!$rs) return false;
896     if ( $rs->RecordCount() > 0 ) {
897         while (!$rs->EOF) {
898             $menu[$rs->fields[0]] = $rs->fields[1];
899             $rs->MoveNext();
900         }
901         return $menu;
902         
903     } else {
904         return false;
905     }
908 function get_field($table, $field, $selector, $value) {
909     global $db;
911     $rs = $db->Execute("SELECT $field FROM $table WHERE $selector = '$value'");
912     if (!$rs) return false;
914     if ( $rs->RecordCount() == 1 ) {
915         return $rs->fields["$field"];
916     } else {
917         return false;
918     }
921 function set_field($table, $field, $newvalue, $selector, $value) {
922     global $db;
924     return $db->Execute("UPDATE $table SET $field = '$newvalue' WHERE $selector = '$value'");
927 function set_config($name, $value) {
928 // No need for get_config because they are usually always available in $CFG
930     if (get_field("config", "value", "name", $name)) {
931         return set_field("config", "value", $value, "name", $name);
932     } else {
933         $config->name = $name;
934         $config->value = $value;
935         return insert_record("config", $config);
936     }
939 function delete_records($table, $selector, $value) {
940 // Delete one or more records from a table
941     global $db;
943     return $db->Execute("DELETE FROM $table WHERE $selector = '$value'");
946 function insert_record($table, $dataobject) {
947 // Insert a record into a table and return the "id" field
948 // $dataobject is an object containing needed data
950     global $db;
952     // Determine all the fields needed
953     if (! $columns = $db->MetaColumns("$table")) {
954         return false;
955     }
957     $data = (array)$dataobject;
959     // Pull out data matching these fields
960     foreach ($columns as $column) {
961         if ($column->name <> "id" && isset($data[$column->name]) ) {
962             $ddd[$column->name] = $data[$column->name];
963         }
964     }
966     // Construct SQL queries
967     if (! $numddd = count($ddd)) {
968         return 0;
969     }
971     $count = 0;
972     $insert = "";
973     $select = "";
975     foreach ($ddd as $key => $value) {
976         $count++;
977         $insert .= "$key = '$value'";
978         $select .= "$key = '$value'";
979         if ($count < $numddd) {
980             $insert .= ", ";
981             $select .= " AND ";
982         }
983     }
985     if (! $rs = $db->Execute("INSERT INTO $table SET $insert")) {
986         return false;
987     } 
989     // Pull it out again to find the id.  This is the most cross-platform method.
990     if ($rs = $db->Execute("SELECT id FROM $table WHERE $select")) {
991         return $rs->fields[0];
992     } else {
993         return false;
994     }
998 function update_record($table, $dataobject) {
999 // Update a record in a table
1000 // $dataobject is an object containing needed data
1002     global $db;
1004     if (! isset($dataobject->id) ) {
1005         return false;
1006     }
1008     // Determine all the fields in the table
1009     if (!$columns = $db->MetaColumns($table)) {
1010         return false;
1011     }
1012     $data = (array)$dataobject;
1014     // Pull out data matching these fields
1015     foreach ($columns as $column) {
1016         if ($column->name <> "id" && isset($data[$column->name]) ) {
1017             $ddd[$column->name] = $data[$column->name];
1018         }
1019     }
1021     // Construct SQL queries
1022     $numddd = count($ddd);
1023     $count = 0;
1024     $update = "";
1026     foreach ($ddd as $key => $value) {
1027         $count++;
1028         $update .= "$key = '$value'";
1029         if ($count < $numddd) {
1030             $update .= ", ";
1031         }
1032     }
1034     if ($rs = $db->Execute("UPDATE $table SET $update WHERE id = '$dataobject->id'")) {
1035         return true;
1036     } else {
1037         return false;
1038     }
1042 function print_object($object) {
1043 // Mostly just for debugging
1045     $array = (array)$object;
1046     foreach ($array as $key => $item) {
1047         echo "$key -> $item <BR>";
1048     }
1052 /// USER DATABASE ////////////////////////////////////////////////
1054 function get_user_info_from_db($field, $value) {
1056     global $db;
1058     if (!$field || !$value) 
1059         return false;
1061     if (! $result = $db->Execute("SELECT * FROM user WHERE $field = '$value' AND deleted <> '1'")) {
1062         error("Could not find any active users!");
1063     }
1065     if ( $result->RecordCount() == 1 ) {
1066         $user = (object)$result->fields;
1068         $rs = $db->Execute("SELECT course FROM user_students WHERE user = '$user->id' ");
1069         while (!$rs->EOF) {
1070             $course = $rs->fields["course"];
1071             $user->student["$course"] = true;
1072             $rs->MoveNext();
1073         }
1075         $rs = $db->Execute("SELECT course FROM user_teachers WHERE user = '$user->id' ");
1076         while (!$rs->EOF) {
1077             $course = $rs->fields["course"];
1078             $user->teacher["$course"] = true;
1079             $rs->MoveNext();
1080         }
1082         $rs = $db->Execute("SELECT * FROM user_admins WHERE user = '$user->id' ");
1083         while (!$rs->EOF) {
1084             $user->admin = true;
1085             $rs->MoveNext();
1086         }
1088         if ($course = get_site()) {
1089             // Everyone is always a member of the top course
1090             $user->student["$course->id"] = true;
1091         }
1093         return $user;
1095     } else {
1096         return false;
1097     }
1100 function update_user_in_db() {
1102    global $db, $USER, $REMOTE_ADDR;
1104    if (!isset($USER->id)) 
1105        return false;
1107    $timenow = time();
1108    if ($db->Execute("UPDATE user SET lastIP='$REMOTE_ADDR', lastaccess='$timenow' WHERE id = '$USER->id' ")) {
1109        return true;
1110    } else {
1111        return false;
1112    }
1115 function require_login($courseid=0) {
1116 // This function checks that the current user is logged in, and optionally
1117 // whether they are "logged in" or allowed to be in a particular course.
1118 // If not, then it redirects them to the site login or course enrolment.
1120     global $CFG, $SESSION, $USER, $FULLME, $HTTP_REFERER, $PHPSESSID;
1121       
1122     // First check that the user is logged in to the site.
1124     if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
1125         $SESSION->wantsurl = $FULLME;
1126         $SESSION->fromurl  = $HTTP_REFERER;
1127         save_session("SESSION");
1128         $USER = NULL;
1129         save_session("USER");
1130         if ($PHPSESSID) { // Cookies not enabled.
1131             redirect("$CFG->wwwroot/login/index.php?PHPSESSID=$PHPSESSID");
1132         } else {
1133             redirect("$CFG->wwwroot/login/index.php");
1134         }
1135         die;
1136     }
1137     
1138     // Next, check if the user can be in a particular course
1139     if ($courseid) {
1140         if ($USER->student[$courseid] || $USER->teacher[$courseid] || $USER->admin) {
1141             if (isset($USER->realuser)) {   // Make sure the REAL person can also access this course
1142                 if (!isteacher($courseid, $USER->realuser)) {
1143                     print_header();
1144                     notice(get_string("studentnotallowed", "", "$USER->firstname $USER->lastname"));
1145                 }
1147             } else {  // just update their last login time
1148                 update_user_in_db();
1149             }
1150             if (!$USER->email) {            // User logged in, but has not set up profile!
1151                                             // This can occur with external authentication
1152                 redirect("$CFG->wwwroot/user/edit.php?id=$USER->id&course=$courseid");
1153                 die;
1154             }
1155             return;   // user is a member of this course.
1156         }
1157         if (! $course = get_record("course", "id", $courseid)) {
1158             error("That course doesn't exist");
1159         }
1160         if ($USER->username == "guest") {
1161             switch ($course->guest) {
1162                 case 0: // Guests not allowed
1163                     print_header();
1164                     notice(get_string("guestsnotallowed", "", $course->fullname));
1165                     break;
1166                 case 1: // Guests allowed
1167                     update_user_in_db();
1168                     return;
1169                 case 2: // Guests allowed with key (drop through)
1170                     break;
1171             }
1172         }
1174         // Currently not enrolled in the course, so see if they want to enrol
1175         $SESSION->wantsurl = $FULLME;
1176         save_session("SESSION");
1177         redirect("$CFG->wwwroot/course/enrol.php?id=$courseid");
1178         die;
1179     }
1184 function update_login_count() {
1185     global $SESSION;
1187     $max_logins = 10;
1189     if (empty($SESSION->logincount)) {
1190         $SESSION->logincount = 1;
1191     } else {
1192         $SESSION->logincount++;
1193     }
1194     save_session("SESSION");
1196     if ($SESSION->logincount > $max_logins) {
1197         unset($SESSION->wantsurl);
1198         save_session("SESSION");
1199         error("Sorry, you have exceeded the allowed number of login attempts. Restart your browser.");
1200     }
1203 function remove_admin($user) {
1204     global $db;
1206     return $db->Execute("DELETE FROM user_admins WHERE user = '$user'");
1209 function remove_teacher($user, $course=0) {
1210     global $db;
1212     if ($course) {
1213         /// First delete any crucial stuff that might still send mail
1214         if ($forums = get_records("forum", "course", $course)) {
1215             foreach ($forums as $forum) {
1216                 $db->Execute("DELETE FROM forum_subscriptions WHERE forum = '$forum->id' AND user = '$user'");
1217             }
1218         }
1219         return $db->Execute("DELETE FROM user_teachers WHERE user = '$user' AND course = '$course'");
1220     } else {
1221         delete_records("forum_subscriptions", "user", $user);
1222         return delete_records("user_teachers", "user", $user);
1223     }
1227 function enrol_student($user, $course) {
1228     global $db;
1230         $timenow = time();
1232         $rs = $db->Execute("INSERT INTO user_students (user, course, start, end, time) 
1233                         VALUES ($user, $course, 0, 0, $timenow)");
1234         if ($rs) {
1235                 return true;
1236         } else {
1237             return false;
1238         }
1241 function unenrol_student($user, $course=0) {
1242     global $db;
1244     if ($course) {
1245         /// First delete any crucial stuff that might still send mail
1246         if ($forums = get_records("forum", "course", $course)) {
1247             foreach ($forums as $forum) {
1248                 $db->Execute("DELETE FROM forum_subscriptions WHERE forum = '$forum->id' AND user = '$user'");
1249             }
1250         }
1251         return $db->Execute("DELETE FROM user_students WHERE user = '$user' AND course = '$course'");
1253     } else {
1254         delete_records("forum_subscriptions", "user", $user);
1255         return delete_records("user_students", "user", $user);
1256     }
1260 function isadmin($userid=0) {
1261     global $USER;
1263     if (!$userid) {
1264         return $USER->admin;
1265     }
1267     return record_exists_sql("SELECT * FROM user_admins WHERE user='$userid'");
1270 function isteacher($courseid, $userid=0) {
1271     global $USER;
1273     if (isadmin($userid)) {  // admins can do anything the teacher can
1274         return true;
1275     }
1277     if (!$userid) {
1278         return $USER->teacher[$courseid];
1279     }
1281     return record_exists_sql("SELECT * FROM user_teachers WHERE user='$userid' AND course='$courseid'");
1285 function isstudent($courseid, $userid=0) {
1286     global $USER;
1288     if (!$userid) {
1289         return $USER->student[$courseid];
1290     }
1292     $timenow = time();   // todo:  add time check below
1294     return record_exists_sql("SELECT * FROM user_students WHERE user='$userid' AND course='$courseid'");
1297 function isguest($userid=0) {
1298     global $USER;
1300     if (!$userid) {
1301         return ($USER->username == "guest");
1302     }
1304     return record_exists_sql("SELECT * FROM user WHERE id='$userid' AND username = 'guest' ");
1307 function isediting($courseid, $user=NULL) {
1308     global $USER;
1309     if (!$user){
1310         $user = $USER;
1311     }
1312     return ($user->editing and isteacher($courseid, $user->id));
1315 function reset_login_count() {
1316     global $SESSION;
1318     $SESSION->logincount = 0;
1319     save_session("SESSION");
1323 function set_moodle_cookie($thing) {
1325     $days = 60;
1326     $seconds = 60*60*24*$days;
1328     setCookie ('MOODLEID', "", time() - 3600, "/");
1329     setCookie ('MOODLEID', rc4encrypt($thing), time()+$seconds, "/");
1333 function get_moodle_cookie() {
1334     global $MOODLEID;
1335     return rc4decrypt($MOODLEID);
1339 function save_session($VAR) {
1340 // Copies temporary session variable to permanent sesson variable
1341 // eg $_SESSION["USER"] = $USER;
1342     global $$VAR;
1343     $_SESSION[$VAR] = $$VAR;
1347 function create_user_record($username, $password) {
1348 // Creates a bare-bones user record 
1349     global $REMOTE_ADDR, $CFG;
1351     if ($CFG->auth_update_userinfo and function_exists(auth_get_userinfo)) {
1352         if ($newinfo = auth_get_userinfo($username)) {
1353             foreach ($newinfo as $key=>$value){
1354                 $newuser->$key = $value;
1355             }
1356         }
1357     }
1359     $newuser->username = $username;
1360     $newuser->password = md5($password);
1361     $newuser->lang = $CFG->lang;
1362     $newuser->confirmed = 1;
1363     $newuser->lastIP = $REMOTE_ADDR;
1364     $newuser->timemodified = time();
1366     if (insert_record("user", $newuser)) {
1367         return get_user_info_from_db("username", $username);
1368     }
1369     return false;
1372 function authenticate_user_login($username, $password) {
1373 // Given a username and password, this function looks them 
1374 // up using the currently selected authentication mechanism,
1375 // and if the authentication is successful, it returns a 
1376 // valid $user object from the 'user' table.
1377 //
1378 // Uses auth_ functions from the currently active auth module
1380     global $CFG;
1382     if (!isset($CFG->auth)) {
1383         $CFG->auth = "email";    // Default authentication module
1384     }
1386     require_once("$CFG->dirroot/auth/$CFG->auth/lib.php");
1388     if (auth_user_login($username, $password)) {  // Successful authentication
1390         if ($user = get_user_info_from_db("username", $username)) {
1391             if (md5($password) <> $user->password) {
1392                 set_field("user", "password", md5($password), "username", $username);
1393             }
1394             return $user;
1396         } else {
1397             return create_user_record($username, $password);
1398         }
1399     }
1400     return false;
1404 function get_site () {
1405 // Returns $course object of the top-level site.
1406     if ( $course = get_record("course", "category", 0)) {
1407         return $course;
1408     } else {
1409         return false;
1410     }
1413 function get_admin () {
1414 // Returns $user object of the main admin user
1416     if ( $admins = get_records_sql("SELECT u.* FROM user u, user_admins a WHERE a.user = u.id ORDER BY u.id ASC")) {
1417         foreach ($admins as $admin) {
1418             return $admin;   // ie the first one 
1419         }
1420     } else {
1421         return false;
1422     }
1425 function get_teacher($courseid) {
1426 // Returns $user object of the main teacher for a course
1427     if ( $teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t 
1428                                       WHERE t.user = u.id AND t.course = '$courseid' 
1429                                       ORDER BY t.authority ASC")) {
1430         foreach ($teachers as $teacher) {
1431             if ($teacher->authority) {
1432                 return $teacher;   // the highest authority teacher
1433             }
1434         }
1435     } else {
1436         return false;
1437     }
1440 function get_course_students($courseid, $sort="u.lastaccess DESC") {
1441     return get_records_sql("SELECT u.* FROM user u, user_students s
1442                             WHERE s.course = '$courseid' AND s.user = u.id AND u.deleted = '0'
1443                             ORDER BY $sort");
1446 function get_course_teachers($courseid, $sort="t.authority ASC") {
1447     return get_records_sql("SELECT u.*,t.authority,t.role FROM user u, user_teachers t
1448                             WHERE t.course = '$courseid' AND t.user = u.id AND u.deleted = '0'
1449                             ORDER BY $sort");
1452 function get_course_users($courseid, $sort="u.lastaccess DESC") {
1453 // Using this method because the direct SQL just would not always work!
1455     $teachers = get_course_teachers($courseid, $sort);
1456     $students = get_course_students($courseid, $sort);
1458     if ($teachers and $students) {
1459         return array_merge($teachers, $students);
1460     } else if ($teachers) {
1461         return $teachers;
1462     } else {
1463         return $students;
1464     }
1466 //    return get_records_sql("SELECT u.* FROM user u, user_students s, user_teachers t
1467 //                            WHERE (s.course = '$courseid' AND s.user = u.id) OR 
1468 //                                  (t.course = '$courseid' AND t.user = u.id)
1469 //                            ORDER BY $sort");
1474 /// MODULE FUNCTIONS /////////////////////////////////////////////////
1476 function get_coursemodule_from_instance($modulename, $instance, $courseid) {
1477 // Given an instance of a module, finds the coursemodule description
1479     return get_record_sql("SELECT cm.*, m.name
1480                            FROM course_modules cm, modules md, $modulename m 
1481                            WHERE cm.course = '$courseid' AND 
1482                                  cm.deleted = '0' AND
1483                                  cm.instance = m.id AND 
1484                                  md.name = '$modulename' AND 
1485                                  md.id = cm.module AND
1486                                  m.id = '$instance'");
1490 function get_all_instances_in_course($modulename, $courseid, $sort="cw.section") {
1491 // Returns an array of all the active instances of a particular
1492 // module in a given course.   Returns false on any errors.
1494     return get_records_sql("SELECT m.*,cw.section,cm.id as coursemodule 
1495                             FROM course_modules cm, course_sections cw, modules md, $modulename m 
1496                             WHERE cm.course = '$courseid' AND 
1497                                   cm.instance = m.id AND 
1498                                   cm.deleted = '0' AND
1499                                   cm.section = cw.id AND 
1500                                   md.name = '$modulename' AND 
1501                                   md.id = cm.module
1502                             ORDER BY $sort");
1509 /// CORRESPONDENCE  ////////////////////////////////////////////////
1511 function email_to_user($user, $from, $subject, $messagetext, $messagehtml="", $attachment="", $attachname="") {
1512 //  user        - a user record as an object
1513 //  from        - a user record as an object
1514 //  subject     - plain text subject line of the email
1515 //  messagetext - plain text version of the message
1516 //  messagehtml - complete html version of the message (optional)
1517 //  attachment  - a file on the filesystem, relative to $CFG->dataroot
1518 //  attachname  - the name of the file (extension indicates MIME)
1520     global $CFG, $_SERVER;
1522     include_once("$CFG->libdir/phpmailer/class.phpmailer.php");
1524     if (!$user) {
1525         return false;
1526     }
1527     
1528     $mail = new phpmailer;
1530     $mail->Version = "Moodle $CFG->version";           // mailer version 
1531     $mail->PluginDir = "$CFG->libdir/phpmailer/";      // plugin directory (eg smtp plugin)
1533     if ($CFG->smtphosts) {
1534         $mail->IsSMTP();                               // use SMTP directly
1535         $mail->Host = "$CFG->smtphosts";               // specify main and backup servers
1537         if ($CFG->smtpuser) {                          // Use SMTP authentication
1538             $mail->SMTPAuth = true;
1539             $mail->Username = $CFG->smtpuser;
1540             $mail->Password = $CFG->smtppass;
1541         }
1542     } else {
1543         $mail->IsMail();                               // use PHP mail() = sendmail
1544     }
1546     $mail->From     = "$from->email";
1547     $mail->FromName = "$from->firstname $from->lastname";
1548     $mail->Subject  =  stripslashes($subject);
1550     $mail->AddAddress("$user->email", "$user->firstname $user->lastname"); 
1552     $mail->WordWrap = 70;                               // set word wrap
1554     if ($messagehtml) {
1555         $mail->IsHTML(true);
1556         $mail->Body    =  $messagehtml;
1557         $mail->AltBody =  "\n$messagetext\n";
1558     } else {
1559         $mail->IsHTML(false);
1560         $mail->Body =  "\n$messagetext\n";
1561     }
1563     if ($attachment && $attachname) {
1564         if (ereg( "\\.\\." ,$attachment )) {    // Security check for ".." in dir path
1565             $adminuser = get_admin();
1566             $mail->AddAddress("$adminuser->email", "$adminuser->firstname $adminuser->lastname");
1567             $mail->AddStringAttachment("Error in attachment.  User attempted to attach a filename with a unsafe name.", "error.txt", "8bit", "text/plain");
1568         } else {
1569             include_once("$CFG->dirroot/files/mimetypes.php");
1570             $mimetype = mimeinfo("type", $attachname);
1571             $mail->AddAttachment("$CFG->dataroot/$attachment", "$attachname", "base64", "$mimetype");
1572         }
1573     }
1575     if ($mail->Send()) {
1576         return true;
1577     } else {
1578         echo "ERROR: $mail->ErrorInfo\n";
1579         $site = get_site();
1580         add_to_log($site->id, "library", "mailer", $_SERVER["REQUEST_URI"], "ERROR: $mail->ErrorInfo");
1581         return false;
1582     }
1586 /// FILE HANDLING  /////////////////////////////////////////////
1588 function make_upload_directory($directory) {
1589 // $directory = a string of directory names under $CFG->dataroot
1590 // eg  stuff/assignment/1
1591 // Returns full directory if successful, false if not
1593     global $CFG;
1595     $currdir = $CFG->dataroot;
1596     if (!file_exists($currdir)) {
1597         if (! mkdir($currdir, 0750)) {
1598             notify("ERROR: You need to create the directory $currdir with web server write access");
1599             return false;
1600         }
1601     }
1603     $dirarray = explode("/", $directory);
1605     foreach ($dirarray as $dir) {
1606         $currdir = "$currdir/$dir";
1607         if (! file_exists($currdir)) {
1608             if (! mkdir($currdir, 0750)) {
1609                 notify("ERROR: Could not find or create a directory ($currdir)");
1610                 return false;
1611             }
1612         }
1613     }
1615     return $currdir;
1618 function make_mod_upload_directory($courseid) {
1619     global $CFG;
1621     if (! $moddata = make_upload_directory("$courseid/$CFG->moddata")) {
1622         return false;
1623     }
1625     $strreadme = get_string("readme");
1627     if (file_exists("$CFG->dirroot/lang/$CFG->lang/docs/module_files.txt")) {
1628         copy("$CFG->dirroot/lang/$CFG->lang/docs/module_files.txt", "$moddata/$strreadme.txt");
1629     } else {
1630         copy("$CFG->dirroot/lang/en/docs/module_files.txt", "$moddata/$strreadme.txt");
1631     }
1632     return $moddata;
1636 function valid_uploaded_file($newfile) {
1637 // Returns current name of file on disk if true
1638     if (is_uploaded_file($newfile['tmp_name']) and $newfile['size'] > 0) {
1639         return $newfile['tmp_name'];
1640     } else {
1641         return "";
1642     }
1645 function get_max_upload_file_size() {
1646     if (! $filesize = ini_get("upload_max_filesize")) {
1647         $filesize = "5M";
1648     }
1649     return get_real_size($filesize);
1652 function get_directory_list($rootdir, $excludefile="", $descend=true) {
1653 // Returns an array with all the filenames in 
1654 // all subdirectories, relative to the given rootdir.
1655 // If excludefile is defined, then that file/directory is ignored
1657     $dirs = array();
1658    
1659     $dir = opendir($rootdir);
1661     while ($file = readdir($dir)) {
1662         if ($file != "." and $file != ".." and $file != "CVS" and $file != $excludefile) {
1663             $fullfile = $rootdir."/".$file;
1664             if ($descend and filetype($fullfile) == "dir") {
1665                 $subdirs = get_directory_list($fullfile, $excludefile, $descend);
1666                 foreach ($subdirs as $subdir) {
1667                     $dirs[] = $file."/".$subdir;
1668                 }
1669             } else {
1670                 $dirs[] = $file;
1671             }
1672         }
1673     }
1674     closedir($dir);
1676     asort($dirs);
1678     return $dirs;
1681 function get_real_size($size=0) {
1682 // Converts numbers like 10M into bytes
1683     if (!$size) {
1684         return 0; 
1685     }
1686     $scan['MB'] = 1048576;
1687     $scan['M'] = 1048576;
1688     $scan['KB'] = 1024;
1689     $scan['K'] = 1024;
1691     while (list($key) = each($scan)) {
1692         if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
1693             $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
1694             break;
1695         }
1696     }
1697     return $size;
1700 function display_size($size) {
1701 // Converts bytes into display form
1702     if ($size >= 1073741824) {
1703         $size = round($size / 1073741824 * 10) / 10 . "Gb";
1704     } else if ($size >= 1048576) {
1705         $size = round($size / 1048576 * 10) / 10 . "Mb";
1706     } else if ($size >= 1024) {
1707         $size = round($size / 1024 * 10) / 10 . "Kb";
1708     } else { 
1709         $size = $size . "b";
1710     }
1711     return $size;
1714 function clean_filename($string) {
1715     $string = eregi_replace("\.\.", "", $string);
1716     $string = eregi_replace("[^([:alnum:]|\.)]", "_", $string);
1717     return    eregi_replace("_+", "_", $string);
1721 /// STRING TRANSLATION  ////////////////////////////////////////
1723 function print_string($identifier, $module="", $a=NULL) {
1724     echo get_string($identifier, $module, $a);
1727 function current_language() {
1728 // Returns the code for the current language
1729     global $CFG, $USER;
1731     if (isset($USER->lang)) {    // User language can override site language
1732         return $USER->lang;
1733     } else {
1734         return $CFG->lang;
1735     }
1738 function get_string($identifier, $module="", $a=NULL) {
1739 // Return the translated string specified by $identifier as 
1740 // for $module.  Uses the same format files as STphp.
1741 // $a is an object, string or number that can be used
1742 // within translation strings
1743 //
1744 // eg "hello \$a->firstname \$a->lastname"
1745 // or "hello \$a"
1747     global $CFG;
1749     $lang = current_language();
1751     if ($module == "") {
1752         $module = "moodle";
1753     }
1755     $langpath = "$CFG->dirroot/lang";
1756     $langfile = "$langpath/$lang/$module.php";
1758     if (!file_exists($langfile)) {                // try English instead
1759         $langfile = "$langpath/en/$module.php";
1760         if (!file_exists($langfile)) {
1761             return "ERROR: No lang file ($langpath/en/$module.php)!";
1762         }
1763     }
1765     if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
1767         eval($result);
1768         return $resultstring;
1770     } else {
1771         if ($lang == "en") {
1772             return "[['$identifier']]";
1774         } else {   // Try looking in the english file.
1775             $langfile = "$langpath/en/$module.php";
1776             if (!file_exists($langfile)) {
1777                 return "ERROR: No lang file ($langpath/en/$module.php)!";
1778             }
1779             if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
1780                 eval($result);
1781                 return $resultstring;
1782             } else {
1783                 return "[['$identifier']]";
1784             }
1785         }
1786     }
1790 function get_string_from_file($identifier, $langfile, $destination) {
1791 // This function is only used from get_string().
1792     include ($langfile);
1794     if (!isset ($string[$identifier])) {
1795         return false;
1796     }
1798     return "$destination = sprintf(\"".$string[$identifier]."\");";
1802 function get_list_of_languages() {
1803 /// Returns a list of language codes and their full names
1804     global $CFG;
1806     if (!$langdirs = get_list_of_plugins("lang")) {
1807         return false;
1808     }
1810     foreach ($langdirs as $lang) {
1811         include("$CFG->dirroot/lang/$lang/moodle.php");
1812         $languages[$lang] = $string["thislanguage"]." ($lang)";
1813         unset($string);
1814     }
1815     return $languages;
1819 /// ENCRYPTION  ////////////////////////////////////////////////
1821 function rc4encrypt($data) {
1822     $password = "nfgjeingjk";
1823     return endecrypt($password, $data, "");
1826 function rc4decrypt($data) {
1827     $password = "nfgjeingjk";
1828     return endecrypt($password, $data, "de");
1831 function endecrypt ($pwd, $data, $case) {
1832 // Based on a class by Mukul Sabharwal [mukulsabharwal@yahoo.com]
1834     if ($case == 'de') {
1835         $data = urldecode($data);
1836     }
1838     $key[] = "";
1839     $box[] = "";
1840     $temp_swap = "";
1841     $pwd_length = 0;
1843     $pwd_length = strlen($pwd);
1845     for ($i = 0; $i <= 255; $i++) {
1846         $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1));
1847         $box[$i] = $i;
1848     }
1850     $x = 0;
1852     for ($i = 0; $i <= 255; $i++) {
1853         $x = ($x + $box[$i] + $key[$i]) % 256;
1854         $temp_swap = $box[$i];
1855         $box[$i] = $box[$x];
1856         $box[$x] = $temp_swap;
1857     }
1859     $temp = "";
1860     $k = "";
1862     $cipherby = "";
1863     $cipher = "";
1865     $a = 0;
1866     $j = 0;
1868     for ($i = 0; $i < strlen($data); $i++) {
1869         $a = ($a + 1) % 256;
1870         $j = ($j + $box[$a]) % 256;
1871         $temp = $box[$a];
1872         $box[$a] = $box[$j];
1873         $box[$j] = $temp;
1874         $k = $box[(($box[$a] + $box[$j]) % 256)];
1875         $cipherby = ord(substr($data, $i, 1)) ^ $k;
1876         $cipher .= chr($cipherby);
1877     }
1879     if ($case == 'de') {
1880         $cipher = urldecode(urlencode($cipher));
1881     } else {
1882         $cipher = urlencode($cipher);
1883     }
1885     return $cipher;
1889 /// MISCELLANEOUS ////////////////////////////////////////////////////////////////////
1891 function count_words($string) {
1892     $string = strip_tags($string);
1893     return count(preg_split("/\w\b/", $string)) - 1;
1896 function getweek ($startdate, $thedate) {
1897 // Given dates in seconds, how many weeks is the date from startdate
1898 // The first week is 1, the second 2 etc ... 
1899     
1900     if ($thedate < $startdate) {   // error
1901         return 0;  
1902     }
1904     return floor(($thedate - $startdate) / 604800.0) + 1;
1907 function add_to_log($course, $module, $action, $url="", $info="") {
1908 // Add an entry to the log table.  These are "action" focussed rather
1909 // than web server hits, and provide a way to easily reconstruct what 
1910 // any particular student has been doing.
1911 //
1912 // course = the course id
1913 // module = forum, journal, resource, course, user etc
1914 // action = view, edit, post (often but not always the same as the file.php)
1915 // url    = the file and parameters used to see the results of the action
1916 // info   = additional description information 
1919     global $db, $USER, $REMOTE_ADDR;
1921     if (isset($USER->realuser)) {  // Don't log
1922         return;
1923     }
1925     $timenow = time();
1926     $info = addslashes($info);
1928     $result = $db->Execute("INSERT INTO log
1929                             SET time = '$timenow', 
1930                                 user = '$USER->id',
1931                                 course = '$course',
1932                                 ip = '$REMOTE_ADDR', 
1933                                 module = '$module',
1934                                 action = '$action',
1935                                 url = '$url',
1936                                 info = '$info'");
1937     if (!$result) {
1938         echo "<P>Error: Could not insert a new entry to the Moodle log</P>";  // Don't throw an error
1939     }    
1942 function generate_password($maxlen=10) {
1943 // returns a randomly generated password of length $maxlen.  inspired by
1944 // http://www.phpbuilder.com/columns/jesus19990502.php3 
1946     global $CFG;
1948     $fillers = "1234567890!$-+";
1949     $wordlist = file($CFG->wordlist);
1951     srand((double) microtime() * 1000000);
1952     $word1 = trim($wordlist[rand(0, count($wordlist) - 1)]);
1953     $word2 = trim($wordlist[rand(0, count($wordlist) - 1)]);
1954     $filler1 = $fillers[rand(0, strlen($fillers) - 1)];
1956     return substr($word1 . $filler1 . $word2, 0, $maxlen);
1959 function moodle_needs_upgrading() {
1960 // Checks version numbers of Main code and all modules to see
1961 // if there are any mismatches ... returns true or false
1962     global $CFG;
1964     include_once("$CFG->dirroot/version.php");  # defines $version and upgrades
1965     if ($CFG->version) { 
1966         if ($version > $CFG->version) {
1967             return true;
1968         }
1969         if ($mods = get_list_of_plugins("mod")) {
1970             foreach ($mods as $mod) {
1971                 $fullmod = "$CFG->dirroot/mod/$mod";
1972                 unset($module);
1973                 include_once("$fullmod/version.php");  # defines $module with version etc
1974                 if ($currmodule = get_record("modules", "name", $mod)) {
1975                     if ($module->version > $currmodule->version) {
1976                         return true;
1977                     }
1978                 }
1979             }
1980         }
1981     } else {
1982         return true;
1983     }
1984     return false;
1988 function get_list_of_plugins($plugin="mod") {
1989 // Lists plugin directories within some directory
1991     global $CFG;
1993     $basedir = opendir("$CFG->dirroot/$plugin");
1994     while ($dir = readdir($basedir)) {
1995         if ($dir == "." || $dir == ".." || $dir == "CVS") {
1996             continue;
1997         }
1998         if (filetype("$CFG->dirroot/$plugin/$dir") != "dir") {
1999             continue;
2000         }
2001         $plugins[] = $dir;
2002     }
2003     if ($plugins) {
2004         asort($plugins);
2005     }
2006     return $plugins;
2010 function check_php_version($version="4.1.0") {
2011 // Returns true is the current version of PHP is greater that the specified one
2012     $minversion = intval(str_replace(".", "", $version));
2013     $curversion = intval(str_replace(".", "", phpversion()));
2014     return ($curversion >= $minversion);
2017 function check_browser_version($brand="MSIE", $version=5.5) {
2018 // Checks to see if is a browser matches the specified
2019 // brand and is equal or better version.
2020     global $HTTP_USER_AGENT;
2022     if (!$HTTP_USER_AGENT) {
2023         return false;
2024     }
2025     $string = explode(";", $HTTP_USER_AGENT);
2026     if (!isset($string[1])) {
2027         return false;
2028     }
2029     $string = explode(" ", trim($string[1]));
2030     if (!isset($string[0]) and !isset($string[1])) {
2031         return false;
2032     }
2033     if ($string[0] == $brand and (float)$string[1] >= $version ) {
2034         return true;
2035     }
2036     return false;
2039 function can_use_richtext_editor() {
2040     global $USER, $CFG;
2041     if ($USER->htmleditor and $CFG->htmleditor) {
2042         return check_browser_version("MSIE", 5.5);
2043     }
2044     return false;
2048 function check_gd_version() {
2049     ob_start();
2050     phpinfo();
2051     $phpinfo = ob_get_contents();
2052     ob_end_clean();
2054     $phpinfo = explode("\n",$phpinfo);
2056     $gdversion = 0;
2058     foreach ($phpinfo as $text) {
2059         $parts = explode('</b>',$text);
2060         foreach ($parts as $key => $val) {
2061             $parts[$key] = strip_tags($val);
2062         }
2063         if ($parts[0]=="GD Version") {
2064             $gdversion = intval($parts[1]);
2065         }
2066     }
2068     return $gdversion;   // 1, 2 or 0
2073 ?>