You are not logged in.
Does anyone have experience integrating ACL with Zend_Navigation when using a custom view partial for menu rendering? For some reason, the view helper does not filter out menu items that are restricted based on ACL if you use a partial (it does if you use the default rendering). It will just ignore it and pass all menu elements in.
Offline
I remember Udo showed me some code that did something to this effect. It might be worth asking him.
Offline
Hello Ryan,
do you refer to this: http://framework.zend.com/manual/en/zen … gation.acl
I had issues using the "integrated" ACL functionality in combination with Zend_Navigation.
Using resources in navigation.xml and giving them access through ACL did not work for me... At least I could not make it work.
How does your approach look like?
Best, Udo
Offline
I have ACL working with Zend_Navigation without using view partials. The problem is that the default rendering of Zend_Navigation is not as flexible as I'd like (it doesn't allow you to customize the individual <li> elements), so I need to use a view partial to render it out myself. This is done using the navigation menu view helper and invoking the "setPartial()" method, passing my partial view path in (see http://framework.zend.com/manual/en/zen … ion.menu).
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 (take a look in Zend/View/Helper/Navigation/Menu.php). So the logic for filtering out the items need to be reimplemented in the view partial (or somewhere else). Seems like there was debate about this in the initial proposal (http://framework.zend.com/wiki/display/ … n+Skoglund). I guess my big question is how to re-implement that ACL logic.
For reference, here's how I have my ACL and Navigation setup in my application:
[code=php]
Bootstrap.php
...
protected function _initAcl() {
...
$acl = new Zend_Acl();
$aclData = new Zend_Config_Xml(APPLICATION_PATH . '/configs/acl.xml', $module);
foreach ($aclData->roles->role as $role) {
$acl->addRole(new Zend_Acl_Role($role));
}
foreach ($aclData->resources->resource as $resource) {
$acl->addResource(new Zend_Acl_Resource($module . '.' . $resource));
}
// If an allow whitelist is specified, process
if (!empty($aclData->whitelist->allow)) {
// Convert it to an array
$whitelist = $aclData->whitelist->allow->toArray();
// If there's more than one allow rule, iterate over and process
// each
if (isset($whitelist[0])) {
foreach ($whitelist as $allow) {
$acl->allow($allow['role'], $module . '.' . $allow['resource']);
}
} else {
$acl->allow($whitelist['role'], $module . '.' . $whitelist['resource']);
}
}
// If a blacklist is specified, process
if (!empty($aclData->blacklist->allow)) {
// Convert it to an array
$blacklist = $aclData->blacklist->allow->toArray();
// If there's more than one allow rule, iterate over and process
// each
if (isset($blacklist[0])) {
foreach ($blacklist as $deny) {
$acl->deny($deny['role'], $module . '.' . $deny['resource']);
}
} else {
$acl->deny($blacklist['role'], $module . '.' . $blacklist['resource']);
}
}
// Store the acl in the registry
Zend_Registry::set('acl', $acl);
return $acl;
}
...
protected function _initNavigation() {
...
// Load the current module's navigation section within the navigation
// config
$config = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml', MODULE);
// Create a new navigation object
$navigation = new Zend_Navigation($config);
// Set the navigation object to the view
Zend_Layout::getMvcInstance()
->getView()
->navigation($navigation);
}[/code]
Controller_Plugin_Init.php (this is a controller plugin that handles identity retrieval and user role)
[code=php]
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request) {
...
$acl = Zend_Registry::get('acl');
// Expose the acl to the view navigation so it knows which items to
// render
Zend_Layout::getMvcInstance()
->getView()
->navigation()
->setAcl($acl)
->setRole($user->Role->name);
}[/code]
And in my layout script
[code=php]
...
echo $this->navigation()->menu();
...[/code]
Offline
I solved this by writing my own navigation menu view helper that extends the one included with the framework. It could go further with optimization, but this is at least working.
Here's the code:
[code=php]
class AOneFiveThree_View_Helper_Navigation_FilteringMenu extends Zend_View_Helper_Navigation_Menu {
/**
* View helper entry point:
* Retrieves helper and optionally sets container to operate on
*
* @param Zend_Navigation_Container $container [optional] container to
* operate on
* @return Zend_View_Helper_Navigation_Menu fluent interface,
* returns self
*/
public function filteringMenu(Zend_Navigation_Container $container = null) {
if (null !== $container) {
$this->setContainer($container);
}
return $this;
}
/**
* Renders menu
*
* Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
*
* If a partial view is registered in the helper, the menu will be rendered
* using the given partial script. Unlike
* Zend_View_Helper_Navigation_Menu::render(), pages will be filtered based
* on visibility and ACL before being passed to the view partial script.
* If no partial is registered, the menu will be rendered as an 'ul' element
* by the helper's internal method.
*
* @see renderPartial()
* @see renderMenu()
*
* @param Zend_Navigation_Container $container [optional] container to
* render. Default is to
* render the container
* registered in the helper.
* @return string helper output
*/
public function render(Zend_Navigation_Container $container = null) {
if (null === $container) {
$container = $this->getContainer();
}
if ($partial = $this->getPartial()) {
return $this->__filterRestrictedPages($container)
->renderPartial($container, $partial);
} else {
return $this->renderMenu($container);
}
}
/**
* Filters out invisible and ACL based restricted pages
*
* @access protected
* @param Zend_Navigation_Container $container
* @return Zend_Navigation_Container
*/
protected function __filterRestrictedPages(Zend_Navigation_Container $container) {
// Need to clone the container as we will be removing pages from the
// actual container within the recursive iterator loop. Removing
// elements from the object we're iterating over *while* iterating will
// trip up the iterator.
$iterator = new RecursiveIteratorIterator(clone $container,
RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $page) {
/* @var $page Zend_Navigation_Page_Mvc */
if (!$this->accept($page)) {
$container->removePage($page);
}
}
return $this;
}
[/code]
In order to use the custom menu, you need to add the helper path to your view (replace the AOneFiveThree with your own library or application namespace):
[code=php]
// In your view-script.phtml
$this->addHelperPath('AOneFiveThree/View/Helper/Navigation', 'AOneFiveThree_View_Helper_Navigation_');
[/code]
Then, to load the helper, tell it to user a view partial for rendering and render it out:
[code=php]
$filteringMenu = $this->navigation()->FilteringMenu();
$filteringMenu->setPartial('partials/default-navigation.phtml');
echo $filteringMenu;
[/code]
And of course, prior to all of this, you need to expose your navigation object, ACL object and the current role to the navigation helper somewhere (probably your bootstrap or a controller plugin). Each page in your navigation object needs to have a resource defined that matches the resources in your ACL for the menu helper to properly filter out restricted pages. The framework reference guide goes over all of this.
[code=php]
// Assuming $navigation is your navigation object
// Expose the navigation object to the navigation view helper
Zend_Layout::getMvcInstance()
->getView()
->navigation($navigation);
// Assuming $acl is your $acl object and $role is the current ACL role in use
// Expose the acl and role to the view navigation helper so it knows which items to
// filter
Zend_Layout::getMvcInstance()
->getView()
->navigation()
->setAcl($acl)
->setRole($role);
[/code]
I can explain this in more detail if anyone wants, just let me know.
Edit: I realize this only works on a shallow navigation
Last edited by ryan.horn (2009-11-28 22:29:23)
Offline
Hi I use this:
Notice the : $this->navigation()->accept($page)
Seems to work with all the tests I have done
Had to do all this though cause I needed an extra Span to control tabs display - annnoying!
[code=php]
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Navigational Menu partial to output the navigation correctly
*
* <code>
* // User in the general Layout
* echo $this->navigation()->menu()->setPartial(array('menu.phtml', 'default'));
* </code>
*
* @category CodeBlender
* @package Theme
* @copyright Copyright (c) 2000-2009 Triangle Solutions Ltd. (http://www.triangle-solutions.com/)
* @license http://codeblender.net/license
* @version SVN: $Id: $
*/
// Set the filter option for the Active navigation
$this->navigation()->findByResource(
Zend_Controller_Front::getInstance()->getRequest()->module .
Zend_Controller_Front::getInstance()->getRequest()->controller
);
?>
<ul>
<?php
// Loop through the container
foreach ($this->container as $page) {
// Check whether the page is set as Visible and also it is accepted through the ACL
if ($page->isVisible() && $this->navigation()->accept($page)) {
// Create the class to use if the tab is active
$liClass = $page->isActive(false) ? ' class="active"' : '';
// HTML to generate the tabs
?>
<li<?php echo $liClass; ?>>
<a href="<?php echo $page->getHref(); ?>" title="<?php echo $page->getTitle(); ?>">
<span><?php echo $page->getLabel() ?></span></a>
</li>
<?php
}
}
?>
</ul>
[/code]
Offline
Activating subscription - forum page offers no link to do this??
Offline
@iwarner what do you mean about the forum? did you have trouble activating your account?
Offline
@ryan.horn
very nice one. What I do miss in your description is how you shaped you default-navigation.phtml where I assume you put your own classes.
Can I ask you to supply the markup for that partial view script as well please to complete your solution? Thanks.
Nice to know it's a feature not implemented in the original Zend_Navigation.
I have to use a check in my partial last time I use Zend_Navigation and found that very "inadequate".
Thanks for the solution came with.
Speaking of Zend_Navigation is there a way to know if a "link" is active (not by setting it but in sort of automatic way) it will be very helpfull for design issue.
Offline
$page->isActive() should work...
Offline
I may have explain a bit more the context.
isActive() is working for mvc page only for uri page the ref says
Zend_Navigation_Page_Uri will not try to determine whether it should be active when calling $page->isActive(). It merely returns what currently is set, so to make a URI page active you have to manually call $page->setActive() or specifying active as a page option when constructing.
So even if your page uri for example :
$page = new Zend_Navigation_Page_uri(array(
'label' => 'Home',
'uri' => 'http://www.example.com/'
));
isActive() doesn't work for your home page
you have to make it manualy active and reflect the state on all the navigation every time the page change...
Offline
Zend_Navigation_Page_Uri was designed to be used for external links. Conceptually an external link cannot be "active" and has no way of checking (versus Zend_Navigation_Page_Mvc, which has access to the request object in order to determine the current module/controller/action). Use Zend_Navigation_Page_Mvc if you want the navigation to automatically detect and set the active state for a page.
Offline
Well thanks for your answers and sorry for the late those thanks come...
I know that Page_Uri was intended (not sure if it's the right word Frenchy inside) for external URL. But i choose to use it because i have a sort of link generator in my CMS and it was more easy to handle "user typed" url (that could be internal or not) and i failed trying to reverse those url to the mvc style for the case they were internal ones. I think i must digg harder and found a way to do that it will be far better.
Offline
I solved this by writing my own navigation menu view helper that extends the one included with the framework. It could go further with optimization, but this is at least working.
Here's the code:
Thanks for putting this code up, I just tried it out and it worked perfectly for me ![]()
Offline
ryan.horn
I would like to explain me more detail
with the same problem I want to make a custom menu with ACL
I need your help please let me know exactly
that part add the code you wrote
thanks
Offline
hi i tried the code ryan.horn but I get an error I work with modules
code here
//In your layout-layout.phtml
<?php
$filteringMenu = $this->navigation()->FilteringMenu();
$filteringMenu->setPartial(array('menu.phtml','default'));
echo $filteringMenu;
?>
// In your default-view-script-menu.phtml
<?php
$this->addHelperPath('Config', 'Config_');
//In config-FilteringMenu.php
<?php
class Config_FilteringMenu extends Zend_View_Helper_Navigation_Menu {
and this is the error that the browser shows me:
Fatal error: Uncaught exception 'Zend_Navigation_Exception' with message 'Bad method call: Unknown method Zend_Navigation::FilteringMenu'
Offline