bfac1492604316bd18297a73de74d8fd7ef63b46
[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="", $menu="") {
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 - HTML code for a button (usually for module editing)
21 // $menu - HTML code for a popup menu 
22     global $USER, $CFG, $THEME;
24     if (file_exists("$CFG->dirroot/theme/$CFG->theme/styles.css")) {
25         $styles = "$CFG->wwwroot/theme/$CFG->theme/styles.css";
26     } else {
27         $styles = "$CFG->wwwroot/theme/standard/styles.css";
28     }
30     if ($navigation == "home") {
31         $home = true;
32         $navigation = "";
33     }
35     if (!$menu and $navigation) {
36         if (isset($USER->id)) {
37             $menu = "<FONT SIZE=2><A TARGET=_parent HREF=\"$CFG->wwwroot/login/logout.php\">".get_string("logout")."</A></FONT>";
38         } else {
39             $menu = "<FONT SIZE=2><A TARGET=_parent HREF=\"$CFG->wwwroot/login/index.php\">".get_string("login")."</A></FONT>";
40         }
41     }
43     // Specify character set ... default is iso-8859-1 but some languages might need something else
44     // Could be optimised by carrying the charset variable around in $USER
45     if (current_language() == "en") {
46         $meta .= "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n";
47     } else {
48         $meta .= "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=".get_string("thischarset")."\">\n";
49     }
51     if ($CFG->langdir == "RTL") {
52         $direction = " DIR=\"RTL\"";
53     } else {
54         $direction = " DIR=\"LTR\"";
55     }
56  
57     if (!$cache) {   // Do everything we can to prevent clients and proxies caching
58         @header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
59         @header("Pragma: no-cache");
60         $meta .= "\n<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">";
61         $meta .= "\n<META HTTP-EQUIV=\"Expires\" CONTENT=\"0\">";
62     }
64     include ("$CFG->dirroot/theme/$CFG->theme/header.html");
65 }
67 function print_footer ($course=NULL) {
68 // Can provide a course object to make the footer contain a link to 
69 // to the course home page, otherwise the link will go to the site home
70     global $USER, $CFG, $THEME;
73 /// Course links
74     if ($course) {
75         if ($course == "home") {   // special case for site home page - please do not remove
76             $homelink  = "<P ALIGN=center><A TITLE=\"Moodle $CFG->release ($CFG->version)\" HREF=\"http://moodle.com/\">";
77             $homelink .= "<BR><IMG WIDTH=130 HEIGHT=19 SRC=\"pix/madewithmoodle2.gif\" BORDER=0></A></P>";
78             $course = get_site();
79             $homepage = true;
80         } else {
81             $homelink = "<A TARGET=_top HREF=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</A>";
82         }
83     } else {
84         $homelink = "<A TARGET=_top HREF=\"$CFG->wwwroot\">".get_string("home")."</A>";
85         $course = get_site();
86     }
88 /// User links
89     if ($USER->realuser) {
90         if ($realuser = get_record("user", "id", $USER->realuser)) {
91             $realuserinfo = " [<A HREF=\"$CFG->wwwroot/course/loginas.php?id=$course->id&return=$realuser->id\">$realuser->firstname $realuser->lastname</A>] ";
92         }
93     }
95     if ($USER->id) {
96         $username = "<A HREF=\"$CFG->wwwroot/user/view.php?id=$USER->id&course=$course->id\">$USER->firstname $USER->lastname</A>";
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");
109 function print_navigation ($navigation) {
110    global $CFG;
112    if ($navigation) {
113        if (! $site = get_site()) {
114            $site->shortname = get_string("home");;
115        }
116        echo "<A TARGET=_top HREF=\"$CFG->wwwroot/\">$site->shortname</A> -> $navigation";
117    }
120 function print_heading($text, $align="CENTER", $size=3) {
121     echo "<P ALIGN=\"$align\"><FONT SIZE=\"$size\"><B>".stripslashes($text)."</B></FONT></P>";
124 function print_heading_with_help($text, $helppage, $module="moodle") {
125 // Centered heading with attached help button (same title text)
126     echo "<P ALIGN=\"CENTER\"><FONT SIZE=\"3\"><B>".stripslashes($text);
127     helpbutton($helppage, $text, $module);
128     echo "</B></FONT></P>";
130     
131 function print_continue($link) {
132     global $HTTP_REFERER;
134     if (!$link) {
135         $link = $HTTP_REFERER;
136     }
138     print_heading("<A HREF=\"$link\">".get_string("continue")."</A>");
142 function print_simple_box($message, $align="", $width="", $color="#FFFFFF", $padding=5, $border=1) {
143     print_simple_box_start($align, $width, $color, $padding, $border);
144     echo "<P>".stripslashes($message)."</P>";
145     print_simple_box_end();
148 function print_simple_box_start($align="", $width="", $color="#FFFFFF", $padding=5, $border=1) {
149     global $THEME;
151     if ($align) {
152         $tablealign = "ALIGN=\"$align\"";
153     }
154     if ($width) {
155         $tablewidth = "WIDTH=\"$width\"";
156         $innertablewidth = "WIDTH=\"100%\"";
157     }
158     echo "<TABLE $tablealign $tablewidth BORDER=0 CELLPADDING=\"$border\" CELLSPACING=0>";
159     echo "<TR><TD BGCOLOR=\"$THEME->borders\">\n";
160     echo "<TABLE $innertablewidth BORDER=0 CELLPADDING=\"$padding\" CELLSPACING=0><TR><TD BGCOLOR=\"$color\">";
163 function print_simple_box_end() {
164     echo "</TD></TR></TABLE>";
165     echo "</TD></TR></TABLE>";
168 function print_single_button($link, $options, $label="OK") {
169     echo "<FORM ACTION=\"$link\" METHOD=GET>";
170     if ($options) {
171         foreach ($options as $name => $value) {
172             echo "<INPUT TYPE=hidden NAME=\"$name\" VALUE=\"$value\">";
173         }
174     }
175     echo "<INPUT TYPE=submit VALUE=\"$label\"></FORM>";
178 function print_spacer($height=1, $width=1, $br=true) {
179     global $CFG;
180     echo "<IMG HEIGHT=\"$height\" WIDTH=\"$width\" SRC=\"$CFG->wwwroot/pix/spacer.gif\" ALT=\"\">";
181     if ($br) {
182         echo "<BR>\n";
183     }
186 function print_file_picture($path, $courseid=0, $height="", $width="", $link="") {
187 // Given the path to a picture file in a course, or a URL,
188 // this function includes the picture in the page.
189     global $CFG;
191     if ($height) {
192         $height = "HEIGHT=\"$height\"";
193     }
194     if ($width) {
195         $width = "WIDTH=\"$width\"";
196     }
197     if ($link) {
198         echo "<A HREF=\"$link\">";
199     }
200     if (substr(strtolower($path), 0, 7) == "http://") {
201         echo "<IMG BORDER=0 $height $width SRC=\"$path\">";
203     } else if ($courseid) {
204         echo "<IMG BORDER=0 $height $width SRC=\"";
205         if ($CFG->slasharguments) {        // Use this method if possible for better caching
206             echo "$CFG->wwwroot/file.php/$courseid/$path";
207         } else {
208             echo "$CFG->wwwroot/file.php?file=$courseid/$path";
209         }
210         echo "\">";
211     } else {
212         echo "Error: must pass URL or course";
213     }
214     if ($link) {
215         echo "</A>";
216     }
219 function print_user_picture($userid, $courseid, $picture, $large=false, $returnstring=false) {
220     global $CFG;
222     $output = "<A HREF=\"$CFG->wwwroot/user/view.php?id=$userid&course=$courseid\">";
223     if ($large) {
224         $file = "f1.jpg";
225         $size = 100;
226     } else {
227         $file = "f2.jpg";
228         $size = 35;
229     }
230     if ($picture) {
231         if ($CFG->slasharguments) {        // Use this method if possible for better caching
232             $output .= "<IMG SRC=\"$CFG->wwwroot/user/pix.php/$userid/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">";
233         } else {
234             $output .= "<IMG SRC=\"$CFG->wwwroot/user/pix.php?file=/$userid/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">";
235         }
236     } else {
237         $output .= "<IMG SRC=\"$CFG->wwwroot/user/default/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">";
238     }
239     $output .= "</A>";
241     if ($returnstring) {
242         return $output;
243     } else {
244         echo $output;
245     }
248 function print_table($table) {
249 // Prints a nicely formatted table.
250 // $table is an object with several properties.
251 //     $table->head      is an array of heading names.
252 //     $table->align     is an array of column alignments
253 //     $table->size      is an array of column sizes
254 //     $table->data[]    is an array of arrays containing the data.
255 //     $table->width     is an percentage of the page
256 //     $table->cellpadding    padding on each cell
257 //     $table->cellspacing    spacing between cells
259     if (isset($table->align)) {
260         foreach ($table->align as $key => $aa) {
261             if ($aa) {
262                 $align[$key] = " ALIGN=\"$aa\"";
263             } else {
264                 $align[$key] = "";
265             }
266         }
267     }
268     if (isset($table->size)) {
269         foreach ($table->size as $key => $ss) {
270             if ($ss) {
271                 $size[$key] = " WIDTH=\"$ss\"";
272             } else {
273                 $size[$key] = "";
274             }
275         }
276     }
278     if (!$table->width) {
279         $table->width = "80%";
280     }
282     if (!$table->cellpadding) {
283         $table->cellpadding = "5";
284     }
286     if (!$table->cellspacing) {
287         $table->cellspacing = "1";
288     }
290     print_simple_box_start("CENTER", "$table->width", "#FFFFFF", 0);
291     echo "<TABLE WIDTH=100% BORDER=0 valign=top align=center ";
292     echo " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\">\n";
294     if ($table->head) {
295         echo "<TR>";
296         foreach ($table->head as $key => $heading) {
297             echo "<TH VALIGN=top ".$align[$key].$size[$key].">$heading</TH>";
298         }
299         echo "</TR>\n";
300     }
302     foreach ($table->data as $row) {
303         echo "<TR VALIGN=TOP>";
304         foreach ($row as $key => $item) {
305             echo "<TD ".$align[$key].$size[$key].">$item</TD>";
306         }
307         echo "</TR>\n";
308     }
309     echo "</TABLE>\n";
310     print_simple_box_end();
312     return true;
315 function print_editing_switch($courseid) {
316     global $CFG, $USER;
318     if (isteacher($courseid)) {
319         if ($USER->editing) {
320             echo "<A HREF=\"$CFG->wwwroot/course/view.php?id=$courseid&edit=off\">Turn editing off</A>";
321         } else {
322             echo "<A HREF=\"$CFG->wwwroot/course/view.php?id=$courseid&edit=on\">Turn editing on</A>";
323         }
324     }
327 function format_float($num, $places=0) {
328     return sprintf("%.$places"."f", $num);
331 function print_textarea($richedit, $rows, $cols, $width, $height, $name, $value="") {
332     global $CFG, $THEME;
334     if ($richedit) {
335         echo "<object id=richedit style=\"BACKGROUND-COLOR: buttonface\"";
336         echo " data=\"$CFG->wwwroot/lib/rte/richedit.html\"";
337         echo " width=\"$width\" height=\"$height\" ";
338         echo " type=\"text/x-scriptlet\" VIEWASTEXT></object>\n";
339         echo "<TEXTAREA style=\"display:none\" NAME=\"$name\" ROWS=1 COLS=1>";
340         p($value);
341         echo "</TEXTAREA>\n";
342     } else {
343         echo "<TEXTAREA name=\"$name\" rows=\"$rows\" cols=\"$cols\" wrap=virtual>";
344         p($value);
345         echo "</TEXTAREA>\n";
346     }
349 function print_richedit_javascript($form, $name, $source="no") {
350     echo "<SCRIPT language=\"JavaScript\" event=\"onload\" for=\"window\">\n";
351     echo "   document.richedit.options = \"history=no;source=$source\";";
352     echo "   document.richedit.docHtml = $form.$name.innerText;";
353     echo "</SCRIPT>";
357 function update_course_icon($courseid) {
358 // Used to be an icon, but it's now a simple form button
359     global $CFG, $USER;
361     if (isteacher($courseid)) {
362         if ($USER->editing) {
363             $string = get_string("turneditingoff");
364             $edit = "off";
365         } else {
366             $string = get_string("turneditingon");
367             $edit = "on";
368         }
369         return "<FORM TARGET=_parent METHOD=GET ACTION=\"$CFG->wwwroot/course/view.php\">".
370                "<INPUT TYPE=hidden NAME=id VALUE=\"$courseid\">".
371                "<INPUT TYPE=hidden NAME=edit VALUE=\"$edit\">".
372                "<INPUT TYPE=submit VALUE=\"$string\"></FORM>";
373     }
376 function update_module_button($moduleid, $courseid, $string) {
377 // Prints the editing button on a module "view" page
378     global $CFG;
380     if (isteacher($courseid)) {
381         $string = get_string("updatethis", "", $string);
382         return "<FORM TARGET=_parent METHOD=GET ACTION=\"$CFG->wwwroot/course/mod.php\">".
383                "<INPUT TYPE=hidden NAME=update VALUE=\"$moduleid\">".
384                "<INPUT TYPE=hidden NAME=return VALUE=\"true\">".
385                "<INPUT TYPE=submit VALUE=\"$string\"></FORM>";
386     }
390 function navmenu($course, $cm) {
391 // Given a course and a (current) coursemodule
392 // This function returns a small popup menu with all the 
393 // course activity modules in it, as a navigation menu
394 // The data is taken from the serialised array stored in 
395 // the course record
397     global $CFG;
399     if ($course->format == 'weeks') {
400         $strsection = get_string("week");
401     } else {
402         $strsection = get_string("topic");
403     }
405     if (!$array = unserialize($course->modinfo)) {
406         return "";
407     }
408     $section = -1;
409     $selected = "";
410     foreach ($array as $mod) {
411         if ($mod->section > 0 and $section <> $mod->section) {
412             $menu[] = "-------------- $strsection $mod->section --------------";
413         }
414         $section = $mod->section;
415         $url = "$mod->mod/view.php?id=$mod->cm";
416         if ($cm->id == $mod->cm) {
417             $selected = $url;
418         }
419         $mod->name = urldecode($mod->name);
420         if (strlen($mod->name) > 55) {
421             $mod->name = substr($mod->name, 0, 50)."...";
422         }
423         $menu[$url] = $mod->name; 
424     }
426     return popup_form("$CFG->wwwroot/mod/", $menu, "navmenu", $selected, get_string("jumpto"), "", "", true);
427 }   
430 function print_date_selector($day, $month, $year, $currenttime=0) {
431 // Currenttime is a default timestamp in GMT
432 // Prints form items with the names $day, $month and $year
434     if (!$currenttime) {
435         $currenttime = time();
436     }
437     $currentdate = usergetdate($currenttime);
439     for ($i=1; $i<=31; $i++) {
440         $days[$i] = "$i";
441     }
442     for ($i=1; $i<=12; $i++) {
443         $months[$i] = date("F", mktime(0,0,0,$i,1,2000));
444     }
445     for ($i=2000; $i<=2010; $i++) {
446         $years[$i] = $i;
447     }
448     choose_from_menu($days,   $day,   $currentdate[mday], "");
449     choose_from_menu($months, $month, $currentdate[mon],  "");
450     choose_from_menu($years,  $year,  $currentdate[year], "");
453 function print_time_selector($hour, $minute, $currenttime=0) {
454 // Currenttime is a default timestamp in GMT
455 // Prints form items with the names $hour and $minute
457     if (!$currenttime) {
458         $currenttime = time();
459     }
460     $currentdate = usergetdate($currenttime);
461     for ($i=0; $i<=23; $i++) {
462         $hours[$i] = sprintf("%02d",$i);
463     }
464     for ($i=0; $i<=59; $i++) {
465         $minutes[$i] = sprintf("%02d",$i);
466     }
467     choose_from_menu($hours,   $hour,   $currentdate[hours],   "");
468     choose_from_menu($minutes, $minute, $currentdate[minutes], "");
471 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0) {
472 // Given date parts in user time, produce a GMT timestamp
474    return mktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year);
478 function format_time($totalsecs, $str=NULL) {
479 // Given an amount of time in seconds, prints it 
480 // nicely as months, days, hours etc as needed
482     $totalsecs = abs($totalsecs);
484     if (!$str) {  // Create the str structure the slow way
485         $str->day   = get_string("day");
486         $str->days  = get_string("days");
487         $str->hour  = get_string("hour");
488         $str->hours = get_string("hours");
489         $str->min   = get_string("min");
490         $str->mins  = get_string("mins");
491         $str->sec   = get_string("sec");
492         $str->secs  = get_string("secs");
493     }
495     $days      = floor($totalsecs/86400);
496     $remainder = $totalsecs - ($days*86400);
497     $hours     = floor($remainder/3600);
498     $remainder = $remainder - ($hours*3600);
499     $mins      = floor($remainder/60);
500     $secs      = $remainder - ($mins*60);
502     $ss = ($secs == 1)  ? $str->sec  : $str->secs;
503     $sm = ($mins == 1)  ? $str->min  : $str->mins;
504     $sh = ($hours == 1) ? $str->hour : $str->hours;
505     $sd = ($days == 1)  ? $str->day  : $str->days;
507     if ($days)  $odays  = "$days $sd";
508     if ($hours) $ohours = "$hours $sh";
509     if ($mins)  $omins  = "$mins $sm";
510     if ($secs)  $osecs  = "$secs $ss";
512     if ($days)  return "$odays $ohours";
513     if ($hours) return "$ohours $omins";
514     if ($mins)  return "$omins $osecs";
515     if ($secs)  return "$osecs";
516     return get_string("now");
519 function userdate($date, $format="", $timezone=99) {
520 // Returns a formatted string that represents a date in user time
521 // WARNING: note that the format is for strftime(), not date().
523     global $USER;
525     if ($format == "") {
526         $format = "%A, %e %B %Y, %I:%M %p";
527     }
528     if ($timezone == 99) {
529         if (isset($USER->timezone)) {
530             $timezone = (float)$USER->timezone;
531         }
532     }
533     if (abs($timezone) > 12) {
534         return strftime("$format", $date);
535     }
536     return gmstrftime($format, $date + (int)($timezone * 3600));
539 function usergetdate($date, $timezone=99) {
540 // Given a $date timestamp in GMT, returns an array 
541 // that represents the date in user time
543     global $USER;
545     if ($timezone == 99) {
546         $timezone = (float)$USER->timezone;
547     }
548     if (abs($timezone) > 12) {
549         return getdate($date);
550     }
551     //There is no gmgetdate so I have to fake it...
552     $date = $date + (int)($timezone * 3600);
553     $getdate["seconds"] = gmstrftime("%S", $date);
554     $getdate["minutes"] = gmstrftime("%M", $date);
555     $getdate["hours"]   = gmstrftime("%H", $date);
556     $getdate["mday"]    = gmstrftime("%d", $date);
557     $getdate["wday"]    = gmstrftime("%u", $date);
558     $getdate["mon"]     = gmstrftime("%m", $date);
559     $getdate["year"]    = gmstrftime("%Y", $date);
560     $getdate["yday"]    = gmstrftime("%j", $date);
561     $getdate["weekday"] = gmstrftime("%A", $date);
562     $getdate["month"]   = gmstrftime("%B", $date);
563     return $getdate;
566 function usertime($date, $timezone=99) {
567 // Given a GMT timestamp (seconds since epoch), offsets it by 
568 // the timezone.  eg 3pm in India is 3pm GMT - 7 * 3600 seconds
569     global $USER;
571     if ($timezone == 99) {
572         $timezone = (float)$USER->timezone;
573     }
574     if (abs($timezone) > 12) {
575         return $date;
576     }
577     return $date - (int)($timezone * 3600);
580 function usergetmidnight($date, $timezone=99) {
581 // Given a time, return the GMT timestamp of the most recent midnight
582 // for the current user.
583     global $USER;
585     if ($timezone == 99) {
586         $timezone = (float)$USER->timezone;
587     }
589     $userdate = usergetdate($date, $timezone);
591     if (abs($timezone) > 12) {
592         return mktime(0, 0, 0, $userdate["mon"], $userdate["mday"], $userdate["year"]);
593     }
595     $timemidnight = gmmktime (0, 0, 0, $userdate["mon"], $userdate["mday"], $userdate["year"]);
596     return usertime($timemidnight, $timezone); // Time of midnight of this user's day, in GMT
600 function usertimezone($timezone=99) {
601 // returns a string that prints the user's timezone
602     global $USER;
604     if ($timezone == 99) {
605         $timezone = (float)$USER->timezone;
606     }
607     if (abs($timezone) > 12) {
608         return "server time";
609     }
610     if (abs($timezone) < 0.5) {
611         return "GMT";
612     }
613     if ($timezone > 0) {
614         return "GMT+$timezone";
615     } else {
616         return "GMT$timezone";
617     }
621 function error ($message, $link="") {
622     global $CFG, $SESSION;
624     print_header(get_string("error"));
625     echo "<BR>";
626     print_simple_box($message, "center", "", "#FFBBBB");
627    
628     if (!$link) {
629         if ( !empty($SESSION->fromurl) ) {
630             $link = "$SESSION->fromurl";
631             unset($SESSION->fromurl);
632             save_session("SESSION");
633         } else {
634             $link = "$CFG->wwwroot";
635         }
636     }
637     print_continue($link);
638     print_footer();
639     die;
642 function helpbutton ($page, $title="", $module="moodle", $image=true, $text="") {
643     // $page = the keyword that defines a help page
644     // $title = the title of links, rollover tips, alt tags etc
645     // $module = which module is the page defined in
646     // $image = use a help image for the link?  (otherwise uses text)
647     // $text = if defined then this text is used in the page, and 
648     //         the $page variable is ignored.
649     global $CFG;
651     if ($module == "") {
652         $module = "moodle";
653     }
654     if ($image) {
655         $linkobject = "<IMG BORDER=0 HEIGHT=17 WIDTH=22 ALT=\"$title\" SRC=\"$CFG->wwwroot/pix/help.gif\">";
656     } else {
657         $linkobject = $title;
658     }
659     if ($text) {
660         $url = "/help.php?module=$module&text=$text";
661     } else {
662         $url = "/help.php?module=$module&file=$page.html";
663     }
664     link_to_popup_window ($url, "popup", $linkobject, 400, 500, $title);
667 function notice ($message, $link="") {
668     global $THEME, $HTTP_REFERER;
670     if (!$link) {
671         $link = $HTTP_REFERER;
672     }
674     echo "<BR>";
675     print_simple_box($message, "center", "", "$THEME->cellheading");
676     print_heading("<A HREF=\"$link\">".get_string("continue")."</A>");
677     print_footer(get_site());
678     die;
681 function notice_yesno ($message, $linkyes, $linkno) {
682     global $THEME;
684     print_simple_box_start("center", "", "$THEME->cellheading");
685     echo "<P ALIGN=CENTER><FONT SIZE=3>$message</FONT></P>";
686     echo "<P ALIGN=CENTER><FONT SIZE=3><B>";
687     echo "<A HREF=\"$linkyes\">".get_string("yes")."</A>";
688     echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
689     echo "<A HREF=\"$linkno\">".get_string("no")."</A>";
690     echo "</B></FONT></P>";
691     print_simple_box_end();
694 function redirect($url, $message="", $delay=0) {
695 // Uses META tags to redirect the user, after printing a notice
697     echo "<META HTTP-EQUIV='Refresh' CONTENT='$delay; URL=$url'>";
699     if (!empty($message)) {
700         print_header();
701         echo "<CENTER>";
702         echo "<P>$message</P>";
703         echo "<P>( <A HREF=\"$url\">".get_string("continue")."</A> )</P>";
704         echo "</CENTER>";
705     }
706     die; 
709 function notify ($message) {
710     echo "<P align=center><B><FONT COLOR=#FF0000>$message</FONT></B></P>\n";
715 /// PARAMETER HANDLING ////////////////////////////////////////////////////
717 function require_variable($var) {
718     if (! isset($var)) {
719         error("A required parameter was missing");
720     }
723 function optional_variable(&$var, $default=0) {
724     if (! isset($var)) {
725         $var = $default;
726     }
732 /// DATABASE HANDLING ////////////////////////////////////////////////
734 function execute_sql($command, $feedback=true) {
735 // Completely general
737     global $db;
738     
739     $result = $db->Execute("$command");
741     if ($result) {
742         if ($feedback) {
743             echo "<P><FONT COLOR=green><B>".get_string("success")."</B></FONT></P>";
744         }
745         return true;
746     } else {
747         if ($feedback) {
748             echo "<P><FONT COLOR=red><B>".get_string("error")."</B></FONT></P>";
749         }
750         return false;
751     }
754 function modify_database($sqlfile) {
755 // Assumes that the input text file consists of a number 
756 // of SQL statements ENDING WITH SEMICOLONS.  The semicolons
757 // MUST be the last character in a line.
758 // Lines that are blank or that start with "#" are ignored.
759 // Only tested with mysql dump files (mysqldump -p -d moodle)
762     if (file_exists($sqlfile)) {
763         $success = true;
764         $lines = file($sqlfile);
765         $command = "";
767         while ( list($i, $line) = each($lines) ) {
768             $line = chop($line);
769             $length = strlen($line);
771             if ($length  &&  substr($line, 0, 1) <> "#") { 
772                 if (substr($line, $length-1, 1) == ";") {
773                     $line = substr($line, 0, $length-1);   // strip ;
774                     $command .= $line;
775                     if (! execute_sql($command)) {
776                         $success = false;
777                     }
778                     $command = "";
779                 } else {
780                     $command .= $line;
781                 }
782             }
783         }
785     } else {
786         $success = false;
787         echo "<P>Tried to modify database, but \"$sqlfile\" doesn't exist!</P>";
788     }
790     return $success;
794 function record_exists($table, $field, $value) {
795     global $db;
797     $rs = $db->Execute("SELECT * FROM $table WHERE $field = '$value' LIMIT 1");
798     if (!$rs) return false;
800     if ( $rs->RecordCount() ) {
801         return true;
802     } else {
803         return false;
804     }
807 function record_exists_sql($sql) {
808     global $db;
810     $rs = $db->Execute($sql);
811     if (!$rs) return false;
813     if ( $rs->RecordCount() ) {
814         return true;
815     } else {
816         return false;
817     }
821 function count_records($table, $selector, $value) {
822 // Get all the records and count them
823     global $db;
825     $rs = $db->Execute("SELECT COUNT(*) FROM $table WHERE $selector = '$value'");
826     if (!$rs) return 0;
828     return $rs->fields[0];
831 function count_records_sql($sql) {
832 // Get all the records and count them
833     global $db;
835     $rs = $db->Execute("$sql");
836     if (!$rs) return 0;
838     return $rs->fields[0];
841 function get_record($table, $selector, $value) {
842 // Get a single record as an object
843     global $db;
845     $rs = $db->Execute("SELECT * FROM $table WHERE $selector = '$value'");
846     if (!$rs) return false;
848     if ( $rs->RecordCount() == 1 ) {
849         return (object)$rs->fields;
850     } else {
851         return false;
852     }
855 function get_record_sql($sql) {
856 // Get a single record as an object
857 // The sql statement is provided as a string.
859     global $db;
861     $rs = $db->Execute("$sql");
862     if (!$rs) return false;
864     if ( $rs->RecordCount() == 1 ) {
865         return (object)$rs->fields;
866     } else {
867         return false;
868     }
871 function get_records($table, $selector, $value, $sort="", $fields="*") {
872 // Get a number of records as an array of objects
873 // Can optionally be sorted eg "time ASC" or "time DESC"
874 // If "fields" is specified, only those fields are returned
875 // The "key" is the first column returned, eg usually "id"
876     global $db;
878     if ($sort) {
879         $sortorder = "ORDER BY $sort";
880     }
881     $sql = "SELECT $fields FROM $table WHERE $selector = '$value' $sortorder";
883     return get_records_sql($sql);
887 function get_records_list($table, $selector, $values, $sort="", $fields="*") {
888 // Get a number of records as an array of objects
889 // Differs from get_records() in that the values variable 
890 // can be a comma-separated list of values eg  "4,5,6,10"
891 // Can optionally be sorted eg "time ASC" or "time DESC"
892 // The "key" is the first column returned, eg usually "id"
893     global $db;
895     if ($sort) {
896         $sortorder = "ORDER BY $sort";
897     }
898     $sql = "SELECT $fields FROM $table WHERE $selector in ($values) $sortorder";
900     return get_records_sql($sql);
904 function get_records_sql($sql) {
905 // Get a number of records as an array of objects
906 // The "key" is the first column returned, eg usually "id"
907 // The sql statement is provided as a string.
909     global $db;
911     $rs = $db->Execute("$sql");
912     if (!$rs) return false;
914     if ( $rs->RecordCount() > 0 ) {
915         if ($records = $rs->GetAssoc(true)) {
916             foreach ($records as $key => $record) {
917                 $objects[$key] = (object) $record;
918             }
919             return $objects;
920         } else {
921             return false;
922         }
923     } else {
924         return false;
925     }
928 function get_records_sql_menu($sql) {
929 // Given an SQL select, this function returns an associative 
930 // array of the first two columns.  This is most useful in 
931 // combination with the choose_from_menu function to create 
932 // a form menu.
934     global $db;
936     $rs = $db->Execute("$sql");
937     if (!$rs) return false;
939     if ( $rs->RecordCount() > 0 ) {
940         while (!$rs->EOF) {
941             $menu[$rs->fields[0]] = $rs->fields[1];
942             $rs->MoveNext();
943         }
944         return $menu;
945         
946     } else {
947         return false;
948     }
951 function get_field($table, $field, $selector, $value) {
952     global $db;
954     $rs = $db->Execute("SELECT $field FROM $table WHERE $selector = '$value'");
955     if (!$rs) return false;
957     if ( $rs->RecordCount() == 1 ) {
958         return $rs->fields["$field"];
959     } else {
960         return false;
961     }
964 function set_field($table, $field, $newvalue, $selector, $value) {
965     global $db;
967     return $db->Execute("UPDATE $table SET $field = '$newvalue' WHERE $selector = '$value'");
970 function set_config($name, $value) {
971 // No need for get_config because they are usually always available in $CFG
973     if (get_field("config", "value", "name", $name)) {
974         return set_field("config", "value", $value, "name", $name);
975     } else {
976         $config->name = $name;
977         $config->value = $value;
978         return insert_record("config", $config);
979     }
982 function delete_records($table, $selector, $value) {
983 // Delete one or more records from a table
984     global $db;
986     return $db->Execute("DELETE FROM $table WHERE $selector = '$value'");
989 function insert_record($table, $dataobject) {
990 // Insert a record into a table and return the "id" field
991 // $dataobject is an object containing needed data
993     global $db;
995     // Determine all the fields needed
996     if (! $columns = $db->MetaColumns("$table")) {
997         return false;
998     }
1000     $data = (array)$dataobject;
1002     // Pull out data matching these fields
1003     foreach ($columns as $column) {
1004         if ($column->name <> "id" && isset($data[$column->name]) ) {
1005             $ddd[$column->name] = $data[$column->name];
1006         }
1007     }
1009     // Construct SQL queries
1010     if (! $numddd = count($ddd)) {
1011         return 0;
1012     }
1014     $count = 0;
1015     $insert = "";
1016     $select = "";
1018     foreach ($ddd as $key => $value) {
1019         $count++;
1020         $insert .= "$key = '$value'";
1021         $select .= "$key = '$value'";
1022         if ($count < $numddd) {
1023             $insert .= ", ";
1024             $select .= " AND ";
1025         }
1026     }
1028     if (! $rs = $db->Execute("INSERT INTO $table SET $insert")) {
1029         return false;
1030     } 
1032     // Pull it out again to find the id.  This is the most cross-platform method.
1033     if ($rs = $db->Execute("SELECT id FROM $table WHERE $select")) {
1034         return $rs->fields[0];
1035     } else {
1036         return false;
1037     }
1041 function update_record($table, $dataobject) {
1042 // Update a record in a table
1043 // $dataobject is an object containing needed data
1045     global $db;
1047     if (! isset($dataobject->id) ) {
1048         return false;
1049     }
1051     // Determine all the fields in the table
1052     if (!$columns = $db->MetaColumns($table)) {
1053         return false;
1054     }
1055     $data = (array)$dataobject;
1057     // Pull out data matching these fields
1058     foreach ($columns as $column) {
1059         if ($column->name <> "id" && isset($data[$column->name]) ) {
1060             $ddd[$column->name] = $data[$column->name];
1061         }
1062     }
1064     // Construct SQL queries
1065     $numddd = count($ddd);
1066     $count = 0;
1067     $update = "";
1069     foreach ($ddd as $key => $value) {
1070         $count++;
1071         $update .= "$key = '$value'";
1072         if ($count < $numddd) {
1073             $update .= ", ";
1074         }
1075     }
1077     if ($rs = $db->Execute("UPDATE $table SET $update WHERE id = '$dataobject->id'")) {
1078         return true;
1079     } else {
1080         return false;
1081     }
1085 function print_object($object) {
1086 // Mostly just for debugging
1088     $array = (array)$object;
1089     foreach ($array as $key => $item) {
1090         echo "$key -> $item <BR>";
1091     }
1095 /// USER DATABASE ////////////////////////////////////////////////
1097 function get_user_info_from_db($field, $value) {
1099     global $db;
1101     if (!$field || !$value) 
1102         return false;
1104     if (! $result = $db->Execute("SELECT * FROM user WHERE $field = '$value' AND deleted <> '1'")) {
1105         error("Could not find any active users!");
1106     }
1108     if ( $result->RecordCount() == 1 ) {
1109         $user = (object)$result->fields;
1111         $rs = $db->Execute("SELECT course FROM user_students WHERE user = '$user->id' ");
1112         while (!$rs->EOF) {
1113             $course = $rs->fields["course"];
1114             $user->student["$course"] = true;
1115             $rs->MoveNext();
1116         }
1118         $rs = $db->Execute("SELECT course FROM user_teachers WHERE user = '$user->id' ");
1119         while (!$rs->EOF) {
1120             $course = $rs->fields["course"];
1121             $user->teacher["$course"] = true;
1122             $rs->MoveNext();
1123         }
1125         $rs = $db->Execute("SELECT * FROM user_admins WHERE user = '$user->id' ");
1126         while (!$rs->EOF) {
1127             $user->admin = true;
1128             $rs->MoveNext();
1129         }
1131         if ($course = get_site()) {
1132             // Everyone is always a member of the top course
1133             $user->student["$course->id"] = true;
1134         }
1136         return $user;
1138     } else {
1139         return false;
1140     }
1143 function update_user_in_db() {
1145    global $db, $USER, $REMOTE_ADDR;
1147    if (!isset($USER->id)) 
1148        return false;
1150    $timenow = time();
1151    if ($db->Execute("UPDATE user SET lastIP='$REMOTE_ADDR', lastaccess='$timenow' WHERE id = '$USER->id' ")) {
1152        return true;
1153    } else {
1154        return false;
1155    }
1158 function require_login($courseid=0) {
1159 // This function checks that the current user is logged in, and optionally
1160 // whether they are "logged in" or allowed to be in a particular course.
1161 // If not, then it redirects them to the site login or course enrolment.
1163     global $CFG, $SESSION, $USER, $FULLME, $HTTP_REFERER, $PHPSESSID;
1164       
1165     // First check that the user is logged in to the site.
1167     if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not
1168         $SESSION->wantsurl = $FULLME;
1169         $SESSION->fromurl  = $HTTP_REFERER;
1170         save_session("SESSION");
1171         $USER = NULL;
1172         save_session("USER");
1173         if ($PHPSESSID) { // Cookies not enabled.
1174             redirect("$CFG->wwwroot/login/index.php?PHPSESSID=$PHPSESSID");
1175         } else {
1176             redirect("$CFG->wwwroot/login/index.php");
1177         }
1178         die;
1179     }
1180     
1181     // Next, check if the user can be in a particular course
1182     if ($courseid) {
1183         if ($USER->student[$courseid] || $USER->teacher[$courseid] || $USER->admin) {
1184             if (isset($USER->realuser)) {   // Make sure the REAL person can also access this course
1185                 if (!isteacher($courseid, $USER->realuser)) {
1186                     print_header();
1187                     notice(get_string("studentnotallowed", "", "$USER->firstname $USER->lastname"), $CFG->wwwroot);
1188                 }
1190             } else {  // just update their last login time
1191                 update_user_in_db();
1192             }
1193             if (!$USER->email) {            // User logged in, but has not set up profile!
1194                                             // This can occur with external authentication
1195                 redirect("$CFG->wwwroot/user/edit.php?id=$USER->id&course=$courseid");
1196                 die;
1197             }
1198             return;   // user is a member of this course.
1199         }
1200         if (! $course = get_record("course", "id", $courseid)) {
1201             error("That course doesn't exist");
1202         }
1203         if ($USER->username == "guest") {
1204             switch ($course->guest) {
1205                 case 0: // Guests not allowed
1206                     print_header();
1207                     notice(get_string("guestsnotallowed", "", $course->fullname));
1208                     break;
1209                 case 1: // Guests allowed
1210                     update_user_in_db();
1211                     return;
1212                 case 2: // Guests allowed with key (drop through)
1213                     break;
1214             }
1215         }
1217         // Currently not enrolled in the course, so see if they want to enrol
1218         $SESSION->wantsurl = $FULLME;
1219         save_session("SESSION");
1220         redirect("$CFG->wwwroot/course/enrol.php?id=$courseid");
1221         die;
1222     }
1227 function update_login_count() {
1228     global $SESSION;
1230     $max_logins = 10;
1232     if (empty($SESSION->logincount)) {
1233         $SESSION->logincount = 1;
1234     } else {
1235         $SESSION->logincount++;
1236     }
1237     save_session("SESSION");
1239     if ($SESSION->logincount > $max_logins) {
1240         unset($SESSION->wantsurl);
1241         save_session("SESSION");
1242         error("Sorry, you have exceeded the allowed number of login attempts. Restart your browser.");
1243     }
1246 function remove_admin($user) {
1247     global $db;
1249     return $db->Execute("DELETE FROM user_admins WHERE user = '$user'");
1252 function remove_teacher($user, $course=0) {
1253     global $db;
1255     if ($course) {
1256         /// First delete any crucial stuff that might still send mail
1257         if ($forums = get_records("forum", "course", $course)) {
1258             foreach ($forums as $forum) {
1259                 $db->Execute("DELETE FROM forum_subscriptions WHERE forum = '$forum->id' AND user = '$user'");
1260             }
1261         }
1262         return $db->Execute("DELETE FROM user_teachers WHERE user = '$user' AND course = '$course'");
1263     } else {
1264         delete_records("forum_subscriptions", "user", $user);
1265         return delete_records("user_teachers", "user", $user);
1266     }
1270 function enrol_student($user, $course) {
1271     global $db;
1273         $timenow = time();
1275         $rs = $db->Execute("INSERT INTO user_students (user, course, start, end, time) 
1276                         VALUES ($user, $course, 0, 0, $timenow)");
1277         if ($rs) {
1278                 return true;
1279         } else {
1280             return false;
1281         }
1284 function unenrol_student($user, $course=0) {
1285     global $db;
1287     if ($course) {
1288         /// First delete any crucial stuff that might still send mail
1289         if ($forums = get_records("forum", "course", $course)) {
1290             foreach ($forums as $forum) {
1291                 $db->Execute("DELETE FROM forum_subscriptions WHERE forum = '$forum->id' AND user = '$user'");
1292             }
1293         }
1294         return $db->Execute("DELETE FROM user_students WHERE user = '$user' AND course = '$course'");
1296     } else {
1297         delete_records("forum_subscriptions", "user", $user);
1298         return delete_records("user_students", "user", $user);
1299     }
1303 function isadmin($userid=0) {
1304     global $USER;
1306     if (!$userid) {
1307         return $USER->admin;
1308     }
1310     return record_exists_sql("SELECT * FROM user_admins WHERE user='$userid'");
1313 function isteacher($courseid, $userid=0) {
1314     global $USER;
1316     if (isadmin($userid)) {  // admins can do anything the teacher can
1317         return true;
1318     }
1320     if (!$userid) {
1321         return $USER->teacher[$courseid];
1322     }
1324     return record_exists_sql("SELECT * FROM user_teachers WHERE user='$userid' AND course='$courseid'");
1328 function isstudent($courseid, $userid=0) {
1329     global $USER;
1331     if (!$userid) {
1332         return $USER->student[$courseid];
1333     }
1335     $timenow = time();   // todo:  add time check below
1337     return record_exists_sql("SELECT * FROM user_students WHERE user='$userid' AND course='$courseid'");
1340 function isguest($userid=0) {
1341     global $USER;
1343     if (!$userid) {
1344         return ($USER->username == "guest");
1345     }
1347     return record_exists_sql("SELECT * FROM user WHERE id='$userid' AND username = 'guest' ");
1350 function isediting($courseid, $user=NULL) {
1351     global $USER;
1352     if (!$user){
1353         $user = $USER;
1354     }
1355     return ($user->editing and isteacher($courseid, $user->id));
1358 function reset_login_count() {
1359     global $SESSION;
1361     $SESSION->logincount = 0;
1362     save_session("SESSION");
1366 function set_moodle_cookie($thing) {
1368     $days = 60;
1369     $seconds = 60*60*24*$days;
1371     setCookie ('MOODLEID', "", time() - 3600, "/");
1372     setCookie ('MOODLEID', rc4encrypt($thing), time()+$seconds, "/");
1376 function get_moodle_cookie() {
1377     global $MOODLEID;
1378     return rc4decrypt($MOODLEID);
1382 function save_session($VAR) {
1383 // Copies temporary session variable to permanent sesson variable
1384 // eg $_SESSION["USER"] = $USER;
1385     global $$VAR;
1386     $_SESSION[$VAR] = $$VAR;
1390 function create_user_record($username, $password) {
1391 // Creates a bare-bones user record 
1392     global $REMOTE_ADDR, $CFG;
1394     if ($CFG->auth_update_userinfo and function_exists(auth_get_userinfo)) {
1395         if ($newinfo = auth_get_userinfo($username)) {
1396             foreach ($newinfo as $key=>$value){
1397                 $newuser->$key = $value;
1398             }
1399         }
1400     }
1402     $newuser->username = $username;
1403     $newuser->password = md5($password);
1404     $newuser->lang = $CFG->lang;
1405     $newuser->confirmed = 1;
1406     $newuser->lastIP = $REMOTE_ADDR;
1407     $newuser->timemodified = time();
1409     if (insert_record("user", $newuser)) {
1410         return get_user_info_from_db("username", $username);
1411     }
1412     return false;
1415 function authenticate_user_login($username, $password) {
1416 // Given a username and password, this function looks them 
1417 // up using the currently selected authentication mechanism,
1418 // and if the authentication is successful, it returns a 
1419 // valid $user object from the 'user' table.
1420 //
1421 // Uses auth_ functions from the currently active auth module
1423     global $CFG;
1425     if (!isset($CFG->auth)) {
1426         $CFG->auth = "email";    // Default authentication module
1427     }
1429     require_once("$CFG->dirroot/auth/$CFG->auth/lib.php");
1431     if (auth_user_login($username, $password)) {  // Successful authentication
1433         if ($user = get_user_info_from_db("username", $username)) {
1434             if (md5($password) <> $user->password) {
1435                 set_field("user", "password", md5($password), "username", $username);
1436             }
1437             return $user;
1439         } else {
1440             return create_user_record($username, $password);
1441         }
1442     }
1443     return false;
1447 function get_site () {
1448 // Returns $course object of the top-level site.
1449     if ( $course = get_record("course", "category", 0)) {
1450         return $course;
1451     } else {
1452         return false;
1453     }
1456 function get_admin () {
1457 // Returns $user object of the main admin user
1459     if ( $admins = get_records_sql("SELECT u.* FROM user u, user_admins a WHERE a.user = u.id ORDER BY u.id ASC")) {
1460         foreach ($admins as $admin) {
1461             return $admin;   // ie the first one 
1462         }
1463     } else {
1464         return false;
1465     }
1468 function get_teacher($courseid) {
1469 // Returns $user object of the main teacher for a course
1470     if ( $teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t 
1471                                       WHERE t.user = u.id AND t.course = '$courseid' 
1472                                       ORDER BY t.authority ASC")) {
1473         foreach ($teachers as $teacher) {
1474             if ($teacher->authority) {
1475                 return $teacher;   // the highest authority teacher
1476             }
1477         }
1478     } else {
1479         return false;
1480     }
1483 function get_course_students($courseid, $sort="u.lastaccess DESC") {
1484     return get_records_sql("SELECT u.* FROM user u, user_students s
1485                             WHERE s.course = '$courseid' AND s.user = u.id AND u.deleted = '0'
1486                             ORDER BY $sort");
1489 function get_course_teachers($courseid, $sort="t.authority ASC") {
1490     return get_records_sql("SELECT u.*,t.authority,t.role FROM user u, user_teachers t
1491                             WHERE t.course = '$courseid' AND t.user = u.id AND u.deleted = '0'
1492                             ORDER BY $sort");
1495 function get_course_users($courseid, $sort="u.lastaccess DESC") {
1496 // Using this method because the direct SQL just would not always work!
1498     $teachers = get_course_teachers($courseid, $sort);
1499     $students = get_course_students($courseid, $sort);
1501     if ($teachers and $students) {
1502         return array_merge($teachers, $students);
1503     } else if ($teachers) {
1504         return $teachers;
1505     } else {
1506         return $students;
1507     }
1509 //    return get_records_sql("SELECT u.* FROM user u, user_students s, user_teachers t
1510 //                            WHERE (s.course = '$courseid' AND s.user = u.id) OR 
1511 //                                  (t.course = '$courseid' AND t.user = u.id)
1512 //                            ORDER BY $sort");
1517 /// MODULE FUNCTIONS /////////////////////////////////////////////////
1519 function get_coursemodule_from_instance($modulename, $instance, $courseid) {
1520 // Given an instance of a module, finds the coursemodule description
1522     return get_record_sql("SELECT cm.*, m.name
1523                            FROM course_modules cm, modules md, $modulename m 
1524                            WHERE cm.course = '$courseid' AND 
1525                                  cm.deleted = '0' AND
1526                                  cm.instance = m.id AND 
1527                                  md.name = '$modulename' AND 
1528                                  md.id = cm.module AND
1529                                  m.id = '$instance'");
1533 function get_all_instances_in_course($modulename, $courseid, $sort="cw.section") {
1534 // Returns an array of all the active instances of a particular
1535 // module in a given course.   Returns false on any errors.
1537     return get_records_sql("SELECT m.*,cw.section,cm.id as coursemodule 
1538                             FROM course_modules cm, course_sections cw, modules md, $modulename m 
1539                             WHERE cm.course = '$courseid' AND 
1540                                   cm.instance = m.id AND 
1541                                   cm.deleted = '0' AND
1542                                   cm.section = cw.id AND 
1543                                   md.name = '$modulename' AND 
1544                                   md.id = cm.module
1545                             ORDER BY $sort");
1552 /// CORRESPONDENCE  ////////////////////////////////////////////////
1554 function email_to_user($user, $from, $subject, $messagetext, $messagehtml="", $attachment="", $attachname="") {
1555 //  user        - a user record as an object
1556 //  from        - a user record as an object
1557 //  subject     - plain text subject line of the email
1558 //  messagetext - plain text version of the message
1559 //  messagehtml - complete html version of the message (optional)
1560 //  attachment  - a file on the filesystem, relative to $CFG->dataroot
1561 //  attachname  - the name of the file (extension indicates MIME)
1563     global $CFG, $_SERVER;
1565     include_once("$CFG->libdir/phpmailer/class.phpmailer.php");
1567     if (!$user) {
1568         return false;
1569     }
1570     
1571     $mail = new phpmailer;
1573     $mail->Version = "Moodle $CFG->version";           // mailer version 
1574     $mail->PluginDir = "$CFG->libdir/phpmailer/";      // plugin directory (eg smtp plugin)
1576     if ($CFG->smtphosts) {
1577         $mail->IsSMTP();                               // use SMTP directly
1578         $mail->Host = "$CFG->smtphosts";               // specify main and backup servers
1580         if ($CFG->smtpuser) {                          // Use SMTP authentication
1581             $mail->SMTPAuth = true;
1582             $mail->Username = $CFG->smtpuser;
1583             $mail->Password = $CFG->smtppass;
1584         }
1585     } else {
1586         $mail->IsMail();                               // use PHP mail() = sendmail
1587     }
1589     $mail->From     = "$from->email";
1590     $mail->FromName = "$from->firstname $from->lastname";
1591     $mail->Subject  =  stripslashes($subject);
1593     $mail->AddAddress("$user->email", "$user->firstname $user->lastname"); 
1595     $mail->WordWrap = 70;                               // set word wrap
1597     if ($messagehtml) {
1598         $mail->IsHTML(true);
1599         $mail->Body    =  $messagehtml;
1600         $mail->AltBody =  "\n$messagetext\n";
1601     } else {
1602         $mail->IsHTML(false);
1603         $mail->Body =  "\n$messagetext\n";
1604     }
1606     if ($attachment && $attachname) {
1607         if (ereg( "\\.\\." ,$attachment )) {    // Security check for ".." in dir path
1608             $adminuser = get_admin();
1609             $mail->AddAddress("$adminuser->email", "$adminuser->firstname $adminuser->lastname");
1610             $mail->AddStringAttachment("Error in attachment.  User attempted to attach a filename with a unsafe name.", "error.txt", "8bit", "text/plain");
1611         } else {
1612             include_once("$CFG->dirroot/files/mimetypes.php");
1613             $mimetype = mimeinfo("type", $attachname);
1614             $mail->AddAttachment("$CFG->dataroot/$attachment", "$attachname", "base64", "$mimetype");
1615         }
1616     }
1618     if ($mail->Send()) {
1619         return true;
1620     } else {
1621         echo "ERROR: $mail->ErrorInfo\n";
1622         $site = get_site();
1623         add_to_log($site->id, "library", "mailer", $_SERVER["REQUEST_URI"], "ERROR: $mail->ErrorInfo");
1624         return false;
1625     }
1629 /// FILE HANDLING  /////////////////////////////////////////////
1631 function make_upload_directory($directory) {
1632 // $directory = a string of directory names under $CFG->dataroot
1633 // eg  stuff/assignment/1
1634 // Returns full directory if successful, false if not
1636     global $CFG;
1638     $currdir = $CFG->dataroot;
1639     if (!file_exists($currdir)) {
1640         if (! mkdir($currdir, 0750)) {
1641             notify("ERROR: You need to create the directory $currdir with web server write access");
1642             return false;
1643         }
1644     }
1646     $dirarray = explode("/", $directory);
1648     foreach ($dirarray as $dir) {
1649         $currdir = "$currdir/$dir";
1650         if (! file_exists($currdir)) {
1651             if (! mkdir($currdir, 0750)) {
1652                 notify("ERROR: Could not find or create a directory ($currdir)");
1653                 return false;
1654             }
1655         }
1656     }
1658     return $currdir;
1661 function make_mod_upload_directory($courseid) {
1662     global $CFG;
1664     if (! $moddata = make_upload_directory("$courseid/$CFG->moddata")) {
1665         return false;
1666     }
1668     $strreadme = get_string("readme");
1670     if (file_exists("$CFG->dirroot/lang/$CFG->lang/docs/module_files.txt")) {
1671         copy("$CFG->dirroot/lang/$CFG->lang/docs/module_files.txt", "$moddata/$strreadme.txt");
1672     } else {
1673         copy("$CFG->dirroot/lang/en/docs/module_files.txt", "$moddata/$strreadme.txt");
1674     }
1675     return $moddata;
1679 function valid_uploaded_file($newfile) {
1680 // Returns current name of file on disk if true
1681     if (is_uploaded_file($newfile['tmp_name']) and $newfile['size'] > 0) {
1682         return $newfile['tmp_name'];
1683     } else {
1684         return "";
1685     }
1688 function get_max_upload_file_size() {
1689     if (! $filesize = ini_get("upload_max_filesize")) {
1690         $filesize = "5M";
1691     }
1692     return get_real_size($filesize);
1695 function get_directory_list($rootdir, $excludefile="", $descend=true) {
1696 // Returns an array with all the filenames in 
1697 // all subdirectories, relative to the given rootdir.
1698 // If excludefile is defined, then that file/directory is ignored
1700     $dirs = array();
1701    
1702     $dir = opendir($rootdir);
1704     if (!$dir) {
1705         notify("Error: unable to read this directory! : $rootdir");
1706         return $dirs;
1707     }
1709     while ($file = readdir($dir)) {
1710         if ($file != "." and $file != ".." and $file != "CVS" and $file != $excludefile) {
1711             $fullfile = $rootdir."/".$file;
1712             if ($descend and filetype($fullfile) == "dir") {
1713                 $subdirs = get_directory_list($fullfile, $excludefile, $descend);
1714                 foreach ($subdirs as $subdir) {
1715                     $dirs[] = $file."/".$subdir;
1716                 }
1717             } else {
1718                 $dirs[] = $file;
1719             }
1720         }
1721     }
1722     closedir($dir);
1724     asort($dirs);
1726     return $dirs;
1729 function get_real_size($size=0) {
1730 // Converts numbers like 10M into bytes
1731     if (!$size) {
1732         return 0; 
1733     }
1734     $scan['MB'] = 1048576;
1735     $scan['M'] = 1048576;
1736     $scan['KB'] = 1024;
1737     $scan['K'] = 1024;
1739     while (list($key) = each($scan)) {
1740         if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
1741             $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
1742             break;
1743         }
1744     }
1745     return $size;
1748 function display_size($size) {
1749 // Converts bytes into display form
1750     if ($size >= 1073741824) {
1751         $size = round($size / 1073741824 * 10) / 10 . "Gb";
1752     } else if ($size >= 1048576) {
1753         $size = round($size / 1048576 * 10) / 10 . "Mb";
1754     } else if ($size >= 1024) {
1755         $size = round($size / 1024 * 10) / 10 . "Kb";
1756     } else { 
1757         $size = $size . "b";
1758     }
1759     return $size;
1762 function clean_filename($string) {
1763     $string = eregi_replace("\.\.", "", $string);
1764     $string = eregi_replace("[^([:alnum:]|\.)]", "_", $string);
1765     return    eregi_replace("_+", "_", $string);
1769 /// STRING TRANSLATION  ////////////////////////////////////////
1771 function print_string($identifier, $module="", $a=NULL) {
1772     echo get_string($identifier, $module, $a);
1775 function current_language() {
1776 // Returns the code for the current language
1777     global $CFG, $USER;
1779     if (isset($USER->lang)) {    // User language can override site language
1780         return $USER->lang;
1781     } else {
1782         return $CFG->lang;
1783     }
1786 function get_string($identifier, $module="", $a=NULL) {
1787 // Return the translated string specified by $identifier as 
1788 // for $module.  Uses the same format files as STphp.
1789 // $a is an object, string or number that can be used
1790 // within translation strings
1791 //
1792 // eg "hello \$a->firstname \$a->lastname"
1793 // or "hello \$a"
1795     global $CFG;
1797     $lang = current_language();
1799     if ($module == "") {
1800         $module = "moodle";
1801     }
1803     $langpath = "$CFG->dirroot/lang";
1804     $langfile = "$langpath/$lang/$module.php";
1806     if (!file_exists($langfile)) {                // try English instead
1807         $langfile = "$langpath/en/$module.php";
1808         if (!file_exists($langfile)) {
1809             return "ERROR: No lang file ($langpath/en/$module.php)!";
1810         }
1811     }
1813     if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
1815         eval($result);
1816         return $resultstring;
1818     } else {
1819         if ($lang == "en") {
1820             return "[['$identifier']]";
1822         } else {   // Try looking in the english file.
1823             $langfile = "$langpath/en/$module.php";
1824             if (!file_exists($langfile)) {
1825                 return "ERROR: No lang file ($langpath/en/$module.php)!";
1826             }
1827             if ($result = get_string_from_file($identifier, $langfile, "\$resultstring")) {
1828                 eval($result);
1829                 return $resultstring;
1830             } else {
1831                 return "[['$identifier']]";
1832             }
1833         }
1834     }
1838 function get_string_from_file($identifier, $langfile, $destination) {
1839 // This function is only used from get_string().
1840     include ($langfile);
1842     if (!isset ($string[$identifier])) {
1843         return false;
1844     }
1846     return "$destination = sprintf(\"".$string[$identifier]."\");";
1850 function get_list_of_languages() {
1851 /// Returns a list of language codes and their full names
1852     global $CFG;
1854     if (!$langdirs = get_list_of_plugins("lang")) {
1855         return false;
1856     }
1858     foreach ($langdirs as $lang) {
1859         include("$CFG->dirroot/lang/$lang/moodle.php");
1860         $languages[$lang] = $string["thislanguage"]." ($lang)";
1861         unset($string);
1862     }
1863     return $languages;
1867 /// ENCRYPTION  ////////////////////////////////////////////////
1869 function rc4encrypt($data) {
1870     $password = "nfgjeingjk";
1871     return endecrypt($password, $data, "");
1874 function rc4decrypt($data) {
1875     $password = "nfgjeingjk";
1876     return endecrypt($password, $data, "de");
1879 function endecrypt ($pwd, $data, $case) {
1880 // Based on a class by Mukul Sabharwal [mukulsabharwal@yahoo.com]
1882     if ($case == 'de') {
1883         $data = urldecode($data);
1884     }
1886     $key[] = "";
1887     $box[] = "";
1888     $temp_swap = "";
1889     $pwd_length = 0;
1891     $pwd_length = strlen($pwd);
1893     for ($i = 0; $i <= 255; $i++) {
1894         $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1));
1895         $box[$i] = $i;
1896     }
1898     $x = 0;
1900     for ($i = 0; $i <= 255; $i++) {
1901         $x = ($x + $box[$i] + $key[$i]) % 256;
1902         $temp_swap = $box[$i];
1903         $box[$i] = $box[$x];
1904         $box[$x] = $temp_swap;
1905     }
1907     $temp = "";
1908     $k = "";
1910     $cipherby = "";
1911     $cipher = "";
1913     $a = 0;
1914     $j = 0;
1916     for ($i = 0; $i < strlen($data); $i++) {
1917         $a = ($a + 1) % 256;
1918         $j = ($j + $box[$a]) % 256;
1919         $temp = $box[$a];
1920         $box[$a] = $box[$j];
1921         $box[$j] = $temp;
1922         $k = $box[(($box[$a] + $box[$j]) % 256)];
1923         $cipherby = ord(substr($data, $i, 1)) ^ $k;
1924         $cipher .= chr($cipherby);
1925     }
1927     if ($case == 'de') {
1928         $cipher = urldecode(urlencode($cipher));
1929     } else {
1930         $cipher = urlencode($cipher);
1931     }
1933     return $cipher;
1937 /// MISCELLANEOUS ////////////////////////////////////////////////////////////////////
1939 function count_words($string) {
1940     $string = strip_tags($string);
1941     return count(preg_split("/\w\b/", $string)) - 1;
1944 function getweek ($startdate, $thedate) {
1945 // Given dates in seconds, how many weeks is the date from startdate
1946 // The first week is 1, the second 2 etc ... 
1947     
1948     if ($thedate < $startdate) {   // error
1949         return 0;  
1950     }
1952     return floor(($thedate - $startdate) / 604800.0) + 1;
1955 function add_to_log($course, $module, $action, $url="", $info="") {
1956 // Add an entry to the log table.  These are "action" focussed rather
1957 // than web server hits, and provide a way to easily reconstruct what 
1958 // any particular student has been doing.
1959 //
1960 // course = the course id
1961 // module = forum, journal, resource, course, user etc
1962 // action = view, edit, post (often but not always the same as the file.php)
1963 // url    = the file and parameters used to see the results of the action
1964 // info   = additional description information 
1967     global $db, $USER, $REMOTE_ADDR;
1969     if (isset($USER->realuser)) {  // Don't log
1970         return;
1971     }
1973     $timenow = time();
1974     $info = addslashes($info);
1976     $result = $db->Execute("INSERT INTO log
1977                             SET time = '$timenow', 
1978                                 user = '$USER->id',
1979                                 course = '$course',
1980                                 ip = '$REMOTE_ADDR', 
1981                                 module = '$module',
1982                                 action = '$action',
1983                                 url = '$url',
1984                                 info = '$info'");
1985     if (!$result) {
1986         echo "<P>Error: Could not insert a new entry to the Moodle log</P>";  // Don't throw an error
1987     }    
1990 function generate_password($maxlen=10) {
1991 // returns a randomly generated password of length $maxlen.  inspired by
1992 // http://www.phpbuilder.com/columns/jesus19990502.php3 
1994     global $CFG;
1996     $fillers = "1234567890!$-+";
1997     $wordlist = file($CFG->wordlist);
1999     srand((double) microtime() * 1000000);
2000     $word1 = trim($wordlist[rand(0, count($wordlist) - 1)]);
2001     $word2 = trim($wordlist[rand(0, count($wordlist) - 1)]);
2002     $filler1 = $fillers[rand(0, strlen($fillers) - 1)];
2004     return substr($word1 . $filler1 . $word2, 0, $maxlen);
2007 function moodle_needs_upgrading() {
2008 // Checks version numbers of Main code and all modules to see
2009 // if there are any mismatches ... returns true or false
2010     global $CFG;
2012     include_once("$CFG->dirroot/version.php");  # defines $version and upgrades
2013     if ($CFG->version) { 
2014         if ($version > $CFG->version) {
2015             return true;
2016         }
2017         if ($mods = get_list_of_plugins("mod")) {
2018             foreach ($mods as $mod) {
2019                 $fullmod = "$CFG->dirroot/mod/$mod";
2020                 unset($module);
2021                 include_once("$fullmod/version.php");  # defines $module with version etc
2022                 if ($currmodule = get_record("modules", "name", $mod)) {
2023                     if ($module->version > $currmodule->version) {
2024                         return true;
2025                     }
2026                 }
2027             }
2028         }
2029     } else {
2030         return true;
2031     }
2032     return false;
2036 function get_list_of_plugins($plugin="mod") {
2037 // Lists plugin directories within some directory
2039     global $CFG;
2041     $basedir = opendir("$CFG->dirroot/$plugin");
2042     while ($dir = readdir($basedir)) {
2043         if ($dir == "." || $dir == ".." || $dir == "CVS") {
2044             continue;
2045         }
2046         if (filetype("$CFG->dirroot/$plugin/$dir") != "dir") {
2047             continue;
2048         }
2049         $plugins[] = $dir;
2050     }
2051     if ($plugins) {
2052         asort($plugins);
2053     }
2054     return $plugins;
2058 function check_php_version($version="4.1.0") {
2059 // Returns true is the current version of PHP is greater that the specified one
2060     $minversion = intval(str_replace(".", "", $version));
2061     $curversion = intval(str_replace(".", "", phpversion()));
2062     return ($curversion >= $minversion);
2065 function check_browser_version($brand="MSIE", $version=5.5) {
2066 // Checks to see if is a browser matches the specified
2067 // brand and is equal or better version.
2068     global $HTTP_USER_AGENT;
2070     if (!$HTTP_USER_AGENT) {
2071         return false;
2072     }
2073     $string = explode(";", $HTTP_USER_AGENT);
2074     if (!isset($string[1])) {
2075         return false;
2076     }
2077     $string = explode(" ", trim($string[1]));
2078     if (!isset($string[0]) and !isset($string[1])) {
2079         return false;
2080     }
2081     if ($string[0] == $brand and (float)$string[1] >= $version ) {
2082         return true;
2083     }
2084     return false;
2087 function can_use_richtext_editor() {
2088     global $USER, $CFG;
2089     if ($USER->htmleditor and $CFG->htmleditor) {
2090         return check_browser_version("MSIE", 5.5);
2091     }
2092     return false;
2096 function check_gd_version() {
2097     ob_start();
2098     phpinfo();
2099     $phpinfo = ob_get_contents();
2100     ob_end_clean();
2102     $phpinfo = explode("\n",$phpinfo);
2104     $gdversion = 0;
2106     foreach ($phpinfo as $text) {
2107         $parts = explode('</b>',$text);
2108         foreach ($parts as $key => $val) {
2109             $parts[$key] = strip_tags($val);
2110         }
2111         if ($parts[0]=="GD Version") {
2112             $gdversion = intval($parts[1]);
2113         }
2114     }
2116     return $gdversion;   // 1, 2 or 0
2121 ?>