# This patch file was generated by NetBeans IDE
# This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
# It uses platform neutral UTF-8 encoding.
# Above lines and this line are ignored by the patching process.
Index: contributions/modules/millennium/millennium.admin.inc
--- contributions/modules/millennium/millennium.admin.inc Base (1.1.2.26)
+++ contributions/modules/millennium/millennium.admin.inc Locally Modified (Based On 1.1.2.26)
@@ -961,7 +961,7 @@
     #array('http://library.hku.hk', 'i', 1100000),
     
     // Bib record tests
-    array('http://millenium.itesm.mx:2082', 'b', 1179000),
+    array('http://millennium.itesm.mx', 'b', 1179000),
     array('http://library.dcccd.edu', 'b', 1100000),
     #array('http://irma.aadl.org', 'b', 1321000),
     #array('http://catalog.nypl.org', 'b', 1100000),
Index: contributions/modules/millennium/millennium.import.inc
--- contributions/modules/millennium/millennium.import.inc Base (1.1.2.19)
+++ contributions/modules/millennium/millennium.import.inc Locally Modified (Based On 1.1.2.19)
@@ -92,12 +92,12 @@
  */
 function millennium_mass_fetch($recnums, $base_url = false) {
   // This seems to be the limit for WebOPACs, do not change =)
-  $max_records_for_bookcart = 50; 
+  $max_records_for_bookcart = 40;
 
   // Ping the server to ensure we have obtained a cookie.
   $ok = millennium_ping($base_url);
   if (!$ok) {
-    return array();
+    return array('found' => array(), 'not_found' => $recnums);
   }
 
   // Don't devote more than X% of max_execution_time
@@ -165,7 +165,7 @@
  */
 function millennium_fetch_records_via_bookcart($recnums, $complete_holdings = false, $base_url = false) {
   // Set the following to true for lots and lots of debugging info =)
-  static $debug = false;
+  $debug = false;
 
   // Start timer to measure average performance
   timer_start("millennium_fetch_records_via_bookcart");
@@ -179,8 +179,15 @@
   $found_items = array();
   $not_found_items = array();
 
-  // Issue N requests to add items to the bookcart, 25 records at a time (apparent maximum)
-  $chunks = array_chunk($recnums, 25);
+  // Clear the cart
+  $path = "/search?//1,-1,-1,B/browse?clear_saves=1";
+  $result = millennium_http_request("{$base_url}{$path}");
+  if ($result->code != 200) {
+    return array('found' => array(), 'not_found' => $recnums);
+  }
+
+  // Issue N requests to add items to the bookcart, 25 records at a time is apparent maximum
+  $chunks = array_chunk($recnums, 20);
   foreach ($chunks as $chunk) {
     // Add items from $recnums array to the cart
     $path = '/search*eng/X//1,1,1/?save=' . implode("&save=", $chunk);
@@ -291,35 +298,39 @@
   // positives get sent to the not_found_items array.
   $found_items_indexes = array_keys($found_items);
   $found_items_index = 0; // start at first item in $found_items
-  foreach ($marc_matches as $match) {
+  foreach ($marc_matches as $count => $match) {
     $marc_text = decode_entities($match[1]);
-    #$marc_text = $match[1]; // TODO FIND OUT if this is better.
     $current_found_item = $found_items[$found_items_indexes[$found_items_index]];
     $current_recnum = $found_items_indexes[$found_items_index];
-    $times = 0; // Counter to protect against infinite loop
-    $max_times = sizeof($found_items);
+    $compare_marc = _millennium_only_letters($marc_text);
+    if ($debug) {
+      dpm("MARC #{$count} : {$compare_marc}");
+    }
     // Check if current found_item title coincides with title in this MARC
     while ($current_recnum) {
       // TODO: The next line is too simplistic; maybe it needs to parse the marc
       //   and only use the 240|a and 245|a portion to compare to the title in 
       //   $current_found_item["title"]
-      if (stripos(_millennium_only_letters($marc_text), drupal_substr(_millennium_only_letters($current_found_item["title"]), 0, 8)) === false) {
+      $compare_title = $current_found_item["title"];
+      $compare_title = _millennium_only_letters($compare_title);
+      if (mb_strpos($compare_marc, $compare_title) === false) {
         // No; then remove this found_item and skip to the next one, check again
         if ($debug) {
-          drupal_set_message("$current_recnum : Found item and marc not in sync! Current found_item:");
+          drupal_set_message("NOT IN SYNC ={$current_recnum}============");
+          drupal_set_message("Current found_item:");
           dpm($current_found_item);
-          drupal_set_message("Found_item title: " . _millennium_only_letters($current_found_item["title"]));
-          drupal_set_message("MARC: " . _millennium_only_letters($marc_text));
+          drupal_set_message("Found_item: " . $compare_title);
         }
         $not_found_items[$current_recnum] = $current_recnum;
         unset($found_items[$current_recnum]);
         $found_items_index++;
         $current_found_item = $found_items[$found_items_indexes[$found_items_index]];
         $current_recnum = $found_items_indexes[$found_items_index];
-        
-      } else {
+      }
+      else {
         if ($debug) {
-          drupal_set_message("$current_recnum : Found item and marc in sync!");
+          drupal_set_message("In sync ===={$current_recnum}=========");
+          drupal_set_message("Found_item: " . $compare_title);
         }
         $found_items[$current_recnum]['marc'] = $marc_text;
         break;
@@ -343,6 +354,16 @@
     }
   }
 
+  // If any candidate found items didn't match to any MARC, clear them off the
+  // $found_items array
+  $recnum = $found_items_indexes[$found_items_index];
+  while ($recnum) {
+    $not_found_items[$recnum] = $recnum;
+    unset($found_items[$recnum]);
+    $found_items_index++;
+    $recnum = $found_items_indexes[$found_items_index];
+  }
+
   // Grab XRECORD for item
   #foreach ($found_items as $item_recnum => $dummy) {
   #  $result = millennium_fetch_recordpage($item_recnum, 'xml', $base_url);
@@ -352,6 +373,11 @@
   #}
   #dpm($found_items);
 
+  // Debug, confirm found/not found items
+  if ($debug) {
+    _millennium_confirm_found_not_found($base_url, $found_items, $not_found_items);
+  }
+
   // Clear the cart
   $path = "/search?//1,-1,-1,B/browse?clear_saves=1";
   $dummy = millennium_http_request("{$base_url}{$path}");
@@ -368,10 +394,54 @@
 
 function _millennium_only_letters($string) {
   // Remove MARC indicators
-  $new = preg_replace("/\|[a-z]/", "", $string);
+  $new = mb_ereg_replace("\|[a-z]", "", $string);
   // Remove things like "[videorecording]" in titles
-  $new = preg_replace("/\[[^\]]+\]/", "", $new);
+  $new = mb_ereg_replace("\[[^\]]+\]*", "", $new);
+  // Remove things like "{232}" in titles
+  $new = mb_ereg_replace("{[0-9]+}*", "", $new);
   // Leave only letters and numbers
-  $new = drupal_strtolower(preg_replace("/[^a-z0-9]/i", "", $new));
+  $new = drupal_strtolower(mb_ereg_replace("\W", "", $new));
+  $new = str_replace(array(' ', "\n", "\r", '"'), '', $new);
   return $new;
 }
+
+/**
+ * Debug function that confirms if found/not found records indeed exist or not
+ * in OPAC. CAUTION: SLOW as it requests records one by one.
+ *
+ * @param $found_items
+ *   Array of found records.
+ * @param $not_found_items
+ *   Array of non-found record numbers.
+ */
+function _millennium_confirm_found_not_found($base_url, $found_items, $not_found_items) {
+  $false_positives = array();
+  foreach ($found_items as $recnum => $found_item) {
+    $tmp = millennium_fetch_recordpage($recnum, 'plain', $base_url);
+    if (strpos($tmp->data, "No Such Record")) {
+      $false_positives[] = $recnum;
+    }
+  }
+  if ($false_positives) {
+    drupal_set_message("FALSE POSITIVES FOR {$base_url}:");
+    dpm($false_positives);
+    #file_put_contents("c:\\temp\\false_positives.txt", var_export($false_positives, TRUE));
+  } else {
+    drupal_set_message("NO FALSE POSITIVES FOR {$base_url}!");
+  }
+
+  $false_negatives = array();
+  foreach ($not_found_items as $recnum => $not_found_item) {
+    $tmp = millennium_fetch_recordpage($recnum, 'plain', $base_url);
+    if (!strpos($tmp->data, "No Such Record")) {
+      $false_negatives[] = $recnum;
+    }
+  }
+  if ($false_negatives) {
+    drupal_set_message("FALSE NEGATIVES FOR {$base_url}:");
+    dpm($false_negatives);
+    #file_put_contents("c:\\temp\\false_positives.txt", var_export($false_positives, TRUE));
+  } else {
+    drupal_set_message("NO FALSE NEGATIVES FOR {$base_url}!");
+  }
+}
\ No newline at end of file
Index: contributions/modules/millennium/millennium.module
--- contributions/modules/millennium/millennium.module Base (1.13.2.33.2.2.2.80)
+++ contributions/modules/millennium/millennium.module Locally Modified (Based On 1.13.2.33.2.2.2.80)
@@ -10,6 +10,7 @@
 define('MILLENNIUM_PERFORMANCE_HISTORY_SIZE', 20);
 define('MILLENNIUM_RECORD_PREG_CHECKDIGIT', '/^[bi][0-9]{5,10}x?$/i');
 define('MILLENNIUM_RECORD_PREG_NOCHECKDIGIT', '/^[bi][0-9]{5,10}$/i');
+define('MILLENNIUM_SESSION_COOKIE_LIFETIME', 300);
 
 /**
  * Implementation of hook_init().
@@ -2793,7 +2794,7 @@
   $base_url = millennium_get_real_baseurl($url);
   $cid = "millennium_http_request_" . $base_url;
 
-  #drupal_set_message("millennium_http_request(): start. URL =" . l($url, $url));
+  #drupal_set_message("millennium_http_request(): start. URL =" . l($url, $url, array('attributes' => array('target' => 'webopac'))));
 
   // If no Cookie given in header, try to fetch one from cache
   if (isset($cookie_static_cache[$base_url])) {
@@ -2848,7 +2849,7 @@
 
     if ($cookie_set) {
       $session_cookie = implode("; ", $all);
-      cache_set($cid, $session_cookie, 'cache', time() + 600);
+      cache_set($cid, $session_cookie, 'cache', time() + MILLENNIUM_SESSION_COOKIE_LIFETIME);
       $cookie_static_cache[$base_url] = $session_cookie;
       #drupal_set_message("millennium_http_request(): $base_url cache_set for cookie $session_cookie");
     }
