? REMOVED.txt
? TODO.txt
? path_redirect.css
? path_redirect.import-export.inc
? path_redirect.migrate.inc
? screenshot.png
Index: path_redirect.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/path_redirect/path_redirect.module,v
retrieving revision 1.3.2.7.2.107
diff -u -p -r1.3.2.7.2.107 path_redirect.module
--- path_redirect.module	7 Oct 2010 15:28:33 -0000	1.3.2.7.2.107
+++ path_redirect.module	30 Nov 2010 01:04:43 -0000
@@ -378,8 +378,8 @@ function path_redirect_load($rid) {
  *   An optional query string to match.
  */
 function path_redirect_load_by_source($source, $language = '', $query = array()) {
-  $where = $query ? "(source = '%s' OR source LIKE '%s')" : "source = '%s'";
-  $args = $query ? array($source, $source . '?%%') : array($source);
+  $where = $query ? "(source = '%s' OR source LIKE '%s%%')" : "source = '%s'";
+  $args = $query ? array($source, $source . '?') : array($source);
   $args[] = $language;
   $rid_query = db_query("SELECT rid FROM {path_redirect} WHERE $where AND language IN ('%s', '') ORDER BY language DESC, source DESC, rid DESC", $args);
   $rids = array();
@@ -387,16 +387,32 @@ function path_redirect_load_by_source($s
     $rids[] = $rid;
   }
 
-  if ($query) {
-    $redirects = path_redirect_load_multiple($rids);
+  if ($rids && $redirects = path_redirect_load_multiple($rids)) {
+    // Narrow down the list of candidates.
     foreach ($redirects as $rid => $redirect) {
-      if (path_redirect_compare_array($redirect['source_query'], $query)) {
-        return $redirect;
+      if (!empty($redirect['source_query'])) {
+        if (empty($query) || !path_redirect_compare_array($redirect['source_query'], $query)) {
+          unset($redirects[$rid]);
+          continue;
+        }
+      }
+
+      // Add a case sensitive matches condition to be used in sorting.
+      if ($source !== $redirect['source']) {
+        $redirects[$rid]['weight'] = 1;
       }
     }
-  }
-  elseif ($rids) {
-    return path_redirect_load(current($rids));
+
+    if (!empty($redirects)) {
+      // Sort the redirects in the proper order.
+      uasort($redirects, '_path_redirect_uasort');
+
+      // Allow other modules to alter the redirect candidates before selecting the top one.
+      $context = array('language' => $language, 'query' => $query);
+      drupal_alter('path_redirect_load_by_source', $redirects, $source, $context);
+
+      return !empty($redirects) ? reset($redirects) : FALSE;
+    }
   }
 
   return FALSE;
@@ -427,6 +443,30 @@ function path_redirect_load_multiple($ri
   return $redirects;
 }
 
+/**
+ * uasort callback; Compare redirects based on language neutrality and rids.
+ */
+function _path_redirect_uasort($a, $b) {
+  $a_weight = isset($a['weight']) ? $a['weight'] : 0;
+  $b_weight = isset($b['weight']) ? $b['weight'] : 0;
+  if ($a_weight != $b_weight) {
+    // First sort by weight (case sensitivity).
+    return $a_weight > $b_weight;
+  }
+  elseif ($a['language'] != $b['language']) {
+    // Then sort by language specific over language neutral.
+    return $a['language'] == '';
+  }
+  elseif (empty($a['source_query']) != empty($b['source_query'])) {
+    // Then sort by redirects that do not have query strings over ones that do.
+    return empty($a['source_query']);
+  }
+  else {
+    // Lastly sort by the highest redirect ID.
+    return $a['rid'] < $b['rid'];
+  }
+}
+
 function _path_redirect_build_conditions(&$query, $rids, $conditions) {
   static $schema;
   if (!isset($schema)) {
Index: path_redirect.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/path_redirect/path_redirect.test,v
retrieving revision 1.1.2.23
diff -u -p -r1.1.2.23 path_redirect.test
--- path_redirect.test	9 Aug 2010 18:39:59 -0000	1.1.2.23
+++ path_redirect.test	30 Nov 2010 01:04:43 -0000
@@ -7,15 +7,11 @@
  */
 
 class PathRedirectTestHelper extends DrupalWebTestCase {
-  function setUp() {
-    $args = func_get_args();
+  function setUp($modules = array()) {
+    $modules[] = 'path';
+    $modules[] = 'path_redirect';
 
     // Call parent::setUp() allowing test cases to pass further modules.
-    $modules = array('path', 'path_redirect');
-    if (isset($args[0]) && is_array($args[0])) {
-      $modules = array_merge($modules, $args[0]);
-    }
-
     $parent_callback = 'parent::setUp';
     if (version_compare(PHP_VERSION, '5.3.0', '<')) {
       $parent_callback = array($this, 'parent::setUp');
@@ -23,15 +19,34 @@ class PathRedirectTestHelper extends Dru
     call_user_func_array($parent_callback, $modules);
   }
 
-  protected function assertRedirect($request, $redirect, $code = FALSE) {
-    $this->drupalGet($request);
-    if ($code) {
-      $this->assertResponse($code);
+  protected function assertHeader($name, $expected, $message = '') {
+    $value = $this->drupalGetHeader(strtolower($name));
+    if (empty($message)) {
+      $message = t('Header @header expected %expected, was %value.', array('@name' => $name, '%expected' => $expected, '%value' => $value));
     }
-    $this->assertEqual($this->getUrl(), url($redirect, array('absolute' => TRUE)), t('Redirected from %request to %redirect.', array('%request' => $request, '%redirect' => $redirect)));
+    $this->assertEqual($expected, $value, $message);
+  }
+
+  protected function assertNoHeader($name, $message = '') {
+    if (empty($message)) {
+      $message = t('Header @header was not found.', array('@name' => $name));
+    }
+    $this->assertIdentical($this->drupalGetHeader(strtolower($name)), FALSE, $message);
+  }
+
+  protected function assertRedirect($request, $redirect, array $request_options = array(), array $redirect_options = array()) {
+    $this->drupalHead($request, $request_options);
+    $expected_url = url($redirect, $redirect_options + array('absolute' => TRUE));
+    $value = $this->drupalGetHeader('location');
+    $this->assertEqual($value, $expected_url, t('Redirected from !request to !redirect, expected !expected.', array('!request' => $this->getUrl(), '!redirect' => $value, '!expected' => $expected_url)));
+  }
+
+  protected function assertNoRedirect($request, array $request_options = array()) {
+    $this->drupalHead($request, $request_options);
+    $this->assertNoHeader('location', t('Not redirected from !request.', array('!request' => $this->getUrl())));
   }
 
-  protected function addRedirect($source, $redirect, $options = array()) {
+  protected function addRedirect($source, $redirect, array $options = array()) {
     $redirect = array(
       'source' => $source,
       'redirect' => $redirect,
@@ -39,13 +54,6 @@ class PathRedirectTestHelper extends Dru
     path_redirect_save($redirect);
     return $redirect;
   }
-
-  /**
-   * Runs cron in the Drupal installed by Simpletest.
-   */
-  protected function cronRun() {
-    $this->drupalGet($GLOBALS['base_url'] . '/cron.php', array('external' => TRUE));
-  }
 }
 
 class PathRedirectUnitTest extends PathRedirectTestHelper {
@@ -57,6 +65,11 @@ class PathRedirectUnitTest extends PathR
     );
   }
 
+  function setUp() {
+    parent::setUp();
+    variable_set('simpletest_maximum_redirects', 0);
+  }
+
   function testCompareArray() {
     $haystack = array('a' => 'aa', 'b' => 'bb', 'c' => array('c1' => 'cc1', 'c2' => 'cc2'));
     $cases = array(
@@ -75,7 +88,7 @@ class PathRedirectUnitTest extends PathR
 
   function testRedirects() {
     // Test a basic redirect (with and without a trailing slash requested).
-    $this->addRedirect('/test/', 'node');
+    $this->addRedirect('test', 'node');
     $this->assertRedirect('test', 'node');
     $this->assertRedirect('test/', 'node');
 
@@ -88,6 +101,27 @@ class PathRedirectUnitTest extends PathR
     $this->assertRedirect('foo_/ferzle-foo.bar', '<front>');
   }
 
+  /**
+   * Test the _path_redirect_uasort() callback used in
+   * path_redirect_load_by_source().
+   */
+  function testRedirectQuerySorting() {
+    $this->addRedirect('test', 'node', array('source_query' => array('foo' => 'bar'), 'query' => array('foo' => 'bar')));
+    $this->assertNoRedirect('test');
+    $this->assertRedirect('test', 'node', array('query' => array('foo' => 'bar')), array('query' => array('foo' => 'bar')));
+    $this->assertNoRedirect('test', array('query' => array('foo' => 'ferzle')));
+
+    $this->addRedirect('test', 'node');
+    $this->assertRedirect('test', 'node');
+    $this->assertRedirect('test', 'node', array('query' => array('foo' => 'bar')), array('query' => array('foo' => 'bar')));
+    $this->assertRedirect('test', 'node', array('query' => array('foo' => 'ferzle')));
+
+    $this->addRedirect('test', 'node', array('source_query' => array('foo' => 'ferzle'), 'query' => array('foo' => 'ferzle')));
+    $this->assertRedirect('test', 'node');
+    $this->assertRedirect('test', 'node', array('query' => array('foo' => 'bar')), array('query' => array('foo' => 'bar')));
+    $this->assertRedirect('test', 'node', array('query' => array('foo' => 'ferzle')), array('query' => array('foo' => 'ferzle')));
+  }
+
   function testNoCleanURLs() {
     $clean_url = (bool) variable_get('clean_url', 0);
     variable_set('clean_url', !$clean_url);
@@ -106,8 +140,10 @@ class PathRedirectUnitTest extends PathR
     );
   }
 
-  function setUp() {
-    parent::setUp('path', 'token', 'pathauto', 'path_redirect');
+  function setUp($modules = array()) {
+    $modules[] = 'pathauto';
+    $modules[] = 'token';
+    parent::setUp($modules);
 
     $this->admin_user = $this->drupalCreateUser(array('administer nodes', 'administer redirects', 'create url aliases'));
     $this->drupalLogin($this->admin_user);
@@ -147,7 +183,7 @@ class PathRedirectFunctionalTest extends
     );
   }
 
-  function setUp() {
+  function setUp($modules = array()) {
     parent::setUp();
 
     $this->admin_user = $this->drupalCreateUser(array('administer redirects', 'administer nodes', 'create url aliases'));
