Index: country_code.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/country_code/country_code.module,v
retrieving revision 1.93
diff -u -p -r1.93 country_code.module
--- country_code.module	12 Nov 2008 14:41:59 -0000	1.93
+++ country_code.module	26 Nov 2008 16:35:01 -0000
@@ -33,6 +33,8 @@ define('COUNTRY_CODE_DEFAULT', 'default'
  * Set global $language variable based on user's country.
  */
 function country_code_init() {
+  global $user;
+  global $language;
   // Language negotiations must be set to country based.
   if (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_COUNTRY_CODE) {
     return;
@@ -44,7 +46,14 @@ function country_code_init() {
   }
 
   // Set a country-based language if applicable.
-  _country_code_init_language();
+  // This is run in hook_boot() for anonymous users when caching is enabled,
+  // so ensure it's not run twice.
+  if ($user->uid != 0 || variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED) {
+    _country_code_boot_language();
+    // Reset the path with the correct language. This is needed e.g. to ensure that
+    // language-specific aliases work.
+    $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'), $language->language);
+  }
 }
 
 /**
@@ -137,40 +146,89 @@ function _country_code_request_is_extern
 }
 
 /**
- * Set a country-based language if applicable.
+ * Implementation of hook_boot().
+ *
+ * Include url rewriting functions.
  */
-function _country_code_init_language() {
+function country_code_boot() {
+  global $user;
   global $language;
+  // Load URL rewriting.
+  // drupal_get_path() is not declared yet.
+  // TODO: handle conflicts with other modules declaring url rewrite functions.
+  if (!(function_exists('custom_url_rewrite_inbound') || function_exists('custom_url_rewrite_outbound'))) {
+    $path = dirname(__FILE__) . '/country_code.inc';
+    require_once($path);
+  }
+  _country_code_init_code();
+
+  // Handle language negotiation for anonymous users, since we're showing
+  // language dependent content at the same path.
+  // Only do this if language negotiation is set to country based and if
+  // page caching is enabled.
+  // @TODO remove when http://drupal.org/node/337378 is fixed.
 
+  // Return early if nothing needs to be done.
+  if (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_COUNTRY_CODE || variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED || $user->uid == 0) {
+    return;
+  }
+  // Language negotiation for anonymous and authenticated users shares the same function.
+  _country_code_boot_language();
+
+  // Add the country code and language variable into the REQUEST_URI. The cache
+  // is keyed by the REQUEST_URI, so this will ensure that pages get cached
+  // and recalled from cache for the appropriate country/language combination.
+  $parts = parse_url(request_uri());
+  $new_uri = $parts['path'] .'?'
+           . (empty($parts['query']) ? '' : $parts['query'] . '&')
+           . country_code()
+           . $_SESSION['country_code_language'];
+  $_SERVER['REQUEST_URI'] = $new_uri;
+}
+
+/**
+ * Shared language handling between anonymous and authenticated users.
+ */
+function _country_code_boot_language() {
+  global $user;
+  global $language;
   $languages = language_list('enabled');
   $languages = $languages[1];
   $country_languages = country_code_languages();
-
   // Recognize a language passed through GET.
   // Only accept an incoming language if it's one of the current country's languages.
   if (isset($_GET['language']) && (isset($country_languages[$_GET['language']]) || isset($languages[$_GET['language']]))) {
     $_SESSION['country_code_language'] = $_GET['language'];
   }
-  // If we are viewing a node, priorize the node's language.
-  // We do this partly because, when we switch countries when viewing a node
-  // that has translations, we link to a node in a language to be set.
-  // See country_code_get_context_language().
-  elseif ($node = menu_get_object()) {
+
+  // menu_get_object() and node_load() are not available during hook_boot(). So
+  // do a direct query on the {node} table if they're not available.
+  // @TODO remove when we no longer need to determine language during hook_boot()
+  // for anonymous users.
+  elseif (function_exists('menu_get_object') && $node = menu_get_object()) {
     $node_language = $node && $node->tnid ? $node->language : NULL;
     $_SESSION['country_code_language'] = country_code_language(NULL, $node_language);
   }
-  // If there is a langauge already in the session, look for it.
+  elseif (!function_exists('menu_get_object')
+          && $user->uid == 0
+          && arg(0) == 'node'
+          && is_numeric(arg(1))
+          && $node_language = db_result(db_query('SELECT language FROM {node} WHERE nid = %d' && tnid <> 0, arg(1)))) {
+    $_SESSION['country_code_language'] = country_code_language(NULL, $node_language);
+  }
+  // If there is a language already in the session, look for it.
   elseif (isset($_SESSION['country_code_language'])) {
     $set = FALSE;
     $language_short = (strlen($_SESSION['country_code_language']) > 2) ? substr($_SESSION['country_code_language'], 0, 2) : $_SESSION['country_code_language'];
     // If it's global, return a two-digit language
-    if ($country == COUNTRY_CODE_GLOBAL && isset($country_languages[$language_short])) {
-      $_SESSION['country_code_language'] = $language_short;
-      $set = TRUE;
-    }
+    // @TODO $country is undefined?
+    //if ($country == COUNTRY_CODE_GLOBAL && isset($country_languages[$language_short])) {
+    //  $_SESSION['country_code_language'] = $language_short;
+    //  $set = TRUE;
+    //}
     // If we have a country, priorize a language that has the same base
     // language as the current session language.
-    else {
+    //else {
       foreach (array_keys($country_languages) as $langcode) {
         if (substr($langcode, 0, 2) == $language_short) {
           $_SESSION['country_code_language'] = $langcode;
@@ -178,7 +236,7 @@ function _country_code_init_language() {
           break;
         }
       }
-    }
+    //}
     // If no language was set, use the default.
     if (!$set) {
       $_SESSION['country_code_language'] = country_code_language();
@@ -188,26 +246,7 @@ function _country_code_init_language() {
   else {
     $_SESSION['country_code_language'] = country_code_language();
   }
-
   $language = $languages[$_SESSION['country_code_language']];
-  // Reset the path with the correct language. This is needed e.g. to ensure that
-  // language-specific aliases work.
-  $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'), $language->language);
-}
-
-/**
- * Implementation of hook_boot().
- *
- * Include url rewriting functions.
- */
-function country_code_boot() {
-  // drupal_get_path() is not declared yet.
-  // TODO: handle conflicts with other modules declaring url rewrite functions.
-  if (!(function_exists('custom_url_rewrite_inbound') || function_exists('custom_url_rewrite_outbound'))) {
-    $path = dirname(__FILE__) . '/country_code.inc';
-    require_once($path);
-  }
-  _country_code_init_code();
 }
 
 /**
@@ -663,7 +702,7 @@ function country_code_language($country 
     return $user->language;
   }
   // Otherwise, return a supported language based on the user's browser data.
-  elseif (variable_get('country_code_browser_language', 1) && $language = language_from_browser()) {
+  elseif (variable_get('country_code_browser_language', 1) && $language = _country_code_language_from_browser()) {
     if (isset($languages[$language->language])) {
       return $language->language;
     }
@@ -682,6 +721,41 @@ function country_code_language($country 
 }
 
 /**
+ * language_from_browser() isn't available during hook_boot()
+ * so clone it.
+ */
+function _country_code_language_from_browser() {
+  // Specified by the user via the browser's Accept Language setting
+  // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
+  $browser_langs = array();
+
+  if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+    $browser_accept = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
+    for ($i = 0; $i < count($browser_accept); $i++) {
+      // The language part is either a code or a code with a quality.
+      // We cannot do anything with a * code, so it is skipped.
+      // If the quality is missing, it is assumed to be 1 according to the RFC.
+      if (preg_match("!([a-z-]+)(;q=([0-9\\.]+))?!", trim($browser_accept[$i]), $found)) {
+        $browser_langs[$found[1]] = (isset($found[3]) ? (float) $found[3] : 1.0);
+      }
+    }
+  }
+
+  // Order the codes by quality
+  arsort($browser_langs);
+
+  // Try to find the first preferred language we have
+  $languages = language_list('enabled');
+  foreach ($browser_langs as $langcode => $q) {
+    if (isset($languages['1'][$langcode])) {
+      return $languages['1'][$langcode];
+    }
+  }
+}
+
+
+
+/**
  * Fetch an individual country.
  *
  * Because we use '%country_code_country' as a wildcard in our hook_menu()
