remove old readme
[atutor.git] / include / classes / ContentManager.class.php
1 <?php
2 /****************************************************************/
3 /* ATutor                                                                                                               */
4 /****************************************************************/
5 /* Copyright (c) 2002-2010                                      */
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
15 class ContentManager
16 {
17         /* db handler   */
18         var $db;
19
20         /*      array           */
21         var $_menu;
22
23         /*      array           */
24         var $_menu_info;
25
26         /*      array           */
27         var $_menu_in_order;
28
29         /* int                  */
30         var $course_id;
31
32         // private
33         var $num_sections;
34
35         // private
36         var $max_depth;
37
38         // private
39         var $content_length;
40         
41         // private
42         var $tree_collapse_icon, $tree_expand_icon, $tree_vertline_icon, $tree_horizontal_icon,
43             $tree_split_icon, $tree_disabled_icon, $tree_end_icon, $tree_space_icon;
44
45         /* constructor  */
46         function ContentManager(&$db, $course_id) {
47                 global $rtl;
48                 
49                 $this->db = $db;
50                 $this->course_id = intval($course_id);
51
52                 // Look for tree icons for displaying content navigation from theme image folder,
53                 // if the icon is not there, look up in atutor root image folder
54                 $this->tree_collapse_icon = find_image($rtl.'tree/tree_collapse.gif', '');
55                 $this->tree_expand_icon = find_image($rtl.'tree/tree_expand.gif', '');
56                 $this->tree_vertline_icon = find_image($rtl.'tree/tree_vertline.gif', '');
57                 $this->tree_horizontal_icon = find_image($rtl.'tree/tree_horizontal.gif', '');
58                 $this->tree_split_icon = find_image($rtl.'tree/tree_split.gif', '');
59                 $this->tree_disabled_icon = find_image($rtl.'tree/tree_disabled.gif', '');
60                 $this->tree_end_icon = find_image($rtl.'tree/tree_end.gif', '');
61                 $this->tree_space_icon = find_image($rtl.'tree/tree_space.gif', '');
62         }
63
64         function initContent( ) {
65                 if (!($this->course_id > 0)) {
66                         return;
67                 }
68                 $sql = "SELECT content_id, content_parent_id, ordering, title, UNIX_TIMESTAMP(release_date) AS u_release_date, content_type 
69                           FROM ".TABLE_PREFIX."content 
70                          WHERE course_id=$this->course_id 
71                          ORDER BY content_parent_id, ordering";
72                 $result = mysql_query($sql, $this->db);
73
74                 /* x could be the ordering or even the content_id       */
75                 /* don't really need the ordering anyway.                       */
76                 /* $_menu[content_parent_id][x] = array('content_id', 'ordering', 'title') */
77                 $_menu = array();
78
79                 /* number of content sections */
80                 $num_sections = 0;
81
82                 $max_depth = array();
83                 $_menu_info = array();
84
85                 while ($row = mysql_fetch_assoc($result)) {
86                         $num_sections++;
87                         $_menu[$row['content_parent_id']][] = array('content_id'=> $row['content_id'],
88                                                                                                                 'ordering'      => $row['ordering'], 
89                                                                                                                 'title'         => htmlspecialchars($row['title']),
90                                                                                                                 'content_type' => $row['content_type']);
91
92                         $_menu_info[$row['content_id']] = array('content_parent_id' => $row['content_parent_id'],
93                                                                                                         'title'                         => htmlspecialchars($row['title']),
94                                                                                                         'ordering'                      => $row['ordering'],
95                                                                                                         'u_release_date'    => $row['u_release_date'],
96                                                                                                         'content_type' => $row['content_type']);
97
98                         /* 
99                          * add test content asscioations
100                          * find associations per content page, and add it as a sublink.
101                          * @author harris
102                          */
103                         $test_rs = $this->getContentTestsAssoc($row['content_id']);
104                         while ($test_row = mysql_fetch_assoc($test_rs)){
105                                 $_menu[$row['content_id']][] = array(   'test_id'       => $test_row['test_id'],
106                                                                                                                 'title'         => htmlspecialchars($test_row['title']),
107                                                                                                                 'content_type' => CONTENT_TYPE_CONTENT);
108                         }
109                         /* End of add test content asscioations */
110
111                         if ($row['content_parent_id'] == 0) {
112                                 $max_depth[$row['content_id']] = 1;
113                         } else {
114                                 $max_depth[$row['content_id']] = $max_depth[$row['content_parent_id']]+1;
115                         }
116                 }
117
118                 $this->_menu = $_menu;
119
120                 $this->_menu_info =  $_menu_info;
121
122                 $this->num_sections = $num_sections;
123
124                 if (count($max_depth) > 1) {
125                         $this->max_depth = max($max_depth);
126                 } else {
127                         $this->max_depth = 0;
128                 }
129
130                 // generate array of all the content ids in the same order that they appear in "content navigation"
131                 $this->_menu_in_order[] = $next_content_id = $this->getNextContentID(0);
132                 while ($next_content_id > 0)
133                 {
134                         $next_content_id = $this->getNextContentID($next_content_id);
135                         
136                         if (in_array($next_content_id, $this->_menu_in_order)) break;
137                         else $this->_menu_in_order[] = $next_content_id;
138                 }
139                 
140                 $this->content_length = count($_menu[0]);
141         }
142
143         // This function is called by initContent to construct $this->_menu_in_order, an array to 
144         // holds all the content ids in the same order that they appear in "content navigation"
145         function getNextContentID($content_id, $order=0) {
146                 // return first root content when $content_id is not given
147                 if (!$content_id) {
148                         return $this->_menu[0][0]['content_id'];
149                 }
150                 
151                 $myParent = $this->_menu_info[$content_id]['content_parent_id'];
152                 $myOrder  = $this->_menu_info[$content_id]['ordering'];
153                 
154                 // calculate $myOrder, add in the number of tests in front of this content page
155                 if (is_array($this->_menu[$myParent])) {
156                         $num_of_tests = 0;
157                         foreach ($this->_menu[$myParent] as $menuContent) {
158                                 if ($menuContent['content_id'] == $content_id) break;
159                                 if (isset($menuContent['test_id'])) $num_of_tests++;
160                         }
161                 }
162                 $myOrder += $num_of_tests;
163                 // end of calculating $myOrder
164                 
165                 /* if this content has children, then take the first one. */
166                 if ( isset($this->_menu[$content_id]) && is_array($this->_menu[$content_id]) && ($order==0) ) {
167                         /* has children */
168                         // if the child is a test, keep searching for the content id
169                         foreach ($this->_menu[$content_id] as $menuID => $menuContent)
170                         {
171                                 if (!empty($menuContent['test_id'])) continue;
172                                 else 
173                                 {
174                                         $nextMenu = $this->_menu[$content_id][$menuID]['content_id'];
175                                         break;
176                                 }
177                         }
178                         
179                         // all children are tests
180                         if (!isset($nextMenu))
181                         {
182                                 if (isset($this->_menu[$myParent][$myOrder]['content_id'])) {
183                                         // has sibling
184                                         return $this->_menu[$myParent][$myOrder]['content_id'];
185                                 }
186                                 else { // no sibling
187                                         $nextMenu = $this->getNextContentID($myParent, 1);
188                                 }
189                         }
190                         return $nextMenu;
191                 } else {
192                         /* no children */
193                         if (isset($this->_menu[$myParent][$myOrder]) && $this->_menu[$myParent][$myOrder] != '') {
194                                 /* Has sibling */
195                                 return $this->_menu[$myParent][$myOrder]['content_id'];
196                         } else {
197                                 /* No more siblings */
198                                 if ($myParent != 0) {
199                                         return $this->getNextContentID($myParent, 1);
200                                 }
201                         }
202                 }
203         }
204         
205         function getContent($parent_id=-1, $length=-1) {
206                 if ($parent_id == -1) {
207                         $my_menu_copy = $this->_menu;
208                         if ($length != -1) {
209                                 $my_menu_copy[0] = array_slice($my_menu_copy[0], 0, $length);
210                         }
211                         return $my_menu_copy;
212                 }
213                 return $this->_menu[$parent_id];
214         }
215
216
217         function &getContentPath($content_id) {
218                 $path = array();
219
220                 $path[] = array('content_id' => $content_id, 'title' => $this->_menu_info[$content_id]['title']);
221
222                 $this->getContentPathRecursive($content_id, $path);
223
224                 $path = array_reverse($path);
225                 return $path;
226         }
227
228
229         function getContentPathRecursive($content_id, &$path) {
230                 $parent_id = $this->_menu_info[$content_id]['content_parent_id'];
231
232                 if ($parent_id > 0) {
233                         $path[] = array('content_id' => $parent_id, 'title' => $this->_menu_info[$parent_id]['title']);
234                         $this->getContentPathRecursive($parent_id, $path);
235                 }
236         }
237
238         function addContent($course_id, $content_parent_id, $ordering, $title, $text, $keywords, 
239                             $related, $formatting, $release_date, $head = '', $use_customized_head = 0, 
240                             $test_message = '', $allow_test_export = 1, $content_type = CONTENT_TYPE_CONTENT) {
241                 
242                 if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && ($_SESSION['course_id'] != -1)) {
243                         return false;
244                 }
245
246                 // shift the new neighbouring content down
247                 $sql = "UPDATE ".TABLE_PREFIX."content SET ordering=ordering+1 
248                          WHERE ordering>=$ordering 
249                            AND content_parent_id=$content_parent_id 
250                            AND course_id=$_SESSION[course_id]";
251                 $result = mysql_query($sql, $this->db);
252
253                 /* main topics all have minor_num = 0 */
254                 $sql = "INSERT INTO ".TABLE_PREFIX."content
255                                (course_id,
256                                 content_parent_id,
257                                 ordering,
258                                 last_modified,
259                                 revision,
260                                 formatting,
261                                 release_date,
262                                 head,
263                                 use_customized_head,
264                                 keywords,
265                                 content_path,
266                                 title,
267                                 text,
268                                                 test_message,
269                                                 allow_test_export,
270                                                 content_type)
271                         VALUES ($course_id, 
272                                 $content_parent_id, 
273                                 $ordering, 
274                                 NOW(), 
275                                 0, 
276                                 $formatting, 
277                                 '$release_date', 
278                                 '$head',
279                                 $use_customized_head,
280                                 '$keywords', 
281                                 '', 
282                                 '$title',
283                                 '$text',
284                                                 '$test_message',
285                                                 $allow_test_export,
286                                                 $content_type)";
287
288                 $err = mysql_query($sql, $this->db);
289
290                 /* insert the related content */
291                 $sql = "SELECT LAST_INSERT_ID() AS insert_id";
292                 $result = mysql_query($sql, $this->db);
293                 $row = mysql_fetch_assoc($result);
294                 $cid = $row['insert_id'];
295
296                 $sql = '';
297                 if (is_array($related)) {
298                         foreach ($related as $x => $related_content_id) {
299                                 $related_content_id = intval($related_content_id);
300
301                                 if ($related_content_id != 0) {
302                                         if ($sql != '') {
303                                                 $sql .= ', ';
304                                         }
305                                         $sql .= '('.$cid.', '.$related_content_id.')';
306                                         $sql .= ', ('.$related_content_id.', '.$cid.')';
307                                 }
308                         }
309
310                         if ($sql != '') {
311                                 $sql    = 'INSERT INTO '.TABLE_PREFIX.'related_content VALUES '.$sql;
312                                 $result = mysql_query($sql, $this->db);
313                         }
314                 }
315
316                 return $cid;
317         }
318         
319         function editContent($content_id, $title, $text, $keywords,$related, $formatting, 
320                              $release_date, $head, $use_customized_head, $test_message, 
321                              $allow_test_export, $content_type) {
322                 if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) {
323                         return FALSE;
324                 }
325
326                 /* update the title, text of the newly moved (or not) content */
327                 $sql    = "UPDATE ".TABLE_PREFIX."content 
328                               SET title='$title', head='$head', use_customized_head=$use_customized_head, 
329                                   text='$text', keywords='$keywords', formatting=$formatting, 
330                                   revision=revision+1, last_modified=NOW(), release_date='$release_date', 
331                                   test_message='$test_message', allow_test_export=$allow_test_export, 
332                           content_type=$content_type
333                             WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
334                 $result = mysql_query($sql, $this->db);
335
336                 /* update the related content */
337                 $result = mysql_query("DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id", $this->db);
338                 $sql = '';
339                 if (is_array($related)) {
340                         foreach ($related as $x => $related_content_id) {
341                                 $related_content_id = intval($related_content_id);
342
343                                 if ($related_content_id != 0) {
344                                         if ($sql != '') {
345                                                 $sql .= ', ';
346                                         }
347                                         $sql .= '('.$content_id.', '.$related_content_id.')';
348                                         $sql .= ', ('.$related_content_id.', '.$content_id.')';
349                                 }
350                         }
351
352                         if ($sql != '') {
353                                 /* delete the old related content */
354                                 $result = mysql_query("DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id", $this->db);
355
356                                 /* insert the new, and the old related content again */
357                                 $sql    = 'INSERT INTO '.TABLE_PREFIX.'related_content VALUES '.$sql;
358                                 $result = mysql_query($sql, $this->db);
359                         }
360                 }
361         }
362
363         function moveContent($content_id, $new_content_parent_id, $new_content_ordering) {
364                 global $msg;
365                 
366                 if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) {
367                         return FALSE;
368                 }
369
370                 /* first get the content to make sure it exists */
371                 $sql    = "SELECT ordering, content_parent_id FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
372                 $result = mysql_query($sql, $this->db);
373                 if (!($row = mysql_fetch_assoc($result)) ) {
374                         return FALSE;
375                 }
376                 $old_ordering           = $row['ordering'];
377                 $old_content_parent_id  = $row['content_parent_id'];
378                 
379                 $sql    = "SELECT max(ordering) max_ordering FROM ".TABLE_PREFIX."content WHERE content_parent_id=$old_content_parent_id AND course_id=$_SESSION[course_id]";
380                 $result = mysql_query($sql, $this->db);
381                 $row = mysql_fetch_assoc($result);
382                 $max_ordering = $row['max_ordering'];
383                 
384                 if ($content_id == $new_content_parent_id) {
385                         $msg->addError("NO_SELF_AS_PARENT");
386                         return;
387                 }
388                 
389                 if ($old_content_parent_id == $new_content_parent_id && $old_ordering == $new_content_ordering) {
390                         $msg->addError("SAME_LOCATION");
391                         return;
392                 }
393                 
394                 $content_path = $this->getContentPath($new_content_parent_id);
395                 foreach ($content_path as $parent){
396                         if ($parent['content_id'] == $content_id) {
397                                 $msg->addError("NO_CHILD_AS_PARENT");
398                                 return;
399                         }
400                 }
401                 
402                 // if the new_content_ordering is greater than the maximum ordering of the parent content, 
403                 // set the $new_content_ordering to the maximum ordering. This happens when move the content 
404                 // to the last element under the same parent content.
405                 if ($old_content_parent_id == $new_content_parent_id && $new_content_ordering > $max_ordering) 
406                         $new_content_ordering = $max_ordering;
407                 
408                 if (($old_content_parent_id != $new_content_parent_id) || ($old_ordering != $new_content_ordering)) {
409                         // remove the gap left by the moved content
410                         $sql = "UPDATE ".TABLE_PREFIX."content 
411                                    SET ordering=ordering-1 
412                                  WHERE ordering>$old_ordering 
413                                    AND content_parent_id=$old_content_parent_id 
414                                    AND content_id<>$content_id 
415                                    AND course_id=$_SESSION[course_id]";
416                         $result = mysql_query($sql, $this->db);
417
418                         // shift the new neighbouring content down
419                         $sql = "UPDATE ".TABLE_PREFIX."content 
420                                    SET ordering=ordering+1 
421                                  WHERE ordering>=$new_content_ordering 
422                                    AND content_parent_id=$new_content_parent_id 
423                                    AND content_id<>$content_id 
424                                    AND course_id=$_SESSION[course_id]";
425                         $result = mysql_query($sql, $this->db);
426
427                         $sql    = "UPDATE ".TABLE_PREFIX."content 
428                                       SET content_parent_id=$new_content_parent_id, ordering=$new_content_ordering 
429                                     WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
430                         $result = mysql_query($sql, $this->db);
431                 }
432         }
433         
434         function deleteContent($content_id) {
435                 if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) {
436                         return false;
437                 }
438
439                 /* check if exists */
440                 $sql    = "SELECT ordering, content_parent_id FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
441                 $result = mysql_query($sql, $this->db);
442                 if (!($row = @mysql_fetch_assoc($result)) ) {
443                         return false;
444                 }
445                 $ordering                       = $row['ordering'];
446                 $content_parent_id      = $row['content_parent_id'];
447
448                 /* check if this content has sub content        */
449                 $children = $this->_menu[$content_id];
450
451                 if (is_array($children) && (count($children)>0) ) {
452                         /* delete its children recursively first*/
453                         foreach ($children as $x => $info) {
454                                 $this->deleteContentRecursive($info['content_id']);
455                         }
456                 }
457
458                 /* delete this content page                                     */
459                 $sql    = "DELETE FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
460                 $result = mysql_query($sql, $this->db);
461
462                 /* delete this content from member tracking page        */
463                 $sql    = "DELETE FROM ".TABLE_PREFIX."member_track WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
464                 $result = mysql_query($sql, $this->db);
465
466                 $sql    = "DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id";
467                 $result = mysql_query($sql, $this->db);
468
469                 /* delete the content tests association */
470                 $sql    = "DELETE FROM ".TABLE_PREFIX."content_tests_assoc WHERE content_id=$content_id";
471                 $result = mysql_query($sql, $this->db);
472
473                 /* delete the content forum association */
474                 $sql    = "DELETE FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id=$content_id";
475                 $result = mysql_query($sql, $this->db);
476
477                 /* Delete all AccessForAll contents */
478                 require_once(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4a.class.php');
479                 $a4a = new A4a($content_id);
480                 $a4a->deleteA4a();
481
482                 /* re-order the rest of the content */
483                 $sql = "UPDATE ".TABLE_PREFIX."content SET ordering=ordering-1 WHERE ordering>=$ordering AND content_parent_id=$content_parent_id AND course_id=$_SESSION[course_id]";
484                 $result = mysql_query($sql, $this->db);
485                 /* end moving block */
486
487                 /* remove the "resume" to this page, b/c it was deleted */
488                 $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET last_cid=0 WHERE course_id=$_SESSION[course_id] AND last_cid=$content_id";
489                 $result = mysql_query($sql, $this->db);
490
491                 return true;
492         }
493
494
495         /* private. call from deleteContent only. */
496         function deleteContentRecursive($content_id) {
497                 /* check if this content has sub content        */
498                 $children = $this->_menu[$content_id];
499
500                 if (is_array($children) && (count($children)>0) ) {
501                         /* delete its children recursively first*/
502                         foreach ($children as $x => $info) {
503                                 $this->deleteContent($info['content_id']);
504                         }
505                 }
506
507                 /* delete this content page                                     */
508                 $sql    = "DELETE FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]";
509                 $result = mysql_query($sql, $this->db);
510
511                 /* delete this content from member tracking page        */
512                 $sql    = "DELETE FROM ".TABLE_PREFIX."member_track WHERE content_id=$content_id";
513                 $result = mysql_query($sql, $this->db);
514
515                 $sql    = "DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id";
516                 $result = mysql_query($sql, $this->db);
517
518                 /* delete the content tests association */
519                 $sql    = "DELETE FROM ".TABLE_PREFIX."content_tests_assoc WHERE content_id=$content_id";
520                 $result = mysql_query($sql, $this->db);
521         }
522
523         function getContentPage($content_id) {
524                 $sql    = "SELECT *, DATE_FORMAT(release_date, '%Y-%m-%d %H:%i:00') AS release_date, release_date+0 AS r_date, NOW()+0 AS n_date FROM ".TABLE_PREFIX."content 
525                             WHERE content_id=$content_id";
526                 $result = mysql_query($sql, $this->db);
527
528                 return $result;
529         }
530         
531         /* @See editor/edit_content.php include/html/dropdowns/related_topics.inc.php include/lib/editor_tabs_functions.inc.php */
532         function getRelatedContent($content_id, $all=false) {
533                 if ($content_id == 0) {
534                         return;
535                 }
536                 if ($content_id == '') {
537                         return;
538                 }
539                 $related_content = array();
540
541                 if ($all) {
542                         $sql = "SELECT * FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id";
543                 } else {
544                         $sql = "SELECT * FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id";
545                 }
546                 $result = mysql_query($sql, $this->db);
547
548                 while ($row = mysql_fetch_assoc($result)) {
549                         if ($row['related_content_id'] != $content_id) {
550                                 $related_content[] = $row['related_content_id'];
551                         } else {
552                                 $related_content[] = $row['content_id'];
553                         }
554                 }
555
556                 return $related_content;
557         }
558
559         /** 
560          * Return a list of tests associated with the selected content
561          * @param       int             the content id that all tests are associated with it.
562          * @return      array   list of tests
563          * @date        Sep 10, 2008
564          * @author      Harris
565          */
566         function & getContentTestsAssoc($content_id){
567                 $sql    = "SELECT ct.test_id, t.title FROM (SELECT * FROM ".TABLE_PREFIX."content_tests_assoc WHERE content_id=$content_id) AS ct LEFT JOIN ".TABLE_PREFIX."tests t ON ct.test_id=t.test_id";
568                 $result = mysql_query($sql, $this->db);
569                 return $result;
570         }
571
572         /*TODO***************BOLOGNA***************REMOVE ME**********/
573         function & getContentForumsAssoc($content_id){
574                 $sql    = "SELECT cf.forum_id, f.title FROM (SELECT * FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id=$content_id) AS cf LEFT JOIN ".TABLE_PREFIX."forums f ON cf.forum_id=f.forum_id";
575                 $result = mysql_query($sql, $this->db);
576                 return $result;
577         }
578
579         function & cleanOutput($value) {
580                 return stripslashes(htmlspecialchars($value));
581         }
582
583
584         /* @See include/html/editor_tabs/properties.inc.php */
585         /* Access: Public */
586         function getNumSections() {
587                 return $this->num_sections;
588         }
589
590         /* Access: Public */
591         function getMaxDepth() {
592                 return $this->max_depth;
593         }
594
595         /* Access: Public */
596         function getContentLength() {
597                 return $this->content_length;
598         }
599
600         /* @See include/html/dropdowns/local_menu.inc.php */
601         function getLocationPositions($parent_id, $content_id) {
602                 $siblings = $this->getContent($parent_id);
603                 for ($i=0;$i<count($siblings); $i++){
604                         if ($siblings[$i]['content_id'] == $content_id) {
605                                 return $i;
606                         }
607                 }
608                 return 0;       
609         }
610
611         /* Access: Private */
612         function getNumbering($content_id) {
613                 $path = $this->getContentPath($content_id);
614                 $parent = 0;
615                 $numbering = '';
616                 foreach ($path as $page) {
617                         $num = $this->getLocationPositions($parent, $page['content_id']) +1;
618                         $parent = $page['content_id'];
619                         $numbering .= $num.'.';
620                 }
621                 $numbering = substr($numbering, 0, -1);
622
623                 return $numbering;
624         }
625
626         function getPreviousContent($content_id) {
627                 if (is_array($this->_menu_in_order))
628                 {
629                         foreach ($this->_menu_in_order as $content_location => $this_content_id)
630                         {
631                                 if ($this_content_id == $content_id) break;
632                         }
633                         
634                         for ($i=$content_location-1; $i >= 0; $i--)
635                         {
636                                 $content_type = $this->_menu_info[$this->_menu_in_order[$i]]['content_type'];
637                                 
638                                 if ($content_type == CONTENT_TYPE_CONTENT || $content_type == CONTENT_TYPE_WEBLINK)
639                                         return array('content_id'       => $this->_menu_in_order[$i],
640                                                  'ordering'             => $this->_menu_info[$this->_menu_in_order[$i]]['ordering'],
641                                                      'title'            => $this->_menu_info[$this->_menu_in_order[$i]]['title']);
642                         }
643                 }
644                 return NULL;
645         }
646         
647         function getNextContent($content_id) {
648                 if (is_array($this->_menu_in_order))
649                 {
650                         foreach ($this->_menu_in_order as $content_location => $this_content_id)
651                         {
652                                 if ($this_content_id == $content_id) break;
653                         }
654                         
655                         for ($i=$content_location+1; $i < count($this->_menu_in_order); $i++)
656                         {
657                                 $content_type = $this->_menu_info[$this->_menu_in_order[$i]]['content_type'];
658                                 
659                                 if ($content_type == CONTENT_TYPE_CONTENT || $content_type == CONTENT_TYPE_WEBLINK)
660                                         return(array('content_id'       => $this->_menu_in_order[$i],
661                                                  'ordering'             => $this->_menu_info[$this->_menu_in_order[$i]]['ordering'],
662                                                      'title'            => $this->_menu_info[$this->_menu_in_order[$i]]['title']));
663                         }
664                 }
665                 return NULL;
666         }
667         
668         /* @See include/header.inc.php */
669         function generateSequenceCrumbs($cid) {
670                 global $_base_path;
671
672                 $sequence_links = array();
673
674                 $first = $this->getNextContent(0); // get first
675                 if ($_SESSION['prefs']['PREF_NUMBERING'] && $first) {
676                         $first['title'] = $this->getNumbering($first['content_id']).' '.$first['title'];
677                 }
678                 if ($first) {
679                         $first['url'] = $_base_path.url_rewrite('content.php?cid='.$first['content_id']);
680                         $sequence_links['first'] = $first;
681                 }
682
683                 if (!$cid && $_SESSION['s_cid']) {
684                         $resume['title'] = $this->_menu_info[$_SESSION['s_cid']]['title'];
685
686                         if ($_SESSION['prefs']['PREF_NUMBERING']) {
687                                 $resume['title'] = $this->getNumbering($_SESSION['s_cid']).' ' . $resume['title'];
688                         }
689
690                         $resume['url'] = $_base_path.url_rewrite('content.php?cid='.$_SESSION['s_cid']);
691
692                         $sequence_links['resume'] = $resume;
693                 } else {
694                         if ($cid) {
695                                 $previous = $this->getPreviousContent($cid);
696                         }
697                         $next = $this->getNextContent($cid ? $cid : 0);
698
699                         if ($_SESSION['prefs']['PREF_NUMBERING']) {
700                                 $previous['title'] = $this->getNumbering($previous['content_id']).' '.$previous['title'];
701                                 $next['title'] = $this->getNumbering($next['content_id']).' '.$next['title'];
702                         }
703
704                         $next['url'] = $_base_path.url_rewrite('content.php?cid='.$next['content_id']);
705                         if (isset($previous['content_id'])) {
706                                 $previous['url'] = $_base_path.url_rewrite('content.php?cid='.$previous['content_id']);
707                         }
708                         
709                         if (isset($previous['content_id'])) {
710                                 $sequence_links['previous'] = $previous;
711                         } else if ($cid) {
712                                 $previous['url']   = $_base_path . url_rewrite('index.php');
713                                 $previous['title'] = _AT('course_home');
714                                 $sequence_links['previous'] = $previous;
715                         }
716                         if (!empty($next['content_id'])) {
717                                 $sequence_links['next'] = $next;
718                         }
719                 }
720
721                 return $sequence_links;
722         }
723
724         /** Generate javascript to hide all root content folders, except the one with current content page
725          * access: private
726          * @return print out javascript function initContentMenu()
727          */
728         function getInitMenuJS(){
729                 global $_base_path;
730                 
731                 echo "\n".'ATutor.course.contentMenu.initContentMenu = function() {'."\n";
732                 
733                 $sql = "SELECT content_id
734                           FROM ".TABLE_PREFIX."content 
735                          WHERE course_id=$this->course_id
736                            AND content_type = ".CONTENT_TYPE_FOLDER;
737                 $result = mysql_query($sql, $this->db);
738
739                 // collapse all root content folders
740                 while ($row = mysql_fetch_assoc($result)) {
741                         echo 'if (ATutor.getcookie("c'.$_SESSION['course_id'].'_'.$row['content_id'].'") == "1") {'."\n".
742                              '  ATutor.course.contentMenu.expandContentFolder('.$row['content_id'].');'."\n".
743                              '} else {'."\n".
744                              '  ATutor.course.contentMenu.collapseContentFolder('.$row['content_id'].');'."\n".
745                              '}'."\n";
746                 }
747                 
748                 // expand the content folder that has current content
749                 if (isset($_SESSION['s_cid']) && $_SESSION['s_cid'] > 0) {
750                         $current_content_path = $this->getContentPath($_SESSION['s_cid']);
751                         
752                         for ($i=0; $i < count($current_content_path)-1; $i++) {
753                                 echo '  ATutor.course.contentMenu.expandContentFolder('.$current_content_path[$i]['content_id'].');'."\n";
754                                      '  ATutor.setcookie("c'.$_SESSION['course_id'].'_'.$current_content_path[$i]['content_id'].'", "1", 1);'."\n";
755                         }
756                 } else { // expand the first folder at user's first visit
757                         // find the first content folder
758                         $sql = "SELECT content_id 
759                                   FROM ".TABLE_PREFIX."content c1 
760                                  WHERE course_id=".$this->course_id."
761                                    AND content_parent_id=0 
762                                    AND content_type=".CONTENT_TYPE_FOLDER."
763                                    AND exists (SELECT * FROM ".TABLE_PREFIX."content c2 
764                                                 WHERE c2.content_parent_id = c1.content_id) 
765                                  ORDER BY ordering 
766                                  LIMIT 1";
767                         $result = mysql_query($sql, $this->db);
768                         
769                         // print out javascript to expand the first content folder
770                         if (mysql_num_rows($result)) {
771                                 $row = mysql_fetch_assoc($result);
772                                 echo '  ATutor.course.contentMenu.expandContentFolder('.$row['content_id'].');'."\n";
773                         }
774                 }
775                 echo '}'."\n"; // end of javascript function initContentMenu()
776         }
777         
778         /* @See include/html/dropdowns/menu_menu.inc.php */
779         function printMainMenu( ) {
780                 if (!($this->course_id > 0)) {
781                         return;
782                 }
783                 
784                 global $_base_path;
785                 
786                 $parent_id    = 0;
787                 $depth        = 0;
788                 $path         = '';
789                 $children     = array();
790                 $truncate     = true;
791                 $ignore_state = true;
792
793                 $this->start = true;
794                 
795                 // DO NOT change id value "editable_table", which is used in ATutorContentMenu.js 
796                 // for the initialization of the inline edit fields
797                 echo '<div id="editable_table">';
798                 
799                 if (authenticate(AT_PRIV_ADMIN,AT_PRIV_RETURN) && !is_mobile_device())
800                 {
801                         echo "\n".'
802                         <div class="menuedit">
803                         <a href="'.$_base_path.'mods/_core/editor/edit_content_folder.php">
804                                 <img id="img_create_top_folder" src="'.$_base_path.'images/folder_new.gif" alt="'._AT("add_top_folder").'" title="'._AT("add_top_folder").'" style="border:0;height:1.2em" />
805                         </a>'."\n".
806                         '<a href="'.$_base_path.'mods/_core/editor/edit_content.php">
807                                 <img id="img_create_top_content" src="'.$_base_path.'images/page_add.gif" alt="'._AT("add_top_page").'" title="'._AT("add_top_page").'" style="border:0;height:1.2em" />
808                         </a>'."\n".
809                         '<a href="javascript:void(0)" onclick="javascript:ATutor.course.contentMenu.switchEditMode();">
810                                 <img id="img_switch_edit_mode" src="'.$_base_path.'images/medit.gif" alt="'._AT("enter_edit_mode").'" title="'._AT("enter_edit_mode").'" style="border:0;height:1.2em" />
811                         </a>
812                         </div>'."\n";
813                 }
814                 $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state);
815                 echo '</div>';
816                 
817                 // javascript for inline editor
818                 echo '<script type="text/javascript">
819 var ATutor = ATutor || {};
820 ATutor.course = ATutor.course || {};
821 ATutor.course.contentMenu = ATutor.course.contentMenu || {};
822
823 ATutor.course.text_enter_edit_mode = "'._AT("enter_edit_mode").'";
824 ATutor.course.text_exit_edit_mode = "'._AT("exit_edit_mode").'";
825 ATutor.base_path = "'.$_base_path.'";
826 ATutor.course.text_expand = "'._AT("expand").'";
827 ATutor.course.text_collapse = "'._AT("collapse").'";
828 ';
829
830                 // get the javascript to initialize the expand/collapse of the content folders
831                 $this->getInitMenuJS();
832                 echo '</script>'."\n";
833                 
834                 // Include the javascript that defines functions for side menu "content navigation" operation
835                 // Note that this javascript calls on js function ATutor.course.contentMenu.initContentMenu()
836                 // that is generated from php function $this->getInitMenuJS(). So, must call the php function
837                 // before including this js script.
838                 echo '<script src="'.AT_BASE_HREF.'jscripts/ATutorContentMenu.js" type="text/javascript"></script>'."\n";
839         }
840
841         /* @See tools/sitemap/index.php */
842         function printSiteMapMenu() {
843                 $parent_id    = 0;
844                 $depth        = 1;
845                 $path         = '';
846                 $children     = array();
847                 $truncate     = false;
848                 $ignore_state = true;
849
850                 $this->start = true;
851                 $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state, 'sitemap');
852         }
853
854         /* @See index.php */
855         function printTOCMenu($cid, $top_num) {
856                 $parent_id    = $cid;
857                 $depth        = 1;
858                 $path         = $top_num.'.';
859                 $children     = array();
860                 $truncate     = false;
861                 $ignore_state = false;
862
863                 $this->start = true;
864                 $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state);
865         }
866
867         /* @See index.php include/html/dropdowns/local_menu.inc.php */
868         function printSubMenu($cid, $top_num) {
869                 $parent_id    = $cid;
870                 $depth        = 1;
871                 $path         = $top_num.'.';
872                 $children     = array();
873                 $truncate     = true;
874                 $ignore_state = false;
875         
876                 $this->start = true;
877                 $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state);
878         }
879
880         /* @See include/html/menu_menu.inc.php  */
881         /* Access: PRIVATE */
882         function printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state, $from = '') {
883                 global $cid, $_my_uri, $_base_path, $rtl, $substr, $strlen;
884                 static $temp_path;
885
886                 if (!isset($temp_path)) {
887                         if ($cid) {
888                                 $temp_path      = $this->getContentPath($cid);
889                         } else {
890                                 $temp_path      = $this->getContentPath($_SESSION['s_cid']);
891                         }
892                 }
893
894                 $highlighted = array();
895                 if (is_array($temp_path)) {
896                         foreach ($temp_path as $temp_path_item) {
897                                 $_SESSION['menu'][$temp_path_item['content_id']] = 1;
898                                 $highlighted[$temp_path_item['content_id']] = true;
899                         }
900                 }
901
902                 if ($this->start) {
903                         reset($temp_path);
904                         $this->start = false;
905                 }
906
907                 if ( isset($this->_menu[$parent_id]) && is_array($this->_menu[$parent_id]) ) {
908                         $top_level = $this->_menu[$parent_id];
909                         $counter = 1;
910                         $num_items = count($top_level);
911                         
912                         echo '<div id="folder'.$parent_id.$from.'">'."\n";
913                         
914                         foreach ($top_level as $garbage => $content) {
915                                 $link = '';
916                                 //tests do not have content id
917                                 $content['content_id'] = isset($content['content_id']) ? $content['content_id'] : '';
918                                 $content['parent_content_id'] = $parent_id;
919
920                                 if (!$ignore_state) {
921                                         $link .= '<a name="menu'.$content['content_id'].'"></a>';
922                                 }
923
924                                 $on = false;
925
926                                 if ( (($_SESSION['s_cid'] != $content['content_id']) || ($_SESSION['s_cid'] != $cid)) && ($content['content_type'] == CONTENT_TYPE_CONTENT || $content['content_type'] == CONTENT_TYPE_WEBLINK)) 
927                                 { // non-current content nodes with content type "CONTENT_TYPE_CONTENT"
928                                         if (isset($highlighted[$content['content_id']])) {
929                                                 $link .= '<strong>';
930                                                 $on = true;
931                                         }
932
933                                         //content test extension  @harris
934                                         //if this is a test link.
935                                         if (isset($content['test_id'])){
936                                                 $title_n_alt =  $content['title'];
937                                                 $in_link = 'mods/_standard/tests/test_intro.php?tid='.$content['test_id'].SEP.'in_cid='.$content['parent_content_id'];
938                                                 $img_link = ' <img src="'.$_base_path.'images/check.gif" title="'.$title_n_alt.'" alt="'.$title_n_alt.'" />';
939                                         } else {
940                                                 $in_link = 'content.php?cid='.$content['content_id'];
941                                                 $img_link = '';
942                                         }
943                                         
944                                         $full_title = $content['title'];
945                                         $link .= $img_link . ' <a href="'.$_base_path.htmlentities_utf8(url_rewrite($in_link)).'" title="';
946                                         $base_title_length = 29;
947                                         if ($_SESSION['prefs']['PREF_NUMBERING']) {
948                                                 $base_title_length = 24;
949                                         }
950
951                                         $link .= $content['title'].'">';
952
953                                         if ($truncate && ($strlen($content['title']) > ($base_title_length-$depth*4)) ) {
954                                                 $content['title'] = htmlspecialchars(rtrim($substr(htmlspecialchars_decode($content['title']), 0, ($base_title_length-$depth*4)-4))).'...';
955                                         }
956                                         
957                                         if (isset($content['test_id'])) {
958                                                 $link .= $content['title'];
959                                         } else {
960                                                 $link .= '<span class="inlineEdits" id="menu-'.$content['content_id'].'" title="'.$full_title.'">';
961                                                 if($_SESSION['prefs']['PREF_NUMBERING']){
962                                                   $link .= $path.$counter;
963                                                 }
964                                                 $link .= '&nbsp;'.$content['title'].'</span>';
965                                         }
966                                         
967                                         $link .= '</a>';
968                                         if ($on) {
969                                                 $link .= '</strong>';
970                                         }
971                                         
972                                         // instructors have privilege to delete content
973                                         if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !isset($content['test_id']) && !is_mobile_device()) {
974                                                 $link .= '<a href="'.$_base_path.'mods/_core/editor/delete_content.php?cid='.$content['content_id'].'"><img src="'.AT_BASE_HREF.'images/x.gif" alt="'._AT("delete_content").'" title="'._AT("delete_content").'" class="del-content-icon" /></a>';
975                                         }
976                                 } 
977                                 else 
978                                 { // current content page & nodes with content type "CONTENT_TYPE_FOLDER"
979                                         $base_title_length = 33;
980                                         if ($_SESSION['prefs']['PREF_NUMBERING']) {
981                                                 $base_title_length = 26;
982                                         }
983                                         
984                                         if (isset($highlighted[$content['content_id']])) {
985                                                 $link .= '<strong>';
986                                                 $on = true;
987                                         }
988
989                                         if ($content['content_type'] == CONTENT_TYPE_CONTENT || $content['content_type'] == CONTENT_TYPE_WEBLINK)
990                                         { // current content page
991                                                 $full_title = $content['title'];
992                                                 $link .= '<a href="'.$_my_uri.'"><img src="'.$_base_path.'images/clr.gif" alt="'._AT('you_are_here').': ';
993                                                 if($_SESSION['prefs']['PREF_NUMBERING']){
994                                                   $link .= $path.$counter;
995                                                 }
996                                                   $link .= $content['title'].'" height="1" width="1" border="0" /></a><strong class="current-content" title="'.$content['title'].'">'."\n";
997                                                 if ($truncate && ($strlen($content['title']) > ($base_title_length-$depth*4)) ) {
998                                                         $content['title'] = htmlspecialchars(rtrim($substr(htmlspecialchars_decode($content['title']), 0, ($base_title_length-$depth*4)-4))).'...';
999                                                 }
1000                                                 $link .= '<a name="menu'.$content['content_id'].'"></a><span class="inlineEdits" id="menu-'.$content['content_id'].'" title="'.$full_title.'">';
1001                                                 if($_SESSION['prefs']['PREF_NUMBERING']){
1002                                                   $link .= $path.$counter;
1003                                                 }
1004                                                 $link .= $content['title'].'</span></strong>';
1005                                                 
1006                                                 // instructors have privilege to delete content
1007                                                 if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) {
1008                                                         $link .= '<a href="'.$_base_path.'mods/_core/editor/delete_content.php?cid='.$content['content_id'].'"><img src="'.AT_BASE_HREF.'images/x.gif" alt="'._AT("delete_content").'" title="'._AT("delete_content").'" class="del-content-icon"  /></a>';
1009                                                 }
1010                                         }
1011                                         else
1012                                         { // nodes with content type "CONTENT_TYPE_FOLDER"
1013                                                 $full_title = $content['title'];
1014                                                 if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) {
1015                                                         $link .= '<a href="'.$_base_path."mods/_core/editor/edit_content_folder.php?cid=".$content['content_id'].'" title="'.$full_title. _AT('click_edit').'">'."\n";
1016                                                 }
1017                                                 else {
1018                                                         $link .= '<span style="cursor:pointer" onclick="javascript: ATutor.course.toggleFolder(\''.$content['content_id'].$from.'\', \''._AT('expand').'\', \''._AT('collapse').'\', '.$this->course_id.'); ">'."\n";
1019                                                 }
1020                                                 
1021                                                 if ($truncate && ($strlen($content['title']) > ($base_title_length-$depth*4)) ) {
1022                                                         $content['title'] = htmlspecialchars(rtrim($substr(htmlspecialchars_decode($content['title']), 0, ($base_title_length-$depth*4)-4))).'...';
1023                                                 }
1024                                                 if (isset($content['test_id']))
1025                                                         $link .= $content['title'];
1026                                                 else
1027                                                         $link .= '<span class="inlineEdits" id="menu-'.$content['content_id'].'" title="'.$full_title.'">';
1028                                                 if($_SESSION['prefs']['PREF_NUMBERING']){
1029                                                   $link .= $path.$counter;
1030                                                 }
1031                                                   $link .= '&nbsp;'.$content['title'].'</span>';
1032                                                 
1033                                                 if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) {
1034                                                         $link .= '</a>'."\n";
1035                                                 }
1036                                                 else {
1037                                                         $link .= '</span>'."\n";
1038                                                 }
1039                                                 
1040                                                 // instructors have privilege to delete content
1041                                                 if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) {
1042                                                         $link .= '<a href="'.$_base_path.'mods/_core/editor/delete_content.php?cid='.$content['content_id'].'"><img src="'.AT_BASE_HREF.'images/x.gif" alt="'._AT("delete_content").'" title="'._AT("delete_content").'" class="del-content-icon" /></a>';
1043                                                 }
1044                                         }
1045                                         
1046                                         if ($on) {
1047                                                 $link .= '</strong>';
1048                                         }
1049                                 }
1050
1051                                 if ($ignore_state) {
1052                                         $on = true;
1053                                 }
1054
1055                                 echo '<span>'."\n";
1056                                 
1057                                 if ( isset($this->_menu[$content['content_id']]) && is_array($this->_menu[$content['content_id']]) ) {
1058                                         /* has children */
1059                                         for ($i=0; $i<$depth; $i++) {
1060                                                 if ($children[$i] == 1) {
1061                                                         echo '<img src="'.AT_BASE_HREF.$this->tree_vertline_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1062                                                 } else {
1063                                                         echo '<img src="'.$_base_path.'images/clr.gif" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1064                                                 }
1065                                         }
1066
1067                                         if (($counter == $num_items) && ($depth > 0)) {
1068                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_end_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1069                                                 $children[$depth] = 0;
1070                                         } else if ($counter == $num_items) {
1071                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_end_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1072                                                 $children[$depth] = 0;
1073                                         } else {
1074                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_split_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1075                                                 $children[$depth] = 1;
1076                                         }
1077
1078                                         if ($_SESSION['s_cid'] == $content['content_id']) {
1079                                                 if (is_array($this->_menu[$content['content_id']])) {
1080                                                         $_SESSION['menu'][$content['content_id']] = 1;
1081                                                 }
1082                                         }
1083
1084                                         if (isset($_SESSION['menu'][$content['content_id']]) && $_SESSION['menu'][$content['content_id']] == 1) {
1085                                                 if ($on) {
1086                                                         echo '<a href="javascript:void(0)" onclick="javascript: ATutor.course.toggleFolder(\''.$content['content_id'].$from.'\', \''._AT('expand').'\', \''._AT('collapse').'\', '.$this->course_id.'); "><img src="'.AT_BASE_HREF.$this->tree_collapse_icon.'" id="tree_icon'.$content['content_id'].$from.'" alt="'._AT('collapse').'" border="0" width="16" height="16" title="'._AT('collapse').'" class="img-size-tree" /></a>'."\n";
1087                                                         
1088                                                 } else {
1089                                                         echo '<a href="'.$_my_uri.'collapse='.$content['content_id'].'">'."\n";
1090                                                         echo '<img src="'.AT_BASE_HREF.$this->tree_collapse_icon.'" id="tree_icon'.$content['content_id'].$from.'" alt="'._AT('collapse').'" border="0" width="16" height="16" title="'._AT('collapse').' '.$content['title'].'" class="img-size-tree" onclick="javascript: ATutor.course.toggleFolder(\''.$content['content_id'].$from.'\', \''._AT('expand').'\', \''._AT('collapse').'\', '.$this->course_id.'); " />'."\n";
1091                                                         echo '</a>'."\n";
1092                                                 }
1093                                         } else {
1094                                                 if ($on) {
1095                                                         echo '<a href="javascript:void(0)" onclick="javascript: ATutor.course.toggleFolder(\''.$content['content_id'].$from.'\', \''._AT('expand').'\', \''._AT('collapse').'\', '.$this->course_id.'); "><img src="'.AT_BASE_HREF.$this->tree_collapse_icon.'" id="tree_icon'.$content['content_id'].$from.'" alt="'._AT('collapse').'" border="0" width="16" height="16" title="'._AT('collapse').'" class="img-size-tree" /></a>'."\n";
1096                                                         
1097                                                 } else {
1098                                                         echo '<a href="'.$_my_uri.'expand='.$content['content_id'].'">'."\n";
1099                                                         echo '<img src="'.AT_BASE_HREF.$this->tree_expand_icon.'" id="tree_icon'.$content['content_id'].$from.'" alt="'._AT('expand').'" border="0" width="16" height="16"      title="'._AT('expand').' '.$content['title'].'" class="img-size-tree" onclick="javascript: ATutor.course.toggleFolder(\''.$content['content_id'].$from.'\', \''._AT('expand').'\', \''._AT('collapse').'\', '.$this->course_id.'); " />';
1100                                                         echo '</a>'."\n";
1101                                                 }
1102                                         }
1103
1104                                 } else {
1105                                         /* doesn't have children */
1106                                         if ($counter == $num_items) {
1107                                                 for ($i=0; $i<$depth; $i++) {
1108                                                         if ($children[$i] == 1) {
1109                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_vertline_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1110                                                         } else {
1111                                                                 echo '<img src="'.$_base_path.'images/clr.gif" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1112                                                         }
1113                                                 }
1114                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_end_icon.'" alt="" border="0" class="img-size-tree" />'."\n";
1115                                         } else {
1116                                                 for ($i=0; $i<$depth; $i++) {
1117                                                         if ($children[$i] == 1) {
1118                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_vertline_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1119                                                         } else {
1120                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_space_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1121                                                         }
1122                                                 }
1123                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_split_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1124                                         }
1125                                         echo '<img src="'.AT_BASE_HREF.$this->tree_horizontal_icon.'" alt="" border="0" width="16" height="16" class="img-size-tree" />'."\n";
1126                                 }
1127
1128                                 
1129                                 echo $link;
1130                                 
1131                                 echo "\n<br /></span>\n\n";
1132                                 
1133                                 if ( $ignore_state || (isset($_SESSION['menu'][$content['content_id']]) && $_SESSION['menu'][$content['content_id']] == 1)) {
1134
1135                                         $depth ++;
1136
1137                                         $this->printMenu($content['content_id'],
1138                                                                                 $depth, 
1139                                                                                 $path.$counter.'.', 
1140                                                                                 $children,
1141                                                                                 $truncate, 
1142                                                                                 $ignore_state,
1143                                                                                 $from);
1144
1145                                                                                 
1146                                         $depth--;
1147
1148                                 }
1149                                 $counter++;
1150                         } // end of foreach
1151
1152                         print "</div>\n\n";
1153                 }
1154         }
1155
1156         /* @See include/html/editor_tabs/properties.inc.php
1157            @See editor/arrange_content.php
1158            
1159             $print_type: "movable" or "related_content"
1160          */
1161         function printActionMenu($menu, $parent_id, $depth, $path, $children, $print_type = 'movable') {
1162                 
1163                 global $cid, $_my_uri, $_base_path, $rtl;
1164
1165                 static $end;
1166
1167                 $top_level = $menu[$parent_id];
1168
1169                 if ( is_array($top_level) ) {
1170                         $counter = 1;
1171                         $num_items = count($top_level);
1172                         foreach ($top_level as $current_num => $content) {
1173                                 if (isset($content['test_id'])){
1174                                         continue;
1175                                 }
1176
1177                                 $link = $buttons = '';
1178
1179                                 echo '<tr>'."\n";
1180                                 
1181                                 if ($print_type == 'movable')
1182                                 {
1183                                         if ($content['content_id'] == $_POST['moved_cid']) {
1184                                                 $radio_selected = ' checked="checked" ';
1185                                         }
1186                                         else {
1187                                                 $radio_selected = '';
1188                                         }
1189                                 
1190                                         $buttons = '<td>'."\n".
1191                                                    '   <small>'."\n".
1192                                                    '      <input type="image" name="move['.$parent_id.'_'.$content['ordering'].']" src="'.$_base_path.'images/before.gif" alt="'._AT('before_topic', $content['title']).'" title="'._AT('before_topic', $content['title']).'" style="height:1.5em; width:1.9em;" />'."\n";
1193
1194                                         if ($current_num + 1 == count($top_level))
1195                                                 $buttons .= '      <input type="image" name="move['.$parent_id.'_'.($content['ordering']+1).']" src="'.$_base_path.'images/after.gif" alt="'._AT('after_topic', $content['title']).'" title="'._AT('after_topic', $content['title']).'" style="height:1.5em; width:1.9em;" />'."\n";
1196                                         
1197                                         $buttons .= '   </small>'."\n".
1198                                                    '</td>'."\n".
1199                                                    '<td>';
1200                                         
1201                                         if ($content['content_type'] == CONTENT_TYPE_FOLDER)
1202                                                 $buttons .= '<input type="image" name="move['.$content['content_id'].'_1]" src="'.$_base_path.'images/child_of.gif" style="height:1.25em; width:1.7em;" alt="'._AT('child_of', $content['title']).'" title="'._AT('child_of', $content['title']).'" />';
1203                                         else
1204                                                 $buttons .= '&nbsp;';
1205                                                 
1206                                         $buttons .= '</td>'."\n".
1207                                                    '<td><input name="moved_cid" value="'.$content['content_id'].'" type="radio" id="r'.$content['content_id'].'" '.$radio_selected .'/></td>'."\n";
1208                                 }
1209                                 
1210                                 $buttons .= '<td>'."\n";
1211                                 if ($print_type == "related_content")
1212                                 {
1213                                         if ($content['content_type'] == CONTENT_TYPE_CONTENT || $content['content_type'] == CONTENT_TYPE_WEBLINK)
1214                                         {
1215                                                 $link .= '<input type="checkbox" name="related[]" value="'.$content['content_id'].'" id="r'.$content['content_id'].'" ';
1216                                                 if (isset($_POST['related']) && in_array($content['content_id'], $_POST['related'])) {
1217                                                         $link .= ' checked="checked"';
1218                                                 }
1219                                                 $link .= ' />'."\n";
1220                                         }
1221                                 }       
1222                                 
1223                                 if ($content['content_type'] == CONTENT_TYPE_FOLDER)
1224                                 {
1225                                         $link .= '<img src="'.$_base_path.'images/folder.gif" />';
1226                                 }
1227                                 $link .= '&nbsp;<label for="r'.$content['content_id'].'">'.$content['title'].'</label>'."\n";
1228
1229                                 if ( is_array($menu[$content['content_id']]) && !empty($menu[$content['content_id']]) ) {
1230                                         /* has children */
1231
1232                                         for ($i=0; $i<$depth; $i++) {
1233                                                 if ($children[$i] == 1) {
1234                                                         echo $buttons;
1235                                                         unset($buttons);
1236                                                         if ($end && ($i==0)) {
1237                                                                 echo '<img src="'.$_base_path.'images/clr.gif" alt="" border="0" width="16" height="16" class="img-size-tree" />';
1238                                                         } else {
1239                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_vertline_icon.'" alt="" border="0" width="16" height="16" />';
1240                                                         }
1241                                                 } else {
1242                                                         echo '<img src="'.$_base_path.'images/clr.gif" alt="" border="0" width="16" height="16" class="img-size-tree" />';
1243                                                 }
1244                                         }
1245
1246                                         if (($counter == $num_items) && ($depth > 0)) {
1247                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_end_icon.'" alt="" border="0" width="16" height="16" />';
1248                                                 $children[$depth] = 0;
1249                                         } else {
1250                                                 echo $buttons;
1251                                                 if (($num_items == $counter) && ($parent_id == 0)) {
1252                                                         echo '<img src="'.AT_BASE_HREF.$this->tree_end_icon.'" alt="" border="0" width="16" height="16" />';
1253                                                         $end = true;
1254                                                 } else {
1255                                                         echo '<img src="'.AT_BASE_HREF.$this->tree_split_icon.'" alt="" border="0" width="16" height="16" />';
1256                                                 }
1257                                                 $children[$depth] = 1;
1258                                         }
1259
1260                                         if ($_SESSION['s_cid'] == $content['content_id']) {
1261                                                 if (is_array($menu[$content['content_id']])) {
1262                                                         $_SESSION['menu'][$content['content_id']] = 1;
1263                                                 }
1264                                         }
1265
1266                                         if ($_SESSION['menu'][$content['content_id']] == 1) {
1267                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_disabled_icon.'" alt="'._AT('toggle_disabled').'" border="0" width="16" height="16" title="'._AT('toggle_disabled').'" />';
1268
1269                                         } else {
1270                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_disabled_icon.'" alt="'._AT('toggle_disabled').'" border="0" width="16" height="16" title="'._AT('toggle_disabled').'" />';
1271                                         }
1272
1273                                 } else {
1274                                         /* doesn't have children */
1275                                         if ($counter == $num_items) {
1276                                                 if ($depth) {
1277                                                         echo $buttons;
1278                                                         for ($i=0; $i<$depth; $i++) {
1279                                                                 if ($children[$i] == 1) {
1280                                                                         if ($end && ($i == 0)) {
1281                                                                                 echo '<img src="'.$_base_path.'images/clr.gif" alt="" border="0" width="16" height="16" class="img-size-tree" />';
1282                                                                         } else {
1283                                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_vertline_icon.'" alt="" border="0" width="16" height="16" />';
1284                                                                         }
1285                                                                 } else {
1286                                                                         echo '<img src="'.$_base_path.'images/clr.gif" alt="" border="0" width="16" height="16" class="img-size-tree" />';
1287                                                                 }
1288                                                         }
1289                                                 } else {
1290                                                         echo $buttons;
1291                                                 }
1292                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_end_icon.'" alt="" border="0" />';
1293                                         } else {
1294                                                 if ($depth) {
1295                                                         echo $buttons;
1296                                                         $print = false;
1297                                                         for ($i=0; $i<$depth; $i++) {
1298                                                                 if ($children[$i] == 1) {
1299                                                                         if ($end && !$print) {
1300                                                                                 $print = true;
1301                                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_space_icon.'" alt="" border="0" width="16" height="16" />';
1302                                                                         } else {
1303                                                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_vertline_icon.'" alt="" border="0" width="16" height="16" />';
1304                                                                         }
1305                                                                 } else {
1306                                                                         echo '<img src="'.AT_BASE_HREF.$this->tree_space_icon.'" alt="" border="0" width="16" height="16" />';
1307                                                                 }
1308                                                         }
1309                                                         $print = false;
1310                                                 } else {
1311                                                         echo $buttons;
1312                                                 }
1313                 
1314                                                 echo '<img src="'.AT_BASE_HREF.$this->tree_split_icon.'" alt="" border="0" width="16" height="16" />';
1315                                         }
1316                                         echo '<img src="'.AT_BASE_HREF.$this->tree_horizontal_icon.'" alt="" border="0" width="16" height="16" />';
1317                                 }
1318
1319                                 echo '<small>';
1320                                   if($_SESSION['prefs']['PREF_NUMBERING']){
1321                                         echo $path.$counter;
1322                                     }
1323                                 
1324                                 echo $link;
1325                                 
1326                                 echo '</small></td>'."\n".'</tr>'."\n";
1327
1328                                 $this->printActionMenu($menu,
1329                                                                         $content['content_id'],
1330                                                                         ++$depth, 
1331                                                                         $path.$counter.'.', 
1332                                                                         $children,
1333                                                                         $print_type);
1334                                 $depth--;
1335
1336                                 $counter++;
1337                         }
1338                 }
1339         }
1340
1341         /* returns the timestamp of release if this page has not yet been released, or is under a page that has not been released, true otherwise */
1342         /* finds the max(timestamp) of all parents and returns that, true if less than now */
1343         /* Access: public */
1344         function isReleased($cid) {
1345                 if ($this->_menu_info[$cid]['content_parent_id'] == 0) {
1346                         // this $cid has no parent, so we check its release date directly
1347                         if ($this->_menu_info[$cid]['u_release_date'] <= time()) {      
1348                                 // yup! it's released
1349                                 return true;
1350                         } else {
1351                                 // nope! not released
1352                                 return $this->_menu_info[$cid]['u_release_date'];
1353                         }
1354                 }
1355                 // this is a sub page, need to check ALL its parents
1356                 $parent = $this->isReleased($this->_menu_info[$cid]['content_parent_id']); // recursion
1357
1358                 if ($parent !== TRUE && $parent > $this->_menu_info[$cid]['u_release_date']) {
1359                         return $parent;
1360                 } else if ($this->_menu_info[$cid]['u_release_date'] <= time()) {
1361                         return true;
1362                 } else {
1363                         return $this->_menu_info[$cid]['u_release_date'];
1364                 }
1365         }
1366
1367         /* Returns the first test_id if this page has pre-test(s) to be passed,
1368          * The pre-tests associated with the parent folders has higher priority to be taken.
1369          * or is under a page that has pre-test(s) to be passed, 
1370          * 0 if has no pre-test(s) to be passed
1371          * -1 if one of the pre-test(s) has expired, the content should not be displayed in this case
1372          * Access: public 
1373          */
1374         function getPretest($cid) {
1375                 // Prepare all the parent folders to this cid since the pre-tests 
1376                 // associated with the parent folders has higher priority to be taken.
1377                 $this_content_path = $this->getContentPath($cid);
1378                 
1379                 foreach ($this_content_path as $this_content) {
1380                         $this_pre_test_id = $this->getOnePretest($this_content['content_id']);
1381                         
1382                         if ($this_pre_test_id > 0 || $this_pre_test_id == -1){
1383                                 return $this_pre_test_id;
1384                         } else {
1385                                 continue;
1386                         }
1387                 }
1388                 
1389                 return 0;
1390         }
1391
1392         /* returns the first test_id if this content has pre-test(s) to be passed, 
1393          * 0 if has no pre-test(s) to be passed
1394          * -1 if one of the pre-test(s) has expired, the content should not be displayed in this case
1395          * Access: public 
1396          */
1397         function getOnePretest($cid) {
1398                 global $db, $msg;
1399                 include_once(AT_INCLUDE_PATH.'../mods/_standard/tests/lib/test_result_functions.inc.php');
1400                 
1401                 $sql = "SELECT *, UNIX_TIMESTAMP(t.start_date) AS start_date, UNIX_TIMESTAMP(t.end_date) AS end_date 
1402                           FROM ".TABLE_PREFIX."tests t, ".TABLE_PREFIX."content_prerequisites cp
1403                          WHERE cp.content_id=".$cid."
1404                            AND cp.type = '".CONTENT_PRE_TEST."'
1405                            AND cp.item_id=t.test_id";
1406                 $result= mysql_query($sql, $db);
1407                 
1408                 while ($row = mysql_fetch_assoc($result))
1409                 {
1410                         // check to make sure we can access this test
1411                         if (!$row['guests'] && ($_SESSION['enroll'] == AT_ENROLL_NO || $_SESSION['enroll'] == AT_ENROLL_ALUMNUS)) {
1412                                 $msg->addInfo('NOT_ENROLLED');
1413                         }
1414                         
1415                         if (!$row['guests'] && !authenticate_test($row['test_id'])) {
1416                                 $msg->addInfo(array('PRETEST_NO_PRIV',$row['title']));
1417                         }
1418                         
1419                         // if the test is not release, not allow student to view the content
1420                         if ($row['start_date'] > time() || $row['end_date'] < time()) {
1421                                 $msg->addInfo(array('PRETEST_EXPIRED',$row['title']));
1422                                 return -1;
1423                         }
1424                         
1425                         $sql = "SELECT tr.result_id, count(*) num_of_questions, sum(ta.score) score, sum(tqa.weight) total_weight
1426                                   FROM ".TABLE_PREFIX."tests_results tr, ".TABLE_PREFIX."tests_answers ta, ".TABLE_PREFIX."tests_questions_assoc tqa 
1427                                  WHERE tr.test_id = ".$row['test_id']."
1428                                    AND tr.member_id = ".$_SESSION['member_id']."
1429                                    AND tr.result_id = ta.result_id
1430                                    AND tr.test_id = tqa.test_id
1431                                    AND ta.question_id = tqa.question_id
1432                                  GROUP BY tr.result_id";
1433                         $result_score = mysql_query($sql, $db);
1434                         
1435                         $num_of_attempts = 0;
1436                         while ($row_score = mysql_fetch_assoc($result_score))
1437                         {
1438                                 // skip the test when:
1439                                 // 1. no pass score is defined. this is a survey.
1440                                 // 2. the student has passed the test 
1441                                 // 3. the test has no question
1442                                 if (($row['passscore'] == 0 && $row['passpercent'] == 0) ||
1443                                     $row_score['num_of_questions'] == 0 ||
1444                                     ($row['passscore']<>0 && $row_score['score']>=$row['passscore']) || 
1445                                     ($row['passpercent']<>0 && ($row_score['score']/$row_score['total_weight']*100)>=$row['passpercent']))
1446                                     continue 2;
1447                                 
1448                                 $num_of_attempts++;
1449                         }
1450                         
1451                         if ($row['num_takes'] != AT_TESTS_TAKE_UNLIMITED && $num_of_attempts >= $row['num_takes'])
1452                         {
1453                                 $msg->addInfo(array('PRETEST_FAILED',$row['title']));
1454                         }
1455                         else
1456                                 return $row['test_id'];
1457                 }
1458                 return 0;
1459         }
1460
1461         /** 
1462          * Return true if this content page allows export, else false.
1463          * @param       int     content id
1464          * @return      true if 'allow_test_export'==1 || is instructor || oauth export into Transformable
1465          */
1466         function allowTestExport($content_id){
1467                 if (isset($_SESSION['is_admin']) || (isset($_REQUEST['m']) && isset($_REQUEST['c']))) {
1468                         return true;
1469                 }
1470                 $sql = "SELECT allow_test_export FROM ".TABLE_PREFIX."content WHERE content_id=$content_id";
1471                 $result = mysql_query($sql, $this->db);
1472                 if ($row = mysql_fetch_assoc($result)){
1473                         if ($row['allow_test_export'] == 1){
1474                                 return true;
1475                         }
1476                         return false;
1477                 }
1478                 return false;
1479         }
1480
1481         /**
1482          * This function returns an array of content tools' shortcuts
1483          * @access: public
1484          * @param: $content_row: an array of the current content information
1485          * @return: an array of all the tool shortcuts that apply to the current content or content folder
1486          */
1487         public static function getToolShortcuts($content_row)
1488         {
1489                 global $_base_href, $contentManager;
1490                 
1491                 $shortcuts = array();
1492                 if ((   ($content_row['r_date'] <= $content_row['n_date'])
1493                                 && ((!$content_row['content_parent_id'] && ($_SESSION['packaging'] == 'top'))
1494                                         || ($_SESSION['packaging'] == 'all'))
1495                         ) || authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) {
1496                         $shortcuts[] = array('title' => _AT('export_content'), 'url' => $_base_href . 'mods/_core/imscp/ims_export.php?cid='.$content_row['content_id'], 'icon' => $_base_href . 'images/download.png');
1497                 }
1498                 
1499                 if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) {
1500                         if ($content_row['content_type'] == CONTENT_TYPE_CONTENT || $content_row['content_type'] == CONTENT_TYPE_WEBLINK) {
1501                                 $shortcuts[] = array(
1502                                 'title' => _AT('edit_this_page'),   
1503                                 'url' => $_base_href . 'mods/_core/editor/edit_content.php?cid='.$content_row['content_id'],
1504                                 'icon' => $_base_href . 'images/medit.gif');
1505                         }
1506                         $shortcuts[] = array(
1507                                 'title' => _AT('add_sibling_folder'), 
1508                                 'url' => $_base_href.'mods/_core/editor/edit_content_folder.php?pid='.$contentManager->_menu_info[$content_row['content_id']]['content_parent_id'], 
1509                                 'icon' => $_base_href . 'images/folder_new_sibling.gif');
1510                 
1511                         if ($content_row['content_type'] == CONTENT_TYPE_FOLDER) {
1512                                 $shortcuts[] = array(
1513                                         'title' => _AT('add_sub_folder'),   
1514                                         'url' => $_base_href . 'mods/_core/editor/edit_content_folder.php?pid='.$content_row['content_id'], 
1515                                         'icon' => $_base_href . 'images/folder_new_sub.gif');
1516                         }
1517                         
1518                         $shortcuts[] = array(
1519                                 'title' => _AT('add_sibling_page'), 
1520                                 'url' => $_base_href.'mods/_core/editor/edit_content.php?pid='.$contentManager->_menu_info[$content_row['content_id']]['content_parent_id'], 
1521                                 'icon' => $_base_href . 'images/page_add_sibling.gif');
1522
1523                         if ($content_row['content_type'] == CONTENT_TYPE_CONTENT || $content_row['content_type'] == CONTENT_TYPE_WEBLINK) {
1524                                 $shortcuts[] = array(
1525                                         'title' => _AT('delete_this_page'), 
1526                                         'url' => $_base_href . 'mods/_core/editor/delete_content.php?cid='.$content_row['content_id'], 
1527                                         'icon' => $_base_href . 'images/page_delete.gif');
1528                         }
1529                         else if ($content_row['content_type'] == CONTENT_TYPE_FOLDER) {
1530                                 $shortcuts[] = array(
1531                                         'title' => _AT('add_sub_page'),     
1532                                         'url' => $_base_href . 'mods/_core/editor/edit_content.php?pid='.$content_row['content_id'], 
1533                                         'icon' => $_base_href . 'images/page_add_sub.gif');
1534                                 $shortcuts[] = array(
1535                                         'title' => _AT('delete_this_folder'), 
1536                                         'url' => $_base_href . 'mods/_core/editor/delete_content.php?cid='.$content_row['content_id'], 
1537                                         'icon' => $_base_href . 'images/page_delete.gif');
1538                         }
1539                 }
1540                 
1541                 return $shortcuts;
1542         }
1543 }
1544
1545 ?>