home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / HTML / Menu.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  18.2 KB  |  623 lines

  1. <?php
  2. /**
  3.  * Generates a HTML menu from a multidimensional hash
  4.  * 
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.01 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_01.txt If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category    HTML
  14.  * @package     HTML_Menu
  15.  * @author      Ulf Wendel <ulf.wendel@phpdoc.de>
  16.  * @author      Sebastian Bergmann <sb@sebastian-bergmann.de>
  17.  * @author      Alexey Borzov <avb@php.net>
  18.  * @copyright   2001-2007 The PHP Group
  19.  * @license     http://www.php.net/license/3_01.txt PHP License 3.01
  20.  * @version     CVS: $Id: Menu.php,v 1.15 2007/05/18 20:54:33 avb Exp $
  21.  * @link        http://pear.php.net/package/HTML_Menu
  22.  */
  23.  
  24. /**#@+
  25.  * Constants for menu entry types
  26.  */
  27. define('HTML_MENU_ENTRY_INACTIVE',      0);
  28. define('HTML_MENU_ENTRY_ACTIVE',        1);
  29. define('HTML_MENU_ENTRY_ACTIVEPATH',    2);
  30. define('HTML_MENU_ENTRY_PREVIOUS',      3);
  31. define('HTML_MENU_ENTRY_NEXT',          4);
  32. define('HTML_MENU_ENTRY_UPPER',         5);
  33. define('HTML_MENU_ENTRY_BREADCRUMB',    6); // like activepath, but for 'urhere' type
  34. /**#@-*/
  35.  
  36. /**
  37.  * Generates a HTML menu from a multidimensional hash.
  38.  *
  39.  * Special thanks to the original author: Alex Vorobiev  <sasha@mathforum.com>.
  40.  *
  41.  * @category    HTML
  42.  * @package     HTML_Menu
  43.  * @author      Ulf Wendel <ulf.wendel@phpdoc.de>
  44.  * @author      Alexey Borzov <avb@php.net>
  45.  * @version     Release: 2.1.4
  46.  */
  47. class HTML_Menu 
  48. {
  49.    /**#@+
  50.     * @access private
  51.     */
  52.    /**
  53.     * Menu structure as a multidimensional hash.
  54.     * @var  array
  55.     * @see  setMenu(), Menu()
  56.     */
  57.     var $_menu = array();
  58.  
  59.    /**
  60.     * Mapping from URL to menu path.
  61.     * @var  array
  62.     * @see  getPath()
  63.     */
  64.     var $_urlMap = array();
  65.  
  66.    /**
  67.     * Path to the current menu item.
  68.     * @var  array
  69.     * @see  get(), getPath()
  70.     */
  71.     var $_path = array();
  72.  
  73.    /**
  74.     * Menu type: tree, rows, you-are-here.
  75.     * @var  array
  76.     * @see  setMenuType()
  77.     */
  78.     var $_menuType = 'tree';
  79.  
  80.    /**
  81.     * URL Environment Variable
  82.     * @var  string
  83.     */
  84.     var $_urlEnvVar = 'PHP_SELF';
  85.  
  86.    /**
  87.     * The URL to use an URL for the current page, instead of the one normally
  88.     * taken from env. variables
  89.     * @var string
  90.     * @see forceCurrentUrl(), getCurrentUrl()
  91.     */
  92.     var $_forcedUrl = '';
  93.  
  94.    /**
  95.     * Index of the menu item that should be made "current"
  96.     * @var mixed 
  97.     * @see forceCurrentIndex()
  98.     */
  99.     var $_forcedIndex = null;
  100.  
  101.    /**
  102.     * URL of the current page.
  103.     * @see  getCurrentURL(), getPath()
  104.     */
  105.     var $_currentUrl = '';
  106.  
  107.    /**
  108.     * The renderer being used to output the menu
  109.     * @var HTML_Menu_Renderer
  110.     * @see render()
  111.     */
  112.     var $_renderer = null;
  113.  
  114.    /**
  115.     * Prefix for menu URLs 
  116.     * @var string
  117.     * @see setUrlPrefix()
  118.     */
  119.     var $_urlPrefix = '';
  120.     /**#@-*/
  121.  
  122.    /**
  123.     * Initializes the menu, sets the type and menu structure.
  124.     *
  125.     * @param    array   menu structure
  126.     * @param    string  menu type
  127.     * @param    string  env. variable used to determine current URL
  128.     * @see      setMenuType(), setMenu(), setURLEnvVar()
  129.     */
  130.     function HTML_Menu($menu = null, $type = 'tree', $urlEnvVar = 'PHP_SELF') 
  131.     {
  132.         if (is_array($menu)) {
  133.             $this->setMenu($menu);
  134.         }
  135.         $this->setMenuType($type);
  136.         $this->setURLEnvVar($urlEnvVar);
  137.     }
  138.  
  139.  
  140.    /**
  141.     * Sets the menu structure.
  142.     *
  143.     * The menu structure is defined by a multidimensional hash. This is
  144.     * quite "dirty" but simple and easy to traverse. An example
  145.     * show the structure. To get the following menu:
  146.     *
  147.     * <pre>
  148.     * 1  - Projects
  149.     * 11 - Projects => PHPDoc
  150.     * 12 - Projects => Forms
  151.     * 2  - Stuff
  152.     * </pre>
  153.     *
  154.     * you need the array:
  155.     *
  156.     * <pre>
  157.     * $menu = array(
  158.     *           1 => array(
  159.     *                  'title' => 'Projects',
  160.     *                  'url' => '/projects/index.php',
  161.     *                  'sub' => array(
  162.     *                           11 => array(
  163.     *                                       'title' => 'PHPDoc',
  164.     *                                       ...
  165.     *                                     ),
  166.     *                           12 => array( ... ),
  167.     *                 )
  168.     *             ),
  169.     *           2 => array( 'title' => 'Stuff', 'url' => '/stuff/index.php' )
  170.     *        )
  171.     * </pre>
  172.     *
  173.     * Note the index 'sub' and the nesting. Note also that 1, 11, 12, 2
  174.     * must be unique. The class uses them as ID's.
  175.     *
  176.     * @param    array
  177.     * @access   public
  178.     */
  179.     function setMenu($menu) 
  180.     {
  181.         $this->_menu   = $menu;
  182.         $this->_urlMap = array();
  183.     }
  184.  
  185.  
  186.    /**
  187.     * Sets the type of the menu.
  188.     * 
  189.     * Available types are: 'tree', 'rows', 'urhere', 'prevnext', 'sitemap'.
  190.     *
  191.     * @param    string type name
  192.     * @access   public
  193.     */
  194.     function setMenuType($menuType) 
  195.     {
  196.         $menuType = strtolower($menuType);
  197.         if (in_array($menuType, array('tree', 'rows', 'urhere', 'prevnext', 'sitemap'))) {
  198.             $this->_menuType = $menuType;
  199.         } else {
  200.             $this->_menuType = 'tree';
  201.         }
  202.     }
  203.  
  204.  
  205.    /**
  206.     * Sets the environment variable to use to get the current URL.
  207.     *
  208.     * @param    string  environment variable for current URL
  209.     * @access   public
  210.     */
  211.     function setURLEnvVar($urlEnvVar) 
  212.     {
  213.         $this->_urlEnvVar = $urlEnvVar;
  214.     }
  215.  
  216.  
  217.    /**
  218.     * Returns the HTML menu.
  219.     *
  220.     * @param    string  Menu type: tree, urhere, rows, prevnext, sitemap
  221.     * @return   string  HTML of the menu
  222.     * @access   public
  223.     * @see render()
  224.     */
  225.     function get($menuType = '') 
  226.     {
  227.         include_once 'HTML/Menu/DirectRenderer.php';
  228.         $renderer =& new HTML_Menu_DirectRenderer();
  229.         $this->render($renderer, $menuType);
  230.         return $renderer->toHtml();
  231.     }
  232.  
  233.  
  234.    /**
  235.     * Prints the HTML menu.
  236.     *
  237.     * @access   public
  238.     * @param    string  Menu type: tree, urhere, rows, prevnext, sitemap
  239.     * @see      get(), render()
  240.     */
  241.     function show($menuType = '') 
  242.     {
  243.         print $this->get($menuType);
  244.     }
  245.  
  246.  
  247.    /**
  248.     * Renders the menu.
  249.     *
  250.     * @access public
  251.     * @param  HTML_Menu_Renderer    Renderer to use
  252.     * @param  string                Type of the menu
  253.     * @throws PEAR_Error
  254.     */
  255.     function render(&$renderer, $menuType = '')
  256.     {
  257.         if ('' != $menuType) {
  258.             $this->setMenuType($menuType);
  259.         }
  260.         $this->_renderer =& $renderer;
  261.         // the renderer will throw an error if it is unable to process this menu type
  262.         $res = $this->_renderer->setMenuType($this->_menuType);
  263.         if (is_object($res) && is_a($res, 'PEAR_Error')) {
  264.             return $res;
  265.         }
  266.  
  267.         // storing to a class variable saves some recursion overhead
  268.         $this->_path = $this->getPath();
  269.  
  270.         switch ($this->_menuType) {
  271.             case 'rows': 
  272.                 $this->_renderRows($this->_menu);
  273.                 break;
  274.  
  275.             case 'prevnext': 
  276.                 $this->_renderPrevNext($this->_menu);
  277.                 break;
  278.  
  279.             case 'urhere':
  280.                 $this->_renderURHere($this->_menu);
  281.                 break;
  282.  
  283.             default:
  284.                 $this->_renderTree($this->_menu);
  285.         } // switch
  286.     }
  287.  
  288.  
  289.    /**
  290.     * Finds the type for the node.
  291.     * 
  292.     * @access private
  293.     * @param mixed   Node id
  294.     * @param string  Node 'url' attribute
  295.     * @param int     Level in the tree
  296.     * @return int    Node type (one of HTML_MENU_ENTRY_* constants)
  297.     */
  298.     function _findNodeType($nodeId, &$nodeUrl, $level)
  299.     {
  300.         $nodeUrl = $this->_prefixUrl($nodeUrl);
  301.         if ($this->_currentUrl == $nodeUrl) {
  302.             // menu item that fits to this url - 'active' menu item
  303.             return HTML_MENU_ENTRY_ACTIVE;
  304.         } elseif (isset($this->_path[$level]) && $this->_path[$level] == $nodeId) {
  305.             // processed menu item is part of the path to the active menu item
  306.             return 'urhere' == $this->_menuType? HTML_MENU_ENTRY_BREADCRUMB: HTML_MENU_ENTRY_ACTIVEPATH;
  307.         } else {
  308.             // not selected, not a part of the path to the active menu item
  309.             return HTML_MENU_ENTRY_INACTIVE;
  310.         }
  311.     }
  312.  
  313.  
  314.    /**
  315.     * Renders the tree menu ('tree' and 'sitemap')
  316.     * 
  317.     * @access private
  318.     * @param  array     (sub)menu being rendered
  319.     * @param  int       current depth in the tree structure
  320.     */
  321.     function _renderTree($menu, $level = 0)
  322.     {
  323.         foreach ($menu as $node_id => $node) {
  324.             $type = $this->_findNodeType($node_id, $node['url'], $level);
  325.  
  326.             $this->_renderer->renderEntry($node, $level, $type);
  327.             $this->_renderer->finishRow($level);
  328.  
  329.             // follow the subtree if the active menu item is in it or if we 
  330.             // want the full menu or if node expansion is forced (request #4391)
  331.             if (isset($node['sub']) && ('sitemap' == $this->_menuType || 
  332.                 HTML_MENU_ENTRY_INACTIVE != $type || !empty($node['forceExpand']))) {
  333.  
  334.                 $this->_renderTree($node['sub'], $level + 1);
  335.             }
  336.         }
  337.         $this->_renderer->finishLevel($level);
  338.         if (0 == $level) {
  339.             $this->_renderer->finishMenu($level);
  340.         }
  341.     }
  342.  
  343.  
  344.    /**
  345.     * Renders the 'urhere' menu
  346.     * 
  347.     * @access private
  348.     * @param  array     (sub)menu being rendered
  349.     * @param  int       current depth in the tree structure
  350.     */
  351.     function _renderURHere($menu, $level = 0)
  352.     {
  353.         foreach ($menu as $node_id => $node) {
  354.             $type = $this->_findNodeType($node_id, $node['url'], $level);
  355.  
  356.             if (HTML_MENU_ENTRY_INACTIVE != $type) {
  357.                 $this->_renderer->renderEntry($node, $level, $type);
  358.                 // follow the subtree if the active menu item is in it
  359.                 if (isset($node['sub'])) {
  360.                     $this->_renderURHere($node['sub'], $level + 1);
  361.                 }
  362.             }
  363.         }
  364.         if (0 == $level) {
  365.             $this->_renderer->finishRow($level);
  366.             $this->_renderer->finishMenu($level);
  367.         }
  368.     }
  369.  
  370.  
  371.    /**
  372.     * Renders the 'rows' menu
  373.     * 
  374.     * @access private
  375.     * @param  array     (sub)menu being rendered
  376.     * @param  int       current depth in the tree structure
  377.     */
  378.     function _renderRows($menu, $level = 0)
  379.     {
  380.         $submenu = false;
  381.  
  382.         foreach ($menu as $node_id => $node) {
  383.             $type = $this->_findNodeType($node_id, $node['url'], $level);
  384.  
  385.             $this->_renderer->renderEntry($node, $level, $type);
  386.  
  387.             // follow the subtree if the active menu item is in it
  388.             if (HTML_MENU_ENTRY_INACTIVE != $type && isset($node['sub'])) {
  389.                 $submenu = $node['sub'];
  390.             }
  391.         }
  392.  
  393.         // every (sub)menu has its own table
  394.         $this->_renderer->finishRow($level);
  395.         $this->_renderer->finishMenu($level);
  396.  
  397.         // go deeper if neccessary
  398.         if ($submenu) {
  399.             $this->_renderRows($submenu, $level + 1);
  400.         }
  401.     }
  402.  
  403.  
  404.    /**
  405.     * Renders the 'prevnext' menu
  406.     * 
  407.     * @access private
  408.     * @param  array     (sub)menu being rendered
  409.     * @param  int       current depth in the tree structure
  410.     * @param  int       flag indicating whether to finish processing
  411.     *                   (0 - continue, 1 - this is "next" node, 2 - stop)
  412.     */
  413.     function _renderPrevNext($menu, $level = 0, $flagStop = 0)
  414.     {
  415.         static $last_node = array(), $up_node = array();
  416.  
  417.         foreach ($menu as $node_id => $node) {
  418.             if (0 != $flagStop) {
  419.                 // add this item to the menu and stop recursion - (next >>) node
  420.                 if ($flagStop == 1) {
  421.                     $node['url'] = $this->_prefixUrl($node['url']);
  422.                     $this->_renderer->renderEntry($node, $level, HTML_MENU_ENTRY_NEXT);
  423.                     $flagStop = 2;
  424.                 }
  425.                 break;
  426.  
  427.             } else {
  428.                 $type = $this->_findNodeType($node_id, $node['url'], $level);
  429.                 if (HTML_MENU_ENTRY_ACTIVE == $type) {
  430.                     $flagStop = 1;
  431.  
  432.                     // WARNING: if there's no previous take the first menu entry - you might not like this rule!
  433.                     if (0 == count($last_node)) {
  434.                         reset($this->_menu);
  435.                         list($node_id, $last_node) = each($this->_menu);
  436.                         $last_node['url'] = $this->_prefixUrl($last_node['url']);
  437.                     }
  438.                     $this->_renderer->renderEntry($last_node, $level, HTML_MENU_ENTRY_PREVIOUS);
  439.  
  440.                     // WARNING: if there's no up take the first menu entry - you might not like this rule!
  441.                     if (0 == count($up_node)) {
  442.                         reset($this->_menu);
  443.                         list($node_id, $up_node) = each($this->_menu);
  444.                         $up_node['url'] = $this->_prefixUrl($up_node['url']);
  445.                     }
  446.                     $this->_renderer->renderEntry($up_node, $level, HTML_MENU_ENTRY_UPPER);
  447.                 }
  448.             }
  449.  
  450.             // remember the last (<< prev) node
  451.             $last_node = $node;
  452.  
  453.             if (isset($node['sub'])) {
  454.                 if (HTML_MENU_ENTRY_INACTIVE != $type) {
  455.                     $up_node = $node;
  456.                 }
  457.                 $flagStop = $this->_renderPrevNext($node['sub'], $level + 1, $flagStop);
  458.             }
  459.         }
  460.  
  461.         if (0 == $level) {
  462.             $this->_renderer->finishRow($level);
  463.             $this->_renderer->finishMenu($level);
  464.         }
  465.         return $flagStop;
  466.     }
  467.  
  468.  
  469.    /**
  470.     * Returns the path of the current page in the menu 'tree'.
  471.     *
  472.     * @return   array    path to the selected menu item
  473.     * @see      _buildUrlMap(), $_urlMap
  474.     */
  475.     function getPath() 
  476.     {
  477.         $this->_currentUrl = $this->getCurrentURL();
  478.         $this->_buildUrlMap($this->_menu, array());
  479.  
  480.         // If there is no match for the current URL, try to come up with
  481.         // the best approximation by shortening the url
  482.         while ($this->_currentUrl && !isset($this->_urlMap[$this->_currentUrl])) {
  483.             $this->_currentUrl = substr($this->_currentUrl, 0, -1);
  484.         }
  485.  
  486.         return isset($this->_urlMap[$this->_currentUrl])? $this->_urlMap[$this->_currentUrl]: array();
  487.     }
  488.  
  489.  
  490.    /**
  491.     * Builds the mappings from node url to the 'path' in the menu
  492.     *
  493.     * @access   private
  494.     * @param    array       (sub)menu being processed
  495.     * @param    array       path to the (sub)menu
  496.     * @return   boolean     true if the path to the current page was found, otherwise false.
  497.     * @see      getPath(), $_urlMap
  498.     */
  499.     function _buildUrlMap($menu, $path) 
  500.     {
  501.         foreach ($menu as $nodeId => $node) {
  502.             $url = $this->_prefixUrl($node['url']); 
  503.             $this->_urlMap[$url] = $path;
  504.  
  505.             if ($url == $this->_currentUrl) {
  506.                 return true;
  507.             }
  508.  
  509.             if (isset($node['sub']) && 
  510.                 $this->_buildUrlMap($node['sub'], array_merge($path, array($nodeId)))) {
  511.                 return true;
  512.             }
  513.         }
  514.         return false;
  515.     }
  516.  
  517.  
  518.    /**
  519.     * Returns the URL of the currently selected page.
  520.     *
  521.     * The returned string is used for all test against the URL's
  522.     * in the menu structure hash.
  523.     *
  524.     * @access public
  525.     * @return string
  526.     */
  527.     function getCurrentURL() 
  528.     {
  529.         if (!empty($this->_forcedUrl)) {
  530.             return $this->_forcedUrl;
  531.         } elseif (!empty($this->_forcedIndex)) {
  532.             return $this->_findUrlByIndex($this->_menu, $this->_forcedIndex);
  533.         } elseif (isset($_SERVER[$this->_urlEnvVar])) {
  534.             return $_SERVER[$this->_urlEnvVar];
  535.         } elseif (isset($GLOBALS[$this->_urlEnvVar])) {
  536.             return $GLOBALS[$this->_urlEnvVar];
  537.         } elseif ($env = getenv($this->_urlEnvVar)) {
  538.             return $env;
  539.         } else {
  540.             return '';
  541.         }
  542.     }
  543.  
  544.  
  545.    /**
  546.     * Forces the given URL to be "current"
  547.     *
  548.     * @access public
  549.     * @param  string    Url to use
  550.     */
  551.     function forceCurrentUrl($url)
  552.     {
  553.         $this->_forcedUrl   = $url;
  554.         $this->_forcedIndex = null;
  555.     }
  556.  
  557.  
  558.    /**
  559.     * Sets the prefix for the URLs in the menu
  560.     * 
  561.     * @param  string
  562.     * @access public
  563.     */
  564.     function setUrlPrefix($prefix)
  565.     {
  566.         if (('' != $prefix) && ('/' != substr($prefix, -1))) {
  567.             $prefix .= '/';
  568.         }
  569.         $this->_urlPrefix = $prefix;
  570.     }
  571.  
  572.  
  573.    /**
  574.     * Adds the prefix to the URL (see request #2935)
  575.     *
  576.     * @access   private
  577.     * @param    string  URL
  578.     * @return   string  URL with prefix
  579.     * @see      setUrlPrefix()
  580.     */
  581.     function _prefixUrl($url)
  582.     {
  583.         return $this->_urlPrefix . ((empty($this->_urlPrefix) || '/' != $url{0})? $url: substr($url, 1));
  584.     }
  585.  
  586.  
  587.    /**
  588.     * Forces the menu item with the given index to become "current"
  589.     *
  590.     * Per request #3237
  591.     *
  592.     * @param    mixed   Menu item index
  593.     * @access   public
  594.     */
  595.     function forceCurrentIndex($index)
  596.     {
  597.         $this->_forcedIndex = $index;
  598.         $this->_forcedUrl   = ''; 
  599.     }
  600.  
  601.  
  602.    /**
  603.     * Returns the 'url' field of the menu item with the given index
  604.     *
  605.     * @param    array   Menu structure to search
  606.     * @param    mixed   Index
  607.     * @return   string  URL
  608.     * @access   private
  609.     */
  610.     function _findUrlByIndex(&$menu, $index)
  611.     {
  612.         foreach (array_keys($menu) as $key) {
  613.             if ($key == $index) {
  614.                 return $menu[$key]['url'];
  615.             } elseif (!empty($menu[$key]['sub']) && '' != ($url = $this->_findUrlByIndex($menu[$key]['sub'], $index))) {
  616.                 return $url;
  617.             }
  618.         }
  619.         return '';
  620.     }
  621. }
  622. ?>
  623.