Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1210 diff -u -p -r1.1210 common.inc --- includes/common.inc 22 Aug 2010 12:46:21 -0000 1.1210 +++ includes/common.inc 22 Aug 2010 17:25:40 -0000 @@ -230,7 +230,7 @@ function drupal_get_profile() { function drupal_set_breadcrumb($breadcrumb = NULL) { $stored_breadcrumb = &drupal_static(__FUNCTION__); - if (!is_null($breadcrumb)) { + if (isset($breadcrumb)) { $stored_breadcrumb = $breadcrumb; } return $stored_breadcrumb; @@ -242,7 +242,7 @@ function drupal_set_breadcrumb($breadcru function drupal_get_breadcrumb() { $breadcrumb = drupal_set_breadcrumb(); - if (is_null($breadcrumb)) { + if (!isset($breadcrumb)) { $breadcrumb = menu_get_active_breadcrumb(); } Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.406 diff -u -p -r1.406 menu.inc --- includes/menu.inc 22 Aug 2010 14:57:44 -0000 1.406 +++ includes/menu.inc 22 Aug 2010 18:06:42 -0000 @@ -141,9 +141,9 @@ define('MENU_NORMAL_ITEM', MENU_VISIBLE_ * Menu type -- A hidden, internal callback, typically used for API calls. * * Callbacks simply register a path so that the correct function is fired - * when the URL is accessed. They are not shown in the menu. + * when the URL is accessed. They do not appear in menus or breadcrumbs. */ -define('MENU_CALLBACK', MENU_VISIBLE_IN_BREADCRUMB); +define('MENU_CALLBACK', 0); /** * Menu type -- A normal menu item, hidden until enabled by an administrator. @@ -728,12 +728,26 @@ function _menu_translate(&$router_item, // Generate the link path for the page request or local tasks. $link_map = explode('/', $router_item['path']); + if (isset($router_item['tab_root'])) { + $tab_root_map = explode('/', $router_item['tab_root']); + } + if (isset($router_item['tab_parent'])) { + $tab_parent_map = explode('/', $router_item['tab_parent']); + } for ($i = 0; $i < $router_item['number_parts']; $i++) { if ($link_map[$i] == '%') { $link_map[$i] = $path_map[$i]; } + if (isset($tab_root_map[$i]) && $tab_root_map[$i] == '%') { + $tab_root_map[$i] = $path_map[$i]; + } + if (isset($tab_parent_map[$i]) && $tab_parent_map[$i] == '%') { + $tab_parent_map[$i] = $path_map[$i]; + } } $router_item['href'] = implode('/', $link_map); + $router_item['tab_root_href'] = implode('/', $tab_root_map); + $router_item['tab_parent_href'] = implode('/', $tab_parent_map); $router_item['options'] = array(); _menu_check_access($router_item, $map); @@ -799,13 +813,34 @@ function _menu_link_translate(&$item) { $item['localized_options'] = $item['options']; } else { + // Complete the path of the menu link with elements from the current path, + // if it contains dynamic placeholders (%). $map = explode('/', $item['link_path']); - if (!empty($item['to_arg_functions'])) { - _menu_link_map_translate($map, $item['to_arg_functions']); + if (strpos($item['link_path'], '%') !== FALSE) { + // Invoke registered to_arg callbacks. + if (!empty($item['to_arg_functions'])) { + _menu_link_map_translate($map, $item['to_arg_functions']); + } + // Or try to derive the path argument map from the current router item, + // if this $item's path is within the router item's path. This means that + // if we are on the current path 'foo/%/bar/%/baz', then menu_get_item() + // will have translated the menu router item for the current path, and we + // can take over the argument map for a link like 'foo/%/bar'. This + // inheritance is primarily used for breadcrumb links. + elseif ($current_path = menu_get_item()) { + if (strpos($current_path['path'], $item['link_path']) === 0) { + $count = count($map); + $map = array_slice($current_path['original_map'], 0, $count); + $item['original_map'] = $map; + if (isset($current_path['map'])) { + $item['map'] = array_slice($current_path['map'], 0, $count); + } + } + } } $item['href'] = implode('/', $map); - // Note - skip callbacks without real values for their arguments. + // Skip links containing untranslated arguments. if (strpos($item['href'], '%') !== FALSE) { $item['access'] = FALSE; return FALSE; @@ -1081,50 +1116,26 @@ function menu_tree_page_data($menu_name, // If the item for the current page is accessible, build the tree // parameters accordingly. if ($item['access']) { - // Check whether a menu link exists that corresponds to the current path. - $args[] = $item['href']; - if (drupal_is_front_page()) { - $args[] = ''; - } - $active_link = db_select('menu_links') - ->fields('menu_links', array( - 'p1', - 'p2', - 'p3', - 'p4', - 'p5', - 'p6', - 'p7', - 'p8', - )) - ->condition('menu_name', $menu_name) - ->condition('link_path', $args, 'IN') - ->execute()->fetchAssoc(); - - if (empty($active_link)) { - // If no link exists, we may be on a local task that's not in the links. - // TODO: Handle the case like a local task on a specific node in the menu. - $active_link = db_select('menu_links') - ->fields('menu_links', array( - 'p1', - 'p2', - 'p3', - 'p4', - 'p5', - 'p6', - 'p7', - 'p8', - )) - ->condition('menu_name', $menu_name) - ->condition('link_path', $item['tab_root']) - ->execute()->fetchAssoc(); - } + // Parent mlids. + $active_link = array(); + + // Find a menu link corresponding to the current path. + $top_link = menu_link_get_preferred(); + if ($top_link) { + // Use all the coordinates, except the last one because there + // can be no child beyond the last column. + for ($i = 1; $i < MENU_MAX_DEPTH; $i++) { + if ($top_link['p' . $i]) { + $active_link[] = $top_link['p' . $i]; + } + } + } // We always want all the top-level links with plid == 0. $active_link[] = '0'; // Use array_values() so that the indices are numeric. - $parents = $active_link = array_unique(array_values($active_link)); + $parents = $active_link = array_values(array_unique($active_link)); $expanded = variable_get('menu_expanded', array()); // Check whether the current menu has any links set to be expanded. @@ -1315,7 +1326,6 @@ function menu_tree_collect_node_links(&$ * menu_tree_collect_node_links(). */ function menu_tree_check_access(&$tree, $node_links = array()) { - if ($node_links) { $nids = array_keys($node_links); $select = db_select('node', 'n'); @@ -1331,7 +1341,6 @@ function menu_tree_check_access(&$tree, } } _menu_tree_check_access($tree); - return; } /** @@ -1730,7 +1739,7 @@ function menu_local_tasks($level = 0) { // If this router item points to its parent, start from the parents to // compute tabs and actions. if ($router_item && ($router_item['type'] & MENU_LINKS_TO_PARENT)) { - $router_item = menu_get_item($router_item['tab_parent']); + $router_item = menu_get_item($router_item['tab_parent_href']); } // If we failed to fetch a router item or the current user doesn't have @@ -2134,53 +2143,22 @@ function menu_set_active_trail($new_trai elseif (!isset($trail)) { $trail = array(); $trail[] = array('title' => t('Home'), 'href' => '', 'localized_options' => array(), 'type' => 0); - $item = menu_get_item(); - // Check whether the current item is a local task (displayed as a tab). - if ($item['tab_parent']) { - // The title of a local task is used for the tab, never the page title. - // Thus, replace it with the item corresponding to the root path to get - // the relevant href and title. For example, the menu item corresponding - // to 'admin' is used when on the 'By module' tab at 'admin/by-module'. - $parts = explode('/', $item['tab_root']); - $args = arg(); - // Replace wildcards in the root path using the current path. - foreach ($parts as $index => $part) { - if ($part == '%') { - $parts[$index] = $args[$index]; - } - } - // Retrieve the menu item using the root path after wildcard replacement. - $root_item = menu_get_item(implode('/', $parts)); - if ($root_item && $root_item['access']) { - $item = $root_item; - } + $preferred_link = menu_link_get_preferred(); + + if ($preferred_link) { + $tree = menu_tree_page_data($preferred_link['menu_name']); + list($key, $curr) = each($tree); } - $menu_names = menu_get_active_menu_names(); - $curr = FALSE; - // Determine if the current page is a link in any of the active menus. - if ($menu_names) { - $query = db_select('menu_links', 'ml'); - $query->fields('ml', array('menu_name')); - $query->condition('ml.link_path', $item['href']); - $query->condition('ml.menu_name', $menu_names, 'IN'); - $result = $query->execute(); - $found = array(); - foreach ($result as $menu) { - $found[] = $menu->menu_name; - } - // The $menu_names array is ordered, so take the first one that matches. - $found_menu_names = array_intersect($menu_names, $found); - $name = current($found_menu_names); - if ($name !== FALSE) { - $tree = menu_tree_page_data($name); - list($key, $curr) = each($tree); - } + else { + $preferred_link = menu_get_item(); + $key = NULL; + $curr = NULL; } while ($curr) { // Terminate the loop when we find the current path in the active trail. - if ($curr['link']['href'] == $item['href']) { + if ($curr['link']['href'] == $preferred_link['href']) { $trail[] = $curr['link']; $curr = FALSE; } @@ -2196,14 +2174,94 @@ function menu_set_active_trail($new_trai // Make sure the current page is in the trail (needed for the page title), // but exclude tabs and the front page. $last = count($trail) - 1; - if ($trail[$last]['href'] != $item['href'] && !(bool) ($item['type'] & MENU_IS_LOCAL_TASK) && !drupal_is_front_page()) { - $trail[] = $item; + if ($trail[$last]['href'] != $preferred_link['href'] && ($preferred_link['type'] & MENU_VISIBLE_IN_BREADCRUMB) == MENU_VISIBLE_IN_BREADCRUMB && !drupal_is_front_page()) { + $trail[] = $preferred_link; } } return $trail; } /** + * Lookup the preferred menu link for a given system path. + * + * @param $path + * The path, for example 'node/5'. The function will find the corresponding + * menu link ('node/5' if it exists, or fallback to 'node/%'). + * + * @return + * A fully translated menu link, or NULL if not matching menu link was + * found. The most specific menu link ('node/5' preferred over 'node/%') in + * the most preferred menu (as defined by menu_get_active_menu_names()) is + * returned. + */ +function menu_link_get_preferred($href = NULL) { + $preferred_links = &drupal_static(__FUNCTION__); + + if (!isset($href)) { + $href = $_GET['q']; + } + + if (!isset($preferred_links[$href])) { + $preferred_links[$href] = FALSE; + + // Look for the correct menu link by building a list of candidate paths, + // which are ordered by priority (translated hrefs are preferred over + // untranslated paths). Afterwards, the most relevant path is picked from + // the menus, ordered by menu preference. + $item = menu_get_item($href); + $path_candidates = array(); + // 1. The current item href. + $path_candidates[] = $item['href']; + // 2. The tab root href of the current item (if any). + if ($item['tab_parent'] && ($tab_root = menu_get_item($item['tab_root_href']))) { + $path_candidates[] = $tab_root['href']; + } + // 3. The current item path (with wildcards). + $path_candidates[] = $item['path']; + // 4. The tab root path of the current item (if any). + if (!empty($tab_root)) { + $path_candidates[] = $tab_root['path']; + } + + // Retrieve a list of menu names, ordered by preference. + $menu_names = menu_get_active_menu_names(); + + $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC)); + $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path'); + $query->fields('ml'); + // Weight must be taken from {menu_links}, not {menu_router}. + $query->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), array('weight'))); + $query->condition('ml.link_path', $path_candidates, 'IN'); + $query->condition('ml.menu_name', $menu_names, 'IN'); + + // Sort candidates by link path and menu name. + $candidates = array(); + foreach ($query->execute() as $candidate) { + $candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate; + } + + // Pick the most specific link, in the most preferred menu. + foreach ($path_candidates as $path) { + if (!isset($candidates[$path])) { + continue; + } + foreach ($menu_names as $menu_name) { + if (!isset($candidates[$path][$menu_name])) { + continue; + } + $candidate_item = $candidates[$path][$menu_name]; + $map = explode('/', $href); + _menu_translate($candidate_item, $map); + $preferred_links[$href] = $candidate_item; + break 2; + } + } + } + + return $preferred_links[$href]; +} + +/** * Gets the active trail (path to root menu root) of the current page. * * See menu_set_active_trail() for details of return value. @@ -2224,7 +2282,7 @@ function menu_get_active_breadcrumb() { } $item = menu_get_item(); - if ($item && $item['access']) { + if (!empty($item['access'])) { $active_trail = menu_get_active_trail(); foreach ($active_trail as $parent) { @@ -2320,6 +2378,7 @@ function menu_reset_static_cache() { drupal_static_reset('menu_tree_all_data'); drupal_static_reset('menu_tree_page_data'); drupal_static_reset('menu_load_all'); + drupal_static_reset('menu_link_get_preferred'); } /** Index: modules/dblog/dblog.module =================================================================== RCS file: /cvs/drupal/drupal/modules/dblog/dblog.module,v retrieving revision 1.54 diff -u -p -r1.54 dblog.module --- modules/dblog/dblog.module 30 Jul 2010 02:47:27 -0000 1.54 +++ modules/dblog/dblog.module 22 Aug 2010 17:25:40 -0000 @@ -67,7 +67,6 @@ function dblog_menu() { 'page callback' => 'dblog_event', 'page arguments' => array(3), 'access arguments' => array('access site reports'), - 'type' => MENU_CALLBACK, 'file' => 'dblog.admin.inc', ); Index: modules/field/modules/options/options.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/options/options.test,v retrieving revision 1.16 diff -u -p -r1.16 options.test --- modules/field/modules/options/options.test 17 Aug 2010 21:48:39 -0000 1.16 +++ modules/field/modules/options/options.test 22 Aug 2010 17:25:40 -0000 @@ -77,7 +77,7 @@ class OptionsWidgetsTestCase extends Fie field_test_entity_save($entity); // With no field data, no buttons are checked. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoFieldChecked("edit-card-1-$langcode-0"); $this->assertNoFieldChecked("edit-card-1-$langcode-1"); $this->assertNoFieldChecked("edit-card-1-$langcode-2"); @@ -89,7 +89,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0)); // Check that the selected button is checked. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFieldChecked("edit-card-1-$langcode-0"); $this->assertNoFieldChecked("edit-card-1-$langcode-1"); $this->assertNoFieldChecked("edit-card-1-$langcode-2"); @@ -104,7 +104,7 @@ class OptionsWidgetsTestCase extends Fie field_update_field($this->card_1); $instance['required'] = TRUE; field_update_instance($instance); - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFieldChecked("edit-card-1-$langcode-99"); } @@ -134,7 +134,7 @@ class OptionsWidgetsTestCase extends Fie field_test_entity_save($entity); // Display form: with no field data, nothing is checked. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoFieldChecked("edit-card-2-$langcode-0"); $this->assertNoFieldChecked("edit-card-2-$langcode-1"); $this->assertNoFieldChecked("edit-card-2-$langcode-2"); @@ -150,7 +150,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0, 2)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFieldChecked("edit-card-2-$langcode-0"); $this->assertNoFieldChecked("edit-card-2-$langcode-1"); $this->assertFieldChecked("edit-card-2-$langcode-2"); @@ -165,7 +165,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFieldChecked("edit-card-2-$langcode-0"); $this->assertNoFieldChecked("edit-card-2-$langcode-1"); $this->assertNoFieldChecked("edit-card-2-$langcode-2"); @@ -194,7 +194,7 @@ class OptionsWidgetsTestCase extends Fie field_update_field($this->card_2); $instance['required'] = TRUE; field_update_instance($instance); - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFieldChecked("edit-card-2-$langcode-99"); } @@ -222,7 +222,7 @@ class OptionsWidgetsTestCase extends Fie field_test_entity_save($entity); // Display form. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); // A required field without any value has a "none" option. $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), t('A required select list has a "Select a value" choice.')); @@ -244,7 +244,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); // A required field with a value has no 'none' option. $this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), t('A required select list with an actual value has no "none" choice.')); $this->assertOptionSelected("edit-card-1-$langcode", 0); @@ -256,12 +256,12 @@ class OptionsWidgetsTestCase extends Fie field_update_instance($instance); // Display form. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); // A non-required field has a 'none' option. $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), t('A non-required select list has a "None" choice.')); // Submit form: Unselect the option. $edit = array("card_1[$langcode]" => '_none'); - $this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $entity->ftid .'/edit', $edit, t('Save')); $this->assertFieldValues($entity_init, 'card_1', $langcode, array()); // Test optgroups. @@ -271,7 +271,7 @@ class OptionsWidgetsTestCase extends Fie field_update_field($this->card_1); // Display form: with no field data, nothing is selected - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoOptionSelected("edit-card-1-$langcode", 0); $this->assertNoOptionSelected("edit-card-1-$langcode", 1); $this->assertNoOptionSelected("edit-card-1-$langcode", 2); @@ -284,14 +284,14 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertOptionSelected("edit-card-1-$langcode", 0); $this->assertNoOptionSelected("edit-card-1-$langcode", 1); $this->assertNoOptionSelected("edit-card-1-$langcode", 2); // Submit form: Unselect the option. $edit = array("card_1[$langcode]" => '_none'); - $this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $entity->ftid .'/edit', $edit, t('Save')); $this->assertFieldValues($entity_init, 'card_1', $langcode, array()); } @@ -318,7 +318,7 @@ class OptionsWidgetsTestCase extends Fie field_test_entity_save($entity); // Display form: with no field data, nothing is selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoOptionSelected("edit-card-2-$langcode", 0); $this->assertNoOptionSelected("edit-card-2-$langcode", 1); $this->assertNoOptionSelected("edit-card-2-$langcode", 2); @@ -330,7 +330,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0, 2)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertOptionSelected("edit-card-2-$langcode", 0); $this->assertNoOptionSelected("edit-card-2-$langcode", 1); $this->assertOptionSelected("edit-card-2-$langcode", 2); @@ -341,7 +341,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertOptionSelected("edit-card-2-$langcode", 0); $this->assertNoOptionSelected("edit-card-2-$langcode", 1); $this->assertNoOptionSelected("edit-card-2-$langcode", 2); @@ -361,18 +361,18 @@ class OptionsWidgetsTestCase extends Fie // Check that the 'none' option has no efect if actual options are selected // as well. $edit = array("card_2[$langcode][]" => array('_none' => '_none', 0 => 0)); - $this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $entity->ftid .'/edit', $edit, t('Save')); $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0)); // Check that selecting the 'none' option empties the field. $edit = array("card_2[$langcode][]" => array('_none' => '_none')); - $this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $entity->ftid .'/edit', $edit, t('Save')); $this->assertFieldValues($entity_init, 'card_2', $langcode, array()); // A required select list does not have an empty key. $instance['required'] = TRUE; field_update_instance($instance); - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2-' . $langcode)), t('A required select list does not have an empty key.')); // We do not have to test that a required select list with one option is @@ -388,7 +388,7 @@ class OptionsWidgetsTestCase extends Fie field_update_instance($instance); // Display form: with no field data, nothing is selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoOptionSelected("edit-card-2-$langcode", 0); $this->assertNoOptionSelected("edit-card-2-$langcode", 1); $this->assertNoOptionSelected("edit-card-2-$langcode", 2); @@ -401,14 +401,14 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertOptionSelected("edit-card-2-$langcode", 0); $this->assertNoOptionSelected("edit-card-2-$langcode", 1); $this->assertNoOptionSelected("edit-card-2-$langcode", 2); // Submit form: Unselect the option. $edit = array("card_2[$langcode][]" => array('_none' => '_none')); - $this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $entity->ftid .'/edit', $edit, t('Save')); $this->assertFieldValues($entity_init, 'card_2', $langcode, array()); } @@ -435,7 +435,7 @@ class OptionsWidgetsTestCase extends Fie field_test_entity_save($entity); // Display form: with no field data, option is unchecked. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoFieldChecked("edit-bool-$langcode"); $this->assertRaw('Some dangerous & unescaped markup', t('Option text was properly filtered.')); @@ -445,7 +445,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'bool', $langcode, array(0)); // Display form: check that the right options are selected. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertFieldChecked("edit-bool-$langcode"); // Submit form: uncheck the option. @@ -454,7 +454,7 @@ class OptionsWidgetsTestCase extends Fie $this->assertFieldValues($entity_init, 'bool', $langcode, array(1)); // Display form: with 'off' value, option is unchecked. - $this->drupalGet('test-entity/' . $entity->ftid .'/edit'); + $this->drupalGet('test-entity/manage/' . $entity->ftid .'/edit'); $this->assertNoFieldChecked("edit-bool-$langcode"); } } Index: modules/field/modules/text/text.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.test,v retrieving revision 1.27 diff -u -p -r1.27 text.test --- modules/field/modules/text/text.test 17 Aug 2010 18:25:31 -0000 1.27 +++ modules/field/modules/text/text.test 22 Aug 2010 17:25:40 -0000 @@ -115,7 +115,7 @@ class TextFieldTestCase extends DrupalWe "{$this->field_name}[$langcode][0][value]" => $value, ); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created')); @@ -184,7 +184,7 @@ class TextFieldTestCase extends DrupalWe "{$this->field_name}[$langcode][0][value]" => $value, ); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created')); @@ -210,7 +210,7 @@ class TextFieldTestCase extends DrupalWe // Display edition form. // We should now have a 'text format' selector. - $this->drupalGet('test-entity/' . $id . '/edit'); + $this->drupalGet('test-entity/manage/' . $id . '/edit'); $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed')); $this->assertFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is displayed')); Index: modules/field/tests/field.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/tests/field.test,v retrieving revision 1.39 diff -u -p -r1.39 field.test --- modules/field/tests/field.test 18 Aug 2010 00:44:52 -0000 1.39 +++ modules/field/tests/field.test 22 Aug 2010 17:25:40 -0000 @@ -1259,14 +1259,14 @@ class FieldFormTestCase extends FieldTes $value = mt_rand(1, 127); $edit = array("{$this->field_name}[$langcode][0][value]" => $value); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created'); $entity = field_test_entity_test_load($id); $this->assertEqual($entity->{$this->field_name}[$langcode][0]['value'], $value, 'Field value was saved'); // Display edit form. - $this->drupalGet('test-entity/' . $id . '/edit'); + $this->drupalGet('test-entity/manage/' . $id . '/edit'); $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", $value, 'Widget is displayed with the correct default value'); $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed'); @@ -1281,7 +1281,7 @@ class FieldFormTestCase extends FieldTes // Empty the field. $value = ''; $edit = array("{$this->field_name}[$langcode][0][value]" => $value); - $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save')); $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated'); $entity = field_test_entity_test_load($id); $this->assertIdentical($entity->{$this->field_name}, array(), 'Field was emptied'); @@ -1306,7 +1306,7 @@ class FieldFormTestCase extends FieldTes $value = mt_rand(1, 127); $edit = array("{$this->field_name}[$langcode][0][value]" => $value); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created'); $entity = field_test_entity_test_load($id); @@ -1315,7 +1315,7 @@ class FieldFormTestCase extends FieldTes // Edit with missing required value. $value = ''; $edit = array("{$this->field_name}[$langcode][0][value]" => $value); - $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save')); $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation'); } @@ -1384,7 +1384,7 @@ class FieldFormTestCase extends FieldTes // Submit the form and create the entity. $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created'); $entity = field_test_entity_test_load($id); @@ -1474,7 +1474,7 @@ class FieldFormTestCase extends FieldTes // Create entity with three values. $edit = array("{$this->field_name}[$langcode]" => '1, 2, 3'); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; // Check that the values were saved. @@ -1482,7 +1482,7 @@ class FieldFormTestCase extends FieldTes $this->assertFieldValues($entity_init, $this->field_name, $langcode, array(1, 2, 3)); // Display the form, check that the values are correctly filled in. - $this->drupalGet('test-entity/' . $id . '/edit'); + $this->drupalGet('test-entity/manage/' . $id . '/edit'); $this->assertFieldByName("{$this->field_name}[$langcode]", '1, 2, 3', t('Widget is displayed.')); // Submit the form with more values than the field accepts. @@ -1529,7 +1529,7 @@ class FieldFormTestCase extends FieldTes // Create entity. $edit = array("{$field_name}[$langcode][0][value]" => 1); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; // Check that the default value was saved. @@ -1539,7 +1539,7 @@ class FieldFormTestCase extends FieldTes // Create a new revision. $edit = array("{$field_name}[$langcode][0][value]" => 2, 'revision' => TRUE); - $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save')); // Check that the new revision has the expected values. $entity = field_test_entity_test_load($id); @@ -2721,7 +2721,7 @@ class FieldTranslationsTestCase extends // Create a new revision. $langcode = field_valid_language(NULL); $edit = array("{$field_name}[$langcode][0][value]" => $entity->{$field_name}[$langcode][0]['value'], 'revision' => TRUE); - $this->drupalPost('test-entity/' . $eid . '/edit', $edit, t('Save')); + $this->drupalPost('test-entity/manage/' . $eid . '/edit', $edit, t('Save')); // Check translation revisions. $this->checkTranslationRevisions($eid, $eid, $available_languages); Index: modules/field/tests/field_test.entity.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/tests/field_test.entity.inc,v retrieving revision 1.12 diff -u -p -r1.12 field_test.entity.inc --- modules/field/tests/field_test.entity.inc 17 Jun 2010 13:44:45 -0000 1.12 +++ modules/field/tests/field_test.entity.inc 22 Aug 2010 17:25:40 -0000 @@ -347,7 +347,7 @@ function field_test_entity_form_submit($ drupal_set_message($message); if ($entity->ftid) { - $form_state['redirect'] = 'test-entity/' . $entity->ftid . '/edit'; + $form_state['redirect'] = 'test-entity/manage/' . $entity->ftid . '/edit'; } else { // Error on save. Index: modules/field/tests/field_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/tests/field_test.module,v retrieving revision 1.9 diff -u -p -r1.9 field_test.module --- modules/field/tests/field_test.module 8 Aug 2010 02:18:53 -0000 1.9 +++ modules/field/tests/field_test.module 22 Aug 2010 17:25:40 -0000 @@ -52,10 +52,10 @@ function field_test_menu() { 'type' => MENU_NORMAL_ITEM, ); } - $items['test-entity/%field_test_entity_test/edit'] = array( + $items['test-entity/manage/%field_test_entity_test/edit'] = array( 'title' => 'Edit test entity', 'page callback' => 'field_test_entity_edit', - 'page arguments' => array(1), + 'page arguments' => array(2), 'access arguments' => array('administer field_test content'), 'type' => MENU_NORMAL_ITEM, ); Index: modules/field_ui/field_ui.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.module,v retrieving revision 1.31 diff -u -p -r1.31 field_ui.module --- modules/field_ui/field_ui.module 26 Jun 2010 02:06:53 -0000 1.31 +++ modules/field_ui/field_ui.module 22 Aug 2010 17:25:40 -0000 @@ -114,7 +114,7 @@ function field_ui_menu() { 'title arguments' => array($field_position), 'page callback' => 'drupal_get_form', 'page arguments' => array('field_ui_field_edit_form', $field_position), - 'type' => MENU_LOCAL_TASK, + 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'field_ui.admin.inc', ) + $access; $items["$path/fields/%field_ui_menu/edit"] = array( Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.340 diff -u -p -r1.340 filter.module --- modules/filter/filter.module 22 Aug 2010 12:55:04 -0000 1.340 +++ modules/filter/filter.module 22 Aug 2010 17:25:40 -0000 @@ -114,7 +114,6 @@ function filter_menu() { 'file' => 'filter.admin.inc', ); $items['admin/config/content/formats/%filter_format'] = array( - 'type' => MENU_CALLBACK, 'title callback' => 'filter_admin_format_title', 'title arguments' => array(4), 'page callback' => 'filter_admin_format_page', @@ -128,7 +127,6 @@ function filter_menu() { 'page arguments' => array('filter_admin_delete', 4), 'access callback' => '_filter_delete_format_access', 'access arguments' => array(4), - 'type' => MENU_CALLBACK, 'file' => 'filter.admin.inc', ); return $items; Index: modules/forum/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v retrieving revision 1.573 diff -u -p -r1.573 forum.module --- modules/forum/forum.module 17 Aug 2010 16:20:07 -0000 1.573 +++ modules/forum/forum.module 22 Aug 2010 17:25:40 -0000 @@ -147,7 +147,6 @@ function forum_menu() { 'page callback' => 'forum_form_main', 'page arguments' => array('container', 5), 'access arguments' => array('administer forums'), - 'type' => MENU_CALLBACK, 'file' => 'forum.admin.inc', ); $items['admin/structure/forum/edit/forum/%taxonomy_term'] = array( @@ -155,7 +154,6 @@ function forum_menu() { 'page callback' => 'forum_form_main', 'page arguments' => array('forum', 5), 'access arguments' => array('administer forums'), - 'type' => MENU_CALLBACK, 'file' => 'forum.admin.inc', ); return $items; Index: modules/forum/forum.test =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.test,v retrieving revision 1.61 diff -u -p -r1.61 forum.test --- modules/forum/forum.test 9 Aug 2010 01:18:46 -0000 1.61 +++ modules/forum/forum.test 22 Aug 2010 17:25:40 -0000 @@ -25,9 +25,25 @@ class ForumTestCase extends DrupalWebTes function setUp() { parent::setUp('taxonomy', 'comment', 'forum'); // Create users. - $this->admin_user = $this->drupalCreateUser(array('administer blocks', 'administer forums', 'administer menu', 'administer taxonomy', 'create forum content')); // 'access administration pages')); - $this->edit_any_topics_user = $this->drupalCreateUser(array('create forum content', 'edit any forum content', 'delete any forum content', 'access administration pages')); - $this->edit_own_topics_user = $this->drupalCreateUser(array('create forum content', 'edit own forum content', 'delete own forum content')); + $this->admin_user = $this->drupalCreateUser(array( + 'access administration pages', + 'administer blocks', + 'administer forums', + 'administer menu', + 'administer taxonomy', + 'create forum content', + )); + $this->edit_any_topics_user = $this->drupalCreateUser(array( + 'access administration pages', + 'create forum content', + 'edit any forum content', + 'delete any forum content', + )); + $this->edit_own_topics_user = $this->drupalCreateUser(array( + 'create forum content', + 'edit own forum content', + 'delete own forum content', + )); $this->web_user = $this->drupalCreateUser(array()); } Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.297 diff -u -p -r1.297 locale.module --- modules/locale/locale.module 5 Aug 2010 08:08:43 -0000 1.297 +++ modules/locale/locale.module 22 Aug 2010 17:25:40 -0000 @@ -150,7 +150,6 @@ function locale_menu() { 'page arguments' => array('locale_languages_edit_form', 5), 'access arguments' => array('administer languages'), 'file' => 'locale.admin.inc', - 'type' => MENU_CALLBACK, ); $items['admin/config/regional/language/delete/%'] = array( 'title' => 'Confirm', @@ -158,7 +157,6 @@ function locale_menu() { 'page arguments' => array('locale_languages_delete_form', 5), 'access arguments' => array('administer languages'), 'file' => 'locale.admin.inc', - 'type' => MENU_CALLBACK, ); // Translation functionality Index: modules/menu/menu.module =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v retrieving revision 1.231 diff -u -p -r1.231 menu.module --- modules/menu/menu.module 1 Aug 2010 23:33:18 -0000 1.231 +++ modules/menu/menu.module 22 Aug 2010 17:25:40 -0000 @@ -702,7 +702,7 @@ function menu_form_node_type_form_alter( $form['menu']['menu_options'] = array( '#type' => 'checkboxes', '#title' => t('Available menus'), - '#default_value' => variable_get('menu_options_' . $type->type, array('main-menu' => 'main-menu')), + '#default_value' => variable_get('menu_options_' . $type->type, array('main-menu')), '#options' => $menu_options, '#description' => t('The menus available to place links in for this content type.'), ); Index: modules/menu/menu.test =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.test,v retrieving revision 1.39 diff -u -p -r1.39 menu.test --- modules/menu/menu.test 22 Aug 2010 14:57:45 -0000 1.39 +++ modules/menu/menu.test 22 Aug 2010 17:58:00 -0000 @@ -112,14 +112,14 @@ class MenuTestCase extends DrupalWebTest // Assert the new menu. $this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit'); - $this->assertText($title, t('Custom menu was added.')); + $this->assertRaw($title, t('Custom menu was added.')); // Edit the menu. $new_title = $this->randomName(16); $menu['title'] = $new_title; menu_save($menu); $this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit'); - $this->assertText($new_title, t('Custom menu was edited.')); + $this->assertRaw($new_title, t('Custom menu was edited.')); } /** @@ -659,3 +659,275 @@ class MenuNodeTestCase extends DrupalWeb $this->assertNoLink($node_title); } } + +/** + * Menu breadcrumbs related tests. + */ +class MenuBreadcrumbTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Breadcrumbs', + 'description' => 'Tests breadcrumbs functionality.', + 'group' => 'Menu', + ); + } + + function setUp() { + parent::setUp(); + $perms = array_keys(module_invoke_all('permission')); + $this->admin_user = $this->drupalCreateUser($perms); + $this->drupalLogin($this->admin_user); + } + + /** + * Tests breadcrumbs on node and administrative paths. + */ + function testBreadCrumbs() { + // Prepare common base breadcrumb elements. + $home = array('' => 'Home'); + $admin = $home + array('admin' => t('Administer')); + $dashboard = $admin; // FIXME + $config = $dashboard + array('admin/config' => t('Configuration')); + $type = 'article'; + + // Verify Taxonomy administration breadcrumbs. + $expected = $dashboard + array( + 'admin/structure' => t('Structure'), + ); + $this->assertBreadcrumb('admin/structure/taxonomy', $expected); + + $expected += array( + 'admin/structure' => t('Structure'), + 'admin/structure/taxonomy' => t('Taxonomy'), + ); + $this->assertBreadcrumb('admin/structure/taxonomy/tags', $expected); + $expected += array( + 'admin/structure/taxonomy/tags' => t('Tags'), + ); + $this->assertBreadcrumb('admin/structure/taxonomy/tags/edit', $expected); + $this->assertBreadcrumb('admin/structure/taxonomy/tags/fields', $expected); + $this->assertBreadcrumb('admin/structure/taxonomy/tags/add', $expected); + + // Verify Node administration breadcrumbs. + $expected = $dashboard + array( + 'admin/structure' => t('Structure'), + 'admin/structure/types' => t('Content types'), + ); + $this->assertBreadcrumb('admin/structure/types/add', $expected); + $this->assertBreadcrumb("admin/structure/types/manage/$type", $expected); + $expected += array( + "admin/structure/types/manage/$type" => t('Article'), + ); + $this->assertBreadcrumb("admin/structure/types/manage/$type/fields", $expected); + $this->assertBreadcrumb("admin/structure/types/manage/$type/display", $expected); + $expected_teaser = $expected + array( + "admin/structure/types/manage/$type/display" => t('Manage display'), + ); + $this->assertBreadcrumb("admin/structure/types/manage/$type/display/teaser", $expected_teaser); + $this->assertBreadcrumb("admin/structure/types/manage/$type/comment/fields", $expected); + $this->assertBreadcrumb("admin/structure/types/manage/$type/comment/display", $expected); + $this->assertBreadcrumb("admin/structure/types/manage/$type/delete", $expected); + $expected += array( + "admin/structure/types/manage/$type/fields" => t('Manage fields'), + ); + $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body", $expected); + $expected += array( + "admin/structure/types/manage/$type/fields/body" => t('Body'), + ); + $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body/widget-type", $expected); + + // Verify Filter text format administration breadcrumbs. + $format = db_query_range("SELECT format, name FROM {filter_format}", 1, 1)->fetch(); + $format_id = $format->format; + $expected = $config + array( + 'admin/config/content' => t('Content authoring'), + ); + $this->assertBreadcrumb('admin/config/content/formats', $expected); + + $expected += array( + 'admin/config/content/formats' => t('Text formats'), + ); + $this->assertBreadcrumb('admin/config/content/formats/add', $expected); + $this->assertBreadcrumb("admin/config/content/formats/$format_id", $expected); + $expected += array( + "admin/config/content/formats/$format_id" => $format->name, + ); + $this->assertBreadcrumb("admin/config/content/formats/$format_id/delete", $expected); + + // Verify node breadcrumbs (without menu link). + $node1 = $this->drupalCreateNode(); + $nid1 = $node1->nid; + $expected = $home; + $this->assertBreadcrumb("node/$nid1", $expected); + $expected += array( + "node/$nid1" => $node1->title, + ); + $this->assertBreadcrumb("node/$nid1/edit", $expected); + + // Verify that node listing page still contains "Home" only. + $expected = array(); + $this->assertBreadcrumb('node', $expected); + + // Verify node breadcrumbs (in menu). + // Do this separately for Main menu and Navigation menu, since only the + // latter is a preferred menu by default. + // @todo Also test all themes? Manually testing led to the suspicion that + // breadcrumbs may differ, possibly due to template.php overrides. + $menus = array('main-menu', 'navigation'); + // Alter node type menu settings. + variable_set("menu_options_$type", $menus); + variable_set("menu_parent_$type", 'navigation:0'); + + foreach ($menus as $menu) { + // Create a parent node in the current menu. + $title = $this->randomName(); + $node2 = $this->drupalCreateNode(array( + 'type' => $type, + 'title' => $title, + 'menu' => array( + 'enabled' => 1, + 'link_title' => 'Parent ' . $title, + 'description' => '', + 'menu_name' => $menu, + 'plid' => 0, + ), + )); + $nid2 = $node2->nid; + + $expected = $home; + $this->assertBreadcrumb("node/$nid2", $expected); + $expected += array( + "node/$nid2" => $node2->menu['link_title'], + ); + $this->assertBreadcrumb("node/$nid2/edit", $expected); + + // Create a child node in the current menu. + $title = $this->randomName(); + $node3 = $this->drupalCreateNode(array( + 'type' => $type, + 'title' => $title, + 'menu' => array( + 'enabled' => 1, + 'link_title' => 'Child ' . $title, + 'description' => '', + 'menu_name' => $menu, + 'plid' => $node2->menu['mlid'], + ), + )); + $nid3 = $node3->nid; + + $this->assertBreadcrumb("node/$nid3", $expected); + $expected += array( + "node/$nid3" => $node3->menu['link_title'], + ); + $this->assertBreadcrumb("node/$nid3/edit", $expected); + + // Verify that node listing page still contains "Home" only. + $expected = array(); + $this->assertBreadcrumb('node', $expected); + + if ($menu == 'navigation') { + $parent = $node2; + $child = $node3; + } + } + + // Create a Navigation menu link for 'node', move the last parent node menu + // link below it, and verify a full breadcrumb for the last child node. + $menu = 'navigation'; + $edit = array( + 'link_title' => 'Root', + 'link_path' => 'node', + ); + $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save')); + $link = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => 'Root'))->fetchAssoc(); + $edit = array( + 'menu[parent]' => $link['menu_name'] . ':' . $link['mlid'], + ); + $this->drupalPost("node/{$parent->nid}/edit", $edit, t('Save')); + $expected = $home + array( + "node" => $link['link_title'], + ); + $this->assertBreadcrumb(NULL, $expected); + $expected += array( + "node/{$parent->nid}" => $parent->menu['link_title'], + ); + $this->assertBreadcrumb("node/{$child->nid}", $expected); + + // @todo Add a taxonomy term + Navigation menu link, verify expected BC. + // @todo Add a Navigation menu link for $this->admin_user, verify expected BC. + + // Create an only slightly privileged user being able to access site reports + // but not administration pages. + $this->web_user = $this->drupalCreateUser(array( + 'access site reports', + )); + $this->drupalLogin($this->web_user); + + // Verify that we can access recent log entries, there is a corresponding + // page title, and that the breadcrumb is empty (because the user is not + // able to access "Administer", so the trail cannot recurse into it). + // @todo Do we really expect no breadcrumb on 403? If there are menus, why + // not a breadcrumb pointing to Home? + $expected = array(); + $this->assertBreadcrumb('admin', $expected); + $this->assertResponse(403); + + $expected = $home; + $this->assertBreadcrumb('admin/reports', $expected); + $this->assertNoResponse(403); + $this->assertTitle(strtr('@title | Drupal', array('@title' => t('Reports')))); + + $this->assertBreadcrumb('admin/reports/dblog', $expected); + $this->assertNoResponse(403); + $this->assertTitle(strtr('@title | Drupal', array('@title' => t('Recent log entries')))); + } + + /** + * Assert that a given path shows certain breadcrumb links. + * + * @param string $goto + * (optional) A system path to pass to DrupalWebTestCase::drupalGet(). + * @param array $links + * An associative array whose keys are expected breadcrumb link paths and + * whose values are expected breadcrumb link texts (sanitized). + */ + protected function assertBreadcrumb($goto, $links) { + if (isset($goto)) { + $this->drupalGet($goto); + } + // Compare paths with actual breadcrumb. + $parts = $this->getParts(); + $pass = TRUE; + foreach ($links as $path => $title) { + $url = url($path); + $part = array_shift($parts); + $pass = ($pass && $part['href'] === $url && $part['text'] === check_plain($title)); + } + // No parts must be left, or an expected "Home" will always pass. + $pass = ($pass && empty($parts)); + + $this->assertTrue($pass, t('Breadcrumb %parts found on @path.', array( + '%parts' => implode(' ยป ', $links), + '@path' => $goto, + ))); + } + + /** + * Returns the breadcrumb contents of the current page in the internal browser. + */ + protected function getParts() { + $parts = array(); + $elements = $this->xpath('//div[@class="breadcrumb"]/a'); + if (!empty($elements)) { + foreach ($elements as $element) { + $parts[] = array( + 'text' => (string) $element, + 'href' => (string) $element['href'], + 'title' => (string) $element['title'], + ); + } + } + return $parts; + } +} Index: modules/node/content_types.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v retrieving revision 1.116 diff -u -p -r1.116 content_types.inc --- modules/node/content_types.inc 8 Aug 2010 13:02:37 -0000 1.116 +++ modules/node/content_types.inc 22 Aug 2010 17:25:40 -0000 @@ -323,6 +323,7 @@ function node_type_form_submit($form, &$ return; } + form_state_values_clean($form_state); $variables = $form_state['values']; // Remove everything that's been saved already - whatever's left is assumed @@ -333,8 +334,6 @@ function node_type_form_submit($form, &$ } } - unset($variables['form_token'], $variables['op'], $variables['submit'], $variables['delete'], $variables['reset'], $variables['form_id'], $variables['form_build_id']); - // Save or reset persistent variable values. foreach ($variables as $key => $value) { $variable_new = $key . '_' . $type->type; Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1291 diff -u -p -r1.1291 node.module --- modules/node/node.module 22 Aug 2010 16:11:12 -0000 1.1291 +++ modules/node/node.module 22 Aug 2010 17:25:40 -0000 @@ -1837,7 +1837,6 @@ function node_menu() { 'title' => 'Delete', 'page arguments' => array('node_type_delete_confirm', 4), 'access arguments' => array('administer content types'), - 'type' => MENU_CALLBACK, 'file' => 'content_types.inc', ); @@ -1846,6 +1845,11 @@ function node_menu() { 'page callback' => 'node_page_default', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, + // The auto-generated menu link for this router item should not be + // considered when generating breadcrumbs on node/%* paths. By assigning + // an empty menu name, menu_link_get_preferred() will skip this item in its + // lookup, since it uses a list of preferred menus as query condition. + 'menu_name' => '', ); $items['node/add'] = array( 'title' => 'Add new content', @@ -1888,7 +1892,6 @@ function node_menu() { 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('view', 1), - 'type' => MENU_CALLBACK, ); $items['node/%node/view'] = array( 'title' => 'View', Index: modules/search/search.test =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.test,v retrieving revision 1.72 diff -u -p -r1.72 search.test --- modules/search/search.test 18 Aug 2010 18:40:50 -0000 1.72 +++ modules/search/search.test 22 Aug 2010 17:25:40 -0000 @@ -236,13 +236,16 @@ class SearchMatchTestCase extends Drupal } } -class SearchBikeShed extends DrupalWebTestCase { +/** + * Tests the bike shed text on no results page, and text on the search page. + */ +class SearchPageText extends DrupalWebTestCase { protected $searching_user; public static function getInfo() { return array( - 'name' => 'Bike shed', - 'description' => 'Tests the bike shed text on the no results page.', + 'name' => 'Search page text', + 'description' => 'Tests the bike shed text on the no results page, and various other text on search pages.', 'group' => 'Search' ); } @@ -251,18 +254,31 @@ class SearchBikeShed extends DrupalWebTe parent::setUp('search'); // Create user. - $this->searching_user = $this->drupalCreateUser(array('search content')); + $this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles')); } - function testFailedSearch() { + /** + * Tests the failed search text, and various other text on the search page. + */ + function testSearchText() { $this->drupalLogin($this->searching_user); $this->drupalGet('search/node'); $this->assertText(t('Enter your keywords')); + $this->assertText(t('Search')); + $title = t('Search') . ' | Drupal'; + $this->assertTitle($title, 'Search page title is correct'); $edit = array(); $edit['keys'] = 'bike shed ' . $this->randomName(); $this->drupalPost('search/node', $edit, t('Search')); $this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), t('Help text is displayed when search returns no results.')); + $this->assertText(t('Search')); + $this->assertTitle($title, 'Search page title is correct'); + + $edit['keys'] = $this->searching_user->name; + $this->drupalPost('search/user', $edit, t('Search')); + $this->assertText(t('Search')); + $this->assertTitle($title, 'Search page title is correct'); } } Index: modules/simpletest/drupal_web_test_case.php =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v retrieving revision 1.226 diff -u -p -r1.226 drupal_web_test_case.php --- modules/simpletest/drupal_web_test_case.php 22 Aug 2010 15:31:18 -0000 1.226 +++ modules/simpletest/drupal_web_test_case.php 22 Aug 2010 17:53:35 -0000 @@ -2977,6 +2977,24 @@ class DrupalWebTestCase extends DrupalTe } /** + * Assert the page do not respond with the specified response code. + * + * @param $code + * Response code. For example 200 is a successful page request. For a list + * of all codes see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. + * @param $message + * Message to display. + * + * @return + * Assertion result. + */ + protected function assertNoResponse($code, $message = '') { + $curl_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); + $match = is_array($code) ? in_array($curl_code, $code) : $curl_code == $code; + return $this->assertFalse($match, $message ? $message : t('HTTP response not expected !code, actual !curl_code', array('!code' => $code, '!curl_code' => $curl_code)), t('Browser')); + } + + /** * Asserts that the most recently sent e-mail message has the given value. * * The field in $name must have the content described in $value. Index: modules/simpletest/simpletest.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v retrieving revision 1.92 diff -u -p -r1.92 simpletest.module --- modules/simpletest/simpletest.module 30 Jul 2010 01:34:38 -0000 1.92 +++ modules/simpletest/simpletest.module 22 Aug 2010 17:25:40 -0000 @@ -56,7 +56,6 @@ function simpletest_menu() { 'page arguments' => array('simpletest_result_form', 5), 'description' => 'View result of tests.', 'access arguments' => array('administer unit tests'), - 'type' => MENU_CALLBACK, 'file' => 'simpletest.pages.inc', ); return $items; Index: modules/simpletest/tests/menu_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/menu_test.module,v retrieving revision 1.15 diff -u -p -r1.15 menu_test.module --- modules/simpletest/tests/menu_test.module 7 Jul 2010 08:05:01 -0000 1.15 +++ modules/simpletest/tests/menu_test.module 22 Aug 2010 17:25:40 -0000 @@ -110,7 +110,6 @@ function menu_test_menu() { 'title' => 'Customize menu', 'page callback' => 'node_page_default', 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, ); $items['menu-test/hidden/menu/manage/%menu/list'] = array( 'title' => 'List links', @@ -135,7 +134,6 @@ function menu_test_menu() { 'title' => 'Delete menu', 'page callback' => 'node_page_default', 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, ); // Hidden tests; two dynamic arguments. Index: modules/statistics/statistics.module =================================================================== RCS file: /cvs/drupal/drupal/modules/statistics/statistics.module,v retrieving revision 1.337 diff -u -p -r1.337 statistics.module --- modules/statistics/statistics.module 10 Jul 2010 01:44:28 -0000 1.337 +++ modules/statistics/statistics.module 22 Aug 2010 17:25:40 -0000 @@ -170,7 +170,6 @@ function statistics_menu() { 'page callback' => 'statistics_access_log', 'page arguments' => array(3), 'access arguments' => array('access statistics'), - 'type' => MENU_CALLBACK, 'file' => 'statistics.admin.inc', ); $items['admin/config/system/statistics'] = array( Index: modules/statistics/statistics.test =================================================================== RCS file: /cvs/drupal/drupal/modules/statistics/statistics.test,v retrieving revision 1.22 diff -u -p -r1.22 statistics.test --- modules/statistics/statistics.test 5 Aug 2010 23:53:39 -0000 1.22 +++ modules/statistics/statistics.test 22 Aug 2010 17:25:40 -0000 @@ -10,7 +10,15 @@ class StatisticsTestCase extends DrupalW parent::setUp('statistics'); // Create user. - $this->blocking_user = $this->drupalCreateUser(array('block IP addresses', 'access statistics', 'administer blocks', 'administer statistics', 'administer users')); + $this->blocking_user = $this->drupalCreateUser(array( + 'access administration pages', + 'access site reports', + 'access statistics', + 'block IP addresses', + 'administer blocks', + 'administer statistics', + 'administer users', + )); $this->drupalLogin($this->blocking_user); // Enable access logging. Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.957 diff -u -p -r1.957 system.module --- modules/system/system.module 22 Aug 2010 13:52:58 -0000 1.957 +++ modules/system/system.module 22 Aug 2010 17:25:40 -0000 @@ -687,7 +687,6 @@ function system_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('system_ip_blocking_delete', 5), 'access arguments' => array('block IP addresses'), - 'type' => MENU_CALLBACK, 'file' => 'system.admin.inc', ); @@ -828,7 +827,6 @@ function system_menu() { $items['admin/config/regional/date-time/types/%/delete'] = array( 'title' => 'Delete date type', 'description' => 'Allow users to delete a configured date type.', - 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('system_delete_date_format_type_form', 5), 'access arguments' => array('administer site configuration'), @@ -856,7 +854,6 @@ function system_menu() { $items['admin/config/regional/date-time/formats/%/edit'] = array( 'title' => 'Edit date format', 'description' => 'Allow users to edit a configured date format.', - 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('system_configure_date_formats_form', 5), 'access arguments' => array('administer site configuration'), @@ -865,7 +862,6 @@ function system_menu() { $items['admin/config/regional/date-time/formats/%/delete'] = array( 'title' => 'Delete date format', 'description' => 'Allow users to delete a configured date format.', - 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('system_date_delete_format_form', 5), 'access arguments' => array('administer site configuration'), @@ -873,7 +869,6 @@ function system_menu() { ); $items['admin/config/regional/date-time/formats/lookup'] = array( 'title' => 'Date and time lookup', - 'type' => MENU_CALLBACK, 'page callback' => 'system_date_time_lookup', 'access arguments' => array('administer site configuration'), 'file' => 'system.admin.inc', Index: modules/system/system.test =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.test,v retrieving revision 1.140 diff -u -p -r1.140 system.test --- modules/system/system.test 22 Aug 2010 14:55:29 -0000 1.140 +++ modules/system/system.test 22 Aug 2010 17:25:40 -0000 @@ -653,8 +653,7 @@ class AccessDeniedTestCase extends Drupa parent::setUp(); // Create an administrative user. - $this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer blocks')); - $this->drupalLogin($this->admin_user); + $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer site configuration', 'administer blocks')); } function testAccessDenied() { @@ -662,6 +661,7 @@ class AccessDeniedTestCase extends Drupa $this->assertText(t('Access denied'), t('Found the default 403 page')); $this->assertResponse(403); + $this->drupalLogin($this->admin_user); $edit = array( 'title' => $this->randomName(10), 'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(100)))), @@ -671,6 +671,7 @@ class AccessDeniedTestCase extends Drupa // Use a custom 403 page. $this->drupalPost('admin/config/system/site-information', array('site_403' => 'node/' . $node->nid), t('Save configuration')); + $this->drupalLogout(); $this->drupalGet('admin'); $this->assertText($node->title, t('Found the custom 403 page')); Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.603 diff -u -p -r1.603 taxonomy.module --- modules/taxonomy/taxonomy.module 22 Aug 2010 15:45:03 -0000 1.603 +++ modules/taxonomy/taxonomy.module 22 Aug 2010 17:25:40 -0000 @@ -285,7 +285,6 @@ function taxonomy_menu() { 'page callback' => 'taxonomy_term_page', 'page arguments' => array(2), 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, 'file' => 'taxonomy.pages.inc', ); $items['taxonomy/term/%taxonomy_term/view'] = array( Index: modules/taxonomy/taxonomy.test =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.test,v retrieving revision 1.90 diff -u -p -r1.90 taxonomy.test --- modules/taxonomy/taxonomy.test 22 Aug 2010 15:45:03 -0000 1.90 +++ modules/taxonomy/taxonomy.test 22 Aug 2010 17:25:40 -0000 @@ -557,7 +557,7 @@ class TaxonomyTermTestCase extends Taxon // the first edit link found on the listing page is to our term. $this->clickLink(t('edit')); - $this->assertText($edit['name'], t('The randomly generated term name is present.')); + $this->assertRaw($edit['name'], t('The randomly generated term name is present.')); $this->assertText($edit['description[value]'], t('The randomly generated term description is present.')); $edit = array( @@ -939,7 +939,7 @@ class TaxonomyTermFieldTestCase extends "{$this->field_name}[$langcode]" => array($term->tid), ); $this->drupalPost(NULL, $edit, t('Save')); - preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created')); Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1194 diff -u -p -r1.1194 user.module --- modules/user/user.module 22 Aug 2010 13:52:59 -0000 1.1194 +++ modules/user/user.module 22 Aug 2010 17:25:40 -0000 @@ -1620,7 +1620,6 @@ function user_menu() { 'page arguments' => array('user_admin_role', 5), 'access callback' => 'user_role_edit_access', 'access arguments' => array(5), - 'type' => MENU_CALLBACK, ); $items['admin/people/permissions/roles/delete/%user_role'] = array( 'title' => 'Delete role', @@ -1628,7 +1627,6 @@ function user_menu() { 'page arguments' => array('user_admin_role_delete_confirm', 5), 'access callback' => 'user_role_edit_access', 'access arguments' => array(5), - 'type' => MENU_CALLBACK, 'file' => 'user.admin.inc', ); @@ -1691,7 +1689,6 @@ function user_menu() { 'page arguments' => array('user_cancel_confirm_form', 1), 'access callback' => 'user_cancel_access', 'access arguments' => array(1), - 'type' => MENU_CALLBACK, 'file' => 'user.pages.inc', );