<?php
// $Id: i18nmenu.module,v 1.2.2.12 2009/02/18 12:42:12 jareyero Exp $

/**
 * @file
 * Internationalization (i18n) submodule: Menu translation.
 *
 * @author Jose A. Reyero, 2005
 *
 */

/**
 * Implementation of hook_locale().
 */
function i18nmenu_locale($op = 'groups', $group = NULL) {
  switch ($op) {
    case 'groups':
      return array('menu' => t('Menu'));

    case 'refresh':
      if ($group == 'menu') {
        return i18nmenu_locale_refresh();
      }
  }
}

/**
 * Refresh locale strings.
 */
function i18nmenu_locale_refresh() {
  // Rebuild menus to ensure all items are altered in i18nmenu_menu_link_alter().
  menu_rebuild();
  foreach (menu_get_menus() as $name => $title) {
    $tree = menu_tree_all_data($name);
    i18nmenu_localize_tree($tree, TRUE);
  }
}

/**
 * Implementation of hook_menu_link_alter().
 *
 * Catch changed links, update language and set alter option.
 */
function i18nmenu_menu_link_alter(&$item, $menu) {
  // If we set option to language it causes an error with the link system
  // This should handle language only as the links are being manually updated
  if (!empty($item['language'])) {
    $item['options']['langcode'] = $item['language'];
  }
  elseif (isset($item['language'])) {
    unset($item['options']['langcode']);
  }
  // If we are handling custom menu items of menu module and no language is set,
  // invoke translation via i18nstrings module.
  if (empty($item['language']) && $item['module'] == 'menu') {
    // Set title_callback to FALSE to avoid calling t().
    $item['title_callback'] = FALSE;
    // Setting the alter option to true ensures that
    // hook_translated_menu_link_alter() will be called.
    $item['options']['alter'] = TRUE;
  }
}

/**
 * Implementation of hook_translated_menu_link_alter().
 *
 * Translate menu links on the fly.
 *
 * @see i18nmenu_menu_link_alter()
 */
function i18nmenu_translated_menu_link_alter(&$item, $map) {
  if ($item['module'] == 'menu') {
    $item['title'] = _i18nmenu_get_item($item);
  }
}

/**
 * Implementation of hook_help().
 */
function i18nmenu_help($path, $arg) {
  switch ($path) {
    case 'admin/help#i18nmenu' :
      $output = '<p>'. t('This module provides support for translatable custom menu items:') .'</p>';
      $output .= '<ul>';
      $output .= '<li>'. t('Create menus as usual, with names in the default language, usually English. If the menu is already created, no changes are needed.') .'</li>';
      $output .= '<li>'. t('Optionally, you can set up a language for a menu item so it is only displayed for that language.') .'</li>';
      $output .= '</ul>';
      $output .= '<p>'. t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/build/translate'))) .'</p>';
      return $output;
  }
}

/**
 * Get localized menu tree.
 */
function i18nmenu_translated_tree($menu_name) {
  static $menu_output = array();

  if (!isset($menu_output[$menu_name])) {
    $tree = menu_tree_page_data($menu_name);
    i18nmenu_localize_tree($tree);
    $menu_output[$menu_name] = menu_tree_output($tree);
  }
  return $menu_output[$menu_name];
}

/**
 * Localize menu tree.
 */
function i18nmenu_localize_tree(&$tree, $update = FALSE) {
  global $language;
  foreach ($tree as $index => $item) {
    $link = $item['link'];
    if ($link['customized']) {
      // Remove links for other languages than current.
      // Links with language wont be localized.
      if (!empty($link['options']['langcode'])) {
        if ($link['options']['langcode'] != $language->language) {
          unset($tree[$index]);
          //dsm("Removed because language:".$link['title'].' language='.$link['options']['langcode']);
        }
      }
      else {
        $router = i18nmenu_get_router($link['router_path']);
        // If the title is the same it will be localized by the menu system.
        if ($link['link_title'] != $router['title']) {
          $tree[$index]['link']['title'] = _i18nmenu_get_item($link, $update);
        }
        // Localize subtree.
        if ($item['below'] !== FALSE) {
          i18nmenu_localize_tree($tree[$index]['below'], $update);
        }
      }
    }
  }
}

/**
 * Optionally insert/update and return a localized menu item title.
 */
function _i18nmenu_get_item($link, $update = FALSE) {
  return tt('menu:item:'. $link['mlid'] .':title', $link['link_title'], NULL, $update);
}

/**
 * Delete a menu item translation.
 */
function _i18nmenu_delete_item($mlid) {
  i18nstrings_remove_string('menu:item:'. $mlid .':title');
}

/**
 * Get the menu router for this router path.
 *
 * We need the untranslated title to compare, and this will be fast.
 * There's no api function to do this?
 */
function i18nmenu_get_router($path) {
  static $cache = array();
  if (!array_key_exists($path, $cache)) {
    $cache[$path] = db_fetch_array(db_query("SELECT title FROM {menu_router} WHERE path = '%s'", $path));
  }
  return $cache[$path];
}

/**
 * Implementation of hook_form_form_id_alter().
 *
 * Add a language selector to the menu_edit_item form and register a submit
 * callback to process items.
 */
