1<?php
2/***********************************************************
3 Copyright (C) 2008-2012 Hewlett-Packard Development Company, L.P.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License version 2.1 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this library; if not, write to the Free Software Foundation, Inc.0
16 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 ***********************************************************/
18/**
19 * \file
20 * \brief Common menu functions
21 */
22const MENU_PATH_SEPARATOR = "::";   ///< Separator used between menu paths
23const MENU_BREAK = "[BREAK]";       ///< Break menu at this
24
25/**
26 * \class menu
27 * \brief
28 * Code for creating a menu list (2D linked list) from a set of plugins.
29 */
30class menu
31{
32  var $Name = "";       ///< Name of the menu item
33  var $URI = NULL;      ///< URI for the plugin (everything after the "?mod=")
34  var $HTML = NULL;     ///< HTML to include (if provided, used in place of all else)
35  var $Order = 0;       ///< Used for ordering menu items
36  var $Target = NULL;   ///< Recommended name of window for showing results
37  var $MaxDepth = 0;    ///< How deep is SubMenu?
38  var $SubMenu = NULL;  ///< Sub menu to show
39  public $FullName;     ///< List to submenu list
40
41  /**
42   * Return the name of the menu
43   * @param boolean $showFullName If true, return FullName and order, else
44   * return Name
45   * @return string
46   */
47  public function getName($showFullName=false)
48  {
49    if ($showFullName) {
50      return $this->FullName . "(" . $this->Order . ")";
51    }
52    return $this->Name;
53  }
54}
55
56/*********************************
57 Global array: don't touch!
58 *********************************/
59$MenuList = array();  ///< Global menu list array
60$MenuMaxDepth = 0;    ///< How deep is the tree (for UI display)
61/**
62 * \brief Create a "First Prev 1 2 ... Next Last" page links for paged output.
63 *
64 * \param int $Page       Page number of the current page
65 * \param int $TotalPage  Last page number
66 * \param string $Uri     URL of the page being displayed. "&page=" will be
67 * appended to the URL
68 *
69 * \return String containing menu HTML
70 */
71function MenuPage($Page, $TotalPage, $Uri = '')
72{
73  $V = "<font class='text'><center>";
74  if (empty($Uri)) {
75    $Uri = Traceback();
76  }
77  $Uri = preg_replace("/&page=[^&]*/", "", $Uri);
78  /* Create first page */
79  if ($Page > 0) {
80    $text = _("First");
81    $text1 = _("Prev");
82    $V.= "<a href='$Uri&page=0'>[$text]</a> ";
83    $V.= "<a href='$Uri&page=" . ($Page - 1) . "'>[$text1]</a> ";
84    if ($Page > 9) {
85      $V.= " ... ";
86    }
87  }
88  /* Create previous list page */
89  for ($i = $Page - 9;$i < $Page;$i++) {
90    if ($i >= 0) {
91      $V.= "<a href='$Uri&page=$i'>" . ($i + 1) . "</a> ";
92    }
93  }
94  /* Show current page number */
95  $V.= "<b>" . ($Page + 1) . "</b>";
96  /* Create next page */
97  for ($i = $Page + 1;($i <= $TotalPage) && ($i < $Page + 9);$i++) {
98    $V.= " <a href='$Uri&page=$i'>" . ($i + 1) . "</a>";
99  }
100  if ($Page < $TotalPage) {
101    if ($Page < $TotalPage - 9) {
102      $V.= " ...";
103    }
104    $text = _("Next");
105    $text1 = _("Last");
106    $V.= " <a href='$Uri&page=" . ($Page + 1) . "'>[$text]</a>";
107    $V.= " <a href='$Uri&page=" . ($TotalPage) . "'>[$text1]</a>";
108  }
109  $V.= "</center></font>";
110  return ($V);
111} // MenuPage
112
113/**
114 * \brief Create a "First Prev 1 2 ... Next" page links for paged output.
115 *
116 * \param int $Page   Page number of the current page
117 * \param bool $Next  True display "Next" and false don't display
118 * \param string $Uri URL of the page being displayed. "&page=" will be appended to the URL
119 *
120 * \return String containing menu HTML
121 */
122function MenuEndlessPage($Page, $Next = 1, $Uri = '')
123{
124  $V = "<font class='text'><center>";
125  if (empty($Uri)) {
126    $Uri = Traceback();
127  }
128  $Uri = preg_replace("/&page=[^&]*/", "", $Uri);
129  /* Create first page */
130  if ($Page > 0) {
131    $text = _("First");
132    $text1 = _("Prev");
133    $V.= "<a href='$Uri&page=0'>[$text]</a> ";
134    $V.= "<a href='$Uri&page=" . ($Page - 1) . "'>[$text1]</a> ";
135    if ($Page > 9) {
136      $V.= " ... ";
137    }
138  }
139  /* Create previous list page */
140  for ($i = $Page - 9;$i < $Page;$i++) {
141    if ($i >= 0) {
142      $V.= "<a href='$Uri&page=$i'>" . ($i + 1) . "</a> ";
143    }
144  }
145  /* Show current page number */
146  $V.= "<b>" . ($Page + 1) . "</b>";
147  /* Create next page */
148  if ($Next) {
149    $text = _("Next");
150    $i = $Page + 1;
151    $V.= " <a href='$Uri&page=$i'>" . ($i + 1) . "</a>";
152    $V.= " ... <a href='$Uri&page=$i'>[$text]</a>";
153  }
154  $V.= "</center></font>";
155  return ($V);
156} // MenuEndlessPage()
157
158/**
159 * \brief Compare two menu items for sorting.
160 *
161 * \param &$a menu a
162 * \param &$b menu b
163 *
164 * \return -1 a > b\n
165 *         1  a < b\n
166 *         0  a->Order = b->Order and a->Name = b->Name
167 */
168function menu_cmp(&$a, &$b)
169{
170  if ($a->Order > $b->Order) {
171    return (-1);
172  }
173  if ($a->Order < $b->Order) {
174    return (1);
175  }
176  $rc = strcmp($a->Name, $b->Name);
177  return (strcmp($a->Name, $b->Name));
178} // menu_cmp()
179
180/**
181 * \brief Given a Path, order level for the last
182 * item, and a plugin name, insert the menu item.
183 *
184 * This is VERY recursive and returns the new menu.
185 * If $URI is blank, nothing is added.
186 *
187 * @param[in,out] array &$menuItems Array of menu items. If null is passed,
188 * new array is created.
189 * @param array $path    Path of the menu item
190 * @param string $pathRemainder
191 * @param int $LastOrder  Order (position) of last menu item
192 * @param string $Target  Name of the Menu target
193 * @param string $URI     URI of the menu
194 * @param string $HTML    HTML of the menu
195 * @param string &$Title  Title of the menu
196 * @return int The max depth of menu
197 */
198function menu_insert_r(&$menuItems, $path, $pathRemainder, $LastOrder, $Target, $URI, $HTML, &$Title)
199{
200  $splitPath = explode(MENU_PATH_SEPARATOR, $pathRemainder, 2);
201  $pathElement = count($splitPath) > 0 ? $splitPath[0] : null;
202  $pathRemainder = count($splitPath) > 1 ? $splitPath[1] : null;
203  $hasPathComponent = $pathElement !== null && $pathElement !== "";
204
205  if (!$hasPathComponent) {
206    return 0;
207  }
208
209  $isLeaf = $pathRemainder === null;
210  $menuItemsExist = isset($menuItems) && is_array($menuItems);
211
212  $currentMenuItem = NULL;
213  if ($menuItemsExist) {
214    foreach ($menuItems as &$menuItem) {
215      // need to escape the [ ] or the string will not match
216      if (!strcmp($menuItem->Name, $pathElement) && strcmp($menuItem->Name, MENU_BREAK)) {
217        $currentMenuItem = $menuItem;
218        break;
219      } else if (!strcmp($menuItem->Name, MENU_BREAK) && ($menuItem->Order == $LastOrder)) {
220        $currentMenuItem = $menuItem;
221        break;
222      }
223    }
224  }
225
226  $path[] = $pathElement;
227  $FullName = str_replace(" ", "_", implode(MENU_PATH_SEPARATOR, $path));
228
229  $sortItems = false;
230  $currentItemIsMissing = empty($currentMenuItem);
231  if ($currentItemIsMissing) {
232    $currentMenuItem = new menu;
233    $currentMenuItem->Name = $pathElement;
234    $currentMenuItem->FullName = $FullName;
235
236    if (! $menuItemsExist) {
237      $menuItems = array();
238    }
239    array_push($menuItems, $currentMenuItem);
240    $sortItems = true;
241  }
242
243  /* $M is set! See if we need to traverse submenus */
244  if ($isLeaf) {
245    if ($LastOrder != 0) {
246      if ($currentMenuItem->Order != $LastOrder) {
247        $sortItems = true;
248      }
249      $currentMenuItem->Order = $LastOrder;
250    }
251    $currentMenuItem->Target = $Target;
252    $currentMenuItem->URI = $URI;
253    $currentMenuItem->HTML = $HTML;
254    $currentMenuItem->Title = $Title;
255  } else {
256    $Depth = menu_insert_r($currentMenuItem->SubMenu, $path, $pathRemainder, $LastOrder, $Target, $URI, $HTML, $Title);
257    $currentMenuItem->MaxDepth = max ($currentMenuItem->MaxDepth, $Depth + 1);
258  }
259
260  if ($sortItems) {
261    usort($menuItems, 'menu_cmp');
262  }
263
264  array_pop($path);
265  return ($currentMenuItem->MaxDepth);
266} // menu_insert_r()
267
268
269/**
270 * \brief Given a Path, order level for the last
271 * item, and optional plugin name, insert the menu item.
272 *
273 * \param string $Path   Path of the new menu item
274 * \param int $LastOrder Is used for grouping items in order.
275 * \param string $URI    URL link of the new menu item
276 * \param string $Title  Title of the new menu item
277 * \param string $Target Target of the new menu item
278 * \param string $HTML   HTML of the new menu item
279 */
280function menu_insert($Path, $LastOrder = 0, $URI = NULL, $Title = NULL, $Target = NULL, $HTML = NULL)
281{
282  global $MenuList;
283  menu_insert_r($MenuList, array(), $Path, $LastOrder, $Target, $URI, $HTML, $Title);
284} // menu_insert()
285
286
287/**
288 * \brief Given a top-level menu name, find
289 * the list of sub-menus below it and max depth of menu.
290 *
291 * \note this this function returns the sub menus of $Name, NOT the menu specified
292 * by $Name.
293 *
294 * \todo Rename this function to menu_find_submenus.
295 *
296 * \param string $Name        Top-level menu name, may be a "::" separated list.
297 * \param[out] int &$MaxDepth Max depth of menu, returned value
298 * \param menu $Menu          menu object array (default is global $MenuList)
299 *
300 * \return Array of sub-menus.  $MaxDepth is also returned
301 */
302function menu_find($Name, &$MaxDepth, $Menu = NULL)
303{
304  global $MenuList;
305  if (empty($Menu)) {
306    $Menu = $MenuList;
307  }
308  if (empty($Name)) {
309    return ($Menu);
310  }
311  $PathParts = explode('::', $Name, 2);
312  foreach ($Menu as $Val) {
313    if ($Val->Name == $PathParts[0]) {
314      if (empty($PathParts[1])) {
315        $MaxDepth = $Val->MaxDepth;
316        return ($Val->SubMenu);
317      } else {
318        return (menu_find($PathParts[1], $MaxDepth, $Val->SubMenu));
319      }
320    }
321  }
322  return (null);
323} // menu_find()
324
325
326$menu_to_1html_counter = 0;  ///< Counter used by menu_to_1html()
327/**
328 * \brief Take a menu and render it as one HTML line.
329 *
330 * This ignores submenus!
331 * This is commonly called the "micro-menu".
332 *
333 * \param menu $Menu          menu list need to show as HTML
334 * \param bool $ShowRefresh   If true, show Refresh
335 * \param bool $ShowTraceback If true, show Tracback
336 * \param bool $ShowAll       If false, then items without hyperlinks are hidden.
337 *
338 * \return HTML string
339 */
340function menu_to_1html($Menu, $ShowRefresh = 1, $ShowTraceback = 0, $ShowAll = 1)
341{
342  $showFullName = isset($_SESSION) && array_key_exists('fullmenudebug', $_SESSION) && $_SESSION['fullmenudebug'] == 1;
343
344  $V = "";
345  $Std = "";
346  global $menu_to_1html_counter;
347  if ($ShowTraceback) {
348    global $Plugins;
349    $Refresh = & $Plugins[plugin_find_id("refresh") ];
350    if (!empty($Refresh)) {
351      $text = _("Traceback");
352      $URL = Traceback_dir() . "?" . $Refresh->GetRefresh();
353      $Std.= "<a href='$URL' target='_top'>$text</a>";
354    }
355  }
356  if ($ShowRefresh) {
357    if (!empty($Std)) {
358      $Std.= " | ";
359    }
360    $text = _("Refresh");
361    $Std.= "<a href='" . Traceback() . "'>$text</a>";
362  }
363  $First = 1;
364  if (!empty($Menu)) {
365    foreach ($Menu as $Val) {
366      if ($Val->Name == MENU_BREAK) {
367        if (!$First) {
368          $V .= " &nbsp;&nbsp;&bull;";
369          if ($showFullName) {
370            $V .= getFullNameAddition($Val);
371          }
372          $V .= "&nbsp;&nbsp; ";
373        }
374        $First = 1;
375      } else if (!empty($Val->HTML)) {
376        $V.= $Val->HTML;
377        if ($showFullName) {
378          $V .= getFullNameAddition($Val);
379
380        }
381        $First = 0;
382      } else if (!empty($Val->URI)) {
383        if (!$First) {
384          $V.= " | ";
385        }
386        $V.= "<a href='" . Traceback_uri() . "?mod=" . $Val->URI . "'";
387        if (!empty($Val->Title)) {
388          $V.= " title='" . htmlentities($Val->Title, ENT_QUOTES) . "'";
389        }
390        $V.= ">";
391        if ($showFullName) {
392          $V.= $Val->FullName . getFullNameAddition($Val);
393        } else {
394          $V.= $Val->Name;
395        }
396        $V.= "</a>";
397        $First = 0;
398      } else if ($ShowAll) {
399        if (!$First) {
400          $V.= " | ";
401        }
402        if ($showFullName) {
403          $V.= $Val->FullName . getFullNameAddition($Val);
404        } else {
405          $V.= $Val->Name;
406        }
407        $First = 0;
408      }
409    }
410  }
411  if (!empty($Std)) {
412    if (!$First) {
413      $V.= " &nbsp;&nbsp;&bull;&nbsp;&nbsp; ";
414    }
415    $V.= $Std;
416    $Std = null;
417  }
418  $menu_to_1html_counter++;
419  return ("<div id='menu1html-$menu_to_1html_counter' align='right' style='padding:0px 5px 0px 5px'><small>$V</small></div>");
420}
421
422/**
423 * Get the additional string for menu full name
424 * @param menu $menu menu
425 * @return string
426 */
427function getFullNameAddition(menu $menu)
428{
429  return "(" . $menu->Order . ")";
430} // menu_to_1html()
431
432
433/**
434 * \brief Take a menu and render it as
435 * one HTML line with items in a "[name]" list.
436 *
437 * \note This ignores submenus!
438 *
439 * \param menu $Menu      menu list need to show as list
440 * \param string &$Parm   A list of parameters to add to the URL.
441 * \param string $Pre     String before "[name]"
442 * \param string $Post    String after "[name]"
443 * \param bool $ShowAll   If false, then items without hyperlinks are hidden.
444 * \param int  $upload_id Upload id
445 *
446 * \return one HTML line with items in a "[name]" list
447 */
448function menu_to_1list($Menu, &$Parm, $Pre = "", $Post = "", $ShowAll = 1, $upload_id  = "")
449{
450  if (empty($Menu)) {
451    return '';
452  }
453
454  $showFullName = isset($_SESSION) && array_key_exists('fullmenudebug', $_SESSION) && $_SESSION['fullmenudebug'] == 1;
455  $V = "";
456
457  foreach ($Menu as $Val) {
458    if (!empty($Val->HTML)) {
459      $entry = $Val->HTML;
460    } else if (!empty($Val->URI)) {
461      if (!empty($upload_id) && "tag" == $Val->URI) {
462        $tagstatus = TagStatus($upload_id);
463        if (0 == $tagstatus) {
464          break; // tagging on this upload is disabled
465        }
466      }
467
468      $entry = "[<a href='" . Traceback_uri() . "?mod=" . $Val->URI . "&" . $Parm . "'";
469      if (!empty($Val->Title)) {
470        $entry .= " title='" . htmlentities($Val->Title, ENT_QUOTES) . "'";
471      }
472      $entry .= ">" ;
473      $entry .= $Val->getName($showFullName);
474      $entry .= "</a>]";
475    } else if ($ShowAll) {
476      $entry = "[" . $Val->getName($showFullName) . "]";
477    } else {
478      continue;
479    }
480    $V .= $Pre . $entry . $Post;
481  }
482  return $V;
483}
484
485
486/**
487 * \brief Debugging code for printing the menu.
488 *
489 * \note This is recursive.
490 *
491 * \param menu &$Menu   menu list to be printed
492 * \param int  $Indent  Indentations to add
493 */
494function menu_print(&$Menu, $Indent)
495{
496  if (!isset($Menu)) {
497    return;
498  }
499  foreach ($Menu as $Val) {
500    for ($i = 0;$i < $Indent;$i++) {
501      print " ";
502    }
503    print "$Val->Name ($Val->Order,$Val->URI)\n";
504    menu_print($Val->SubMenu, $Indent + 1);
505  }
506} // menu_print()
507
508// DEBUG CODE
509/**********
510 if (0)
511 {
512 menu_insert("abc::def::",0,"");
513 menu_insert("Applications::Accessories::Dictionary",0,"");
514 menu_insert("Applications::Accessories::Ark",0,"");
515 menu_insert("Places::Computer",3,"");
516 menu_insert("Places::CD/DVD Creator",3,"");
517 menu_insert("Places::Home Folder",4,"");
518 menu_insert("Places::Network Servers",2,"");
519 menu_insert("Places::Search for Files...",0,"");
520 menu_insert("Places::Desktop",4,"");
521 menu_insert("Places::Recent Documents",0,"");
522 menu_insert("Places::Connect to Server...",2,"");
523 menu_insert("Applications::Accessories::Calculator",0,"");
524 menu_print($MenuList,0);
525 print "Max depth: $MenuMaxDepth\n";
526 }
527 **********/
528
529
530/**
531 * \brief Remove a menu object (based on an object name)
532 *  from a menu list.
533 *
534 * For example,
535 * \code
536 *   $mymenu = menu_find("Browse-Pfile", $MenuDepth);
537 *   $myNewMenu = menu_remove($mymenu, "Compare");
538 * \endcode
539 *
540 * \param menu $Menu     menu list the menu item remove from
541 * \param string $RmName Remove name of menu
542 *
543 * \return A new menu list without $RmName
544 */
545function menu_remove($Menu, $RmName)
546{
547  $NewArray = array();
548  foreach ($Menu as $MenuObj) {
549    if ($MenuObj->Name != $RmName) {
550      $NewArray[] = $MenuObj;
551    }
552  }
553  return $NewArray;
554}
555