You are here

jump_menu.module in Better Jump Menus 8

Same filename and directory in other branches
  1. 6 jump_menu.module
  2. 7 jump_menu.module

Make use of the CTools jump menu and grabs from an existing menu. See: modules/ctools/includes/jump-menu.inc NOTE: Menu items must be checked as "expanded" for traversing to work.

File

jump_menu.module
View source
<?php

/**
 * @file
 * Make use of the CTools jump menu and grabs from an existing menu.
 * See: modules/ctools/includes/jump-menu.inc
 * NOTE: Menu items must be checked as "expanded" for traversing to work.
 */
define('JUMP_MENU_DEFAULT_CHOOSE', '-- Choose --');
define('JUMP_MENU_DEFAULT_BLOCK_SETTINGS_SHOW_CURRENT', 0);

/**
 * Output a core menu as a select jump menu.
 */
function jump_menu($menu, $parent, $btn = FALSE, $max_depth = 0, $choose = 'Select a destination', $current = FALSE) {
  ctools_include('jump-menu');

  // Load up the menu.
  $menu = menu_tree_all_data($menu);

  // Trim to the needed portion, start at parent menuID.
  foreach ($menu as $m) {

    // The mlid is i18n tranlsation friendly.
    if ($m['link']['mlid'] == $parent) {
      $menu = $m['below'];
      break;
    }
  }

  // Initialize for building.
  $depths = array(
    'current' => 1,
    'max' => $max_depth,
  );
  $targets = array();

  // Build the jump options from the menu.
  _jump_menu_create_options($targets, $menu, $depths);

  // Output...
  if (count($targets) == 0) {
    return 'Jump menu contains no items!';
  }
  else {
    $options = array();

    // Handle button option.
    if ($btn) {
      $options['hide'] = FALSE;
      $options['button'] = $btn;
    }
    else {
      $options['hide'] = TRUE;
    }

    // Place initial select option value.
    $options['choose'] = t($choose);

    // Set current location if desired.
    if ($current) {
      $current_path = base_path() . request_path();
      if (!empty($current_path)) {
        $options['default_value'] = $current_path;
      }
    }

    // Other available options...
    // 'title' => The text to display for the #title attribute.
    // 'description': The text to display for the #description attribute.
    // 'image': If set, an image button will be used instead, and the image set to this.
    // 'inline': If set to TRUE (default) the display will be forced inline.
    return drupal_render(drupal_get_form('ctools_jump_menu', $targets, $options));
  }
}

/**
 * Recursive menu to select option building.
 */
function _jump_menu_create_options(&$t, &$m, &$d) {

  // Set the option.
  foreach ($m as $item) {

    // Kill non-viewable menu items.
    if ($item['link']['hidden'] == 0) {

      // Add depth indicators to titles.
      if ($d['current'] > 1) {
        $title = ' ' . str_repeat('-', $d['current'] - 1) . ' ' . $item['link']['title'];
      }
      else {
        $title = $item['link']['title'];
      }

      // Add targets...
      // Active and depth classes (for aggressive theming).
      $classes = 'd-' . $d['current'];

      // @todo Break this off once dealing with language, since it's duplicated in local tasks.
      // @todo Add active trail, but beware: http://drupal.org/node/1854356
      $current_path = drupal_get_normal_path(request_path());
      if ($item['link']['href'] == $current_path) {
        $classes .= ' active';
      }

      // Allow for special menu item dummy items for grouping.
      if (module_exists('special_menu_items') && $item['link']['page_callback'] == 'drupal_not_found') {

        // Create a dummy option using optgroups.
        $t[] = array(
          'title' => t($title),
          '#attributes' => array(
            'class' => $classes,
          ),
        );
      }
      else {

        // Create a normal option.
        $url_options = array(
          'query' => $item['link']['localized_options']['query'],
          'fragment' => $item['link']['localized_options']['fragment'],
        );
        $t[] = array(
          'value' => url($item['link']['href'], $url_options),
          'title' => t($title),
          '#attributes' => array(
            'class' => $classes,
          ),
        );
      }
    }

    // Loop deeper if there is no max or we haven't reached it.
    if ($item['below'] && ($d['max'] == 0 || $d['current'] < $d['max'])) {

      // Drop current depth.
      $d['current']++;
      _jump_menu_create_options($t, $item['below'], $d);
    }
  }

  // Raise current depth back up.
  $d['current']--;
}

/**
 * Abstract block rendering to be more flexible about when/how this happens.
 */
