Index: path.inc
===================================================================
RCS file: path.inc
diff -N path.inc
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ path.inc  9 Jul 2010 12:46:27 -0000
@@ -0,0 +1,612 @@
+<?php
+// $Id: path.inc,v 1.60 2010/03/10 19:36:14 webchick Exp $
+
+/**
+ * @file
+ * Functions to handle paths in Drupal, including path aliasing.
+ *
+ * These functions are not loaded for cached pages, but modules that need
+ * to use them in hook_boot() or hook exit() can make them available, by
+ * executing "drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);".
+ */
+
+/**
+ * Initialize the $_GET['q'] variable to the proper normal path.
+ */
+function drupal_path_initialize() {
+  if (!empty($_GET['q'])) {
+    $_GET['q'] = drupal_get_normal_path($_GET['q']);
+  }
+  else {
+    $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
+  }
+}
+
+/**
+ * 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.
+ *
+ * @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.
+ * @param $path_language
+ *   Optional language code to search the path with. Defaults to the page language.
+ *   If there's no path defined for that language it will search paths without
+ *   language.
+ *
+ * @return
+ *   Either a Drupal system path, an aliased path, or FALSE if no path was
+ *   found.
+ */
+function drupal_lookup_path($action, $path = '', $path_language = NULL) {
+  global $language_content;
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['cache'] = &drupal_static(__FUNCTION__);
+  }
+  $cache = &$drupal_static_fast['cache'];
+
+  if (!isset($cache)) {
+    $cache = array(
+      'map' => array(),
+      'no_source' => array(),
+      'whitelist' => NULL,
+      'system_paths' => array(),
+      'no_aliases' => array(),
+      'first_call' => TRUE,
+    );
+  }
+
+  // Retrieve the path alias whitelist.
+  if (!isset($cache['whitelist'])) {
+    $cache['whitelist'] = variable_get('path_alias_whitelist', NULL);
+    if (!isset($cache['whitelist'])) {
+      $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
+    }
+  }
+
+  $path_language = $path_language ? $path_language : $language_content->language;
+
+  if ($action == 'wipe') {
+    $cache = array();
+    $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
+  }
+  elseif ($cache['whitelist'] && $path != '') {
+    if ($action == 'alias') {
+      // During the first call to drupal_lookup_path() per language, load the
+      // expected system paths for the page from cache.
+      if (!empty($cache['first_call'])) {
+        $cache['first_call'] = FALSE;
+
+        $cache['map'][$path_language] = array();
+        // Load system paths from cache.
+        $cid = current_path();
+
+        // Small hack to allow pre-filling of system paths cache via
+        // drupal_static(), you can set $cache['system_paths'] and
+        // $cache['first_call'] then get multiple lookups for any
+        // arbitrary set of system paths..
+        $cached = FALSE;
+        if (!empty($cache['system_paths']) || ($cached = cache_get($cid, 'cache_path'))) {
+          if ($cached) {
+            $cache['system_paths'] = $cached->data;
+          }
+          // End hack.
+
+          // Now fetch the aliases corresponding to these system paths.
+          // We order by ASC and overwrite array keys to ensure the correct
+          // alias is used when there are multiple aliases per path.
+
+          // First try to fetch the path aliaes from cache. By using
+          // cache_get_multiple() we allow caching backends which support it to
+          // fetch all cached paths in a single request. $cid needs to be built
+          // from both path language and path.
+          foreach ($cache['system_paths'] as $system_path) {
+            $system_cids[] = $path_language . ':' .  $system_path;
+            $system_paths[$system_path] = $system_path;
+          }
+          if ($cached_paths = cache_get_multiple($system_cids, 'cache_path_alias')) {
+            foreach ($cached_paths as $cached) {
+              $system_path = str_replace($path_language . ':', '', $cached->cid);
+              // Remove this path from the aliases to query against the
+              // database.
+              unset($system_paths[$system_path]);
+              if ($cached->data) {
+                $cache['map'][$path_language][$system_path] = $cached->data;
+              }
+              else {
+                $cache['no_aliases'][$path_language][$system_path] = $system_path;
+              }
+            }
+          }
+          // Fetch remaining paths from the database.
+          if (!empty($system_paths)) {
+            $new_paths = db_query("SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND language IN (:language, :language_none) ORDER BY language ASC, pid ASC", array(
+              ':system' => $cache['system_paths'],
+              ':language' => $path_language,
+              ':language_none' => LANGUAGE_NONE,
+            ))->fetchAllKeyed();
+            $cache['map'][$path_language] += $new_paths;
+            // Keep a record of paths with no alias to avoid querying twice.
+            $new_misses = array_diff_key($cache['system_paths'], array_keys($cache['map'][$path_language]));
+            foreach ($new_misses as $new_miss) {
+              $path_cid = $path_language . ':' . $new_miss;
+              cache_set($path_cid, '', 'cache_path_alias');
+              $cache['no_aliases'][$path_language][$new_miss] = $new_miss;
+            }
+            // Cache the individual paths.
+            foreach ($new_paths as $new_path => $new_alias) {
+              $path_cid = $path_language . ':' . $new_path;
+              cache_set($path_cid, $new_alias, 'cache_path_alias');
+            }
+          }
+        }
+      }
+      // If the alias has already been loaded, return it.
+      if (isset($cache['map'][$path_language][$path])) {
+        return $cache['map'][$path_language][$path];
+      }
+      // Check the path whitelist, if the top_level part before the first /
+      // is not in the list, then there is no need to do anything further,
+      // it is not in the database.
+      elseif (!isset($cache['whitelist'][strtok($path, '/')])) {
+        return FALSE;
+      }
+      // For system paths which were not cached, query aliases individually.
+      else if (!isset($cache['no_aliases'][$path_language][$path])) {
+        $path_cid = $path_language . ':' . $path;
+        $cached = cache_get($path_cid, 'cache_path_alias');
+        if ($cached) {
+          $cache['map'][$path_language][$path] = $alias = $cached->data;
+        }
+        else {
+          // Get the most fitting result falling back with alias without language
+          $alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", array(
+            ':source' => $path,
+            ':language' => $path_language,
+            ':language_none' => LANGUAGE_NONE,
+          ))->fetchField();
+          $cache['map'][$path_language][$path] = $alias;
+          if ($alias) {
+            cache_set($path_cid, $alias, 'cache_path_alias');
+          }
+          else {
+            // When there is no alias, cache this fact as well, but only when
+            // the user is anonymous to avoid bloating the cache with paths
+            // like node/n/edit.
+            cache_set($path_cid, '', 'cache_path_alias');
+          }
+        }
+        return $alias;
+      }
+    }
+    // Check $no_source for this $path in case we've already determined that there
+    // isn't a path that has this alias
+    elseif ($action == 'source' && !isset($cache['no_source'][$path_language][$path])) {
+      // Look for the value $path within the cached $map
+      $source = '';
+      if (!isset($cache['map'][$path_language]) || !($source = array_search($path, $cache['map'][$path_language]))) {
+        $path_cid = $path_language . ':' . $path;
+
+        if ($cached = cache_get($path_cid, 'cache_path_source')) {
+          if (!empty($cached->data)) {
+            $cache['map'][$path_language][$source] = $source =  $cached->data;
+          }
+          else {
+            $cache['no_source'][$path_language][$path] = TRUE;
+          }
+        }
+        else {
+          // Get the most fitting result falling back with alias without language.
+          $source = db_query("SELECT source FROM {url_alias} WHERE alias = :alias AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", array(
+                       ':alias' => $path,
+                       ':language' => $path_language,
+                       ':language_none' => LANGUAGE_NONE))
+              ->fetchField();
+          cache_set($path_cid, FALSE, 'cache_path_source');
+          if ($source) {
+            $cache['map'][$path_language][$source] = $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_source.
+            $cache['no_source'][$path_language][$path] = TRUE;
+            // Add a cache entry with an empty string - this saves hitting the
+            // db for zero results. We can afford to do this because the
+            // whitelist will prevent attempts to cache paths like comment/$cid.
+            // And because memcache operates an LRU cache. However, only do this
+            // for anonymous users to avoid bloating the cache with paths like
+            // node/n/edit.
+            if ($GLOBALS['user']->uid) {
+              cache_set($path_cid, '', 'cache_path_source');
+            }
+          }
+        }
+      }
+      return $source;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+ * Cache system paths for a page.
+ *
+ * Cache an array of the system paths available on each page. We assume
+ * that aliases will be needed for the majority of these paths during
+ * subsequent requests, and load them in a single query during
+ * drupal_lookup_path().
+ */
+function drupal_cache_system_paths() {
+  // Check if the system paths for this page were loaded from cache in this
+  // request to avoid writing to cache on every request.
+  $cache = &drupal_static('drupal_lookup_path', array());
+  if (empty($cache['system_paths']) && !empty($cache['map'])) {
+    // Generate a cache ID (cid) specifically for this page.
+    $cid = current_path();
+    // The static $map array used by drupal_lookup_path() includes all
+    // system paths for the page request.
+    if ($paths = current($cache['map'])) {
+      $data = array_keys($paths);
+      $expire = REQUEST_TIME + (60 * 60 * 24);
+      cache_set($cid, $data, 'cache_path', $expire);
+    }
+  }
+}
+
+/**
+ * Given an internal Drupal path, return the alias set by the administrator.
+ *
+ * If no path is provided, the function will return the alias of the current
+ * page.
+ *
+ * @param $path
+ *   An internal Drupal path.
+ * @param $path_language
+ *   An optional language code to look up the path in.
+ *
+ * @return
+ *   An aliased path if one was found, or the original path if no alias was
+ *   found.
+ */
+function drupal_get_path_alias($path = NULL, $path_language = NULL) {
+  // If no path is specified, use the current page's path.
+  if ($path == NULL) {
+    $path = $_GET['q'];
+  }
+  $result = $path;
+  if ($alias = drupal_lookup_path('alias', $path, $path_language)) {
+    $result = $alias;
+  }
+  return $result;
+}
+
+/**
+ * Given a path alias, return the internal path it represents.
+ *
+ * @param $path
+ *   A Drupal path alias.
+ * @param $path_language
+ *   An optional language code to look up the path in.
+ *
+ * @return
+ *   The internal path represented by the alias, or the original alias if no
+ *   internal path was found.
+ */
+function drupal_get_normal_path($path, $path_language = NULL) {
+  $original_path = $path;
+
+  // Lookup the path alias first.
+  if ($source = drupal_lookup_path('source', $path, $path_language)) {
+    $path = $source;
+  }
+
+  // Allow other modules to alter the inbound URL. We cannot use drupal_alter()
+  // here because we need to run hook_url_inbound_alter() in the reverse order
+  // of hook_url_outbound_alter().
+  foreach (array_reverse(module_implements('url_inbound_alter')) as $module) {
+    $function = $module . '_url_inbound_alter';
+    $function($path, $original_path, $path_language);
+  }
+
+  return $path;
+}
+
+/**
+ * Check if the current page is the front page.
+ *
+ * @return
+ *   Boolean value: TRUE if the current page is the front page; FALSE if otherwise.
+ */
+function drupal_is_front_page() {
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['is_front_page'] = &drupal_static(__FUNCTION__);
+  }
+  $is_front_page = &$drupal_static_fast['is_front_page'];
+
+  if (!isset($is_front_page)) {
+    // As drupal_path_initialize updates $_GET['q'] with the 'site_frontpage' path,
+    // we can check it against the 'site_frontpage' variable.
+    $is_front_page = ($_GET['q'] == variable_get('site_frontpage', 'node'));
+  }
+
+  return $is_front_page;
+}
+
+/**
+ * Check if a path matches any pattern in a set of patterns.
+ *
+ * @param $path
+ *   The path to match.
+ * @param $patterns
+ *   String containing a set of patterns separated by \n, \r or \r\n.
+ *
+ * @return
+ *   Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
+ */
+function drupal_match_path($path, $patterns) {
+  $regexps = &drupal_static(__FUNCTION__);
+
+  if (!isset($regexps[$patterns])) {
+    $regexps[$patterns] = '/^(' . preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2'), preg_quote($patterns, '/')) . ')$/';
+  }
+  return (bool)preg_match($regexps[$patterns], $path);
+}
+
+/**
+ * Return the current 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
+ *   "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.
+ *
+ * @param $source
+ *   An optional system path for which an alias is being inserted.
+ *
+ * @return
+ *   An array containing a white list of path aliases.
+ */
+function drupal_path_alias_whitelist_rebuild($source = NULL) {
+  // When paths are inserted, only rebuild the whitelist if the system path
+  // has a top level component which is not already in the whitelist.
+  if (!empty($source)) {
+    $whitelist = variable_get('path_alias_whitelist', NULL);
+    if (isset($whitelist[strtok($source, '/')])) {
+      return $whitelist;
+    }
+  }
+  // For each alias in the database, get the top level component of the system
+  // path it corresponds to. This is the portion of the path before the first
+  // '/', if present, otherwise the whole path itself.
+  $whitelist = array();
+  $result = db_query("SELECT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias} GROUP BY path");
+  foreach ($result as $row) {
+    $whitelist[$row->path] = TRUE;
+  }
+  variable_set('path_alias_whitelist', $whitelist);
+  return $whitelist;
+}
+
+/**
+ * Fetch a specific URL alias from the database.
+ *
+ * @param $conditions
+ *   A string representing the source, a number representing the pid, or an
+ *   array of query conditions.
+ *
+ * @return
+ *   FALSE if no alias was found or an associative array containing the
+ *   following keys:
+ *   - source: The internal system path.
+ *   - alias: The URL alias.
+ *   - pid: Unique path alias identifier.
+ *   - language: The language of the alias.
+ */
+function path_load($conditions) {
+  if (is_numeric($conditions)) {
+    $conditions = array('pid' => $conditions);
+  }
+  elseif (is_string($conditions)) {
+    $conditions = array('source' => $conditions);
+  }
+  elseif (!is_array($conditions)) {
+    return FALSE;
+  }
+  $select = db_select('url_alias');
+  foreach ($conditions as $field => $value) {
+    $select->condition($field, $value);
+  }
+  return $select
+    ->fields('url_alias')
+    ->execute()
+    ->fetchAssoc();
+}
+
+/**
+ * Save a path alias to the database.
+ *
+ * @param $path
+ *   An associative array containing the following keys:
+ *   - source: The internal system path.
+ *   - alias: The URL alias.
+ *   - pid: (optional) Unique path alias identifier.
+ *   - language: (optional) The language of the alias.
+ */
+function path_save(&$path) {
+  $path += array('pid' => NULL, 'language' => LANGUAGE_NONE);
+
+  // Insert or update the alias.
+  $status = drupal_write_record('url_alias', $path, (!empty($path['pid']) ? 'pid' : NULL));
+
+  // Verify that a record was written.
+  if (isset($status) && $status) {
+    if ($status === SAVED_NEW) {
+      module_invoke_all('path_insert', $path);
+    }
+    else {
+      module_invoke_all('path_update', $path);
+    }
+    drupal_clear_path_cache($path['source']);
+  }
+}
+
+/**
+ * Delete a URL alias.
+ *
+ * @param $criteria
+ *   A number representing the pid or an array of criteria.
+ */
+function path_delete($criteria) {
+  if (!is_array($criteria)) {
+    $criteria = array('pid' => $criteria);
+  }
+  $path = path_load($criteria);
+  $query = db_delete('url_alias');
+  foreach ($criteria as $field => $value) {
+    $query->condition($field, $value);
+  }
+  $query->execute();
+  module_invoke_all('path_delete', $path);
+  drupal_clear_path_cache($path['source']);
+}
+
+/**
+ * Determine whether a path is in the administrative section of the site.
+ *
+ * By default, paths are considered to be non-administrative. If a path does not
+ * match any of the patterns in path_get_admin_paths(), or if it matches both
+ * administrative and non-administrative patterns, it is considered
+ * non-administrative.
+ *
+ * @param $path
+ *   A Drupal path.
+ * @return
+ *   TRUE if the path is administrative, FALSE otherwise.
+ *
+ * @see path_get_admin_paths()
+ * @see hook_admin_paths()
+ * @see hook_admin_paths_alter()
+ */
+function path_is_admin($path) {
+  $path_map = &drupal_static(__FUNCTION__);
+  if (!isset($path_map['admin'][$path])) {
+    $patterns = path_get_admin_paths();
+    $path_map['admin'][$path] = drupal_match_path($path, $patterns['admin']);
+    $path_map['non_admin'][$path] = drupal_match_path($path, $patterns['non_admin']);
+  }
+  return $path_map['admin'][$path] && !$path_map['non_admin'][$path];
+}
+
+/**
+ * Get a list of administrative and non-administrative paths.
+ *
+ * @return array
+ *   An associative array containing the following keys:
+ *   'admin': An array of administrative paths and regular expressions
+ *            in a format suitable for drupal_match_path().
+ *   'non_admin': An array of non-administrative paths and regular expressions.
+ *
+ * @see hook_admin_paths()
+ * @see hook_admin_paths_alter()
+ */
+function path_get_admin_paths() {
+  $patterns = &drupal_static(__FUNCTION__);
+  if (!isset($patterns)) {
+    $paths = module_invoke_all('admin_paths');
+    drupal_alter('admin_paths', $paths);
+    // Combine all admin paths into one array, and likewise for non-admin paths,
+    // for easier handling.
+    $patterns = array();
+    $patterns['admin'] = array();
+    $patterns['non_admin'] = array();
+    foreach ($paths as $path => $enabled) {
+      if ($enabled) {
+        $patterns['admin'][] = $path;
+      }
+      else {
+        $patterns['non_admin'][] = $path;
+      }
+    }
+    $patterns['admin'] = implode("\n", $patterns['admin']);
+    $patterns['non_admin'] = implode("\n", $patterns['non_admin']);
+  }
+  return $patterns;
+}
+
+/**
+ * Checks a path exists and the current user has access to it.
+ *
+ * @param $path
+ *   The path to check.
+ * @param $dynamic_allowed
+ *   Whether paths with menu wildcards (like user/%) should be allowed.
+ *
+ * @return
+ *   TRUE if it is a valid path AND the current user has access permission,
+ *   FALSE otherwise.
+ */
+function drupal_valid_path($path, $dynamic_allowed = FALSE) {
+  global $menu_admin;
+  // We indicate that a menu administrator is running the menu access check.
+  $menu_admin = TRUE;
+  if ($path == '<front>' || url_is_external($path)) {
+    $item = array('access' => TRUE);
+  }
+  elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) {
+    // Path is dynamic (ie 'user/%'), so check directly against menu_router table.
+    if ($item = db_query("SELECT * FROM {menu_router} where path = :path", array(':path' => $path))->fetchAssoc()) {
+      $item['link_path']  = $form_item['link_path'];
+      $item['link_title'] = $form_item['link_title'];
+      $item['external']   = FALSE;
+      $item['options'] = '';
+      _menu_link_translate($item);
+    }
+  }
+  else {
+    $item = menu_get_item($path);
+  }
+  $menu_admin = FALSE;
+  return $item && $item['access'];
+}
+
+/**
+ * Clear the path cache.
+ *
+ * @param $source
+ *   An optional system path for which an alias is being changed.
+ */
+function drupal_clear_path_cache($source = NULL) {
+  // Clear the drupal_lookup_path() static cache.
+  drupal_static_reset('drupal_lookup_path');
+  drupal_path_alias_whitelist_rebuild($source);
+}
Index: path.inc.patch
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/pathcache/path.inc.patch,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 path.inc.patch
--- path.inc.patch  6 Nov 2009 03:14:15 -0000 1.1.2.1
+++ path.inc.patch  9 Jul 2010 12:46:27 -0000
@@ -1,109 +0,0 @@
-Index: includes/path.inc
-===================================================================
-RCS file: /cvs/drupal/drupal/includes/path.inc,v
-retrieving revision 1.19.2.1
-diff -u -p -r1.19.2.1 path.inc
---- includes/path.inc  13 Oct 2008 21:06:41 -0000  1.19.2.1
-+++ includes/path.inc  6 Nov 2009 03:11:14 -0000
-@@ -46,10 +46,101 @@ function drupal_init_path() {
- function drupal_lookup_path($action, $path = '', $path_language = '') {
-   global $language;
-   // $map is an array with language keys, holding arrays of Drupal paths to alias relations
-+  /*
-   static $map = array(), $no_src = array(), $count;
-+  */
-+  static $map = array(), $no_src = array();
- 
-   $path_language = $path_language ? $path_language : $language->language;
- 
-+  // ************* Begin pathcache.module Path Caching Modifications **************
-+
-+  // Drupal core checks to see if the url_alias table is empty on each request.
-+  // We assume that the table is nonempty, since it would be very silly to enable
-+  // the Path Cache module if you had no paths to cache. Thus, we have eliminated
-+  // the $count variable altogether.
-+  /* 
-+  
-+  // Use $count to avoid looking up paths in subsequent calls if there simply are no aliases
-+  if (!isset($count)) {
-+    $count = (int)db_result(db_query('SELECT pid FROM {url_alias} LIMIT 1'));
-+  }
-+  */
-+
-+  // A wipe is most commonly performed by path_set_alias() in path.module via 
-+  // drupal_clear_path_cache() in common.inc. The intent it to clear the static
-+  // variables $map and $no_src, but we can also use it as an indicator that
-+  // the path cache is dirty.
-+  if ($action == 'wipe') {
-+    $map = array();
-+    $no_src = array();
-+    // This will flush the entire cache_path cache.
-+    // Drastic, but if running with a memcached caching backend, wildcards
-+    // are not supported so the whole bin must be flushed.
-+    cache_clear_all('*', 'cache_pathdst', TRUE);
-+    cache_clear_all('*', 'cache_pathsrc', TRUE);
-+  }
-+  elseif ($path != '') {
-+    // When action is 'alias' we are given a Drupal path and are looking to
-+    // see if there is an alias for it.
-+    if ($action == 'alias') {
-+      // First check the static variable in case we've looked up this system path earlier
-+      // in this request.
-+      if (isset($map[$path_language][$path])) {
-+        return $map[$path_language][$path];
-+      }
-+      // Not in the static variable. Try cache.
-+      $cid = $path . $path_language;
-+      $cache = cache_get($cid, 'cache_pathdst');
-+      if ($cache) {
-+        return $cache->data;
-+      }
-+      // Get the most fitting result falling back with alias without language.      
-+      $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language));
-+      // Store in static variable in case we encounter the same system path again
-+      // during this request.
-+      $map[$path_language][$path] = $alias;
-+      // Store in cache.
-+      cache_set($cid, $alias, 'cache_pathdst', time() + variable_get('pathcache_expire', 43200)); // 12 hours
-+      return $alias;
-+    }
-+    // When action is 'source' we are given a URL alias and need to return the
-+    // Drupal path.
-+    // 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_language][$path])) {
-+      $src = '';
-+      // Look for the value $path within the cached $map.
-+      if (!isset($map[$path_language]) || !($src = array_search($path, $map[$path_language]))) {
-+        $cid = $path . $path_language;
-+        $cache = cache_get($cid, 'cache_pathsrc');
-+        if ($cache) {
-+          return $cache->data;
-+        }
-+        
-+        // Get the most fitting result falling back with alias without language
-+        if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language))) {
-+          $map[$path_language][$src] = $path;
-+          cache_set($cid, $src, 'cache_pathsrc', time() + variable_get('pathcache_expire', 43200)); // 12 hours
-+        }
-+        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.
-+          // Setting this flag makes the array_search($path, $map[$path_language]) test
-+          // above false, and skips to return $src, below.
-+          $no_src[$path_language][$path] = TRUE;
-+          // We cache that the src is the same as the dst so we don't have to fail
-+          // the cache hit and do a pointless query.
-+          cache_set($cid, $path, 'cache_pathsrc', time() + variable_get('pathcache_expire', 43200)); // 12 hours
-+        }
-+      }
-+      return $src;
-+    }
-+  }
-+
-+  return FALSE;  
-+  // ************** End pathcache.module Path Caching Modifications ***************  
-   // 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}'));
Index: pathcache.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/pathcache/pathcache.info,v
retrieving revision 1.1
diff -u -p -r1.1 pathcache.info
--- pathcache.info  3 Dec 2008 21:29:46 -0000 1.1
+++ pathcache.info  9 Jul 2010 12:46:27 -0000
@@ -3,4 +3,5 @@ name = Path Cache
 description = Perform path caching for better URL alias performance.
 dependencies[] = path
 package = Caching
-core = 6.x
\ No newline at end of file
+core = 7.x
+files[] = pathcache.module
Index: pathcache.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/pathcache/pathcache.install,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 pathcache.install
--- pathcache.install 6 Nov 2009 03:16:57 -0000 1.1.2.3
+++ pathcache.install 9 Jul 2010 12:46:27 -0000
@@ -7,80 +7,16 @@
  */
 
 /**
- * Implementation of hook_requirements().
- */
-function pathcache_requirements($phase) {
-  // Ensure translations don't break at install time.
-  $t = get_t();
-
-  $aliases_exist = (int)db_result(db_query('SELECT pid FROM {url_alias} LIMIT 1'));
-  $requirements['pathcache'] = array(
-    'title' => $t('Path Cache'),
-    'value' => $aliases_exist ? t('URL aliases present') : t('No URL aliases exist'),
-    'severity' => $aliases_exist ? REQUIREMENT_OK : REQUIREMENT_WARNING,
-    'description' => $aliases_exist ? '' : $t('The path cache module expects to have URL aliases in your url_aliases database table. Currently that table is empty. If you are not using path aliases (by using the Path module, for example) the Path Cache module will actually slow down your site instead of speeding it up.'),
-  );
-
-  return $requirements;
-}
-
-/**
- * Implementation of hook_install().
- */
-function pathcache_install() {
-  drupal_install_schema('pathcache');
-}
-
-/**
- * Implementation of hook_uninstall().
- */
-function pathcache_uninstall() {
-  // Remove tables.
-  drupal_uninstall_schema('pathcache');
-}
-
-/**
  * Implementation of hook_schema().
  */
 function pathcache_schema() {
-  $schema['cache_pathsrc'] = array(
-    'description' => t('Cache table for URL aliases.'),
-    'fields' => array(
-      'cid' => array(
-        'description' => t('Primary Key: Unique cache ID.'),
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => ''),
-      'data' => array(
-        'description' => t('A collection of data to cache.'),
-        'type' => 'blob',
-        'not null' => FALSE,
-        'size' => 'big'),
-      'expire' => array(
-        'description' => t('A Unix timestamp indicating when the cache entry should expire, or 0 for never.'),
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0),
-      'created' => array(
-        'description' => t('A Unix timestamp indicating when the cache entry was created.'),
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0),
-      'headers' => array(
-        'description' => t('Any custom HTTP headers to be added to cached data.'),
-        'type' => 'text',
-        'not null' => FALSE),
-      'serialized' => array(
-        'description' => t('A flag to indicate whether content is serialized (1) or not (0).'),
-        'type' => 'int',
-        'size' => 'small',
-        'not null' => TRUE,
-        'default' => 0)
-      ),
-    'indexes' => array('expire' => array('expire')),
-    'primary key' => array('cid'),
-    );
-  $schema['cache_pathdst'] = $schema['cache_pathsrc'];
+  $schema = array();
+  $t = get_t();
+  $description = $t('Cache table for URL aliases');
+  $cache_schema = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_path_source'] = $cache_schema;
+  $schema['cache_path_source']['description'] = $description;
+  $schema['cache_path_alias'] = $cache_schema;
+  $schema['cache_path_alias']['description'] = $description;
   return $schema;
-}
\ No newline at end of file
+}
Index: pathcache.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/pathcache/pathcache.module,v
retrieving revision 1.1
diff -u -p -r1.1 pathcache.module
--- pathcache.module  3 Dec 2008 21:29:46 -0000 1.1
+++ pathcache.module  9 Jul 2010 12:46:27 -0000
@@ -7,11 +7,43 @@
  */
 
 /**
- * Implementation of hook_flush_caches().
+ * Implements hook_flush_caches().
  */
 function pathcache_flush_caches() {
-  return array(
-    'cache_pathsrc',
-    'cache_pathdst',
-  );
-}
\ No newline at end of file
+  return array('cache_path_source', 'cache_path_alias');
+}
+
+/**
+ * Helper function to clear the alias and source caches for a path.
+ */
+function pathcache_clear_cache($path) {
+  // Since adding a path in any language could affect results for both
+  // that language and the default language, clear all possible
+  // combinations here.
+  $languages = array_unique(array($path['language'],  LANGUAGE_NONE));
+  foreach ($languages as $language) {
+    cache_clear_all($language . ':' . $path['source'], 'cache_path_alias');
+    cache_clear_all($language . ':' . $path['alias'], 'cache_path_source');
+  }
+}
+
+/**
+ * Implements hook_path_insert().
+ */
+function pathcache_path_insert($path) {
+  pathcache_clear_cache($path);
+}
+
+/**
+ * Implements hook_path_update().
+ */
+function pathcache_path_update($path) {
+  pathcache_clear_cache($path);
+}
+
+/**
+ * Implements hook_path_delete().
+ */
+function pathcache_path_delete($path) {
+  pathcache_clear_cache($path);
+}

