menu_select.module in Menu Select 7
Expands the node menu select field functionality by adding filters and an expandable hierarchy.
File
menu_select.moduleView source
<?php
/**
* @file
* Expands the node menu select field functionality by adding filters
* and an expandable hierarchy.
*/
/**
* Implements hook_menu().
*/
function menu_select_menu() {
$items['admin/config/content/menu-select'] = array(
'title' => 'Menu Select',
'description' => 'Administer and configure settings for Menu Select such as enabling Search or modifying the auto-expand cut off point.',
'access arguments' => array(
'administer site configuration',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'menu_select_config_form',
),
);
$items['menu-select/autocomplete/%'] = array(
'title' => 'Autocomplete for menu select',
'page callback' => '_menu_select_autocomplete',
'page arguments' => array(
2,
),
'access callback' => '_menu_select_access_autocomplete',
'access arguments' => array(
2,
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_form_alter().
*/
function menu_select_form_alter(&$form, &$form_state) {
// Target the Menu Position form and parent
if (module_exists('menu_position') && ($form['#id'] == 'menu-position-add-rule-form' || $form['#id'] == 'menu-position-edit-rule-form')) {
$menu_select_form =& $form;
$menu_select_parent =& $form['plid'];
}
// Target the Menu Item form and parent
if ($form['#id'] == 'menu-edit-item' && isset($form['parent'])) {
$menu_select_form =& $form;
$menu_select_parent =& $form['parent'];
}
// Target any form that contains menu, menu link, and menu link parent
if (isset($form['menu']) && isset($form['menu']['link']) && isset($form['menu']['link']['parent'])) {
$menu_select_form =& $form['menu']['link'];
$menu_select_parent =& $form['menu']['link']['parent'];
}
if (isset($menu_select_form) && isset($menu_select_parent)) {
$items =& $menu_select_parent;
$menu = array();
// The array of nested menu items
$map = array();
// An mkey to title lookup
$depth = 0;
// Array of pointers for accessing previous depths of the 2d array.
// $pointers[0] will always point to $menu and the pointer at $pointer[1]
// will always be a root item
$pointers = array();
$pointers[] =& $menu;
// Iterate through the flat array and try to parse out a multi-dimensional structure
foreach ($items['#options'] as $mkey => $label) {
// Process root item labels
if (strpos($label, '<') === 0) {
$label = substr($label, 1, strlen($label) - 2);
}
// Ascertain depth by the number of dashes before the label.
// None is root/1, two is first level/2, etc
$parts = explode(' ', $label);
if (count($parts) > 1 && $parts[0][1] === '-') {
$new_depth = substr_count($parts[0], '-') / 2 + 1;
}
else {
$new_depth = 1;
}
// Insert menu items into the $menu array using the appropriate pointer
if ($new_depth > $depth) {
// Inserting a new child item
$pointers[$new_depth - 1][$mkey] = array();
$pointers[] =& $pointers[$depth][$mkey];
}
elseif ($new_depth < $depth) {
// Going up ($depth - $new_depth) level(s) and inserting a sibling
$pointers = array_slice($pointers, 0, $new_depth);
$pointers[$new_depth - 1][$mkey] = array();
$pointers[] =& $pointers[$new_depth - 1][$mkey];
}
else {
// Inserting a sibling
$pointers[$new_depth - 1][$mkey] = array();
// Change the pointer at this level so if the next item is a child it's inserted under this item
$pointers[$depth] =& $pointers[$new_depth - 1][$mkey];
}
$map[$mkey] = trim(substr($label, ($new_depth - 1) * 2));
$depth = $new_depth;
}
//_menu_select_debug_tree($menu, $map);
$cut_off = variable_get('menu_select_cut_off', 30);
$menu_options = array();
foreach ($menu as $mkey => $children) {
$menu_options[$mkey] = $map[$mkey];
}
// Set the default menu
$default_menu = key($menu_options);
// Change the default menu if the menu item belongs to a menu that isn't the default
if (isset($items['#default_value']) && !empty($items['#default_value'])) {
$default_value = explode(':', $items['#default_value']);
$default_value = $default_value[0] . ':0';
if (array_key_exists($default_value, $menu_options)) {
$default_menu = $default_value;
}
}
$menu_select_form['#attached']['js'][] = drupal_get_path('module', 'menu_select') . '/js/menu_select.js';
$menu_select_form['#attached']['js'][] = array(
'data' => array(
'menu_select' => compact('menu', 'map', 'cut_off'),
),
'type' => 'setting',
);
$menu_select_form['#attached']['css'][] = drupal_get_path('module', 'menu_select') . '/css/menu_select_icons.css';
$menu_select_form['#attached']['css'][] = drupal_get_path('module', 'menu_select') . '/css/menu_select.css';
// For debugging purposes
/*
$menu_select_form['#collapsed'] = false;
$menu_select_form['#enabled']['#default_value'] = 1;
$menu_select_form['#enabled']['#value'] = 1;
$menu_select_form['#states']['invisible']['input[name="menu[enabled]"']['checked'] = true;
*/
// Give everything a weight so we can insert our parent select widget in where Parent item should be
$weight = 0;
foreach ($menu_select_form as &$item) {
if (is_array($item) && isset($item['#type'])) {
$item['#weight'] = ++$weight;
}
}
// Push actions to the bottom again
$menu_select_form['actions']['#weight'] = $weight + 51;
// Add a custom class to the existing menu parent select
$menu_select_parent['#attributes']['class'][] = 'menu-select-menu-parent';
// Build new form element
$menu_select_form['menu_select'] = array(
'#type' => 'fieldset',
'#title' => t('Parent item'),
'#collapsible' => FALSE,
'#collapsed' => FALSE,
'#weight' => $menu_select_parent['#weight'],
'#attributes' => array(
'class' => array(
'element-invisible',
'menu-select-menu-select',
),
),
'preview' => array(
'#type' => 'item',
'#title' => t('Menu link position'),
'#markup' => '<span class="menu-select-parent-menu-title" title="' . $default_menu . '">' . $map[$default_menu] . '</span> / <span class="menu-select-position-preview"></span> <span class="current-menu-item">' . t('Current menu item') . '</span>',
'#description' => t('Preview of the menu item\'s hierarchy compared to its parent(s).'),
),
'select_menu_link_position' => array(
'#type' => 'fieldset',
'#title' => t('Select menu link position'),
'#collapsible' => FALSE,
'#collapsed' => FALSE,
'#attributes' => array(
'class' => array(
'menu-select-select-menu-link-position',
),
),
'parent_menu' => array(
'#type' => 'select',
'#title' => t('Menu'),
'#options' => $menu_options,
'#description' => t('Select which menu you would like this item to belong to.'),
'#default_value' => $default_menu,
'#attributes' => array(
'class' => array(
'menu-select-parent-menu',
),
),
),
'parent_menu_item' => array(
'#type' => 'item',
'#title' => t('Menu hierarchy'),
'#markup' => '<div class="menu-select-menu-hierarchy"></div>',
'#description' => t('Select the menu item\'s parent from the hierarchy above.'),
),
),
);
if (variable_get('menu_select_search_enabled', 0) === 1) {
if (isset($form['type']['#value'])) {
$type = $form['type']['#value'];
}
else {
$type = '';
}
$menu_select_form['menu_select']['select_menu_link_position']['parent_menu_item_search'] = array(
'#type' => 'textfield',
'#title' => t('Search'),
'#autocomplete_path' => 'menu-select/autocomplete/' . $type,
'#description' => 'Alternatively, use this autocomplete search to find the menu item\'s parent and select it.',
'#attributes' => array(
'class' => array(
'menu-select-parent-menu-item-search',
),
),
);
}
}
}
/**
* Config form for 'admin/config/content/menu-select'.
*
* @see menu_select_menu().
*/
function menu_select_config_form($form, $form_state) {
$form['menu_select_cut_off'] = array(
'#title' => t('Auto-expansion cut off'),
'#description' => t('The minimum number of menu items in a tree required to have the tree collapsed by default. Set to -1 to disable.'),
'#type' => 'textfield',
'#element_validate' => array(
'element_validate_number',
),
'#default_value' => variable_get('menu_select_cut_off', 30),
'#size' => 4,
);
$form['menu_select_search_enabled'] = array(
'#title' => t('Enable autocomplete search'),
'#description' => t('By default the search field is disabled because a core patch is required. If you wish to use the search field visit <a href="http://drupal.org/node/365241#comment-6314864">http://drupal.org/node/365241#comment-6314864</a> and apply the core patch. Once done, check this box and submit this form.'),
'#type' => 'checkbox',
'#default_value' => variable_get('menu_select_search_enabled', 0),
);
return system_settings_form($form);
}
/**
* Permission to access Menu Select autocomplete config.
*
* @see menu_select_menu().
*/
function _menu_select_access_autocomplete($type) {
return user_access('create ' . $type . ' content');
}
/**
* Menu Select autocomplete config page.
*
* @see menu_select_menu().
*/
function _menu_select_autocomplete($type = '', $partial = '') {
$matches = array();
$partial = strtolower($partial);
$options = menu_parent_options(menu_get_menus(), $type, $type);
foreach ($options as $mkey => $option) {
if (strpos(strtolower($option), $partial) !== FALSE) {
$option = preg_replace('/^\\-+\\s/', '', $option);
$option = str_replace(array(
'<',
'>',
), '', $option);
$matches[$mkey] = $option . ' (' . $mkey . ')';
}
}
drupal_json_output($matches);
}
/**
* Helper for a recursive function.
*/
function _menu_select_hierarchy($menu_level, &$map) {
$output = '';
foreach ($menu_level as $key => $children) {
$output .= '<li>' . $map[$key] . '</li>';
}
}
/**
* Debug function for testing the menu tree.
*/
function _menu_select_debug_tree($level, &$map, $depth = 0) {
foreach ($level as $key => $children) {
if (!empty($children)) {
_menu_select_debug_tree($children, $map, $depth + 1);
}
}
}
Functions
Name | Description |
---|---|
menu_select_config_form | Config form for 'admin/config/content/menu-select'. |
menu_select_form_alter | Implements hook_form_alter(). |
menu_select_menu | Implements hook_menu(). |
_menu_select_access_autocomplete | Permission to access Menu Select autocomplete config. |
_menu_select_autocomplete | Menu Select autocomplete config page. |
_menu_select_debug_tree | Debug function for testing the menu tree. |
_menu_select_hierarchy | Helper for a recursive function. |