This is a simple module that enables the site to have drop down/flyout CSS menus for site and admin navigation.

Remember to activate and configure the menu blocks in !link

', array('!link' => l('admin/build/block', 'admin/build/block'))); break; } return $output; } /** * Implementation of hook_form_alter(). */ function nice_menus_form_alter(&$form, $form_state, $form_id) { switch ($form_id) { case 'system_theme_settings': // This is a global setting, so only insert the field // on the global settings page. if (arg(4)) { return; } // Have to add a custom submit handler since this form doesn't use // the standard system submit handler. $form['#submit'][] = 'nice_menus_system_theme_settings_submit'; // Add global theme setting for a custom CSS file. $form['nice_menus_custom_css'] = array( '#type' => 'textfield', '#title' => t('Path to custom Nice Menus CSS file'), '#description' => t('To override the default Nice Menus CSS layout, enter the path to your custom CSS file. It should be a relative path from the root of your Drupal install (e.g. sites/all/themes/example/mymenu.css).'), '#default_value' => variable_get('nice_menus_custom_css', ''), // Field appears below submit buttons without this -- yucky. '#weight' => 0, ); break; } } /** * Records the nice menu custom CSS file per theme. */ function nice_menus_system_theme_settings_submit($form, &$form_state) { variable_set('nice_menus_custom_css', $form_state['values']['nice_menus_custom_css']); } /** * Implemention of hook_menu(). */ function nice_menus_menu() { $items['admin/settings/nice_menus'] = array( 'title' => 'Nice Menus', 'description' => 'Configure Nice Menus.', 'page callback' => 'drupal_get_form', 'page arguments' => array('nice_menus_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); return $items; } /** * Settings form as implemented by hook_menu */ function nice_menus_admin_settings() { $form['nice_menus_number'] = array( '#type' => 'textfield', '#description' => t('The total number of independent Nice menus blocks you want. Enter a number between 0 and 99. If you set this to 0, you will have no blocks created but you can still use the Nice menus theme functions directly in your theme.'), '#default_value' => variable_get('nice_menus_number', '2'), '#size' => 2, ); $form['nice_menus_ie'] = array( '#type' => 'checkbox', '#title' => t('Enable IE support'), '#description' => t('This will add necessary JavaScript for Nice Menus to work properly in Internet Explorer.'), '#default_value' => variable_get('nice_menus_ie', 1), ); // Custom validation to make sure the user is entering numbers. $form['#validate'][] = 'nice_menus_settings_validate'; return system_settings_form($form); } /** * Custom validation for the settings form. */ function nice_menus_settings_validate($form, &$form_state) { $number = $form_state['values']['nice_menus_number']; // Check to make sure it is a number and that is a maximum of 2 digits. if (!is_numeric($number) || strlen($number) > 2) { form_set_error('nice_menus_number', t('You must enter a number from 0 to 99.')); } } /** * Implementation of hook_block(). */ function nice_menus_block($op = 'list', $delta = 0, $edit = array()) { global $user; switch ($op) { case 'list': for ($i = 1; $i <= variable_get('nice_menus_number', '2'); $i++) { $blocks[$i]['info'] = variable_get('nice_menus_name_'. $i, 'Nice Menu '. $i) .' (Nice Menu)'; // We have too many things changing per user, per page to cache. $blocks[$i]['cache'] = BLOCK_NO_CACHE; } return $blocks; break; case 'configure': $form['nice_menus_name_'. $delta] = array( '#type' => 'textfield', '#title' => t('Menu Name'), '#default_value' => variable_get('nice_menus_name_'. $delta, 'Nice Menu '. $delta), ); $form['nice_menus_menu_'. $delta] = array( '#type' => 'select', '#title' => t('Source Menu Tree'), '#description' => t('The menu tree from which to show a nice menu.'), '#default_value' => variable_get('nice_menus_menu_'. $delta, 'navigation:0'), '#options' => menu_parent_options(menu_get_menus(), 0), ); $form['nice_menus_type_'. $delta] = array( '#type' => 'select', '#title' => t('Menu Style'), '#description' => t('right: menu items are listed on top of each other and expand to the right') .'
'. t('left: menu items are listed on top of each other and expand to the left') .'
'. t('down: menu items are listed side by side and expand down'), '#default_value' => variable_get('nice_menus_type_'. $delta, 'right'), '#options' => drupal_map_assoc(array('right', 'left', 'down')), ); return $form; break; case 'save': variable_set('nice_menus_name_'. $delta, $edit['nice_menus_name_'. $delta]); variable_set('nice_menus_menu_'. $delta, $edit['nice_menus_menu_'. $delta]); variable_set('nice_menus_type_'. $delta, $edit['nice_menus_type_'. $delta]); break; case 'view': // Build the nice menu for the block. list($menu_name, $mlid) = explode(':', variable_get('nice_menus_menu_'. $delta, 'navigation:0')); $direction = variable_get('nice_menus_type_'. $delta, 'right'); if ($output = theme('nice_menu', $delta, $menu_name, $mlid, $direction)) { $block['content'] = $output['content']; if (variable_get('nice_menus_type_'. $delta, 'right') == 'down') { $class = 'nice-menu-hide-title'; } else { $class = 'nice-menu-show-title'; } // If we're building the navigation block // use the same block title logic as menu module. if ($output['subject'] == t('Navigation') && $user->uid) { $subject = $user->name; } else { $subject = $output['subject']; } $block['subject'] = ''. check_plain($subject) .''; } else { $block['content'] = false; } return $block; break; } } /** * Implmentation of hook_theme(). */ function nice_menus_theme() { return array( 'nice_menu_tree' => array( 'arguments' => array('menu_name' => NULL, 'mlid' => NULL, 'menu' => NULL), ), 'nice_menu_build' => array( 'arguments' => array('menu' => NULL), ), 'nice_menu' => array( 'arguments' => array('id' => NULL, 'menu_name' => NULL, 'mlid' => NULL, 'direction' => 'right', 'menu' => NULL), ), 'nice_menu_primary_links' => array( 'arguments' => array('direction' => 'down', 'menu' => NULL), ), 'nice_menu' => array( 'arguments' => array('id' => NULL, 'pid' => NULL, 'direction' => 'right', 'menu' => NULL), ), 'nice_menu_primary_links' => array( 'arguments' => array('direction' => 'down', 'menu' => NULL), ), ); } /** * Implementation of hook_init(). * * We are adding the JavaScript and CSS here rather than theme_nice_menu * because when block caching is enabled none of it would get fired * and the menus are unstyled. */ function nice_menus_init() { // Add JavaScript, if enabled. if (variable_get('nice_menus_ie', 1) == 1) { drupal_add_js(drupal_get_path('module', 'nice_menus') .'/nice_menus.js'); } // Add main CSS functionality. drupal_add_css(drupal_get_path('module', 'nice_menus') .'/nice_menus.css'); // Add custom CSS layout if specified. if ($custom = variable_get('nice_menus_custom_css', '')) { drupal_add_css($custom); } // Fall back to default layout. else { drupal_add_css(drupal_get_path('module', 'nice_menus') .'/nice_menus_default.css'); } } /** * Builds the final nice menu. * * @param $menu_name * The top-level menu name that contains the menu to use (e.g. navigation * or primary-links) for Drupal menus. For custom $menus this is just the * name for menu display. * @param $mlid * The menu ID from which to start building the items, i.e. the parent * of the displayed menu. * @param $menu * Optional. A custom menu array to use for theming -- it should have * the same structure as that returned by menu_tree_all_data(). * @return * An HTML string of properly nested nice menu lists. */ function theme_nice_menu_tree($menu_name, $mlid = NULL, $menu = NULL) { // Load the full menu array. $menu = isset($menu) ? $menu : menu_tree_all_data($menu_name); // For custom $menus and menus built all the way from the top-level we // don't need to "create" the specific sub-menu and we need to get the title // from the $menu_name since there is no "parent item" array. // Create the specific menu if we have a mlid. if (!empty($mlid)) { // Load the parent menu item. $item = menu_link_load($mlid); $title = check_plain($item['title']); // Narrow down the full menu to the specific sub-tree we need. for ($p = 1; $p < 10; $p++) { if ($sub_mlid = $item["p$p"]) { $subitem = menu_link_load($sub_mlid); // Menu sets these ghetto-ass keys in _menu_tree_check_access(). $menu = $menu[(50000 + $subitem['weight']) .' '. $subitem['title'] .' '. $subitem['mlid']]['below']; } } } // Otherwise just set a title and move on. else { // Get the title from the DB since we don't have it in the $menu. $result = db_result(db_query("SELECT title FROM {menu_custom} WHERE menu_name = '%s'", $menu_name)); $title = check_plain($result); } $output['content'] = ''; $output['subject'] = $title; if ($menu) { $output['content'] .= theme('nice_menu_build', $menu); } return $output; } /** * Helper function that builds the nested lists of a nice menu. * * @param $menu * Menu array from which to build the nested lists. */ function theme_nice_menu_build($menu) { $output = ''; foreach ($menu as $menu_item) { $mlid = $menu_item['link']['mlid']; // Check to see if it is a visible menu item. if ($menu_item['link']['hidden'] == 0) { // Build class name based on menu path // e.g. to give each menu item individual style. // Strip funny symbols. $clean_path = str_replace(array('http://', 'www', '<', '>', '&', '=', '?', ':'), '', $menu_item['link']['href']); // Convert slashes to dashes. $clean_path = str_replace('/', '-', $clean_path); $path_class = 'menu-path-'. $clean_path; // If it has children build a nice little tree under it. if ((!empty($menu_item['link']['has_children'])) && (!empty($menu_item['below']))) { // Keep passing children into the function 'til we get them all. $children = theme('nice_menu_build', $menu_item['below']); // Set the class to parent only of children are displayed. $parent_class = $children ? 'menuparent ' : ''; $output .= '\n"; } else { $output .= ''."\n"; } } } return $output; } /** * General theming function to allow any menu tree to be themed * as a nice menu. * * @param $id * The nice menu ID. * @param $menu_name * The top parent menu name from which to build the full menu. * @param $mlid * The menu ID from which to build the displayed menu. * @param $direction * Optional. The direction the menu expands. Default is 'right'. * @param $menu * Optional. A custom menu array to use for theming -- * it should have the same structure as that returned * by menu_tree_all_data(). Default is the standard menu tree. * @return * An HTML string of nice menu links. */ function theme_nice_menu($id, $menu_name, $mlid, $direction = 'right', $menu = NULL) { $output = array(); if ($menu_tree = theme('nice_menu_tree', $menu_name, $mlid, $menu)) { if ($menu_tree['content']) { $output['content'] = ''."\n"; $output['subject'] = $menu_tree['subject']; } } return $output; } /** * Theme primary links as nice menus * * @param $direction * Optional. The direction the menu expands. Default is 'down'. * @param $menu * Optional. A custom menu array to use for theming -- * it should have the same structure as that returned * by menu_tree_all_data(). Default is the standard menu tree. * @return * An HTML string of nice menu primary links. */ function theme_nice_menu_primary_links($direction = 'down', $menu = NULL) { $menu_name = variable_get('menu_primary_links_source', 'primary-links'); $output = theme('nice_menu', 0, $menu_name, 0, $direction, $menu); return $output['content']; }