ZF-10641: Zend_View_Helper_Navigation_Menu::renderPartial() don't works with ACL

Description

Hi all, I've tried ACL working with Zend_Navigation using view partials. This is done using the navigation menu view helper and invoking the "setPartial()" method, passing my partial view path in.

The problem is that when you tell it to use a partial, it skips the default rendering method (renderMenu()), which happens to also contain the logic for filtering out menu items based on ACL.

I've temporarily fixed this by inserting a few lines into class Zend_View_Helper_Navigation_Menu, starting at row 596:


596
597     $iterator = new RecursiveIteratorIterator(clone $container, RecursiveIteratorIterator::SELF_FIRST);
598     foreach ($iterator as $page) {
599         /* @var $page Zend_Navigation_Page_Mvc */
600         if (!$this->accept($page)) {
601             $container->removePage($page);
602         }
603     }
604
605     $model = array(
606         'container' => $container
607     );
608

But this works only for items with depth=0...

I hope this helps to find a solution... Regards, Fabio.

Comments

Can you provide a reproduce case (preferably as a unit test), please? It should provide: * Minimal code necessary to reproduce the issue * Expected results * Actual results

We need this in order to better evaluate and diagnose the issue.

This is the code:

CONTROLLER:


class TestmenuController extends Zend_Controller_Action {
    public function indexAction() {
        /**
         * Setting up the menu
         */ 
        $menu = array (
            array (
                'label' => 'the first', 
                'uri' => '#',
                'pages' => array (
                    array ('label' => 'child first one', 'uri' => '#' ), 
                    array ('label' => 'child first two', 'uri' => '#' ) 
                ) 
            ), 
            array (
                'label' => 'the second', 
                'uri' => '#', 
                'resource' => 'secret',
                'pages' => array (
                    array ('label' => 'child one', 'uri' => '#' ), 
                    array ('label' => 'child two', 'uri' => '#' ) 
                ) 
            ), 
            array (
                'label' => 'the third', 
                'uri' => '#', 
                'pages' => array (
                  array ('label'=>'another child', 'uri'=>'#', 'resource'=>'secret'),
                  array ('label'=>'another child', 'uri'=>'#', 'resource'=>'secret') 
                ) 
            ) 
        );
        
        /**
         * Setting up the ACL
         */
        $acl=new Zend_Acl();
        $acl->addResource('secret');
        $acl->addRole('user');
        $acl->deny('user','secret');
        
        /**
         * Setting up the Navigator
         */ 
        $navigator=new Zend_Navigation();
        $navigator->addPages($menu);
        $this->view->navigation($navigator)
                    ->setAcl($acl)
                    ->setRole('user');
        
    }
}

VIEW: index.phtml


echo $this->navigation()->menu();
echo "";
echo $this->navigation()->menu()->setPartial('testmenu/_partial.phtml');

_partial.phtml


$iterator = new RecursiveIteratorIterator($this->container, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $page) {
    echo $this->navigation()->menu()->htmlify($page) . "
"; }

Expected results:

the first child first one child first two

the third

the first child first one child first two the third

Actual results:

the first child first one child first two

the third

the first child first one child first two the second child one child two the third another child another child


foreach ($iterator as $page) {
    if ($this->navigation()->menu()->accept($page)) {
        echo $this->navigation()->menu()->htmlify($page) . "
"; } }

My suggestion:


public function renderPartial(Zend_Navigation_Container $container = null,
                              $partial = null)
{
    // ...
    
    if ($this->getUseAcl()) {
        // ...
        
        foreach ($iterator as $page) {
            if (!$this->accept($page)) {
                $container->removePage($page);
            }
        }
    }
    
    // ...
}

Another idea:


// Create iterator
$iterator = new RecursiveIteratorIterator($container,
                    RecursiveIteratorIterator::SELF_FIRST);

$maxDepth = $this->getMaxDepth();
if (is_int($maxDepth)) {
    $iterator->setMaxDepth($maxDepth);
}

{quote}But this works only for items with depth=0...{quote} It's very ugly. I'm looking for a solution.

I have found a solution and I will write some unit tests.