# 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.21)
+++ contributions/modules/millennium/millennium.admin.inc Locally Modified (Based On 1.1.2.21)
@@ -298,6 +298,7 @@
     '#options' => array(
       'list' => t('Record number listing'), 
       'range' => t('Record number range'), 
+      'query' => t('Enter a keyword query'),
       'existing' => t('All already-imported records (@num items)', array("@num" => $tot_imported_nodes)),
       'test' => t('Run predefined tests against some known OPACs'),
     ),
@@ -361,6 +362,25 @@
     '#size' => 7,
     '#title' => t('Ending number'),
   );
+  $form['query'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Keyword query'),
+    '#attributes' =>  array('id' => 'query-fieldset'),
+  );
+  $form['query']['kw'] = array(
+    '#type' => 'textfield',
+    '#default_value' => variable_get('millennium_import_form_kw', ''),
+    '#size' => 80,
+    '#title' => t('Keywords to search for'),
+    '#description' => t('Type in keywords as you would type them in the WebOPAC keyword search. Some lesser-known available fields are: mattype:mattype[letter] branch:branch[locationcode]')
+  );
+  $form['query']['num_items'] = array(
+    '#type' => 'textfield',
+    '#default_value' => variable_get('millennium_import_form_num_items', 10),
+    '#size' => 5,
+    '#title' => t('Maximum number of items to import'),
+    '#description' => t('Enter the number of items to import.')
+  );
   $form['force_update'] = array(
     '#type' => 'radios',
     '#title' => t('Import action'),
@@ -402,7 +422,8 @@
 function millennium_admin_queue_validate($form, &$form_state) {
 
   if ($form_state["values"]["source"] == "range" || 
-    $form_state["values"]["source"] == "range") {
+    $form_state["values"]["source"] == "list" ||
+    $form_state["values"]["source"] == "query") {
     $base_url = $form_state['values']['millennium_webopac_baseurl'];
     if (!$base_url) {
       form_set_error('millennium_webopac_baseurl', t("Not a valid base URL."));
@@ -448,7 +469,16 @@
         );
     }
   }
+
+  if ($form_state["values"]["source"] == "query") {
+    if (!$form_state['values']['kw']) {
+      form_set_error('kw', t("You must enter a query."));
 }
+    if (($form_state['values']['num_items']+0) <= 0) {
+      form_set_error('num_items', t("You must enter a number greater than 0."));
+    }
+  }
+}
 
 /**
  * Submit function for millennium_admin_queue() form
@@ -467,6 +497,8 @@
     variable_set('millennium_range_form_type', $form_state["values"]["type"]);
     variable_set('millennium_range_form_start', $form_state["values"]["start"]);
     variable_set('millennium_range_form_end', $form_state["values"]["end"]);
+    variable_set('millennium_import_form_kw', $form_state["values"]["kw"]);
+    variable_set('millennium_import_form_num_items', $form_state["values"]["num_items"]);
 
     if ($form_state["clicked_button"]["#value"] == t("Process these records during cron runs")) {
 
@@ -569,6 +601,19 @@
         }
       }
 
+      if ($form_state["values"]["source"] == "query") {
+        require_once 'millennium.import.inc';
+        $base_url = $form_state['values']['millennium_webopac_baseurl'];
+        $records = millennium_query_scrape(
+          $form_state["values"]["kw"],
+          $base_url,
+          $form_state["values"]["num_items"]
+        );
+        foreach ($records as $recnum) {
+          $records_to_crawl[$base_url][$recnum] = $recnum;
+        }
+      }
+
       if ($form_state["values"]["source"] == "existing") {
         $result = db_query("SELECT bib_recnum, base_url FROM {millennium_node_bib} ORDER BY base_url");
         while ($data = db_fetch_object($result)) {
Index: contributions/modules/millennium/millennium.import.inc
--- contributions/modules/millennium/millennium.import.inc Base (1.1.2.14)
+++ contributions/modules/millennium/millennium.import.inc Locally Modified (Based On 1.1.2.14)
@@ -2,6 +2,85 @@
 // $Id: millennium.import.inc,v 1.1.2.14 2010/02/17 20:36:06 janusman Exp $
 
 /**
+ * Recieves a keyword query, and returns scraped record numbers to later harvest
+ * via other functions like millennium_mass_fetch().
+ * 
+ * @param $query
+ *   a string with a keyword query, same as that typed on the /search/X URL on
+ *   the WebOPAC.
+ * @param $base_url
+ * @param $limit
+ *   Maximum number of records to return.
+ */
+function millennium_query_scrape($query, $base_url = false, $limit = 100) {
+  $records = array();
+  $headers = array();
+
+  if ($base_url == false) {
+    $base_url = millennium_get_real_baseurl();
+  } else {
+    $base_url = millennium_get_real_baseurl($base_url);
+  }
+
+  // TODO: Cache the session cookie!
+  // Build query URL
+  $path = "/search/X?" . drupal_urlencode($query);
+
+  // This will loop while more pages of results are found
+  while (TRUE) {
+    $url = $base_url . $path;
+    
+    // Launch
+    $result = drupal_http_request($url, $headers);
+    if ($result->code != 200) {
+      break;
+    }
+      
+    // If no sesion cookie yet, store it for next calls
+    if (empty($headers['Cookie'])) {
+      if (preg_match_all('/(SESSION_SCOPE=[0-9]+|III_SESSION_ID=[a-zA-Z0-9\.]+)/', $result->headers["Set-Cookie"], $matches)) {
+        $headers['Cookie'] = implode("; ", $matches[1]);
+      }
+    }
+
+    // Look for <input type="checkbox" name="save" value="b1890629" >
+    $ok = preg_match_all(
+      '/name="save" value="(b[0-9]+)"[^>]+>/si',
+      $result->data,
+      $matches
+    );
+    if (!$ok) {
+      // Try second regexp: cart button
+      $ok = preg_match_all(
+        '/\?save=(b[0-9]+)/si',
+        $result->data,
+        $matches
+      );
+    }
+    if (!$ok) {
+      break;
+    }
+    $records = array_merge($records, $matches[1]);
+    if (sizeof($records) >= $limit) {
+      break;
+    }
+
+    // TODO: Cycle thru pages!
+    #dpm($result);
+
+    // Look for "next" pager
+    $ok = preg_match('/<td .*?class=.browsePager.*?<strong>[0-9]+<\/strong>[^<]*<a href="(\/search[^"]+)">([0-9]+)/si', $result->data, $pager_link_matches);
+    #dpm($pager_link_matches);
+    if (!$ok) {
+      break;
+    }
+    // Set path for next call from "Next" pager
+    $path = $pager_link_matches[1];
+  }
+  return array_splice($records, 0, $limit);
+}
+
+/**
  * Gets a sequential number of records obeying PHP's max_execution_time setting.
  * @param $recnums 
  *   Array of item or bib numbers to fetch.
Index: contributions/modules/millennium/millennium_admin.js
--- contributions/modules/millennium/millennium_admin.js Base (1.1.2.2)
+++ contributions/modules/millennium/millennium_admin.js Locally Modified (Based On 1.1.2.2)
@@ -10,6 +10,7 @@
         $("#edit-offline-refresh-wrapper").hide();
         $("input[@name=\'offline_refresh\']").change();
         $("#baseurl-table").show();
+        $("#query-fieldset").hide();
         $("#import-options").show();
         $("#edit-using-cron").show();
       }
@@ -20,14 +21,25 @@
         $("#options-fieldset").show();
         $("#edit-using-cron").show();
         $("#baseurl-table").show();
+        $("#query-fieldset").hide();
         $("#import-options").show();
         $("#edit-using-cron").show();
       }
+       if (choice == "query") {
+        $("#range-fieldset").hide();
+        $("#list-fieldset").hide();
+        $("#edit-offline-refresh-wrapper").hide();
+        $("#baseurl-table").show();
+        $("#query-fieldset").show();
+        $("#import-options").show();
+        $("#edit-using-cron").hide();
+      }
       if (choice == "existing") {
         $("#range-fieldset").hide();
         $("#list-fieldset").hide();
         $("#edit-offline-refresh-wrapper").show();
         $("#baseurl-table").hide();
+        $("#query-fieldset").hide();
         $("#import-options").show();
         $("#edit-using-cron").show();
       }
@@ -36,6 +48,7 @@
         $("#list-fieldset").hide();
         $("#edit-offline-refresh-wrapper").hide();
         $("#baseurl-table").hide();
+        $("#query-fieldset").hide();
         $("#import-options").hide();
         $("#edit-using-cron").hide();
       }
@@ -44,6 +57,7 @@
         $("#list-fieldset").hide();
         $("#edit-offline-refresh-wrapper").show();
         $("#baseurl-table").hide();
+        $("#query-fieldset").hide();
\ No newline at end of file
         $("#import-options").show();
         $("#edit-using-cron").hide();
       }
