You are here

hs_menu.module in Hierarchical Select 7.3

Same filename and directory in other branches
  1. 5.3 modules/hs_menu.module
  2. 6.3 modules/hs_menu.module

Implementation of the Hierarchical Select API for the Menu module.

File

modules/hs_menu.module
View source
<?php

/**
 * @file
 * Implementation of the Hierarchical Select API for the Menu module.
 */

//----------------------------------------------------------------------------

// Drupal core hooks.

/**
 * Implements of hook_menu().
 */
function hs_menu_menu() {
  $items['admin/config/content/hierarchical_select/menu'] = array(
    'title' => 'Menu',
    'description' => 'Hierarchical Select configuration for Menu',
    'access arguments' => array(
      'administer site configuration',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'hs_menu_admin_settings',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_form_FORMID_alter().
 *
 * Alter the node form's menu form.
 */
function hs_menu_form_node_form_alter(&$form, &$form_state) {
  $active_types = array_filter(variable_get('hs_menu_content_types', array()));
  $active = empty($active_types) || in_array($form_state['node']->type, $active_types);
  if ($active && isset($form['menu']['link']['parent']) && isset($form['menu']['#access']) && $form['menu']['#access']) {
    unset($form['menu']['link']['parent']['#options']);
    $form['menu']['link']['parent']['#type'] = 'hierarchical_select';

    // Get menu name, needed to exclude current node.
    $menu_name = explode(':', $form['menu']['link']['parent']['#default_value']);
    _hs_menu_apply_config($form['menu']['link']['parent'], array(
      0 => $menu_name[0],
      1 => $form['menu']['link']['mlid']['#value'],
      'type' => $form['type']['#value'],
    ));

    // Set custom submit callback.
    array_unshift($form['#submit'], 'hs_menu_node_form_submit');

    // Change the loaded default value into an array so we can populate the
    // Hierarchical Select element.
    $form['menu']['link']['parent']['#default_value'] = array(
      $form['menu']['link']['parent']['#default_value'],
    );
  }
}

/**
 * Implements hook_form_BASE_FORMID_alter().
 *
 * Alter the widget type form; dynamically add the Hierarchical Select
 * Configuration form when it is needed.
 */
function hs_menu_form_menu_edit_item_alter(&$form, &$form_state) {
  unset($form['parent']['#options']);
  $original_item = $form['original_item']['#value'];
  $form['parent']['#type'] = 'hierarchical_select';
  _hs_menu_apply_config($form['parent'], array(
    'exclude' => array(
      $original_item['menu_name'],
      $original_item['mlid'],
    ),
  ));

  // Set custom submit callback.
  array_unshift($form['#submit'], 'hs_menu_menu_edit_item_form_submit');
}

//----------------------------------------------------------------------------

// Form API callbacks.

/**
 * Submit callback; menu edit item form.
 */
function hs_menu_menu_edit_item_form_submit(&$form, &$form_state) {

  // Don't return an array, but a single item.
  $form_state['values']['parent'] = $form_state['values']['parent'][0];
}

/**
 * Submit callback; node edit form.
 */
function hs_menu_node_form_submit(&$form, &$form_state) {

  // Don't return an array, but a single item.
  $form_state['values']['menu']['parent'] = $form_state['values']['menu']['parent'][0];
}

//----------------------------------------------------------------------------

// Menu callbacks.

/**
 * Form definition; admin settings.
 */
function hs_menu_admin_settings() {
  $form['hs_menu_resizable'] = array(
    '#type' => 'radios',
    '#title' => t('Resizable'),
    '#description' => t("When enabled, a handle appears below the Hierarchical Select to allow\n      the user to dynamically resize it. Double clicking will toggle between\n      the smallest and a sane 'big size'."),
    '#options' => array(
      0 => t('Disabled'),
      1 => t('Enabled'),
    ),
    '#default_value' => variable_get('hs_menu_resizable', 1),
  );
  $form['hs_menu_content_types'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Content types'),
    '#description' => t("Select the content types to use Hierarchical Select Menu on. If no content types are selected, then it will apply to all content types."),
    '#options' => node_type_get_names(),
    '#default_value' => variable_get('hs_menu_content_types', array()),
  );
  return system_settings_form($form);
}

//----------------------------------------------------------------------------

// Hierarchical Select hooks.

/**
 * Implements hook_hierarchical_select_params().
 */
function hs_menu_hierarchical_select_params() {
  $params = array(
    'exclude',
  );
  return $params;
}

/**
 * Implements hook_hierarchical_select_root_level().
 */
function hs_menu_hierarchical_select_root_level($params) {
  $menus = array();
  $result = db_query("SELECT menu_name, title FROM {menu_custom} ORDER BY title");

  // If the type is set, respect the core menu options setting.
  if (isset($params['type'])) {
    $type_menus = variable_get('menu_options_' . $params['type'], array(
      'main-menu' => 'main-menu',
    ));
    while ($menu = $result
      ->fetchObject()) {
      if (in_array($menu->menu_name, $type_menus, TRUE)) {
        $menus[$menu->menu_name . ':0'] = $menu->title;
      }
    }
  }
  else {
    while ($menu = $result
      ->fetchObject()) {
      $menus[$menu->menu_name . ':0'] = $menu->title;
    }
  }
  return $menus;
}

/**
 * Implements hook_hierarchical_select_children().
 */
function hs_menu_hierarchical_select_children($parent, $params) {
  $children = array();
  list($menu_name, $plid) = explode(':', $parent);
  $tree = menu_tree_all_data($menu_name, NULL);
  return _hs_menu_children($tree, $menu_name, $plid, $params['exclude']);
}

/**
 * Implements hook_hierarchical_select_lineage().
 */
function hs_menu_hierarchical_select_lineage($item, $params) {
  $lineage = array(
    $item,
  );
  list($menu_name, $mlid) = explode(':', $item);

  // If the initial mlid is zero, then this is the root level, so we don't
  // have to get the lineage.
  if ($mlid > 0) {

    // Prepend each parent mlid (i.e. plid) to the lineage.
    do {
      $plid = db_query("SELECT plid FROM {menu_links} WHERE mlid = :mlid", array(
        ':mlid' => $mlid,
      ))
        ->fetchField();
      array_unshift($lineage, "{$menu_name}:{$plid}");
      if ($mlid == $plid) {

        // Somehow we have an infinite loop situation. Bail out of the loop.
        break;
      }
      $mlid = $plid;
    } while ($plid > 0);
  }
  return $lineage;
}

/**
 * Implements hook_hierarchical_select_valid_item().
 */
function hs_menu_hierarchical_select_valid_item($item, $params) {
  $parts = explode(':', $item);
  $valid = TRUE;

  // Validate menu name.
  $valid = array_key_exists($parts[0], menu_get_menus());

  // Validate hierarchy of mlids.
  for ($i = 1; $valid && $i < count($parts); $i++) {
    $valid = $valid && is_numeric($parts[$i]);
  }

  // Ensure that this isn't the excluded menu link.
  $valid = $valid && $item != $params['exclude'][0] . $params['exclude'][1];
  return $valid;
}

/**
 * Implements hook_hierarchical_select_item_get_label().
 */
function hs_menu_hierarchical_select_item_get_label($item, $params) {
  static $labels = array();
  $parts = explode(':', $item);
  if (count($parts) == 1) {

    // Get the menu name.
    $menu_name = $parts[0];
    $labels[$item] = db_query("SELECT title FROM {menu_custom} WHERE menu_name = :menu_name", array(
      ':menu_name' => $menu_name,
    ))
      ->fetchField();
  }
  else {

    // Get the menu link title.
    $mlid = end($parts);
    $menu_link = menu_link_load($mlid);
    $labels[$item] = $menu_link['title'];
  }
  return $labels[$item];
}

/**
 * Implements hook_hierarchical_select_implementation_info().
 */
function hs_menu_hierarchical_select_implementation_info() {
  return array(
    'hierarchy type' => t('Menu'),
    'entity type' => t('N/A'),
  );
}

//----------------------------------------------------------------------------

// Private functions.

/**
 * Recursive helper function for hs_menu_hierarchical_select_children().
 */
function _hs_menu_children($tree, $menu_name, $plid = 0, $exclude = FALSE) {
  $children = array();
  foreach ($tree as $data) {
    if ($data['link']['plid'] == $plid && $data['link']['hidden'] >= 0) {
      if ($exclude && $data['link']['menu_name'] === $exclude[0] && $data['link']['mlid'] == $exclude[1]) {
        continue;
      }
      $title = truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
      if ($data['link']['hidden']) {
        $title .= ' (' . t('disabled') . ')';
      }
      $children[$menu_name . ':' . $data['link']['mlid']] = $title;
      if ($data['below']) {
        $children += _hs_menu_children($data['below'], $menu_name, $plid, $exclude);
      }
    }
    elseif ($data['below']) {
      $children += _hs_menu_children($data['below'], $menu_name, $plid, $exclude);
    }
  }
  return $children;
}

/**
 * Helper function to apply the HS config to a form item.
 */
function _hs_menu_apply_config(&$form, $params) {

  // The following is to ensure via javascript self is not listed.
  if (!empty($params['exclude'])) {
    $params['exclude'] = $params['exclude'][0] . ':' . $params['exclude'][1];
    drupal_add_js('jQuery(document).ready(function () {
        jQuery("[value*=\\"' . $params['exclude'] . '\\"]").hide();
    });', 'inline');
  }
  $form['#config'] = array(
    'module' => 'hs_menu',
    'params' => array(
      'exclude' => isset($params['exclude']) ? $params['exclude'] : NULL,
      'type' => isset($params['type']) ? $params['type'] : NULL,
    ),
    'save_lineage' => 0,
    'enforce_deepest' => 0,
    'resizable' => variable_get('hs_menu_resizable', 1),
    'level_labels' => array(
      'status' => 0,
    ),
    'dropbox' => array(
      'status' => 0,
    ),
    'editability' => array(
      'status' => 0,
    ),
    'entity_count' => array(
      'enabled' => 0,
      'require_entity' => 0,
      'settings' => array(
        'count_children' => 0,
        'entity_types' => array(),
      ),
    ),
    'render_flat_select' => 0,
  );
}

Functions

Namesort descending Description
hs_menu_admin_settings Form definition; admin settings.
hs_menu_form_menu_edit_item_alter Implements hook_form_BASE_FORMID_alter().
hs_menu_form_node_form_alter Implements hook_form_FORMID_alter().
hs_menu_hierarchical_select_children Implements hook_hierarchical_select_children().
hs_menu_hierarchical_select_implementation_info Implements hook_hierarchical_select_implementation_info().
hs_menu_hierarchical_select_item_get_label Implements hook_hierarchical_select_item_get_label().
hs_menu_hierarchical_select_lineage Implements hook_hierarchical_select_lineage().
hs_menu_hierarchical_select_params Implements hook_hierarchical_select_params().
hs_menu_hierarchical_select_root_level Implements hook_hierarchical_select_root_level().
hs_menu_hierarchical_select_valid_item Implements hook_hierarchical_select_valid_item().
hs_menu_menu Implements of hook_menu().
hs_menu_menu_edit_item_form_submit Submit callback; menu edit item form.
hs_menu_node_form_submit Submit callback; node edit form.
_hs_menu_apply_config Helper function to apply the HS config to a form item.
_hs_menu_children Recursive helper function for hs_menu_hierarchical_select_children().