<?php
// $Id: multiple_node_menu.module,v 1.0 2009/7/28 14:19:15 petes Exp $

/**
 * Implementation of hook_menu()
 */
function multiple_node_menu_menu() {
	$items = array();

	$items['multiple_menu/js/%'] = array(
		'page callback' => 'multiple_node_menu_js',
		'page arguments' => array(2),
		'access arguments' => array('access content'),
		'type ' => MENU_CALLBACK,
	);

	return $items;
}


function multiple_node_menu_js($delta = 0) {
	$form = multiple_node_menu_ajax_form_handler($delta);

	// Render the new output.
	$multiple_menu_form = $form['menu']['multiple_menu']['menu_links'];
	// Prevent duplicate wrappers.
	unset($multiple_menu_form['#prefix'], $multiple_menu_form['#suffix']);

	// $output = theme('status_messages') . drupal_render($multiple_menu_form);
	$output = drupal_render($multiple_menu_form);

	// AHAH is not being nice to us and doesn't know about the "Remove" button.
	// This causes it not to attach AHAH behaviours to it after modifying the form.
	// So we need to tell it first.
	$javascript = drupal_add_js(NULL, NULL);
	if (isset($javascript['setting'])) {
		$output .= '<script type="text/javascript">';
		$output .= 'jQuery.extend(Drupal.settings, '. drupal_to_js(call_user_func_array('array_merge_recursive', $javascript['setting'])) .');';
		$output .= '</script>';
	}

	// Final rendering callback.
	drupal_json(array('status' => TRUE, 'data' => $output));
}


function multiple_node_menu_ajax_form_handler($delta = 0) {
	// The form is generated in an include file which we need to include manually.
	include_once 'modules/node/node.pages.inc';
	$form_state = array('storage' => NULL, 'submitted' => FALSE);
	$form_build_id = $_POST['form_build_id'];

	// Get the form from the cache.
	$form = form_get_cache($form_build_id, $form_state);
	$args = $form['#parameters'];

	if (!is_array($args))
		return;
	$form_id = array_shift($args);

	// We need to process the form, prepare for that by setting a few internals.
	$form_state['post'] = $form['#post'] = $_POST;
	$form['#programmed'] = $form['#redirect'] = FALSE;

	// Set up our form state variable, needed for removing rows
	$form_state['remove_delta'] = $delta;

	// Build, validate and if possible, submit the form.
	drupal_process_form($form_id, $form, $form_state);

	// If validation fails, force form submission - this is my own "hack" for overcoming
	// issues where all required fields need to be filled out before the 'add more' button
	// can be clicked.A better solution is being worked on in Drupal's issue queue.
	if (form_get_errors()) {
		form_execute_handlers('submit', $form, $form_state);
	}

	// This call recreates the form relying solely on the form_state that the
	// drupal_process_form set up.
	$form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);

	return $form;
}


/**
 * Implementation of hook_nodeapi()
 */
