display content without submenu items

keylope - March 24, 2007 - 00:16
Project:Menu Trim
Version:5.x-1.x-dev
Component:Code
Category:feature request
Priority:normal
Assigned:Unassigned
Status:won't fix
Description

Hello again,

I was wondering if you could help me figure this problem out. Currently I've got pages and other content types listed under menu categories but are not part of the menu that is trimmed. When these pages are viewed, the menu dissappears (expected behavior) but I was hoping that even through there is no active menu item, that the menu would still display based on the url.

I've been playing with the code and have been semi successful in getting it to work but only for the top level categories. To be truly helpful, this should be able to "find" an item down the tree.

I've added a new mode that "hides and but still shows the parent category" and have added this to the end of the if/else statements in the view case under the menu_trim_block() function

elseif ($mode == MENU_TRIM_ENABLED_SHOW_MENU_SECTION) {
  //compare the parent menu url to the url path, if there is a
  //partial match, show that section
  $item = menu_get_item($delta);
  foreach($item['children'] as $id => $mid) {
    $top_category = menu_get_item($mid);
    $top_path = drupal_get_path_alias($top_category['path']);

    if($_SERVER['QUERY_STRING'] != null)
      $url_path = explode("/",substr($_SERVER['QUERY_STRING'],2));
    else
      $url_path = explode("/",substr($_SERVER['PHP_SELF'],1));

    if($top_path == $url_path[0]) {
      $item = menu_get_item($mid);
      $data['subject'] = check_plain($item['title']);
      $data['content'] = theme('menu_tree', $mid);
      break;
    }
  }
}

Would you have any ideas of how to efficiently traverse the menu tree and be able to pattern match based on the url to get into the sub menu items?

Any help would be appreciated.

Thanks!

#1

keylope - March 27, 2007 - 23:27

So I figured out the problem and hope that you can use this. Here's the breakdown of what I've done.

I created a new option in the admin dropdown that asks if you want to trim the menu but still show the parent menu (based on the url path).

Into the code, if the active trail is not found, create a trail based on the url path. This mimics the active trail and target the last id in the trail to be able to display any children.

When the appropriate menu section is rendered, check the mode and recreate the menu and all children and replace this newly created menu with the old menu created.

- This is done because since there is no "real" active item, any menu children are not expanded. I couldn't find good way to do this other than copying and modifying the menu_tree function to be able to target a specific menu id.
- The output isn't themed and since most theme functions only take an id, I wrapped the output by copying the theme_menu_tree function which just appends the ul tag and adds a class of menu.

Anyway, I'd love to get your opinion on this and see if it's something you would want to add to your project. That way I could get any other updates that you add in the future!

Thanks. Below is the code for menu_trim.module.

<?php
// $Id: menu_trim.module,v 1.2 2007/01/13 00:59:05 davidlesieur Exp $

/**
* @file Allows menu hierarchies to be trimmed when navigated. By trimming, here
* we mean skipping the display of parent menu items when any of some designated
* items becomes active.
*
* At the simplest level, this module allows to hide a menu until some of its
* items has become the active item (by reaching the corresponding url). This is
* easier than configuring the menu's block visibility for each possible path
* contained in the menu.
*
* This module can be used to make a "contextual secondary menu" based on the
* primary links (e.g. you set the Primary links menu for "Allow trimming for
* this menu, hide menu when no item is active", activate the "Primary links
* (menu_trim)" block, then edit the menu's top-level items and check the "Trim
* parent items" option). After that, when an item is selected in the primary
* links, the contextual menu will appear, showing only the subitems of the
* selected item. Drupal already allows contextual "secondary links" based on
* the primary links, but these secondary links are limited to a depth of one
* level. Menu Trim, on the other hand, can display complete menu subtrees.
*
* This module can also be used in any menu to trim parents when reaching deep
* menu items.  This can make deep menus more usable, and the breadcrumb will
* still show the full path.
*/

// TODO: When a menu item is deleted, remove the corresponding menu_trim_item_mid variable, if any.

define ('MENU_TRIM_DISABLED', 0);
define ('MENU_TRIM_ENABLED_SHOW_MENU_ALWAYS', 1);
define ('MENU_TRIM_ENABLED_SHOW_MENU_ACTIVE', 2);
define ('MENU_TRIM_ENABLED_SHOW_MENU_SECTION', 3);

/**
* Implementation for hook_menu().
*/
function menu_trim_menu($may_cache) {
  $items = array();

  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/menu_trim',
      'title' => t('Menu trim'),
      'description' => t('Choose what menus need trimming.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array('menu_trim_admin_settings'),
      'access' => user_access('administer site configuration'),
      'type' => MENU_NORMAL_ITEM,
    );
  }

  return $items;  
}

