Index: .htaccess =================================================================== RCS file: /cvs/drupal/drupal/.htaccess,v retrieving revision 1.104 diff -u -9 -p -r1.104 .htaccess --- .htaccess 16 Aug 2009 12:10:36 -0000 1.104 +++ .htaccess 2 Jan 2010 23:47:30 -0000 @@ -82,17 +82,18 @@ DirectoryIndex index.php index.html inde # VirtualDocumentRoot and the rewrite rules are not working properly. # For example if your site is at http://example.com/drupal uncomment and # modify the following line: # RewriteBase /drupal # # If your site is running in a VirtualDocumentRoot at http://example.com/, # uncomment the following line: # RewriteBase / - # Rewrite URLs of the form 'x' to the form 'index.php?q=x'. + # Pass all requests not referring directly to files in the filesystem to + # index.php. Clean URLs are handled in drupal_environment_initialize(). RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !=/favicon.ico - RewriteRule ^(.*)$ index.php?q=$1 [L,QSA] + RewriteRule ^ index.php [L] # $Id: .htaccess,v 1.104 2009/08/16 12:10:36 dries Exp $ Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.338 diff -u -9 -p -r1.338 bootstrap.inc --- includes/bootstrap.inc 30 Dec 2009 08:16:55 -0000 1.338 +++ includes/bootstrap.inc 2 Jan 2010 23:47:31 -0000 @@ -486,18 +486,25 @@ function drupal_environment_initialize() exit; } } else { // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is // defined for E_ALL compliance. $_SERVER['HTTP_HOST'] = ''; } + // When clean URLs are enabled, emulate ?q=foo/bar using REQUEST_URI. It is + // not possible to append the query string using mod_rewrite without the B + // flag (this was added in Apache 2.2.8), because mod_rewrite unescapes the + // path before passing it on to PHP. This is a problem when the path contains + // e.g. "&" or "%" that have special meanings in URLs and must be encoded. + $_GET['q'] = request_path(); + // Enforce E_ALL, but allow users to set levels not part of E_ALL. error_reporting(E_ALL | error_reporting()); // Override PHP settings required for Drupal to work properly. // sites/default/default.settings.php contains more runtime settings. // The .htaccess file contains settings that cannot be changed at runtime. // Prevent PHP from generating HTML error messages. ini_set('html_errors', 0); @@ -553,20 +560,20 @@ function drupal_settings_initialize() { else { // Create base URL $http_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST']; $base_url = $base_root; // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not // be modified by a visitor. - if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) { - $base_path = "/$dir"; + if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')) { + $base_path = $dir; $base_url .= $base_path; $base_path .= '/'; } else { $base_path = '/'; } } $is_https = $http_protocol == 'https'; $base_secure_url = str_replace('http://', 'https://', $base_url); @@ -1850,18 +1857,62 @@ function language_list($field = 'languag * @param $property * Optional property of the language object to return */ function language_default($property = NULL) { $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '')); return $property ? $language->$property : $language; } /** + * Returns the requested URL path of the page being viewed. + * + * Examples: + * - http://example.com/node/306 returns "node/306". + * - http://example.com/drupalfolder/node/306 returns "node/306" while + * base_path() returns "/drupalfolder/". + * - http://example.com/path/alias (which is a path alias for node/306) returns + * "path/alias" as opposed to the internal path. + * + * @return + * The requested Drupal URL path. + * + * @see current_path() + */ +function request_path() { + static $path; + + if (isset($path)) { + return $path; + } + + if (isset($_GET['q'])) { + // This is a request with a ?q=foo/bar query string. $_GET['q'] is + // overwritten in drupal_path_initialize(), but request_path() is called + // very early in the bootstrap process, so the original value is saved in + // $path and returned in later calls. + $path = $_GET['q']; + } + elseif (isset($_SERVER['REQUEST_URI'])) { + // This is a request using a clean URL. Extract the path from REQUEST_URI. + $request_path = strtok($_SERVER['REQUEST_URI'], '?'); + $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')); + // Unescape and strip $base_path prefix, leaving q without a leading slash. + $path = substr(urldecode($request_path), $base_path_len + 1); + } + else { + // This is the front page. + $path = ''; + } + + return $path; +} + +/** * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of * the proxy server, and not the client's. If Drupal is run in a cluster * we use the X-Cluster-Client-Ip header instead. * * @return * IP address of client machine, adjusted for reverse proxy and/or cluster * environments. */ Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1072 diff -u -9 -p -r1.1072 common.inc --- includes/common.inc 2 Jan 2010 17:39:19 -0000 1.1072 +++ includes/common.inc 2 Jan 2010 23:47:31 -0000 @@ -495,19 +495,18 @@ function drupal_http_build_query(array $ if (is_array($value)) { $params[] = drupal_http_build_query($value, $key); } // If a query parameter value is NULL, only append its key. elseif (!isset($value)) { $params[] = $key; } else { // For better readability of paths in query strings, we decode slashes. - // @see drupal_encode_path() $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value)); } } return implode('&', $params); } /** * Prepare a 'destination' URL query parameter for use in combination with drupal_goto(). @@ -617,50 +616,27 @@ function drupal_parse_url($url) { if (isset($options['query']['q'])) { $options['path'] = $options['query']['q']; unset($options['query']['q']); } return $options; } /** - * Encode a path for usage in a URL. + * Encodes a Drupal path for use in a URL. * - * Wrapper around rawurlencode() which avoids Apache quirks. Should be used when - * placing arbitrary data into the path component of an URL. + * For aesthetic reasons slashes are not escaped. * - * Do not use this function to pass a path to url(). url() properly handles - * and encodes paths internally. - * This function should only be used on paths, not on query string arguments. - * Otherwise, unwanted double encoding will occur. - * - * Notes: - * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature' - * in Apache where it 404s on any path containing '%2F'. - * - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when clean - * URLs are used, which are interpreted as delimiters by PHP. These - * characters are double escaped so PHP will still see the encoded version. - * - With clean URLs, Apache changes '//' to '/', so every second slash is - * double escaped. - * - * @param $path - * The URL path component to encode. + * Note that url() takes care of calling this function, so a path passed to that + * function should not be encoded in advance. */ -function drupal_encode_path($path) { - if (!empty($GLOBALS['conf']['clean_url'])) { - return str_replace(array('%2F', '%26', '%23', '//'), - array('/', '%2526', '%2523', '/%252F'), - rawurlencode($path) - ); - } - else { - return str_replace('%2F', '/', rawurlencode($path)); - } +function drupal_encode_path($text) { + return str_replace('%2F', '/', rawurlencode($text)); } /** * Send the user to a different Drupal page. * * This issues an on-site HTTP redirect. The function makes sure the redirected * URL is formatted correctly. * * Usually the redirected URL is constructed from this function's input Index: includes/file.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/file.inc,v retrieving revision 1.200 diff -u -9 -p -r1.200 file.inc --- includes/file.inc 30 Dec 2009 08:16:55 -0000 1.200 +++ includes/file.inc 2 Jan 2010 23:47:32 -0000 @@ -871,18 +871,22 @@ function file_unmunge_filename($filename * @param $basename * String filename * @param $directory * String containing the directory or parent URI. * @return * File path consisting of $directory and a unique filename based off * of $basename. */ function file_create_filename($basename, $directory) { + // Strip control characters (ASCII value < 32). Though these are allowed in + // some filesystems, not many applications handle them well. + $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename); + // A URI or path may already have a trailing slash or look like "public://". if (substr($directory, -1) == '/') { $separator = ''; } else { $separator = '/'; } $destination = $directory . $separator . $basename; Index: includes/path.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/path.inc,v retrieving revision 1.54 diff -u -9 -p -r1.54 path.inc --- includes/path.inc 17 Dec 2009 13:10:18 -0000 1.54 +++ includes/path.inc 2 Jan 2010 23:47:32 -0000 @@ -372,18 +372,20 @@ function drupal_match_path($path, $patte * "node/306" as opposed to the path alias. * * This function is not available in hook_boot() so use $_GET['q'] instead. * However, be careful when doing that because in the case of Example #3 * $_GET['q'] will contain "path/alias". If "node/306" is needed, calling * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available. * * @return * The current Drupal URL path. + * + * @see request_path() */ function current_path() { return $_GET['q']; } /** * Rebuild the path alias white list. * * @return Index: includes/stream_wrappers.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/stream_wrappers.inc,v retrieving revision 1.8 diff -u -9 -p -r1.8 stream_wrappers.inc --- includes/stream_wrappers.inc 15 Dec 2009 05:22:05 -0000 1.8 +++ includes/stream_wrappers.inc 2 Jan 2010 23:47:32 -0000 @@ -574,19 +574,19 @@ class DrupalPublicStreamWrapper extends } /** * Overrides getExternalUrl(). * * Return the HTML URI of a public file. */ function getExternalUrl() { $path = str_replace('\\', '/', file_uri_target($this->uri)); - return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . $path; + return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path); } } /** * Drupal private (private://) stream wrapper class. * * Provides support for storing privately accessible files with the Drupal file * interface. Index: misc/autocomplete.js =================================================================== RCS file: /cvs/drupal/drupal/misc/autocomplete.js,v retrieving revision 1.34 diff -u -9 -p -r1.34 autocomplete.js --- misc/autocomplete.js 5 Sep 2009 12:03:31 -0000 1.34 +++ misc/autocomplete.js 2 Jan 2010 23:47:32 -0000 @@ -270,19 +270,19 @@ Drupal.ACDB.prototype.search = function if (this.timer) { clearTimeout(this.timer); } this.timer = setTimeout(function () { db.owner.setStatus('begin'); // Ajax GET request for autocompletion. $.ajax({ type: 'GET', - url: db.uri + '/' + Drupal.encodePath(searchString), + url: db.uri + '/' + encodeURIComponent(searchString), dataType: 'json', success: function (matches) { if (typeof matches.status == 'undefined' || matches.status != 0) { db.cache[searchString] = matches; // Verify if these are still the matches the user wants to see. if (db.searchString == searchString) { db.owner.found(matches); } db.owner.setStatus('found'); Index: misc/drupal.js =================================================================== RCS file: /cvs/drupal/drupal/misc/drupal.js,v retrieving revision 1.62 diff -u -9 -p -r1.62 drupal.js --- misc/drupal.js 14 Dec 2009 23:57:39 -0000 1.62 +++ misc/drupal.js 2 Jan 2010 23:47:32 -0000 @@ -276,26 +276,25 @@ Drupal.freezeHeight = function () { /** * Unfreeze the body height. */ Drupal.unfreezeHeight = function () { $('#freeze-height').remove(); }; /** - * Wrapper around encodeURIComponent() which avoids Apache quirks (equivalent of - * drupal_encode_path() in PHP). This function should only be used on paths, not - * on query string arguments. + * Encodes a Drupal path for use in a URL. + * + * For aesthetic reasons slashes are not escaped. */ Drupal.encodePath = function (item, uri) { uri = uri || location.href; - item = encodeURIComponent(item).replace(/%2F/g, '/'); - return (uri.indexOf('?q=') != -1) ? item : item.replace(/%26/g, '%2526').replace(/%23/g, '%2523').replace(/\/\//g, '/%252F'); + return encodeURIComponent(item).replace(/%2F/g, '/'); }; /** * Get the text selection in a textarea. */ Drupal.getSelection = function (element) { if (typeof element.selectionStart != 'number' && document.selection) { // The current selection. var range1 = document.selection.createRange(); Index: modules/path/path.test =================================================================== RCS file: /cvs/drupal/drupal/modules/path/path.test,v retrieving revision 1.29 diff -u -9 -p -r1.29 path.test --- modules/path/path.test 9 Dec 2009 19:22:04 -0000 1.29 +++ modules/path/path.test 2 Jan 2010 23:47:32 -0000 @@ -60,23 +60,25 @@ class PathTestCase extends DrupalWebTest $edit['source'] = 'node/' . $node1->nid; $edit['alias'] = $this->randomName(8); $this->drupalPost('admin/config/search/path/add', $edit, t('Create new alias')); // Confirm that the alias works. $this->drupalGet($edit['alias']); $this->assertText($node1->title[LANGUAGE_NONE][0]['value'], 'Alias works.'); $this->assertResponse(200); - // Change alias. + // Change alias to one containing "exotic" characters. $pid = $this->getPID($edit['alias']); $previous = $edit['alias']; - $edit['alias'] = $this->randomName(8); + $edit['alias'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. + "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. + "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. $this->drupalPost('admin/config/search/path/edit/' . $pid, $edit, t('Update alias')); // Confirm that the alias works. $this->drupalGet($edit['alias']); $this->assertText($node1->title[LANGUAGE_NONE][0]['value'], 'Changed alias works.'); $this->assertResponse(200); drupal_static_reset('drupal_lookup_path'); // Confirm that previous alias no longer works. @@ -115,21 +117,23 @@ class PathTestCase extends DrupalWebTest $edit = array(); $edit['path[alias]'] = $this->randomName(8); $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save')); // Confirm that the alias works. $this->drupalGet($edit['path[alias]']); $this->assertText($node1->title[LANGUAGE_NONE][0]['value'], 'Alias works.'); $this->assertResponse(200); - // Change alias. + // Change alias to one containing "exotic" characters. $previous = $edit['path[alias]']; - $edit['path[alias]'] = $this->randomName(8); + $edit['path[alias]'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. + "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. + "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. $this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save')); // Confirm that the alias works. $this->drupalGet($edit['path[alias]']); $this->assertText($node1->title[LANGUAGE_NONE][0]['value'], 'Changed alias works.'); $this->assertResponse(200); // Make sure that previous alias no longer works. $this->drupalGet($previous); Index: modules/simpletest/tests/file.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file.test,v retrieving revision 1.50 diff -u -9 -p -r1.50 file.test --- modules/simpletest/tests/file.test 28 Dec 2009 14:00:01 -0000 1.50 +++ modules/simpletest/tests/file.test 2 Jan 2010 23:47:32 -0000 @@ -1882,18 +1882,20 @@ class FileDownloadTest extends FileTestC return array( 'name' => 'File download', 'description' => 'Tests for file download/transfer functions.', 'group' => 'File API', ); } function setUp() { parent::setUp('file_test'); + // Clear out any hook calls. + file_test_reset(); } /** * Test the public file transfer system. */ function testPublicFileTransfer() { // Test generating an URL to a created file. $file = $this->createFile(); $url = file_create_url($file->uri); @@ -1931,18 +1933,82 @@ class FileDownloadTest extends FileTestC file_test_set_return('download', -1); $this->drupalHead($url); $this->assertResponse(403, t('Correctly denied access to a file when file_test sets the header to -1.')); // Try non-existent file. $url = file_create_url('private://' . $this->randomName()); $this->drupalHead($url); $this->assertResponse(404, t('Correctly returned 404 response for a non-existent file.')); } + + /** + * Test file_create_url(). + */ + function testFileCreateUrl() { + global $base_url; + + $basename = " -._~!$'\"()*@[]?&+%#,;=:\n\x00" . // "Special" ASCII characters. + "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. + "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. + $basename_encoded = '%20-._%7E%21%24%27%22%28%29%2A%40%5B%5D%3F%26%2B%25%23%2C%3B%3D%3A__' . + '%2523%2525%2526%252B%252F%253F' . + '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E'; + + $this->checkUrl('public', '', $basename, $base_url . '/' . file_directory_path() . '/' . $basename_encoded); + $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded); + $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0'); + } + + /** + * Download a file from the URL generated by file_create_url(). + * + * Create a file with the specified scheme, directory and filename; check that + * the URL generated by file_create_url() for the specified file equals the + * specified URL; fetch the URL and then compare the contents to the file. + * + * @param $scheme + * A scheme, e.g. "public" + * @param $directory + * A directory, possibly "" + * @param $filename + * A filename + * @param $expected_url + * The expected URL + * @param $clean_url + * The value of the clean_url setting + */ + private function checkUrl($scheme, $directory, $filename, $expected_url, $clean_url = '1') { + variable_set('clean_url', $clean_url); + + // Convert $filename to a valid filename, i.e. strip characters not + // supported by the filesystem, and create the file in the specified + // directory. + $filepath = file_create_filename($filename, $directory); + $directory_uri = $scheme . '://' . dirname($filepath); + file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY); + $file = $this->createFile($filepath, NULL, $scheme); + + $url = file_create_url($file->uri); + $this->assertEqual($url, $expected_url, t('Generated URL matches expected URL.')); + + if ($scheme == 'private') { + // Tell the implementation of hook_file_download() in file_test.module + // that this file may be downloaded. + file_test_set_return('download', array('x-foo' => 'Bar')); + } + + $this->drupalGet($url); + if ($this->assertResponse(200) == 'pass') { + $this->assertRaw(file_get_contents($file->uri), t('Contents of the file are correct.')); + } + + file_delete($file); + } } /** * Tests for file URL rewriting. */ class FileURLRewritingTest extends FileTestCase { public static function getInfo() { return array( 'name' => 'File URL rewriting', Index: modules/simpletest/tests/menu.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/menu.test,v retrieving revision 1.26 diff -u -9 -p -r1.26 menu.test --- modules/simpletest/tests/menu.test 14 Dec 2009 20:23:01 -0000 1.26 +++ modules/simpletest/tests/menu.test 2 Jan 2010 23:47:32 -0000 @@ -46,18 +46,29 @@ class MenuRouterTestCase extends DrupalW * Test that the theme callback is properly inherited. */ function testThemeCallbackInheritance() { $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance'); $this->assertText('Requested theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', t('Theme callback inheritance correctly uses the administrative theme.')); $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page.")); } /** + * Test path containing "exotic" characters. + */ + function testExoticPath() { + $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. + "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. + "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. + $this->drupalGet($path); + $this->assertRaw('This is menu_test_callback().'); + } + + /** * Test the theme callback when the site is in maintenance mode. */ function testThemeCallbackMaintenanceMode() { variable_set('maintenance_mode', TRUE); // For a regular user, the fact that the site is in maintenance mode means // we expect the theme callback system to be bypassed entirely. $this->drupalGet('menu-test/theme-callback/use-admin-theme'); $this->assertRaw('garland/style.css', t("The maintenance theme's CSS appears on the page.")); Index: modules/simpletest/tests/menu_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/menu_test.module,v retrieving revision 1.11 diff -u -9 -p -r1.11 menu_test.module --- modules/simpletest/tests/menu_test.module 14 Dec 2009 20:23:01 -0000 1.11 +++ modules/simpletest/tests/menu_test.module 2 Jan 2010 23:47:32 -0000 @@ -52,18 +52,27 @@ function menu_test_menu() { 'theme callback' => 'menu_test_theme_callback', 'theme arguments' => array(2), ); $items['menu-test/theme-callback/%/inheritance'] = array( 'title' => 'Page that tests theme callback inheritance.', 'page callback' => 'menu_test_theme_page_callback', 'page arguments' => array(TRUE), 'access arguments' => array('access content'), ); + // Path containing "exotic" characters. + $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. + "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. + "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. + $items[$path] = array( + 'title' => '"Exotic" path', + 'page callback' => 'menu_test_callback', + 'access arguments' => array('access content'), + ); // Hidden tests; base parents. // Same structure as in Menu and Block modules. Since those structures can // change, we need to simulate our own in here. $items['menu-test'] = array( 'title' => 'Menu test root', 'page callback' => 'node_page_default', 'access arguments' => array('access content'), ); @@ -168,19 +177,19 @@ function menu_test_menu() { } /** * Dummy callback for hook_menu() to point to. * * @return * A random string. */ function menu_test_callback() { - return $this->randomName(); + return 'This is menu_test_callback().'; } /** * Page callback to use when testing the theme callback functionality. * * @param $inherited * An optional boolean to set to TRUE when the requested page is intended to * inherit the theme of its parent. * @return Index: modules/simpletest/tests/path.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/path.test,v retrieving revision 1.2 diff -u -9 -p -r1.2 path.test --- modules/simpletest/tests/path.test 24 Oct 2009 05:13:44 -0000 1.2 +++ modules/simpletest/tests/path.test 2 Jan 2010 23:47:32 -0000 @@ -195,18 +195,27 @@ class UrlAlterFunctionalTest extends Dru $this->assertUrlOutboundAlter("taxonomy/term/$tid", "community/$tid"); // Test that a non-existant forum URL is not altered. $tid++; $this->assertUrlInboundAlter("taxonomy/term/$tid", "taxonomy/term/$tid"); $this->assertUrlOutboundAlter("taxonomy/term/$tid", "taxonomy/term/$tid"); } /** + * Test current_path() and request_path(). + */ + function testCurrentUrlRequestedPath() { + $this->drupalGet('url-alter-test/bar'); + $this->assertRaw('request_path=url-alter-test/bar', t('request_path() returns the requested path.')); + $this->assertRaw('current_path=url-alter-test/foo', t('current_path() returns the internal path.')); + } + + /** * Assert that an outbound path is altered to an expected value. * * @param $original * A string with the original path that is run through url(). * @param $final * A string with the expected result after url(). * @return * TRUE if $original was correctly altered to $final, FALSE otherwise. */ Index: modules/simpletest/tests/url_alter_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/url_alter_test.module,v retrieving revision 1.2 diff -u -9 -p -r1.2 url_alter_test.module --- modules/simpletest/tests/url_alter_test.module 4 Dec 2009 16:49:47 -0000 1.2 +++ modules/simpletest/tests/url_alter_test.module 2 Jan 2010 23:47:32 -0000 @@ -1,33 +1,58 @@ 'Foo', + 'page callback' => 'url_alter_test_foo', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Menu callback. + */ +function url_alter_test_foo() { + print 'current_path=' . current_path() . ' request_path=' . request_path(); + exit; +} + +/** * Implements hook_url_inbound_alter(). */ function url_alter_test_url_inbound_alter(&$path, $original_path, $path_language) { // Rewrite user/username to user/uid. if (preg_match('!^user/([^/]+)(/.*)?!', $path, $matches)) { if ($account = user_load_by_name($matches[1])) { $matches += array(2 => ''); $path = 'user/' . $account->uid . $matches[2]; } } // Rewrite community/ to forum/. if ($path == 'community' || strpos($path, 'community/') === 0) { $path = 'forum' . substr($path, 9); } + + if ($path == 'url-alter-test/bar') { + $path = 'url-alter-test/foo'; + } } /** * Implements hook_url_outbound_alter(). */ function url_alter_test_url_outbound_alter(&$path, &$options, $original_path) { // Rewrite user/uid to user/username. if (preg_match('!^user/([0-9]+)(/.*)?!', $path, $matches)) { if ($account = user_load($matches[1])) {