function multiple_node_menu_nodeapi(&$node, $op, $arg1, $arg2) {
	switch ($op) {
		case 'insert':
		case 'update':
		if (isset($node->menu_links)) {
			if (count($node->menu_links)) {
				$node->menu = reset($node->menu_links);
			}

			$original_links = array();
			$new_links = array();

			$results = db_query("SELECT * FROM {menu_links} where router_path = 'node/%' AND link_path = 'node/%d' ORDER BY weight ASC", $node->nid);
			while ($row = db_fetch_array($results)) {
				$original_links[$row['mlid']] = $row;
			}

			$first_original_link = reset($original_links);
			$first_new_link = reset($node->menu_links);

			$reinsert = FALSE;

			/*
			Because of a sort order change, there is a new "first" menu item. 
			Since Drupal considers the lowest mlid the most important, 
			we basically have to delete all of our menu items and re-save them
			*/
			if ($first_original_link['mlid'] != $first_new_link['link_mlid']) {
				$reinsert = TRUE;
			}

			foreach ($node->menu_links AS $new_link) {
				if (!(isset($new_link['link_mlid']) && is_numeric($new_link['link_mlid']) && $new_link['link_mlid'] > 0)) {
					$new_link['link_mlid'] = 0;
				}

				$plid = end(explode(':', $new_link['link_parent']));
				$item = array(
					'link_title' 	=> $new_link['link_title'],
					'mlid' 		=> $new_link['link_mlid'],
					'router_path' 	=> 'node/%',
					'plid' 		=> $plid,
					'weight' 	=> $new_link['link_weight'],
					'link_path' 	=> 'node/' . $node->nid,
				);

				if ($new_link['link_enable'] != 1) {
					$item['hidden'] = 1;
				}

				if ($reinsert) {
					if ($item['mlid'] > 0)
						menu_link_delete($item['mlid']);

					$item['mlid'] = 0;
				}

				menu_link_save($item);

				if (!menu_link_save($item)) {
					drupal_set_message(t('There was an error saving the menu link.'), 'error');
				}

				$new_links[$new_link['link_mlid']] = $new_link;
			}
			// Delete menu links that are in the database but not the form -- they've been deleted
			foreach ($original_links AS $original_mlid => $original_link) {
				if (!isset($new_links[$original_mlid]))
				menu_link_delete($original_mlid);
			}

			// don't need this anymore because we don't really want the menu module to do anything else
			unset($node->menu);
		}
	}
}


/**
 * Implementation of hook_form_alter()
 */
function multiple_node_menu_form_alter(&$form, &$form_state, $form_id) {
	drupal_add_css( drupal_get_path('module', 'multiple_node_menu') . '/css/multiple_menu.css', 'module');

	if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
		$node = $form['#node'];
		if (!$node)
			return;

		$form['menu']['link_title']['#prefix'] = '<div class="add-menu-link-wrapper" style="background-color: #EEE; padding: 10px; margin: 10px 0px;"><h3>Add a new menu link</h3>';
		$form['menu']['hidden']['#attributes'] = array('style' => 'display: none');
		$form['menu']['weight']['#type'] = 'hidden';
		$form['menu']['parent']['#type'] = 'hidden';

		$form['#cache'] = TRUE;
		$form['#submit'][] = 'multiple_node_menu_submit';

		$form['menu']['multiple_menu'] = array(
			'#weight' => 5,
			'#tree' => FALSE,
			'#prefix' => '<div class="clear-block" id="menu-multiple-wrapper">',
			'#suffix' => '</div>',
		);

		$form['menu']['multiple_menu_add_more'] = array(
			'#prefix' => '<div class="clear-block menu-add-more-container">',
			'#suffix' => '</div></div>',
			'#type' => 'submit',
			'#value' => t('Add new menu link'),
			'#weight' => 1,
			'#submit' => array('multiple_node_menu_add_more_submit'),
			'#ahah' => array(
				'path' => 'multiple_menu/js/0',
				'wrapper' => 'multiple-menu-items',
				'method' => 'replace',
				'effect' => 'fade',
			),
		);

		$form['menu']['multiple_menu']['menu_links'] = array(
			'#prefix' => '<div id="multiple-menu-items"><h3>Current Menu Links</h3>',
			'#suffix' => '</div>',
			'#theme' => 'multiple_node_menu_table',
		);

		if (isset($form_state['post']['menu_links']) && count($form_state['post']['menu_links'])) {
			$node->multiple_menu_links = $form_state['post']['menu_links'];
		}

		if (!$node->multiple_menu_links) {
			$links = array();
			$results = db_query("SELECT * FROM {menu_links} where router_path = 'node/%' AND link_path = 'node/%d' ORDER BY weight ASC", $node->nid);
			$links = array();
			while ($row = db_fetch_array($results)) {
				$menu_link = array(
					'link_title' 	=> $row['link_title'],
					'link_weight' 	=> $row['weight'],
					'link_parent' 	=> $row['menu_name'] . ':' . $row['plid'],
					'link_mlid' 	=> $row['mlid'],
					'link_enable' 	=> $row['hidden'] == 0 ? 1 : 0,
				);
				$links[] = $menu_link;
			}
			$node->multiple_menu_links = $links;
		}
		$link_count = count($node->multiple_menu_links);

		// If a new track added, add to list and update the track count.
		if (isset($form_state['new_link'])) {
			$node->multiple_menu_links = array_merge($node->multiple_menu_links, array($form_state['new_link']));
			$link_count++;
		}

		// If a track removed, remove from list and update the track count.
		$remove_delta = -1;

		if (!empty($form_state['remove_delta'])) {
			$remove_delta = $form_state['remove_delta'] - 1;
			unset($node->multiple_menu_links[$remove_delta]);
			// Re-number the values.
			$node->multiple_menu_links = array_values($node->multiple_menu_links);
			$link_count--;
		}

		if ($link_count == 0) {
			$form['menu']['multiple_menu']['menu_links']['no_links'] = array(
				'#weight' 	=> 10,
				'#value' 	=> 'This page is not currently in the menu.',
			);
		} else {
			// Add the existing links to the form.
			for ($delta = 0; $delta < $link_count; $delta++) {
				$form['menu']['multiple_menu']['menu_links'][$delta] = multiple_node_menu_display_form($form['#node'], $delta, $node->multiple_menu_links[$delta]);
			}
		}
	}
}