/**
* Administration page callback.
*/
function menu_trim_admin_settings() {
  $form['menu_trim'] = array(
    '#type' => 'fieldset',
    '#title' => t('Trim menus'),
    '#description' => t('Choose the menus that need to be trimmed. Each of the menus where trimming is allowed will get a corresponding <em>Menu trim</em> block. You should then !enable, and !edit that need to have their parents trimmed.', array('!enable' => l(t('enable those blocks'), 'admin/build/block'), '!edit' => l(t('edit the menu items'), 'admin/build/menu'))),
  );
   
  $menus = menu_get_root_menus();
  foreach ($menus as $mid => $title) {
    $form['menu_trim']['menu_trim_menu_'. $mid] = array(
      '#type' => 'select',
      '#title' => check_plain($title),
      '#options' => array(
        MENU_TRIM_DISABLED => t('Never trim this menu'),
        MENU_TRIM_ENABLED_SHOW_MENU_ALWAYS => t('Allow trimming of this menu'),
        MENU_TRIM_ENABLED_SHOW_MENU_ACTIVE => t('Allow trimming of this menu, hide menu when no item is active'),
MENU_TRIM_ENABLED_SHOW_MENU_SECTION => t('Allow trimming of this menu, show section menu when no item is active'),
      ),
      '#default_value' => variable_get('menu_trim_menu_'. $mid, MENU_TRIM_DISABLED),
    );
  }

  return system_settings_form($form);
}

/**
* Implementation of hook_block().
*/
function menu_trim_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks = array();
      $menus = menu_get_root_menus();
      foreach ($menus as $mid => $title) {
        if (variable_get('menu_trim_menu_'. $mid, MENU_TRIM_DISABLED) != MENU_TRIM_DISABLED) {
          $blocks[$mid]['info'] = check_plain($title) .' ('. t('Menu trim') .')';
        }
      }
      return $blocks;
     
    case 'view':
      if (($mode = variable_get('menu_trim_menu_'. $delta, MENU_TRIM_DISABLED)) != MENU_TRIM_DISABLED) {
        // Get the trail of the active item in the $delta menu. If the active
        // item is not in that menu, the menu won't be shown at all.
        $trail = _menu_get_active_trail_in_submenu($delta);

        // Get the trail from the url path for nodes that are not menu items
        if(!is_array($trail) && $mode == MENU_TRIM_ENABLED_SHOW_MENU_SECTION) {
          if($_SERVER['QUERY_STRING'] != null)
            $url_path = explode("/",substr($_SERVER['QUERY_STRING'],2));
          else
            $url_path = explode("/",substr($_SERVER['PHP_SELF'],1));
         
          $trail = menu_trim_get_url_section($delta, $url_path);
          $last_child_id = $trail[count($trail)-1];
        }

if (is_array($trail)) {
          // The active item is child of this menu, check for trimming
          while ($mid = array_pop($trail)) {
            if (variable_get('menu_trim_item_'. $mid, FALSE)) {
              // Show the menu using the item as root
              $item = menu_get_item($mid);
              $data['subject'] = check_plain($item['title']);
 
  // display all children for the lowest id in the trail (this is the code from menu_tree)
              if(isset($last_child_id) && $mode == MENU_TRIM_ENABLED_SHOW_MENU_SECTION) {
                $newmenu = menu_get_menu();
                if (isset($newmenu['visible'][$mid]) && $newmenu['visible'][$mid]['children']) {
                  foreach ($newmenu['visible'][$mid]['children'] as $sid) {
                    $type = isset($newmenu['visible'][$sid]['type']) ? $newmenu['visible'][$sid]['type'] : NULL;
                    $children = isset($newmenu['visible'][$sid]['children']) ? $newmenu['visible'][$sid]['children'] : NULL;
                    $output .= theme('menu_item', $sid, ($sid == $last_child_id) ? theme('menu_tree', $sid) : '', count($children) == 0);
                  }
                }
// code from theme_menu_tree
                $output = "\n<ul class=\"menu\">\n". $output ."\n</ul>\n";
                $data['content'] = $output;
              } else {
                $data['content'] = theme('menu_tree', $mid);
  }
  break;
            }
          }
          if (!$data) {
            // No trim flag found in the trail, show the full menu
            $item = menu_get_item($delta);
            $data['subject'] = check_plain($item['title']);
            $data['content'] = theme('menu_tree', $delta);
          }
        }
        elseif ($mode == MENU_TRIM_ENABLED_SHOW_MENU_ALWAYS) {
          // The active item is not child of this menu, show the plain menu
          $item = menu_get_item($delta);
          $data['subject'] = check_plain($item['title']);
          $data['content'] = theme('menu_tree', $delta);
        }
        // Otherwise, $mode == MENU_TRIM_ENABLED_MENU_SHOW_ACTIVE, do not show
        // the menu since it has to be shown only when it contains an active
        // item
      }
      return $data;
  }
}

