moved code up one level to eliminate the docs subdirectory
[acontent.git] / include / classes / Menu.class.php
1 <?php
2 /************************************************************************/
3 /* AContent                                                             */
4 /************************************************************************/
5 /* Copyright (c) 2010                                                   */
6 /* Inclusive Design Institute                                           */
7 /*                                                                      */
8 /* This program is free software. You can redistribute it and/or        */
9 /* modify it under the terms of the GNU General Public License          */
10 /* as published by the Free Software Foundation.                        */
11 /************************************************************************/
12
13 /**
14  * Menu
15  * 1. Generate main menu items based on login user
16  * 2. Generate path in bread crumb
17  * 3. Decide the page to display (redirect) based on login user's privilege.
18  *    This page is set as current page
19  * 4. Generate sub menus of current page
20  * 5. Generate back to page of current page
21  * @access      public
22  * @author      Cindy Qi Li
23  * @package     Menu
24  */
25
26 if (!defined('TR_INCLUDE_PATH')) exit;
27
28 require_once(TR_INCLUDE_PATH. 'classes/DAO/PrivilegesDAO.class.php');
29 require_once(TR_INCLUDE_PATH. 'classes/Utility.class.php');
30
31 class Menu {
32
33         // all private
34         var $pages;                               // top tab pages
35         var $current_page;                        // current page
36         var $root_page;                           // root page relative to current page
37         var $breadcrumb_path = array();           // array of breadcrumb path
38         var $sub_menus;                           // array of sub-menus of current page
39         var $path;                                // array of all parent pages to current page, used for breadcrumb path and generating back to page
40         var $back_to_page;                        // string of parent page to go back to
41
42         /**
43          * Constructor: Initialize top pages (tab menu), all pages accessible by current user, current page.
44          * Generate top tab menu items based on session user_id. If no user login in (public view), use public menu
45          * @access  public
46          * @param   None
47          * @author  Cindy Qi Li
48          */
49         function Menu()
50         {
51                 $this->pages[TR_NAV_TOP] = array();        // top tab pages
52
53                 $this->init();           // Initialize $this->pages[TR_NAV_PUBLIC] & $this->pages
54                 $this->setTopPages();    // set top pages based on user id
55
56                 // decide current page.
57                 // if the page that user tries to access is from one of the public link
58                 // but not define in user's priviledge pages, re-direct to the first $this->pages[TR_NAV_TOP]
59                 $this->setCurrentPage();
60                 $this->sub_menus = $this->setSubMenus($this->current_page);   // loop recursively to set $this->submenus to the top parent of $this->current_page
61                 $this->root_page = $this->setRootPage($this->current_page);  
62                 $this->path = $this->setPath($this->current_page);
63                 $this->back_to_page = $this->setBackToPage();
64         }
65
66         /**
67          * initialize: public accessible items ($this->pages[TR_NAV_PUBLIC]); all accessible pages ($this->pages)
68          * @access  private
69          * @param   user id
70          * @return  true
71          * @author  Cindy Qi Li
72          */
73         private function init()
74         {
75                 // $_pages is defined in include/constants.inc.php
76                 global $_pages, $_base_path;
77
78                 // initialize $this->pages
79                 $this->pages = $_pages;
80                 // end of initializing $this->pages
81                 
82                 $priviledgesDAO = new PrivilegesDAO();
83                 $rows = $priviledgesDAO->getPublicPrivileges();
84
85                 if (is_array($rows))
86                 {
87                         foreach ($rows as $id => $row)
88                         {
89                                 $this->pages[TR_NAV_PUBLIC][] = array($row['link'] => array('title_var'=>$row['title_var'], 'parent'=>TR_NAV_TOP));
90                         }
91                 }
92                 // end of initializing $this->pages[TR_NAV_PUBLIC]
93
94                 return true;
95         }
96
97         /**
98          * Set top pages array based on login user's priviledge. If there's no login user, use priviledges that are open to public.
99          * @access  private
100          * @param   none
101          * @return  true
102          * @author  Cindy Qi Li
103          */
104         private function setTopPages()
105         {
106                 global $_base_path, $_course_id, $_content_id;
107
108                 $priviledgesDAO = new PrivilegesDAO();
109
110                 if (isset($_SESSION['user_id']) && $_SESSION['user_id'] <> 0)
111                 {
112                         $rows = $priviledgesDAO->getUserPrivileges($_SESSION['user_id']);
113                 }
114                 else // public pages
115                 {
116                         $rows = $priviledgesDAO->getPublicPrivileges();
117                 }
118                 if (is_array($rows))
119                 {
120                         foreach ($rows as $id => $row)
121                         {
122                                 // replace the required constants in link
123                                 $row['link'] = Utility::replaceConstants($row['link']);
124                                 list($url, $param) = Utility::separateURLAndParam($row['link']);
125                                 if (Utility::authenticate($row['user_requirement'], false)) {
126                                         $this->pages[TR_NAV_TOP][] = array('url' => $_base_path.$row['link'], 
127                                                                    'title' => _AT($row['title_var']),
128                                                                        'param' => $param);
129                                 }
130                                 
131                                 // add section pages if it has not been defined in $this->pages
132                                 if (!isset($this->pages[$url]))
133                                 {
134                                     $this->pages = array_merge($this->pages, 
135                                                            array($url => array('title_var'=>$row['title_var'], 'parent'=>TR_NAV_TOP, 'param' => $param)));
136                                 }
137                                 else
138                                 {
139                                         $this->pages[$url]['param'] = $param;
140                                 }
141                         }
142                 }
143                 
144                 return true;
145         }
146
147         /**
148          * Decide current page.
149          * if the page that user tries to access is from one of the public link
150          * but not define in user's priviledge pages, re-direct to the first $this->pages[TR_NAV_TOP]
151          * @access  private
152          * @return  true
153          * @author  Cindy Qi Li
154          */
155         private function setCurrentPage()
156         {
157                 global $_base_path, $_base_href, $msg;
158
159                 $this->current_page = substr($_SERVER['PHP_SELF'], strlen($_base_path));
160
161                 if (!isset($this->pages[$this->current_page]))
162                 {
163                         if (!$this->isPublicLink($this->current_page))  // report error if the link is not from a public link
164                         {
165                                 $msg->addError(array('PAGE_NOT_FOUND', $_base_href.$this->current_page));
166                         }
167
168                         // re-direct to first $_pages URL
169                         foreach ($this->pages[TR_NAV_TOP] as $page)
170                         {
171 //                              debug($_base_path.$this->current_page);debug($page);
172                                 if ($_base_path.$this->current_page != $page['url'])
173                                 {
174                                         header('Location: '.$page['url']);
175                                                 
176                                         // reset current_page after re-direction
177                                         $this->current_page = substr($_SERVER['PHP_SELF'], strlen($_base_path));
178                                                 
179                                         // Note: must exit. otherwise, the rest of includeheader.inc.php proceeds and prints out all messages
180                                         // which is not going to be displayed at re-directed page.
181                                         exit;
182                                 }
183                         }
184                 }
185         }
186
187         /**
188         * Set sub-menus of current page by $_pages[$current_page]['children']
189         * @access  private
190         * @return  true
191         * @author  Cindy Qi Li
192         */
193         private function setSubMenus($page) {
194                 global $_base_path, $_course_id;
195
196                 if (isset($page) && defined($page)) 
197                 {
198                         // reached the top
199                         return array();
200                 } 
201                 else if (isset($this->pages[$page]['children'])) 
202                 {
203                         $param = $this->getParam($page);
204 //                      $sub_menus[] = array('url' => $_base_path . $page.$param, 'title' => $this->getPageTitle($page), 'param' => $param);
205
206                         foreach ($this->pages[$page]['children'] as $child) 
207                         {
208                                 $this->pages[$child]['param'] = $param;
209                                 $sub_menus[] = array('url' => $this->addUrlParam($_base_path . $child, $param), 
210                                                     'title' => $this->getPageTitle($child), 
211                                                     'has_children' => isset($this->pages[$child]['children']),
212                                                     'param' => $param);
213                         }
214                 } 
215                 else if (isset($this->pages[$page]['parent'])) 
216                 {
217                         // no children
218                         return $this->setSubMenus($this->pages[$page]['parent']);
219                 }
220
221                 return $sub_menus;
222         }
223
224         /**
225         * Set the back to page of $this->current_page
226         * @access  private
227         * @return  true
228         * @author  Cindy Qi Li
229         */
230         private function setBackToPage() 
231         {
232                 $back_to_page = '';
233                 
234                 unset($this->path[0]);
235                 if (isset($this->path[2]['url'], $this->sub_menus[0]['url']) && $this->path[2]['url'] == $this->sub_menus[0]['url']) {
236                         $back_to_page = $this->path[3];
237                 } 
238                 else if (isset($this->path[1]['url'], $this->sub_menus[0]['url']) && $this->path[1]['url'] == $this->sub_menus[0]['url']) {
239                         $back_to_page = isset($this->path[2]) ? $this->path[2] : null;
240                 } 
241                 else if (isset($this->path[1])) {
242                         $back_to_page = $this->path[1];
243                 }
244                 
245                 return $back_to_page;
246         }
247         
248         /**
249          * Check if the given link is a pre-defined public link
250          * @access  private
251          * @param   $page
252          * @return  true  if is a pre-defined public link
253          *          false if not a pre-defined public link
254          * @author  Cindy Qi Li
255          */
256         private function isPublicLink($url)
257         {
258                 foreach ($this->pages[TR_NAV_PUBLIC] as $page => $garbage)
259                 {
260                         if ($page == $url) return true;
261                 }
262
263                 return false;
264         }
265
266         /**
267          * Return the page title of given page
268          * @access  private
269          * @param   $page
270          * @return  page title
271          *          empty if page is not defined
272          * @author  Cindy Qi Li
273          */
274         private function getPageTitle($page)
275         {
276                 if (isset($this->pages[$page]['title'])) 
277                 {
278                         $page_title = $this->pages[$page]['title'];
279                 } 
280                 else 
281                 {
282                         $page_title = _AT($this->pages[$page]['title_var']);
283                 }
284                 
285                 return $page_title;
286         }
287         
288         /**
289          * Return the URL with the given parameter attached. $param can have multiple parameters.
290          * The repetitive parameter is skipped
291          * @access  private
292          * @param   $url: URL, can be completed or not completed. For example: tests/index.php?a=1
293          *          $param: URL parameters. For example: ?a=3&b=4
294          * @return  the URL with the given parameter attached at the end. The repetitive parameter is skipped.
295          * @author  Cindy Qi Li
296          */
297         private function addUrlParam($url, $param)
298         {
299                 // remove '?'
300                 $param = str_replace('?', '', trim($param));
301                 if ($param == '') return $url;
302                 
303                 $has_question_mark = false;
304                 if (strpos($url, '?') > 0) $has_question_mark = true;
305                 else $counter = 0;
306                 
307                 $all_params = explode('&', $param);
308                 if (is_array($all_params)) {
309                         foreach ($all_params as $each_param)
310                         {
311                                 $pair = explode('=', $each_param);
312                                 // check if the parameter is already in the url
313                                 if (strpos($url, $pair[0].'=') > 0) continue;
314                                 else {
315                                         if ($has_question_mark || $counter > 0)
316                                                 $url .= '&'.$each_param;
317                                         else {
318                                                 $url .= '?'.$each_param;
319                                                 $counter++;
320                                         }
321                                 }
322                         }
323                 }
324                 
325                 return $url;
326         }
327         
328         /**
329          * Return all pages array
330          * @access  public
331          * @return  all pages array
332          * @author  Cindy Qi Li
333          */
334         public function getAllPages()
335         {
336                 return $this->pages;
337         }
338
339         /**
340          * Return top tab menu item array
341          * @access  public
342          * @return  top tab menu item array
343          * @author  Cindy Qi Li
344          */
345         public function getTopPages()
346         {
347                 return $this->pages[TR_NAV_TOP];
348         }
349
350         /**
351          * Return top tab menu item array
352          * @access  public
353          * @return  top tab menu item array
354          * @author  Cindy Qi Li
355          */
356         public function getCurrentPage()
357         {
358                 return $this->current_page;
359         }
360
361         /**
362          * Return sub menus of current page
363          * @access  public
364          * @return  top tab menu item array
365          * @author  Cindy Qi Li
366          */
367         public function getSubMenus()
368         {
369                 return $this->sub_menus;
370         }
371
372         /**
373          * Return back to page of current page
374          * @access  public
375          * @return  back to page array
376          * @author  Cindy Qi Li
377          */
378         public function getBackToPage()
379         {
380                 return $this->back_to_page;
381         }
382         
383         /**
384          * Set root page relative to the current page
385          * @access  public
386          * @return  root page
387          * @author  Cindy Qi Li
388          */
389         private function setRootPage($page)
390         {
391                 global $_base_path;
392
393                 $parent_page = $this->pages[$page]['parent'];
394
395                 if (isset($parent_page) && defined($parent_page)) // check if $parent_page is
396                 {
397                         return $_base_path . $page;
398                 }
399                 else if (isset($parent_page))
400                 {
401                         return $this->setRootPage($parent_page);
402                 }
403                 else
404                 {
405                         return $_base_path . $page;
406                 }
407         }
408
409         /**
410          * Return root page relative to the current page
411          * @access  public
412          * @return  root page
413          * @author  Cindy Qi Li
414          */
415         public function getRootPage()
416         {
417                 return $this->root_page;
418         }
419         
420         /**
421          * Return array of all parent items path to current page
422          * this array is used to determine back to page 
423          * @access  private
424          * @return  array of breadcrumb path
425          * @author  Cindy Qi Li
426          */
427         public function setPath($page)
428         {
429                 global $_base_path;
430
431                 // all children pages inherit URL parameter of the parent page
432                 $parent_page = $this->pages[$page]['parent'];
433                 $parent_page_param = $this->getParam($page);
434                 
435                 if (stripos($page, str_replace(array(SEP, '?'), array('', ''), $parent_page_param)) > 0) {
436                         $page_url = $page;
437                 } else {
438                         $page_url = $page.$parent_page_param;
439                 }
440
441                 $page_title = $this->getPageTitle($page);
442
443                 if (isset($parent_page) && defined($parent_page))
444                 {
445                         $path[] = array('url' => $_base_path . $page_url, 'title' => $page_title, 'param' => $parent_page_param);
446                 }
447                 else if (isset($parent_page))
448                 {
449                         $path[] = array('url' => $_base_path . $page_url, 'title' => $page_title, 'param' => $parent_page_param);
450                         $path = array_merge((array) $path, $this->setPath($parent_page));
451                 } else {
452                         $path[] = array('url' => $_base_path . $page_url, 'title' => $page_title, 'param' => $parent_page_param);
453                 }
454
455                 return $path;
456         }
457
458         /**
459          * Return breadcrumb path
460          * @access  public
461          * @return  root page
462          * @author  Cindy Qi Li
463          */
464         public function getPath()
465         {
466                 return $this->path;
467         }
468         
469         /**
470          * return "param" element of the given page or the parents of the given page
471          * @param $page
472          * @return "param" element value
473          */
474         private function getParam($page)
475         {
476                 if (isset($this->pages[$page]['param'])) return $this->pages[$page]['param'];
477                 
478                 if ($page == TR_NAV_TOP || $page == TR_NAV_PUBLIC) return '';
479                 
480                 if (isset($this->pages[$this->pages[$page]['parent']]['param']))
481                         return $this->pages[$this->pages[$page]['parent']]['param'];
482                 else
483                         return $this->getParam($this->pages[$page]['parent']);
484         }
485 }
486 ?>