Commit 17bdb61f authored by catch's avatar catch

Issue #520106 by JohnAlbin, pillarsdotnet, chx, sun, drifter: Fixed No way to...

Issue #520106 by JohnAlbin, pillarsdotnet, chx, sun, drifter: Fixed No way to dynamically set active trail.
parent 140dbb60
......@@ -1133,6 +1133,45 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
return $tree[$cid];
}
/**
* Set the path for determining the active trail of the specified menu tree.
*
* This path will also affect the breadcrumbs under some circumstances.
* Breadcrumbs are built using the preferred link returned by
* menu_link_get_preferred(). If the preferred link is inside one of the menus
* specified in calls to menu_tree_set_path(), the preferred link will be
* overridden by the corresponding path returned by menu_tree_get_path().
*
* Setting this path does not affect the main content; for that use
* menu_set_active_item() instead.
*
* @param $menu_name
* The name of the affected menu tree.
* @param $path
* The path to use when finding the active trail.
*/
function menu_tree_set_path($menu_name, $path = NULL) {
$paths = &drupal_static(__FUNCTION__);
if (isset($path)) {
$paths[$menu_name] = $path;
}
return isset($paths[$menu_name]) ? $paths[$menu_name] : NULL;
}
/**
* Get the path for determining the active trail of the specified menu tree.
*
* @param $menu_name
* The menu name of the requested tree.
*
* @return
* A string containing the path. If no path has been specified with
* menu_tree_set_path(), NULL is returned.
*/
function menu_tree_get_path($menu_name) {
return menu_tree_set_path($menu_name);
}
/**
* Get the data structure representing a named menu tree, based on the current page.
*
......@@ -1158,8 +1197,10 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE) {
$tree = &drupal_static(__FUNCTION__, array());
// Check if the active trail has been overridden for this menu tree.
$active_path = menu_tree_get_path($menu_name);
// Load the menu item corresponding to the current page.
if ($item = menu_get_item()) {
if ($item = menu_get_item($active_path)) {
if (isset($max_depth)) {
$max_depth = min($max_depth, MENU_MAX_DEPTH);
}
......@@ -1198,8 +1239,9 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
// If the item for the current page is accessible, build the tree
// parameters accordingly.
if ($item['access']) {
// Find a menu link corresponding to the current path.
if ($active_link = menu_link_get_preferred()) {
// Find a menu link corresponding to the current path. If $active_path
// is NULL, let menu_link_get_preferred() determine the path.
if ($active_link = menu_link_get_preferred($active_path)) {
// The active link may only be taken into account to build the
// active trail, if it resides in the requested menu. Otherwise,
// we'd needlessly re-run _menu_build_tree() queries for every menu
......@@ -2213,38 +2255,36 @@ function menu_get_active_menu_names() {
/**
* Set the active path, which determines which page is loaded.
*
* @param $path
* A Drupal path - not a path alias.
*
* Note that this may not have the desired effect unless invoked very early
* in the page load, such as during hook_boot, or unless you call
* menu_execute_active_handler() to generate your page output.
*
* @param $path
* A Drupal path - not a path alias.
*/
function menu_set_active_item($path) {
$_GET['q'] = $path;
}
/**
* Sets or gets the active trail (path to menu tree root) of the current page.
* Sets the active trail (path to menu tree root) of the current page.
*
* Any trail set by this function will only be used for functionality that calls
* menu_get_active_trail(). Drupal core only uses trails set here for
* breadcrumbs and the page title and not for menu trees or page content.
* Additionally, breadcrumbs set by drupal_set_breadcrumb() will override any
* trail set here.
*
* To affect the trail used by menu trees, use menu_tree_set_path(). To affect
* the page content, use menu_set_active_item() instead.
*
* @param $new_trail
* Menu trail to set, or NULL to use previously-set or calculated trail. If
* supplying a trail, use the same format as the return value (see below).
* Menu trail to set; the value is saved in a static variable and can be
* retrieved by menu_get_active_trail(). The format of this array should be
* the same as the return value of menu_get_active_trail().
*
* @return
* Path to menu root of the current page, as an array of menu link items,
* starting with the site's home page. Each link item is an associative array
* with the following components:
* - title: Title of the item.
* - href: Drupal path of the item.
* - localized_options: Options for passing into the l() function.
* - type: A menu type constant, such as MENU_DEFAULT_LOCAL_TASK, or 0 to
* indicate it's not really in the menu (used for the home page item).
* If $new_trail is supplied, the value is saved in a static variable and
* returned. If $new_trail is not supplied, and there is a saved value from
* a previous call, the saved value is returned. If $new_trail is not supplied
* and there is no saved value, the path to the current page is calculated,
* saved as the static value, and returned.
* The active trail. See menu_get_active_trail() for details.
*/
function menu_set_active_trail($new_trail = NULL) {
$trail = &drupal_static(__FUNCTION__);
......@@ -2403,7 +2443,20 @@ function menu_link_get_preferred($path = NULL) {
/**
* Gets the active trail (path to root menu root) of the current page.
*
* See menu_set_active_trail() for details of return value.
* If a trail is supplied to menu_set_active_trail(), that value is returned. If
* a trail is not supplied to menu_set_active_trail(), the path to the current
* page is calculated and returned. The calculated trail is also saved as a
* static value for use by subsequent calls to menu_get_active_trail().
*
* @return
* Path to menu root of the current page, as an array of menu link items,
* starting with the site's home page. Each link item is an associative array
* with the following components:
* - title: Title of the item.
* - href: Drupal path of the item.
* - localized_options: Options for passing into the l() function.
* - type: A menu type constant, such as MENU_DEFAULT_LOCAL_TASK, or 0 to
* indicate it's not really in the menu (used for the home page item).
*/
function menu_get_active_trail() {
return menu_set_active_trail();
......
......@@ -5,6 +5,122 @@
* Provides SimpleTests for menu.inc.
*/
class MenuWebTestCase extends DrupalWebTestCase {
function setUp() {
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
parent::setUp($modules);
}
/**
* Assert that a given path shows certain breadcrumb links.
*
* @param string $goto
* (optional) A system path to pass to DrupalWebTestCase::drupalGet().
* @param array $trail
* An associative array whose keys are expected breadcrumb link paths and
* whose values are expected breadcrumb link texts (not sanitized).
* @param string $page_title
* (optional) A page title to additionally assert via
* DrupalWebTestCase::assertTitle(). Without site name suffix.
* @param array $tree
* (optional) An associative array whose keys are link paths and whose
* values are link titles (not sanitized) of an expected active trail in a
* menu tree output on the page.
* @param $last_active
* (optional) Whether the last link in $tree is expected to be active (TRUE)
* or just to be in the active trail (FALSE).
*/
protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, array $tree = array(), $last_active = TRUE) {
if (isset($goto)) {
$this->drupalGet($goto);
}
// Compare paths with actual breadcrumb.
$parts = $this->getParts();
$pass = TRUE;
foreach ($trail 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(' » ', $trail),
'@path' => $this->getUrl(),
)));
// Additionally assert page title, if given.
if (isset($page_title)) {
$this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title)));
}
// Additionally assert active trail in a menu tree output, if given.
if ($tree) {
end($tree);
$active_link_path = key($tree);
$active_link_title = array_pop($tree);
$xpath = '';
if ($tree) {
$i = 0;
foreach ($tree as $link_path => $link_title) {
$part_xpath = (!$i ? '//' : '/following-sibling::ul/descendant::');
$part_xpath .= 'li[contains(@class, :class)]/a[contains(@href, :href) and contains(text(), :title)]';
$part_args = array(
':class' => 'active-trail',
':href' => url($link_path),
':title' => $link_title,
);
$xpath .= $this->buildXPathQuery($part_xpath, $part_args);
$i++;
}
$elements = $this->xpath($xpath);
$this->assertTrue(!empty($elements), t('Active trail to current page was found in menu tree.'));
// Append prefix for active link asserted below.
$xpath .= '/following-sibling::ul/descendant::';
}
else {
$xpath .= '//';
}
$xpath_last_active = ($last_active ? 'and contains(@class, :class-active)' : '');
$xpath .= 'li[contains(@class, :class-trail)]/a[contains(@href, :href) ' . $xpath_last_active . 'and contains(text(), :title)]';
$args = array(
':class-trail' => 'active-trail',
':class-active' => 'active',
':href' => url($active_link_path),
':title' => $active_link_title,
);
$elements = $this->xpath($xpath, $args);
$this->assertTrue(!empty($elements), t('Active link %title was found in menu tree, including active trail links %tree.', array(
'%title' => $active_link_title,
'%tree' => implode(' » ', $tree),
)));
}
}
/**
* 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;
}
}
class MenuRouterTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
......@@ -872,7 +988,7 @@ class MenuTreeOutputTestCase extends DrupalWebTestCase {
'group' => 'Menu',
);
}
function setUp() {
parent::setUp();
}
......@@ -900,7 +1016,7 @@ class MenuTreeOutputTestCase extends DrupalWebTestCase {
/**
* Menu breadcrumbs related tests.
*/
class MenuBreadcrumbTestCase extends DrupalWebTestCase {
class MenuBreadcrumbTestCase extends MenuWebTestCase {
public static function getInfo() {
return array(
'name' => 'Breadcrumbs',
......@@ -910,7 +1026,12 @@ class MenuBreadcrumbTestCase extends DrupalWebTestCase {
}
function setUp() {
parent::setUp(array('menu_test'));
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
$modules[] = 'menu_test';
parent::setUp($modules);
$perms = array_keys(module_invoke_all('permission'));
$this->admin_user = $this->drupalCreateUser($perms);
$this->drupalLogin($this->admin_user);
......@@ -1385,110 +1506,117 @@ class MenuBreadcrumbTestCase extends DrupalWebTestCase {
$this->assertBreadcrumb('admin/reports/dblog', $trail, t('Recent log messages'));
$this->assertNoResponse(403);
}
}
/**
* Assert that a given path shows certain breadcrumb links.
*
* @param string $goto
* (optional) A system path to pass to DrupalWebTestCase::drupalGet().
* @param array $trail
* An associative array whose keys are expected breadcrumb link paths and
* whose values are expected breadcrumb link texts (not sanitized).
* @param string $page_title
* (optional) A page title to additionally assert via
* DrupalWebTestCase::assertTitle(). Without site name suffix.
* @param array $tree
* (optional) An associative array whose keys are link paths and whose
* values are link titles (not sanitized) of an expected active trail in a
* menu tree output on the page.
* @param $last_active
* (optional) Whether the last link in $tree is expected to be active (TRUE)
* or just to be in the active trail (FALSE).
*/
protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, array $tree = array(), $last_active = TRUE) {
if (isset($goto)) {
$this->drupalGet($goto);
}
// Compare paths with actual breadcrumb.
$parts = $this->getParts();
$pass = TRUE;
foreach ($trail 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(' » ', $trail),
'@path' => $this->getUrl(),
)));
/**
* Tests active menu trails.
*/
class MenuTrailTestCase extends MenuWebTestCase {
public static function getInfo() {
return array(
'name' => 'Active trail',
'description' => 'Tests active menu trails and alteration functionality.',
'group' => 'Menu',
);
}
// Additionally assert page title, if given.
if (isset($page_title)) {
$this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title)));
function setUp() {
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
$modules[] = 'menu_test';
parent::setUp($modules);
$this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'access administration pages'));
$this->drupalLogin($this->admin_user);
// Additionally assert active trail in a menu tree output, if given.
if ($tree) {
end($tree);
$active_link_path = key($tree);
$active_link_title = array_pop($tree);
$xpath = '';
if ($tree) {
$i = 0;
foreach ($tree as $link_path => $link_title) {
$part_xpath = (!$i ? '//' : '/following-sibling::ul/descendant::');
$part_xpath .= 'li[contains(@class, :class)]/a[contains(@href, :href) and contains(text(), :title)]';
$part_args = array(
':class' => 'active-trail',
':href' => url($link_path),
':title' => $link_title,
);
$xpath .= $this->buildXPathQuery($part_xpath, $part_args);
$i++;
}
$elements = $this->xpath($xpath);
$this->assertTrue(!empty($elements), t('Active trail to current page was found in menu tree.'));
// This test puts menu links in the Navigation menu and then tests for
// their presence on the page, so we need to ensure that the Navigation
// block will be displayed in all active themes.
db_update('block')
->fields(array(
// Use a region that is valid for all themes.
'region' => 'content',
'status' => 1,
))
->condition('module', 'system')
->condition('delta', 'navigation')
->execute();
// Append prefix for active link asserted below.
$xpath .= '/following-sibling::ul/descendant::';
}
else {
$xpath .= '//';
}
$xpath_last_active = ($last_active ? 'and contains(@class, :class-active)' : '');
$xpath .= 'li[contains(@class, :class-trail)]/a[contains(@href, :href) ' . $xpath_last_active . 'and contains(text(), :title)]';
$args = array(
':class-trail' => 'active-trail',
':class-active' => 'active',
':href' => url($active_link_path),
':title' => $active_link_title,
);
$elements = $this->xpath($xpath, $args);
$this->assertTrue(!empty($elements), t('Active link %title was found in menu tree, including active trail links %tree.', array(
'%title' => $active_link_title,
'%tree' => implode(' » ', $tree),
)));
}
// This test puts menu links in the Management menu and then tests for
// their presence on the page, so we need to ensure that the Management
// block will be displayed in all active themes.
db_update('block')
->fields(array(
// Use a region that is valid for all themes.
'region' => 'content',
'status' => 1,
))
->condition('module', 'system')
->condition('delta', 'management')
->execute();
}
/**
* Returns the breadcrumb contents of the current page in the internal browser.
* Tests active trails are properly affected by menu_tree_set_path().
*/
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;
function testMenuTreeSetPath() {
$home = array('<front>' => 'Home');
$config_tree = array(
'admin' => t('Administration'),
'admin/config' => t('Configuration'),
);
$config = $home + $config_tree;
// The menu_test_menu_tree_set_path system variable controls whether or not
// the menu_test_menu_trail_callback() callback (used by all paths in these
// tests) issues an overriding call to menu_trail_set_path().
$test_menu_path = array(
'menu_name' => 'management',
'path' => 'admin/config/system/site-information',
);
$breadcrumb = $home + array(
'menu-test' => t('Menu test root'),
);
$tree = array(
'menu-test' => t('Menu test root'),
'menu-test/menu-trail' => t('Menu trail - Case 1'),
);
// Test the tree generation for the Navigation menu.
variable_del('menu_test_menu_tree_set_path');
$this->assertBreadcrumb('menu-test/menu-trail', $breadcrumb, t('Menu trail - Case 1'), $tree);
// Override the active trail for the Management tree; it should not affect
// the Navigation tree.
variable_set('menu_test_menu_tree_set_path', $test_menu_path);
$this->assertBreadcrumb('menu-test/menu-trail', $breadcrumb, t('Menu trail - Case 1'), $tree);
$breadcrumb = $config + array(
'admin/config/development' => t('Development'),
);
$tree = $config_tree + array(
'admin/config/development' => t('Development'),
'admin/config/development/menu-trail' => t('Menu trail - Case 2'),
);
$override_breadcrumb = $config + array(
'admin/config/system' => t('System'),
'admin/config/system/site-information' => t('Site information'),
);
$override_tree = $config_tree + array(
'admin/config/system' => t('System'),
'admin/config/system/site-information' => t('Site information'),
);
// Test the tree generation for the Management menu.
variable_del('menu_test_menu_tree_set_path');
$this->assertBreadcrumb('admin/config/development/menu-trail', $breadcrumb, t('Menu trail - Case 2'), $tree);
// Override the active trail for the Management tree; it should affect the
// breadcrumbs and Management tree.
variable_set('menu_test_menu_tree_set_path', $test_menu_path);
$this->assertBreadcrumb('admin/config/development/menu-trail', $override_breadcrumb, t('Menu trail - Case 2'), $override_tree);
}
}
......@@ -217,6 +217,20 @@ function menu_test_menu() {
'type' => MENU_LOCAL_TASK,
) + $base;
// Menu trail tests.
// @see MenuTrailTestCase
$items['menu-test/menu-trail'] = array(
'title' => 'Menu trail - Case 1',
'page callback' => 'menu_test_menu_trail_callback',
'access arguments' => array('access content'),
);
$items['admin/config/development/menu-trail'] = array(
'title' => 'Menu trail - Case 2',
'description' => 'Tests menu_tree_set_path()',
'page callback' => 'menu_test_menu_trail_callback',
'access arguments' => array('access administration pages'),
);
// File inheritance tests. This menu item should inherit the page callback
// system_admin_menu_block_page() and therefore render its children as links
// on the page.
......@@ -344,6 +358,17 @@ function menu_test_callback() {
return 'This is menu_test_callback().';
}
/**
* Callback that test menu_test_menu_tree_set_path().
*/
function menu_test_menu_trail_callback() {
$menu_path = variable_get('menu_test_menu_tree_set_path', array());
if (!empty($menu_path)) {
menu_tree_set_path($menu_path['menu_name'], $menu_path['path']);
}
return 'This is menu_test_menu_trail_callback().';
}
/**
* Page callback to use when testing the theme callback functionality.
*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment