MDL-12296:
[moodle-linuxchix.git] / lib / weblib.php
blob826fda7f44ccb6e22e1c721fb4911d60ff6f1c9c
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // //
12 // This program is free software; you can redistribute it and/or modify //
13 // it under the terms of the GNU General Public License as published by //
14 // the Free Software Foundation; either version 2 of the License, or //
15 // (at your option) any later version. //
16 // //
17 // This program is distributed in the hope that it will be useful, //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20 // GNU General Public License for more details: //
21 // //
22 // http://www.gnu.org/copyleft/gpl.html //
23 // //
24 ///////////////////////////////////////////////////////////////////////////
26 /**
27 * Library of functions for web output
29 * Library of all general-purpose Moodle PHP functions and constants
30 * that produce HTML output
32 * Other main libraries:
33 * - datalib.php - functions that access the database.
34 * - moodlelib.php - general-purpose Moodle functions.
35 * @author Martin Dougiamas
36 * @version $Id$
37 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
38 * @package moodlecore
41 /// We are going to uses filterlib functions here
42 require_once("$CFG->libdir/filterlib.php");
44 require_once("$CFG->libdir/ajax/ajaxlib.php");
46 /// Constants
48 /// Define text formatting types ... eventually we can add Wiki, BBcode etc
50 /**
51 * Does all sorts of transformations and filtering
53 define('FORMAT_MOODLE', '0'); // Does all sorts of transformations and filtering
55 /**
56 * Plain HTML (with some tags stripped)
58 define('FORMAT_HTML', '1'); // Plain HTML (with some tags stripped)
60 /**
61 * Plain text (even tags are printed in full)
63 define('FORMAT_PLAIN', '2'); // Plain text (even tags are printed in full)
65 /**
66 * Wiki-formatted text
67 * Deprecated: left here just to note that '3' is not used (at the moment)
68 * and to catch any latent wiki-like text (which generates an error)
70 define('FORMAT_WIKI', '3'); // Wiki-formatted text
72 /**
73 * Markdown-formatted text http://daringfireball.net/projects/markdown/
75 define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/
77 /**
78 * TRUSTTEXT marker - if present in text, text cleaning should be bypassed
80 define('TRUSTTEXT', '#####TRUSTTEXT#####');
83 /**
84 * Allowed tags - string of html tags that can be tested against for safe html tags
85 * @global string $ALLOWED_TAGS
87 global $ALLOWED_TAGS;
88 $ALLOWED_TAGS =
89 '<p><br><b><i><u><font><table><tbody><span><div><tr><td><th><ol><ul><dl><li><dt><dd><h1><h2><h3><h4><h5><h6><hr><img><a><strong><emphasis><em><sup><sub><address><cite><blockquote><pre><strike><param><acronym><nolink><lang><tex><algebra><math><mi><mn><mo><mtext><mspace><ms><mrow><mfrac><msqrt><mroot><mstyle><merror><mpadded><mphantom><mfenced><msub><msup><msubsup><munder><mover><munderover><mmultiscripts><mtable><mtr><mtd><maligngroup><malignmark><maction><cn><ci><apply><reln><fn><interval><inverse><sep><condition><declare><lambda><compose><ident><quotient><exp><factorial><divide><max><min><minus><plus><power><rem><times><root><gcd><and><or><xor><not><implies><forall><exists><abs><conjugate><eq><neq><gt><lt><geq><leq><ln><log><int><diff><partialdiff><lowlimit><uplimit><bvar><degree><set><list><union><intersect><in><notin><subset><prsubset><notsubset><notprsubset><setdiff><sum><product><limit><tendsto><mean><sdev><variance><median><mode><moment><vector><matrix><matrixrow><determinant><transpose><selector><annotation><semantics><annotation-xml><tt><code>';
91 /**
92 * Allowed protocols - array of protocols that are safe to use in links and so on
93 * @global string $ALLOWED_PROTOCOLS
95 $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
96 'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style',
97 'border', 'margin', 'padding', 'background'); // CSS as well to get through kses
100 /// Functions
103 * Add quotes to HTML characters
105 * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
106 * This function is very similar to {@link p()}
108 * @param string $var the string potentially containing HTML characters
109 * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
110 * true should be used to print data from forms and false for data from DB.
111 * @return string
113 function s($var, $strip=false) {
115 if ($var == '0') { // for integer 0, boolean false, string '0'
116 return '0';
119 if ($strip) {
120 return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars(stripslashes_safe($var)));
121 } else {
122 return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars($var));
127 * Add quotes to HTML characters
129 * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
130 * This function is very similar to {@link s()}
132 * @param string $var the string potentially containing HTML characters
133 * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
134 * true should be used to print data from forms and false for data from DB.
135 * @return string
137 function p($var, $strip=false) {
138 echo s($var, $strip);
142 * Does proper javascript quoting.
143 * Do not use addslashes anymore, because it does not work when magic_quotes_sybase is enabled.
145 * @since 1.8 - 22/02/2007
146 * @param mixed value
147 * @return mixed quoted result
149 function addslashes_js($var) {
150 if (is_string($var)) {
151 $var = str_replace('\\', '\\\\', $var);
152 $var = str_replace(array('\'', '"', "\n", "\r", "\0"), array('\\\'', '\\"', '\\n', '\\r', '\\0'), $var);
153 $var = str_replace('</', '<\/', $var); // XHTML compliance
154 } else if (is_array($var)) {
155 $var = array_map('addslashes_js', $var);
156 } else if (is_object($var)) {
157 $a = get_object_vars($var);
158 foreach ($a as $key=>$value) {
159 $a[$key] = addslashes_js($value);
161 $var = (object)$a;
163 return $var;
167 * Remove query string from url
169 * Takes in a URL and returns it without the querystring portion
171 * @param string $url the url which may have a query string attached
172 * @return string
174 function strip_querystring($url) {
176 if ($commapos = strpos($url, '?')) {
177 return substr($url, 0, $commapos);
178 } else {
179 return $url;
184 * Returns the URL of the HTTP_REFERER, less the querystring portion if required
185 * @return string
187 function get_referer($stripquery=true) {
188 if (isset($_SERVER['HTTP_REFERER'])) {
189 if ($stripquery) {
190 return strip_querystring($_SERVER['HTTP_REFERER']);
191 } else {
192 return $_SERVER['HTTP_REFERER'];
194 } else {
195 return '';
201 * Returns the name of the current script, WITH the querystring portion.
202 * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
203 * return different things depending on a lot of things like your OS, Web
204 * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
205 * <b>NOTE:</b> This function returns false if the global variables needed are not set.
207 * @return string
209 function me() {
211 if (!empty($_SERVER['REQUEST_URI'])) {
212 return $_SERVER['REQUEST_URI'];
214 } else if (!empty($_SERVER['PHP_SELF'])) {
215 if (!empty($_SERVER['QUERY_STRING'])) {
216 return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
218 return $_SERVER['PHP_SELF'];
220 } else if (!empty($_SERVER['SCRIPT_NAME'])) {
221 if (!empty($_SERVER['QUERY_STRING'])) {
222 return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
224 return $_SERVER['SCRIPT_NAME'];
226 } else if (!empty($_SERVER['URL'])) { // May help IIS (not well tested)
227 if (!empty($_SERVER['QUERY_STRING'])) {
228 return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
230 return $_SERVER['URL'];
232 } else {
233 notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
234 return false;
239 * Like {@link me()} but returns a full URL
240 * @see me()
241 * @return string
243 function qualified_me() {
245 global $CFG;
247 if (!empty($CFG->wwwroot)) {
248 $url = parse_url($CFG->wwwroot);
251 if (!empty($url['host'])) {
252 $hostname = $url['host'];
253 } else if (!empty($_SERVER['SERVER_NAME'])) {
254 $hostname = $_SERVER['SERVER_NAME'];
255 } else if (!empty($_ENV['SERVER_NAME'])) {
256 $hostname = $_ENV['SERVER_NAME'];
257 } else if (!empty($_SERVER['HTTP_HOST'])) {
258 $hostname = $_SERVER['HTTP_HOST'];
259 } else if (!empty($_ENV['HTTP_HOST'])) {
260 $hostname = $_ENV['HTTP_HOST'];
261 } else {
262 notify('Warning: could not find the name of this server!');
263 return false;
266 if (!empty($url['port'])) {
267 $hostname .= ':'.$url['port'];
268 } else if (!empty($_SERVER['SERVER_PORT'])) {
269 if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
270 $hostname .= ':'.$_SERVER['SERVER_PORT'];
274 // TODO, this does not work in the situation described in MDL-11061, but
275 // I don't know how to fix it. Possibly believe $CFG->wwwroot ahead of what
276 // the server reports.
277 if (isset($_SERVER['HTTPS'])) {
278 $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
279 } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
280 $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
281 } else {
282 $protocol = 'http://';
285 $url_prefix = $protocol.$hostname;
286 return $url_prefix . me();
291 * Class for creating and manipulating urls.
293 * See short write up here http://docs.moodle.org/en/Development:lib/weblib.php_moodle_url
295 class moodle_url {
296 var $scheme = '';// e.g. http
297 var $host = '';
298 var $port = '';
299 var $user = '';
300 var $pass = '';
301 var $path = '';
302 var $fragment = '';
303 var $params = array(); //associative array of query string params
306 * Pass no arguments to create a url that refers to this page. Use empty string to create empty url.
308 * @param string $url url default null means use this page url with no query string
309 * empty string means empty url.
310 * if you pass any other type of url it will be parsed into it's bits, including query string
311 * @param array $params these params override anything in the query string where params have the same name.
313 function moodle_url($url = null, $params = array()){
314 global $FULLME;
315 if ($url !== ''){
316 if ($url === null){
317 $url = strip_querystring($FULLME);
319 $parts = parse_url($url);
320 if ($parts === FALSE){
321 error('invalidurl');
323 if (isset($parts['query'])){
324 parse_str(str_replace('&amp;', '&', $parts['query']), $this->params);
326 unset($parts['query']);
327 foreach ($parts as $key => $value){
328 $this->$key = $value;
330 $this->params($params);
334 * Add an array of params to the params for this page. The added params override existing ones if they
335 * have the same name.
337 * @param array $params
339 function params($params){
340 $this->params = $params + $this->params;
344 * Remove all params if no arguments passed. Or else remove param $arg1, $arg2, etc.
346 * @param string $arg1
347 * @param string $arg2
348 * @param string $arg3
350 function remove_params(){
351 if ($thisargs = func_get_args()){
352 foreach ($thisargs as $arg){
353 if (isset($this->params->$arg)){
354 unset($this->params->$arg);
357 } else { // no args
358 $this->params = array();
363 * Add a param to the params for this page. The added param overrides existing one if they
364 * have the same name.
366 * @param string $paramname name
367 * @param string $param value
369 function param($paramname, $param){
370 $this->params = array($paramname => $param) + $this->params;
374 function get_query_string($overrideparams = array()){
375 $arr = array();
376 $params = $overrideparams + $this->params;
377 foreach ($params as $key => $val){
378 $arr[] = urlencode($key)."=".urlencode($val);
380 return implode($arr, "&amp;");
383 * Outputs params as hidden form elements.
385 * @param array $exclude params to ignore
386 * @param integer $indent indentation
387 * @return string html for form elements.
389 function hidden_params_out($exclude = array(), $indent = 0){
390 $tabindent = str_repeat("\t", $indent);
391 $str = '';
392 foreach ($this->params as $key => $val){
393 if (FALSE === array_search($key, $exclude)) {
394 $val = s($val);
395 $str.= "$tabindent<input type=\"hidden\" name=\"$key\" value=\"$val\" />\n";
398 return $str;
401 * Output url
403 * @param boolean $noquerystring whether to output page params as a query string in the url.
404 * @param array $overrideparams params to add to the output url, these override existing ones with the same name.
405 * @return string url
407 function out($noquerystring = false, $overrideparams = array()) {
408 $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): '';
409 $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':'';
410 $uri .= $this->host ? $this->host : '';
411 $uri .= $this->port ? ':'.$this->port : '';
412 $uri .= $this->path ? $this->path : '';
413 if (!$noquerystring){
414 $uri .= (count($this->params)||count($overrideparams)) ? '?'.$this->get_query_string($overrideparams) : '';
416 $uri .= $this->fragment ? '#'.$this->fragment : '';
417 return $uri;
420 * Output action url with sesskey
422 * @param boolean $noquerystring whether to output page params as a query string in the url.
423 * @return string url
425 function out_action($overrideparams = array()) {
426 $overrideparams = array('sesskey'=> sesskey()) + $overrideparams;
427 return $this->out(false, $overrideparams);
432 * Determine if there is data waiting to be processed from a form
434 * Used on most forms in Moodle to check for data
435 * Returns the data as an object, if it's found.
436 * This object can be used in foreach loops without
437 * casting because it's cast to (array) automatically
439 * Checks that submitted POST data exists and returns it as object.
441 * @param string $url not used anymore
442 * @return mixed false or object
444 function data_submitted($url='') {
446 if (empty($_POST)) {
447 return false;
448 } else {
449 return (object)$_POST;
454 * Moodle replacement for php stripslashes() function,
455 * works also for objects and arrays.
457 * The standard php stripslashes() removes ALL backslashes
458 * even from strings - so C:\temp becomes C:temp - this isn't good.
459 * This function should work as a fairly safe replacement
460 * to be called on quoted AND unquoted strings (to be sure)
462 * @param mixed something to remove unsafe slashes from
463 * @return mixed
465 function stripslashes_safe($mixed) {
466 // there is no need to remove slashes from int, float and bool types
467 if (empty($mixed)) {
468 //nothing to do...
469 } else if (is_string($mixed)) {
470 if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
471 $mixed = str_replace("''", "'", $mixed);
472 } else { //the rest, simple and double quotes and backslashes
473 $mixed = str_replace("\\'", "'", $mixed);
474 $mixed = str_replace('\\"', '"', $mixed);
475 $mixed = str_replace('\\\\', '\\', $mixed);
477 } else if (is_array($mixed)) {
478 foreach ($mixed as $key => $value) {
479 $mixed[$key] = stripslashes_safe($value);
481 } else if (is_object($mixed)) {
482 $vars = get_object_vars($mixed);
483 foreach ($vars as $key => $value) {
484 $mixed->$key = stripslashes_safe($value);
488 return $mixed;
492 * Recursive implementation of stripslashes()
494 * This function will allow you to strip the slashes from a variable.
495 * If the variable is an array or object, slashes will be stripped
496 * from the items (or properties) it contains, even if they are arrays
497 * or objects themselves.
499 * @param mixed the variable to remove slashes from
500 * @return mixed
502 function stripslashes_recursive($var) {
503 if (is_object($var)) {
504 $new_var = new object();
505 $properties = get_object_vars($var);
506 foreach($properties as $property => $value) {
507 $new_var->$property = stripslashes_recursive($value);
510 } else if(is_array($var)) {
511 $new_var = array();
512 foreach($var as $property => $value) {
513 $new_var[$property] = stripslashes_recursive($value);
516 } else if(is_string($var)) {
517 $new_var = stripslashes($var);
519 } else {
520 $new_var = $var;
523 return $new_var;
527 * Recursive implementation of addslashes()
529 * This function will allow you to add the slashes from a variable.
530 * If the variable is an array or object, slashes will be added
531 * to the items (or properties) it contains, even if they are arrays
532 * or objects themselves.
534 * @param mixed the variable to add slashes from
535 * @return mixed
537 function addslashes_recursive($var) {
538 if (is_object($var)) {
539 $new_var = new object();
540 $properties = get_object_vars($var);
541 foreach($properties as $property => $value) {
542 $new_var->$property = addslashes_recursive($value);
545 } else if (is_array($var)) {
546 $new_var = array();
547 foreach($var as $property => $value) {
548 $new_var[$property] = addslashes_recursive($value);
551 } else if (is_string($var)) {
552 $new_var = addslashes($var);
554 } else { // nulls, integers, etc.
555 $new_var = $var;
558 return $new_var;
562 * Given some normal text this function will break up any
563 * long words to a given size by inserting the given character
565 * It's multibyte savvy and doesn't change anything inside html tags.
567 * @param string $string the string to be modified
568 * @param int $maxsize maximum length of the string to be returned
569 * @param string $cutchar the string used to represent word breaks
570 * @return string
572 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
574 /// Loading the textlib singleton instance. We are going to need it.
575 $textlib = textlib_get_instance();
577 /// First of all, save all the tags inside the text to skip them
578 $tags = array();
579 filter_save_tags($string,$tags);
581 /// Process the string adding the cut when necessary
582 $output = '';
583 $length = $textlib->strlen($string);
584 $wordlength = 0;
586 for ($i=0; $i<$length; $i++) {
587 $char = $textlib->substr($string, $i, 1);
588 if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
589 $wordlength = 0;
590 } else {
591 $wordlength++;
592 if ($wordlength > $maxsize) {
593 $output .= $cutchar;
594 $wordlength = 0;
597 $output .= $char;
600 /// Finally load the tags back again
601 if (!empty($tags)) {
602 $output = str_replace(array_keys($tags), $tags, $output);
605 return $output;
609 * This does a search and replace, ignoring case
610 * This function is only used for versions of PHP older than version 5
611 * which do not have a native version of this function.
612 * Taken from the PHP manual, by bradhuizenga @ softhome.net
614 * @param string $find the string to search for
615 * @param string $replace the string to replace $find with
616 * @param string $string the string to search through
617 * return string
619 if (!function_exists('str_ireplace')) { /// Only exists in PHP 5
620 function str_ireplace($find, $replace, $string) {
622 if (!is_array($find)) {
623 $find = array($find);
626 if(!is_array($replace)) {
627 if (!is_array($find)) {
628 $replace = array($replace);
629 } else {
630 // this will duplicate the string into an array the size of $find
631 $c = count($find);
632 $rString = $replace;
633 unset($replace);
634 for ($i = 0; $i < $c; $i++) {
635 $replace[$i] = $rString;
640 foreach ($find as $fKey => $fItem) {
641 $between = explode(strtolower($fItem),strtolower($string));
642 $pos = 0;
643 foreach($between as $bKey => $bItem) {
644 $between[$bKey] = substr($string,$pos,strlen($bItem));
645 $pos += strlen($bItem) + strlen($fItem);
647 $string = implode($replace[$fKey],$between);
649 return ($string);
654 * Locate the position of a string in another string
656 * This function is only used for versions of PHP older than version 5
657 * which do not have a native version of this function.
658 * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
660 * @param string $haystack The string to be searched
661 * @param string $needle The string to search for
662 * @param int $offset The position in $haystack where the search should begin.
664 if (!function_exists('stripos')) { /// Only exists in PHP 5
665 function stripos($haystack, $needle, $offset=0) {
667 return strpos(strtoupper($haystack), strtoupper($needle), $offset);
672 * This function will create a HTML link that will work on both
673 * Javascript and non-javascript browsers.
674 * Relies on the Javascript function openpopup in javascript.php
676 * $url must be relative to home page eg /mod/survey/stuff.php
677 * @param string $url Web link relative to home page
678 * @param string $name Name to be assigned to the popup window
679 * @param string $linkname Text to be displayed as web link
680 * @param int $height Height to assign to popup window
681 * @param int $width Height to assign to popup window
682 * @param string $title Text to be displayed as popup page title
683 * @param string $options List of additional options for popup window
684 * @todo Add code examples and list of some options that might be used.
685 * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
686 * @return string
687 * @uses $CFG
689 function link_to_popup_window ($url, $name='popup', $linkname='click here',
690 $height=400, $width=500, $title='Popup window',
691 $options='none', $return=false) {
693 global $CFG;
695 if ($options == 'none') {
696 $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
698 $fullscreen = 0;
700 if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
701 $url = substr($url, strlen($CFG->wwwroot));
704 $link = '<a title="'. s(strip_tags($title)) .'" href="'. $CFG->wwwroot . $url .'" '.
705 "onclick=\"this.target='$name'; return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
706 if ($return) {
707 return $link;
708 } else {
709 echo $link;
714 * This function will print a button submit form element
715 * that will work on both Javascript and non-javascript browsers.
716 * Relies on the Javascript function openpopup in javascript.php
718 * $url must be relative to home page eg /mod/survey/stuff.php
719 * @param string $url Web link relative to home page
720 * @param string $name Name to be assigned to the popup window
721 * @param string $linkname Text to be displayed as web link
722 * @param int $height Height to assign to popup window
723 * @param int $width Height to assign to popup window
724 * @param string $title Text to be displayed as popup page title
725 * @param string $options List of additional options for popup window
726 * @param string $return If true, return as a string, otherwise print
727 * @return string
728 * @uses $CFG
730 function button_to_popup_window ($url, $name='popup', $linkname='click here',
731 $height=400, $width=500, $title='Popup window', $options='none', $return=false,
732 $id='', $class='') {
734 global $CFG;
736 if ($options == 'none') {
737 $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
740 if ($id) {
741 $id = ' id="'.$id.'" ';
743 if ($class) {
744 $class = ' class="'.$class.'" ';
746 $fullscreen = 0;
748 $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
749 "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
750 if ($return) {
751 return $button;
752 } else {
753 echo $button;
759 * Prints a simple button to close a window
761 function close_window_button($name='closewindow', $return=false) {
762 global $CFG;
764 $output = '';
766 $output .= '<div class="closewindow">' . "\n";
767 $output .= '<form action="'.$CFG->wwwroot.'"><div>'; // We don't use this
768 $output .= '<input type="button" onclick="self.close();" value="'.get_string($name).'" />';
769 $output .= '</div></form>';
770 $output .= '</div>' . "\n";
772 if ($return) {
773 return $output;
774 } else {
775 echo $output;
780 * Try and close the current window immediately using Javascript
782 function close_window($delay=0) {
784 <script type="text/javascript">
785 //<![CDATA[
786 function close_this_window() {
787 self.close();
789 setTimeout("close_this_window()", <?php echo $delay * 1000 ?>);
790 //]]>
791 </script>
792 <noscript><center>
793 <?php print_string('pleaseclose') ?>
794 </center></noscript>
795 <?php
796 die;
801 * Given an array of values, output the HTML for a select element with those options.
802 * Normally, you only need to use the first few parameters.
804 * @param array $options The options to offer. An array of the form
805 * $options[{value}] = {text displayed for that option};
806 * @param string $name the name of this form control, as in &lt;select name="..." ...
807 * @param string $selected the option to select initially, default none.
808 * @param string $nothing The label for the 'nothing is selected' option. Defaults to get_string('choose').
809 * Set this to '' if you don't want a 'nothing is selected' option.
810 * @param string $script in not '', then this is added to the &lt;select> element as an onchange handler.
811 * @param string $nothingvalue The value corresponding to the $nothing option. Defaults to 0.
812 * @param boolean $return if false (the default) the the output is printed directly, If true, the
813 * generated HTML is returned as a string.
814 * @param boolean $disabled if true, the select is generated in a disabled state. Default, false.
815 * @param int $tabindex if give, sets the tabindex attribute on the &lt;select> element. Default none.
816 * @param string $id value to use for the id attribute of the &lt;select> element. If none is given,
817 * then a suitable one is constructed.
819 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
820 $nothingvalue='0', $return=false, $disabled=false, $tabindex=0, $id='') {
822 if ($nothing == 'choose') {
823 $nothing = get_string('choose') .'...';
826 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
827 if ($disabled) {
828 $attributes .= ' disabled="disabled"';
831 if ($tabindex) {
832 $attributes .= ' tabindex="'.$tabindex.'"';
835 if ($id ==='') {
836 $id = 'menu'.$name;
837 // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
838 $id = str_replace('[', '', $id);
839 $id = str_replace(']', '', $id);
842 $output = '<select id="'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
843 if ($nothing) {
844 $output .= ' <option value="'. s($nothingvalue) .'"'. "\n";
845 if ($nothingvalue === $selected) {
846 $output .= ' selected="selected"';
848 $output .= '>'. $nothing .'</option>' . "\n";
850 if (!empty($options)) {
851 foreach ($options as $value => $label) {
852 $output .= ' <option value="'. s($value) .'"';
853 if ((string)$value == (string)$selected) {
854 $output .= ' selected="selected"';
856 if ($label === '') {
857 $output .= '>'. $value .'</option>' . "\n";
858 } else {
859 $output .= '>'. $label .'</option>' . "\n";
863 $output .= '</select>' . "\n";
865 if ($return) {
866 return $output;
867 } else {
868 echo $output;
873 * Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
874 * Other options like choose_from_menu.
876 function choose_from_menu_yesno($name, $selected, $script = '',
877 $return = false, $disabled = false, $tabindex = 0) {
878 return choose_from_menu(array(get_string('no'), get_string('yes')), $name,
879 $selected, '', $script, '0', $return, $disabled, $tabindex);
883 * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
884 * including option headings with the first level.
886 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
887 $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
889 if ($nothing == 'choose') {
890 $nothing = get_string('choose') .'...';
893 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
894 if ($disabled) {
895 $attributes .= ' disabled="disabled"';
898 if ($tabindex) {
899 $attributes .= ' tabindex="'.$tabindex.'"';
902 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
903 if ($nothing) {
904 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
905 if ($nothingvalue === $selected) {
906 $output .= ' selected="selected"';
908 $output .= '>'. $nothing .'</option>' . "\n";
910 if (!empty($options)) {
911 foreach ($options as $section => $values) {
913 $output .= ' <optgroup label="'. s(format_string($section)) .'">'."\n";
914 foreach ($values as $value => $label) {
915 $output .= ' <option value="'. format_string($value) .'"';
916 if ((string)$value == (string)$selected) {
917 $output .= ' selected="selected"';
919 if ($label === '') {
920 $output .= '>'. $value .'</option>' . "\n";
921 } else {
922 $output .= '>'. $label .'</option>' . "\n";
925 $output .= ' </optgroup>'."\n";
928 $output .= '</select>' . "\n";
930 if ($return) {
931 return $output;
932 } else {
933 echo $output;
939 * Given an array of values, creates a group of radio buttons to be part of a form
941 * @param array $options An array of value-label pairs for the radio group (values as keys)
942 * @param string $name Name of the radiogroup (unique in the form)
943 * @param string $checked The value that is already checked
945 function choose_from_radio ($options, $name, $checked='', $return=false) {
947 static $idcounter = 0;
949 if (!$name) {
950 $name = 'unnamed';
953 $output = '<span class="radiogroup '.$name."\">\n";
955 if (!empty($options)) {
956 $currentradio = 0;
957 foreach ($options as $value => $label) {
958 $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
959 $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
960 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
961 if ($value == $checked) {
962 $output .= ' checked="checked"';
964 if ($label === '') {
965 $output .= ' /> <label for="'.$htmlid.'">'. $value .'</label></span>' . "\n";
966 } else {
967 $output .= ' /> <label for="'.$htmlid.'">'. $label .'</label></span>' . "\n";
969 $currentradio = ($currentradio + 1) % 2;
973 $output .= '</span>' . "\n";
975 if ($return) {
976 return $output;
977 } else {
978 echo $output;
982 /** Display an standard html checkbox with an optional label
984 * @param string $name The name of the checkbox
985 * @param string $value The valus that the checkbox will pass when checked
986 * @param boolean $checked The flag to tell the checkbox initial state
987 * @param string $label The label to be showed near the checkbox
988 * @param string $alt The info to be inserted in the alt tag
990 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
992 static $idcounter = 0;
994 if (!$name) {
995 $name = 'unnamed';
998 if ($alt) {
999 $alt = strip_tags($alt);
1000 } else {
1001 $alt = 'checkbox';
1004 if ($checked) {
1005 $strchecked = ' checked="checked"';
1006 } else {
1007 $strchecked = '';
1010 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
1011 $output = '<span class="checkbox '.$name."\">";
1012 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onclick="'.$script.'" ' : '').' />';
1013 if(!empty($label)) {
1014 $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
1016 $output .= '</span>'."\n";
1018 if (empty($return)) {
1019 echo $output;
1020 } else {
1021 return $output;
1026 /** Display an standard html text field with an optional label
1028 * @param string $name The name of the text field
1029 * @param string $value The value of the text field
1030 * @param string $label The label to be showed near the text field
1031 * @param string $alt The info to be inserted in the alt tag
1033 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) {
1035 static $idcounter = 0;
1037 if (empty($name)) {
1038 $name = 'unnamed';
1041 if (empty($alt)) {
1042 $alt = 'textfield';
1045 if (!empty($maxlength)) {
1046 $maxlength = ' maxlength="'.$maxlength.'" ';
1049 $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
1050 $output = '<span class="textfield '.$name."\">";
1051 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
1053 $output .= '</span>'."\n";
1055 if (empty($return)) {
1056 echo $output;
1057 } else {
1058 return $output;
1065 * Implements a complete little popup form
1067 * @uses $CFG
1068 * @param string $common The URL up to the point of the variable that changes
1069 * @param array $options Alist of value-label pairs for the popup list
1070 * @param string $formid Id must be unique on the page (originaly $formname)
1071 * @param string $selected The option that is already selected
1072 * @param string $nothing The label for the "no choice" option
1073 * @param string $help The name of a help page if help is required
1074 * @param string $helptext The name of the label for the help button
1075 * @param boolean $return Indicates whether the function should return the text
1076 * as a string or echo it directly to the page being rendered
1077 * @param string $targetwindow The name of the target page to open the linked page in.
1078 * @param string $selectlabel Text to place in a [label] element - preferred for accessibility.
1079 * @param array $optionsextra TODO, an array?
1080 * @return string If $return is true then the entire form is returned as a string.
1081 * @todo Finish documenting this function<br>
1083 function popup_form($common, $options, $formid, $selected='', $nothing='choose', $help='', $helptext='', $return=false,
1084 $targetwindow='self', $selectlabel='', $optionsextra=NULL) {
1086 global $CFG;
1087 static $go, $choose; /// Locally cached, in case there's lots on a page
1089 if (empty($options)) {
1090 return '';
1093 if (!isset($go)) {
1094 $go = get_string('go');
1097 if ($nothing == 'choose') {
1098 if (!isset($choose)) {
1099 $choose = get_string('choose');
1101 $nothing = $choose.'...';
1104 // changed reference to document.getElementById('id_abc') instead of document.abc
1105 // MDL-7861
1106 $output = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
1107 ' method="get" '.
1108 $CFG->frametarget.
1109 ' id="'.$formid.'"'.
1110 ' class="popupform">';
1111 if ($help) {
1112 $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
1113 } else {
1114 $button = '';
1117 if ($selectlabel) {
1118 $selectlabel = '<label for="'.$formid.'_jump">'.$selectlabel.'</label>';
1121 $output .= '<div>'.$selectlabel.$button.'<select id="'.$formid.'_jump" name="jump" onchange="'.$targetwindow.'.location=document.getElementById(\''.$formid.'\').jump.options[document.getElementById(\''.$formid.'\').jump.selectedIndex].value;">'."\n";
1123 if ($nothing != '') {
1124 $output .= " <option value=\"javascript:void(0)\">$nothing</option>\n";
1127 $inoptgroup = false;
1129 foreach ($options as $value => $label) {
1131 if ($label == '--') { /// we are ending previous optgroup
1132 /// Check to see if we already have a valid open optgroup
1133 /// XHTML demands that there be at least 1 option within an optgroup
1134 if ($inoptgroup and (count($optgr) > 1) ) {
1135 $output .= implode('', $optgr);
1136 $output .= ' </optgroup>';
1138 $optgr = array();
1139 $inoptgroup = false;
1140 continue;
1141 } else if (substr($label,0,2) == '--') { /// we are starting a new optgroup
1143 /// Check to see if we already have a valid open optgroup
1144 /// XHTML demands that there be at least 1 option within an optgroup
1145 if ($inoptgroup and (count($optgr) > 1) ) {
1146 $output .= implode('', $optgr);
1147 $output .= ' </optgroup>';
1150 unset($optgr);
1151 $optgr = array();
1153 $optgr[] = ' <optgroup label="'. s(format_string(substr($label,2))) .'">'; // Plain labels
1155 $inoptgroup = true; /// everything following will be in an optgroup
1156 continue;
1158 } else {
1159 if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
1161 $url=sid_process_url( $common . $value );
1162 } else
1164 $url=$common . $value;
1166 $optstr = ' <option value="' . $url . '"';
1168 if ($value == $selected) {
1169 $optstr .= ' selected="selected"';
1172 if (!empty($optionsextra[$value])) {
1173 $optstr .= ' '.$optionsextra[$value];
1176 if ($label) {
1177 $optstr .= '>'. $label .'</option>' . "\n";
1178 } else {
1179 $optstr .= '>'. $value .'</option>' . "\n";
1182 if ($inoptgroup) {
1183 $optgr[] = $optstr;
1184 } else {
1185 $output .= $optstr;
1191 /// catch the final group if not closed
1192 if ($inoptgroup and count($optgr) > 1) {
1193 $output .= implode('', $optgr);
1194 $output .= ' </optgroup>';
1197 $output .= '</select>';
1198 $output .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
1199 $output .= '<div id="noscript'.$formid.'" style="display: inline;">';
1200 $output .= '<input type="submit" value="'.$go.'" /></div>';
1201 $output .= '<script type="text/javascript">'.
1202 "\n//<![CDATA[\n".
1203 'document.getElementById("noscript'.$formid.'").style.display = "none";'.
1204 "\n//]]>\n".'</script>';
1205 $output .= '</div>';
1206 $output .= '</form>';
1208 if ($return) {
1209 return $output;
1210 } else {
1211 echo $output;
1217 * Prints some red text
1219 * @param string $error The text to be displayed in red
1221 function formerr($error) {
1223 if (!empty($error)) {
1224 echo '<span class="error">'. $error .'</span>';
1229 * Validates an email to make sure it makes sense.
1231 * @param string $address The email address to validate.
1232 * @return boolean
1234 function validate_email($address) {
1236 return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
1237 '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
1238 '@'.
1239 '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1240 '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1241 $address));
1245 * Extracts file argument either from file parameter or PATH_INFO
1247 * @param string $scriptname name of the calling script
1248 * @return string file path (only safe characters)
1250 function get_file_argument($scriptname) {
1251 global $_SERVER;
1253 $relativepath = FALSE;
1255 // first try normal parameter (compatible method == no relative links!)
1256 $relativepath = optional_param('file', FALSE, PARAM_PATH);
1257 if ($relativepath === '/testslasharguments') {
1258 echo 'test -1 : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
1259 die;
1262 // then try extract file from PATH_INFO (slasharguments method)
1263 if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1264 $path_info = $_SERVER['PATH_INFO'];
1265 // check that PATH_INFO works == must not contain the script name
1266 if (!strpos($path_info, $scriptname)) {
1267 $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1268 if ($relativepath === '/testslasharguments') {
1269 echo 'test 1 : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
1270 die;
1275 // now if both fail try the old way
1276 // (for compatibility with misconfigured or older buggy php implementations)
1277 if (!$relativepath) {
1278 $arr = explode($scriptname, me());
1279 if (!empty($arr[1])) {
1280 $path_info = strip_querystring($arr[1]);
1281 $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1282 if ($relativepath === '/testslasharguments') {
1283 echo 'test 2 : Slasharguments test passed (compatibility hack). Server confguration may be compatible with file.php/1/pic.jpg slashargument setting'; //indicate ok for health center
1284 die;
1289 return $relativepath;
1293 * Searches the current environment variables for some slash arguments
1295 * @param string $file ?
1296 * @todo Finish documenting this function
1298 function get_slash_arguments($file='file.php') {
1300 if (!$string = me()) {
1301 return false;
1304 $pathinfo = explode($file, $string);
1306 if (!empty($pathinfo[1])) {
1307 return addslashes($pathinfo[1]);
1308 } else {
1309 return false;
1314 * Extracts arguments from "/foo/bar/something"
1315 * eg http://mysite.com/script.php/foo/bar/something
1317 * @param string $string ?
1318 * @param int $i ?
1319 * @return array|string
1320 * @todo Finish documenting this function
1322 function parse_slash_arguments($string, $i=0) {
1324 if (detect_munged_arguments($string)) {
1325 return false;
1327 $args = explode('/', $string);
1329 if ($i) { // return just the required argument
1330 return $args[$i];
1332 } else { // return the whole array
1333 array_shift($args); // get rid of the empty first one
1334 return $args;
1339 * Just returns an array of text formats suitable for a popup menu
1341 * @uses FORMAT_MOODLE
1342 * @uses FORMAT_HTML
1343 * @uses FORMAT_PLAIN
1344 * @uses FORMAT_MARKDOWN
1345 * @return array
1347 function format_text_menu() {
1349 return array (FORMAT_MOODLE => get_string('formattext'),
1350 FORMAT_HTML => get_string('formathtml'),
1351 FORMAT_PLAIN => get_string('formatplain'),
1352 FORMAT_MARKDOWN => get_string('formatmarkdown'));
1356 * Given text in a variety of format codings, this function returns
1357 * the text as safe HTML.
1359 * @uses $CFG
1360 * @uses FORMAT_MOODLE
1361 * @uses FORMAT_HTML
1362 * @uses FORMAT_PLAIN
1363 * @uses FORMAT_WIKI
1364 * @uses FORMAT_MARKDOWN
1365 * @param string $text The text to be formatted. This is raw text originally from user input.
1366 * @param int $format Identifier of the text format to be used
1367 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1368 * @param array $options ?
1369 * @param int $courseid ?
1370 * @return string
1371 * @todo Finish documenting this function
1373 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
1375 global $CFG, $COURSE;
1377 if ($text === '') {
1378 return ''; // no need to do any filters and cleaning
1381 if (!isset($options->trusttext)) {
1382 $options->trusttext = false;
1385 if (!isset($options->noclean)) {
1386 $options->noclean=false;
1388 if (!isset($options->nocache)) {
1389 $options->nocache=false;
1391 if (!isset($options->smiley)) {
1392 $options->smiley=true;
1394 if (!isset($options->filter)) {
1395 $options->filter=true;
1397 if (!isset($options->para)) {
1398 $options->para=true;
1400 if (!isset($options->newlines)) {
1401 $options->newlines=true;
1404 if (empty($courseid)) {
1405 $courseid = $COURSE->id;
1408 if (!empty($CFG->cachetext) and empty($options->nocache)) {
1409 $time = time() - $CFG->cachetext;
1410 $md5key = md5($text.'-'.(int)$courseid.'-'.current_language().'-'.(int)$format.(int)$options->trusttext.(int)$options->noclean.(int)$options->smiley.(int)$options->filter.(int)$options->para.(int)$options->newlines);
1411 if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
1412 if ($oldcacheitem->timemodified >= $time) {
1413 return $oldcacheitem->formattedtext;
1418 // trusttext overrides the noclean option!
1419 if ($options->trusttext) {
1420 if (trusttext_present($text)) {
1421 $text = trusttext_strip($text);
1422 if (!empty($CFG->enabletrusttext)) {
1423 $options->noclean = true;
1424 } else {
1425 $options->noclean = false;
1427 } else {
1428 $options->noclean = false;
1430 } else if (!debugging('', DEBUG_DEVELOPER)) {
1431 // strip any forgotten trusttext in non-developer mode
1432 // do not forget to disable text cache when debugging trusttext!!
1433 $text = trusttext_strip($text);
1436 $CFG->currenttextiscacheable = true; // Default status - can be changed by any filter
1438 switch ($format) {
1439 case FORMAT_HTML:
1440 if ($options->smiley) {
1441 replace_smilies($text);
1443 if (!$options->noclean) {
1444 $text = clean_text($text, FORMAT_HTML);
1446 if ($options->filter) {
1447 $text = filter_text($text, $courseid);
1449 break;
1451 case FORMAT_PLAIN:
1452 $text = s($text); // cleans dangerous JS
1453 $text = rebuildnolinktag($text);
1454 $text = str_replace(' ', '&nbsp; ', $text);
1455 $text = nl2br($text);
1456 break;
1458 case FORMAT_WIKI:
1459 // this format is deprecated
1460 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
1461 this message as all texts should have been converted to Markdown format instead.
1462 Please post a bug report to http://moodle.org/bugs with information about where you
1463 saw this message.</p>'.s($text);
1464 break;
1466 case FORMAT_MARKDOWN:
1467 $text = markdown_to_html($text);
1468 if ($options->smiley) {
1469 replace_smilies($text);
1471 if (!$options->noclean) {
1472 $text = clean_text($text, FORMAT_HTML);
1475 if ($options->filter) {
1476 $text = filter_text($text, $courseid);
1478 break;
1480 default: // FORMAT_MOODLE or anything else
1481 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
1482 if (!$options->noclean) {
1483 $text = clean_text($text, FORMAT_HTML);
1486 if ($options->filter) {
1487 $text = filter_text($text, $courseid);
1489 break;
1492 if (empty($options->nocache) and !empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1493 $newcacheitem = new object();
1494 $newcacheitem->md5key = $md5key;
1495 $newcacheitem->formattedtext = addslashes($text);
1496 $newcacheitem->timemodified = time();
1497 if ($oldcacheitem) { // See bug 4677 for discussion
1498 $newcacheitem->id = $oldcacheitem->id;
1499 @update_record('cache_text', $newcacheitem); // Update existing record in the cache table
1500 // It's unlikely that the cron cache cleaner could have
1501 // deleted this entry in the meantime, as it allows
1502 // some extra time to cover these cases.
1503 } else {
1504 @insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table
1505 // Again, it's possible that another user has caused this
1506 // record to be created already in the time that it took
1507 // to traverse this function. That's OK too, as the
1508 // call above handles duplicate entries, and eventually
1509 // the cron cleaner will delete them.
1513 return $text;
1516 /** Converts the text format from the value to the 'internal'
1517 * name or vice versa. $key can either be the value or the name
1518 * and you get the other back.
1520 * @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1521 * @return mixed as above but the other way around!
1523 function text_format_name( $key ) {
1524 $lookup = array();
1525 $lookup[FORMAT_MOODLE] = 'moodle';
1526 $lookup[FORMAT_HTML] = 'html';
1527 $lookup[FORMAT_PLAIN] = 'plain';
1528 $lookup[FORMAT_MARKDOWN] = 'markdown';
1529 $value = "error";
1530 if (!is_numeric($key)) {
1531 $key = strtolower( $key );
1532 $value = array_search( $key, $lookup );
1534 else {
1535 if (isset( $lookup[$key] )) {
1536 $value = $lookup[ $key ];
1539 return $value;
1543 /** Given a simple string, this function returns the string
1544 * processed by enabled filters if $CFG->filterall is enabled
1546 * @param string $string The string to be filtered.
1547 * @param boolean $striplinks To strip any link in the result text (Moodle 1.8 default changed from false to true! MDL-8713)
1548 * @param int $courseid Current course as filters can, potentially, use it
1549 * @return string
1551 function format_string ($string, $striplinks=true, $courseid=NULL ) {
1553 global $CFG, $COURSE;
1555 //We'll use a in-memory cache here to speed up repeated strings
1556 static $strcache = false;
1558 if ($strcache === false or count($strcache) > 2000 ) { // this number might need some tuning to limit memory usage in cron
1559 $strcache = array();
1562 //init course id
1563 if (empty($courseid)) {
1564 $courseid = $COURSE->id;
1567 //Calculate md5
1568 $md5 = md5($string.'<+>'.$striplinks.'<+>'.$courseid.'<+>'.current_language());
1570 //Fetch from cache if possible
1571 if (isset($strcache[$md5])) {
1572 return $strcache[$md5];
1575 // First replace all ampersands not followed by html entity code
1576 $string = preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&amp;", $string);
1578 if (!empty($CFG->filterall)) {
1579 $string = filter_string($string, $courseid);
1582 // If the site requires it, strip ALL tags from this string
1583 if (!empty($CFG->formatstringstriptags)) {
1584 $string = strip_tags($string);
1586 // Otherwise strip just links if that is required (default)
1587 } else if ($striplinks) { //strip links in string
1588 $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1591 //Store to cache
1592 $strcache[$md5] = $string;
1594 return $string;
1598 * Given text in a variety of format codings, this function returns
1599 * the text as plain text suitable for plain email.
1601 * @uses FORMAT_MOODLE
1602 * @uses FORMAT_HTML
1603 * @uses FORMAT_PLAIN
1604 * @uses FORMAT_WIKI
1605 * @uses FORMAT_MARKDOWN
1606 * @param string $text The text to be formatted. This is raw text originally from user input.
1607 * @param int $format Identifier of the text format to be used
1608 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1609 * @return string
1611 function format_text_email($text, $format) {
1613 switch ($format) {
1615 case FORMAT_PLAIN:
1616 return $text;
1617 break;
1619 case FORMAT_WIKI:
1620 $text = wiki_to_html($text);
1621 /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1622 /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
1623 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1624 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1625 break;
1627 case FORMAT_HTML:
1628 return html_to_text($text);
1629 break;
1631 case FORMAT_MOODLE:
1632 case FORMAT_MARKDOWN:
1633 default:
1634 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1635 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1636 break;
1641 * Given some text in HTML format, this function will pass it
1642 * through any filters that have been defined in $CFG->textfilterx
1643 * The variable defines a filepath to a file containing the
1644 * filter function. The file must contain a variable called
1645 * $textfilter_function which contains the name of the function
1646 * with $courseid and $text parameters
1648 * @param string $text The text to be passed through format filters
1649 * @param int $courseid ?
1650 * @return string
1651 * @todo Finish documenting this function
1653 function filter_text($text, $courseid=NULL) {
1654 global $CFG, $COURSE;
1656 if (empty($courseid)) {
1657 $courseid = $COURSE->id; // (copied from format_text)
1660 if (!empty($CFG->textfilters)) {
1661 require_once($CFG->libdir.'/filterlib.php');
1662 $textfilters = explode(',', $CFG->textfilters);
1663 foreach ($textfilters as $textfilter) {
1664 if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1665 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
1666 $functionname = basename($textfilter).'_filter';
1667 if (function_exists($functionname)) {
1668 $text = $functionname($courseid, $text);
1674 /// <nolink> tags removed for XHTML compatibility
1675 $text = str_replace('<nolink>', '', $text);
1676 $text = str_replace('</nolink>', '', $text);
1678 return $text;
1683 * Given a string (short text) in HTML format, this function will pass it
1684 * through any filters that have been defined in $CFG->stringfilters
1685 * The variable defines a filepath to a file containing the
1686 * filter function. The file must contain a variable called
1687 * $textfilter_function which contains the name of the function
1688 * with $courseid and $text parameters
1690 * @param string $string The text to be passed through format filters
1691 * @param int $courseid The id of a course
1692 * @return string
1694 function filter_string($string, $courseid=NULL) {
1695 global $CFG, $COURSE;
1697 if (empty($CFG->textfilters)) { // All filters are disabled anyway so quit
1698 return $string;
1701 if (empty($courseid)) {
1702 $courseid = $COURSE->id;
1705 require_once($CFG->libdir.'/filterlib.php');
1707 if (isset($CFG->stringfilters)) { // We have a predefined list to use, great!
1708 if (empty($CFG->stringfilters)) { // but it's blank, so finish now
1709 return $string;
1711 $stringfilters = explode(',', $CFG->stringfilters); // ..use the list we have
1713 } else { // Otherwise try to derive a list from textfilters
1714 if (strpos($CFG->textfilters, 'filter/multilang') !== false) { // Multilang is here
1715 $stringfilters = array('filter/multilang'); // Let's use just that
1716 $CFG->stringfilters = 'filter/multilang'; // Save it for next time through
1717 } else {
1718 $CFG->stringfilters = ''; // Save the result and return
1719 return $string;
1724 foreach ($stringfilters as $stringfilter) {
1725 if (is_readable($CFG->dirroot .'/'. $stringfilter .'/filter.php')) {
1726 include_once($CFG->dirroot .'/'. $stringfilter .'/filter.php');
1727 $functionname = basename($stringfilter).'_filter';
1728 if (function_exists($functionname)) {
1729 $string = $functionname($courseid, $string);
1734 /// <nolink> tags removed for XHTML compatibility
1735 $string = str_replace('<nolink>', '', $string);
1736 $string = str_replace('</nolink>', '', $string);
1738 return $string;
1742 * Is the text marked as trusted?
1744 * @param string $text text to be searched for TRUSTTEXT marker
1745 * @return boolean
1747 function trusttext_present($text) {
1748 if (strpos($text, TRUSTTEXT) !== FALSE) {
1749 return true;
1750 } else {
1751 return false;
1756 * This funtion MUST be called before the cleaning or any other
1757 * function that modifies the data! We do not know the origin of trusttext
1758 * in database, if it gets there in tweaked form we must not convert it
1759 * to supported form!!!
1761 * Please be carefull not to use stripslashes on data from database
1762 * or twice stripslashes when processing data recieved from user.
1764 * @param string $text text that may contain TRUSTTEXT marker
1765 * @return text without any TRUSTTEXT marker
1767 function trusttext_strip($text) {
1768 global $CFG;
1770 while (true) { //removing nested TRUSTTEXT
1771 $orig = $text;
1772 $text = str_replace(TRUSTTEXT, '', $text);
1773 if (strcmp($orig, $text) === 0) {
1774 return $text;
1780 * Mark text as trusted, such text may contain any HTML tags because the
1781 * normal text cleaning will be bypassed.
1782 * Please make sure that the text comes from trusted user before storing
1783 * it into database!
1785 function trusttext_mark($text) {
1786 global $CFG;
1787 if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
1788 return TRUSTTEXT.$text;
1789 } else {
1790 return $text;
1793 function trusttext_after_edit(&$text, $context) {
1794 if (has_capability('moodle/site:trustcontent', $context)) {
1795 $text = trusttext_strip($text);
1796 $text = trusttext_mark($text);
1797 } else {
1798 $text = trusttext_strip($text);
1802 function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
1803 global $CFG;
1805 $options = new object();
1806 $options->smiley = false;
1807 $options->filter = false;
1808 if (!empty($CFG->enabletrusttext)
1809 and has_capability('moodle/site:trustcontent', $context)
1810 and trusttext_present($text)) {
1811 $options->noclean = true;
1812 } else {
1813 $options->noclean = false;
1815 $text = trusttext_strip($text);
1816 if ($usehtmleditor) {
1817 $text = format_text($text, $format, $options);
1818 $format = FORMAT_HTML;
1819 } else if (!$options->noclean){
1820 $text = clean_text($text, $format);
1825 * Given raw text (eg typed in by a user), this function cleans it up
1826 * and removes any nasty tags that could mess up Moodle pages.
1828 * @uses FORMAT_MOODLE
1829 * @uses FORMAT_PLAIN
1830 * @uses ALLOWED_TAGS
1831 * @param string $text The text to be cleaned
1832 * @param int $format Identifier of the text format to be used
1833 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1834 * @return string The cleaned up text
1836 function clean_text($text, $format=FORMAT_MOODLE) {
1838 global $ALLOWED_TAGS, $CFG;
1840 if (empty($text) or is_numeric($text)) {
1841 return (string)$text;
1844 switch ($format) {
1845 case FORMAT_PLAIN:
1846 case FORMAT_MARKDOWN:
1847 return $text;
1849 default:
1851 if (!empty($CFG->enablehtmlpurifier)) {
1852 $text = purify_html($text);
1853 } else {
1854 /// Fix non standard entity notations
1855 $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1856 $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1858 /// Remove tags that are not allowed
1859 $text = strip_tags($text, $ALLOWED_TAGS);
1861 /// Clean up embedded scripts and , using kses
1862 $text = cleanAttributes($text);
1864 /// Again remove tags that are not allowed
1865 $text = strip_tags($text, $ALLOWED_TAGS);
1869 /// Remove potential script events - some extra protection for undiscovered bugs in our code
1870 $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1871 $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
1873 return $text;
1878 * KSES replacement cleaning function - uses HTML Purifier.
1880 function purify_html($text) {
1881 global $CFG;
1883 static $purifier = false;
1884 if (!$purifier) {
1885 make_upload_directory('cache/htmlpurifier', false);
1886 require_once $CFG->libdir.'/htmlpurifier/HTMLPurifier.auto.php';
1887 $config = HTMLPurifier_Config::createDefault();
1888 $config->set('Core', 'AcceptFullDocuments', false);
1889 $config->set('Core', 'Encoding', 'UTF-8');
1890 $config->set('HTML', 'Doctype', 'XHTML 1.0 Transitional');
1891 $config->set('Cache', 'SerializerPath', $CFG->dataroot.'/cache/htmlpurifier');
1892 $config->set('URI', 'AllowedSchemes', array('http'=>1, 'https'=>1, 'ftp'=>1, 'irc'=>1, 'nntp'=>1, 'news'=>1, 'rtsp'=>1, 'teamspeak'=>1, 'gopher'=>1, 'mms'=>1));
1893 $purifier = new HTMLPurifier($config);
1895 return $purifier->purify($text);
1899 * This function takes a string and examines it for HTML tags.
1900 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1901 * which checks for attributes and filters them for malicious content
1902 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
1904 * @param string $str The string to be examined for html tags
1905 * @return string
1907 function cleanAttributes($str){
1908 $result = preg_replace_callback(
1909 '%(<[^>]*(>|$)|>)%m', #search for html tags
1910 "cleanAttributes2",
1911 $str
1913 return $result;
1917 * This function takes a string with an html tag and strips out any unallowed
1918 * protocols e.g. javascript:
1919 * It calls ancillary functions in kses which are prefixed by kses
1920 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
1922 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1923 * element the html to be cleared
1924 * @return string
1926 function cleanAttributes2($htmlArray){
1928 global $CFG, $ALLOWED_PROTOCOLS;
1929 require_once($CFG->libdir .'/kses.php');
1931 $htmlTag = $htmlArray[1];
1932 if (substr($htmlTag, 0, 1) != '<') {
1933 return '&gt;'; //a single character ">" detected
1935 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1936 return ''; // It's seriously malformed
1938 $slash = trim($matches[1]); //trailing xhtml slash
1939 $elem = $matches[2]; //the element name
1940 $attrlist = $matches[3]; // the list of attributes as a string
1942 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1944 $attStr = '';
1945 foreach ($attrArray as $arreach) {
1946 $arreach['name'] = strtolower($arreach['name']);
1947 if ($arreach['name'] == 'style') {
1948 $value = $arreach['value'];
1949 while (true) {
1950 $prevvalue = $value;
1951 $value = kses_no_null($value);
1952 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1953 $value = kses_decode_entities($value);
1954 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1955 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1956 if ($value === $prevvalue) {
1957 $arreach['value'] = $value;
1958 break;
1961 $arreach['value'] = preg_replace("/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t/i", "Xjavascript", $arreach['value']);
1962 $arreach['value'] = preg_replace("/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n/i", "Xexpression", $arreach['value']);
1963 } else if ($arreach['name'] == 'href') {
1964 //Adobe Acrobat Reader XSS protection
1965 $arreach['value'] = preg_replace('/(\.(pdf|fdf|xfdf|xdp|xfd))[^a-z0-9_\.\-].*$/i', '$1', $arreach['value']);
1967 $attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'"';
1970 $xhtml_slash = '';
1971 if (preg_match('%/\s*$%', $attrlist)) {
1972 $xhtml_slash = ' /';
1974 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1978 * Replaces all known smileys in the text with image equivalents
1980 * @uses $CFG
1981 * @param string $text Passed by reference. The string to search for smily strings.
1982 * @return string
1984 function replace_smilies(&$text) {
1986 global $CFG;
1988 $lang = current_language();
1990 /// this builds the mapping array only once
1991 static $e = array();
1992 static $img = array();
1993 static $emoticons = array(
1994 ':-)' => 'smiley',
1995 ':)' => 'smiley',
1996 ':-D' => 'biggrin',
1997 ';-)' => 'wink',
1998 ':-/' => 'mixed',
1999 'V-.' => 'thoughtful',
2000 ':-P' => 'tongueout',
2001 'B-)' => 'cool',
2002 '^-)' => 'approve',
2003 '8-)' => 'wideeyes',
2004 ':o)' => 'clown',
2005 ':-(' => 'sad',
2006 ':(' => 'sad',
2007 '8-.' => 'shy',
2008 ':-I' => 'blush',
2009 ':-X' => 'kiss',
2010 '8-o' => 'surprise',
2011 'P-|' => 'blackeye',
2012 '8-[' => 'angry',
2013 'xx-P' => 'dead',
2014 '|-.' => 'sleepy',
2015 '}-]' => 'evil',
2016 '(h)' => 'heart',
2017 '(heart)' => 'heart',
2018 '(y)' => 'yes',
2019 '(n)' => 'no',
2020 '(martin)' => 'martin',
2021 '( )' => 'egg'
2024 if (empty($img[$lang])) { /// After the first time this is not run again
2025 $e[$lang] = array();
2026 $img[$lang] = array();
2027 foreach ($emoticons as $emoticon => $image){
2028 $alttext = get_string($image, 'pix');
2030 $e[$lang][] = $emoticon;
2031 $img[$lang][] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
2035 // Exclude from transformations all the code inside <script> tags
2036 // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
2037 // Based on code from glossary fiter by Williams Castillo.
2038 // - Eloy
2040 // Detect all the <script> zones to take out
2041 $excludes = array();
2042 preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
2044 // Take out all the <script> zones from text
2045 foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
2046 $excludes['<+'.$key.'+>'] = $value;
2048 if ($excludes) {
2049 $text = str_replace($excludes,array_keys($excludes),$text);
2052 /// this is the meat of the code - this is run every time
2053 $text = str_replace($e[$lang], $img[$lang], $text);
2055 // Recover all the <script> zones to text
2056 if ($excludes) {
2057 $text = str_replace(array_keys($excludes),$excludes,$text);
2062 * Given plain text, makes it into HTML as nicely as possible.
2063 * May contain HTML tags already
2065 * @uses $CFG
2066 * @param string $text The string to convert.
2067 * @param boolean $smiley Convert any smiley characters to smiley images?
2068 * @param boolean $para If true then the returned string will be wrapped in paragraph tags
2069 * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
2070 * @return string
2073 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
2076 global $CFG;
2078 /// Remove any whitespace that may be between HTML tags
2079 $text = eregi_replace(">([[:space:]]+)<", "><", $text);
2081 /// Remove any returns that precede or follow HTML tags
2082 $text = eregi_replace("([\n\r])<", " <", $text);
2083 $text = eregi_replace(">([\n\r])", "> ", $text);
2085 convert_urls_into_links($text);
2087 /// Make returns into HTML newlines.
2088 if ($newlines) {
2089 $text = nl2br($text);
2092 /// Turn smileys into images.
2093 if ($smiley) {
2094 replace_smilies($text);
2097 /// Wrap the whole thing in a paragraph tag if required
2098 if ($para) {
2099 return '<p>'.$text.'</p>';
2100 } else {
2101 return $text;
2106 * Given Markdown formatted text, make it into XHTML using external function
2108 * @uses $CFG
2109 * @param string $text The markdown formatted text to be converted.
2110 * @return string Converted text
2112 function markdown_to_html($text) {
2113 global $CFG;
2115 require_once($CFG->libdir .'/markdown.php');
2117 return Markdown($text);
2121 * Given HTML text, make it into plain text using external function
2123 * @uses $CFG
2124 * @param string $html The text to be converted.
2125 * @return string
2127 function html_to_text($html) {
2129 global $CFG;
2131 require_once($CFG->libdir .'/html2text.php');
2133 return html2text($html);
2137 * Given some text this function converts any URLs it finds into HTML links
2139 * @param string $text Passed in by reference. The string to be searched for urls.
2141 function convert_urls_into_links(&$text) {
2142 /// Make lone URLs into links. eg http://moodle.com/
2143 $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
2144 "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
2146 /// eg www.moodle.com
2147 $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
2148 "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
2152 * This function will highlight search words in a given string
2153 * It cares about HTML and will not ruin links. It's best to use
2154 * this function after performing any conversions to HTML.
2155 * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
2157 * @param string $needle The string to search for
2158 * @param string $haystack The string to search for $needle in
2159 * @param int $case whether to do case-sensitive or insensitive matching.
2160 * @return string
2161 * @todo Finish documenting this function
2163 function highlight($needle, $haystack, $case=0,
2164 $left_string='<span class="highlight">', $right_string='</span>') {
2165 if (empty($needle)) {
2166 return $haystack;
2169 //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101
2170 $list_of_words = $needle;
2171 $list_array = explode(' ', $list_of_words);
2172 for ($i=0; $i<sizeof($list_array); $i++) {
2173 if (strlen($list_array[$i]) == 1) {
2174 $list_array[$i] = '';
2177 $list_of_words = implode(' ', $list_array);
2178 $list_of_words_cp = $list_of_words;
2179 $final = array();
2180 preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
2182 foreach (array_unique($list_of_words[0]) as $key=>$value) {
2183 $final['<|'.$key.'|>'] = $value;
2186 $haystack = str_replace($final,array_keys($final),$haystack);
2187 $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
2189 if ($list_of_words_cp{0}=='|') {
2190 $list_of_words_cp{0} = '';
2192 if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
2193 $list_of_words_cp{strlen($list_of_words_cp)-1}='';
2196 $list_of_words_cp = trim($list_of_words_cp);
2198 if ($list_of_words_cp) {
2200 $list_of_words_cp = "(". $list_of_words_cp .")";
2202 if (!$case){
2203 $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
2204 } else {
2205 $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
2208 $haystack = str_replace(array_keys($final),$final,$haystack);
2210 return $haystack;
2214 * This function will highlight instances of $needle in $haystack
2215 * It's faster that the above function and doesn't care about
2216 * HTML or anything.
2218 * @param string $needle The string to search for
2219 * @param string $haystack The string to search for $needle in
2220 * @return string
2222 function highlightfast($needle, $haystack) {
2224 $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
2226 $pos = 0;
2228 foreach ($parts as $key => $part) {
2229 $parts[$key] = substr($haystack, $pos, strlen($part));
2230 $pos += strlen($part);
2232 $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
2233 $pos += strlen($needle);
2236 return (join('', $parts));
2240 * Return a string containing 'lang', xml:lang and optionally 'dir' HTML attributes.
2241 * Internationalisation, for print_header and backup/restorelib.
2242 * @param $dir Default false.
2243 * @return string Attributes.
2245 function get_html_lang($dir = false) {
2246 $direction = '';
2247 if ($dir) {
2248 if (get_string('thisdirection') == 'rtl') {
2249 $direction = ' dir="rtl"';
2250 } else {
2251 $direction = ' dir="ltr"';
2254 //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2255 $language = str_replace('_', '-', str_replace('_utf8', '', current_language()));
2256 @header('Content-Language: '.$language);
2257 return ($direction.' lang="'.$language.'" xml:lang="'.$language.'"');
2261 * Return the markup for the destination of the 'Skip to main content' links.
2262 * Accessibility improvement for keyboard-only users.
2263 * Used in course formats, /index.php and /course/index.php
2264 * @return string HTML element.
2266 function skip_main_destination() {
2267 return '<span id="maincontent"></span>';
2271 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
2274 * Print a standard header
2276 * @uses $USER
2277 * @uses $CFG
2278 * @uses $SESSION
2279 * @param string $title Appears at the top of the window
2280 * @param string $heading Appears at the top of the page
2281 * @param array $navigation Array of $navlinks arrays (keys: name, link, type) for use as breadcrumbs links
2282 * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
2283 * @param string $meta Meta tags to be added to the header
2284 * @param boolean $cache Should this page be cacheable?
2285 * @param string $button HTML code for a button (usually for module editing)
2286 * @param string $menu HTML code for a popup menu
2287 * @param boolean $usexml use XML for this page
2288 * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2289 * @param bool $return If true, return the visible elements of the header instead of echoing them.
2291 function print_header ($title='', $heading='', $navigation='', $focus='',
2292 $meta='', $cache=true, $button='&nbsp;', $menu='',
2293 $usexml=false, $bodytags='', $return=false) {
2295 global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $COURSE;
2297 if (gettype($navigation) == 'string' && strlen($navigation) != 0 && $navigation != 'home') {
2298 debugging("print_header() was sent a string as 3rd ($navigation) parameter. "
2299 . "This is deprecated in favour of an array built by build_navigation(). Please upgrade your code.");
2302 $heading = format_string($heading); // Fix for MDL-8582
2304 /// This makes sure that the header is never repeated twice on a page
2305 if (defined('HEADER_PRINTED')) {
2306 debugging('print_header() was called more than once - this should not happen. Please check the code for this page closely. Note: error() and redirect() are now safe to call after print_header().');
2307 return;
2309 define('HEADER_PRINTED', 'true');
2312 /// Add the required stylesheets
2313 $stylesheetshtml = '';
2314 foreach ($CFG->stylesheets as $stylesheet) {
2315 $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
2317 $meta = $stylesheetshtml.$meta;
2320 /// Add the meta page from the themes if any were requested
2322 $metapage = '';
2324 if (!isset($THEME->standardmetainclude) || $THEME->standardmetainclude) {
2325 ob_start();
2326 include_once($CFG->dirroot.'/theme/standard/meta.php');
2327 $metapage .= ob_get_contents();
2328 ob_end_clean();
2331 if ($THEME->parent && (!isset($THEME->parentmetainclude) || $THEME->parentmetainclude)) {
2332 if (file_exists($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php')) {
2333 ob_start();
2334 include_once($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php');
2335 $metapage .= ob_get_contents();
2336 ob_end_clean();
2340 if (!isset($THEME->metainclude) || $THEME->metainclude) {
2341 if (file_exists($CFG->dirroot.'/theme/'.current_theme().'/meta.php')) {
2342 ob_start();
2343 include_once($CFG->dirroot.'/theme/'.current_theme().'/meta.php');
2344 $metapage .= ob_get_contents();
2345 ob_end_clean();
2349 $meta = $meta."\n".$metapage;
2351 $meta .= "\n".require_js('',1);
2353 /// Set up some navigation variables
2355 if (is_newnav($navigation)){
2356 $home = false;
2357 } else {
2358 if ($navigation == 'home') {
2359 $home = true;
2360 $navigation = '';
2361 } else {
2362 $home = false;
2366 /// This is another ugly hack to make navigation elements available to print_footer later
2367 $THEME->title = $title;
2368 $THEME->heading = $heading;
2369 $THEME->navigation = $navigation;
2370 $THEME->button = $button;
2371 $THEME->menu = $menu;
2372 $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2374 if ($button == '') {
2375 $button = '&nbsp;';
2378 if (!$menu and $navigation) {
2379 if (empty($CFG->loginhttps)) {
2380 $wwwroot = $CFG->wwwroot;
2381 } else {
2382 $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2384 $menu = user_login_string($COURSE);
2387 if (isset($SESSION->justloggedin)) {
2388 unset($SESSION->justloggedin);
2389 if (!empty($CFG->displayloginfailures)) {
2390 if (!empty($USER->username) and $USER->username != 'guest') {
2391 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
2392 $menu .= '&nbsp;<font size="1">';
2393 if (empty($count->accounts)) {
2394 $menu .= get_string('failedloginattempts', '', $count);
2395 } else {
2396 $menu .= get_string('failedloginattemptsall', '', $count);
2398 if (has_capability('moodle/site:viewreports', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
2399 $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
2400 '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
2402 $menu .= '</font>';
2409 $meta = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' .
2410 "\n" . $meta . "\n";
2411 if (!$usexml) {
2412 @header('Content-Type: text/html; charset=utf-8');
2414 @header('Content-Script-Type: text/javascript');
2415 @header('Content-Style-Type: text/css');
2417 //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2418 $direction = get_html_lang($dir=true);
2420 if ($cache) { // Allow caching on "back" (but not on normal clicks)
2421 @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
2422 @header('Pragma: no-cache');
2423 @header('Expires: ');
2424 } else { // Do everything we can to always prevent clients and proxies caching
2425 @header('Cache-Control: no-store, no-cache, must-revalidate');
2426 @header('Cache-Control: post-check=0, pre-check=0', false);
2427 @header('Pragma: no-cache');
2428 @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
2429 @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2431 $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
2432 $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
2434 @header('Accept-Ranges: none');
2436 $currentlanguage = current_language();
2438 if (empty($usexml)) {
2439 $direction = ' xmlns="http://www.w3.org/1999/xhtml"'. $direction; // See debug_header
2440 } else {
2441 $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
2442 if(!$mathplayer) {
2443 header('Content-Type: application/xhtml+xml');
2445 echo '<?xml version="1.0" ?>'."\n";
2446 if (!empty($CFG->xml_stylesheets)) {
2447 $stylesheets = explode(';', $CFG->xml_stylesheets);
2448 foreach ($stylesheets as $stylesheet) {
2449 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
2452 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
2453 if (!empty($CFG->xml_doctype_extra)) {
2454 echo ' plus '. $CFG->xml_doctype_extra;
2456 echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
2457 $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
2458 xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
2459 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2460 $direction";
2461 if($mathplayer) {
2462 $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
2463 $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
2464 $meta .= '</object>'."\n";
2465 $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
2469 // Clean up the title
2471 $title = format_string($title); // fix for MDL-8582
2472 $title = str_replace('"', '&quot;', $title);
2474 // Create class and id for this page
2476 page_id_and_class($pageid, $pageclass);
2478 $pageclass .= ' course-'.$COURSE->id;
2480 if (($pageid != 'site-index') && ($pageid != 'course-view') &&
2481 (strstr($pageid, 'admin') === FALSE)) {
2482 $pageclass .= ' nocoursepage';
2485 if (!isloggedin()) {
2486 $pageclass .= ' notloggedin';
2489 if (!empty($USER->editing)) {
2490 $pageclass .= ' editing';
2493 if (!empty($CFG->blocksdrag)) {
2494 $pageclass .= ' drag';
2497 $pageclass .= ' dir-'.get_string('thisdirection');
2499 $pageclass .= ' lang-'.$currentlanguage;
2501 $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
2503 ob_start();
2504 include($CFG->header);
2505 $output = ob_get_contents();
2506 ob_end_clean();
2508 // Skip to main content, see skip_main_destination().
2509 if ($pageid=='course-view' or $pageid=='site-index' or $pageid=='course-index') {
2510 $skiplink = '<a class="skip" href="#maincontent">'.get_string('tocontent', 'access').'</a>';
2511 if (! preg_match('/(.*<div.*?page.>)(.*)/s', $output, $matches)) {
2512 preg_match('/(.*<body.*?>)(.*)/s', $output, $matches);
2514 $output = $matches[1]."\n". $skiplink .$matches[2];
2517 $output = force_strict_header($output);
2519 if (!empty($CFG->messaging)) {
2520 $output .= message_popup_window();
2523 // Add in any extra JavaScript libraries that occurred during the header
2524 $output .= require_js('', 2);
2526 if ($return) {
2527 return $output;
2528 } else {
2529 echo $output;
2533 define('REQUIREJS_BEFOREHEADER',0);
2534 define('REQUIREJS_INHEADER',1);
2535 define('REQUIREJS_AFTERHEADER',2);
2538 * Used to include JavaScript libraries.
2540 * When the $lib parameter is given, the function will ensure that the
2541 * named library is loaded onto the page - either in the HTML <head>,
2542 * just after the header, or at an arbitrary later point in the page,
2543 * depending on where this function is called.
2545 * Libraries will not be included more than once, so this works like
2546 * require_once in PHP.
2548 * There are two special-case calls to this function which are both used only
2549 * by weblib print_header:
2550 * $extracthtml = 1: this is used before printing the header.
2551 * It returns the script tag code that should go inside the <head>.
2552 * $extracthtml = 2: this is used after printing the header and handles any
2553 * require_js calls that occurred within the header itself.
2555 * @param mixed $lib - string or array of strings
2556 * string(s) should be the shortname for the library or the
2557 * full path to the library file.
2558 * @param int $extracthtml Do not set this parameter usually (leave 0), only
2559 * weblib should set this to 1 or 2 in print_header function.
2560 * @return mixed No return value, except when using $extracthtml it returns the html code.
2562 function require_js($lib,$extracthtml=0) {
2563 global $CFG;
2564 static $loadlibs = array();
2566 static $state = REQUIREJS_BEFOREHEADER;
2567 static $latecode = '';
2569 if (!empty($lib)) {
2570 // Add the lib to the list of libs to be loaded, if it isn't already
2571 // in the list.
2572 if (is_array($lib)) {
2573 foreach($lib as $singlelib) {
2574 require_js($singlelib);
2576 } else {
2577 $libpath = ajax_get_lib($lib);
2578 if (array_search($libpath, $loadlibs) === false) {
2579 $loadlibs[] = $libpath;
2581 // For state other than 0 we need to take action as well as just
2582 // adding it to loadlibs
2583 if($state != REQUIREJS_BEFOREHEADER) {
2584 // Get the script statement for this library
2585 $scriptstatement=get_require_js_code(array($libpath));
2587 if($state == REQUIREJS_AFTERHEADER) {
2588 // After the header, print it immediately
2589 print $scriptstatement;
2590 } else {
2591 // Haven't finished the header yet. Add it after the
2592 // header
2593 $latecode .= $scriptstatement;
2598 } else if($extracthtml==1) {
2599 if($state !== REQUIREJS_BEFOREHEADER) {
2600 debugging('Incorrect state in require_js (expected BEFOREHEADER): be careful not to call with empty $lib (except in print_header)');
2601 } else {
2602 $state = REQUIREJS_INHEADER;
2605 return get_require_js_code($loadlibs);
2606 } else if($extracthtml==2) {
2607 if($state !== REQUIREJS_INHEADER) {
2608 debugging('Incorrect state in require_js (expected INHEADER): be careful not to call with empty $lib (except in print_header)');
2609 return '';
2610 } else {
2611 $state = REQUIREJS_AFTERHEADER;
2612 return $latecode;
2614 } else {
2615 debugging('Unexpected value for $extracthtml');
2620 * Should not be called directly - use require_js. This function obtains the code
2621 * (script tags) needed to include JavaScript libraries.
2622 * @param array $loadlibs Array of library files to include
2623 * @return string HTML code to include them
2625 function get_require_js_code($loadlibs) {
2626 global $CFG;
2627 // Return the html needed to load the JavaScript files defined in
2628 // our list of libs to be loaded.
2629 $output = '';
2630 foreach ($loadlibs as $loadlib) {
2631 $output .= '<script type="text/javascript" ';
2632 $output .= " src=\"$loadlib\"></script>\n";
2633 if ($loadlib == $CFG->wwwroot.'/lib/yui/logger/logger-min.js') {
2634 // Special case, we need the CSS too.
2635 $output .= '<link type="text/css" rel="stylesheet" ';
2636 $output .= " href=\"{$CFG->wwwroot}/lib/yui/logger/assets/logger.css\" />\n";
2639 return $output;
2644 * Debugging aid: serve page as 'application/xhtml+xml' where possible,
2645 * and substitute the XHTML strict document type.
2646 * Note, requires the 'xmlns' fix in function print_header above.
2647 * See: http://tracker.moodle.org/browse/MDL-7883
2648 * TODO:
2650 function force_strict_header($output) {
2651 global $CFG;
2652 $strict = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2653 $xsl = '/lib/xhtml.xsl';
2655 if (!headers_sent() && !empty($CFG->xmlstrictheaders)) { // With xml strict headers, the browser will barf
2656 $ctype = 'Content-Type: ';
2657 $prolog= "<?xml version='1.0' encoding='utf-8'?>\n";
2659 if (isset($_SERVER['HTTP_ACCEPT'])
2660 && false !== strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml')) {
2661 //|| false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') //Safari "Entity 'copy' not defined".
2662 // Firefox et al.
2663 $ctype .= 'application/xhtml+xml';
2664 $prolog .= "<!--\n DEBUG: $ctype \n-->\n";
2666 } else if (file_exists($CFG->dirroot.$xsl)
2667 && preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
2668 // XSL hack for IE 5+ on Windows.
2669 //$www_xsl = preg_replace('/(http:\/\/.+?\/).*/', '', $CFG->wwwroot) .$xsl;
2670 $www_xsl = $CFG->wwwroot .$xsl;
2671 $ctype .= 'application/xml';
2672 $prolog .= "<?xml-stylesheet type='text/xsl' href='$www_xsl'?>\n";
2673 $prolog .= "<!--\n DEBUG: $ctype \n-->\n";
2675 } else {
2676 //ELSE: Mac/IE, old/non-XML browsers.
2677 $ctype .= 'text/html';
2678 $prolog = '';
2680 @header($ctype.'; charset=utf-8');
2681 $output = $prolog . $output;
2683 // Test parser error-handling.
2684 if (isset($_GET['error'])) {
2685 $output .= "__ TEST: XML well-formed error < __\n";
2689 $output = preg_replace('/(<!DOCTYPE.+?>)/s', $strict, $output); // Always change the DOCTYPE to Strict 1.0
2691 return $output;
2697 * This version of print_header is simpler because the course name does not have to be
2698 * provided explicitly in the strings. It can be used on the site page as in courses
2699 * Eventually all print_header could be replaced by print_header_simple
2701 * @param string $title Appears at the top of the window
2702 * @param string $heading Appears at the top of the page
2703 * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2704 * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
2705 * @param string $meta Meta tags to be added to the header
2706 * @param boolean $cache Should this page be cacheable?
2707 * @param string $button HTML code for a button (usually for module editing)
2708 * @param string $menu HTML code for a popup menu
2709 * @param boolean $usexml use XML for this page
2710 * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2711 * @param bool $return If true, return the visible elements of the header instead of echoing them.
2713 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
2714 $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
2716 global $COURSE, $CFG;
2718 // If old style nav prepend course short name otherwise leave $navigation object alone
2719 if (!is_newnav($navigation)) {
2720 if ($COURSE->id != SITEID) {
2721 $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $COURSE->id .'">'. $COURSE->shortname .'</a> ->';
2722 $navigation = $shortname.' '.$navigation;
2726 $output = print_header($COURSE->shortname .': '. $title, $COURSE->fullname .' '. $heading, $navigation, $focus, $meta,
2727 $cache, $button, $menu, $usexml, $bodytags, true);
2729 if ($return) {
2730 return $output;
2731 } else {
2732 echo $output;
2738 * Can provide a course object to make the footer contain a link to
2739 * to the course home page, otherwise the link will go to the site home
2741 * @uses $CFG
2742 * @uses $USER
2743 * @param course $course {@link $COURSE} object containing course information
2744 * @param ? $usercourse ?
2745 * @todo Finish documenting this function
2747 function print_footer($course=NULL, $usercourse=NULL, $return=false) {
2748 global $USER, $CFG, $THEME, $COURSE;
2750 if (defined('ADMIN_EXT_HEADER_PRINTED') and !defined('ADMIN_EXT_FOOTER_PRINTED')) {
2751 admin_externalpage_print_footer();
2752 return;
2755 /// Course links
2756 if ($course) {
2757 if (is_string($course) && $course == 'none') { // Don't print any links etc
2758 $homelink = '';
2759 $loggedinas = '';
2760 $home = false;
2761 } else if (is_string($course) && $course == 'home') { // special case for site home page - please do not remove
2762 $course = get_site();
2763 $homelink = '<div class="sitelink">'.
2764 '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/">'.
2765 '<img style="width:100px;height:30px" src="pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
2766 $home = true;
2767 } else {
2768 $homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.
2769 '/course/view.php?id='.$course->id.'">'.format_string($course->shortname).'</a></div>';
2770 $home = false;
2772 } else {
2773 $course = get_site(); // Set course as site course by default
2774 $homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
2775 $home = false;
2778 /// Set up some other navigation links (passed from print_header by ugly hack)
2779 $menu = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2780 $title = isset($THEME->title) ? $THEME->title : '';
2781 $button = isset($THEME->button) ? $THEME->button : '';
2782 $heading = isset($THEME->heading) ? $THEME->heading : '';
2783 $navigation = isset($THEME->navigation) ? $THEME->navigation : '';
2784 $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2787 /// Set the user link if necessary
2788 if (!$usercourse and is_object($course)) {
2789 $usercourse = $course;
2792 if (!isset($loggedinas)) {
2793 $loggedinas = user_login_string($usercourse, $USER);
2796 if ($loggedinas == $menu) {
2797 $menu = '';
2800 /// Provide some performance info if required
2801 $performanceinfo = '';
2802 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
2803 $perf = get_performance_info();
2804 if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
2805 error_log("PERF: " . $perf['txt']);
2807 if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
2808 $performanceinfo = $perf['html'];
2812 /// Close eventually open custom_corner divs
2813 if ((!empty($THEME->customcorners)) && ($THEME->customcornersopen > 1)) {
2814 require_once($CFG->dirroot.'/lib/custom_corners_lib.php');
2815 while ($THEME->customcornersopen > 1) {
2816 print_custom_corners_end();
2820 /// Include the actual footer file
2822 ob_start();
2823 include($CFG->footer);
2824 $output = ob_get_contents();
2825 ob_end_clean();
2827 if ($return) {
2828 return $output;
2829 } else {
2830 echo $output;
2835 * Returns the name of the current theme
2837 * @uses $CFG
2838 * @uses $USER
2839 * @uses $SESSION
2840 * @uses $COURSE
2841 * @uses $FULLME
2842 * @return string
2844 function current_theme() {
2845 global $CFG, $USER, $SESSION, $COURSE, $FULLME;
2847 if (empty($CFG->themeorder)) {
2848 $themeorder = array('page', 'course', 'category', 'session', 'user', 'site');
2849 } else {
2850 $themeorder = $CFG->themeorder;
2853 if (isloggedin() and $USER->mnethostid != $CFG->mnet_localhost_id) {
2854 require_once($CFG->dirroot.'/mnet/peer.php');
2855 $mnet_peer = new mnet_peer();
2856 $mnet_peer->set_id($USER->mnethostid);
2859 $theme = '';
2860 foreach ($themeorder as $themetype) {
2862 if (!empty($theme)) continue;
2864 switch ($themetype) {
2865 case 'page': // Page theme is for special page-only themes set by code
2866 if (!empty($CFG->pagetheme)) {
2867 $theme = $CFG->pagetheme;
2869 break;
2870 case 'course':
2871 if (!empty($CFG->allowcoursethemes) and !empty($COURSE->theme)) {
2872 $theme = $COURSE->theme;
2874 break;
2875 case 'category':
2876 if (!empty($CFG->allowcategorythemes)) {
2877 /// Nasty hack to check if we're in a category page
2878 if (stripos($FULLME, 'course/category.php') !== false) {
2879 global $id;
2880 if (!empty($id)) {
2881 $theme = current_category_theme($id);
2883 /// Otherwise check if we're in a course that has a category theme set
2884 } else if (!empty($COURSE->category)) {
2885 $theme = current_category_theme($COURSE->category);
2888 break;
2889 case 'session':
2890 if (!empty($SESSION->theme)) {
2891 $theme = $SESSION->theme;
2893 break;
2894 case 'user':
2895 if (!empty($CFG->allowuserthemes) and !empty($USER->theme)) {
2896 if (isloggedin() and $USER->mnethostid != $CFG->mnet_localhost_id && $mnet_peer->force_theme == 1 && $mnet_peer->theme != '') {
2897 $theme = $mnet_peer->theme;
2898 } else {
2899 $theme = $USER->theme;
2902 break;
2903 case 'site':
2904 if (isloggedin() and $USER->mnethostid != $CFG->mnet_localhost_id && $mnet_peer->force_theme == 1 && $mnet_peer->theme != '') {
2905 $theme = $mnet_peer->theme;
2906 } else {
2907 $theme = $CFG->theme;
2909 break;
2910 default:
2911 /// do nothing
2915 /// A final check in case 'site' was not included in $CFG->themeorder
2916 if (empty($theme)) {
2917 $theme = $CFG->theme;
2920 return $theme;
2924 * Retrieves the category theme if one exists, otherwise checks the parent categories.
2925 * Recursive function.
2927 * @uses $COURSE
2928 * @param integer $categoryid id of the category to check
2929 * @return string theme name
2931 function current_category_theme($categoryid=0) {
2932 global $COURSE;
2934 /// Use the COURSE global if the categoryid not set
2935 if (empty($categoryid)) {
2936 if (!empty($COURSE->category)) {
2937 $categoryid = $COURSE->category;
2938 } else {
2939 return false;
2943 /// Retrieve the current category
2944 if ($category = get_record('course_categories', 'id', $categoryid)) {
2946 /// Return the category theme if it exists
2947 if (!empty($category->theme)) {
2948 return $category->theme;
2950 /// Otherwise try the parent category if one exists
2951 } else if (!empty($category->parent)) {
2952 return current_category_theme($category->parent);
2955 /// Return false if we can't find the category record
2956 } else {
2957 return false;
2962 * This function is called by stylesheets to set up the header
2963 * approriately as well as the current path
2965 * @uses $CFG
2966 * @param int $lastmodified ?
2967 * @param int $lifetime ?
2968 * @param string $thename ?
2970 function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
2972 global $CFG, $THEME;
2974 // Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
2975 $lastmodified = time();
2977 header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2978 header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
2979 header('Cache-Control: max-age='. $lifetime);
2980 header('Pragma: ');
2981 header('Content-type: text/css'); // Correct MIME type
2983 $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
2985 if (empty($themename)) {
2986 $themename = current_theme(); // So we have something. Normally not needed.
2987 } else {
2988 $themename = clean_param($themename, PARAM_SAFEDIR);
2991 if (!empty($forceconfig)) { // Page wants to use the config from this theme instead
2992 unset($THEME);
2993 include($CFG->themedir.'/'.$forceconfig.'/'.'config.php');
2996 /// If this is the standard theme calling us, then find out what sheets we need
2998 if ($themename == 'standard') {
2999 if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
3000 $THEME->sheets = $DEFAULT_SHEET_LIST;
3001 } else if (empty($THEME->standardsheets)) { // We can stop right now!
3002 echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
3003 exit;
3004 } else { // Use the provided subset only
3005 $THEME->sheets = $THEME->standardsheets;
3008 /// If we are a parent theme, then check for parent definitions
3010 } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
3011 if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) { // Use all the sheets we have
3012 $THEME->sheets = $DEFAULT_SHEET_LIST;
3013 } else if (empty($THEME->parentsheets)) { // We can stop right now!
3014 echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
3015 exit;
3016 } else { // Use the provided subset only
3017 $THEME->sheets = $THEME->parentsheets;
3021 /// Work out the last modified date for this theme
3023 foreach ($THEME->sheets as $sheet) {
3024 if (file_exists($CFG->themedir.'/'.$themename.'/'.$sheet.'.css')) {
3025 $sheetmodified = filemtime($CFG->themedir.'/'.$themename.'/'.$sheet.'.css');
3026 if ($sheetmodified > $lastmodified) {
3027 $lastmodified = $sheetmodified;
3033 /// Get a list of all the files we want to include
3034 $files = array();
3036 foreach ($THEME->sheets as $sheet) {
3037 $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
3040 if ($themename == 'standard') { // Add any standard styles included in any modules
3041 if (!empty($THEME->modsheets)) { // Search for styles.php within activity modules
3042 if ($mods = get_list_of_plugins('mod')) {
3043 foreach ($mods as $mod) {
3044 if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
3045 $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
3051 if (!empty($THEME->blocksheets)) { // Search for styles.php within block modules
3052 if ($mods = get_list_of_plugins('blocks')) {
3053 foreach ($mods as $mod) {
3054 if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
3055 $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
3061 if (!isset($THEME->courseformatsheets) || $THEME->courseformatsheets) { // Search for styles.php in course formats
3062 if ($mods = get_list_of_plugins('format','',$CFG->dirroot.'/course')) {
3063 foreach ($mods as $mod) {
3064 if (file_exists($CFG->dirroot.'/course/format/'.$mod.'/styles.php')) {
3065 $files[] = array($CFG->dirroot, '/course/format/'.$mod.'/styles.php');
3071 if (!isset($THEME->gradereportsheets) || $THEME->gradereportsheets) { // Search for styles.php in grade reports
3072 if ($reports = get_list_of_plugins('grade/report')) {
3073 foreach ($reports as $report) {
3074 if (file_exists($CFG->dirroot.'/grade/report/'.$report.'/styles.php')) {
3075 $files[] = array($CFG->dirroot, '/grade/report/'.$report.'/styles.php');
3081 if (!empty($THEME->langsheets)) { // Search for styles.php within the current language
3082 if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
3083 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
3088 if ($files) {
3089 /// Produce a list of all the files first
3090 echo '/**************************************'."\n";
3091 echo ' * THEME NAME: '.$themename."\n *\n";
3092 echo ' * Files included in this sheet:'."\n *\n";
3093 foreach ($files as $file) {
3094 echo ' * '.$file[1]."\n";
3096 echo ' **************************************/'."\n\n";
3099 /// check if csscobstants is set
3100 if (!empty($THEME->cssconstants)) {
3101 require_once("$CFG->libdir/cssconstants.php");
3102 /// Actually collect all the files in order.
3103 $css = '';
3104 foreach ($files as $file) {
3105 $css .= '/***** '.$file[1].' start *****/'."\n\n";
3106 $css .= file_get_contents($file[0].'/'.$file[1]);
3107 $ccs .= '/***** '.$file[1].' end *****/'."\n\n";
3109 /// replace css_constants with their values
3110 echo replace_cssconstants($css);
3111 } else {
3112 /// Actually output all the files in order.
3113 if (empty($CFG->CSSEdit) && empty($THEME->CSSEdit)) {
3114 foreach ($files as $file) {
3115 echo '/***** '.$file[1].' start *****/'."\n\n";
3116 @include_once($file[0].'/'.$file[1]);
3117 echo '/***** '.$file[1].' end *****/'."\n\n";
3119 } else {
3120 foreach ($files as $file) {
3121 echo '/* @group '.$file[1].' */'."\n\n";
3122 if (strstr($file[1], '.css') !== FALSE) {
3123 echo '@import url("'.$CFG->themewww.'/'.$file[1].'");'."\n\n";
3124 } else {
3125 @include_once($file[0].'/'.$file[1]);
3127 echo '/* @end */'."\n\n";
3133 return $CFG->themewww.'/'.$themename; // Only to help old themes (1.4 and earlier)
3137 function theme_setup($theme = '', $params=NULL) {
3138 /// Sets up global variables related to themes
3140 global $CFG, $THEME, $SESSION, $USER, $HTTPSPAGEREQUIRED;
3142 if (empty($theme)) {
3143 $theme = current_theme();
3146 /// If the theme doesn't exist for some reason then revert to standardwhite
3147 if (!file_exists($CFG->themedir .'/'. $theme .'/config.php')) {
3148 $CFG->theme = $theme = 'standardwhite';
3151 /// Load up the theme config
3152 $THEME = NULL; // Just to be sure
3153 include($CFG->themedir .'/'. $theme .'/config.php'); // Main config for current theme
3155 /// Put together the parameters
3156 if (!$params) {
3157 $params = array();
3160 if ($theme != $CFG->theme) {
3161 $params[] = 'forceconfig='.$theme;
3164 /// Force language too if required
3165 if (!empty($THEME->langsheets)) {
3166 $params[] = 'lang='.current_language();
3170 /// Convert params to string
3171 if ($params) {
3172 $paramstring = '?'.implode('&', $params);
3173 } else {
3174 $paramstring = '';
3177 /// Set up image paths
3178 if(isset($CFG->smartpix) && $CFG->smartpix==1) {
3179 if($CFG->slasharguments) { // Use this method if possible for better caching
3180 $extra='';
3181 } else {
3182 $extra='?file=';
3185 $CFG->pixpath = $CFG->wwwroot. '/pix/smartpix.php'.$extra.'/'.$theme;
3186 $CFG->modpixpath = $CFG->wwwroot .'/pix/smartpix.php'.$extra.'/'.$theme.'/mod';
3187 } else if (empty($THEME->custompix)) { // Could be set in the above file
3188 $CFG->pixpath = $CFG->wwwroot .'/pix';
3189 $CFG->modpixpath = $CFG->wwwroot .'/mod';
3190 } else {
3191 $CFG->pixpath = $CFG->themewww .'/'. $theme .'/pix';
3192 $CFG->modpixpath = $CFG->themewww .'/'. $theme .'/pix/mod';
3195 /// Header and footer paths
3196 $CFG->header = $CFG->themedir .'/'. $theme .'/header.html';
3197 $CFG->footer = $CFG->themedir .'/'. $theme .'/footer.html';
3199 /// Define stylesheet loading order
3200 $CFG->stylesheets = array();
3201 if ($theme != 'standard') { /// The standard sheet is always loaded first
3202 $CFG->stylesheets[] = $CFG->themewww.'/standard/styles.php'.$paramstring;
3204 if (!empty($THEME->parent)) { /// Parent stylesheets are loaded next
3205 $CFG->stylesheets[] = $CFG->themewww.'/'.$THEME->parent.'/styles.php'.$paramstring;
3207 $CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/styles.php'.$paramstring;
3209 /// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
3210 if (!empty($HTTPSPAGEREQUIRED)) {
3211 $CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
3212 $CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
3213 $CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
3214 foreach ($CFG->stylesheets as $key => $stylesheet) {
3215 $CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
3219 // RTL support - only for RTL languages, add RTL CSS
3220 if (get_string('thisdirection') == 'rtl') {
3221 $CFG->stylesheets[] = $CFG->themewww.'/standard/rtl.css'.$paramstring;
3222 $CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/rtl.css'.$paramstring;
3228 * Returns text to be displayed to the user which reflects their login status
3230 * @uses $CFG
3231 * @uses $USER
3232 * @param course $course {@link $COURSE} object containing course information
3233 * @param user $user {@link $USER} object containing user information
3234 * @return string
3236 function user_login_string($course=NULL, $user=NULL) {
3237 global $USER, $CFG, $SITE;
3239 if (empty($user) and !empty($USER->id)) {
3240 $user = $USER;
3243 if (empty($course)) {
3244 $course = $SITE;
3247 if (!empty($user->realuser)) {
3248 if ($realuser = get_record('user', 'id', $user->realuser)) {
3249 $fullname = fullname($realuser, true);
3250 $realuserinfo = " [<a $CFG->frametarget
3251 href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1&amp;sesskey=".sesskey()."\">$fullname</a>] ";
3253 } else {
3254 $realuserinfo = '';
3257 if (empty($CFG->loginhttps)) {
3258 $wwwroot = $CFG->wwwroot;
3259 } else {
3260 $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
3263 if (empty($course->id)) {
3264 // $course->id is not defined during installation
3265 return '';
3266 } else if (!empty($user->id)) {
3267 $context = get_context_instance(CONTEXT_COURSE, $course->id);
3269 $fullname = fullname($user, true);
3270 $username = "<a $CFG->frametarget href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
3271 if (is_mnet_remote_user($user) and $idprovider = get_record('mnet_host', 'id', $user->mnethostid)) {
3272 $username .= " from <a $CFG->frametarget href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
3274 if (isset($user->username) && $user->username == 'guest') {
3275 $loggedinas = $realuserinfo.get_string('loggedinasguest').
3276 " (<a $CFG->frametarget href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
3277 } else if (!empty($user->access['rsw'][$context->path])) {
3278 $rolename = '';
3279 if ($role = get_record('role', 'id', $user->access['rsw'][$context->path])) {
3280 $rolename = ': '.format_string($role->name);
3282 $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
3283 " (<a $CFG->frametarget
3284 href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
3285 } else {
3286 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
3287 " (<a $CFG->frametarget href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
3289 } else {
3290 $loggedinas = get_string('loggedinnot', 'moodle').
3291 " (<a $CFG->frametarget href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
3293 return '<div class="logininfo">'.$loggedinas.'</div>';
3297 * Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
3298 * If not it applies sensible defaults.
3300 * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
3301 * search forum block, etc. Important: these are 'silent' in a screen-reader
3302 * (unlike &gt; &raquo;), and must be accompanied by text.
3303 * @uses $THEME
3305 function check_theme_arrows() {
3306 global $THEME;
3308 if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
3309 // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
3310 // Also OK in Win 9x/2K/IE 5.x
3311 $THEME->rarrow = '&#x25BA;';
3312 $THEME->larrow = '&#x25C4;';
3313 $uagent = $_SERVER['HTTP_USER_AGENT'];
3314 if (false !== strpos($uagent, 'Opera')
3315 || false !== strpos($uagent, 'Mac')) {
3316 // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
3317 // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
3318 $THEME->rarrow = '&#x25B6;';
3319 $THEME->larrow = '&#x25C0;';
3321 elseif (false !== strpos($uagent, 'Konqueror')) {
3322 $THEME->rarrow = '&rarr;';
3323 $THEME->larrow = '&larr;';
3325 elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
3326 && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
3327 // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
3328 // To be safe, non-Unicode browsers!
3329 $THEME->rarrow = '&gt;';
3330 $THEME->larrow = '&lt;';
3333 /// RTL support - in RTL languages, swap r and l arrows
3334 if (right_to_left()) {
3335 $t = $THEME->rarrow;
3336 $THEME->rarrow = $THEME->larrow;
3337 $THEME->larrow = $t;
3344 * Return the right arrow with text ('next'), and optionally embedded in a link.
3345 * See function above, check_theme_arrows.
3346 * @param string $text HTML/plain text label (set to blank only for breadcrumb separator cases).
3347 * @param string $url An optional link to use in a surrounding HTML anchor.
3348 * @param bool $accesshide True if text should be hidden (for screen readers only).
3349 * @param string $addclass Additional class names for the link, or the arrow character.
3350 * @return string HTML string.
3352 function link_arrow_right($text, $url='', $accesshide=false, $addclass='') {
3353 global $THEME;
3354 check_theme_arrows();
3355 $arrowclass = 'arrow ';
3356 if (! $url) {
3357 $arrowclass .= $addclass;
3359 $arrow = '<span class="'.$arrowclass.'">'.$THEME->rarrow.'</span>';
3360 $htmltext = '';
3361 if ($text) {
3362 $htmltext = $text.'&nbsp;';
3363 if ($accesshide) {
3364 $htmltext = get_accesshide($htmltext);
3367 if ($url) {
3368 $class = '';
3369 if ($addclass) {
3370 $class =" class=\"$addclass\"";
3372 return '<a'.$class.' href="'.$url.'" title="'.preg_replace('/<.*?>/','',$text).'">'.$htmltext.$arrow.'</a>';
3374 return $htmltext.$arrow;
3378 * Return the left arrow with text ('previous'), and optionally embedded in a link.
3379 * See function above, check_theme_arrows.
3380 * @param string $text HTML/plain text label (set to blank only for breadcrumb separator cases).
3381 * @param string $url An optional link to use in a surrounding HTML anchor.
3382 * @param bool $accesshide True if text should be hidden (for screen readers only).
3383 * @param string $addclass Additional class names for the link, or the arrow character.
3384 * @return string HTML string.
3386 function link_arrow_left($text, $url='', $accesshide=false, $addclass='') {
3387 global $THEME;
3388 check_theme_arrows();
3389 $arrowclass = 'arrow ';
3390 if (! $url) {
3391 $arrowclass .= $addclass;
3393 $arrow = '<span class="'.$arrowclass.'">'.$THEME->larrow.'</span>';
3394 $htmltext = '';
3395 if ($text) {
3396 $htmltext = '&nbsp;'.$text;
3397 if ($accesshide) {
3398 $htmltext = get_accesshide($htmltext);
3401 if ($url) {
3402 $class = '';
3403 if ($addclass) {
3404 $class =" class=\"$addclass\"";
3406 return '<a'.$class.' href="'.$url.'" title="'.preg_replace('/<.*?>/','',$text).'">'.$arrow.$htmltext.'</a>';
3408 return $arrow.$htmltext;
3412 * Return a HTML element with the class "accesshide", for accessibility.
3413 * Please use cautiously - where possible, text should be visible!
3414 * @param string $text Plain text.
3415 * @param string $elem Lowercase element name, default "span".
3416 * @param string $class Additional classes for the element.
3417 * @param string $attrs Additional attributes string in the form, "name='value' name2='value2'"
3418 * @return string HTML string.
3420 function get_accesshide($text, $elem='span', $class='', $attrs='') {
3421 return "<$elem class=\"accesshide $class\" $attrs>$text</$elem>";
3425 * Return the breadcrumb trail navigation separator.
3426 * @return string HTML string.
3428 function get_separator() {
3429 //Accessibility: the 'hidden' slash is preferred for screen readers.
3430 return ' '.link_arrow_right($text='/', $url='', $accesshide=true, 'sep').' ';
3434 * Prints breadcrumb trail of links, called in theme/-/header.html
3436 * @uses $CFG
3437 * @param mixed $navigation The breadcrumb navigation string to be printed
3438 * @param string $separator The breadcrumb trail separator. The default 0 leads to the use
3439 * of $THEME->rarrow, themes could use '&rarr;', '/', or '' for a style-sheet solution.
3440 * @param boolean $return False to echo the breadcrumb string (default), true to return it.
3442 function print_navigation ($navigation, $separator=0, $return=false) {
3443 global $CFG, $THEME;
3444 $output = '';
3446 if (0 === $separator) {
3447 $separator = get_separator();
3449 else {
3450 $separator = '<span class="sep">'. $separator .'</span>';
3453 if ($navigation) {
3455 if (is_newnav($navigation)) {
3456 if ($return) {
3457 return($navigation['navlinks']);
3458 } else {
3459 echo $navigation['navlinks'];
3460 return;
3462 } else {
3463 debugging('Navigation needs to be updated to use build_navigation()', DEBUG_DEVELOPER);
3466 if (!is_array($navigation)) {
3467 $ar = explode('->', $navigation);
3468 $navigation = array();
3470 foreach ($ar as $a) {
3471 if (strpos($a, '</a>') === false) {
3472 $navigation[] = array('title' => $a, 'url' => '');
3473 } else {
3474 if (preg_match('/<a.*href="([^"]*)">(.*)<\/a>/', $a, $matches)) {
3475 $navigation[] = array('title' => $matches[2], 'url' => $matches[1]);
3481 if (! $site = get_site()) {
3482 $site = new object();
3483 $site->shortname = get_string('home');
3486 //Accessibility: breadcrumb links now in a list, &raquo; replaced with a 'silent' character.
3487 $output .= get_accesshide(get_string('youarehere','access'), 'h2')."<ul>\n";
3489 $output .= '<li class="first">'."\n".'<a '.$CFG->frametarget.' onclick="this.target=\''.$CFG->framename.'\'" href="'
3490 .$CFG->wwwroot.((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))
3491 && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
3492 ? '/my' : '') .'/">'. format_string($site->shortname) ."</a>\n</li>\n";
3495 foreach ($navigation as $navitem) {
3496 $title = trim(strip_tags(format_string($navitem['title'], false)));
3497 $url = $navitem['url'];
3499 if (empty($url)) {
3500 $output .= '<li class="first">'."$separator $title</li>\n";
3501 } else {
3502 $output .= '<li class="first">'."$separator\n<a ".$CFG->frametarget.' onclick="this.target=\''.$CFG->framename.'\'" href="'
3503 .$url.'">'."$title</a>\n</li>\n";
3507 $output .= "</ul>\n";
3510 if ($return) {
3511 return $output;
3512 } else {
3513 echo $output;
3518 * This function will build the navigation string to be used by print_header
3519 * and others.
3521 * It automatically generates the site and course level (if appropriate) links.
3523 * If you pass in a $cm object, the method will also generate the activity (e.g. 'Forums')
3524 * and activityinstances (e.g. 'General Developer Forum') navigation levels.
3526 * If you want to add any further navigation links after the ones this function generates,
3527 * the pass an array of extra link arrays like this:
3528 * array(
3529 * array('name' => $linktext1, 'link' => $url1, 'type' => $linktype1),
3530 * array('name' => $linktext2, 'link' => $url2, 'type' => $linktype2)
3532 * The normal case is to just add one further link, for example 'Editing forum' after
3533 * 'General Developer Forum', with no link.
3534 * To do that, you need to pass
3535 * array(array('name' => $linktext, 'link' => '', 'type' => 'title'))
3536 * However, becuase this is a very common case, you can use a shortcut syntax, and just
3537 * pass the string 'Editing forum', instead of an array as $extranavlinks.
3539 * At the moment, the link types only have limited significance. Type 'activity' is
3540 * recognised in order to implement the $CFG->hideactivitytypenavlink feature. Types
3541 * that are known to appear are 'home', 'course', 'activity', 'activityinstance' and 'title'.
3542 * This really needs to be documented better. In the mean time, try to be consistent, it will
3543 * enable people to customise the navigation more in future.
3545 * When passing a $cm object, the fields used are $cm->modname, $cm->name and $cm->course.
3546 * If you get the $cm object using the function get_coursemodule_from_instance or
3547 * get_coursemodule_from_id (as recommended) then this will be done for you automatically.
3548 * If you don't have $cm->modname or $cm->name, this fuction will attempt to find them using
3549 * the $cm->module and $cm->instance fields, but this takes extra database queries, so a
3550 * warning is printed in developer debug mode.
3552 * @uses $CFG
3553 * @uses $THEME
3555 * @param mixed $extranavlinks - Normally an array of arrays, keys: name, link, type. If you
3556 * only want one extra item with no link, you can pass a string instead. If you don't want
3557 * any extra links, pass an empty string.
3558 * @param mixed $cm - optionally the $cm object, if you want this function to generate the
3559 * activity and activityinstance levels of navigation too.
3561 * @return $navigation as an object so it can be differentiated from old style
3562 * navigation strings.
3564 function build_navigation($extranavlinks, $cm = null) {
3565 global $CFG, $COURSE;
3567 if (is_string($extranavlinks)) {
3568 if ($extranavlinks == '') {
3569 $extranavlinks = array();
3570 } else {
3571 $extranavlinks = array(array('name' => $extranavlinks, 'link' => '', 'type' => 'title'));
3575 $navlinks = array();
3577 //Site name
3578 if ($site = get_site()) {
3579 $navlinks[] = array(
3580 'name' => format_string($site->shortname),
3581 'link' => "$CFG->wwwroot/",
3582 'type' => 'home');
3585 // Course name, if appropriate.
3586 if (isset($COURSE) && $COURSE->id != SITEID) {
3587 $navlinks[] = array(
3588 'name' => format_string($COURSE->shortname),
3589 'link' => "$CFG->wwwroot/course/view.php?id=$COURSE->id",
3590 'type' => 'course');
3593 // Activity type and instance, if appropriate.
3594 if (is_object($cm)) {
3595 if (!isset($cm->modname)) {
3596 debugging('The field $cm->modname should be set if you call build_navigation with '.
3597 'a $cm parameter. If you get $cm using get_coursemodule_from_instance or ',
3598 'get_coursemodule_from_id, this will be done automatically.', DEBUG_DEVELOPER);
3599 if (!$cm->modname = get_field('modules', 'name', 'id', $cm->module)) {
3600 error('Cannot get the module type in build navigation.');
3603 if (!isset($cm->name)) {
3604 debugging('The field $cm->name should be set if you call build_navigation with '.
3605 'a $cm parameter. If you get $cm using get_coursemodule_from_instance or ',
3606 'get_coursemodule_from_id, this will be done automatically.', DEBUG_DEVELOPER);
3607 if (!$cm->name = get_field($cm->modname, 'name', 'id', $cm->instance)) {
3608 error('Cannot get the module name in build navigation.');
3611 $navlinks[] = array(
3612 'name' => get_string('modulenameplural', $cm->modname),
3613 'link' => $CFG->wwwroot . '/mod/' . $cm->modname . '/index.php?id=' . $cm->course,
3614 'type' => 'activity');
3615 $navlinks[] = array(
3616 'name' => format_string($cm->name),
3617 'link' => $CFG->wwwroot . '/mod/' . $cm->modname . '/view.php?id=' . $cm->id,
3618 'type' => 'activityinstance');
3621 //Merge in extra navigation links
3622 $navlinks = array_merge($navlinks, $extranavlinks);
3624 // Work out whether we should be showing the activity (e.g. Forums) link.
3625 // Note: build_navigation() is called from many places --
3626 // install & upgrade for example -- where we cannot count on the
3627 // roles infrastructure to be defined. Hence the $CFG->rolesactive check.
3628 if (!isset($CFG->hideactivitytypenavlink)) {
3629 $CFG->hideactivitytypenavlink = 0;
3631 if ($CFG->hideactivitytypenavlink == 2) {
3632 $hideactivitylink = true;
3633 } else if ($CFG->hideactivitytypenavlink == 1 && $CFG->rolesactive &&
3634 !empty($COURSE->id) && $COURSE->id != SITEID) {
3635 if (!isset($COURSE->context)) {
3636 $COURSE->context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
3638 $hideactivitylink = !has_capability('moodle/course:manageactivities', $COURSE->context);
3639 } else {
3640 $hideactivitylink = false;
3643 //Construct an unordered list from $navlinks
3644 //Accessibility: heading hidden from visual browsers by default.
3645 $navigation = get_accesshide(get_string('youarehere','access'), 'h2')." <ul>\n";
3646 $lastindex = count($navlinks) - 1;
3647 $i = -1; // Used to count the times, so we know when we get to the last item.
3648 $first = true;
3649 foreach ($navlinks as $navlink) {
3650 $i++;
3651 $last = ($i == $lastindex);
3652 if (!is_array($navlink)) {
3653 continue;
3655 if ($navlink['type'] == 'activity' && !$last && $hideactivitylink) {
3656 continue;
3658 $navigation .= '<li class="first">';
3659 if (!$first) {
3660 $navigation .= get_separator();
3662 if ((!empty($navlink['link'])) && !$last) {
3663 $navigation .= "<a onclick=\"this.target='$CFG->framename'\" href=\"{$navlink['link']}\">";
3665 $navigation .= "{$navlink['name']}";
3666 if ((!empty($navlink['link'])) && !$last) {
3667 $navigation .= "</a>";
3670 $navigation .= "</li>";
3671 $first = false;
3673 $navigation .= "</ul>";
3675 return(array('newnav' => true, 'navlinks' => $navigation));
3680 * Prints a string in a specified size (retained for backward compatibility)
3682 * @param string $text The text to be displayed
3683 * @param int $size The size to set the font for text display.
3685 function print_headline($text, $size=2, $return=false) {
3686 $output = print_heading($text, '', $size, true);
3687 if ($return) {
3688 return $output;
3689 } else {
3690 echo $output;
3695 * Prints text in a format for use in headings.
3697 * @param string $text The text to be displayed
3698 * @param string $align The alignment of the printed paragraph of text
3699 * @param int $size The size to set the font for text display.
3701 function print_heading($text, $align='', $size=2, $class='main', $return=false) {
3702 if ($align) {
3703 $align = ' style="text-align:'.$align.';"';
3705 if ($class) {
3706 $class = ' class="'.$class.'"';
3708 $output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
3710 if ($return) {
3711 return $output;
3712 } else {
3713 echo $output;
3718 * Centered heading with attached help button (same title text)
3719 * and optional icon attached
3721 * @param string $text The text to be displayed
3722 * @param string $helppage The help page to link to
3723 * @param string $module The module whose help should be linked to
3724 * @param string $icon Image to display if needed
3726 function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
3727 $output = '';
3728 $output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
3729 $output .= helpbutton($helppage, $text, $module, true, false, '', true);
3730 $output .= '</h2>';
3732 if ($return) {
3733 return $output;
3734 } else {
3735 echo $output;
3740 function print_heading_block($heading, $class='', $return=false) {
3741 //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
3742 $output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
3744 if ($return) {
3745 return $output;
3746 } else {
3747 echo $output;
3753 * Print a link to continue on to another page.
3755 * @uses $CFG
3756 * @param string $link The url to create a link to.
3758 function print_continue($link, $return=false) {
3760 global $CFG;
3762 // in case we are logging upgrade in admin/index.php stop it
3763 if (function_exists('upgrade_log_finish')) {
3764 upgrade_log_finish();
3767 $output = '';
3769 if ($link == '') {
3770 if (!empty($_SERVER['HTTP_REFERER'])) {
3771 $link = $_SERVER['HTTP_REFERER'];
3772 $link = str_replace('&', '&amp;', $link); // make it valid XHTML
3773 } else {
3774 $link = $CFG->wwwroot .'/';
3778 $options = array();
3779 $linkparts = parse_url(str_replace('&amp;', '&', $link));
3780 if (isset($linkparts['query'])) {
3781 parse_str($linkparts['query'], $options);
3784 $output .= '<div class="continuebutton">';
3786 $output .= print_single_button($link, $options, get_string('continue'), 'get', $CFG->framename, true);
3787 $output .= '</div>'."\n";
3789 if ($return) {
3790 return $output;
3791 } else {
3792 echo $output;
3798 * Print a message in a standard themed box.
3799 * Replaces print_simple_box (see deprecatedlib.php)
3801 * @param string $message, the content of the box
3802 * @param string $classes, space-separated class names.
3803 * @param string $ids, space-separated id names.
3804 * @param boolean $return, return as string or just print it
3806 function print_box($message, $classes='generalbox', $ids='', $return=false) {
3808 $output = print_box_start($classes, $ids, true);
3809 $output .= stripslashes_safe($message);
3810 $output .= print_box_end(true);
3812 if ($return) {
3813 return $output;
3814 } else {
3815 echo $output;
3820 * Starts a box using divs
3821 * Replaces print_simple_box_start (see deprecatedlib.php)
3823 * @param string $classes, space-separated class names.
3824 * @param string $ids, space-separated id names.
3825 * @param boolean $return, return as string or just print it
3827 function print_box_start($classes='generalbox', $ids='', $return=false) {
3828 $output = '';
3830 if ($ids) {
3831 $ids = ' id="'.$ids.'"';
3834 $output .= '<div'.$ids.' class="box '.$classes.'">';
3836 if ($return) {
3837 return $output;
3838 } else {
3839 echo $output;
3845 * Simple function to end a box (see above)
3846 * Replaces print_simple_box_end (see deprecatedlib.php)
3848 * @param boolean $return, return as string or just print it
3850 function print_box_end($return=false) {
3851 $output = '</div>';
3852 if ($return) {
3853 return $output;
3854 } else {
3855 echo $output;
3861 * Function adds custom_corners to boxes
3863 * @param string $message, the content of the box
3864 * @param string $classes, space-separated class names.
3865 * @param string $ids, space-separated id names.
3866 * @param boolean $return, return as string or just print it
3868 function print_custom_corners_box($message, $classes='generalbox', $ids='', $return=false) {
3870 $output = print_custom_corners_box_start($classes, $ids, true);
3871 $output .= stripslashes_safe($message);
3872 $output .= print_custom_corners_box_end(true);
3874 if ($return) {
3875 return $output;
3876 } else {
3877 echo $output;
3883 * Function adds custom_corners to boxes
3884 * Calls print_box_start
3886 * @param string $classes, space-separated class names.
3887 * @param string $ids, space-separated id names.
3888 * @param boolean $return, return as string or just print it
3890 function print_custom_corners_box_start($classes='generalbox', $ids='', $return=false) {
3891 global $CFG, $THEME;
3893 $output = print_box_start('ccbox '.$classes, $ids, true);
3895 if (!empty($THEME->customcorners)) {
3896 require_once($CFG->dirroot.'/lib/custom_corners_lib.php');
3898 $output .= print_custom_corners_start(true, true);
3901 if ($return) {
3902 return $output;
3903 } else {
3904 echo $output;
3910 * Function adds custom_corners to boxes
3911 * Calls print_box_end
3913 * @param boolean $return, return as string or just print it
3915 function print_custom_corners_box_end($return=false) {
3916 global $CFG, $THEME;
3918 $output = '';
3920 if (!empty($THEME->customcorners)) {
3921 require_once($CFG->dirroot.'/lib/custom_corners_lib.php');
3923 $output .= print_custom_corners_end(true);
3926 $output .= print_box_end(true);;
3928 if ($return) {
3929 return $output;
3930 } else {
3931 echo $output;
3937 * Print a self contained form with a single submit button.
3939 * @param string $link ?
3940 * @param array $options ?
3941 * @param string $label ?
3942 * @param string $method ?
3943 * @todo Finish documenting this function
3945 function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false, $tooltip='') {
3946 $output = '';
3947 $link = str_replace('"', '&quot;', $link); //basic XSS protection
3948 $output .= '<div class="singlebutton">';
3949 // taking target out, will need to add later target="'.$target.'"
3950 $output .= '<form action="'. $link .'" method="'. $method .'">';
3951 $output .= '<div>';
3952 if ($options) {
3953 foreach ($options as $name => $value) {
3954 $output .= '<input type="hidden" name="'. $name .'" value="'. s($value) .'" />';
3957 if ($tooltip) {
3958 $tooltip = 'title="' . s($tooltip) . '"';
3959 } else {
3960 $tooltip = '';
3962 $output .= '<input type="submit" value="'. s($label) .'" ' . $tooltip . ' /></div></form></div>';
3964 if ($return) {
3965 return $output;
3966 } else {
3967 echo $output;
3973 * Print a spacer image with the option of including a line break.
3975 * @param int $height ?
3976 * @param int $width ?
3977 * @param boolean $br ?
3978 * @todo Finish documenting this function
3980 function print_spacer($height=1, $width=1, $br=true, $return=false) {
3981 global $CFG;
3982 $output = '';
3984 $output .= '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
3985 if ($br) {
3986 $output .= '<br />'."\n";
3989 if ($return) {
3990 return $output;
3991 } else {
3992 echo $output;
3997 * Given the path to a picture file in a course, or a URL,
3998 * this function includes the picture in the page.
4000 * @param string $path ?
4001 * @param int $courseid ?
4002 * @param int $height ?
4003 * @param int $width ?
4004 * @param string $link ?
4005 * @todo Finish documenting this function
4007 function print_file_picture($path, $courseid=0, $height='', $width='', $link='', $return=false) {
4008 global $CFG;
4009 $output = '';
4011 if ($height) {
4012 $height = 'height="'. $height .'"';
4014 if ($width) {
4015 $width = 'width="'. $width .'"';
4017 if ($link) {
4018 $output .= '<a href="'. $link .'">';
4020 if (substr(strtolower($path), 0, 7) == 'http://') {
4021 $output .= '<img style="height:'.$height.'px;width:'.$width.'px;" src="'. $path .'" />';
4023 } else if ($courseid) {
4024 $output .= '<img style="height:'.$height.'px;width:'.$width.'px;" src="';
4025 if ($CFG->slasharguments) { // Use this method if possible for better caching
4026 $output .= $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
4027 } else {
4028 $output .= $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
4030 $output .= '" />';
4031 } else {
4032 $output .= 'Error: must pass URL or course';
4034 if ($link) {
4035 $output .= '</a>';
4038 if ($return) {
4039 return $output;
4040 } else {
4041 echo $output;
4046 * Print the specified user's avatar.
4048 * If you pass a $user object that has id, picture, imagealt, firstname, lastname
4049 * you save a DB query.
4051 * @param int $user takes a userid, or a userobj
4052 * @param int $courseid ?
4053 * @param boolean $picture Print the user picture?
4054 * @param int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
4055 * @param boolean $return If false print picture to current page, otherwise return the output as string
4056 * @param boolean $link Enclose printed image in a link to view specified course?
4057 * @param string $target link target attribute
4058 * @param boolean $alttext use username or userspecified text in image alt attribute
4059 * return string
4060 * @todo Finish documenting this function
4062 function print_user_picture($user, $courseid, $picture=NULL, $size=0, $return=false, $link=true, $target='', $alttext=true) {
4063 global $CFG, $HTTPSPAGEREQUIRED;
4065 $needrec = false;
4066 // only touch the DB if we are missing data...
4067 if (is_object($user)) {
4068 // Note - both picture and imagealt _can_ be empty
4069 // what we are trying to see here is if they have been fetched
4070 // from the DB. We should use isset() _except_ that some installs
4071 // have those fields as nullable, and isset() will return false
4072 // on null. The only safe thing is to ask array_key_exists()
4073 // which works on objects. property_exists() isn't quite
4074 // what we want here...
4075 if (! (array_key_exists('picture', $user)
4076 && ($alttext && array_key_exists('imagealt', $user)
4077 || (isset($user->firstname) && isset($user->lastname)))) ) {
4078 $needrec = true;
4079 $user = $user->id;
4081 } else {
4082 if ($alttext) {
4083 // we need firstname, lastname, imagealt, can't escape...
4084 $needrec = true;
4085 } else {
4086 $userobj = new StdClass; // fake it to save DB traffic
4087 $userobj->id = $user;
4088 $userobj->picture = $picture;
4089 $user = clone($userobj);
4090 unset($userobj);
4093 if ($needrec) {
4094 $user = get_record('user','id',$user, '', '', '', '', 'id,firstname,lastname,imagealt');
4097 if ($link) {
4098 if ($target) {
4099 $target=' target="_blank"';
4101 $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $courseid .'">';
4102 } else {
4103 $output = '';
4105 if (empty($size)) {
4106 $file = 'f2';
4107 $size = 35;
4108 } else if ($size === true or $size == 1) {
4109 $file = 'f1';
4110 $size = 100;
4111 } else if ($size >= 50) {
4112 $file = 'f1';
4113 } else {
4114 $file = 'f2';
4116 $class = "userpicture";
4117 if (!empty($HTTPSPAGEREQUIRED)) {
4118 $wwwroot = $CFG->httpswwwroot;
4119 } else {
4120 $wwwroot = $CFG->wwwroot;
4123 if (is_null($picture)) {
4124 $picture = $user->picture;
4127 if ($picture) { // Print custom user picture
4128 if ($CFG->slasharguments) { // Use this method if possible for better caching
4129 $src = $wwwroot .'/user/pix.php/'. $user->id .'/'. $file .'.jpg';
4130 } else {
4131 $src = $wwwroot .'/user/pix.php?file=/'. $user->id .'/'. $file .'.jpg';
4133 } else { // Print default user pictures (use theme version if available)
4134 $class .= " defaultuserpic";
4135 $src = "$CFG->pixpath/u/$file.png";
4137 $imagealt = '';
4138 if ($alttext) {
4139 if (!empty($user->imagealt)) {
4140 $imagealt = $user->imagealt;
4141 } else {
4142 $imagealt = get_string('pictureof','',fullname($user));
4146 $output .= '<img class="'.$class.'" src="'.$src.'" alt="'.s($imagealt).'" />';
4147 if ($link) {
4148 $output .= '</a>';
4151 if ($return) {
4152 return $output;
4153 } else {
4154 echo $output;
4159 * Prints a summary of a user in a nice little box.
4161 * @uses $CFG
4162 * @uses $USER
4163 * @param user $user A {@link $USER} object representing a user
4164 * @param course $course A {@link $COURSE} object representing a course
4166 function print_user($user, $course, $messageselect=false, $return=false) {
4168 global $CFG, $USER;
4170 $output = '';
4172 static $string;
4173 static $datestring;
4174 static $countries;
4176 $context = get_context_instance(CONTEXT_COURSE, $course->id);
4177 if (isset($user->context->id)) {
4178 $usercontext = get_context_instance_by_id($user->context->id);
4181 if (empty($string)) { // Cache all the strings for the rest of the page
4183 $string->email = get_string('email');
4184 $string->location = get_string('location');
4185 $string->lastaccess = get_string('lastaccess');
4186 $string->activity = get_string('activity');
4187 $string->unenrol = get_string('unenrol');
4188 $string->loginas = get_string('loginas');
4189 $string->fullprofile = get_string('fullprofile');
4190 $string->role = get_string('role');
4191 $string->name = get_string('name');
4192 $string->never = get_string('never');
4194 $datestring->day = get_string('day');
4195 $datestring->days = get_string('days');
4196 $datestring->hour = get_string('hour');
4197 $datestring->hours = get_string('hours');
4198 $datestring->min = get_string('min');
4199 $datestring->mins = get_string('mins');
4200 $datestring->sec = get_string('sec');
4201 $datestring->secs = get_string('secs');
4202 $datestring->year = get_string('year');
4203 $datestring->years = get_string('years');
4205 $countries = get_list_of_countries();
4208 /// Get the hidden field list
4209 if (has_capability('moodle/course:viewhiddenuserfields', $context)) {
4210 $hiddenfields = array();
4211 } else {
4212 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
4215 $output .= '<table class="userinfobox">';
4216 $output .= '<tr>';
4217 $output .= '<td class="left side">';
4218 $output .= print_user_picture($user, $course->id, $user->picture, true, true);
4219 $output .= '</td>';
4220 $output .= '<td class="content">';
4221 $output .= '<div class="username">'.fullname($user, has_capability('moodle/site:viewfullnames', $context)).'</div>';
4222 $output .= '<div class="info">';
4223 if (!empty($user->role) and ($user->role <> $course->teacher)) {
4224 $output .= $string->role .': '. $user->role .'<br />';
4226 if ($user->maildisplay == 1 or ($user->maildisplay == 2 and ($course->id != SITEID) and !isguest()) or
4227 has_capability('moodle/course:viewhiddenuserfields', $context)) {
4228 $output .= $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
4230 if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
4231 $output .= $string->location .': ';
4232 if ($user->city && !isset($hiddenfields['city'])) {
4233 $output .= $user->city;
4235 if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
4236 if ($user->city && !isset($hiddenfields['city'])) {
4237 $output .= ', ';
4239 $output .= $countries[$user->country];
4241 $output .= '<br />';
4244 if (!isset($hiddenfields['lastaccess'])) {
4245 if ($user->lastaccess) {
4246 $output .= $string->lastaccess .': '. userdate($user->lastaccess);
4247 $output .= '&nbsp; ('. format_time(time() - $user->lastaccess, $datestring) .')';
4248 } else {
4249 $output .= $string->lastaccess .': '. $string->never;
4252 $output .= '</div></td><td class="links">';
4253 //link to blogs
4254 if ($CFG->bloglevel > 0) {
4255 $output .= '<a href="'.$CFG->wwwroot.'/blog/index.php?userid='.$user->id.'">'.get_string('blogs','blog').'</a><br />';
4257 //link to notes
4258 if (has_capability('moodle/notes:manage', $context) || has_capability('moodle/notes:view', $context)) {
4259 $output .= '<a href="'.$CFG->wwwroot.'/notes/index.php?course=' . $course->id. '&amp;user='.$user->id.'">'.get_string('notes','notes').'</a><br />';
4262 if (has_capability('moodle/user:viewuseractivitiesreport', $context) || (isset($usercontext) && has_capability('moodle/user:viewuseractivitiesreport', $usercontext))) {
4263 $timemidnight = usergetmidnight(time());
4264 $output .= '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
4266 if (has_capability('moodle/role:assign', $context, NULL)) { // Includes admins
4267 $output .= '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
4269 if ($USER->id != $user->id && empty($USER->realuser) && has_capability('moodle/user:loginas', $context) &&
4270 ! has_capability('moodle/site:doanything', $context, $user->id, false)) {
4271 $output .= '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'&amp;sesskey='. sesskey() .'">'. $string->loginas .'</a><br />';
4273 $output .= '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
4275 if (!empty($messageselect)) {
4276 $output .= '<br /><input type="checkbox" name="user'.$user->id.'" /> ';
4279 $output .= '</td></tr></table>';
4281 if ($return) {
4282 return $output;
4283 } else {
4284 echo $output;
4289 * Print a specified group's avatar.
4291 * @param group $group A single {@link group} object OR array of groups.
4292 * @param int $courseid The course ID.
4293 * @param boolean $large Default small picture, or large.
4294 * @param boolean $return If false print picture, otherwise return the output as string
4295 * @param boolean $link Enclose image in a link to view specified course?
4296 * @return string
4297 * @todo Finish documenting this function
4299 function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) {
4300 global $CFG;
4302 if (is_array($group)) {
4303 $output = '';
4304 foreach($group as $g) {
4305 $output .= print_group_picture($g, $courseid, $large, true, $link);
4307 if ($return) {
4308 return $output;
4309 } else {
4310 echo $output;
4311 return;
4315 $context = get_context_instance(CONTEXT_COURSE, $courseid);
4317 if ($group->hidepicture and !has_capability('moodle/course:managegroups', $context)) {
4318 return '';
4321 if ($link or has_capability('moodle/site:accessallgroups', $context)) {
4322 $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
4323 } else {
4324 $output = '';
4326 if ($large) {
4327 $file = 'f1';
4328 $size = 100;
4329 } else {
4330 $file = 'f2';
4331 $size = 35;
4333 if ($group->picture) { // Print custom group picture
4334 if ($CFG->slasharguments) { // Use this method if possible for better caching
4335 $output .= '<img class="grouppicture" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
4336 ' style="width:'.$size.'px;height:'.$size.'px;" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
4337 } else {
4338 $output .= '<img class="grouppicture" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
4339 ' style="width:'.$size.'px;height:'.$size.'px;" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
4342 if ($link or has_capability('moodle/site:accessallgroups', $context)) {
4343 $output .= '</a>';
4346 if ($return) {
4347 return $output;
4348 } else {
4349 echo $output;
4354 * Print a png image.
4356 * @param string $url ?
4357 * @param int $sizex ?
4358 * @param int $sizey ?
4359 * @param boolean $return ?
4360 * @param string $parameters ?
4361 * @todo Finish documenting this function
4363 function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
4364 global $CFG;
4365 static $recentIE;
4367 if (!isset($recentIE)) {
4368 $recentIE = check_browser_version('MSIE', '5.0');
4371 if ($recentIE) { // work around the HORRIBLE bug IE has with alpha transparencies
4372 $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
4373 ' class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
4374 ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
4375 "'$url', sizingMethod='scale') ".
4376 ' '. $parameters .' />';
4377 } else {
4378 $output .= '<img src="'. $url .'" style="width: '. $sizex .'px; height: '. $sizey .'px; '. $parameters .' />';
4381 if ($return) {
4382 return $output;
4383 } else {
4384 echo $output;
4389 * Print a nicely formatted table.
4391 * @param array $table is an object with several properties.
4392 * <ul>
4393 * <li>$table->head - An array of heading names.
4394 * <li>$table->align - An array of column alignments
4395 * <li>$table->size - An array of column sizes
4396 * <li>$table->wrap - An array of "nowrap"s or nothing
4397 * <li>$table->data[] - An array of arrays containing the data.
4398 * <li>$table->width - A percentage of the page
4399 * <li>$table->tablealign - Align the whole table
4400 * <li>$table->cellpadding - Padding on each cell
4401 * <li>$table->cellspacing - Spacing between cells
4402 * <li>$table->class - class attribute to put on the table
4403 * <li>$table->id - id attribute to put on the table.
4404 * <li>$table->rowclass[] - classes to add to particular rows.
4405 * </ul>
4406 * @param bool $return whether to return an output string or echo now
4407 * @return boolean or $string
4408 * @todo Finish documenting this function
4410 function print_table($table, $return=false) {
4411 $output = '';
4413 if (isset($table->align)) {
4414 foreach ($table->align as $key => $aa) {
4415 if ($aa) {
4416 $align[$key] = ' text-align:'. fix_align_rtl($aa) .';'; // Fix for RTL languages
4417 } else {
4418 $align[$key] = '';
4422 if (isset($table->size)) {
4423 foreach ($table->size as $key => $ss) {
4424 if ($ss) {
4425 $size[$key] = ' width:'. $ss .';';
4426 } else {
4427 $size[$key] = '';
4431 if (isset($table->wrap)) {
4432 foreach ($table->wrap as $key => $ww) {
4433 if ($ww) {
4434 $wrap[$key] = ' white-space:nowrap;';
4435 } else {
4436 $wrap[$key] = '';
4441 if (empty($table->width)) {
4442 $table->width = '80%';
4445 if (empty($table->tablealign)) {
4446 $table->tablealign = 'center';
4449 if (empty($table->cellpadding)) {
4450 $table->cellpadding = '5';
4453 if (empty($table->cellspacing)) {
4454 $table->cellspacing = '1';
4457 if (empty($table->class)) {
4458 $table->class = 'generaltable';
4461 $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
4463 $output .= '<table width="'.$table->width.'" ';
4464 $output .= " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class boxalign$table->tablealign\" $tableid>\n";
4466 $countcols = 0;
4468 if (!empty($table->head)) {
4469 $countcols = count($table->head);
4470 $output .= '<tr>';
4471 foreach ($table->head as $key => $heading) {
4473 if (!isset($size[$key])) {
4474 $size[$key] = '';
4476 if (!isset($align[$key])) {
4477 $align[$key] = '';
4480 $output .= '<th class="header c'.$key.'" scope="col">'. $heading .'</th>';
4481 // commenting the following code out as <th style does not validate MDL-7861
4482 //$output .= '<th sytle="vertical-align:top;'. $align[$key].$size[$key] .';white-space:nowrap;" class="header c'.$key.'" scope="col">'. $heading .'</th>';
4484 $output .= '</tr>'."\n";
4487 if (!empty($table->data)) {
4488 $oddeven = 1;
4489 foreach ($table->data as $key => $row) {
4490 $oddeven = $oddeven ? 0 : 1;
4491 if (!isset($table->rowclass[$key])) {
4492 $table->rowclass[$key] = '';
4494 $output .= '<tr class="r'.$oddeven.' '.$table->rowclass[$key].'">'."\n";
4495 if ($row == 'hr' and $countcols) {
4496 $output .= '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
4497 } else { /// it's a normal row of data
4498 foreach ($row as $key => $item) {
4499 if (!isset($size[$key])) {
4500 $size[$key] = '';
4502 if (!isset($align[$key])) {
4503 $align[$key] = '';
4505 if (!isset($wrap[$key])) {
4506 $wrap[$key] = '';
4508 $output .= '<td style="'. $align[$key].$size[$key].$wrap[$key] .'" class="cell c'.$key.'">'. $item .'</td>';
4511 $output .= '</tr>'."\n";
4514 $output .= '</table>'."\n";
4516 if ($return) {
4517 return $output;
4520 echo $output;
4521 return true;
4525 * Creates a nicely formatted table and returns it.
4527 * @param array $table is an object with several properties.
4528 * <ul<li>$table->head - An array of heading names.
4529 * <li>$table->align - An array of column alignments
4530 * <li>$table->size - An array of column sizes
4531 * <li>$table->wrap - An array of "nowrap"s or nothing
4532 * <li>$table->data[] - An array of arrays containing the data.
4533 * <li>$table->class - A css class name
4534 * <li>$table->fontsize - Is the size of all the text
4535 * <li>$table->tablealign - Align the whole table
4536 * <li>$table->width - A percentage of the page
4537 * <li>$table->cellpadding - Padding on each cell
4538 * <li>$table->cellspacing - Spacing between cells
4539 * </ul>
4540 * @return string
4541 * @todo Finish documenting this function
4543 function make_table($table) {
4545 if (isset($table->align)) {
4546 foreach ($table->align as $key => $aa) {
4547 if ($aa) {
4548 $align[$key] = ' align="'. $aa .'"';
4549 } else {
4550 $align[$key] = '';
4554 if (isset($table->size)) {
4555 foreach ($table->size as $key => $ss) {
4556 if ($ss) {
4557 $size[$key] = ' width="'. $ss .'"';
4558 } else {
4559 $size[$key] = '';
4563 if (isset($table->wrap)) {
4564 foreach ($table->wrap as $key => $ww) {
4565 if ($ww) {
4566 $wrap[$key] = ' style="white-space:nowrap;" ';
4567 } else {
4568 $wrap[$key] = '';
4573 if (empty($table->width)) {
4574 $table->width = '80%';
4577 if (empty($table->tablealign)) {
4578 $table->tablealign = 'center';
4581 if (empty($table->cellpadding)) {
4582 $table->cellpadding = '5';
4585 if (empty($table->cellspacing)) {
4586 $table->cellspacing = '1';
4589 if (empty($table->class)) {
4590 $table->class = 'generaltable';
4593 if (empty($table->fontsize)) {
4594 $fontsize = '';
4595 } else {
4596 $fontsize = '<font size="'. $table->fontsize .'">';
4599 $output = '<table width="'. $table->width .'" align="'. $table->tablealign .'" ';
4600 $output .= ' cellpadding="'. $table->cellpadding .'" cellspacing="'. $table->cellspacing .'" class="'. $table->class .'">'."\n";
4602 if (!empty($table->head)) {
4603 $output .= '<tr valign="top">';
4604 foreach ($table->head as $key => $heading) {
4605 if (!isset($size[$key])) {
4606 $size[$key] = '';
4608 if (!isset($align[$key])) {
4609 $align[$key] = '';
4611 $output .= '<th valign="top" '. $align[$key].$size[$key] .' style="white-space:nowrap;" class="'. $table->class .'header" scope="col">'.$fontsize.$heading.'</th>';
4613 $output .= '</tr>'."\n";
4616 foreach ($table->data as $row) {
4617 $output .= '<tr valign="top">';
4618 foreach ($row as $key => $item) {
4619 if (!isset($size[$key])) {
4620 $size[$key] = '';
4622 if (!isset($align[$key])) {
4623 $align[$key] = '';
4625 if (!isset($wrap[$key])) {
4626 $wrap[$key] = '';
4628 $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="'. $table->class .'cell">'. $fontsize . $item .'</td>';
4630 $output .= '</tr>'."\n";
4632 $output .= '</table>'."\n";
4634 return $output;
4637 function print_recent_activity_note($time, $user, $text, $link, $return=false) {
4638 static $strftimerecent;
4639 $output = '';
4641 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
4642 $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
4644 if (empty($strftimerecent)) {
4645 $strftimerecent = get_string('strftimerecent');
4648 $date = userdate($time, $strftimerecent);
4649 $name = fullname($user, $viewfullnames);
4651 $output .= '<div class="head">';
4652 $output .= '<div class="date">'.$date.'</div> '.
4653 '<div class="name">'.fullname($user, $viewfullnames).'</div>';
4654 $output .= '</div>';
4655 $output .= '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>';
4657 if ($return) {
4658 return $output;
4659 } else {
4660 echo $output;
4666 * Prints a basic textarea field.
4668 * @uses $CFG
4669 * @param boolean $usehtmleditor ?
4670 * @param int $rows ?
4671 * @param int $cols ?
4672 * @param null $width <b>Legacy field no longer used!</b> Set to zero to get control over mincols
4673 * @param null $height <b>Legacy field no longer used!</b> Set to zero to get control over minrows
4674 * @param string $name ?
4675 * @param string $value ?
4676 * @param int $courseid ?
4677 * @todo Finish documenting this function
4679 function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0, $return=false, $id='') {
4680 /// $width and height are legacy fields and no longer used as pixels like they used to be.
4681 /// However, you can set them to zero to override the mincols and minrows values below.
4683 global $CFG, $COURSE, $HTTPSPAGEREQUIRED;
4684 static $scriptcount = 0; // For loading the htmlarea script only once.
4686 $mincols = 65;
4687 $minrows = 10;
4688 $str = '';
4690 if ($id === '') {
4691 $id = 'edit-'.$name;
4694 if ( empty($CFG->editorsrc) ) { // for backward compatibility.
4695 if (empty($courseid)) {
4696 $courseid = $COURSE->id;
4699 if ($usehtmleditor) {
4700 if (!empty($courseid) and has_capability('moodle/course:managefiles', get_context_instance(CONTEXT_COURSE, $courseid))) {
4701 $httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '&ampt;httpsrequired=1';
4702 // needed for course file area browsing in image insert plugin
4703 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
4704 $CFG->httpswwwroot .'/lib/editor/htmlarea/htmlarea.php?id='.$courseid.$httpsrequired.'"></script>'."\n" : '';
4705 } else {
4706 $httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '?httpsrequired=1';
4707 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
4708 $CFG->httpswwwroot .'/lib/editor/htmlarea/htmlarea.php'.$httpsrequired.'"></script>'."\n" : '';
4711 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
4712 $CFG->httpswwwroot .'/lib/editor/htmlarea/lang/en.php"></script>'."\n" : '';
4713 $scriptcount++;
4715 if ($height) { // Usually with legacy calls
4716 if ($rows < $minrows) {
4717 $rows = $minrows;
4720 if ($width) { // Usually with legacy calls
4721 if ($cols < $mincols) {
4722 $cols = $mincols;
4727 $str .= '<textarea class="form-textarea" id="'. $id .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'">';
4728 if ($usehtmleditor) {
4729 $str .= htmlspecialchars($value); // needed for editing of cleaned text!
4730 } else {
4731 $str .= s($value);
4733 $str .= '</textarea>'."\n";
4735 if ($usehtmleditor) {
4736 // Show shortcuts button if HTML editor is in use, but only if JavaScript is enabled (MDL-9556)
4737 $str .= '<script type="text/javascript">
4738 //<![CDATA[
4739 document.write(\''.addslashes_js(editorshortcutshelpbutton()).'\');
4740 //]]>
4741 </script>';
4744 if ($return) {
4745 return $str;
4747 echo $str;
4751 * Sets up the HTML editor on textareas in the current page.
4752 * If a field name is provided, then it will only be
4753 * applied to that field - otherwise it will be used
4754 * on every textarea in the page.
4756 * In most cases no arguments need to be supplied
4758 * @param string $name Form element to replace with HTMl editor by name
4760 function use_html_editor($name='', $editorhidebuttons='', $id='') {
4761 global $THEME;
4763 $editor = 'editor_'.md5($name); //name might contain illegal characters
4764 if ($id === '') {
4765 $id = 'edit-'.$name;
4767 echo "\n".'<script type="text/javascript" defer="defer">'."\n";
4768 echo '//<![CDATA['."\n\n"; // Extra \n is to fix odd wiki problem, MDL-8185
4769 echo "$editor = new HTMLArea('$id');\n";
4770 echo "var config = $editor.config;\n";
4772 echo print_editor_config($editorhidebuttons);
4774 if (empty($THEME->htmleditorpostprocess)) {
4775 if (empty($name)) {
4776 echo "\nHTMLArea.replaceAll($editor.config);\n";
4777 } else {
4778 echo "\n$editor.generate();\n";
4780 } else {
4781 if (empty($name)) {
4782 echo "\nvar HTML_name = '';";
4783 } else {
4784 echo "\nvar HTML_name = \"$name;\"";
4786 echo "\nvar HTML_editor = $editor;";
4788 echo '//]]>'."\n";
4789 echo '</script>'."\n";
4792 function print_editor_config($editorhidebuttons='', $return=false) {
4793 global $CFG;
4795 $str = "config.pageStyle = \"body {";
4797 if (!(empty($CFG->editorbackgroundcolor))) {
4798 $str .= " background-color: $CFG->editorbackgroundcolor;";
4801 if (!(empty($CFG->editorfontfamily))) {
4802 $str .= " font-family: $CFG->editorfontfamily;";
4805 if (!(empty($CFG->editorfontsize))) {
4806 $str .= " font-size: $CFG->editorfontsize;";
4809 $str .= " }\";\n";
4810 $str .= "config.killWordOnPaste = ";
4811 $str .= (empty($CFG->editorkillword)) ? "false":"true";
4812 $str .= ';'."\n";
4813 $str .= 'config.fontname = {'."\n";
4815 $fontlist = isset($CFG->editorfontlist) ? explode(';', $CFG->editorfontlist) : array();
4816 $i = 1; // Counter is used to get rid of the last comma.
4818 foreach ($fontlist as $fontline) {
4819 if (!empty($fontline)) {
4820 if ($i > 1) {
4821 $str .= ','."\n";
4823 list($fontkey, $fontvalue) = split(':', $fontline);
4824 $str .= '"'. $fontkey ."\":\t'". $fontvalue ."'";
4826 $i++;
4829 $str .= '};';
4831 if (!empty($editorhidebuttons)) {
4832 $str .= "\nconfig.hideSomeButtons(\" ". $editorhidebuttons ." \");\n";
4833 } else if (!empty($CFG->editorhidebuttons)) {
4834 $str .= "\nconfig.hideSomeButtons(\" ". $CFG->editorhidebuttons ." \");\n";
4837 if (!empty($CFG->editorspelling) && !empty($CFG->aspellpath)) {
4838 $str .= print_speller_code($CFG->htmleditor, true);
4841 if ($return) {
4842 return $str;
4844 echo $str;
4848 * Returns a turn edit on/off button for course in a self contained form.
4849 * Used to be an icon, but it's now a simple form button
4851 * Note that the caller is responsible for capchecks.
4853 * @uses $CFG
4854 * @uses $USER
4855 * @param int $courseid The course to update by id as found in 'course' table
4856 * @return string
4858 function update_course_icon($courseid) {
4859 global $CFG, $USER;
4861 if (!empty($USER->editing)) {
4862 $string = get_string('turneditingoff');
4863 $edit = '0';
4864 } else {
4865 $string = get_string('turneditingon');
4866 $edit = '1';
4869 return '<form '.$CFG->frametarget.' method="get" action="'.$CFG->wwwroot.'/course/view.php">'.
4870 '<div>'.
4871 '<input type="hidden" name="id" value="'.$courseid.'" />'.
4872 '<input type="hidden" name="edit" value="'.$edit.'" />'.
4873 '<input type="hidden" name="sesskey" value="'.sesskey().'" />'.
4874 '<input type="submit" value="'.$string.'" />'.
4875 '</div></form>';
4879 * Returns a little popup menu for switching roles
4881 * @uses $CFG
4882 * @uses $USER
4883 * @param int $courseid The course to update by id as found in 'course' table
4884 * @return string
4886 function switchroles_form($courseid) {
4888 global $CFG, $USER;
4891 if (!$context = get_context_instance(CONTEXT_COURSE, $courseid)) {
4892 return '';
4895 if (!empty($USER->access['rsw'][$context->path])){ // Just a button to return to normal
4896 $options = array();
4897 $options['id'] = $courseid;
4898 $options['sesskey'] = sesskey();
4899 $options['switchrole'] = 0;
4901 return print_single_button($CFG->wwwroot.'/course/view.php', $options,
4902 get_string('switchrolereturn'), 'post', '_self', true);
4905 if (has_capability('moodle/role:switchroles', $context)) {
4906 if (!$roles = get_assignable_roles($context)) {
4907 return ''; // Nothing to show!
4909 // unset default user role - it would not work
4910 unset($roles[$CFG->guestroleid]);
4911 return popup_form($CFG->wwwroot.'/course/view.php?id='.$courseid.'&amp;sesskey='.sesskey().'&amp;switchrole=',
4912 $roles, 'switchrole', '', get_string('switchroleto'), 'switchrole', get_string('switchroleto'), true);
4915 return '';
4920 * Returns a turn edit on/off button for course in a self contained form.
4921 * Used to be an icon, but it's now a simple form button
4923 * @uses $CFG
4924 * @uses $USER
4925 * @param int $courseid The course to update by id as found in 'course' table
4926 * @return string
4928 function update_mymoodle_icon() {
4930 global $CFG, $USER;
4932 if (!empty($USER->editing)) {
4933 $string = get_string('updatemymoodleoff');
4934 $edit = '0';
4935 } else {
4936 $string = get_string('updatemymoodleon');
4937 $edit = '1';
4940 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/my/index.php\">".
4941 "<div>".
4942 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4943 "<input type=\"submit\" value=\"$string\" /></div></form>";
4947 * Returns a turn edit on/off button for tag in a self contained form.
4949 * @uses $CFG
4950 * @uses $USER
4951 * @return string
4953 function update_tag_button($tagid) {
4955 global $CFG, $USER;
4957 if (!empty($USER->editing)) {
4958 $string = get_string('turneditingoff');
4959 $edit = '0';
4960 } else {
4961 $string = get_string('turneditingon');
4962 $edit = '1';
4965 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/tag/index.php\">".
4966 "<div>".
4967 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4968 "<input type=\"hidden\" name=\"id\" value=\"$tagid\" />".
4969 "<input type=\"submit\" value=\"$string\" /></div></form>";
4973 * Prints the editing button on a module "view" page
4975 * @uses $CFG
4976 * @param type description
4977 * @todo Finish documenting this function
4979 function update_module_button($moduleid, $courseid, $string) {
4980 global $CFG, $USER;
4982 if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $moduleid))) {
4983 $string = get_string('updatethis', '', $string);
4985 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/mod.php\" onsubmit=\"this.target='{$CFG->framename}'; return true\">".//hack to allow edit on framed resources
4986 "<div>".
4987 "<input type=\"hidden\" name=\"update\" value=\"$moduleid\" />".
4988 "<input type=\"hidden\" name=\"return\" value=\"true\" />".
4989 "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
4990 "<input type=\"submit\" value=\"$string\" /></div></form>";
4991 } else {
4992 return '';
4997 * Prints the editing button on a category page
4999 * @uses $CFG
5000 * @uses $USER
5001 * @param int $categoryid ?
5002 * @return string
5003 * @todo Finish documenting this function
5005 function update_category_button($categoryid) {
5006 global $CFG, $USER;
5008 if (has_capability('moodle/category:update', get_context_instance(CONTEXT_COURSECAT, $categoryid))) {
5009 if (!empty($USER->categoryediting)) {
5010 $string = get_string('turneditingoff');
5011 $edit = 'off';
5012 } else {
5013 $string = get_string('turneditingon');
5014 $edit = 'on';
5017 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/category.php\">".
5018 '<div>'.
5019 "<input type=\"hidden\" name=\"id\" value=\"$categoryid\" />".
5020 "<input type=\"hidden\" name=\"categoryedit\" value=\"$edit\" />".
5021 "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
5022 "<input type=\"submit\" value=\"$string\" /></div></form>";
5027 * Prints the editing button on categories listing
5029 * @uses $CFG
5030 * @uses $USER
5031 * @return string
5033 function update_categories_button() {
5034 global $CFG, $USER;
5036 if (has_capability('moodle/category:update', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
5037 if (!empty($USER->categoryediting)) {
5038 $string = get_string('turneditingoff');
5039 $categoryedit = 'off';
5040 } else {
5041 $string = get_string('turneditingon');
5042 $categoryedit = 'on';
5045 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/index.php\">".
5046 '<div>'.
5047 '<input type="hidden" name="categoryedit" value="'. $categoryedit .'" />'.
5048 '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />'.
5049 '<input type="submit" value="'. $string .'" /></div></form>';
5054 * Prints the editing button on search results listing
5055 * For bulk move courses to another category
5058 function update_categories_search_button($search,$page,$perpage) {
5059 global $CFG, $USER;
5061 // not sure if this capability is the best here
5062 if (has_capability('moodle/category:update', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
5063 if (!empty($USER->categoryediting)) {
5064 $string = get_string("turneditingoff");
5065 $edit = "off";
5066 $perpage = 30;
5067 } else {
5068 $string = get_string("turneditingon");
5069 $edit = "on";
5072 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/search.php\">".
5073 '<div>'.
5074 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
5075 "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
5076 "<input type=\"hidden\" name=\"search\" value=\"".s($search, true)."\" />".
5077 "<input type=\"hidden\" name=\"page\" value=\"$page\" />".
5078 "<input type=\"hidden\" name=\"perpage\" value=\"$perpage\" />".
5079 "<input type=\"submit\" value=\"".s($string)."\" /></div></form>";
5084 * Given a course and a (current) coursemodule
5085 * This function returns a small popup menu with all the
5086 * course activity modules in it, as a navigation menu
5087 * The data is taken from the serialised array stored in
5088 * the course record
5090 * @param course $course A {@link $COURSE} object.
5091 * @param course $cm A {@link $COURSE} object.
5092 * @param string $targetwindow ?
5093 * @return string
5094 * @todo Finish documenting this function
5096 function navmenu($course, $cm=NULL, $targetwindow='self') {
5098 global $CFG, $THEME, $USER;
5100 if (empty($THEME->navmenuwidth)) {
5101 $width = 50;
5102 } else {
5103 $width = $THEME->navmenuwidth;
5106 if ($cm) {
5107 $cm = $cm->id;
5110 if ($course->format == 'weeks') {
5111 $strsection = get_string('week');
5112 } else {
5113 $strsection = get_string('topic');
5115 $strjumpto = get_string('jumpto');
5117 /// Casting $course->modinfo to string prevents one notice when the field is null
5118 if (!$modinfo = unserialize((string)$course->modinfo)) {
5119 return '';
5121 $context = get_context_instance(CONTEXT_COURSE, $course->id);
5123 $section = -1;
5124 $selected = '';
5125 $url = '';
5126 $previousmod = NULL;
5127 $backmod = NULL;
5128 $nextmod = NULL;
5129 $selectmod = NULL;
5130 $logslink = NULL;
5131 $flag = false;
5132 $menu = array();
5133 $menustyle = array();
5135 $sections = get_records('course_sections','course',$course->id,'section','section,visible,summary');
5137 if (!empty($THEME->makenavmenulist)) { /// A hack to produce an XHTML navmenu list for use in themes
5138 $THEME->navmenulist = navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width, $cm);
5141 foreach ($modinfo as $mod) {
5142 if ($mod->mod == 'label') {
5143 continue;
5146 if ($mod->section > $course->numsections) { /// Don't show excess hidden sections
5147 break;
5149 $mod->id = $mod->cm;
5150 $mod->course = $course->id;
5151 if (!groups_course_module_visible($mod)) {
5152 continue;
5155 if ($mod->section > 0 and $section <> $mod->section) {
5156 $thissection = $sections[$mod->section];
5158 if ($thissection->visible or !$course->hiddensections or
5159 has_capability('moodle/course:viewhiddensections', $context)) {
5160 $thissection->summary = strip_tags(format_string($thissection->summary,true));
5161 if ($course->format == 'weeks' or empty($thissection->summary)) {
5162 $menu[] = '--'.$strsection ." ". $mod->section;
5163 } else {
5164 if (strlen($thissection->summary) < ($width-3)) {
5165 $menu[] = '--'.$thissection->summary;
5166 } else {
5167 $menu[] = '--'.substr($thissection->summary, 0, $width).'...';
5173 $section = $mod->section;
5175 //Only add visible or teacher mods to jumpmenu
5176 if ($mod->visible or has_capability('moodle/course:viewhiddenactivities',
5177 get_context_instance(CONTEXT_MODULE, $mod->cm))) {
5178 $url = $mod->mod .'/view.php?id='. $mod->cm;
5179 if ($flag) { // the current mod is the "next" mod
5180 $nextmod = $mod;
5181 $flag = false;
5183 if ($cm == $mod->cm) {
5184 $selected = $url;
5185 $selectmod = $mod;
5186 $backmod = $previousmod;
5187 $flag = true; // set flag so we know to use next mod for "next"
5188 $mod->name = $strjumpto;
5189 $strjumpto = '';
5190 } else {
5191 $mod->name = strip_tags(format_string(urldecode($mod->name),true));
5192 if (strlen($mod->name) > ($width+5)) {
5193 $mod->name = substr($mod->name, 0, $width).'...';
5195 if (!$mod->visible) {
5196 $mod->name = '('.$mod->name.')';
5199 $menu[$url] = $mod->name;
5200 if (empty($THEME->navmenuiconshide)) {
5201 $menustyle[$url] = 'style="background-image: url('.$CFG->modpixpath.'/'.$mod->mod.'/icon.gif);"'; // Unfortunately necessary to do this here
5203 $previousmod = $mod;
5206 //Accessibility: added Alt text, replaced &gt; &lt; with 'silent' character and 'accesshide' text.
5208 if ($selectmod and has_capability('moodle/site:viewreports', $context)) {
5209 $logstext = get_string('alllogs');
5210 $logslink = '<li>'."\n".'<a title="'.$logstext.'" '.
5211 $CFG->frametarget.'onclick="this.target=\''.$CFG->framename.'\';"'.' href="'.
5212 $CFG->wwwroot.'/course/report/log/index.php?chooselog=1&amp;user=0&amp;date=0&amp;id='.
5213 $course->id.'&amp;modid='.$selectmod->cm.'">'.
5214 '<img class="icon log" src="'.$CFG->pixpath.'/i/log.gif" alt="'.$logstext.'" /></a>'."\n".'</li>';
5217 if ($backmod) {
5218 $backtext= get_string('activityprev', 'access');
5219 $backmod = '<li><form action="'.$CFG->wwwroot.'/mod/'.$backmod->mod.'/view.php" '.
5220 'onclick="this.target=\''.$CFG->framename.'\';"'.'><fieldset class="invisiblefieldset">'.
5221 '<input type="hidden" name="id" value="'.$backmod->cm.'" />'.
5222 '<button type="submit" title="'.$backtext.'">'.link_arrow_left($backtext, $url='', $accesshide=true).
5223 '</button></fieldset></form></li>';
5225 if ($nextmod) {
5226 $nexttext= get_string('activitynext', 'access');
5227 $nextmod = '<li><form action="'.$CFG->wwwroot.'/mod/'.$nextmod->mod.'/view.php" '.
5228 'onclick="this.target=\''.$CFG->framename.'\';"'.'><fieldset class="invisiblefieldset">'.
5229 '<input type="hidden" name="id" value="'.$nextmod->cm.'" />'.
5230 '<button type="submit" title="'.$nexttext.'">'.link_arrow_right($nexttext, $url='', $accesshide=true).
5231 '</button></fieldset></form></li>';
5234 return '<div class="navigation">'."\n".'<ul>'.$logslink . $backmod .
5235 '<li>'.popup_form($CFG->wwwroot .'/mod/', $menu, 'navmenupopup', $selected, $strjumpto,
5236 '', '', true, $targetwindow, '', $menustyle).'</li>'.
5237 $nextmod . '</ul>'."\n".'</div>';
5241 * Given a course
5242 * This function returns a small popup menu with all the
5243 * course activity modules in it, as a navigation menu
5244 * outputs a simple list structure in XHTML
5245 * The data is taken from the serialised array stored in
5246 * the course record
5248 * @param course $course A {@link $COURSE} object.
5249 * @return string
5250 * @todo Finish documenting this function
5252 function navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width=50, $cmid=0) {
5254 global $CFG;
5256 $section = -1;
5257 $selected = '';
5258 $url = '';
5259 $previousmod = NULL;
5260 $backmod = NULL;
5261 $nextmod = NULL;
5262 $selectmod = NULL;
5263 $logslink = NULL;
5264 $flag = false;
5265 $menu = array();
5267 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
5269 $menu[] = '<ul class="navmenulist"><li class="jumpto section"><span>'.$strjumpto.'</span><ul>';
5270 foreach ($modinfo as $mod) {
5271 if ($mod->mod == 'label') {
5272 continue;
5275 if ($mod->section > $course->numsections) { /// Don't show excess hidden sections
5276 break;
5279 if ($mod->section >= 0 and $section <> $mod->section) {
5280 $thissection = $sections[$mod->section];
5282 if ($thissection->visible or !$course->hiddensections or
5283 has_capability('moodle/course:viewhiddensections', $coursecontext)) {
5284 $thissection->summary = strip_tags(format_string($thissection->summary,true));
5285 if (!empty($doneheading)) {
5286 $menu[] = '</ul></li>';
5288 if ($course->format == 'weeks' or empty($thissection->summary)) {
5289 $item = $strsection ." ". $mod->section;
5290 } else {
5291 if (strlen($thissection->summary) < ($width-3)) {
5292 $item = $thissection->summary;
5293 } else {
5294 $item = substr($thissection->summary, 0, $width).'...';
5297 $menu[] = '<li class="section"><span>'.$item.'</span>';
5298 $menu[] = '<ul>';
5299 $doneheading = true;
5303 $section = $mod->section;
5305 //Only add visible or teacher mods to jumpmenu
5306 if ($mod->visible or has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_MODULE, $mod->cm))) {
5307 $url = $mod->mod .'/view.php?id='. $mod->cm;
5308 if ($flag) { // the current mod is the "next" mod
5309 $nextmod = $mod;
5310 $flag = false;
5312 $mod->name = strip_tags(format_string(urldecode($mod->name),true));
5313 if (strlen($mod->name) > ($width+5)) {
5314 $mod->name = substr($mod->name, 0, $width).'...';
5316 if (!$mod->visible) {
5317 $mod->name = '('.$mod->name.')';
5319 $class = 'activity '.$mod->mod;
5320 $class .= ($cmid == $mod->cm) ? ' selected' : '';
5321 $menu[] = '<li class="'.$class.'">'.
5322 '<img src="'.$CFG->modpixpath.'/'.$mod->mod.'/icon.gif" alt="" />'.
5323 '<a href="'.$CFG->wwwroot.'/mod/'.$url.'">'.$mod->name.'</a></li>';
5324 $previousmod = $mod;
5327 if ($doneheading) {
5328 $menu[] = '</ul></li>';
5330 $menu[] = '</ul></li></ul>';
5332 return implode("\n", $menu);
5336 * Prints form items with the names $day, $month and $year
5338 * @param string $day fieldname
5339 * @param string $month fieldname
5340 * @param string $year fieldname
5341 * @param int $currenttime A default timestamp in GMT
5342 * @param boolean $return
5344 function print_date_selector($day, $month, $year, $currenttime=0, $return=false) {
5346 if (!$currenttime) {
5347 $currenttime = time();
5349 $currentdate = usergetdate($currenttime);
5351 for ($i=1; $i<=31; $i++) {
5352 $days[$i] = $i;
5354 for ($i=1; $i<=12; $i++) {
5355 $months[$i] = userdate(gmmktime(12,0,0,$i,15,2000), "%B");
5357 for ($i=1970; $i<=2020; $i++) {
5358 $years[$i] = $i;
5360 return choose_from_menu($days, $day, $currentdate['mday'], '', '', '0', $return)
5361 .choose_from_menu($months, $month, $currentdate['mon'], '', '', '0', $return)
5362 .choose_from_menu($years, $year, $currentdate['year'], '', '', '0', $return);
5367 *Prints form items with the names $hour and $minute
5369 * @param string $hour fieldname
5370 * @param string ? $minute fieldname
5371 * @param $currenttime A default timestamp in GMT
5372 * @param int $step minute spacing
5373 * @param boolean $return
5375 function print_time_selector($hour, $minute, $currenttime=0, $step=5, $return=false) {
5377 if (!$currenttime) {
5378 $currenttime = time();
5380 $currentdate = usergetdate($currenttime);
5381 if ($step != 1) {
5382 $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
5384 for ($i=0; $i<=23; $i++) {
5385 $hours[$i] = sprintf("%02d",$i);
5387 for ($i=0; $i<=59; $i+=$step) {
5388 $minutes[$i] = sprintf("%02d",$i);
5391 return choose_from_menu($hours, $hour, $currentdate['hours'], '','','0',$return)
5392 .choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','0',$return);
5396 * Prints time limit value selector
5398 * @uses $CFG
5399 * @param int $timelimit default
5400 * @param string $unit
5401 * @param string $name
5402 * @param boolean $return
5404 function print_timer_selector($timelimit = 0, $unit = '', $name = 'timelimit', $return=false) {
5406 global $CFG;
5408 if ($unit) {
5409 $unit = ' '.$unit;
5412 // Max timelimit is sessiontimeout - 10 minutes.
5413 $maxvalue = ($CFG->sessiontimeout / 60) - 10;
5415 for ($i=1; $i<=$maxvalue; $i++) {
5416 $minutes[$i] = $i.$unit;
5418 return choose_from_menu($minutes, $name, $timelimit, get_string('none'), '','','0',$return);
5422 * Prints a grade menu (as part of an existing form) with help
5423 * Showing all possible numerical grades and scales
5425 * @uses $CFG
5426 * @param int $courseid ?
5427 * @param string $name ?
5428 * @param string $current ?
5429 * @param boolean $includenograde ?
5430 * @todo Finish documenting this function
5432 function print_grade_menu($courseid, $name, $current, $includenograde=true, $return=false) {
5434 global $CFG;
5436 $output = '';
5437 $strscale = get_string('scale');
5438 $strscales = get_string('scales');
5440 $scales = get_scales_menu($courseid);
5441 foreach ($scales as $i => $scalename) {
5442 $grades[-$i] = $strscale .': '. $scalename;
5444 if ($includenograde) {
5445 $grades[0] = get_string('nograde');
5447 for ($i=100; $i>=1; $i--) {
5448 $grades[$i] = $i;
5450 $output .= choose_from_menu($grades, $name, $current, '', '', 0, true);
5452 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
5453 $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
5454 $linkobject, 400, 500, $strscales, 'none', true);
5456 if ($return) {
5457 return $output;
5458 } else {
5459 echo $output;
5464 * Prints a scale menu (as part of an existing form) including help button
5465 * Just like {@link print_grade_menu()} but without the numeric grades
5467 * @param int $courseid ?
5468 * @param string $name ?
5469 * @param string $current ?
5470 * @todo Finish documenting this function
5472 function print_scale_menu($courseid, $name, $current, $return=false) {
5474 global $CFG;
5476 $output = '';
5477 $strscales = get_string('scales');
5478 $output .= choose_from_menu(get_scales_menu($courseid), $name, $current, '', '', 0, true);
5480 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
5481 $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
5482 $linkobject, 400, 500, $strscales, 'none', true);
5483 if ($return) {
5484 return $output;
5485 } else {
5486 echo $output;
5491 * Prints a help button about a scale
5493 * @uses $CFG
5494 * @param id $courseid ?
5495 * @param object $scale ?
5496 * @todo Finish documenting this function
5498 function print_scale_menu_helpbutton($courseid, $scale, $return=false) {
5500 global $CFG;
5502 $output = '';
5503 $strscales = get_string('scales');
5505 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$scale->name.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
5506 $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true&amp;scaleid='. $scale->id, 'ratingscale',
5507 $linkobject, 400, 500, $scale->name, 'none', true);
5508 if ($return) {
5509 return $output;
5510 } else {
5511 echo $output;
5516 * Print an error page displaying an error message.
5517 * Old method, don't call directly in new code - use print_error instead.
5520 * @uses $SESSION
5521 * @uses $CFG
5522 * @param string $message The message to display to the user about the error.
5523 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
5525 function error ($message, $link='') {
5527 global $CFG, $SESSION;
5528 $message = clean_text($message); // In case nasties are in here
5530 if (defined('FULLME') && FULLME == 'cron') {
5531 // Errors in cron should be mtrace'd.
5532 mtrace($message);
5533 die;
5536 if (! defined('HEADER_PRINTED')) {
5537 //header not yet printed
5538 @header('HTTP/1.0 404 Not Found');
5539 print_header(get_string('error'));
5542 echo '<br />';
5543 print_simple_box($message, '', '', '', '', 'errorbox');
5545 debugging('Stack trace:', DEBUG_DEVELOPER);
5547 // in case we are logging upgrade in admin/index.php stop it
5548 if (function_exists('upgrade_log_finish')) {
5549 upgrade_log_finish();
5552 if (empty($link) and !defined('ADMIN_EXT_HEADER_PRINTED')) {
5553 if ( !empty($SESSION->fromurl) ) {
5554 $link = $SESSION->fromurl;
5555 unset($SESSION->fromurl);
5556 } else {
5557 $link = $CFG->wwwroot .'/';
5561 if (!empty($link)) {
5562 print_continue($link);
5565 print_footer();
5567 for ($i=0;$i<512;$i++) { // Padding to help IE work with 404
5568 echo ' ';
5571 die;
5575 * Print an error page displaying an error message. New method - use this for new code.
5577 * @uses $SESSION
5578 * @uses $CFG
5579 * @param string $errorcode The name of the string from error.php to print
5580 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
5581 * @param object $a Extra words and phrases that might be required in the error string
5583 function print_error ($errorcode, $module='', $link='', $a=NULL) {
5585 global $CFG;
5587 if (empty($module) || $module == 'moodle' || $module == 'core') {
5588 $module = 'error';
5589 $modulelink = 'moodle';
5590 } else {
5591 $modulelink = $module;
5594 if (!empty($CFG->errordocroot)) {
5595 $errordocroot = $CFG->errordocroot;
5596 } else if (!empty($CFG->docroot)) {
5597 $errordocroot = $CFG->docroot;
5598 } else {
5599 $errordocroot = 'http://docs.moodle.org';
5602 $message = '<p class="errormessage">'.get_string($errorcode, $module, $a).'</p>'.
5603 '<p class="errorcode">'.
5604 '<a href="'.$errordocroot.'/en/error/'.$modulelink.'/'.$errorcode.'">'.
5605 get_string('moreinformation').'</a></p>';
5606 error($message, $link);
5609 * Returns a string of html with an image of a help icon linked to a help page on a number of help topics.
5610 * Should be used only with htmleditor or textarea.
5611 * @param mixed $helptopics variable amount of params accepted. Each param may be a string or an array of arguments for
5612 * helpbutton.
5613 * @return string
5615 function editorhelpbutton(){
5616 global $CFG, $SESSION;
5617 $items = func_get_args();
5618 $i = 1;
5619 $urlparams = array();
5620 $titles = array();
5621 foreach ($items as $item){
5622 if (is_array($item)){
5623 $urlparams[] = "keyword$i=".urlencode($item[0]);
5624 $urlparams[] = "title$i=".urlencode($item[1]);
5625 if (isset($item[2])){
5626 $urlparams[] = "module$i=".urlencode($item[2]);
5628 $titles[] = trim($item[1], ". \t");
5629 }elseif (is_string($item)){
5630 $urlparams[] = "button$i=".urlencode($item);
5631 switch ($item){
5632 case 'reading' :
5633 $titles[] = get_string("helpreading");
5634 break;
5635 case 'writing' :
5636 $titles[] = get_string("helpwriting");
5637 break;
5638 case 'questions' :
5639 $titles[] = get_string("helpquestions");
5640 break;
5641 case 'emoticons' :
5642 $titles[] = get_string("helpemoticons");
5643 break;
5644 case 'richtext' :
5645 $titles[] = get_string('helprichtext');
5646 break;
5647 case 'text' :
5648 $titles[] = get_string('helptext');
5649 break;
5650 default :
5651 error('Unknown help topic '.$item);
5654 $i++;
5656 if (count($titles)>1){
5657 //join last two items with an 'and'
5658 $a = new object();
5659 $a->one = $titles[count($titles) - 2];
5660 $a->two = $titles[count($titles) - 1];
5661 $titles[count($titles) - 2] = get_string('and', '', $a);
5662 unset($titles[count($titles) - 1]);
5664 $alttag = join (', ', $titles);
5666 $paramstring = join('&', $urlparams);
5667 $linkobject = '<img alt="'.$alttag.'" class="iconhelp" src="'.$CFG->pixpath .'/help.gif" />';
5668 return link_to_popup_window(s('/lib/form/editorhelp.php?'.$paramstring), $alttag, $linkobject, 400, 500, $alttag, 'none', true);
5672 * Print a help button.
5674 * @uses $CFG
5675 * @param string $page The keyword that defines a help page
5676 * @param string $title The title of links, rollover tips, alt tags etc
5677 * 'Help with' (or the language equivalent) will be prefixed and '...' will be stripped.
5678 * @param string $module Which module is the page defined in
5679 * @param mixed $image Use a help image for the link? (true/false/"both")
5680 * @param boolean $linktext If true, display the title next to the help icon.
5681 * @param string $text If defined then this text is used in the page, and
5682 * the $page variable is ignored.
5683 * @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.
5684 * @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif
5685 * @return string
5686 * @todo Finish documenting this function
5688 function helpbutton ($page, $title='', $module='moodle', $image=true, $linktext=false, $text='', $return=false,
5689 $imagetext='') {
5690 global $CFG, $COURSE;
5692 // fix for MDL-7734
5693 if (!empty($COURSE->lang)) {
5694 $forcelang = $COURSE->lang;
5695 } else {
5696 $forcelang = '';
5699 if ($module == '') {
5700 $module = 'moodle';
5703 $tooltip = get_string('helpprefix2', '', trim($title, ". \t"));
5705 $linkobject = '';
5707 if ($image) {
5708 if ($linktext) {
5709 // MDL-7469 If text link is displayed with help icon, change to alt to "help with this".
5710 $linkobject .= $title.'&nbsp;';
5711 $tooltip = get_string('helpwiththis');
5713 if ($imagetext) {
5714 $linkobject .= $imagetext;
5715 } else {
5716 $linkobject .= '<img class="iconhelp" alt="'.s(strip_tags($tooltip)).'" src="'.
5717 $CFG->pixpath .'/help.gif" />';
5719 } else {
5720 $linkobject .= $tooltip;
5723 $tooltip .= ' ('.get_string('newwindow').')'; // Warn users about new window for Accessibility
5725 // fix for MDL-7734
5726 if ($text) {
5727 $url = '/help.php?module='. $module .'&amp;text='. s(urlencode($text).'&amp;forcelang='.$forcelang);
5728 } else {
5729 $url = '/help.php?module='. $module .'&amp;file='. $page .'.html&amp;forcelang='.$forcelang;
5732 $link = '<span class="helplink">'.
5733 link_to_popup_window ($url, 'popup', $linkobject, 400, 500, $tooltip, 'none', true).
5734 '</span>';
5736 if ($return) {
5737 return $link;
5738 } else {
5739 echo $link;
5744 * Print a help button.
5746 * Prints a special help button that is a link to the "live" emoticon popup
5747 * @uses $CFG
5748 * @uses $SESSION
5749 * @param string $form ?
5750 * @param string $field ?
5751 * @todo Finish documenting this function
5753 function emoticonhelpbutton($form, $field, $return = false) {
5755 global $CFG, $SESSION;
5757 $SESSION->inserttextform = $form;
5758 $SESSION->inserttextfield = $field;
5759 $imagetext = '<img src="' . $CFG->pixpath . '/s/smiley.gif" alt="" class="emoticon" style="margin-left:3px; padding-right:1px;width:15px;height:15px;" />';
5760 $help = helpbutton('emoticons', get_string('helpemoticons'), 'moodle', true, true, '', true, $imagetext);
5761 if (!$return){
5762 echo $help;
5763 } else {
5764 return $help;
5769 * Print a help button.
5771 * Prints a special help button for html editors (htmlarea in this case)
5772 * @uses $CFG
5774 function editorshortcutshelpbutton() {
5776 global $CFG;
5777 $imagetext = '<img src="' . $CFG->httpswwwroot . '/lib/editor/htmlarea/images/kbhelp.gif" alt="'.
5778 get_string('editorshortcutkeys').'" class="iconkbhelp" />';
5780 return helpbutton('editorshortcuts', get_string('editorshortcutkeys'), 'moodle', true, false, '', true, $imagetext);
5784 * Print a message and exit.
5786 * @uses $CFG
5787 * @param string $message ?
5788 * @param string $link ?
5789 * @todo Finish documenting this function
5791 function notice ($message, $link='', $course=NULL) {
5792 global $CFG, $SITE;
5794 $message = clean_text($message);
5796 print_box($message, 'generalbox', 'notice');
5797 print_continue($link);
5799 if (empty($course)) {
5800 print_footer($SITE);
5801 } else {
5802 print_footer($course);
5804 exit;
5808 * Print a message along with "Yes" and "No" links for the user to continue.
5810 * @param string $message The text to display
5811 * @param string $linkyes The link to take the user to if they choose "Yes"
5812 * @param string $linkno The link to take the user to if they choose "No"
5813 * TODO Document remaining arguments
5815 function notice_yesno ($message, $linkyes, $linkno, $optionsyes=NULL, $optionsno=NULL, $methodyes='post', $methodno='post') {
5817 global $CFG;
5819 $message = clean_text($message);
5820 $linkyes = clean_text($linkyes);
5821 $linkno = clean_text($linkno);
5823 print_box_start('generalbox', 'notice');
5824 echo '<p>'. $message .'</p>';
5825 echo '<div class="buttons">';
5826 print_single_button($linkyes, $optionsyes, get_string('yes'), $methodyes, $CFG->framename);
5827 print_single_button($linkno, $optionsno, get_string('no'), $methodno, $CFG->framename);
5828 echo '</div>';
5829 print_box_end();
5833 * Provide an definition of error_get_last for PHP before 5.2.0. This simply
5834 * returns NULL, since there is not way to get the right answer.
5836 if (!function_exists('error_get_last')) {
5837 // the eval is needed to prevent PHP 5.2+ from getting a parse error!
5838 eval('
5839 function error_get_last() {
5840 return NULL;
5846 * Redirects the user to another page, after printing a notice
5848 * @param string $url The url to take the user to
5849 * @param string $message The text message to display to the user about the redirect, if any
5850 * @param string $delay How long before refreshing to the new page at $url?
5851 * @todo '&' needs to be encoded into '&amp;' for XHTML compliance,
5852 * however, this is not true for javascript. Therefore we
5853 * first decode all entities in $url (since we cannot rely on)
5854 * the correct input) and then encode for where it's needed
5855 * echo "<script type='text/javascript'>alert('Redirect $url');</script>";
5857 function redirect($url, $message='', $delay=-1) {
5859 global $CFG;
5861 if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
5862 $url = sid_process_url($url);
5865 $message = clean_text($message);
5867 $encodedurl = preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&amp;", $url);
5868 $encodedurl = preg_replace('/^.*href="([^"]*)".*$/', "\\1", clean_text('<a href="'.$encodedurl.'" />'));
5869 $url = str_replace('&amp;', '&', $encodedurl);
5871 /// At developer debug level. Don't redirect if errors have been printed on screen.
5872 /// Currenly only works in PHP 5.2+; we do not want strict PHP5 errors
5873 $lasterror = error_get_last();
5874 $error = defined('DEBUGGING_PRINTED') or (!empty($lasterror) && ($lasterror['type'] & DEBUG_DEVELOPER));
5875 $errorprinted = debugging('', DEBUG_ALL) && $CFG->debugdisplay && $error;
5876 if ($errorprinted) {
5877 $message = "<strong>Error output, so disabling automatic redirect.</strong></p><p>" . $message;
5880 $performanceinfo = '';
5881 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
5882 if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
5883 $perf = get_performance_info();
5884 error_log("PERF: " . $perf['txt']);
5888 /// when no message and header printed yet, try to redirect
5889 if (empty($message) and !defined('HEADER_PRINTED')) {
5891 // Technically, HTTP/1.1 requires Location: header to contain
5892 // the absolute path. (In practice browsers accept relative
5893 // paths - but still, might as well do it properly.)
5894 // This code turns relative into absolute.
5895 if (!preg_match('|^[a-z]+:|', $url)) {
5896 // Get host name http://www.wherever.com
5897 $hostpart = preg_replace('|^(.*?[^:/])/.*$|', '$1', $CFG->wwwroot);
5898 if (preg_match('|^/|', $url)) {
5899 // URLs beginning with / are relative to web server root so we just add them in
5900 $url = $hostpart.$url;
5901 } else {
5902 // URLs not beginning with / are relative to path of current script, so add that on.
5903 $url = $hostpart.preg_replace('|\?.*$|','',me()).'/../'.$url;
5905 // Replace all ..s
5906 while (true) {
5907 $newurl = preg_replace('|/(?!\.\.)[^/]*/\.\./|', '/', $url);
5908 if ($newurl == $url) {
5909 break;
5911 $url = $newurl;
5915 $delay = 0;
5916 //try header redirection first
5917 @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); //302 might not work for POST requests, 303 is ignored by obsolete clients
5918 @header('Location: '.$url);
5919 //another way for older browsers and already sent headers (eg trailing whitespace in config.php)
5920 echo '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />';
5921 echo '<script type="text/javascript">'. "\n" .'//<![CDATA['. "\n". "location.replace('".addslashes_js($url)."');". "\n". '//]]>'. "\n". '</script>'; // To cope with Mozilla bug
5922 die;
5925 if ($delay == -1) {
5926 $delay = 3; // if no delay specified wait 3 seconds
5928 if (! defined('HEADER_PRINTED')) {
5929 // this type of redirect might not be working in some browsers - such as lynx :-(
5930 print_header('', '', '', '', $errorprinted ? '' : ('<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'));
5931 $delay += 3; // double redirect prevention, it was sometimes breaking upgrades before 1.7
5933 echo '<div style="text-align:center">';
5934 echo '<div>'. $message .'</div>';
5935 echo '<div>( <a href="'. $encodedurl .'">'. get_string('continue') .'</a> )</div>';
5936 echo '</div>';
5938 if (!$errorprinted) {
5940 <script type="text/javascript">
5941 //<![CDATA[
5943 function redirect() {
5944 document.location.replace('<?php echo addslashes_js($url) ?>');
5946 setTimeout("redirect()", <?php echo ($delay * 1000) ?>);
5947 //]]>
5948 </script>
5949 <?php
5952 print_footer('none');
5953 die;
5957 * Print a bold message in an optional color.
5959 * @param string $message The message to print out
5960 * @param string $style Optional style to display message text in
5961 * @param string $align Alignment option
5962 * @param bool $return whether to return an output string or echo now
5964 function notify($message, $style='notifyproblem', $align='center', $return=false) {
5965 if ($style == 'green') {
5966 $style = 'notifysuccess'; // backward compatible with old color system
5969 $message = clean_text($message);
5971 $output = '<div class="'.$style.'" style="text-align:'. $align .'">'. $message .'</div>'."\n";
5973 if ($return) {
5974 return $output;
5976 echo $output;
5981 * Given an email address, this function will return an obfuscated version of it
5983 * @param string $email The email address to obfuscate
5984 * @return string
5986 function obfuscate_email($email) {
5988 $i = 0;
5989 $length = strlen($email);
5990 $obfuscated = '';
5991 while ($i < $length) {
5992 if (rand(0,2)) {
5993 $obfuscated.='%'.dechex(ord($email{$i}));
5994 } else {
5995 $obfuscated.=$email{$i};
5997 $i++;
5999 return $obfuscated;
6003 * This function takes some text and replaces about half of the characters
6004 * with HTML entity equivalents. Return string is obviously longer.
6006 * @param string $plaintext The text to be obfuscated
6007 * @return string
6009 function obfuscate_text($plaintext) {
6011 $i=0;
6012 $length = strlen($plaintext);
6013 $obfuscated='';
6014 $prev_obfuscated = false;
6015 while ($i < $length) {
6016 $c = ord($plaintext{$i});
6017 $numerical = ($c >= ord('0')) && ($c <= ord('9'));
6018 if ($prev_obfuscated and $numerical ) {
6019 $obfuscated.='&#'.ord($plaintext{$i}).';';
6020 } else if (rand(0,2)) {
6021 $obfuscated.='&#'.ord($plaintext{$i}).';';
6022 $prev_obfuscated = true;
6023 } else {
6024 $obfuscated.=$plaintext{$i};
6025 $prev_obfuscated = false;
6027 $i++;
6029 return $obfuscated;
6033 * This function uses the {@link obfuscate_email()} and {@link obfuscate_text()}
6034 * to generate a fully obfuscated email link, ready to use.
6036 * @param string $email The email address to display
6037 * @param string $label The text to dispalyed as hyperlink to $email
6038 * @param boolean $dimmed If true then use css class 'dimmed' for hyperlink
6039 * @return string
6041 function obfuscate_mailto($email, $label='', $dimmed=false) {
6043 if (empty($label)) {
6044 $label = $email;
6046 if ($dimmed) {
6047 $title = get_string('emaildisable');
6048 $dimmed = ' class="dimmed"';
6049 } else {
6050 $title = '';
6051 $dimmed = '';
6053 return sprintf("<a href=\"%s:%s\" $dimmed title=\"$title\">%s</a>",
6054 obfuscate_text('mailto'), obfuscate_email($email),
6055 obfuscate_text($label));
6059 * Prints a single paging bar to provide access to other pages (usually in a search)
6061 * @param int $totalcount Thetotal number of entries available to be paged through
6062 * @param int $page The page you are currently viewing
6063 * @param int $perpage The number of entries that should be shown per page
6064 * @param mixed $baseurl If this is a string then it is the url which will be appended with $pagevar, an equals sign and the page number.
6065 * If this is a moodle_url object then the pagevar param will be replaced by the page no, for each page.
6066 * @param string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
6067 * @param bool $nocurr do not display the current page as a link
6068 * @param bool $return whether to return an output string or echo now
6069 * @return bool or string
6071 function print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar='page',$nocurr=false, $return=false) {
6072 $maxdisplay = 18;
6073 $output = '';
6075 if ($totalcount > $perpage) {
6076 $output .= '<div class="paging">';
6077 $output .= get_string('page') .':';
6078 if ($page > 0) {
6079 $pagenum = $page - 1;
6080 if (!is_a($baseurl, 'moodle_url')){
6081 $output .= '&nbsp;(<a href="'. $baseurl . $pagevar .'='. $pagenum .'">'. get_string('previous') .'</a>)&nbsp;';
6082 } else {
6083 $output .= '&nbsp;(<a href="'. $baseurl->out(false, array($pagevar => $pagenum)).'">'. get_string('previous') .'</a>)&nbsp;';
6086 if ($perpage > 0) {
6087 $lastpage = ceil($totalcount / $perpage);
6088 } else {
6089 $lastpage = 1;
6091 if ($page > 15) {
6092 $startpage = $page - 10;
6093 if (!is_a($baseurl, 'moodle_url')){
6094 $output .= '&nbsp;<a href="'. $baseurl . $pagevar .'=0">1</a>&nbsp;...';
6095 } else {
6096 $output .= '&nbsp;<a href="'. $baseurl->out(false, array($pagevar => 0)).'">1</a>&nbsp;...';
6098 } else {
6099 $startpage = 0;
6101 $currpage = $startpage;
6102 $displaycount = 0;
6103 while ($displaycount < $maxdisplay and $currpage < $lastpage) {
6104 $displaypage = $currpage+1;
6105 if ($page == $currpage && empty($nocurr)) {
6106 $output .= '&nbsp;&nbsp;'. $displaypage;
6107 } else {
6108 if (!is_a($baseurl, 'moodle_url')){
6109 $output .= '&nbsp;&nbsp;<a href="'. $baseurl . $pagevar .'='. $currpage .'">'. $displaypage .'</a>';
6110 } else {
6111 $output .= '&nbsp;&nbsp;<a href="'. $baseurl->out(false, array($pagevar => $currpage)).'">'. $displaypage .'</a>';
6115 $displaycount++;
6116 $currpage++;
6118 if ($currpage < $lastpage) {
6119 $lastpageactual = $lastpage - 1;
6120 if (!is_a($baseurl, 'moodle_url')){
6121 $output .= '&nbsp;...<a href="'. $baseurl . $pagevar .'='. $lastpageactual .'">'. $lastpage .'</a>&nbsp;';
6122 } else {
6123 $output .= '&nbsp;...<a href="'. $baseurl->out(false, array($pagevar => $lastpageactual)).'">'. $lastpage .'</a>&nbsp;';
6126 $pagenum = $page + 1;
6127 if ($pagenum != $displaypage) {
6128 if (!is_a($baseurl, 'moodle_url')){
6129 $output .= '&nbsp;&nbsp;(<a href="'. $baseurl . $pagevar .'='. $pagenum .'">'. get_string('next') .'</a>)';
6130 } else {
6131 $output .= '&nbsp;&nbsp;(<a href="'. $baseurl->out(false, array($pagevar => $pagenum)) .'">'. get_string('next') .'</a>)';
6134 $output .= '</div>';
6137 if ($return) {
6138 return $output;
6141 echo $output;
6142 return true;
6146 * This function is used to rebuild the <nolink> tag because some formats (PLAIN and WIKI)
6147 * will transform it to html entities
6149 * @param string $text Text to search for nolink tag in
6150 * @return string
6152 function rebuildnolinktag($text) {
6154 $text = preg_replace('/&lt;(\/*nolink)&gt;/i','<$1>',$text);
6156 return $text;
6160 * Prints a nice side block with an optional header. The content can either
6161 * be a block of HTML or a list of text with optional icons.
6163 * @param string $heading Block $title embedded in HTML tags, for example <h2>.
6164 * @param string $content ?
6165 * @param array $list ?
6166 * @param array $icons ?
6167 * @param string $footer ?
6168 * @param array $attributes ?
6169 * @param string $title Plain text title, as embedded in the $heading.
6170 * @todo Finish documenting this function. Show example of various attributes, etc.
6172 function print_side_block($heading='', $content='', $list=NULL, $icons=NULL, $footer='', $attributes = array(), $title='') {
6174 //Accessibility: skip block link, with title-text (or $block_id) to differentiate links.
6175 static $block_id = 0;
6176 $block_id++;
6177 if (empty($heading)) {
6178 $skip_text = get_string('skipblock', 'access').' '.$block_id;
6180 else {
6181 $skip_text = get_string('skipa', 'access', strip_tags($title));
6183 $skip_link = '<a href="#sb-'.$block_id.'" class="skip-block">'.$skip_text.'</a>';
6184 $skip_dest = '<span id="sb-'.$block_id.'" class="skip-block-to"></span>';
6186 if (! empty($heading)) {
6187 echo $skip_link;
6189 //ELSE: a single link on a page "Skip block 4" is too confusing - ignore.
6191 print_side_block_start($heading, $attributes);
6193 if ($content) {
6194 echo $content;
6195 if ($footer) {
6196 echo '<div class="footer">'. $footer .'</div>';
6198 } else {
6199 if ($list) {
6200 $row = 0;
6201 //Accessibility: replaced unnecessary table with list, see themes/standard/styles_layout.css
6202 echo "\n<ul class='list'>\n";
6203 foreach ($list as $key => $string) {
6204 echo '<li class="r'. $row .'">';
6205 if ($icons) {
6206 echo '<div class="icon column c0">'. $icons[$key] .'</div>';
6208 echo '<div class="column c1">'. $string .'</div>';
6209 echo "</li>\n";
6210 $row = $row ? 0:1;
6212 echo "</ul>\n";
6214 if ($footer) {
6215 echo '<div class="footer">'. $footer .'</div>';
6220 print_side_block_end($attributes);
6221 echo $skip_dest;
6225 * Starts a nice side block with an optional header.
6227 * @param string $heading ?
6228 * @param array $attributes ?
6229 * @todo Finish documenting this function
6231 function print_side_block_start($heading='', $attributes = array()) {
6233 global $CFG, $THEME;
6235 if (!empty($THEME->customcorners)) {
6236 require_once($CFG->dirroot.'/lib/custom_corners_lib.php');
6239 // If there are no special attributes, give a default CSS class
6240 if (empty($attributes) || !is_array($attributes)) {
6241 $attributes = array('class' => 'sideblock');
6243 } else if(!isset($attributes['class'])) {
6244 $attributes['class'] = 'sideblock';
6246 } else if(!strpos($attributes['class'], 'sideblock')) {
6247 $attributes['class'] .= ' sideblock';
6250 // OK, the class is surely there and in addition to anything
6251 // else, it's tagged as a sideblock
6255 // IE misery: if I do it this way, blocks which start hidden cannot be "unhidden"
6257 // If there is a cookie to hide this thing, start it hidden
6258 if (!empty($attributes['id']) && isset($_COOKIE['hide:'.$attributes['id']])) {
6259 $attributes['class'] = 'hidden '.$attributes['class'];
6263 $attrtext = '';
6264 foreach ($attributes as $attr => $val) {
6265 $attrtext .= ' '.$attr.'="'.$val.'"';
6268 echo '<div '.$attrtext.'>';
6270 if (!empty($THEME->customcorners)) {
6271 echo '<div class="wrap">'."\n";
6273 if ($heading) {
6274 //Accessibility: replaced <div> with H2; no, H2 more appropriate in moodleblock.class.php: _title_html.
6275 // echo '<div class="header">'.$heading.'</div>';
6276 echo '<div class="header">';
6277 if (!empty($THEME->customcorners)) {
6278 echo '<div class="bt"><div>&nbsp;</div></div>';
6279 echo '<div class="i1"><div class="i2">';
6280 echo '<div class="i3">';
6282 echo $heading;
6283 if (!empty($THEME->customcorners)) {
6284 echo '</div></div></div>';
6286 echo '</div>';
6287 } else {
6288 if (!empty($THEME->customcorners)) {
6289 echo '<div class="bt"><div>&nbsp;</div></div>';
6293 if (!empty($THEME->customcorners)) {
6294 echo '<div class="i1"><div class="i2">';
6295 echo '<div class="i3">';
6296 $THEME->customcornersopen += 1;
6298 echo '<div class="content">';
6304 * Print table ending tags for a side block box.
6306 function print_side_block_end($attributes = array()) {
6307 global $CFG, $THEME;
6309 echo '</div>';
6311 if (!empty($THEME->customcorners)) {
6312 require_once($CFG->dirroot.'/lib/custom_corners_lib.php');
6313 print_custom_corners_end();
6316 echo '</div>';
6318 // IE workaround: if I do it THIS way, it works! WTF?
6319 if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) {
6320 echo '<script type="text/javascript">'."\n//<![CDATA[\n".'elementCookieHide("'.$attributes['id'].'"); '.
6321 "\n//]]>\n".'</script>';
6328 * Prints out code needed for spellchecking.
6329 * Original idea by Ludo (Marc Alier).
6331 * Opening CDATA and <script> are output by weblib::use_html_editor()
6332 * @uses $CFG
6333 * @param boolean $usehtmleditor Normally set by $CFG->htmleditor, can be overriden here
6334 * @param boolean $return If false, echos the code instead of returning it
6335 * @todo Find out if lib/editor/htmlarea/htmlarea.class.php::print_speller_code() is still used, and delete if not
6337 function print_speller_code ($usehtmleditor=false, $return=false) {
6338 global $CFG;
6339 $str = '';
6341 if(!$usehtmleditor) {
6342 $str .= 'function openSpellChecker() {'."\n";
6343 $str .= "\tvar speller = new spellChecker();\n";
6344 $str .= "\tspeller.popUpUrl = \"" . $CFG->wwwroot ."/lib/speller/spellchecker.html\";\n";
6345 $str .= "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php\";\n";
6346 $str .= "\tspeller.spellCheckAll();\n";
6347 $str .= '}'."\n";
6348 } else {
6349 $str .= "function spellClickHandler(editor, buttonId) {\n";
6350 $str .= "\teditor._textArea.value = editor.getHTML();\n";
6351 $str .= "\tvar speller = new spellChecker( editor._textArea );\n";
6352 $str .= "\tspeller.popUpUrl = \"" . $CFG->wwwroot ."/lib/speller/spellchecker.html\";\n";
6353 $str .= "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php\";\n";
6354 $str .= "\tspeller._moogle_edit=1;\n";
6355 $str .= "\tspeller._editor=editor;\n";
6356 $str .= "\tspeller.openChecker();\n";
6357 $str .= '}'."\n";
6360 if ($return) {
6361 return $str;
6363 echo $str;
6367 * Print button for spellchecking when editor is disabled
6369 function print_speller_button () {
6370 echo '<input type="button" value="Check spelling" onclick="openSpellChecker();" />'."\n";
6374 function page_id_and_class(&$getid, &$getclass) {
6375 // Create class and id for this page
6376 global $CFG, $ME;
6378 static $class = NULL;
6379 static $id = NULL;
6381 if (empty($CFG->pagepath)) {
6382 $CFG->pagepath = $ME;
6385 if (empty($class) || empty($id)) {
6386 $path = str_replace($CFG->httpswwwroot.'/', '', $CFG->pagepath); //Because the page could be HTTPSPAGEREQUIRED
6387 $path = str_replace('.php', '', $path);
6388 if (substr($path, -1) == '/') {
6389 $path .= 'index';
6391 if (empty($path) || $path == 'index') {
6392 $id = 'site-index';
6393 $class = 'course';
6394 } else if (substr($path, 0, 5) == 'admin') {
6395 $id = str_replace('/', '-', $path);
6396 $class = 'admin';
6397 } else {
6398 $id = str_replace('/', '-', $path);
6399 $class = explode('-', $id);
6400 array_pop($class);
6401 $class = implode('-', $class);
6405 $getid = $id;
6406 $getclass = $class;
6410 * Prints a maintenance message from /maintenance.html
6412 function print_maintenance_message () {
6413 global $CFG, $SITE;
6415 print_header(strip_tags($SITE->fullname), $SITE->fullname, 'home');
6416 print_simple_box_start('center');
6417 print_heading(get_string('sitemaintenance', 'admin'));
6418 @include($CFG->dataroot.'/1/maintenance.html');
6419 print_simple_box_end();
6420 print_footer();
6424 * Adjust the list of allowed tags based on $CFG->allowobjectembed and user roles (admin)
6426 function adjust_allowed_tags() {
6428 global $CFG, $ALLOWED_TAGS;
6430 if (!empty($CFG->allowobjectembed)) {
6431 $ALLOWED_TAGS .= '<embed><object>';
6435 /// Some code to print tabs
6437 /// A class for tabs
6438 class tabobject {
6439 var $id;
6440 var $link;
6441 var $text;
6442 var $linkedwhenselected;
6444 /// A constructor just because I like constructors
6445 function tabobject ($id, $link='', $text='', $title='', $linkedwhenselected=false) {
6446 $this->id = $id;
6447 $this->link = $link;
6448 $this->text = $text;
6449 $this->title = $title ? $title : $text;
6450 $this->linkedwhenselected = $linkedwhenselected;
6457 * Returns a string containing a nested list, suitable for formatting into tabs with CSS.
6459 * @param array $tabrows An array of rows where each row is an array of tab objects
6460 * @param string $selected The id of the selected tab (whatever row it's on)
6461 * @param array $inactive An array of ids of inactive tabs that are not selectable.
6462 * @param array $activated An array of ids of other tabs that are currently activated
6464 function print_tabs($tabrows, $selected=NULL, $inactive=NULL, $activated=NULL, $return=false) {
6465 global $CFG;
6467 /// $inactive must be an array
6468 if (!is_array($inactive)) {
6469 $inactive = array();
6472 /// $activated must be an array
6473 if (!is_array($activated)) {
6474 $activated = array();
6477 /// Convert the tab rows into a tree that's easier to process
6478 if (!$tree = convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated)) {
6479 return false;
6482 /// Print out the current tree of tabs (this function is recursive)
6484 $output = convert_tree_to_html($tree);
6486 $output = "\n\n".'<div class="tabtree">'.$output.'</div><div class="clearer"> </div>'."\n\n";
6488 /// We're done!
6490 if ($return) {
6491 return $output;
6493 echo $output;
6497 function convert_tree_to_html($tree, $row=0) {
6499 $str = "\n".'<ul class="tabrow'.$row.'">'."\n";
6501 $first = true;
6502 $count = count($tree);
6504 foreach ($tree as $tab) {
6505 $count--; // countdown to zero
6507 $liclass = '';
6509 if ($first && ($count == 0)) { // Just one in the row
6510 $liclass = 'first last';
6511 $first = false;
6512 } else if ($first) {
6513 $liclass = 'first';
6514 $first = false;
6515 } else if ($count == 0) {
6516 $liclass = 'last';
6519 if ((empty($tab->subtree)) && (!empty($tab->selected))) {
6520 $liclass .= (empty($liclass)) ? 'onerow' : ' onerow';
6523 if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) {
6524 if ($tab->selected) {
6525 $liclass .= (empty($liclass)) ? 'here selected' : ' here selected';
6526 } else if ($tab->active) {
6527 $liclass .= (empty($liclass)) ? 'here active' : ' here active';
6531 $str .= (!empty($liclass)) ? '<li class="'.$liclass.'">' : '<li>';
6533 if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) {
6534 // The a tag is used for styling
6535 $str .= '<a class="nolink"><span>'.$tab->text.'</span></a>';
6536 } else {
6537 $str .= '<a href="'.$tab->link.'" title="'.$tab->title.'"><span>'.$tab->text.'</span></a>';
6540 if (!empty($tab->subtree)) {
6541 $str .= convert_tree_to_html($tab->subtree, $row+1);
6542 } else if ($tab->selected) {
6543 $str .= '<div class="tabrow'.($row+1).' empty">&nbsp;</div>'."\n";
6546 $str .= ' </li>'."\n";
6548 $str .= '</ul>'."\n";
6550 return $str;
6554 function convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated) {
6556 /// Work backwards through the rows (bottom to top) collecting the tree as we go.
6558 $tabrows = array_reverse($tabrows);
6560 $subtree = array();
6562 foreach ($tabrows as $row) {
6563 $tree = array();
6565 foreach ($row as $tab) {
6566 $tab->inactive = in_array((string)$tab->id, $inactive);
6567 $tab->active = in_array((string)$tab->id, $activated);
6568 $tab->selected = (string)$tab->id == $selected;
6570 if ($tab->active || $tab->selected) {
6571 if ($subtree) {
6572 $tab->subtree = $subtree;
6575 $tree[] = $tab;
6577 $subtree = $tree;
6580 return $subtree;
6585 * Returns a string containing a link to the user documentation for the current
6586 * page. Also contains an icon by default. Shown to teachers and admin only.
6588 * @param string $text The text to be displayed for the link
6589 * @param string $iconpath The path to the icon to be displayed
6591 function page_doc_link($text='', $iconpath='') {
6592 global $ME, $COURSE, $CFG;
6594 if (empty($CFG->docroot) or empty($CFG->rolesactive)) {
6595 return '';
6598 if (empty($COURSE->id)) {
6599 $context = get_context_instance(CONTEXT_SYSTEM);
6600 } else {
6601 $context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
6604 if (!has_capability('moodle/site:doclinks', $context)) {
6605 return '';
6608 if (empty($CFG->pagepath)) {
6609 $CFG->pagepath = $ME;
6612 $path = str_replace($CFG->httpswwwroot.'/','', $CFG->pagepath); // Because the page could be HTTPSPAGEREQUIRED
6613 $path = str_replace('.php', '', $path);
6615 if (empty($path)) { // Not for home page
6616 return '';
6618 return doc_link($path, $text, $iconpath);
6622 * Returns a string containing a link to the user documentation.
6623 * Also contains an icon by default. Shown to teachers and admin only.
6625 * @param string $path The page link after doc root and language, no
6626 * leading slash.
6627 * @param string $text The text to be displayed for the link
6628 * @param string $iconpath The path to the icon to be displayed
6630 function doc_link($path='', $text='', $iconpath='') {
6631 global $CFG;
6633 if (empty($CFG->docroot)) {
6634 return '';
6637 $target = '';
6638 if (!empty($CFG->doctonewwindow)) {
6639 $target = ' target="_blank"';
6642 $lang = str_replace('_utf8', '', current_language());
6644 $str = '<a href="' .$CFG->docroot. '/' .$lang. '/' .$path. '"' .$target. '>';
6646 if (empty($iconpath)) {
6647 $iconpath = $CFG->httpswwwroot . '/pix/docs.gif';
6650 // alt left blank intentionally to prevent repetition in screenreaders
6651 $str .= '<img class="iconhelp" src="' .$iconpath. '" alt="" />' .$text. '</a>';
6653 return $str;
6658 * Returns true if the current site debugging settings are equal or above specified level.
6659 * If passed a parameter it will emit a debugging notice similar to trigger_error(). The
6660 * routing of notices is controlled by $CFG->debugdisplay
6661 * eg use like this:
6663 * 1) debugging('a normal debug notice');
6664 * 2) debugging('something really picky', DEBUG_ALL);
6665 * 3) debugging('annoying debug message only for develpers', DEBUG_DEVELOPER);
6666 * 4) if (debugging()) { perform extra debugging operations (do not use print or echo) }
6668 * In code blocks controlled by debugging() (such as example 4)
6669 * any output should be routed via debugging() itself, or the lower-level
6670 * trigger_error() or error_log(). Using echo or print will break XHTML
6671 * JS and HTTP headers.
6674 * @param string $message a message to print
6675 * @param int $level the level at which this debugging statement should show
6676 * @return bool
6678 function debugging($message='', $level=DEBUG_NORMAL) {
6680 global $CFG;
6682 if (empty($CFG->debug)) {
6683 return false;
6686 if ($CFG->debug >= $level) {
6687 if ($message) {
6688 $callers = debug_backtrace();
6689 $from = '<ul style="text-align: left">';
6690 foreach ($callers as $caller) {
6691 if (!isset($caller['line'])) {
6692 $caller['line'] = '?'; // probably call_user_func()
6694 if (!isset($caller['file'])) {
6695 $caller['file'] = $CFG->dirroot.'/unknownfile'; // probably call_user_func()
6697 $from .= '<li>line ' . $caller['line'] . ' of ' . substr($caller['file'], strlen($CFG->dirroot) + 1);
6698 if (isset($caller['function'])) {
6699 $from .= ': call to ';
6700 if (isset($caller['class'])) {
6701 $from .= $caller['class'] . $caller['type'];
6703 $from .= $caller['function'] . '()';
6705 $from .= '</li>';
6707 $from .= '</ul>';
6708 if (!isset($CFG->debugdisplay)) {
6709 $CFG->debugdisplay = ini_get('display_errors');
6711 if ($CFG->debugdisplay) {
6712 if (!defined('DEBUGGING_PRINTED')) {
6713 define('DEBUGGING_PRINTED', 1); // indicates we have printed something
6715 notify($message . $from, 'notifytiny');
6716 } else {
6717 trigger_error($message . $from, E_USER_NOTICE);
6720 return true;
6722 return false;
6726 * Disable debug messages from debugging(), while keeping PHP error reporting level as is.
6728 function disable_debugging() {
6729 global $CFG;
6730 $CFG->debug = $CFG->debug | 0x80000000; // switch the sign bit in integer number ;-)
6735 * Returns string to add a frame attribute, if required
6737 function frametarget() {
6738 global $CFG;
6740 if (empty($CFG->framename) or ($CFG->framename == '_top')) {
6741 return '';
6742 } else {
6743 return ' target="'.$CFG->framename.'" ';
6748 * Outputs a HTML comment to the browser. This is used for those hard-to-debug
6749 * pages that use bits from many different files in very confusing ways (e.g. blocks).
6750 * @usage print_location_comment(__FILE__, __LINE__);
6751 * @param string $file
6752 * @param integer $line
6753 * @param boolean $return Whether to return or print the comment
6754 * @return mixed Void unless true given as third parameter
6756 function print_location_comment($file, $line, $return = false)
6758 if ($return) {
6759 return "<!-- $file at line $line -->\n";
6760 } else {
6761 echo "<!-- $file at line $line -->\n";
6767 * Returns an image of an up or down arrow, used for column sorting. To avoid unnecessary DB accesses, please
6768 * provide this function with the language strings for sortasc and sortdesc.
6769 * If no sort string is associated with the direction, an arrow with no alt text will be printed/returned.
6770 * @param string $direction 'up' or 'down'
6771 * @param string $strsort The language string used for the alt attribute of this image
6772 * @param bool $return Whether to print directly or return the html string
6773 * @return string HTML for the image
6775 * TODO See if this isn't already defined somewhere. If not, move this to weblib
6777 function print_arrow($direction='up', $strsort=null, $return=false) {
6778 global $CFG;
6780 if (!in_array($direction, array('up', 'down', 'right', 'left', 'move'))) {
6781 return null;
6784 $return = null;
6786 switch ($direction) {
6787 case 'up':
6788 $sortdir = 'asc';
6789 break;
6790 case 'down':
6791 $sortdir = 'desc';
6792 break;
6793 case 'move':
6794 $sortdir = 'asc';
6795 break;
6796 default:
6797 $sortdir = null;
6798 break;
6801 // Prepare language string
6802 $strsort = '';
6803 if (empty($strsort) && !empty($sortdir)) {
6804 $strsort = get_string('sort' . $sortdir, 'grades');
6807 $return = ' <img src="'.$CFG->pixpath.'/t/' . $direction . '.gif" alt="'.$strsort.'" /> ';
6809 if ($return) {
6810 return $return;
6811 } else {
6812 echo $return;
6817 * Returns boolean true if the current language is right-to-left (Hebrew, Arabic etc)
6820 function right_to_left() {
6821 static $result;
6823 if (isset($result)) {
6824 return $result;
6826 return $result = (get_string('thisdirection') == 'rtl');
6831 * Returns swapped left<=>right if in RTL environment.
6832 * part of RTL support
6834 * @param string $align align to check
6835 * @return string
6837 function fix_align_rtl($align) {
6838 if (!right_to_left()) {
6839 return $align;
6841 if ($align=='left') { return 'right'; }
6842 if ($align=='right') { return 'left'; }
6843 return $align;
6848 * Returns true if the page is displayed in a popup window.
6849 * Gets the information from the URL parameter inpopup.
6851 * @return boolean
6853 * TODO Use a central function to create the popup calls allover Moodle and
6854 * TODO In the moment only works with resources and probably questions.
6856 function is_in_popup() {
6857 $inpopup = optional_param('inpopup', '', PARAM_BOOL);
6859 return ($inpopup);
6863 // vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140: