=== modified file 'includes/common.inc'
--- includes/common.inc	2007-01-05 05:32:22 +0000
+++ includes/common.inc	2007-01-09 14:34:51 +0000
@@ -122,7 +122,8 @@ function drupal_get_html_head() {
  * Reset the static variable which holds the aliases mapped for this request.
  */
 function drupal_clear_path_cache() {
-  drupal_lookup_path('wipe');
+  drupal_lookup_alias('wipe');
+  drupal_lookup_source('wipe');
 }
 
 /**
@@ -254,69 +255,6 @@ function drupal_get_destination() {
 }
 
 /**
- * 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
- * parameters. However you may override that behavior by setting a
- * <em>destination</em> in either the $_REQUEST-array (i.e. by using
- * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by
- * using a hidden form field). This is used to direct the user back to
- * the proper page after completing a form. For example, after editing
- * a post on the 'admin/content/node'-page or after having logged on using the
- * 'user login'-block in a sidebar. The function drupal_get_destination()
- * can be used to help set the destination URL.
- *
- * It is advised to use drupal_goto() instead of PHP's header(), because
- * drupal_goto() will append the user's session ID to the URI when PHP is
- * compiled with "--enable-trans-sid".
- *
- * This function ends the request; use it rather than a print theme('page')
- * statement in your menu callback.
- *
- * @param $path
- *   A Drupal path or a full URL.
- * @param $query
- *   The query string component, if any.
- * @param $fragment
- *   The destination fragment identifier (named anchor).
- * @param $http_response_code
- *   Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
- *   - 301 Moved Permanently (the recommended value for most redirects)
- *   - 302 Found (default in Drupal and PHP, sometimes used for spamming search
- *         engines)
- *   - 303 See Other
- *   - 304 Not Modified
- *   - 305 Use Proxy
- *   - 307 Temporary Redirect (an alternative to "503 Site Down for Maintenance")
- *   Note: Other values are defined by RFC 2616, but are rarely used and poorly
- *         supported.
- * @see drupal_get_destination()
- */
-function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) {
-  if (isset($_REQUEST['destination'])) {
-    extract(parse_url(urldecode($_REQUEST['destination'])));
-  }
-  else if (isset($_REQUEST['edit']['destination'])) {
-    extract(parse_url(urldecode($_REQUEST['edit']['destination'])));
-  }
-
-  $url = url($path, $query, $fragment, TRUE);
-
-  // Before the redirect, allow modules to react to the end of the page request.
-  module_invoke_all('exit', $url);
-
-  header('Location: '. $url, TRUE, $http_response_code);
-
-  // The "Location" header sends a REDIRECT status code to the http
-  // daemon. In some cases this can go wrong, so we make sure none
-  // of the code below the drupal_goto() call gets executed when we redirect.
-  exit();
-}
-
-/**
  * Generates a site off-line message
  */
 function drupal_site_offline() {
@@ -1111,104 +1049,6 @@ function format_date($timestamp, $type =
 }
 
 /**
- * @} End of "defgroup format".
- */
-
-/**
- * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
- *
- * @param $path
- *   The Drupal path being linked to, such as "admin/content/node", or an existing URL
- *   like "http://drupal.org/".
- * @param $query
- *   A query string to append to the link or URL.
- * @param $fragment
- *   A fragment identifier (named anchor) to append to the link. If an existing
- *   URL with a fragment identifier is used, it will be replaced. Note, do not
- *   include the '#'.
- * @param $absolute
- *   Whether to force the output to be an absolute link (beginning with http:).
- *   Useful for links that will be displayed outside the site, such as in an
- *   RSS feed.
- * @return
- *   a string containing a URL to the given path.
- *
- * When creating links in modules, consider whether l() could be a better
- * alternative than url().
- */
-function url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE) {
-  if (isset($fragment)) {
-    $fragment = '#'. $fragment;
-  }
-
-  // Return an external link if $path contains an allowed absolute URL.
-  // Only call the slow filter_xss_bad_protocol if $path contains a ':'.
-  if (strpos($path, ':') !== FALSE && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {
-    // Split off the fragment
-    if (strpos($path, '#') !== FALSE) {
-      list($path, $old_fragment) = explode('#', $path, 2);
-      if (isset($old_fragment) && !isset($fragment)) {
-        $fragment = '#'. $old_fragment;
-      }
-    }
-    // Append the query
-    if (isset($query)) {
-      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $query;
-    }
-    // Reassemble
-    return $path . $fragment;
-  }
-
-  global $base_url;
-  static $script;
-  static $clean_url;
-
-  if (empty($script)) {
-    // On some web servers, such as IIS, we can't omit "index.php". So, we
-    // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
-    // Apache.
-    $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : '';
-  }
-
-  // Cache the clean_url variable to improve performance.
-  if (!isset($clean_url)) {
-    $clean_url = (bool)variable_get('clean_url', '0');
-  }
-
-  $base = ($absolute ? $base_url . '/' : base_path());
-
-  // The special path '<front>' links to the default front page.
-  if (!empty($path) && $path != '<front>') {
-    $path = drupal_get_path_alias($path);
-    $path = drupal_urlencode($path);
-    if (!$clean_url) {
-      if (isset($query)) {
-        return $base . $script .'?q='. $path .'&'. $query . $fragment;
-      }
-      else {
-        return $base . $script .'?q='. $path . $fragment;
-      }
-    }
-    else {
-      if (isset($query)) {
-        return $base . $path .'?'. $query . $fragment;
-      }
-      else {
-        return $base . $path . $fragment;
-      }
-    }
-  }
-  else {
-    if (isset($query)) {
-      return $base . $script .'?'. $query . $fragment;
-    }
-    else {
-      return $base . $fragment;
-    }
-  }
-}
-
-/**
  * Format an attribute string to insert in a tag.
  *
  * @param $attributes
@@ -1718,34 +1558,6 @@ function drupal_to_js($var) {
 }
 
 /**
- * Wrapper around urlencode() which avoids Apache quirks.
- *
- * Should be used when placing arbitrary data in an URL. Note that Drupal paths
- * are urlencoded() when passed through url() and do not require urlencoding()
- * of individual components.
- *
- * 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's unescapes %-encoded ampersands and hashes 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.
- *
- * @param $text
- *   String to encode
- */
-function drupal_urlencode($text) {
-  if (variable_get('clean_url', '0')) {
-    return str_replace(array('%2F', '%26', '%23'),
-                       array('/', '%2526', '%2523'),
-                       urlencode($text));
-  }
-  else {
-    return str_replace('%2F', '/', urlencode($text));
-  }
-}
-
-/**
  * Ensure the private key variable used to generate tokens is set.
  *
  * @return

=== modified file 'includes/path.inc'
--- includes/path.inc	2006-12-23 22:04:52 +0000
+++ includes/path.inc	2007-01-09 15:03:53 +0000
@@ -11,37 +11,45 @@
  */
 
 /**
- * Initialize the $_GET['q'] variable to the proper normal path.
+ * Initialize the $_GET['q'] variable to the proper normal path. If the
+ * current path is an alias but not the active one, redirect to the
+ * active one.
  */
 function drupal_init_path() {
-  if (!empty($_GET['q'])) {
-    $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));
+  if (empty($_GET['q'])) {
+    $src = drupal_get_normal_path(variable_get('site_frontpage', 'node'), TRUE);
   }
   else {
-    $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
+    $path = trim($_GET['q'], '/');
+    $src = drupal_get_normal_path($path, TRUE);
+  }
+  if ($src['active']) {
+    $_GET['q'] = $src['src'];
+  }
+  else {
+    $get = $_GET;
+    unset($get['q']);
+    $get = implode('&', $get);
+    drupal_goto(drupal_get_path_alias($src['src']), $get ? $get : NULL, NULL, 301);
   }
 }
 
 /**
- * Given an alias, return its Drupal system URL if one exists. Given a Drupal
- * system URL return one of its aliases if such a one exists. Otherwise,
- * return FALSE.
+ * Return the active alias of the given Drupal path if it has an alias.
+ * Otherwise, return FALSE.
  *
- * @param $action
- *   One of the following values:
- *   - wipe: delete the alias cache.
- *   - alias: return an alias for a given Drupal system path (if one exists).
- *   - source: return the Drupal system URL for a path alias (if one exists).
  * @param $path
- *   The path to investigate for corresponding aliases or system URLs.
+ *   The path to investigate for its alias.
+ * @param $wipe
+ *   Set this to TRUE if you want to wipe the static cache.
  *
  * @return
  *   Either a Drupal system path, an aliased path, or FALSE if no path was
  *   found.
  */
-function drupal_lookup_path($action, $path = '') {
+function drupal_lookup_alias($path, $wipe = FALSE) {
   // $map keys are Drupal paths and the values are the corresponding aliases
-  static $map = array(), $no_src = array();
+  static $map = array();
   static $count;
 
   // Use $count to avoid looking up paths in subsequent calls if there simply are no aliases
@@ -49,39 +57,51 @@ function drupal_lookup_path($action, $pa
     $count = db_result(db_query('SELECT COUNT(pid) FROM {url_alias}'));
   }
 
-  if ($action == 'wipe') {
+  if ($wipe) {
     $map = array();
-    $no_src = array();
   }
-  elseif ($count > 0 && $path != '') {
-    if ($action == 'alias') {
-      if (isset($map[$path])) {
-        return $map[$path];
-      }
-      $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s'", $path));
-      $map[$path] = $alias;
-      return $alias;
-    }
-    // Check $no_src for this $path in case we've already determined that there
-    // isn't a path that has this alias
-    elseif ($action == 'source' && !isset($no_src[$path])) {
-      // Look for the value $path within the cached $map
-      if (!$src = array_search($path, $map)) {
-        if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path))) {
-          $map[$src] = $path;
-        }
-        else {
-          // We can't record anything into $map because we do not have a valid
-          // index and there is no need because we have not learned anything
-          // about any Drupal path. Thus cache to $no_src.
-          $no_src[$path] = TRUE;
-        }
-      }
-      return $src;
+  elseif ($count > 0) {
+    if (isset($map[$path])) {
+      return $map[$path];
     }
+    $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND active = 1", $path));
+    $map[$path] = $alias;
+    return $alias;
+  }
+}
+/**
+ * Given an alias, return its Drupal system URL and whether its active or not.
+ * If the alias has no system URL, return FALSE.
+ *
+ * @param $path
+ *   The path to investigate for system URL and activeness.
+ * @param $wipe
+ *   Whether to wipe the static cache.
+ * @return
+ *   On success, an associative array, the value of the key 'src' will be the
+ *   system path, the value of the key 'active' will be whether this is the
+ *   active alias or not.
+ */
+function drupal_lookup_source($path, $wipe = FALSE) {
+  static $map, $count;
+
+  // Use $count to avoid looking up paths in subsequent calls if there simply are no aliases
+  if (!isset($count)) {
+    $count = db_result(db_query('SELECT COUNT(pid) FROM {url_alias}'));
   }
 
-  return FALSE;
+  if ($wipe) {
+    $map = array();
+  }
+  elseif ($count > 0) {
+    if (isset($map[$path])) {
+      return $map[$path];
+    }
+    $sql = "SELECT src, active FROM {url_alias} WHERE dst = '%s'";
+    $src = db_fetch_array(db_query($sql, $path));
+    $map[$path] = $src;
+    return $src;
+  }
 }
 
 /**
@@ -96,7 +116,7 @@ function drupal_lookup_path($action, $pa
  */
 function drupal_get_path_alias($path) {
   $result = $path;
-  if ($alias = drupal_lookup_path('alias', $path)) {
+  if ($alias = drupal_lookup_alias($path)) {
     $result = $alias;
   }
   if (function_exists('custom_url_rewrite')) {
@@ -106,24 +126,30 @@ function drupal_get_path_alias($path) {
 }
 
 /**
- * Given a path alias, return the internal path it represents.
+ * Given a path alias, return the internal path it represents and optionally
+ * whether it's the active alias or not.
  *
  * @param $path
  *   A Drupal path alias.
+ * @param $return_active
+ *   Whether to return or not the activeness information.
  *
  * @return
- *   The internal path represented by the alias, or the original alias if no
- *   internal path was found.
+ *   If $return_active is FALSE, then the internal path represented by the
+ *   alias, or the original alias if no internal path was found.
+ *   If $return_active is TRUE then an associative, the value of key 'src'
+ *   will be the information outlined above and the value of key 'active'
+ *   will be be whether this is the active alias or not.
  */
-function drupal_get_normal_path($path) {
-  $result = $path;
-  if ($src = drupal_lookup_path('source', $path)) {
+function drupal_get_normal_path($path, $return_active = FALSE) {
+  $result = array('src' => $path, 'active' => 1);
+  if ($src = drupal_lookup_source($path)) {
     $result = $src;
   }
   if (function_exists('custom_url_rewrite')) {
     $result = custom_url_rewrite('source', $result, $path);
   }
-  return $result;
+  return $return_active ? $result : $result['src'];
 }
 
 /**
@@ -205,3 +231,231 @@ function drupal_is_front_page() {
   // we can check it against the 'site_frontpage' variable.
   return $_GET['q'] == drupal_get_normal_path(variable_get('site_frontpage', 'node'));
 }
+
+/**
+ * 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
+ * parameters. However you may override that behavior by setting a
+ * <em>destination</em> in either the $_REQUEST-array (i.e. by using
+ * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by
+ * using a hidden form field). This is used to direct the user back to
+ * the proper page after completing a form. For example, after editing
+ * a post on the 'admin/content/node'-page or after having logged on using the
+ * 'user login'-block in a sidebar. The function drupal_get_destination()
+ * can be used to help set the destination URL.
+ *
+ * It is advised to use drupal_goto() instead of PHP's header(), because
+ * drupal_goto() will append the user's session ID to the URI when PHP is
+ * compiled with "--enable-trans-sid".
+ *
+ * This function ends the request; use it rather than a print theme('page')
+ * statement in your menu callback.
+ *
+ * @param $path
+ *   A Drupal path or a full URL.
+ * @param $query
+ *   The query string component, if any.
+ * @param $fragment
+ *   The destination fragment identifier (named anchor).
+ * @param $http_response_code
+ *   Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
+ *   - 301 Moved Permanently (the recommended value for most redirects)
+ *   - 302 Found (default in Drupal and PHP, sometimes used for spamming search
+ *         engines)
+ *   - 303 See Other
+ *   - 304 Not Modified
+ *   - 305 Use Proxy
+ *   - 307 Temporary Redirect (an alternative to "503 Site Down for Maintenance")
+ *   Note: Other values are defined by RFC 2616, but are rarely used and poorly
+ *         supported.
+ * @see drupal_get_destination()
+ */
+function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) {
+  if (isset($_REQUEST['destination'])) {
+    extract(parse_url(urldecode($_REQUEST['destination'])));
+  }
+  else if (isset($_REQUEST['edit']['destination'])) {
+    extract(parse_url(urldecode($_REQUEST['edit']['destination'])));
+  }
+
+  $url = url($path, $query, $fragment, TRUE);
+
+  // Before the redirect, allow modules to react to the end of the page request.
+  bootstrap_invoke_all('exit', $url);
+  
+  header('Location: '. $url, TRUE, $http_response_code);
+
+  // The "Location" header sends a REDIRECT status code to the http
+  // daemon. In some cases this can go wrong, so we make sure none
+  // of the code below the drupal_goto() call gets executed when we redirect.
+  exit();
+}
+/**
+ * @} End of "defgroup format".
+ */
+
+/**
+ * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
+ *
+ * @param $path
+ *   The Drupal path being linked to, such as "admin/content/node", or an existing URL
+ *   like "http://drupal.org/".
+ * @param $query
+ *   A query string to append to the link or URL.
+ * @param $fragment
+ *   A fragment identifier (named anchor) to append to the link. If an existing
+ *   URL with a fragment identifier is used, it will be replaced. Note, do not
+ *   include the '#'.
+ * @param $absolute
+ *   Whether to force the output to be an absolute link (beginning with http:).
+ *   Useful for links that will be displayed outside the site, such as in an
+ *   RSS feed.
+ * @return
+ *   a string containing a URL to the given path.
+ *
+ * When creating links in modules, consider whether l() could be a better
+ * alternative than url().
+ */
+function url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE) {
+  if (isset($fragment)) {
+    $fragment = '#'. $fragment;
+  }
+
+  // Return an external link if $path contains an allowed absolute URL.
+  // Only call the slow filter_xss_bad_protocol if $path contains a ':'.
+  if (strpos($path, ':') !== FALSE && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {
+    // Split off the fragment
+    if (strpos($path, '#') !== FALSE) {
+      list($path, $old_fragment) = explode('#', $path, 2);
+      if (isset($old_fragment) && !isset($fragment)) {
+        $fragment = '#'. $old_fragment;
+      }
+    }
+    // Append the query
+    if (isset($query)) {
+      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $query;
+    }
+    // Reassemble
+    return $path . $fragment;
+  }
+
+  global $base_url;
+  static $script;
+  static $clean_url;
+
+  if (empty($script)) {
+    // On some web servers, such as IIS, we can't omit "index.php". So, we
+    // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
+    // Apache.
+    $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : '';
+  }
+
+  // Cache the clean_url variable to improve performance.
+  if (!isset($clean_url)) {
+    $clean_url = (bool)variable_get('clean_url', '0');
+  }
+
+  $base = ($absolute ? $base_url . '/' : base_path());
+
+  // The special path '<front>' links to the default front page.
+  if (!empty($path) && $path != '<front>') {
+    $path = drupal_get_path_alias($path);
+    $path = drupal_urlencode($path);
+    if (!$clean_url) {
+      if (isset($query)) {
+        return $base . $script .'?q='. $path .'&'. $query . $fragment;
+      }
+      else {
+        return $base . $script .'?q='. $path . $fragment;
+      }
+    }
+    else {
+      if (isset($query)) {
+        return $base . $path .'?'. $query . $fragment;
+      }
+      else {
+        return $base . $path . $fragment;
+      }
+    }
+  }
+  else {
+    if (isset($query)) {
+      return $base . $script .'?'. $query . $fragment;
+    }
+    else {
+      return $base . $fragment;
+    }
+  }
+}
+
+/**
+ * Processes an HTML attribute value and ensures it does not contain an URL
+ * with a disallowed protocol (e.g. javascript:)
+ *
+ * @param $string
+ *   The string with the attribute value.
+ * @param $decode
+ *   Whether to decode entities in the $string. Set to FALSE if the $string
+ *   is in plain text, TRUE otherwise. Defaults to TRUE.
+ * @return
+ *   Cleaned up and HTML-escaped version of $string.
+ */
+function filter_xss_bad_protocol($string, $decode = TRUE) {
+  static $allowed_protocols;
+  if (!isset($allowed_protocols)) {
+    $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')));
+  }
+
+  // Get the plain text representation of the attribute value (i.e. its meaning)
+  if ($decode) {
+    $string = decode_entities($string);
+  }
+  // Remove soft hyphen
+  $string = str_replace(chr(194) . chr(173), '', $string);
+  // Strip protocols
+
+  do {
+    $before = $string;
+    $colonpos = strpos($string, ':');
+    if ($colonpos > 0) {
+      $protocol = substr($string, 0, $colonpos);
+      if (!isset($allowed_protocols[$protocol])) {
+        $string = substr($string, $colonpos + 1);
+      }
+    }
+  } while ($before != $string);
+  return check_plain($string);
+}
+
+
+/**
+ * Wrapper around urlencode() which avoids Apache quirks.
+ *
+ * Should be used when placing arbitrary data in an URL. Note that Drupal paths
+ * are urlencoded() when passed through url() and do not require urlencoding()
+ * of individual components.
+ *
+ * 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's unescapes %-encoded ampersands and hashes 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.
+ *
+ * @param $text
+ *   String to encode
+ */
+function drupal_urlencode($text) {
+  if (variable_get('clean_url', '0')) {
+    return str_replace(array('%2F', '%26', '%23'),
+                       array('/', '%2526', '%2523'),
+                       urlencode($text));
+  }
+  else {
+    return str_replace('%2F', '/', urlencode($text));
+  }
+}

=== modified file 'modules/filter/filter.module'
--- modules/filter/filter.module	2006-12-16 01:05:11 +0000
+++ modules/filter/filter.module	2006-12-31 17:57:26 +0000
@@ -1457,45 +1457,6 @@ function _filter_xss_attributes($attr) {
 }
 
 /**
- * Processes an HTML attribute value and ensures it does not contain an URL
- * with a disallowed protocol (e.g. javascript:)
- *
- * @param $string
- *   The string with the attribute value.
- * @param $decode
- *   Whether to decode entities in the $string. Set to FALSE if the $string
- *   is in plain text, TRUE otherwise. Defaults to TRUE.
- * @return
- *   Cleaned up and HTML-escaped version of $string.
- */
-function filter_xss_bad_protocol($string, $decode = TRUE) {
-  static $allowed_protocols;
-  if (!isset($allowed_protocols)) {
-    $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')));
-  }
-
-  // Get the plain text representation of the attribute value (i.e. its meaning)
-  if ($decode) {
-    $string = decode_entities($string);
-  }
-  // Remove soft hyphen
-  $string = str_replace(chr(194) . chr(173), '', $string);
-  // Strip protocols
-
-  do {
-    $before = $string;
-    $colonpos = strpos($string, ':');
-    if ($colonpos > 0) {
-      $protocol = substr($string, 0, $colonpos);
-      if (!isset($allowed_protocols[$protocol])) {
-        $string = substr($string, $colonpos + 1);
-      }
-    }
-  } while ($before != $string);
-  return check_plain($string);
-}
-
-/**
  * @} End of "Standard filters".
  */
 

=== modified file 'modules/path/path.module'
--- modules/path/path.module	2006-12-18 11:16:51 +0000
+++ modules/path/path.module	2007-01-09 14:47:17 +0000
@@ -6,344 +6,213 @@
  * Enables users to rename URLs.
  */
 
-/**
- * Implementation of hook_help().
- */
-function path_help($section) {
-  switch ($section) {
-    case 'admin/help#path':
-      $output = '<p>'. t('The path module allows you to specify aliases for Drupal URLs. Such aliases improve readability of URLs for your users and may help internet search engines to index your content more effectively. More than one alias may be created for a given page.') .'</p>';
-      $output .= t('<p>Some examples of URL aliases are:</p>
-<ul>
-<li>user/login =&gt; login</li>
-<li>image/tid/16 =&gt; store</li>
-<li>taxonomy/term/7+19+20+21 =&gt; store/products/whirlygigs</li>
-<li>node/3 =&gt; contact</li>
-</ul>
-');
-      $output .= '<p>'. t('The path module enables an extra field for aliases in all node input and editing forms (when users have the appropriate permissions). It also provides an interface to view and edit all URL aliases. The two permissions related to URL aliasing are "administer url aliases" and "create url aliases". ') .'</p>';
-      $output .= '<p>'. t('This module also comes with user-defined mass URL aliasing capabilities, which is useful if you wish to uniformly use URLs different from the default. For example, you may want to have your URLs presented in a different language. Access to the Drupal source code on the web server is required to set up these kinds of aliases. ') .'</p>';
-      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@path">Path page</a>.', array('@path' => 'http://drupal.org/handbook/modules/path/')) .'</p>';
-      return $output;
-    case 'admin/build/path':
-      return '<p>'. t("Drupal provides users complete control over URLs through aliasing. This feature is typically used to make URLs human-readable or easy to remember. For example, one could map the relative URL 'node/1' onto 'about'. Each system path can have multiple aliases.") .'</p>';
-    case 'admin/build/path/add':
-      return '<p>'. t('Enter the path you wish to create the alias for, followed by the name of the new alias.') .'</p>';
-  }
-}
-
-/**
- * Implementation of hook_menu().
- */
-function path_menu($may_cache) {
-  $items = array();
-
-  if ($may_cache) {
-    $items[] = array('path' => 'admin/build/path', 'title' => t('URL aliases'),
-      'description' => t('Change your site\'s URL paths by aliasing them.'),
-      'callback' => 'path_admin',
-      'access' => user_access('administer url aliases'));
-    $items[] = array('path' => 'admin/build/path/edit', 'title' => t('Edit alias'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('path_admin_edit'),
-      'access' => user_access('administer url aliases'),
-      'type' => MENU_CALLBACK);
-    $items[] = array('path' => 'admin/build/path/delete', 'title' => t('Delete alias'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('path_admin_delete_confirm'),
-      'access' => user_access('administer url aliases'),
-      'type' => MENU_CALLBACK);
-    $items[] = array('path' => 'admin/build/path/list', 'title' => t('List'),
-      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
-    $items[] = array('path' => 'admin/build/path/add', 'title' => t('Add alias'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('path_admin_edit'),
-      'access' => user_access('administer url aliases'),
-      'type' => MENU_LOCAL_TASK);
+function path_block($op = 'list', $delta = 0, $edit = array()) {
+  switch ($op) {
+    case 'list':
+      $blocks[0]['info'] = t('Administer url aliases');
+      return $blocks;
+    case 'view':
+      if (user_access('create url aliases') || user_access('administer url aliases')) {
+        $block['content'] = drupal_get_form('path_form', $_GET['q']);
+        return $block;
+      }
   }
-
-  return $items;
 }
-
 /**
- * Menu callback; presents an overview of all URL aliases.
+ * Implementation of hook_perm().
  */
-function path_admin() {
-  return path_overview();
+function path_perm() {
+  return array('create url aliases', 'administer url aliases');
 }
 
 /**
- * Menu callback; handles pages for creating and editing URL aliases.
+ * Set an alias for a given Drupal path or delete one if the given alias is empty.
+ *
+ * @param $path
+ *   The path we are setting the alias for.
+ * @param $alias
+ *   
  */
-function path_admin_edit($pid = 0) {
+function path_set_alias($path, $alias = NULL, $pid = NULL, $active = FALSE) {
   if ($pid) {
-    $alias = path_load($pid);
-    drupal_set_title($alias['dst']);
-    $output = path_form($alias);
+    if ($alias) {
+      db_query("UPDATE {url_alias} SET src = '%s', dst = '%s', active = %d WHERE pid = %d", $path, $alias, $active, $pid);
+    }
+    else {
+      db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid);
+    }
   }
   else {
-    $output = path_form();
+    db_query("INSERT INTO {url_alias} (src, dst, active) VALUES ('%s', '%s', %d)", $path, $alias, $active);
   }
-
-  return $output;
+  drupal_clear_path_cache();
 }
 
 /**
- * Menu callback; confirms deleting an URL alias
- **/
-function path_admin_delete_confirm($pid) {
-  $path = path_load($pid);
+ * Return a form for editing or creating URL aliases of a page.
+ */
+function path_form($path) {
+  $form['path'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Administer url aliases'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#theme' => 'path_form_table',
+  );
+  $form['path']['path'] = array(
+    '#type' => 'value',
+    '#value' => $path,
+  );
+  $form['path']['explain']['#value'] = '<p>'. t('If you delete the value of an alias and press Save, that will be deleted. As this leads to link rot, such an action is not advised.') .'</p>';
+  // Only administrators can change the existing values.
   if (user_access('administer url aliases')) {
-    $form['pid'] = array('#type' => 'value', '#value' => $pid);
-    $output = confirm_form($form,
-  t('Are you sure you want to delete path alias %title?', array('%title' => $path['dst'])),
-   $_GET['destination'] ? $_GET['destination'] : 'admin/build/path', t('This action cannot be undone.'),
-  t('Delete'), t('Cancel') );
+    $key = '#default_value';
+    $type = 'textfield';
   }
-
-  return $output;
-}
-
-/**
- * Execute URL alias deletion
- **/
-function path_admin_delete_confirm_submit($form_id, $form_values) {
-  if ($form_values['confirm']) {
-    path_admin_delete($form_values['pid']);
-    return 'admin/build/path';
+  else {
+    $form['path']['readonly'] = array(
+      '#value' => t('You do not have necessary permissions to change existing aliases. TODO: make better wording for this warning.'),
+      '#weight' => -1,
+    );
+    $key = '#value';
+    $type = 'markup';
   }
-}
-
-/**
- * Post-confirmation; delete an URL alias.
- */
-function path_admin_delete($pid = 0) {
-  db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid);
-  drupal_set_message(t('The alias has been deleted.'));
-}
-
-
-
-/**
- * Set an aliased path for a given Drupal path, preventing duplicates.
- */
-function path_set_alias($path = NULL, $alias = NULL, $pid = NULL) {
-  if ($path && !$alias) {
-    db_query("DELETE FROM {url_alias} WHERE src = '%s'", $path);
-    drupal_clear_path_cache();
-  }
-  else if (!$path && $alias) {
-    db_query("DELETE FROM {url_alias} WHERE dst = '%s'", $alias);
-    drupal_clear_path_cache();
-  }
-  else if ($path && $alias) {
-    $path = urldecode($path);
-    $path_count = db_result(db_query("SELECT COUNT(src) FROM {url_alias} WHERE src = '%s'", $path));
-    $alias = urldecode($alias);
-    $alias_count = db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s'", $alias));
-
-    // We have an insert:
-    if ($path_count == 0 && $alias_count == 0) {
-      db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
-      drupal_clear_path_cache();
-    }
-    else if ($path_count >= 1 && $alias_count == 0) {
-      if ($pid) {
-        db_query("UPDATE {url_alias} SET dst = '%s', src = '%s' WHERE pid = %d", $alias, $path, $pid);
-      }
-      else {
-        db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
-      }
-      drupal_clear_path_cache();
-    }
-    else if ($path_count == 0 && $alias_count == 1) {
-      db_query("UPDATE {url_alias} SET src = '%s' WHERE dst = '%s'", $path, $alias);
-      drupal_clear_path_cache();
+  $original_element = array(
+    '#type' => $type,
+    $key => $path,
+    '#size' => 40,
+    '#description' => t('This is the original path, edit only if you want to move your aliases to another path'),
+    '#weight' => -1,
+    '#required' => TRUE,
+  );
+  $active = 0;
+  $result = db_query("SELECT pid, dst, active FROM {url_alias} WHERE src = '%s'", $path);
+  while ($alias = db_fetch_object($result)) {
+    if ($alias->dst == $path) {
+      $form['path']['alias'][$alias->pid] = $original_element;
+      unset($original_element);
+    }
+    else {
+      $form['path']['alias'][$alias->pid] = array(
+        '#type' => $type,
+        $key => $alias->dst,
+        '#size' => 40,
+      );
     }
-    else if ($path_count == 1 && $alias_count == 1) {
-      // This will delete the path that alias was originally pointing to:
-      path_set_alias(NULL, $alias);
-      path_set_alias($path);
-      path_set_alias($path, $alias);
+    $options[$alias->pid] = '';
+    if ($alias->active) {
+      $active = $alias->pid;
     }
   }
-}
-
-/**
- * Return a form for editing or creating an individual URL alias.
- */
-function path_form($edit = '') {
-  $form['#base'] = 'path_form';
-
-  $form['src'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Existing system path'),
-    '#default_value' => $edit['src'],
-    '#maxlength' => 64,
-    '#size' => 45,
-    '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1+2.'),
-    '#field_prefix' => url(NULL, NULL, NULL, TRUE) . (variable_get('clean_url', 0) ? '' : '?q=')
-  );
-  $form['dst'] = array(
+  if ($original_element) {
+    $form['path']['alias'][0] = $original_element;
+    $options[0] = '';
+  }
+  $options[-1] = '';
+  $form['path']['alias'][-1] = array(
     '#type' => 'textfield',
-    '#default_value' => $edit['dst'],
-    '#maxlength' => 64,
-    '#size' => 45,
-    '#description' => t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
-    '#field_prefix' => url(NULL, NULL, NULL, TRUE) . (variable_get('clean_url', 0) ? '' : '?q=')
+    '#size' => 40,
+    '#description' => t('Here you can add a new alias'),
   );
-
-  if ($edit['pid']) {
-    $form['pid'] = array('#type' => 'hidden', '#value' => $edit['pid']);
-    $form['submit'] = array('#type' => 'submit', '#value' => t('Update alias'));
+  $form['path']['alias']['#tree'] = TRUE;
+  if (user_access('administer url aliases')) {
+    $form['path']['active'] = array(
+      '#type' => 'radios',
+      '#options' => $options,
+      '#default_value' => $active,
+    );
   }
   else {
-    $form['submit'] = array('#type' => 'submit', '#value' => t('Create new alias'));
+    // We show X or nbsp; instead of radios.
+    foreach ($options as $pid => $value) {
+      $form['path']['active'][$pid]['#value'] = $pid == $active ? 'X' : '&nbsp;';
+    }
   }
-
+  $form['path']['save'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
   return $form;
 }
 
-/**
- * Implementation of hook_nodeapi().
- *
- * Allows URL aliases for nodes to be specified at node edit time rather
- * than through the administrative interface.
- */
-function path_nodeapi(&$node, $op, $arg) {
-  if (user_access('create url aliases') || user_access('administer url aliases')) {
-    switch ($op) {
-      case 'validate':
-        $node->path = trim($node->path);
-        if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s' AND src != '%s'", $node->path, "node/$node->nid"))) {
-          form_set_error('path', t('The path is already in use.'));
-        }
-        break;
-
-      case 'load':
-        $path = "node/$node->nid";
-        // We don't use drupal_get_path_alias() to avoid custom rewrite functions.
-        // We only care about exact aliases.
-        $result = db_query("SELECT dst FROM {url_alias} WHERE src = '%s'", $path);
-        if (db_num_rows($result)) {
-          $node->path = db_result($result);
-        }
-        break;
-
-      case 'insert':
-        // Don't try to insert if path is NULL. We may have already set
-        // the alias ahead of time.
-        if ($node->path) {
-          path_set_alias("node/$node->nid", $node->path);
-        }
-        break;
-
-      case 'update':
-        path_set_alias("node/$node->nid", $node->path, $node->pid);
-        break;
-
-      case 'delete':
-        $path = "node/$node->nid";
-        if (drupal_get_path_alias($path) != $path) {
-          path_set_alias($path);
-        }
-        break;
-    }
+function theme_path_form_table($form) {
+  $output = '';
+  if (isset($form['readonly'])) {
+    $output .= drupal_render($form['readonly']);
+  }
+  $rows = array();
+  uasort($form['alias'], '_element_sort');
+  foreach (element_children($form['alias']) as $key) {
+    $rows[] = array(
+      drupal_render($form['active'][$key]),
+      drupal_render($form['alias'][$key]),
+    );
   }
+  $output .= theme('table', array(t('Active'), t('Alias')), $rows);
+  $output .= drupal_render($form);
+  return $output;
 }
 
 /**
- * Implementation of hook_form_alter().
+ * Verify that a new URL alias is valid
  */
-function path_form_alter($form_id, &$form) {
-  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
-    $path = $form['#node']->path;
-    $form['path'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('URL path settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => empty($path),
-      '#access' => user_access('create url aliases'),
-      '#weight' => 30,
-    );
-    $form['path']['path'] = array(
-      '#type' => 'textfield',
-      '#default_value' => $path,
-      '#maxlength' => 250,
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-      '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
-    );
-    if ($path) {
-      $form['path']['pid'] = array(
-        '#type' => 'value',
-        '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", $path))
-      );
+function path_form_validate($form_id, $form_values) {
+  foreach ($form_values['alias'] as $pid => $alias) {
+    if ($alias && db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s'", $pid, $alias))) {
+      form_set_error('alias]['. $pid, t('The alias %alias is already in use.', array('%alias' => $alias)));
     }
   }
+  $active = $form_values['active'];
+  if ($active && empty($form_values['alias'][$active])) {
+    form_set_error('alias]['. $active, t('You can not set an empty alias active'));
+  }
 }
 
-
 /**
- * Implementation of hook_perm().
+ * Save a new URL alias to the database.
  */
-function path_perm() {
-  return array('create url aliases', 'administer url aliases');
+function path_form_submit($form_id, $form_values) {
+  foreach ($form_values['alias'] as $pid => $alias) {
+    // New, empty aliases are not saved
+    if ($pid != -1 || $alias) {
+      path_set_alias($form_values['path'], $alias, $pid == -1 ? NULL : $pid, $pid == $form_values['active']);
+    }
+  }
+
+  drupal_set_message(t('The aliases has been saved.'));
+  return $form_values['path'] ."/path";
 }
 
 /**
- * Return a listing of all defined URL aliases.
+ * Delete every alias of a path and every children of the path.
  */
-function path_overview() {
-  $sql = 'SELECT * FROM {url_alias}';
-  $header = array(
-    array('data' => t('Alias'), 'field' => 'dst', 'sort' => 'asc'),
-    array('data' => t('System'), 'field' => 'src'),
-    array('data' => t('Operations'), 'colspan' => '2')
-  );
-  $sql .= tablesort_sql($header);
-  $result = pager_query($sql, 50);
-
-  $destination = drupal_get_destination();
-  while ($data = db_fetch_object($result)) {
-    $rows[] = array($data->dst, $data->src, l(t('edit'), "admin/build/path/edit/$data->pid", array(), $destination), l(t('delete'), "admin/build/path/delete/$data->pid", array(), $destination));
-  }
-
-  if (!$rows) {
-    $rows[] = array(array('data' => t('No URL aliases available.'), 'colspan' => '4'));
-  }
-
-  $output = theme('table', $header, $rows);
-  $output .= theme('pager', NULL, 50, 0);
-  return $output;
+function path_delete($path) {
+  db_query("DELETE FROM {url_alias} WHERE src = '%s' OR src LIKE '%s/%%'", $path, $path);
 }
 
 /**
- * Fetch a specific URL alias from the database.
+ * Implementation of hook_nodeapi.
  */
-function path_load($pid) {
-  return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid));
+function path_nodeapi($node, $op) {
+  if ($op == 'delete') {
+    path_delete('node/'. $node->nid);
+  }
 }
 
 /**
- * Verify that a new URL alias is valid
+ * Implementation of hook_user.
  */
-function path_form_validate($form_id, $form_values) {
-  $src = $form_values['src'];
-  $dst = $form_values['dst'];
-  $pid = $form_values['pid'];
-
-  if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s'", $pid, $dst))) {
-    form_set_error('dst', t('The alias %alias is already in use.', array('%alias' => $dst)));
+function path_user($op, $edit, $user) {
+  if ($op == 'delete') {
+    path_delete('user/'. $user->uid);
   }
 }
 
 /**
- * Save a new URL alias to the database.
+ * Implementation of hook_comment.
  */
-function path_form_submit($form_id, $form_values) {
-  path_set_alias($form_values['src'], $form_values['dst'], $form_values['pid']);
-
-  drupal_set_message(t('The alias has been saved.'));
-  return 'admin/build/path';
-}
+function path_comment($comment, $op) {
+  if ($op == 'delete') {
+    path_delete('comment/'. $comment->cid);
+  }
+}
\ No newline at end of file