function _jump_menu_render_block($delta, $options = array()) {

  // Strip off jump_menu.
  $block_name = str_replace('jump_menu-', '', $delta);

  // Cache menu list.
  static $menus;
  if (!isset($menus)) {
    $menus = menu_get_menus(TRUE);
  }

  // Options are always available.
  $options['hide'] = isset($options['hide']) ? $options['hide'] : TRUE;

  // Allow setting active item.
  $settings = variable_get('jump_menu_block_settings_show_current', array());
  $current = isset($settings[$delta]) ? $settings[$delta] : JUMP_MENU_DEFAULT_BLOCK_SETTINGS_SHOW_CURRENT;

  // If a menu block.
  if (substr($block_name, 0, 2) == 'm_') {
    foreach ($menus as $k => $v) {

      // Block delta prefix length is 12 chars, leaves 20 for menu name.
      // Compare as much fits against the menu portion of the delta.
      if (substr($k, 0, 20) == substr($block_name, 2)) {

        // Set the title.
        $data['subject'] = check_plain($menus[$k]);

        // Set default 'choose' text to menu name.
        $options['choose'] = isset($options['choose']) ? $options['choose'] : check_plain($menus[$k]);
        $data['content'] = jump_menu($k, 0, FALSE, 0, $options['choose'], $current);
      }
    }
  }
  elseif (substr($block_name, 0, 11) == 'local-tasks') {

    // Collect the local tasks.
    $links = menu_local_tasks(0);
    $links_secondary = menu_local_tasks(1);

    // Are there any real secondary tasks?
    $secondary = count($links_secondary['tabs']['output']) != 0 ? TRUE : FALSE;
    if ($links['tabs']['count'] > 0) {
      $targets = array();

      // Create select list targets.
      foreach ($links['tabs']['output'] as $l) {
        if ($l['#link']['access'] == TRUE) {

          // Set active.
          $classes = $l['#active'] ? 'active' : '';
          $targets[] = array(
            'value' => url($l['#link']['href']),
            'title' => t($l['#link']['title']),
            '#attributes' => array(
              'class' => $classes,
            ),
          );

          // Do secondary tabs fit with this item?
          if ($secondary && $links_secondary['tabs']['output'][0]['#link']['tab_parent_href'] == $l['#link']['href']) {
            foreach ($links_secondary['tabs']['output'] as $sl) {

              // Set active.
              $classes = $l['#active'] ? 'active' : '';
              $targets[] = array(
                'value' => url($sl['#link']['href']),
                'title' => '- ' . t($sl['#link']['title']),
                '#attributes' => array(
                  'class' => $classes,
                ),
              );
            }
          }
        }
      }

      // Take options and place defaults.
      $options['choose'] = isset($options['choose']) ? $options['choose'] : t(JUMP_MENU_DEFAULT_CHOOSE);

      // Process setting active item.
      if ($current) {
        $current_path = base_path() . request_path();
        if (!empty($current_path)) {
          $options['default_value'] = $current_path;
        }
      }

      // Populate block.
      $data['subject'] = t('Local Tasks');
      $data['content'] = drupal_render(drupal_get_form('ctools_jump_menu', $targets, $options));
    }
    else {
      $data = FALSE;
    }
  }
  else {
    $notice = t('Something is wrong with the Jump Menu module, please report.');
    if (variable_get('error_level') > 0) {
      drupal_set_message($notice);
    }
    else {
      $message = 'Unable to render Jump Menu block. Likely due to a bad menu delta in the database.';
      watchdog('jump_menu', $message, array(), WATCHDOG_ERROR);
      drupal_set_message($notice);
    }
    $data = FALSE;
  }
  return $data;
}

/**
 * Alter theme registration to allow overriding select theme function,
 * to allow for extra attributes within options through form API.
 * This is to add depth classes.
 */
function jump_menu_theme_registry_alter(&$theme_registry) {
  $theme_registry['select']['function'] = 'jump_menu_select';
}

/**
 * Override select theme function (points select theming to following function).
 */
function jump_menu_select($variables) {
  $element = $variables['element'];
  element_set_attributes($element, array(
    'id',
    'name',
    'size',
  ));
  _form_set_class($element, array(
    'form-select',
  ));

  // Provide alternate rendering path for jump menus.
  // To be careful, only altering jump menu selects, http://drupal.org/node/1512550
  $output = '<select' . drupal_attributes($element['#attributes']) . '>';

  // @todo This is a weak thing to test.
  if ($element['#attributes']['class'][0] == 'ctools-jump-menu-select') {
    $output .= jump_menu_form_select_options($element);
  }
  else {
    $output .= form_select_options($element);
  }
  $output .= '</select>';
  return $output;
}

/**
 * Provide alternate select options builder.
 * Taken from form_select_options() within includes/form.inc
 */
