1 : <?php
2 : /**
3 : * PHPDevShell is a RAD Framework aimed at developing administrative applications.
4 : *
5 : * @package PHPDevShell
6 : * @link http://www.phpdevshell.org
7 : * @copyright Copyright (C) 2007 Jason Schoeman, All rights reserved.
8 : * @license GNU/LGPL, see readme/licensed_under_lgpl or http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
9 : * @author Jason Schoeman, Contact: titan [at] phpdevshell [dot] org.
10 : *
11 : * Copyright notice: See readme/notice
12 : * By using PHPDevShell you agree to notice and license, if you dont agree to this notice/license you are not allowed to use PHPDevShell.
13 : *
14 : * This is the main engine.
15 : */
16 : /**
17 : *
18 : */
19 : class PHPDS_navigation extends navigation
20 1 : {
21 : /**
22 : * @var array
23 : */
24 : protected $breadcrumbArray = null;
25 : /**
26 : * @var string
27 : */
28 : public $child = null;
29 : /**
30 : * Holds all menu item information.
31 : *
32 : * @var array
33 : */
34 : public $navigation;
35 : /**
36 : * Holds all menu item information.
37 : *
38 : * @var array
39 : */
40 : public $navAlias;
41 :
42 : /**
43 : * This methods loads the menu structure, this according to permission and conditions.
44 : *
45 : * @return string
46 : * @author Jason Schoeman
47 : */
48 : public function extractMenu ()
49 : {
50 0 : $db = $this->db;
51 : // Get all users role ids.
52 0 : $all_user_roles = $this->user->getRoles($this->configuration['user_id']);
53 : // Get required menu data.
54 0 : if ($db->cacheEmpty('navigation')) {
55 0 : if (empty($this->navigation)) $this->navigation = array();
56 0 : if (empty($this->child)) $this->child = array();
57 0 : if (empty($this->navAlias)) $this->alias = array();
58 0 : $db->invokeQuery('NAVIGATION_extractMenuQuery', $all_user_roles);
59 :
60 : // Write menu data to cache.
61 0 : $db->cacheWrite('navigation', $this->navigation);
62 0 : $db->cacheWrite('child_navigation', $this->child);
63 0 : $db->cacheWrite('nav_alias', $this->navAlias);
64 0 : } else {
65 0 : $this->navigation = $db->cacheRead('navigation');
66 0 : $this->child = $db->cacheRead('child_navigation');
67 0 : $this->navAlias = $db->cacheRead('nav_alias');
68 : }
69 :
70 0 : return $this;
71 : }
72 :
73 : /**
74 : * Determines what the menu item should be named.
75 : *
76 : * @param string $replacement_name
77 : * @param string $menu_link
78 : * @param int $menu_id
79 : * @return string
80 : */
81 : public function determineMenuName ($replacement_name = '', $menu_link = '', $menu_id = false, $plugin='')
82 : {
83 0 : $lang = $this->lang;
84 : // Do we have a language replacement for this item?
85 0 : if (! empty($replacement_name)) {
86 0 : return __("$replacement_name", "$plugin");
87 0 : } else if (! empty($lang[$menu_id])) {
88 0 : return $lang[$menu_id];
89 0 : } else if (! empty($lang[$menu_link])) {
90 0 : return $lang[$menu_link];
91 : } else {
92 0 : return $menu_link;
93 : }
94 : }
95 :
96 : /**
97 : * Returns true if menu should show.
98 : *
99 : * @param integer $hide_type
100 : * @param integer $menu_id
101 : */
102 : public function showMenu ($hide_type, $menu_id = false)
103 : {
104 : // Check if we have a special condition.
105 0 : if (! empty($menu_id) && ($hide_type == 4) && $this->configuration['m'] == $menu_id) {
106 0 : return true;
107 : } else {
108 : // Check if we should show.
109 0 : if ($hide_type == 0 || $hide_type == 2) {
110 0 : return true;
111 : } else {
112 0 : return false;
113 : }
114 : }
115 : }
116 :
117 : /**
118 : * Compiles menu items in order.
119 : *
120 : * @return string
121 : * @author Jason Schoeman
122 : */
123 : public function createMenuStructure ()
124 : {
125 : // Define.
126 0 : $menu = false;
127 0 : $configuration = $this->configuration;
128 0 : $nav = $this->navigation;
129 : // Make sure array is not empty, otherwise error will be produced.
130 0 : if (! empty($nav)) {
131 : // Get menu group to compare against.
132 0 : if (! empty($nav[$configuration['m']]['is_parent'])) {
133 0 : $menu_group = $configuration['m'];
134 0 : } else {
135 0 : if (! empty($nav[$configuration['m']]['parent_menu_id'])) {
136 0 : $menu_group = $nav[$configuration['m']]['parent_menu_id'];
137 0 : } else {
138 0 : $menu_group = 0;
139 : }
140 : }
141 : // Start dynamic render engine.
142 0 : foreach ($nav as $currentkey => $m) {
143 : // Check if active item is parent.
144 0 : if ($this->showMenu($m['hide'], $m['menu_id']) && $nav[$m['menu_id']]['parent_menu_id'] == $menu_group) {
145 :
146 : // Check if current looped url is active.
147 0 : ($m['menu_id'] == $configuration['m']) ? $url_active = 'current' : $url_active = 'inactive';
148 : // Swith menu types.
149 0 : if ($m['is_parent'] == 1) {
150 : // Root item with Children.
151 0 : $call_family = $this->callFamily($m['menu_id']);
152 0 : if (! empty($call_family)) {
153 0 : $call_family = '<ul class="navparent">' . $call_family . '</ul>';
154 0 : $p_type = 'grandparent';
155 0 : } else {
156 0 : $p_type = $url_active;
157 : }
158 0 : $menu .= "<li class=\"$p_type\">" . sprintf($m['url'], '<span class="nav-grand"></span>') . $call_family . '</li>';
159 0 : } else {
160 : // Root item with no Children.
161 0 : $menu .= "<li class=\"$url_active\">" . sprintf($m['url'], '<span class="child"></span>') . '</li>';
162 : }
163 0 : }
164 0 : }
165 : // If no menu item is present, at least call its parent!
166 0 : if (empty($menu)) {
167 0 : $menu = '<li class="current">' . sprintf($nav[$configuration['m']]['url'], '') . '</li>';
168 0 : }
169 0 : }
170 : // Return Menu.
171 0 : return $menu;
172 :
173 : }
174 :
175 : /**
176 : * Assists write_menu in calling menu children.
177 : *
178 : * @param int $menu_id
179 : */
180 : public function callFamily ($menu_id = false)
181 : {
182 : // Define.
183 0 : $menu = false;
184 0 : $configuration = $this->configuration;
185 0 : $nav = $this->navigation;
186 : // Only loop when we have something.
187 0 : if (! empty($this->child[$menu_id])) {
188 : // Loop menu items finding its children.
189 0 : $child = $this->child[$menu_id];
190 0 : foreach ($child as $currentkey => $m) {
191 : // Should item be shown.
192 0 : if ($this->showMenu($nav[$m]['hide'], $m)) {
193 : // Check if current looped url is active.
194 0 : ($m == $configuration['m']) ? $url_active = 'current' : $url_active = 'inactive';
195 : // Check if we have a parent, if we do, we need to call her children.
196 0 : if ($nav[$m]['is_parent'] == 1) {
197 : // Child item with Children.
198 0 : $call_family = $this->callFamily($m);
199 0 : if (! empty($call_family)) {
200 0 : $call_family = '<ul class="ulchild">' . $call_family . '</ul>';
201 0 : $p_type = 'parent';
202 0 : } else {
203 0 : $p_type = $url_active;
204 : }
205 0 : $menu .= "<li class=\"$p_type\">" . sprintf($nav[$m]['url'], '<span class="nav-parent"></span>') . $call_family . '</li>';
206 0 : } else {
207 : // Child item without Children.
208 0 : $menu .= "<li class=\"$url_active\">" . sprintf($nav[$m]['url'], '<span class="child"></span>') . '</li>';
209 : }
210 0 : }
211 0 : }
212 0 : }
213 0 : return $menu;
214 : }
215 :
216 : /**
217 : * This method compiles the history tree seen, this is the tree that the user sees expand when going deeper into menu levels.
218 : * On the default template this is the navigation link string top left above the menus.
219 : *
220 : * @return string
221 : */
222 : public function createBreadcrumbs ()
223 : {
224 0 : $configuration = $this->configuration;
225 0 : $nav = $this->navigation;
226 : // Check if user has permission to view Jump Menu!
227 0 : if (isset($nav['940041356']['menu_id'])) {
228 0 : $jump_menu = '<li class="jump"><a href="' . $this->buildURL(940041356) . '"><span></span>' . __('Dashboard', 'PHPDevShell') . '</a></li>';
229 0 : } else {
230 0 : $jump_menu = '';
231 : }
232 : // Front Page.
233 0 : $main_item = '<li class="home"><a href="' . $configuration['absolute_url'] . '/"><span></span>' . ___('Front Page') . '</a></li>' . $jump_menu;
234 : // Call breadcrumb method.
235 0 : $this->callbackParentItem($configuration['m']);
236 : // Set.
237 0 : $history_url = '';
238 0 : foreach (array_reverse($this->breadcrumbArray, true) as $key => $breadcrumb_id) {
239 0 : if (! empty($nav[$breadcrumb_id]['menu_name']) && (($breadcrumb_id != $configuration['m']) || ($key != 0))) {
240 0 : if ($nav[$breadcrumb_id]['is_parent']) {
241 0 : $bread_parent = $this->callFamily($breadcrumb_id);
242 0 : if (! empty($bread_parent))
243 0 : $bread_parent = '<ul class="breadparent">' . $bread_parent . '</ul>';
244 0 : $history_url .= '<li class="grandparent">' . sprintf($nav[$breadcrumb_id]['url'], '<span class="nav-grand"></span>') . $bread_parent . '</li>';
245 0 : } else {
246 0 : $history_url .= '</li>' . sprintf($nav[$breadcrumb_id]['url'], '') . '</a></li>';
247 : }
248 0 : }
249 0 : }
250 : // Go up one level.
251 0 : if (! empty($nav[$configuration['m']]['parent_menu_id'])) {
252 0 : $up_parent_id = $nav[$configuration['m']]['parent_menu_id'];
253 0 : if (! empty($nav[$up_parent_id]['type']) && $nav[$up_parent_id]['type'] > 3 ) {
254 0 : $up_parent_id = 0;
255 0 : }
256 0 : } else {
257 0 : $up_parent_id = 0;
258 : }
259 : // Create Up link.
260 0 : if (! empty($up_parent_id)) {
261 : // Create level up icon.
262 0 : $up = '<li class="up"><a href="' . $this->buildURL($up_parent_id) . '"><span></span>' . ___('Up') . '</a></li>';
263 0 : } else {
264 0 : $up = '';
265 : }
266 : // Do we have the history URL?
267 0 : if (empty($history_url)) $history_url = false;
268 : // Return items, in order.
269 0 : return $main_item . $history_url . $up;
270 : }
271 :
272 : /**
273 : * Method assists method generate_history_tree in getting breadcrumb links.
274 : *
275 : * @param integer
276 : * @return array
277 : */
278 : private function callbackParentItem ($menu_id_)
279 : {
280 0 : $nav = $this->navigation;
281 : // Check what called menu items parent id is.
282 0 : if (! empty($nav[$menu_id_]['parent_menu_id'])) {
283 0 : $recall_parent_menu_id = $nav[$menu_id_]['parent_menu_id'];
284 0 : } else {
285 0 : $recall_parent_menu_id = 0;
286 : }
287 : // Return results.
288 0 : $this->breadcrumbArray[] = $menu_id_;
289 : // Call function itself to see if this item has a parent item.
290 0 : if ($recall_parent_menu_id != 0) {
291 0 : $this->callbackParentItem($recall_parent_menu_id);
292 0 : }
293 0 : }
294 :
295 : /**
296 : * Simply returns current menu id.
297 : *
298 : * @return int
299 : */
300 : public function currentMenuID()
301 : {
302 0 : return $this->configuration['m'];
303 : }
304 :
305 : /**
306 : * Will try and locate the full path of a filename of a given menu id, if it is a link, the original filename will be returned.
307 : *
308 : * @param int $menu_id
309 : * @param string $plugin
310 : * @return string
311 : */
312 : public function menuFile ($menu_id=false, $plugin=false)
313 : {
314 0 : if (empty($menu_id)) $menu_id = $this->configuration['m'];
315 0 : $absolute_path = $this->configuration['absolute_path'];
316 0 : list($plugin, $menu_link) = $this->menuPath($menu_id, $plugin);
317 0 : if (file_exists($absolute_path . 'plugins/' . $plugin . '/controllers/' . $menu_link)) {
318 0 : return $absolute_path . 'plugins/' . $plugin . '/controllers/' . $menu_link;
319 0 : } else if (file_exists($absolute_path . 'plugins/' . $plugin . '/' . $menu_link)) {
320 0 : return $absolute_path . 'plugins/' . $plugin . '/' . $menu_link;
321 : } else {
322 0 : return false;
323 : }
324 : }
325 :
326 : /**
327 : * Will locate the menus item full path.
328 : *
329 : * @param int $menu_id
330 : * @param string $plugin
331 : * @return array
332 : */
333 : public function menuPath ($menu_id=false, $plugin=false)
334 : {
335 0 : $configuration = $this->configuration;
336 0 : $navigation = $this->navigation;
337 : // Do we need a custom menu id link?
338 0 : if (empty($configuration['m']))
339 0 : $configuration['m'] = 0;
340 0 : if (empty($menu_id)) $menu_id = $configuration['m'];
341 : // Obtain correct menu file.
342 0 : if (empty($navigation[$menu_id]['extend'])) {
343 0 : if (!empty($navigation[$menu_id])) {
344 0 : $menu_link = $navigation[$menu_id]['menu_link'];
345 : // Set plugin for this menu item.
346 0 : if (empty($plugin))
347 0 : $plugin = $navigation[$menu_id]['plugin'];
348 0 : }
349 0 : } else {
350 0 : $extend = $navigation[$menu_id]['extend'];
351 0 : $menu_link = $navigation[$extend]['menu_link'];
352 : // Set plugin for this menu item.
353 0 : if (empty($plugin))
354 0 : $plugin = $navigation[$extend]['plugin'];
355 : }
356 0 : if (empty($plugin))
357 0 : $plugin = 'PHPDevShell';
358 0 : if (empty($menu_link))
359 0 : $menu_link = '';
360 : // Create location path.
361 0 : return array($plugin, $menu_link);
362 : }
363 :
364 : /**
365 : * Will return the url for a certain menu item when path is provided.
366 : * @param string $item_path The string to the path of the menu item, 'user/control-panel.php'
367 : * @param string $plugin_name The plugin name to look for it under, if empty, active plugin will be used.
368 : * @param string $extend_url Will extend url with some get values.
369 : * @return string Will return complete and cleaned sef url if available else normal url will be returned.
370 : */
371 : public function buildURLFromPath ($item_path, $plugin_name = '', $extend_url = '')
372 : {
373 : // What is our developer looking for?
374 0 : if (empty($plugin_name))
375 0 : $plugin_name = $this->core->activePlugin();
376 0 : $lookup = array('plugin'=>$plugin_name, 'menu_link'=>$item_path);
377 : // Lets loop and look if we can find anything.
378 0 : $menu_id = PU_ArraySearch($lookup, $this->navigation);
379 0 : if (! empty($menu_id)) {
380 0 : return $this->buildURL($menu_id, $extend_url);
381 : } else {
382 0 : return $this->pageNotFound();
383 : }
384 : }
385 :
386 : /**
387 : * Returns the correct string for use in href when creating a link for a menu id. Will return sef url if possible.
388 : * Will return self url when no menu id is given. No starting & or ? is needed, this gets auto determined!
389 : * If left empty it will return current active menu.
390 : *
391 : * @param mixed The menu id or menu file location to create a url from.
392 : * @param string extend_url
393 : * @param boolean strip_trail Will strip unwanted empty operators at the end.
394 : * @return string
395 : * @author Jason Schoeman
396 : */
397 : public function buildURL ($menu_id = false, $extend_url = '', $strip_trail = '')
398 : {
399 : // Should we assign menu id.
400 0 : if (empty($menu_id)) $menu_id = $this->configuration['m'];
401 : // Can we use sef url?
402 0 : if (! empty($this->configuration['sef_url'])) {
403 : // We first need to check if we have such an alias available:
404 0 : if (empty($this->navigation["$menu_id"]['alias'])) {
405 0 : $alias = $this->db->invokeQuery('NAVIGATION_findAliasQuery', $menu_id);;
406 0 : } else {
407 0 : $alias = $this->navigation["$menu_id"]['alias'];
408 : }
409 : // Add extra string. Thought not 100% correct, this is for backwards compatibility.
410 0 : if (! empty($extend_url)) {
411 0 : $extend_url = "?$extend_url";
412 0 : } else if (! empty($strip_trail)) {
413 0 : $extend_url = false;
414 0 : } else {
415 0 : $extend_url = '?';
416 : }
417 0 : $url_append = empty($this->configuration['url_append']) ? '' : $this->configuration['url_append'];
418 : // Menu id is set for sef mode, so return correct integer id.
419 0 : $url = $alias . $url_append . "$extend_url";
420 0 : } else {
421 : // Add extra string.
422 0 : if (! empty($extend_url)) {
423 0 : $extend_url = "&$extend_url";
424 0 : } else {
425 0 : $extend_url = false;
426 : }
427 : // Menu id is an integer.
428 0 : $url = 'index.php?m=' . "$menu_id" . "$extend_url";
429 : }
430 0 : if (! empty($url)) {
431 0 : return $this->configuration['absolute_url'] . "/$url";
432 : } else {
433 0 : return false;
434 : }
435 : }
436 :
437 : /**
438 : * Parses the REQUEST_URI to get the page id
439 : *
440 : * @date 20101007 (v1.0.2) (greg) moved from PHPDS to PHPDS_navigation ; little cleanup
441 : * @date 20100109
442 : * @version 1.0.2
443 : * @author Ross Kuyper
444 : */
445 : public function parseRequestString()
446 : {
447 0 : $configuration = $this->configuration;
448 : // let's set up a default page
449 0 : if ($this->user->isLoggedIn()) {
450 0 : $configuration['m'] = $configuration['front_page_id_in'];
451 0 : } else {
452 0 : $configuration['m'] = $configuration['front_page_id'];
453 : }
454 0 : if (! empty($_GET['m'])) {
455 0 : $configuration['m'] = $_GET['m'];
456 0 : $get_menu_id = $_GET['m'];
457 0 : } else {
458 0 : $get_menu_id = NULL;
459 : }
460 :
461 : // Give SEF rewrites priority if active
462 0 : if(! empty($configuration['sef_url'])) {
463 :
464 : // if the request string is empty, we're on the frontpage
465 0 : if(! empty($_SERVER['REQUEST_URI'])) {
466 :
467 : // Find everything before ?
468 0 : list($req) = explode('?', $_SERVER['REQUEST_URI']);
469 :
470 : // Replace base patch dir.
471 0 : $basepath = parse_url($configuration['absolute_url'], PHP_URL_PATH);
472 :
473 : // Destroy base paths.
474 0 : $req = trim(str_replace($basepath, '', $req),'/');
475 :
476 : // Make sure we have correct value.
477 0 : $uriarray = explode('/', $req);
478 :
479 : // Final alias should be cool now...
480 0 : $alias = array_shift($uriarray);
481 :
482 : // All of the rest of $uriarray should be variables.
483 0 : foreach ($uriarray as $get) {
484 : // First one is key.
485 0 : if (empty($key)) {
486 0 : $key = $get;
487 0 : } else {
488 0 : $getarray[$key] = $get;
489 0 : $key = '';
490 : }
491 0 : }
492 :
493 : // Assign variables to get.
494 0 : if (! empty($getarray)) {
495 0 : if(! empty($_GET)) $_GET = array_merge($getarray, $_GET);
496 0 : else $_GET = $getarray;
497 0 : }
498 :
499 0 : if (!empty($alias) && ($alias != 'index.php')) {
500 : // Is there a set URL append?
501 0 : if (!empty($configuration['url_append']) ) $alias = str_replace($configuration['url_append'], '', $alias);
502 :
503 0 : if (isset($this->navAlias[$alias])) {
504 0 : $configuration['m'] = $this->navAlias[$alias];
505 0 : } else {
506 : // Check if we can locate alias in database.
507 0 : $required_menu_id = $this->db->invokeQuery('NAVIGATION_findMenuQuery', $alias, $get_menu_id);
508 0 : if (empty($required_menu_id)) {
509 : // This means, the url really does not exist, lets just forward user to 404.
510 : //throw new PHPDS_exception(___('Page not found'), 404);
511 0 : $this->template->stopScript = array('type'=>'404','message'=>___('Page not found'));
512 0 : } else {
513 0 : if ($this->user->isLoggedIn()) {
514 0 : $this->template->stopScript = array('type'=>'403','message'=>___('Page found, however you dont have the required permission to access this page.'));
515 : //throw new PHPDS_securityException403(___('Page found, however you dont have the required permission to access this page.'), 403);
516 0 : } else {
517 0 : $this->template->stopScript = array('type'=>'auth','message'=>___('Authentication Required'));
518 : //$this->template->stopScript = ___('Authentication Required');
519 0 : $configuration['m'] = $required_menu_id;
520 : }
521 : }
522 : }
523 0 : }
524 0 : }
525 0 : }
526 :
527 0 : return $this;
528 : }
529 :
530 : /**
531 : * This method saves the current URL with the option to add more $this->security->get variables like ("&variable1=1&variable2=2")
532 : * This is mostly used for when additional $this->security->get variables are required! Usefull when using forms.
533 : *
534 : * @param string Add more $this->security->get variables like ("&variable1=1&variable2=2")
535 : * @return string
536 : * @author Jason Schoeman
537 : */
538 : public function selfUrl ($extra_get_variables = false)
539 : {
540 : // Workaround.
541 0 : return $this->buildURL(false, $extra_get_variables, true);
542 : }
543 :
544 : /**
545 : * Will convert any given plugin script location to its correct url.
546 : *
547 : * @param $file_path The full file path, "DummyPlugin/sample/sample1.php"
548 : * @param $extend_url Should the url be extended with $_GET vars, 'e=12'
549 : * @param $strip_trail Will strip unwanted empty operators at the end.
550 : * @return string
551 : */
552 : public function purl ($file_path, $extend_url = false, $strip_trail = false)
553 : {
554 : // Menu id.
555 0 : $menu_id = $this->createMenuId($file_path);
556 :
557 : // Get page url.
558 0 : return $this->buildURL($menu_id, $extend_url, $strip_trail);
559 : }
560 :
561 : /**
562 : * Convert plugin file location to unsigned CRC32 value. This is unique and allows one to locate a menu item from location as well.
563 : *
564 : * @param string The plugin folder the file is in.
565 : * @return integer
566 : * @author Jason Schoeman
567 : */
568 : public function createMenuId ($path)
569 : {
570 : // Create menu id from string.
571 0 : return sprintf('%u', crc32(str_ireplace('/', '', $path)));
572 : }
573 :
574 : /**
575 : * Redirects to new url.
576 : *
577 : * @param string URL to redirect to.
578 : * @param integer Time in seconds before redirecting.
579 : * @author Jason Schoeman
580 : */
581 : public function redirect ($url = false, $time = 0)
582 : {
583 : // Check if we only need to redirect to the same page, a simple refresh.
584 0 : if ($url == false) {
585 0 : $redirect_url = '<META HTTP-EQUIV="refresh" CONTENT="' . $time . '; URL=' . $this->buildURL($this->configuration['m']) . '">';
586 0 : } else {
587 0 : $redirect_url = '<META HTTP-EQUIV="refresh" CONTENT="' . $time . '; URL=' . $url . '">';
588 : }
589 0 : print $redirect_url;
590 0 : }
591 :
592 : /**
593 : * Returns the url of the 404 page selected by the admin.
594 : *
595 : * @return string
596 : */
597 : public function pageNotFound ()
598 : {
599 : // Menu id.
600 0 : $menu_id = $this->db->getSettings(array('404_error_page'), 'PHPDevShell');
601 :
602 : // Get page url.
603 0 : return $this->buildURL($menu_id['404_error_page']);
604 : }
|