9a74c0542f12fcde5f6992a200629771901ca703
[atutor.git] / docs / include / lib / output.inc.php
1 <?php
2 /****************************************************************/
3 /* ATutor                                                                                                               */
4 /****************************************************************/
5 /* Copyright (c) 2002-2009                                                                              */
6 /* Inclusive Design Institute                                   */
7 /* http://atutor.ca                                                                                             */
8 /*                                                              */
9 /* This program is free software. You can redistribute it and/or*/
10 /* modify it under the terms of the GNU General Public License  */
11 /* as published by the Free Software Foundation.                                */
12 /****************************************************************/
13 // $Id$
14 if (!defined('AT_INCLUDE_PATH')) { exit; }
15 //require_once(AT_INCLUDE_PATH . 'classes/ContentOutputUtils.class.php');
16
17 /**********************************************************************************/
18 /* Output functions found in this file, in order:
19 /*
20 /*      - AT_date(format, timestamp, format_type)
21 /*
22 /*      - _AT([...])
23 /*      - AT_print(input, name, Boolean runtime_html)
24 /*
25 /*      - smile_replace(text)
26 /*      - myCodes(text)
27 /*      - make_clickable(text)
28 /*      - image_replace(text)
29 /*      - format_final_output(text, Boolean nl2br)
30 /*      - highlight (input, var)
31 /*      - format_content(input, Boolean html, glossary)
32 /*      - find_terms(find_text)
33 /*
34 /**********************************************************************************/
35
36
37 /**
38 * Returns a formatted date string - Uses the same options as date(), but requires a % infront of each argument and the
39 * textual values are language dependent (unlike date()).
40 * @access  public
41 * @param   string $format               preferred date format 
42 * @param   string $timestamp    value of timestamp
43 * @param   int $format_type             timestamp format, an AT_DATE constant
44 * @return  string                               formatted date
45 * @see     AT_DATE constants    in include/lib/constants.inc.php
46 * @author  Joel Kronenberg
47 */
48
49 /* 
50         The following options were added as language dependant:
51         %D: A textual representation of a week, three letters Mon through Sun
52         %F: A full textual representation of a month, such as January or March January through December
53         %l (lowercase 'L'): A full textual representation of the day of the week Sunday through Saturday
54         %M: A short textual representation of a month, three letters Jan through Dec
55
56         Support for the following maybe added later:
57         ?? %S: English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
58         ?? %a: Lowercase Ante meridiem and Post meridiem am or pm 
59         ?? %A: Uppercase Ante meridiem and Post meridiem AM or PM 
60
61         valid format_types:
62         AT_DATE_MYSQL_DATETIME:         YYYY-MM-DD HH:MM:SS
63         AT_DATE_MYSQL_TIMESTAMP_14:     YYYYMMDDHHMMSS
64         AT_DATE_UNIX_TIMESTAMP:         seconds since epoch
65         AT_DATE_INDEX_VALUE:            0-x, index into a date array
66 */
67 function AT_date($format='%Y-%M-%d', $timestamp = '', $format_type=AT_DATE_MYSQL_DATETIME) {    
68         static $day_name_ext, $day_name_con, $month_name_ext, $month_name_con;
69         global $_config;
70
71         if (!isset($day_name_ext)) {
72                 $day_name_ext = array(  'date_sunday', 
73                                                                 'date_monday', 
74                                                                 'date_tuesday', 
75                                                                 'date_wednesday', 
76                                                                 'date_thursday', 
77                                                                 'date_friday',
78                                                                 'date_saturday');
79
80                 $day_name_con = array(  'date_sun', 
81                                                                 'date_mon', 
82                                                                 'date_tue', 
83                                                                 'date_wed',
84                                                                 'date_thu', 
85                                                                 'date_fri', 
86                                                                 'date_sat');
87
88                 $month_name_ext = array('date_january', 
89                                                                 'date_february', 
90                                                                 'date_march', 
91                                                                 'date_april', 
92                                                                 'date_may',
93                                                                 'date_june', 
94                                                                 'date_july', 
95                                                                 'date_august', 
96                                                                 'date_september', 
97                                                                 'date_october', 
98                                                                 'date_november',
99                                                                 'date_december');
100
101                 $month_name_con = array('date_jan', 
102                                                                 'date_feb', 
103                                                                 'date_mar', 
104                                                                 'date_apr', 
105                                                                 'date_may_short',
106                                                                 'date_jun', 
107                                                                 'date_jul', 
108                                                                 'date_aug', 
109                                                                 'date_sep', 
110                                                                 'date_oct', 
111                                                                 'date_nov',
112                                                                 'date_dec');
113         }
114
115         if ($format_type == AT_DATE_INDEX_VALUE) {
116                 // apply timezone offset
117 //              $timestamp = apply_timezone($timestamp);
118         
119                 if ($format == '%D') {
120                         return _AT($day_name_con[$timestamp-1]);
121                 } else if ($format == '%l') {
122                         return _AT($day_name_ext[$timestamp-1]);
123                 } else if ($format == '%F') {
124                         return _AT($month_name_ext[$timestamp-1]);
125                 } else if ($format == '%M') {
126                         return _AT($month_name_con[$timestamp-1]);
127                 }
128         }
129
130         if ($timestamp == '') {
131                 $timestamp = time();
132                 $format_type = AT_DATE_UNIX_TIMESTAMP;
133         }
134
135         /* convert the date to a Unix timestamp before we do anything with it */
136         if ($format_type == AT_DATE_MYSQL_DATETIME) {
137                 $year   = substr($timestamp,0,4);
138                 $month  = substr($timestamp,5,2);
139                 $day    = substr($timestamp,8,2);
140                 $hour   = substr($timestamp,11,2);
141                 $min    = substr($timestamp,14,2);
142                 $sec    = substr($timestamp,17,2);
143             $timestamp  = mktime($hour, $min, $sec, $month, $day, $year);
144
145         } else if ($format_type == AT_DATE_MYSQL_TIMESTAMP_14) {
146             $year               = substr($timestamp,0,4);
147             $month              = substr($timestamp,4,2);
148             $day                = substr($timestamp,6,2);
149                 $hour           = substr($timestamp,8,2);
150             $minute             = substr($timestamp,10,2);
151             $second             = substr($timestamp,12,2);
152             $timestamp  = mktime($hour, $minute, $second, $month, $day, $year);  
153         }
154
155         // apply timezone offset
156         $timestamp = apply_timezone($timestamp);
157
158         /* pull out all the %X items from $format */
159         $first_token = strpos($format, '%');
160         if ($first_token === false) {
161                 /* no tokens found */
162                 return $timestamp;
163         } else {
164                 $tokened_format = substr($format, $first_token);
165         }
166         $tokens = explode('%', $tokened_format);
167         array_shift($tokens);
168         $num_tokens = count($tokens);
169
170         $output = $format;
171         for ($i=0; $i<$num_tokens; $i++) {
172                 $tokens[$i] = substr($tokens[$i],0,1);
173
174                 if ($tokens[$i] == 'D') {
175                         $output = str_replace('%D', _AT($day_name_con[date('w', $timestamp)]),$output);
176                 
177                 } else if ($tokens[$i] == 'l') {
178                         $output = str_replace('%l', _AT($day_name_ext[date('w', $timestamp)]),$output);
179                 
180                 } else if ($tokens[$i] == 'F') {
181                         $output = str_replace('%F', _AT($month_name_ext[date('n', $timestamp)-1]),$output);             
182                 
183                 } else if ($tokens[$i] == 'M') {
184                         $output = str_replace('%M', _AT($month_name_con[date('n', $timestamp)-1]),$output);
185
186                 } else {
187
188                         /* this token doesn't need translating */
189                         $value = date($tokens[$i], $timestamp);
190                         if ($value != $tokens[$i]) {
191                                 $output = str_replace('%'.$tokens[$i], $value, $output);
192                         } /* else: this token isn't valid. so don't replace it. Eg. try %q */
193                 }
194         }
195
196         return $output;
197 }
198
199 /**
200 * Converts language code to actual language message, caches them according to page url
201 * @access       public
202 * @param        args                            unlimited number of arguments allowed but first arg MUST be name of the language variable/term
203 *                                                               i.e             $args[0] = the term to the format string $_template[term]
204 *                                                                               $args[1..x] = optional arguments to the formatting string 
205 * @return       string|array            full resulting message
206 * @see          $db                             in include/vitals.inc.php
207 * @see          cache()                         in include/phpCache/phpCache.inc.php
208 * @see          cache_variable()        in include/phpCache/phpCache.inc.php
209 * @author       Joel Kronenberg
210 */
211 function _AT() {
212         global $_cache_template, $lang_et, $_rel_url;
213         static $_template;
214         
215         $args = func_get_args();
216         
217         // a feedback msg
218         if (!is_array($args[0])) {
219                 /**
220                  * Added functionality for translating language code String (AT_ERROR|AT_INFOS|AT_WARNING|AT_FEEDBACK|AT_HELP).*
221                  * to its text and returning the result. No caching needed.
222                  * @author Jacek Materna
223                  */
224
225                 // Check for specific language prefix, extendible as needed
226                 // 0002767:  a substring+in_array test should be faster than a preg_match test.
227                 // replaced the preg_match with a test of the substring.
228                 $sub_arg = substr($args[0], 0, 7); // 7 is the shortest type of msg (AT_HELP)
229                 if (in_array($sub_arg, array('AT_ERRO','AT_INFO','AT_WARN','AT_FEED','AT_HELP','AT_CONF'))) {
230                         global $db;
231                         global $_base_path, $addslashes;
232
233                         $args[0] = $addslashes($args[0]);
234                                         
235                         /* get $_msgs_new from the DB */
236                         $sql    = 'SELECT text FROM '.TABLE_PREFIX.'language_text WHERE term="' . $args[0] . '" AND (variable="_msgs" OR variable="_c_msgs") AND language_code="'.$_SESSION['lang'].'" ORDER BY variable ASC LIMIT 1';
237
238                         $result = @mysql_query($sql, $db);
239                         $i = 1;
240                         $msgs = '';
241                                         
242                         if ($row = @mysql_fetch_assoc($result)) {
243                                 // do not cache key as a digit (no contstant(), use string)
244                                 $msgs = str_replace('SITE_URL/', $_base_path, $row['text']);
245                                 if (defined('AT_DEVEL') && AT_DEVEL) {
246                                         $msgs .= ' <small><small>('. $args[0] .')</small></small>';
247                                 }
248                         }
249
250                         $sql = 'INSERT INTO '.TABLE_PREFIX.'language_pages (`term`, `page`) VALUES ("'.$args[0].'", "'.$_rel_url.'")';
251                         mysql_query($sql, $db);
252
253                         return $msgs;
254                 }
255         }
256         
257         // a template variable
258         if (!isset($_template)) {
259                 $url_parts = parse_url(AT_BASE_HREF);
260                 $name = substr($_SERVER['PHP_SELF'], strlen($url_parts['path'])-1);
261
262                 if ( !($lang_et = cache(120, 'lang', $_SESSION['lang'].'_'.$name)) ) {
263                         global $db;
264
265                         /* get $_template from the DB */
266                         
267                         $sql = "SELECT L.* FROM ".TABLE_PREFIX."language_text L, ".TABLE_PREFIX."language_pages P WHERE L.language_code='{$_SESSION['lang']}' AND L.variable<>'_msgs' AND L.term=P.term AND P.page='$_rel_url' ORDER BY L.variable ASC";
268                         $result = mysql_query($sql, $db);
269                         while ($row = mysql_fetch_assoc($result)) {
270                                 //Do not overwrite the variable that existed in the cache_template already.
271                                 //The edited terms (_c_template) will always be at the top of the resultset
272                                 //0003279
273                                 if (isset($_cache_template[$row['term']])){
274                                         continue;
275                                 }
276
277                                 // saves us from doing an ORDER BY
278                                 if ($row['language_code'] == $_SESSION['lang']) {
279                                         $_cache_template[$row['term']] = stripslashes($row['text']);
280                                 } else if (!isset($_cache_template[$row['term']])) {
281                                         $_cache_template[$row['term']] = stripslashes($row['text']);
282                                 }
283                         }
284                 
285                         cache_variable('_cache_template');
286                         endcache(true, false);
287                 }
288                 $_template = $_cache_template;
289         }
290         $num_args = func_num_args();
291         if (is_array($args[0])) {
292                 $args = $args[0];
293                 $num_args = count($args);
294         }
295         $format   = array_shift($args);
296
297         if (isset($_template[$format])) {
298                 /*
299                 var_dump($_template);
300                 var_dump($format);
301                 var_dump($args);
302                 exit;
303                 */
304                 $outString      = vsprintf($_template[$format], $args);
305                 $str = ob_get_contents();
306         } else {
307                 $outString = '';
308         }
309
310
311         if ($outString === false) {
312                 return ('[Error parsing language. Variable: <code>'.$format.'</code>. Language: <code>'.$_SESSION['lang'].'</code> ]');
313         }
314
315         if (empty($outString)) {
316                 global $db;
317                 $sql    = 'SELECT L.* FROM '.TABLE_PREFIX.'language_text L WHERE L.language_code="'.$_SESSION['lang'].'" AND L.variable<>"_msgs" AND L.term="'.$format.'"';
318
319                 $result = mysql_query($sql, $db);
320                 $row = mysql_fetch_assoc($result);
321
322                 $_template[$row['term']] = stripslashes($row['text']);
323                 $outString = $_template[$row['term']];
324                 if (empty($outString)) {
325                         return ('[ '.$format.' ]');
326                 }
327                 $outString = $_template[$row['term']];
328                 $outString = vsprintf($outString, $args);
329
330                 /* update the locations */
331                 $sql = 'INSERT INTO '.TABLE_PREFIX.'language_pages (`term`, `page`) VALUES ("'.$format.'", "'.$_rel_url.'")';
332                 mysql_query($sql, $db);
333         }
334
335         return $outString;
336 }
337
338 /**********************************************************************************************************/
339         /**
340         *       Transforms text based on formatting preferences.  Original $input is also changed (passed by reference).
341         *       Can be called as:
342         *       1) $output = AT_print($input, $name);
343         *          echo $output;
344         *
345         *       2) echo AT_print($input, $name); // prefered method
346         *
347         * @access       public
348         * @param        string $input                   text being transformed
349         * @param        string $name                    the unique name of this field (convension: table_name.field_name)
350         * @param        boolean $runtime_html   forcefully disables html formatting for $input (only used by fields that 
351         *                                                                       have the 'formatting' option
352         * @return       string                                  transformed $input
353         * @see          AT_FORMAT constants             in include/lib/constants.inc.php
354         * @see          query_bit()                             in include/vitals.inc.php
355         * @author       Joel Kronenberg
356         */
357         function AT_print($input, $name, $runtime_html = true) {
358                 global $_field_formatting, $_config;
359
360                 if (!isset($_field_formatting[$name])) {
361                         /* field not set, check if there's a global setting */
362                         $parts = explode('.', $name);
363                         
364                         /* check if wildcard is set: */
365                         if (isset($_field_formatting[$parts[0].'.*'])) {
366                                 $name = $parts[0].'.*';
367                         } else {
368                                 /* field not set, and there's no global setting */
369                                 /* same as AT_FORMAT_NONE */
370                                 return $input;
371                         }
372                 }
373
374                 if (query_bit($_field_formatting[$name], AT_FORMAT_QUOTES)) {
375                         $input = str_replace('"', '&quot;', $input);
376             $input = str_replace('\'', '&apos;', $input);
377                 }
378
379                 if (query_bit($_field_formatting[$name], AT_FORMAT_CONTENT_DIR)) {
380                         $input = str_replace('CONTENT_DIR/', '', $input);
381                 }
382
383                 if (query_bit($_field_formatting[$name], AT_FORMAT_HTML) && $runtime_html) {
384                         /* what special things do we have to do if this is HTML ? remove unwanted HTML? validate? */
385                 } else {
386                         $input = str_replace('<', '&lt;', $input);
387                         $input = nl2br($input);
388                 }
389
390                 if (isset($_config['latex_server']) && $_config['latex_server']) {
391                         $input = preg_replace('/\[tex\](.*?)\[\/tex\]/sie', "'<img src=\"'.\$_config['latex_server'].rawurlencode('$1').'\" align=\"middle\" alt=\"'.'$1'.'\" title=\"'.'$1'.'\">'", $input);
392                 }
393
394                 /* this has to be here, only because AT_FORMAT_HTML is the only check that has an else-block */
395                 if ($_field_formatting[$name] === AT_FORMAT_NONE) {
396                         return $input;
397                 }
398
399                 if (query_bit($_field_formatting[$name], AT_FORMAT_EMOTICONS)) {
400                         $input = smile_replace($input);
401                 }
402
403                 if (query_bit($_field_formatting[$name], AT_FORMAT_ATCODES)) {
404                         $input = trim(myCodes(' ' . $input . ' '));
405                 }
406
407                 if (query_bit($_field_formatting[$name], AT_FORMAT_LINKS)) {
408                         $input = trim(make_clickable(' ' . $input . ' '));
409                 }
410
411                 if (query_bit($_field_formatting[$name], AT_FORMAT_IMAGES)) {
412                         $input = trim(image_replace(' ' . $input . ' '));
413                 }
414                 return $input;
415         }
416
417 /********************************************************************************************/
418 // Global variables for emoticons
419  
420 global $smile_pics;
421 global $smile_codes;
422 if (!isset($smile_pics)) {
423         $smile_pics[0] = $_base_path.'images/forum/smile.gif';
424         $smile_pics[1] = $_base_path.'images/forum/wink.gif';
425         $smile_pics[2] = $_base_path.'images/forum/frown.gif';
426         $smile_pics[3] = $_base_path.'images/forum/ohwell.gif';
427         $smile_pics[4] = $_base_path.'images/forum/tongue.gif';
428         $smile_pics[5] = $_base_path.'images/forum/51.gif';
429         $smile_pics[6] = $_base_path.'images/forum/52.gif';
430         $smile_pics[7] = $_base_path.'images/forum/54.gif';
431         $smile_pics[8] = $_base_path.'images/forum/27.gif';
432         $smile_pics[9] = $_base_path.'images/forum/19.gif';
433         $smile_pics[10] = $_base_path.'images/forum/3.gif';
434         $smile_pics[11] = $_base_path.'images/forum/56.gif';
435 }
436
437 if (!isset($smile_codes)) {
438         $smile_codes[0] = ':)';
439         $smile_codes[1] = ';)';
440         $smile_codes[2] = ':(';
441         $smile_codes[3] = '::ohwell::';
442         $smile_codes[4] = ':P';
443         $smile_codes[5] = '::evil::';
444         $smile_codes[6] = '::angry::';
445         $smile_codes[7] = '::lol::';
446         $smile_codes[8] = '::crazy::';
447         $smile_codes[9] = '::tired::';
448         $smile_codes[10] = '::confused::';
449         $smile_codes[11] = '::muah::';
450 }
451
452 /**
453 * Replaces smile-code text into smilie image.
454 * @access       public
455 * @param        string $text            smile text to be transformed
456 * @return       string                          transformed $text
457 * @see          $smile_pics                     in include/lib/output.inc.php (above)
458 * @see          $smile_codes            in include/lib/output.inc.php (above)
459 * @author       Joel Kronenberg
460 */
461 function smile_replace($text) {
462         global $smile_pics;
463         global $smile_codes;
464         static $smiles;
465
466         $smiles[0] = '<img src="'.$smile_pics[0].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_smile').'" />';
467         $smiles[1] = '<img src="'.$smile_pics[1].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_wink').'" />';
468         $smiles[2] = '<img src="'.$smile_pics[2].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_frown').'" />';
469         $smiles[3]= '<img src="'.$smile_pics[3].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_oh_well').'" />';
470         $smiles[4]= '<img src="'.$smile_pics[4].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_tongue').'" />';
471         $smiles[5]= '<img src="'.$smile_pics[5].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_evil').'" />';
472         $smiles[6]= '<img src="'.$smile_pics[6].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_angry').'" />';
473         $smiles[7]= '<img src="'.$smile_pics[7].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_lol').'" />';
474         $smiles[8]= '<img src="'.$smile_pics[8].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_crazy').'" />';
475         $smiles[9]= '<img src="'.$smile_pics[9].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_tired').'" />';
476         $smiles[10]= '<img src="'.$smile_pics[10].'" border="0" height="17" width="19" align="bottom" alt="'._AT('smile_confused').'" />';
477         $smiles[11]= '<img src="'.$smile_pics[11].'" border="0" height="15" width="15" align="bottom" alt="'._AT('smile_muah').'" />';
478
479         $text = str_replace($smile_codes[0],$smiles[0],$text);
480         $text = str_replace($smile_codes[1],$smiles[1],$text);
481         $text = str_replace($smile_codes[2],$smiles[2],$text);
482         $text = str_replace($smile_codes[3],$smiles[3],$text);
483         $text = str_replace($smile_codes[4],$smiles[4],$text);
484         $text = str_replace($smile_codes[5],$smiles[5],$text);
485         $text = str_replace($smile_codes[6],$smiles[6],$text);
486         $text = str_replace($smile_codes[7],$smiles[7],$text);
487         $text = str_replace($smile_codes[8],$smiles[8],$text);
488         $text = str_replace($smile_codes[9],$smiles[9],$text);
489         $text = str_replace($smile_codes[10],$smiles[10],$text);
490         $text = str_replace($smile_codes[11],$smiles[11],$text);
491
492         return $text;
493 }
494
495
496 /* Used specifically for the visual editor
497 */
498 function smile_javascript () {
499         global $_base_path;
500         global $smile_pics;
501         global $smile_codes;
502
503         static $i = 0;
504
505         while ($smile_pics [$i]) {
506                 echo 'case "'.$smile_codes[$i].'":'."\n";
507                 echo 'pic = "'.$smile_pics[$i].'";'."\n";
508                 echo 'break;'."\n";
509                 $i++;
510         }
511 }
512     
513 function myCodes($text, $html = false) {
514         global $_base_path;
515         global $HTTP_USER_AGENT;
516
517         if (substr($HTTP_USER_AGENT,0,11) == 'Mozilla/4.7') {
518                 $text = str_replace('[quote]','</p><p class="block">',$text);
519                 $text = str_replace('[/quote]','</p><p>',$text);
520
521                 $text = str_replace('[reply]','</p><p class="block">',$text);
522                 $text = str_replace('[/reply]','</p><p>',$text);
523         } else {
524                 $text = str_replace('[quote]','<blockquote>',$text);
525                 $text = str_replace('[/quote]','</blockquote><p>',$text);
526
527                 $text = str_replace('[reply]','</p><blockquote class="block"><p>',$text);
528                 $text = str_replace('[/reply]','</p></blockquote><p>',$text);
529         }
530
531         $text = str_replace('[b]','<strong>',$text);
532         $text = str_replace('[/b]','</strong>',$text);
533
534         $text = str_replace('[i]','<em>',$text);
535         $text = str_replace('[/i]','</em>',$text);
536
537         $text = str_replace('[u]','<u>',$text);
538         $text = str_replace('[/u]','</u>',$text);
539
540         $text = str_replace('[center]','<center>',$text);
541         $text = str_replace('[/center]','</center><p>',$text);
542
543         /* colours */
544         $text = str_replace('[blue]','<span style="color: blue;">',$text);
545         $text = str_replace('[/blue]','</span>',$text);
546
547         $text = str_replace('[orange]','<span style="color: orange;">',$text);
548         $text = str_replace('[/orange]','</span>',$text);
549
550         $text = str_replace('[red]','<span style="color: red;">',$text);
551         $text = str_replace('[/red]','</span>',$text);
552
553         $text = str_replace('[purple]','<span style="color: purple;">',$text);
554         $text = str_replace('[/purple]','</span>',$text);
555
556         $text = str_replace('[green]','<span style="color: green;">',$text);
557         $text = str_replace('[/green]','</span>',$text);
558
559         $text = str_replace('[gray]','<span style="color: gray;">',$text);
560         $text = str_replace('[/gray]','</span>',$text);
561
562         $text = str_replace('[op]','<span class="bigspacer"></span> <a href="',$text);
563         $text = str_replace('[/op]','">'._AT('view_entire_post').'</a>',$text);
564
565         $text = str_replace('[head1]','<h2>',$text);
566         $text = str_replace('[/head1]','</h2>',$text);
567
568         $text = str_replace('[head2]','<h3>',$text);
569         $text = str_replace('[/head2]','</h3>',$text);
570
571         $text = str_replace('[cid]',$_base_path.'content.php?cid='.$_SESSION['s_cid'],$text);
572
573         // fix for http://www.atutor.ca/atutor/mantis/view.php?id=4104
574         global $sequence_links;
575         if ($_SESSION['course_id'] > 0 && !isset($sequence_links) && $_REQUEST['cid'] > 0) {
576                 global $contentManager;
577                 $sequence_links = $contentManager->generateSequenceCrumbs($_REQUEST['cid']);
578         }
579         if (isset($sequence_links['previous']) && $sequence_links['previous']['url']) {
580                 $text = str_replace('[pid]', $sequence_links['previous']['url'], $text);
581         }
582         if (isset($sequence_links['next']) && $sequence_links['next']['url']) {
583                 $text = str_replace('[nid]', $sequence_links['next']['url'], $text);
584         }
585         if (isset($sequence_links['resume']) && $sequence_links['resume']['url']) {
586                 $text = str_replace('[nid]', $sequence_links['resume']['url'], $text);
587         }
588         if (isset($sequence_links['first']) && $sequence_links['first']['url']) {
589                 $text = str_replace('[fid]', $sequence_links['first']['url'], $text);
590         }
591
592 //LAW - replace </p><p> tags in [code] tags with <br />
593 //http://www.atutor.ca/atutor/mantis/view.php?id=4134 - attempt to fix this bug - does not work as required
594 //      $outputUtils = new ContentOutputUtils();
595 //      $text = $outputUtils ->stripPtags($text);
596         
597         /* contributed by Thomas M. Duffey <tduffey at homeboyz.com> */
598     $html = !$html ? 0 : 1;
599     
600         // little hack added by greg to add syntax highlighting without using <?php \?\>
601         
602         $text = str_replace("[code]","[code]<?php",$text);
603         $text = str_replace("[/code]","?>[/code]",$text);
604
605         $text = preg_replace("/\[code\]\s*(.*)\s*\[\\/code\]/Usei", "highlight_code(fix_quotes('\\1'), $html)", $text);
606         // now remove the <?php added above and leave the syntax colour behind.
607         $text = str_replace("&lt;?php", "", $text);
608         $text = str_replace("?&gt;", "", $text);
609
610         return $text;
611 }
612
613 /* contributed by Thomas M. Duffey <tduffey at homeboyz.com> */
614 function highlight_code($code, $html) {
615         // XHTMLize PHP highlight_string output until it gets fixed in PHP
616         static $search = array(
617                 '<br>',
618                 '<font',
619                 '</font>',
620                 'color="');
621
622         static $replace = array(
623                 '<br />',
624                 '<span',
625                 '</span>',
626                 'style="color:');
627         if (!$html) {
628                 $code = str_replace('&lt;', '<', $code);
629                 $code = str_replace("\r", '', $code);
630         }
631
632         return str_replace($search, $replace, highlight_string($code, true));
633 }
634
635 /* contributed by Thomas M. Duffey <tduffey at homeboyz.com> */
636 function fix_quotes($text){
637         return str_replace('\\"', '"', $text);
638 }
639
640 /*
641  * This function converts the youtube playable url used in <object> tag (for instance: http://www.youtube.com/v/a0ryB0m0MiM)
642  * to youtube url that is used to browse (for instance: http://www.youtube.com/watch?v=a0ryB0m0MiM)
643  * @param: youtube playable URL. For instance, http://www.youtube.com/v/a0ryB0m0MiM
644  * @return: if the param is a youtube playable url, return the according youtube URL used to browse. 
645  *          For instance: http://www.youtube.com/watch?v=a0ryB0m0MiM
646  *          Otherwise, return the original send-in parameter.
647  */
648 function convert_youtube_playURL_to_watchURL($youtube_playURL) {
649         return preg_replace("/(http:\/\/[a-z0-9\.]*)?youtube.com\/v\/(.*)/",
650                             "\\1youtube.com/watch?v=\\2", $youtube_playURL);
651 }
652
653 /*
654  * This function converts the youtube url that is used to browse (for instance: http://www.youtube.com/watch?v=a0ryB0m0MiM)
655  * to youtube playable url used in <object> tag (for instance: http://www.youtube.com/v/a0ryB0m0MiM)
656  * @param: the youtube URL used to browse. 
657  *         For instance: http://www.youtube.com/watch?v=a0ryB0m0MiM
658  * @return: if the param is a youtube url used to browse, return the according youtube playable URL. 
659  *          For instance, http://www.youtube.com/v/a0ryB0m0MiM
660  *          Otherwise, return the original send-in parameter.
661  */
662 function convert_youtube_watchURL_to_playURL($youtube_watchURL) {
663         return preg_replace("/(http:\/\/[a-z0-9\.]*)?youtube.com\/watch\?v=(.*)/",
664                             "\\1youtube.com/v/\\2", $youtube_watchURL);
665 }
666
667 function embed_media($text) {
668         global $_base_path;
669         
670         if (preg_match("/\[media(\|[0-9]+\|[0-9]+)?\]*/", $text)==0){
671                 return $text;
672         }
673
674         // 1. remove the spaces in [media] tag, otherwise, the next line converts URL inside [media] into <a> tag
675         $text = preg_replace("/(\[media\])([\s]*)(.*)(\[\/media\])/", '$1$3$4', $text);
676         $text = preg_replace("/(\[media\])(.*)([\s]*)(\[\/media\])/U", '$1$2$4', $text);
677         
678         $media_matches = array();
679         $media_replace = array();
680         
681         // First, we search though the text for all different kinds of media defined by media tags and store the results in $media_matches.
682         // Then the different replacements for the different media tags are stored in $media_replace.
683         // Lastly, we loop through all $media_matches / $media_replaces. (We choose $media_replace as index because $media_matches is multi-dimensioned.) It is important that for each $media_matches there is a $media_replace with the same index. For each media match we check the width/height, or we use the default value of 425x350. We then replace the height/width/media1/media2 parameter placeholders in $media_replace with the correct ones, before running a str_replace on $text, replacing the given media with its correct replacement.
684
685         // youtube videos
686         if (is_mobile_device() && get_mobile_device_type() == BLACKBERRY_DEVICE) {
687                 preg_match_all("#\[media[0-9a-z\|]*\]http://([a-z0-9\.]*)?youtube.com/watch\?v=(.*)\[/media\]#iU",$text,$media_matches[],PREG_SET_ORDER);
688                 $media_replace[] = '<script type="text/javascript" src="'.$_base_path.'jscripts/ATutorYouTubeOnBlackberry.js"></script>'."\n".
689                         '<p id="blackberry_##MEDIA2##">'."\n".
690                         '<script'."\n".
691                         '  src="http://gdata.youtube.com/feeds/mobile/videos/##MEDIA2##?alt=json-in-script&amp;callback=ATutor.course.showYouTubeOnBlackberry&amp;format=6" [^]'."\n".
692                         '  type="text/javascript">'."\n".
693                         '</script>';
694         } else {
695                 preg_match_all("#\[media[0-9a-z\|]*\]http://([a-z0-9\.]*)?youtube.com/watch\?v=(.*)\[/media\]#iU",$text,$media_matches[],PREG_SET_ORDER);
696                 $media_replace[] = '<object width="##WIDTH##" height="##HEIGHT##"><param name="movie" value="http://##MEDIA1##youtube.com/v/##MEDIA2##"></param><embed src="http://##MEDIA1##youtube.com/v/##MEDIA2##" type="application/x-shockwave-flash" width="##WIDTH##" height="##HEIGHT##"></embed></object>';
697         }
698         
699         // .mpg
700         preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).mpg\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
701         $media_replace[] = "<object data=\"##MEDIA1##.mpg\" type=\"video/mpeg\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.mpg\"><param name=\"autoplay\" value=\"false\"><param name=\"autoStart\" value=\"0\"><a href=\"##MEDIA1##.mpg\">##MEDIA1##.mpg</a></object>";
702         
703         // .avi
704         preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).avi\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
705         $media_replace[] = "<object data=\"##MEDIA1##.avi\" type=\"video/x-msvideo\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.avi\"><param name=\"autoplay\" value=\"false\"><param name=\"autoStart\" value=\"0\"><a href=\"##MEDIA1##.avi\">##MEDIA1##.avi</a></object>";
706         
707         // .wmv
708         preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).wmv\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
709         $media_replace[] = "<object data=\"##MEDIA1##.wmv\" type=\"video/x-ms-wmv\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.wmv\"><param name=\"autoplay\" value=\"false\"><param name=\"autoStart\" value=\"0\"><a href=\"##MEDIA1##.wmv\">##MEDIA1##.wmv</a></object>";
710         
711         // .mov
712         preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).mov\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
713         $media_replace[] = "<object classid=\"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B\" codebase=\"http://www.apple.com/qtactivex/qtplugin.cab\" width=\"##WIDTH##\" height=\"##HEIGHT##\">\n".
714                            "  <param name=\"src\" value=\"##MEDIA1##.mov\">\n".
715                            "  <param name=\"controller\" value=\"true\">\n".
716                            "  <param name=\"autoplay\" value=\"false\">\n".
717                            "  <!--[if gte IE 7] > <!-->\n".
718                            "  <object type=\"video/quicktime\" data=\"##MEDIA1##.mov\" width=\"##WIDTH##\" height=\"##HEIGHT##\">\n".
719                            "    <param name=\"controller\" value=\"true\">\n".
720                            "    <param name=\"autoplay\" value=\"false\">\n".
721                            "    <a href=\"##MEDIA1##.mov\">##MEDIA1##.mov</a>\n".
722                            "  </object>\n".
723                            "  <!--<![endif]-->\n".
724                            "  <!--[if lt IE 7]>\n".
725                            "  <a href=\"##MEDIA1##.mov\">##MEDIA1##.mov</a>\n".
726                            "  <![endif]-->\n".
727                            "</object>";
728         
729         // .swf
730         preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).swf\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
731         $media_replace[] = "<object type=\"application/x-shockwave-flash\" data=\"##MEDIA1##.swf\" width=\"##WIDTH##\" height=\"##HEIGHT##\">  <param name=\"movie\" value=\"##MEDIA1##.swf\"><param name=\"loop\" value=\"false\"><a href=\"##MEDIA1##.swf\">##MEDIA1##.swf</a></object>";
732
733         // .mp3
734         preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).mp3\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
735         $media_replace[] = "<object type=\"audio/mpeg\" data=\"##MEDIA1##.mp3\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.mp3\"><param name=\"autoplay\" value=\"false\"><param name=\"autoStart\" value=\"0\"><a href=\"##MEDIA1##.mp3\">##MEDIA1##.mp3</a></object>";
736         
737         // .wav
738         preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).wav\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
739         $media_replace[] ="<object type=\"audio/x-wav\" data=\"##MEDIA1##.wav\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.wav\"><param name=\"autoplay\" value=\"false\"><param name=\"autoStart\" value=\"0\"><a href=\"##MEDIA1##.wav\">##MEDIA1##.wav</a></object>";
740         
741         // .ogg
742         preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).ogg\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
743         $media_replace[] ="<object type=\"application/ogg\" data=\"##MEDIA1##.ogg\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.ogg\"><a href=\"##MEDIA1##.ogg\">##MEDIA1##.ogg</a></object>";
744         
745         // .ogm
746         preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).ogm\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
747         $media_replace[] ="<object type=\"application/ogm\" data=\"##MEDIA1##.ogm\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.ogm\"><a href=\"##MEDIA1##.ogg\">##MEDIA1##.ogm</a></object>";
748         
749         // .mid
750         preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).mid\[/media\]#i",$text,$media_matches[],PREG_SET_ORDER);
751         $media_replace[] ="<object type=\"application/x-midi\" data=\"##MEDIA1##.mid\" width=\"##WIDTH##\" height=\"##HEIGHT##\"><param name=\"src\" value=\"##MEDIA1##.mid\"><a href=\"##MEDIA1##.mid\">##MEDIA1##.mid</a></object>";
752         
753         $text = preg_replace("#\[media[0-9a-z\|]*\](.+[^\s\"]+).mid\[/media\]#i", "<object type=\"application/x-midi\" data=\"\\1.mid\" width=\"".$width."\" height=\"".$height."\"><param name=\"src\" value=\"\\1.mid\"><a href=\"\\1.mid\">\\1.mid</a></object>", $text);
754
755         // Executing the replace
756         for ($i=0;$i<count($media_replace);$i++){
757                 foreach($media_matches[$i] as $media)
758                 {
759                         //find width and height for each matched media
760                         if (preg_match("/\[media\|([0-9]*)\|([0-9]*)\]*/", $media[0], $matches)) 
761                         {
762                                 $width = $matches[1];
763                                 $height = $matches[2];
764                         }
765                         else
766                         {
767                                 $width = 425;
768                                 $height = 350;
769                         }
770                         
771                         //replace media tags with embedded media for each media tag
772                         $media_input = $media_replace[$i];
773                         $media_input = str_replace("##WIDTH##","$width",$media_input);
774                         $media_input = str_replace("##HEIGHT##","$height",$media_input);
775                         $media_input = str_replace("##MEDIA1##","$media[1]",$media_input);
776                         $media_input = str_replace("##MEDIA2##","$media[2]",$media_input);
777                         
778                         $text = str_replace($media[0],$media_input,$text);
779                 }
780         }
781         return $text;
782
783 }
784
785 function make_clickable($text) {
786         $text = embed_media($text);
787
788 //      $text = eregi_replace("([[:space:]])(http[s]?)://([^[:space:]<]*)([[:alnum:]#?/&=])", "\\1<a href=\"\\2://\\3\\4\">\\3\\4</a>", $text);
789 //
790 //      $text = eregi_replace(  '([_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.
791 //                                                      '\@'.'[_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'(\.[a-zA-Z]{1,6})+)',
792 //                                                      "<a href=\"mailto:\\1\">\\1</a>",
793 //                                                      $text);
794
795         // convert plain text URL to clickable URL.
796         // Limited conversion: It doesn't cover the case when the stuff in front of the URL is not a word. For example:
797         // <p>http://google.ca</p>
798         // "http://google.ca" 
799         $text = preg_replace('/(^|[\n ])([\w]*?)((?<!(\[media\]))http(s)?:\/\/[\w]+[^ \,\"\n\r\t\)<]*)/is', 
800                              '$1$2<a href="$3">$3</a>', $text);
801         
802         // convert email address to clickable URL that pops up "send email" interface with the address filled in
803         $text = preg_replace('/(?|<a href="mailto[\s]*:[\s]*([_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'\@'
804                             .'[_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'(\.[a-zA-Z]{1,6})+)">(.*)<\/a>'
805                             .'|((((([_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'\@'
806                             .'[_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'(\.[a-zA-Z]{1,6})+))))))/i',
807                                                 "<a href=\"mailto:\\1\">\\5</a>",
808                                                 $text);
809         return $text;
810 }
811
812 function image_replace($text) {
813         /* image urls do not require http:// */
814         
815 //      $text = eregi_replace("\[image(\|)?([[:alnum:][:space:]]*)\]" .
816 //                                               "[:space:]*" .
817 //                                               "([[:alnum:]#?/&=:\"'_.-]+)" .
818 //                                               "[:space:]*" .
819 //                                               "((\[/image\])|(.*\[/image\]))",
820 //                                "<img src=\"\\3\" alt=\"\\2\" />",
821 //                                $text);
822          
823         $text = preg_replace("/\[image(\|)?([a-zA-Z0-9\s]*)\]".
824                              "[\s]*".
825                              "([a-zA-Z0-9\#\?\/\&\=\:\\\"\'\_\.\-]+)[\s]*".
826                              "((\[\/image\])|(.*\[\/image\]))/i",
827                                   "<img src=\"\\3\" alt=\"\\2\" />",
828                                   $text);
829                                   
830         return $text;
831 }
832
833 function format_final_output($text, $nl2br = true) {
834         global $_base_path;
835
836         $text = str_replace('CONTENT_DIR/', '', $text);
837         if ($nl2br) {
838                 return nl2br(image_replace(make_clickable(myCodes(' '.$text, false))));
839         }
840
841         return image_replace(make_clickable(myCodes(' '.$text, true)));
842 }
843
844 // 
845 function apply_customized_format($input) {
846         global $_input, $moduleFactory, $content_base_href, $_content_base_href;
847         
848         $_input = $input;
849         $_content_base_href = $content_base_href;
850         
851         $enabled_modules = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED);
852
853         if (is_array($enabled_modules))
854         {
855                 foreach ($enabled_modules as $dir_name => $mod)
856                 {
857                         $module_content_format_file = AT_INCLUDE_PATH . '../mods/'.$dir_name.'/module_format_content.php';
858                         if (file_exists($module_content_format_file))
859                         {
860                                 include($module_content_format_file);
861                         }
862                 }
863         }
864         
865         return $_input;
866 }
867 /****************************************************************************************/
868 /* @See: ./user/search.php & ./index.php */
869 function highlight($input, $var) {//$input is the string, $var is the text to be highlighted
870         if ($var != "") {
871                 $xtemp = "";
872                 $i=0;
873                 /*
874                         The following 'if' statement is a check to ensure that the search term is not part of the tag, '<strong class="highlight">'.  Words within this string are avoided in case a previously highlighted string is used for the haystack, $input.  To avoid any html breaks in the highlighted string, the search word is avoided completely.
875                 */
876                 if (strpos('<strong class="highlight">', $var) !== false) {
877                         return $input;
878                 }
879                 while($i<strlen($input)){
880                         if((($i + strlen($var)) <= strlen($input)) && (strcasecmp($var, substr($input, $i, strlen($var))) == 0)) {
881                                 $xtemp .= '<strong class="highlight">' . substr($input, $i , strlen($var)) . '</strong>';
882                                 $i += strlen($var);
883                         }
884                         else {
885                                 $xtemp .= $input{$i};
886                                 $i++;
887                         }
888                 }
889                 $input = $xtemp;
890         }
891         return $input;
892 }
893
894
895 /* @See: ./index.php */
896 function format_content($input, $html = 0, $glossary, $simple = false) {
897         global $_base_path, $_config;
898
899         if (!$html) {
900                 $input = str_replace('<', '&lt;', $input);
901                 $input = str_replace('&lt;?php', '<?php', $input); // for bug #2087
902         } elseif ($html==2) {
903                 $output = '<iframe width="100%" frameborder="0" id="content_frame" marginheight="0" marginwidth="0" src="'.$input.'"></iframe>';
904                 $output .=      '<script type="text/javascript">
905                                         function resizeIframe() {
906                                                 var height = document.documentElement.clientHeight;
907                                                 
908                                                 // not sure how to get this dynamically
909                                                 height -= 20; /* whatever you set your body bottom margin/padding to be */
910                                                 
911                                                 document.getElementById(\'content_frame\').style.height = height +"px";
912                                                 
913                                         };
914                                         document.getElementById(\'content_frame\').onload = resizeIframe;
915                                         window.onresize = resizeIframe;
916                                         </script>';
917                 return $output;
918         }
919
920         /* do the glossary search and replace: */
921         if (is_array($glossary)) {
922                 foreach ($glossary as $k => $v) {
923                         $k = urldecode($k);
924                         $v = str_replace("\n", '<br />', $v);
925                         $v = str_replace("\r", '', $v);
926
927                         $k = str_replace('&lt;', '<', $k);
928
929                         $original_term = str_replace('/', '\/', $k);;
930                         $term = $k;
931              if (!$html) {
932                 $term = str_replace('<', '&lt;', $term);
933             }
934
935                         /* escape special characters */
936             $term = preg_quote($term, "/");
937             $term = '(\s*'.$term.'\s*)';
938                         $term = str_replace(' ','((<br \/>)*\s*)', $term); 
939                         
940                         // Uncomment the line below and comment the following line
941                         // when the jquery UI tooltip supports the html display.
942                         //$def = htmlspecialchars($v, ENT_QUOTES, 'UTF-8');
943                         $def = htmlspecialchars(strip_tags($v), ENT_QUOTES, 'UTF-8');
944                         
945                         if ($simple) {
946                                 $input = preg_replace
947                                                 ("/(\[\?\])$term(\[\/\?\])/i",
948                                                 '<a href="'.$simple.'glossary.html#'.urlencode($original_term).'" target="body" class="at-term">\\2</a>',
949                                                 $input);
950                         } else {
951                                 $input = preg_replace
952                                                 ("/(\[\?\])".$term."(\[\/\?\])/i",
953                                                 '<a class="tooltip" href="'.$_base_path.'mods/_core/glossary/index.php?g_cid='.$_SESSION['s_cid'].htmlentities(SEP).'w='.urlencode($original_term).'#term" title="'.htmlentities_utf8($original_term).': '.$def.'">\\2</a>',$input);
954                         }
955                 }
956         } else if (!$user_glossary) {
957                 $input = str_replace(array('[?]','[/?]'), '', $input);
958         }
959         
960         $input = str_replace('CONTENT_DIR', '', $input);
961
962         if (isset($_config['latex_server']) && $_config['latex_server']) {
963                 $input = preg_replace('/\[tex\](.*?)\[\/tex\]/sie', "'<img src=\"'.\$_config['latex_server'].rawurlencode('$1').'\" align=\"middle\" alt=\"'.'$1'.'\" title=\"'.'$1'.'\">'", $input);
964         }
965
966         if ($html) {
967                 $x = apply_customized_format(format_final_output($input, false));
968                 return $x;
969         }
970
971 // the following has been taken out for this: 
972 // http://atutor.ca/atutor/mantis/view.php?id=4593
973 // @date Oct 18, 2010
974 //      $output = apply_customized_format(format_final_output($input));
975     $output = $input;
976
977         $output = '<p>'.$output.'</p>';
978         return $output;
979 }
980
981 function get_content_table($content)
982 {
983         preg_match_all("/<(h[\d]+)[^>]*>(.*)<\/(\s*)\\1(\s*)>/i", $content, $found_headers, PREG_SET_ORDER);
984         
985         if (count($found_headers) == 0) return array("", $content);
986         else
987         {
988                 $num_of_headers = 0;
989
990                 for ($i = 0; $i < count($found_headers); $i++)
991                 {
992                         $div_id = "_header_" . $num_of_headers++;
993                         
994                         if ($i == 0)
995                         {
996                                 $content_table = "<div id=\"toc\">\n<fieldset class=\"toc\"><legend>". _AT("table_of_contents")."</legend>\n";
997                         }
998
999                         $content = str_replace($found_headers[$i][0], '<div id="'.$div_id.'">'.$found_headers[$i][0].'</div>', $content);
1000                         $content_table .= '<a href="'.$_SERVER["REQUEST_URI"].'#'.$div_id.'" class="'.$found_headers[$i][1].'">'. $found_headers[$i][2]."</a>\n";
1001
1002                         if ($i == count($found_headers) - 1)
1003                         {
1004                                 $content_table .= "</fieldset></div><br />";
1005                         }
1006                 }
1007                 return array($content_table, $content);
1008         }
1009 }
1010
1011 function find_terms($find_text) {
1012         preg_match_all("/(\[\?\])(.[^\?]*)(\[\/\?\])/i", $find_text, $found_terms, PREG_PATTERN_ORDER);
1013         return $found_terms;
1014 }
1015
1016 /***********************************************************************
1017         @See /include/Classes/Message/Message.class.php
1018         Jacek Materna
1019 */
1020
1021 /**
1022 * Take a code as input and grab its language specific message. Also cache the resulting 
1023 * message. Return the message. Same as get_message but key value in cache is string
1024 * @access  public
1025 * @param   string $codes        Message Code to translate - > 'term' field in DB
1026 * @return  string                       The translated language specific message for code $code
1027 * @author  Jacek Materna
1028 */
1029 function getTranslatedCodeStr($codes) {
1030         
1031         /* this is where we want to get the msgs from the database inside a static variable */
1032         global $_cache_msgs_new;
1033         static $_msgs_new;
1034
1035         if (!isset($_msgs_new)) {
1036                 if ( !($lang_et = cache(120, 'msgs_new', $_SESSION['lang'])) ) {
1037                         global $db, $_base_path;
1038
1039                         $parent = Language::getParentCode($_SESSION['lang']);
1040
1041                         /* get $_msgs_new from the DB */
1042                         $sql    = 'SELECT * FROM '.TABLE_PREFIX.'language_text WHERE variable="_msgs" AND (language_code="'.$_SESSION['lang'].'" OR language_code="'.$parent.'")';
1043                         $result = @mysql_query($sql, $db);
1044                         $i = 1;
1045                         while ($row = @mysql_fetch_assoc($result)) {
1046                                 // do not cache key as a digit (no contstant(), use string)
1047                                 $_cache_msgs_new[$row['term']] = str_replace('SITE_URL/', $_base_path, $row['text']);
1048                                 if (AT_DEVEL) {
1049                                         $_cache_msgs_new[$row['term']] .= ' <small><small>('.$row['term'].')</small></small>';
1050                                 }
1051                         }
1052
1053                         cache_variable('_cache_msgs_new');
1054                         endcache(true, false);
1055                 }
1056                 $_msgs_new = $_cache_msgs_new;
1057         }
1058
1059         if (is_array($codes)) {
1060                 /* this is an array with terms to replace */            
1061                 $code           = array_shift($codes);
1062
1063                 $message        = $_msgs_new[$code];
1064                 $terms          = $codes;
1065
1066                 /* replace the tokens with the terms */
1067                 $message        = vsprintf($message, $terms);
1068
1069         } else {
1070                 $message = $_msgs_new[$codes];
1071
1072                 if ($message == '') {
1073                         /* the language for this msg is missing: */
1074                 
1075                         $sql    = 'SELECT * FROM '.TABLE_PREFIX.'language_text WHERE variable="_msgs"';
1076                         $result = @mysql_query($sql, $db);
1077                         $i = 1;
1078                         while ($row = @mysql_fetch_assoc($result)) {
1079                                 if (($row['term']) === $codes) {
1080                                         $message = '['.$row['term'].']';
1081                                         break;
1082                                 }
1083                         }
1084                 }
1085                 $code = $codes;
1086         }
1087         return $message;
1088 }
1089
1090 function html_get_list($array) {
1091         $list = '';
1092         foreach ($array as $value) {
1093                 $list .= '<li>'.$value.'</li>';
1094         }
1095         return $list;
1096 }
1097
1098 /**
1099  * print_paginator
1100  *
1101  * print out list of page links
1102  */
1103 function print_paginator($current_page, $num_rows, $request_args, $rows_per_page = 50, $window = 5) {
1104         $num_pages = ceil($num_rows / $rows_per_page);
1105         $request_args = '?'.$request_args;
1106
1107     if ($num_rows) {
1108                 echo '<div class="paging">';
1109             echo '<ul>';
1110                 
1111                 $i=max($current_page-$window - max($window-$num_pages+$current_page,0), 1);
1112
1113                 if ($i > 1) {
1114                         echo '<li><a href="'.$_SERVER['PHP_SELF'].$request_args.htmlspecialchars(SEP).'p=1" title="'._AT('page').' 1">1</a></li>';
1115                         if ($i > 2) {
1116                         echo '<li>&hellip;</li>';
1117                         }
1118                 }
1119
1120                 for ($i; $i<= min($current_page+$window -min($current_page-$window,0),$num_pages); $i++) {
1121                         if ($current_page == $i) {
1122                                 echo '<li><a href="'.$_SERVER['PHP_SELF'].$request_args.htmlspecialchars(SEP).'p='.$i.'" class="current" title="'._AT('page').' '. $current_page.'"><em>'.$current_page.'</em></a></li>';
1123                         } else {
1124                                 echo '<li><a href="'.$_SERVER['PHP_SELF'].$request_args.htmlspecialchars(SEP).'p='.$i.'" title="'._AT('page').' '. $i.'">'.$i.'</a></li>';
1125                         }
1126                 }
1127         if ($i <= $num_pages) {
1128                         if ($i < $num_pages) {
1129                         echo '<li>&hellip;</li>';
1130                 }
1131                         echo '<li><a href="'.$_SERVER['PHP_SELF'].$request_args.htmlspecialchars(SEP).'p='.$num_pages.'" title="'._AT('page').' '. $num_pages.'">'.$num_pages.'</a></li>';
1132                 }
1133                 echo '</ul>';
1134                 echo '</div>';
1135         }
1136 }
1137
1138 /**
1139 * Replace or append source object with alternatives according to user's preferences
1140 * @access       public
1141 * @param        $cid:                           content id.
1142 * @param        $content:                       the original content page ($content_row['text'], from content.php).
1143 * @param    $info_only:         boolean. Default value is "false". When it's "true", returns an array of 4 values:
1144 *                               $has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative
1145 * @param    $only_on_secondary_type: Default value is "". Accept one of the values: 1(auditory), 2(sign_language), 3(text), 4(visual)
1146 *                               When the value is given, ignore the alternative preference settings and only replace/append 
1147 *                               (replace or append is still from session preference) the objects with the alternatives with
1148 *                               the given alternative types.
1149 * @return       string                          $content: the content page with the appropriated resources.
1150 * @see          $db                             from include/vitals.inc.php
1151 * @author       Cindy Qi Li
1152 */
1153 function provide_alternatives($cid, $content, $info_only = false, $only_on_secondary_type = 0){
1154         global $db;
1155         
1156         $video_exts = array("mpg", "avi", "wmv", "mov", "swf", "mp4", "flv");
1157         
1158         $audio_exts = array("mp3", "wav", "ogg", "mid");
1159         $audio_width = 425;
1160         $audio_height = 27;
1161         
1162         $txt_exts = array("txt", "html", "htm");
1163         $image_exts = array("gif", "bmp", "png", "jpg", "jpeg", "png", "tif");
1164         $only_on_secondary_type = intval($only_on_secondary_type);
1165         
1166         // intialize the 4 returned values when $info_only is on
1167         if ($info_only)
1168         {
1169                 $has_text_alternative = false;
1170                 $has_audio_alternative = false;
1171                 $has_visual_alternative = false;
1172                 $has_sign_lang_alternative = false;
1173         }
1174
1175         if (!$info_only && !$only_on_secondary_type && 
1176             ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==0) && 
1177             ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_AUDIO']==0) && 
1178             ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_VISUAL']==0)) 
1179         {
1180                 //No user's preferences related to content format are declared
1181                 if (!$info_only) {
1182                         return $content;
1183                 } else {
1184                         return array($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative);
1185                 }
1186         }
1187         
1188         // get all relations between primary resources and their alternatives
1189         $sql = "SELECT DISTINCT c.content_path, pr.resource, prt.type_id primary_type, 
1190                        sr.secondary_resource, srt.type_id secondary_type
1191                   FROM ".TABLE_PREFIX."primary_resources pr, ".
1192                          TABLE_PREFIX."primary_resources_types prt,".
1193                          TABLE_PREFIX."secondary_resources sr,".
1194                          TABLE_PREFIX."secondary_resources_types srt,".
1195                          TABLE_PREFIX."content c
1196                  WHERE pr.content_id=".$cid."
1197                        AND pr.primary_resource_id = prt.primary_resource_id
1198                        AND pr.primary_resource_id = sr.primary_resource_id
1199                        AND sr.language_code='".$_SESSION['lang']."'
1200                        AND sr.secondary_resource_id = srt.secondary_resource_id
1201                    AND pr.content_id = c.content_id";
1202         if ($only_on_secondary_type > 0) {
1203                 $sql .= " AND srt.type_id=".$only_on_secondary_type;
1204         }
1205         $sql .= " ORDER BY pr.primary_resource_id, prt.type_id";
1206         
1207         $result = mysql_query($sql, $db);
1208
1209         if (mysql_num_rows($result) == 0) {
1210                 if (!$info_only) {
1211                         return $content;
1212                 } else {
1213                         return array($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative);
1214                 }
1215         }
1216         
1217         $primary_resource_names = array();
1218         while ($row = mysql_fetch_assoc($result)) {
1219                 // if the primary resource is defined with multiple resource type,
1220                 // the primary resource would be replaced/appended multiple times.
1221                 // This is what we want at applying alternatives by default, but
1222                 // not when only one secondary type is chosen to apply.
1223                 // This fix is to remove the duplicates on the same primary resource.
1224                 // A dilemma of this fix is, for example, if the primary resource type
1225                 // is "text" and "visual", but
1226                 // $_SESSION['prefs']['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE'] == 'replace'
1227                 // $_SESSION['prefs']['PREF_ALT_TO_VISUAL_APPEND_OR_REPLACE'] == 'append'
1228                 // so, should replace happen or append happen? With this fix, whichever
1229                 // the first in the sql return gets preserved in the array and processed.
1230                 // The further improvement is requried to keep rows based on the selected
1231                 // secondary type (http://www.atutor.ca/atutor/mantis/view.php?id=4598). 
1232                 if ($only_on_secondary_type > 0) {
1233                         if (in_array($row['resource'], $primary_resource_names)) {
1234                                 continue;
1235                         } else {
1236                                 $primary_resource_names[] = $row['resource'];
1237                         }
1238                 }
1239                 $alternative_rows[] = $row;
1240                 
1241                 $youtube_playURL = convert_youtube_watchURL_to_playURL($row['resource']);
1242                 
1243                 if ($row['resource'] <> $youtube_playURL) {
1244                         $row['resource'] = $youtube_playURL;
1245                         $alternative_rows[] = $row;
1246                 }
1247         }
1248
1249         foreach ($alternative_rows as $row) 
1250         {
1251                 if ($info_only || $only_on_secondary_type ||
1252                     ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==1 && $row['primary_type']==3 &&
1253                     ($_SESSION['prefs']['PREF_ALT_TO_TEXT']=="audio" && $row['secondary_type']==1 || 
1254                      $_SESSION['prefs']['PREF_ALT_TO_TEXT']=="visual" && $row['secondary_type']==4 || 
1255                      $_SESSION['prefs']['PREF_ALT_TO_TEXT']=="sign_lang" && $row['secondary_type']==2)) ||
1256                      
1257                      ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_AUDIO']==1 && $row['primary_type']==1 &&
1258                      ($_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="visual" && $row['secondary_type']==4 || 
1259                       $_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="text" && $row['secondary_type']==3 || 
1260                       $_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="sign_lang" && $row['secondary_type']==2)) ||
1261                       
1262                      ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_VISUAL']==1 && $row['primary_type']==4 &&
1263                      ($_SESSION['prefs']['PREF_ALT_TO_VISUAL']=="audio" && $row['secondary_type']==1 || 
1264                       $_SESSION['prefs']['PREF_ALT_TO_VISUAL']=="text" && $row['secondary_type']==3 || 
1265                       $_SESSION['prefs']['PREF_ALT_TO_VISUAL']=="sign_lang" && $row['secondary_type']==2))
1266                     )
1267                 {
1268                         $ext = substr($row['secondary_resource'], strrpos($row['secondary_resource'], '.')+1);
1269                         
1270                         // alternative is video or a youtube url
1271                         if (in_array($ext, $video_exts) || in_array($ext, $audio_exts) || 
1272                             preg_match("/http:\/\/.*youtube.com\/watch.*/", $row['secondary_resource'])) {
1273                             if (in_array($ext, $audio_exts)) {
1274                                 // display audio medias in a smaller width/height (425 * 27)
1275                                 // A hack for now to handle audio media player size
1276                                 $target = '[media|'.$audio_width.'|'.$audio_height.']'.$row['secondary_resource'].'[/media]';
1277                             } else {
1278                                 // use default media size for video medias
1279                                 $target = '[media]'.$row['secondary_resource'].'[/media]';
1280                             }
1281                         }
1282                         // a text primary to be replaced by a visual alternative 
1283                         else if (in_array($ext, $txt_exts))
1284                         {
1285                                 if ($row['content_path'] <> '') 
1286                                         $file_location = $row['content_path'].'/'.$row['secondary_resource'];
1287                                 else 
1288                                         $file_location = $row['secondary_resource'];
1289                                 
1290                                 $file = AT_CONTENT_DIR.$_SESSION['course_id'] . '/'.$file_location;
1291                                 $target = '<br />'.file_get_contents($file);
1292                                 
1293                                 // check whether html file
1294                                 if (preg_match('/.*\<html.*\<\/html\>.*/s', $target))
1295                                 { // is a html file, use iframe to display
1296                                         // get real path to the text file
1297                                         if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) {
1298                                                 $course_base_href = 'get.php/';
1299                                         } else {
1300                                                 $course_base_href = 'content/' . $_SESSION['course_id'] . '/';
1301                                         }
1302         
1303                                         $file = AT_BASE_HREF . $course_base_href.$file_location;
1304                                                 
1305                                         $target = '<iframe width="100%" frameborder="0" class="autoHeight" scrolling="auto" src="'.$file.'"></iframe>';
1306                                 }
1307                                 else
1308                                 { // is a text file, insert/replace into content
1309                                         $target = nl2br($target);
1310                                 }
1311                         } 
1312                         else if (in_array($ext, $image_exts))
1313                                 $target = '<img border="0" alt="'._AT('alternate_text').'" src="'.$row['secondary_resource'].'"/>';
1314                         // otherwise
1315                         else
1316                                 $target = '<p><a href="'.$row['secondary_resource'].'">'.$row['secondary_resource'].'</a></p>';
1317
1318                         // replace or append the target alternative to the source
1319                         if (($row['primary_type']==3 && $_SESSION['prefs']['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE'] == 'replace') ||
1320                                 ($row['primary_type']==1 && $_SESSION['prefs']['PREF_ALT_TO_AUDIO_APPEND_OR_REPLACE']=='replace') ||
1321                                 ($row['primary_type']==4 && $_SESSION['prefs']['PREF_ALT_TO_VISUAL_APPEND_OR_REPLACE']=='replace'))
1322                                 $pattern_replace_to = '${1}'."\n".$target."\n".'${3}';
1323                         else
1324                                 $pattern_replace_to = '${1}${2}'."<br /><br />\n".$target."\n".'${3}';
1325
1326                         // *** Alternative replace/append starts from here ***
1327                         $processed = false;    // one primary resource is only processed once 
1328                         
1329                         // append/replace target alternative to [media]source[/media]
1330                         if (!$processed && preg_match("/".preg_quote("[media").".*".preg_quote("]".$row['resource']."[/media]", "/")."/sU", $content))
1331                         {
1332                                 $processed = true;
1333                                 if (!$info_only) {
1334                                         $content = preg_replace("/(.*)(".preg_quote("[media").".*".preg_quote("]".$row['resource']."[/media]", "/").")(.*)/sU", 
1335                                      $pattern_replace_to, $content);
1336                                 } else {
1337                                         if ($row['secondary_type'] == 1) $has_audio_alternative = true;
1338                                         if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true;
1339                                         if ($row['secondary_type'] == 3) $has_text_alternative = true;
1340                                         if ($row['secondary_type'] == 4) $has_visual_alternative = true;
1341                                 }
1342                         }
1343                         
1344                         // append/replace target alternative to <img ... src="source" ...></a>
1345                         if (!$processed && preg_match("/\<img.*src=\"".preg_quote($row['resource'], "/")."\".*\/\>/sU", $content))
1346                         {
1347                                 $processed = true;
1348                                 if (!$info_only) {
1349                                         $content = preg_replace("/(.*)(\<img.*src=\"".preg_quote($row['resource'], "/")."\".*\/\>)(.*)/sU", 
1350                                                 $pattern_replace_to, $content);
1351                                 } else {
1352                                         if ($row['secondary_type'] == 1) $has_audio_alternative = true;
1353                                         if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true;
1354                                         if ($row['secondary_type'] == 3) $has_text_alternative = true;
1355                                         if ($row['secondary_type'] == 4) $has_visual_alternative = true;
1356                                 }
1357                         }
1358                         
1359                         // append/replace target alternative to <object ... source ...></object>
1360                         if (!$processed && preg_match("/\<object.*".preg_quote($row['resource'], "/").".*\<\/object\>/sU", $content))
1361                         {
1362                                 $processed = true;
1363                                 if (!$info_only) {
1364                                         $content = preg_replace("/(.*)(\<object.*".preg_quote($row['resource'], "/").".*\<\/object\>)(.*)/sU", 
1365                                                 $pattern_replace_to, $content);
1366                                 } else {
1367                                         if ($row['secondary_type'] == 1) $has_audio_alternative = true;
1368                                         if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true;
1369                                         if ($row['secondary_type'] == 3) $has_text_alternative = true;
1370                                         if ($row['secondary_type'] == 4) $has_visual_alternative = true;
1371                                 }
1372                         }
1373                         
1374                         // append/replace target alternative to <a>...source...</a> or <a ...source...>...</a>
1375                         // skip this "if" when the source object has been processed in aboved <img> tag
1376                         if (!$processed && preg_match("/\<a.*".preg_quote($row['resource'], "/").".*\<\/a\>/sU", $content))
1377                         {
1378                                 $processed = true;
1379                                 if (!$info_only) {
1380                                         $content = preg_replace("/(.*)(\<a.*".preg_quote($row['resource'], "/").".*\<\/a\>)(.*)/sU", 
1381                                                 $pattern_replace_to, $content);
1382                                 } else {
1383                                         if ($row['secondary_type'] == 1) $has_audio_alternative = true;
1384                                         if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true;
1385                                         if ($row['secondary_type'] == 3) $has_text_alternative = true;
1386                                         if ($row['secondary_type'] == 4) $has_visual_alternative = true;
1387                                 }
1388                         }
1389                         
1390                         // append/replace target alternative to <embed ... source ...>
1391                         if (!$processed && preg_match("/\<embed.*".preg_quote($row['resource'], "/").".*\>/sU", $content))
1392                         {
1393                                 $processed = true;
1394                                 if (!$info_only) {
1395                                         $content = preg_replace("/(.*)(\<embed.*".preg_quote($row['resource'], "/").".*\>)(.*)/sU", 
1396                                                 $pattern_replace_to, $content);
1397                                 } else {
1398                                         if ($row['secondary_type'] == 1) $has_audio_alternative = true;
1399                                         if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true;
1400                                         if ($row['secondary_type'] == 3) $has_text_alternative = true;
1401                                         if ($row['secondary_type'] == 4) $has_visual_alternative = true;
1402                                 }
1403                         }
1404                 }
1405         }
1406         
1407         if (!$info_only) {
1408                 return $content;
1409         } else {
1410                 return array($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative);
1411         }
1412 }       
1413                 
1414 /**
1415 * apply_timezone
1416 * converts a unix timestamp into another UNIX timestamp with timezone offset added up.
1417 * Adds the user's timezone offset, then converts back to a MYSQL timestamp
1418 * Available both as a system config option, and a user preference, if both are set
1419 * they are added together
1420 * @param   date  MYSQL timestamp.
1421 * @return  date  MYSQL timestamp plus user's and/or system's timezone offset.
1422 * @author  Greg Gay  .
1423 */
1424 function apply_timezone($timestamp){
1425         global $_config;
1426 /*
1427         if($_config['time_zone']){
1428                 $timestamp = ($timestamp + ($_config['time_zone']*3600));
1429         }
1430 */
1431
1432         if(isset($_SESSION['prefs']['PREF_TIMEZONE'])){
1433                 $timestamp = ($timestamp + ($_SESSION['prefs']['PREF_TIMEZONE']*3600));
1434         }
1435
1436         return $timestamp;
1437 }
1438 ?>