Message
  • 404 Not Found

    This site has been redesigned and many things have moved. Please use our search utility to find the updated link.

Fatica Consulting Blog

Overriding Joomla! default mod_mainmenu output

Attention: open in a new window.  Print  E-mail

With Joomla 1.5, template overrides have redefined the way people work with Joomla. Creating XHTML-compliant table-less output in Mambo was nearly impossible and involved hacking the core to correct the output. Now, Joomla allows you to drop your own output template in your /templates directory. This allows you to encapsulate all of your site-specific data in one folder. This is important not only for organization purposes, but for the ability to upgrade without fear of undoing some custom work.

There's one exception to these easy overrides and it's mod_mainmenu, which is responsible for the output of the menu code. I had a reason to do this when I tried to create a menu with CSS rounded corners in Joomla. The normal Joomla menu output is an unordered list, eg.

<ul>

<li><a href="#"><span>Menu 1</span></a></li>

<li><a href="#"><span>Menu 2</span></a></li>

</ul>

This proved to be a challenge since the CSS I was using required a few wrapping divs which were needed to apply the various styles for this rounded corner technique. It basicall requires that your menu output look like this:

<div class="dialog">
<div class="content">
<div class="t"></div>
<!-- Your content goes here -->
</div>
<div class="b"><div></div></div></div>

There's no real way to make that happen in Joomla unless create another Menu module of your own, or you override the default mod_mainmenu output, as described here. Basically, we begin by creating a new file, under templates/yourtemplatename/html/mod_mainmenu/default.php. There are two critical functions in this file, roundedCornerPatch and modMyMainMenuXMLCallback. Now, the first function is what I use to add the wrapping code required to the menu output by using a series of "find & replace"-style operations. The second is a required callback function that processes the menu XML into an unordered list with correct class attribes and Itemids. Its basically unchanged from that found in the default mod_mainmenu template. Note that both functions are wrapped in a conditional statement that checks if they've been defined yet. This is because the module may be displayed more than once on a singe page, and therefore would result in "already defined" errors.

<?php // no direct access
defined('_JEXEC') or die('Restricted access');
 
if ( ! defined('roundedCornerPatch') )
 
  {
function roundedCornerPatch($result,$tag){
 
   //this is what we add before the UL tag
  $top = '<div class="r_dialog"><div class="r_content"><div class="r_t"></div>';
 
  //this is what we add after the UL tag
  $bottom = '</div><div class="r_b"><div></div></div></div>';
 
  //do the replacement
  $result = str_replace("<ul","$top<ul", $result);
 
  $result = str_replace("</ul>", "</ul>$bottom", $result);
 
  //return the code
   return $result; 
 
  }
 
 define('roundedCornerPatch', true);
 
}//This is copied from mod_mainmenu and renamed to 'modMyMainMenuXMLCallbackDefined'
 
if ( ! defined('modMyMainMenuXMLCallbackDefined') )
 
  {
 
  function modMyMainMenuXMLCallback(&$node, $args)
 
  {
 
  $user  = &JFactory::getUser();
 
  $menu  = &JSite::getMenu();
 
  $active  = $menu->getActive();
 
  $path  = isset($active) ? array_reverse($active->tree) : null; if (($args['end']) && ($node->attributes('level') >= $args['end']))
  {
 
  $children = $node->children();
 
  foreach ($node->children() as $child)
 
  {
 
  if ($child->name() == 'ul') {
 
  $node->removeChild($child);
 
  }
 
  }
 
  }
 
 if ($node->name() == 'ul') {
 
  foreach ($node->children() as $child)
 
  {
 
  if ($child->attributes('access') > $user->get('aid', 0)) {
 
  $node->removeChild($child);
 
  }
 
  }
 
  }
 
 if (($node->name() == 'li') && isset($node->ul)) {
 
  $node->addAttribute('class', 'parent');
 
  }
 
 if (isset($path) && in_array($node->attributes('id'), $path))
 
  {
 
  if ($node->attributes('class')) {
 
  $node->addAttribute('class', $node->attributes('class').' active');
 
  } else {
 
  $node->addAttribute('class', 'active');
 
  }
 
  }
 
  else
 
  {
 
  if (isset($args['children']) && !$args['children'])
 
  {
 
  $children = $node->children();
 
  foreach ($node->children() as $child)
 
  {
 
  if ($child->name() == 'ul') {
 
  $node->removeChild($child);
 
  }
 
  }
 
  }
 
  }
 
 if (($node->name() == 'li') && ($id = $node->attributes('id'))) {
 
  if ($node->attributes('class')) {
 
  $node->addAttribute('class', $node->attributes('class').' item'.$id);
 
  } else {
 
  $node->addAttribute('class', 'item'.$id);
 
  }
 
  }
 
 if (isset($path) && $node->attributes('id') == $path[0]) {
 
  $node->addAttribute('id', 'current');
 
  } else {
 
  $node->removeAttribute('id');
 
  }
 
  $node->removeAttribute('level');
 
  $node->removeAttribute('access');
 
 
 
  }
 
  define('modMyMainMenuXMLCallbackDefined', true);
 
}
ob_start();//render the menu, and capture the output using output buffering
 
modMainMenuHelper::render($params, 'modMyMainMenuXMLCallback');
$menu_html = ob_get_contents();
ob_end_clean(); //You can use the "tag" parameter to apply this to only specific menus (not used in this example)
 
if($params->get('menutype')=="primarynav"){
  $tag = $params->get('tag_id');
  }
//output the menu!
echo roundedCornerPatch($menu_html,$tag);
?>