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

Annotation of /moodle/lib/weblib.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1344 - (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 : jamiesensei 1.874 }
552 :    
553 : dhawes 1.302 /**
554 : nicolasconnault 1.1306 * Given an unknown $url type (string or moodle_url), returns a string URL.
555 :     * A relative URL is handled with $PAGE->url but gives a debugging error.
556 :     *
557 :     * @param mixed $url The URL (moodle_url or string)
558 :     * @param bool $stripformparams Whether or not to strip the query params from the URL
559 : tjhunt 1.1311 * @return string the URL. &s are unescaped. You must use s(...) to output this to XHTML. ($OUTPUT normally does this automatically.)
560 : nicolasconnault 1.1306 */
561 :     function prepare_url($url, $stripformparams=false) {
562 :     global $CFG, $PAGE;
563 :    
564 :     $output = $url;
565 :    
566 :     if ($url instanceof moodle_url) {
567 : tjhunt 1.1311 $output = $url->out($stripformparams, array(), false);
568 : nicolasconnault 1.1306 }
569 :    
570 :     // Handle relative URLs
571 :     if (substr($output, 0, 4) != 'http' && substr($output, 0, 1) != '/') {
572 :     if (preg_match('/(.*)\/([A-Za-z0-9-_]*\.php)$/', $PAGE->url->out(true), $matches)) {
573 :     return $matches[1] . "/$output";
574 : nicolasconnault 1.1327 } else if ($output == '') {
575 :     return $PAGE->url->out(false, array(), false) . '#';
576 : nicolasconnault 1.1306 } else {
577 : 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.');
578 : nicolasconnault 1.1306 }
579 :     }
580 :    
581 : samhemelryk 1.1336 // Add wwwroot only if the URL does not already start with http:// or https://
582 :     if (!preg_match('|https?://|', $output) ) {
583 :     $output = $CFG->wwwroot . $output;
584 :     }
585 :    
586 : nicolasconnault 1.1306 return $output;
587 :     }
588 :    
589 :     /**
590 : dhawes 1.302 * Determine if there is data waiting to be processed from a form
591 :     *
592 :     * Used on most forms in Moodle to check for data
593 :     * Returns the data as an object, if it's found.
594 :     * This object can be used in foreach loops without
595 :     * casting because it's cast to (array) automatically
596 : urs_hunkler 1.347 *
597 : skodak 1.698 * Checks that submitted POST data exists and returns it as object.
598 : dhawes 1.304 *
599 : samhemelryk 1.1255 * @uses $_POST
600 : skodak 1.698 * @return mixed false or object
601 : dhawes 1.302 */
602 : skodak 1.1082 function data_submitted() {
603 : dhawes 1.304
604 : moodler 1.50 if (empty($_POST)) {
605 :     return false;
606 : moodler 1.47 } else {
607 : skodak 1.1082 return (object)$_POST;
608 : moodler 1.47 }
609 : martin 1.1 }
610 :    
611 : dhawes 1.302 /**
612 : dhawes 1.304 * Given some normal text this function will break up any
613 :     * long words to a given size by inserting the given character
614 :     *
615 : moodler 1.510 * It's multibyte savvy and doesn't change anything inside html tags.
616 :     *
617 : dhawes 1.302 * @param string $string the string to be modified
618 : dhawes 1.308 * @param int $maxsize maximum length of the string to be returned
619 : dhawes 1.302 * @param string $cutchar the string used to represent word breaks
620 :     * @return string
621 :     */
622 : moodler 1.226 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
623 : julmis 1.436
624 : moodler 1.510 /// Loading the textlib singleton instance. We are going to need it.
625 :     $textlib = textlib_get_instance();
626 : moodler 1.226
627 : moodler 1.510 /// First of all, save all the tags inside the text to skip them
628 :     $tags = array();
629 :     filter_save_tags($string,$tags);
630 : moodler 1.257
631 : moodler 1.510 /// Process the string adding the cut when necessary
632 : moodler 1.226 $output = '';
633 : skodak 1.683 $length = $textlib->strlen($string);
634 : moodler 1.226 $wordlength = 0;
635 :    
636 :     for ($i=0; $i<$length; $i++) {
637 : skodak 1.683 $char = $textlib->substr($string, $i, 1);
638 : moodler 1.510 if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
639 : moodler 1.226 $wordlength = 0;
640 :     } else {
641 :     $wordlength++;
642 :     if ($wordlength > $maxsize) {
643 :     $output .= $cutchar;
644 :     $wordlength = 0;
645 :     }
646 :     }
647 :     $output .= $char;
648 :     }
649 : moodler 1.510
650 :     /// Finally load the tags back again
651 :     if (!empty($tags)) {
652 :     $output = str_replace(array_keys($tags), $tags, $output);
653 :     }
654 :    
655 : moodler 1.226 return $output;
656 :     }
657 :    
658 : samhemelryk 1.1333 /**
659 : tjhunt 1.1185 * Try and close the current window using JavaScript, either immediately, or after a delay.
660 : samhemelryk 1.1255 *
661 :     * Echo's out the resulting XHTML & javascript
662 :     *
663 :     * @global object
664 :     * @global object
665 : tjhunt 1.1185 * @param integer $delay a delay in seconds before closing the window. Default 0.
666 :     * @param boolean $reloadopener if true, we will see if this window was a pop-up, and try
667 :     * to reload the parent window before this one closes.
668 : moodler 1.408 */
669 : tjhunt 1.1185 function close_window($delay = 0, $reloadopener = false) {
670 : nicolasconnault 1.1323 global $THEME, $PAGE, $OUTPUT;
671 : tjhunt 1.1185
672 : tjhunt 1.1237 if (!$PAGE->headerprinted) {
673 : samhemelryk 1.1333 $PAGE->set_title(get_string('closewindow'));
674 :     echo $OUTPUT->header();
675 : tjhunt 1.1185 } else {
676 :     print_container_end_all(false, $THEME->open_header_containers);
677 :     }
678 :    
679 :     if ($reloadopener) {
680 :     $function = 'close_window_reloading_opener';
681 :     } else {
682 :     $function = 'close_window';
683 : moodler 1.408 }
684 : tjhunt 1.1185 echo '<p class="centerpara">' . get_string('windowclosing') . '</p>';
685 : tjhunt 1.1262
686 :     $PAGE->requires->js_function_call($function)->after_delay($delay);
687 : tjhunt 1.1185
688 : nicolasconnault 1.1323 echo $OUTPUT->footer();
689 : tjhunt 1.1185 exit;
690 : moodler 1.408 }
691 :    
692 : nicolasconnault 1.1321 /**
693 :     * Returns a string containing a link to the user documentation for the current
694 :     * page. Also contains an icon by default. Shown to teachers and admin only.
695 :     *
696 :     * @global object
697 :     * @global object
698 :     * @param string $text The text to be displayed for the link
699 :     * @param string $iconpath The path to the icon to be displayed
700 :     * @return string The link to user documentation for this current page
701 :     */
702 :     function page_doc_link($text='', $iconpath='') {
703 :     global $CFG, $PAGE, $OUTPUT;
704 :    
705 :     if (empty($CFG->docroot) || during_initial_install()) {
706 :     return '';
707 :     }
708 :     if (!has_capability('moodle/site:doclinks', $PAGE->context)) {
709 :     return '';
710 :     }
711 :    
712 :     $path = $PAGE->docspath;
713 :     if (!$path) {
714 :     return '';
715 :     }
716 :     return $OUTPUT->doc_link($path, $text, $iconpath);
717 :     }
718 :    
719 : mjollnir_ 1.513
720 :     /**
721 : dhawes 1.304 * Validates an email to make sure it makes sense.
722 :     *
723 :     * @param string $address The email address to validate.
724 :     * @return boolean
725 :     */
726 : dhawes 1.308 function validate_email($address) {
727 : martin 1.1
728 : martinlanghoff 1.578 return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
729 :     '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
730 : martin 1.1 '@'.
731 :     '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
732 :     '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
733 :     $address));
734 :     }
735 :    
736 : dhawes 1.304 /**
737 : skodak 1.327 * Extracts file argument either from file parameter or PATH_INFO
738 : skodak 1.1198 * Note: $scriptname parameter is not needed anymore
739 : skodak 1.327 *
740 : samhemelryk 1.1255 * @global string
741 :     * @uses $_SERVER
742 :     * @uses PARAM_PATH
743 : skodak 1.327 * @return string file path (only safe characters)
744 :     */
745 : skodak 1.1198 function get_file_argument() {
746 :     global $SCRIPT;
747 : skodak 1.327
748 :     $relativepath = optional_param('file', FALSE, PARAM_PATH);
749 :    
750 :     // then try extract file from PATH_INFO (slasharguments method)
751 : skodak 1.1198 if ($relativepath === false and isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] !== '') {
752 : skodak 1.327 // check that PATH_INFO works == must not contain the script name
753 : skodak 1.1198 if (strpos($_SERVER['PATH_INFO'], $SCRIPT) === false) {
754 :     $relativepath = clean_param(urldecode($_SERVER['PATH_INFO']), PARAM_PATH);
755 : skodak 1.327 }
756 :     }
757 :    
758 : skodak 1.1198 // note: we are not using any other way because they are not compatible with unicode file names ;-)
759 : skodak 1.327
760 :     return $relativepath;
761 :     }
762 :    
763 :     /**
764 : dhawes 1.308 * Just returns an array of text formats suitable for a popup menu
765 : dhawes 1.304 *
766 : dhawes 1.308 * @uses FORMAT_MOODLE
767 :     * @uses FORMAT_HTML
768 :     * @uses FORMAT_PLAIN
769 :     * @uses FORMAT_MARKDOWN
770 :     * @return array
771 : dhawes 1.304 */
772 : moodler 1.23 function format_text_menu() {
773 : dhawes 1.304
774 : dhawes 1.301 return array (FORMAT_MOODLE => get_string('formattext'),
775 :     FORMAT_HTML => get_string('formathtml'),
776 :     FORMAT_PLAIN => get_string('formatplain'),
777 :     FORMAT_MARKDOWN => get_string('formatmarkdown'));
778 : moodler 1.23 }
779 :    
780 : dhawes 1.304 /**
781 :     * Given text in a variety of format codings, this function returns
782 : urs_hunkler 1.347 * the text as safe HTML.
783 : dhawes 1.304 *
784 : jamiesensei 1.1023 * This function should mainly be used for long strings like posts,
785 : poltawski 1.1004 * answers, glossary items etc. For short strings @see format_string().
786 :     *
787 : samhemelryk 1.1255 * @todo Finish documenting this function
788 :     *
789 :     * @global object
790 :     * @global object
791 :     * @global object
792 :     * @global object
793 : dhawes 1.308 * @uses FORMAT_MOODLE
794 :     * @uses FORMAT_HTML
795 :     * @uses FORMAT_PLAIN
796 :     * @uses FORMAT_WIKI
797 :     * @uses FORMAT_MARKDOWN
798 : samhemelryk 1.1255 * @uses CLI_SCRIPT
799 :     * @staticvar array $croncache
800 : dhawes 1.308 * @param string $text The text to be formatted. This is raw text originally from user input.
801 : urs_hunkler 1.347 * @param int $format Identifier of the text format to be used
802 : samhemelryk 1.1255 * [FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN]
803 :     * @param object $options ?
804 :     * @param int $courseid The courseid to use, defaults to $COURSE->courseid
805 : dhawes 1.308 * @return string
806 : dhawes 1.304 */
807 : skodak 1.615 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
808 : tjhunt 1.1238 global $CFG, $COURSE, $DB, $PAGE;
809 : moodler 1.193
810 : skodak 1.1032 static $croncache = array();
811 : skodak 1.1206
812 : dongsheng 1.1188 $hashstr = '';
813 : skodak 1.1032
814 : skodak 1.678 if ($text === '') {
815 :     return ''; // no need to do any filters and cleaning
816 :     }
817 : dongsheng 1.1305 if (!empty($options->comments) && !empty($CFG->usecomments)) {
818 :     require_once($CFG->libdir . '/commentlib.php');
819 :     $comment = new comment($options->comments);
820 :     $cmt = $comment->init(true);
821 :     } else {
822 :     $cmt = '';
823 :     }
824 :    
825 : skodak 1.678
826 : skodak 1.1231 if (!isset($options->trusted)) {
827 :     $options->trusted = false;
828 : skodak 1.615 }
829 : skodak 1.500 if (!isset($options->noclean)) {
830 : skodak 1.1231 if ($options->trusted and trusttext_active()) {
831 :     // no cleaning if text trusted and noclean not specified
832 :     $options->noclean=true;
833 :     } else {
834 :     $options->noclean=false;
835 :     }
836 : skodak 1.500 }
837 : skodak 1.653 if (!isset($options->nocache)) {
838 :     $options->nocache=false;
839 :     }
840 : skodak 1.500 if (!isset($options->smiley)) {
841 :     $options->smiley=true;
842 :     }
843 :     if (!isset($options->filter)) {
844 :     $options->filter=true;
845 :     }
846 :     if (!isset($options->para)) {
847 :     $options->para=true;
848 :     }
849 :     if (!isset($options->newlines)) {
850 :     $options->newlines=true;
851 : moodler 1.206 }
852 : moodler 1.193 if (empty($courseid)) {
853 : skodak 1.802 $courseid = $COURSE->id;
854 : moodler 1.193 }
855 : moodler 1.148
856 : tjhunt 1.1227 if ($options->filter) {
857 :     $filtermanager = filter_manager::instance();
858 :     } else {
859 :     $filtermanager = new null_filter_manager();
860 : dongsheng 1.1188 }
861 : tjhunt 1.1238 $context = $PAGE->context;
862 : tjhunt 1.1227
863 : skodak 1.653 if (!empty($CFG->cachetext) and empty($options->nocache)) {
864 : tjhunt 1.1227 $hashstr .= $text.'-'.$filtermanager->text_filtering_hash($context, $courseid).'-'.(int)$courseid.'-'.current_language().'-'.
865 : skodak 1.1231 (int)$format.(int)$options->trusted.(int)$options->noclean.(int)$options->smiley.
866 : tjhunt 1.1227 (int)$options->filter.(int)$options->para.(int)$options->newlines;
867 : dongsheng 1.1188
868 : skodak 1.500 $time = time() - $CFG->cachetext;
869 : skodak 1.1206 $md5key = md5($hashstr);
870 : skodak 1.1197 if (CLI_SCRIPT) {
871 : skodak 1.1032 if (isset($croncache[$md5key])) {
872 : dongsheng 1.1305 return $croncache[$md5key].$cmt;
873 : skodak 1.1032 }
874 :     }
875 :    
876 : skodak 1.1294 if ($oldcacheitem = $DB->get_record('cache_text', array('md5key'=>$md5key), '*', IGNORE_MULTIPLE)) {
877 : moodler 1.554 if ($oldcacheitem->timemodified >= $time) {
878 : skodak 1.1197 if (CLI_SCRIPT) {
879 : skodak 1.1032 if (count($croncache) > 150) {
880 : skodak 1.1038 reset($croncache);
881 :     $key = key($croncache);
882 :     unset($croncache[$key]);
883 : skodak 1.1032 }
884 :     $croncache[$md5key] = $oldcacheitem->formattedtext;
885 :     }
886 : dongsheng 1.1305 return $oldcacheitem->formattedtext.$cmt;
887 : moodler 1.554 }
888 : skodak 1.500 }
889 :     }
890 :    
891 : moodler 1.23 switch ($format) {
892 : moodler 1.63 case FORMAT_HTML:
893 : skodak 1.615 if ($options->smiley) {
894 : skodak 1.500 replace_smilies($text);
895 :     }
896 : skodak 1.615 if (!$options->noclean) {
897 : skodak 1.790 $text = clean_text($text, FORMAT_HTML);
898 : moodler 1.272 }
899 : tjhunt 1.1227 $text = $filtermanager->filter_text($text, $context, $courseid);
900 : moodler 1.63 break;
901 :    
902 : moodler 1.81 case FORMAT_PLAIN:
903 : skodak 1.790 $text = s($text); // cleans dangerous JS
904 : stronk7 1.241 $text = rebuildnolinktag($text);
905 : dhawes 1.301 $text = str_replace(' ', '&nbsp; ', $text);
906 : moodler 1.81 $text = nl2br($text);
907 :     break;
908 :    
909 : moodler 1.87 case FORMAT_WIKI:
910 : thepurpleblob 1.441 // this format is deprecated
911 : julmis 1.448 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
912 :     this message as all texts should have been converted to Markdown format instead.
913 : moodler 1.442 Please post a bug report to http://moodle.org/bugs with information about where you
914 : skodak 1.500 saw this message.</p>'.s($text);
915 : moodler 1.87 break;
916 :    
917 : moodler 1.265 case FORMAT_MARKDOWN:
918 :     $text = markdown_to_html($text);
919 : skodak 1.615 if ($options->smiley) {
920 : skodak 1.500 replace_smilies($text);
921 :     }
922 : skodak 1.615 if (!$options->noclean) {
923 : skodak 1.790 $text = clean_text($text, FORMAT_HTML);
924 : moodler 1.272 }
925 : tjhunt 1.1227 $text = $filtermanager->filter_text($text, $context, $courseid);
926 : moodler 1.265 break;
927 :    
928 : moodler 1.63 default: // FORMAT_MOODLE or anything else
929 : moodler 1.225 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
930 : skodak 1.615 if (!$options->noclean) {
931 : skodak 1.790 $text = clean_text($text, FORMAT_HTML);
932 : moodler 1.272 }
933 : tjhunt 1.1227 $text = $filtermanager->filter_text($text, $context, $courseid);
934 :     break;
935 :     }
936 : julmis 1.530
937 : tjhunt 1.1227 // Warn people that we have removed this old mechanism, just in case they
938 :     // were stupid enough to rely on it.
939 :     if (isset($CFG->currenttextiscacheable)) {
940 :     debugging('Once upon a time, Moodle had a truly evil use of global variables ' .
941 :     'called $CFG->currenttextiscacheable. The good news is that this no ' .
942 :     'longer exists. The bad news is that you seem to be using a filter that '.
943 :     'relies on it. Please seek out and destroy that filter code.', DEBUG_DEVELOPER);
944 : moodler 1.206 }
945 : moodler 1.162
946 : tjhunt 1.1227 if (empty($options->nocache) and !empty($CFG->cachetext)) {
947 : skodak 1.1197 if (CLI_SCRIPT) {
948 : skodak 1.1032 // special static cron cache - no need to store it in db if its not already there
949 :     if (count($croncache) > 150) {
950 : skodak 1.1038 reset($croncache);
951 :     $key = key($croncache);
952 :     unset($croncache[$key]);
953 : skodak 1.1032 }
954 :     $croncache[$md5key] = $text;
955 : dongsheng 1.1305 return $text.$cmt;
956 : skodak 1.1032 }
957 :    
958 : skodak 1.670 $newcacheitem = new object();
959 : moodler 1.554 $newcacheitem->md5key = $md5key;
960 : skodak 1.1073 $newcacheitem->formattedtext = $text;
961 : moodler 1.554 $newcacheitem->timemodified = time();
962 :     if ($oldcacheitem) { // See bug 4677 for discussion
963 :     $newcacheitem->id = $oldcacheitem->id;
964 : skodak 1.1229 try {
965 :     $DB->update_record('cache_text', $newcacheitem); // Update existing record in the cache table
966 :     } catch (dml_exception $e) {
967 :     // It's unlikely that the cron cache cleaner could have
968 :     // deleted this entry in the meantime, as it allows
969 :     // some extra time to cover these cases.
970 :     }
971 :     } else {
972 :     try {
973 :     $DB->insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table
974 :     } catch (dml_exception $e) {
975 :     // Again, it's possible that another user has caused this
976 :     // record to be created already in the time that it took
977 :     // to traverse this function. That's OK too, as the
978 :     // call above handles duplicate entries, and eventually
979 :     // the cron cleaner will delete them.
980 :     }
981 : moodler 1.554 }
982 : moodler 1.23 }
983 : dongsheng 1.1305 return $text.$cmt;
984 : nicolasconnault 1.1314
985 : moodler 1.23 }
986 :    
987 : samhemelryk 1.1255 /**
988 :     * Converts the text format from the value to the 'internal'
989 : nicolasconnault 1.1314 * name or vice versa.
990 : samhemelryk 1.1255 *
991 :     * $key can either be the value or the name and you get the other back.
992 : julmis 1.530 *
993 : samhemelryk 1.1255 * @uses FORMAT_MOODLE
994 :     * @uses FORMAT_HTML
995 :     * @uses FORMAT_PLAIN
996 :     * @uses FORMAT_MARKDOWN
997 :     * @param mixed $key int 0-4 or string one of 'moodle','html','plain','markdown'
998 :     * @return mixed as above but the other way around!
999 : thepurpleblob 1.467 */
1000 :     function text_format_name( $key ) {
1001 :     $lookup = array();
1002 :     $lookup[FORMAT_MOODLE] = 'moodle';
1003 :     $lookup[FORMAT_HTML] = 'html';
1004 :     $lookup[FORMAT_PLAIN] = 'plain';
1005 :     $lookup[FORMAT_MARKDOWN] = 'markdown';
1006 :     $value = "error";
1007 :     if (!is_numeric($key)) {
1008 :     $key = strtolower( $key );
1009 :     $value = array_search( $key, $lookup );
1010 :     }
1011 :     else {
1012 :     if (isset( $lookup[$key] )) {
1013 :     $value = $lookup[ $key ];
1014 :     }
1015 :     }
1016 :     return $value;
1017 :     }
1018 :    
1019 : skodak 1.1013 /**
1020 :     * Resets all data related to filters, called during upgrade or when filter settings change.
1021 : samhemelryk 1.1255 *
1022 :     * @global object
1023 :     * @global object
1024 : skodak 1.1013 * @return void
1025 :     */
1026 :     function reset_text_filters_cache() {
1027 : skodak 1.1079 global $CFG, $DB;
1028 : skodak 1.1013
1029 : skodak 1.1079 $DB->delete_records('cache_text');
1030 : skodak 1.1013 $purifdir = $CFG->dataroot.'/cache/htmlpurifier';
1031 :     remove_dir($purifdir, true);
1032 :     }
1033 : skodak 1.821
1034 : nicolasconnault 1.1314 /**
1035 : samhemelryk 1.1255 * Given a simple string, this function returns the string
1036 :     * processed by enabled string filters if $CFG->filterall is enabled
1037 :     *
1038 :     * This function should be used to print short strings (non html) that
1039 :     * need filter processing e.g. activity titles, post subjects,
1040 :     * glossary concepts.
1041 :     *
1042 :     * @global object
1043 :     * @global object
1044 :     * @global object
1045 :     * @staticvar bool $strcache
1046 :     * @param string $string The string to be filtered.
1047 :     * @param boolean $striplinks To strip any link in the result text.
1048 :     Moodle 1.8 default changed from false to true! MDL-8713
1049 :     * @param int $courseid Current course as filters can, potentially, use it
1050 :     * @return string
1051 : stronk7 1.410 */
1052 : tjhunt 1.1227 function format_string($string, $striplinks=true, $courseid=NULL ) {
1053 : tjhunt 1.1238 global $CFG, $COURSE, $PAGE;
1054 : skodak 1.832
1055 : stronk7 1.421 //We'll use a in-memory cache here to speed up repeated strings
1056 : skodak 1.821 static $strcache = false;
1057 :    
1058 : skodak 1.832 if ($strcache === false or count($strcache) > 2000 ) { // this number might need some tuning to limit memory usage in cron
1059 : skodak 1.821 $strcache = array();
1060 :     }
1061 : jamiesensei 1.894
1062 : skodak 1.832 //init course id
1063 : skodak 1.833 if (empty($courseid)) {
1064 : skodak 1.832 $courseid = $COURSE->id;
1065 :     }
1066 :    
1067 : stronk7 1.421 //Calculate md5
1068 : skodak 1.832 $md5 = md5($string.'<+>'.$striplinks.'<+>'.$courseid.'<+>'.current_language());
1069 : stronk7 1.421
1070 :     //Fetch from cache if possible
1071 : skodak 1.832 if (isset($strcache[$md5])) {
1072 : stronk7 1.421 return $strcache[$md5];
1073 :     }
1074 :    
1075 : nicolasconnault 1.838 // First replace all ampersands not followed by html entity code
1076 : nicolasconnault 1.1271 // Regular expression moved to its own method for easier unit testing
1077 :     $string = replace_ampersands_not_followed_by_entity($string);
1078 : nicolasconnault 1.849
1079 : tjhunt 1.1228 if (!empty($CFG->filterall) && $CFG->version >= 2009040600) { // Avoid errors during the upgrade to the new system.
1080 : tjhunt 1.1238 $context = $PAGE->context;
1081 : tjhunt 1.1227 $string = filter_manager::instance()->filter_string($string, $context, $courseid);
1082 : stronk7 1.410 }
1083 : jamiesensei 1.894
1084 : moodler 1.847 // If the site requires it, strip ALL tags from this string
1085 :     if (!empty($CFG->formatstringstriptags)) {
1086 :     $string = strip_tags($string);
1087 :    
1088 : skodak 1.1113 } else {
1089 :     // Otherwise strip just links if that is required (default)
1090 :     if ($striplinks) { //strip links in string
1091 : nicolasconnault 1.1272 $string = strip_links($string);
1092 : skodak 1.1113 }
1093 :     $string = clean_text($string);
1094 : stronk7 1.412 }
1095 :    
1096 : stronk7 1.421 //Store to cache
1097 :     $strcache[$md5] = $string;
1098 : jamiesensei 1.894
1099 : stronk7 1.410 return $string;
1100 :     }
1101 :    
1102 : dhawes 1.304 /**
1103 : nicolasconnault 1.1271 * Given a string, performs a negative lookahead looking for any ampersand character
1104 :     * that is not followed by a proper HTML entity. If any is found, it is replaced
1105 :     * by &amp;. The string is then returned.
1106 :     *
1107 :     * @param string $string
1108 :     * @return string
1109 :     */
1110 :     function replace_ampersands_not_followed_by_entity($string) {
1111 :     return preg_replace("/\&(?![a-zA-Z0-9#]{1,8};)/", "&amp;", $string);
1112 :     }
1113 :    
1114 :     /**
1115 : nicolasconnault 1.1272 * Given a string, replaces all <a>.*</a> by .* and returns the string.
1116 : nicolasconnault 1.1314 *
1117 : nicolasconnault 1.1272 * @param string $string
1118 :     * @return string
1119 :     */
1120 :     function strip_links($string) {
1121 :     return preg_replace('/(<a\s[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1122 :     }
1123 :    
1124 :     /**
1125 :     * This expression turns links into something nice in a text format. (Russell Jungwirth)
1126 :     *
1127 :     * @param string $string
1128 :     * @return string
1129 :     */
1130 :     function wikify_links($string) {
1131 :     return preg_replace('~(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)~i','$3 [ $2 ]', $string);
1132 :     }
1133 :    
1134 :     /**
1135 :     * Replaces non-standard HTML entities
1136 : nicolasconnault 1.1314 *
1137 : nicolasconnault 1.1272 * @param string $string
1138 :     * @return string
1139 :     */
1140 :     function fix_non_standard_entities($string) {
1141 :     $text = preg_replace('/(&#[0-9]+)(;?)/', '$1;', $string);
1142 : nicolasconnault 1.1314 $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', '$1;', $text);
1143 : nicolasconnault 1.1272 return $text;
1144 :     }
1145 :    
1146 :     /**
1147 : dhawes 1.304 * Given text in a variety of format codings, this function returns
1148 :     * the text as plain text suitable for plain email.
1149 :     *
1150 : dhawes 1.308 * @uses FORMAT_MOODLE
1151 :     * @uses FORMAT_HTML
1152 :     * @uses FORMAT_PLAIN
1153 :     * @uses FORMAT_WIKI
1154 :     * @uses FORMAT_MARKDOWN
1155 :     * @param string $text The text to be formatted. This is raw text originally from user input.
1156 : urs_hunkler 1.347 * @param int $format Identifier of the text format to be used
1157 : samhemelryk 1.1255 * [FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN]
1158 : dhawes 1.308 * @return string
1159 : dhawes 1.304 */
1160 : moodler 1.87 function format_text_email($text, $format) {
1161 :    
1162 :     switch ($format) {
1163 :    
1164 :     case FORMAT_PLAIN:
1165 :     return $text;
1166 :     break;
1167 :    
1168 :     case FORMAT_WIKI:
1169 :     $text = wiki_to_html($text);
1170 : nicolasconnault 1.1272 $text = wikify_links($text);
1171 : moodler 1.116 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1172 : moodler 1.87 break;
1173 :    
1174 : moodler 1.176 case FORMAT_HTML:
1175 :     return html_to_text($text);
1176 :     break;
1177 :    
1178 : moodler 1.265 case FORMAT_MOODLE:
1179 :     case FORMAT_MARKDOWN:
1180 : julmis 1.279 default:
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 :     }
1186 : martin 1.1
1187 : dhawes 1.304 /**
1188 :     * Given some text in HTML format, this function will pass it
1189 : tjhunt 1.1227 * through any filters that have been configured for this context.
1190 : dhawes 1.304 *
1191 : samhemelryk 1.1255 * @global object
1192 :     * @global object
1193 :     * @global object
1194 : dhawes 1.308 * @param string $text The text to be passed through format filters
1195 : tjhunt 1.1227 * @param int $courseid The current course.
1196 :     * @return string the filtered string.
1197 : dhawes 1.304 */
1198 : moodler 1.193 function filter_text($text, $courseid=NULL) {
1199 : tjhunt 1.1238 global $CFG, $COURSE, $PAGE;
1200 : skodak 1.802
1201 :     if (empty($courseid)) {
1202 :     $courseid = $COURSE->id; // (copied from format_text)
1203 :     }
1204 : moodler 1.162
1205 : tjhunt 1.1238 $context = $PAGE->context;
1206 : moodler 1.847
1207 : tjhunt 1.1227 return filter_manager::instance()->filter_text($text, $context, $courseid);
1208 : moodler 1.843 }
1209 : skodak 1.1233 /**
1210 :     * Formats activity intro text
1211 : samhemelryk 1.1255 *
1212 :     * @global object
1213 :     * @uses CONTEXT_MODULE
1214 : skodak 1.1233 * @param string $module name of module
1215 :     * @param object $activity instance of activity
1216 :     * @param int $cmid course module id
1217 : skodak 1.1235 * @param bool $filter filter resulting html text
1218 : skodak 1.1233 * @return text
1219 :     */
1220 : skodak 1.1235 function format_module_intro($module, $activity, $cmid, $filter=true) {
1221 : skodak 1.1234 global $CFG;
1222 :     require_once("$CFG->libdir/filelib.php");
1223 : skodak 1.1235 $options = (object)array('noclean'=>true, 'para'=>false, 'filter'=>false);
1224 : skodak 1.1233 $context = get_context_instance(CONTEXT_MODULE, $cmid);
1225 : skodak 1.1236 $intro = file_rewrite_pluginfile_urls($activity->intro, 'pluginfile.php', $context->id, $module.'_intro', null);
1226 : skodak 1.1234 return trim(format_text($intro, $activity->introformat, $options));
1227 : skodak 1.1233 }
1228 : moodler 1.843
1229 : dhawes 1.304 /**
1230 : skodak 1.1231 * Legacy function, used for cleaning of old forum and glossary text only.
1231 : samhemelryk 1.1255 *
1232 :     * @global object
1233 : jamiesensei 1.739 * @param string $text text that may contain TRUSTTEXT marker
1234 : skodak 1.615 * @return text without any TRUSTTEXT marker
1235 :     */
1236 :     function trusttext_strip($text) {
1237 :     global $CFG;
1238 :    
1239 :     while (true) { //removing nested TRUSTTEXT
1240 : jamiesensei 1.739 $orig = $text;
1241 : skodak 1.1231 $text = str_replace('#####TRUSTTEXT#####', '', $text);
1242 : skodak 1.615 if (strcmp($orig, $text) === 0) {
1243 :     return $text;
1244 :     }
1245 :     }
1246 :     }
1247 :    
1248 :     /**
1249 : skodak 1.1231 * Must be called before editing of all texts
1250 :     * with trust flag. Removes all XSS nasties
1251 :     * from texts stored in database if needed.
1252 : samhemelryk 1.1255 *
1253 : skodak 1.1231 * @param object $object data object with xxx, xxxformat and xxxtrust fields
1254 :     * @param string $field name of text field
1255 :     * @param object $context active context
1256 :     * @return object updated $object
1257 :     */
1258 :     function trusttext_pre_edit($object, $field, $context) {
1259 :     $trustfield = $field.'trust';
1260 : nicolasconnault 1.1314 $formatfield = $field.'format';
1261 :    
1262 : skodak 1.1231 if (!$object->$trustfield or !trusttext_trusted($context)) {
1263 :     $object->$field = clean_text($object->$field, $object->$formatfield);
1264 :     }
1265 :    
1266 :     return $object;
1267 :     }
1268 :    
1269 :     /**
1270 : samhemelryk 1.1255 * Is current user trusted to enter no dangerous XSS in this context?
1271 :     *
1272 : skodak 1.1231 * Please note the user must be in fact trusted everywhere on this server!!
1273 : samhemelryk 1.1255 *
1274 :     * @param object $context
1275 : skodak 1.1231 * @return bool true if user trusted
1276 :     */
1277 :     function trusttext_trusted($context) {
1278 : nicolasconnault 1.1314 return (trusttext_active() and has_capability('moodle/site:trustcontent', $context));
1279 : skodak 1.1231 }
1280 :    
1281 :     /**
1282 :     * Is trusttext feature active?
1283 : samhemelryk 1.1255 *
1284 :     * @global object
1285 :     * @param object $context
1286 : skodak 1.1231 * @return bool
1287 :     */
1288 :     function trusttext_active() {
1289 :     global $CFG;
1290 :    
1291 : nicolasconnault 1.1314 return !empty($CFG->enabletrusttext);
1292 : skodak 1.1231 }
1293 :    
1294 :     /**
1295 : dhawes 1.304 * Given raw text (eg typed in by a user), this function cleans it up
1296 :     * and removes any nasty tags that could mess up Moodle pages.
1297 :     *
1298 : dhawes 1.308 * @uses FORMAT_MOODLE
1299 :     * @uses FORMAT_PLAIN
1300 : samhemelryk 1.1255 * @global string
1301 :     * @global object
1302 : dhawes 1.308 * @param string $text The text to be cleaned
1303 : urs_hunkler 1.347 * @param int $format Identifier of the text format to be used
1304 : samhemelryk 1.1255 * [FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN]
1305 : dhawes 1.308 * @return string The cleaned up text
1306 : dhawes 1.304 */
1307 : moodler 1.192 function clean_text($text, $format=FORMAT_MOODLE) {
1308 : martin 1.2
1309 : skodak 1.866 global $ALLOWED_TAGS, $CFG;
1310 :    
1311 : skodak 1.865 if (empty($text) or is_numeric($text)) {
1312 : jamiesensei 1.894 return (string)$text;
1313 : skodak 1.865 }
1314 : moodler 1.28
1315 : defacer 1.242 switch ($format) {
1316 : moodler 1.265 case FORMAT_PLAIN:
1317 : skodak 1.862 case FORMAT_MARKDOWN:
1318 : moodler 1.265 return $text;
1319 :    
1320 :     default:
1321 :    
1322 : skodak 1.865 if (!empty($CFG->enablehtmlpurifier)) {
1323 :     $text = purify_html($text);
1324 :     } else {
1325 :     /// Fix non standard entity notations
1326 : nicolasconnault 1.1272 $text = fix_non_standard_entities($text);
1327 : jamiesensei 1.894
1328 : skodak 1.865 /// Remove tags that are not allowed
1329 :     $text = strip_tags($text, $ALLOWED_TAGS);
1330 : jamiesensei 1.894
1331 : skodak 1.865 /// Clean up embedded scripts and , using kses
1332 :     $text = cleanAttributes($text);
1333 : skodak 1.898
1334 :     /// Again remove tags that are not allowed
1335 :     $text = strip_tags($text, $ALLOWED_TAGS);
1336 :    
1337 : skodak 1.865 }
1338 : skodak 1.351
1339 : skodak 1.865 /// Remove potential script events - some extra protection for undiscovered bugs in our code
1340 : nicolasconnault 1.1271 $text = preg_replace("~([^a-z])language([[:space:]]*)=~i", "$1Xlanguage=", $text);
1341 :     $text = preg_replace("~([^a-z])on([a-z]+)([[:space:]]*)=~i", "$1Xon$2=", $text);
1342 : moodler 1.81
1343 : moodler 1.28 return $text;
1344 : moodler 1.23 }
1345 : martin 1.2 }
1346 : moodler 1.23
1347 : dhawes 1.304 /**
1348 : skodak 1.865 * KSES replacement cleaning function - uses HTML Purifier.
1349 : samhemelryk 1.1255 *
1350 :     * @global object
1351 :     * @param string $text The (X)HTML string to purify
1352 : skodak 1.865 */
1353 :     function purify_html($text) {
1354 :     global $CFG;
1355 :    
1356 : skodak 1.1013 // this can not be done only once because we sometimes need to reset the cache
1357 : skodak 1.1088 $cachedir = $CFG->dataroot.'/cache/htmlpurifier';
1358 : skodak 1.1013 $status = check_dir_exists($cachedir, true, true);
1359 :    
1360 : skodak 1.865 static $purifier = false;
1361 : skodak 1.1088 static $config;
1362 : skodak 1.1013 if ($purifier === false) {
1363 : skodak 1.1088 require_once $CFG->libdir.'/htmlpurifier/HTMLPurifier.safe-includes.php';
1364 : skodak 1.865 $config = HTMLPurifier_Config::createDefault();
1365 : skodak 1.1304 $config->set('Core.ConvertDocumentToFragment', true);
1366 :     $config->set('Core.Encoding', 'UTF-8');
1367 :     $config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
1368 :     $config->set('Cache.SerializerPath', $cachedir);
1369 :     $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));
1370 :     $config->set('Attr.AllowedFrameTargets', array('_blank'));
1371 : skodak 1.865 $purifier = new HTMLPurifier($config);
1372 :     }
1373 :     return $purifier->purify($text);
1374 :     }
1375 :    
1376 :     /**
1377 : dhawes 1.308 * This function takes a string and examines it for HTML tags.
1378 : samhemelryk 1.1255 *
1379 : dhawes 1.304 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1380 : samhemelryk 1.1255 * which checks for attributes and filters them for malicious content
1381 : dhawes 1.304 *
1382 :     * @param string $str The string to be examined for html tags
1383 :     * @return string
1384 :     */
1385 : moodler 1.276 function cleanAttributes($str){
1386 : stronk7 1.454 $result = preg_replace_callback(
1387 :     '%(<[^>]*(>|$)|>)%m', #search for html tags
1388 :     "cleanAttributes2",
1389 : moodler 1.276 $str
1390 : julmis 1.279 );
1391 : moodler 1.276 return $result;
1392 : julmis 1.279 }
1393 :    
1394 : dhawes 1.304 /**
1395 :     * This function takes a string with an html tag and strips out any unallowed
1396 :     * protocols e.g. javascript:
1397 : samhemelryk 1.1255 *
1398 : dhawes 1.304 * It calls ancillary functions in kses which are prefixed by kses
1399 :     *
1400 : samhemelryk 1.1255 * @global object
1401 :     * @global string
1402 : stronk7 1.454 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1403 :     * element the html to be cleared
1404 : dhawes 1.304 * @return string
1405 :     */
1406 : stronk7 1.454 function cleanAttributes2($htmlArray){
1407 : moodler 1.276
1408 : moodler 1.310 global $CFG, $ALLOWED_PROTOCOLS;
1409 : dhawes 1.301 require_once($CFG->libdir .'/kses.php');
1410 : moodler 1.276
1411 : stronk7 1.454 $htmlTag = $htmlArray[1];
1412 : moodler 1.310 if (substr($htmlTag, 0, 1) != '<') {
1413 : moodler 1.276 return '&gt;'; //a single character ">" detected
1414 :     }
1415 : moodler 1.310 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1416 : julmis 1.279 return ''; // It's seriously malformed
1417 :     }
1418 : moodler 1.276 $slash = trim($matches[1]); //trailing xhtml slash
1419 : julmis 1.279 $elem = $matches[2]; //the element name
1420 : moodler 1.276 $attrlist = $matches[3]; // the list of attributes as a string
1421 :    
1422 : moodler 1.310 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1423 : moodler 1.276
1424 : julmis 1.279 $attStr = '';
1425 : moodler 1.310 foreach ($attrArray as $arreach) {
1426 : skodak 1.527 $arreach['name'] = strtolower($arreach['name']);
1427 :     if ($arreach['name'] == 'style') {
1428 :     $value = $arreach['value'];
1429 :     while (true) {
1430 :     $prevvalue = $value;
1431 :     $value = kses_no_null($value);
1432 :     $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1433 :     $value = kses_decode_entities($value);
1434 :     $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1435 :     $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1436 :     if ($value === $prevvalue) {
1437 :     $arreach['value'] = $value;
1438 :     break;
1439 :     }
1440 :     }
1441 :     $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']);
1442 :     $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']);
1443 : skodak 1.1041 $arreach['value'] = preg_replace("/b\s*i\s*n\s*d\s*i\s*n\s*g/i", "Xbinding", $arreach['value']);
1444 : skodak 1.734 } else if ($arreach['name'] == 'href') {
1445 : skodak 1.760 //Adobe Acrobat Reader XSS protection
1446 : skodak 1.1189 $arreach['value'] = preg_replace('/(\.(pdf|fdf|xfdf|xdp|xfd)[^#]*)#.*$/i', '$1', $arreach['value']);
1447 : skodak 1.527 }
1448 : skodak 1.703 $attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'"';
1449 : moodler 1.276 }
1450 : julmis 1.357
1451 : moodler 1.276 $xhtml_slash = '';
1452 : moodler 1.310 if (preg_match('%/\s*$%', $attrlist)) {
1453 : julmis 1.279 $xhtml_slash = ' /';
1454 : moodler 1.276 }
1455 : dhawes 1.301 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1456 : moodler 1.276 }
1457 :    
1458 : dhawes 1.304 /**
1459 :     * Replaces all known smileys in the text with image equivalents
1460 :     *
1461 : samhemelryk 1.1255 * @global object
1462 :     * @staticvar array $e
1463 :     * @staticvar array $img
1464 :     * @staticvar array $emoticons
1465 : dhawes 1.304 * @param string $text Passed by reference. The string to search for smily strings.
1466 :     * @return string
1467 :     */
1468 : moodler 1.92 function replace_smilies(&$text) {
1469 : tjhunt 1.1286 global $CFG, $OUTPUT;
1470 : stronk7 1.1069
1471 :     if (empty($CFG->emoticons)) { /// No emoticons defined, nothing to process here
1472 :     return;
1473 :     }
1474 :    
1475 : skodak 1.853 $lang = current_language();
1476 : toyomoyo 1.998 $emoticonstring = $CFG->emoticons;
1477 : gregb_cc 1.72 static $e = array();
1478 :     static $img = array();
1479 : toyomoyo 1.998 static $emoticons = null;
1480 :    
1481 :     if (is_null($emoticons)) {
1482 :     $emoticons = array();
1483 :     if ($emoticonstring) {
1484 :     $items = explode('{;}', $CFG->emoticons);
1485 :     foreach ($items as $item) {
1486 :     $item = explode('{:}', $item);
1487 : jamiesensei 1.1023 $emoticons[$item[0]] = $item[1];
1488 : toyomoyo 1.998 }
1489 :     }
1490 :     }
1491 :    
1492 : skodak 1.853 if (empty($img[$lang])) { /// After the first time this is not run again
1493 :     $e[$lang] = array();
1494 :     $img[$lang] = array();
1495 : gregb_cc 1.71 foreach ($emoticons as $emoticon => $image){
1496 : moodler 1.179 $alttext = get_string($image, 'pix');
1497 : moodler 1.1177 $alttext = preg_replace('/^\[\[(.*)\]\]$/', '$1', $alttext); /// Clean alttext in case there isn't lang string for it.
1498 : skodak 1.853 $e[$lang][] = $emoticon;
1499 : tjhunt 1.1286 $img[$lang][] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $OUTPUT->old_icon_url('s/' . $image) . '" />';
1500 : gregb_cc 1.71 }
1501 : gregb_cc 1.73 }
1502 : martin 1.2
1503 : moodler 1.231 // Exclude from transformations all the code inside <script> tags
1504 :     // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1505 :     // Based on code from glossary fiter by Williams Castillo.
1506 :     // - Eloy
1507 :    
1508 :     // Detect all the <script> zones to take out
1509 :     $excludes = array();
1510 :     preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1511 :    
1512 :     // Take out all the <script> zones from text
1513 :     foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1514 :     $excludes['<+'.$key.'+>'] = $value;
1515 :     }
1516 :     if ($excludes) {
1517 :     $text = str_replace($excludes,array_keys($excludes),$text);
1518 :     }
1519 :    
1520 : moodler 1.179 /// this is the meat of the code - this is run every time
1521 : skodak 1.853 $text = str_replace($e[$lang], $img[$lang], $text);
1522 : moodler 1.231
1523 :     // Recover all the <script> zones to text
1524 :     if ($excludes) {
1525 :     $text = str_replace(array_keys($excludes),$excludes,$text);
1526 :     }
1527 : moodler 1.25 }
1528 : martin 1.2
1529 : dhawes 1.308 /**
1530 : tjhunt 1.1153 * This code is called from help.php to inject a list of smilies into the
1531 :     * emoticons help file.
1532 :     *
1533 : samhemelryk 1.1255 * @global object
1534 :     * @global object
1535 : tjhunt 1.1153 * @return string HTML for a list of smilies.
1536 :     */
1537 : tjhunt 1.1262 function get_emoticons_list_for_help_file() {
1538 : tjhunt 1.1290 global $CFG, $SESSION, $PAGE, $OUTPUT;
1539 : tjhunt 1.1153 if (empty($CFG->emoticons)) {
1540 :     return '';
1541 :     }
1542 :    
1543 :     $items = explode('{;}', $CFG->emoticons);
1544 :     $output = '<ul id="emoticonlist">';
1545 :     foreach ($items as $item) {
1546 :     $item = explode('{:}', $item);
1547 : tjhunt 1.1290 $output .= '<li><img src="' . $OUTPUT->old_icon_url('s/' . $item[1]) . '" alt="' .
1548 : tjhunt 1.1153 $item[0] . '" /><code>' . $item[0] . '</code></li>';
1549 :     }
1550 :     $output .= '</ul>';
1551 :     if (!empty($SESSION->inserttextform)) {
1552 :     $formname = $SESSION->inserttextform;
1553 :     $fieldname = $SESSION->inserttextfield;
1554 :     } else {
1555 :     $formname = 'theform';
1556 :     $fieldname = 'message';
1557 :     }
1558 : tjhunt 1.1171
1559 : tjhunt 1.1262 $PAGE->requires->yui_lib('event');
1560 :     $PAGE->requires->js_function_call('emoticons_help.init', array($formname, $fieldname, 'emoticonlist'));
1561 : tjhunt 1.1153 return $output;
1562 :    
1563 :     }
1564 :    
1565 :     /**
1566 : dhawes 1.308 * Given plain text, makes it into HTML as nicely as possible.
1567 :     * May contain HTML tags already
1568 :     *
1569 : samhemelryk 1.1255 * @global object
1570 : dhawes 1.308 * @param string $text The string to convert.
1571 :     * @param boolean $smiley Convert any smiley characters to smiley images?
1572 :     * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1573 :     * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1574 :     * @return string
1575 :     */
1576 :    
1577 : moodler 1.225 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1578 : urs_hunkler 1.347 ///
1579 : martin 1.2
1580 : moodler 1.149 global $CFG;
1581 :    
1582 : moodler 1.30 /// Remove any whitespace that may be between HTML tags
1583 : nicolasconnault 1.1271 $text = preg_replace("~>([[:space:]]+)<~i", "><", $text);
1584 : martin 1.15
1585 : moodler 1.30 /// Remove any returns that precede or follow HTML tags
1586 : nicolasconnault 1.1271 $text = preg_replace("~([\n\r])<~i", " <", $text);
1587 :     $text = preg_replace("~>([\n\r])~i", "> ", $text);
1588 : martin 1.15
1589 : moodler 1.92 convert_urls_into_links($text);
1590 : martin 1.1
1591 : moodler 1.30 /// Make returns into HTML newlines.
1592 : moodler 1.225 if ($newlines) {
1593 :     $text = nl2br($text);
1594 :     }
1595 : martin 1.1
1596 : moodler 1.30 /// Turn smileys into images.
1597 : martin 1.4 if ($smiley) {
1598 : moodler 1.92 replace_smilies($text);
1599 : martin 1.4 }
1600 : martin 1.1
1601 : moodler 1.30 /// Wrap the whole thing in a paragraph tag if required
1602 : martin 1.13 if ($para) {
1603 : dhawes 1.301 return '<p>'.$text.'</p>';
1604 : martin 1.13 } else {
1605 :     return $text;
1606 :     }
1607 : moodler 1.84 }
1608 :    
1609 : dhawes 1.304 /**
1610 :     * Given Markdown formatted text, make it into XHTML using external function
1611 :     *
1612 : samhemelryk 1.1255 * @global object
1613 : dhawes 1.308 * @param string $text The markdown formatted text to be converted.
1614 :     * @return string Converted text
1615 : dhawes 1.304 */
1616 : moodler 1.265 function markdown_to_html($text) {
1617 :     global $CFG;
1618 :    
1619 : dhawes 1.301 require_once($CFG->libdir .'/markdown.php');
1620 : moodler 1.265
1621 :     return Markdown($text);
1622 :     }
1623 :    
1624 : dhawes 1.304 /**
1625 : dhawes 1.308 * Given HTML text, make it into plain text using external function
1626 : dhawes 1.304 *
1627 : samhemelryk 1.1255 * @global object
1628 : dhawes 1.304 * @param string $html The text to be converted.
1629 :     * @return string
1630 :     */
1631 : moodler 1.176 function html_to_text($html) {
1632 : dhawes 1.308
1633 : moodler 1.177 global $CFG;
1634 : moodler 1.176
1635 : dhawes 1.301 require_once($CFG->libdir .'/html2text.php');
1636 : moodler 1.176
1637 : fmarier 1.1187 $h2t = new html2text($html);
1638 :     $result = $h2t->get_text();
1639 : dongsheng 1.1103
1640 : sam_marshall 1.1087 return $result;
1641 : moodler 1.176 }
1642 :    
1643 : dhawes 1.304 /**
1644 :     * Given some text this function converts any URLs it finds into HTML links
1645 :     *
1646 :     * @param string $text Passed in by reference. The string to be searched for urls.
1647 :     */
1648 : moodler 1.92 function convert_urls_into_links(&$text) {
1649 :     /// Make lone URLs into links. eg http://moodle.com/
1650 : nicolasconnault 1.1271 $text = preg_replace("~([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])~i",
1651 : nicolasconnault 1.1272 '$1<a href="$2://$3$4">$2://$3$4</a>', $text);
1652 : moodler 1.92
1653 :     /// eg www.moodle.com
1654 : nicolasconnault 1.1271 $text = preg_replace("~([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])~i",
1655 : nicolasconnault 1.1272 '$1<a href="http://www.$2$3">www.$2$3</a>', $text);
1656 : martin 1.9 }
1657 :    
1658 : dhawes 1.304 /**
1659 :     * This function will highlight search words in a given string
1660 : samhemelryk 1.1255 *
1661 : dhawes 1.304 * It cares about HTML and will not ruin links. It's best to use
1662 :     * this function after performing any conversions to HTML.
1663 :     *
1664 : tjhunt 1.1186 * @param string $needle The search string. Syntax like "word1 +word2 -word3" is dealt with correctly.
1665 :     * @param string $haystack The string (HTML) within which to highlight the search terms.
1666 :     * @param boolean $matchcase whether to do case-sensitive. Default case-insensitive.
1667 :     * @param string $prefix the string to put before each search term found.
1668 :     * @param string $suffix the string to put after each search term found.
1669 :     * @return string The highlighted HTML.
1670 : dhawes 1.304 */
1671 : tjhunt 1.1186 function highlight($needle, $haystack, $matchcase = false,
1672 :     $prefix = '<span class="highlight">', $suffix = '</span>') {
1673 : skodak 1.1015
1674 : tjhunt 1.1186 /// Quick bail-out in trivial cases.
1675 : skodak 1.1015 if (empty($needle) or empty($haystack)) {
1676 : moodler 1.184 return $haystack;
1677 :     }
1678 : moodler 1.123
1679 : tjhunt 1.1186 /// Break up the search term into words, discard any -words and build a regexp.
1680 :     $words = preg_split('/ +/', trim($needle));
1681 :     foreach ($words as $index => $word) {
1682 :     if (strpos($word, '-') === 0) {
1683 :     unset($words[$index]);
1684 :     } else if (strpos($word, '+') === 0) {
1685 :     $words[$index] = '\b' . preg_quote(ltrim($word, '+'), '/') . '\b'; // Match only as a complete word.
1686 :     } else {
1687 :     $words[$index] = preg_quote($word, '/');
1688 : moodler 1.123 }
1689 :     }
1690 : tjhunt 1.1186 $regexp = '/(' . implode('|', $words) . ')/u'; // u is do UTF-8 matching.
1691 :     if (!$matchcase) {
1692 :     $regexp .= 'i';
1693 :     }
1694 : moodler 1.123
1695 : tjhunt 1.1186 /// Another chance to bail-out if $search was only -words
1696 :     if (empty($words)) {
1697 :     return $haystack;
1698 : moodler 1.123 }
1699 :    
1700 : tjhunt 1.1186 /// Find all the HTML tags in the input, and store them in a placeholders array.
1701 :     $placeholders = array();
1702 :     $matches = array();
1703 :     preg_match_all('/<[^>]*>/', $haystack, $matches);
1704 :     foreach (array_unique($matches[0]) as $key => $htmltag) {
1705 :     $placeholders['<|' . $key . '|>'] = $htmltag;
1706 : moodler 1.123 }
1707 :    
1708 : tjhunt 1.1186 /// In $hastack, replace each HTML tag with the corresponding placeholder.
1709 :     $haystack = str_replace($placeholders, array_keys($placeholders), $haystack);
1710 : fiedorow 1.333
1711 : tjhunt 1.1186 /// In the resulting string, Do the highlighting.
1712 :     $haystack = preg_replace($regexp, $prefix . '$1' . $suffix, $haystack);
1713 : fiedorow 1.333
1714 : tjhunt 1.1186 /// Turn the placeholders back into HTML tags.
1715 :     $haystack = str_replace(array_keys($placeholders), $placeholders, $haystack);
1716 : moodler 1.123
1717 : moodler 1.305 return $haystack;
1718 : moodler 1.123 }
1719 :    
1720 : dhawes 1.304 /**
1721 :     * This function will highlight instances of $needle in $haystack
1722 : samhemelryk 1.1255 *
1723 :     * It's faster that the above function {@link highlight()} and doesn't care about
1724 : dhawes 1.304 * HTML or anything.
1725 :     *
1726 :     * @param string $needle The string to search for
1727 :     * @param string $haystack The string to search for $needle in
1728 : samhemelryk 1.1255 * @return string The highlighted HTML
1729 : dhawes 1.304 */
1730 : moodler 1.123 function highlightfast($needle, $haystack) {
1731 : martin 1.9
1732 : skodak 1.1015 if (empty($needle) or empty($haystack)) {
1733 :     return $haystack;
1734 :     }
1735 :    
1736 : skodak 1.673 $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
1737 : martin 1.9
1738 : skodak 1.1015 if (count($parts) === 1) {
1739 :     return $haystack;
1740 :     }
1741 :    
1742 : martin 1.9 $pos = 0;
1743 :    
1744 :     foreach ($parts as $key => $part) {
1745 :     $parts[$key] = substr($haystack, $pos, strlen($part));
1746 :     $pos += strlen($part);
1747 :    
1748 : dhawes 1.301 $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
1749 : martin 1.9 $pos += strlen($needle);
1750 : defacer 1.242 }
1751 : martin 1.9
1752 : skodak 1.1015 return str_replace('<span class="highlight"></span>', '', join('', $parts));
1753 : moodler 1.39 }
1754 :    
1755 : nfreear 1.817 /**
1756 :     * Return a string containing 'lang', xml:lang and optionally 'dir' HTML attributes.
1757 :     * Internationalisation, for print_header and backup/restorelib.
1758 : samhemelryk 1.1255 *
1759 :     * @param bool $dir Default false
1760 :     * @return string Attributes
1761 : nfreear 1.817 */
1762 :     function get_html_lang($dir = false) {
1763 :     $direction = '';
1764 :     if ($dir) {
1765 :     if (get_string('thisdirection') == 'rtl') {
1766 :     $direction = ' dir="rtl"';
1767 :     } else {
1768 :     $direction = ' dir="ltr"';
1769 :     }
1770 :     }
1771 :     //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
1772 :     $language = str_replace('_', '-', str_replace('_utf8', '', current_language()));
1773 : nfreear 1.836 @header('Content-Language: '.$language);
1774 : jamiesensei 1.894 return ($direction.' lang="'.$language.'" xml:lang="'.$language.'"');
1775 : nfreear 1.817 }
1776 :    
1777 : tjhunt 1.1278
1778 :     /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1779 :    
1780 : nfreear 1.979 /**
1781 : tjhunt 1.1278 * Send the HTTP headers that Moodle requires.
1782 :     * @param $cacheable Can this page be cached on back?
1783 : nfreear 1.979 */
1784 : tjhunt 1.1278 function send_headers($contenttype, $cacheable = true) {
1785 :     @header('Content-Type: ' . $contenttype);
1786 :     @header('Content-Script-Type: text/javascript');
1787 :     @header('Content-Style-Type: text/css');
1788 :    
1789 :     if ($cacheable) {
1790 :     // Allow caching on "back" (but not on normal clicks)
1791 :     @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
1792 :     @header('Pragma: no-cache');
1793 :     @header('Expires: ');
1794 :     } else {
1795 :     // Do everything we can to always prevent clients and proxies caching
1796 :     @header('Cache-Control: no-store, no-cache, must-revalidate');
1797 :     @header('Cache-Control: post-check=0, pre-check=0', false);
1798 :     @header('Pragma: no-cache');
1799 :     @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
1800 :     @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
1801 :     }
1802 :     @header('Accept-Ranges: none');
1803 : nfreear 1.979 }
1804 :    
1805 : dhawes 1.304 /**
1806 :     * Returns text to be displayed to the user which reflects their login status
1807 :     *
1808 : samhemelryk 1.1255 * @global object
1809 :     * @global object
1810 :     * @global object
1811 :     * @global object
1812 :     * @uses CONTEXT_COURSE
1813 : dhawes 1.308 * @param course $course {@link $COURSE} object containing course information
1814 :     * @param user $user {@link $USER} object containing user information
1815 : samhemelryk 1.1255 * @return string HTML
1816 : dhawes 1.304 */
1817 : moodler 1.389 function user_login_string($course=NULL, $user=NULL) {
1818 : skodak 1.1073 global $USER, $CFG, $SITE, $DB;
1819 : moodler 1.80
1820 : tjhunt 1.1278 if (during_initial_install()) {
1821 :     return '';
1822 :     }
1823 :    
1824 : skodak 1.620 if (empty($user) and !empty($USER->id)) {
1825 : moodler 1.80 $user = $USER;
1826 :     }
1827 :    
1828 : moodler 1.389 if (empty($course)) {
1829 :     $course = $SITE;
1830 :     }
1831 :    
1832 : skodak 1.1194 if (session_is_loggedinas()) {
1833 :     $realuser = session_get_realuser();
1834 : skodak 1.1193 $fullname = fullname($realuser, true);
1835 :     $realuserinfo = " [<a $CFG->frametarget
1836 :     href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1&amp;sesskey=".sesskey()."\">$fullname</a>] ";
1837 : moodler 1.41 } else {
1838 : dhawes 1.301 $realuserinfo = '';
1839 : moodler 1.39 }
1840 :    
1841 : skodak 1.1195 $loginurl = get_login_url();
1842 : julmis 1.255
1843 : skodak 1.640 if (empty($course->id)) {
1844 :     // $course->id is not defined during installation
1845 :     return '';
1846 : skodak 1.852 } else if (!empty($user->id)) {
1847 : moodler 1.647 $context = get_context_instance(CONTEXT_COURSE, $course->id);
1848 :    
1849 : moodler 1.181 $fullname = fullname($user, true);
1850 : skodak 1.743 $username = "<a $CFG->frametarget href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
1851 : skodak 1.1073 if (is_mnet_remote_user($user) and $idprovider = $DB->get_record('mnet_host', array('id'=>$user->mnethostid))) {
1852 : skodak 1.743 $username .= " from <a $CFG->frametarget href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
1853 : martinlanghoff 1.735 }
1854 : moodler 1.639 if (isset($user->username) && $user->username == 'guest') {
1855 : moodler 1.456 $loggedinas = $realuserinfo.get_string('loggedinasguest').
1856 : skodak 1.1195 " (<a $CFG->frametarget href=\"$loginurl\">".get_string('login').'</a>)';
1857 : martinlanghoff 1.944 } else if (!empty($user->access['rsw'][$context->path])) {
1858 : moodler 1.645 $rolename = '';
1859 : skodak 1.1073 if ($role = $DB->get_record('role', array('id'=>$user->access['rsw'][$context->path]))) {
1860 : moodler 1.645 $rolename = ': '.format_string($role->name);
1861 :     }
1862 :     $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
1863 : skodak 1.743 " (<a $CFG->frametarget
1864 : moodler 1.645 href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
1865 : gustav_delius 1.244 } else {
1866 : moodler 1.621 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
1867 : skodak 1.828 " (<a $CFG->frametarget href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
1868 : gustav_delius 1.244 }
1869 : moodler 1.39 } else {
1870 : dhawes 1.301 $loggedinas = get_string('loggedinnot', 'moodle').
1871 : skodak 1.1195 " (<a $CFG->frametarget href=\"$loginurl\">".get_string('login').'</a>)';
1872 : moodler 1.39 }
1873 : tjhunt 1.1278
1874 :     $loggedinas = '<div class="logininfo">'.$loggedinas.'</div>';
1875 :    
1876 :     if (isset($SESSION->justloggedin)) {
1877 :     unset($SESSION->justloggedin);
1878 :     if (!empty($CFG->displayloginfailures)) {
1879 :     if (!empty($USER->username) and $USER->username != 'guest') {
1880 :     if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
1881 :     $loggedinas .= '&nbsp;<div class="loginfailures">';
1882 :     if (empty($count->accounts)) {
1883 :     $loggedinas .= get_string('failedloginattempts', '', $count);
1884 :     } else {
1885 :     $loggedinas .= get_string('failedloginattemptsall', '', $count);
1886 :     }
1887 :     if (has_capability('coursereport/log:view', get_context_instance(CONTEXT_SYSTEM))) {
1888 :     $loggedinas .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
1889 :     '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
1890 :     }
1891 :     $loggedinas .= '</div>';
1892 :     }
1893 :     }
1894 :     }
1895 :     }
1896 :    
1897 :     return $loggedinas;
1898 : moodler 1.39 }
1899 :    
1900 : dhawes 1.304 /**
1901 : nfreear 1.641 * Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
1902 :     * If not it applies sensible defaults.
1903 :     *
1904 :     * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
1905 : jamiesensei 1.739 * search forum block, etc. Important: these are 'silent' in a screen-reader
1906 : nfreear 1.641 * (unlike &gt; &raquo;), and must be accompanied by text.
1907 : samhemelryk 1.1255 *
1908 :     * @global object
1909 :     * @uses $_SERVER
1910 : nfreear 1.641 */
1911 :     function check_theme_arrows() {
1912 :     global $THEME;
1913 : jamiesensei 1.739
1914 : nfreear 1.641 if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
1915 : nfreear 1.692 // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
1916 :     // Also OK in Win 9x/2K/IE 5.x
1917 : nfreear 1.641 $THEME->rarrow = '&#x25BA;';
1918 :     $THEME->larrow = '&#x25C4;';
1919 : moodler 1.1100 if (empty($_SERVER['HTTP_USER_AGENT'])) {
1920 :     $uagent = '';
1921 :     } else {
1922 :     $uagent = $_SERVER['HTTP_USER_AGENT'];
1923 :     }
1924 : nfreear 1.692 if (false !== strpos($uagent, 'Opera')
1925 :     || false !== strpos($uagent, 'Mac')) {
1926 :     // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
1927 :     // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
1928 : nfreear 1.641 $THEME->rarrow = '&#x25B6;';
1929 :     $THEME->larrow = '&#x25C0;';
1930 : nfreear 1.692 }
1931 :     elseif (false !== strpos($uagent, 'Konqueror')) {
1932 :     $THEME->rarrow = '&rarr;';
1933 :     $THEME->larrow = '&larr;';
1934 :     }
1935 :     elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
1936 :     && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
1937 :     // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
1938 :     // To be safe, non-Unicode browsers!
1939 :     $THEME->rarrow = '&gt;';
1940 :     $THEME->larrow = '&lt;';
1941 :     }
1942 : jamiesensei 1.1023
1943 : moodler 1.922 /// RTL support - in RTL languages, swap r and l arrows
1944 : jamiesensei 1.1023 if (right_to_left()) {
1945 :     $t = $THEME->rarrow;
1946 :     $THEME->rarrow = $THEME->larrow;
1947 :     $THEME->larrow = $t;
1948 :     }
1949 : nfreear 1.641 }
1950 :     }
1951 :    
1952 : moodler 1.922
1953 : nfreear 1.641 /**
1954 : nfreear 1.881 * Return the right arrow with text ('next'), and optionally embedded in a link.
1955 :     * See function above, check_theme_arrows.
1956 : samhemelryk 1.1255 *
1957 :     * @global object
1958 : nfreear 1.886 * @param string $text HTML/plain text label (set to blank only for breadcrumb separator cases).
1959 : nfreear 1.881 * @param string $url An optional link to use in a surrounding HTML anchor.
1960 :     * @param bool $accesshide True if text should be hidden (for screen readers only).
1961 :     * @param string $addclass Additional class names for the link, or the arrow character.
1962 :     * @return string HTML string.
1963 : nfreear 1.879 */
1964 : nfreear 1.881 function link_arrow_right($text, $url='', $accesshide=false, $addclass='') {
1965 : nfreear 1.879 global $THEME;
1966 :     check_theme_arrows();
1967 : nfreear 1.881 $arrowclass = 'arrow ';
1968 :     if (! $url) {
1969 :     $arrowclass .= $addclass;
1970 :     }
1971 :     $arrow = '<span class="'.$arrowclass.'">'.$THEME->rarrow.'</span>';
1972 :     $htmltext = '';
1973 :     if ($text) {
1974 : sam_marshall 1.1071 $htmltext = '<span class="arrow_text">'.$text.'</span>&nbsp;';
1975 : nfreear 1.881 if ($accesshide) {
1976 : nfreear 1.954 $htmltext = get_accesshide($htmltext);
1977 : nfreear 1.881 }
1978 :     }
1979 :     if ($url) {
1980 : sam_marshall 1.1071 $class = 'arrow_link';
1981 : nfreear 1.881 if ($addclass) {
1982 : sam_marshall 1.1071 $class .= ' '.$addclass;
1983 : nfreear 1.881 }
1984 : sam_marshall 1.1071