Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.346 diff -u -9 -p -r1.346 menu.inc --- includes/menu.inc 18 Sep 2009 10:54:20 -0000 1.346 +++ includes/menu.inc 29 Sep 2009 19:33:55 -0000 @@ -2514,53 +2514,42 @@ function menu_link_children_relative_dep return ($max_depth > $item['depth']) ? $max_depth - $item['depth'] : 0; } /** * Update the children of a menu link that's being moved. * * The menu name, parents (p1 - p6), and depth are updated for all children of * the link, and the has_children status of the previous parent is updated. */ -function _menu_link_move_children($item, $existing_item) { - $query = db_update('menu_links'); +function _menu_link_move_children($item, $existing_item = NULL) { + $child_mlids = db_query('SELECT mlid FROM {menu_links} WHERE plid = :plid', array(':plid' => $item['mlid']))->fetchCol(); + foreach ($child_mlids as $mlid) { + $query = db_update('menu_links'); + $child_item = array('mlid' => $mlid, 'depth' => $item['depth'] + 1); + _menu_link_parents_set($child_item, $item); - $query->fields(array('menu_name' => $item['menu_name'])); + $query->expression('depth', $child_item['depth']); + for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) { + $query->expression('p' . $i, $child_item['p' . $i]); + } + $query->condition('mlid', $mlid); - $p = 'p1'; - for ($i = 1; $i <= $item['depth']; $p = 'p' . ++$i) { - $query->fields(array($p => $item[$p])); - } - $j = $existing_item['depth'] + 1; - while ($i <= MENU_MAX_DEPTH && $j <= MENU_MAX_DEPTH) { - $query->expression('p' . $i++, 'p' . $j++); - } - while ($i <= MENU_MAX_DEPTH) { - $query->fields(array('p' . $i++ => 0)); - } + $query->execute(); - $shift = $item['depth'] - $existing_item['depth']; - if ($shift < 0) { - $query->expression('depth', 'depth - :depth', array(':depth' => -$shift)); - } - elseif ($shift > 0) { - $query->expression('depth', 'depth + :depth', array(':depth' => $shift)); + if (!empty($item['has_children'])) { + _menu_link_move_children($child_item); + } } - $query->condition('menu_name', $existing_item['menu_name']); - $p = 'p1'; - for ($i = 1; $i <= MENU_MAX_DEPTH && $existing_item[$p]; $p = 'p' . ++$i) { - $query->condition($p, $existing_item[$p]); + if ($existing_item) { + // Check the has_children status of the parent, while excluding this item. + _menu_update_parental_status($existing_item, TRUE); } - - $query->execute(); - - // Check the has_children status of the parent, while excluding this item. - _menu_update_parental_status($existing_item, TRUE); } /** * Check and update the has_children status for the parent of a link. */ function _menu_update_parental_status($item, $exclude = FALSE) { // If plid == 0, there is nothing to update. if ($item['plid']) { // Check if at least one visible child exists in the table. Index: modules/menu/menu.test =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.test,v retrieving revision 1.22 diff -u -9 -p -r1.22 menu.test --- modules/menu/menu.test 28 Sep 2009 22:14:30 -0000 1.22 +++ modules/menu/menu.test 29 Sep 2009 19:33:55 -0000 @@ -156,47 +156,68 @@ class MenuTestCase extends DrupalWebTest /** * Test menu functionality using navigation menu. * */ function doMenuTests($menu_name = 'navigation') { // Add nodes to use as links for menu links. $node1 = $this->drupalCreateNode(array('type' => 'article')); $node2 = $this->drupalCreateNode(array('type' => 'article')); + $node3 = $this->drupalCreateNode(array('type' => 'article')); + $node4 = $this->drupalCreateNode(array('type' => 'article')); + $node5 = $this->drupalCreateNode(array('type' => 'article')); // Add menu links. $item1 = $this->addMenuLink(0, 'node/' . $node1->nid, $menu_name); $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->nid, $menu_name); + $item3 = $this->addMenuLink($item2['mlid'], 'node/' . $node3->nid, $menu_name); + $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => 0)); + $this->assertMenuLink($item2['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => 0)); + $this->assertMenuLink($item3['mlid'], array('depth' => 3, 'has_children' => 0, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => $item3['mlid'], 'p4' => 0)); // Verify menu links. $this->verifyMenuLink($item1, $node1); $this->verifyMenuLink($item2, $node2, $item1, $node1); + $this->verifyMenuLink($item3, $node3, $item2, $node2); + + // Add more menu links. + $item4 = $this->addMenuLink(0, 'node/' . $node4->nid, $menu_name); + $item5 = $this->addMenuLink($item4['mlid'], 'node/' . $node5->nid, $menu_name); + $this->assertMenuLink($item4['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => 0)); + $this->assertMenuLink($item5['mlid'], array('depth' => 2, 'has_children' => 0, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => 0)); // Modify menu links. $this->modifyMenuLink($item1); $this->modifyMenuLink($item2); // Toggle menu links. $this->toggleMenuLink($item1); $this->toggleMenuLink($item2); + // Move link and verify that descendants are updated. + $this->moveMenuLink($item2, $item5['mlid'], $menu_name); + $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 0, 'p1' => $item1['mlid'], 'p2' => 0)); + $this->assertMenuLink($item4['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => 0)); + $this->assertMenuLink($item5['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => 0)); + $this->assertMenuLink($item2['mlid'], array('depth' => 3, 'has_children' => 1, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => $item2['mlid'], 'p4' => 0)); + $this->assertMenuLink($item3['mlid'], array('depth' => 4, 'has_children' => 0, 'p1' => $item4['mlid'], 'p2' => $item5['mlid'], 'p3' => $item2['mlid'], 'p4' => $item3['mlid'], 'p5' => 0)); + // Enable a link via the overview form. $this->disableMenuLink($item1); $edit = array(); // Note in the UI the 'mlid:x[hidden]' form element maps to enabled, or // NOT hidden. $edit['mlid:' . $item1['mlid'] . '[hidden]'] = TRUE; $this->drupalPost('admin/structure/menu/manage/' . $item1['menu_name'], $edit, t('Save configuration')); // Verify in the database. - $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item1['mlid']))->fetchField(); - $this->assertEqual($hidden, 0, t('Link is not hidden in the database table when enabled via the overview form')); + $this->assertMenuLink($item1['mlid'], array('hidden' => 0)); // Save menu links for later tests. $this->items[] = $item1; $this->items[] = $item2; } /** * Add a menu link using the menu module UI. * @@ -222,34 +243,21 @@ class MenuTestCase extends DrupalWebTest ); // Add menu link. $this->drupalPost("admin/structure/menu/manage/$menu_name/add", $edit, t('Save')); $this->assertResponse(200); // Unlike most other modules, there is no confirmation message displayed. $this->assertText($title, 'Menu link was added'); - // Retrieve menu link. - $item = db_query("SELECT * FROM {menu_links} WHERE link_title = :title", array(':title' => $title))->fetchAssoc(); - - // Check the structure in the DB of the two menu links. - // In general, if $n = $item['depth'] then $item['p'. $n] == $item['mlid'] and $item['p' . ($n - 1)] == $item['plid'] (unless depth == 0). - // All $item['p' . $n] for $n > depth must be 0. - // We know link1 is at the top level, so $item1['deptj'] == 1 and $item1['plid'] == 0. - // We know that the parent of link2 is link1, so $item2['plid'] == $item1['mlid']. - // Both menu links were created in the navigation menu. - $this->assertTrue($item['menu_name'] == $menu_name && $item['plid'] == $plid && $item['link_path'] == $link && $item['link_title'] == $title, 'Menu link has correct data'); - if ($plid == 0) { - $this->assertTrue($item['depth'] == 1 && !$item['has_children'] && $item['p1'] == $item['mlid'] && $item['p2'] == 0, 'Menu link has correct data'); - } - else { - $this->assertTrue($item['depth'] == 2 && !$item['has_children'] && $item['p1'] == $plid && $item['p2'] == $item['mlid'], 'Menu link has correct data'); - } + $item = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => $title))->fetchAssoc(); + $this->assertTrue(t('Menu link was found in database.')); + $this->assertMenuLink($item['mlid'], array('menu_name' => $menu_name, 'has_children' => 0)); return $item; } /** * Attempt to add menu link with invalid path or no access permission. * * @param string $menu_name Menu name. */ @@ -294,18 +302,30 @@ class MenuTestCase extends DrupalWebTest $this->assertText($title, 'Menu link was displayed'); // Verify menu link link. $this->clickLink($title); $title = $item_node->title; $this->assertTitle(t("@title | Drupal", array('@title' => $title)), t('Menu link link target was correct')); } /** + * Change the parent of a menu link using the menu module UI. + */ + function moveMenuLink($item, $plid, $menu_name) { + $mlid = $item['mlid']; + + $edit = array(); + $edit['menu[parent]'] = $menu_name . ':' . $plid; + $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save')); + $this->assertResponse(200); + } + + /** * Modify a menu link using the menu module UI. * * @param array &$item Menu link passed by reference. */ function modifyMenuLink(&$item) { $item['link_title'] = $this->randomName(16); $mlid = $item['mlid']; $title = $item['link_title']; @@ -391,36 +411,51 @@ class MenuTestCase extends DrupalWebTest * Menu link. */ function disableMenuLink($item) { $mlid = $item['mlid']; $edit['menu[enabled]'] = FALSE; $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save')); // Unlike most other modules, there is no confirmation message displayed. // Verify in the database. - $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField(); - $this->assertEqual($hidden, 1, t('Link is hidden in the database table')); + $this->assertMenuLink($mlid, array('hidden' => 1)); } /** * Enable a menu link. * * @param $item * Menu link. */ function enableMenuLink($item) { $mlid = $item['mlid']; $edit['menu[enabled]'] = TRUE; $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save')); // Verify in the database. - $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField(); - $this->assertEqual($hidden, 0, t('Link is not hidden in the database table')); + $this->assertMenuLink($mlid, array('hidden' => 0)); + } + + /** + * Fetch the menu item from the database and compare it to the specified + * array. + * + * @param $mlid + * Menu item id. + * @param $item + * Array containing properties to verify. + */ + function assertMenuLink($mlid, array $expected_item) { + // Retrieve menu link. + $item = db_query('SELECT * FROM {menu_links} WHERE mlid = :mlid', array(':mlid' => $mlid))->fetchAssoc(); + foreach ($expected_item as $key => $value) { + $this->assertEqual($item[$key], $value, t('Parameter %key had expected value.', array('%key' => $key))); + } } /** * Get standard menu link. */ private function getStandardMenuLink() { // Retrieve menu link id of the Log out menu link, which will always be on the front page. $mlid = db_query("SELECT mlid FROM {menu_links} WHERE module = 'system' AND router_path = 'user/logout'")->fetchField(); $this->assertTrue($mlid > 0, 'Standard menu link id was found');