[moodle] / moodle / lib / weblib.php Repository:

Annotation of /moodle/lib/weblib.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1348 - (view) (download)

1 : samhemelryk 1.1255 <?php
2 : martin 1.1
3 : samhemelryk 1.1257 // This file is part of Moodle - http://moodle.org/
4 :     //
5 : samhemelryk 1.1255 // Moodle is free software: you can redistribute it and/or modify
6 :     // it under the terms of the GNU General Public License as published by
7 :     // the Free Software Foundation, either version 3 of the License, or
8 :     // (at your option) any later version.
9 :     //
10 :     // Moodle is distributed in the hope that it will be useful,
11 :     // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 :     // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 :     // GNU General Public License for more details.
14 : samhemelryk 1.1257 //
15 : samhemelryk 1.1255 // You should have received a copy of the GNU General Public License
16 :     // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 : martin 1.1
18 : dhawes 1.302 /**
19 :     * Library of functions for web output
20 :     *
21 :     * Library of all general-purpose Moodle PHP functions and constants
22 :     * that produce HTML output
23 :     *
24 :     * Other main libraries:
25 :     * - datalib.php - functions that access the database.
26 :     * - moodlelib.php - general-purpose Moodle functions.
27 : samhemelryk 1.1255 *
28 : samhemelryk 1.1257 * @package moodlecore
29 : samhemelryk 1.1255 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
30 :     * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31 : dhawes 1.302 */
32 : urs_hunkler 1.347
33 : moodler 1.23 /// Constants
34 :    
35 : moodler 1.30 /// Define text formatting types ... eventually we can add Wiki, BBcode etc
36 : dhawes 1.302
37 :     /**
38 :     * Does all sorts of transformations and filtering
39 :     */
40 : dhawes 1.301 define('FORMAT_MOODLE', '0'); // Does all sorts of transformations and filtering
41 : dhawes 1.302
42 :     /**
43 :     * Plain HTML (with some tags stripped)
44 :     */
45 : dhawes 1.301 define('FORMAT_HTML', '1'); // Plain HTML (with some tags stripped)
46 : dhawes 1.302
47 :     /**
48 :     * Plain text (even tags are printed in full)
49 :     */
50 : dhawes 1.301 define('FORMAT_PLAIN', '2'); // Plain text (even tags are printed in full)
51 : dhawes 1.302
52 :     /**
53 :     * Wiki-formatted text
54 : thepurpleblob 1.441 * Deprecated: left here just to note that '3' is not used (at the moment)
55 :     * and to catch any latent wiki-like text (which generates an error)
56 : dhawes 1.302 */
57 : dhawes 1.301 define('FORMAT_WIKI', '3'); // Wiki-formatted text
58 : dhawes 1.302
59 :     /**
60 :     * Markdown-formatted text http://daringfireball.net/projects/markdown/
61 :     */
62 : dhawes 1.301 define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/
63 : moodler 1.23
64 : skodak 1.615 /**
65 :     * TRUSTTEXT marker - if present in text, text cleaning should be bypassed
66 :     */
67 :     define('TRUSTTEXT', '#####TRUSTTEXT#####');
68 :    
69 : nicolasconnault 1.1324 /**
70 :     * A moodle_url comparison using this flag will return true if the base URLs match, params are ignored
71 :     */
72 :     define('URL_MATCH_BASE', 0);
73 :     /**
74 :     * A moodle_url comparison using this flag will return true if the base URLs match and the params of url1 are part of url2
75 :     */
76 :     define('URL_MATCH_PARAMS', 1);
77 :     /**
78 : nicolasconnault 1.1325 * A moodle_url comparison using this flag will return true if the two URLs are identical, except for the order of the params
79 : nicolasconnault 1.1324 */
80 :     define('URL_MATCH_EXACT', 2);
81 : dhawes 1.302
82 :     /**
83 :     * Allowed tags - string of html tags that can be tested against for safe html tags
84 :     * @global string $ALLOWED_TAGS
85 : samhemelryk 1.1255 * @name $ALLOWED_TAGS
86 : dhawes 1.302 */
87 : tjhunt 1.868 global $ALLOWED_TAGS;
88 : moodler 1.166 $ALLOWED_TAGS =
89 : skodak 1.1114 '<p><br><b><i><u><font><table><tbody><thead><tfoot><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>';
90 : moodler 1.471
91 : moodler 1.310 /**
92 :     * Allowed protocols - array of protocols that are safe to use in links and so on
93 :     * @global string $ALLOWED_PROTOCOLS
94 : samhemelryk 1.1255 * @name $ALLOWED_PROTOCOLS
95 : moodler 1.310 */
96 : moodler 1.470 $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
97 : stronk7 1.1124 'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style', 'font-family',
98 : ericmerrill 1.1181 'border', 'margin', 'padding', 'background', 'background-color', 'text-decoration'); // CSS as well to get through kses
99 : moodler 1.310
100 :    
101 : moodler 1.23 /// Functions
102 :    
103 : dhawes 1.302 /**
104 :     * Add quotes to HTML characters
105 :     *
106 :     * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
107 :     * This function is very similar to {@link p()}
108 :     *
109 : samhemelryk 1.1255 * @todo Remove obsolete param $obsolete if not used anywhere
110 :     *
111 : dhawes 1.302 * @param string $var the string potentially containing HTML characters
112 : tjhunt 1.1223 * @param boolean $obsolete no longer used.
113 : dhawes 1.302 * @return string
114 :     */
115 : tjhunt 1.1223 function s($var, $obsolete = false) {
116 : stronk7 1.572
117 : skodak 1.483 if ($var == '0') { // for integer 0, boolean false, string '0'
118 :     return '0';
119 : moodler 1.103 }
120 : stronk7 1.572
121 : tjhunt 1.1224 return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars($var));
122 : martin 1.1 }
123 :    
124 : dhawes 1.302 /**
125 :     * Add quotes to HTML characters
126 :     *
127 : dhawes 1.304 * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
128 : samhemelryk 1.1255 * This function simply calls {@link s()}
129 :     * @see s()
130 :     *
131 :     * @todo Remove obsolete param $obsolete if not used anywhere
132 : dhawes 1.302 *
133 :     * @param string $var the string potentially containing HTML characters
134 : tjhunt 1.1223 * @param boolean $obsolete no longer used.
135 : dhawes 1.302 * @return string
136 :     */
137 : tjhunt 1.1223 function p($var, $obsolete = false) {
138 :     echo s($var, $obsolete);
139 : martin 1.1 }
140 :    
141 : skodak 1.721 /**
142 :     * Does proper javascript quoting.
143 : samhemelryk 1.1255 *
144 : jamiesensei 1.739 * Do not use addslashes anymore, because it does not work when magic_quotes_sybase is enabled.
145 :     *
146 : samhemelryk 1.1255 * @param mixed $var String, Array, or Object to add slashes to
147 : skodak 1.721 * @return mixed quoted result
148 :     */
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 : skodak 1.722 $var = str_replace('</', '<\/', $var); // XHTML compliance
154 : skodak 1.721 } 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);
160 :     }
161 :     $var = (object)$a;
162 :     }
163 :     return $var;
164 :     }
165 : dhawes 1.302
166 :     /**
167 :     * Remove query string from url
168 :     *
169 :     * Takes in a URL and returns it without the querystring portion
170 :     *
171 :     * @param string $url the url which may have a query string attached
172 : samhemelryk 1.1255 * @return string The remaining URL
173 : dhawes 1.302 */
174 :     function strip_querystring($url) {
175 : martin 1.1
176 : moodler 1.43 if ($commapos = strpos($url, '?')) {
177 :     return substr($url, 0, $commapos);
178 :     } else {
179 :     return $url;
180 :     }
181 : martin 1.1 }
182 :    
183 : dhawes 1.302 /**
184 : moodler 1.835 * Returns the URL of the HTTP_REFERER, less the querystring portion if required
185 : samhemelryk 1.1255 *
186 :     * @uses $_SERVER
187 : scyrma 1.1026 * @param boolean $stripquery if true, also removes the query part of the url.
188 : samhemelryk 1.1255 * @return string The resulting referer or emtpy string
189 : dhawes 1.302 */
190 : moodler 1.835 function get_referer($stripquery=true) {
191 : skodak 1.720 if (isset($_SERVER['HTTP_REFERER'])) {
192 : moodler 1.835 if ($stripquery) {
193 :     return strip_querystring($_SERVER['HTTP_REFERER']);
194 :     } else {
195 :     return $_SERVER['HTTP_REFERER'];
196 :     }
197 : skodak 1.720 } else {
198 : jamiesensei 1.739 return '';
199 : skodak 1.720 }
200 : martin 1.1 }
201 :    
202 : moodler 1.30
203 : dhawes 1.302 /**
204 :     * Returns the name of the current script, WITH the querystring portion.
205 : samhemelryk 1.1255 *
206 :     * This function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
207 : dhawes 1.302 * return different things depending on a lot of things like your OS, Web
208 :     * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
209 : dhawes 1.304 * <b>NOTE:</b> This function returns false if the global variables needed are not set.
210 :     *
211 : samhemelryk 1.1255 * @global string
212 :     * @return mixed String, or false if the global variables needed are not set
213 : dhawes 1.302 */
214 : moodler 1.1266 function me() {
215 :     global $ME;
216 :     return $ME;
217 : martin 1.1 }
218 :    
219 : dhawes 1.302 /**
220 : samhemelryk 1.1255 * Returns the name of the current script, WITH the full URL.
221 :     *
222 :     * This function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
223 :     * return different things depending on a lot of things like your OS, Web
224 :     * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.
225 :     * <b>NOTE:</b> This function returns false if the global variables needed are not set.
226 :     *
227 : dhawes 1.304 * Like {@link me()} but returns a full URL
228 : dhawes 1.302 * @see me()
229 : samhemelryk 1.1255 *
230 :     * @global string
231 :     * @return mixed String, or false if the global variables needed are not set
232 : dhawes 1.302 */
233 : martin 1.1 function qualified_me() {
234 : skodak 1.1198 global $FULLME;
235 :     return $FULLME;
236 : martin 1.1 }
237 :    
238 : jamiesensei 1.874 /**
239 :     * Class for creating and manipulating urls.
240 : jamiesensei 1.894 *
241 : samhemelryk 1.1255 * It can be used in moodle pages where config.php has been included without any further includes.
242 :     *
243 : nicolasconnault 1.1314 * It is useful for manipulating urls with long lists of params.
244 :     * One situation where it will be useful is a page which links to itself to perfrom various actions
245 : samhemelryk 1.1255 * and / or to process form data. A moodle_url object :
246 : nicolasconnault 1.1314 * can be created for a page to refer to itself with all the proper get params being passed from page call to
247 :     * page call and methods can be used to output a url including all the params, optionally adding and overriding
248 : samhemelryk 1.1255 * params and can also be used to
249 :     * - output the url without any get params
250 : nicolasconnault 1.1314 * - and output the params as hidden fields to be output within a form
251 : samhemelryk 1.1255 *
252 :     * One important usage note is that data passed to methods out, out_action, get_query_string and
253 : nicolasconnault 1.1314 * hidden_params_out affect what is returned by the function and do not change the data stored in the object.
254 :     * This is to help with typical usage of these objects where one object is used to output urls
255 :     * in many places in a page.
256 : samhemelryk 1.1255 *
257 :     * @link http://docs.moodle.org/en/Development:lib/weblib.php_moodle_url See short write up here
258 :     * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
259 :     * @package moodlecore
260 : jamiesensei 1.874 */
261 :     class moodle_url {
262 : samhemelryk 1.1255 /**
263 :     * @var string
264 :     * @access protected
265 :     */
266 : tjhunt 1.1243 protected $scheme = ''; // e.g. http
267 :     protected $host = '';
268 :     protected $port = '';
269 :     protected $user = '';
270 :     protected $pass = '';
271 :     protected $path = '';
272 :     protected $fragment = '';
273 : samhemelryk 1.1255 /**
274 : nicolasconnault 1.1314 * @var array
275 : samhemelryk 1.1255 * @access protected
276 :     */
277 : tjhunt 1.1243 protected $params = array(); // Associative array of query string params
278 : jamiesensei 1.894
279 : jamiesensei 1.874 /**
280 : nicolasconnault 1.1314 * Pass no arguments to create a url that refers to this page.
281 : samhemelryk 1.1255 * Use empty string to create empty url.
282 : jamiesensei 1.894 *
283 : samhemelryk 1.1255 * @global string
284 : tjhunt 1.1244 * @param mixed $url a number of different forms are accespted:
285 :     * null - create a URL that is the same as the URL used to load this page, but with no query string
286 :     * '' - and empty URL
287 :     * string - a URL, will be parsed into it's bits, including query string
288 :     * array - as returned from the PHP function parse_url
289 :     * moodle_url - make a copy of another moodle_url
290 : tjhunt 1.1243 * @param array $params these params override anything in the query string
291 :     * where params have the same name.
292 : jamiesensei 1.874 */
293 : tjhunt 1.1243 public function __construct($url = null, $params = array()) {
294 : tjhunt 1.1244 if ($url === '') {
295 :     // Leave URL blank.
296 :     } else if (is_a($url, 'moodle_url')) {
297 :     $this->scheme = $url->scheme;
298 :     $this->host = $url->host;
299 :     $this->port = $url->port;
300 :     $this->user = $url->user;
301 :     $this->pass = $url->pass;
302 : tjhunt 1.1246 $this->path = $url->path;
303 : tjhunt 1.1244 $this->fragment = $url->fragment;
304 :     $this->params = $url->params;
305 :     } else {
306 : tjhunt 1.1243 if ($url === null) {
307 : tjhunt 1.1244 global $ME;
308 : skodak 1.1198 $url = $ME;
309 : jamiesensei 1.874 }
310 : tjhunt 1.1244 if (is_string($url)) {
311 :     $url = parse_url($url);
312 :     }
313 :     $parts = $url;
314 : tjhunt 1.1243 if ($parts === FALSE) {
315 : tjhunt 1.1244 throw new moodle_exception('invalidurl');
316 : jamiesensei 1.874 }
317 : tjhunt 1.1243 if (isset($parts['query'])) {
318 : jamiesensei 1.890 parse_str(str_replace('&amp;', '&', $parts['query']), $this->params);
319 : jamiesensei 1.874 }
320 :     unset($parts['query']);
321 : tjhunt 1.1243 foreach ($parts as $key => $value) {
322 : jamiesensei 1.874 $this->$key = $value;
323 :     }
324 :     }
325 : tjhunt 1.1244 $this->params($params);
326 : jamiesensei 1.894 }
327 : tjhunt 1.1243
328 : jamiesensei 1.874 /**
329 : nicolasconnault 1.1314 * Add an array of params to the params for this page.
330 : samhemelryk 1.1255 *
331 :     * The added params override existing ones if they have the same name.
332 : jamiesensei 1.874 *
333 : nicolasconnault 1.1306 * @param array $params Defaults to null. If null then returns all params.
334 : samhemelryk 1.1255 * @return array Array of Params for url.
335 : jamiesensei 1.874 */
336 : tjhunt 1.1243 public function params($params = null) {
337 :     if (!is_null($params)) {
338 : jamiesensei 1.1110 return $this->params = $params + $this->params;
339 :     } else {
340 :     return $this->params;
341 :     }
342 : jamiesensei 1.874 }
343 : jamiesensei 1.894
344 : jamiesensei 1.874 /**
345 : nicolasconnault 1.1314 * Remove all params if no arguments passed.
346 :     * Remove selected params if arguments are passed.
347 : samhemelryk 1.1255 *
348 :     * Can be called as either remove_params('param1', 'param2')
349 : tjhunt 1.1244 * or remove_params(array('param1', 'param2')).
350 : jamiesensei 1.874 *
351 : tjhunt 1.1244 * @param mixed $params either an array of param names, or a string param name,
352 :     * @param string $params,... any number of additional param names.
353 : jamiesensei 1.874 */
354 : tjhunt 1.1245 public function remove_params($params = NULL) {
355 : tjhunt 1.1244 if (empty($params)) {
356 :     $this->params = array();
357 :     return;
358 :     }
359 :     if (!is_array($params)) {
360 :     $params = func_get_args();
361 :     }
362 :     foreach ($params as $param) {
363 :     if (isset($this->params[$param])) {
364 :     unset($this->params[$param]);
365 : jamiesensei 1.874 }
366 :     }
367 :     }
368 :    
369 :     /**
370 : nicolasconnault 1.1314 * Add a param to the params for this page.
371 : samhemelryk 1.1255 *
372 :     * The added param overrides existing one if theyhave the same name.
373 : jamiesensei 1.874 *
374 :     * @param string $paramname name
375 : samhemelryk 1.1255 * @param string $param Param value. Defaults to null. If null then return value of param 'name'
376 :     * @return void|string If $param was null then the value of $paramname was returned
377 : tjhunt 1.1262 * (null is returned if that param does not exist).
378 : jamiesensei 1.874 */
379 : tjhunt 1.1243 public function param($paramname, $param = null) {
380 :     if (!is_null($param)) {
381 : jamiesensei 1.1110 $this->params = array($paramname => $param) + $this->params;
382 : tjhunt 1.1262 } else if (array_key_exists($paramname, $this->params)) {
383 :     return $this->params[$paramname];
384 : jamiesensei 1.1110 } else {
385 : tjhunt 1.1262 return null;
386 : jamiesensei 1.1110 }
387 : jamiesensei 1.874 }
388 :    
389 : tjhunt 1.1243 /**
390 :     * Get the params as as a query string.
391 : samhemelryk 1.1255 *
392 : tjhunt 1.1243 * @param array $overrideparams params to add to the output params, these
393 :     * override existing ones with the same name.
394 : mudrd8mz 1.1295 * @param boolean $escaped Use &amp; as params separator instead of plain &
395 : tjhunt 1.1243 * @return string query string that can be added to a url.
396 :     */
397 : mudrd8mz 1.1295 public function get_query_string($overrideparams = array(), $escaped = true) {
398 : jamiesensei 1.874 $arr = array();
399 :     $params = $overrideparams + $this->params;
400 : tjhunt 1.1243 foreach ($params as $key => $val) {
401 : jamiesensei 1.874 $arr[] = urlencode($key)."=".urlencode($val);
402 :     }
403 : mudrd8mz 1.1295 if ($escaped) {
404 :     return implode('&amp;', $arr);
405 :     } else {
406 :     return implode('&', $arr);
407 :     }
408 : jamiesensei 1.874 }
409 : tjhunt 1.1243
410 : jamiesensei 1.874 /**
411 :     * Outputs params as hidden form elements.
412 : jamiesensei 1.875 *
413 : samhemelryk 1.1255 * @param array $exclude params to ignore
414 : jamiesensei 1.884 * @param integer $indent indentation
415 : jamiesensei 1.1076 * @param array $overrideparams params to add to the output params, these
416 : tjhunt 1.1243 * override existing ones with the same name.
417 : jamiesensei 1.874 * @return string html for form elements.
418 :     */
419 : tjhunt 1.1243 public function hidden_params_out($exclude = array(), $indent = 0, $overrideparams=array()) {
420 : jamiesensei 1.874 $tabindent = str_repeat("\t", $indent);
421 :     $str = '';
422 : jamiesensei 1.1076 $params = $overrideparams + $this->params;
423 : tjhunt 1.1243 foreach ($params as $key => $val) {
424 : jamiesensei 1.875 if (FALSE === array_search($key, $exclude)) {
425 : jamiesensei 1.885 $val = s($val);
426 : jamiesensei 1.875 $str.= "$tabindent<input type=\"hidden\" name=\"$key\" value=\"$val\" />\n";
427 :     }
428 : jamiesensei 1.874 }
429 :     return $str;
430 :     }
431 : tjhunt 1.1243
432 : jamiesensei 1.874 /**
433 :     * Output url
434 : jamiesensei 1.894 *
435 : mudrd8mz 1.1295 * If you use the returned URL in HTML code, you want the escaped ampersands. If you use
436 :     * the returned URL in HTTP headers, you want $escaped=false.
437 :     *
438 : tjhunt 1.1251 * @param boolean $omitquerystring whether to output page params as a query string in the url.
439 : jamiesensei 1.874 * @param array $overrideparams params to add to the output url, these override existing ones with the same name.
440 : mudrd8mz 1.1295 * @param boolean $escaped Use &amp; as params separator instead of plain &
441 : samhemelryk 1.1255 * @return string Resulting URL
442 : jamiesensei 1.874 */
443 : mudrd8mz 1.1295 public function out($omitquerystring = false, $overrideparams = array(), $escaped = true) {
444 : jamiesensei 1.874 $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): '';
445 :     $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':'';
446 :     $uri .= $this->host ? $this->host : '';
447 :     $uri .= $this->port ? ':'.$this->port : '';
448 :     $uri .= $this->path ? $this->path : '';
449 : tjhunt 1.1251 if (!$omitquerystring) {
450 : mudrd8mz 1.1295 $querystring = $this->get_query_string($overrideparams, $escaped);
451 : tjhunt 1.1251 if ($querystring) {
452 :     $uri .= '?' . $querystring;
453 :     }
454 : jamiesensei 1.874 }
455 :     $uri .= $this->fragment ? '#'.$this->fragment : '';
456 : jamiesensei 1.894 return $uri;
457 : jamiesensei 1.874 }
458 : tjhunt 1.1243
459 : jamiesensei 1.874 /**
460 : tjhunt 1.1298 * Return a URL relative to $CFG->wwwroot.
461 :     *
462 :     * Throws an exception if this URL does not start with $CFG->wwwroot.
463 :     *
464 :     * The main use for this is when you want to pass a returnurl from one script to another.
465 :     * In this case the returnurl should be relative to $CFG->wwwroot for two reasons.
466 :     * First, it is shorter. More imporatantly, some web servers (e.g. IIS by default)
467 :     * give a 'security' error if you try to pass a full URL as a GET parameter in another URL.
468 :     *
469 :     * @return string the URL relative to $CFG->wwwroot. Note, you will need to urlencode
470 :     * this result if you are outputting a URL manually (but not if you are adding
471 :     * it to another moodle_url).
472 :     */
473 :     public function out_returnurl() {
474 :     global $CFG;
475 :     $fulluri = $this->out(false, array(), false);
476 :     $uri = str_replace($CFG->wwwroot . '/', '', $fulluri);
477 :     if ($uri == $fulluri) {
478 :     throw new coding_exception('This URL (' . $fulluri . ') is not relative to $CFG->wwwroot.');
479 :     }
480 :     return $uri;
481 :     }
482 :    
483 :     /**
484 : jamiesensei 1.874 * Output action url with sesskey
485 : jamiesensei 1.894 *
486 : samhemelryk 1.1255 * Adds sesskey and overriderparams then calls {@link out()}
487 :     * @see out()
488 :     *
489 :     * @param array $overrideparams Allows you to override params
490 : jamiesensei 1.874 * @return string url
491 :     */
492 : tjhunt 1.1243 public function out_action($overrideparams = array()) {
493 : jamiesensei 1.874 $overrideparams = array('sesskey'=> sesskey()) + $overrideparams;
494 : jamiesensei 1.891 return $this->out(false, $overrideparams);
495 : jamiesensei 1.874 }
496 : nicolasconnault 1.1324
497 :     /**
498 :     * Compares this moodle_url with another
499 :     * See documentation of constants for an explanation of the comparison flags.
500 :     * @param moodle_url $url The moodle_url object to compare
501 :     * @param int $matchtype The type of comparison (URL_MATCH_BASE, URL_MATCH_PARAMS, URL_MATCH_EXACT)
502 :     * @return boolean
503 :     */
504 :     public function compare(moodle_url $url, $matchtype = URL_MATCH_EXACT) {
505 : samhemelryk 1.1342
506 : samhemelryk 1.1338 $baseself = $this->out(true);
507 :     $baseother = $url->out(true);
508 :    
509 :     // Append index.php if there is no specific file
510 :     if (substr($baseself,-1)=='/') {
511 :     $baseself .= 'index.php';
512 :     }
513 :     if (substr($baseother,-1)=='/') {
514 :     $baseother .= 'index.php';
515 :     }
516 :    
517 :     // Compare the two base URLs
518 :     if ($baseself != $baseother) {
519 : nicolasconnault 1.1324 return false;
520 :     }
521 :    
522 :     if ($matchtype == URL_MATCH_BASE) {
523 :     return true;
524 :     }
525 :    
526 :     $urlparams = $url->params();
527 :     foreach ($this->params() as $param => $value) {
528 :     if ($param == 'sesskey') {
529 :     continue;
530 :     }
531 :     if (!array_key_exists($param, $urlparams) || $urlparams[$param] != $value) {
532 :     return false;
533 :     }
534 :     }
535 :    
536 :     if ($matchtype == URL_MATCH_PARAMS) {
537 :     return true;
538 :     }
539 :    
540 :     foreach ($urlparams as $param => $value) {
541 :     if ($param == 'sesskey') {
542 :     continue;
543 :     }
544 :     if (!array_key_exists($param, $this->params()) || $this->param($param) != $value) {
545 :     return false;
546 :     }
547 :     }
548 :    
549 :     return true;
550 :     }
551 : samhemelryk 1.1345
552 :     /**
553 :     * Sets the anchor for the URI (the bit after the hash)
554 :     * @param string $anchor
555 :     */
556 :     public function set_anchor($anchor) {
557 :     // Match the anchor against the NMTOKEN spec
558 :     if (preg_match('#[a-zA-Z\_\:][a-zA-Z0-9\_\-\.\:]*#', $anchor)) {
559 :     $this->fragment = $anchor;
560 :     }
561 :     }
562 : jamiesensei 1.874 }
563 :    
564 : dhawes 1.302 /**
565 : nicolasconnault 1.1306 * Given an unknown $url type (string or moodle_url), returns a string URL.
566 :     * A relative URL is handled with $PAGE->url but gives a debugging error.
567 :     *
568 :     * @param mixed $url The URL (moodle_url or string)
569 :     * @param bool $stripformparams Whether or not to strip the query params from the URL
570 : tjhunt 1.1311 * @return string the URL. &s are unescaped. You must use s(...) to output this to XHTML. ($OUTPUT normally does this automatically.)
571 : nicolasconnault 1.1306 */
572 :     function prepare_url($url, $stripformparams=false) {
573 :     global $CFG, $PAGE;
574 :    
575 :     $output = $url;
576 :    
577 :     if ($url instanceof moodle_url) {
578 : tjhunt 1.1311 $output = $url->out($stripformparams, array(), false);
579 : nicolasconnault 1.1306 }
580 :    
581 :     // Handle relative URLs
582 :     if (substr($output, 0, 4) != 'http' && substr($output, 0, 1) != '/') {
583 :     if (preg_match('/(.*)\/([A-Za-z0-9-_]*\.php)$/', $PAGE->url->out(true), $matches)) {
584 :     return $matches[1] . "/$output";
585 : nicolasconnault 1.1327 } else if ($output == '') {
586 :     return $PAGE->url->out(false, array(), false) . '#';
587 : nicolasconnault 1.1306 } else {
588 : samhemelryk 1.1335 throw new coding_exception('Unrecognied URL scheme. Please check the formatting of the URL passed to this function. Absolute URLs are the preferred scheme.');
589 : nicolasconnault 1.1306 }
590 :     }
591 :    
592 : samhemelryk 1.1336 // Add wwwroot only if the URL does not already start with http:// or https://
593 :     if (!preg_match('|https?://|', $output) ) {
594 :     $output = $CFG->wwwroot . $output;
595 :     }
596 :    
597 : nicolasconnault 1.1306 return $output;
598 :     }
599 :    
600 :     /**
601 : dhawes 1.302 * Determine if there is data waiting to be processed from a form
602 :     *
603 :     * Used on most forms in Moodle to check for data
604 :     * Returns the data as an object, if it's found.
605 :     * This object can be used in foreach loops without
606 :     * casting because it's cast to (array) automatically
607 : urs_hunkler 1.347 *
608 : skodak 1.698 * Checks that submitted POST data exists and returns it as object.
609 : dhawes 1.304 *
610 : samhemelryk 1.1255 * @uses $_POST
611 : skodak 1.698 * @return mixed false or object
612 : dhawes 1.302 */
613 : skodak 1.1082 function data_submitted() {
614 : dhawes 1.304
615 : moodler 1.50 if (empty($_POST)) {
616 :     return false;
617 : moodler 1.47 } else {
618 : skodak 1.1082 return (object)$_POST;
619 : moodler 1.47 }
620 : martin 1.1 }
621 :    
622 : dhawes 1.302 /**
623 : dhawes 1.304 * Given some normal text this function will break up any
624 :     * long words to a given size by inserting the given character
625 :     *
626 : moodler 1.510 * It's multibyte savvy and doesn't change anything inside html tags.
627 :     *
628 : dhawes 1.302 * @param string $string the string to be modified
629 : dhawes 1.308 * @param int $maxsize maximum length of the string to be returned
630 : dhawes 1.302 * @param string $cutchar the string used to represent word breaks
631 :     * @return string
632 :     */
633 : moodler 1.226 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
634 : julmis 1.436
635 : moodler 1.510 /// Loading the textlib singleton instance. We are going to need it.
636 :     $textlib = textlib_get_instance();
637 : moodler 1.226
638 : moodler 1.510 /// First of all, save all the tags inside the text to skip them
639 :     $tags = array();
640 :     filter_save_tags($string,$tags);
641 : moodler 1.257
642 : moodler 1.510 /// Process the string adding the cut when necessary
643 : moodler 1.226 $output = '';
644 : skodak 1.683 $length = $textlib->strlen($string);
645 : moodler 1.226 $wordlength = 0;
646 :    
647 :     for ($i=0; $i<$length; $i++) {
648 : skodak 1.683 $char = $textlib->substr($string, $i, 1);
649 : moodler 1.510 if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
650 : moodler 1.226 $wordlength = 0;
651 :     } else {
652 :     $wordlength++;
653 :     if ($wordlength > $maxsize) {
654 :     $output .= $cutchar;
655 :     $wordlength = 0;
656 :     }
657 :     }
658 :     $output .= $char;
659 :     }
660 : moodler 1.510
661 :     /// Finally load the tags back again
662 :     if (!empty($tags)) {
663 :     $output = str_replace(array_keys($tags), $tags, $output);
664 :     }
665 :    
666 : moodler 1.226 return $output;
667 :     }
668 :    
669 : samhemelryk 1.1333 /**
670 : tjhunt 1.1185 * Try and close the current window using JavaScript, either immediately, or after a delay.
671 : samhemelryk 1.1255 *
672 :     * Echo's out the resulting XHTML & javascript
673 :     *
674 :     * @global object
675 :     * @global object
676 : tjhunt 1.1185 * @param integer $delay a delay in seconds before closing the window. Default 0.
677 :     * @param boolean $reloadopener if true, we will see if this window was a pop-up, and try
678 :     * to reload the parent window before this one closes.
679 : moodler 1.408 */
680 : tjhunt 1.1185 function close_window($delay = 0, $reloadopener = false) {
681 : nicolasconnault 1.1323 global $THEME, $PAGE, $OUTPUT;
682 : tjhunt 1.1185
683 : tjhunt 1.1237 if (!$PAGE->headerprinted) {
684 : samhemelryk 1.1333 $PAGE->set_title(get_string('closewindow'));
685 :     echo $OUTPUT->header();
686 : tjhunt 1.1185 } else {
687 :     print_container_end_all(false, $THEME->open_header_containers);
688 :     }
689 :    
690 :     if ($reloadopener) {
691 :     $function = 'close_window_reloading_opener';
692 :     } else {
693 :     $function = 'close_window';
694 : moodler 1.408 }
695 : tjhunt 1.1185 echo '<p class="centerpara">' . get_string('windowclosing') . '</p>';
696 : tjhunt 1.1262
697 :     $PAGE->requires->js_function_call($function)->after_delay($delay);
698 : tjhunt 1.1185
699 : nicolasconnault 1.1323 echo $OUTPUT->footer();
700 : tjhunt 1.1185 exit;
701 : moodler 1.408 }
702 :    
703 : nicolasconnault 1.1321 /**
704 :     * Returns a string containing a link to the user documentation for the current
705 :     * page. Also contains an icon by default. Shown to teachers and admin only.
706 :     *
707 :     * @global object
708 :     * @global object
709 :     * @param string $text The text to be displayed for the link
710 :     * @param string $iconpath The path to the icon to be displayed
711 :     * @return string The link to user documentation for this current page
712 :     */
713 :     function page_doc_link($text='', $iconpath='') {
714 :     global $CFG, $PAGE, $OUTPUT;
715 :    
716 :     if (empty($CFG->docroot) || during_initial_install()) {
717 :     return '';
718 :     }
719 :     if (!has_capability('moodle/site:doclinks', $PAGE->context)) {
720 :     return '';
721 :     }
722 :    
723 :     $path = $PAGE->docspath;
724 :     if (!$path) {
725 :     return '';
726 :     }
727 :     return $OUTPUT->doc_link($path, $text, $iconpath);
728 :     }
729 :    
730 : mjollnir_ 1.513
731 :     /**
732 : dhawes 1.304 * Validates an email to make sure it makes sense.
733 :     *
734 :     * @param string $address The email address to validate.
735 :     * @return boolean
736 :     */
737 : dhawes 1.308 function validate_email($address) {
738 : martin 1.1
739 : andyjdavis 1.1348 return (preg_match('#^[-!\#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
740 :     '(\.[-!\#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
741 : martin 1.1 '@'.
742 : andyjdavis 1.1348 '[-!\#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
743 :     '[-!\#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$#',
744 : martin 1.1 $address));
745 :     }
746 :    
747 : dhawes 1.304 /**
748 : skodak 1.327 * Extracts file argument either from file parameter or PATH_INFO
749 : skodak 1.1198 * Note: $scriptname parameter is not needed anymore
750 : skodak 1.327 *
751 : samhemelryk 1.1255 * @global string
752 :     * @uses $_SERVER
753 :     * @uses PARAM_PATH
754 : skodak 1.327 * @return string file path (only safe characters)
755 :     */
756 : skodak 1.1198 function get_file_argument() {
757 :     global $SCRIPT;
758 : skodak 1.327
759 :     $relativepath = optional_param('file', FALSE, PARAM_PATH);
760 :    
761 :     // then try extract file from PATH_INFO (slasharguments method)
762 : skodak 1.1198 if ($relativepath === false and isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') {
763 : skodak 1.327 // check that PATH_INFO works == must not contain the script name
764 : skodak 1.1198 if (strpos($_SERVER['PATH_INFO'], $SCRIPT) === false) {
765 :     $relativepath = clean_param(urldecode($_SERVER['PATH_INFO']), PARAM_PATH);
766 : skodak 1.327 }
767 :     }
768 :    
769 : skodak 1.1198 // note: we are not using any other way because they are not compatible with unicode file names ;-)
770 : skodak 1.327
771 :     return $relativepath;
772 :     }
773 :    
774 :     /**
775 : dhawes 1.308 * Just returns an array of text formats suitable for a popup menu
776 : dhawes 1.304 *
777 : dhawes 1.308 * @uses FORMAT_MOODLE
778 :     * @uses FORMAT_HTML
779 :     * @uses FORMAT_PLAIN
780 :     * @uses FORMAT_MARKDOWN
781 :     * @return array
782 : dhawes 1.304 */
783 : moodler 1.23 function format_text_menu() {
784 : dhawes 1.304
785 : dhawes 1.301 return array (FORMAT_MOODLE => get_string('formattext'),
786 :     FORMAT_HTML => get_string('formathtml'),
787 :     FORMAT_PLAIN => get_string('formatplain'),
788 :     FORMAT_MARKDOWN => get_string('formatmarkdown'));
789 : moodler 1.23 }
790 :    
791 : dhawes 1.304 /**
792 :     * Given text in a variety of format codings, this function returns
793 : urs_hunkler 1.347 * the text as safe HTML.
794 : dhawes 1.304 *
795 : jamiesensei 1.1023 * This function should mainly be used for long strings like posts,
796 : poltawski 1.1004 * answers, glossary items etc. For short strings @see format_string().
797 :     *
798 : samhemelryk 1.1255 * @todo Finish documenting this function
799 :     *
800 :     * @global object
801 :     * @global object
802 :     * @global object
803 :     * @global object
804 : dhawes 1.308 * @uses FORMAT_MOODLE
805 :     * @uses FORMAT_HTML
806 :     * @uses FORMAT_PLAIN
807 :     * @uses FORMAT_WIKI
808 :     * @uses FORMAT_MARKDOWN
809 : samhemelryk 1.1255 * @uses CLI_SCRIPT
810 :     * @staticvar array $croncache
811 : dhawes 1.308 * @param string $text The text to be formatted. This is raw text originally from user input.
812 : urs_hunkler 1.347 * @param int $format Identifier of the text format to be used
813 : samhemelryk 1.1255 * [FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN]
814 :     * @param object $options ?
815 :     * @param int $courseid The courseid to use, defaults to $COURSE->courseid
816 : dhawes 1.308 * @return string
817 : dhawes 1.304 */
818 : skodak 1.615 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
819 : tjhunt 1.1238 global $CFG, $COURSE, $DB, $PAGE;
820 : moodler 1.193
821 : skodak 1.1032 static $croncache = array();
822 : skodak 1.1206
823 : dongsheng 1.1188 $hashstr = '';
824 : skodak 1.1032
825 : skodak 1.678 if ($text === '') {
826 :     return ''; // no need to do any filters and cleaning
827 :     }
828 : dongsheng 1.1305 if (!empty($options->comments) && !empty($CFG->usecomments)) {
829 :     require_once($CFG->libdir . '/commentlib.php');
830 :     $comment = new comment($options->comments);
831 :     $cmt = $comment->init(true);
832 :     } else {
833 :     $cmt = '';
834 :     }
835 :    
836 : skodak 1.678
837 : skodak 1.1231 if (!isset($options->trusted)) {
838 :     $options->trusted = false;
839 : skodak 1.615 }
840 : skodak 1.500 if (!isset($options->noclean)) {
841 : skodak 1.1231 if ($options->trusted and trusttext_active()) {
842 :     // no cleaning if text trusted and noclean not specified
843 :     $options->noclean=true;
844 :     } else {
845 :     $options->noclean=false;
846 :     }
847 : skodak 1.500 }
848 : skodak 1.653 if (!isset($options->nocache)) {
849 :     $options->nocache=false;
850 :     }
851 : skodak 1.500 if (!isset($options->smiley)) {
852 :     $options->smiley=true;
853 :     }
854 :     if (!isset($options->filter)) {
855 :     $options->filter=true;
856 :     }
857 :     if (!isset($options->para)) {
858 :     $options->para=true;
859 :     }
860 :     if (!isset($options->newlines)) {
861 :     $options->newlines=true;
862 : moodler 1.206 }
863 : moodler 1.193 if (empty($courseid)) {
864 : skodak 1.802 $courseid = $COURSE->id;
865 : moodler 1.193 }
866 : moodler 1.148
867 : tjhunt 1.1227 if ($options->filter) {
868 :     $filtermanager = filter_manager::instance();
869 :     } else {
870 :     $filtermanager = new null_filter_manager();
871 : dongsheng 1.1188 }
872 : tjhunt 1.1238 $context = $PAGE->context;
873 : tjhunt 1.1227
874 : skodak 1.653 if (!empty($CFG->cachetext) and empty($options->nocache)) {
875 : tjhunt 1.1227 $hashstr .= $text.'-'.$filtermanager->text_filtering_hash($context, $courseid).'-'.(int)$courseid.'-'.current_language().'-'.
876 : skodak 1.1231 (int)$format.(int)$options->trusted.(int)$options->noclean.(int)$options->smiley.
877 : tjhunt 1.1227 (int)$options->filter.(int)$options->para.(int)$options->newlines;
878 : dongsheng 1.1188
879 : skodak 1.500 $time = time() - $CFG->cachetext;
880 : skodak 1.1206 $md5key = md5($hashstr);
881 : skodak 1.1197 if (CLI_SCRIPT) {
882 : skodak 1.1032 if (isset($croncache[$md5key])) {
883 : dongsheng 1.1305 return $croncache[$md5key].$cmt;
884 : skodak 1.1032 }
885 :     }
886 :    
887 : skodak 1.1294 if ($oldcacheitem = $DB->get_record('cache_text', array('md5key'=>$md5key), '*', IGNORE_MULTIPLE)) {
888 : moodler 1.554 if ($oldcacheitem->timemodified >= $time) {
889 : skodak 1.1197 if (CLI_SCRIPT) {
890 : skodak 1.1032 if (count($croncache) > 150) {
891 : skodak 1.1038 reset($croncache);
892 :     $key = key($croncache);
893 :     unset($croncache[$key]);
894 : skodak 1.1032 }
895 :     $croncache[$md5key] = $oldcacheitem->formattedtext;
896 :     }
897 : dongsheng 1.1305 return $oldcacheitem->formattedtext.$cmt;
898 : moodler 1.554 }
899 : skodak 1.500 }
900 :     }
901 :    
902 : moodler 1.23 switch ($format) {
903 : moodler 1.63 case FORMAT_HTML:
904 : skodak 1.615 if ($options->smiley) {
905 : skodak 1.500 replace_smilies($text);
906 :     }
907 : skodak 1.615 if (!$options->noclean) {
908 : skodak 1.790 $text = clean_text($text, FORMAT_HTML);
909 : moodler 1.272 }
910 : tjhunt 1.1227 $text = $filtermanager->filter_text($text, $context, $courseid);
911 : moodler 1.63 break;
912 :    
913 : moodler 1.81 case FORMAT_PLAIN:
914 : skodak 1.790 $text = s($text); // cleans dangerous JS
915 : stronk7 1.241 $text = rebuildnolinktag($text);
916 : dhawes 1.301 $text = str_replace(' ', '&nbsp; ', $text);
917 : moodler 1.81 $text = nl2br($text);
918 :     break;
919 :    
920 : moodler 1.87 case FORMAT_WIKI:
921 : thepurpleblob 1.441 // this format is deprecated
922 : julmis 1.448 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
923 :     this message as all texts should have been converted to Markdown format instead.
924 : moodler 1.442 Please post a bug report to http://moodle.org/bugs with information about where you
925 : skodak 1.500 saw this message.</p>'.s($text);
926 : moodler 1.87 break;
927 :    
928 : moodler 1.265 case FORMAT_MARKDOWN:
929 :     $text = markdown_to_html($text);
930 : skodak 1.615 if ($options->smiley) {
931 : skodak 1.500 replace_smilies($text);
932 :     }
933 : skodak 1.615 if (!$options->noclean) {
934 : skodak 1.790 $text = clean_text($text, FORMAT_HTML);
935 : moodler 1.272 }
936 : tjhunt 1.1227 $text = $filtermanager->filter_text($text, $context, $courseid);
937 : moodler 1.265 break;
938 :    
939 : moodler 1.63 default: // FORMAT_MOODLE or anything else
940 : moodler 1.225 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
941 : skodak 1.615 if (!$options->noclean) {
942 : skodak 1.790 $text = clean_text($text, FORMAT_HTML);
943 : moodler 1.272 }
944 : tjhunt 1.1227 $text = $filtermanager->filter_text($text, $context, $courseid);
945 :     break;
946 :     }
947 : julmis 1.530
948 : tjhunt 1.1227 // Warn people that we have removed this old mechanism, just in case they
949 :     // were stupid enough to rely on it.
950 :     if (isset($CFG->currenttextiscacheable)) {
951 :     debugging('Once upon a time, Moodle had a truly evil use of global variables ' .
952 :     'called $CFG->currenttextiscacheable. The good news is that this no ' .
953 :     'longer exists. The bad news is that you seem to be using a filter that '.
954 :     'relies on it. Please seek out and destroy that filter code.', DEBUG_DEVELOPER);
955 : moodler 1.206 }
956 : moodler 1.162
957 : tjhunt 1.1227 if (empty($options->nocache) and !empty($CFG->cachetext)) {
958 : skodak 1.1197 if (CLI_SCRIPT) {
959 : skodak 1.1032 // special static cron cache - no need to store it in db if its not already there
960 :     if (count($croncache) > 150) {
961 : skodak 1.1038 reset($croncache);
962 :     $key = key($croncache);
963 :     unset($croncache[$key]);
964 : skodak 1.1032 }
965 :     $croncache[$md5key] = $text;
966 : dongsheng 1.1305 return $text.$cmt;
967 : skodak 1.1032 }
968 :    
969 : skodak 1.670 $newcacheitem = new object();
970 : moodler 1.554 $newcacheitem->md5key = $md5key;
971 : skodak 1.1073 $newcacheitem->formattedtext = $text;
972 : moodler 1.554 $newcacheitem->timemodified = time();
973 :     if ($oldcacheitem) { // See bug 4677 for discussion
974 :     $newcacheitem->id = $oldcacheitem->id;
975 : skodak 1.1229 try {
976 :     $DB->update_record('cache_text', $newcacheitem); // Update existing record in the cache table
977 :     } catch (dml_exception $e) {
978 :     // It's unlikely that the cron cache cleaner could have
979 :     // deleted this entry in the meantime, as it allows
980 :     // some extra time to cover these cases.
981 :     }
982 :     } else {
983 :     try {
984 :     $DB->insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table
985 :     } catch (dml_exception $e) {
986 :     // Again, it's possible that another user has caused this
987 :     // record to be created already in the time that it took
988 :     // to traverse this function. That's OK too, as the
989 :     // call above handles duplicate entries, and eventually
990 :     // the cron cleaner will delete them.
991 :     }
992 : moodler 1.554 }
993 : moodler 1.23 }
994 : dongsheng 1.1305 return $text.$cmt;
995 : nicolasconnault 1.1314
996 : moodler 1.23 }
997 :    
998 : samhemelryk 1.1255 /**
999 :     * Converts the text format from the value to the 'internal'
1000 : nicolasconnault 1.1314 * name or vice versa.
1001 : samhemelryk 1.1255 *
1002 :     * $key can either be the value or the name and you get the other back.
1003 : julmis 1.530 *
1004 : samhemelryk 1.1255 * @uses FORMAT_MOODLE
1005 :     * @uses FORMAT_HTML
1006 :     * @uses FORMAT_PLAIN
1007 :     * @uses FORMAT_MARKDOWN
1008 :     * @param mixed $key int 0-4 or string one of 'moodle','html','plain','markdown'
1009 :     * @return mixed as above but the other way around!
1010 : thepurpleblob 1.467 */
1011 :     function text_format_name( $key ) {
1012 :     $lookup = array();
1013 :     $lookup[FORMAT_MOODLE] = 'moodle';
1014 :     $lookup[FORMAT_HTML] = 'html';
1015 :     $lookup[FORMAT_PLAIN] = 'plain';
1016 :     $lookup[FORMAT_MARKDOWN] = 'markdown';
1017 :     $value = "error";
1018 :     if (!is_numeric($key)) {
1019 :     $key = strtolower( $key );
1020 :     $value = array_search( $key, $lookup );
1021 :     }
1022 :     else {
1023 :     if (isset( $lookup[$key] )) {
1024 :     $value = $lookup[ $key ];
1025 :     }
1026 :     }
1027 :     return $value;
1028 :     }
1029 :    
1030 : skodak 1.1013 /**
1031 :     * Resets all data related to filters, called during upgrade or when filter settings change.
1032 : samhemelryk 1.1255 *
1033 :     * @global object
1034 :     * @global object
1035 : skodak 1.1013 * @return void
1036 :     */
1037 :     function reset_text_filters_cache() {
1038 : skodak 1.1079 global $CFG, $DB;
1039 : skodak 1.1013
1040 : skodak 1.1079 $DB->delete_records('cache_text');
1041 : skodak 1.1013 $purifdir = $CFG->dataroot.'/cache/htmlpurifier';
1042 :     remove_dir($purifdir, true);
1043 :     }
1044 : skodak 1.821
1045 : nicolasconnault 1.1314 /**
1046 : samhemelryk 1.1255 * Given a simple string, this function returns the string
1047 :     * processed by enabled string filters if $CFG->filterall is enabled
1048 :     *
1049 :     * This function should be used to print short strings (non html) that
1050 :     * need filter processing e.g. activity titles, post subjects,
1051 :     * glossary concepts.
1052 :     *
1053 :     * @global object
1054 :     * @global object
1055 :     * @global object
1056 :     * @staticvar bool $strcache
1057 :     * @param string $string The string to be filtered.
1058 :     * @param boolean $striplinks To strip any link in the result text.
1059 :     Moodle 1.8 default changed from false to true! MDL-8713
1060 :     * @param int $courseid Current course as filters can, potentially, use it
1061 :     * @return string
1062 : stronk7 1.410 */
1063 : tjhunt 1.1227 function format_string($string, $striplinks=true, $courseid=NULL ) {
1064 : tjhunt 1.1238 global $CFG, $COURSE, $PAGE;
1065 : skodak 1.832
1066 : stronk7 1.421 //We'll use a in-memory cache here to speed up repeated strings
1067 : skodak 1.821 static $strcache = false;
1068 :    
1069 : skodak 1.832 if ($strcache === false or count($strcache) > 2000 ) { // this number might need some tuning to limit memory usage in cron
1070 : skodak 1.821 $strcache = array();
1071 :     }
1072 : jamiesensei 1.894
1073 : skodak 1.832 //init course id
1074 : skodak 1.833 if (empty($courseid)) {
1075 : skodak 1.832 $courseid = $COURSE->id;
1076 :     }
1077 :    
1078 : stronk7 1.421 //Calculate md5
1079 : skodak 1.832 $md5 = md5($string.'<+>'.$striplinks.'<+>'.$courseid.'<+>'.current_language());
1080 : stronk7 1.421
1081 :     //Fetch from cache if possible
1082 : skodak 1.832 if (isset($strcache[$md5])) {
1083 : stronk7 1.421 return $strcache[$md5];
1084 :     }
1085 :    
1086 : nicolasconnault 1.838 // First replace all ampersands not followed by html entity code
1087 : nicolasconnault 1.1271 // Regular expression moved to its own method for easier unit testing
1088 :     $string = replace_ampersands_not_followed_by_entity($string);
1089 : nicolasconnault 1.849
1090 : tjhunt 1.1228 if (!empty($CFG->filterall) && $CFG->version >= 2009040600) { // Avoid errors during the upgrade to the new system.
1091 : tjhunt 1.1238 $context = $PAGE->context;
1092 : tjhunt 1.1227 $string = filter_manager::instance()->filter_string($string, $context, $courseid);
1093 : stronk7 1.410 }
1094 : jamiesensei 1.894
1095 : moodler 1.847 // If the site requires it, strip ALL tags from this string
1096 :     if (!empty($CFG->formatstringstriptags)) {
1097 :     $string = strip_tags($string);
1098 :    
1099 : skodak 1.1113 } else {
1100 :     // Otherwise strip just links if that is required (default)
1101 :     if ($striplinks) { //strip links in string
1102 : nicolasconnault 1.1272 $string = strip_links($string);
1103 : skodak 1.1113 }
1104 :     $string = clean_text($string);
1105 : stronk7 1.412 }
1106 :    
1107 : stronk7 1.421 //Store to cache
1108 :     $strcache[$md5] = $string;
1109 : jamiesensei 1.894
1110 : stronk7 1.410 return $string;
1111 :     }
1112 :    
1113 : dhawes 1.304 /**
1114 : nicolasconnault 1.1271 * Given a string, performs a negative lookahead looking for any ampersand character
1115 :     * that is not followed by a proper HTML entity. If any is found, it is replaced
1116 :     * by &amp;. The string is then returned.
1117 :     *
1118 :     * @param string $string
1119 :     * @return string
1120 :     */
1121 :     function replace_ampersands_not_followed_by_entity($string) {
1122 :     return preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&amp;", $string);
1123 :     }
1124 :    
1125 :     /**
1126 : nicolasconnault 1.1272 * Given a string, replaces all <a>.*</a> by .* and returns the string.
1127 : nicolasconnault 1.1314 *
1128 : nicolasconnault 1.1272 * @param string $string
1129 :     * @return string
1130 :     */
1131 :     function strip_links($string) {
1132 :     return preg_replace('/(<a\s[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1133 :     }
1134 :    
1135 :     /**
1136 :     * This expression turns links into something nice in a text format. (Russell Jungwirth)
1137 :     *
1138 :     * @param string $string
1139 :     * @return string
1140 :     */
1141 :     function wikify_links($string) {
1142 :     return preg_replace('~(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)~i','$3 [ $2 ]', $string);
1143 :     }
1144 :    
1145 :     /**
1146 :     * Replaces non-standard HTML entities
1147 : nicolasconnault 1.1314 *
1148 : nicolasconnault 1.1272 * @param string $string
1149 :     * @return string
1150 :     */
1151 :     function fix_non_standard_entities($string) {
1152 :     $text = preg_replace('/(&#[0-9]+)(;?)/', '$1;', $string);
1153 : nicolasconnault 1.1314 $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', '$1;', $text);
1154 : nicolasconnault 1.1272 return $text;
1155 :     }
1156 :    
1157 :     /**
1158 : dhawes 1.304 * Given text in a variety of format codings, this function returns
1159 :     * the text as plain text suitable for plain email.
1160 :     *
1161 : dhawes 1.308 * @uses FORMAT_MOODLE
1162 :     * @uses FORMAT_HTML
1163 :     * @uses FORMAT_PLAIN
1164 :     * @uses FORMAT_WIKI
1165 :     * @uses FORMAT_MARKDOWN
1166 :     * @param string $text The text to be formatted. This is raw text originally from user input.
1167 : urs_hunkler 1.347 * @param int $format Identifier of the text format to be used
1168 : samhemelryk 1.1255 * [FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN]
1169 : dhawes 1.308 * @return string
1170 : dhawes 1.304 */
1171 : moodler 1.87 function format_text_email($text, $format) {
1172 :    
1173 :     switch ($format) {
1174 :    
1175 :     case FORMAT_PLAIN:
1176 :     return $text;
1177 :     break;
1178 :    
1179 :     case FORMAT_WIKI:
1180 :     $text = wiki_to_html($text);
1181 : nicolasconnault 1.1272 $text = wikify_links($text);
1182 : moodler 1.116 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1183 : moodler 1.87 break;
1184 :    
1185 : moodler 1.176 case FORMAT_HTML:
1186 :     return html_to_text($text);
1187 :     break;
1188 :    
1189 : moodler 1.265 case FORMAT_MOODLE:
1190 :     case FORMAT_MARKDOWN:
1191 : julmis 1.279 default:
1192 : nicolasconnault 1.1272 $text = wikify_links($text);
1193 : moodler 1.116 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1194 : moodler 1.87 break;
1195 :     }
1196 :     }
1197 : martin 1.1
1198 : dhawes 1.304 /**
1199 :     * Given some text in HTML format, this function will pass it
1200 : tjhunt 1.1227 * through any filters that have been configured for this context.
1201 : dhawes 1.304 *
1202 : samhemelryk 1.1255 * @global object
1203 :     * @global object
1204 :     * @global object
1205 : dhawes 1.308 * @param string $text The text to be passed through format filters
1206 : tjhunt 1.1227 * @param int $courseid The current course.
1207 :     * @return string the filtered string.
1208 : dhawes 1.304 */
1209 : moodler 1.193 function filter_text($text, $courseid=NULL) {
1210 : tjhunt 1.1238 global $CFG, $COURSE, $PAGE;
1211 : skodak 1.802
1212 :     if (empty($courseid)) {
1213 :     $courseid = $COURSE->id; // (copied from format_text)
1214 :     }
1215 : moodler 1.162
1216 : tjhunt 1.1238 $context = $PAGE->context;
1217 : moodler 1.847
1218 : tjhunt 1.1227 return filter_manager::instance()->filter_text($text, $context, $courseid);
1219 : moodler 1.843 }
1220 : skodak 1.1233 /**
1221 :     * Formats activity intro text
1222 : samhemelryk 1.1255 *
1223 :     * @global object
1224 :     * @uses CONTEXT_MODULE
1225 : skodak 1.1233 * @param string $module name of module
1226 :     * @param object $activity instance of activity
1227 :     * @param int $cmid course module id
1228 : skodak 1.1235 * @param bool $filter filter resulting html text
1229 : skodak 1.1233 * @return text
1230 :     */
1231 : skodak 1.1235 function format_module_intro($module, $activity, $cmid, $filter=true) {
1232 : skodak 1.1234 global $CFG;
1233 :     require_once("$CFG->libdir/filelib.php");
1234 : skodak 1.1235 $options = (object)array('noclean'=>true, 'para'=>false, 'filter'=>false);
1235 : skodak 1.1233 $context = get_context_instance(CONTEXT_MODULE, $cmid);
1236 : skodak 1.1236 $intro = file_rewrite_pluginfile_urls($activity->intro, 'pluginfile.php', $context->id, $module.'_intro', null);
1237 : skodak 1.1234 return trim(format_text($intro, $activity->introformat, $options));
1238 : skodak 1.1233 }
1239 : moodler 1.843
1240 : dhawes 1.304 /**
1241 : skodak 1.1231 * Legacy function, used for cleaning of old forum and glossary text only.
1242 : samhemelryk 1.1255 *
1243 :     * @global object
1244 : jamiesensei 1.739 * @param string $text text that may contain TRUSTTEXT marker
1245 : skodak 1.615 * @return text without any TRUSTTEXT marker
1246 :     */
1247 :     function trusttext_strip($text) {
1248 :     global $CFG;
1249 :    
1250 :     while (true) { //removing nested TRUSTTEXT
1251 : jamiesensei 1.739 $orig = $text;
1252 : skodak 1.1231 $text = str_replace('#####TRUSTTEXT#####', '', $text);
1253 : skodak 1.615 if (strcmp($orig, $text) === 0) {
1254 :     return $text;
1255 :     }
1256 :     }
1257 :     }
1258 :    
1259 :     /**
1260 : skodak 1.1231 * Must be called before editing of all texts
1261 :     * with trust flag. Removes all XSS nasties
1262 :     * from texts stored in database if needed.
1263 : samhemelryk 1.1255 *
1264 : skodak 1.1231 * @param object $object data object with xxx, xxxformat and xxxtrust fields
1265 :     * @param string $field name of text field
1266 :     * @param object $context active context
1267 :     * @return object updated $object
1268 :     */
1269 :     function trusttext_pre_edit($object, $field, $context) {
1270 :     $trustfield = $field.'trust';
1271 : nicolasconnault 1.1314 $formatfield = $field.'format';
1272 :    
1273 : skodak 1.1231 if (!$object->$trustfield or !trusttext_trusted($context)) {
1274 :     $object->$field = clean_text($object->$field, $object->$formatfield);
1275 :     }
1276 :    
1277 :     return $object;
1278 :     }
1279 :    
1280 :     /**
1281 : samhemelryk 1.1255 * Is current user trusted to enter no dangerous XSS in this context?
1282 :     *
1283 : skodak 1.1231 * Please note the user must be in fact trusted everywhere on this server!!
1284 : samhemelryk 1.1255 *
1285 :     * @param object $context
1286 : skodak 1.1231 * @return bool true if user trusted
1287 :     */
1288 :     function trusttext_trusted($context) {
1289 : nicolasconnault 1.1314 return (trusttext_active() and has_capability('moodle/site:trustcontent', $context));
1290 : skodak 1.1231 }
1291 :    
1292 :     /**
1293 :     * Is trusttext feature active?
1294 : samhemelryk 1.1255 *
1295 :     * @global object
1296 :     * @param object $context
1297 : skodak 1.1231 * @return bool
1298 :     */
1299 :     function trusttext_active() {
1300 :     global $CFG;
1301 :    
1302 : nicolasconnault 1.1314 return !empty($CFG->enabletrusttext);
1303 : skodak 1.1231 }
1304 :    
1305 :     /**
1306 : dhawes 1.304 * Given raw text (eg typed in by a user), this function cleans it up
1307 :     * and removes any nasty tags that could mess up Moodle pages.
1308 :     *
1309 : dhawes 1.308 * @uses FORMAT_MOODLE
1310 :     * @uses FORMAT_PLAIN
1311 : samhemelryk 1.1255 * @global string
1312 :     * @global object
1313 : dhawes 1.308 * @param string $text The text to be cleaned
1314 : urs_hunkler 1.347 * @param int $format Identifier of the text format to be used
1315 : samhemelryk 1.1255 * [FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN]
1316 : dhawes 1.308 * @return string The cleaned up text
1317 : dhawes 1.304 */
1318 : moodler 1.192 function clean_text($text, $format=FORMAT_MOODLE) {
1319 : martin 1.2
1320 : skodak 1.866 global $ALLOWED_TAGS, $CFG;
1321 :    
1322 : skodak 1.865 if (empty($text) or is_numeric($text)) {
1323 : jamiesensei 1.894 return (string)$text;
1324 : skodak 1.865 }
1325 : moodler 1.28
1326 : defacer 1.242 switch ($format) {
1327 : moodler 1.265 case FORMAT_PLAIN:
1328 : skodak 1.862 case FORMAT_MARKDOWN:
1329 : moodler 1.265 return $text;
1330 :    
1331 :     default:
1332 :    
1333 : skodak 1.865 if (!empty($CFG->enablehtmlpurifier)) {
1334 :     $text = purify_html($text);
1335 :     } else {
1336 :     /// Fix non standard entity notations
1337 : nicolasconnault 1.1272 $text = fix_non_standard_entities($text);
1338 : jamiesensei 1.894
1339 : skodak 1.865 /// Remove tags that are not allowed
1340 :     $text = strip_tags($text, $ALLOWED_TAGS);
1341 : jamiesensei 1.894
1342 : skodak 1.865 /// Clean up embedded scripts and , using kses
1343 :     $text = cleanAttributes($text);
1344 : skodak 1.898
1345 :     /// Again remove tags that are not allowed
1346 :     $text = strip_tags($text, $ALLOWED_TAGS);
1347 :    
1348 : skodak 1.865 }
1349 : skodak 1.351
1350 : skodak 1.865 /// Remove potential script events - some extra protection for undiscovered bugs in our code
1351 : nicolasconnault 1.1271 $text = preg_replace("~([^a-z])language([[:space:]]*)=~i", "$1Xlanguage=", $text);
1352 :     $text = preg_replace("~([^a-z])on([a-z]+)([[:space:]]*)=~i", "$1Xon$2=", $text);
1353 : moodler 1.81
1354 : moodler 1.28 return $text;
1355 : moodler 1.23 }
1356 : martin 1.2 }
1357 : moodler 1.23
1358 : dhawes 1.304 /**
1359 : skodak 1.865 * KSES replacement cleaning function - uses HTML Purifier.
1360 : samhemelryk 1.1255 *
1361 :     * @global object
1362 :     * @param string $text The (X)HTML string to purify
1363 : skodak 1.865 */
1364 :     function purify_html($text) {
1365 :     global $CFG;
1366 :    
1367 : skodak 1.1013 // this can not be done only once because we sometimes need to reset the cache
1368 : skodak 1.1088 $cachedir = $CFG->dataroot.'/cache/htmlpurifier';
1369 : skodak 1.1013 $status = check_dir_exists($cachedir, true, true);
1370 :    
1371 : skodak 1.865 static $purifier = false;
1372 : skodak 1.1088 static $config;
1373 : skodak 1.1013 if ($purifier === false) {
1374 : skodak 1.1088 require_once $CFG->libdir.'/htmlpurifier/HTMLPurifier.safe-includes.php';
1375 : skodak 1.865 $config = HTMLPurifier_Config::createDefault();
1376 : skodak 1.1304 $config->set('Core.ConvertDocumentToFragment', true);
1377 :     $config->set('Core.Encoding', 'UTF-8');
1378 :     $config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
1379 :     $config->set('Cache.SerializerPath', $cachedir);
1380 :     $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));
1381 :     $config->set('Attr.AllowedFrameTargets', array('_blank'));
1382 : skodak 1.865 $purifier = new HTMLPurifier($config);
1383 :     }
1384 :     return $purifier->purify($text);
1385 :     }
1386 :    
1387 :     /**
1388 : dhawes 1.308 * This function takes a string and examines it for HTML tags.
1389 : samhemelryk 1.1255 *
1390 : dhawes 1.304 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1391 : samhemelryk 1.1255 * which checks for attributes and filters them for malicious content
1392 : dhawes 1.304 *
1393 :     * @param string $str The string to be examined for html tags
1394 :     * @return string
1395 :     */
1396 : moodler 1.276 function cleanAttributes($str){
1397 : stronk7 1.454 $result = preg_replace_callback(
1398 :     '%(<[^>]*(>|$)|>)%m', #search for html tags
1399 :     "cleanAttributes2",
1400 : moodler 1.276 $str
1401 : julmis 1.279 );
1402 : moodler 1.276 return $result;
1403 : julmis 1.279 }
1404 :    
1405 : dhawes 1.304 /**
1406 :     * This function takes a string with an html tag and strips out any unallowed
1407 :     * protocols e.g. javascript:
1408 : samhemelryk 1.1255 *
1409 : dhawes 1.304 * It calls ancillary functions in kses which are prefixed by kses
1410 :     *
1411 : samhemelryk 1.1255 * @global object
1412 :     * @global string
1413 : stronk7 1.454 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1414 :     * element the html to be cleared
1415 : dhawes 1.304 * @return string
1416 :     */
1417 : stronk7 1.454 function cleanAttributes2($htmlArray){
1418 : moodler 1.276
1419 : moodler 1.310 global $CFG, $ALLOWED_PROTOCOLS;
1420 : dhawes 1.301 require_once($CFG->libdir .'/kses.php');
1421 : moodler 1.276
1422 : stronk7 1.454 $htmlTag = $htmlArray[1];
1423 : moodler 1.310 if (substr($htmlTag, 0, 1) != '<') {
1424 : moodler 1.276 return '&gt;'; //a single character ">" detected
1425 :     }
1426 : moodler 1.310 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1427 : julmis 1.279 return ''; // It's seriously malformed
1428 :     }
1429 : moodler 1.276 $slash = trim($matches[1]); //trailing xhtml slash
1430 : julmis 1.279 $elem = $matches[2]; //the element name
1431 : moodler 1.276 $attrlist = $matches[3]; // the list of attributes as a string
1432 :    
1433 : moodler 1.310 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1434 : moodler 1.276
1435 : julmis 1.279 $attStr = '';
1436 : moodler 1.310 foreach ($attrArray as $arreach) {
1437 : skodak 1.527 $arreach['name'] = strtolower($arreach['name']);
1438 :     if ($arreach['name'] == 'style') {
1439 :     $value = $arreach['value'];
1440 :     while (true) {
1441 :     $prevvalue = $value;
1442 :     $value = kses_no_null($value);
1443 :     $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1444 :     $value = kses_decode_entities($value);
1445 :     $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1446 :     $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1447 :     if ($value === $prevvalue) {
1448 :     $arreach['value'] = $value;
1449 :     break;
1450 :     }
1451 :     }
1452 :     $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']);
1453 :     $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']);
1454 : skodak 1.1041 $arreach['value'] = preg_replace("/b\s*i\s*n\s*d\s*i\s*n\s*g/i", "Xbinding", $arreach['value']);
1455 : skodak 1.734 } else if ($arreach['name'] == 'href') {
1456 : skodak 1.760 //Adobe Acrobat Reader XSS protection
1457 : skodak 1.1189 $arreach['value'] = preg_replace('/(\.(pdf|fdf|xfdf|xdp|xfd)[^#]*)#.*$/i', '$1', $arreach['value']);
1458 : skodak 1.527 }
1459 : skodak 1.703 $attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'"';
1460 : moodler 1.276 }
1461 : julmis 1.357
1462 : moodler 1.276 $xhtml_slash = '';
1463 : moodler 1.310 if (preg_match('%/\s*$%', $attrlist)) {
1464 : julmis 1.279 $xhtml_slash = ' /';
1465 : moodler 1.276 }
1466 : dhawes 1.301 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1467 : moodler 1.276 }
1468 :    
1469 : dhawes 1.304 /**
1470 :     * Replaces all known smileys in the text with image equivalents
1471 :     *
1472 : samhemelryk 1.1255 * @global object
1473 :     * @staticvar array $e
1474 :     * @staticvar array $img
1475 :     * @staticvar array $emoticons
1476 : dhawes 1.304 * @param string $text Passed by reference. The string to search for smily strings.
1477 :     * @return string
1478 :     */
1479 : moodler 1.92 function replace_smilies(&$text) {
1480 : tjhunt 1.1286 global $CFG, $OUTPUT;
1481 : stronk7 1.1069
1482 :     if (empty($CFG->emoticons)) { /// No emoticons defined, nothing to process here
1483 :     return;
1484 :     }
1485 :    
1486 : skodak 1.853 $lang = current_language();
1487 : toyomoyo 1.998 $emoticonstring = $CFG->emoticons;
1488 : gregb_cc 1.72 static $e = array();
1489 :     static $img = array();
1490 : toyomoyo 1.998 static $emoticons = null;
1491 :    
1492 :     if (is_null($emoticons)) {
1493 :     $emoticons = array();
1494 :     if ($emoticonstring) {
1495 :     $items = explode('{;}', $CFG->emoticons);
1496 :     foreach ($items as $item) {
1497 :     $item = explode('{:}', $item);
1498 : jamiesensei 1.1023 $emoticons[$item[0]] = $item[1];
1499 : toyomoyo 1.998 }
1500 :     }
1501 :     }
1502 :    
1503 : skodak 1.853 if (empty($img[$lang])) { /// After the first time this is not run again
1504 :     $e[$lang] = array();
1505 :     $img[$lang] = array();
1506 : gregb_cc 1.71 foreach ($emoticons as $emoticon => $image){
1507 : moodler 1.179 $alttext = get_string($image, 'pix');
1508 : moodler 1.1177 $alttext = preg_replace('/^\[\[(.*)\]\]$/', '$1', $alttext); /// Clean alttext in case there isn't lang string for it.
1509 : skodak 1.853 $e[$lang][] = $emoticon;
1510 : tjhunt 1.1286 $img[$lang][] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $OUTPUT->old_icon_url('s/' . $image) . '" />';
1511 : gregb_cc 1.71 }
1512 : gregb_cc 1.73 }
1513 : martin 1.2
1514 : moodler 1.231 // Exclude from transformations all the code inside <script> tags
1515 :     // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1516 :     // Based on code from glossary fiter by Williams Castillo.
1517 :     // - Eloy
1518 :    
1519 :     // Detect all the <script> zones to take out
1520 :     $excludes = array();
1521 :     preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1522 :    
1523 :     // Take out all the <script> zones from text
1524 :     foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1525 :     $excludes['<+'.$key.'+>'] = $value;
1526 :     }
1527 :     if ($excludes) {
1528 :     $text = str_replace($excludes,array_keys($excludes),$text);
1529 :     }
1530 :    
1531 : moodler 1.179 /// this is the meat of the code - this is run every time
1532 : skodak 1.853 $text = str_replace($e[$lang], $img[$lang], $text);
1533 : moodler 1.231
1534 :     // Recover all the <script> zones to text
1535 :     if ($excludes) {
1536 :     $text = str_replace(array_keys($excludes),$excludes,$text);
1537 :     }
1538 : moodler 1.25 }
1539 : martin 1.2
1540 : dhawes 1.308 /**
1541 : tjhunt 1.1153 * This code is called from help.php to inject a list of smilies into the
1542 :     * emoticons help file.
1543 :     *
1544 : samhemelryk 1.1255 * @global object
1545 :     * @global object
1546 : tjhunt 1.1153 * @return string HTML for a list of smilies.
1547 :     */
1548 : tjhunt 1.1262 function get_emoticons_list_for_help_file() {
1549 : tjhunt 1.1290 global $CFG, $SESSION, $PAGE, $OUTPUT;
1550 : tjhunt 1.1153 if (empty($CFG->emoticons)) {
1551 :     return '';
1552 :     }
1553 :    
1554 :     $items = explode('{;}', $CFG->emoticons);
1555 :     $output = '<ul id="emoticonlist">';
1556 :     foreach ($items as $item) {
1557 :     $item = explode('{:}', $item);
1558 : tjhunt 1.1290 $output .= '<li><img src="' . $OUTPUT->old_icon_url('s/' . $item[1]) . '" alt="' .
1559 : tjhunt 1.1153 $item[0] . '" /><code>' . $item[0] . '</code></li>';
1560 :     }
1561 :     $output .= '</ul>';
1562 :     if (!empty($SESSION->inserttextform)) {
1563 :     $formname = $SESSION->inserttextform;
1564 :     $fieldname = $SESSION->inserttextfield;
1565 :     } else {
1566 :     $formname = 'theform';
1567 :     $fieldname = 'message';
1568 :     }
1569 : tjhunt 1.1171
1570 : tjhunt 1.1262 $PAGE->requires->yui_lib('event');
1571 :     $PAGE->requires->js_function_call('emoticons_help.init', array($formname, $fieldname, 'emoticonlist'));
1572 : tjhunt 1.1153 return $output;
1573 :    
1574 :     }
1575 :    
1576 :     /**
1577 : dhawes 1.308 * Given plain text, makes it into HTML as nicely as possible.
1578 :     * May contain HTML tags already
1579 :     *
1580 : samhemelryk 1.1255 * @global object
1581 : dhawes 1.308 * @param string $text The string to convert.
1582 :     * @param boolean $smiley Convert any smiley characters to smiley images?
1583 :     * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1584 :     * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1585 :     * @return string
1586 :     */
1587 :    
1588 : moodler 1.225 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1589 : urs_hunkler 1.347 ///
1590 : martin 1.2
1591 : moodler 1.149 global $CFG;
1592 :    
1593 : moodler 1.30 /// Remove any whitespace that may be between HTML tags
1594 : nicolasconnault 1.1271 $text = preg_replace("~>([[:space:]]+)<~i", "><", $text);
1595 : martin 1.15
1596 : moodler 1.30 /// Remove any returns that precede or follow HTML tags
1597 : nicolasconnault 1.1271 $text = preg_replace("~([\n\r])<~i", " <", $text);
1598 :     $text = preg_replace("~>([\n\r])~i", "> ", $text);
1599 : martin 1.15
1600 : moodler 1.92 convert_urls_into_links($text);
1601 : martin 1.1
1602 : moodler 1.30 /// Make returns into HTML newlines.
1603 : moodler 1.225 if ($newlines) {
1604 :     $text = nl2br($text);
1605 :     }
1606 : martin 1.1
1607 : moodler 1.30 /// Turn smileys into images.
1608 : martin 1.4 if ($smiley) {
1609 : moodler 1.92 replace_smilies($text);
1610 : martin 1.4 }
1611 : martin 1.1
1612 : moodler 1.30 /// Wrap the whole thing in a paragraph tag if required
1613 : martin 1.13 if ($para) {
1614 : dhawes 1.301 return '<p>'.$text.'</p>';
1615 : martin 1.13 } else {
1616 :     return $text;
1617 :     }
1618 : moodler 1.84 }
1619 :    
1620 : dhawes 1.304 /**
1621 :     * Given Markdown formatted text, make it into XHTML using external function
1622 :     *
1623 : samhemelryk 1.1255 * @global object
1624 : dhawes 1.308 * @param string $text The markdown formatted text to be converted.
1625 :     * @return string Converted text
1626 : dhawes 1.304 */
1627 : moodler 1.265 function markdown_to_html($text) {
1628 :     global $CFG;
1629 :    
1630 : dhawes 1.301 require_once($CFG->libdir .'/markdown.php');
1631 : moodler 1.265
1632 :     return Markdown($text);
1633 :     }
1634 :    
1635 : dhawes 1.304 /**
1636 : dhawes 1.308 * Given HTML text, make it into plain text using external function
1637 : dhawes 1.304 *
1638 : samhemelryk 1.1255 * @global object
1639 : dhawes 1.304 * @param string $html The text to be converted.
1640 :     * @return string
1641 :     */
1642 : moodler 1.176 function html_to_text($html) {
1643 : dhawes 1.308
1644 : moodler 1.177 global $CFG;
1645 : moodler 1.176
1646 : dhawes 1.301 require_once($CFG->libdir .'/html2text.php');
1647 : moodler 1.176
1648 : fmarier 1.1187 $h2t = new html2text($html);
1649 :     $result = $h2t->get_text();
1650 : dongsheng 1.1103
1651 : sam_marshall 1.1087 return $result;
1652 : moodler 1.176 }
1653 :    
1654 : dhawes 1.304 /**
1655 :     * Given some text this function converts any URLs it finds into HTML links
1656 :     *
1657 :     * @param string $text Passed in by reference. The string to be searched for urls.
1658 :     */
1659 : moodler 1.92 function convert_urls_into_links(&$text) {
1660 :     /// Make lone URLs into links. eg http://moodle.com/
1661 : nicolasconnault 1.1271 $text = preg_replace("~([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])~i",
1662 : nicolasconnault 1.1272 '$1<a href="$2://$3$4">$2://$3$4</a>', $text);
1663 : moodler 1.92
1664 :     /// eg www.moodle.com
1665 : nicolasconnault 1.1271 $text = preg_replace("~([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])~i",
1666 : nicolasconnault 1.1272 '$1<a href="http://www.$2$3">www.$2$3</a>', $text);
1667 : martin 1.9 }
1668 :    
1669 : dhawes 1.304 /**
1670 :     * This function will highlight search words in a given string
1671 : samhemelryk 1.1255 *
1672 : dhawes 1.304 * It cares about HTML and will not ruin links. It's best to use
1673 :     * this function after performing any conversions to HTML.
1674 :     *
1675 : tjhunt 1.1186 * @param string $needle The search string. Syntax like "word1 +word2 -word3" is dealt with correctly.
1676 :     * @param string $haystack The string (HTML) within which to highlight the search terms.
1677 :     * @param boolean $matchcase whether to do case-sensitive. Default case-insensitive.
1678 :     * @param string $prefix the string to put before each search term found.
1679 :     * @param string $suffix the string to put after each search term found.
1680 :     * @return string The highlighted HTML.
1681 : dhawes 1.304 */
1682 : tjhunt 1.1186 function highlight($needle, $haystack, $matchcase = false,
1683 :     $prefix = '<span class="highlight">', $suffix = '</span>') {
1684 : skodak 1.1015
1685 : tjhunt 1.1186 /// Quick bail-out in trivial cases.
1686 : skodak 1.1015 if (empty($needle) or empty($haystack)) {
1687 : moodler 1.184 return $haystack;
1688 :     }
1689 : moodler 1.123
1690 : tjhunt 1.1186 /// Break up the search term into words, discard any -words and build a regexp.
1691 :     $words = preg_split('/ +/', trim($needle));
1692 :     foreach ($words as $index => $word) {
1693 :     if (strpos($word, '-') === 0) {
1694 :     unset($words[$index]);
1695 :     } else if (strpos($word, '+') === 0) {
1696 :     $words[$index] = '\b' . preg_quote(ltrim($word, '+'), '/') . '\b'; // Match only as a complete word.
1697 :     } else {
1698 :     $words[$index] = preg_quote($word, '/');
1699 : moodler 1.123 }
1700 :     }
1701 : tjhunt 1.1186 $regexp = '/(' . implode('|', $words) . ')/u'; // u is do UTF-8 matching.
1702 :     if (!$matchcase) {
1703 :     $regexp .= 'i';
1704 :     }
1705 : moodler 1.123
1706 : tjhunt 1.1186 /// Another chance to bail-out if $search was only -words
1707 :     if (empty($words)) {
1708 :     return $haystack;
1709 : moodler 1.123 }
1710 :    
1711 : tjhunt 1.1186 /// Find all the HTML tags in the input, and store them in a placeholders array.
1712 :     $placeholders = array();
1713 :     $matches = array();
1714 :     preg_match_all('/<[^>]*>/', $haystack, $matches);
1715 :     foreach (array_unique($matches[0]) as $key => $htmltag) {
1716 :     $placeholders['<|' . $key . '|>'] = $htmltag;
1717 : moodler 1.123 }
1718 :    
1719 : tjhunt 1.1186 /// In $hastack, replace each HTML tag with the corresponding placeholder.
1720 :     $haystack = str_replace($placeholders, array_keys($placeholders), $haystack);
1721 : fiedorow 1.333
1722 : tjhunt 1.1186 /// In the resulting string, Do the highlighting.
1723 :     $haystack = preg_replace($regexp, $prefix . '$1' . $suffix, $haystack);
1724 : fiedorow 1.333
1725 : tjhunt 1.1186 /// Turn the placeholders back into HTML tags.
1726 :     $haystack = str_replace(array_keys($placeholders), $placeholders, $haystack);
1727 : moodler 1.123
1728 : moodler 1.305 return $haystack;
1729 : moodler 1.123 }
1730 :    
1731 : dhawes 1.304 /**
1732 :     * This function will highlight instances of $needle in $haystack
1733 : samhemelryk 1.1255 *
1734 :     * It's faster that the above function {@link highlight()} and doesn't care about
1735 : dhawes 1.304 * HTML or anything.
1736 :     *
1737 :     * @param string $needle The string to search for
1738 :     * @param string $haystack The string to search for $needle in
1739 : samhemelryk 1.1255 * @return string The highlighted HTML
1740 : dhawes 1.304 */
1741 : moodler 1.123 function highlightfast($needle, $haystack) {
1742 : martin 1.9
1743 : skodak 1.1015 if (empty($needle) or empty($haystack)) {
1744 :     return $haystack;
1745 :     }
1746 :    
1747 : skodak 1.673 $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
1748 : martin 1.9
1749 : skodak 1.1015 if (count($parts) === 1) {
1750 :     return $haystack;
1751 :     }
1752 :    
1753 : martin 1.9 $pos = 0;
1754 :    
1755 :     foreach ($parts as $key => $part) {
1756 :     $parts[$key] = substr($haystack, $pos, strlen($part));
1757 :     $pos += strlen($part);
1758 :    
1759 : dhawes 1.301 $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
1760 : martin 1.9 $pos += strlen($needle);
1761 : defacer 1.242 }
1762 : martin 1.9
1763 : skodak 1.1015 return str_replace('<span class="highlight"></span>', '', join('', $parts));
1764 : moodler 1.39 }
1765 :    
1766 : nfreear 1.817 /**
1767 :     * Return a string containing 'lang', xml:lang and optionally 'dir' HTML attributes.
1768 :     * Internationalisation, for print_header and backup/restorelib.
1769 : samhemelryk 1.1255 *
1770 :     * @param bool $dir Default false
1771 :     * @return string Attributes
1772 : nfreear 1.817 */
1773 :     function get_html_lang($dir = false) {
1774 :     $direction = '';
1775 :     if ($dir) {
1776 :     if (get_string('thisdirection') == 'rtl') {
1777 :     $direction = ' dir="rtl"';
1778 :     } else {
1779 :     $direction = ' dir="ltr"';
1780 :     }
1781 :     }
1782 :     //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
1783 :     $language = str_replace('_', '-', str_replace('_utf8', '', current_language()));
1784 : nfreear 1.836 @header('Content-Language: '.$language);
1785 : jamiesensei 1.894 return ($direction.' lang="'.$language.'" xml:lang="'.$language.'"');
1786 : nfreear 1.817 }
1787 :    
1788 : tjhunt 1.1278
1789 :     /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1790 :    
1791 : nfreear 1.979 /**
1792 : tjhunt 1.1278 * Send the HTTP headers that Moodle requires.
1793 :     * @param $cacheable Can this page be cached on back?
1794 : nfreear 1.979 */
1795 : tjhunt 1.1278 function send_headers($contenttype, $cacheable = true) {
1796 :     @header('Content-Type: ' . $contenttype);
1797 :     @header('Content-Script-Type: text/javascript');
1798 :     @header('Content-Style-Type: text/css');
1799 :    
1800 :     if ($cacheable) {
1801 :     // Allow caching on "back" (but not on normal clicks)
1802 :     @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
1803 :     @header('Pragma: no-cache');
1804 :     @header('Expires: ');
1805 :     } else {
1806 :     // Do everything we can to always prevent clients and proxies caching
1807 :     @header('Cache-Control: no-store, no-cache, must-revalidate');
1808 :     @header('Cache-Control: post-check=0, pre-check=0', false);
1809 :     @header('Pragma: no-cache');
1810 :     @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
1811 :     @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
1812 :     }
1813 :     @header('Accept-Ranges: none');
1814 : nfreear 1.979 }
1815 :    
1816 : dhawes 1.304 /**
1817 :     * Returns text to be displayed to the user which reflects their login status
1818 :     *
1819 : samhemelryk 1.1255 * @global object
1820 :     * @global object
1821 :     * @global object
1822 :     * @global object
1823 :     * @uses CONTEXT_COURSE
1824 : dhawes 1.308 * @param course $course {@link $COURSE} object containing course information
1825 :     * @param user $user {@link $USER} object containing user information
1826 : samhemelryk 1.1255 * @return string HTML
1827 : dhawes 1.304 */
1828 : moodler 1.389 function user_login_string($course=NULL, $user=NULL) {
1829 : skodak 1.1073 global $USER, $CFG, $SITE, $DB;
1830 : moodler 1.80
1831 : tjhunt 1.1278 if (during_initial_install()) {
1832 :     return '';
1833 :     }
1834 :    
1835 : skodak 1.620 if (empty($user) and !empty($USER->id)) {
1836 : moodler 1.80 $user = $USER;
1837 :     }
1838 :    
1839 : moodler 1.389 if (empty($course)) {
1840 :     $course = $SITE;
1841 :     }
1842 :    
1843 : skodak 1.1194 if (session_is_loggedinas()) {
1844 :     $realuser = session_get_realuser();
1845 : skodak 1.1193 $fullname = fullname($realuser, true);
1846 :     $realuserinfo = " [<a $CFG->frametarget
1847 :     href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1&amp;sesskey=".sesskey()."\">$fullname</a>] ";
1848 : moodler 1.41 } else {
1849 : dhawes 1.301 $realuserinfo = '';
1850 : moodler 1.39 }
1851 :    
1852 : skodak 1.1195 $loginurl = get_login_url();
1853 : julmis 1.255
1854 : skodak 1.640 if (empty($course->id)) {
1855 :     // $course->id is not defined during installation
1856 :     return '';
1857 : skodak 1.852 } else if (!empty($user->id)) {
1858 : moodler 1.647 $context = get_context_instance(CONTEXT_COURSE, $course->id);
1859 :    
1860 : moodler 1.181 $fullname = fullname($user, true);
1861 : skodak 1.743 $username = "<a $CFG->frametarget href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
1862 : skodak 1.1073 if (is_mnet_remote_user($user) and $idprovider = $DB->get_record('mnet_host', array('id'=>$user->mnethostid))) {
1863 : skodak 1.743 $username .= " from <a $CFG->frametarget href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
1864 : martinlanghoff 1.735 }
1865 : moodler 1.639 if (isset($user->username) && $user->username == 'guest') {
1866 : moodler 1.456 $loggedinas = $realuserinfo.get_string('loggedinasguest').
1867 : skodak 1.1195 " (<a $CFG->frametarget href=\"$loginurl\">".get_string('login').'</a>)';
1868 : martinlanghoff 1.944 } else if (!empty($user->access['rsw'][$context->path])) {
1869 : moodler 1.645 $rolename = '';
1870 : skodak 1.1073 if ($role = $DB->get_record('role', array('id'=>$user->access['rsw'][$context->path]))) {
1871 : moodler 1.645 $rolename = ': '.format_string($role->name);
1872 :     }
1873 :     $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
1874 : skodak 1.743 " (<a $CFG->frametarget
1875 : moodler 1.645 href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
1876 : gustav_delius 1.244 } else {
1877 : moodler 1.621 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
1878 : skodak 1.828 " (<a $CFG->frametarget href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
1879 : gustav_delius 1.244 }
1880 : moodler 1.39 } else {
1881 : dhawes 1.301 $loggedinas = get_string('loggedinnot', 'moodle').
1882 : skodak 1.1195 " (<a $CFG->frametarget href=\"$loginurl\">".get_string('login').'</a>)';
1883 : moodler 1.39 }
1884 : tjhunt 1.1278
1885 :     $loggedinas = '<div class="logininfo">'.$loggedinas.'</div>';
1886 :    
1887 :     if (isset($SESSION->justloggedin)) {
1888 :     unset($SESSION->justloggedin);
1889 :     if (!empty($CFG->displayloginfailures)) {
1890 :     if (!empty($USER->username) and $USER->username != 'guest') {
1891 :     if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
1892 :     $loggedinas .= '&nbsp;<div class="loginfailures">';
1893 :     if (empty($count->accounts)) {
1894 :     $loggedinas .= get_string('failedloginattempts', '', $count);
1895 :     } else {
1896 :     $loggedinas .= get_string('failedloginattemptsall', '', $count);
1897 :     }
1898 :     if (has_capability('coursereport/log:view', get_context_instance(CONTEXT_SYSTEM))) {
1899 :     $loggedinas .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
1900 :     '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
1901 :     }
1902 :     $loggedinas .= '</div>';
1903 :     }
1904 :     }
1905 :     }
1906 :     }
1907 :    
1908 :     return $loggedinas;
1909 : moodler 1.39 }
1910 :    
1911 : dhawes 1.304 /**
1912 : nfreear 1.641 * Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
1913 :     * If not it applies sensible defaults.
1914 :     *
1915 :     * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
1916 : jamiesensei 1.739 * search forum block, etc. Important: these are 'silent' in a screen-reader
1917 : nfreear 1.641 * (unlike &gt; &raquo;), and must be accompanied by text.
1918 : samhemelryk 1.1255 *
1919 :     * @global object
1920 :     * @uses $_SERVER
1921 : nfreear 1.641 */
1922 :     function check_theme_arrows() {
1923 :     global $THEME;
1924 : jamiesensei 1.739
1925 : nfreear 1.641 if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
1926 : nfreear 1.692 // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
1927 :     // Also OK in Win 9x/2K/IE 5.x
1928 : nfreear 1.641 $THEME->rarrow = '&#x25BA;';
1929 :     $THEME->larrow = '&#x25C4;';
1930 : moodler 1.1100 if (empty($_SERVER['HTTP_USER_AGENT'])) {
1931 :     $uagent = '';
1932 :     } else {
1933 :     $uagent = $_SERVER['HTTP_USER_AGENT'];
1934 :     }
1935 : nfreear 1.692 if (false !== strpos($uagent, 'Opera')
1936 :     || false !== strpos($uagent, 'Mac')) {
1937 :     // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
1938 :     // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
1939 : nfreear 1.641 $THEME->rarrow = '&#x25B6;';
1940 :     $THEME->larrow = '&#x25C0;';
1941 : nfreear 1.692 }
1942 :     elseif (false !== strpos($uagent, 'Konqueror')) {
1943 :     $THEME->rarrow = '&rarr;';
1944 :     $THEME->larrow = '&larr;';
1945 :     }
1946 :     elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
1947 :     && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
1948 :     // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
1949 :     // To be safe, non-Unicode browsers!
1950 :     $THEME->rarrow = '&gt;';
1951 :     $THEME->larrow = '&lt;';
1952 :     }
1953 : jamiesensei 1.1023
1954 : moodler 1.922 /// RTL support - in RTL languages, swap r and l arrows
1955 : jamiesensei 1.1023 if (right_to_left()) {
1956 :     $t = $THEME->rarrow;
1957 :     $THEME->rarrow = $THEME->larrow;
1958 :     $THEME->larrow = $t;
1959 :     }
1960 : nfreear 1.641 }
1961 :     }
1962 :    
1963 : moodler 1.922
1964 : nfreear 1.641 /**
1965 : nfreear 1.881 * Return the right arrow with text ('next'), and optionally embedded in a link.
1966 :     * See function above, check_theme_arrows.
1967 : samhemelryk 1.1255 *
1968 :     * @global object
1969 : nfreear 1.886 * @param string $text HTML/plain text label (set to blank only for breadcrumb separator cases).
1970 : nfreear 1.881 * @param string $url An optional link to use in a surrounding HTML anchor.
1971 :     * @param bool $accesshide True if text should be hidden (for screen readers only).
1972 :     * @param string $addclass Additional class names for the link, or the arrow character.
1973 :     * @return string HTML string.
1974 : nfreear 1.879 */
1975 : nfreear 1.881 function link_arrow_right($text, $url='', $accesshide=false, $addclass='') {
1976 : nfreear 1.879 global $THEME;
1977 :     check_theme_arrows();
1978 : nfreear 1.881 $arrowclass = 'arrow ';
1979 :     if (! $url) {
1980 :     $arrowclass .= $addclass;
1981 :     }
1982 :     $arrow = '<span class="'.$arrowclass.'">'.$THEME->rarrow.'</span>';
1983 :     $htmltext = '';
1984 :     if ($text) {
1985 : sam_marshall 1.1071 $htmltext = '<span class="arrow_text