jump_menu.module in Better Jump Menus 7
Same filename and directory in other branches
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.moduleView 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.
*/
// Constants.
define('JUMP_MENU_DEFAULT_CHOOSE', '-- Choose --');
define('JUMP_MENU_BLOCK_DEFAULTS_SHOW_CURRENT', 0);
define('JUMP_MENU_BLOCK_DEFAULTS_SHOW_BUTTON', 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);
// Localize the tree.
if (module_exists('i18n_menu')) {
$menu = i18n_menu_localize_tree($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_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 option 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';
}
$attributes = array(
'class' => $classes,
);
// Add attribute for opening mode.
if (isset($item['link']['options']['attributes']['target'])) {
$attributes['target'] = $item['link']['options']['attributes']['target'];
}
// 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' => $attributes,
);
}
else {
// Create a normal option.
$url_options = array();
if (!empty($item['link']['localized_options']['query'])) {
$url_options['query'] = $item['link']['localized_options']['query'];
}
if (!empty($item['link']['localized_options']['fragment'])) {
$url_options['fragment'] = $item['link']['localized_options']['fragment'];
}
$t[] = array(
'value' => url($item['link']['href'], $url_options),
'title' => t($title),
'#attributes' => $attributes,
);
}
}
// 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']--;
}
/**
* Register jump blocks for all menus.
*/
function jump_menu_block_info() {
// Add all menus as blocks.
$menus = menu_get_menus(TRUE);
$blocks = array();
foreach ($menus as $name => $title) {
// Prefix length is 12 chars, leaves 20 for menu name according to 32 char db limit.
$delta = 'jump_menu-m_' . substr($name, 0, 20);
$blocks[$delta]['info'] = t('Jump Menu') . ' ' . t('menu') . ' - ' . check_plain($title);
// Menu blocks can't be cached because each menu item can have a custom
// access callback. menu.inc manages its own caching.
$blocks[$delta]['cache'] = DRUPAL_NO_CACHE;
}
// Add local menu tasks block.
$blocks['jump_menu-local-tasks']['info'] = t('Jump Menu') . ': ' . t('Local tasks');
// Caching would need to be PER ROLE and PER PAGE.
$blocks['jump_menu-local-tasks']['cache'] = DRUPAL_NO_CACHE;
return $blocks;
}
/**
* Display jump menu block.
*/
function jump_menu_block_view($delta = '') {
// Default rendering.
// The block_view_alter function re-renders if block settings are in place.
return _jump_menu_render_block($delta);
}
/**
* Make use of block settings on display.
*/
function jump_menu_block_view_alter(&$data, $block) {
// Only bother if the title is set.
if ($block->module == 'jump_menu' && $block->title) {
$options = array();
// Pass in the block settings.
if ($block->title != '<none>') {
$options['choose'] = $block->title != '<none>' ? $block->title : t(JUMP_MENU_DEFAULT_CHOOSE);
}
// Replace content with user set title as choice text.
// Would be nice to avoid rendering the menu twice in the stack.
$data_built = _jump_menu_render_block($block->delta, $options);
if (isset($data) && $data_built) {
$data['content'] = $data_built['content'];
}
}
}
/**
* For killing off the title.
*/
function jump_menu_preprocess_block(&$vars, $hook) {
// Kill off the title if set. It's being used as the choose value.
if ($vars['block']->module == 'jump_menu' && $vars['block']->title) {
$vars['block']->subject = '';
}
}
/**
* 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 = _jump_menu_get_settings($delta);
// Allow showing a button via block UI.
if ($settings['show_button']) {
$options['hide'] = FALSE;
$options['button'] = 'Go';
}
else {
$options['hide'] = isset($options['hide']) ? $options['hide'] : TRUE;
$options['button'] = FALSE;
}
// 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, $options['button'], 0, $options['choose'], $settings['show_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) {
// Add plugin.
ctools_include('jump-menu');
$targets = array();
// Create select list targets.
foreach ($links['tabs']['output'] as $l) {
if ($l['#link']['access'] == TRUE) {
// Set active.
$classes = isset($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 ($settings['show_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_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;
}
/**
* Utility.
*/
function _trim_name(&$value) {
$value = $value + 1;
}
/**
* 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'];
$settings = variable_get('jump_menu_block_settings', array());
// Add jump menu fieldset with options.
$form['jump_menu_options'] = array(
'#type' => 'fieldset',
'#title' => t('Jump Menu'),
'#weight' => 1,
// Show current page selected.
'jump_menu_show_current' => array(
'#type' => 'checkbox',
'#title' => t('Display Curent Location'),
'#description' => t('Set the menu item to the currently active page location.'),
'#default_value' => isset($settings[$delta]['show_current']) ? $settings[$delta]['show_current'] : JUMP_MENU_BLOCK_DEFAULTS_SHOW_CURRENT,
),
// Show Go button.
'jump_menu_show_button' => array(
'#type' => 'checkbox',
'#title' => t('Display Go Button'),
'#description' => t('Show button on jump menu.'),
'#default_value' => isset($settings[$delta]['show_button']) ? $settings[$delta]['show_button'] : JUMP_MENU_BLOCK_DEFAULTS_SHOW_CURRENT,
),
);
// Attach a submit handler to save our preferences.
$form['#submit'][] = 'jump_menu_block_settings_submit';
}
}
/**
* Submit handler to save block-specific jump menu settings.
* Keeps the variable clean and tidy!
*/
function jump_menu_block_settings_submit($form, &$form_state) {
$delta = $form_state['values']['delta'];
$settings = variable_get('jump_menu_block_settings', array());
$update_needed = FALSE;
$system_defaults = array(
'show_current' => JUMP_MENU_BLOCK_DEFAULTS_SHOW_CURRENT,
'show_button' => JUMP_MENU_BLOCK_DEFAULTS_SHOW_BUTTON,
);
// Check all settings to clear out array if just default.
foreach ($system_defaults as $config => $val) {
// NOTE: Separated routes due to PHP notices with empty comparisons.
// Also, every save shouldn't trigger a variable_set().
// Config IS empty (new save).
if (!isset($settings[$delta][$config])) {
// Submission IS NOT default.
if ($form_state['values']['jump_menu_' . $config] != $val) {
$settings[$delta][$config] = $form_state['values']['jump_menu_' . $config];
$update_needed = TRUE;
}
else {
// Submission IS default, do nothing.
}
}
else {
// Submission DOES NOT match what was set previously.
if ($form_state['values']['jump_menu_' . $config] != $settings[$delta][$config]) {
$update_needed = TRUE;
// Submission matches system default (set back to default).
if ($form_state['values']['jump_menu_' . $config] == $val) {
// Delete it to remove setting.
unset($settings[$delta][$config]);
}
else {
// Collect the new configuration.
$settings[$delta][$config] = $form_state['values']['jump_menu_' . $config];
}
}
}
// No there is meaningful data for this block, but it's stored.
if (isset($settings[$delta]) && count($settings[$delta]) == 0) {
unset($settings[$delta]);
$update_needed = TRUE;
}
}
// Update the block settings variable if necessary.
if ($update_needed) {
variable_set('jump_menu_block_settings', $settings);
}
}
/**
* Utility: Grab settings or defaults, for display or settings form.
*/
function _jump_menu_get_settings($delta) {
$settings = variable_get('jump_menu_block_settings', array());
return array(
'show_current' => isset($settings[$delta]['show_current']) ? $settings[$delta]['show_current'] : JUMP_MENU_BLOCK_DEFAULTS_SHOW_CURRENT,
'show_button' => isset($settings[$delta]['show_button']) ? $settings[$delta]['show_button'] : JUMP_MENU_BLOCK_DEFAULTS_SHOW_BUTTON,
);
}
Functions
Name | Description |
---|---|
jump_menu | Output a core menu as a select jump menu. |
jump_menu_block_info | Register jump blocks for all menus. |
jump_menu_block_settings_submit | Submit handler to save block-specific jump menu settings. Keeps the variable clean and tidy! |
jump_menu_block_view | Display jump menu block. |
jump_menu_block_view_alter | Make use of block settings on display. |
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_preprocess_block | For killing off the title. |
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_get_settings | Utility: Grab settings or defaults, for display or settings form. |
_jump_menu_render_block | Abstract block rendering to be more flexible about when/how this happens. |
_trim_name | Utility. |