4781: Check if theme paths are relative, if so, silently set it back to default and...
[atutor.git] / docs / include / vitals.inc.php
1 <?php
2 /************************************************************************/
3 /* ATutor                                                               */
4 /************************************************************************/
5 /* Copyright (c) 2002 - 2009                                            */
6 /* Inclusive Design Institute                                           */
7 /*                                                                      */
8 /* This program is free software. You can redistribute it and/or        */
9 /* modify it under the terms of the GNU General Public License          */
10 /* as published by the Free Software Foundation.                        */
11 /************************************************************************/
12 // $Id$
13
14 if (!defined('AT_INCLUDE_PATH')) { exit; }
15
16 define('AT_DEVEL', 1);
17 define('AT_ERROR_REPORTING', E_ALL ^ E_NOTICE); // default is E_ALL ^ E_NOTICE, use E_ALL or E_ALL + E_STRICT for developing
18 define('AT_DEVEL_TRANSLATE', 0);
19
20 // Emulate register_globals off. src: http://php.net/manual/en/faq.misc.php#faq.misc.registerglobals
21 function unregister_GLOBALS() {
22    if (!ini_get('register_globals')) { return; }
23
24    // Might want to change this perhaps to a nicer error
25    if (isset($_REQUEST['GLOBALS'])) { die('GLOBALS overwrite attempt detected'); }
26
27    // Variables that shouldn't be unset
28    $noUnset = array('GLOBALS','_GET','_POST','_COOKIE','_REQUEST','_SERVER','_ENV', '_FILES');
29    $input = array_merge($_GET,$_POST,$_COOKIE,$_SERVER,$_ENV,$_FILES,isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
30   
31    foreach ($input as $k => $v) {
32        if (!in_array($k, $noUnset) && isset($GLOBALS[$k])) { unset($GLOBALS[$k]); }
33    }
34 }
35
36 //functions for properly escaping input strings
37 function my_add_null_slashes( $string ) {
38     return mysql_real_escape_string(stripslashes($string));
39 }
40 function my_null_slashes($string) {
41     return $string;
42 }
43
44 if ( get_magic_quotes_gpc() == 1 ) {
45     $addslashes   = 'my_add_null_slashes';
46     $stripslashes = 'stripslashes';
47 } else {
48     $addslashes   = 'mysql_real_escape_string';
49     $stripslashes = 'my_null_slashes';
50 }
51
52 function regenerate_session($reload = false)
53 {
54         if(!isset($_SESSION['IPaddress']) || $reload)
55                 $_SESSION['IPaddress'] = $_SERVER['REMOTE_ADDR'];
56
57         if(!isset($_SESSION['userAgent']) || $reload)
58                 $_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
59
60         $session_values = $_SESSION;
61
62         // Set current session to expire in 10 seconds
63         $_SESSION['OBSOLETE'] = true;
64         $_SESSION['EXPIRES'] = time() + 10;
65
66         // Create new session without destroying the old one
67         session_regenerate_id(false);
68
69         // Grab current session ID and close both sessions to allow other scripts to use them
70         $newSession = session_id();
71         session_write_close();
72
73         // Set session ID to the new one, and start it back up again
74         session_id($newSession);
75         session_start();
76
77         $_SESSION = $session_values; 
78 }
79
80 function check_session()
81 {
82         if($_SESSION['OBSOLETE'] && ($_SESSION['EXPIRES'] < time())) {
83                 return false;
84         }
85                     
86         if($_SESSION['IPaddress'] != $_SERVER['REMOTE_ADDR']) {
87                 return false;
88         }
89                     
90         if($_SESSION['userAgent'] != $_SERVER['HTTP_USER_AGENT']) {
91                 return false;
92         }
93                     
94         if(!$_SESSION['OBSOLETE']) {
95                 regenerate_session();
96         }
97         return true;
98 }
99
100 /*
101  * structure of this document (in order):
102  *
103  * 0. load config.inc.php
104  * 1. load constants
105  * 2. initialize db connection and populate $_config
106  * 3. initialize session
107  * 4. enable output compression
108  * 5. validate login user
109  * 6. load language
110  * 7. load cache/ContentManagement/output/Savant/Message libraries
111  ***/
112
113 /**** 0. start system configuration options block ****/
114         //set the timezone, php 5.3+ problem. http://atutor.ca/atutor/mantis/view.php?id=4409
115         date_default_timezone_set('UTC');
116
117         error_reporting(0);
118         if (!defined('AT_REDIRECT_LOADED')){
119                 include_once(AT_INCLUDE_PATH.'config.inc.php');
120         }
121         error_reporting(AT_ERROR_REPORTING);
122
123         if (!defined('AT_INSTALL') || !AT_INSTALL) {
124                 header('Cache-Control: no-store, no-cache, must-revalidate');
125                 header('Pragma: no-cache');
126
127                 $relative_path = substr(AT_INCLUDE_PATH, 0, -strlen('include/'));
128                 header('Location: ' . $relative_path . 'install/not_installed.php');
129                 exit;
130         }
131 /*** end system config block ***/
132
133 /*** 1. constants ***/
134 if (!defined('AT_REDIRECT_LOADED')){
135         require_once(AT_INCLUDE_PATH.'lib/constants.inc.php');
136 }
137
138 /*** 2. initialize db connection and populate $_config ***/
139
140 if (!defined('AT_REDIRECT_LOADED')){
141         require_once(AT_INCLUDE_PATH.'lib/mysql_connect.inc.php');
142 }
143
144 /* get config variables. if they're not in the db then it uses the installation default value in constants.inc.php */
145 $sql    = "SELECT * FROM ".TABLE_PREFIX."config";
146 $result = mysql_query($sql, $db);
147 while ($row = mysql_fetch_assoc($result)) { 
148         $_config[$row['name']] = $row['value'];
149 }
150
151 /***** 3. start session initilization block *****/
152 if (headers_sent()) {
153         require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php');
154         $err = new ErrorHandler();
155         trigger_error('VITAL#<br /><br /><code><strong>An error occurred. Output sent before it should have. Please correct the above error(s).' . '</strong></code><br /><hr /><br />', E_USER_ERROR);
156 }
157
158 @set_time_limit(0);
159 @ini_set('session.gc_maxlifetime', '36000'); /* 10 hours */
160 @session_cache_limiter('private, must-revalidate');
161 session_name('ATutorID');
162 error_reporting(AT_ERROR_REPORTING);
163
164 if (headers_sent()) {
165         require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php');
166         $err = new ErrorHandler();
167         trigger_error('VITAL#<br /><code><strong>Headers already sent. ' .
168                                         'Cannot initialise session.</strong></code><br /><hr /><br />', E_USER_ERROR);
169         exit;
170 }
171
172 $isHttps = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
173            ? false
174            : true;
175 ob_start();
176 session_set_cookie_params(0, $_config["session_path"], "", $isHttps);
177 session_start();
178
179 // Regenerate session id at every page refresh to prevent CSRF
180 $valid_session = true;
181 if (count($_SESSION) == 0) {
182         regenerate_session();
183 } else {
184         $valid_session = check_session();
185 }
186
187 $str = ob_get_contents();
188 ob_end_clean();
189 unregister_GLOBALS();
190
191 // Re-direct to login page at a potential session hijack
192 if (!$valid_session) {
193         $_SESSION = array();
194         header('Location: '.AT_BASE_HREF.'login.php');
195         exit;
196 }
197
198 if ($str) {
199         require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php');
200         $err = new ErrorHandler();
201         trigger_error('VITAL#<br /><code><strong>Error initializing session. ' .
202                                         'Please varify that session.save_path is correctly set in your php.ini file ' .
203                                         'and the directory exists.</strong></code><br /><hr /><br />', E_USER_ERROR);
204         exit;
205 }
206 /***** end session initilization block ****/
207
208 /**** 4. enable output compression, if it isn't already enabled: ****/
209 if ((@ini_get('output_handler') == '') && (@ini_get('zlib.output_handler') == '')) {
210         @ini_set('zlib.output_compression', 1);
211 }
212
213 /**** 5. validate login user ****/
214 if (!isset($_SESSION['course_id']) && !isset($_SESSION['valid_user']) && (!isset($_user_location) || $_user_location != 'public') && !isset($_pretty_url_course_id)) {
215         if (isset($in_get) && $in_get && (($pos = strpos($_SERVER['PHP_SELF'], 'get.php/')) !== FALSE)) {
216                 $redirect = substr($_SERVER['PHP_SELF'], 0, $pos) . 'login.php';
217                 header('Location: '.$redirect);
218                 exit;
219         }
220
221         header('Location: '.AT_BASE_HREF.'login.php');
222         exit;
223 }
224
225 /* following is added as a transition period and backwards compatability: */
226 define('EMAIL',                     $_config['contact_email']);
227 define('EMAIL_NOTIFY',              $_config['email_notification']);
228 define('ALLOW_INSTRUCTOR_REQUESTS', $_config['allow_instructor_requests']);
229 define('AUTO_APPROVE_INSTRUCTORS',  $_config['auto_approve_instructors']);
230 define('SITE_NAME',                 $_config['site_name']);
231 define('HOME_URL',                  $_config['home_url']);
232 define('DEFAULT_LANGUAGE',          $_config['default_language']);
233 define('CACHE_DIR',                 $_config['cache_dir']);
234 define('AT_ENABLE_CATEGORY_THEMES', $_config['theme_categories']);
235 define('AT_COURSE_BACKUPS',         $_config['course_backups']);
236 define('AT_EMAIL_CONFIRMATION',     $_config['email_confirmation']);
237 define('AT_MASTER_LIST',            $_config['master_list']);
238 $MaxFileSize       = $_config['max_file_size']; 
239 $MaxCourseSize     = $_config['max_course_size'];
240 $MaxCourseFloat    = $_config['max_course_float'];
241 $IllegalExtentions = explode('|',$_config['illegal_extentions']);
242 define('AT_DEFAULT_PREFS',  isset($_config['prefs_default']) ? $_config['prefs_default'] : '');
243 $_config['home_defaults'] .= (isset($_config['home_defaults_2']) ? $_config['home_defaults_2'] : '');
244 $_config['main_defaults'] .= (isset($_config['main_defaults_2']) ? $_config['main_defaults_2'] : '');
245
246 if ($_config['time_zone']) {
247         //$sql = "SET time_zone='{$_config['time_zone']}'";
248         //mysql_query($sql, $db);
249
250         if (function_exists('date_default_timezone_set')) {
251         foreach($utc_timezones as $zone){
252
253                 if($zone[1] ==  $_config['time_zone']){
254                 $zone_name = $zone[2];
255                 break;
256                 }
257         }
258                 date_default_timezone_set($zone_name);
259         } else {
260                 @putenv("TZ={$_config['time_zone']}");
261         }
262 }
263 /***** 6. load language *****/
264 // set current language
265 require(AT_INCLUDE_PATH . '../mods/_core/languages/classes/LanguageManager.class.php');
266 $languageManager = new LanguageManager();
267
268 $myLang =& $languageManager->getMyLanguage();
269
270 if ($myLang === FALSE) {
271         echo 'There are no languages installed!';
272         exit;
273 }
274 $myLang->saveToSession();
275 if (isset($_GET['lang']) && $_SESSION['valid_user']) {
276         if ($_SESSION['course_id'] == -1) {
277                 $myLang->saveToPreferences($_SESSION['login'], 1);      //1 for admin                   
278         } else {
279                 $myLang->saveToPreferences($_SESSION['member_id'], 0);  //0 for non-admin
280         }
281 }
282 $myLang->sendContentTypeHeader();
283
284 /* set right-to-left language */
285 $rtl = '';
286 if ($myLang->isRTL()) {
287         $rtl = 'rtl_'; /* basically the prefix to a rtl variant directory/filename. eg. rtl_tree */
288 }
289 /***** end language block ****/
290
291 /* 7. load common libraries */
292 require(AT_INCLUDE_PATH.'classes/ContentManager.class.php');  /* content management class */
293 require_once(AT_INCLUDE_PATH.'lib/output.inc.php');           /* output functions */
294 if (!(defined('AT_REDIRECT_LOADED'))){
295         require_once(AT_INCLUDE_PATH . 'classes/UrlRewrite/UrlParser.class.php');       /* pretty url tool */
296 }
297 require(AT_INCLUDE_PATH.'classes/Savant2/Savant2.php');       /* for the theme and template management */
298
299 // set default template paths:
300 $savant = new Savant2();
301 $savant->addPath('template', AT_INCLUDE_PATH . '../themes/default/');
302
303 //if user has requested theme change, make the change here
304 if (($_POST['theme'] || $_POST['mobile_theme']) && $_POST['submit']) {
305     //http://atutor.ca/atutor/mantis/view.php?id=4781
306     //Themes should be in the same folder, disallow '../'
307     $newTheme = str_replace("../", "", $_POST['theme']);
308     $newMobileTheme = str_replace("../", "", $_POST['mobile_theme']);
309     if ($newTheme != $_POST['theme'] || $newMobileTheme != $_POST['mobile_theme']) {
310         header('Location:'.AT_BASE_HREF.'users/preferences.php');
311             exit;
312     }
313     
314     $_SESSION['prefs']['PREF_THEME'] = $addslashes($_POST['theme']);
315     $_SESSION['prefs']['PREF_MOBILE_THEME'] = $addslashes($_POST['mobile_theme']);
316 } else if ($_POST['set_default']) {
317     $_SESSION['prefs']['PREF_THEME'] = 'default';
318     $_SESSION['prefs']['PREF_MOBILE_THEME'] = 'mobile';
319 }
320
321 // Reset PREF_THEME when:
322 // 1. If PREF_THEME is not set 
323 // 2. The request is from the mobile device but PREF_THEME is not a mobile theme 
324 if (!isset($_SESSION['prefs']['PREF_THEME']) ||
325     $_SESSION['prefs']['PREF_THEME'] == "" ||
326     (is_mobile_device() && !is_mobile_theme($_SESSION['prefs']['PREF_THEME']))) {
327         // get default
328         $default_theme = get_default_theme();
329         
330         $_SESSION['prefs']['PREF_THEME'] = $default_theme['dir_name'];
331 }
332
333 if (!is_dir(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME']) || $_SESSION['prefs']['PREF_THEME'] == '') {
334         $_SESSION['prefs']['PREF_THEME'] = get_system_default_theme();
335 }
336
337 // use "mobile" theme for mobile devices. For now, there's only one mobile theme and it's hardcoded.
338 // When more mobile themes come in, this should be changed.
339 if (isset($_SESSION['prefs']['PREF_THEME']) && file_exists(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME']) && isset($_SESSION['valid_user']) && $_SESSION['valid_user']) {
340         if ($_SESSION['course_id'] == -1) {
341                 if ($_SESSION['prefs']['PREF_THEME'] == '' || !is_dir(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'])) {
342                         $_SESSION['prefs']['PREF_THEME'] = get_system_default_theme();
343                 }
344         } else {
345                 //check if enabled
346                 $sql    = "SELECT status FROM ".TABLE_PREFIX."themes WHERE dir_name = '".$_SESSION['prefs']['PREF_THEME']."'";
347                 $result = mysql_query($sql, $db);
348                 $row = mysql_fetch_assoc($result);
349                 if ($row['status'] > 0) {
350                 } else {
351                         // get default
352                         $default_theme = get_default_theme();
353                         if (!is_dir(AT_INCLUDE_PATH . '../themes/' . $default_theme['dir_name'])) {
354                                 $default_theme = array('dir_name' => get_system_default_theme());
355                         }
356                         $_SESSION['prefs']['PREF_THEME'] = $default_theme['dir_name'];
357                 }
358         }
359 }
360
361 $savant->addPath('template', AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'] . '/');
362 require(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'] . '/theme.cfg.php');
363
364 require(AT_INCLUDE_PATH.'classes/Message/Message.class.php');
365 $msg = new Message($savant);
366
367 $contentManager = new ContentManager($db, isset($_SESSION['course_id']) ? $_SESSION['course_id'] : $_GET['p_course']);
368 $contentManager->initContent();
369
370 /**************************************************/
371 require(AT_INCLUDE_PATH.'phpCache/phpCache.inc.php'); // cache library
372 require(AT_INCLUDE_PATH.'lib/utf8.php');                        //UTF-8 multibyte library
373
374 if (!file_exists(AT_INCLUDE_PATH.'../sha-1factory.js')) {
375         require(AT_INCLUDE_PATH.'header.inc.php');
376         $msg->printErrors('MISSING_SHA1');
377         require(AT_INCLUDE_PATH.'footer.inc.php');
378         exit;
379 }
380
381 if (isset($_user_location) && ($_user_location == 'users') && $_SESSION['valid_user'] && ($_SESSION['course_id'] > 0)) {
382         $_SESSION['course_id'] = 0;
383 }
384
385 if ((!isset($_SESSION['course_id']) || $_SESSION['course_id'] == 0) && ($_user_location != 'users') && ($_user_location != 'prog') && !isset($_GET['h']) && ($_user_location != 'public') && (!isset($_pretty_url_course_id) || $_pretty_url_course_id == 0)) {
386         header('Location:'.AT_BASE_HREF.'users/index.php');
387         exit;
388 }
389 /* check if we are in the requested course, if not, bounce to it.
390  * @author harris, for pretty url, read AT_PRETTY_URL_HANDLER
391  */ 
392 if ((isset($_SESSION['course_id']) && isset($_pretty_url_course_id) && $_SESSION['course_id'] != $_pretty_url_course_id) ||
393         (isset($_pretty_url_course_id) && !isset($_SESSION['course_id']) && !isset($_REQUEST['ib']))) {
394
395         if($_config['pretty_url'] == 0){
396                 header('Location: '.AT_BASE_HREF.'bounce.php?course='.$_pretty_url_course_id.SEP.'pu='.$_SERVER['PATH_INFO'].urlencode('?'.$_SERVER['QUERY_STRING']));
397         } else {
398                 header('Location: '.AT_BASE_HREF.'bounce.php?course='.$_pretty_url_course_id.SEP.'pu='.$_SERVER['PATH_INFO']);
399         }
400         exit;
401 }
402
403    /**
404    * This function is used for printing variables into log file for debugging.
405    * @access  public
406    * @param   mixed $var        The variable to output
407    * @param   string $log       The location of the log file. If not provided, use the default one.
408    * @author  Cindy Qi Li
409    */
410 function debug_to_log($var, $log='') {
411         if (!defined('AT_DEVEL') || !AT_DEVEL) {
412                 return;
413         }
414         
415         if ($log == '') $log = AT_CONTENT_DIR. 'atutor.log';
416         $handle = fopen($log, 'a');
417         fwrite($handle, "\n\n");
418         fwrite($handle, date("F j, Y, g:i a"));
419         fwrite($handle, "\n");
420         fwrite($handle, var_export($var,1));
421         
422         fclose($handle);
423 }
424
425    /**
426    * This function is used for printing variables for debugging.
427    * @access  public
428    * @param   mixed $var        The variable to output
429    * @param   string $title     The name of the variable, or some mark-up identifier.
430    * @author  Joel Kronenberg
431    */
432 function debug($var, $title='') {
433         if (!defined('AT_DEVEL') || !AT_DEVEL) {
434                 return;
435         }
436         
437         echo '<pre style="border: 1px black solid; padding: 0px; margin: 10px;" title="debugging box">';
438         if ($title) {
439                 echo '<h4>'.$title.'</h4>';
440         }
441         
442         ob_start();
443         print_r($var);
444         $str = ob_get_contents();
445         ob_end_clean();
446
447         $str = str_replace('<', '&lt;', $str);
448
449         $str = str_replace('[', '<span style="color: red; font-weight: bold;">[', $str);
450         $str = str_replace(']', ']</span>', $str);
451         $str = str_replace('=>', '<span style="color: blue; font-weight: bold;">=></span>', $str);
452         $str = str_replace('Array', '<span style="color: purple; font-weight: bold;">Array</span>', $str);
453         echo $str;
454         echo '</pre>';
455 }
456
457 /********************************************************************/
458 /* the system course information                                                                        */
459 /* system_courses[course_id] = array(title, description, subject)       */
460 $system_courses = array();
461
462 // temporary set to a low number
463 $sql = 'SELECT * FROM '.TABLE_PREFIX.'courses ORDER BY title';
464 $result = mysql_query($sql, $db);
465 while ($row = mysql_fetch_assoc($result)) {
466         $course = $row['course_id'];
467         unset($row['course_id']);
468         $system_courses[$course] = $row;
469 }
470 /*                                                                                                                                      */
471 /********************************************************************/
472 // p_course is set when pretty url is on and guests access a public course. @see bounce.php
473 // First, santinize p_course
474 if (isset($_REQUEST['p_course'])) {
475         $_REQUEST['p_course'] = intval($_REQUEST['p_course']);
476 }
477
478 if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0 || $_REQUEST['p_course'] > 0) {
479         $sql = 'SELECT * FROM '.TABLE_PREFIX.'glossary 
480                  WHERE course_id='.($_SESSION['course_id']>0 ? $_SESSION['course_id'] : $_REQUEST['p_course']).' 
481                  ORDER BY word';
482         $result = mysql_query($sql, $db);
483         $glossary = array();
484         $glossary_ids = array();
485         while ($row_g = mysql_fetch_assoc($result)) {           
486                 $row_g['word'] = htmlspecialchars($row_g['word'], ENT_QUOTES, 'UTF-8');
487                 $glossary[$row_g['word']] = str_replace("'", "\'",$row_g['definition']);
488                 $glossary_ids[$row_g['word_id']] = $row_g['word'];
489
490                 /* a kludge to get the related id's for when editing content */
491                 /* it's ugly, but beats putting this query AGAIN on the edit_content.php page */
492                 if (isset($get_related_glossary)) {
493                         $glossary_ids_related[$row_g['word']] = $row_g['related_word_id'];
494                 }
495         }
496 }
497
498 function get_html_body($text) {
499         /* strip everything before <body> */
500         $start_pos      = strpos(strtolower($text), '<body');
501         if ($start_pos !== false) {
502                 $start_pos      += strlen('<body');
503                 $end_pos        = strpos(strtolower($text), '>', $start_pos);
504                 $end_pos        += strlen('>');
505
506                 $text = substr($text, $end_pos);
507         }
508
509         /* strip everything after </body> */
510         $end_pos        = strpos(strtolower($text), '</body>');
511         if ($end_pos !== false) {
512                 $text = trim(substr($text, 0, $end_pos));
513         }
514
515         return $text;
516 }
517
518 function get_html_head ($text) {
519         /* make all text lower case */
520 //      $text = strtolower($text);
521
522         /* strip everything before <head> */
523         $start_pos      = stripos($text, '<head');
524         if ($start_pos !== false) {
525                 $start_pos      += strlen('<head');
526                 $end_pos        = stripos($text, '>', $start_pos);
527                 $end_pos        += strlen('>');
528
529                 $text = substr($text, $end_pos);
530         }
531
532         /* strip everything after </head> */
533         $end_pos        = stripos($text, '</head');
534         if ($end_pos !== false) {
535                 $text = trim(substr($text, 0, $end_pos));
536         }
537         return $text;
538 }
539
540 /**
541 * This function cuts out requested tag information from html head
542 * @access  public
543 * @param   $text  html text
544 * @param   $tags  a string or an array of requested tags
545 * @author  Cindy Qi Li
546 */
547 function get_html_head_by_tag($text, $tags)
548 {
549         $head = get_html_head($text);
550         $rtn_text = "";
551         
552         if (!is_array($tags) && strlen(trim($tags)) > 0)
553         {
554                 $tags = array(trim($tags));
555         }
556         foreach ($tags as $tag)
557         {
558                 $tag = strtolower($tag);
559
560                 /* strip everything before <{tag}> */
561                 $start_pos      = stripos($head, '<'.$tag);
562                 $temp_head = $head;
563                 
564                 while ($start_pos !== false) 
565                 {
566                         $temp_text = substr($temp_head, $start_pos);
567         
568                         /* strip everything after </{tag}> or />*/
569                         $end_pos        = stripos($temp_text, '</' . $tag . '>');
570         
571                         if ($end_pos !== false) 
572                         {
573                                 $end_pos += strlen('</' . $tag . '>');
574                                 
575                                 // add an empty line after each tag information
576                                 $rtn_text .= trim(substr($temp_text, 0, $end_pos)) . '
577         
578 ';
579                         }
580                         else  // match /> as ending tag if </tag> is not found
581                         {
582                                 $end_pos        = stripos($temp_text, '/>');
583                                 
584                                 if($end_pos === false && stripos($temp_text, $tag.'>')===false){
585                                         //if /> is not found, then this is not a valid XHTML
586                                         //text iff it's not tag>
587                                         $end_pos = stripos($temp_text, '>');
588                                         $end_pos += strlen('>');
589                                 } else {
590                                         $end_pos += strlen('/>');
591                                 }
592                                 // add an empty line after each tag information
593                                 $rtn_text .= trim(substr($temp_text, 0, $end_pos)) . '
594         
595 ';
596                         }
597                         
598                         // initialize vars for next round of matching
599                         $temp_head = substr($temp_text, $end_pos);
600                         $start_pos = stripos($temp_head, '<'.$tag);
601                 }
602         }
603         return $rtn_text;
604 }
605
606 if (version_compare(phpversion(), '4.3.0') < 0) {
607         function file_get_contents($filename) {
608                 $fd = @fopen($filename, 'rb');
609                 if ($fd === false) {
610                         $content = false;
611                 } else {
612                         $content = @fread($fd, filesize($filename));
613                         @fclose($fd);
614                 }
615
616                 return $content;
617         }
618
619         function mysql_real_escape_string($input) {
620                 return mysql_escape_string($input);
621         }
622 }
623
624
625 function add_user_online() {
626         if (!isset($_SESSION['member_id']) || !($_SESSION['member_id'] > 0)) {
627                 return;
628         }
629         global $db, $addslashes;
630
631     $expiry = time() + 900; // 15min
632     $sql    = 'REPLACE INTO '.TABLE_PREFIX.'users_online VALUES ('.$_SESSION['member_id'].', '.$_SESSION['course_id'].', "'.$addslashes(get_display_name($_SESSION['member_id'])).'", '.$expiry.')';
633     $result = mysql_query($sql, $db);
634
635         /* garbage collect and optimize the table every so often */
636         mt_srand((double) microtime() * 1000000);
637         $rand = mt_rand(1, 20);
638         if ($rand == 1) {
639                 $sql = 'DELETE FROM '.TABLE_PREFIX.'users_online WHERE expiry<'.time();
640                 $result = @mysql_query($sql, $db);
641         }
642 }
643
644 /**
645  * Returns the login name of a member.
646  * @access  public
647  * @param   int $id     The ID of the member.
648  * @return  Returns the login name of the member whose ID is $id.
649  * @author  Joel Kronenberg
650  */
651 function get_login($id){
652         global $db, $_config_defaults;
653
654         if (is_array($id)) {
655                 $id             = implode(',',$id);
656                 $sql    = 'SELECT login, member_id FROM '.TABLE_PREFIX.'members WHERE member_id IN ('.$id.') ORDER BY login';
657
658                 $rows = array();
659                 $result = mysql_query($sql, $db);
660                 while( $row     = mysql_fetch_assoc($result)) {
661                         $rows[$row['member_id']] = $row['login'];
662                 }
663                 return $rows;
664         } else {
665                 $id             = intval($id);
666                 $sql    = 'SELECT login FROM '.TABLE_PREFIX.'members WHERE member_id='.$id;
667
668                 $result = mysql_query($sql, $db);
669                 $row    = mysql_fetch_assoc($result);
670
671                 return $row['login'];
672         }
673
674 }
675
676 function get_display_name($id) {
677         static $db, $_config, $display_name_formats;
678         if (!$id) {
679                 return $_SESSION['login'];
680         }
681
682         if (!isset($db, $_config)) {
683                 global $db, $_config, $display_name_formats;
684         }
685
686         if (substr($id, 0, 2) == 'g_' || substr($id, 0, 2) == 'G_')
687         {
688                 $sql    = "SELECT name FROM ".TABLE_PREFIX."guests WHERE guest_id='".$id."'";
689                 $result = mysql_query($sql, $db);
690                 $row    = mysql_fetch_assoc($result);
691
692                 return _AT($display_name_formats[$_config['display_name_format']], '', $row['name'], '', '');
693         }
694         else
695         {
696                 $sql    = 'SELECT login, first_name, second_name, last_name FROM '.TABLE_PREFIX.'members WHERE member_id='.$id;
697                 $result = mysql_query($sql, $db);
698                 $row    = mysql_fetch_assoc($result);
699
700                 return _AT($display_name_formats[$_config['display_name_format']], $row['login'], $row['first_name'], $row['second_name'], $row['last_name']);
701         }
702 }
703
704 function get_forum_name($fid){
705         global $db;
706
707         $fid = intval($fid);
708
709         $sql    = 'SELECT title FROM '.TABLE_PREFIX.'forums WHERE forum_id='.$fid;
710         $result = mysql_query($sql, $db);
711         if (($row = mysql_fetch_assoc($result)) && $row['title']) {
712                 return $row['title'];           
713         }
714
715         $sql = "SELECT group_id FROM ".TABLE_PREFIX."forums_groups WHERE forum_id=$fid";
716         $result = mysql_query($sql, $db);
717         if ($row = mysql_fetch_assoc($result)) {
718                 return get_group_title($row['group_id']);
719         }
720
721         return FALSE;
722 }
723
724 // takes the array of valid prefs and assigns them to the current session 
725 // @params: prefs - an array of preferences
726 // @params: optional. Values are 0 or 1. Default value is 0
727 //          when 1, assign PREF_MOBILE_THEME to PREF_THEME if the request is from a mobile device
728 //                  this value is to set when the prefs values are set for display
729 //                  if this function is used as a front shot for save_prefs(), the value should be 0
730 function assign_session_prefs($prefs, $switch_mobile_theme = 0) {
731         if (is_array($prefs)) {
732                 foreach($prefs as $pref_name => $value) {
733                         $_SESSION['prefs'][$pref_name] = $value;
734                 }
735         }
736         if (is_mobile_device() && $switch_mobile_theme) {
737                 $_SESSION['prefs']['PREF_THEME'] = $_SESSION['prefs']['PREF_MOBILE_THEME'];
738         }
739 }
740
741 function save_prefs( ) {
742         global $db, $addslashes;
743
744         if ($_SESSION['valid_user']) {
745                 $data   = $addslashes(serialize($_SESSION['prefs']));
746                 $sql    = 'UPDATE '.TABLE_PREFIX.'members SET preferences="'.$data.'", creation_date=creation_date, last_login=last_login WHERE member_id='.$_SESSION['member_id'];
747                 $result = mysql_query($sql, $db); 
748         }
749 }
750
751 function save_email_notification($mnot) {
752     global $db;
753     
754     if ($_SESSION['valid_user']) {
755         $sql = "UPDATE ".TABLE_PREFIX."members SET inbox_notify =". $mnot .", creation_date=creation_date, last_login=last_login WHERE member_id =".$_SESSION['member_id'];
756         $result = mysql_query($sql, $db);
757     }
758 }
759
760 /**
761 * Saves the last viewed content page in a user's course so that on next visit, user can start reading where they left off
762 * @access  public
763 * @param   int $cid             the content page id
764 * @return  none
765 * @see     $db                  in include/vitals.inc.php
766 * @author  Joel Kronenberg
767 */
768 function save_last_cid($cid) {
769         if ($_SESSION['enroll'] == AT_ENROLL_NO) {
770                 return;
771         }
772         global $db;
773
774         $_SESSION['s_cid']    = intval($_GET['cid']);
775
776         if (!$_SESSION['is_admin']   && 
777                 !$_SESSION['privileges'] && 
778                 !isset($in_get)          && 
779                 !$_SESSION['cid_time']   && 
780                 ($_SESSION['course_id'] > 0) ) 
781                 {
782                         $_SESSION['cid_time'] = time();
783         }
784
785         $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET last_cid=$cid WHERE course_id=$_SESSION[course_id] AND member_id=$_SESSION[member_id]";
786         mysql_query($sql, $db);
787 }
788
789 // there has to be a better way of expressing this if-statement!
790 // and, does it really have to be here?
791 if ((!isset($_SESSION['is_admin']) || !$_SESSION['is_admin'])       && 
792         (!isset($_SESSION['privileges']) || !$_SESSION['privileges'])     &&
793         !isset($in_get)              && 
794         isset($_SESSION['s_cid']) && $_SESSION['s_cid'] && 
795         isset($_SESSION['cid_time']) && $_SESSION['cid_time'] &&
796     ($_SESSION['course_id'] > 0) && 
797         ($_SESSION['s_cid'] != $_GET['cid']) && 
798         ($_SESSION['enroll'] != AT_ENROLL_NO) )  
799         {
800                 $diff = time() - $_SESSION['cid_time'];
801                 if ($diff > 0) {
802                         $sql = "UPDATE ".TABLE_PREFIX."member_track SET counter=counter+1, duration=duration+$diff, last_accessed=NOW() WHERE member_id=$_SESSION[member_id] AND content_id=$_SESSION[s_cid]";
803
804                         $result = mysql_query($sql, $db);
805
806                         if (mysql_affected_rows($db) == 0) {
807                                 $sql = "INSERT INTO ".TABLE_PREFIX."member_track VALUES ($_SESSION[member_id], $_SESSION[course_id], $_SESSION[s_cid], 1, $diff, NOW())";
808                                 $result = mysql_query($sql, $db);
809                         }
810                 }
811
812                 $_SESSION['cid_time'] = 0;
813 }
814
815
816 /**
817 * Checks if the $_SESSION[member_id] is an instructor (true) or not (false)
818 * The result is only fetched once - it is then available via a static variable, $is_instructor
819 * @access  public
820 * @param   none
821 * @return  bool true if is instructor, false otherwise.
822 * @see     $db   in include/vitals.inc.php
823 * @author  Joel Kronenberg
824 */      
825 function get_instructor_status() {
826         static $is_instructor;
827
828         if (isset($is_instructor)) {
829                 return $is_instructor;
830         }
831
832         global $db;
833
834         $is_instructor = false;
835
836         $sql = 'SELECT status FROM '.TABLE_PREFIX.'members WHERE member_id='.$_SESSION['member_id'];
837         $result = mysql_query($sql, $db);
838         if (!($row = @mysql_fetch_assoc($result))) {
839                 $is_instructor = FALSE;
840                 return FALSE;
841         }
842
843         if ($row['status'] == AT_STATUS_INSTRUCTOR) {
844                 $is_instructor = TRUE;
845                 return TRUE;
846         }
847
848         $is_instructor = FALSE;
849         return FALSE;
850 }
851
852 /****************************************************/
853 /* update the user online list                                          */
854 if (isset($_SESSION['valid_user']) && $_SESSION['valid_user']) {
855         $new_minute = time()/60;
856         if (!isset($_SESSION['last_updated'])) {
857                 $_SESSION['last_updated'] = $new_minute;
858         }
859         $diff       = abs($_SESSION['last_updated'] - $new_minute);
860         if ($diff > ONLINE_UPDATE) {
861                 $_SESSION['last_updated'] = $new_minute;
862                 add_user_online();
863         }
864 }
865
866 /****************************************************/
867 /* compute the $_my_uri variable                                        */
868         $bits     = explode(SEP, getenv('QUERY_STRING'));
869         $num_bits = count($bits);
870         $_my_uri  = '';
871
872         for ($i=0; $i<$num_bits; $i++) {
873                 if (    (strpos($bits[$i], 'enable=')   === 0) 
874                         ||      (strpos($bits[$i], 'disable=')  === 0)
875                         ||      (strpos($bits[$i], 'expand=')   === 0)
876                         ||      (strpos($bits[$i], 'collapse=') === 0)
877                         ||      (strpos($bits[$i], 'lang=')             === 0)
878                         ) {
879                         /* we don't want this variable added to $_my_uri */
880                         continue;
881                 }
882
883                 if (($_my_uri == '') && ($bits[$i] != '')) {
884                         $_my_uri .= htmlentities('?');
885                 } else if ($bits[$i] != ''){
886                         $_my_uri .= htmlentities(SEP);
887                 }
888                 $_my_uri .= $bits[$i];
889         }
890         if ($_my_uri == '') {
891                 $_my_uri .= htmlentities('?');
892         } else {
893                 $_my_uri .= htmlentities(SEP);
894         }
895         $_my_uri = $_SERVER['PHP_SELF'].$_my_uri;
896
897 /**
898  * If MBString extension is loaded, 4.3.0+, then use it.
899  * Otherwise we will have to use include/utf8 library
900  * @author      Harris
901  * @date Oct 10, 2007
902  * @version     1.5.6
903  */
904  if (extension_loaded('mbstring')){
905          $strtolower = 'mb_strtolower';
906          $strtoupper = 'mb_strtoupper';
907          $substr = 'mb_substr';
908          $strpos = 'mb_strpos';
909          $strrpos = 'mb_strrpos';
910          $strlen = 'mb_strlen';
911  } else {
912          $strtolower = 'utf8_strtolower';
913          $strtoupper = 'utf8_strtoupper';
914          $substr = 'utf8_substr';
915          $strpos = 'utf8_strpos';
916          $strrpos = 'utf8_strrpos';
917          $strlen = 'utf8_strlen';
918  }
919
920
921 /*~~~~~~~~~~~~~~~~~flash detection~~~~~~~~~~~~~~~~*/
922 if(isset($_COOKIE["flash"])){
923     $_SESSION['flash'] = $_COOKIE["flash"];
924
925     //delete the cookie
926     ATutor.setcookie("flash",'',time()-3600);
927 }
928
929 if (!isset($_SESSION["flash"])) {
930         $_custom_head .= '
931                 <script type="text/javascript">
932                 <!--
933
934                         //VB-Script for InternetExplorer
935                         function iExploreCheck()
936                         {
937                                 document.writeln("<scr" + "ipt language=\'VBscript\'>");
938                                 //document.writeln("\'Test to see if VBScripting works");
939                                 document.writeln("detectableWithVB = False");
940                                 document.writeln("If ScriptEngineMajorVersion >= 2 then");
941                                 document.writeln("   detectableWithVB = True");
942                                 document.writeln("End If");
943                                 //document.writeln("\'This will check for the plugin");
944                                 document.writeln("Function detectActiveXControl(activeXControlName)");
945                                 document.writeln("   on error resume next");
946                                 document.writeln("   detectActiveXControl = False");
947                                 document.writeln("   If detectableWithVB Then");
948                                 document.writeln("      detectActiveXControl = IsObject(CreateObject(activeXControlName))");
949                                 document.writeln("   End If");
950                                 document.writeln("End Function");
951                                 document.writeln("</scr" + "ipt>");
952                                 return detectActiveXControl("ShockwaveFlash.ShockwaveFlash.1");
953                         }
954
955
956                         var plugin = (navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]) ? navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin : false;
957                         if(!(plugin) && (navigator.userAgent && navigator.userAgent.indexOf("MSIE")>=0 && (navigator.appVersion.indexOf("Win") != -1)))
958                                 if (iExploreCheck())
959                                         flash_detect = "flash=yes";
960                                 else
961                                         flash_detect = "flash=no";
962
963                         else if(plugin)
964                                 flash_detect = "flash=yes";
965                         else
966                                 flash_detect = "flash=no";
967
968                         writeCookie(flash_detect);
969
970                         function writeCookie(value)
971                         {
972                                 var today = new Date();
973                                 var the_date = new Date("December 31, 2099");
974                                 var the_cookie_date = the_date.toGMTString();
975                                 var the_cookie = value + ";expires=" + the_cookie_date;
976                                 document.cookie = the_cookie;
977                         }
978                 //-->
979                 </script>
980 ';
981 }
982
983
984
985 /*~~~~~~~~~~~~~~end flash detection~~~~~~~~~~~~~~~*/
986
987
988
989 /**
990 * Checks if the data exceeded the database predefined length, if so,
991 * truncate it.
992 * This is used on data that are being inserted into the database.
993 * If this function is used for display purposes, you may want to add the '...' 
994 *  at the end of the string by setting the $forDisplay=1
995 * @param        the mbstring that needed to be checked
996 * @param        the byte length of what the input should be in the database.
997 * @param        (OPTIONAL)
998 *                       append '...' at the end of the string.  Should not use this when 
999 *                       dealing with database.  This should only be set for display purposes.
1000 * @return       the mbstring safe sql entry
1001 * @author       Harris Wong
1002 */
1003 function validate_length($input, $len, $forDisplay=0){
1004         global $strlen, $substr;
1005         $input_bytes_len = strlen($input);
1006         $input_len = $strlen($input);
1007
1008         //If the input has exceeded the db column limit
1009         if ($input_bytes_len > $len){
1010                 //calculate where to chop off the string
1011                 $percentage = $input_bytes_len / $input_len;
1012                 //Get the suitable length that should be stored in the db
1013                 $suitable_len = floor($len / $percentage);
1014
1015                 if ($forDisplay===1){
1016                         return $substr($input, 0, $suitable_len).'...';
1017                 }
1018                 return $substr($input, 0, $suitable_len);
1019         }
1020         //if valid length
1021         return $input;
1022
1023 /*
1024  * Instead of blindly cutting off the input from the given param
1025  * 
1026         global $strlen, $substr;
1027         if ($strlen($input) > $len) {
1028                 if ($forDisplay===1){
1029                         return $substr($input, 0, $len).'...';
1030                 }
1031                 return $substr($input, 0, $len);
1032         }
1033         return $input;
1034 */
1035 }
1036
1037 /**
1038  * If pretty URL within admin config is switched on.  We will apply pretty URL 
1039  * to all the links in ATutor.  This function will authenticate itself towards the current pages.
1040  * In our definition, admins, login, registration pages shouldn't have pretty url applied.  However,
1041  * if one want to use url_rewrite on these pages, please force it by using the third parameter.  
1042  * Note: If system config has turned off this feature, $force will have no effect.
1043  * @param       string  the Url should be a relative link, have to improve this later on, to check if 
1044  *                                      it's a relative link, if not, truncate it.
1045  * @param       boolean Available values are AT_PRETTY_URL_IS_HEADER, AT_PRETTY_URL_NOT_HEADER(default)
1046  *                      use AT_PRETTY_URL_IS_HEADER if url_rewrite is used in php header('Location:..'), absolute path is needed for this.
1047  * @param       boolean true to force the url_rewrite, false otheriwse.  False is the default.
1048  * @author      Harris Wong
1049  */
1050 function url_rewrite($url, $is_rewriting_header=AT_PRETTY_URL_NOT_HEADER, $force=false){
1051         global $_config, $db;
1052         $url_parser = new UrlParser();
1053         $pathinfo = $url_parser->getPathArray();
1054
1055         /* If this is any kind of admins, don't prettify the url
1056          * $_SESSION['is_guest'] is used to check against login/register/browse page, the links on this page will 
1057          * only be prettified when a user has logged in.
1058          * Had used $_SESSION[valid_user] before but it created this problem: 
1059          * http://www.atutor.ca/atutor/mantis/view.php?id=3426
1060          */
1061         if ($force || (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0)) {
1062                 //if course id is defined, apply pretty url.
1063         } 
1064         //if this is something that is displayed on the login page, don't modify the urls.
1065         else if ( (admin_authenticate(AT_ADMIN_PRIV_ADMIN, AT_PRIV_RETURN) 
1066                         || (isset($_SESSION['privileges']) && admin_authenticate($_SESSION['privileges'], AT_PRIV_RETURN))) 
1067                         || (isset($_SESSION['is_guest']) && $_SESSION['is_guest']==1)){
1068                 return $url;
1069         } 
1070
1071         //if we allow pretty url in the system
1072         if ($_config['pretty_url'] > 0){
1073                 $course_id = 0;
1074                 //If we allow course dir name from sys perf             
1075                 if ($_config['course_dir_name'] > 0){
1076                         if (preg_match('/bounce.php\?course=([\d]+)$/', $url, $matches) == 1){
1077                                 // bounce has the highest priority, even if session is set, work on 
1078                                 // bounce first.
1079                                 $course_id = $url_parser->getCourseDirName($matches[1]);
1080                         } elseif (isset($_REQUEST['course'])){
1081                                 //jump menu
1082                                 $course_id = $url_parser->getCourseDirName($_REQUEST['course']);
1083                         } elseif (isset($_REQUEST['p_course'])){
1084                                 // is set when guests access public course. @see bounce.php
1085                                 $course_id = $url_parser->getCourseDirName($_REQUEST['p_course']);
1086                         } elseif (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0){
1087                                 $course_id = $url_parser->getCourseDirName($_SESSION['course_id']);
1088                         } 
1089                 } else {
1090                         $course_id = $_SESSION['course_id'];
1091                 }
1092                 $url = $pathinfo[1]->convertToPrettyUrl($course_id, $url);
1093         } elseif ($_config['course_dir_name'] > 0) {
1094                 //enabled course directory name, disabled pretty url
1095                 if (preg_match('/bounce.php\?course=([\d]+)$/', $url, $matches) == 1){
1096                         // bounce has the highest priority, even if session is set, work on 
1097                         // bounce first.
1098                         $course_id = $url_parser->getCourseDirName($matches[1]);
1099                 } elseif (isset($_REQUEST['course'])){
1100                         $course_id = $url_parser->getCourseDirName($_REQUEST['course']);
1101                 } elseif (isset($_REQUEST['p_course'])){
1102                         // is set when guests access public course. @see bounce.php
1103                         $course_id = $url_parser->getCourseDirName($_REQUEST['p_course']);
1104                 } elseif (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0){
1105                         $course_id = $url_parser->getCourseDirName($_SESSION['course_id']);
1106                 } 
1107                 $url = $pathinfo[1]->convertToPrettyUrl($course_id, $url);
1108         }
1109
1110         //instead of putting AT_BASE_HREF in all the headers location, we will put it here.
1111         //Abs paths are required for pretty url because otherwise the url location will be appeneded.
1112         //ie.   ATutor_161/blogs/CoURSe_rOAd/blogs/view.php/ot/1/oid/1/ instead of 
1113         //              ATutor_161/CoURSe_rOAd/blogs/view.php/ot/1/oid/1/
1114         if ($is_rewriting_header==true){
1115                 return AT_BASE_HREF.$url;
1116         } 
1117         return $url;
1118 }
1119
1120
1121 /**
1122 * Applies $addslashes or intval() recursively.
1123 * @access  public
1124 * @param   mixed $input The input to clean.
1125 * @return  A safe version of $input
1126 * @author  Joel Kronenberg
1127 */
1128 function sql_quote($input) {
1129         global $addslashes;
1130
1131         if (is_array($input)) {
1132                 foreach ($input as $key => $value) {
1133                         if (is_array($input[$key])) {
1134                                 $input[$key] = sql_quote($input[$key]);
1135                         } else if (!empty($input[$key]) && is_numeric($input[$key])) {
1136                                 $input[$key] = intval($input[$key]);
1137                         } else {
1138                                 $input[$key] = $addslashes(trim($input[$key]));
1139                         }
1140                 }
1141         } else {
1142                 if (!empty($input) && is_numeric($input)) {
1143                         $input = intval($input);
1144                 } else {
1145                         $input = $addslashes(trim($input));
1146                 }
1147         }
1148         return $input;
1149 }
1150
1151 function query_bit( $bitfield, $bit ) {
1152         if (!is_int($bitfield)) {
1153                 $bitfield = intval($bitfield);
1154         }
1155         if (!is_int($bit)) {
1156                 $bit = intval($bit);
1157         }
1158         return ( $bitfield & $bit ) ? true : false;
1159
1160
1161 /**
1162 * Authenticates the current user against the specified privilege.
1163 * @access  public
1164 * @param   int  $privilege              privilege to check against.
1165 * @param   bool $check                  whether or not to return the result or to abort/exit.
1166 * @return  bool true if this user is authenticated, false otherwise.
1167 * @see     query_bit() in include/vitals.inc.php
1168 * @author  Joel Kronenberg
1169 */
1170 function authenticate($privilege, $check = false) {
1171         if ($_SESSION['is_admin']) {
1172                 return true;
1173         }
1174
1175         $auth = query_bit($_SESSION['privileges'], $privilege);
1176         
1177         if (!$_SESSION['valid_user'] || !$auth) {
1178                 if (!$check){
1179                         global $msg;
1180                         $msg->addInfo('NO_PERMISSION');
1181                         require(AT_INCLUDE_PATH.'header.inc.php'); 
1182                         require(AT_INCLUDE_PATH.'footer.inc.php'); 
1183                         exit;
1184                 } else {
1185                         return false;
1186                 }
1187         }
1188         return true;
1189 }
1190
1191 function admin_authenticate($privilege = 0, $check = false) {
1192         if (!isset($_SESSION['valid_user']) || !$_SESSION['valid_user'] || ($_SESSION['course_id'] != -1)) {
1193                 if ($check) {
1194                         return false;
1195                 }
1196                 header('Location: '.AT_BASE_HREF.'login.php');
1197                 exit;
1198         }
1199
1200         if ($_SESSION['privileges'] == AT_ADMIN_PRIV_ADMIN) {
1201                 return true;
1202         }
1203
1204         if ($privilege) {
1205                 $auth = query_bit($_SESSION['privileges'], $privilege);
1206
1207                 if (!$auth) {
1208                         if ($check) {
1209                                 return false;
1210                         }
1211                         global $msg;
1212                         $msg->addError('ACCESS_DENIED');
1213                         require(AT_INCLUDE_PATH.'header.inc.php'); 
1214                         require(AT_INCLUDE_PATH.'footer.inc.php'); 
1215                         exit;
1216                 }
1217         }
1218         return true;
1219 }
1220
1221 function get_default_theme() {
1222         global $db;
1223
1224         if (is_mobile_device()) {
1225                 $default_status = 3;
1226         } else {
1227                 $default_status = 2;
1228         }
1229         $sql    = "SELECT dir_name FROM ".TABLE_PREFIX."themes WHERE status=".$default_status;
1230         $result = mysql_query($sql, $db);
1231         $row = mysql_fetch_assoc($result);
1232
1233         return $row;
1234 }
1235
1236 function get_system_default_theme() {
1237         if (is_mobile_device()) {
1238                 return 'mobile';
1239         } else {
1240                 return 'default';
1241         }
1242 }
1243
1244 function is_mobile_theme($theme) {
1245         global $db;
1246
1247         $sql    = "SELECT dir_name FROM ".TABLE_PREFIX."themes WHERE type='".MOBILE_DEVICE."'";
1248         $result = mysql_query($sql, $db);
1249         while ($row = mysql_fetch_assoc($result)) {
1250                 if ($row['dir_name'] == $theme && is_dir(AT_INCLUDE_PATH . '../themes/' . $theme)) return true;
1251         }
1252
1253         return false;
1254 }
1255
1256 if (isset($_GET['expand'])) {
1257         $_SESSION['menu'][intval($_GET['expand'])] = 1;
1258 } else if (isset($_GET['collapse'])) {
1259         unset($_SESSION['menu'][intval($_GET['collapse'])]);
1260 }
1261
1262 /**
1263 * Writes present action to admin log db
1264 * @access  private
1265 * @param   string $operation_type       The type of operation
1266 * @param   string $table_name           The table affected
1267 * @param   string $num_affected         The number of rows in the table affected
1268 * @author  Shozub Qureshi
1269 */
1270 function write_to_log($operation_type, $table_name, $num_affected, $details) {
1271         global $db, $addslashes;
1272
1273         if ($num_affected > 0) {
1274                 $details = $addslashes(stripslashes($details));
1275                 $sql    = "INSERT INTO ".TABLE_PREFIX."admin_log VALUES ('$_SESSION[login]', NULL, $operation_type, '$table_name', $num_affected, '$details')";
1276                 $result = mysql_query($sql, $db);
1277         }
1278 }
1279
1280 function get_group_title($group_id) {
1281         global $db;
1282         $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$group_id";
1283         $result = mysql_query($sql, $db);
1284         if ($row = mysql_fetch_assoc($result)) {
1285                 return $row['title'];
1286         }
1287         return FALSE;
1288 }
1289
1290 function get_status_name($status_id) {
1291         switch ($status_id) {
1292                 case AT_STATUS_DISABLED:
1293                                 return _AT('disabled');
1294                                 break;
1295                 case AT_STATUS_UNCONFIRMED:
1296                         return _AT('unconfirmed');
1297                         break;
1298                 case AT_STATUS_STUDENT:
1299                         return _AT('student');
1300                         break;
1301                 case AT_STATUS_INSTRUCTOR:
1302                         return _AT('instructor');
1303                         break;
1304         }
1305 }
1306
1307 function profile_image_exists($id) {
1308         $extensions = array('gif', 'jpg', 'png');
1309
1310         foreach ($extensions as $extension) {
1311                 if (file_exists(AT_CONTENT_DIR.'profile_pictures/originals/'. $id.'.'.$extension)) {
1312                         return true;
1313                 }
1314         }
1315 }
1316
1317 /**
1318  * print thumbnails or profile pic
1319  * @param       int             image id
1320  * @param       int             1 for thumbnail, 2 for profile
1321  */
1322 function print_profile_img($id, $type=1) {
1323         global $moduleFactory;
1324         $mod = $moduleFactory->getModule('_standard/profile_pictures');
1325         if ($mod->isEnabled() === FALSE) {
1326                 return;
1327         }
1328         if (profile_image_exists($id)) {
1329                 if ($type==1){
1330                         echo '<img src="get_profile_img.php?id='.$id.'" class="profile-picture" alt="" />';
1331                 } elseif($type==2){
1332                         echo '<img src="get_profile_img.php?id='.$id.SEP.'size=p" class="profile-picture" alt="" />';
1333                 }
1334         } else {
1335                 echo '<img src="images/clr.gif" height="100" width="100" class="profile-picture" alt="" />';
1336         }
1337 }
1338
1339 function profile_image_delete($id) {
1340         $extensions = array('gif', 'jpg', 'png');
1341
1342         foreach ($extensions as $extension) {
1343                 if (file_exists(AT_CONTENT_DIR.'profile_pictures/originals/'. $id.'.'.$extension)) {
1344                         unlink(AT_CONTENT_DIR.'profile_pictures/originals/'. $id.'.'.$extension);
1345                 }
1346                 if (file_exists(AT_CONTENT_DIR.'profile_pictures/profile/'. $id.'.'.$extension)) {
1347                         unlink(AT_CONTENT_DIR.'profile_pictures/profile/'. $id.'.'.$extension);
1348                 }
1349                 if (file_exists(AT_CONTENT_DIR.'profile_pictures/thumbs/'. $id.'.'.$extension)) {
1350                         unlink(AT_CONTENT_DIR.'profile_pictures/thumbs/'. $id.'.'.$extension);
1351                 }               
1352         }
1353 }
1354
1355 /**
1356  * get_group_concat
1357  * returns a list of $field values from $table using $where_clause, separated by $separator.
1358  * uses mysql's GROUP_CONCAT() if available and if within the limit (default is 1024), otherwise
1359  * it does it the old school way.
1360  * returns the list (as a string) or (int) 0, if none found.
1361  */
1362 function get_group_concat($table, $field, $where_clause = 1, $separator = ',') {
1363         global $_config, $db;
1364         if (!isset($_config['mysql_group_concat_max_len'])) {
1365                 $sql = "SELECT  @@global.group_concat_max_len AS max";
1366                 $result = mysql_query($sql, $db);
1367                 if ($result && ($row = mysql_fetch_assoc($result))) {
1368                         $_config['mysql_group_concat_max_len'] = $row['max'];
1369                 } else {
1370                         $_config['mysql_group_concat_max_len'] = 0;
1371                 }
1372                 $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('mysql_group_concat_max_len', '{$_config['mysql_group_concat_max_len']}')";
1373                 mysql_query($sql, $db);
1374         }
1375         if ($_config['mysql_group_concat_max_len'] > 0) {
1376                 $sql = "SELECT GROUP_CONCAT($field SEPARATOR '$separator') AS list FROM ".TABLE_PREFIX."$table WHERE $where_clause";
1377                 $result = mysql_query($sql, $db);
1378                 if ($row = mysql_fetch_assoc($result)) {
1379                         if (!$row['list']) {
1380                                 return 0; // empty
1381                         } else if ($row['list'] && strlen($row['list']) < $_config['mysql_group_concat_max_len']) {
1382                                 return $row['list'];
1383                         } // else: list is truncated, do it the old way
1384                 } else {
1385                         // doesn't actually get here.
1386                         return 0; // empty
1387                 }
1388         } // else:
1389
1390         $list = '';
1391         $sql = "SELECT $field AS id FROM ".TABLE_PREFIX."$table WHERE $where_clause";
1392         $result = mysql_query($sql, $db);
1393         while ($row = mysql_fetch_assoc($result)) {
1394                 $list .= $row['id'] . ',';
1395         }
1396         if ($list) {
1397                 return substr($list, 0, -1); }
1398         return 0;
1399 }
1400
1401 function get_human_time($seconds) {
1402         if ($seconds < 0) { 
1403                 $out = '0'._AT('second_short'); 
1404         } else if ($seconds > 60 * 60) { // more than 60 minutes.
1405                 $hours = floor($seconds / 60 / 60);
1406                 $minutes = floor(($seconds - $hours * 60 * 60) / 60);
1407                 $out = $hours ._AT('hour_short').' '.$minutes._AT('minute_short');
1408
1409                 //$out = ($seconds
1410         } else if ($seconds > 60) { // more than a minute
1411                 $minutes = floor($seconds / 60);
1412                 $out = $minutes ._AT('minute_short').' '.($seconds - $minutes * 60)._AT('second_short');
1413         } else { // less than a minute
1414                 $out = $seconds . _AT('second_short');
1415         }
1416
1417         return $out;
1418 }
1419
1420 function is_mobile_device() {
1421         $http_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
1422         return ((stripos($http_user_agent, IPOD_DEVICE) !== false && stripos($http_user_agent, IPOD_DEVICE) >= 0) ||
1423                         (stripos($http_user_agent, IPHONE_DEVICE) !== false && stripos($http_user_agent, IPHONE_DEVICE) >= 0) ||
1424                 (stripos($http_user_agent, BLACKBERRY_DEVICE) !== false && stripos($http_user_agent, BLACKBERRY_DEVICE) >= 0) ||
1425                 (stripos($http_user_agent, ANDROID_DEVICE) !== false && stripos($http_user_agent, ANDROID_DEVICE) >= 0)) 
1426                 ? true : false;
1427 }
1428
1429 function get_mobile_device_type() {
1430         $http_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
1431         if (stripos($http_user_agent, IPOD_DEVICE) !== false && stripos($http_user_agent, IPOD_DEVICE) >= 0) {
1432                 return IPOD_DEVICE;
1433         } else if (stripos($http_user_agent, IPHONE_DEVICE) !== false && stripos($http_user_agent, IPHONE_DEVICE) >= 0) {
1434                 return IPHONE_DEVICE;
1435         } else if (stripos($http_user_agent, BLACKBERRY_DEVICE) !== false && stripos($http_user_agent, BLACKBERRY_DEVICE) >= 0) {
1436                 return BLACKBERRY_DEVICE;
1437         } else if (stripos($http_user_agent, ANDROID_DEVICE) !== false && stripos($http_user_agent, ANDROID_DEVICE) >= 0) {
1438                 return ANDROID_DEVICE;
1439         } else {
1440                 return UNKNOWN_DEVICE;
1441         }
1442 }
1443
1444 /**
1445  * Convert all input to htmlentities output, in UTF-8.
1446  * @param       string  input to be convert
1447  * @param       boolean true if we wish to change all newlines(\r\n) to a <br/> tag, false otherwise.  
1448  *                      ref: http://php.net/manual/en/function.nl2br.php
1449  * @author      Harris Wong
1450  * @date        March 12, 2010
1451  */
1452 function htmlentities_utf8($str, $use_nl2br=true){
1453         $return = htmlentities($str, ENT_QUOTES, 'UTF-8');
1454         if ($use_nl2br){
1455                 return nl2br($return);
1456         } 
1457         return $return;
1458 }
1459
1460 /**
1461  * Convert all '&' to '&amp;' from the input
1462  * @param   string  any string input, mainly URLs.
1463  * @return  input with & replaced to '&amp;'
1464  * @author  Harris Wong
1465  * @date    Oct 7, 2010
1466  */
1467 function convert_amp($input){
1468     $input = str_replace('&amp;', '&', $input); //convert everything to '&' first
1469     return str_replace('&', '&amp;', $input);
1470 }
1471
1472 /**
1473  * Check if json_encode/json_decode exists, if not, use the json service library.
1474  * NOTE:  json_encode(), json_decode() are NOT available piror to php 5.2
1475  * @author      Harris Wong
1476  * @date        April 21, 2010
1477  */
1478  if ( !function_exists('json_encode') ){
1479     function json_encode($content){
1480                 require_once (AT_INCLUDE_PATH.'lib/json.inc.php');
1481                 $json = new Services_JSON;               
1482         return $json->encode($content);
1483     }
1484 }
1485 if ( !function_exists('json_decode') ){
1486     function json_decode($content, $assoc=false){
1487                 require_once (AT_INCLUDE_PATH.'lib/json.inc.php');
1488                 if ( $assoc ){
1489                         $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
1490         } else {
1491                         $json = new Services_JSON;
1492                 }
1493         return $json->decode($content);
1494     }
1495 }
1496
1497 /*
1498  * Finds the image in the theme image folder first. If the image does not exist, look up in 
1499  * core image folder.
1500  * @param: image_name - The relative path and name to the image. 
1501  *             "Relative" means relative to the "image" folder, with subfolders and image name.
1502  *         actual_relative_path - Used when find_image() is called, for example in a class,
1503  *             which is then called by different scripts that give different AT_INCLUDE_PATH. However,
1504  *             the path to the image itself is consistent regardless of the caller script. This value
1505  *             should be the consistent relative path to the image itself.
1506  * @return: The path to the image in the theme folder, if exists. 
1507  *          Otherwise, the path to the image in the core image folder.
1508  * Example: 
1509  *   1. pass in "rtl_tree/tree/collapse.gif"
1510  *   2. return the path to this image in the theme folder: include/../themes/default/images/rtl_tree/tree/collapse.gif
1511  *      if the theme image does not exist, return the path to the image in the core "image" folder: include/../images/rtl_tree/tree/collapse.gif
1512  *      These pathes are relative to ATutor installation directory.
1513  */
1514 function find_image($image_name, $actual_relative_path = AT_INCLUDE_PATH) {
1515         // The returned path is determined by AT_INCLUDE_PATH. If AT_INCLUDE_PATH is undefined, return the parameter itself.
1516         if (!defined('AT_INCLUDE_PATH')) return $image_name;
1517         
1518         // string concanation cannot be used at assigning parameter default value
1519         if ($actual_relative_path == AT_INCLUDE_PATH) $actual_relative_path .= '../';
1520         
1521         // remove leading "/"
1522         if (substr($image_name, 0, 1) == "/") $image_name = substr($image_name, 1);
1523         
1524         $theme_image_folder = 'themes/'.$_SESSION['prefs']['PREF_THEME'].'/images/';
1525         $atutor_image_folder = 'images/';
1526         
1527         // Use the path that is relative to AT_INCLUDE_PATH in the caller script, to check the existence of the image
1528         // but the return path is based on the pass-in actual path parameter.
1529         if (file_exists(AT_INCLUDE_PATH.'../'.$theme_image_folder.$image_name)) {
1530                 return $actual_relative_path.$theme_image_folder.$image_name;
1531         } else {
1532                 return $actual_relative_path.$atutor_image_folder.$image_name;
1533         }
1534 }
1535
1536 require(AT_INCLUDE_PATH . '../mods/_core/modules/classes/Module.class.php');
1537
1538 $moduleFactory = new ModuleFactory(TRUE); // TRUE is for auto_loading the module.php files
1539
1540 if (isset($_GET['submit_language']) && $_SESSION['valid_user']) {
1541         if ($_SESSION['course_id'] == -1) {
1542                 $sql = "UPDATE ".TABLE_PREFIX."admins SET language = '$_SESSION[lang]' WHERE login = '$_SESSION[login]'";
1543                 $result = mysql_query($sql, $db);
1544         } else {
1545                 $sql = "UPDATE ".TABLE_PREFIX."members SET language = '$_SESSION[lang]', creation_date=creation_date, last_login=last_login WHERE member_id = $_SESSION[member_id]";
1546                 $result = mysql_query($sql, $db);
1547         }
1548 }
1549
1550 if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0) {
1551     $_custom_head .= '<script type="text/javascript" src="'.$_base_path.'jscripts/ATutorCourse.js"></script>';
1552 }
1553 ?>