/**
 * Implementation of hook_theme()
 */
function multiple_node_menu_theme() {
	return array(
		'multiple_node_menu_table' => array(
			'arguments' => array('form')
		)
	);
}


function theme_multiple_node_menu_table($form) {
	$headers = array(
		t(''), // Spacer
		t('Menu link title'),
		t('Parent Item'),
		t('Weight'),
		t('Enable'),
		t(''), // Delete
	);

	$rows = array(array());

	foreach (element_children($form) as $key) {
		// No need to print the field title every time.
		unset(
			$form[$key]['link_title']['#title'],
			$form[$key]['link_remove']['#title'],
			$form[$key]['link_mlid']['#title'],
			$form[$key]['link_enable']['#title'],
			$form[$key]['link_parent']['#title'],
			$form[$key]['link_weight']['#title']
		);

		// Build the table row.
		$form[$key]['link_weight']['#attributes']['class'] = 'multiple-menu-weight';
		$row = array(
			'data' => array(
				array('style' => 'width: 40px;'),
				array('data' => drupal_render($form[$key]['link_title']), 'class' => 'link-title'),
				array('data' => drupal_render($form[$key]['link_parent']), 'class' => 'link-parent'),
				array('data' => drupal_render($form[$key]['link_weight']), 'class' => 'link-weight'),
				array('data' => drupal_render($form[$key]['link_enable']), 'class' => 'link-enable'),
				array('data' => drupal_render($form[$key]['link_remove']). drupal_render($form[$key]['link_mlid']) , 'class' => 'remove-menu-link'),
			), 'class' => 'draggable',
		);
		$rows[] = $row;
	}

	if (!$form['no_links'])
		drupal_add_tabledrag('multiple-menu-table', 'order', 'sibling', 'multiple-menu-weight');

	$output = theme('table', $headers, $rows, array('id' => 'multiple-menu-table'));
	$output .= drupal_render($form);
	return $output;
}