function i18nmenu_form_menu_edit_item_alter(&$form, $form_state) {
  if ($form['menu']['#item'] && isset($form['menu']['#item']['options']['langcode'])) {
    $language = $form['menu']['#item']['options']['langcode'];
  }
  else {
    $language = '';
  }
  $form['menu']['language'] = array(
    '#type' => 'select',
    '#title' => t('Language'),
    '#description' => t('Select a language for this menu item. Choose "All languages" to make the menu item translatable into different languages.'),
    '#options' => array('' => t('All languages')) + locale_language_list('name'),
    '#default_value' => $language,
  );
  $form['#submit'][] = 'i18nmenu_menu_item_update';
}

/**
 * Submit handler for the menu_edit_item form.
 *
 * On menu item insert or update, save a translation record.
 */
function i18nmenu_menu_item_update($form, &$form_state) {
  // Ensure we have a menu item to work with.
  if (isset($form_state['values']['menu'])) {
    $item = $form_state['values']['menu'];
    _i18nmenu_update_item($item);
  }
}

/**
 * Update the translation data for a menu item that has been inserted
 * or updated.
 *
 * @see i18nmenu_menu_item_update()
 * @see i18nmenu_nodeapi()
 */
function _i18nmenu_update_item($item) {
  list($item['menu_name'], $item['plid']) = explode(':', $item['parent']);
  // If this was an insert, determine the ID that was set.
  if (!isset($link['mlid'])) {
    $item['mlid'] = db_result(db_query("SELECT MAX(mlid) FROM {menu_links} WHERE link_path = '%s' AND menu_name = '%s' AND module = 'menu' AND plid = %d AND link_title = '%s'", $item['link_path'], $item['menu_name'], $item['plid'], $item['link_title']));
  }
  if (!empty($item['mlid'])) {
    _i18nmenu_get_item($item, TRUE);
  }
}

/**
 * Implementation of hook_form_form_id_alter().
 *
 * Add a submit handler to the the menu item deletion confirmation form.
 */
function i18nmenu_form_menu_item_delete_form_alter(&$form, $form_state) {
  $form['#submit'][] = 'i18nmenu_item_delete_submit';
}

/**
 * Submit function for the delete button on the menu item editing form.
 */
function i18nmenu_item_delete_submit($form, &$form_state) {
  _i18nmenu_delete_item($form['#item']['mlid']);
}

/**
 * Implementation of hook_form_alter().
 *
 * Add language to menu settings of the node form, as well as setting defaults
 * to match the translated item's menu settings.
 */
function i18nmenu_form_alter(&$form, &$form_state, $form_id) {
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
    $node = $form['#node'];
    
    // Do nothing if the node already has a menu.
    if (!empty($node->menu['mlid'])) {
      return;
    }
    
    // Find the translation source node.  If creating a new node,
    // translation_source is set.  Otherwise, node_load the tnid.
    // New translation.
    if (!empty($node->translation_source)) {
      $tnode = $node->translation_source;
    }
    // Editing existing translation.
    elseif (!empty($node->nid) && !empty($node->tnid) && $node->nid != $node->tnid) {
      $tnode = node_load($node->tnid);
    }
    // If no translation node, return.
    else {
      return;
    }
    
    // Prepare the tnode so the menu item will be available.
    node_object_prepare($tnode);

    if ($tnode->menu) {
      // Set default values based on translation source's menu.
      $form['menu']['link_title']['#default_value'] = $tnode->menu['link_title'];
      $form['menu']['weight']['#default_value'] = $tnode->menu['weight'];
      $form['menu']['parent']['#default_value'] = $tnode->menu['menu_name'] .':'. $tnode->menu['plid'];
      
      // Set menu language to node language.
      $form['menu']['language'] = array('#type' => 'value', '#value' => $node->language);
      
      // Customized must be set to 1 to save language.
      $form['menu']['customized'] = array('#type' => 'value', '#value' => 1);
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 *
 * Save or delete menu item strings associated with nodes.
 */
function i18nmenu_nodeapi(&$node, $op) {
  switch ($op) {
    case 'insert':
    case 'update':
      if (isset($node->menu)) {
        $item = $node->menu;
        if (!empty($item['delete'])) {
          _i18nmenu_delete_item($item['mlid']);
        }
        elseif (trim($item['link_title'])) {
          $item['link_title'] = trim($item['link_title']);
          $item['link_path'] = "node/$node->nid";
          _i18nmenu_update_item($item);
        }
      }
      break;
    case 'delete':
      // Delete all menu item link translations that point to this node.
      $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = 'node/%d' AND module = 'menu'", $node->nid);
      while ($m = db_fetch_array($result)) {
        _i18nmenu_delete_item($m['mlid']);
      }
      break;
    case 'prepare translation':
      if (!$node->menu->mlid && !empty($node->translation_source)) {
        $tnode = $node->translation_source;
        // Prepare the tnode so the menu item will be available.
        node_object_prepare($tnode);
        $node->menu['link_title'] = $tnode->menu['link_title'];
        $node->menu['weight'] = $tnode->menu['weight'];
      }
      break;
  }
}
