Skip to content
hansel.module 27.4 KiB
Newer Older
<?php

/**
 * @file
 * Hansel module
 *
 * This module provides a way to customize the breadcrumbs for your site.
 */

/**
Tim Leytens's avatar
Tim Leytens committed
 * Implements hook_init().
 */
function hansel_init() {
  if (arg(0) != 'admin' && variable_get('hansel_set_menu_name', TRUE)) {
    $menu_item = menu_get_item();
    $mlid = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :link_path AND menu_name <> 'admin_menu'", array(':link_path' => $menu_item['href']))->fetchField();
    if ($mlid) {
      $menu_name = db_query("SELECT menu_name FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField();
      if ($menu_name !== FALSE && $menu_name != 'navigation') {
        menu_set_active_menu_names(array($menu_name));
      }
    }
  }
  
  if (variable_get('hansel_set_menu_item', FALSE)) {
    // Set the active menu item.
    _hansel_activate_menu();
  }
Tim Leytens's avatar
Tim Leytens committed
}

/**
 * Implements hook_theme_registry_alter().
Tim Leytens's avatar
Tim Leytens committed
function hansel_theme_registry_alter(&$theme_registry) {
  $theme_registry['breadcrumb_original'] = $theme_registry['breadcrumb'];
  $theme_registry['breadcrumb']['function'] = 'hansel_get_themed_breadcrumbs';
}
Tim Leytens's avatar
Tim Leytens committed
 * Implements hook_permission().
Tim Leytens's avatar
Tim Leytens committed
function hansel_permission() {
  return array(
Tim Leytens's avatar
Tim Leytens committed
    'administer hansel' => array(
      'title' => t('Administer hansel')
    ),
    'use PHP for hansel configuration' => array(
      'title' => t('Use PHP for hansel configuration')
    ),
    'test hansel' => array(
      'title' => t('Test hansel')
    )
Tim Leytens's avatar
Tim Leytens committed
 * Implements hook_hansel_switch_types().
 *
 * @see _hansel_get_switch_types()
Tim Leytens's avatar
Tim Leytens committed
function hansel_hansel_switch_types() {
  return array(
    'url argument' => array(
      'compare' => 'hansel_switch_url_argument_compare',
      'info' => 'hansel_switch_url_argument_info',
      'config form' => 'hansel_ui_switch_url_argument_config_form',
      'file' => 'hansel.switches.inc',
    ),
    'php' => array(
      'compare' => 'hansel_switch_php_argument_compare',
      'config form' => 'hansel_ui_switch_php_argument_config_form',
      'file' => 'hansel.switches.inc',
    ),
    'node id' => array(
      'compare' => 'hansel_switch_node_id_compare',
      'file' => 'hansel.switches.inc',
    ),
    'node type' => array(
      'compare' => 'hansel_switch_node_type_compare',
      'file' => 'hansel.switches.inc',
    ),
    'node age' => array(
      'compare' => 'hansel_switch_node_age_compare',
      'info' => 'hansel_switch_node_age_info',
      'config form' => 'hansel_ui_switch_node_age_config_form',
      'file' => 'hansel.switches.inc',
    ),
    'path alias' => array(
      'compare' => 'hansel_switch_path_alias_compare',
      'info' => 'hansel_switch_path_alias_info',
      'config form' => 'hansel_ui_switch_path_alias_config_form',
      'file' => 'hansel.switches.inc',
    'request uri' => array(
      'compare' => 'hansel_switch_request_uri_compare',
      'info' => 'hansel_switch_request_uri_info',
      'config form' => 'hansel_ui_switch_request_uri_config_form',
      'file' => 'hansel.switches.inc',
    ),
Tim Leytens's avatar
Tim Leytens committed
}
Tim Leytens's avatar
Tim Leytens committed
/**
 * Implements hook_hansel_action_types().
 *
 * @see _hansel_get_action_types()
 */
function hansel_hansel_action_types() {
  return array(
    'add single link' => array(
      'get crumbs' => 'hansel_action_add_single_link_get_crumbs',
      'info' => 'hansel_action_add_single_link_info',
      'config form' => 'hansel_ui_action_add_single_link_config_form',
      'file' => 'hansel.actions.inc',
      'show token list' => TRUE,
    ),
    'add link to node' => array(
      'get crumbs' => 'hansel_action_add_link_to_node_get_crumbs',
      'file' => 'hansel.actions.inc',
    ),
    'add link to nodetype' => array(
      'get crumbs' => 'hansel_action_add_link_to_nodetype_get_crumbs',
      'config form' => 'hansel_ui_action_add_link_to_nodetype_config_form',
      'file' => 'hansel.actions.inc',
    ),
    'add link to user' => array(
      'get crumbs' => 'hansel_action_add_link_to_user_get_crumbs',
      'config form' => 'hansel_ui_action_add_link_to_user_config_form',
      'file' => 'hansel.actions.inc',
    ),
    'add link to current page' => array(
      'get crumbs' => 'hansel_action_add_link_to_current_page_get_crumbs',
      'file' => 'hansel.actions.inc',
    ),
    'add parents' => array(
      'get crumbs' => 'hansel_action_add_parents_get_crumbs',
      'config form' => 'hansel_action_add_parents_config_form',
      'file' => 'hansel.actions.inc',
    ),
  );
}

/**
 * Get all Hansel rules for a specified parent id in a list.
 *
 * @access private
 * @param int $pid Return only rules with this parent id
 * @return array
 */
function _hansel_get_rules($pid = 0) {
  $output = array();
Tim Leytens's avatar
Tim Leytens committed

  $sql = "SELECT r.rid, r.pid, r.name, r.crumb_action, r.crumb_action_arguments,
    rag.destination as goto_destination,
    ral.restore_original as leave_restore,
    ras.handler as switch_handler,
    ras.arguments as switch_arguments,
    CASE COALESCE(r.pid, 0) WHEN 0 THEN
    CASE r.name WHEN 'start' THEN 0 ELSE 1 END
    ELSE 1 END AS start_order
    FROM {hansel_rule} r
    LEFT JOIN {hansel_rule_action_goto} rag ON rag.rid = r.rid
    LEFT JOIN {hansel_rule_action_leave} ral ON ral.rid = r.rid
    LEFT JOIN {hansel_rule_action_switch} ras ON ras.rid = r.rid ";

  if ($pid != -1) { // Means; get all rules as a flat list
Tim Leytens's avatar
Tim Leytens committed
    $sql .= "WHERE COALESCE(pid, 0) = :pid ";
Tim Leytens's avatar
Tim Leytens committed
  $sql .= "ORDER BY start_order ASC, r.name ASC";
  $res = db_query($sql, array(':pid' => $pid));
  while ($rec = $res->fetchAssoc()) {
    $rule = new stdClass();
    $rule->rid = $rec['rid'];
    $rule->pid = $rec['pid'];
    $rule->name = $rec['name'];
    $rule->crumb_action = $rec['crumb_action'];
    if (empty($rec['crumb_action_arguments'])) {
      $rule->crumb_action_arguments = array();
    }
    else {
      $rule->crumb_action_arguments = unserialize($rec['crumb_action_arguments']);
    }
    if (!empty($rec['goto_destination'])) {
      $rule->action = 'goto';
      $rule->destination = $rec['goto_destination'];
    }
    elseif (!empty($rec['switch_handler'])) {
      $rule->action = 'switch';
      $rule->handler = $rec['switch_handler'];
      if (!empty($rec['switch_arguments'])) {
        $rule->arguments = unserialize($rec['switch_arguments']);
      }
      else {
        $rule->arguments = array();
      }
    }
    else {
      $rule->action = 'leave';
      $rule->restore = $rec['leave_restore'] ? TRUE : FALSE;
    }
    $output[$rule->rid] = $rule;
  }
  return $output;
}

/**
 * Get all Hansel rules in a list including depth,
 * sorted by hierarchy, then alphabetically.
 *
 * @param int $pid
 * @param int $depth
 * @return array
 */
function _hansel_get_rules_tree($pid = 0, $depth = 0) {
  $output = array();
  if ($pid == -1) {
    // Include root element.
    $output[0] = '<root>';
    $depth = 1;
    $pid = 0;
  }
  foreach (_hansel_get_rules($pid) as $rule) {
    $pad_str = '- ';
Tim Leytens's avatar
Tim Leytens committed
    $name = str_pad('', $depth * drupal_strlen($pad_str), $pad_str) . $rule->name;
    $output[$rule->rid] = $name;
    foreach (_hansel_get_rules_tree($rule->rid, $depth + 1) as $rid => $name) {
      $output[$rid] = $name;
    }
  }
  return $output;
}

/**
 * Get list of all available switch types.
 *
 * @access private
 * @return array
 */
function _hansel_get_switch_types() {
  $output = array();
  foreach (module_implements('hansel_switch_types') as $module) {
    $types = module_invoke($module, 'hansel_switch_types');
    foreach ($types as $type => $details) {
      $details['module'] = $module;
      $output[$type] = $details;
      if (!empty($details['file'])) {
        // Seperate filename and extension.
        preg_match('/^(.*)\\.([^\\.]+)$/', $details['file'], $match);
        module_load_include($match[2], $module, $match[1]);
      }
    }
  }
  return $output;
}

/**
 * Get list of all available breadcrumb action types.
 *
 * @access private
 * @return array
 */
function _hansel_get_action_types() {
  $output = array();
  foreach (module_implements('hansel_action_types') as $module) {
    $types = module_invoke($module, 'hansel_action_types');
    foreach ($types as $type => $details) {
      $details['module'] = $module;
      $output[$type] = $details;
      if (!empty($details['file'])) {
        // Seperate filename and extension.
        preg_match('/^(.*)\\.([^\\.]+)$/', $details['file'], $match);
        module_load_include($match[2], $module, $match[1]);
      }
    }
  }
  return $output;
}

/**
 * Check if the switch has a configuration form.
 *
 * @param string $handler
 * @return bool
 */
function _hansel_is_configurable_switch($handler) {
  $types = _hansel_get_switch_types();
  if (!isset($types[$handler])) {
    return FALSE;
  }
  return !empty($types[$handler]['config form']);
}

/**
 * Get switch information line, used for display in admin.
 *
 * @access private
 * @param object $rule
 * @return string
 */
function _hansel_get_switch_info($rule) {
  $types = _hansel_get_switch_types();
  if (!isset($types[$rule->handler])) {
    return '';
  }
  if (empty($types[$rule->handler]['info'])) {
    return '';
  }
  return call_user_func($types[$rule->handler]['info'], $rule->arguments);
}

/**
 * Get information line for a breadcrumb action, used for display in admin.
 *
 * @access private
 * @param object $rule
 * @return string
 */
function _hansel_get_crumb_action_info($rule) {
  $types = _hansel_get_action_types();
  if (!isset($types[$rule->crumb_action])) {
    return '';
  }
  if (empty($types[$rule->crumb_action]['info'])) {
    return $rule->crumb_action;
  }
  return filter_xss(call_user_func($types[$rule->crumb_action]['info'], $rule->crumb_action_arguments));
}

/**
 * Set the test path for Hansel, used on the test tab.
 *
 * @access private
 * @param string $path
 */
function _hansel_set_test_path($path) {
  global $_hansel_test_path;
  // The arg() output - which we use to switch on - is always the real path, not the alias.
  if (empty($path)) {
    $path = variable_get('site_frontpage', 'node');
    drupal_set_message(t('Path %path is used as frontpage', array('%path' => $path)));
  }
  if (module_exists('path')) {
Tim Leytens's avatar
Tim Leytens committed
    $res = db_query("SELECT source FROM {url_alias} WHERE alias = :path", array(':path' => $path));
    if ($src = $res->fetchField()) {
      drupal_set_message(t('Given path is an alias, using %src instead', array('%src' => $src)), 'warning');
      $path = $src;
    }
  }
  $_hansel_test_path = $path;
}

/**
 * Get a part of the query string.
 *
 * Hansel extensions must use hansel_arg() instead of arg(). This is to make the
 * testsuite work (found at admin/build/hansel/test).
Tim Leytens's avatar
Tim Leytens committed
 *
 * @param int $number Part number of the query string, starting with 0.
 * @return string
 */
function hansel_arg($number) {
  global $_hansel_test_path;
  if (!empty($_hansel_test_path)) {
    return arg($number, $_hansel_test_path);
  }
  return arg($number);
}

/**
 * Get the query string
 *
 * Hansel extensions must use hansel_path() instead of $_GET['q']. This is to
 * make the testsuite work (found at admin/build/hansel/test).
 *
 * @return string
 */
function hansel_path() {
  global $_hansel_test_path;
  if (!empty($_hansel_test_path)) {
    return $_hansel_test_path;
  }
  return isset($_GET['q']) ? $_GET['q'] : '';
}

/**
 * Build the Hansel configuration object.
 *
 * This object is used to cache the whole Hansel configuration. We use this
 * to generate the breadcrumbs and for the export functionality.
 *
 * @access private
 * @return array
 */
function _hansel_build_config() {
  $start_rid = NULL;
  $rules = array();
  foreach (_hansel_get_rules(-1) as $name => $rule) {
    // Add children
    $rule->children = array();
Tim Leytens's avatar
Tim Leytens committed
    $res = db_query("SELECT r.rid FROM {hansel_rule} r WHERE r.pid = :pid", array(':pid' => $rule->rid));
    while ($rec = $res->fetchAssoc()) {
      $rule->children[] = $rec['rid'];
    }
    $rules[$rule->rid] = $rule;
    if (empty($rule->pid) && $rule->name == 'start') {
      $start_rid = $rule->rid;
    }
  }
  return array(
Tim Leytens's avatar
Tim Leytens committed
    'version' => 1,
    'start_rid' => $start_rid,
    'rules' => $rules,
    'nodetypes' => variable_get('hansel_nodetypes', array()),
  );
}

/**
 * Add a message to the trace.
Tim Leytens's avatar
Tim Leytens committed
 *
 * @param string $message
 */
function _hansel_test_message($message) {
  global $_hansel_test_messages;
  if (empty($_hansel_test_messages)) {
    $_hansel_test_messages = array();
  }
  $_hansel_test_messages[] = filter_xss($message);
}

/**
 * Get Hansel configuration.
 *
 * @return array
 */
function hansel_get_config() {
Tim Leytens's avatar
Tim Leytens committed
  $config = cache_get('hansel_config');
  if (!$config || $config->data['version'] != 1) {
    $config = _hansel_build_config();
Tim Leytens's avatar
Tim Leytens committed
    cache_set('hansel_config', $config);
  }
  else {
    $config = $config->data;
  }
  return $config;
}

/**
 * Generate breadcrumbs.
 *
mauritsl's avatar
mauritsl committed
 * @param bool $test
 *   Save test messages.
 * @param bool $html
 *   Return the output in plain text rather than HTML.
 * @param bool $keep_last_item
 *   Force this function to keep the last item, even when the settings are set
 *   to hide it. This is used for the alias token.
 * @param array $flags
 *   Breadcrumb actions and switches can act differently upon the given flags.
mauritsl's avatar
mauritsl committed
 * @return mixed
 *   Array with breadcrumbs or FALSE (means: leave with restore option).
function hansel_get_breadcrumbs($test = FALSE, $plaintext = FALSE, $keep_last_item = FALSE, $flags = array()) {
  global $_hansel_flags;
  $_hansel_flags = $flags;
  
  $config = hansel_get_config();
  extract($config);
  if (!$start_rid) {
    !$test or _hansel_test_message(t('No rule found with name "start" on the top level'));
    return FALSE;
  }
Tim Leytens's avatar
Tim Leytens committed

  $crumb_actions = _hansel_get_action_types();
  $switch_types = _hansel_get_switch_types();

  // Define variable containing rule id's we had used.
  // We need this to prevent infinite loops generated by wrong goto actions.
  $history = array();

  // Define variable containing the newly generated breadcrumbs.
  $breadcrumbs = array();

  // Set the current rule id to the start rule.
  $rid = $start_rid;

  // Walk through the rules until we return or break.
  while (TRUE) {
    // Check for recursion.
    if (in_array($rid, $history)) {
      !$test or _hansel_test_message(t('Got recursion on rule %name', array('%name' => $rules[$rid]->name)));
      return FALSE;
    }
    $history[] = $rid;
Tim Leytens's avatar
Tim Leytens committed

    !$test or _hansel_test_message(t('Executing rule %name', array('%name' => $rules[$rid]->name)));
Tim Leytens's avatar
Tim Leytens committed

    // Execute crumb action.
    if ($rules[$rid]->crumb_action) {
      !$test or _hansel_test_message(t('Executing crumb action %type', array('%type' => $rules[$rid]->crumb_action)));
      $callback = $crumb_actions[$rules[$rid]->crumb_action]['get crumbs'];
      if (!function_exists($callback)) {
        !$test or _hansel_test_message(t('Callback not found'));
        break; // Break while (TRUE) loop
      }
      $crumbs = call_user_func($callback, $rules[$rid]->crumb_action_arguments);
      for ($i = 0; $i < count($crumbs); $i++) {
        if (empty($crumbs[$i]) || (is_array($crumbs[$i]) && empty($crumbs[$i]['title']))) {
          // Skip empty breadcrumbs in output.
          continue;
        }
        $breadcrumbs[] = $crumbs[$i];
      }
      !$test or _hansel_test_message(t('Added %count crumb(s)', array('%count' => count($crumbs))));
    }
    // Execute action.
    switch ($rules[$rid]->action) {
      case 'goto':
        if (!isset($rules[$rules[$rid]->destination])) {
          !$test or _hansel_test_message(t('Destination not found, leaving'));
          break(2); // Break while (TRUE) loop
        }
        !$test or _hansel_test_message(t('Goto rule %name', array('%name' => $rules[$rules[$rid]->destination]->name)));
        $rid = $rules[$rid]->destination;
        break;
      case 'switch':
        !$test or _hansel_test_message(t('switch on %handler', array('%handler' => $rules[$rid]->handler)));
        $callback = $switch_types[$rules[$rid]->handler]['compare'];
        if (!function_exists($callback)) {
          !$test or _hansel_test_message(t('Callback not found'));
          break(2); // Break while (TRUE) loop
        }
        $default_rid = NULL;
        foreach ($rules[$rid]->children as $child_rid) {
          if (call_user_func($callback, $rules[$rid]->arguments, $rules[$child_rid]->name)) {
            !$test or _hansel_test_message(t('Match on rule %name', array('%name' => $rules[$child_rid]->name)));
            $rid = $child_rid;
            break(2);
          }
          else {
            !$test or _hansel_test_message(t('No match on rule %name', array('%name' => $rules[$child_rid]->name)));
            if ($rules[$child_rid]->name == '<default>') {
              $default_rid = $child_rid;
            }
          }
        }
        if ($default_rid) {
          !$test or _hansel_test_message(t('Using default rule'));
          $rid = $default_rid;
        }
        else {
          !$test or _hansel_test_message(t('No default rule found, leaving'));
          break(2); // Break while (TRUE) loop
        }
        break;
      case 'leave':
      default:
        // Check if the "restore original breadcrumbs" option is checked.
        if ($rules[$rid]->restore) {
mauritsl's avatar
mauritsl committed
          hansel_breadcrumb_was_restored(TRUE);
          !$test or _hansel_test_message(t('Leave and restore old breadcrumbs'));
          return FALSE;
        }
        else {
          !$test or _hansel_test_message(t('Leave'));
          break(2); // Break while (TRUE) loop
  $count = count($breadcrumbs);
  $last_item_number = $count - 1;
Tim Leytens's avatar
Tim Leytens committed
  $last_item_link = variable_get('hansel_breadcrumb_last_item_link', TRUE);
  // Convert breadcrumbs array structure into an array with HTML links
    // Breadcrumbs should be returned in an array by the get crumbs callbacks.
    // Strings are supported for legacy, but are deprecated.
    // Please note that tokens are not supported only on arrays.
    if (is_array($breadcrumbs[$i])) {
Tim Leytens's avatar
Tim Leytens committed
      $breadcrumbs[$i]['title'] = hansel_replace_tokens($breadcrumbs[$i]['title'], $breadcrumbs[$i]);
      if ($plaintext) {
        $breadcrumbs[$i] = $breadcrumbs[$i]['title'];
        continue;
      }
Tim Leytens's avatar
Tim Leytens committed
      $breadcrumbs[$i]['href'] = hansel_replace_tokens($breadcrumbs[$i]['href'], $breadcrumbs[$i]);
      $breadcrumbs[$i]['title'] = _hansel_trim($breadcrumbs[$i]['title']);
      switch ($breadcrumbs[$i]['href']) {
        case '<none>':
          $breadcrumbs[$i] = check_plain($breadcrumbs[$i]['title']);
          break;
        case '<front>':
          $breadcrumbs[$i] = ($last_item_link || $i != $last_item_number) ? l($breadcrumbs[$i]['title'], '') : check_plain($breadcrumbs[$i]['title']);
          if ($breadcrumbs[$i]['href'] && $breadcrumbs[$i]['href']{0} == '/') {
            // Prefix paths starting with a slash with the full URI.
            if (preg_match('/^(https?...[^\\/]+)/', url('', array('absolute' => TRUE)), $match)) {
              $breadcrumbs[$i]['href'] = $match[1] . $breadcrumbs[$i]['href'];
            }
          }
          $breadcrumbs[$i] = ($last_item_link || $i != $last_item_number) ? l($breadcrumbs[$i]['title'], $breadcrumbs[$i]['href']) : check_plain($breadcrumbs[$i]['title']);
  // Remove the last item if the "Hide last item" configuration option is set.
mauritsl's avatar
mauritsl committed
  if (variable_get('hansel_breadcrumb_last_item_hide', FALSE) && !$keep_last_item) {
    if ($count) {
      !$test or _hansel_test_message(t('Removing last breadcrumb item (hide last item configuration option is set)'));
      array_pop($breadcrumbs);
      --$count;
    }
  }

  // Trim whole items if a maximum item count is set.
Tim Leytens's avatar
Tim Leytens committed
  if ($max_items = variable_get('hansel_max_item_count', 0)) {
    if ($count > $max_items) {
      $trimmed_breadcrumbs = array();
      for ($i = 0; $i < floor($max_items / 2); $i++) {
        $trimmed_breadcrumbs[] = $breadcrumbs[$i];
      }
Tim Leytens's avatar
Tim Leytens committed
      if ($replacement = variable_get('hansel_removed_items_replacement', '(...)')) {
        $trimmed_breadcrumbs[] = check_plain($replacement);
      }
      for ($i = $count - ceil($max_items / 2); $i < $count; $i++) {
        $trimmed_breadcrumbs[] = $breadcrumbs[$i];
      }
      $breadcrumbs = $trimmed_breadcrumbs;
    }
  }
Tim Leytens's avatar
Tim Leytens committed
  return array('breadcrumb' => $breadcrumbs);
/**
 * Trim the title for breadcrumb links.
 */
function _hansel_trim($title) {
Tim Leytens's avatar
Tim Leytens committed
  $max_length = variable_get('hansel_max_item_length', 0);
  $word_boundary = variable_get('hansel_trim_on_word_boundary', TRUE);
  $ellpisis = variable_get('hansel_trim_ellipsis', '...');

  if (!$max_length) {
    // Trimming is disabled.
    return $title;
  }

  if (drupal_strlen($title) <= $max_length) {
    return $title;
  }

  $title = drupal_substr($title, 0, $max_length);
Tim Leytens's avatar
Tim Leytens committed
  if (strrpos($title, ' ') > drupal_strlen($title) / 3 && $word_boundary) {
    $title = drupal_substr($title, 0, strrpos($title, ' '));
/**
 * Get themed breadcrumbs, which content may be overruled by Hansel.
 *
 * @param array $original_breadcrumbs
 * @return string
 */
function hansel_get_themed_breadcrumbs($original_breadcrumbs) {
  $breadcrumbs = hansel_get_breadcrumbs();
  if (is_array($breadcrumbs)) {
    return theme('breadcrumb_original', $breadcrumbs);
  }
  return theme('breadcrumb_original', $original_breadcrumbs);
}

/**
 * Replace tokens using token module, using the node or user from the path.
 *
 * @param string $input
 * @return string
 */
Tim Leytens's avatar
Tim Leytens committed
function hansel_replace_tokens($input, $token) {
  global $user;
  // Statically cache token type and object
  static $token_type;
  static $token_object;

  // Check if token module is enabled and at least one token is used.
Tim Leytens's avatar
Tim Leytens committed
  if (preg_match('/\\[.+\\]/', $input)) {
    // Determine the context for the token replacement and load the node or user object.
    if (!isset($token_type)) {
      $token_type = 'global';
      $token_object = NULL;
Tim Leytens's avatar
Tim Leytens committed
      if (drupal_strtolower(hansel_arg(0)) == 'node' && is_numeric(hansel_arg(1))) {
        $token_type = 'node';
        $token_object = node_load(hansel_arg(1));
      }
Tim Leytens's avatar
Tim Leytens committed
      elseif (drupal_strtolower(hansel_arg(0)) == 'user') {
        $token_type = 'user';
        if (is_numeric(hansel_arg(1))) {
          $token_object = user_load(hansel_arg(1));
        }
        else {
          $token_object = $user;
        }
      }
    }
    // Replace tokens using token module.
Tim Leytens's avatar
Tim Leytens committed
    $input = token_replace($input, array($token_type => $token_object));
  }

  // Replace [arg-N] tokens
  while ($arg = hansel_arg(++$i)) {
    $input = str_replace("[arg-$i]", $arg, $input);
  }

  return $input;
mauritsl's avatar
mauritsl committed
}

/**
 * Static restore function. DX experience.
 *
 * Determine if the breadcrumbs where restored.
 */
function hansel_breadcrumb_was_restored($restored = FALSE) {
  static $restored_value, $ran = FALSE;
  if (!$ran) {
    $restored_value = $restored;
    $ran = TRUE;
  }
  return $restored_value;
}

/**
 * Read data from Hansel cache, respecting Hansel cache settings.
 *
 * @param string $cid
 * @return mixed
 */
function hansel_cache_get($cid) {
  static $setting = NULL;
  if (is_null($setting)) {
Tim Leytens's avatar
Tim Leytens committed
    $setting = variable_get('hansel_cache', 0);
  }
  if ($setting) {
    $cid = "hansel:$cid";
    if ($data = cache_get($cid)) {
      return $data->data;
    }
  }
  return NULL;
}

/**
 * Write data to Hansel cache, respecting Hansel cache settings.
 *
 * @param string $cid
 * @param mixed $data
 */
function hansel_cache_set($cid, $data) {
  static $setting = NULL;
  if (is_null($setting)) {
Tim Leytens's avatar
Tim Leytens committed
    $setting = variable_get('hansel_cache', 0);
Tim Leytens's avatar
Tim Leytens committed
    cache_set($cid, $data, 'cache', REQUEST_TIME + $setting);
Tim Leytens's avatar
Tim Leytens committed
}

/**
 * Implements of hook_tokens().
 */
function hansel_tokens($type, $tokens, $data = array(), $options = array()) {
  global $_hansel_test_path;
  if (!in_array($type, array('node', 'taxonomy_term', 'user'))) {
    return array();
  }
  switch ($type) {
    case 'node':
      $object = $data['node'];
      $_hansel_test_path = 'node/' . $object->nid;
      $title = $object->title;
      break;
    case 'taxonomy':
      $object = $data['taxonomy_term'];
      $_hansel_test_path = 'taxonomy/term/' . $object->tid;
      $title = $object->name;
      break;
    case 'user':
      $object = $data['user'];
      $_hansel_test_path = 'user/' . $object->uid;
      $title = $object->name;
      break;
    default:
      return array();
  }

  $path = hansel_get_breadcrumbs(FALSE, TRUE, TRUE, array('alias'));
mauritsl's avatar
mauritsl committed
  if (is_array($path)) {
    $path = $path['breadcrumb'];
  }
  else {
    $path = array();
  }
  $_hansel_test_path = NULL;
  
  if (variable_get('hansel_remove_first_token_item', TRUE)) {
    array_shift($path);
  }
  
  if (empty($path)) {
    $path = array(t('content'), $title);
  }
  
  $path = array_map('strtolower', $path);
  if (module_exists('pathauto')) {
    module_load_include('inc', 'pathauto');
    if (function_exists('pathauto_cleanstring')) {
      $path = array_map('pathauto_cleanstring', $path);
    }
  }
  $path = implode('/', $path);
  
  $replacements = array();
  foreach ($tokens as $name => $original) {
    if ($name == 'hansel-path') {
      $replacements[$original] = $path;
    }
  }
  
  return $replacements;
}

/**
 * Implements of hook_token_info().
 */
function hansel_token_info() {
  $tokens = array(
    'hansel-path' => array(
      'name' => t('Hansel path'),
      'description' => t('The URL alias generated by Hansel.'),
    ),
  );
  
  return array(
    'tokens' => array(
      'node' => $tokens,
      'taxonomy_term' => $tokens,
      'user' => $tokens,
    ),
  );
}
/**
 * Implements hook_features_api().
 */
function hansel_features_api() {
  return array(
    'hansel' => array(
      'name' => 'Hansel',
      'default_hook' => 'hansel_default',
      'default_file' => FEATURES_DEFAULTS_INCLUDED,
      'feature_source' => TRUE,
      'file' => drupal_get_path('module', 'hansel') .'/hansel.features.inc',
    ),
  );
}

/**
 * Set the active menu item to the item corresponding the breadcrumbs.
 */
function _hansel_activate_menu() {
  $path = hansel_get_breadcrumbs(FALSE, TRUE);
  if (is_array($path)) {
    $path = $path['breadcrumb'];
  }
  if (variable_get('hansel_set_menu_item_skip_first', TRUE)) {
    array_shift($path);
  }
  
  // Build a list of menu names. We will only look for items in one of these menu's.
  $menus = menu_get_menus();
  if (isset($menus['devel'])) {
    // Do not use the development menu.
    unset($menus['devel']);
  }
  $menus = array_keys($menus);
  
  // Let other modules alter the menu list.
  drupal_alter('hansel_menus', $menus);
  
  if (!$menus) {
    // No menu's to search for items.
    return;
  }
  
  $link_path = FALSE;
  $plid = 0;
  for ($i = 0; $i < count($path); ++$i) {
    $link = db_select('menu_links', 'ml')
      ->fields('ml', array('menu_name', 'mlid', 'link_path'))
      ->condition('link_title', $path[$i])
      ->condition('plid', $plid)
      ->condition('menu_name', $menus, 'in')
      ->range(0, 1)
      ->execute()
      ->fetchObject();
    if ($link) {
      $plid = $link->mlid;
      $link_path = $link->link_path;
      $menu_name = $link->menu_name;
    }
  }
  
  if ($link_path) {
    $item = menu_get_item();
    $item['href'] = $link_path;
    if (!empty($item['access'])) {
      menu_set_item(NULL, $item);
    }
    $menus = menu_set_active_menu_names();
    if (!in_array($menu_name, $menus)) {
      $menus[] = $menu_name;
      menu_set_active_menu_names($menus);
    }
  }
}

/**
 * Get the real path when alias is specified.
 * 
 * @param string $path
 * @return string 
 */
function _hansel_get_original_path($path) {
  if (module_exists('path')) {
    $source = db_select('url_alias', 'ua')
            ->fields('ua', array('source'))
            ->condition('alias', $path)
            ->range(0, 1)
            ->execute()
            ->fetchField();
    if ($source) {
      return $source;
    }
  }
  return $path;
}