function multiple_node_menu_display_form(&$form_node, $delta, $menu_item) {
	$form = array(
		'#tree' => TRUE,
	);

	// Menu link title.
	$form['link_title'] = array(
		'#type' => 'textfield',
		'#value' => $menu_item['link_title'],
		'#parents' => array('menu_links', $delta, 'link_title'),
	);

	$form['link_enable'] = array(
	 	'#type' => 'checkbox',
		'#title' => t('Enable'),
		'#value' => $menu_item['link_enable'] == 0 ? 0 : 1,
		'#parents' => array('menu_links', $delta, 'link_enable'),
	);

	// Generate a list of possible parents (not including this item or descendants).
	if ($menu_item['mlid']) {
		$options = menu_parent_options(menu_get_menus(), $menu_item);
	} else {
		$options = menu_parent_options(menu_get_menus(), array('mlid' => 0));
	}

	if ($menu_item['link_parent'])
		$default = $menu_item['link_parent'];

	if (!(is_array($options) && !isset($options[$default]))) {
		$default = 'primary-links:0';
	}

	$this_mlid = '';
	if (isset($menu_item['link_mlid']) && is_numeric($menu_item['link_mlid']))
		$this_mlid = $menu_item['link_mlid'];

	$form['link_mlid'] = array(
		'#type' => 'hidden',
		'#value' => $this_mlid,
		'#parents' => array('menu_links', $delta, 'link_mlid'),
	);

	$form['link_parent'] = array(
		'#type' => 'select',
		'#title' => t('Parent item'),
		'#default_value' => $default,
		'#options' => $options,
		'#attributes' => array('class' => 'menu-title-select'),
		'#parents' => array('menu_links', $delta, 'link_parent'),
	);

	multiple_node_menu_apply_hierarchical_select($form_node, $form['link_parent']);

	// "weight" field. should be hidden if JS is enabled
	$form['link_weight'] = array(
		'#type' => 'textfield',
		'#title' => t('Weight'),
		'#default_value' => $menu_item['link_weight'],
		'#attributes' => array('class' => 'link-weight'),
		'#parents' => array('menu_links', $delta, 'link_weight'),
	);

	// "remove" button
	$form['link_remove'] = array(
		'#type' => 'submit',
		'#name' => 'link_remove_' . $delta,
		'#value' => t('Delete'),
		'#weight' => 1,
		'#submit' => array('multiple_node_menu_remove_row_submit'),
		'#parents' => array('menu_links', $delta, 'link_remove'),
		'#ahah' => array(
			'path' => 'multiple_menu/js/' . ($delta + 1),
			'wrapper' => 'multiple-menu-items',
			'method' => 'replace',
			'effect' => 'fade',
		),
	);

	return $form;
}


/**
 * Submit handler for 'Add menu link' button
 */
function multiple_node_menu_add_more_submit($form, &$form_state) {
	if (!$form_state['post']['menu']['link_title'])
		return;

	$form_state['remove_delta'] = 0;


	// Set the form to rebuild and run submit handlers.
	node_form_submit_build_node($form, $form_state);

	// Make the changes we want to the form state.
	if ($form_state['values']['menu']['multiple_menu_add_more']) {
		$new_link = array();
		$new_link['link_title'] = $form_state['post']['menu']['link_title'];
		list($menu_name, $plid) = explode(':', $form_state['post']['menu']['parent']);

		$new_link['link_parent'] = $form_state['post']['menu']['parent'];
		$new_link['link_enable'] = isset($form_state['post']['menu']['hidden']) ? 0 : 1;
		$new_link['link_weight'] = $form_state['post']['menu']['weight'];
		$form_state['new_link'] = $new_link;
	}
}


function multiple_node_menu_apply_hierarchical_select(&$form_node, &$form_menu_field) {
	return; // Hold off on this until HS supports nested arrays of form fields

	// make sure Hierarchical Select is enabled, as well as its Menu integration add-on
	if (!function_exists('_hs_menu_apply_config'))
		return;

	$exclude = array();
	$parent_menu = variable_get($form_node->type.'_menu_parent', NULL);

	if ($parent_menu) {
		list($parent_menu, $parent_mlid) = explode(':', $parent_menu);

		foreach (menu_get_menus() AS $menu_id => $menu_name) {
			if ($menu_id != $parent_menu)
				$exclude[] = array($menu_id, 0);
		}
	}

	unset($form_menu_field['#options']);
	$form_menu_field['#type'] = 'hierarchical_select';
	_hs_menu_apply_config($form_menu_field, $exclude);
}


function multiple_node_menu_submit($form, &$form_state) {
	/*
	For some reason form_state['values']['menu_links'] is stuck on the defaults here,
	instead of the submitted values. Yet another needlessly complex Drupal voodoo process.
	*/
	$form_state['values']['menu_links'] = $form['#post']['menu_links'];
}


function multiple_node_menu_remove_row_submit($form, &$form_state) {
	// Set the form to rebuild and run submit handlers.
	node_form_submit_build_node($form, $form_state);
}