function jump_menu_form_select_options($element, $choices = NULL) {
  if (!isset($choices)) {
    $choices = $element['#options'];
  }

  // Using array_key_exists() accommodates the rare event where $element['#value'] is NULL.
  // Note that isset() fails in this situation.
  $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
  $value_is_array = $value_valid && is_array($element['#value']);
  $options = '';
  foreach ($choices as $key => $choice) {

    // Allow overloading options with an array.
    if (is_array($choice)) {
      if (isset($choice['value'])) {

        // Overloaded option array.
        $opt_value = (string) $choice['value'];
      }
      else {
        if (isset($choice['title'])) {

          // Optgroup for special menu items.
          $options .= '<optgroup label="' . $choice['title'] . '"></optgroup>';
        }
        else {

          // Normal optgroups.
          $options .= '<optgroup label="' . $key . '">';
          $options .= form_select_options($element, $choice);
          $options .= '</optgroup>';
        }
      }
    }
    else {

      // Simple options.
      $opt_value = $key;
      $choice = array(
        'title' => $choice,
      );
    }

    // Create the HTML output.
    if (isset($opt_value)) {
      if (!isset($choice['#attributes'])) {
        $choice['#attributes'] = array();
      }

      // Note this makes the first item no longer selected, but that doesn't matter.
      if ($value_valid && (!$value_is_array && (string) $element['#value'] === $opt_value || $value_is_array && in_array($opt_value, $element['#value']))) {
        $selected = ' selected="selected"';
      }
      else {
        $selected = '';
      }
      $options .= '<option value="' . check_plain($opt_value) . '"' . $selected . drupal_attributes($choice['#attributes']) . '>' . check_plain($choice['title']) . '</option>';
    }
    unset($opt_value);
  }
  return $options;
}

/**
 * Implements hook_form_FORM_ID_alter().
 * Add custom options to block editing forms.
 */
function jump_menu_form_block_admin_configure_alter(&$form, &$form_state, $form_id) {
  if ($form['module']['#value'] == 'jump_menu') {
    $delta = $form['delta']['#value'];

    // Determine the default value for this block.
    $settings = variable_get('jump_menu_block_settings_show_current', array());
    $default = isset($settings[$delta]) ? $settings[$delta] : JUMP_MENU_DEFAULT_BLOCK_SETTINGS_SHOW_CURRENT;

    // Add jump menu fieldset with options.
    $form['jump_menu_options'] = array(
      '#type' => 'details',
      '#title' => t('Jump Menu'),
      '#weight' => 1,
      'jump_menu_show_current' => array(
        '#type' => 'checkbox',
        '#title' => t('Display Curent Location'),
        '#description' => t('Set the jump menu to the currently active page location.'),
        '#default_value' => $default,
      ),
    );

    // Attach a submit handler to save our preferences.
    $form['#submit'][] = 'jump_menu_block_settings_submit';
  }
}

/**
 * Submit handler to save block-specific jump menu settings.
 */
function jump_menu_block_settings_submit($form, &$form_state) {
  $settings = variable_get('jump_menu_block_settings_show_current', array());
  $delta = $form_state['values']['delta'];

  // Clear out our block settings array if no restrictions are applied.
  if ($form_state['values']['jump_menu_show_current'] == JUMP_MENU_DEFAULT_BLOCK_SETTINGS_SHOW_CURRENT) {

    // $setting = variable_get('jump_menu_block_settings_show_current');
    if (isset($settings[$delta])) {
      unset($settings[$delta]);
    }
  }
  else {
    $settings[$delta] = $form_state['values']['jump_menu_show_current'];
  }

  // Save the settings.
  variable_set('jump_menu_block_settings_show_current', $settings);
}

Functions

Namesort descending Description
jump_menu Output a core menu as a select jump menu.
jump_menu_block_settings_submit Submit handler to save block-specific jump menu settings.
jump_menu_form_block_admin_configure_alter Implements hook_form_FORM_ID_alter(). Add custom options to block editing forms.
jump_menu_form_select_options Provide alternate select options builder. Taken from form_select_options() within includes/form.inc
jump_menu_select Override select theme function (points select theming to following function).
jump_menu_theme_registry_alter Alter theme registration to allow overriding select theme function, to allow for extra attributes within options through form API. This is to add depth classes.
_jump_menu_create_options Recursive menu to select option building.
_jump_menu_render_block Abstract block rendering to be more flexible about when/how this happens.

Constants

Namesort descending Description
JUMP_MENU_DEFAULT_BLOCK_SETTINGS_SHOW_CURRENT
JUMP_MENU_DEFAULT_CHOOSE @file Make use of the CTools jump menu and grabs from an existing menu. See: modules/ctools/includes/jump-menu.inc NOTE: Menu items must be checked as "expanded" for traversing to work.