vendor/pimcore/pimcore/lib/Navigation/Renderer/Menu.php line 1040

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. /**
  15.  * ----------------------------------------------------------------------------------
  16.  * based on @author ZF1 Zend_View_Helper_Navigation_Menu
  17.  * ----------------------------------------------------------------------------------
  18.  */
  19. /**
  20.  * Zend Framework
  21.  *
  22.  * LICENSE
  23.  *
  24.  * This source file is subject to the new BSD license that is bundled
  25.  * with this package in the file LICENSE.txt.
  26.  * It is also available through the world-wide-web at this URL:
  27.  * http://framework.zend.com/license/new-bsd
  28.  * If you did not receive a copy of the license and are unable to
  29.  * obtain it through the world-wide-web, please send an email
  30.  * to license@zend.com so we can send you a copy immediately.
  31.  *
  32.  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  33.  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  34.  */
  35. namespace Pimcore\Navigation\Renderer;
  36. use Pimcore\Navigation\Container;
  37. use Pimcore\Navigation\Page;
  38. class Menu extends AbstractRenderer
  39. {
  40.     /**
  41.      * CSS class to use for the ul element
  42.      *
  43.      * @var string
  44.      */
  45.     protected $_ulClass 'navigation';
  46.     /**
  47.      * Unique identifier (id) for the ul element
  48.      *
  49.      * @var string
  50.      */
  51.     protected $_ulId null;
  52.     /**
  53.      * CSS class to use for the active elements
  54.      *
  55.      * @var string
  56.      */
  57.     protected $_activeClass 'active';
  58.     /**
  59.      * CSS class to use for the parent li element
  60.      *
  61.      * @var string
  62.      */
  63.     protected $_parentClass 'menu-parent';
  64.     /**
  65.      * Whether parent li elements should be rendered with parent class
  66.      *
  67.      * @var bool
  68.      */
  69.     protected $_renderParentClass false;
  70.     /**
  71.      * Whether only active branch should be rendered
  72.      *
  73.      * @var bool
  74.      */
  75.     protected $_onlyActiveBranch false;
  76.     /**
  77.      * Whether parents should be rendered when only rendering active branch
  78.      *
  79.      * @var bool
  80.      */
  81.     protected $_renderParents true;
  82.     /**
  83.      * Partial view script to use for rendering menu
  84.      *
  85.      * @var string|array
  86.      */
  87.     protected $_template null;
  88.     /**
  89.      * Expand all sibling nodes of active branch nodes
  90.      *
  91.      * @var bool
  92.      */
  93.     protected $_expandSiblingNodesOfActiveBranch false;
  94.     /**
  95.      * Adds CSS class from page to li element
  96.      *
  97.      * @var bool
  98.      */
  99.     protected $_addPageClassToLi false;
  100.     /**
  101.      * Inner indentation string
  102.      *
  103.      * @var string
  104.      */
  105.     protected $_innerIndent '    ';
  106.     // Accessors:
  107.     /**
  108.      * Sets CSS class to use for the first 'ul' element when rendering
  109.      *
  110.      * @param  string $ulClass                   CSS class to set
  111.      *
  112.      * @return $this
  113.      */
  114.     public function setUlClass($ulClass)
  115.     {
  116.         if (is_string($ulClass)) {
  117.             $this->_ulClass $ulClass;
  118.         }
  119.         return $this;
  120.     }
  121.     /**
  122.      * Returns CSS class to use for the first 'ul' element when rendering
  123.      *
  124.      * @return string  CSS class
  125.      */
  126.     public function getUlClass()
  127.     {
  128.         return $this->_ulClass;
  129.     }
  130.     /**
  131.      * Sets unique identifier (id) to use for the first 'ul' element when
  132.      * rendering
  133.      *
  134.      * @param  string|null  $ulId                Unique identifier (id) to set
  135.      *
  136.      * @return $this
  137.      */
  138.     public function setUlId($ulId)
  139.     {
  140.         if (is_string($ulId)) {
  141.             $this->_ulId $ulId;
  142.         }
  143.         return $this;
  144.     }
  145.     /**
  146.      * Returns unique identifier (id) to use for the first 'ul' element when
  147.      * rendering
  148.      *
  149.      * @return string|null  Unique identifier (id); Default is 'null'
  150.      */
  151.     public function getUlId()
  152.     {
  153.         return $this->_ulId;
  154.     }
  155.     /**
  156.      * Sets CSS class to use for the active elements when rendering
  157.      *
  158.      * @param string $activeClass               CSS class to set
  159.      *
  160.      * @return $this
  161.      */
  162.     public function setActiveClass($activeClass)
  163.     {
  164.         if (is_string($activeClass)) {
  165.             $this->_activeClass $activeClass;
  166.         }
  167.         return $this;
  168.     }
  169.     /**
  170.      * Returns CSS class to use for the active elements when rendering
  171.      *
  172.      * @return string  CSS class
  173.      */
  174.     public function getActiveClass()
  175.     {
  176.         return $this->_activeClass;
  177.     }
  178.     /**
  179.      * Sets CSS class to use for the parent li elements when rendering
  180.      *
  181.      * @param  string $parentClass              CSS class to set to parents
  182.      *
  183.      * @return $this
  184.      */
  185.     public function setParentClass($parentClass)
  186.     {
  187.         if (is_string($parentClass)) {
  188.             $this->_parentClass $parentClass;
  189.         }
  190.         return $this;
  191.     }
  192.     /**
  193.      * Returns CSS class to use for the parent lie elements when rendering
  194.      *
  195.      * @return string CSS class
  196.      */
  197.     public function getParentClass()
  198.     {
  199.         return $this->_parentClass;
  200.     }
  201.     /**
  202.      * Enables/disables rendering of parent class to the li element
  203.      *
  204.      * @param bool $flag                        [optional] render with parent
  205.      *                                          class. Default is true.
  206.      *
  207.      * @return $this
  208.      */
  209.     public function setRenderParentClass($flag true)
  210.     {
  211.         $this->_renderParentClass = (bool) $flag;
  212.         return $this;
  213.     }
  214.     /**
  215.      * Returns flag indicating whether parent class should be rendered to the li
  216.      * element
  217.      *
  218.      * @return bool  whether parent class should be rendered
  219.      */
  220.     public function getRenderParentClass()
  221.     {
  222.         return $this->_renderParentClass;
  223.     }
  224.     /**
  225.      * Sets a flag indicating whether only active branch should be rendered
  226.      *
  227.      * @param  bool $flag                        [optional] render only active
  228.      *                                           branch. Default is true.
  229.      *
  230.      * @return $this
  231.      */
  232.     public function setOnlyActiveBranch($flag true)
  233.     {
  234.         $this->_onlyActiveBranch = (bool) $flag;
  235.         return $this;
  236.     }
  237.     /**
  238.      * Returns a flag indicating whether only active branch should be rendered
  239.      *
  240.      * By default, this value is false, meaning the entire menu will be
  241.      * be rendered.
  242.      *
  243.      * @return bool  whether only active branch should be rendered
  244.      */
  245.     public function getOnlyActiveBranch()
  246.     {
  247.         return $this->_onlyActiveBranch;
  248.     }
  249.     /**
  250.      * Sets a flag indicating whether to expand all sibling nodes of the active branch
  251.      *
  252.      * @param  bool $flag                        [optional] expand all siblings of
  253.      *                                           nodes in the active branch. Default is true.
  254.      *
  255.      * @return $this
  256.      */
  257.     public function setExpandSiblingNodesOfActiveBranch($flag true)
  258.     {
  259.         $this->_expandSiblingNodesOfActiveBranch = (bool) $flag;
  260.         return $this;
  261.     }
  262.     /**
  263.      * Returns a flag indicating whether to expand all sibling nodes of the active branch
  264.      *
  265.      * By default, this value is false, meaning the entire menu will be
  266.      * be rendered.
  267.      *
  268.      * @return bool  whether siblings of nodes in the active branch should be expanded
  269.      */
  270.     public function getExpandSiblingNodesOfActiveBranch()
  271.     {
  272.         return $this->_expandSiblingNodesOfActiveBranch;
  273.     }
  274.     /**
  275.      * Enables/disables rendering of parents when only rendering active branch
  276.      *
  277.      * See {@link setOnlyActiveBranch()} for more information.
  278.      *
  279.      * @param  bool $flag                        [optional] render parents when
  280.      *                                           rendering active branch.
  281.      *                                           Default is true.
  282.      *
  283.      * @return $this
  284.      */
  285.     public function setRenderParents($flag true)
  286.     {
  287.         $this->_renderParents = (bool) $flag;
  288.         return $this;
  289.     }
  290.     /**
  291.      * Returns flag indicating whether parents should be rendered when rendering
  292.      * only the active branch
  293.      *
  294.      * By default, this value is true.
  295.      *
  296.      * @return bool  whether parents should be rendered
  297.      */
  298.     public function getRenderParents()
  299.     {
  300.         return $this->_renderParents;
  301.     }
  302.     /**
  303.      * @return array|string
  304.      */
  305.     public function getTemplate()
  306.     {
  307.         return $this->_template;
  308.     }
  309.     /**
  310.      * @param array|string $template
  311.      */
  312.     public function setTemplate($template)
  313.     {
  314.         $this->_template $template;
  315.     }
  316.     /**
  317.      * Alias of setTemplate()
  318.      *
  319.      * @param array|string $partial
  320.      *
  321.      * @return $this
  322.      */
  323.     public function setPartial($partial)
  324.     {
  325.         $this->_template $partial;
  326.         return $this;
  327.     }
  328.     /**
  329.      * Alias of getTemplate()
  330.      *
  331.      * @return array|string
  332.      */
  333.     public function getPartial()
  334.     {
  335.         return $this->_template;
  336.     }
  337.     /**
  338.      * Adds CSS class from page to li element
  339.      *
  340.      * Before:
  341.      * <code>
  342.      * <li>
  343.      *     <a href="#" class="foo">Bar</a>
  344.      * </li>
  345.      * </code>
  346.      *
  347.      * After:
  348.      * <code>
  349.      * <li class="foo">
  350.      *     <a href="#">Bar</a>
  351.      * </li>
  352.      * </code>
  353.      *
  354.      * @param bool $flag                        [optional] adds CSS class from
  355.      *                                          page to li element
  356.      *
  357.      * @return $this
  358.      */
  359.     public function addPageClassToLi($flag true)
  360.     {
  361.         $this->_addPageClassToLi = (bool) $flag;
  362.         return $this;
  363.     }
  364.     /**
  365.      * Returns a flag indicating whether the CSS class from page to be added to
  366.      * li element
  367.      *
  368.      * @return bool
  369.      */
  370.     public function getAddPageClassToLi()
  371.     {
  372.         return $this->_addPageClassToLi;
  373.     }
  374.     /**
  375.      * Set the inner indentation string for using in {@link render()}, optionally
  376.      * a number of spaces to indent with
  377.      *
  378.      * @param  string|int $indent                          indentation string or
  379.      *                                                     number of spaces
  380.      *
  381.      * @return AbstractRenderer  fluent interface,
  382.      *                                                     returns self
  383.      */
  384.     public function setInnerIndent($indent)
  385.     {
  386.         $this->_innerIndent $this->_getWhitespace($indent);
  387.         return $this;
  388.     }
  389.     /**
  390.      * Returns inner indentation (format output is respected)
  391.      *
  392.      * @return string       indentation string or an empty string
  393.      */
  394.     public function getInnerIndent()
  395.     {
  396.         return $this->_innerIndent;
  397.     }
  398.     // Public methods:
  399.     /**
  400.      * Returns an HTML string containing an 'a' element for the given page if
  401.      * the page's href is not empty, and a 'span' element if it is empty
  402.      *
  403.      * @param  Page $page  page to generate HTML for
  404.      *
  405.      * @return string                      HTML string for the given page
  406.      */
  407.     public function htmlify(Page $page)
  408.     {
  409.         $label $page->getLabel();
  410.         $title $page->getTitle();
  411.         // get attribs for element
  412.         $attribs = [
  413.             'id' => $page->getId(),
  414.             'title' => $title,
  415.         ];
  416.         if (false === $this->getAddPageClassToLi()) {
  417.             $attribs['class'] = $page->getClass();
  418.         }
  419.         // does page have a href?
  420.         if ($href $page->getHref()) {
  421.             $element 'a';
  422.             $attribs['href'] = $href;
  423.             $attribs['target'] = $page->getTarget();
  424.             $attribs['accesskey'] = $page->getAccessKey();
  425.         } else {
  426.             $element 'span';
  427.         }
  428.         // Add custom HTML attributes
  429.         $attribs array_merge($attribs$page->getCustomHtmlAttribs());
  430.         return '<' $element $this->_htmlAttribs($attribs) . '>'
  431.              htmlspecialchars($labelENT_COMPAT'UTF-8')
  432.              . '</' $element '>';
  433.     }
  434.     /**
  435.      * Normalizes given render options
  436.      *
  437.      * @param  array $options  [optional] options to normalize
  438.      *
  439.      * @return array           normalized options
  440.      */
  441.     protected function _normalizeOptions(array $options = [])
  442.     {
  443.         // Ident
  444.         if (isset($options['indent'])) {
  445.             $options['indent'] = $this->_getWhitespace($options['indent']);
  446.         } else {
  447.             $options['indent'] = $this->getIndent();
  448.         }
  449.         // Inner ident
  450.         if (isset($options['innerIndent'])) {
  451.             $options['innerIndent'] =
  452.                 $this->_getWhitespace($options['innerIndent']);
  453.         } else {
  454.             $options['innerIndent'] = $this->getInnerIndent();
  455.         }
  456.         // UL class
  457.         if (isset($options['ulClass']) && $options['ulClass'] !== null) {
  458.             $options['ulClass'] = $options['ulClass'];
  459.         } else {
  460.             $options['ulClass'] = $this->getUlClass();
  461.         }
  462.         // UL id
  463.         if (isset($options['ulId']) && $options['ulId'] !== null) {
  464.             $options['ulId'] = (string) $options['ulId'];
  465.         } else {
  466.             $options['ulId'] = $this->getUlId();
  467.         }
  468.         // Active class
  469.         if (isset($options['activeClass']) && $options['activeClass'] !== null
  470.         ) {
  471.             $options['activeClass'] = (string) $options['activeClass'];
  472.         } else {
  473.             $options['activeClass'] = $this->getActiveClass();
  474.         }
  475.         // Parent class
  476.         if (isset($options['parentClass']) && $options['parentClass'] !== null) {
  477.             $options['parentClass'] = (string) $options['parentClass'];
  478.         } else {
  479.             $options['parentClass'] = $this->getParentClass();
  480.         }
  481.         // Minimum depth
  482.         if (array_key_exists('minDepth'$options)) {
  483.             if (null !== $options['minDepth']) {
  484.                 $options['minDepth'] = (int) $options['minDepth'];
  485.             }
  486.         } else {
  487.             $options['minDepth'] = $this->getMinDepth();
  488.         }
  489.         if ($options['minDepth'] < || $options['minDepth'] === null) {
  490.             $options['minDepth'] = 0;
  491.         }
  492.         // Maximum depth
  493.         if (array_key_exists('maxDepth'$options)) {
  494.             if (null !== $options['maxDepth']) {
  495.                 $options['maxDepth'] = (int) $options['maxDepth'];
  496.             }
  497.         } else {
  498.             $options['maxDepth'] = $this->getMaxDepth();
  499.         }
  500.         // Only active branch
  501.         if (!isset($options['onlyActiveBranch'])) {
  502.             $options['onlyActiveBranch'] = $this->getOnlyActiveBranch();
  503.         }
  504.         // Expand sibling nodes of active branch
  505.         if (!isset($options['expandSiblingNodesOfActiveBranch'])) {
  506.             $options['expandSiblingNodesOfActiveBranch'] = $this->getExpandSiblingNodesOfActiveBranch();
  507.         }
  508.         // Render parents?
  509.         if (!isset($options['renderParents'])) {
  510.             $options['renderParents'] = $this->getRenderParents();
  511.         }
  512.         // Render parent class?
  513.         if (!isset($options['renderParentClass'])) {
  514.             $options['renderParentClass'] = $this->getRenderParentClass();
  515.         }
  516.         // Add page CSS class to LI element
  517.         if (!isset($options['addPageClassToLi'])) {
  518.             $options['addPageClassToLi'] = $this->getAddPageClassToLi();
  519.         }
  520.         return $options;
  521.     }
  522.     // Render methods:
  523.     /**
  524.      * Renders the deepest active menu within [$minDepth, $maxDeth], (called
  525.      * from {@link renderMenu()})
  526.      *
  527.      * @param  Container $container     container to render
  528.      * @param  string                    $ulClass       CSS class for first UL
  529.      * @param  string                    $indent        initial indentation
  530.      * @param  string                    $innerIndent   inner indentation
  531.      * @param  int|null                  $minDepth      minimum depth
  532.      * @param  int|null                  $maxDepth      maximum depth
  533.      * @param  string|null               $ulId          unique identifier (id)
  534.      *                                                  for first UL
  535.      * @param  bool                      $addPageClassToLi  adds CSS class from
  536.      *                                                      page to li element
  537.      * @param  string|null               $activeClass       CSS class for active
  538.      *                                                      element
  539.      * @param  string                    $parentClass       CSS class for parent
  540.      *                                                      li's
  541.      * @param  bool                      $renderParentClass Render parent class?
  542.      *
  543.      * @return string                                       rendered menu (HTML)
  544.      */
  545.     protected function _renderDeepestMenu(
  546.         Container $container,
  547.         $ulClass,
  548.         $indent,
  549.         $innerIndent,
  550.         $minDepth,
  551.         $maxDepth,
  552.         $ulId,
  553.         $addPageClassToLi,
  554.         $activeClass,
  555.         $parentClass,
  556.         $renderParentClass
  557.     ) {
  558.         if (!$active $this->findActive($container$minDepth 1$maxDepth)) {
  559.             return '';
  560.         }
  561.         // special case if active page is one below minDepth
  562.         if ($active['depth'] < $minDepth) {
  563.             if (!$active['page']->hasPages()) {
  564.                 return '';
  565.             }
  566.         } elseif (!$active['page']->hasPages()) {
  567.             // found pages has no children; render siblings
  568.             $active['page'] = $active['page']->getParent();
  569.         } elseif (is_int($maxDepth) && $active['depth'] + $maxDepth) {
  570.             // children are below max depth; render siblings
  571.             $active['page'] = $active['page']->getParent();
  572.         }
  573.         $attribs = [
  574.             'class' => $ulClass,
  575.             'id' => $ulId,
  576.         ];
  577.         // We don't need a prefix for the menu ID (backup)
  578.         $skipValue $this->_skipPrefixForId;
  579.         $this->skipPrefixForId();
  580.         $html $indent '<ul'
  581.                         $this->_htmlAttribs($attribs)
  582.                         . '>'
  583.                         $this->getEOL();
  584.         // Reset prefix for IDs
  585.         $this->_skipPrefixForId $skipValue;
  586.         /** @var Page $subPage */
  587.         foreach ($active['page'] as $subPage) {
  588.             if (!$this->accept($subPage)) {
  589.                 continue;
  590.             }
  591.             $liClass '';
  592.             if ($subPage->isActive(true) && $addPageClassToLi) {
  593.                 $liClass $this->_htmlAttribs(
  594.                     ['class' => $activeClass ' ' $subPage->getClass()]
  595.                 );
  596.             } elseif ($subPage->isActive(true)) {
  597.                 $liClass $this->_htmlAttribs(['class' => $activeClass]);
  598.             } elseif ($addPageClassToLi) {
  599.                 $liClass $this->_htmlAttribs(
  600.                     ['class' => $subPage->getClass()]
  601.                 );
  602.             }
  603.             $html .= $indent $innerIndent '<li' $liClass '>' $this->getEOL();
  604.             $html .= $indent str_repeat($innerIndent2) . $this->htmlify($subPage)
  605.                                                            . $this->getEOL();
  606.             $html .= $indent $innerIndent '</li>' $this->getEOL();
  607.         }
  608.         $html .= $indent '</ul>';
  609.         return $html;
  610.     }
  611.     /**
  612.      * Renders a normal menu (called from {@link renderMenu()})
  613.      *
  614.      * @param  Container                 $container     container to render
  615.      * @param  string|string[]           $ulClasses     CSS class for UL levels
  616.      * @param  string                    $indent        initial indentation
  617.      * @param  string                    $innerIndent   inner indentation
  618.      * @param  int|null                  $minDepth      minimum depth
  619.      * @param  int|null                  $maxDepth      maximum depth
  620.      * @param  bool                      $onlyActive    render only active branch?
  621.      * @param  bool                      $expandSibs    render siblings of active
  622.      *                                                  branch nodes?
  623.      * @param  string|null               $ulId          unique identifier (id)
  624.      *                                                  for first UL
  625.      * @param  bool                      $addPageClassToLi  adds CSS class from
  626.      *                                                      page to li element
  627.      * @param  string|null               $activeClass       CSS class for active
  628.      *                                                      element
  629.      * @param  string                    $parentClass       CSS class for parent
  630.      *                                                      li's
  631.      * @param  bool                      $renderParentClass Render parent class?
  632.      *
  633.      * @return string                                       rendered menu (HTML)
  634.      */
  635.     protected function _renderMenu(
  636.         Container $container,
  637.         $ulClasses,
  638.         $indent,
  639.         $innerIndent,
  640.         $minDepth,
  641.         $maxDepth,
  642.         $onlyActive,
  643.         $expandSibs,
  644.         $ulId,
  645.         $addPageClassToLi,
  646.         $activeClass,
  647.         $parentClass,
  648.         $renderParentClass
  649.     ) {
  650.         $html '';
  651.         // find deepest active
  652.         if ($found $this->findActive($container$minDepth$maxDepth)) {
  653.             $foundPage $found['page'];
  654.             $foundDepth $found['depth'];
  655.         } else {
  656.             $foundPage null;
  657.             $foundDepth null;
  658.         }
  659.         // create iterator
  660.         $iterator = new \RecursiveIteratorIterator($container\RecursiveIteratorIterator::SELF_FIRST);
  661.         if (is_int($maxDepth)) {
  662.             $iterator->setMaxDepth($maxDepth);
  663.         }
  664.         // iterate container
  665.         $prevDepth = -1;
  666.         foreach ($iterator as $page) {
  667.             $depth $iterator->getDepth();
  668.             $isActive $page->isActive(true);
  669.             // Set ulClass depth wise if array of classes is supplied.
  670.             if (\is_array($ulClasses)) {
  671.                 $ulClass $ulClasses[$depth] ?? $ulClasses['default'];
  672.             } else {
  673.                 $ulClass = (string) $ulClasses;
  674.             }
  675.             if ($depth $minDepth || !$this->accept($page)) {
  676.                 // page is below minDepth or not accepted by visibilty
  677.                 continue;
  678.             } elseif ($expandSibs && $depth $minDepth) {
  679.                 // page is not active itself, but might be in the active branch
  680.                 $accept false;
  681.                 if ($foundPage) {
  682.                     if ($foundPage->hasPage($page)) {
  683.                         // accept if page is a direct child of the active page
  684.                         $accept true;
  685.                     } elseif ($page->getParent()->isActive(true)) {
  686.                         // page is a sibling of the active branch...
  687.                         $accept true;
  688.                     }
  689.                 }
  690.                 if (!$isActive && !$accept) {
  691.                     continue;
  692.                 }
  693.             } elseif ($onlyActive && !$isActive) {
  694.                 // page is not active itself, but might be in the active branch
  695.                 $accept false;
  696.                 if ($foundPage) {
  697.                     if ($foundPage->hasPage($page)) {
  698.                         // accept if page is a direct child of the active page
  699.                         $accept true;
  700.                     } elseif ($foundPage->getParent()->hasPage($page)) {
  701.                         // page is a sibling of the active page...
  702.                         if (!$foundPage->hasPages() ||
  703.                             is_int($maxDepth) && $foundDepth $maxDepth) {
  704.                             // accept if active page has no children, or the
  705.                             // children are too deep to be rendered
  706.                             $accept true;
  707.                         }
  708.                     }
  709.                 }
  710.                 if (!$accept) {
  711.                     continue;
  712.                 }
  713.             }
  714.             // make sure indentation is correct
  715.             $depth -= $minDepth;
  716.             $myIndent $indent str_repeat($innerIndent$depth 2);
  717.             if ($depth $prevDepth) {
  718.                 $attribs = [];
  719.                 // start new ul tag
  720.                 if ((is_string($ulClasses) && == $depth) || is_array($ulClasses)) {
  721.                     $attribs = [
  722.                         'class' => $ulClass,
  723.                         'id' => $ulId,
  724.                     ];
  725.                 }
  726.                 // We don't need a prefix for the menu ID (backup)
  727.                 $skipValue $this->_skipPrefixForId;
  728.                 $this->skipPrefixForId();
  729.                 $html .= $myIndent '<ul'
  730.                                    $this->_htmlAttribs($attribs)
  731.                                    . '>'
  732.                                    $this->getEOL();
  733.                 // Reset prefix for IDs
  734.                 $this->_skipPrefixForId $skipValue;
  735.             } elseif ($prevDepth $depth) {
  736.                 // close li/ul tags until we're at current depth
  737.                 for ($i $prevDepth$i $depth$i--) {
  738.                     $ind $indent str_repeat($innerIndent$i 2);
  739.                     $html .= $ind $innerIndent '</li>' $this->getEOL();
  740.                     $html .= $ind '</ul>' $this->getEOL();
  741.                 }
  742.                 // close previous li tag
  743.                 $html .= $myIndent $innerIndent '</li>' $this->getEOL();
  744.             } else {
  745.                 // close previous li tag
  746.                 $html .= $myIndent $innerIndent '</li>' $this->getEOL();
  747.             }
  748.             // render li tag and page
  749.             $liClasses = [];
  750.             // Is page active?
  751.             if ($isActive) {
  752.                 $liClasses[] = $activeClass;
  753.             }
  754.             // Add CSS class from page to LI?
  755.             if ($addPageClassToLi) {
  756.                 $liClasses[] = $page->getClass();
  757.             }
  758.             // Add CSS class for parents to LI?
  759.             if ($renderParentClass && $page->hasChildren()) {
  760.                 // Check max depth
  761.                 if ((is_int($maxDepth) && ($depth $maxDepth))
  762.                     || !is_int($maxDepth)
  763.                 ) {
  764.                     $liClasses[] = $parentClass;
  765.                 }
  766.             }
  767.             $html .= $myIndent $innerIndent '<li'
  768.                    $this->_htmlAttribs(['class' => implode(' '$liClasses)])
  769.                    . '>' $this->getEOL()
  770.                    . $myIndent str_repeat($innerIndent2)
  771.                    . $this->htmlify($page)
  772.                    . $this->getEOL();
  773.             // store as previous depth for next iteration
  774.             $prevDepth $depth;
  775.         }
  776.         if ($html) {
  777.             // done iterating container; close open ul/li tags
  778.             for ($i $prevDepth 1$i 0$i--) {
  779.                 $myIndent $indent str_repeat($innerIndent $innerIndent$i 1);
  780.                 $html .= $myIndent $innerIndent '</li>' $this->getEOL()
  781.                          . $myIndent '</ul>' $this->getEOL();
  782.             }
  783.             $html rtrim($html$this->getEOL());
  784.         }
  785.         return $html;
  786.     }
  787.     /**
  788.      * Renders helper
  789.      *
  790.      * Renders a HTML 'ul' for the given $container. If $container is not given,
  791.      * the container registered in the helper will be used.
  792.      *
  793.      * Available $options:
  794.      *
  795.      * @param  Container $container
  796.      * @param  array $options    [optional] options for controlling rendering
  797.      *
  798.      * @return string rendered menu
  799.      */
  800.     public function renderMenu(Container $container, array $options = [])
  801.     {
  802.         $options $this->_normalizeOptions($options);
  803.         if ($options['onlyActiveBranch'] && !$options['renderParents']) {
  804.             $html $this->_renderDeepestMenu(
  805.                 $container,
  806.                 $options['ulClass'],
  807.                 $options['indent'],
  808.                 $options['innerIndent'],
  809.                 $options['minDepth'],
  810.                 $options['maxDepth'],
  811.                 $options['ulId'],
  812.                 $options['addPageClassToLi'],
  813.                 $options['activeClass'],
  814.                 $options['parentClass'],
  815.                 $options['renderParentClass']
  816.             );
  817.         } else {
  818.             $html $this->_renderMenu(
  819.                 $container,
  820.                 $options['ulClass'],
  821.                 $options['indent'],
  822.                 $options['innerIndent'],
  823.                 $options['minDepth'],
  824.                 $options['maxDepth'],
  825.                 $options['onlyActiveBranch'],
  826.                 $options['expandSiblingNodesOfActiveBranch'],
  827.                 $options['ulId'],
  828.                 $options['addPageClassToLi'],
  829.                 $options['activeClass'],
  830.                 $options['parentClass'],
  831.                 $options['renderParentClass']
  832.             );
  833.         }
  834.         return $html;
  835.     }
  836.     /**
  837.      * Renders the inner-most sub menu for the active page in the $container
  838.      *
  839.      * This is a convenience method which is equivalent to the following call:
  840.      * <code>
  841.      * renderMenu($container, array(
  842.      *     'indent'           => $indent,
  843.      *     'ulClass'          => $ulClass,
  844.      *     'minDepth'         => null,
  845.      *     'maxDepth'         => null,
  846.      *     'onlyActiveBranch' => true,
  847.      *     'renderParents'    => false
  848.      * ));
  849.      * </code>
  850.      *
  851.      * @param  Container $container
  852.      * @param  string|null               $ulClass    [optional] CSS class to
  853.      *                                               use for UL element. Default
  854.      *                                               is to use the value from
  855.      *                                               {@link getUlClass()}.
  856.      * @param  string|int                $indent     [optional] indentation as
  857.      *                                               a string or number of
  858.      *                                               spaces. Default is to use
  859.      *                                               the value retrieved from
  860.      *                                               {@link getIndent()}.
  861.      * @param  string|null               $ulId       [optional] Unique identifier
  862.      *                                               (id) use for UL element
  863.      * @param  bool                      $addPageClassToLi  adds CSS class from
  864.      *                                                      page to li element
  865.      * @param  string|int                $innerIndent   [optional] inner
  866.      *                                                  indentation as a string
  867.      *                                                  or number of spaces.
  868.      *                                                  Default is to use the
  869.      *                                                  {@link getInnerIndent()}.
  870.      *
  871.      * @return string                                   rendered content
  872.      */
  873.     public function renderSubMenu(
  874.         Container $container,
  875.         $ulClass null,
  876.         $indent null,
  877.         $ulId null,
  878.         $addPageClassToLi false,
  879.         $innerIndent null
  880.     ) {
  881.         return $this->renderMenu($container, [
  882.             'indent' => $indent,
  883.             'innerIndent' => $innerIndent,
  884.             'ulClass' => $ulClass,
  885.             'minDepth' => null,
  886.             'maxDepth' => null,
  887.             'onlyActiveBranch' => true,
  888.             'renderParents' => false,
  889.             'ulId' => $ulId,
  890.             'addPageClassToLi' => $addPageClassToLi,
  891.         ]);
  892.     }
  893.     /**
  894.      * Renders the given $container with the given template
  895.      *
  896.      * The container will simply be passed on as a model to the view script
  897.      * as-is, and will be available in the partial script as 'container', e.g.
  898.      * <code>echo 'Number of pages: ', count($this->container);</code>.
  899.      *
  900.      * @param  Container $container
  901.      * @param  string|array|null $partial     [optional] partial view
  902.      *                                               script to use. Default is to
  903.      *                                               use the partial registered
  904.      *                                               in the helper. If an array
  905.      *                                               is given, it is expected to
  906.      *                                               contain two values; the
  907.      *                                               partial view script to use,
  908.      *                                               and the module where the
  909.      *                                               script can be found.
  910.      *
  911.      * @return string                                helper output
  912.      *
  913.      * @throws \Exception   When no partial script is set
  914.      */
  915.     public function renderTemplate(Container $container$partial null)
  916.     {
  917.         if (null === $partial) {
  918.             $partial $this->getTemplate();
  919.         }
  920.         if (empty($partial)) {
  921.             $e = new \Exception('Unable to render menu: No partial view script provided');
  922.             throw $e;
  923.         }
  924.         $model = [
  925.             'pages' => $container// we can't use `container` as index name, since this is used by the DI-container
  926.         ];
  927.         return $this->templatingEngine->render($partial$model);
  928.     }
  929.     /**
  930.      * Alias of renderTemplate()
  931.      *
  932.      * @param Container $container
  933.      * @param string|array|null $partial
  934.      *
  935.      * @return string
  936.      */
  937.     public function renderPartial(Container $container$partial null)
  938.     {
  939.         return $this->renderTemplate($container$partial);
  940.     }
  941.     /**
  942.      * {@inheritdoc}
  943.      */
  944.     public function render(Container $container)
  945.     {
  946.         if ($partial $this->getTemplate()) {
  947.             return $this->renderTemplate($container$partial);
  948.         } else {
  949.             return $this->renderMenu($container);
  950.         }
  951.     }
  952. }