diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 48d471f47d..cc87015198 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -2837,10 +2837,12 @@ function _drupal_bootstrap_variables() { // url_is_external() requires the variable system to be available. if (isset($_GET['destination']) || isset($_REQUEST['destination'])) { require_once DRUPAL_ROOT . '/includes/common.inc'; - // If the destination is an external URL, remove it. - if (isset($_GET['destination']) && url_is_external($_GET['destination'])) { - unset($_GET['destination']); - unset($_REQUEST['destination']); + if (isset($_GET['destination'])) { + // If the destination is an external URL or a 403/404 page, remove it. + if (url_is_external($_GET['destination']) || in_array($_GET['destination'], array('system/403', 'system/404'))) { + unset($_GET['destination']); + unset($_REQUEST['destination']); + } } // Use the DrupalRequestSanitizer to ensure that the destination's query // parameters are not dangerous. diff --git a/includes/common.inc b/includes/common.inc index 38ea92abea..e6a82388a5 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2781,12 +2781,21 @@ function drupal_deliver_html_page($page_callback_result) { if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { // Standard 404 handler. - drupal_set_title(t('Page not found')); - $return = t('The requested page "@path" could not be found.', array('@path' => request_uri())); + $path = 'system/404'; + menu_set_active_item($path); + $return = menu_execute_active_handler($path, FALSE); } drupal_set_page_content($return); $page = element_info('page'); + // Use the path as key so that custom 404 don't get overriden by old + // cache. + $page['#cache'] = array( + 'keys' => explode('/', $path), + 'granularity' => DRUPAL_CACHE_PER_ROLE, + 'expire' => CACHE_TEMPORARY, + 'bin' => 'cache_page', + ); print drupal_render_page($page); break; @@ -2813,8 +2822,9 @@ function drupal_deliver_html_page($page_callback_result) { if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { // Standard 403 handler. - drupal_set_title(t('Access denied')); - $return = t('You are not authorized to access this page.'); + $path = 'system/403'; + menu_set_active_item($path); + $return = menu_execute_active_handler($path, FALSE); } print drupal_render_page($return); diff --git a/modules/node/node.test b/modules/node/node.test index 91e3b871b5..ed36403bef 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -2163,14 +2163,18 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase { $this->assertTrue($bid, 'Custom block with visibility rule was created.'); // Verify visibility rules. + $this->drupalLogout(); + $this->drupalLogin($this->web_user); $this->drupalGet(''); - $this->assertNoText($custom_block['title'], 'Block was displayed on the front page.'); + $this->assertNoText($custom_block['title'], 'Block was not displayed on the front page.'); $this->drupalGet('node/add/article'); $this->assertText($custom_block['title'], 'Block was displayed on the node/add/article page.'); $this->drupalGet('node/' . $node1->nid); $this->assertText($custom_block['title'], 'Block was displayed on the node/N.'); // Delete the created custom block & verify that it's been deleted. + $this->drupalLogout(); + $this->drupalLogin($this->admin_user); $this->drupalPost('admin/structure/block/manage/block/' . $bid . '/delete', array(), t('Delete')); $bid = db_query("SELECT 1 FROM {block_node_type} WHERE module = 'block' AND delta = :delta", array(':delta' => $bid))->fetchField(); $this->assertFalse($bid, 'Custom block was deleted.'); diff --git a/modules/shortcut/shortcut.module b/modules/shortcut/shortcut.module index 2f6db0ad35..df423bec17 100644 --- a/modules/shortcut/shortcut.module +++ b/modules/shortcut/shortcut.module @@ -647,7 +647,7 @@ function shortcut_preprocess_page(&$variables) { // shortcuts and if the page's actual content is being shown (for example, // we do not want to display it on "access denied" or "page not found" // pages). - if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) { + if (current_path() != 'system/404' && current_path() != 'system/403' && shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) { $link = $_GET['q']; $query_parameters = drupal_get_query_parameters(); if (!empty($query_parameters)) { diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test index 4bbb2688a4..b45a087375 100644 --- a/modules/simpletest/tests/menu.test +++ b/modules/simpletest/tests/menu.test @@ -1523,11 +1523,10 @@ class MenuBreadcrumbTestCase extends MenuWebTestCase { // 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). - $trail = array(); + $trail = $home; $this->assertBreadcrumb('admin', $trail, t('Access denied')); $this->assertResponse(403); - $trail = $home; $this->assertBreadcrumb('admin/reports', $trail, t('Reports')); $this->assertNoResponse(403); diff --git a/modules/statistics/statistics.test b/modules/statistics/statistics.test index 2e283359d5..21ba40c4da 100644 --- a/modules/statistics/statistics.test +++ b/modules/statistics/statistics.test @@ -161,8 +161,9 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase { $this->drupalGet($long_path); $log = db_query('SELECT * FROM {accesslog}')->fetchAll(PDO::FETCH_ASSOC); $this->assertTrue(is_array($log) && count($log) == 8, 'Page request was logged for a path over 255 characters.'); - $this->assertEqual($log[7]['path'], truncate_utf8($long_path, 255)); + $this->assertEqual($log[7]['path'], 'system/404'); } + } /** diff --git a/modules/system/system.module b/modules/system/system.module index ba5da73f7a..4be7e7f413 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -528,6 +528,18 @@ function system_element_info() { * Implements hook_menu(). */ function system_menu() { + $items['system/403'] = array( + 'title' => 'Access denied', + 'page callback' => 'system_access_denied', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + $items['system/404'] = array( + 'title' => 'Page not found', + 'page callback' => 'system_not_found', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); $items['system/files'] = array( 'title' => 'File download', 'page callback' => 'file_download', @@ -2180,6 +2192,26 @@ function system_admin_menu_block($item) { return $content; } +/** + * Menu callback; returns a 403 error message. + * + * Page callback functions wanting to report an "access denied" message should + * not use this function. Instead see drupal_access_denied(). + */ +function system_access_denied() { + return array('#markup' => t('You are not authorized to access this page.')); +} + +/** + * Menu callback; returns a 404 error message. + * + * Page callback functions wanting to report a "not found" message should not + * use this function. Instead see drupal_not_found(). + */ +function system_not_found() { + return array('#markup' => t('The requested page could not be found.')); +} + /** * Checks the existence of the directory specified in $form_element. * diff --git a/modules/system/system.test b/modules/system/system.test index 6655bfa388..c8e6eee122 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -1083,24 +1083,26 @@ class AccessDeniedTestCase extends DrupalWebTestCase { $this->drupalGet('admin'); $this->assertText($node->title, 'Found the custom 403 page'); - // Logout and check that the user login block is shown on custom 403 pages. + // Logout and check that blocks and menu items are shown on custom 403 pages. $this->drupalLogout(); $this->drupalGet('admin'); $this->assertText($node->title, 'Found the custom 403 page'); $this->assertText(t('User login'), 'Blocks are shown on the custom 403 page'); + $this->assertText(t('Main menu'), t('Menu items are shown on the custom 403 page')); // Log back in and remove the custom 403 page. $this->drupalLogin($this->admin_user); $this->drupalPost('admin/config/system/site-information', array('site_403' => ''), t('Save configuration')); - // Logout and check that the user login block is shown on default 403 pages. + // Logout and check that blocks and menu items are shown on default 403 pages. $this->drupalLogout(); $this->drupalGet('admin'); $this->assertText(t('Access denied'), 'Found the default 403 page'); $this->assertResponse(403); $this->assertText(t('User login'), 'Blocks are shown on the default 403 page'); + $this->assertText(t('Main menu'), t('Menu items are shown on the default 403 page')); // Log back in, set the custom 403 page to /user and remove the block $this->drupalLogin($this->admin_user); @@ -1156,9 +1158,15 @@ class PageNotFoundTestCase extends DrupalWebTestCase { } function testPageNotFound() { + // Logout and check that blocks and menu items are shown on default 404 pages. + $this->drupalLogout(); $this->drupalGet($this->randomName(10)); $this->assertText(t('Page not found'), 'Found the default 404 page'); + $this->assertText(t('User login'), t('Blocks are shown on the default 404 page')); + $this->assertText(t('Main menu'), t('Menu items are shown on the default 404 page')); + // Log back in and add a custom 404 page. + $this->drupalLogin($this->admin_user); $edit = array( 'title' => $this->randomName(10), 'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(100)))), @@ -1173,8 +1181,13 @@ class PageNotFoundTestCase extends DrupalWebTestCase { // Use a custom 404 page. $this->drupalPost('admin/config/system/site-information', array('site_404' => 'node/' . $node->nid), t('Save configuration')); + // Logout and check that blocks and menu items are shown on custom 404 pages. + $this->drupalLogout(); + $this->drupalGet($this->randomName(10)); $this->assertText($node->title, 'Found the custom 404 page'); + $this->assertText(t('User login'), t('Blocks are shown on the custom 404 page')); + $this->assertText(t('Main menu'), t('Menu items are shown on the custom 404 page')); } }