/**
* Implementation of hook_form_alter().
*/
function menu_trim_form_alter($form_id, &$form) {
  if ($form_id == 'menu_edit_item_form') {
    $mid = $form['mid']['#value'];
    $item = menu_get_item($mid);
    // Check if this item is in a menu that is set for trimming
    while ($item['pid']) {
      $root_mid = $item['pid'];
      $item = menu_get_item($item['pid']);
    }
    if (variable_get('menu_trim_menu_'. $root_mid, MENU_TRIM_DISABLED) != MENU_TRIM_DISABLED) {
      $form['trim_item'] = array(
        '#type' => 'checkbox',
        '#title' => t('Trim parent items.'),
        '#description' => t('Check this option to trim the parent items when this item or any of its children becomes active.') .'<br />'. t('Trimming applies only to the menu\'s corresponding <em>Menu trim</em> !block, not to its normal block. When trimming occurs, this item\'s title will become the menu\'s title. Note also that if this item has no children, has its parents trimmed, and becomes active, then no menu will be displayed at all since such menu would be empty.', array('!block' => l(t('block'), 'admin/build/block'))),
        '#default_value' => variable_get('menu_trim_item_'. $mid, FALSE),
        '#weight' => 1,
      );
      $form['#submit']['menu_trim_edit_item_form_submit'] = current($form['#submit']);

      // Make sure the button remains at the form's bottom
      $form['submit']['#weight'] = 10;
    }
  }
}

function menu_trim_edit_item_form_submit($form_id, $form_values) {
  variable_set('menu_trim_item_'. $form_values['mid'], $form_values['trim_item']);
}

/**
* Find the menu for a given page by finding the lowest matching section in the url
*/
function menu_trim_get_url_section($delta, $url_path, $depth = 0) {
  $section = array();
  $menu_items = menu_get_item($delta);
 
  if(isset($menu_items['children']) && is_array($menu_items['children'])) {
    while($mid = array_pop($menu_items['children'])) {
      $child_category = menu_get_item($mid);
      $child_path = drupal_get_path_alias($child_category['path']);
     
      // found match, check the next url slice
      if(strpos($child_path,$url_path[0]) !== false) {
        $path_left = array_slice($url_path, 1);
$section[$depth] = $mid;
$depth++;
       
$section += menu_trim_get_url_section($mid, $path_left,$depth);
      }
    }
  }
  return $section;
}

#2

keylope - March 27, 2007 - 23:47

Sheesh... already found a tweak.

This is to fix the case where there really is no trail, hide the menu, which is the current behavior of "show active menu".

Please add this code after line 114 (assigning variable $last_child_id):

// there is no url path trail so do not show the menu
if(count($trail) == 0) {
  $trail = null;
  unset($last_child_id);
}

#3

DynV - April 1, 2008 - 04:27
Assigned to:Anonymous» DynV

This is alot of code to go through, could you do a diff (-up) instead ? Would you mind explaining more the problem and the solution ?

#4

DynV - April 1, 2008 - 17:07
Status:active» postponed (maintainer needs more info)

#5

ArjanLikesDrupal - April 5, 2008 - 15:48
Title:content under menu category but not a menu item» display content without submenu items

I've read what you've done (not the code though, that's a little over my head for now). I hope my question is relevant here; your node title says exactly what i'm trying to get done, so I guess so.

I want to have a Primary Link, in my case 'Publications', and clicking that should show all content items (nodes, in this case a node type 'Publication' made with CCK) that are Publications. Since I want to show many publications (info like title, author, year, journal, keywords; say about a hundred items) these should not be included as submenu items.

There are probably more ways than one to do this, so:
- Can I do this using taxonomy as my menu structure?
- Do I need to use Views to achieve this?
- Or should I look into using the code posted above?

I'm new to Drupal so please point me in the right direction, thanks in advance.

Arjan

Edit: Hmm, I suppose it should actually be a lot easier in my situation, I guess I could just make a (Primary) menu item and make the url point to the category: /category/publications/ (sorry ;-))

#6

DynV - April 6, 2008 - 15:58

@Arjan.M I understand you are unexperienced so this isn't flaming. First, does this issue has anything to do with the original issue ? If not, it's not appropriate to take over an issue and editing to remove your issue (saying something like wrong place) and set the title back like it was would be appropriate. Second, this sounds a lot like a support issue and this one is feature request. Usually support are done in the forum unless related to a specific module which can, but not always, be in a project issue. Lastly, there's a few concepts all rolled into one so in the right place for your issue, please untangle them and try to give specific examples.

#7

David Lesieur - November 1, 2008 - 21:51
Assigned to:DynV» Anonymous
Status:postponed (maintainer needs more info)» won't fix

I'm not convinced that there would be many use cases for this feature. It would also make the module harder to understand to users (even with its current features, it is already not as intuitive as I'd like it to be).

 
 

Drupal is a registered trademark of Dries Buytaert.