diff -urN apachesolr.orig/apachesolr.admin.inc apachesolr/apachesolr.admin.inc
--- apachesolr.orig/apachesolr.admin.inc	2009-12-27 18:29:34.000000000 +0100
+++ apachesolr/apachesolr.admin.inc	2010-02-07 14:38:13.000000000 +0100
@@ -22,27 +22,6 @@
     }
   }
 
-  $form['apachesolr_host'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Solr host name'),
-    '#default_value' => variable_get('apachesolr_host', 'localhost'),
-    '#description' => t('Host name of your Solr server, e.g. <code>localhost</code> or <code>example.com</code>.'),
-    '#required' => TRUE,
-  );
-  $form['apachesolr_port'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Solr port'),
-    '#default_value' => variable_get('apachesolr_port', '8983'),
-    '#description' => t('Port on which the Solr server listens. The Jetty example server is 8983, while Tomcat is 8080 by default.'),
-    '#required' => TRUE,
-  );
-  $form['apachesolr_path'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Solr path'),
-    '#default_value' => variable_get('apachesolr_path', '/solr'),
-    '#description' => t('Path that identifies the Solr request handler to be used.'),
-  );
-
   $numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
   $form['apachesolr_cron_limit'] = array(
     '#type' => 'select',
@@ -118,6 +97,239 @@
 }
 
 /**
+ * Callback for configuring Solr servers.
+ */
+function apachesolr_settings_servers($server_id = NULL, $delete = NULL) {
+  $output = '';
+  $servers = variable_get('apachesolr_servers', array());
+  if ($delete == 'delete' && isset($servers[$server_id])) {
+    return drupal_get_form('apachesolr_settings_servers_delete', $server_id);
+  }
+  $server_id = is_null($server_id) ? 'add' : $server_id;
+  if (count($servers)) {
+    $header = array('#', t('Server name'), t('Host name'), t('Port'), t('Path'), t('Query server'), t('Index server'), t('Status'), array('data' => t('Operations'), 'colspan' => 2));
+    $rows = array();
+    foreach ($servers as $delta => $server) {
+      $ping = FALSE;
+      try {
+        $solr = apachesolr_get_server($server['host'], $server['port'], $server['path']);
+        $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
+        // If there is no $solr object, there is no server available, so don't continue.
+        if (!$ping) {
+          throw new Exception(t('No Solr instance available for !host:!port/!path', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'))));
+        }
+      }
+      catch (Exception $e) {
+        watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      }
+ 
+      $row = array($delta, $server['name'], $server['host'], $server['port'], $server['path'], $server['query'] ? t('Yes') : t('No'), $server['index']  ? t('Yes') : t('No'), $ping ? t('Successfully contacted') : t('Cannot be contacted!'), l(t('modify'), 'admin/settings/apachesolr/servers/'. $delta), l(t('delete'), 'admin/settings/apachesolr/servers/'. $delta .'/delete'));
+      if ($server_id == (string)$delta) {
+        $rows[] = array(
+          'data' => $row,
+          'class' => 'apachesolr-servers-active-server',
+        );
+      }
+      else {
+        $rows[] = $row;
+      }
+    }
+    $element = array(
+      '#type' => 'fieldset',
+      '#title' => t('Solr servers currentlly configured on this site'),
+      '#collapsible' => TRUE,
+      '#collpased' => FALSE,
+      '#value' => theme('table', $header, $rows) . ($server_id != 'add' ? '<div class="apachesolr-servers-add">'. l(t('Add a new server'), 'admin/settings/apachesolr/servers') .'</div>' : ''),
+    );
+    $output .= theme('fieldset', $element);
+    drupal_add_css(drupal_get_path('module', 'apachesolr') .'/apachesolr.css');
+  }
+  else {
+    $output .= t('There are no Solr servers configured yet. Use the form bellow to enter a Solr server.');
+  }
+  return $output . drupal_get_form('apachesolr_settings_servers_form', $server_id);
+}
+
+/**
+ * Solr servers form definition.
+ */
+function apachesolr_settings_servers_form(&$form_state, $server_id) {
+  $servers = variable_get('apachesolr_servers', array());
+  $server = $server_id == 'add' ? array('name' => '', 'host' => 'localhost', 'port' => '8983', 'path' => '/solr', 'query' => TRUE, 'index' => FALSE) : $servers[$server_id];
+  $form = array();
+  $form['server_id'] = array(
+    '#type' => 'value',
+    '#value' => $server_id,
+  );
+  $form['container'] = array(
+    '#type' => 'fieldset',
+    '#title' => $server_id == 'add' ? t('Add a new Solr server') : t('Configuration for server #%no: %name', array('%no' => $server_id, '%name' => $server['name'])),
+    '#collapsible' => TRUE,
+    '#collpased' => FALSE,
+  );
+  $form['container']['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Server name'),
+    '#description' => t('Enter a maximum 50 characters internal name for this server. It will be use only for administrative reasons, to help dealing with more than one server.'),
+    '#default_value' => $server['name'],
+    '#required' => TRUE,
+    '#maxlength' => 50,
+  );
+  $form['container']['url'] = array(
+    '#tree' => TRUE,
+  );
+  $form['container']['url']['host'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr host name'),
+    '#description' => t('Host name of your Solr server, e.g. localhost or example.com.'),
+    '#default_value' => $server['host'],
+    '#required' => TRUE,
+  );
+  $form['container']['url']['port'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr port'),
+    '#description' => t('Port on which the Solr server listens. The Jetty example server is 8983, while Tomcat is 8080 by default.'),
+    '#default_value' => $server['port'],
+    '#required' => TRUE,
+  );
+  $form['container']['url']['path'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr path'),
+    '#description' => t('Path that identifies the Solr request handler to be used.'),
+    '#default_value' => $server['path'],
+  );
+  $form['container']['query'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('This server is used for queries'),
+    '#description' => t('Check this box in order to have this server acting as query server.'),
+    '#default_value' => $server['query'],
+  );
+  $form['container']['index'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('This server used for indexing'),
+    '#description' => t('Check this box if you want too use this server as index server. Note that you cannot configure more than one index server.'),
+    '#default_value' => $server['index'],
+  );
+  $form['container']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => is_numeric($server_id) ? t('Save') : t('Add'),
+  );
+  return $form;
+}
+
+/**
+ * Solr servers form validation callback handler.
+ */
+function apachesolr_settings_servers_form_validate($form, &$form_state) {
+  // Check for existing servers other than current.
+  foreach (variable_get('apachesolr_servers', array()) as $delta => $server) {
+    if (($form_state['values']['server_id'] == 'add') || ($form_state['values']['server_id'] != 'add' && $form_state['values']['server_id'] != $delta)) {
+
+      // Does other server have this name?
+      if ($form_state['values']['name'] == $server['name']) {
+        form_set_error('name', t('A server with the name <strong>@name</strong> is already defined in row #@row.', array('@name' => $server['name'], '@row' => $delta)));
+      }
+      
+      // Does other server have this URL?
+      if ($form_state['values']['url']['host'] == $server['host'] && $form_state['values']['url']['port'] == $server['port'] && $form_state['values']['url']['path'] == $server['path']) {
+        form_set_error('url', t('A server with the URL <strong>!host:!port/!path</strong> is already defined in row #@row.', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'), '@row' => $delta)));
+      }
+    }
+  }
+}
+
+/**
+ * Solr servers form submit callback handler.
+ */
+function apachesolr_settings_servers_form_submit($form, &$form_state) {
+  $servers = variable_get('apachesolr_servers', array());
+  $server = array(
+    'name' => $form_state['values']['name'],
+    'host' => $form_state['values']['url']['host'],
+    'port' => $form_state['values']['url']['port'],
+    'path' => $form_state['values']['url']['path'],
+    'query' => $form_state['values']['query'],
+    'index' => $form_state['values']['index'],
+  );
+  
+  // If this is an index server, remove the index attribute from the others.
+  if ($server['index']) {
+    foreach ($servers as $delta => $server_item) {
+      $servers[$delta]['index'] = FALSE;
+    }
+  }
+  
+  if ($form_state['values']['server_id'] == 'add') {
+    $servers[] = $server;
+  }
+  else {
+    $servers[$form_state['values']['server_id']] = $server;
+  }
+  
+  // Save servers into one single variable.
+  variable_set('apachesolr_servers', $servers);
+  
+  drupal_set_message(t('Server !host:!port/!path was saved as %name', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'), '%name' => $server['name'])));
+  $form_state['redirect'] = 'admin/settings/apachesolr/servers';
+}
+
+/**
+ * Solr server deleting confirmation page
+ */
+function apachesolr_settings_servers_delete(&$form_state, $server_id) {
+  $servers = variable_get('apachesolr_servers', array());
+  $server = $servers[$server_id];
+  
+  $form['#redirect'] = 'admin/settings/apachesolr/servers';
+  $form['server_id'] = array(
+    '#type' =>  'value',
+    '#value' => $server_id,
+  );
+
+  $question = t('Are you sure you want to delete the Solr server %name?', array('%name' => $server['name']));
+  
+  $items = array(
+    t('Name: @name', array('@name' => $server['name'])),
+    t('Host: @host', array('@host' => $server['host'])),
+    t('Port: @port', array('@port' => $server['port'])),
+    t('Path: @path', array('@path' => $server['path'])),
+    t('Query server: @query', array('@query' => $server['query'] ? t('Yes') : t('No'))),
+    t('Index server: @index', array('@index' => $server['index'] ? t('Yes') : t('No'))),
+  );
+  $description = theme('item_list', $items, t('Solr server details:'));
+  
+  // Check if this is the only query server.
+  $query = 0;
+  foreach ($servers as $delta => $server_item) {
+    if ($delta != $server_id && $server_item['query']) {
+        $query++;
+    }
+  }
+
+  $items = array(); 
+  if ($server['index']) {
+    $items[] = t('This Solr server is the server used to index this site. If you will delete it than you\'ll have to configure other server as index server.');
+  }
+  if ($query == 0) {
+    $items[] = t('This Solr server is the only one configured to be queried for data. If you will delete it than you\'ll have to configure at least other server as query server.');
+  }
+  $items[] = t('This action cannot be undone.');
+  $description .= theme('item_list', $items, t('Warnings:'));
+  
+  return confirm_form($form, $question, 'admin/settings/apachesolr/servers', $description, t('Delete'), t('Cancel'));
+}
+
+/**
+ * Solr server delete callback handler.
+ */ 
+function apachesolr_settings_servers_delete_submit($form, &$form_state) {
+  $servers = variable_get('apachesolr_servers', array());
+  $server = array_splice($servers, $form_state['values']['server_id'], 1);
+  drupal_set_message(t('The Solr server %name (!host:!port/!path) has been deleted.', array('%name' => $server[0]['name'], '!host' => $server[0]['host'], '!port' => $server[0]['port'], '!path' => ltrim($server[0]['path'], '/'))));
+  variable_set('apachesolr_servers', $servers);
+}
+
+/**
  * Gets information about the fields already in solr index.
  */
 function apachesolr_index_page() {
diff -urN apachesolr.orig/apachesolr.admin.inc.orig apachesolr/apachesolr.admin.inc.orig
--- apachesolr.orig/apachesolr.admin.inc.orig	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/apachesolr.admin.inc.orig	2009-12-27 18:29:34.000000000 +0100
@@ -0,0 +1,824 @@
+<?php
+// $Id: apachesolr.admin.inc,v 1.1.2.28.2.23 2009/12/27 17:29:34 robertDouglass Exp $
+
+/**
+ * @file
+ *   Administrative pages for the Apache Solr framework.
+ */
+
+function apachesolr_settings() {
+  $form = array();
+
+  // Perform a check to ensure the server is there
+  if (empty($_POST)) {
+    // update_help() loads install.inc via include_once, so not much
+    // point in working around it.
+    include_once './includes/install.inc';
+    module_load_include('install', 'apachesolr');
+
+    foreach (apachesolr_requirements('runtime') as $requirement) {
+      $status = $requirement['severity'] == REQUIREMENT_ERROR ? 'error' : 'status';
+      drupal_set_message($requirement['title'] . ': ' . $requirement['value'], $status);
+    }
+  }
+
+  $form['apachesolr_host'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr host name'),
+    '#default_value' => variable_get('apachesolr_host', 'localhost'),
+    '#description' => t('Host name of your Solr server, e.g. <code>localhost</code> or <code>example.com</code>.'),
+    '#required' => TRUE,
+  );
+  $form['apachesolr_port'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr port'),
+    '#default_value' => variable_get('apachesolr_port', '8983'),
+    '#description' => t('Port on which the Solr server listens. The Jetty example server is 8983, while Tomcat is 8080 by default.'),
+    '#required' => TRUE,
+  );
+  $form['apachesolr_path'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr path'),
+    '#default_value' => variable_get('apachesolr_path', '/solr'),
+    '#description' => t('Path that identifies the Solr request handler to be used.'),
+  );
+
+  $numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
+  $form['apachesolr_cron_limit'] = array(
+    '#type' => 'select',
+    '#title' => t('Number of items to index per cron run'),
+    '#default_value' => variable_get('apachesolr_cron_limit', 50),
+    '#options' => $numbers,
+    '#description' => t('The maximum number of items indexed in each pass of a <a href="@cron">cron maintenance task</a>. If necessary, reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status')))
+  );
+
+  $options = drupal_map_assoc(array(5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100));
+  $form['apachesolr_rows'] = array(
+    '#type' => 'select',
+    '#title' => t('Results per page'),
+    '#default_value' => variable_get('apachesolr_rows', 10),
+    '#options' => $options,
+    '#description' => t('The number of results that will be shown per page.'),
+  );
+  $form['apachesolr_facetstyle'] = array(
+    '#type' => 'radios',
+    '#title' => t('Style of facet links'),
+    '#default_value' => variable_get('apachesolr_facetstyle', 'checkboxes'),
+    '#options' => array('links' => t('Links only'), 'checkboxes' => t('Links with checkboxes')),
+  );
+  $form['apachesolr_failure'] = array(
+    '#type' => 'select',
+    '#title' => t('On failure'),
+    '#options' => array('show_error' => t('Show error message'),
+      'show_drupal_results' => t('Show core Drupal results'),
+      'show_no_results' => t('Show no results')
+    ),
+    '#default_value' => variable_get('apachesolr_failure', 'show_error'),
+    '#description' => t('What to display if Apache Solr search is not available.'),
+  );
+  // Add a link to add more mlt blocks.
+  $form['mlt_link'] = array(
+    '#type' => 'item',
+    '#value' => l(t('Add a new content recommendation block'), 'admin/settings/apachesolr/mlt/add_block'),
+    '#description' => format_plural(count(apachesolr_mlt_list_blocks()),  'You currently have 1 block.', 'You currenly have @count blocks.'),
+  );
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced configuration'),
+    '#collapsed' => TRUE,
+    '#collapsible' => TRUE,
+  );
+  $form['advanced']['apachesolr_set_nodeapi_messages'] = array(
+    '#type' => 'radios',
+    '#title' => t('Extra help messages for administrators'),
+    '#default_value' => variable_get('apachesolr_set_nodeapi_messages', 1),
+    '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
+  );
+  $form['advanced']['apachesolr_read_only'] = array(
+    '#type' => 'radios',
+    '#title' => t('Index write access'),
+    '#default_value' => variable_get('apachesolr_read_only', APACHESOLR_READ_WRITE),
+    '#options' => array(APACHESOLR_READ_WRITE => t('Read and write (normal)'), APACHESOLR_READ_ONLY => t('Read only')),
+    '#description' => t('<em>Read only</em> stops this site from sending updates to your search index. Useful for development sites.'),
+  );
+  return system_settings_form($form);
+}
+
+/**
+ * Validation function for the apachesolr_settings form.
+ */
+function apachesolr_settings_validate($form, &$form_state) {
+  if (isset($form['apachesolr_port'])) {
+    $port = $form_state['values']['apachesolr_port'];
+    // TODO: Port range should be 0-65535, but 0 crashes apachesolr
+    if (!ctype_digit($port) || $port < 1 || $port > 65535) {
+      form_set_error('apachesolr_port', t('The port has to be an integer between 1 and 65535.'));
+    }
+  }
+}
+
+/**
+ * Gets information about the fields already in solr index.
+ */
+function apachesolr_index_page() {
+  try {
+    $solr = apachesolr_get_solr();
+    // TODO: possibly clear this every page view if we are running multi-site.
+    if (apachesolr_index_get_last_updated()) {
+      $solr->clearCache();
+    }
+    $data = $solr->getLuke();
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
+    $data->fields = array();
+  }
+
+  $output  = '';
+  if (isset($data->index->numDocs)) {
+    $pending_docs = $delay_msg = $delete_msg = '';
+    try {
+      $stats_summary = $solr->getStatsSummary();
+      $solr_msg = '<p>' . t('Using schema.xml version: <strong>@schema_version</strong>', $stats_summary);
+      if ($stats_summary['@core_name']) {
+        $solr_msg .= '<br />' . t('Solr core name: <strong>@core_name</strong>', $stats_summary);
+      }
+      $delay_msg = '<br />' . t('<em>The server has a @autocommit_time delay before updates are processed.</em>', $stats_summary) . "</p>\n";
+      $delete_msg = '<p>' . t('Number of pending deletions: @deletes_total', $stats_summary) . "</p>\n";
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    }
+    $output .= $solr_msg . $delay_msg;
+    $pending_msg = $stats_summary['@pending_docs'] ? t('(@pending_docs sent but not yet processed)', $stats_summary) : '';
+    $output .= '<p>' . t('Number of documents in index: @num !pending', array('@num' => $data->index->numDocs, '!pending' => $pending_msg)) . "</p>\n";
+    $output .= $delete_msg;
+  }
+  $output .= '<p>' . l(t('View more details on the search index contents'), 'admin/reports/apachesolr') . "</p>\n";
+  if (variable_get('apachesolr_read_only', APACHESOLR_READ_WRITE) == APACHESOLR_READ_WRITE) {
+    // Display the Delete Index form.
+    $output .= drupal_get_form('apachesolr_delete_index_form');
+  }
+  else {
+    drupal_set_message(t('The index is in read-only mode. Options for deleting and re-indexing are therefore not available. The index mode can be changed on the !settings_page', array('!settings_page' => l(t('settings page'), 'admin/settings/apachesolr'))), 'warning'); 
+  }
+  
+
+  return $output;
+}
+
+function apachesolr_index_report() {
+  try {
+    $solr = apachesolr_get_solr();
+    // TODO: possibly clear this every page view if we are running multi-site.
+    if (apachesolr_index_get_last_updated()) {
+      $solr->clearCache();
+    }
+    $data = $solr->getLuke();
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
+    $data->fields = array();
+  }
+
+  $output = '<p>' . t('Number of documents in index: @num !pending', array('@num' => $data->index->numDocs, '!pending' => '')) . "</p>\n";
+
+  $limit = variable_get('apachesolr_luke_limit', 20000);
+  if (isset($data->index->numDocs) && $data->index->numDocs > $limit) {
+    $output .= '<p>' . t('You have more than @limit documents, so term frequencies are being omitted for performance reasons.', array('@limit' => $limit)) . "</p>\n";
+    $not_found = t('<em>Omitted</em>');
+  }
+  elseif (isset($data->index->numDocs)) {
+    $not_found = t('Not indexed');
+    try {
+      $solr = apachesolr_get_solr();
+      // Note: we use 2 since 1 fails on Ubuntu Hardy.
+      $data = $solr->getLuke(2);
+      $output .= '<p>' . t('Number of terms in index: @num', array('@num' => $data->index->numTerms)) . "</p>\n";
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      $data->fields = array();
+    }
+  }
+
+  $fields = (array)$data->fields;
+  if ($fields) {
+    $output .= '<p>' . t('Number of fields in index: @num', array('@num' => count($fields))) . "</p>\n";
+    $rows = array();
+    foreach ($fields as $name => $field) {
+      // TODO: try to map the name to something more meaningful.
+      $rows[$name] = array($name, $field->type, isset($field->distinct) ? $field->distinct : $not_found);
+    }
+    ksort($rows);
+    // Display the table of Field names, Index Types, and term counts.
+    $output .= theme('table', array(t('Field name'), t('Index type'), t('Distinct terms')), $rows);
+  }
+  else {
+    $output .= '<p>' . t('No data on indexed fields.') . "</p>\n";
+  }
+  return $output;
+}
+
+/**
+ * Indicates what order the specified facets should be listed in.  This function is used in a usort
+ * invocation.
+ * @param $a
+ *   The first facet.
+ * @param $b
+ *   The second facet.
+ * @return
+ *   A signed integer that indicates which of the specified facets should come first.
+ */
+function apachesolr_sort_facets($a, $b) {
+  return strcasecmp($a['info'], $b['info']);
+}
+
+/**
+ * This is the submit handler for the active facets form.
+ *
+ * The form values for each module are array filtereed to remove non-enabled items and
+ * stored in the variable table with the name 'apachesolr_enabled_facets'.
+ *
+ * @see apachesolr_enabled_facets_form()
+ */
+function apachesolr_enabled_facets_form_submit($form, &$form_state) {
+  $enabled = array();
+  foreach ($form_state['values']['apachesolr_enabled_facets'] as $module => $facets) {
+    $enabled[$module] = array_filter($facets);
+  }
+  variable_set('apachesolr_enabled_facets', $enabled);
+  drupal_set_message($form_state['values']['submit_message'], 'warning');
+}
+
+/**
+ * Creates the form that allows the user to select which facets will be enabled.
+ *
+ * Only enabled facets are sent to solr.  Fewer enabled facets can reduce the
+ * load on the search server.  Blocks are only offered for enabled facets, so
+ * this also reduces the clutter on the blocks admin page.
+ */
+function apachesolr_enabled_facets_form() {
+  $facets = array();
+  $module_facets = array();
+  $module_list = array();
+  foreach (module_implements('apachesolr_facets') as $module) {
+    $module_facets[$module] = module_invoke($module, 'apachesolr_facets');
+    uasort($module_facets[$module], 'apachesolr_sort_facets');
+    $module_list[$module] = $module;
+  }
+
+  $enabled_facets = apachesolr_get_enabled_facets();
+  $form = array();
+  $form['apachesolr_enabled_facets']['help'] = array (
+    '#type' => 'item',
+    '#value' => t('You can use this screen to select which search filter blocks should be created by enabling the corresponding filters on this page. For performance reasons, you should only enable filters that you intend to have available to users on the search page.  After selecting which filter blocks to create, you will be sent to the blocks page where you can choose which of those blocks should be enabled when your users search by placing each block in a region.'),
+  );
+  if ($module_list) {
+    $placeholders = implode(', ', array_fill(0, count($module_list), "'%s'"));
+    $result = db_query("SELECT name, info FROM {system} WHERE name IN (". $placeholders .") AND type = 'module'", $module_list);
+    while ($item = db_fetch_array($result)) {
+      $module_list[$item['name']] = unserialize($item['info']);
+    }
+  }
+  foreach($module_facets as $module => $facets) {
+    $form['apachesolr_enabled_facets'][$module] = array(
+      '#type' => 'fieldset',
+      '#title' => check_plain($module_list[$module]['name']),
+      '#collapsible' => TRUE,
+      '#collapsed' => FALSE,
+    );
+    // We must use module + delta as the keys since that combination is
+    // guaranteed to be unique.  A single module could, for example, have
+    // two different blocks that expose different faceting on the same
+    // field in the index.
+    foreach($facets as $delta => $data) {
+      $form['apachesolr_enabled_facets'][$module][$delta] = array(
+        '#type' => 'checkbox',
+        '#title' => $data['info'],
+        '#return_value' => $data['facet_field'],
+        '#default_value' => isset($enabled_facets[$module][$delta]) ? $data['facet_field'] : 0,
+      );
+    }
+  }
+
+  $has_facets = (bool)$module_facets;
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#access' => $has_facets,
+  );
+  $form['no-facets-message'] = array(
+    '#value' => t('<em>No filters are available from your currently enabled modules</em>'),
+    '#access' => !$has_facets,
+  );
+
+  $form['#tree'] = TRUE;
+  $form['submit_message'] = array(
+    '#type' => 'value',
+    '#value' => t('The Apache Solr filters settings were changed.  To arrange the blocks for your enabled filters, visit the <a href="@url">blocks administration page</a>.', array('@url' => url('admin/build/block'))),
+  );
+
+  return $form;
+}
+
+/**
+ * Create a form for deleting the contents of the Solr index.
+ */
+function apachesolr_delete_index_form() {
+  $form = array();
+  $form['markup'] = array(
+    '#prefix' => '<h3>',
+    '#value' => t('Index controls'),
+    '#suffix' => '</h3>',
+  );
+  $form['batch'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Reindex immediately'),
+    '#description' => t('If checked, the index will be rebuilt immediately in this browser session using the batch API. Depending on the size of your index it may take a while. Leave unchecked to reindex on cron runs.'),
+  );
+  $form['reindex'] = array(
+    '#type' => 'submit',
+    '#value' => t('Re-index all content'),
+    '#submit' => array('apachesolr_clear_index_submit'),
+  );
+  $form['reindex-desc'] = array(
+    '#type' => 'item',
+    '#description' => t('Re-indexing will add all content to the index again (overwriting the index), but existing content in the index will remain searchable.'),
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete the index'),
+    '#submit' => array('apachesolr_delete_index_submit'),
+  );
+  $form['delete-desc'] = array(
+    '#type' => 'item',
+    '#description' => t('Deletes all of the documents in the Solr index. This is rarely necessary unless your index is corrupt or you have installed a new schema.xml.'),
+  );
+
+  return $form;
+}
+
+/**
+ * Submit function for the "Re-index all content" button.
+ *
+ * @see apachesolr_delete_index_form()
+ */
+function apachesolr_clear_index($form, &$form_state) {
+  return apachesolr_comfirm_clear_index($form, $form_state);
+}
+
+/**
+ * Submit function for the "Re-index all content" button
+ * @see apachesolr_delete_index_form()
+ *
+ */
+function apachesolr_clear_index_submit($form, &$form_state) {
+  if ($form_state['values']['batch']) {
+    $form_state['redirect'] = 'admin/settings/apachesolr/index/batch/confirm';
+  }
+  else {
+  $form_state['redirect'] = 'admin/settings/apachesolr/index/clear/confirm';
+}
+}
+
+/**
+ * Confirmation form for "Re-index all content" button
+ * @see apachesolr_clear_index_submit()
+ */
+function apachesolr_clear_index_confirm() {
+  $form = array();
+  return confirm_form($form, t('Are you sure you want to re-index?'), 'admin/settings/apachesolr/index', NULL, t('Re-index'), t('Cancel'));
+}
+
+/**
+ * Submit function for the "Re-index all content" confirmation form.
+ *
+ * @see apachesolr_clear_index_confirm()
+ */
+function apachesolr_clear_index_confirm_submit($form, &$form_state) {
+  $form_state['redirect'] = 'admin/settings/apachesolr/index';
+  apachesolr_rebuild_index_table();
+}
+
+
+/**
+ * Confirmation form for "Batch re-index all content" button
+ * @see apachesolr_batch_index_confirm_submit()
+ */
+function apachesolr_batch_index_confirm() {
+  $form = array();
+  return confirm_form($form, t('Are you sure you want to batch re-index? This may take some time.'), 'admin/settings/apachesolr/index', NULL, t('Batch re-index'), t('Cancel'));
+}
+
+/**
+ * Submit function for the "Batch re-index all content" confirmation form.
+ *
+ * @see apachesolr_clear_index_confirm()
+ */
+function apachesolr_batch_index_confirm_submit($form, &$form_state) {
+  $form_state['redirect'] = 'admin/settings/apachesolr/index';
+  apachesolr_batch_reindex();
+}
+
+/**
+ * Submit function for the "Delete the index" button
+ * @see apachesolr_delete_index_form()
+ *
+ */
+function apachesolr_delete_index_submit($form, &$form_state) {
+  $form_state['redirect'] = 'admin/settings/apachesolr/index/delete/confirm';
+}
+
+/**
+ * Confirmation form for "Delete the index" button
+ * @see apachesolr_delete_index_submit()
+ */
+function apachesolr_delete_index_confirm() {
+  $form = array();
+  return confirm_form($form, t('Are you sure you want to delete your search index and start re-indexing?'), 'admin/settings/apachesolr/index', NULL, t('Delete'), t('Cancel'));
+}
+
+/**
+ * Submit function for the "Delete the index" confirmation form.
+ *
+ * @see apachesolr_delete_index_confirm()
+ */
+function apachesolr_delete_index_confirm_submit($form, &$form_state) {
+  $form_state['redirect'] = 'admin/settings/apachesolr/index';
+  try {
+    apachesolr_delete_index();
+    // This form can't be seen by anyone without 'administer search'
+    // permission, so no need to check perms before displaying a run-cron link.
+    drupal_set_message(t('The Apache Solr content index has been erased. You must now !run_cron until your entire site has been re-indexed.', array('!run_cron' => l(t('run cron'), 'admin/reports/status/run-cron', array('query' => array('destination' => 'admin/settings/apachesolr/index'))))));
+
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Utility function to delete the index and reset all index counters.
+ *
+ * @param $type
+ *   a single content type to be deleted from the index.
+ *
+ * @throws Exception
+ */
+function apachesolr_delete_index($type = NULL) {
+  // Instantiate a new Solr object.
+  $solr = apachesolr_get_solr();
+  if ($type) {
+    $query = 'type:' . $type;
+  }
+  else {
+    $query = '*:*';
+  }
+  // Allow other modules to modify the delete query.
+  // For example, use the site hash so that you only delete this site's
+  // content:  $query = 'hash:' . apachesolr_site_hash()
+  drupal_alter('apachesolr_delete_index', $query);
+  $solr->deleteByQuery($query);
+  $solr->commit();
+  // Rebuild our node-tracking table.
+  apachesolr_rebuild_index_table();
+  apachesolr_index_set_last_updated(time());
+}
+
+/**
+ * MoreLikeThis administration and utility functions.
+ */
+
+function apachesolr_mlt_add_block_form() {
+  $form = apachesolr_mlt_block_form();
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => '5',
+  );
+  return $form;
+}
+
+function apachesolr_mlt_add_block_form_submit($form, &$form_state) {
+  apachesolr_mlt_save_block($form_state['values']);
+  drupal_set_message('New content recommendation block created.  Drag it into a region to enable it');
+  $form_state['redirect'] = 'admin/build/block';
+}
+
+/**
+ * Form to edit moreLikeThis block settings.
+ *
+ * @param int $delta If editing, the id of the block to edit.
+ *
+ * @return array The form used for editing.
+ * TODO:
+ *   Add term boost settings.
+ *   Enable the user to specify a query, rather then forcing suggestions based
+ *     on the node id.
+ *
+ */
+function apachesolr_mlt_block_form($delta = NULL) {
+  if (isset($delta)) {
+    $block = apachesolr_mlt_load_block($delta);
+    if (!$block) {
+      return array();
+    }
+  }
+  else{
+    $block = apachesolr_mlt_block_defaults();
+  }
+
+  $form['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Block name'),
+    '#description' => t('The block name displayed to site users.'),
+    '#required' => TRUE,
+    '#default_value' => $block['name'],
+    '#weight' => '-2',
+  );
+  $form['num_results'] = array(
+    '#type' => 'select',
+    '#title' => t('Maximum number of related items to display'),
+    '#default_value' => $block['num_results'],
+    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)),
+    '#weight' => -1,
+    );
+  $form['mlt_fl'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Fields for finding related content'),
+    '#description' => t('Choose the fields to be used in calculating similarity. The default combination of %taxonomy_names and %title will provide relevant results for typical sites.', array("%taxonomy_names" => apachesolr_field_name_map("taxonomy_names"), "%title" => apachesolr_field_name_map("title"))),
+    '#options' => apachesolr_mlt_get_fields(),
+    '#required' => TRUE,
+    '#default_value' =>  $block['mlt_fl'],
+  );
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced configuration'),
+    '#weight' => '1',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $options = drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7));
+  $form['advanced']['mlt_mintf'] = array(
+    '#type' => 'select',
+    '#title' => t('Minimum term frequency'),
+    '#description' => t('A word must appear this many times in any given document before the document is considered relevant for comparison.'),
+    '#default_value' => $block['mlt_mintf'],
+    '#options' => $options,
+  );
+  $form['advanced']['mlt_mindf'] = array(
+    '#type' => 'select',
+    '#title' => t('Minimum document frequency'),
+    '#description' => t('A word must occur in at least this many documents before it will be used for similarity comparison.'),
+    '#default_value' => $block['mlt_mindf'],
+    '#options' => $options,
+  );
+  $form['advanced']['mlt_minwl'] = array(
+    '#type' => 'select',
+    '#title' => t('Minimum word length'),
+    '#description' => 'You can use this to eliminate short words such as "the" and "it" from similarity comparisons. Words must be at least this number of characters or they will be ignored.',
+    '#default_value' => $block['mlt_minwl'],
+    '#options' => $options,
+  );
+  $form['advanced']['mlt_maxwl'] = array(
+    '#type' => 'select',
+    '#title' => t('Maximum word length'),
+    '#description' => t('You can use this to eliminate very long words from similarity comparisons. Words of more than this number of characters will be ignored.'),
+    '#default_value' => $block['mlt_maxwl'],
+    '#options' => drupal_map_assoc(array(8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)),
+  );
+  $form['advanced']['mlt_maxqt'] = array(
+    '#type' => 'select',
+    '#title' => t('Maximum number of query terms'),
+    '#description' => t('The maximum number of query terms that will be included in any query. Lower numbers will result in fewer recommendations but will get results faster. If a content recommendation is not returning any recommendations, you can either check more "Comparison fields" checkboxes or increase the maximum number of query terms here.'),
+    '#options' => drupal_map_assoc(array(3, 5, 7, 10, 12, 15, 20, 25, 30, 35, 40)),
+    '#default_value' => $block['mlt_maxqt'],
+  );
+  
+  $form['restrictions'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Filters'),
+    '#weight' => '1',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $type_options = array();
+  foreach (node_get_types('types') as $key => $type) {
+    $type_options[$key] = $type->name;
+  }
+
+  $form['restrictions']['mlt_type_filters'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Content Types'),
+    '#default_value' => is_array($block['mlt_type_filters']) ? $block['mlt_type_filters'] : array_keys($type_options),
+    '#options' => $type_options,
+    '#description' => t('Select the content types that similarity suggestions should be restricted to. Multiple types are joined with an OR query, so selecting more types results in more recommendations.'),
+    '#weight' => '-2',
+  );
+
+  $form['restrictions']['mlt_custom_filters'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Additional Query'),
+    '#description' => t('An additional query, in Lucene syntax, which will further filter the similarity suggestions. Ex. \'title:strategy\' will filter related content further to only those with strategy in the title. ' .
+                        'Likewise \'body:HDTV^3\' will boost the term HDTV by a factor of 3. Here are some more examples:' .
+                        '<ul>
+                            <li>language:fr</li>
+                            <li>tid:5 OR tid 7</li>
+                            <li>created:[2009-05-01T23:59:59Z TO 2009-07-28T12:30:00Z]</li>
+                            <li>-uid:0 AND -uid:1</li>
+                        </ul>'),
+    '#required' => FALSE,
+    '#default_value' => $block['mlt_custom_filters'],
+    '#weight' => '-1',
+  );
+
+
+  return $form;
+}
+
+/**
+ * Merge supplied settings with the standard defaults..
+ */
+function apachesolr_mlt_block_defaults($block = array()) {
+  return $block + array(
+    'name' => '',
+    'num_results' => '5',
+    'mlt_fl' =>  array(
+      'title' => 'title',
+      'taxonomy_names' => 'taxonomy_names',
+    ),
+    'mlt_mintf' => '1',
+    'mlt_mindf' => '1',
+    'mlt_minwl' => '3',
+    'mlt_maxwl' => '15',
+    'mlt_maxqt' => '20',
+    'mlt_type_filters' => array_keys(node_get_types('types')),
+    'mlt_custom_filters' => '',
+  );
+}
+
+/**
+ * Constructs a list of field names used on the settings form.
+ *
+ * @return array An array containing a the fields in the solr instance.
+ */
+function apachesolr_mlt_get_fields() {
+  $solr = apachesolr_get_solr();
+  $fields = $solr->getFields();
+  $rows = array();
+  foreach ($fields as $field_name => $field) {
+    if ($field->schema{4} == 'V')
+    $rows[$field_name] = apachesolr_field_name_map($field_name);
+  }
+  ksort($rows);
+  return $rows;
+}
+
+/**
+ * A helper function to save MLT block data.
+ *
+ * If passed a block delta, the function will update block settings. If it is
+ * not passed a block delta, the function will create a new block.
+ *
+ * @param array $block_settings An array containing the settings required to form
+ * a moreLikeThis request.
+ *
+ * @param int $delta The id of the block you wish to update.
+ */
+function apachesolr_mlt_save_block($block_settings = array(), $delta = NULL) {
+  $blocks = variable_get('apachesolr_mlt_blocks', array());
+  if (is_null($delta)) {
+    $count = 0;
+    ksort($blocks);
+    // Construct a new array key.
+    if (end($blocks)) {
+      list(, $count) = explode('-', key($blocks));
+    }
+    $delta = sprintf('mlt-%03d', 1 + $count);
+  }
+  $defaults = apachesolr_mlt_block_defaults();
+  // Remove stray form values.
+  $blocks[$delta] = array_intersect_key($block_settings, $defaults) + $defaults;
+  // Eliminate non-selected fields.
+  $blocks[$delta]['mlt_fl'] = array_filter($blocks[$delta]['mlt_fl']);
+  $blocks[$delta]['mlt_type_filters'] = array_filter($blocks[$delta]['mlt_type_filters']);
+  $blocks[$delta]['mlt_custom_filters'] = trim($blocks[$delta]['mlt_custom_filters']);
+  variable_set('apachesolr_mlt_blocks', $blocks);
+}
+
+function apachesolr_mlt_delete_block_form($form_state, $delta) {
+  if ($block = apachesolr_mlt_load_block($delta)) {
+    $form['delta'] = array(
+      '#type' => 'value',
+      '#value' => $delta
+    );
+
+    return confirm_form($form,
+      t('Are you sure you want to delete the Apache Solr content recommendation block %name?', array('%name' => $block['name'])),
+      'admin/build/block',
+      t('The block will be deleted. This action cannot be undone.'),
+      t('Delete'), t('Cancel'));
+  }
+}
+
+function apachesolr_mlt_delete_block_form_submit($form, &$form_state) {
+  $blocks = variable_get('apachesolr_mlt_blocks', array());
+  unset($blocks[$form_state['values']['delta']]);
+  variable_set('apachesolr_mlt_blocks', $blocks);
+  drupal_set_message(t('The block has been deleted.'));
+  $form_state['redirect'] = 'admin/build/block';
+}
+
+/**
+ * Batch reindex functions.
+ */
+
+/**
+* Control a batch reindex operation using the batch API.
+*/
+function apachesolr_batch_reindex() {
+  $batch = array(
+    'operations' => array(
+      array('apachesolr_batch_reindex_process', array()),
+    ),
+    'finished' => 'apachesolr_batch_reindex_finished',
+    'title' => t('Reindexing'),
+    'init_message' => t('Batch reindexing is starting.'),
+    'file' => drupal_get_path('module', 'apachesolr') . '/apachesolr.admin.inc',
+    //'progress_message' => t('Reindexed @current out of @total.'),
+    'error_message' => t('Batch reindexing has encountered an error.'),
+  );
+  batch_set($batch);
+}
+
+/**
+* Batch Operation Callback
+*/
+function apachesolr_batch_reindex_process(&$context) {
+  if (empty($context['sandbox'])) {
+    try {
+      // Get the $solr object
+      $solr = apachesolr_get_solr();
+      // If there is no server available, don't continue.
+      if (!$solr->ping()) {
+        throw new Exception(t('No Solr instance available during indexing.'));
+      }
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR);
+      return FALSE;
+    }
+
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['current_node'] = 0;
+    apachesolr_rebuild_index_table();
+  }
+  // We can safely process the apachesolr_cron_limit nodes at a time without a timeout.
+  $limit = variable_get('apachesolr_cron_limit', 50);
+
+  // Pull the total and remaining variables using apachesolr_search status function.
+  // This is used to push the progress bar for each group of nodes being indexed.
+  // Must set progress before calling the apachesolr_index_nodes() function to properly increment
+  // the progress else progress and total will never equal.
+  $status = module_invoke('apachesolr_search', 'search', 'status');
+  $remaining = $status['remaining'];
+  $total = $status['total'];
+  $nodes_indexed = db_result(db_query('SELECT COUNT(nid) FROM {apachesolr_search_node}'));
+  $context['sandbox']['progress'] += min($limit, $remaining);
+  $number_indexed = min($limit, $remaining);
+  $context['message'] = t('Reindexing @current of @total.', array('@current' => $context['sandbox']['progress'], '@total' => $total));
+  $context['sandbox']['max'] = $total;
+
+  // With each pass through the callback, retrieve the next group of nids.
+  $result = apachesolr_get_nodes_to_index('apachesolr_search', $limit);
+  apachesolr_index_nodes($result, 'apachesolr_search');
+
+  // Inform the batch engine that we are not finished,
+  // and provide an estimation of the completion level we reached.
+  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+}
+
+/**
+* Batch 'finished' callback
+*/
+function apachesolr_batch_reindex_finished($success, $results, $operations) {
+  if ($success) {
+    // Here we do something meaningful with the results.
+    $message = format_plural(count($results), '1 item successfully processed.', '@count items successfully processed.');
+    $message .= theme('item_list', $results);
+  }
+  else {
+    // An error occurred.
+    // $operations contains the operations that remained unprocessed.
+    $error_operation = reset($operations);
+    $message = t('An error occurred while processing @num with arguments :', array('@num' => $error_operation[0])) . print_r($error_operation[0], TRUE);
+  }
+  drupal_set_message($message, 'error');
+}
diff -urN apachesolr.orig/apachesolr-balancer-267831-D6.patch apachesolr/apachesolr-balancer-267831-D6.patch
--- apachesolr.orig/apachesolr-balancer-267831-D6.patch	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/apachesolr-balancer-267831-D6.patch	2010-01-27 17:26:43.000000000 +0100
@@ -0,0 +1,673 @@
+Index: CHANGELOG.txt
+===================================================================
+RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/CHANGELOG.txt,v
+retrieving revision 1.1.2.154
+diff -u -p -r1.1.2.154 CHANGELOG.txt
+--- CHANGELOG.txt	9 Jan 2010 00:50:43 -0000	1.1.2.154
++++ CHANGELOG.txt	27 Jan 2010 16:23:41 -0000
+@@ -5,6 +5,7 @@ Apache Solr Search Integration x.x-x.x, 
+ 
+ Apache Solr Search Integration 6.x-1.x, xxxx-xx-xx
+ ------------------------------
++#267831 by claudiu.cristea: Load balancer implementation.
+ #679522 by pwolanin, Add gettableFiles to solr admin intefrace config.
+ 
+ Apache Solr Search Integration 6.x-1.0-RC5, 2010-01-04
+Index: Drupal_Apache_Solr_Service.php
+===================================================================
+RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/Drupal_Apache_Solr_Service.php,v
+retrieving revision 1.1.2.26
+diff -u -p -r1.1.2.26 Drupal_Apache_Solr_Service.php
+--- Drupal_Apache_Solr_Service.php	26 Dec 2009 17:12:38 -0000	1.1.2.26
++++ Drupal_Apache_Solr_Service.php	27 Jan 2010 16:23:41 -0000
+@@ -33,7 +33,8 @@ class Drupal_Apache_Solr_Service extends
+    * @return
+    *   (float) seconds taken to ping the server, FALSE if timeout occurs.
+    */
+-  public function ping($timeout = 2) {
++  public function ping() {
++    $timeout = func_get_arg(0);
+     $start = microtime(TRUE);
+ 
+     if ($timeout <= 0.0) {
+Index: Drupal_Apache_Solr_Service_Balancer.php
+===================================================================
+RCS file: Drupal_Apache_Solr_Service_Balancer.php
+diff -N Drupal_Apache_Solr_Service_Balancer.php
+--- /dev/null	1 Jan 1970 00:00:00 -0000
++++ Drupal_Apache_Solr_Service_Balancer.php	27 Jan 2010 16:23:41 -0000
+@@ -0,0 +1,107 @@
++<?php
++require_once 'SolrPhpClient/Apache/Solr/Service/Balancer.php';
++
++class Drupal_Apache_Solr_Service_Balancer extends Apache_Solr_Service_Balancer {
++
++  /**
++   * Get summary information about the first index servive found.
++   */
++  public function getStatsSummary() {
++    $service = $this->_selectWriteService();
++
++    do {
++      try {
++        return $service->getStatsSummary();
++      }
++      catch (Exception $e) {
++        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
++          throw $e;
++        }
++      }
++      $service = $this->_selectWriteService(TRUE);
++    } while ($service);
++    
++    return FALSE;
++  }
++  
++  /**
++   * Clear cached Solr data.
++   */
++  public function clearCache() {
++    foreach ($this->_readableServices as $service) {
++      @$service->clearCache;
++    }
++    foreach ($this->_writableServices as $service) {
++      @$service->clearCache;
++    }
++  }
++  
++  /**
++   * Get meta-data about the index.
++   */
++  public function getLuke($num_terms = 0) {
++    $service = $this->_selectWriteService();
++
++    do {
++      try {
++        return $service->getLuke($num_terms);
++      }
++      catch (Exception $e) {
++        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
++          throw $e;
++        }
++      }
++      $service = $this->_selectWriteService(TRUE);
++    } while ($service);
++
++    return FALSE;
++  }
++  /**
++   * Get just the field meta-data about the index.
++   */
++  public function getFields($num_terms = 0) {
++    return $this->getLuke($num_terms)->fields;
++  }
++  
++  /**
++   * For some reasons this was not implemented in Balancer.
++   */
++  public function deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600) {
++    $service = $this->_selectWriteService();
++
++    do {
++      try {
++        return $service->deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600);
++      }
++      catch (Exception $e) {
++        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
++          throw $e;
++        }
++      }
++      $service = $this->_selectWriteService(TRUE);
++    } while ($service); 
++
++    return FALSE;
++  }
++  
++  /**
++   * Check if an index or query server is vailable.
++   */
++  public function ping($timeout = 2, $service_type) {
++    $service = $service_type == 'index' ? $this->_selectWriteService() : $this->_selectReadService();
++
++    do {
++      try {
++        return $service->ping($timeout);
++      }
++      catch (Exception $e) {
++        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
++          throw $e;
++        }
++      }
++      $service = $service_type == 'index' ? $this->_selectWriteService(TRUE) : $this->_selectReadService(TRUE);
++    } while ($service);
++
++    return FALSE;
++  }
++}
+Index: apachesolr.admin.inc
+===================================================================
+RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.admin.inc,v
+retrieving revision 1.1.2.46
+diff -u -p -r1.1.2.46 apachesolr.admin.inc
+--- apachesolr.admin.inc	27 Dec 2009 16:47:37 -0000	1.1.2.46
++++ apachesolr.admin.inc	27 Jan 2010 16:23:41 -0000
+@@ -22,27 +22,6 @@ function apachesolr_settings() {
+     }
+   }
+ 
+-  $form['apachesolr_host'] = array(
+-    '#type' => 'textfield',
+-    '#title' => t('Solr host name'),
+-    '#default_value' => variable_get('apachesolr_host', 'localhost'),
+-    '#description' => t('Host name of your Solr server, e.g. <code>localhost</code> or <code>example.com</code>.'),
+-    '#required' => TRUE,
+-  );
+-  $form['apachesolr_port'] = array(
+-    '#type' => 'textfield',
+-    '#title' => t('Solr port'),
+-    '#default_value' => variable_get('apachesolr_port', '8983'),
+-    '#description' => t('Port on which the Solr server listens. The Jetty example server is 8983, while Tomcat is 8080 by default.'),
+-    '#required' => TRUE,
+-  );
+-  $form['apachesolr_path'] = array(
+-    '#type' => 'textfield',
+-    '#title' => t('Solr path'),
+-    '#default_value' => variable_get('apachesolr_path', '/solr'),
+-    '#description' => t('Path that identifies the Solr request handler to be used.'),
+-  );
+-
+   $numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
+   $form['apachesolr_cron_limit'] = array(
+     '#type' => 'select',
+@@ -110,6 +89,239 @@ function apachesolr_settings_validate($f
+ }
+ 
+ /**
++ * Callback for configuring Solr servers.
++ */
++function apachesolr_settings_servers($server_id = NULL, $delete = NULL) {
++  $output = '';
++  $servers = variable_get('apachesolr_servers', array());
++  if ($delete == 'delete' && isset($servers[$server_id])) {
++    return drupal_get_form('apachesolr_settings_servers_delete', $server_id);
++  }
++  $server_id = is_null($server_id) ? 'add' : $server_id;
++  if (count($servers)) {
++    $header = array('#', t('Server name'), t('Host name'), t('Port'), t('Path'), t('Query server'), t('Index server'), t('Status'), array('data' => t('Operations'), 'colspan' => 2));
++    $rows = array();
++    foreach ($servers as $delta => $server) {
++      $ping = FALSE;
++      try {
++        $solr = apachesolr_get_server($server['host'], $server['port'], $server['path']);
++        $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
++        // If there is no $solr object, there is no server available, so don't continue.
++        if (!$ping) {
++          throw new Exception(t('No Solr instance available for !host:!port/!path', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'))));
++        }
++      }
++      catch (Exception $e) {
++        watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
++      }
++ 
++      $row = array($delta, $server['name'], $server['host'], $server['port'], $server['path'], $server['query'] ? t('Yes') : t('No'), $server['index']  ? t('Yes') : t('No'), $ping ? t('Successfully contacted') : t('Cannot be contacted!'), l(t('modify'), 'admin/settings/apachesolr/servers/'. $delta), l(t('delete'), 'admin/settings/apachesolr/servers/'. $delta .'/delete'));
++      if ($server_id == (string)$delta) {
++        $rows[] = array(
++          'data' => $row,
++          'class' => 'apachesolr-servers-active-server',
++        );
++      }
++      else {
++        $rows[] = $row;
++      }
++    }
++    $element = array(
++      '#type' => 'fieldset',
++      '#title' => t('Solr servers currentlly configured on this site'),
++      '#collapsible' => TRUE,
++      '#collpased' => FALSE,
++      '#value' => theme('table', $header, $rows) . ($server_id != 'add' ? '<div class="apachesolr-servers-add">'. l(t('Add a new server'), 'admin/settings/apachesolr/servers') .'</div>' : ''),
++    );
++    $output .= theme('fieldset', $element);
++    drupal_add_css(drupal_get_path('module', 'apachesolr') .'/apachesolr.css');
++  }
++  else {
++    $output .= t('There are no Solr servers configured yet. Use the form bellow to enter a Solr server.');
++  }
++  return $output . drupal_get_form('apachesolr_settings_servers_form', $server_id);
++}
++
++/**
++ * Solr servers form definition.
++ */
++function apachesolr_settings_servers_form(&$form_state, $server_id) {
++  $servers = variable_get('apachesolr_servers', array());
++  $server = $server_id == 'add' ? array('name' => '', 'host' => 'localhost', 'port' => '8983', 'path' => '/solr', 'query' => TRUE, 'index' => FALSE) : $servers[$server_id];
++  $form = array();
++  $form['server_id'] = array(
++    '#type' => 'value',
++    '#value' => $server_id,
++  );
++  $form['container'] = array(
++    '#type' => 'fieldset',
++    '#title' => $server_id == 'add' ? t('Add a new Solr server') : t('Configuration for server #%no: %name', array('%no' => $server_id, '%name' => $server['name'])),
++    '#collapsible' => TRUE,
++    '#collpased' => FALSE,
++  );
++  $form['container']['name'] = array(
++    '#type' => 'textfield',
++    '#title' => t('Server name'),
++    '#description' => t('Enter a maximum 50 characters internal name for this server. It will be use only for administrative reasons, to help dealing with more than one server.'),
++    '#default_value' => $server['name'],
++    '#required' => TRUE,
++    '#maxlength' => 50,
++  );
++  $form['container']['url'] = array(
++    '#tree' => TRUE,
++  );
++  $form['container']['url']['host'] = array(
++    '#type' => 'textfield',
++    '#title' => t('Solr host name'),
++    '#description' => t('Host name of your Solr server, e.g. localhost or example.com.'),
++    '#default_value' => $server['host'],
++    '#required' => TRUE,
++  );
++  $form['container']['url']['port'] = array(
++    '#type' => 'textfield',
++    '#title' => t('Solr port'),
++    '#description' => t('Port on which the Solr server listens. The Jetty example server is 8983, while Tomcat is 8080 by default.'),
++    '#default_value' => $server['port'],
++    '#required' => TRUE,
++  );
++  $form['container']['url']['path'] = array(
++    '#type' => 'textfield',
++    '#title' => t('Solr path'),
++    '#description' => t('Path that identifies the Solr request handler to be used.'),
++    '#default_value' => $server['path'],
++  );
++  $form['container']['query'] = array(
++    '#type' => 'checkbox',
++    '#title' => t('This server is used for queries'),
++    '#description' => t('Check this box in order to have this server acting as query server.'),
++    '#default_value' => $server['query'],
++  );
++  $form['container']['index'] = array(
++    '#type' => 'checkbox',
++    '#title' => t('This server used for indexing'),
++    '#description' => t('Check this box if you want too use this server as index server. Note that you cannot configure more than one index server.'),
++    '#default_value' => $server['index'],
++  );
++  $form['container']['submit'] = array(
++    '#type' => 'submit',
++    '#value' => is_numeric($server_id) ? t('Save') : t('Add'),
++  );
++  return $form;
++}
++
++/**
++ * Solr servers form validation callback handler.
++ */
++function apachesolr_settings_servers_form_validate($form, &$form_state) {
++  // Check for existing servers other than current.
++  foreach (variable_get('apachesolr_servers', array()) as $delta => $server) {
++    if (($form_state['values']['server_id'] == 'add') || ($form_state['values']['server_id'] != 'add' && $form_state['values']['server_id'] != $delta)) {
++
++      // Does other server have this name?
++      if ($form_state['values']['name'] == $server['name']) {
++        form_set_error('name', t('A server with the name <strong>@name</strong> is already defined in row #@row.', array('@name' => $server['name'], '@row' => $delta)));
++      }
++      
++      // Does other server have this URL?
++      if ($form_state['values']['url']['host'] == $server['host'] && $form_state['values']['url']['port'] == $server['port'] && $form_state['values']['url']['path'] == $server['path']) {
++        form_set_error('url', t('A server with the URL <strong>!host:!port/!path</strong> is already defined in row #@row.', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'), '@row' => $delta)));
++      }
++    }
++  }
++}
++
++/**
++ * Solr servers form submit callback handler.
++ */
++function apachesolr_settings_servers_form_submit($form, &$form_state) {
++  $servers = variable_get('apachesolr_servers', array());
++  $server = array(
++    'name' => $form_state['values']['name'],
++    'host' => $form_state['values']['url']['host'],
++    'port' => $form_state['values']['url']['port'],
++    'path' => $form_state['values']['url']['path'],
++    'query' => $form_state['values']['query'],
++    'index' => $form_state['values']['index'],
++  );
++  
++  // If this is an index server, remove the index attribute from the others.
++  if ($server['index']) {
++    foreach ($servers as $delta => $server_item) {
++      $servers[$delta]['index'] = FALSE;
++    }
++  }
++  
++  if ($form_state['values']['server_id'] == 'add') {
++    $servers[] = $server;
++  }
++  else {
++    $servers[$form_state['values']['server_id']] = $server;
++  }
++  
++  // Save servers into one single variable.
++  variable_set('apachesolr_servers', $servers);
++  
++  drupal_set_message(t('Server !host:!port/!path was saved as %name', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'), '%name' => $server['name'])));
++  $form_state['redirect'] = 'admin/settings/apachesolr/servers';
++}
++
++/**
++ * Solr server deleting confirmation page
++ */
++function apachesolr_settings_servers_delete(&$form_state, $server_id) {
++  $servers = variable_get('apachesolr_servers', array());
++  $server = $servers[$server_id];
++  
++  $form['#redirect'] = 'admin/settings/apachesolr/servers';
++  $form['server_id'] = array(
++    '#type' =>  'value',
++    '#value' => $server_id,
++  );
++
++  $question = t('Are you sure you want to delete the Solr server %name?', array('%name' => $server['name']));
++  
++  $items = array(
++    t('Name: @name', array('@name' => $server['name'])),
++    t('Host: @host', array('@host' => $server['host'])),
++    t('Port: @port', array('@port' => $server['port'])),
++    t('Path: @path', array('@path' => $server['path'])),
++    t('Query server: @query', array('@query' => $server['query'] ? t('Yes') : t('No'))),
++    t('Index server: @index', array('@index' => $server['index'] ? t('Yes') : t('No'))),
++  );
++  $description = theme('item_list', $items, t('Solr server details:'));
++  
++  // Check if this is the only query server.
++  $query = 0;
++  foreach ($servers as $delta => $server_item) {
++    if ($delta != $server_id && $server_item['query']) {
++        $query++;
++    }
++  }
++
++  $items = array(); 
++  if ($server['index']) {
++    $items[] = t('This Solr server is the server used to index this site. If you will delete it than you\'ll have to configure other server as index server.');
++  }
++  if ($query == 0) {
++    $items[] = t('This Solr server is the only one configured to be queried for data. If you will delete it than you\'ll have to configure at least other server as query server.');
++  }
++  $items[] = t('This action cannot be undone.');
++  $description .= theme('item_list', $items, t('Warnings:'));
++  
++  return confirm_form($form, $question, 'admin/settings/apachesolr/servers', $description, t('Delete'), t('Cancel'));
++}
++
++/**
++ * Solr server delete callback handler.
++ */ 
++function apachesolr_settings_servers_delete_submit($form, &$form_state) {
++  $servers = variable_get('apachesolr_servers', array());
++  $server = array_splice($servers, $form_state['values']['server_id'], 1);
++  drupal_set_message(t('The Solr server %name (!host:!port/!path) has been deleted.', array('%name' => $server[0]['name'], '!host' => $server[0]['host'], '!port' => $server[0]['port'], '!path' => ltrim($server[0]['path'], '/'))));
++  variable_set('apachesolr_servers', $servers);
++}
++
++/**
+  * Gets information about the fields already in solr index.
+  */
+ function apachesolr_index_page() {
+Index: apachesolr.css
+===================================================================
+RCS file: apachesolr.css
+diff -N apachesolr.css
+--- /dev/null	1 Jan 1970 00:00:00 -0000
++++ apachesolr.css	27 Jan 2010 16:23:41 -0000
+@@ -0,0 +1,7 @@
++/* $Id$ */
++
++tr.apachesolr-servers-active-server td {
++  font-weight: bold;
++  color: #ffffff; 
++  background-color: #bbbbbb;
++}
+Index: apachesolr.install
+===================================================================
+RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.install,v
+retrieving revision 1.1.4.30
+diff -u -p -r1.1.4.30 apachesolr.install
+--- apachesolr.install	27 Dec 2009 16:47:37 -0000	1.1.4.30
++++ apachesolr.install	27 Jan 2010 16:23:41 -0000
+@@ -15,26 +15,51 @@ function apachesolr_requirements($phase)
+   // Ensure translations don't break at install time
+   $t = get_t();
+   if ($phase == 'runtime' && $file_exists) {
+-    $host = variable_get('apachesolr_host', 'localhost');
+-    $port = variable_get('apachesolr_port', 8983);
+-    $path = variable_get('apachesolr_path', '/solr');
+-    $ping = FALSE;
+-    try {
+-      $solr = apachesolr_get_solr();
+-      $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
+-      // If there is no $solr object, there is no server available, so don't continue.
+-      if (!$ping) {
+-        throw new Exception(t('No Solr instance available when checking requirements.'));
+-      }
+-    }
+-    catch (Exception $e) {
+-      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+-    }
+-    $value =  $ping ? $t('Your site has contacted the Apache Solr server.') : $t('Your site was unable to contact the Apache Solr server.');
+-    $severity = $ping ? REQUIREMENT_OK : REQUIREMENT_ERROR;
+-    $description = theme('item_list', array($t('Host: %host', array('%host' => $host)),
+-                        $t('Port: %port', array('%port' => $port)),
+-                        $t('Path: %path', array('%path' => $path))));
++    $servers = variable_get('apachesolr_servers', array());
++    $contacted = 0;
++    $items = array();
++    foreach ($servers as $delta => $server) {
++      $ping = FALSE;
++      try {
++        $solr = apachesolr_get_server($server['host'], $server['port'], $server['path']);
++        $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
++        // If there is no $solr object, there is no server available, so don't continue.
++        if ($ping) {
++          $contacted++;
++        }
++        else {
++          throw new Exception(t('No Solr instance available when checking requirements.'));
++        }
++      }
++      catch (Exception $e) {
++        watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
++      }
++      $roles = array();
++      if ($server['query']) {
++        $roles[] = $t('query server');
++      }
++      if ($server['index']) {
++        $roles[] = $t('index server');
++      }
++      $roles = count($roles) ? implode(', ', $roles) : $t('No role assigned');
++      $items[] = $t('<strong>URL:</strong> !host:!port/!path; <strong>Roles:</strong> @roles; <strong>Status:</strong> @contacted', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'), '@roles' => $roles, '@contacted' => $ping ? $t('successfully contacted') : $t('cannot be contacted!')));
++    }
++    if (count($servers)) {
++      $value = $t('@count Solr servers are configured on your site.', array('@count' => count($servers))) .' ';
++      if ($contacted) {
++        $value .= ($contacted == 1) ? $t('One has been successfully contacted.') : $t('@contacted of them were successfully contacted.', array('@contacted' => $contacted));
++        $severity = REQUIREMENT_OK;
++      }
++      else {
++        $value .= $t('None were successfully contacted.');
++        $severity = REQUIREMENT_ERROR;
++      }
++    }
++    else {
++      $value = $t('Your site has no Solr server configured yet. You can add Solr servers <a href="!solr">here</a>.', array('!url' => url('admin/settings/apachesolr/servers')));
++      $severity = REQUIREMENT_ERROR;
++    }
++    $description = count($servers) ? theme('item_list', $items) : '';
+     $requirements['apachesolr'] = array(
+       'title' => $t('Apache Solr'),
+       'value' => $value,
+@@ -261,3 +286,28 @@ function apachesolr_update_6005() {
+   db_create_table($ret, 'cache_apachesolr', $table);
+   return $ret;
+ }
++
++/**
++ * Copy the old Solr connection infos to the new array variable.
++ */
++function apachesolr_update_6006() {
++  $ret = array();
++  $server = array(
++    'name' => 'Solr server',
++    'host' => variable_get('apachesolr_host', 'localhost'),
++    'port' => variable_get('apachesolr_port', '8983'),
++    'path' => variable_get('apachesolr_path', '/solr'),
++    'query' => TRUE,
++    'index' => TRUE,
++  );
++  variable_set('apachesolr_servers', array($server));
++  $ret[] = array('success' => TRUE, 'query' => 'Solr data is transfered to the new format:<pre>'. print_r(array($server), TRUE) .'</pre>');
++  
++  // @todo: Delete old variables
++  // variable_del('apachesolr_host');
++  // variable_del('apachesolr_port');
++  // variable_del('apachesolr_path');
++  // $ret[] = array('success' => TRUE, 'query' => 'Drupal variables: "apachesolr_host", "apachesolr_port", "apachesolr_path" were deleted.');
++  
++  return $ret;
++}
+Index: apachesolr.module
+===================================================================
+RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.module,v
+retrieving revision 1.1.2.12.2.182
+diff -u -p -r1.1.2.12.2.182 apachesolr.module
+--- apachesolr.module	2 Jan 2010 13:48:14 -0000	1.1.2.12.2.182
++++ apachesolr.module	27 Jan 2010 16:23:41 -0000
+@@ -30,6 +30,30 @@ function apachesolr_menu() {
+     'file'               => 'apachesolr.admin.inc',
+     'type'               => MENU_DEFAULT_LOCAL_TASK,
+   );
++  $items['admin/settings/apachesolr/servers'] = array(
++    'title'              => 'Solr Servers',
++    'page callback'      => 'apachesolr_settings_servers',
++    'weight'             => -9,
++    'access arguments'   => array('administer search'),
++    'file'               => 'apachesolr.admin.inc',
++    'type'               => MENU_LOCAL_TASK,
++  );
++  $items['admin/settings/apachesolr/servers/%apachesolr_server'] = array(
++    'title'              => 'Solr Servers',
++    'page callback'      => 'apachesolr_settings_servers',
++    'page arguments'     => array(4),
++    'access arguments'   => array('administer search'),
++    'file'               => 'apachesolr.admin.inc',
++    'type'               => MENU_CALLBACK,
++  );
++  $items['admin/settings/apachesolr/servers/%apachesolr_server/delete'] = array(
++    'title'              => 'Solr Servers',
++    'page callback'      => 'apachesolr_settings_servers',
++    'page arguments'     => array(4, 'delete'),
++    'access arguments'   => array('administer search'),
++    'file'               => 'apachesolr.admin.inc',
++    'type'               => MENU_CALLBACK,
++  );
+   $items['admin/settings/apachesolr/enabled-filters'] = array(
+     'title'              => 'Enabled filters',
+     'page callback'      => 'drupal_get_form',
+@@ -92,6 +116,20 @@ function apachesolr_menu() {
+ }
+ 
+ /**
++ * Menu loader for %apachesolr_server.
++ *
++ * @param $server_id
++ *   Argument passed.
++ *
++ * @return
++ *   The server informations ID of FALSE.
++ */
++function apachesolr_server_load($server_id = NULL) {
++  $servers = variable_get('apachesolr_servers', array());
++  return isset($servers[$server_id]) ? $servers[$server_id] : FALSE;
++}
++
++/**
+  * Determines Apache Solr's behavior when searching causes an exception (e.g. Solr isn't available.)
+  * Depending on the admin settings, possibly redirect to Drupal's core search.
+  *
+@@ -1113,24 +1151,56 @@ function apachesolr_has_searched($search
+ }
+ 
+ /**
+- * Factory method for solr singleton object. Structure allows for an arbitrary
+- * number of solr objects to be used based on the host, port, path combination.
+- * Get an instance like this:
+- *   $solr = apachesolr_get_solr();
++ * Method used to obtain the Solr load balancer object
+  */
+-function apachesolr_get_solr($host = NULL, $port = NULL, $path = NULL) {
+-  static $solr_cache;
++function apachesolr_get_solr() {
++  static $solr;
++  $servers = variable_get('apachesolr_servers', array());
++  if (!isset($solr)) {
++    // We use only one Solr server (this may be the main usage of this module)
++    // Do not load the entire Balancer API use only a single Solr object.
++    if (count($servers) == 1) {
++      $solr = apachesolr_get_server($servers[0]['host'], $servers[0]['port'], $servers[0]['path']);
++    }
++    // More than one Solr servers are configured on the site so prepare the Solr
++    // object as a Balancer object.
++    else {
++      $query_services = $index_services = array();
++      foreach ($servers as $delta => $server) {
+ 
+-  if (empty($host)) {
+-    $host = variable_get('apachesolr_host', 'localhost');
+-  }
+-  if (empty($port)) {
+-    $port = variable_get('apachesolr_port', '8983');
+-  }
+-  if (empty($path)) {
+-    $path = variable_get('apachesolr_path', '/solr');
++        $service = ($server['query'] || $server['index']) ? apachesolr_get_server($server['host'], $server['port'], $server['path']) : FALSE;
++        
++        // Collect query services (AKA readable services).
++        if ($server['query'] && $service) {
++          $query_services[] = $service;
++        }
++
++        // Collect index services (AKA writable services).
++        if ($server['index'] && $service) {
++          $index_services[] = $service;
++        }
++      }
++
++      include_once(drupal_get_path('module', 'apachesolr') .'/Drupal_Apache_Solr_Service_Balancer.php');
++      try {
++        $solr = new Drupal_Apache_Solr_Service_Balancer($query_services, $index_services);
++      }
++      catch (Exception $e) {
++        watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
++        return;
++      }
++    }
+   }
+ 
++  return $solr;
++}
++
++/**
++ * Method used to obtain a single Solr object. A clone of old apachesolr_get_solr() function but arguments are mandatory.
++ */
++function apachesolr_get_server($host, $port, $path) {
++  static $solr_cache;
++
+   if (empty($solr_cache[$host][$port][$path])) {
+     list($module, $filepath, $class) = variable_get('apachesolr_service_class', array('apachesolr', 'Drupal_Apache_Solr_Service.php', 'Drupal_Apache_Solr_Service'));
+     include_once(drupal_get_path('module', $module) .'/'. $filepath);
diff -urN apachesolr.orig/apachesolr.css apachesolr/apachesolr.css
--- apachesolr.orig/apachesolr.css	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/apachesolr.css	2010-02-07 14:38:13.000000000 +0100
@@ -0,0 +1,7 @@
+/* $Id$ */
+
+tr.apachesolr-servers-active-server td {
+  font-weight: bold;
+  color: #ffffff; 
+  background-color: #bbbbbb;
+}
diff -urN apachesolr.orig/apachesolr.install apachesolr/apachesolr.install
--- apachesolr.orig/apachesolr.install	2009-12-27 18:15:50.000000000 +0100
+++ apachesolr/apachesolr.install	2010-02-07 14:38:13.000000000 +0100
@@ -15,26 +15,51 @@
   // Ensure translations don't break at install time
   $t = get_t();
   if ($phase == 'runtime' && $file_exists) {
-    $host = variable_get('apachesolr_host', 'localhost');
-    $port = variable_get('apachesolr_port', 8983);
-    $path = variable_get('apachesolr_path', '/solr');
-    $ping = FALSE;
-    try {
-      $solr = apachesolr_get_solr();
-      $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
-      // If there is no $solr object, there is no server available, so don't continue.
-      if (!$ping) {
-        throw new Exception(t('No Solr instance available when checking requirements.'));
-      }
-    }
-    catch (Exception $e) {
-      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
-    }
-    $value =  $ping ? $t('Your site has contacted the Apache Solr server.') : $t('Your site was unable to contact the Apache Solr server.');
-    $severity = $ping ? REQUIREMENT_OK : REQUIREMENT_ERROR;
-    $description = theme('item_list', array($t('Host: %host', array('%host' => $host)),
-                        $t('Port: %port', array('%port' => $port)),
-                        $t('Path: %path', array('%path' => $path))));
+    $servers = variable_get('apachesolr_servers', array());
+    $contacted = 0;
+    $items = array();
+    foreach ($servers as $delta => $server) {
+      $ping = FALSE;
+      try {
+        $solr = apachesolr_get_server($server['host'], $server['port'], $server['path']);
+        $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
+        // If there is no $solr object, there is no server available, so don't continue.
+        if ($ping) {
+          $contacted++;
+        }
+        else {
+          throw new Exception(t('No Solr instance available when checking requirements.'));
+        }
+      }
+      catch (Exception $e) {
+        watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      }
+      $roles = array();
+      if ($server['query']) {
+        $roles[] = $t('query server');
+      }
+      if ($server['index']) {
+        $roles[] = $t('index server');
+      }
+      $roles = count($roles) ? implode(', ', $roles) : $t('No role assigned');
+      $items[] = $t('<strong>URL:</strong> !host:!port/!path; <strong>Roles:</strong> @roles; <strong>Status:</strong> @contacted', array('!host' => $server['host'], '!port' => $server['port'], '!path' => ltrim($server['path'], '/'), '@roles' => $roles, '@contacted' => $ping ? $t('successfully contacted') : $t('cannot be contacted!')));
+    }
+    if (count($servers)) {
+      $value = $t('@count Solr servers are configured on your site.', array('@count' => count($servers))) .' ';
+      if ($contacted) {
+        $value .= ($contacted == 1) ? $t('One has been successfully contacted.') : $t('@contacted of them were successfully contacted.', array('@contacted' => $contacted));
+        $severity = REQUIREMENT_OK;
+      }
+      else {
+        $value .= $t('None were successfully contacted.');
+        $severity = REQUIREMENT_ERROR;
+      }
+    }
+    else {
+      $value = $t('Your site has no Solr server configured yet. You can add Solr servers <a href="!solr">here</a>.', array('!url' => url('admin/settings/apachesolr/servers')));
+      $severity = REQUIREMENT_ERROR;
+    }
+    $description = count($servers) ? theme('item_list', $items) : '';
     $requirements['apachesolr'] = array(
       'title' => $t('Apache Solr'),
       'value' => $value,
@@ -261,3 +286,28 @@
   db_create_table($ret, 'cache_apachesolr', $table);
   return $ret;
 }
+
+/**
+ * Copy the old Solr connection infos to the new array variable.
+ */
+function apachesolr_update_6006() {
+  $ret = array();
+  $server = array(
+    'name' => 'Solr server',
+    'host' => variable_get('apachesolr_host', 'localhost'),
+    'port' => variable_get('apachesolr_port', '8983'),
+    'path' => variable_get('apachesolr_path', '/solr'),
+    'query' => TRUE,
+    'index' => TRUE,
+  );
+  variable_set('apachesolr_servers', array($server));
+  $ret[] = array('success' => TRUE, 'query' => 'Solr data is transfered to the new format:<pre>'. print_r(array($server), TRUE) .'</pre>');
+  
+  // @todo: Delete old variables
+  // variable_del('apachesolr_host');
+  // variable_del('apachesolr_port');
+  // variable_del('apachesolr_path');
+  // $ret[] = array('success' => TRUE, 'query' => 'Drupal variables: "apachesolr_host", "apachesolr_port", "apachesolr_path" were deleted.');
+  
+  return $ret;
+}
diff -urN apachesolr.orig/apachesolr.module apachesolr/apachesolr.module
--- apachesolr.orig/apachesolr.module	2010-01-02 15:42:58.000000000 +0100
+++ apachesolr/apachesolr.module	2010-02-07 15:35:42.000000000 +0100
@@ -30,6 +30,30 @@
     'file'               => 'apachesolr.admin.inc',
     'type'               => MENU_DEFAULT_LOCAL_TASK,
   );
+  $items['admin/settings/apachesolr/servers'] = array(
+    'title'              => 'Solr Servers',
+    'page callback'      => 'apachesolr_settings_servers',
+    'weight'             => -9,
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items['admin/settings/apachesolr/servers/%apachesolr_server'] = array(
+    'title'              => 'Solr Servers',
+    'page callback'      => 'apachesolr_settings_servers',
+    'page arguments'     => array(4),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items['admin/settings/apachesolr/servers/%apachesolr_server/delete'] = array(
+    'title'              => 'Solr Servers',
+    'page callback'      => 'apachesolr_settings_servers',
+    'page arguments'     => array(4, 'delete'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
   $items['admin/settings/apachesolr/enabled-filters'] = array(
     'title'              => 'Enabled filters',
     'page callback'      => 'drupal_get_form',
@@ -100,6 +124,20 @@
 }
 
 /**
+ * Menu loader for %apachesolr_server.
+ *
+ * @param $server_id
+ *   Argument passed.
+ *
+ * @return
+ *   The server informations ID of FALSE.
+ */
+function apachesolr_server_load($server_id = NULL) {
+  $servers = variable_get('apachesolr_servers', array());
+  return isset($servers[$server_id]) ? $servers[$server_id] : FALSE;
+}
+
+/**
  * Determines Apache Solr's behavior when searching causes an exception (e.g. Solr isn't available.)
  * Depending on the admin settings, possibly redirect to Drupal's core search.
  *
@@ -1312,24 +1350,116 @@
 }
 
 /**
- * Factory method for solr singleton object. Structure allows for an arbitrary
- * number of solr objects to be used based on the host, port, path combination.
- * Get an instance like this:
- *   $solr = apachesolr_get_solr();
- */
-function apachesolr_get_solr($host = NULL, $port = NULL, $path = NULL) {
-  static $solr_cache;
-
-  if (empty($host)) {
-    $host = variable_get('apachesolr_host', 'localhost');
+ * Internal method to get a solr server instance. This is exported to its own
+ * function so we could check servers availability through cron.
+ * 
+ * This method does heavy ping() usage which implies numerous and useless
+ * HTTP request in most cases. That's why this method should be externalized.
+ * 
+ * TODO: note that using this implementation, the Balancer override is almost
+ * useless, this could be improved.
+ */
+function _apachesolr_get_solr($readonly) {
+  static $loadbalancer;
+
+  // Load includes and instanciate balancer if we need it.
+  if (!isset($loadbalancer)) {
+    include_once(drupal_get_path('module', 'apachesolr') . '/Drupal_Apache_Solr_Service_Balancer.php');
+    try {
+      $loadbalancer = new Drupal_Apache_Solr_Service_Balancer();
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      return;
+    }
   }
-  if (empty($port)) {
-    $port = variable_get('apachesolr_port', '8983');
+
+  // Get available servers (event if in case we are going through
+  // apachesolr_get_solr() this variable already been loaded this won't hurt
+  // because all variables are statically cached.
+  $servers = variable_get('apachesolr_servers', array());
+
+  // Go through services to load them all (matching to the $readonly
+  // parameter).
+  foreach ($servers as $delta => $server) {
+    try {
+      if ($readonly && $server['query']) {
+        $loadbalancer->addReadService(apachesolr_get_server($server['host'], $server['port'], $server['path']));
+      }
+      else if (!$readonly && $server['index']) {
+        $loadbalancer->addReadService(apachesolr_get_server($server['host'], $server['port'], $server['path']));
+      }
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      return;
+    }
   }
-  if (empty($path)) {
-    $path = variable_get('apachesolr_path', '/solr');
+
+  // Selected reader and writers are cached within the load balancer class.
+  return $readonly ? $loadbalancer->getReadService() : $loadbalancer->getWriteService();
+}
+
+/**
+ * Method used to obtain the Solr load balancer object
+ */
+function apachesolr_get_solr($readonly = TRUE) {
+  static $reader, $writer;
+
+  if (($readonly && !isset($reader)) || (!$readonly && !isset($writer)) ) {
+
+    // Get available servers
+    $servers = variable_get('apachesolr_servers', array());
+
+    // We use only one Solr server (this may be the main usage of this module)
+    // Do not load the entire Balancer API use only a single Solr object.
+    if (count($servers) == 1) {
+      $writer = apachesolr_get_server($servers[0]['host'], $servers[0]['port'], $servers[0]['path']);
+      $reader = $writer;
+    }
+
+    // Asked for a reader, hope then the PHP runtime won't ask for a writer
+    // unfortunately this is difficult to predict.
+    else if ($readonly && $delta = variable_get('apachesolr_current_reader', FALSE)) {
+      $server = &$servers[$delta];
+      $reader = apachesolr_get_server($server['host'], $server['port'], $server['path']);
+    }
+
+    // Asked for a writer.
+    else if (!$readonly && $delta = variable_get('apachesolr_current_writer', FALSE)) {
+      $server = &$servers[$delta];
+      $writer = apachesolr_get_server($server['host'], $server['port'], $server['path']);
+      // In case we set the writer, set also the reader on the same instance
+      // to ensure we are going to modify and read on the same instance.
+      // TODO: this might be wrong, correct me if it is.
+      $reader = $writer;
+    }
+
+    // None variable set, we are going to run the load balancer implementation
+    // to find one available server. Note that this will do a lot of HTTP SolR
+    // ping requests so it can be quite heavy.
+    else {
+      if ($readonly) {
+        $reader = _apachesolr_get_solr($readonly);
+      }
+      else {
+        // We won't load writer and reader together, because in most cases
+        // clients will hit to do search requests, only cron (in most cases)
+        // will ask for a writer.
+        $writer = _apachesolr_get_solr($readonly);
+      }
+    }
   }
 
+  return $readonly ? $reader : $writer;
+}
+
+/**
+ * Method used to obtain a single Solr object. A clone of old apachesolr_get_solr() function but arguments are mandatory.
+ */
+function apachesolr_get_server($host, $port, $path) {
+  static $solr_cache;
+
   if (empty($solr_cache[$host][$port][$path])) {
     list($module, $filepath, $class) = variable_get('apachesolr_service_class', array('apachesolr', 'Drupal_Apache_Solr_Service.php', 'Drupal_Apache_Solr_Service'));
     include_once(drupal_get_path('module', $module) .'/'. $filepath);
diff -urN apachesolr.orig/apachesolr.module.orig apachesolr/apachesolr.module.orig
--- apachesolr.orig/apachesolr.module.orig	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/apachesolr.module.orig	2010-01-02 15:42:58.000000000 +0100
@@ -0,0 +1,2072 @@
+<?php
+// $Id: apachesolr.module,v 1.1.2.12.2.155.2.57 2010/01/02 14:42:58 robertDouglass Exp $
+
+/**
+ * @file
+ *   Integration with the Apache Solr search application.
+ */
+
+define('APACHESOLR_READ_WRITE', 0);
+define('APACHESOLR_READ_ONLY', 1);
+
+/**
+ * Implementation of hook_menu().
+ */
+function apachesolr_menu() {
+  $items = array();
+  $items['admin/settings/apachesolr'] = array(
+    'title'              => 'Apache Solr',
+    'description'        => 'Administer Apache Solr.',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_settings'),
+    'access callback'    => 'user_access',
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items['admin/settings/apachesolr/settings'] = array(
+    'title'              => 'Settings',
+    'weight'             => -10,
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['admin/settings/apachesolr/enabled-filters'] = array(
+    'title'              => 'Enabled filters',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_enabled_facets_form'),
+    'weight'             => -7,
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items['admin/settings/apachesolr/index'] = array(
+    'title'              => 'Search index',
+    'page callback'      => 'apachesolr_index_page',
+    'access arguments'   => array('administer search'),
+    'weight'             => -8,
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items['admin/settings/apachesolr/index/clear/confirm'] = array(
+    'title'              => 'Confirm the re-indexing of all content',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_clear_index_confirm'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items['admin/settings/apachesolr/index/batch/confirm'] = array(
+    'title'              => 'Confirm the batch re-indexing of all content',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_batch_index_confirm'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items['admin/settings/apachesolr/index/delete/confirm'] = array(
+    'title'              => 'Confirm index deletion',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_delete_index_confirm'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items['admin/reports/apachesolr'] = array(
+    'title'              => 'Apache Solr search index',
+    'page callback'      => 'apachesolr_index_report',
+    'access arguments'   => array('access site reports'),
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items['admin/reports/apachesolr/index'] = array(
+    'title'              => 'Search index',
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['admin/settings/apachesolr/mlt/add_block'] = array(
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_mlt_add_block_form'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items['admin/settings/apachesolr/mlt/delete_block/%'] = array(
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_mlt_delete_block_form', 5),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Determines Apache Solr's behavior when searching causes an exception (e.g. Solr isn't available.)
+ * Depending on the admin settings, possibly redirect to Drupal's core search.
+ *
+ * @param $search_name
+ *   The name of the search implementation.
+ *
+ * @param $querystring
+ *   The search query that was issued at the time of failure.
+ */
+function apachesolr_failure($search_name, $querystring) {
+  $fail_rule = variable_get('apachesolr_failure', 'show_error');
+
+  switch ($fail_rule) {
+    case 'show_error':
+      drupal_set_message(t('The Apache Solr search engine is not available. Please contact your site administrator.'), 'error');
+      break;
+    case 'show_drupal_results':
+      drupal_set_message(t("%search_name is not available. Your search is being redirected.", array('%search_name' => $search_name)));
+      drupal_goto('search/node/' . drupal_urlencode($querystring));
+      break;
+    case 'show_no_results':
+      return;
+  }
+}
+
+/**
+ * Like $site_key in _update_refresh() - returns a site-specific hash.
+ */
+function apachesolr_site_hash() {
+  if (!($hash = variable_get('apachesolr_site_hash', FALSE))) {
+    global $base_url;
+    $hash = substr(md5(md5($base_url . drupal_get_private_key() . 'apachesolr')), 0, 12);
+    variable_set('apachesolr_site_hash', $hash);
+  }
+  return $hash;
+}
+
+/**
+ * Generate a unique ID for an entity being indexed.
+ *
+ * @param $id
+ *   An id number (or string) unique to this site, such as a node ID.
+ * @param $entity
+ *   A string like 'node', 'file', 'user', or some other Drupal object type.
+ *
+ * @return
+ *   A string combining the parameters with the site hash.
+ */
+function apachesolr_document_id($id, $entity = 'node') {
+  return apachesolr_site_hash() . "/$entity/" . $id;
+}
+
+/**
+ * Implementation of hook_user().
+ *
+ * Mark nodes as needing re-indexing if the author name changes.
+ */
+function apachesolr_user($op, &$edit, &$account) {
+  switch ($op) {
+    case 'update':
+      if (isset($edit['name']) && $account->name != $edit['name']) {
+        db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE nid IN (SELECT nid FROM {node} WHERE uid = %d)", time(), $account->uid);
+      }
+      break;
+  }
+}
+
+/**
+ * Implementation of hook_taxonomy().
+ *
+ * Mark nodes as needing re-indexing if a term name changes.
+ */
+function apachesolr_taxonomy($op, $type, $edit) {
+  if ($type == 'term' && ($op == 'update')) {
+    db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE nid IN (SELECT nid FROM {term_node} WHERE tid = %d)", time(), $edit['tid']);
+  }
+  // TODO: the rest, such as term deletion.
+}
+
+/**
+ * Implementation of hook_comment().
+ *
+ * Mark nodes as needing re-indexing if comments are added or changed.
+ * Like search_comment().
+ */
+function apachesolr_comment($edit, $op) {
+  $edit = (array) $edit;
+  switch ($op) {
+    // Reindex the node when comments are added or changed
+    case 'insert':
+    case 'update':
+    case 'delete':
+    case 'publish':
+    case 'unpublish':
+      // TODO: do we want to skip this if we are excluding comments
+      // from the index for this node type?
+      apachesolr_mark_node($edit['nid']);
+      break;
+  }
+}
+
+/**
+ * Mark one node as needing re-indexing.
+ */
+function apachesolr_mark_node($nid) {
+  db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE nid = %d", time(), $nid);
+}
+
+/**
+ * Implementation of hook_node_type().
+ *
+ * Mark nodes as needing re-indexing if a node type name changes.
+ */
+function apachesolr_node_type($op, $info) {
+  if ($op != 'delete' && !empty($info->old_type) && $info->old_type != $info->type) {
+    // We cannot be sure we are going before or after node module.
+    db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE nid IN (SELECT nid FROM {node} WHERE type = '%s' OR type = '%s')", time(), $info->old_type, $info->type);
+  }
+}
+
+/**
+ * Helper function for modules implmenting hook_search's 'status' op.
+ */
+function apachesolr_index_status($namespace) {
+  list($excluded_types, $args, $join_sql, $exclude_sql) = apachesolr_exclude_types($namespace);
+  $total = db_result(db_query("SELECT COUNT(asn.nid) FROM {apachesolr_search_node} asn ". $join_sql ."WHERE asn.status = 1 " . $exclude_sql, $excluded_types));
+  $remaining = db_result(db_query("SELECT COUNT(asn.nid) FROM {apachesolr_search_node} asn ". $join_sql ."WHERE (asn.changed > %d OR (asn.changed = %d AND asn.nid > %d)) AND asn.status = 1 "  . $exclude_sql, $args));
+  return array('remaining' => $remaining, 'total' => $total);
+}
+
+/**
+ * Returns last changed and last nid for an indexing namespace.
+ */
+function apachesolr_get_last_index($namespace) {
+  $stored = variable_get('apachesolr_index_last', array());
+  return isset($stored[$namespace]) ? $stored[$namespace] : array('last_change' => 0, 'last_nid' => 0);
+}
+
+/**
+ * Clear a specific namespace's last changed and nid, or clear all.
+ */
+function apachesolr_clear_last_index($namespace = '') {
+  if ($namespace) {
+    $stored = variable_get('apachesolr_index_last', array());
+    unset($stored[$namespace]);
+    variable_set('apachesolr_index_last', $stored);
+  }
+  else {
+    variable_del('apachesolr_index_last');
+  }
+}
+
+/**
+ * Truncate and rebuild the apachesolr_search_node table, reset the apachesolr_index_last variable.
+ * This is the most complete way to force reindexing, or to build the indexing table for the
+ * first time.
+ *
+ * @param $type
+ *   A single content type to be reindexed, leaving the others unaltered.
+ */
+function apachesolr_rebuild_index_table($type = NULL) {
+  if (isset($type)) {
+    db_query("DELETE FROM {apachesolr_search_node} WHERE nid IN (SELECT nid FROM {node} WHERE type = '%s')", $type);
+    // Populate table
+    db_query("INSERT INTO {apachesolr_search_node} (nid, status, changed)
+              SELECT n.nid, n.status, %d AS changed
+              FROM {node} n WHERE n.type = '%s'", time(), $type);
+  }
+  else {
+    db_query("DELETE FROM {apachesolr_search_node}");
+    // Populate table
+    db_query("INSERT INTO {apachesolr_search_node} (nid, status, changed)
+              SELECT n.nid, n.status, GREATEST(n.created, n.changed, c.last_comment_timestamp) AS changed
+              FROM {node} n
+              LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid");
+    // Make sure no nodes end up with a timestamp that's in the future.
+    $time = time();
+    db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE changed > %d", $time, $time);
+    apachesolr_clear_last_index();
+  }
+}
+
+function apachesolr_exclude_types($namespace) {
+  extract(apachesolr_get_last_index($namespace));
+  $excluded_types = module_invoke_all('apachesolr_types_exclude', $namespace);
+  $args = array($last_change, $last_change, $last_nid);
+  $join_sql = '';
+  $exclude_sql = '';
+  if ($excluded_types) {
+    $excluded_types = array_unique($excluded_types);
+    $join_sql = "INNER JOIN {node} n ON n.nid = asn.nid ";
+    $exclude_sql = "AND n.type NOT IN(". db_placeholders($excluded_types, 'varchar') .") ";
+    $args = array_merge($args, $excluded_types);
+  }
+  return array($excluded_types, $args, $join_sql, $exclude_sql);
+}
+
+/**
+ * Returns an array of rows from a query based on an indexing namespace.
+ */
+function apachesolr_get_nodes_to_index($namespace, $limit) {
+  $rows = array();
+  if (variable_get('apachesolr_read_only', 0)) {
+    return $rows;
+  }
+  list($excluded_types, $args, $join_sql, $exclude_sql) = apachesolr_exclude_types($namespace);
+  $result = db_query_range("SELECT asn.nid, asn.changed FROM {apachesolr_search_node} asn ". $join_sql ."WHERE (asn.changed > %d OR (asn.changed = %d AND asn.nid > %d)) AND asn.status = 1 ". $exclude_sql ."ORDER BY asn.changed ASC, asn.nid ASC", $args, 0, $limit);
+  while($row = db_fetch_object($result)) {
+    $rows[] = $row;
+  }
+  return $rows;
+}
+
+/**
+ * Function to handle the indexing of nodes.
+ *
+ * The calling function must supply a namespace.
+ * Returns FALSE if no nodes were indexed (none found or error).
+ */
+/**
+ * Handles the indexing of nodes.
+ *
+ * @param array $rows
+ *   Each $row in $rows must have:
+ *   $row->nid
+ *   $row->changed
+ * @param string $namespace
+ *   Usually the calling module. Is used as a clue for other modules
+ *   when they decide whether to create extra $documents, and is used
+ *   to track the last_index timestamp.
+ * @return timestamp $position
+ *   Either a timestamp representing the last value of apachesolr_get_last_index
+ *   to be indexed, or FALSE if indexing failed.
+ */
+function apachesolr_index_nodes($rows, $namespace) {
+  if (!$rows) {
+    // Nothing to do.
+    return FALSE;
+  }
+
+  try {
+    // Get the $solr object
+    $solr = apachesolr_get_solr();
+    // If there is no server available, don't continue.
+    if (!$solr->ping(variable_get('apachesolr_ping_timeout', 4))) {
+      throw new Exception(t('No Solr instance available during indexing.'));
+    }
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    return FALSE;
+  }
+  include_once(drupal_get_path('module', 'apachesolr') .'/apachesolr.index.inc');
+  $documents = array();
+  $old_position = apachesolr_get_last_index($namespace);
+  $position = $old_position;
+
+  // Invoke hook_apachesolr_document_handlers to find out what modules build $documents
+  // from nodes in this namespace.
+  $callbacks = module_invoke_all('apachesolr_document_handlers', 'node', $namespace);
+  $callbacks = array_filter($callbacks, 'function_exists');
+
+  foreach ($rows as $row) {
+    try {
+      // Build node. Set reset = TRUE to avoid static caching of all nodes that get indexed.
+      if ($node = node_load($row->nid, NULL, TRUE)) {
+        foreach ($callbacks as $callback) {
+          // The callback can either return a $document or an array of $documents.
+          $documents[] = $callback($node, $namespace);
+        }
+      }
+      // Variables to track the last item changed.
+      $position['last_change'] = $row->changed;
+      $position['last_nid'] = $row->nid;
+    }
+    catch (Exception $e) {
+      // Something bad happened - don't continue.
+      watchdog('Apache Solr', 'Error constructing documents to index: <br /> !message', array('!message' => nl2br(strip_tags($e->getMessage()))), WATCHDOG_ERROR);
+      break;
+    }
+  }
+
+  // Flatten $documents
+  $tmp = (object) array('a' => array());
+  array_walk_recursive($documents, create_function('&$v, $k, &$t', '$t->a[] = $v;'), $tmp);
+  $documents = $tmp->a;
+
+  if (count($documents)) {
+    try {
+      watchdog('Apache Solr', 'Adding @count documents.', array('@count' => count($documents)));
+      // Chunk the adds by 20s
+      $docs_chunk = array_chunk($documents, 20);
+      foreach ($docs_chunk as $docs) {
+        $solr->addDocuments($docs);
+      }
+      // Set the timestamp to indicate an index update.
+      apachesolr_index_set_last_updated(time());
+    }
+    catch (Exception $e) {
+      $nids = array();
+      if (!empty($docs)) {
+        foreach ($docs as $doc) {
+          $nids[] = $doc->nid;
+        }
+      }
+      watchdog('Apache Solr', 'Indexing failed on one of the following nodes: @nids <br /> !message',
+               array('@nids' => implode(', ', $nids), '!message' => nl2br(strip_tags($e->getMessage()))), WATCHDOG_ERROR);
+      return FALSE;
+    }
+  }
+
+  // Save the new position in case it changed.
+  if ($namespace && $position != $old_position) {
+    $stored = variable_get('apachesolr_index_last', array());
+    $stored[$namespace] = $position;
+    variable_set('apachesolr_index_last', $stored);
+  }
+
+  return $position;
+}
+
+/**
+ * Convert date from timestamp into ISO 8601 format.
+ * http://lucene.apache.org/solr/api/org/apache/solr/schema/DateField.html
+ */
+function apachesolr_date_iso($date_timestamp) {
+  return gmdate('Y-m-d\TH:i:s\Z', $date_timestamp);
+}
+
+function apachesolr_delete_node_from_index($node) {
+  static $failed = FALSE;
+  if ($failed) {
+    return FALSE;
+  }
+  try {
+    $solr = apachesolr_get_solr();
+    $solr->deleteById(apachesolr_document_id($node->nid));
+    apachesolr_index_set_last_updated(time());
+    return TRUE;
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    // Don't keep trying queries if they are failing.
+    $failed = TRUE;
+    return FALSE;
+  }
+}
+
+/**
+ * Set the timestamp of the last index update
+ * @param $updated
+ *   A timestamp or zero. If zero, the variable is deleted.
+ */
+function apachesolr_index_set_last_updated($updated = 0) {
+  if ($updated) {
+    variable_set('apachesolr_index_updated', (int) $updated);
+  }
+  else {
+    variable_del('apachesolr_index_updated');
+  }
+}
+
+/**
+ * Get the timestamp of the last index update.
+ * @return integer (timestamp)
+ */
+function apachesolr_index_get_last_updated() {
+  return variable_get('apachesolr_index_updated', 0);
+}
+
+/**
+ * Implementation of hook_cron().
+ */
+function apachesolr_cron() {
+  try {
+    $solr = apachesolr_get_solr();
+
+    // Check for unpublished content that wasn't deleted from the index.
+    $result = db_query("SELECT n.nid, n.status FROM {apachesolr_search_node} asn INNER JOIN {node} n ON n.nid = asn.nid WHERE asn.status != n.status");
+    while ($node = db_fetch_object($result)) {
+      apachesolr_nodeapi_update($node, FALSE);
+    }
+
+    // Check for deleted content that wasn't deleted from the index.
+    $result = db_query("SELECT asn.nid FROM {apachesolr_search_node} asn LEFT JOIN {node} n ON n.nid = asn.nid WHERE n.nid IS NULL");
+    while ($node = db_fetch_object($result)) {
+      apachesolr_nodeapi_delete($node, FALSE);
+    }
+
+    // Optimize the index (by default once a day).
+    $optimize_interval = variable_get('apachesolr_optimize_interval', 60 * 60 * 24);
+    $last = variable_get('apachesolr_last_optimize', 0);
+    $time = time();
+    if ($optimize_interval && ($time - $last > $optimize_interval)) {
+      $solr->optimize(FALSE, FALSE);
+      variable_set('apachesolr_last_optimize', $time);
+      apachesolr_index_set_last_updated($time);
+    }
+
+    // Only clear the cache if the index changed.
+    // TODO: clear on some schedule if running multi-site.
+    $updated = apachesolr_index_get_last_updated();
+    if ($updated) {
+      $solr->clearCache();
+      // Re-populate the luke cache.
+      $solr->getLuke();
+      // TODO: an admin interface for setting this.  Assume for now 5 minutes.
+      if ($time - $updated >= variable_get('apachesolr_cache_delay', 300)) {
+        // Clear the updated flag.
+        apachesolr_index_set_last_updated(0);
+      }
+    }
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())) . ' in apachesolr_cron', NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function apachesolr_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+  switch ($op) {
+    case 'delete':
+      apachesolr_nodeapi_delete($node);
+      break;
+    case 'insert':
+      // Make sure no node ends up with a timestamp that's in the future
+      // by using time() rather than the node's changed or created timestamp.
+      db_query("INSERT INTO {apachesolr_search_node} (nid, status, changed) VALUES  (%d, %d, %d)", $node->nid, $node->status, time());
+      break;
+    case 'update':
+      apachesolr_nodeapi_update($node);
+      break;
+  }
+}
+
+/**
+ * Helper function for hook_nodeapi().
+ */
+function apachesolr_nodeapi_delete($node, $set_message = TRUE) {
+  if (apachesolr_delete_node_from_index($node)) {
+    // There was no exception, so delete from the table.
+    db_query("DELETE FROM {apachesolr_search_node} WHERE nid = %d", $node->nid);
+    if ($set_message && user_access('administer search') && variable_get('apachesolr_set_nodeapi_messages', 1)) {
+      apachesolr_set_stats_message('Deleted content will be removed from the Apache Solr search index in approximately @autocommit_time.');
+    }
+  }
+}
+
+/**
+ * Helper function for hook_nodeapi().
+ */
+function apachesolr_nodeapi_update($node, $set_message = TRUE) {
+  // Check if the node has gone from published to unpublished.
+  if (!$node->status && db_result(db_query("SELECT status FROM {apachesolr_search_node} WHERE nid = %d", $node->nid))) {
+    if (apachesolr_delete_node_from_index($node)) {
+      // There was no exception, so update the table.
+      db_query("UPDATE {apachesolr_search_node} SET changed = %d, status = %d WHERE nid  = %d", time(), $node->status, $node->nid);
+      if ($set_message && user_access('administer search') && variable_get('apachesolr_set_nodeapi_messages', 1)) {
+        apachesolr_set_stats_message('Unpublished content will be removed from the Apache Solr search index in approximately @autocommit_time.');
+      }
+    }
+  }
+  else {
+    db_query("UPDATE {apachesolr_search_node} SET changed = %d, status = %d WHERE nid  = %d", time(), $node->status, $node->nid);
+  }
+}
+
+/**
+ * Call drupal_set_message() with the text.
+ *
+ * The text is translated with t() and substituted using Solr stats.
+ */
+function apachesolr_set_stats_message($text, $type = 'status', $repeat = FALSE) {
+  try {
+    $solr = apachesolr_get_solr();
+    $stats_summary = $solr->getStatsSummary();
+    drupal_set_message(t($text, $stats_summary), $type, FALSE);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Return the enabled facets from the specified block array.
+ *
+ * @param $module
+ *   The module (optional).
+ * @return
+ *   An array consisting info for facets that have been enabled
+ *   for the specified module, or all enabled facets.
+ */
+function apachesolr_get_enabled_facets($module = NULL) {
+  $enabled = variable_get('apachesolr_enabled_facets', array());
+  if (isset($module)) {
+    return isset($enabled[$module]) ? $enabled[$module] : array();
+  }
+  return $enabled;
+}
+
+/**
+ * Implementation of hook_block().
+ */
+function apachesolr_block($op = 'list', $delta = 0, $edit = array()) {
+  static $access;
+
+  switch ($op) {
+    case 'list':
+      // Get all of the moreLikeThis blocks that the user has created
+      $blocks = apachesolr_mlt_list_blocks();
+      // Add the sort block.
+      $blocks['sort'] = array(
+        'info' => t('Apache Solr Core: Sorting'),
+        'cache' => BLOCK_CACHE_PER_PAGE,
+      );
+      return $blocks;
+
+    case 'view':
+      if ($delta != 'sort' && ($node = menu_get_object()) && (!arg(2) || arg(2) == 'view')) {
+        $suggestions = array();
+        // Determine whether the user can view the current node.
+        if (!isset($access)) {
+          $access = node_access('view', $node);
+        }
+        $block = apachesolr_mlt_load_block($delta);
+        if ($access && $block) {
+          $docs = apachesolr_mlt_suggestions($block, apachesolr_document_id($node->nid));
+          if (!empty($docs)) {
+            $suggestions['subject'] = check_plain($block['name']);
+            $suggestions['content'] = theme('apachesolr_mlt_recommendation_block', $docs);
+            if (user_access('administer search')) {
+               $suggestions['content'] .= l(t('Configure this block'),'admin/build/block/configure/apachesolr/' . $delta, array('attributes' => array('class' => 'apachesolr-mlt-admin-link')));
+            }
+          }
+        }
+        return $suggestions;
+      }
+      elseif (apachesolr_has_searched() && $delta == 'sort') {
+        // Get the query and response. Without these no blocks make sense.
+        $response = apachesolr_static_response_cache();
+        if (empty($response) || ($response->response->numFound < 2)) {
+          return;
+        }
+
+        $query = apachesolr_current_query();
+        $sorts = $query->get_available_sorts();
+
+        // Get the current sort as an array.
+        $solrsort = $query->get_solrsort();
+
+        $sort_links = array();
+        $path = $query->get_path();
+        $new_query = clone $query;
+        $toggle = array('asc' => 'desc', 'desc' => 'asc');
+        foreach ($sorts as $name => $sort) {
+          $active = $solrsort['#name'] == $name;
+          $direction = '';
+          $new_direction = $sort['default'];
+          if ($name == 'score') {
+            // We only sort by ascending score.
+            $new_direction = 'asc';
+          }
+          elseif ($active) {
+            $direction = $solrsort['#direction'];
+            $new_direction = $toggle[$solrsort['#direction']];
+          }
+          $new_query->set_solrsort($name, $new_direction);
+          $sort_links[$name] = array(
+            'title' => $sort['title'],
+            'path' => $path,
+            'options' => array('query' => $new_query->get_url_queryvalues()),
+            'active' => $active,
+            'direction' => $direction,
+          );
+        }
+        // Allow other modules to add or remove sorts.
+        drupal_alter('apachesolr_sort_links', $sort_links);
+        foreach ($sort_links as $name => $link) {
+          $themed_links[$name] = theme('apachesolr_sort_link', $link['title'], $link['path'], $link['options'], $link['active'], $link['direction']);
+        }
+        return array('subject' => t('Sort by'),
+                     'content' => theme('apachesolr_sort_list', $themed_links));
+      }
+      break;
+    case 'configure':
+      if ($delta != 'sort') {
+        require_once(drupal_get_path('module', 'apachesolr') .'/apachesolr.admin.inc');
+        return apachesolr_mlt_block_form($delta);
+      }
+      break;
+    case 'save':
+      if ($delta != 'sort') {
+        require_once(drupal_get_path('module', 'apachesolr') .'/apachesolr.admin.inc');
+        apachesolr_mlt_save_block($edit, $delta);
+      }
+      break;
+  }
+}
+
+/**
+ * This code makes a decision whether to show a block or not.
+ * @param $query
+ *   The current query object.
+ * @param string $module
+ *   The module's name to whom this block belongs.
+ * @param string $delta
+ *   The delta string the identifies the block within $module.
+ * @return boolean
+ *   Whether the block should be visible. Other factors, like
+ *   the block system's visibility settings, apply as well.
+ */
+function apachesolr_block_visibility($query, $module, $delta) {
+  // TYPE HIERARCHY.
+  // If the block is configured to heed type hierarcy then it looks
+  // to see if a suitable type filter has been chosen. If not,
+  // the function returns.
+  // This variable is not static cached because variable_get() already does that.
+  $type_filters = variable_get('apachesolr_type_filter', array());
+  if (isset($type_filters[$module][$delta]) && $type_filters[$module][$delta] == TRUE) {
+    $facet_info = apachesolr_get_facet_definitions();
+    if (isset($facet_info[$module][$delta]['content_types'])) {
+      $has_filter = $query->get_filters($facet_info[$module][$delta]['facet_field']);
+      $show = count($has_filter);
+      foreach ($facet_info[$module][$delta]['content_types'] as $content_type) {
+        if ($query->has_filter('type', $content_type)) {
+          $show = TRUE;
+        }
+      }
+      if (!$show) {
+        return FALSE;
+      }
+    }
+  }
+  return TRUE;
+}
+
+function apachesolr_get_facet_definitions() {
+  static $definitions;
+  if (is_null($definitions)) {
+    $operator_settings = variable_get('apachesolr_operator', array());
+    foreach (module_implements('apachesolr_facets') as $module) {
+      $facets = module_invoke($module, 'apachesolr_facets');
+      foreach ($facets as $delta => $info) {
+        $definitions[$module][$delta] = $info;
+        if(isset($definitions[$module][$delta])) {
+          $definitions[$module][$delta]['operator'] = isset($operator_settings[$module][$delta]) ? $operator_settings[$module][$delta] : 'AND';
+        }
+      }
+    }
+  }
+  return $definitions;
+}
+
+/**
+ * Implementation of hook_form_[form_id]_alter().
+ *
+ * Hide the core 'title' field in favor of our 'name' field.
+ *
+ * Add a checkbox to enable type-specific visibility.
+ */
+function apachesolr_form_block_admin_configure_alter(&$form, $form_state) {
+  // Hide the core title field.
+  if ($form['module']['#value'] == 'apachesolr' && $form['delta']['#value'] != 'sort') {
+    $form['block_settings']['title']['#access'] = FALSE;
+  }
+
+  // Add a type-specific visibility checkbox.
+  $module = $form['module']['#value'];
+  $delta = $form['delta']['#value'];
+  $enabled_facets = apachesolr_get_enabled_facets();
+
+  // If this block isn't enabled as a facet, get out of here.
+  if (!isset($enabled_facets[$module][$delta])) {
+    return;
+  }
+
+  $facet_info = apachesolr_get_facet_definitions();
+
+  if (isset($facet_info[$module][$delta]['content_types'])) {
+    $type_filter_settings = variable_get('apachesolr_type_filter', array());
+    // Set up some variables for the verbiage of the form element.
+    $count = count($facet_info[$module][$delta]['content_types']);
+    $types = format_plural($count, t('type'), t('types'));
+    $are = format_plural($count, t('is'), t('are'));
+    $content_types = implode(', ', $facet_info[$module][$delta]['content_types']) . '.';
+    $form['block_settings']['apachesolr_type_filter'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show this block only when the type filter is selected for: %content_types',  array('%content_types' => $content_types)),
+      '#default_value' => isset($type_filter_settings[$module][$delta]) ? $type_filter_settings[$module][$delta] : FALSE,
+      '#description' => t('This filter is relevant only for specific content types. Check this to display the block only when the type filter has been selected for one of the relevant content types.'),
+      '#weight' => 11,
+    );
+  }
+  $operator_settings = variable_get('apachesolr_operator', array());
+  $form['block_settings']['apachesolr_operator'] = array(
+    '#type' => 'radios',
+    '#title' => t('Operator to use for facets'),
+    '#options' => drupal_map_assoc(array('AND', 'OR')),
+    '#default_value' => isset($operator_settings[$module][$delta]) ? $operator_settings[$module][$delta] : 'AND',
+    '#description' => t('AND filters are exclusive. OR filters are inclusive. Selecting more AND filters narrows the result set. Selecting more OR filters widens the result set.'),
+    '#weight' => 12,
+  );
+  // Add a submit handler to save the value.
+  $form['#validate'][] = 'apachesolr_block_admin_configure_submit';
+}
+
+function apachesolr_block_admin_configure_submit($form, &$form_state) {
+  if (isset($form_state['values']['apachesolr_type_filter'])) {
+    $type_filter_settings = variable_get('apachesolr_type_filter', array());
+    $module = $form_state['values']['module'];
+    $delta = $form_state['values']['delta'];
+    unset($type_filter_settings[$module][$delta]);
+    $type_filter_settings[$module][$delta] = $form_state['values']['apachesolr_type_filter'];
+    variable_set('apachesolr_type_filter', $type_filter_settings);
+  }
+  if (isset($form_state['values']['apachesolr_operator'])) {
+    $operator_settings = variable_get('apachesolr_operator', array());
+    $module = $form_state['values']['module'];
+    $delta = $form_state['values']['delta'];
+    unset($operator_settings[$module][$delta]);
+    $operator_settings[$module][$delta] = $form_state['values']['apachesolr_operator'];
+    variable_set('apachesolr_operator', $operator_settings);
+  }
+}
+
+/**
+ * Helper function for displaying a facet block.
+ */
+function apachesolr_facet_block($response, $query, $module, $delta, $facet_field, $filter_by, $facet_callback = FALSE) {
+  if (!empty($response->facet_counts->facet_fields->$facet_field)) {
+    $facet_query_sorts = variable_get('apachesolr_facet_query_sorts', array());
+    $contains_active = FALSE;
+    $items = array();
+    foreach ($response->facet_counts->facet_fields->$facet_field as $facet => $count) {
+      $sortpre = 1000000 - $count;
+      $options = array('delta' => $delta);
+      $exclude = FALSE;
+      // Solr sends this back if it's empty.
+      if ($facet == '_empty_') {
+         $exclude = TRUE;
+         $facet = '[* TO *]';
+         $facet_text = theme('placeholder', t('Missing this field'));
+         $options['html'] = TRUE;
+         // Put this just below any active facets.
+         // '-' sorts before all numbers, but after '*'.
+         $sortpre = '-';
+       }
+       else {
+         $facet_text = $facet;
+      }
+
+      if ($facet_callback && function_exists($facet_callback)) {
+        $facet_text = $facet_callback($facet, $options);
+      }
+      // If this block is to be alphabetically sorted, change $sortpre.
+      if (isset($facet_query_sorts[$module][$delta]) && ($facet_query_sorts[$module][$delta] != 'count')) {
+        $sortpre = $facet_text;
+      }
+      $unclick_link = '';
+      $active = FALSE;
+      $new_query = clone $query;
+      if ($query->has_filter($facet_field, $facet)) {
+        $contains_active = $active = TRUE;
+        // '*' sorts before all numbers.
+        $sortpre = '*';
+        $new_query->remove_filter($facet_field, $facet);
+        $options['query'] = $new_query->get_url_queryvalues();
+        $link = theme('apachesolr_unclick_link', $facet_text, $new_query->get_path(), $options);
+      }
+      else {
+        $new_query->add_filter($facet_field, $facet, $exclude);
+        $options['query'] = $new_query->get_url_queryvalues();
+        $link = theme('apachesolr_facet_link', $facet_text, $new_query->get_path(), $options, $count, FALSE, $response->response->numFound);
+      }
+      if ($count || $active) {
+        $items[$sortpre . '*' . $facet_text] = $link;
+      }
+    }
+    // Unless a facet is active only display 2 or more.
+    if ($items && ($response->response->numFound > 1 || $contains_active)) {
+      if (!isset($facet_query_sorts[$module][$delta]) || ($facet_query_sorts[$module][$delta] == 'index asc')) {
+        ksort($items, SORT_STRING);
+      }
+      else if ($facet_query_sorts[$module][$delta] == 'index desc') {
+        krsort($items, SORT_STRING);
+      }
+      // Get information needed by the rest of the blocks about limits.
+      $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array());
+      $limit = isset($initial_limits[$module][$delta]) ? $initial_limits[$module][$delta] : variable_get('apachesolr_facet_query_initial_limit_default', 10);
+      $output = theme('apachesolr_facet_list', $items, $limit, $delta);
+      return array('subject' => $filter_by, 'content' => $output);
+    }
+  }
+  return NULL;
+}
+
+/**
+ * Helper function for displaying a date facet block.
+ *
+ * TODO: Refactor with apachesolr_facet_block().
+ */
+function apachesolr_date_facet_block($response, $query, $module, $delta, $facet_field, $filter_by, $facet_callback = FALSE) {
+  $items = array();
+
+  $new_query = clone $query;
+  foreach (array_reverse($new_query->get_filters($facet_field)) as $filter) {
+    $options = array();
+    // Iteratively remove the date facets.
+    $new_query->remove_filter($facet_field, $filter['#value']);
+    if ($facet_callback && function_exists($facet_callback)) {
+      $facet_text = $facet_callback($filter['#start'], $options);
+    }
+    else {
+      $facet_text = apachesolr_date_format_iso_by_gap(apachesolr_date_find_query_gap($filter['#start'], $filter['#end']), $filter['#start']);
+    }
+    $options['query'] = $new_query->get_url_queryvalues();
+    array_unshift($items, theme('apachesolr_unclick_link', $facet_text, $new_query->get_path(), $options));
+  }
+  // Add links for additional date filters.
+  if (!empty($response->facet_counts->facet_dates->$facet_field)) {
+    $field = clone $response->facet_counts->facet_dates->$facet_field;
+
+    $end = $field->end;
+    unset($field->end);
+
+    $gap = $field->gap;
+    unset($field->gap);
+
+    // Treat each date facet as a range start, and use the next date
+    // facet as range end.  Use 'end' for the final end.
+    $range_end = array();
+    foreach ($field as $facet => $count) {
+      if (isset($prev_facet)) {
+        $range_end[$prev_facet] = $facet;
+      }
+      $prev_facet = $facet;
+    }
+    $range_end[$prev_facet] = $end;
+
+    foreach ($field as $facet => $count) {
+      $options = array();
+      // Solr sends this back if it's empty.
+      if ($facet == '_empty_' || $count == 0) {
+        continue;
+      }
+      if ($facet_callback && function_exists($facet_callback)) {
+        $facet_text = $facet_callback($facet, $options);
+      }
+      else {
+        $facet_text = apachesolr_date_format_iso_by_gap(substr($gap, 2), $facet);
+      }
+      $new_query = clone $query;
+      $new_query->add_filter($facet_field, '['. $facet .' TO '. $range_end[$facet] .']');
+      $options['query'] = $new_query->get_url_queryvalues();
+      $items[] = theme('apachesolr_facet_link', $facet_text, $new_query->get_path(), $options, $count, FALSE, $response->response->numFound);
+    }
+  }
+  if (count($items) > 0) {
+    // Get information needed by the rest of the blocks about limits.
+    $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array());
+    $limit = isset($initial_limits[$module][$delta]) ? $initial_limits[$module][$delta] : variable_get('apachesolr_facet_query_initial_limit_default', 10);
+    $output = theme('apachesolr_facet_list', $items, $limit, $delta);
+    return array('subject' => $filter_by, 'content' => $output);
+  }
+  return NULL;
+}
+
+/**
+ * Determine the gap in a date range query filter that we generated.
+ *
+ * This function assumes that the start and end dates are the
+ * beginning and end of a single period: 1 year, month, day, hour,
+ * minute, or second (all date range query filters we generate meet
+ * this criteria).  So, if the seconds are different, it is a second
+ * gap.  If the seconds are the same (incidentally, they will also be
+ * 0) but the minutes are different, it is a minute gap.  If the
+ * minutes are the same but hours are different, it's an hour gap.
+ * etc.
+ *
+ * @param $start
+ *   Start date as an ISO date string.
+ * @param $end
+ *   End date as an ISO date string.
+ * @return
+ *   YEAR, MONTH, DAY, HOUR, MINUTE, or SECOND.
+ */
+function apachesolr_date_find_query_gap($start_iso, $end_iso) {
+  $gaps = array('SECOND' => 6, 'MINUTE' => 5, 'HOUR' => 4, 'DAY' => 3, 'MONTH' => 2, 'YEAR' => 1);
+  $re = '@(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})@';
+  if (preg_match($re, $start_iso, $start) && preg_match($re, $end_iso, $end)) {
+    foreach ($gaps as $gap => $idx) {
+      if ($start[$idx] != $end[$idx]) {
+        return $gap;
+      }
+    }
+  }
+  // can't tell
+  return 'YEAR';
+}
+
+/**
+ * Format an ISO date string based on the gap used to generate it.
+ *
+ * This function assumes that gaps less than one day will be displayed
+ * in a search context in which a larger containing gap including a
+ * day is already displayed.  So, HOUR, MINUTE, and SECOND gaps only
+ * display time information, without date.
+ *
+ * @param $gap
+ *   A gap.
+ * @param $iso
+ *   An ISO date string.
+ * @return
+ *   A gap-appropriate formatted date.
+ */
+function apachesolr_date_format_iso_by_gap($gap, $iso) {
+  // TODO: If we assume that multiple search queries are formatted in
+  // order, we could store a static list of all gaps we've formatted.
+  // Then, if we format an HOUR, MINUTE, or SECOND without previously
+  // having formatted a DAY or later, we could include date
+  // information.  However, we'd need to do that per-field and I'm not
+  // our callers always have field information handy.
+  $unix = strtotime($iso);
+  if ($unix > 0) {
+    switch ($gap) {
+      case 'YEAR':
+        return gmdate('Y', $unix);
+      case 'MONTH':
+        return gmdate('F Y', $unix);
+      case 'DAY':
+        return gmdate('F j, Y', $unix);
+      case 'HOUR':
+        return gmdate('g A', $unix);
+      case 'MINUTE':
+        return gmdate('g:i A', $unix);
+      case 'SECOND':
+        return gmdate('g:i:s A', $unix);
+    }
+  }
+
+  return $iso;
+}
+
+/**
+ * Format the beginning of a date range query filter that we
+ * generated.
+ *
+ * @param $start_iso
+ *   The start date.
+ * @param $end_iso
+ *   The end date.
+ * @return
+ *   A display string reprepsenting the date range, such as "January
+ * 2009" for "2009-01-01T00:00:00Z TO 2009-02-01T00:00:00Z"
+ */
+function apachesolr_date_format_range($start_iso, $end_iso) {
+  $gap = apachesolr_date_find_query_gap($start_iso, $end_iso);
+  return apachesolr_date_format_iso_by_gap($gap, $start_iso);
+}
+
+/**
+ * Determine the best search gap to use for an arbitrary date range.
+ *
+ * Generally, we the maximum gap that fits between the start and end
+ * date.  If they are more than a year apart, 1 year; if they are more
+ * than a month apart, 1 month; etc.
+ *
+ * This function uses Unix timestamps for its computation and so is
+ * not useful for dates outside that range.
+ *
+ * @param $start
+ *   Start date as an ISO date string.
+ * @param $end
+ *   End date as an ISO date string.
+ * @return
+ *   YEAR, MONTH, DAY, HOUR, MINUTE, or SECOND depending on how far
+ *   apart $start and $end are.
+ */
+function apachesolr_date_determine_gap($start, $end) {
+  $start = strtotime($start);
+  $end = strtotime($end);
+
+  if ($end - $start >= 86400*365) {
+    return 'YEAR';
+  }
+
+  if (date('m', $start) != date('m', $end)) {
+    return 'MONTH';
+  }
+
+  if ($end - $start > 86400) {
+    return 'DAY';
+  }
+  // For now, HOUR is a reasonable smallest gap.
+  return 'HOUR';
+}
+
+/**
+ * Return the next smaller date gap.
+ *
+ * @param $gap
+ *   A gap.
+ * @return
+ *   The next smaller gap, or NULL if there is no smaller gap.
+ */
+function apachesolr_date_gap_drilldown($gap) {
+  $drill = array(
+    'YEAR' => 'MONTH',
+    'MONTH' => 'DAY',
+    'DAY' => 'HOUR',
+    // For now, HOUR is a reasonable smallest gap.
+    // 'HOUR' => 'MINUTE',
+  );
+  return isset($drill[$gap]) ? $drill[$gap] : NULL;
+}
+
+/**
+ * Used by the 'configure' $op of hook_block so that modules can generically set
+ * facet limits on their blocks.
+ */
+function apachesolr_facetcount_form($module, $delta) {
+  $initial = variable_get('apachesolr_facet_query_initial_limits', array());
+  $limits = variable_get('apachesolr_facet_query_limits', array());
+  $sorts = variable_get('apachesolr_facet_query_sorts', array());
+  $facet_missing = variable_get('apachesolr_facet_missing', array());
+
+  $limit = drupal_map_assoc(array(50, 40, 30, 20, 15, 10, 5, 3));
+
+  $form['apachesolr_facet_query_initial_limit'] = array(
+    '#type' => 'select',
+    '#title' => t('Initial filter links'),
+    '#options' => $limit,
+    '#description' => t('The initial number of filter links to show in this block.'),
+    '#default_value' => isset($initial[$module][$delta]) ? $initial[$module][$delta] : variable_get('apachesolr_facet_query_initial_limit_default', 10),
+  );
+  $limit = drupal_map_assoc(array(100, 75, 50, 40, 30, 20, 15, 10, 5, 3));
+  $form['apachesolr_facet_query_limit'] = array(
+    '#type' => 'select',
+    '#title' => t('Maximum filter links'),
+    '#options' => $limit,
+    '#description' => t('The maximum number of filter links to show in this block.'),
+    '#default_value' => isset($limits[$module][$delta]) ? $limits[$module][$delta] : variable_get('apachesolr_facet_query_limit_default', 20),
+  );
+  
+  // TODO: Generalize how we know what type a facet block is by putting field
+  // type into the facet definition. 'created' and 'changed' are date blocks.
+  if ($delta != 'created' && $delta != 'changed') {
+    $form['apachesolr_facet_query_sort'] = array(
+      '#type' => 'radios',
+      '#title' => t('Sort order of facet links'),
+      '#options' => array('count' => t('Count'), 'index asc' => t('Alphanumeric, ascending'), 'index desc' => t('Alphanumeric, descending')),
+      '#description' => t('The sort order of facet links in this block. %Count, which is the default, will show facets with the most results first. %Alphanumeric will sort alphabetically, either ascending or descending.', array('%Count' => t('Count'), '%Alphanumeric' => t('Alphanumeric'))),
+      '#default_value' => isset($sorts[$module][$delta]) ? $sorts[$module][$delta] : 'count',
+    );
+  }
+  $form['apachesolr_facet_missing'] = array(
+    '#type' => 'radios',
+    '#title' => t('Include a facet for missing'),
+    '#options' => array(0 => t('No'), 1 => t('Yes')),
+    '#description' => t('A facet can be generated corresponding to all documents entirely missing this field.'),
+    '#default_value' => isset($facet_missing[$module][$delta]) ? $facet_missing[$module][$delta] : 0,
+  );
+  return $form;
+}
+
+/**
+ * Used by the 'save' $op of hook_block so that modules can generically set
+ * facet limits on their blocks.
+ */
+function apachesolr_facetcount_save($edit) {
+  // Save query limits
+  $module = $edit['module'];
+  $delta = $edit['delta'];
+  $limits = variable_get('apachesolr_facet_query_limits', array());
+  $limits[$module][$delta] = (int)$edit['apachesolr_facet_query_limit'];
+  variable_set('apachesolr_facet_query_limits', $limits);
+  $sorts = variable_get('apachesolr_facet_query_sorts', array());
+  $sorts[$module][$delta] = $edit['apachesolr_facet_query_sort'];
+  variable_set('apachesolr_facet_query_sorts', $sorts);
+  $initial = variable_get('apachesolr_facet_query_initial_limits', array());
+  $initial[$module][$delta] = (int)$edit['apachesolr_facet_query_initial_limit'];
+  variable_set('apachesolr_facet_query_initial_limits', $initial);
+  $facet_missing = variable_get('apachesolr_facet_missing', array());
+  $facet_missing[$module][$delta] = (int)$edit['apachesolr_facet_missing'];
+  variable_set('apachesolr_facet_missing', $facet_missing);
+}
+
+/**
+ * Initialize a pager for theme('pager') without running an SQL query.
+ *
+ * @see pager_query()
+ *
+ * @param $total
+ *  The total number of items found.
+ * @param $limit
+ *  The number of items you will display per page.
+ * @param $element
+ *  An optional integer to distinguish between multiple pagers on one page.
+ *
+ * @return
+ *  The current page for $element. 0 by default if $_GET['page'] is empty.
+ */
+function apachesolr_pager_init($total, $limit = 10, $element = 0) {
+  global $pager_page_array, $pager_total, $pager_total_items;
+  $page = isset($_GET['page']) ? $_GET['page'] : '';
+
+  // Convert comma-separated $page to an array, used by other functions.
+  $pager_page_array = explode(',', $page);
+
+  // We calculate the total of pages as ceil(items / limit).
+  $pager_total_items[$element] = $total;
+  $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
+  $pager_page_array[$element] = max(0, min((int)$pager_page_array[$element], ((int)$pager_total[$element]) - 1));
+  return $pager_page_array[$element];
+}
+
+/**
+ * This hook allows modules to modify the query and params objects.
+ *
+ * Example:
+ *
+ * function my_module_apachesolr_modify_query(&$query, &$params) {
+ *   // I only want to see articles by the admin!
+ *   $query->add_filter("uid", 1);
+ *
+ * }
+ */
+function apachesolr_modify_query(&$query, &$params, $caller) {
+   if (empty($query)) {
+     // This should only happen if Solr is not set up - avoids fatal errors.
+     return;
+   }
+
+  // Call the hooks first because otherwise any modifications to the
+  // $query object don't end up in the $params.
+  foreach (module_implements('apachesolr_modify_query') as $module) {
+    $function_name = $module . '_apachesolr_modify_query';
+    $function_name($query, $params, $caller);
+  }
+
+  // TODO: The query object should hold all the params.
+  // Add array of fq parameters.
+  if ($query && ($fq = $query->get_fq())) {
+    foreach ($fq as $delta => $values) {
+      if (is_array($values) || is_object($values)) {
+        foreach ($values as $value) {
+          $params['fq'][$delta][] = $value;
+        }
+      }
+    }
+  }
+  // Add sort if present.
+  if ($query) {
+    $sort = $query->get_solrsort();
+    $sortstring = $sort['#name'] .' '. $sort['#direction'];
+    // We don't bother telling Solr to do its default sort.
+    if ($sortstring != 'score asc') {
+      $params['sort'] = $sortstring;
+    }
+  }
+  $ors = array();
+  $facet_info = apachesolr_get_facet_definitions();
+  foreach ($facet_info as $infos) {
+    foreach($infos as $delta => $facet) {
+      if ($facet['operator'] == 'OR') {
+        $ors[] = $delta;
+      }
+    }
+  }
+
+  if ($filter_queries = $params['fq']) {
+    foreach ($filter_queries as $delta => $value) {
+      $fq = $tag = '';
+      $op = 'AND';
+      // CCK facet field block deltas are not the same as their Solr index field names.
+      $cck_delta = '';
+      if (strpos($delta, '_cck_')) {
+        $cck_delta = trim(drupal_substr($delta, 7, drupal_strlen($delta)));
+      }
+      if (in_array($delta, $ors) || in_array($cck_delta, $ors)) {
+        $tag = "{!tag=$delta}";
+        $op = 'OR';
+      }
+      $fq = implode(" $op ", $params['fq'][$delta]);
+      $params['fq'][] = $tag . $fq;
+      unset($params['fq'][$delta]);
+    }
+  }
+}
+
+/**
+ * Semaphore that indicates whether a search has been done. Blocks use this
+ * later to decide whether they should load or not.
+ *
+ * @param $searched
+ *   A boolean indicating whether a search has been executed.
+ *
+ * @return
+ *   TRUE if a search has been executed.
+ *   FALSE otherwise.
+ */
+function apachesolr_has_searched($searched = NULL) {
+  static $_searched = FALSE;
+  if (is_bool($searched)) {
+    $_searched = $searched;
+  }
+  return $_searched;
+}
+
+/**
+ * Factory method for solr singleton object. Structure allows for an arbitrary
+ * number of solr objects to be used based on the host, port, path combination.
+ * Get an instance like this:
+ *   $solr = apachesolr_get_solr();
+ */
+function apachesolr_get_solr($host = NULL, $port = NULL, $path = NULL) {
+  static $solr_cache;
+
+  if (empty($host)) {
+    $host = variable_get('apachesolr_host', 'localhost');
+  }
+  if (empty($port)) {
+    $port = variable_get('apachesolr_port', '8983');
+  }
+  if (empty($path)) {
+    $path = variable_get('apachesolr_path', '/solr');
+  }
+
+  if (empty($solr_cache[$host][$port][$path])) {
+    list($module, $filepath, $class) = variable_get('apachesolr_service_class', array('apachesolr', 'Drupal_Apache_Solr_Service.php', 'Drupal_Apache_Solr_Service'));
+    include_once(drupal_get_path('module', $module) .'/'. $filepath);
+    try {
+      $solr_cache[$host][$port][$path] = new $class($host, $port, $path);
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      return;
+    }
+  }
+  return $solr_cache[$host][$port][$path];
+}
+
+/**
+ * Checks if a specific Apache Solr server is available.
+ *
+ * @return boolean TRUE if the server can be pinged, FALSE otherwise.
+ */
+function apachesolr_server_status($host = NULL, $port = NULL, $path = NULL) {
+  if (empty($host)) {
+    $host = variable_get('apachesolr_host', 'localhost');
+  }
+  if (empty($port)) {
+    $port = variable_get('apachesolr_port', '8983');
+  }
+  if (empty($path)) {
+    $path = variable_get('apachesolr_path', '/solr');
+  }
+
+  $ping = FALSE;
+  try {
+    $solr = apachesolr_get_solr($host, $port, $path);
+    $ping = @$solr->ping(variable_get('apachesolr_ping_timeout', 4));
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', check_plain($e->getMessage()), NULL, WATCHDOG_ERROR);
+  }
+  return $ping;
+}
+
+/**
+ * It is important to hold on to the Solr response object for the duration of the
+ * page request so that we can use it for things like building facet blocks.
+ */
+function apachesolr_static_response_cache($response = NULL) {
+  static $_response;
+
+  if (!empty($response)) {
+    $_response = clone $response;
+  }
+  return $_response;
+}
+
+/**
+ * Factory function for query objects.
+ *
+ * @param $keys
+ *   The string that a user would type into the search box. Suitable input
+ *   may come from search_get_keys().
+ *
+ * @param $filters
+ *   Key and value pairs that are applied as a filter query.
+ *
+ * @param $solrsort
+ *   Visible string telling solr how to sort.
+ *
+ * @param $base_path
+ *   The search base path (without the keywords) for this query.
+ *
+ * @param $solr
+ *   An instance of Drupal_Apache_Solr_Service.
+ */
+function apachesolr_drupal_query($keys = '', $filters = '', $solrsort = '', $base_path = '', $solr = NULL) {
+  list($module, $class) = variable_get('apachesolr_query_class', array('apachesolr', 'Solr_Base_Query'));
+  include_once drupal_get_path('module', $module) .'/'. $class .'.php';
+  
+  if (empty($solr)) {
+    $solr = apachesolr_get_solr();
+  }
+  try {
+    $query = new $class($solr, $keys, $filters, $solrsort, $base_path);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    $query = NULL;
+  }
+
+  return $query;
+}
+
+/**
+ * Static getter/setter for the current query
+ */
+function apachesolr_current_query($query = NULL) {
+  static $saved_query = NULL;
+  if (is_object($query)) {
+    $saved_query = clone $query;
+  }
+
+  return empty($saved_query) ? $saved_query : clone $saved_query;
+}
+
+/**
+ * array('index_type' => 'integer',
+ *        'multiple' => TRUE,
+ *        'name' => 'fieldname',
+ *        ),
+ */
+function apachesolr_index_key($field) {
+  switch ($field['index_type']) {
+    case 'text':
+      $type_prefix = 't';
+      break;
+    case 'string':
+      $type_prefix = 's';
+      break;
+    case 'integer':
+      $type_prefix = 'i';
+      break;
+    case 'sint':
+      $type_prefix = 'si';
+      break;
+   case 'double':
+      $type_prefix = 'p'; // reserve d for date
+      break;
+    case 'boolean':
+      $type_prefix = 'b';
+      break;
+    case 'date':
+      $type_prefix = 'd';
+      break;
+    case 'float':
+      $type_prefix = 'f';
+      break;
+    case 'tdate':
+      $type_prefix = 'td';
+      break;
+    case 'tint':
+      $type_prefix = 'ti';
+      break;
+    case 'tlong';
+      $type_prefix = 'tl';
+      break;
+    case 'tfloat':
+      $type_prefix = 'tf';
+      break;
+    case 'tdouble':
+      $type_prefix = 'tp';
+      break;
+    default:
+      $type_prefix = 's';
+  }
+  $sm = $field['multiple'] ? 'm_' : 's_';
+  return $type_prefix . $sm . $field['name'];
+}
+
+/**
+ * Try to map a schema field name to a human-readable description.
+ */
+function apachesolr_field_name_map($field_name) {
+  static $map;
+
+  if (!isset($map)) {
+    $map = array(
+      'body' => t('Body text - the full, rendered content'),
+      'title' => t('Title'),
+      'teaser' => t('Teaser'),
+      'name' => t('Author name'),
+      'path_alias' => t('Path alias'),
+      'taxonomy_names' => t('All taxonomy term names'),
+      'tags_h1' => t('Body text inside H1 tags'),
+      'tags_h2_h3' => t('Body text inside H2 or H3 tags'),
+      'tags_h4_h5_h6' => t('Body text inside H4, H4, or H6 tags'),
+      'tags_inline' => t('Body text in inline tags like EM or STRONG'),
+      'tags_a' => t('Body text inside links (A tags)'),
+      'tid' => t('Taxonomy term IDs'),
+    );
+    if (module_exists('taxonomy')) {
+      foreach(taxonomy_get_vocabularies() as $vocab) {
+        $map['ts_vid_'. $vocab->vid .'_names'] = t('Taxonomy term names only from the %name vocabulary', array('%name' => $vocab->name));
+        $map['im_vid_'. $vocab->vid] = t('Taxonomy term IDs from the %name vocabulary', array('%name' => $vocab->name));
+      }
+    }
+    foreach (apachesolr_cck_fields() as $name => $field) {
+      $map[apachesolr_index_key($field)] = t('CCK @type field %label', array('@type' => $field['index_type'], '%label' => $field['label']));
+    }
+    drupal_alter('apachesolr_field_name_map', $map);
+  }
+  return isset($map[$field_name]) ? $map[$field_name] : $field_name;
+}
+
+/**
+ * Invokes hook_apachesolr_cck_field_mappings to find out how to handle CCK fields.
+ */
+function apachesolr_cck_fields() {
+  static $fields;
+
+  if (is_null($fields)) {
+    $fields = array();
+    // If CCK isn't enabled, do nothing.
+    if (module_exists('content')) {
+      // A single default mapping for all text fields.
+      $mappings['text'] = array(
+        'optionwidgets_select' => array(
+          'display_callback' => 'apachesolr_cck_text_field_callback',
+          'index_type' => 'string',
+        ),
+        'optionwidgets_buttons' => array(
+          'display_callback' => 'apachesolr_cck_text_field_callback',
+          'index_type' => 'string',
+        ),
+      );
+      $mappings['nodereference'] = array(
+        'nodereference_buttons' => array(
+          'display_callback' => 'apachesolr_cck_nodereference_field_callback',
+          'index_type' => 'integer',
+        ),
+        'nodereference_select' => array(
+          'display_callback' => 'apachesolr_cck_nodereference_field_callback',
+          'index_type' => 'integer',
+        ),
+        'nodereference_autocomplete' => array(
+          'display_callback' => 'apachesolr_cck_nodereference_field_callback',
+          'index_type' => 'integer',
+        ),
+      );
+      $mappings['userreference'] = array(
+        'userreference_buttons' => array(
+          'display_callback' => 'apachesolr_cck_userreference_field_callback',
+          'index_type' => 'integer',
+        ),
+        'userreference_select' => array(
+          'display_callback' => 'apachesolr_cck_userreference_field_callback',
+          'index_type' => 'integer',
+        ),
+        'userreference_autocomplete' => array(
+          'display_callback' => 'apachesolr_cck_userreference_field_callback',
+          'index_type' => 'integer',
+        ),
+      );
+      // Allow other modules to add or alter mappings.
+      drupal_alter('apachesolr_cck_fields', $mappings);
+
+      $result = db_query("SELECT  i.field_name, f.multiple, f.type AS field_type, i.widget_type, i.label, i.type_name AS content_type FROM {content_node_field_instance} i INNER JOIN {content_node_field} f ON i.field_name = f.field_name;");
+      while ($row = db_fetch_object($result)) {
+        // Only deal with fields that have option widgets (facets don't make sense otherwise), or fields that have specific mappings.
+        if ((isset($mappings[$row->field_type][$row->widget_type]) ||  isset($mappings['per-field'][$row->field_name]))) {
+          if (isset($mappings['per-field'][$row->field_name])) {
+            $fields[$row->field_name] = $mappings['per-field'][$row->field_name];
+            $row->index_type = $mappings['per-field'][$row->field_name]['index_type'];
+            $row->indexing_callback = $mappings['per-field'][$row->field_name]['indexing_callback'];
+            $row->display_callback = $mappings['per-field'][$row->field_name]['display_callback'];
+          }
+          else {
+            $row->index_type = $mappings[$row->field_type][$row->widget_type]['index_type'];
+            $row->indexing_callback = $mappings[$row->field_type][$row->widget_type]['indexing_callback'];
+            $row->display_callback = $mappings[$row->field_type][$row->widget_type]['display_callback'];
+          }
+          $row->multiple = (bool) $row->multiple;
+          // It's important that we put the 'cck_' here because several points in the later processing
+          // depend on it to route program flow to cck specific handlers.
+          $row->name = 'cck_' . $row->field_name;
+          $fields[$row->field_name] = array_merge((array) $fields[$row->field_name], (array) $row);
+          $fields[$row->field_name]['content_types'][] = $row->content_type;
+          unset($fields[$row->field_name]['content_type']);
+        }
+      }
+    }
+  }
+
+  return $fields;
+}
+
+/**
+ * Use the content.module's content_format() to format the
+ * field based on its value ($facet).
+ *
+ *  @param $facet string
+ *    The indexed value
+ *  @param $options
+ *    An array of options including the hook_block $delta.
+ */
+function apachesolr_cck_text_field_callback($facet, $options) {
+  if (function_exists('content_format')) {
+    return content_format($options['delta'], array('value' => $facet));
+  }
+  else {
+    return $facet;
+  }
+}
+
+/**
+ * Use the content.module's content_format() to format the
+ * field based on its nid ($facet).
+ *
+ *  @param $facet string
+ *    The indexed value
+ *  @param $options
+ *    An array of options including the hook_block $delta.
+ */
+function apachesolr_cck_nodereference_field_callback($facet, $options) {
+  if (function_exists('content_format')) {
+    return strip_tags(content_format($options['delta'], array('nid' => $facet)));
+  }
+  else {
+    return $facet;
+  }
+}
+
+/**
+ * Use the content.module's content_format() to format the
+ * field based on its uid ($facet).
+ *
+ *  @param $facet string
+ *    The indexed value
+ *  @param $options
+ *    An array of options including the hook_block $delta.
+ */
+function apachesolr_cck_userreference_field_callback($facet, $options) {
+  if (function_exists('content_format')) {
+    return strip_tags(content_format($options['delta'], array('uid' => $facet)));
+  }
+  else {
+    return $facet;
+  }
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function apachesolr_theme() {
+  return array(
+    /**
+     * Returns a link for a facet term, with the number (count) of results for that term
+     */
+    'apachesolr_facet_link' => array(
+      'arguments' => array('facet_text' => NULL, 'path' => NULL, 'options' => NULL, 'count' => NULL, 'active' => FALSE, 'num_found' => NULL),
+    ),
+    /**
+     * Returns a link to remove a facet filter from the current search.
+     */
+    'apachesolr_unclick_link' => array(
+      'arguments' => array('facet_text' => NULL, 'path' => NULL, 'options' => NULL),
+    ),
+    /**
+     * Returns a list of links from the above functions (apachesolr_facet_item and apachesolr_unclick_link)
+     */
+    'apachesolr_facet_list' => array(
+      'arguments' => array('items' => NULL, 'display_limit' => 0, 'delta' => ''),
+    ),
+    /**
+     * Returns a list of links generated by apachesolr_sort_link
+     */
+    'apachesolr_sort_list' => array(
+      'arguments' => array('items' => NULL),
+    ),
+    /**
+     * Returns a link which can be used to search the results.
+     */
+    'apachesolr_sort_link' => array(
+      'arguments' => array('text' => NULL, 'path' => NULL, 'options' => NULL, 'active' => FALSE, 'direction' => ''),
+    ),
+    /**
+     * Returns a list of results (docs) in content recommendation block
+     */
+    'apachesolr_mlt_recommendation_block' => array(
+      'arguments' => array('docs' => NULL),
+    ),
+  );
+}
+
+/**
+ * Performs a moreLikeThis query using the settings and retrieves documents.
+ *
+ * @param $settings
+ *   An array of settings.
+ * @param $id
+ *   The Solr ID of the document for which you want related content.
+ *   For a node that is apachesolr_document_id($node->nid)
+ *
+ * @return An array of response documents, or NULL
+ */
+function apachesolr_mlt_suggestions($settings, $id) {
+
+  try {
+    $solr = apachesolr_get_solr();
+    $fields = array(
+      'mlt_mintf' => 'mlt.mintf',
+      'mlt_mindf' => 'mlt.mindf',
+      'mlt_minwl' => 'mlt.minwl',
+      'mlt_maxwl' => 'mlt.maxwl',
+      'mlt_maxqt' => 'mlt.maxqt',
+      'mlt_boost' => 'mlt.boost',
+      'mlt_qf' => 'mlt.qf',
+    );
+
+    $params = array(
+      'qt' => 'mlt',
+      'fl' => 'nid,title,path,url',
+      'mlt.fl' => implode(',', $settings['mlt_fl']),
+    );
+
+    foreach ($fields as $form_key => $name) {
+      if (!empty($settings[$form_key])) {
+        $params[$name] = $settings[$form_key];
+      }
+    }
+    $query = apachesolr_drupal_query('id:' . $id);
+    
+    $type_filters = array();
+    if (is_array($settings['mlt_type_filters'])) {
+      foreach ($settings['mlt_type_filters'] as $type_filter) {
+        $type_filters[] = "type:{$type_filter}";
+      }
+      $params['fq']['mlt'][] = '(' . implode(' OR ', $type_filters) . ') ';
+    }
+
+    if ($custom_filters = $settings['mlt_custom_filters']) {
+      $params['fq']['mlt'][] = $custom_filters;
+    }
+
+    // This hook allows modules to modify the query and params objects.
+    apachesolr_modify_query($query, $params, 'apachesolr_mlt');
+    if (empty($query)) {
+      return;
+    }
+
+    $response = $solr->search($query->get_query_basic(), 0, $settings['num_results'], $params);
+
+    if ($response->response) {
+      $docs = (array) end($response->response);
+      return $docs;
+    }
+  }
+  catch ( Exception $e ) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR );
+  }
+}
+
+/**
+ * Implementation of hook_form_[form_id]_alter
+ */
+function apachesolr_form_block_admin_display_form_alter(&$form) {
+  foreach ($form as $key => $block) {
+    if ((strpos($key, "apachesolr_mlt-") === 0) && $block['module']['#value'] == 'apachesolr') {
+      $form[$key]['delete'] = array('#value' => l(t('delete'), 'admin/settings/apachesolr/mlt/delete_block/'. $block['delta']['#value']));
+    }
+  }
+}
+
+/**
+ * Returns a list of blocks. Used by hook_block
+ */
+function apachesolr_mlt_list_blocks() {
+  $blocks = variable_get('apachesolr_mlt_blocks', array());
+  foreach ($blocks as $delta => $settings) {
+    $blocks[$delta] += array('info' => t('Apache Solr recommendations: !name', array('!name' => $settings['name'])) , 'cache' => BLOCK_CACHE_PER_PAGE);
+  }
+  return $blocks;
+}
+
+function apachesolr_mlt_load_block($delta) {
+  $blocks = variable_get('apachesolr_mlt_blocks', array());
+  return isset($blocks[$delta]) ? $blocks[$delta] : FALSE;
+}
+
+function theme_apachesolr_mlt_recommendation_block($docs) {
+  $links = array();
+  foreach ($docs as $result) {
+    // Suitable for single-site mode.
+    $links[] = l($result->title, $result->path, array('html' => TRUE));
+  }
+  return theme('item_list', $links);
+}
+
+function theme_apachesolr_facet_link($facet_text, $path, $options = array(), $count, $active = FALSE, $num_found = NULL) {
+  $options['attributes']['class'][] = 'apachesolr-facet';
+  if ($active) {
+    $options['attributes']['class'][] = 'active';
+  }
+  $options['attributes']['class'] = implode(' ', $options['attributes']['class']);
+  return apachesolr_l($facet_text ." ($count)",  $path, $options);
+}
+
+/**
+ * A replacement for l()
+ *  - doesn't add the 'active' class
+ *  - retains all $_GET parameters that ApacheSolr may not be aware of
+ *  - if set, $options['query'] MUST be an array
+ *
+ * @see http://api.drupal.org/api/function/l/6 for parameters and options.
+ *
+ * @return
+ *   an HTML string containing a link to the given path.
+ */
+function apachesolr_l($text, $path, $options = array()) {
+  // Merge in defaults.
+  $options += array(
+    'attributes' => array(),
+    'html' => FALSE,
+    'query' => array(),
+  );
+
+  // Don't need this, and just to be safe.
+  unset($options['attributes']['title']);
+  // Double encode + characters for clean URL Apache quirks.
+  if (variable_get('clean_url', '0')) {
+    $path = str_replace('+', '%2B', $path);
+  }
+
+  // Retain GET parameters that ApacheSolr knows nothing about.
+  $query = apachesolr_current_query();
+  $get = array_diff_key($_GET, array('q' => 1, 'page' => 1), $options['query'], $query->get_url_queryvalues());
+  $options['query'] += $get;
+
+  return '<a href="'. check_url(url($path, $options)) .'"'. drupal_attributes($options['attributes']) .'>'. ($options['html'] ? $text : check_plain($text)) .'</a>';
+}
+
+function apachesolr_js() {
+  static $settings;
+  // Only add the js stuff once.
+  if (empty($settings)) {
+    $settings['apachesolr_facetstyle'] = variable_get('apachesolr_facetstyle', 'checkboxes');
+    // This code looks for enabled facet blocks and injects the block #ids into
+    // Drupal.settings as jQuery selectors to add the Show more links.
+    $show_more_blocks = array();
+    $facet_map = array();
+    foreach (apachesolr_get_facet_definitions() as $module => $definitions) {
+      foreach ($definitions as $facet => $facet_definition) {
+        $facet_map[$facet_definition['facet_field']] = $facet;
+      }
+    }
+    foreach (apachesolr_get_enabled_facets() as $module => $blocks) {
+      foreach ($blocks as $block) {
+        $show_more_selector[] = "#block-{$module}-{$facet_map[$block]}:has(.apachesolr-hidden-facet)";
+      }
+    }
+    $settings['apachesolr_show_more_blocks'] = implode(', ', $show_more_selector);
+    drupal_add_js($settings, 'setting');
+    drupal_add_js(drupal_get_path('module', 'apachesolr') . '/apachesolr.js');
+  }
+}
+
+function theme_apachesolr_unclick_link($facet_text, $path, $options = array()) {
+  apachesolr_js();
+  if (empty($options['html'])) {
+    $facet_text = check_plain($facet_text);
+  }
+  else {
+    // Don't pass this option as TRUE into apachesolr_l().
+    unset($options['html']);
+  }
+  $options['attributes']['class'] = 'apachesolr-unclick';
+  return apachesolr_l("(-)", $path, $options) . ' '. $facet_text;
+}
+
+function theme_apachesolr_sort_link($text, $path, $options = array(), $active = FALSE, $direction = '') {
+  $icon = '';
+  if ($direction) {
+    $icon = ' '. theme('tablesort_indicator', $direction);
+  }
+  if ($active) {
+    if (isset($options['attributes']['class'])) {
+      $options['attributes']['class'] .= ' active';
+    }
+    else {
+      $options['attributes']['class'] = 'active';
+    }
+  }
+  return $icon . apachesolr_l($text, $path, $options);
+}
+
+function theme_apachesolr_facet_list($items, $display_limit = 0, $delta = '') {
+  apachesolr_js();
+  // theme('item_list') expects a numerically indexed array.
+  $items = array_values($items);
+  // If there is a limit and the facet count is over the limit, hide the rest.
+  if (($display_limit > 0) && (count($items) > $display_limit)) {
+    // Split items array into displayed and hidden.
+    $hidden_items = array_splice($items, $display_limit);
+    foreach ($hidden_items as $hidden_item) {
+      if (!is_array($hidden_item)) {
+        $hidden_item = array('data' => $hidden_item);
+      }
+      $items[] = $hidden_item + array('class' => 'apachesolr-hidden-facet');
+    }
+  }
+  $admin_link = '';
+  if (user_access('administer search')) {
+    $admin_link = l(t('Configure enabled filters'), 'admin/settings/apachesolr/enabled-filters');
+  }
+  return theme('item_list', $items) . $admin_link;
+}
+
+function theme_apachesolr_sort_list($items) {
+  // theme('item_list') expects a numerically indexed array.
+  $items = array_values($items);
+  return theme('item_list', $items);
+}
+
+/**
+ * The interface for all 'query' objects.
+ */
+interface Drupal_Solr_Query_Interface {
+  /**
+   * Checks to see if a specific filter is already present.
+   *
+   * @param string $field
+   * the facet field to check
+   *
+   * @param string $value
+   * The facet value to check against
+   */
+  function has_filter($field, $value);
+
+  /**
+   * Remove a filter from the query
+   *
+   * @param string $field
+   * the facet field to remove
+   *
+   * @param string $value
+   * The facet value to remove
+   * This value can be NULL
+   */
+  function remove_filter($field, $value = NULL);
+
+  /**
+   * Add a filter to a query
+   *
+   * @param string $field
+   *   the facet field to apply to this query
+   *
+   * @param string value
+   *   the value of the facet to apply
+   *
+   * @param boolean $exclude
+   *   Optional paramter.  If TRUE, the filter will be negative,
+   *   meaning that matching values will be excluded from the
+   *   result set.
+   */
+  function add_filter($field, $value, $exclude = FALSE);
+
+  /**
+   * Return the search path (including the search keywords).
+   */
+  function get_path();
+
+  /**
+   * Return the query's keywords.
+   */
+  function get_keys();
+
+  /**
+   * Set the query's keywords.
+   *
+   * @param string keys
+   *   The new keywords.
+   */
+  function set_keys($keys);
+
+  /**
+   * Return an array of parameters for use in the l function.
+   *
+   * @see l()
+   */
+  function get_url_queryvalues();
+
+  /**
+   * return the basic string query
+   */
+  function get_query_basic();
+
+  /**
+   * Set the solrsort.
+   *
+   * @param $field
+   *  The name of a field in the solr index that's an allowed sort.
+   *
+   * @param $direction
+   *  'asc' or 'desc'
+   */
+  function set_solrsort($field, $direction);
+
+  /**
+   * Get the solrsort.
+   *
+   * Returns the non-urlencode, non-aliased sort field and direction.
+   * as an array keyed with '#name' and '#direction'.
+   */
+  function get_solrsort();
+
+  /**
+   * Return an array of all filters.
+   */
+  function get_filters($name = NULL);
+
+  /**
+   * Add a subquery to the query.
+   *
+   * @param Drupal_Solr_Query_Interface $query
+   *   The query to add to the orginal query - may have keywords or filters.
+   *
+   * @param string $fq_operator
+   *   The operator to use within the filter part of the subquery
+   *
+   * @param string $q_operator
+   *   The operator to use in joining the subquery to
+   *   the main keywords.  Note - this is unlikely to work
+   *   with the Dismax handler when the main query is only
+   *   keywords.
+   */
+  function add_subquery(Drupal_Solr_Query_Interface $query, $fq_operator = 'OR', $q_operator = 'AND');
+
+  /**
+   * return the sorts that are provided by the query object
+   *
+   * @return array all the sorts provided
+   */
+  function get_available_sorts();
+
+  /**
+   * Remove a specific subquery
+   *
+   * @param Drupal_Solr_Query_Interface $query
+   * the query to remove
+   */
+  function remove_subquery(Drupal_Solr_Query_Interface $query);
+
+  /**
+   * remove all subqueries
+   */
+  function remove_subqueries();
+
+  /**
+   * make a sort available
+   */
+  function set_available_sort($field, $sort);
+}
diff -urN apachesolr.orig/CHANGELOG.txt.orig apachesolr/CHANGELOG.txt.orig
--- apachesolr.orig/CHANGELOG.txt.orig	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/CHANGELOG.txt.orig	2010-01-09 01:53:18.000000000 +0100
@@ -0,0 +1,273 @@
+// $Id: CHANGELOG.txt,v 1.1.2.99.2.87 2010/01/09 00:53:18 pwolanin Exp $
+
+Apache Solr integration x.x-x.x, xxxx-xx-xx
+------------------------------
+
+Apache Solr integration 6.x-2.x, xxxx-xx-xx
+------------------------------
+
+Apache Solr integration 6.x-2.0-ALPHA2, 2010-01-08
+------------------------------
+#679522 by pwolanin, Add gettableFiles to solr admin interface config.
+http://drupal.org/cvs?commit=309746 by robertDouglass, add entity='comment' to comments on indexing.
+#672882 by David Lesieur: Fixed Broken 'Show more' link on taxonomy facets.
+#604566 by robertDouglass | jhedstrom: Fixed index_value() never set for CCK fields that aren't of type text, node or user referrence.
+#672530 by robertDouglass: Fixed Change array key names from display callback to display_callback() and indexing callback to indexing_callback().
+#672518 by robertDouglass: Fixed Add new trie prefixes to helper function .
+#551582 by drewish: Fixed Show value instead of key in CCK facets.
+#668396 by pwolanin, closer to fix from #655006 for PHP notices.
+#664818 by robertDouglass, pounard, pwolanin | Scott Reynolds: Fixed Wrong watchdog() usage.
+#657648 by kcoop: Added Add Smaller Limit Options to Apache Solr Cron Indexing.
+
+Apache Solr integration 6.x-2.0-ALPHA1, 2009-12-26
+------------------------------  
+#664818 by robertDouglass, pounard, pwolanin | Scott Reynolds: Fixed Wrong watchdog() usage.
+#662232 by pwolanin | anarchivist: Changed Use language-neutral code like D7.
+#666648 by pwolanin: Changed Make hook_apachesolr_update_index() more generic by taking a namespace param also.
+#667110 by pwolanin: Fixed Replace bogus use of pager_query().
+#667124 by pwolanin: Fixed Use current query not altered query for the breadcrumb.
+#667650 by Dave Reid: Fixed Results of apachesolr_process_response() should return absolute URLs.
+#664572 by pwolanin: Added Add schema and core name to admin screen.
+#664860 by pounard: Fixed Wrong t() usage in apachesolr_field_name_map().
+#528086 by pwolanin, better (but still problematic) handling of entities.
+#662232 by pwolanin, index zxx as the Language neutral code.
+#401234 by mkalkbrenner, janusman, and pwolanin, reflect hierarchical taxonomy vocabulary in facets.
+#661952 by pwolanin, fix no results help text for dismax syntax.
+#348668 by pwolanin, add indexing of the 'node' entity string.
+#641954 by anarchivist, swentel, pwolanin, update schema.xml.
+#651044 by kcoop use node title for comment title when comment has no title.
+#655006 by Scott Reynolds, pwolanin fix warnings on constants.
+#652512 by robertDouglass enable use of more than just the default solr server.
+#642602 by robertDouglass, change 'content type' to 'content_type' in facet definitions.
+#641452 by robertDouglass, prevent admin from trying to re-index when in read-only mode.
+#372767 by socki, robertDouglass, pwolanin allow MLT blocks to be filtered by type and custom filters.
+#372336 by der, janusman, robertDouglass, allow name sorting of facet links.
+#611670 by pwolanin, allow modules to abort the building of documents for indexing.
+#628080 by pwolanin, update to use Solr PHP library r22 and check for it in hook_requirements.
+#638236 by mkalkbrenner and robertDouglass, undocumented dependency on taxonomy module.
+#562458 by janusman, fix typo preventing menu_rebuild regarding taxonomy hijack.
+#630798 by joshk, robertDouglass make cache_apachesolr table to facilitate better memcache utilization.
+#623046 by robertDouglass make the results that come back from a search more useful.
+#622120 by robertDouglass make the "Show more" block selection dynamic to accommodate other modules.
+#621922 by robertDouglass make the "Show more" js more robust.
+#612024 by pwolanin, Add method to allow requests to additional Solr servelets.
+#561082 by pwolanin, consolidate Solr delete queries on cron.
+#580404 by pwolanin per content type comment exclusion.
+#597174 by Frando, add hook_apachesolr_prepare_query() to enable custom sorts.
+#591278 by robertDouglass fix bug that was preventing hook_apachesolr_modify_query from working correctly.
+#590982 by swentel fix warnings on indexing.
+#554136 by emackn, Jaza make results-per-page alterable.
+#580764 by robertDougalss Add a new contrib module that allows searching on just comments.
+#548160 by robertDouglass get rid of functions that begin with underscore. Yuck.
+#580404 by robertDouglass make indexing of comments optional. New variable, apachesolr_index_comments_with_node.
+#538636 by robertDouglass allow modules to register document handlers so that multiple documents can be indexed per entity.
+#557382 by Josh Waihi, Scott Reynolds mlt blocks were double encoding titles.
+#578008 by robertDouglass improve performance by not including unused facet queries.
+#552152 by robertDouglass OR operator for facet blocks.
+#576092 by robertDouglass use Drush to search the site using Solr.
+#576040 by robertDouglass use Drush to download the SolrPhpClient: drush solr phpclient
+#457826 by janusman Make the behavior of empty searches configurable.
+#573038 by robertDouglass Automatically create facets for user and node reference CCK fields.
+#570476 by robertDouglass add initial Drush support with commands drush solr delete index and drush solr reindex.
+#570476 by robertDouglass allow for deleting or reindexing single content types.
+#456420 by anarchivist, janusman, robertDouglass Reindex using Batch API.
+#551510 by Scott Reynolds Add in ability to theme different facet blocks differently.
+#551620 by robertDouglass Type dependent facet blocks.
+#549664 by Scott Reynolds Ignore node_access for Solr Views queries.
+#551582 by robertDouglass make CCK breadcrumbs, facets, and current search show the value, not the key.
+#551278 by robertDouglass CCK mappings don't respect shared fields
+#535654 by drunken monkey Add apachesolr_server_status() function
+#543226 by drunken monkey validate port on settings form.
+#502976 by Scott Reynolds followup to Other GET parameters ignored by Apache Solr Facet Blocks
+#473554 by janusman Add an "unclick" link to search keys
+#545094 by loganfsmyth add getter and setter methods for a query's keys.
+#530910 by Damien Tournoud fix offset problem in field settings administration.
+#526344 by drunken monkey Remove apachesolr_read_only check from Drupal_ Apache_Solr_Service::_sendRawPost().
+#525980 by robertDouglass Clarify the API of apachesolr_index_updated.
+#530196 by pwolanin, fix facecount form function calls in apachesolr_og.
+#548102 by robertDouglass change wording on enabled filters page to improve usability.
+#529606 by Damien Tournoud update schema.xml with WhitespaceTokenizerFactory and SnowballPorterFilterFactory. Note that you need to stop solr, replace schema.xml, delete your index, and re-index your site.
+#528002 by janusman, Add RSS discovery to taxonomy hijack page
+#528888 by robertDouglass turn spellchecker on by default
+#528596 part 1 by robertDouglass add JS enabled checkboxes to facet and unclick links
+#525918 by robertDouglass be more forceful when reindexing; rebuild the apachesolr_search_node table completely.
+#528516 by robertDouglass add apachesolr-facet and apachesolr-unclick CSS classes to unclick and facet links.
+#528484 by robertDouglass switch to Drupal.behaviors in apachesolr.js
+#515682 by robertDouglass, add confirmation form to re-index button.
+#509526 by pwolanin, {apachesolr_search_node} table should be rebuilt when index is deleted.
+#457826 by janusman, robertDouglass - show browsable facet blocks in the search well when no search term is present to allow browsing. Hello 6.2 branch of ApacheSolr :D
+
+Apache Solr integration 6.x-1.x, xxxx-xx-xx
+------------------------------
+#508364 by pwolanin, Don't offer non-indexed fields as search options.
+#508548 by pwolanin, Don't implode params['fq'] if it's not set.
+
+Apache Solr integration 6.x-1.0-RC1, 2009-07-02
+------------------------------
+#502976 by pwolanin, Scott Reynolds, robertDouglass facet links and form submissions respect non ApacheSolr $_GET parameters. Note that this changes the interface API: get_url_querystring is now get_url_queryvalues and returns and array instead of a string.
+#507708 by pwolanin, fix sort parameters to use field aliases, validate in query object.
+#299539 by kleung11 and pwolanin, use 'administer search' for permission checking.
+#503644 by pwolanin and Jeremy, make sure we strip ctrl chars last, add logging.
+#505652 by bdurbin, add apachesolr-showhide class to Show more link.
+#472600 by janusman, JacobSingh, and pwolanin, optionally hijack taxonomy pages.
+#496650 by mkalkbrenner, make unclick links work after #463900.
+#495258 comment out timeAllowed params (partial roll-back of #490076)
+#495012 by pwolanin, fix Anonymous user and other 0-value facets.
+#463900 by pwolanin and JacobSingh, facet theme function clean-up.
+#405206 by pwolanin, allow Apache Solr to be the default, let core search index 0 nodes.
+#453310 by pwolanin, allow easier theming of username display.
+#490076 by pwolanin, spellcheck more popular, maxqt to 20, limit search time.
+#358166 by David Lesieur, janusman, cptnCauliflower, and pwolanin, search for just facet(s).
+#489654 by JacobSingh, and pwolanin, allow users to set their index as "read only".
+
+Apache Solr integration 6.x-1.0-beta11, 2009-06-11
+------------------------------
+#348218 by David Lesieur, janusman, and pwolanin, retain filters for next search.
+#401046 by pwolanin, revist urlencoding of query strings.
+#467810 by aufumy, Pass in page number and caller to apachesolr_search_execute.
+#481838 by JacobSingh and pwolanin, enable plus sign in search when using clean URLs.
+#480814 by mkalkbrenner and pwolanin, add more detail to logging on errors.
+#464758 by pwolanin, 4th param to htmlspecialchars breaks PHP < 5.2.3.
+#466328 by pwolanin, fix classes for sort links.
+
+Apache Solr integration 6.x-1.0-beta10, 2009-05-14
+------------------------------
+#449414 by pwolanin, aufumy, & Scott Reynolds, refactor apachesolr_search_search().
+#462836 by pwolanin, catch fatal error in _nodeaccess if no solr.
+#461506 by pwolanin, do nothing if there are no nodes to index.
+#459930 by Scott Reynolds and pwolanin, clean up hook_enable(), uninstall, update_6004
+#453338 by pwolanin and JacobSingh, move mlt functionality into the framework module.
+#365495 by pwolanin, improve admin screens and usability of field weights.
+#454608 by pwolanin, fix current search block.
+#453182 by pwolanin, use stored path rather than forcing node/$nid.
+#448298 by JacobSingh and pwolanin, use a confirm form for index deletion.
+#454352 by Damien Tournoud, make optimize interval configurable, document variables.
+
+Apache Solr integration 6.x-1.0-beta9, 2009-04-30
+------------------------------
+#435924 by pwolanin, only clear cache on cron after updates and if the server is available.
+#405780 by blackdog and pwolanin, skip excluded node types during counting and indexing.
+#441628 by aufumy and pwolanin, update _og for negative facets, minor fixes, install/enable/update hooks.
+#447622 by pwolanin and mkalkbrenner, better encoding of html entities and CCK facets.
+#447890 by pwolanin, properly respect 'access content' permission in _nodeaccess.
+#271753 by pwolanin, more granular CCK field mappings via _alter hook.
+#436074 by pwolanin, better query class handling of negative queries.
+#442198 by Scott Reynolds and pwolanin, update the Drupal_Solr_Query_Interface interface.
+#443252 by Scott Reynolds, (bugfix for regression) make protected id public again.
+#337737 by David Lesieur, mikejoconnor and Scott Reynolds, localize arg(1) dependence.
+        Changes query get_path() to facilitate generating facets outside the search page.
+
+Apache Solr integration 6.x-1.0-beta8, 2009-04-16
+------------------------------
+#343252 by pwolanin, fix nodeaccess for method name changes, make multi-site aware.
+#432946 by pwolanin, query class and sort cleanups.
+#393480 by pwolanin and Jody Lynn, provide a book facet and facets for missing fields.
+#432140 by Damien Tournoud, use format_interval() for more attractive, localizable time intervals.
+#348029 by pwolanin, Handle negative filters and improve date facet block code.
+#254565 by drunken monkey and Scott Reynolds, change the query class to enable Views integration.
+
+Apache Solr integration 6.x-1.0-beta7, 2009-04-03
+------------------------------
+#410330 by pwolanin and bhuga, return more information for error 0.
+#293989 by bjaspan and vladimir.dolgopolov, add date facets for created and changed dates. 
+#420290 by mkalkbrenner and pwolanin, add spaces around tags to avoid running words together.
+#368688 by hurleyit and pwolanin, send MLT docs instead of processed links to theme function.
+#383478 by pwolanin and JacobSingh, provide more information about autocommit lag, pending deletes.
+#339490 by aufumy, pwolanin, and JacobSingh, Organic groups Apachesolr integration, new _alter hook.
+
+Apache Solr integration 6.x-1.0-beta6, 2009-03-20
+------------------------------
+#305370 by pwolanin, Handle failed delete requests so unpublished/deleted content doesn't stay in the index.
+#407570 by pwolanin and moshe weitzman, _alter for sort links, hide for < 2 results.
+#392978 by pwolanin and ncameron, workaround for those using php 5.1, update README.
+#402984 by JacobSingh and pwolanin, put MLT menu under the general ApacheSolr settings.
+#401442 by Janusman and pwolanin, no sort block when 0 results.
+#405732 by JacobSingh, pwolanin: Update to new SolrPhpClient (r6) and make ping() use drupal_http_request. 
+#405722 by JacobSingh, increase ping timeout and make it variable.
+#400882 by mkalkbrenner, fix faceting bug due to static counter in method add_field.
+#382358 by pwolanin, use tokenizer solr.CharStreamAwareWhitespaceTokenizerFactory to fix highlighting.
+
+Apache Solr integration 6.x-1.0-beta5, 2009-02-27
+------------------------------
+#305370 by pwolanin, don't delete from apachesolr table if Solr query fails.
+#385348 by moshe weitzman, use key in sort links array.
+#385362 by pwolanin, Shorten hash from 32 chars to 12.
+#383804 by JacobSingh, fix query building that broke nodeaccess.
+
+Apache Solr integration 6.x-1.0-beta4, 2009-02-23
+------------------------------
+#380670 by pwolanin, only add a bq param for a node-type boost > 'Normal'.
+#379518 by pwolanin, correct mismatch in default boost between 
+        solrconfig.xml and apachesolr_search.
+#380594 by pwolanin, empty the spellcheck dictionary if the index is deleted.
+#380644 by JacobSingh, Backwards compatability for old sort fields. 
+#380538 by pwolanin, fix code to find vid for taxonomy facet blocks.
+
+Apache Solr integration 6.x-1.0-beta3, 2009-02-20
+------------------------------
+#378222 by janusman and pwolanin, add boost settings for "sticky" and "promote".
+#378566 by pwolanin, nodeaccess not correctly marking single nodes for re-indexing.
+#378270 by pwolanin, suppress MLT admin link when there is no block content.
+#378196 by pwolanin, remove PHP client from CVS per Drupal.org policy.
+#231200 by janusman and pwolanin, turn on mapping of accented to non-accented
+        ISO characters for indexing and search. 
+#377494 by pwolanin, Update text type in schema to new example.
+#376270 by pwolanin, also add option to bias on recent comments/changes.
+#337879 by pwolanin and blackdog, Store relative not absolute paths.
+#376255 by pwolanin, Index more node fields, use boolean fields.
+#376966 by JacobSingh and pwolanin, requesting the top terms from luke is very
+        expensive, so normally request 0, and only get them for smaller indexes.
+#375991 by pwolanin, build spellcheck index on optimize.
+#370707 by pwolanin, make sort field names consistent, make ignored multiValued.
+#375723 by pwolanin, prevent fatal error if available facet list changes.
+#373921 by JacobSingh, show pending docs on index page from autocommit.
+#371858 by pwolanin, re-fill the Luke cache after we empty it on cron.
+#372120 by pwolanin, create one MLT block by default on install of apachesolr_mlt.
+#370707 compact field names, create "order by" fields in schema.xml
+        by pwolanin and Damien Tournoud.
+#370796 avoid repeated looping/indexing in apachesolr_index_nodes() by Damien Tournoud.
+#369944 Add field aliases and further clean-up the query class, by pwolanin.
+#366959 make usage of solrsort consistent in Solr_Base_Query::solrsort by Damien Tournoud.
+#       Update errant watchdog calls that were using D5 signature by robertDouglass.
+#369780 Rearrange code for better organization and performance by robertDouglass.
+#365901 Fix bug where indexing might hang & improved API by adding a separate
+        hook for modules to indicate that a node is excluded, by pwolanin.
+#367361 Use the machine-readable name for disabled node types by pwolanin.
+#366957 Add a "configure" link to the more like this block by JacobSingh.
+#365901 Add a bias on node type (and node-type exclusion) by Damien Tournoud and pwolanin.
+
+Apache Solr integration 6.x-1.0-beta2, 2009-01-28 (changes since 6.x-1.0-alpha6 2009-Jan-08)
+------------------------------
+#365684 Get PHP library from new svn home by pwolanin
+#365620 clear stale data on hook_enable by pwolanin
+#365312 don't redirect after enabling filters by pwolanin
+#365245 invalid foreach when no facets available reported by Damien Tournoud
+#        don't let attachements be enabled since it seems be broken by pwolanin
+#363972 fix ApacheSolr to Apache Solr
+#363972 Text improvements for UI by horncologne and pwolanin
+#365063 fix module name in admin screen by pwolanin
+#365022 fatal error in MLT when no Solr server by pwolanin
+#364446 fix space problem and clean up query class by pwolanin
+#355525 fix mis-named variables, patch by pwolanin,  bug reported by flexer
+#339467 centralize/register facets blocks by paul.lovvik, JacobSingh and
+        pwolanin
+#362389 make the _image module work by pwolanin
+#364140 fix mlt schema bug, thanks to webrascal
+#364384 reorder selects to have bigger numbers at the top by pwolanin, suggested
+        by horncologne
+#350330 make sure to index dates as GMT, thanks to webrascal
+#363416 reindex without blowing away either the solr index or the core search index by pwolanin
+#360227 strip ctl chars() also on path, reported by flexer
+#348215 cleanup - simplify branching, numerically index array by pwolanin
+#348215 add missing js file with minor text chenges (js file by vladimir.dolgopolov)
+#359923 separate cron limit for apachesolr by pwolanin
+#       remove lang module - code was already added to apachesolr_search
+#292662 commit after we delete the index by pwolanin
+#356696 by pwolanin: copies the author's name to a string field for use in
+        multisite search or sorting by author.  Also snuck in non-compression on
+        the body for performance.
+#348215 by vladimir.dolgopolov: More link for additional facets.
+#292662 update README by pwolanin
+#344249 obey 32 char limit for block deltas by pwolanin
+#355479 fix PHP warning when request fails by pwolanin
+#355544 Add a ->clearCache(); in apachesolr_index_page(), by flexer and pwolanin
diff -urN apachesolr.orig/CHANGELOG.txt.rej apachesolr/CHANGELOG.txt.rej
--- apachesolr.orig/CHANGELOG.txt.rej	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/CHANGELOG.txt.rej	2010-02-07 14:38:13.000000000 +0100
@@ -0,0 +1,10 @@
+--- CHANGELOG.txt	9 Jan 2010 00:50:43 -0000	1.1.2.154
++++ CHANGELOG.txt	27 Jan 2010 16:23:41 -0000
+@@ -5,6 +5,7 @@
+ 
+ Apache Solr Search Integration 6.x-1.x, xxxx-xx-xx
+ ------------------------------
++#267831 by claudiu.cristea: Load balancer implementation.
+ #679522 by pwolanin, Add gettableFiles to solr admin intefrace config.
+ 
+ Apache Solr Search Integration 6.x-1.0-RC5, 2010-01-04
diff -urN apachesolr.orig/Drupal_Apache_Solr_Service_Balancer.php apachesolr/Drupal_Apache_Solr_Service_Balancer.php
--- apachesolr.orig/Drupal_Apache_Solr_Service_Balancer.php	1970-01-01 01:00:00.000000000 +0100
+++ apachesolr/Drupal_Apache_Solr_Service_Balancer.php	2010-02-07 15:23:31.000000000 +0100
@@ -0,0 +1,127 @@
+<?php
+require_once 'SolrPhpClient/Apache/Solr/Service/Balancer.php';
+
+class Drupal_Apache_Solr_Service_Balancer extends Apache_Solr_Service_Balancer {
+
+  /**
+   * Get found read service for current PHP script execution.
+   * 
+   * @return Drupal_Apache_Solr_Service
+   *   Service instance
+   */
+  public function getReadService() {
+    return $this->_selectReadService();
+  }
+
+  /**
+   * Get found write service for current PHP script execution.
+   * 
+   * @return Drupal_Apache_Solr_Service
+   *   Service instance
+   */
+  public function getWriteService() {
+    return $this->_selectWriteService();
+  }
+
+  /**
+   * Get summary information about the first index servive found.
+   */
+  public function getStatsSummary() {
+    $service = $this->_selectWriteService();
+
+    do {
+      try {
+        return $service->getStatsSummary();
+      }
+      catch (Exception $e) {
+        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
+          throw $e;
+        }
+      }
+      $service = $this->_selectWriteService(TRUE);
+    } while ($service);
+    
+    return FALSE;
+  }
+  
+  /**
+   * Clear cached Solr data.
+   */
+  public function clearCache() {
+    foreach ($this->_readableServices as $service) {
+      @$service->clearCache;
+    }
+    foreach ($this->_writableServices as $service) {
+      @$service->clearCache;
+    }
+  }
+  
+  /**
+   * Get meta-data about the index.
+   */
+  public function getLuke($num_terms = 0) {
+    $service = $this->_selectWriteService();
+
+    do {
+      try {
+        return $service->getLuke($num_terms);
+      }
+      catch (Exception $e) {
+        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
+          throw $e;
+        }
+      }
+      $service = $this->_selectWriteService(TRUE);
+    } while ($service);
+
+    return FALSE;
+  }
+  /**
+   * Get just the field meta-data about the index.
+   */
+  public function getFields($num_terms = 0) {
+    return $this->getLuke($num_terms)->fields;
+  }
+  
+  /**
+   * For some reasons this was not implemented in Balancer.
+   */
+  public function deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600) {
+    $service = $this->_selectWriteService();
+
+    do {
+      try {
+        return $service->deleteByMultipleIds($ids, $fromPending = true, $fromCommitted = true, $timeout = 3600);
+      }
+      catch (Exception $e) {
+        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
+          throw $e;
+        }
+      }
+      $service = $this->_selectWriteService(TRUE);
+    } while ($service); 
+
+    return FALSE;
+  }
+  
+  /**
+   * Check if an index or query server is vailable.
+   */
+  public function ping($timeout = 2, $service_type) {
+    $service = $service_type == 'index' ? $this->_selectWriteService() : $this->_selectReadService();
+
+    do {
+      try {
+        return $service->ping($timeout);
+      }
+      catch (Exception $e) {
+        if ($e->getCode() != 0) { //IF NOT COMMUNICATION ERROR
+          throw $e;
+        }
+      }
+      $service = $service_type == 'index' ? $this->_selectWriteService(TRUE) : $this->_selectReadService(TRUE);
+    } while ($service);
+
+    return FALSE;
+  }
+}
diff -urN apachesolr.orig/Drupal_Apache_Solr_Service.php apachesolr/Drupal_Apache_Solr_Service.php
--- apachesolr.orig/Drupal_Apache_Solr_Service.php	2009-12-26 18:12:12.000000000 +0100
+++ apachesolr/Drupal_Apache_Solr_Service.php	2010-02-07 14:49:08.000000000 +0100
@@ -33,7 +33,8 @@
    * @return
    *   (float) seconds taken to ping the server, FALSE if timeout occurs.
    */
-  public function ping($timeout = 2) {
+  public function ping() {
+    $timeout = func_get_arg(0);
     $start = microtime(TRUE);
 
     if ($timeout <= 0.0) {
