changeset 0:a2b4f67e73dc default tip

initial
author Dirk Wintergruen <dwinter@mpiwg-berlin.mpg.de>
date Mon, 08 Jun 2015 10:21:54 +0200
parents
children
files Apache_Solr_Document.php CopyOfsolrsearch_search.pages.OLD Drupal_Apache_Solr_Service.php Solr_Base_Query.php apachesolr.admin.inc facetapi.callbacks.inc plugins/facetapi/adapter.inc plugins/facetapi/query_type_date.inc plugins/facetapi/query_type_geo.inc plugins/facetapi/query_type_integer.inc plugins/facetapi/query_type_numeric_range.inc plugins/facetapi/query_type_term.inc solr-search-result.tpl.php solr-search-results.tpl.php solr/schema.xml solr/solrconfig.xml solrsearch-author-block-form.tpl.php solrsearch-block-form.tpl.php solrsearch-mpiwg.js solrsearch-term-list-author.tpl.php solrsearch-term-list-title.tpl.php solrsearch-term-selection-form.tpl.php solrsearch-title-block-form.tpl.php solrsearch.admin.inc solrsearch.css solrsearch.index.inc_unused solrsearch.info solrsearch.install solrsearch.interface.inc solrsearch.module solrsearch.pages.inc solrsearch_search.admin.inc solrsearch_search.info solrsearch_search.install solrsearch_search.module solrsearch_search.pages.inc solrsearch_search_author_block.inc solrsearch_search_blocks.inc solrsearch_search_title_block.inc solrsearch_terms.inc
diffstat 40 files changed, 18968 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Apache_Solr_Document.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,410 @@
+<?php
+/**
+ * Copyright (c) 2007-2009, Conduit Internet Technologies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  - Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  - Neither the name of Conduit Internet Technologies, Inc. nor the names of
+ *    its contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @copyright Copyright 2007-2009 Conduit Internet Technologies, Inc. (http://conduit-it.com)
+ * @license New BSD (http://solr-php-client.googlecode.com/svn/trunk/COPYING)
+ * @version $Id: Document.php 15 2009-08-04 17:53:08Z donovan.jimenez $
+ *
+ * @package Apache
+ * @subpackage Solr
+ * @author Donovan Jimenez <djimenez@conduit-it.com>
+ */
+
+/**
+ * Additional code Copyright (c) 2011 by Peter Wolanin, and 
+ * additional contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program as the file LICENSE.txt; if not, please see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ */ 
+
+/**
+ * Holds Key / Value pairs that represent a Solr Document along with any associated boost
+ * values. Field values can be accessed by direct dereferencing such as:
+ *
+ * @code
+ * $document->title = 'Something';
+ * echo $document->title;
+ *
+ * Additionally, the field values can be iterated with foreach
+ *
+ * @code
+ *   foreach ($document as $fieldName => $fieldValue) {
+ *   ...
+ *   }
+ * </code>
+ */
+class ApacheSolrDocument implements IteratorAggregate {
+
+  /**
+   * Document boost value
+   *
+   * @var float
+   */
+  protected $_documentBoost = FALSE;
+
+  /**
+   * Document field values, indexed by name
+   *
+   * @var array
+   */
+  protected $_fields = array();
+
+  /**
+   * Document field boost values, indexed by name
+   *
+   * @var array array of floats
+   */
+  protected $_fieldBoosts = array();
+
+  /**
+   * Clear all boosts and fields from this document
+   */
+  public function clear() {
+    $this->_documentBoost = FALSE;
+
+    $this->_fields = array();
+    $this->_fieldBoosts = array();
+  }
+
+  /**
+   * Get current document boost
+   *
+   * @return mixed
+   *   will be false for default, or else a float
+   */
+  public function getBoost() {
+    return $this->_documentBoost;
+  }
+
+  /**
+   * Set document boost factor
+   *
+   * @param mixed $boost
+   *   Use false for default boost, else cast to float that should be > 0 or will be treated as false
+   */
+  public function setBoost($boost) {
+    $boost = (float) $boost;
+
+    if ($boost > 0.0) {
+      $this->_documentBoost = $boost;
+    }
+    else {
+      $this->_documentBoost = FALSE;
+    }
+  }
+
+  /**
+   * Add a value to a multi-valued field
+   *
+   * NOTE: the solr XML format allows you to specify boosts
+   * PER value even though the underlying Lucene implementation
+   * only allows a boost per field. To remedy this, the final
+   * field boost value will be the product of all specified boosts
+   * on field values - this is similar to SolrJ's functionality.
+   *
+   * @code
+   *   $doc = new ApacheSolrDocument();
+   *   $doc->addField('foo', 'bar', 2.0);
+   *   $doc->addField('foo', 'baz', 3.0);
+   *   // resultant field boost will be 6!
+   *   echo $doc->getFieldBoost('foo');
+   *
+   * @param string $key
+   * @param mixed $value
+   * @param mixed $boost
+   *   Use false for default boost, else cast to float that should be > 0 or will be treated as false
+   */
+  public function addField($key, $value, $boost = FALSE) {
+    if (!isset($this->_fields[$key])) {
+      // create holding array if this is the first value
+      $this->_fields[$key] = array();
+    }
+    else if (!is_array($this->_fields[$key])) {
+      // move existing value into array if it is not already an array
+      $this->_fields[$key] = array($this->_fields[$key]);
+    }
+
+    if ($this->getFieldBoost($key) === FALSE) {
+      // boost not already set, set it now
+      $this->setFieldBoost($key, $boost);
+    }
+    else if ((float) $boost > 0.0) {
+      // multiply passed boost with current field boost - similar to SolrJ implementation
+      $this->_fieldBoosts[$key] *= (float) $boost;
+    }
+
+    // add value to array
+    $this->_fields[$key][] = $value;
+  }
+
+  /**
+   * Handle the array manipulation for a multi-valued field
+   *
+   * @param string $key
+   * @param string $value
+   * @param mixed $boost
+   *   Use false for default boost, else cast to float that should be > 0 or will be treated as false
+   *
+   * @deprecated Use addField(...) instead
+   */
+  public function setMultiValue($key, $value, $boost = FALSE) {
+    $this->addField($key, $value, $boost);
+  }
+
+  /**
+   * Get field information
+   *
+   * @param string $key
+   * @return mixed associative array of info if field exists, false otherwise
+   */
+  public function getField($key) {
+    if (isset($this->_fields[$key])) {
+      return array(
+        'name' => $key,
+        'value' => $this->_fields[$key],
+        'boost' => $this->getFieldBoost($key)
+      );
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Set a field value. Multi-valued fields should be set as arrays
+   * or instead use the addField(...) function which will automatically
+   * make sure the field is an array.
+   *
+   * @param string $key
+   * @param mixed $value
+   * @param mixed $boost
+   *   Use false for default boost, else cast to float that should be > 0 or will be treated as false
+   */
+  public function setField($key, $value, $boost = FALSE) {
+    $this->_fields[$key] = $value;
+    $this->setFieldBoost($key, $boost);
+  }
+
+  /**
+   * Get the currently set field boost for a document field
+   *
+   * @param string $key
+   * @return float
+   *   currently set field boost, false if one is not set
+   */
+  public function getFieldBoost($key) {
+    return isset($this->_fieldBoosts[$key]) ? $this->_fieldBoosts[$key] : FALSE;
+  }
+
+  /**
+   * Set the field boost for a document field
+   *
+   * @param string $key
+   *   field name for the boost
+   * @param mixed $boost
+   *   Use false for default boost, else cast to float that should be > 0 or will be treated as false
+   */
+  public function setFieldBoost($key, $boost) {
+    $boost = (float) $boost;
+
+    if ($boost > 0.0) {
+      $this->_fieldBoosts[$key] = $boost;
+    }
+    else {
+      $this->_fieldBoosts[$key] = FALSE;
+    }
+  }
+
+  /**
+   * Return current field boosts, indexed by field name
+   *
+   * @return array
+   */
+  public function getFieldBoosts() {
+    return $this->_fieldBoosts;
+  }
+
+  /**
+   * Get the names of all fields in this document
+   *
+   * @return array
+   */
+  public function getFieldNames() {
+    return array_keys($this->_fields);
+  }
+
+  /**
+   * Get the values of all fields in this document
+   *
+   * @return array
+   */
+  public function getFieldValues() {
+    return array_values($this->_fields);
+  }
+
+  /**
+   * IteratorAggregate implementation function. Allows usage:
+   *
+   * @code
+   *   foreach ($document as $key => $value) {
+   *     ...
+   *   }
+   *
+   */
+  public function getIterator() {
+    $arrayObject = new ArrayObject($this->_fields);
+
+    return $arrayObject->getIterator();
+  }
+
+  /**
+   * Magic get for field values
+   *
+   * @param string $key
+   * @return mixed
+   */
+  public function __get($key) {
+    return $this->_fields[$key];
+  }
+
+  /**
+   * Magic set for field values. Multi-valued fields should be set as arrays
+   * or instead use the addField(...) function which will automatically
+   * make sure the field is an array.
+   *
+   * @param string $key
+   * @param mixed $value
+   */
+  public function __set($key, $value) {
+    $this->setField($key, $value);
+  }
+
+  /**
+   * Magic isset for fields values.  Do not call directly. Allows usage:
+   *
+   * @code
+   *   isset($document->some_field);
+   *
+   * @param string $key
+   * @return boolean
+   *   Whether the given key is set in the document
+   */
+  public function __isset($key) {
+    return isset($this->_fields[$key]);
+  }
+
+  /**
+   * Magic unset for field values. Do not call directly. Allows usage:
+   *
+   * @code
+   *   unset($document->some_field);
+   *
+   * @param string $key
+   */
+  public function __unset($key) {
+    unset($this->_fields[$key]);
+    unset($this->_fieldBoosts[$key]);
+  }
+
+  /**
+   * Create an XML fragment from a ApacheSolrDocument instance appropriate for use inside a Solr add call
+   *
+   * @param ApacheSolrDocument $document
+   *
+   * @return string
+   *   an xml formatted string from the given document
+   */
+  public static function documentToXml(ApacheSolrDocument $document) {
+    $xml = '<doc';
+
+    if ($document->getBoost() !== FALSE) {
+      $xml .= ' boost="' . $document->getBoost() . '"';
+    }
+
+    $xml .= '>';
+
+    foreach ($document as $key => $value) {
+      $key = htmlspecialchars($key, ENT_QUOTES, 'UTF-8');
+      $fieldBoost = $document->getFieldBoost($key);
+
+      if (is_array($value)) {
+        foreach ($value as $multivalue) {
+          $xml .= '<field name="' . $key . '"';
+
+          if ($fieldBoost !== FALSE) {
+            $xml .= ' boost="' . $fieldBoost . '"';
+
+            // Only set the boost for the first field in the set
+            $fieldBoost = FALSE;
+          }
+
+          $xml .= '>' . htmlspecialchars($multivalue, ENT_NOQUOTES, 'UTF-8') . '</field>';
+        }
+      }
+      else {
+        $xml .= '<field name="' . $key . '"';
+
+        if ($fieldBoost !== FALSE) {
+          $xml .= ' boost="' . $fieldBoost . '"';
+        }
+
+        $xml .= '>' . htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8') . '</field>';
+      }
+    }
+
+    $xml .= '</doc>';
+
+    // Remove any control characters to avoid Solr XML parser exception
+    return self::stripCtrlChars($xml);
+  }
+
+  /**
+   * Replace control (non-printable) characters from string that are invalid to Solr's XML parser with a space.
+   *
+   * @param string $string
+   * @return string
+   */
+  public static function stripCtrlChars($string) {
+    // See:  http://w3.org/International/questions/qa-forms-utf-8.html
+    // Printable utf-8 does not include any of these chars below x7F
+    return preg_replace('@[\x00-\x08\x0B\x0C\x0E-\x1F]@', ' ', $string);
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CopyOfsolrsearch_search.pages.OLD	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ *   Provides the page callback for user defined search pages.
+ */
+
+/**
+ * Returns search results on user defined search pages.
+ */
+function solrsearch_search_custom_page($page_id, $keys = '', $path_replacer = NULL) {
+  $search_page = solrsearch_search_page_load($page_id);
+  if (empty($search_page)) {
+    drupal_set_message(t('This search page cannot be found'), 'error');
+    return drupal_not_found();
+  }
+  // Add our replacement value in the conditions array
+  if (!empty($path_replacer)) {
+    $search_page['settings']['solrsearch_search_path_replacer'] = $path_replacer;
+  }
+  // Replace dynamic path with current path
+  $search_page['search_path'] = str_replace('%', $path_replacer, $search_page['search_path']);
+  // Retrieve the conditions that apply to this page
+  $conditions = solrsearch_search_conditions_default($search_page);
+  // Process our keys so they are clean
+  $keys = rawurldecode($keys);
+  // Retrieve the results of the search
+  $results = solrsearch_search_search_results($keys, $conditions, $search_page);
+  // Initiate our build array
+  $build = array();
+  // Add a custom search form if required
+  if (!empty($search_page['settings']['solrsearch_search_search_box'])) {
+    // Adds the search form to the page.
+    $build['search_form'] = drupal_get_form('solrsearch_search_custom_page_search_form', $search_page, $keys);
+  }
+  // Build our page and allow modification.
+  $build_results = solrsearch_search_search_page_custom($results, $search_page, $build);
+  return $build_results;
+}
+
+/**
+ * Search for placed on user defined search pages.
+ */
+function solrsearch_search_custom_page_search_form($form, &$form_state, $search_page, $keys = '') {
+  // Loads the core Search CSS file, use the core search module's classes.
+  drupal_add_css(drupal_get_path('module', 'search') . '/search.css');
+
+  $form = array();
+  $form['#id'] = 'search-form';
+  $form['#attributes']['class'][] = 'search-form';
+  $form['#search_page'] = $search_page;
+  $form['basic'] = array(
+    '#type' => 'container',
+    '#attributes' => array('class' => array('container-inline')),
+  );
+  $form['basic']['keys'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Enter terms'),
+    '#default_value' => $keys,
+    '#size' => 20,
+    '#maxlength' => 255,
+  );
+  $form['basic']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Search'),
+  );
+
+  $form['basic']['get'] = array(
+    '#type' => 'hidden',
+    '#default_value' => json_encode(array_diff_key($_GET, array('q' => 1, 'page' => 1, 'solrsort' => 1, 'retain-filters' => 1))),
+  );
+
+  $fq = NULL;
+
+  if (solrsearch_has_searched($search_page['env_id'])) {
+    $query = solrsearch_current_query($search_page['env_id']);
+    // We use the presence of filter query params as a flag for the retain filters checkbox.
+    $fq = $query->getParam('fq');
+  }
+
+  if ($fq || isset($form_state['input']['retain-filters'])) {
+    $form['basic']['retain-filters'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Retain current filters'),
+      '#default_value' => (int) !empty($_GET['retain-filters']),
+    );
+  }
+
+  return $form;
+}
+
+/**
+ * Processes solrsearch_search_custom_page_search_form submissions.
+ */
+function solrsearch_search_custom_page_search_form_submit(&$form, &$form_state) {
+  $search_page = $form['#search_page'];
+  $redirect = $search_page['search_path'];
+
+  // Also encode slashes so we don't get akward situations when obtaining the
+  // search key. We can't use drupal_encode_path because for "aestetic" reasons
+  // they don't encode slashes...
+  $redirect_value = rawurlencode($form_state['values']['keys']);
+
+  if (strlen($form_state['values']['keys'])) {
+    $redirect .= '/' . $redirect_value;
+  }
+
+  $get = array();
+  if (isset($form_state['values']['get'])) {
+    $get = json_decode($form_state['values']['get'], TRUE);
+  }
+  if (!empty($form_state['values']['retain-filters'])) {
+    // Add our saved values
+    $get['retain-filters'] = '1';
+  }
+  else {
+    // Remove all filters
+    if (!empty($search_page['settings']['solrsearch_search_allow_user_input'])) {
+      unset($get['fq']);
+    }
+    if (module_exists('facetapi')) {
+      unset($get['f']);
+    }
+  }
+
+  // Add the query values into the redirect.
+  $form_state['redirect'] = array($redirect, array('query' => $get));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Drupal_Apache_Solr_Service.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,913 @@
+<?php
+
+/**
+ * Copyright (c) 2007-2009, Conduit Internet Technologies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  - Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  - Neither the name of Conduit Internet Technologies, Inc. nor the names of
+ *    its contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @copyright Copyright 2007-2009 Conduit Internet Technologies, Inc. (http://conduit-it.com)
+ * @license New BSD (http://solr-php-client.googlecode.com/svn/trunk/COPYING)
+ * @version $Id: Service.php 22 2009-11-09 22:46:54Z donovan.jimenez $
+ *
+ * @package Apache
+ * @subpackage Solr
+ * @author Donovan Jimenez <djimenez@conduit-it.com>
+ */
+
+/**
+ * Additional code Copyright (c) 2008-2011 by Robert Douglass, James McKinney,
+ * Jacob Singh, Alejandro Garza, Peter Wolanin, and additional contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program as the file LICENSE.txt; if not, please see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ */
+
+/**
+ * Starting point for the Solr API. Represents a Solr server resource and has
+ * methods for pinging, adding, deleting, committing, optimizing and searching.
+ */
+
+class DrupalsolrsearchService implements DrupalApacheSolrServiceInterface {
+  /**
+   * How NamedLists should be formatted in the output.  This specifically effects facet counts. Valid values
+   * are 'map' (default) or 'flat'.
+   *
+   */
+  const NAMED_LIST_FORMAT = 'map';
+
+  /**
+   * Servlet mappings
+   */
+  const PING_SERVLET = 'admin/ping';
+  const UPDATE_SERVLET = 'update';
+  const SEARCH_SERVLET = 'select';
+  const LUKE_SERVLET = 'admin/luke';
+  const SYSTEM_SERVLET = 'admin/system';
+  const STATS_SERVLET = 'admin/stats.jsp';
+  const STATS_SERVLET_4 = 'admin/mbeans?wt=xml&stats=true';
+
+  /**
+   * Server url
+   *
+   * @var array
+   */
+  protected $parsed_url;
+
+  /**
+   * Constructed servlet full path URLs
+   *
+   * @var string
+   */
+  protected $update_url;
+
+  /**
+   * Default HTTP timeout when one is not specified (initialized to default_socket_timeout ini setting)
+   *
+   * var float
+   */
+  protected $_defaultTimeout;
+  protected $env_id;
+  protected $luke;
+  protected $stats;
+  protected $system_info;
+
+  /**
+   * Flag that denotes whether to use soft commits for Solr 4.x, defaults to FALSE.
+   *
+   * @var bool
+   */
+  protected $soft_commit = FALSE;
+
+  /**
+   * Call the /admin/ping servlet, to test the connection to the server.
+   *
+   * @param $timeout
+   *   maximum time to wait for ping in seconds, -1 for unlimited (default 2).
+   * @return
+   *   (float) seconds taken to ping the server, FALSE if timeout occurs.
+   */
+  public function ping($timeout = 2) {
+    $start = microtime(TRUE);
+
+    if ($timeout <= 0.0) {
+      $timeout = -1;
+    }
+    $pingUrl = $this->_constructUrl(self::PING_SERVLET);
+    // Attempt a HEAD request to the solr ping url.
+    $options = array(
+      'method' => 'HEAD',
+      'timeout' => $timeout,
+    );
+    $response = $this->_makeHttpRequest($pingUrl, $options);
+
+    if ($response->code == 200) {
+      // Add 0.1 ms to the ping time so we never return 0.0.
+      return microtime(TRUE) - $start + 0.0001;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Flags whether to use soft commits for Solr 4.x.
+   *
+   * @param bool $soft_commit
+   *   Whether or not to use soft commits for Solr 4.x.
+   */
+  public function setSoftCommit($soft_commit) {
+    $this->soft_commit = (bool) $soft_commit;
+  }
+
+  /**
+   * Returns the flag that denotes whether to use soft commits for Solr 4.x.
+   *
+   * @return bool
+   *   Whether to use soft commits for Solr 4.x.
+   */
+  public function getSoftCommit() {
+    return $this->soft_commit;
+  }
+
+  /**
+   * Call the /admin/system servlet
+   *
+   * @return
+   *   (array) With all the system info
+   */
+  protected function setSystemInfo() {
+    $url = $this->_constructUrl(self::SYSTEM_SERVLET, array('wt' => 'json'));
+    if ($this->env_id) {
+      $this->system_info_cid = $this->env_id . ":system:" . drupal_hash_base64($url);
+      $cache = cache_get($this->system_info_cid, 'cache_solrsearch');
+      if (isset($cache->data)) {
+        $this->system_info = json_decode($cache->data);
+      }
+    }
+    // Second pass to populate the cache if necessary.
+    if (empty($this->system_info)) {
+      $response = $this->_sendRawGet($url);
+      $this->system_info = json_decode($response->data);
+      if ($this->env_id) {
+        cache_set($this->system_info_cid, $response->data, 'cache_solrsearch');
+      }
+    }
+  }
+
+  /**
+   * Get information about the Solr Core.
+   *
+   * @return
+   *   (string) system info encoded in json
+   */
+  public function getSystemInfo() {
+    if (!isset($this->system_info)) {
+      $this->setSystemInfo();
+    }
+    return $this->system_info;
+  }
+
+  /**
+   * Sets $this->luke with the meta-data about the index from admin/luke.
+   */
+  protected function setLuke($num_terms = 0) {
+    if (empty($this->luke[$num_terms])) {
+      $params = array(
+        'numTerms' => "$num_terms",
+        'wt' => 'json',
+        'json.nl' => self::NAMED_LIST_FORMAT,
+      );
+      $url = $this->_constructUrl(self::LUKE_SERVLET, $params);
+      if ($this->env_id) {
+        $cid = $this->env_id . ":luke:" . drupal_hash_base64($url);
+        $cache = cache_get($cid, 'cache_solrsearch');
+        if (isset($cache->data)) {
+          $this->luke = $cache->data;
+        }
+      }
+    }
+    // Second pass to populate the cache if necessary.
+    if (empty($this->luke[$num_terms])) {
+      $this->luke[$num_terms] = $this->_sendRawGet($url);
+      if ($this->env_id) {
+        cache_set($cid, $this->luke, 'cache_solrsearch');
+      }
+    }
+  }
+
+  /**
+   * Get just the field meta-data about the index.
+   */
+  public function getFields($num_terms = 0) {
+    return $this->getLuke($num_terms)->fields;
+  }
+
+  /**
+   * Get meta-data about the index.
+   */
+  public function getLuke($num_terms = 0) {
+    if (!isset($this->luke[$num_terms])) {
+      $this->setLuke($num_terms);
+    }
+    return $this->luke[$num_terms];
+  }
+
+  /**
+   * Get the current solr version. This could be 1, 3 or 4
+   *
+   * @return int
+   *   1, 3 or 4. Does not give a more details version, for that you need
+   *   to get the system info.
+   */
+  public function getSolrVersion() {
+    $system_info = $this->getSystemInfo();
+    // Get our solr version number
+    if (isset($system_info->lucene->{'solr-spec-version'})) {
+      return $system_info->lucene->{'solr-spec-version'}[0];
+    }
+    return 0;
+  }
+
+  /**
+   * Sets $this->stats with the information about the Solr Core form
+   */
+  protected function setStats() {
+    $data = $this->getLuke();
+    $solr_version = $this->getSolrVersion();
+    // Only try to get stats if we have connected to the index.
+    if (empty($this->stats) && isset($data->index->numDocs)) {
+      if ($solr_version >= 4) {
+        $url = $this->_constructUrl(self::STATS_SERVLET_4);
+      }
+      else {
+        $url = $this->_constructUrl(self::STATS_SERVLET);
+      }
+      if ($this->env_id) {
+        $this->stats_cid = $this->env_id . ":stats:" . drupal_hash_base64($url);
+        $cache = cache_get($this->stats_cid, 'cache_solrsearch');
+        if (isset($cache->data)) {
+          $this->stats = simplexml_load_string($cache->data);
+        }
+      }
+      // Second pass to populate the cache if necessary.
+      if (empty($this->stats)) {
+        $response = $this->_sendRawGet($url);
+        $this->stats = simplexml_load_string($response->data);
+        if ($this->env_id) {
+          cache_set($this->stats_cid, $response->data, 'cache_solrsearch');
+        }
+      }
+    }
+  }
+
+  /**
+   * Get information about the Solr Core.
+   *
+   * Returns a Simple XMl document
+   */
+  public function getStats() {
+    if (!isset($this->stats)) {
+      $this->setStats();
+    }
+    return $this->stats;
+  }
+
+  /**
+   * Get summary information about the Solr Core.
+   */
+  public function getStatsSummary() {
+    $stats = $this->getStats();
+    $solr_version = $this->getSolrVersion();
+
+    $summary = array(
+     '@pending_docs' => '',
+     '@autocommit_time_seconds' => '',
+     '@autocommit_time' => '',
+     '@deletes_by_id' => '',
+     '@deletes_by_query' => '',
+     '@deletes_total' => '',
+     '@schema_version' => '',
+     '@core_name' => '',
+     '@index_size' => '',
+    );
+
+    if (!empty($stats)) {
+      if ($solr_version <= 3) {
+        $docs_pending_xpath = $stats->xpath('//stat[@name="docsPending"]');
+        $summary['@pending_docs'] = (int) trim(current($docs_pending_xpath));
+        $max_time_xpath = $stats->xpath('//stat[@name="autocommit maxTime"]');
+        $max_time = (int) trim(current($max_time_xpath));
+        // Convert to seconds.
+        $summary['@autocommit_time_seconds'] = $max_time / 1000;
+        $summary['@autocommit_time'] = format_interval($max_time / 1000);
+        $deletes_id_xpath = $stats->xpath('//stat[@name="deletesById"]');
+        $summary['@deletes_by_id'] = (int) trim(current($deletes_id_xpath));
+        $deletes_query_xpath = $stats->xpath('//stat[@name="deletesByQuery"]');
+        $summary['@deletes_by_query'] = (int) trim(current($deletes_query_xpath));
+        $summary['@deletes_total'] = $summary['@deletes_by_id'] + $summary['@deletes_by_query'];
+        $schema = $stats->xpath('/solr/schema[1]');
+        $summary['@schema_version'] = trim($schema[0]);
+        $core = $stats->xpath('/solr/core[1]');
+        $summary['@core_name'] = trim($core[0]);
+        $size_xpath = $stats->xpath('//stat[@name="indexSize"]');
+        $summary['@index_size'] = trim(current($size_xpath));
+      }
+      else {
+        $system_info = $this->getSystemInfo();
+        $docs_pending_xpath = $stats->xpath('//lst["stats"]/long[@name="docsPending"]');
+        $summary['@pending_docs'] = (int) trim(current($docs_pending_xpath));
+        $max_time_xpath = $stats->xpath('//lst["stats"]/str[@name="autocommit maxTime"]');
+        $max_time = (int) trim(current($max_time_xpath));
+        // Convert to seconds.
+        $summary['@autocommit_time_seconds'] = $max_time / 1000;
+        $summary['@autocommit_time'] = format_interval($max_time / 1000);
+        $deletes_id_xpath = $stats->xpath('//lst["stats"]/long[@name="deletesById"]');
+        $summary['@deletes_by_id'] = (int) trim(current($deletes_id_xpath));
+        $deletes_query_xpath = $stats->xpath('//lst["stats"]/long[@name="deletesByQuery"]');
+        $summary['@deletes_by_query'] = (int) trim(current($deletes_query_xpath));
+        $summary['@deletes_total'] = $summary['@deletes_by_id'] + $summary['@deletes_by_query'];
+        $schema = $system_info->core->schema;
+        $summary['@schema_version'] = $schema;
+        $core = $stats->xpath('//lst["core"]/str[@name="coreName"]');
+        $summary['@core_name'] = trim(current($core));
+        $size_xpath = $stats->xpath('//lst["core"]/str[@name="indexSize"]');
+        $summary['@index_size'] = trim(current($size_xpath));
+      }
+    }
+
+    return $summary;
+  }
+
+  /**
+   * Clear cached Solr data.
+   */
+  public function clearCache() {
+    // Don't clear cached data if the server is unavailable.
+    if (@$this->ping()) {
+      $this->_clearCache();
+    }
+    else {
+      throw new Exception('No Solr instance available when trying to clear the cache.');
+    }
+  }
+
+  protected function _clearCache() {
+    if ($this->env_id) {
+      cache_clear_all($this->env_id . ":stats:", 'cache_solrsearch', TRUE);
+      cache_clear_all($this->env_id . ":luke:", 'cache_solrsearch', TRUE);
+    }
+    $this->luke = array();
+    $this->stats = NULL;
+  }
+
+  /**
+   * Constructor
+   *
+   * @param $url
+   *   The URL to the Solr server, possibly including a core name.  E.g. http://localhost:8983/solr/
+   *   or https://search.example.com/solr/core99/
+   * @param $env_id
+   *   The machine name of a corresponding saved configuration used for loading
+   *   data like which facets are enabled.
+   */
+  public function __construct($url, $env_id = NULL) {
+    $this->env_id = $env_id;
+    $this->setUrl($url);
+
+    // determine our default http timeout from ini settings
+    $this->_defaultTimeout = (int) ini_get('default_socket_timeout');
+
+    // double check we didn't get 0 for a timeout
+    if ($this->_defaultTimeout <= 0) {
+      $this->_defaultTimeout = 60;
+    }
+  }
+
+  function getId() {
+    return $this->env_id;
+  }
+
+  /**
+   * Check the reponse code and thow an exception if it's not 200.
+   *
+   * @param stdClass $response
+   *   response object.
+   *
+   * @return
+   *  response object
+   * @thows Exception
+   */
+  protected function checkResponse($response) {
+    $code = (int) $response->code;
+    if ($code != 200) {
+      if ($code >= 400 && $code != 403 && $code != 404) {
+        // Add details, like Solr's exception message.
+        $response->status_message .= $response->data;
+      }
+      throw new Exception('"' . $code . '" Status: ' . $response->status_message);
+    }
+    return $response;
+  }
+
+  /**
+   * Make a request to a servlet (a path) that's not a standard path.
+   *
+   * @param string $servlet
+   *   A path to be added to the base Solr path. e.g. 'extract/tika'
+   *
+   * @param array $params
+   *   Any request parameters when constructing the URL.
+   *
+   * @param array $options
+   *  @see drupal_http_request() $options.
+   *
+   * @return
+   *  response object
+   *
+   * @thows Exception
+   */
+  public function makeServletRequest($servlet, $params = array(), $options = array()) {
+    // Add default params.
+    $params += array(
+      'wt' => 'json',
+      'json.nl' => self::NAMED_LIST_FORMAT,
+    );
+
+    $url = $this->_constructUrl($servlet, $params);
+    $response = $this->_makeHttpRequest($url, $options);
+    return $this->checkResponse($response);
+  }
+
+  /**
+   * Central method for making a GET operation against this Solr Server
+   */
+  protected function _sendRawGet($url, $options = array()) {
+    $response = $this->_makeHttpRequest($url, $options);
+    return $this->checkResponse($response);
+  }
+
+  /**
+   * Central method for making a POST operation against this Solr Server
+   */
+  protected function _sendRawPost($url, $options = array()) {
+    $options['method'] = 'POST';
+    // Normally we use POST to send XML documents.
+    if (!isset($options['headers']['Content-Type'])) {
+      $options['headers']['Content-Type'] = 'text/xml; charset=UTF-8';
+    }
+    $response = $this->_makeHttpRequest($url, $options);
+    return $this->checkResponse($response);
+  }
+
+  /**
+   * Central method for making the actual http request to the Solr Server
+   *
+   * This is just a wrapper around drupal_http_request().
+   */
+  protected function _makeHttpRequest($url, array $options = array()) {
+    if (!isset($options['method']) || $options['method'] == 'GET' || $options['method'] == 'HEAD') {
+      // Make sure we are not sending a request body.
+      $options['data'] = NULL;
+    }
+
+    $result = drupal_http_request($url, $options);
+
+    if (!isset($result->code) || $result->code < 0) {
+      $result->code = 0;
+      $result->status_message = 'Request failed';
+      $result->protocol = 'HTTP/1.0';
+    }
+    // Additional information may be in the error property.
+    if (isset($result->error)) {
+      $result->status_message .= ': ' . check_plain($result->error);
+    }
+
+    if (!isset($result->data)) {
+      $result->data = '';
+      $result->response = NULL;
+    }
+    else {
+      $response = json_decode($result->data);
+      if (is_object($response)) {
+        foreach ($response as $key => $value) {
+          $result->$key = $value;
+        }
+      }
+    }
+    return $result;
+  }
+
+
+  /**
+   * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
+   *
+   * NOTE: inside a phrase fewer characters need escaped, use {@link DrupalsolrsearchService::escapePhrase()} instead
+   *
+   * @param string $value
+   * @return string
+   */
+  static public function escape($value)
+  {
+    //list taken from http://lucene.apache.org/java/docs/queryparsersyntax.html#Escaping%20Special%20Characters
+    $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
+    $replace = '\\\$1';
+
+    return preg_replace($pattern, $replace, $value);
+  }
+
+  /**
+   * Escape a value meant to be contained in a phrase for special query characters
+   *
+   * @param string $value
+   * @return string
+   */
+  static public function escapePhrase($value)
+  {
+    $pattern = '/("|\\\)/';
+    $replace = '\\\$1';
+
+    return preg_replace($pattern, $replace, $value);
+  }
+
+  /**
+   * Convenience function for creating phrase syntax from a value
+   *
+   * @param string $value
+   * @return string
+   */
+  static public function phrase($value)
+  {
+    return '"' . self::escapePhrase($value) . '"';
+  }
+
+  /**
+   * Return a valid http URL given this server's host, port and path and a provided servlet name
+   *
+   * @param $servlet
+   *  A string path to a Solr request handler.
+   * @param $params
+   * @param $parsed_url
+   *   A url to use instead of the stored one.
+   *
+   * @return string
+   */
+  protected function _constructUrl($servlet, $params = array(), $added_query_string = NULL) {
+    // PHP's built in http_build_query() doesn't give us the format Solr wants.
+    $query_string = $this->httpBuildQuery($params);
+
+    if ($query_string) {
+      $query_string = '?' . $query_string;
+      if ($added_query_string) {
+        $query_string = $query_string . '&' . $added_query_string;
+      }
+    }
+    elseif ($added_query_string) {
+      $query_string = '?' . $added_query_string;
+    }
+
+    $url = $this->parsed_url;
+    return $url['scheme'] . $url['user'] . $url['pass'] . $url['host'] . $url['port'] . $url['path'] . $servlet . $query_string;
+  }
+
+  /**
+   * Get the Solr url
+   *
+   * @return string
+   */
+  public function getUrl() {
+    return $this->_constructUrl('');
+  }
+
+  /**
+   * Set the Solr url.
+   *
+   * @param $url
+   *
+   * @return $this
+   */
+  public function setUrl($url) {
+    $parsed_url = parse_url($url);
+
+    if (!isset($parsed_url['scheme'])) {
+      $parsed_url['scheme'] = 'http';
+    }
+    $parsed_url['scheme'] .= '://';
+
+    if (!isset($parsed_url['user'])) {
+      $parsed_url['user'] = '';
+    }
+    else {
+      $parsed_url['host'] = '@' . $parsed_url['host'];
+    }
+    $parsed_url['pass'] = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
+    $parsed_url['port'] = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
+
+    if (isset($parsed_url['path'])) {
+      // Make sure the path has a single leading/trailing slash.
+      $parsed_url['path'] = '/' . ltrim($parsed_url['path'], '/');
+      $parsed_url['path'] = rtrim($parsed_url['path'], '/') . '/';
+    }
+    else {
+      $parsed_url['path'] = '/';
+    }
+    // For now we ignore query and fragment.
+    $this->parsed_url = $parsed_url;
+    // Force the update url to be rebuilt.
+    unset($this->update_url);
+    return $this;
+  }
+
+  /**
+   * Raw update Method. Takes a raw post body and sends it to the update service. Post body
+   * should be a complete and well formed xml document.
+   *
+   * @param string $rawPost
+   * @param float $timeout Maximum expected duration (in seconds)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function update($rawPost, $timeout = FALSE) {
+    // @todo: throw exception if updates are disabled.
+    if (empty($this->update_url)) {
+      // Store the URL in an instance variable since many updates may be sent
+      // via a single instance of this class.
+      $this->update_url = $this->_constructUrl(self::UPDATE_SERVLET, array('wt' => 'json'));
+    }
+    $options['data'] = $rawPost;
+    if ($timeout) {
+      $options['timeout'] = $timeout;
+    }
+    return $this->_sendRawPost($this->update_url, $options);
+  }
+
+  /**
+   * Add an array of Solr Documents to the index all at once
+   *
+   * @param array $documents Should be an array of solrsearchDocument instances
+   * @param boolean $allowDups
+   * @param boolean $overwritePending
+   * @param boolean $overwriteCommitted
+   *
+   * @return response objecte
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function addDocuments($documents, $overwrite = NULL, $commitWithin = NULL) {
+    $attr = '';
+
+    if (isset($overwrite)) {
+      $attr .= ' overwrite="' . empty($overwrite) ? 'false"' : 'true"';
+    }
+    if (isset($commitWithin)) {
+      $attr .= ' commitWithin="' . intval($commitWithin) . '"';
+    }
+
+    $rawPost = "<add{$attr}>";
+    foreach ($documents as $document) {
+      if (is_object($document) && ($document instanceof solrsearchDocument)) {
+        $rawPost .= solrsearchDocument::documentToXml($document);
+      }
+    }
+    $rawPost .= '</add>';
+
+    return $this->update($rawPost);
+  }
+
+  /**
+   * Send a commit command.  Will be synchronous unless both wait parameters are set to false.
+   *
+   * @param boolean $optimize Defaults to true
+   *   optimizes the index files. Only valid for solr versions <= 3
+   * @param boolean $waitFlush
+   *   block until index changes are flushed to disk. Only valid for solr versions <= 3
+   * @param boolean $waitSearcher
+   *   block until a new searcher is opened and registered as the main query searcher, making the changes visible.
+   * @param float $timeout
+   *   Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function commit($optimize = TRUE, $waitFlush = TRUE, $waitSearcher = TRUE, $timeout = 3600) {
+    $optimizeValue = $optimize ? 'true' : 'false';
+    $flushValue = $waitFlush ? 'true' : 'false';
+    $searcherValue = $waitSearcher ? 'true' : 'false';
+    $softCommit = $this->soft_commit ? 'true' : 'false';
+
+    $solr_version = $this->getSolrVersion();
+    if ($solr_version <= 3) {
+      $rawPost = '<commit waitSearcher="' . $searcherValue . '" waitFlush="' . $flushValue . '" optimize="' . $optimizeValue . '" />';
+    }
+    else {
+      $rawPost = '<commit waitSearcher="' . $searcherValue . '" softCommit="' . $softCommit . '" />';
+    }
+
+    $response = $this->update($rawPost, $timeout);
+    $this->_clearCache();
+    return $response;
+  }
+
+  /**
+   * Create a delete document based on document ID
+   *
+   * @param string $id Expected to be utf-8 encoded
+   * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function deleteById($id, $timeout = 3600) {
+    return $this->deleteByMultipleIds(array($id), $timeout);
+  }
+
+  /**
+   * Create and post a delete document based on multiple document IDs.
+   *
+   * @param array $ids Expected to be utf-8 encoded strings
+   * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function deleteByMultipleIds($ids, $timeout = 3600) {
+    $rawPost = '<delete>';
+
+    foreach ($ids as $id) {
+      $rawPost .= '<id>' . htmlspecialchars($id, ENT_NOQUOTES, 'UTF-8') . '</id>';
+    }
+    $rawPost .= '</delete>';
+
+    return $this->update($rawPost, $timeout);
+  }
+
+  /**
+   * Create a delete document based on a query and submit it
+   *
+   * @param string $rawQuery Expected to be utf-8 encoded
+   * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
+   * @return stdClass response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function deleteByQuery($rawQuery, $timeout = 3600) {
+    $rawPost = '<delete><query>' . htmlspecialchars($rawQuery, ENT_NOQUOTES, 'UTF-8') . '</query></delete>';
+
+    return $this->update($rawPost, $timeout);
+  }
+
+  /**
+   * Send an optimize command.  Will be synchronous unless both wait parameters are set
+   * to false.
+   *
+   * @param boolean $waitFlush
+   *   block until index changes are flushed to disk  Removed in Solr 4.0
+   * @param boolean $waitSearcher
+   *   block until a new searcher is opened and registered as the main query searcher, making the changes visible.
+   * @param float $timeout
+   *   Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function optimize($waitFlush = TRUE, $waitSearcher = TRUE, $timeout = 3600) {
+    $flushValue = $waitFlush ? 'true' : 'false';
+    $searcherValue = $waitSearcher ? 'true' : 'false';
+    $softCommit = $this->soft_commit ? 'true' : 'false';
+
+    $solr_version = $this->getSolrVersion();
+    if ($solr_version <= 3) {
+      $rawPost = '<optimize waitSearcher="' . $searcherValue . '" waitFlush="' . $flushValue . '" />';
+    }
+    else {
+      $rawPost = '<optimize waitSearcher="' . $searcherValue . '" softCommit="' . $softCommit . '" />';
+    }
+
+    return $this->update($rawPost, $timeout);
+  }
+
+  /**
+   * Like PHP's built in http_build_query(), but uses rawurlencode() and no [] for repeated params.
+   */
+  protected function httpBuildQuery(array $query, $parent = '') {
+    $params = array();
+
+    foreach ($query as $key => $value) {
+      $key = ($parent ? $parent : rawurlencode($key));
+
+      // Recurse into children.
+      if (is_array($value)) {
+        $params[] = $this->httpBuildQuery($value, $key);
+      }
+      // If a query parameter value is NULL, only append its key.
+      elseif (!isset($value)) {
+        $params[] = $key;
+      }
+      else {
+        $params[] = $key . '=' . rawurlencode($value);
+      }
+    }
+
+    return implode('&', $params);
+  }
+
+  /**
+   * Simple Search interface
+   *
+   * @param string $query The raw query string
+   * @param array $params key / value pairs for other query parameters (see Solr documentation), use arrays for parameter keys used more than once (e.g. facet.field)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  public function search($query = '', array $params = array(), $method = 'GET') {
+    // Always use JSON. See http://code.google.com/p/solr-php-client/issues/detail?id=6#c1 for reasoning
+
+
+
+    if (!user_access("view restricted content")){
+      $params['fq'][]='access-type:free';
+
+
+    }
+
+    $params['wt'] = 'json';
+    // Additional default params.
+    $params += array(
+      'json.nl' => self::NAMED_LIST_FORMAT,
+    );
+    if ($query) {
+      $params['q'] = $query;
+    }
+    // PHP's built in http_build_query() doesn't give us the format Solr wants.
+    $queryString = $this->httpBuildQuery($params);
+    // Check string length of the query string, change method to POST
+    $len = strlen($queryString);
+    // Fetch our threshold to find out when to flip to POST
+    $max_len = solrsearch_environment_variable_get($this->env_id, 'solrsearch_search_post_threshold', 3600);
+
+    // if longer than $max_len (default 3600) characters
+    // we should switch to POST (a typical server handles 4096 max).
+    // If this class is used independently (without environments), we switch automatically to POST at an
+    // limit of 1800 chars.
+    if (($len > 1800) && (empty($this->env_id) || ($len > $max_len))) {
+      $method = 'POST';
+    }
+
+    if ($method == 'GET') {
+      $searchUrl = $this->_constructUrl(self::SEARCH_SERVLET, array(), $queryString);
+
+      return $this->_sendRawGet($searchUrl);
+    }
+    else if ($method == 'POST') {
+      $searchUrl = $this->_constructUrl(self::SEARCH_SERVLET);
+      $options['data'] = $queryString;
+      $options['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
+      return $this->_sendRawPost($searchUrl, $options);
+    }
+    else {
+      throw new Exception("Unsupported method '$method' for search(), use GET or POST");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Solr_Base_Query.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,657 @@
+<?php
+/**
+ * This class allows you to make operations on a query that will be sent to
+ * Apache Solr. methods such as adding and removing sorts, remove and replace
+ * parameters, adding and removing filters, getters and setters for various
+ * parameters and more
+ * @file
+ *   Class that defines the base query for the Apache Solr Drupal module.
+ */
+
+class SolrFilterSubQuery {
+
+  /**
+   * Static shared by all instances, used to increment ID numbers.
+   */
+  protected static $idCount = 0;
+
+  /**
+   * Each query/subquery will have a unique ID.
+   */
+  public $id;
+  public $operator;
+
+  /**
+   * A keyed array where the key is a position integer and the value
+   * is an array with #name and #value properties.  Each value is a
+   * used for filter queries, e.g. array('#name' => 'is_uid', '#value' => 0)
+   * for anonymous content.
+   */
+  protected $fields = array();
+
+  /**
+   * An array of subqueries.
+   */
+  protected $subqueries = array();
+
+  function __construct($operator = 'OR') {
+    $this->operator = $operator;
+    $this->id = ++SolrFilterSubQuery::$idCount;
+  }
+
+  function __clone() {
+    $this->id = ++SolrFilterSubQuery::$idCount;
+  }
+
+  public function getFilters($name = NULL) {
+    if (empty($name)) {
+      return $this->fields;
+    }
+    reset($this->fields);
+    $matches = array();
+    foreach ($this->fields as $filter) {
+      if ($filter['#name'] == $name) {
+        $matches[] = $filter;
+      }
+    }
+    return $matches;
+  }
+
+  public function hasFilter($name, $value, $exclude = FALSE) {
+    foreach ($this->fields as $pos => $values) {
+      if ($values['#name'] == $name && $values['#value'] == $value && $values['#exclude'] == $exclude) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  public function addFilter($name, $value, $exclude = FALSE, $local = '') {
+    // @todo - escape the value if it has spaces in it and is not a range query or parenthesized.
+    $filter = array(
+      '#exclude' => (bool) $exclude,
+      '#name' => trim($name),
+      '#value' => trim($value),
+      '#local' => trim($local),
+    );
+    $this->fields[] = $filter;
+    return $this;
+  }
+
+  public function removeFilter($name, $value = NULL, $exclude = FALSE) {
+    // Remove from the public list of filters.
+    $this->unsetFilter($this->fields, $name, $value, $exclude);
+    return $this;
+  }
+
+  protected function unsetFilter(&$fields, $name, $value, $exclude) {
+    if (!isset($value)) {
+      foreach ($fields as $pos => $values) {
+        if ($values['#name'] == $name) {
+          unset($fields[$pos]);
+        }
+      }
+    }
+    else {
+      foreach ($fields as $pos => $values) {
+        if ($values['#name'] == $name && $values['#value'] == $value && $values['#exclude'] == $exclude) {
+          unset($fields[$pos]);
+        }
+      }
+    }
+  }
+
+  public function getFilterSubQueries() {
+    return $this->subqueries;
+  }
+
+  public function addFilterSubQuery(SolrFilterSubQuery $query) {
+    $this->subqueries[$query->id] = $query;
+    return $this;
+  }
+
+  public function removeFilterSubQuery(SolrFilterSubQuery $query) {
+    unset($this->subqueries[$query->id]);
+    return $this;
+  }
+
+  public function removeFilterSubQueries() {
+    $this->subqueries = array();
+    return $this;
+  }
+
+  public function makeFilterQuery(array $filter) {
+    $prefix = empty($filter['#exclude']) ? '' : '-';
+    if ($filter['#local']) {
+      $prefix = '{!' . $filter['#local'] . '}' . $prefix;
+    }
+    // If the field value contains a colon or a space, wrap it in double quotes,
+    // unless it is a range query or is already wrapped in double quotes or
+    // parentheses.
+    if (preg_match('/[ :]/', $filter['#value']) && !preg_match('/^[\[\{]\S+ TO \S+[\]\}]$/', $filter['#value']) && !preg_match('/^["\(].*["\)]$/', $filter['#value'])) {
+      $filter['#value'] = '"' . $filter['#value'] . '"';
+    }
+    return $prefix . $filter['#name'] . ':' . $filter['#value'];
+  }
+
+  /**
+   * Make sure our query matches the pattern name:value or name:"value"
+   * Make sure that if we are ranges we use name:[ AND ]
+   * allowed inputs :
+   * a. bundle:article
+   * b. date:[1970-12-31T23:59:59Z TO NOW]
+   * Split the text in 4 different parts
+   * 1. name, eg.: bundle or date
+   * 2. The first opening bracket (or nothing), eg.: [
+   * 3. The value of the field, eg. article or 1970-12-31T23:59:59Z TO NOW
+   * 4. The last closing bracket, eg.: ]
+   * @param string $filter
+   *   The filter to validate
+   * @return boolean
+   */
+  public static function validFilterValue($filter) {
+    $opening = 0;
+    $closing = 0;
+    $name = NULL;
+    $value = NULL;
+
+    if (preg_match('/(?P<name>[^:]+):(?P<value>.+)?$/', $filter, $matches)) {
+      foreach ($matches as $match_id => $match) {
+        switch($match_id) {
+          case 'name' :
+            $name = $match;
+            break;
+          case 'value' :
+            $value = $match;
+            break;
+        }
+      }
+
+      // For the name we allow any character that fits between the A-Z0-9 range and
+      // any alternative for this in other languages. No special characters allowed
+      if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
+        return FALSE;
+      }
+
+      // For the value we allow anything that is UTF8
+      if (!drupal_validate_utf8($value)) {
+        return FALSE;
+      }
+
+      // Check our bracket count. If it does not match it is also not valid
+      $valid_brackets = TRUE;
+      $brackets['opening']['{'] = substr_count($value, '{');
+      $brackets['closing']['}'] = substr_count($value, '}');
+      $valid_brackets = ($brackets['opening']['{'] != $brackets['closing']['}']) ? FALSE : TRUE;
+      $brackets['opening']['['] = substr_count($value, '[');
+      $brackets['closing'][']'] = substr_count($value, ']');
+      $valid_brackets = ($brackets['opening']['['] != $brackets['closing'][']']) ? FALSE : TRUE;
+      $brackets['opening']['('] = substr_count($value, '(');
+      $brackets['closing'][')'] = substr_count($value, ')');
+      $valid_brackets = ($brackets['opening']['('] != $brackets['closing'][')']) ? FALSE : TRUE;
+      if (!$valid_brackets) {
+        return FALSE;
+      }
+
+      // Check the date field inputs
+      if (preg_match('/\[(.+) TO (.+)\]$/', $value, $datefields)) {
+        // Only Allow a value in the form of
+        // http://lucene.apache.org/solr/api/org/apache/solr/schema/DateField.html
+        // http://lucene.apache.org/solr/api/org/apache/solr/util/DateMathParser.html
+        // http://wiki.apache.org/solr/SolrQuerySyntax
+        // 1976-03-06T23:59:59.999Z (valid)
+        // * (valid)
+        // 1995-12-31T23:59:59.999Z (valid)
+        // 2007-03-06T00:00:00Z (valid)
+        // NOW-1YEAR/DAY (valid)
+        // NOW/DAY+1DAY (valid)
+        // 1976-03-06T23:59:59.999Z (valid)
+        // 1976-03-06T23:59:59.999Z+1YEAR (valid)
+        // 1976-03-06T23:59:59.999Z/YEAR (valid)
+        // 1976-03-06T23:59:59.999Z (valid)
+        // 1976-03-06T23::59::59.999Z (invalid)
+        if (!empty($datefields[1]) && !empty($datefields[2])) {
+          // Do not check to full value, only the splitted ones
+          unset($datefields[0]);
+          // Check if both matches are valid datefields
+          foreach ($datefields as $datefield) {
+            if (!preg_match('/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:[\d\.]{2,6}Z(\S)*)|(^([A-Z\*]+)(\A-Z0-9\+\-\/)*)/', $datefield, $datefield_match)) {
+              return FALSE;
+            }
+          }
+        }
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * Builds a set of filter queries from $this->fields and all subqueries.
+   *
+   * Returns an array of strings that can be combined into
+   * a URL query parameter or passed to Solr as fq paramters.
+   */
+  protected function rebuildFq() {
+    $fq = array();
+    foreach ($this->fields as $pos => $field) {
+      $fq[] = $this->makeFilterQuery($field);
+    }
+    foreach ($this->subqueries as $subquery) {
+      $subfq = $subquery->rebuildFq();
+      if ($subfq) {
+        $operator = $subquery->operator;
+        $fq[] = "(" . implode(" $operator ", $subfq) . ")";
+      }
+    }
+    return $fq;
+  }
+
+}
+
+class SolrBaseQuery extends SolrFilterSubQuery implements DrupalSolrQueryInterface {
+
+  /**
+   * The parameters that get sent to Solr.
+   */
+  protected $params = array('start' => 0, 'rows' => 10, 'fq' => array());
+
+  /**
+   * The search base path.
+   */
+  protected $base_path;
+  protected $field_map = array();
+
+  /**
+   * DrupalApacheSolrService object
+   */
+  protected $solr;
+  // The array keys must always be real Solr index fields.
+  protected $available_sorts;
+
+  /**
+   * The query name is used to construct a searcher string. Mostly the
+   * environment id
+   */
+  protected $name;
+  protected $context = array();
+  // Makes sure we always have a valid sort.
+  protected $solrsort = array('#name' => 'score', '#direction' => 'desc');
+  // A flag to allow the search to be aborted.
+  public $abort_search = FALSE;
+
+  // A flag to check if need to retrieve another page of the result set
+  public $page = 0;
+
+  /**
+   * @param $name
+   *   The search name, used for finding the correct blocks and other config.
+   *   Typically "apachesolr".
+   *
+   * @param $solr
+   *   An instantiated DrupalApacheSolrService Object.
+   *   Can be instantiated from apachesolr_get_solr().
+   *
+   * @param $params
+   *   Array of params to initialize the object (typically 'q' and 'fq').
+   *
+   * @param $sortstring
+   *   Visible string telling solr how to sort - added to GET query params.
+   *
+   * @param $base_path
+   *   The search base path (without the keywords) for this query, without trailing slash.
+   */
+  function __construct($name, $solr, array $params = array(), $sortstring = '', $base_path = '', $context = array()) {
+    parent::__construct();
+    $this->name = $name;
+    $this->solr = $solr;
+    $this->addContext((array) $context);
+    $this->addParams((array) $params);
+    $this->available_sorts = $this->defaultSorts();
+    $this->sortstring = trim($sortstring);
+    $this->parseSortString();
+    $this->base_path = $base_path;
+  }
+
+  protected function defaultSorts() {
+    return array(
+      'score' => array('title' => t('Relevancy'), 'default' => 'desc'),
+      'title' => array('title' => t('Title'), 'default' => 'asc'),
+      'author_s' => array('title' => t('Author'), 'default' => 'asc'),
+      'date' => array('title' => t('Date'), 'default' => 'desc'),
+    );
+  }
+
+  /**
+   * Get query name.
+   */
+  public function getName() {
+    return $this->name;
+  }
+
+  /**
+   * Get query searcher name (for facetapi, views, pages, etc).
+   */
+  public function getSearcher() {
+    return $this->name . '@' . $this->solr->getId();
+  }
+
+  /**
+   * Get context values.
+   */
+  public function getContext() {
+    return $this->context;
+  }
+
+  /**
+   * Set context value.
+   */
+  public function addContext(array $context) {
+    foreach ($context as $k => $v) {
+      $this->context[$k] = $v;
+    }
+    // The env_id must match that of the actual $solr object
+    $this->context['env_id'] = $this->solr->getId();
+    return $this->context;
+  }
+
+  protected $single_value_params = array(
+    'q' => TRUE, // http://wiki.apache.org/solr/SearchHandler#q
+    'q.op' => TRUE, // http://wiki.apache.org/solr/SearchHandler#q.op
+    'q.alt' => TRUE, // http://wiki.apache.org/solr/SearchHandler#q
+    'df' => TRUE,
+    'qt' => TRUE,
+    'defType' => TRUE,
+    'timeAllowed' => TRUE,
+    'omitHeader' => TRUE,
+    'debugQuery' => TRUE,
+    'start' => TRUE,
+    'rows' => TRUE,
+    'stats' => TRUE,
+    'facet' => TRUE,
+    'facet.prefix' => TRUE,
+    'facet.limit' => TRUE,
+    'facet.offset' => TRUE,
+    'facet.mincount' => TRUE,
+    'facet.missing' => TRUE,
+    'facet.method' => TRUE,
+    'facet.enum.cache.minDf' => TRUE,
+    'facet.date.start' => TRUE,
+    'facet.date.end' => TRUE,
+    'facet.date.gap' => TRUE,
+    'facet.date.hardend' => TRUE,
+    'facet.date.other' => TRUE,
+    'facet.date.include' => TRUE,
+    'hl' => TRUE,
+    'hl.snippets' => TRUE,
+    'hl.fragsize' => TRUE,
+    'hl.mergeContiguous' => TRUE,
+    'hl.requireFieldMatch' => TRUE,
+    'hl.maxAnalyzedChars' => TRUE,
+    'hl.alternateField' => TRUE,
+    'hl.maxAlternateFieldLength' => TRUE,
+    'hl.formatter' => TRUE,
+    'hl.simple.pre/hl.simple.post' => TRUE,
+    'hl.fragmenter' => TRUE,
+    'hl.fragListBuilder' => TRUE,
+    'hl.fragmentsBuilder' => TRUE,
+    'hl.useFastVectorHighlighter' => TRUE,
+    'hl.usePhraseHighlighter' => TRUE,
+    'hl.highlightMultiTerm' => TRUE,
+    'hl.regex.slop' => TRUE,
+    'hl.regex.pattern' => TRUE,
+    'hl.regex.maxAnalyzedChars' => TRUE,
+    'spellcheck' => TRUE,
+  );
+
+  public function getParam($name) {
+    if ($name == 'fq') {
+      return $this->rebuildFq();
+    }
+    $empty = isset($this->single_value_params[$name]) ? NULL : array();
+    return isset($this->params[$name]) ? $this->params[$name] : $empty;
+  }
+
+  public function getParams() {
+    $params = $this->params;
+    $params['fq'] = $this->rebuildFq();
+    return $params;
+  }
+
+  public function getSolrParams() {
+    $params = $this->getParams();
+    // For certain fields Solr prefers a comma separated list.
+    foreach (array('fl', 'hl.fl', 'sort', 'mlt.fl') as $name) {
+      if (isset($params[$name])) {
+        $params[$name] = implode(',', $params[$name]);
+      }
+    }
+    return $params;
+  }
+
+  protected function addFq($string, $index = NULL) {
+    $string = trim($string);
+    $local = '';
+    $exclude = FALSE;
+    $name = NULL;
+    $value = NULL;
+
+    // Check if we are dealing with an exclude
+    if (preg_match('/^-(.*)/', $string, $matches)) {
+      $exclude = TRUE;
+      $string = $matches[1];
+    }
+
+    // If {!something} is found as first character then this is a local value
+    if (preg_match('/\{!([^}]+)\}(.*)/', $string, $matches)) {
+      $local = $matches[1];
+      $string = $matches[2];
+    }
+
+    // Anything that has a name and value
+    // check if we have a : in the string
+    if (strstr($string, ':')) {
+      list($name, $value) = explode(":", $string, 2);
+    }
+    else {
+      $value = $string;
+    }
+    $this->addFilter($name, $value, $exclude, $local);
+    return $this;
+  }
+
+  public function addParam($name, $value) {
+    if (isset($this->single_value_params[$name])) {
+      if (is_array($value)) {
+        $value = end($value);
+      }
+      $this->params[$name] = $this->normalizeParamValue($value);
+      return $this;
+    }
+    // We never actually populate $this->params['fq'].  Instead
+    // we manage everything via the filter methods.
+    if ($name == 'fq') {
+      if (is_array($value)) {
+        array_walk_recursive($value, array($this, 'addFq'));
+        return $this;
+      }
+      else {
+        return $this->addFq($value);
+      }
+    }
+
+    if (!isset($this->params[$name])) {
+      $this->params[$name] = array();
+    }
+
+    if (!is_array($value)) {
+      // Convert to array for array_map.
+      $param_values = array($value);
+    }
+    else {
+      // Convert to a numerically keyed array.
+      $param_values = array_values($value);
+    }
+    $this->params[$name] = array_merge($this->params[$name], array_map(array($this, 'normalizeParamValue'), $param_values));
+
+    return $this;
+  }
+
+  protected function normalizeParamValue($value) {
+    // Convert boolean to string.
+    if (is_bool($value)) {
+      return $value ? 'true' : 'false';
+    }
+    // Convert to trimmed string.
+    return trim($value);
+  }
+
+  public function addParams(Array $params) {
+    foreach ($params as $name => $value) {
+      $this->addParam($name, $value);
+    }
+    return $this;
+  }
+
+  public function removeParam($name) {
+    unset($this->params[$name]);
+    if ($name == 'fq') {
+      $this->fields = array();
+      $this->subqueries = array();
+    }
+    return $this;
+  }
+
+  public function replaceParam($name, $value) {
+    $this->removeParam($name);
+    return $this->addParam($name, $value);
+  }
+
+  /**
+   * Handles aliases for field to make nicer URLs.
+   *
+   * @param $field_map
+   *   An array keyed with real Solr index field names with the alias as value.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  public function addFieldAliases($field_map) {
+    $this->field_map = array_merge($this->field_map, $field_map);
+    // We have to re-parse the filters.
+    $this->parseSortString();
+    return $this;
+  }
+
+  public function getFieldAliases() {
+    return $this->field_map;
+  }
+
+  public function clearFieldAliases() {
+    $this->field_map = array();
+    // We have to re-parse the filters.
+    $this->parseSortString();
+    return $this;
+  }
+
+  protected function parseSortString() {
+    // Substitute any field aliases with real field names.
+    $sortstring = strtr($this->sortstring, $this->field_map);
+    // Score is a special case - it's the default sort for Solr.
+    if ('' == $sortstring || 'score desc' == $sortstring) {
+      $this->solrsort['#name'] = 'score';
+      $this->solrsort['#direction'] = 'desc';
+      unset($this->params['sort']);
+    }
+    else {
+      // Validate and set sort parameter
+      $fields = implode('|', array_keys($this->available_sorts));
+      if (preg_match('/^(?:(' . $fields . ') (asc|desc),?)+$/', $sortstring, $matches)) {
+        // We only use the last match.
+        $this->solrsort['#name'] = $matches[1];
+        $this->solrsort['#direction'] = $matches[2];
+        $this->params['sort'] = array($sortstring);
+      }
+    }
+  }
+
+  public function getAvailableSorts() {
+    return $this->available_sorts;
+  }
+
+  public function setAvailableSort($name, $sort) {
+    // We expect non-aliased sorts to be added.
+    $this->available_sorts[$name] = $sort;
+    // Re-parse the sortstring.
+    $this->parseSortString();
+    return $this;
+  }
+
+  public function setAvailableSorts($sorts) {
+    // We expect a complete array of valid sorts.
+    $this->available_sorts = $sorts;
+    $this->parseSortString();
+    return $this;
+  }
+
+  public function removeAvailableSort($name) {
+    unset($this->available_sorts[$name]);
+    // Re-parse the sortstring.
+    $this->parseSortString();
+    return $this;
+  }
+
+  public function getSolrsort() {
+    return $this->solrsort;
+  }
+
+  public function setSolrsort($name, $direction) {
+    $this->sortstring = trim($name) . ' ' . trim($direction);
+    $this->parseSortString();
+    return $this;
+  }
+
+  public function getPath($new_keywords = NULL) {
+    if (isset($new_keywords)) {
+      return $this->base_path . '/' . $new_keywords;
+    }
+    elseif ($this->getParam('q')) {
+      return $this->base_path . '/' . $this->getParam('q');
+    }
+    else {
+      // Return with empty query (the slash). The path for a facet
+      // becomes $this->base_path . '//facetinfo';
+      // We do this so we can have a consistent way of retrieving the query +
+      // additional parameters
+      return $this->base_path . '/';
+    }
+  }
+
+  public function getSolrsortUrlQuery() {
+    $queryvalues = array();
+    $solrsort = $this->solrsort;
+    if ($solrsort && ($solrsort['#name'] != 'score')) {
+      if (isset($this->field_map[$solrsort['#name']])) {
+        $solrsort['#name'] = $this->field_map[$solrsort['#name']];
+      }
+      $queryvalues['solrsort'] = $solrsort['#name'] . ' ' . $solrsort['#direction'];
+    }
+    else {
+      // Return to default relevancy sort.
+      unset($queryvalues['solrsort']);
+    }
+    return $queryvalues;
+  }
+
+  public function search($keys = NULL) {
+    if ($this->abort_search) {
+      return NULL;
+    }
+
+    return $this->solr->search($keys, $this->getSolrParams());
+  }
+
+  public function solr($method) {
+    return $this->solr->$method();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apachesolr.admin.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,1342 @@
+<?php
+
+/**
+ * @file
+ *   Administrative pages for the Apache Solr framework.
+ */
+
+/**
+ * Form to delete a search environment
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return array output of confirm_form()
+ */
+function apachesolr_environment_delete_form(array $form, array &$form_state, array $environment) {
+  $form['env_id'] = array(
+    '#type' => 'value',
+    '#value' => $environment['env_id'],
+  );
+  if (isset($environment['export_type']) && $environment['export_type'] == 3) {
+    $verb = t('Revert');
+  }
+  else {
+    $verb = t('Delete');
+  }
+  return confirm_form(
+    $form,
+    t('Are you sure you want to !verb search environment %name?', array('%name' => $environment['name'], '!verb' => strtolower($verb))),
+    'admin/config/search/apachesolr',
+    t('This action cannot be undone.'),
+    $verb,
+    t('Cancel')
+  );
+}
+
+/**
+ * Submit handler for the delete form
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_environment_delete_form_submit(array $form, array &$form_state) {
+  if (apachesolr_environment_delete($form_state['values']['env_id'])) {
+    drupal_set_message(t('The search environment was deleted'));
+  }
+  $form_state['redirect'] = 'admin/config/search/apachesolr/settings';
+}
+
+function apachesolr_environment_edit_delete_submit($form, &$form_state) {
+  $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $form_state['values']['env_id'] . '/delete';
+
+  // Regardlessly of the destination parameter we want to go to another page
+  unset($_GET['destination']);
+  drupal_static_reset('drupal_get_destination');
+  drupal_get_destination();
+}
+
+/**
+ * Settings page for a specific environment (or default one if not provided)
+ *
+ * @param array|bool $environment
+ *
+ * @return array Render array for a settings page
+ */
+function apachesolr_environment_settings_page(array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = apachesolr_default_environment();
+    $environment = apachesolr_environment_load($env_id);
+  }
+  $env_id = $environment['env_id'];
+
+ // Initializes output with information about which environment's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $output = array(
+    'apachesolr_environment' => array(
+      '#theme' => 'apachesolr_settings_title',
+      '#env_id' => $env_id,
+    ),
+  );
+  $output['form'] = drupal_get_form('apachesolr_environment_edit_form', $environment);
+  return $output;
+}
+
+/**
+ * Form to clone a certain environment
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return array output of confirm_form()
+ */
+function apachesolr_environment_clone_form(array $form, array &$form_state, array $environment) {
+  $form['env_id'] = array(
+    '#type' => 'value',
+    '#value' => $environment['env_id'],
+  );
+  return confirm_form(
+    $form,
+    t('Are you sure you want to clone search environment %name?', array('%name' => $environment['name'])),
+    'admin/config/search/apachesolr',
+    '',
+    t('Clone'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Submit handler for the clone form
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_environment_clone_form_submit(array $form, array &$form_state) {
+  if (apachesolr_environment_clone($form_state['values']['env_id'])) {
+    drupal_set_message(t('The search environment was cloned'));
+  }
+  $form_state['redirect'] = 'admin/config/search/apachesolr/settings';
+}
+
+/**
+ * Submit handler for the confirmation page of cloning an environment
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_environment_clone_submit(array $form, array &$form_state) {
+  $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $form_state['values']['env_id'] . '/clone';
+}
+
+/**
+ * Form builder for adding/editing a Solr environment used as a menu callback.
+ */
+function apachesolr_environment_edit_form(array $form, array &$form_state, array $environment = array()) {
+  if (empty($environment)) {
+    $environment = array();
+  }
+  $environment += array('env_id' => '', 'name' => '', 'url' => '', 'service_class' => '', 'conf' => array());
+
+  $form['#environment'] = $environment;
+  $form['url'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr server URL'),
+    '#default_value' => $environment['url'],
+    '#description' => t('Example: http://localhost:8983/solr'),
+    '#required' => TRUE,
+  );
+  $is_default = $environment['env_id'] == apachesolr_default_environment();
+  $form['make_default'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Make this Solr search environment the default'),
+    '#default_value' => $is_default,
+    '#disabled' => $is_default,
+  );
+  $form['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Description'),
+    '#default_value' => $environment['name'],
+    '#required' => TRUE,
+  );
+  $form['env_id'] = array(
+    '#type' => 'machine_name',
+    '#title' => t('Environment id'),
+    '#machine_name' => array(
+      'exists' => 'apachesolr_environment_load',
+    ),
+    '#default_value' => $environment['env_id'],
+    '#disabled' => !empty($environment['env_id']), // Cannot change it once set.
+    '#description' => t('Unique, machine-readable identifier for this Solr environment.'),
+    '#required' => TRUE,
+  );
+  $form['service_class'] = array(
+    '#type' => 'value',
+    '#value' => $environment['service_class'],
+  );
+  $form['conf'] = array(
+    '#tree' => TRUE,
+  );
+  $form['conf']['apachesolr_read_only'] = array(
+    '#type' => 'radios',
+    '#title' => t('Index write access'),
+    '#default_value' => isset($environment['conf']['apachesolr_read_only']) ? $environment['conf']['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 this search environment. Useful for development sites.'),
+  );
+  $form['actions'] = array(
+    '#type' => 'actions',
+  );
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#validate' => array('apachesolr_environment_edit_validate'),
+    '#submit' => array('apachesolr_environment_edit_submit'),
+    '#value' => t('Save'),
+  );
+  $form['actions']['save_edit'] = array(
+    '#type' => 'submit',
+    '#validate' => array('apachesolr_environment_edit_validate'),
+    '#submit' => array('apachesolr_environment_edit_submit'),
+    '#value' => t('Save and edit'),
+  );
+  $form['actions']['test'] = array(
+    '#type' => 'submit',
+    '#validate' => array('apachesolr_environment_edit_validate'),
+    '#submit' => array('apachesolr_environment_edit_test_submit'),
+    '#value' => t('Test connection'),
+  );
+  if (!empty($environment['env_id']) && !$is_default) {
+    $form['actions']['delete'] = array(
+      '#type' => 'submit',
+      '#submit' => array('apachesolr_environment_edit_delete_submit'),
+      '#value' => t('Delete'),
+    );
+  }
+
+  // Ensures destination is an internal URL, builds "cancel" link.
+  if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
+    $destination = $_GET['destination'];
+  }
+  else {
+    $destination = 'admin/config/search/apachesolr/settings';
+  }
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => $destination,
+  );
+
+  return $form;
+}
+
+/**
+ * Submit handler for the test button in the environment edit page
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_environment_edit_test_submit(array $form, array &$form_state) {
+  $ping = apachesolr_server_status($form_state['values']['url'], $form_state['values']['service_class']);
+  if ($ping) {
+    drupal_set_message(t('Your site has contacted the Apache Solr server.'));
+  }
+  else {
+    drupal_set_message(t('Your site was unable to contact the Apache Solr server.'), 'error');
+  }
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Validate handler for the environment edit page
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_environment_edit_validate(array $form, array &$form_state) {
+  $parts = parse_url($form_state['values']['url']);
+  foreach (array('scheme', 'host', 'path') as $key) {
+    if (empty($parts[$key])) {
+      form_set_error('url', t('The Solr server URL needs to include a !part', array('!part' => $key)));
+    }
+  }
+  if (isset($parts['port'])) {
+    // parse_url() should always give an integer for port. Since drupal_http_request()
+    // also uses parse_url(), we don't need to validate anything except the range.
+    $pattern = empty($parts['user']) ? '@://[^:]+:([^/]+)@' : '#://[^@]+@[^:]+:([^/]+)#';
+    preg_match($pattern, $form_state['values']['url'], $m);
+    if (empty($m[1]) || !ctype_digit($m[1]) || $m[1] < 1 || $m[1] > 65535) {
+      form_set_error('port', t('The port has to be an integer between 1 and 65535.'));
+    }
+    else {
+      // Normalize the url by removing extra slashes and whitespace.
+      $form_state['values']['url'] = trim($form_state['values']['url'], "/ \t\r\n\0\x0B");
+    }
+  }
+}
+
+/**
+ * Submit handler for the environment  edit page
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_environment_edit_submit(array $form, array &$form_state) {
+  apachesolr_environment_save($form_state['values']);
+  if (!empty($form_state['values']['make_default'])) {
+    apachesolr_set_default_environment($form_state['values']['env_id']);
+  }
+  cache_clear_all('apachesolr:environments', 'cache_apachesolr');
+  drupal_set_message(t('The %name search environment has been saved.', array('%name' => $form_state['values']['name'])));
+  if ($form_state['values']['op'] == t('Save')) {
+    $form_state['redirect'] = 'admin/config/search/apachesolr/settings';
+  }
+  else {
+    $form_state['redirect'] = current_path();
+  }
+  // Regardlessly of the destination parameter we want to go to another page
+  unset($_GET['destination']);
+  drupal_static_reset('drupal_get_destination');
+  drupal_get_destination();
+}
+
+/**
+ * Check to see if the facetapi module is installed, and if not put up
+ * a message.
+ *
+ * Only call this function if the user is already in a position for this to
+ * be useful.
+ */
+function apachesolr_check_facetapi() {
+  if (!module_exists('facetapi')) {
+    $filename = db_query_range("SELECT filename FROM {system} WHERE type = 'module' AND name = 'facetapi'", 0, 1)
+      ->fetchField();
+    if ($filename && file_exists($filename)) {
+      drupal_set_message(t('If you <a href="@modules">enable the facetapi module</a>, Apache Solr Search will provide you with configurable facets.', array('@modules' => url('admin/modules'))));
+    }
+    else {
+      drupal_set_message(t('If you install the facetapi module from !href, Apache Solr Search will provide you with configurable facets.', array('!href' => url('http://drupal.org/project/facetapi'))));
+    }
+  }
+}
+
+/**
+ * Form builder for general settings used as a menu callback.
+ *
+ * @param array $form
+ * @param array $form_state
+ *
+ * @return array Output of the system_settings_form()
+ */
+function apachesolr_settings(array $form, array &$form_state) {
+  $form = array();
+  $rows = array();
+
+  // Environment settings
+  $id = apachesolr_default_environment();
+  $environments = apachesolr_load_all_environments();
+  $default_environment = apachesolr_default_environment();
+  apachesolr_check_facetapi();
+
+  // Reserve a row for the default one
+  $rows[$default_environment] = array();
+
+  foreach ($environments as $environment_id => $data) {
+    // Define all the Operations
+    $confs = array();
+    $ops = array();
+    // Whenever facetapi is enabled we also enable our operation link
+    if (module_exists('facetapi')) {
+      $confs['facets'] = array(
+        'class' => 'operation',
+        'data' => l(t('Facets'),
+          'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/facets',
+          array('query' => array('destination' => current_path()))
+        ),
+      );
+    }
+    // These are our result and bias settings
+    if (module_exists('apachesolr_search')) {
+      $confs['result_bias'] = array(
+        'class' => 'operation',
+        'data' => l(t('Bias'),
+          'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/bias',
+          array('query' => array('destination' => current_path()))
+        ),
+      );
+    }
+    $confs['index'] = array(
+      'class' => 'operation',
+      'data' => l(t('Index'),
+        'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/index'
+      ),
+    );
+    $ops['edit'] = array(
+      'class' => 'operation',
+      'data' => l(t('Edit'),
+        'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/edit',
+        array('query' => array('destination' => current_path()))
+      ),
+    );
+
+    $ops['clone'] = array(
+      'class' => 'operation',
+      'data' => l(t('Clone'),
+        'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/clone',
+        array('query' => array('destination' => $_GET['q']))
+      ),
+    );
+    $env_name = l($data['name'], 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/edit', array('query' => array('destination' => $_GET['q'])));
+
+    // Is this row our default environment?
+    if ($environment_id == $default_environment) {
+      $env_name = t('!environment <em>(Default)</em>', array('!environment' => $env_name));
+      $env_class_row = 'default-environment';
+    }
+    else {
+      $env_class_row = '';
+    }
+    // For every non-default we add a delete link
+    // Allow to revert a search environment or to delete it
+    $delete_value = '';
+    if (!isset($data['in_code_only'])) {
+      if ((isset($data['type']) && $data['type'] == 'Overridden')) {
+        $delete_value = array(
+          'class' => 'operation',
+          'data' => l(t('Revert'), 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/delete'),
+        );
+      }
+      // don't allow the deletion of the default environment
+      elseif ($environment_id != $default_environment) {
+        $delete_value = array(
+          'class' => 'operation',
+          'data' => l(t('Delete'), 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/delete'),
+        );
+      }
+    }
+    $ops['delete'] = $delete_value;
+
+    // When we are receiving a http POST (so the page does not show) we do not
+    // want to check the statusses of any environment
+    $class = '';
+    if (empty($form_state['input'])) {
+      $class = apachesolr_server_status($data['url'], $data['service_class']) ? 'ok' : 'error';
+    }
+
+    $headers = array(
+      array('data' => t('Name'), 'colspan' => 2),
+      t('URL'),
+      array('data' => t('Configuration'), 'colspan' => count($confs)),
+      array('data' => t('Operations'), 'colspan' => count($ops)),
+    );
+
+    $rows[$environment_id] = array('data' =>
+      array(
+        // Cells
+        array(
+          'class' => 'status-icon',
+          'data' => '<div title="' . $class . '"><span class="element-invisible">' . $class . '</span></div>',
+        ),
+        array(
+          'class' => $env_class_row,
+          'data' => $env_name,
+        ),
+        check_plain($data['url']),
+      ),
+      'class' => array(drupal_html_class($class)),
+    );
+    // Add the links to the page
+    $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $confs);
+    $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $ops);
+  }
+
+  $form['apachesolr_host_settings']['actions'] = array(
+    '#markup' => '<ul class="action-links">' . drupal_render($actions) . '</ul>',
+  );
+  $form['apachesolr_host_settings']['table'] = array(
+    '#theme' => 'table',
+    '#header' => $headers,
+    '#rows' => array_values($rows),
+    '#attributes' => array('class' => array('admin-apachesolr')),
+  );
+
+  $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'),
+    '#description' => t('Adds notices to a page whenever Drupal changed content that needs reindexing'),
+    '#default_value' => variable_get('apachesolr_set_nodeapi_messages', 1),
+    '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
+  );
+
+  // Number of Items to index
+  $numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
+  $default_cron_limit = variable_get('apachesolr_cron_limit', 50);
+
+  // apachesolr_cron_limit may be overridden in settings.php. If its current
+  // value is not among the default set of options, add it.
+  if (!isset($numbers[$default_cron_limit])) {
+    $numbers[$default_cron_limit] = $default_cron_limit;
+  }
+  $form['advanced']['apachesolr_cron_limit'] = array(
+    '#type' => 'select',
+    '#title' => t('Number of items to index per cron run'),
+    '#default_value' => $default_cron_limit,
+    '#options' => $numbers,
+    '#description' => t('Reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status')))
+  );
+
+  $options = array('apachesolr:show_error' => t('Show error message'));
+  $system_info = system_get_info('module');
+  if (module_exists('search')) {
+    foreach (search_get_info() as $module => $search_info) {
+      // Don't allow apachesolr to return results on failure of apachesolr.
+      if ($module == 'apachesolr_search') {
+        continue;
+      }
+      $options[$module] = t('Show @name search results', array('@name' => $system_info[$module]['name']));
+    }
+  }
+
+  $options['apachesolr:show_no_results'] = t('Show no results');
+  $form['advanced']['apachesolr_failure'] = array(
+    '#type' => 'select',
+    '#title' => t('On failure'),
+    '#options' => $options,
+    '#default_value' => variable_get('apachesolr_failure', 'apachesolr:show_error'),
+  );
+
+  return system_settings_form($form);
+}
+
+/**
+ * Gets information about the fields already in solr index.
+ *
+ * @param array $environment
+ *   The environment for which we need to ask the status from
+ *
+ * @return array page render array
+ */
+function apachesolr_status_page($environment = array()) {
+  if (empty($environment)) {
+    $env_id = apachesolr_default_environment();
+    $environment = apachesolr_environment_load($env_id);
+  }
+  else {
+    $env_id = $environment['env_id'];
+  }
+
+  // Check for availability
+  if (!apachesolr_server_status($environment['url'], $environment['service_class'])) {
+    drupal_set_message(t('The server seems to be unavailable. Please verify the server settings at the <a href="!settings_page">settings page</a>', array('!settings_page' => url("admin/config/search/apachesolr/settings/{$environment['env_id']}/edit", array('query' =>  drupal_get_destination())))), 'warning');
+    return '';
+  }
+
+  try {
+    $solr = apachesolr_get_solr($environment["env_id"]);
+    $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 = new stdClass;
+    $data->fields = array();
+  }
+
+  $messages = array();
+  if (isset($data->index->numDocs)) {
+    try {
+      // Collect the stats
+      $stats_summary = $solr->getStatsSummary();
+      module_load_include('inc', 'apachesolr', 'apachesolr.index');
+      $status = apachesolr_index_status($environment["env_id"]);
+      // We need a schema version greater than beta3. This is mostly to catch
+      // people using the Drupal 6 schema.
+      if (preg_match('/^drupal-[13]/', $stats_summary['@schema_version'])) {
+        $minimum = 'drupal-3.0-beta4';
+        if (version_compare($stats_summary['@schema_version'], $minimum, '<')) {
+          drupal_set_message(t('Your schema.xml version is too old. You must update it to at least %minimum and re-index your content.', array('%minimum' => $minimum)), 'error');
+        }
+      }
+      $pending_msg = $stats_summary['@pending_docs'] ? t('(@pending_docs sent but not yet processed)', $stats_summary) : '';
+      $index_msg = $stats_summary['@index_size'] ? t('(@index_size on disk)', $stats_summary) : '';
+      $indexed_message = t('@num Items !pending !index_msg', array(
+        '@num' => $data->index->numDocs,
+        '!pending' => $pending_msg,
+        '!index_msg' => $index_msg,
+      ));
+      $messages[] = array(t('Indexed'), $indexed_message);
+
+      $remaining_message = t('@items (@percentage% has been sent to the server)', array(
+          '@items' => format_plural($status['remaining'], t('1 item'), t('@count items')),
+          '@percentage' => ((int)min(100, 100 * ($status['total'] - $status['remaining']) / max(1, $status['total']))),
+        )
+      );
+      $messages[] = array(t('Remaining'), $remaining_message);
+
+      $messages[] = array(t('Schema'), t('@schema_version', $stats_summary));
+      if (!empty($stats_summary['@core_name'])) {
+        $messages[] = array(t('Solr Core Name'), t('@core_name', $stats_summary));
+      }
+      $messages[] = array(t('Delay'), t('@autocommit_time before updates are processed.', $stats_summary));
+      $messages[] = array(t('Pending Deletions'), t('@deletes_total', $stats_summary));
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    }
+  }
+  if (empty($messages)) {
+    $messages[] = array(t('Error'), t('No data was returned from the server. Check your log messages.'));
+  }
+  // Initializes output with information about which server's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $output['apachesolr_index_action_status'] = array(
+    '#prefix' => '<h3>' . t('@environment: Search Index Content', array('@environment' => $environment['name'])) . '</h3>',
+    '#theme' => 'table',
+    '#header' => array(t('Type'), t('Value')),
+    '#rows' => $messages,
+  );
+
+  $output['viewmore'] = array(
+    '#markup' => l(t('View more details on the search index contents'), 'admin/reports/apachesolr'),
+  );
+
+  $write_status = apachesolr_environment_variable_get($env_id, 'apachesolr_read_only', APACHESOLR_READ_WRITE);
+  if ($write_status == APACHESOLR_READ_WRITE) {
+     $output['index_action_form'] = drupal_get_form('apachesolr_index_action_form', $env_id);
+     $output['index_config_form'] = drupal_get_form('apachesolr_index_config_form', $env_id);
+  }
+  else {
+    drupal_set_message(t('Options for deleting and re-indexing are not available because the index is read-only. This can be changed on the <a href="!settings_page">settings page</a>', array('!settings_page' => url('admin/config/search/apachesolr/settings/' . $env_id . '/edit', array('query' =>  drupal_get_destination())))), 'warning');
+  }
+
+  return $output;
+}
+
+/**
+ * Get the report, eg.: some statistics and useful data from the Apache Solr index
+ *
+ * @param array $environment
+ *
+ * @return array page render array
+ */
+function apachesolr_index_report(array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = apachesolr_default_environment();
+    drupal_goto('admin/reports/apachesolr/' . $env_id);
+  }
+  $environments = apachesolr_load_all_environments();
+  $environments_list = array();
+  foreach ($environments as $env) {
+    $var_status = array('!name' =>$env['name']);
+    $environments_list[] = l(t('Statistics for !name', $var_status), 'admin/reports/apachesolr/' . $env['env_id']);
+  }
+  $output['environments_list'] = array(
+    '#theme' => 'item_list',
+    '#items' => $environments_list,
+  );
+
+  try {
+    $solr = apachesolr_get_solr($environment['env_id']);
+    $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");
+    return $output;
+  }
+
+  $messages = array();
+  $messages[] = array(t('Number of documents in index'), $data->index->numDocs);
+
+  $limit = variable_get('apachesolr_luke_limit', 20000);
+  if (isset($data->index->numDocs) && $data->index->numDocs > $limit) {
+    $messages[] = array(t('Limit'), t('You have more than @limit documents, so term frequencies are being omitted for performance reasons.', array('@limit' => $limit)));
+    $not_found = t('<em>Omitted</em>');
+  }
+  elseif (isset($data->index->numDocs)) {
+    $not_found = t('Not indexed');
+    try {
+      $solr = apachesolr_get_solr($environment['env_id']);
+      // Note: we use 2 since 1 fails on Ubuntu Hardy.
+      $data = $solr->getLuke(2);
+      if (isset($data->index->numTerms)) {
+        $messages[] = array(t('# of terms in index'), $data->index->numTerms);
+      }
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      $data->fields = array();
+    }
+  }
+  // Initializes output with information about which server's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $fields = (array)$data->fields;
+  if ($fields) {
+    $messages[] = array(t('# of fields in index'), count($fields));
+  }
+
+  // Output the messages we have for this page
+  $output['apachesolr_index_report'] = array(
+    '#theme' => 'table',
+    '#header' => array('type', 'value'),
+    '#rows' => $messages,
+  );
+
+  if ($fields) {
+    // Initializes table header.
+    $header = array(
+      'name' => t('Field name'),
+      'type' => t('Index type'),
+      'terms' => t('Distinct terms'),
+    );
+
+    // Builds table rows.
+    $rows = array();
+    foreach ($fields as $name => $field) {
+      // TODO: try to map the name to something more meaningful.
+      $rows[$name] = array(
+        'name' => $name,
+        'type' => $field->type,
+        'terms' => isset($field->distinct) ? $field->distinct : $not_found
+      );
+    }
+    ksort($rows);
+    // Output the fields we found for this environment
+    $output['field_table'] = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+    );
+  }
+  else {
+    $output['field_table'] = array('#markup' => t('No data on indexed fields.'));
+  }
+  return $output;
+}
+
+/**
+ * Page callback to show available conf files.
+ *
+ * @param array $environment
+ *
+ * @return string
+ *   A non-render array but plain theme output for the config files overview. Could be done better probably
+ */
+function apachesolr_config_files_overview(array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = apachesolr_default_environment();
+  }
+  else {
+    $env_id = $environment['env_id'];
+  }
+
+  $xml = NULL;
+  try {
+    $solr = apachesolr_get_solr($env_id);
+    $response = $solr->makeServletRequest('admin/file', array('wt' => 'xml'));
+    $xml = simplexml_load_string($response->data);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
+  }
+
+  if ($xml) {
+    // Retrieve our items from the xml using xpath
+    $items = $xml->xpath('//lst[@name="files"]/lst');
+
+    // Add all the data of the file in a files array
+    $files = array();
+    foreach ($items as $item_id => $item) {
+      // Do not list directories. Always a bool
+      if (isset($item->bool)) {
+        break;
+      }
+      // Get data from the files.
+      $name =  $item->attributes();
+      $name = ((string)$item->attributes()) ? (string)$item->attributes() : t('No name found');
+      $files[$item_id]['name'] = l($name, 'admin/reports/apachesolr/' . $env_id . '/conf/' . $name);
+
+      // Retrieve the date attribute
+      if (isset($item->date)) {
+        $modified = ((string)$item->date->attributes() == 'modified') ? (string) $item->date : t('No date found');
+        $files[$item_id]['modified'] = format_date(strtotime($modified));
+      }
+
+      // Retrieve the size attribute
+      if (isset($item->long)) {
+        $size = ((string)$item->long->attributes() == 'size') ? (string) $item->long : t('No size found');
+        $files[$item_id]['size'] = t('Size (bytes): @bytes', array('@bytes' => $size));
+      }
+    }
+    // Sort our files alphabetically
+    ksort($files);
+
+    // Initializes table header.
+    $header = array(
+      'name' => t('File name'),
+      'date' => t('Modified'),
+      'size' => t('Size'),
+    );
+
+    // Display the table of field names, index types, and term counts.
+    $variables = array(
+      'header' => $header,
+      'rows' => $files,
+    );
+    $output = theme('table', $variables);
+  }
+  else {
+    $output = '<p>' . t('No data about any file found.') . "</p>\n";
+  }
+  return $output;
+}
+
+/**
+ * Page callback to show one conf file.
+ *
+ * @param string $name
+ * @param array $environment
+ *
+ * @return string
+ *   the requested config file
+ */
+function apachesolr_config_file($name, array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = apachesolr_default_environment();
+  }
+  else {
+    $env_id = $environment['env_id'];
+  }
+
+  $output = '';
+  try {
+    $solr = apachesolr_get_solr($env_id);
+    $response = $solr->makeServletRequest('admin/file', array('file' => $name));
+    $raw_file = $response->data;
+    $output = '<pre>' . check_plain($raw_file) . '</pre>';
+    drupal_set_title(check_plain($name));
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
+  }
+  return $output;
+}
+
+/**
+ * Form builder for the Apachesolr Indexer actions form.
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @see apachesolr_index_action_form_delete_submit().
+ *
+ * @return array $form
+ */
+function apachesolr_index_action_form(array $form, array $form_state, $env_id) {
+  $form = array();
+  $form['action'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Actions'),
+    '#collapsible' => TRUE,
+  );
+
+  $form['action']['env_id'] = array(
+    '#type' => 'value',
+    '#value' => $env_id,
+  );
+
+  $form['action']['cron'] = array(
+    '#prefix' => '<div>',
+    '#type' => 'submit',
+    '#value' => t('Index queued content (!amount)', array('!amount' => variable_get('apachesolr_cron_limit', 50))),
+    '#submit' => array('apachesolr_index_action_form_cron_submit'),
+  );
+  $form['action']['cron_description'] = array(
+    '#prefix' => '<span>',
+    '#suffix' => '</span></div>',
+    '#markup' => t('Indexes just as many items as 1 cron run would do.'),
+  );
+
+  $form['action']['remaining'] = array(
+    '#prefix' => '<div>',
+    '#type' => 'submit',
+    '#value' => t('Index all queued content'),
+    '#submit' => array('apachesolr_index_action_form_remaining_submit'),
+  );
+  $form['action']['remaining_description'] = array(
+    '#prefix' => '<span>',
+    '#suffix' => '</span></div>',
+    '#markup' => t('Could take time and could put an increased load on your server.'),
+  );
+
+  $form['action']['reset'] = array(
+    '#prefix' => '<div>',
+    '#suffix' => '</div>',
+    '#type' => 'submit',
+    '#value' => t('Queue all content for reindexing'),
+    '#submit' => array('apachesolr_index_action_form_reset_submit'),
+  );
+  $form['action']['delete'] = array(
+    '#prefix' => '<div>',
+    '#type' => 'submit',
+    '#value' => t('Delete the Search & Solr index'),
+    '#submit' => array('apachesolr_index_action_form_delete_submit'),
+  );
+  $form['action']['delete_description'] = array(
+    '#prefix' => '<span>',
+    '#suffix' => '</span></div>',
+    '#markup' => t('Useful with a corrupt index or a new schema.xml.'),
+  );
+
+  return $form;
+}
+
+/**
+ * Submit handler for the Indexer actions form, delete button.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_remaining_submit(array $form, array &$form_state) {
+  $destination = array();
+  if (isset($_GET['destination'])) {
+    $destination = drupal_get_destination();
+    unset($_GET['destination']);
+  }
+  $env_id = $form_state['values']['env_id'];
+  $form_state['redirect'] = array('admin/config/search/apachesolr/settings/' . $env_id . '/index/remaining', array('query' => $destination));
+}
+
+/**
+ * Submit handler for the Indexer actions form, delete button.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_delete_submit(array $form, array &$form_state) {
+  $destination = array();
+  if (isset($_GET['destination'])) {
+    $destination = drupal_get_destination();
+    unset($_GET['destination']);
+  }
+  $env_id = $form_state['values']['env_id'];
+  $form_state['redirect'] = array('admin/config/search/apachesolr/settings/' . $env_id . '/index/delete', array('query' => $destination));
+}
+
+/**
+ * Submit handler for the Indexer actions form, delete button.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_reset_submit(array $form, array &$form_state) {
+  $destination = array();
+  if (isset($_GET['destination'])) {
+    $destination = drupal_get_destination();
+    unset($_GET['destination']);
+  }
+  $env_id = $form_state['values']['env_id'];
+  $form_state['redirect'] = array('admin/config/search/apachesolr/settings/' . $env_id . '/index/reset', array('query' => $destination));
+}
+
+/**
+ * Submit handler for the deletion form.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_cron_submit(array $form, array &$form_state) {
+  if (!empty($form_state['build_info']['args'][0])) {
+    $env_id = $form_state['build_info']['args'][0];
+    $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
+  }
+  else {
+    $env_id = apachesolr_default_environment();
+    $form_state['redirect'] = 'admin/config/search/apachesolr';
+  }
+  apachesolr_cron($env_id);
+  drupal_set_message(t('Apachesolr cron succesfully executed'));
+}
+
+/**
+ * Form builder for to reindex the remaining items left in the queue.
+ *
+ * @see apachesolr_index_action_form_delete_confirm_submit().
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return mixed
+ */
+function apachesolr_index_action_form_remaining_confirm(array $form, array &$form_state, array $environment) {
+  return confirm_form($form,
+    t('Are you sure you want index all remaining content?'),
+    'admin/config/search/apachesolr/settings/' . $environment['env_id'] . '/index',
+    NULL,
+    t('Index all remaining')
+  );
+}
+
+/**
+ * Submit handler for the deletion form.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_remaining_confirm_submit(array $form, array &$form_state) {
+  if (!empty($form_state['build_info']['args'][0]['env_id'])) {
+    $env_id = $form_state['build_info']['args'][0]['env_id'];
+    $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
+  }
+  else {
+    $env_id = apachesolr_default_environment();
+    $form_state['redirect'] = 'admin/config/search/apachesolr';
+  }
+  apachesolr_index_batch_index_remaining($env_id);
+}
+
+/**
+ * Form builder for the index re-enqueue form.
+ *
+ * @see apachesolr_index_action_form_reset_confirm_submit().
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return mixed
+ */
+function apachesolr_index_action_form_reset_confirm(array $form, array &$form_state, array $environment) {
+  return confirm_form($form,
+    t('Are you sure you want to queue content for reindexing?'),
+    'admin/config/search/apachesolr/settings/' . $environment['env_id'] . '/index',
+    t('All content on the site will be queued for indexing. The documents currently in the Solr index will remain searchable.'),
+    t('Queue all content')
+  );
+}
+
+/**
+ * Submit handler for the deletion form.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_reset_confirm_submit(array $form, array &$form_state) {
+  if (!empty($form_state['build_info']['args'][0]['env_id'])) {
+    $env_id = $form_state['build_info']['args'][0]['env_id'];
+    $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
+  }
+  else {
+    $env_id = apachesolr_default_environment();
+    $form_state['redirect'] = 'admin/config/search/apachesolr';
+  }
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  apachesolr_index_mark_for_reindex($env_id);
+  drupal_set_message(t('All the content on your site is queued for indexing. You can wait for it to be indexed during cron runs, or you can manually reindex it.'));
+}
+
+/**
+ * Form builder for the index delete/clear form.
+ *
+ * @see apachesolr_index_action_form_delete_confirm_submit().
+
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return array output of confirm_form()
+ */
+function apachesolr_index_action_form_delete_confirm(array $form, array &$form_state, array $environment) {
+  return confirm_form($form,
+    t('Are you sure you want to clear your index?'),
+    'admin/config/search/apachesolr/settings/' . $environment['env_id'] . '/index',
+    t('This will remove all data from your index and all search results will be incomplete until your site is reindexed.'),
+    t('Delete index')
+  );
+}
+
+/**
+ * Submit handler for the deletion form.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_action_form_delete_confirm_submit(array $form, array &$form_state) {
+  if (!empty($form_state['build_info']['args'][0]['env_id'])) {
+    $env_id = $form_state['build_info']['args'][0]['env_id'];
+    $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
+  }
+  else {
+    $env_id = apachesolr_default_environment();
+    $form_state['redirect'] = 'admin/config/search/apachesolr';
+  }
+  // Rebuild our tracking table.
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  apachesolr_index_delete_index($env_id);
+  drupal_set_message(t('The index has been deleted.'));
+}
+
+/**
+ * Submit a batch job to index the remaining, non-indexed content.
+ *
+ * @param string $env_id
+ *   The environment ID where it needs to index the remaining items for
+ */
+function apachesolr_index_batch_index_remaining($env_id, $total_limit = null) {
+  $batch = array(
+    'operations' => array(
+      array(
+        'apachesolr_index_batch_index_entities',
+        array(
+          $env_id,
+          $total_limit,
+        ),
+      ),
+    ),
+    'finished' => 'apachesolr_index_batch_index_finished',
+    'title' => t('Indexing'),
+    'init_message' => t('Preparing to submit content to Solr for indexing...'),
+    'progress_message' => t('Submitting content to Solr...'),
+    'error_message' => t('Solr indexing has encountered an error.'),
+    'file' => drupal_get_path('module', 'apachesolr') . '/apachesolr.admin.inc',
+  );
+  batch_set($batch);
+}
+
+
+/**
+ * Batch Operation Callback
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param $total_limit
+ *   The total number of items to index across all batches
+ * @param array $context
+ *
+ * @return false
+ *   return false when an exception was caught
+ *
+ * @throws Exception
+ *   When solr gives an error, throw an exception that solr is not available
+ */
+function apachesolr_index_batch_index_entities($env_id, $total_limit = NULL, &$context) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  if (empty($context['sandbox'])) {
+    try {
+      // Get the $solr object
+      $solr = apachesolr_get_solr($env_id);
+      // 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;
+    }
+
+    $status = apachesolr_index_status($env_id);
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['submitted'] = 0;
+
+    // How many items do we want to index? All or a limited set of items
+    if (empty($total_limit)) {
+      $context['sandbox']['max'] = $status['remaining'];
+    }
+    else {
+      $context['sandbox']['max'] = $total_limit;
+    }
+  }
+
+  // We can safely process the apachesolr_cron_limit nodes at a time without a
+  // timeout or out of memory error.
+  $limit = variable_get('apachesolr_cron_limit', 50);
+
+  // Reduce the limit for our final batch if we would be processing more than had been requested
+  if ($limit + $context['sandbox']['progress'] > $context['sandbox']['max']) {
+    $limit = $context['sandbox']['max'] - $context['sandbox']['progress'];
+  }
+
+  if ($context['sandbox']['max'] >= $context['sandbox']['progress'] + $limit) {
+    $context['sandbox']['progress'] += $limit;
+  }
+  else {
+    $context['sandbox']['progress'] = $context['sandbox']['max'];
+  }
+  $context['sandbox']['submitted'] += apachesolr_index_entities($env_id, $limit);
+
+  $arguments = array(
+    '@current' => $context['sandbox']['progress'],
+    '@total' => $context['sandbox']['max'],
+    '@submitted' => $context['sandbox']['submitted'],
+    );
+  $context['message'] = t('Inspected @current of @total entities. Submitted @submitted documents to Solr', $arguments);
+
+  // Inform the batch engine that we are not finished, and provide an
+  // estimation of the completion level we reached.
+  $context['finished'] = empty($context['sandbox']['max']) ? 1 : $context['sandbox']['progress'] / $context['sandbox']['max'];
+
+  // Put the total into the results section when we're finished so we can
+  // show it to the admin.
+  if ($context['finished']) {
+    $context['results']['count'] = $context['sandbox']['progress'];
+    $context['results']['submitted'] = $context['sandbox']['submitted'];
+  }
+}
+
+/**
+ * Batch 'finished' callback
+ *
+ * @param bool $success
+ *   Whether the batch ended with success or not
+ * @param array $results
+ * @param array $operations
+ */
+function apachesolr_index_batch_index_finished($success, array $results, array $operations) {
+  $message = '';
+  // $results['count'] will not be set if Solr is unavailable.
+  if (isset($results['count'])) {
+    $message .= format_plural($results['count'], '1 item processed successfully. ', '@count items successfully processed. ');
+  }
+  if (isset($results['submitted'])) {
+    $message .= format_plural($results['submitted'], '1 document successfully sent to Solr.', '@count documents successfully sent to Solr.');
+  }
+  if ($success) {
+    $type = 'status';
+  }
+  else {
+    // An error occurred. $operations contains the unprocessed operations.
+    $error_operation = reset($operations);
+    $message .= ' ' . t('An error occurred while processing @num with arguments: @args', array('@num' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE)));
+    $type = 'error';
+  }
+  drupal_set_message($message, $type);
+}
+
+/**
+ * Form builder for the bundle configuration form.
+ *
+ * @see apachesolr_index_config_form_submit().
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param string $env_id
+ *   The machine name of the environment.
+ *
+ * @return array $form
+ */
+function apachesolr_index_config_form(array $form, array $form_state, $env_id) {
+  $form['config'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Configuration'),
+    '#collapsible' => TRUE,
+  );
+
+  $form['config']['bundles'] = array(
+    '#type' => 'markup',
+    '#markup' => t('Select the entity types and bundles that should be indexed.'),
+  );
+
+  // For future extensibility, when we have multiple cores.
+  $form['config']['env_id'] = array(
+    '#type' => 'value',
+    '#value' => $env_id,
+  );
+
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    if (!empty($entity_info['apachesolr']['indexable'])) {
+      $options = array();
+      foreach ($entity_info['bundles'] as $key => $info) {
+        $options[$key] = $info['label'];
+      }
+
+      $form['config']['entities']['#tree'] = TRUE;
+      $form['config']['entities'][$entity_type] = array(
+        '#type' => 'checkboxes',
+        '#title' => check_plain($entity_info['label']),
+        '#options' => $options,
+        '#default_value' => apachesolr_get_index_bundles($env_id, $entity_type),
+      );
+    }
+  }
+
+  $form['config']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
+
+  return $form;
+}
+
+/**
+ * Submit handler for the bundle configuration form.
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function apachesolr_index_config_form_submit(array $form, array &$form_state) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $form_values = $form_state['values'];
+  $env_id = $form_values['env_id'];
+
+  foreach ($form_values['entities'] as $entity_type => $bundles) {
+    $existing_bundles = apachesolr_get_index_bundles($env_id, $entity_type);
+    $all_bundles = array_keys($bundles);
+    $new_bundles = array_values(array_filter($bundles));
+    apachesolr_index_set_bundles($env_id, $entity_type, $new_bundles);
+
+    // Remove all excluded bundles - this happens on form submit
+    // even if there is no change so the admin can remove
+    // bundles if there was an error.
+    $excluded_bundles = array_diff($all_bundles, $new_bundles);
+    if (apachesolr_index_delete_bundles($env_id, $entity_type, $excluded_bundles)) {
+      $callback = apachesolr_entity_get_callback($entity_type, 'bundles changed callback');
+      if (!empty($callback)) {
+        call_user_func($callback, $env_id, $existing_bundles, $new_bundles);
+      }
+    }
+    else {
+      drupal_set_message(t('Search is temporarily unavailable. If the problem persists, please contact the site administrator.'), 'error');
+    }
+  }
+
+  // Clear the entity cache, since we will be changing its data.
+  entity_info_cache_clear();
+  cache_clear_all('apachesolr:environments', 'cache_apachesolr');
+  drupal_set_message(t('Your settings have been saved.'));
+}
+
+/**
+ * Page callback for node/%node/devel/apachesolr.
+ *
+ * @param object $node
+ * @return string debugging information
+ */
+function apachesolr_devel($node) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $item = new stdClass();
+  $item->entity_type = 'node';
+  $item->entity_id = $node->nid;
+  $output = '';
+  foreach (apachesolr_load_all_environments() as $env_id => $environment) {
+    $documents = apachesolr_index_entity_to_documents($item, $env_id);
+    $output .= '<h1>' . t('Environment %name (%env_id)', array('%name' => $environment['name'], '%env_id' => $env_id)). '</h1>';
+    foreach ($documents as $document) {
+      $debug_data = array();
+      foreach ($document as $key => $value) {
+        $debug_data[$key] = $value;
+      }
+      $output .= kdevel_print_object($debug_data);
+    }
+  }
+  return $output;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/facetapi.callbacks.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * @file
+ * Callbacks referenced in hook implementations.
+ */
+
+/**
+ * Maps bundle machine names to their human friendly label.
+ *
+ * @param array $values
+ *   An array of indexed values being mapped.
+ * @param array $options
+ *   An associative array of map options containing:
+ *   - entities: An array of entities that $values are bundles for.
+ *
+ * @return array
+ *   An array mapping the indexed values to human readable values.
+ */
+function facetapi_map_bundle(array $values, array $options) {
+  if (empty($options['entities'])) {
+    $options['entities'] = array('node');
+  }
+  foreach ($options['entities'] as $entity_type) {
+    if ($info = entity_get_info($entity_type)) {
+      foreach ($info['bundles'] as $bundle_name => $bundle_info) {
+        $names[$bundle_name] = $bundle_info['label'];
+      }
+    }
+  }
+  return array_intersect_key($names, array_flip($values));
+}
+
+/**
+ * Map callback for node authors.
+ */
+function facetapi_map_author(array $values) {
+  $map = array();
+  $users = user_load_multiple($values);
+  foreach ($users as $user) {
+    $map[$user->uid] = format_username($user);
+  }
+  if (isset($map[0])) {
+    $map[0] = variable_get('anonymous', t('Anonymous'));
+  }
+  return $map;
+}
+
+/**
+ * Map callback for languages.
+ */
+function facetapi_map_language(array $values) {
+  $map = array();
+  $language_list = language_list();
+  foreach ($values as $language) {
+    if (isset($language_list[$language])) {
+      $map[$language] = t($language_list[$language]->name);
+    }
+    else {
+      $map[$language] = t('Language neutral');
+    }
+  }
+  return $map;
+}
+
+/**
+ * Maps date ranges to human readable dates.
+ *
+ * @param array $values
+ *   An array of indexed values being mapped.
+ * @param array $options
+ *   An associative array of map options containing:
+ *   - format callback: The callback used to format the date, defaults to
+ *     "facetapi_format_timestamp".
+ *
+ * @return array
+ *   An array mapping the indexed values to human readable values.
+ */
+function facetapi_map_date(array $values, array $options) {
+  if (empty($options['format callback'])) {
+    $options['format callback'] = 'facetapi_format_timestamp';
+  }
+  $map = array();
+  foreach ($values as $value) {
+    $range = explode(' TO ', trim($value, '{[]}'));
+    if (isset($range[1])) {
+      $gap = facetapi_get_date_gap($range[0], $range[1]);
+      $map[$value] = facetapi_format_date($range[0], $gap, $options['format callback']);
+    }
+  }
+  return $map;
+}
+
+/**
+ * Callback that returns the minimum date in the node table.
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *   The minimum time in the node table.
+ *
+ * @todo Cache this value.
+ */
+function facetapi_get_min_date(array $facet) {
+  $query = db_select('node', 'n')->condition('status', 1);
+  $query->addExpression('MIN(' . $facet['name'] . ')', 'max');
+  return $query->execute()->fetch()->max;
+}
+
+/**
+ * Callback that returns the minimum value in the node table.
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *   The minimum time in the node table.
+ *
+ * @todo Cache this value.
+ */
+function facetapi_get_max_date(array $facet) {
+  $query = db_select('node', 'n')->condition('status', 1);
+  $query->addExpression('MAX(' . $facet['name'] . ')', 'max');
+  return $query->execute()->fetch()->max;
+}
+
+/**
+ * Map callback for taxonomy terms.
+ */
+function facetapi_map_taxonomy_terms(array $values) {
+  $map = array();
+  $terms = taxonomy_term_load_multiple($values);
+  foreach ($terms as $term) {
+    $map[$term->tid] = entity_label('taxonomy_term', $term);
+  }
+  return $map;
+}
+
+/**
+ * Gets parent information for taxonomy terms.
+ *
+ * @param array $values
+ *   An array containing the term ids.
+ *
+ * @return
+ *   An associative array keyed by term ID to parent ID.
+ */
+function facetapi_get_taxonomy_hierarchy(array $values) {
+  $result = db_select('taxonomy_term_hierarchy', 'th')
+    ->fields('th', array('tid', 'parent'))
+    ->condition('th.parent', '0', '>')
+    ->condition(db_or()
+      ->condition('th.tid', $values, 'IN')
+      ->condition('th.parent', $values, 'IN')
+    )
+    ->execute();
+
+  $parents = array();
+  foreach ($result as $record) {
+    $parents[$record->tid][] = $record->parent;
+  }
+  return $parents;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/facetapi/adapter.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,217 @@
+<?php
+
+/**
+ * @file
+ * Classes used by the Facet API module.
+ */
+
+/**
+ * Facet API adapter for the Apache Solr Search Integration module.
+ */
+class solrsearchFacetapiAdapter extends FacetapiAdapter {
+
+  /**
+   * Returns the path to the admin settings for a given realm.
+   *
+   * @param string $realm_name
+   *   The name of the realm.
+   *
+   * @return string
+   *   The path to the admin settings.
+   */
+  public function getPath($realm_name) {
+    $path = 'admin/config/search/solrsearch/settings';
+    // $adapter will be an instance of class FacetapiAdapter
+    if ($adapter = menu_get_object('facetapi_adapter', 4)) {
+      // Get the environment ID from the machine name of the searcher.
+      $env_id = ltrim(strstr($adapter->getSearcher(), '@'), '@');
+      $path .= '/' . $env_id . '/facets';
+      // Add the realm name to the path if it is not the first one in the list.
+      if (key(facetapi_get_realm_info()) != $realm_name) {
+        $path .= '/' . $realm_name;
+      }
+    }
+    return $path;
+  }
+
+  /**
+   * Allows the backend to initialize its query object before adding the facet
+   * filters.
+   *
+   * @param mixed $query
+   *   The backend's native object.
+   */
+  function initActiveFilters($query) {
+    $enabled_facets = facetapi_get_enabled_facets($this->info['name']);
+    if ($enabled_facets) {
+      $query->addParam('facet', 'true');
+      $query->addParam('facet.sort', 'count');
+      $query->addParam('facet.mincount', '1');
+    }
+  }
+
+  /**
+   * Returns a boolean flagging whether $this->_searcher executed a search.
+   */
+  public function searchExecuted() {
+    // Initial check - has ANY solr query run in our environment.
+    $env_id = $this->info['instance'];
+    $this_has_searched = solrsearch_has_searched($env_id);
+    // Secondary check - do we have results for this searcher?
+    $this_has_searched = $this_has_searched && solrsearch_static_response_cache($this->getSearcher());
+    return $this_has_searched;
+  }
+
+  /**
+   * Suppress output of the realm
+   *
+   * @param string $realm_name
+   *
+   * @return bool $flag
+   *   Returns if it was suppressed or not
+   */
+  public function suppressOutput($realm_name) {
+    $flag = FALSE;
+    //dpm("suppressoutput");
+    if ($realm_name == 'block') {
+      //dpm($this->info);
+      $env_id = $this->info['instance'];
+      $flag = solrsearch_suppress_blocks($env_id);
+      //dpm($env_id);
+      //dpm($flag);
+
+    }
+    return FALSE;
+    return $flag || !$this->searchExecuted();  }
+
+  /**
+   * Returns the search keys.
+   *
+   * @return string
+   */
+  public function getSearchKeys() {
+    if (NULL === $this->keys) {
+      $env_id = $this->info['instance'];
+      if ($query = solrsearch_current_query($env_id)) {
+        return $query->getParam('q');
+      }
+    }
+    else {
+      return $this->keys;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Returns the search path.
+   *
+   * @return string
+   *   A string containing the search path.
+   *
+   * @todo D8 should provide an API function for this.
+   */
+  public function getSearchPath() {
+    $env_id = $this->info['instance'];
+    $query = solrsearch_current_query($env_id);
+    if (!$query || (NULL === $this->searchPath && NULL === $query->getPath())) {
+      if ($path = module_invoke($this->info['module'] . '_search', 'search_info')) {
+        $this->searchPath = 'search/' . $path['path'];
+        if (!isset($_GET['keys']) && ($keys = $this->getSearchKeys())) {
+          $this->searchPath .= '/' . $keys;
+        }
+      }
+    }
+    if (!$query || NULL === $query->getPath()) {
+      return $this->searchPath;
+    }
+    else {
+      return $query->getPath();
+    }
+
+  }
+
+  /**
+   * Returns the number of total results found for the current search.
+   *
+   * @return bool|int
+   *   Number of results or false if no search response was found
+   */
+  public function getResultCount() {
+    $response = solrsearch_static_response_cache($this->getSearcher());
+    if ($response) {
+      return $response->response->numFound;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Allows for backend specific overrides to the settings form.
+   *
+   * @param array $form
+   * @param array $form_state
+   */
+  public function settingsForm(&$form, &$form_state) {
+    $form['#validate'][] = 'solrsearch_facet_form_validate';
+  }
+
+
+  /**
+   * Allows the backend to add facet queries to its native query object.
+   *
+   * This method is called by the implementing module to initialize the facet
+   * display process. The following actions are taken:
+   * - FacetapiAdapter::initActiveFilters() hook is invoked.
+   * - Dependency plugins are instantiated and executed.
+   * - Query type plugins are executed.
+   *
+   * @param mixed $query
+   *   The backend's native query object.
+   *
+   * @todo Should this method be deprecated in favor of one name init()? This
+   *   might make the code more readable in implementing modules.
+   *
+   * @see FacetapiAdapter::initActiveFilters()
+   */
+  function addActiveFilters($query) {
+    module_load_include('inc', 'solrsearch', 'facetapi.callbacks');
+    facetapi_add_active_searcher($this->info['name']);
+
+    // Invoke initActiveFilters hook.
+    $this->initActiveFilters($query);
+
+
+    foreach ($this->getEnabledFacets() as $facet) {
+
+      $settings = $this->getFacet($facet)->getSettings();
+
+      // Instantiate and execute dependency plugins.
+      $display = TRUE;
+      foreach ($facet['dependency plugins'] as $id) {
+        $class = ctools_plugin_load_class('facetapi', 'dependencies', $id, 'handler');
+        $plugin = new $class($id, $this, $facet, $settings, $this->activeItems['facet']);
+        if (NULL !== ($return = $plugin->execute())) {
+          $display = $return;
+        }
+      }
+
+      // Store whether this facet passed its dependencies.
+      $this->dependenciesPassed[$facet['name']] = $display;
+
+      // Execute query type plugin if dependencies were met, otherwise remove
+      // the facet's active items so they don't display in the current search
+      // block or appear as active in the breadcrumb trail.
+      if ($display && $this->queryTypes[$facet['name']]) {
+        $this->queryTypes[$facet['name']]->execute($query);
+      }
+      else {
+        foreach ($this->activeItems['facet'][$facet['name']] as $item) {
+          $this->urlProcessor->removeParam($item['pos']);
+          $filter = $item['field alias'] . ':' . $item['value'];
+          unset($this->activeItems['filter'][$filter]);
+        }
+        $this->activeItems['facet'][$facet['name']] = array();
+      }
+        }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/facetapi/query_type_date.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,185 @@
+<?php
+
+/**
+ * @file
+ * Date query type plugin for the Apache Solr adapter.
+ */
+
+/**
+ * Plugin for "date" query types.
+ */
+class solrsearchFacetapiDate extends FacetapiQueryTypeDate implements FacetapiQueryTypeInterface {
+
+  /**
+   * Returns the query type associated with the plugin.
+   *
+   * @return string
+   *   The query type.
+   */
+  static public function getType() {
+    return 'date';
+  }
+
+  /**
+   * Adds the filter to the query object.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   An object containing the query in the backend's native API.
+   */
+  public function execute($query) {
+    // Gets the data range in formats that Solr understands.
+    $date_range = $this->getDateRange($query);
+    if (empty($date_range)) {
+      return NULL;
+    }
+    list($start, $end, $gap) = $date_range;
+    $query->addParam('facet.date', $this->facet['field']);
+    $query->addParam('f.' . $this->facet['field'] . '.facet.date.start', $start);
+    $query->addParam('f.' . $this->facet['field'] . '.facet.date.end', $end);
+    $query->addParam('f.' . $this->facet['field'] . '.facet.date.gap', $gap);
+
+    // Adds "hard limit" parameter to prevent too many return values.
+    $settings = $this->adapter->getFacet($this->facet)->getSettings();
+    $limit = empty($settings->settings['hard_limit']) ? 20 : (int) $settings->settings['hard_limit'];
+    $query->addParam('f.' . $this->facet['field'] . '.facet.limit', $limit);
+
+    $active = $this->adapter->getActiveItems($this->facet);
+    // Date filters don't support OR operator.
+    foreach ($active as $value => $item) {
+      $query->addFilter($this->facet['field'], $value);
+    }
+  }
+
+  /**
+   * Gets the range of dates we are using.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   A SolrBaseQuery object.
+   *
+   * @return bool|array
+   *   An array containing the gap and range information or false if not present
+   */
+  function getDateRange(DrupalSolrQueryInterface $query) {
+    $return = NULL;
+    $gap = NULL;
+
+    // Attempts to get next gap from passed date filters.
+    foreach ($this->adapter->getActiveItems($this->facet) as $item) {
+      if ($gap = facetapi_get_date_gap($item['start'], $item['end'])) {
+        $next_gap = facetapi_get_next_date_gap($gap, FACETAPI_DATE_SECOND);
+        if ($next_gap == $gap) {
+          $next_gap = NULL;
+          return NULL;
+        }
+        $return = array(
+          "{$item['start']}/$next_gap",
+          "{$item['end']}+1$next_gap/$next_gap",
+          "+1$next_gap",
+        );
+      }
+    }
+
+    // If no filters were passed, get default range.
+    if (NULL === $return) {
+
+      // Builds SQL that gets minimum and maximum values from node table.
+      $minimum = $maximum = FALSE;
+      if ($this->facet['min callback'] && is_callable($this->facet['min callback'])) {
+        $minimum = $this->facet['min callback']($this->facet);
+      }
+      if ($this->facet['max callback'] && is_callable($this->facet['max callback'])) {
+        $maximum = $this->facet['max callback']($this->facet);
+      }
+
+      // Gets the default gap.
+      //$gap = FACETAPI_DATE_YEAR;
+      if ($minimum && $maximum) {
+        $gap = facetapi_get_timestamp_gap($minimum, $maximum);
+        $minimum = facetapi_isodate($minimum, $gap);
+        $maximum = facetapi_isodate($maximum, $gap);
+        $return = array(
+          "$minimum/$gap",
+          "$maximum+1$gap/$gap",
+          "+1$gap",
+        );
+      }
+    }
+    // Returns the range information.
+    return $return;
+  }
+
+  /**
+   * Initializes the facet's build array.
+   *
+   * @return array
+   *   The initialized render array.
+   */
+  public function build() {
+
+    // Initializes build and gets static response.
+    if (!$response = solrsearch_static_response_cache($this->adapter->getSearcher())) {
+      return array();
+    }
+    $build = array();
+
+    // Gets total number of documents matched in search.
+    $total = $response->response->numFound;
+
+    // Gets the active date facets, starts to builds the "parent - child"
+    // relationships.
+    $parent = NULL;
+    foreach ($this->adapter->getActiveItems($this->facet) as $value => $item) {
+      // Builds the raw facet "value", the count for selected items will be the
+      // total number of rows returned in the query.
+      $build[$value] = array('#count' => $total);
+
+      // If there is a previous item, there is a parent, uses a reference so the
+      // arrays are populated when they are updated.
+      if (NULL !== $parent) {
+        $build[$parent]['#item_children'][$value] = &$build[$value];
+        $build[$value]['#item_parents'][$parent] = $parent;
+      }
+
+      // Stores the last value iterated over.
+      $parent = $value;
+    }
+
+    // Gets raw facet data from the Solr server.
+    if (isset($response->facet_counts->facet_dates) && isset($response->facet_counts->facet_dates->{$this->facet['field']})) {
+      $raw_data = (array) $response->facet_counts->facet_dates->{$this->facet['field']};
+    }
+    else {
+      $raw_data = array();
+    }
+    //$end = (!empty($raw_data['end'])) ? $raw_data['end'] : '';
+    //$start = (!empty($raw_data['start'])) ? $raw_data['start'] : '';
+    $gap = (!empty($raw_data['gap'])) ? $raw_data['gap'] : '';
+
+    // We cannot list anything below a minute (range of 00 seconds till 59
+    // seconds. Milliseconds are not possible)
+    if ($gap != "+1SECOND") {
+      unset($raw_data['start']);
+      unset($raw_data['end']);
+      unset($raw_data['gap']);
+
+      // Treat each date facet as a range start, and use the next date facet
+      // as range end.  Use 'end' for the final end.
+      $previous = NULL;
+
+      // Builds facet counts object used by the server.
+      foreach ($raw_data as $value => $count) {
+        if ($count) {
+          $from = $value;
+          $to = facetapi_isodate(strtotime($value . $gap));
+          $new_value = '[' . $from . ' TO ' . $to . ']';
+          $build[$new_value] = array('#count' => $count, '#active' => 0);
+          if (NULL !== $parent) {
+            $build[$parent]['#item_children'][$new_value] = &$build[$new_value];
+            $build[$new_value]['#item_parents'][$parent] = $parent;
+          }
+        }
+      }
+    }
+    return $build;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/facetapi/query_type_geo.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Plugin for "apachesolr_geo" query types.
+ */
+class solrsearchFacetapiGeo extends FacetapiQueryType implements FacetapiQueryTypeInterface {
+  // Center point is Denver.
+  public $center_point = '39.7391667,-104.9841667';
+  public $facet_options = '0.5,0.1,0.01';
+  public $default_radius = 100;
+
+  /**
+   * Returns the query type associated with the plugin.
+   *
+   * @return string
+   *   The query type.
+   */
+  static public function getType() {
+    return 'geo';
+  }
+
+  /**
+   * Adds the filter to the query object.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   An object containing the query in the backend's native API.
+   */
+  public function execute($query) {
+    // Retrieve settings of the facet.
+    // We should be able to get all constants as facet options.
+    $settings = $this->adapter->getFacet($this->facet)->getSettings();
+
+    $facet_distances = explode(',', $this->facet_options);
+
+    $active_items = $this->adapter->getActiveItems($this->facet);
+
+    if (empty($active_items)) {
+      $distance = $this->default_radius;
+    }
+    else {
+      $active_item = array_pop($active_items);
+      $distance = substr($active_item['value'], 1);
+      // Add current selected distance to have possibility to unselect it.
+      $facet_distances[] = 1;
+    }
+
+    // Search center point.
+    $query->addParam('pt', $this->center_point);
+
+    // Set location field name.
+    $query->addParam('sfield', $this->facet['field']);
+    $query->addParam('fq', '{!geofilt sfield=' . $this->facet['field'] . '}');
+
+    // Set search radius.
+    $query->addParam('d', $distance);
+
+    // Set facets.
+    foreach ($facet_distances as $facet_option) {
+      $facet_distance = $distance * $facet_option;
+      $query->addParam('facet.query', '{!geofilt d=' . $facet_distance . ' key=d' . $facet_distance . '}');
+    }
+  }
+
+  /**
+   * Initializes the facet's build array.
+   *
+   * @return array
+   *   The initialized render array.
+   */
+  public function build() {
+    $build = array();
+    if ($response = apachesolr_static_response_cache($this->adapter->getSearcher())) {
+      if (isset($response->facet_counts->facet_queries)) {
+        foreach ($response->facet_counts->facet_queries as $value => $count) {
+          // Skip zero results values.
+          if ($count > 0) {
+            $build[$value] = array('#count' => $count);
+          }
+        }
+      }
+    }
+    return $build;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/facetapi/query_type_integer.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,223 @@
+<?php
+
+/**
+ * @file
+ * Date query type plugin for the Apache Solr adapter.
+ * @author dwinter
+ *
+ */
+
+
+/**
+ * Regex pattern for integer ranges.
+ */
+define('REGEX_INTEGER_RANGE', '/^\[(\d*) TO (\d*)\]$/');
+
+
+/**
+ * Plugin for "Integer" query types.
+ */
+class solrsearchFacetapiInteger extends FacetapiQueryType implements FacetapiQueryTypeInterface {
+
+  /**
+   * Overrides FacetapiQueryType::extract().
+   *
+   * Adds the "start" and "end" values for the integer range.
+   */
+  public function extract(array $item) {
+    $return = array();
+    if (preg_match(REGEX_INTEGER_RANGE, $item['value'], $matches)) {
+
+      $return['start'] = $matches[1];
+      $return['end'] = $matches[2];
+    }
+    return $return;
+  }
+
+
+  /**
+   * Returns the query type associated with the plugin.
+   *
+   * @return string
+   *   The query type.
+   */
+  static public function getType() {
+    return 'integer';
+  }
+
+  /**
+   * Adds the filter to the query object.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   An object containing the query in the backend's native API.
+   */
+  public function execute($query) {
+    // Gets the data range in formats that Solr understands.
+
+    $integer_range = $this->getIntegerRange($query);
+
+    if (empty($integer_range)) {
+      return NULL;
+    }
+    list($start, $end, $gap) = $integer_range;
+    $query->addParam('facet.range', $this->facet['field']);
+    $query->addParam('f.' . $this->facet['field'] . '.facet.range.start', $start);
+    $query->addParam('f.' . $this->facet['field'] . '.facet.range.end', $end);
+    $query->addParam('f.' . $this->facet['field'] . '.facet.range.gap', $gap);
+
+    // Adds "hard limit" parameter to prevent too many return values.
+    $settings = $this->adapter->getFacet($this->facet)->getSettings();
+    $limit = empty($settings->settings['hard_limit']) ? 20 : (int) $settings->settings['hard_limit'];
+    $query->addParam('f.' . $this->facet['field'] . '.facet.limit', $limit);
+
+    $active = $this->adapter->getActiveItems($this->facet);
+    // Date filters don't support OR operator.
+    foreach ($active as $value => $item) {
+      $query->addFilter($this->facet['field'], $value);
+    }
+  }
+
+  /**
+   * Gets the range of integer rage we are using.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   A SolrBaseQuery object.
+   *
+   * @return bool|array
+   *   An array containing the gap and range information or false if not present
+   */
+  function getIntegerRange(DrupalSolrQueryInterface $query) {
+
+    $return = NULL;
+    $gap = NULL;
+
+    // Attempts to get next gap from passed date filters.
+    foreach ($this->adapter->getActiveItems($this->facet) as $item) {
+
+      if ($gap = ($item['end'] - $item['start'])) {
+        //$next_gap = facetapi_get_next_date_gap($gap, FACETAPI_DATE_SECOND);
+        //TODO: is something similar needed for integer range?
+        $next_gap =$gap;
+        if ($next_gap == $gap) {
+          $next_gap = NULL;
+          return NULL;
+        }
+        $return = array(
+          $item['start'],
+          $item['end'],
+          $next_gap,
+        );
+      }
+    }
+
+    // If no filters were passed, get default range.
+    if (NULL === $return) {
+
+      // Builds SQL that gets minimum and maximum values from node table.
+      $minimum = $maximum =$gap = FALSE;
+      if ($this->facet['min callback'] && is_callable($this->facet['min callback'])) {
+        $minimum = $this->facet['min callback']($this->facet);
+      }
+      if ($this->facet['max callback'] && is_callable($this->facet['max callback'])) {
+        $maximum = $this->facet['max callback']($this->facet);
+      }
+
+      if ($this->facet['gap callback'] && is_callable($this->facet['gap callback'])) {
+        $gap = $this->facet['gap callback']($this->facet);
+      }
+
+
+      // Gets the default gap.
+      //$gap = FACETAPI_DATE_YEAR;
+      if ($minimum && $maximum && $gap) {
+
+        //$minimum = facetapi_isodate($minimum, $gap);
+        //$maximum = facetapi_isodate($maximum, $gap);
+        $return = array(
+          $minimum,
+          $maximum,
+          $gap ,
+        );
+      }
+    }
+    // Returns the range information.
+    return $return;
+  }
+
+  /**
+   * Initializes the facet's build array.
+   *
+   * @return array
+   *   The initialized render array.
+   */
+  public function build() {
+
+    // Initializes build and gets static response.
+    if (!$response = solrsearch_static_response_cache($this->adapter->getSearcher())) {
+      return array();
+    }
+    $build = array();
+
+    // Gets total number of documents matched in search.
+    $total = $response->response->numFound;
+
+    // Gets the active date facets, starts to builds the "parent - child"
+    // relationships.
+    $parent = NULL;
+    foreach ($this->adapter->getActiveItems($this->facet) as $value => $item) {
+      // Builds the raw facet "value", the count for selected items will be the
+      // total number of rows returned in the query.
+      $build[$value] = array('#count' => $total);
+
+      // If there is a previous item, there is a parent, uses a reference so the
+      // arrays are populated when they are updated.
+      if (NULL !== $parent) {
+        $build[$parent]['#item_children'][$value] = &$build[$value];
+        $build[$value]['#item_parents'][$parent] = $parent;
+      }
+
+      // Stores the last value iterated over.
+      $parent = $value;
+    }
+
+    // Gets raw facet data from the Solr server.
+    if (isset($response->facet_counts->facet_ranges) && isset($response->facet_counts->facet_ranges->{$this->facet['field']})) {
+      $raw_data = (array) $response->facet_counts->facet_ranges->{$this->facet['field']};
+    }
+    else {
+      $raw_data = array();
+    }
+    //$end = (!empty($raw_data['end'])) ? $raw_data['end'] : '';
+    //$start = (!empty($raw_data['start'])) ? $raw_data['start'] : '';
+    $gap = (!empty($raw_data['gap'])) ? $raw_data['gap'] : '';
+
+    // We cannot list anything below a minute (range of 00 seconds till 59
+    // seconds. Milliseconds are not possible)
+
+      unset($raw_data['start']);
+      unset($raw_data['end']);
+      unset($raw_data['gap']);
+
+      // Treat each date facet as a range start, and use the next date facet
+      // as range end.  Use 'end' for the final end.
+      $previous = NULL;
+
+      // Builds facet counts object used by the server.
+
+      foreach ($raw_data as $value => $counts) {
+        foreach($counts as $val => $count){
+        if ($count) {
+                  $from = $val;
+          $to = $val+$gap;
+          $new_value = '[' . $from . ' TO ' . $to . ']';
+          $build[$new_value] = array('#count' => $count, '#active' => 0);
+          if (NULL !== $parent) {
+            $build[$parent]['#item_children'][$new_value] = &$build[$new_value];
+            $build[$new_value]['#item_parents'][$parent] = $parent;
+          }
+        }
+      }
+      }
+    return $build;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/facetapi/query_type_numeric_range.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Numeric range query type plugin for the Apache Solr adapter.
+ */
+
+/**
+ * Plugin for "numeric_range" query types.
+ */
+class solrsearchFacetapiNumericRange extends FacetapiQueryType implements FacetapiQueryTypeInterface {
+
+  private $single_key;
+  /**
+   * Returns the query type associated with the plugin.
+   *
+   * @return string
+   *   The query type.
+   */
+  static public function getType() {
+    return 'numeric_range';
+  }
+
+  /**
+   * Adds the filter to the query object.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   An object containing the query in the backend's native API.
+   * @todo Cache the values based on the filter query or any other way?
+   */
+  public function execute($query) {
+    // Check if we have a cache of this field
+    //
+    $settings = $this->adapter->getFacet($this->facet)->getSettings();
+    $active = $this->adapter->getActiveItems($this->facet);
+
+    $singular_field_info = $this->facet['map options'];
+    $singular_field_info['multiple'] = FALSE;
+
+    $this->single_key = solrsearch_index_key($singular_field_info);
+    // See:  http://wiki.apache.org/solr/StatsComponent
+    $query->addParam('stats', 'true');
+    $query->addParam('stats.field', $this->single_key);
+    $query->addParam('stats.facet', $this->single_key);
+    // Range filters don't support OR operator.
+    foreach ($active as $value => $item) {
+      $query->addFilter($this->single_key, $value);
+    }
+  }
+
+  /**
+   * Initializes the facet's build array.
+   *
+   * Any calls to this method need to be wrapped in a try-catch block.
+   *
+   * @return array
+   *   The initialized render array.
+   */
+  public function build() {
+    $build = array();
+    if (!isset($this->single_key)) {
+      return $build;
+    }
+
+    // Per key we save our statistics result
+    $cache = cache_get('stats_' . $this->single_key, 'cache_solrsearch');
+    $stats_minmax = array();
+
+    if (!isset($cache->data)) {
+      // we need an additional query for the statistics of the field
+      // We can optionally specify a Solr object.
+      $solr = solrsearch_get_solr();
+
+      // We possibly need some caching for this query
+      $query_stats = solrsearch_drupal_query('solrsearch_stats', array(), '', '', $solr);
+      $query_stats->addParam('stats', 'true');
+      $query_stats->addParam('stats.field', $this->single_key);
+      $query_stats->addParam('stats.facet', $this->single_key);
+      $response_stats = $query_stats->search();
+
+      if ($response_stats->response) {
+        $stats_minmax = $response_stats->stats->stats_fields->{$this->single_key};
+        cache_set('stats_' . $this->single_key, $stats_minmax, 'cache_solrsearch');
+      }
+    }
+    else {
+      // Set our statistics from the cache
+      $stats_minmax = $cache->data;
+    }
+
+    if ($response = solrsearch_static_response_cache($this->adapter->getSearcher())) {
+      if (isset($response->stats->stats_fields->{$this->single_key})) {
+        $stats = (array) $response->stats->stats_fields->{$this->single_key};
+        foreach ($stats as $key => $val) {
+          $build[$this->facet['field']]['#range_' . $key] = $val;
+          $build[$this->facet['field']]['#global_range_' . $key] = $stats_minmax->$key;
+        }
+      }
+    }
+    return $build;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/facetapi/query_type_term.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * @file
+ * Term query type plugin for the Apache Solr adapter.
+ */
+
+/**
+ * Plugin for "term" query types.
+ */
+class solrsearchFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTypeInterface {
+
+  /**
+   * Returns the query type associated with the plugin.
+   *
+   * @return string
+   *   The query type.
+   */
+  static public function getType() {
+    return 'term';
+  }
+
+  /**
+   * Adds the filter to the query object.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   An object containing the query in the backend's native API.
+   */
+  public function execute($query) {
+    $settings = $this->adapter->getFacet($this->facet)->getSettings();
+    // Adds the operator parameter.
+    $operator = $settings->settings['operator'];
+    $ex = (FACETAPI_OPERATOR_OR != $operator) ? '' : "{!ex={$this->facet['field']}}";
+    $query->addParam('facet.field', $ex . $this->facet['field']);
+
+    if (!empty($settings->settings['facet_missing'])) {
+      $query->addParam('f.' . $this->facet['field'] . '.facet.missing', 'true');
+    }
+    // Adds "hard limit" parameter to prevent too many return values.
+    $limit = empty($settings->settings['hard_limit']) ? 20 : (int) $settings->settings['hard_limit'];
+    $query->addParam('f.' . $this->facet['field'] . '.facet.limit', $limit);
+
+    // Adds "facet mincount" parameter to limit the number of facets.
+    if (isset($settings->settings['facet_mincount'])) {
+      $count = $settings->settings['facet_mincount'];
+      $query->addParam('f.' . $this->facet['field'] . '.facet.mincount', $count);
+    }
+
+    $active = $this->adapter->getActiveItems($this->facet);
+
+    // Adds filter based on the operator.
+    if (FACETAPI_OPERATOR_OR != $operator) {
+      foreach ($active as $value => $item) {
+        // Handle facet missing:
+        if ($value == '_empty_' && !empty($settings->settings['facet_missing'])) {
+          $query->addFilter($this->facet['field'], '[* TO *]', TRUE);
+        }
+        else {
+          $query->addFilter($this->facet['field'], $value);
+        }
+      }
+    }
+    else {
+      // OR facet.
+      $local = "tag={$this->facet['field']}";
+      $values = array_keys($active);
+      if ($values) {
+        // Quote any values that have white space or colons.
+        foreach ($values as &$v) {
+          if (preg_match('/[:\s]/', $v)) {
+            $v = '"' . $v . '"';
+          }
+        }
+        $query->addFilter($this->facet['field'], '(' . implode(' OR ', $values) . ')', FALSE, $local);
+      }
+    }
+  }
+
+  /**
+   * Initializes the facet's build array.
+   *
+   * @return array
+   *   The initialized render array.
+   */
+  public function build() {
+
+
+    $build = array();
+    if ($response = solrsearch_static_response_cache($this->adapter->getSearcher())) {
+
+      $settings = $this->adapter->getFacet($this->facet)->getSettings();
+
+      if (isset($response->facet_counts->facet_fields->{$this->facet['field']})) {
+        $values = (array) $response->facet_counts->facet_fields->{$this->facet['field']};
+        foreach ($values as $value => $count) {
+          // Facet missing may return 0 even if mincount is 1.
+          if (empty($settings->settings['facet_mincount']) || $count) {
+            $build[$value] = array('#count' => $count);
+          }
+        }
+      }
+    }
+
+    return $build;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solr-search-result.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation for displaying a single search result.
+
+ * @see template_preprocess()
+ * @see template_preprocess_search_result()
+ * @see template_process()
+ *
+ * @ingroup themeable
+ */
+?>
+
+<?php if ($doc_type == "indexMeta"):?>
+<div class="single-result indexmeta <?php print $classes; ?>" <?php print $attributes; ?>>
+    <div class="thumbnail">
+       <?php if ($access_type !="free"):?>
+            <a href="<?php print $url; ?>">
+                <img src="<?php print $thumburl; ?>"/>
+                <p class="solrsearch_internal">internal</p>
+            </a>
+        <?php else: ;?>
+            <img src="<?php print $thumburl; ?>"/>
+        <?php endif;?>
+    </div>
+    <div class="search-result-meta">
+        <div class="search-result-author">
+            <?php print $author; ?>
+        </div>
+        <div class="search-result-title">  
+            <a href="<?php print $url; ?>">
+            <?php print $title; ?>
+            <?php if (isset ($signature)):?>
+                <?php print $signature; ?>
+            <?php endif;?>
+            </a>
+        </div>
+        <div class="search-result-date">
+            <?php if (isset ($date)):?>
+                <?php print $date; ?>
+            <?php else: ?>
+                <?php print $year; ?>
+            <?php endif;?>
+        </div>
+        <div class="search-result-dataSource">
+            <?php if (isset ($dataSource)):?>
+                <?php print $dataSource; ?>
+            <?php else: ?>
+                <?php print $dataSource; ?>
+            <?php endif;?>
+        </div>
+
+
+
+    </div>
+<!--</div> closing div is in solrsearch.pages.inc line 174-->
+<?php elseif ($doc_type == "external"):?>
+<div class="single-result indexmeta <?php print $classes; ?>" <?php print $attributes; ?>>
+    <div class="thumbnail">
+   <?php if ($external_image!=""): ?>
+            <img src="<?php print $external_image; ?>"/>
+        <?php endif;?>
+    </div>
+
+    <div class="search-result-meta">
+        <div class="search-result-author">
+            <?php print $author; ?>
+        </div>
+        <div class="search-result-title">  
+        <?php if ($external_url!=""): ?>
+            <a href="<?php print $external_url; ?>">
+        <?php endif; ?> 
+            <?php print $title; ?>
+            <?php if (isset ($signature)):?>
+                <?php print $signature; ?>
+            <?php endif;?>
+            </a>
+        </div>
+        <div class="search-result-date">
+            <?php if (isset ($date)):?>
+                <?php print $date; ?>
+            <?php else: ?>
+                <?php print $year; ?>
+            <?php endif;?>
+        </div>
+
+   <div class="search-result-provider">
+            <?php if (isset ($provider)):?>
+                <?php print $provider; ?>
+            <?php else: ?>
+                <?php print $provider; ?>
+            <?php endif;?>
+        </div>
+    
+   <div class="search-result-dataSource">
+            <?php if (isset ($dataSource)):?>
+   provided by: <?php print $dataSource; ?>
+            <?php else: ?>
+                <?php print $dataSource; ?>
+            <?php endif;?>
+        </div>
+
+    </div>
+
+
+
+<?php elseif ($doc_type == "institutesLibrary"): ?>
+<div class="single-result institutesLibrary <?php print $classes; ?>"<?php print $attributes; ?>>
+    <div class="thumbnail">
+    &nbsp;
+    </div>
+    <div class="search-result-meta">
+        <div class="libraryfield">
+            <?php if (isset ($signature)):?>
+                <span class="signatureLabel">Signature Institute's Library:</span>
+                <span class="signature"><?php print $signature; ?></span>
+            <?php endif;?>
+        </div>
+        <div class="search-result-author">
+            <?php print $author; ?>
+        </div>
+        <div class="search-result-title">
+            <a target="_blank" href="<?php print $librarySearchPath; ?>">
+                <?php print $title; ?>
+            </a>
+        </div>
+        <div class="search-result-date">
+            <?php if (isset ($date)):?>
+                <?php print $date; ?>
+            <?php else: ?>
+                <?php print $year; ?>
+            <?php endif;?>
+        </div>
+    </div>
+<!--</div> closing div is in solrsearch.pages.inc line 174-->
+<?php else: ?>
+<div class="single-result collection <?php print $classes; ?>" <?php print $attributes; ?>>
+    <div class="thumbnail">
+    &nbsp;
+    </div>
+    <div class="search-result-meta">
+        <div class="search-result-author">
+            <?php print $author; ?>
+        </div>
+        <div class="search-result-title">
+            <span class="collectionLabel">Collection:</span>
+            <a href="<?php print $collectionPath; ?>">
+                <?php print $title; ?>
+            </a>
+        </div>
+        <div class="search-result-date">
+            <?php if (isset ($date)):?>
+                <?php print $date; ?>
+            <?php else: ?>
+                <?php print $year; ?>
+            <?php endif;?>
+        </div>
+        <div class="collection-content">
+            <div><?php print $content?></div>
+        </div>
+    </div>
+<!--</div> closing div is in solrsearch.pages.inc line 174-->
+<?php endif;?>
+
+
+<!--  <tr class="line">
+<td>&nbsp;</td>
+ <td  colspan="4">
+<div class="search-snippet-info">
+    <?php if ($snippet): ?>
+      <p class="search-snippet"<?php print $content_attributes; ?>><?php print $snippet; ?></p>
+    <?php endif; ?>
+  </div>
+  </td>
+  </tr>-->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solr-search-results.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation for displaying search results.
+ *
+ * This template collects each invocation of theme_search_result(). This and
+ * the child template are dependent to one another sharing the markup for
+ * definition lists.
+ *
+ * Note that modules may implement their own search type and theme function
+ * completely bypassing this template.
+ *
+ * Available variables:
+ * - $search_results: All results as it is rendered through
+ *   search-result.tpl.php
+ * - $module: The machine-readable name of the module (tab) being searched, such
+ *   as "node" or "user".
+ *
+ *
+ * @see template_preprocess_search_results()
+ *
+ * @ingroup themeable
+ */
+?>
+<div class="search-output">
+    <?php if ($search_results): ?>
+        <?php print $description; ?>
+        <h2><?php print t('search results for:');  ?></h2>
+        <?php if (isset($digitalobjects_items_select)):?>
+            <form method="GET" action="<?php print $digitalobjects_items_select ?>">
+        <?php endif;?>
+        <div id="toppager"><?php print $pager; ?></div>
+
+        <div class="items search-results <?php print $module; ?>-results">
+            <?php print $search_results; ?>
+        </div>
+
+        <div id="bottompager"><?php print $pager; ?></div>
+
+        <?php if (isset($digitalobjects_items_select)):?>
+            </form>
+        <?php endif;?>
+    <?php else : ?>
+        <h2><?php print t('Your search yielded no results');?></h2>
+        <?php print search_help('search#noresults', drupal_help_arg()); ?>
+    <?php endif; ?>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solr/schema.xml	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,1114 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--  
+ This is the Solr schema file. This file should be named "schema.xml" and
+ should be in the conf directory under the solr home
+ (i.e. ./solr/conf/schema.xml by default) 
+ or located where the classloader for the Solr webapp can find it.
+
+ This example schema is the recommended starting point for users.
+ It should be kept correct and concise, usable out-of-the-box.
+
+ For more information, on how to customize this file, please see
+ http://wiki.apache.org/solr/SchemaXml
+
+ PERFORMANCE NOTE: this schema includes many optional features and should not
+ be used for benchmarking.  To improve performance one could
+  - set stored="false" for all fields possible (esp large fields) when you
+    only need to search on the field but don't need to return the original
+    value.
+  - set indexed="false" if you don't need to search on the field, but only
+    return the field as a result of searching on other indexed fields.
+  - remove all unneeded copyField statements
+  - for best index size and searching performance, set "index" to false
+    for all general text fields, use copyField to copy them to the
+    catchall "text" field, and use that for searching.
+  - For maximum indexing performance, use the StreamingUpdateSolrServer
+    java client.
+  - Remember to run the JVM in server mode, and use a higher logging level
+    that avoids logging every request
+-->
+
+<schema name="example" version="1.5">
+  <!-- attribute "name" is the name of this schema and is only used for display purposes.
+       version="x.y" is Solr's version number for the schema syntax and 
+       semantics.  It should not normally be changed by applications.
+
+       1.0: multiValued attribute did not exist, all fields are multiValued 
+            by nature
+       1.1: multiValued attribute introduced, false by default 
+       1.2: omitTermFreqAndPositions attribute introduced, true by default 
+            except for text fields.
+       1.3: removed optional field compress feature
+       1.4: autoGeneratePhraseQueries attribute introduced to drive QueryParser
+            behavior when a single string produces multiple tokens.  Defaults 
+            to off for version >= 1.4
+       1.5: omitNorms defaults to true for primitive field types 
+            (int, float, boolean, string...)
+     -->
+
+ <fields>
+   <!-- Valid attributes for fields:
+     name: mandatory - the name for the field
+     type: mandatory - the name of a field type from the 
+       <types> fieldType section
+     indexed: true if this field should be indexed (searchable or sortable)
+     stored: true if this field should be retrievable
+     multiValued: true if this field may contain multiple values per document
+     omitNorms: (expert) set to true to omit the norms associated with
+       this field (this disables length normalization and index-time
+       boosting for the field, and saves some memory).  Only full-text
+       fields or fields that need an index-time boost need norms.
+       Norms are omitted for primitive (non-analyzed) types by default.
+     termVectors: [false] set to true to store the term vector for a
+       given field.
+       When using MoreLikeThis, fields used for similarity should be
+       stored for best performance.
+     termPositions: Store position information with the term vector.  
+       This will increase storage costs.
+     termOffsets: Store offset information with the term vector. This 
+       will increase storage costs.
+     required: The field is required.  It will throw an error if the
+       value does not exist
+     default: a value that should be used if no value is specified
+       when adding a document.
+   -->
+
+   <!-- field names should consist of alphanumeric or underscore characters only and
+      not start with a digit.  This is not currently strictly enforced,
+      but other field names will not have first class support from all components
+      and back compatibility is not guaranteed.  Names with both leading and
+      trailing underscores (e.g. _version_) are reserved.
+   -->
+        
+   <field name="archive-path" type="string" indexed="true" stored="true" required="true" multiValued="false" /> 
+    
+   <field name="archive-creation-date" type="string" indexed="true" stored="true" required="false" multiValued="false" /> 
+   <field name="year" type="int" indexed="true" stored="true" required="false" multiValued="true" /> 
+   
+   
+
+   <!-- Common metadata fields, named specifically to match up with
+     SolrCell metadata when parsing rich documents such as Word, PDF.
+     Some fields are multiValued only because Tika currently may return
+     multiple values for them. Some metadata is parsed from the documents,
+     but there are some which come from the client context:
+       "content_type": From the HTTP headers of incoming stream
+       "resourcename": From SolrCell request param resource.name
+   -->
+   <field name="title" type="text_general" indexed="true" stored="true" multiValued="true"/>
+   <field name="subtitle" type="text_general" indexed="true" stored="true" multiValued="true"/>
+   <field name="author" type="author" indexed="true" stored="true"  multiValued="true"/>
+   <field name="author_c" type="author_c" indexed="true" stored="true"  multiValued="true"/>
+   <field name="keyword" type="string" indexed="true" stored="true"  multiValued="false"/>
+   <field name="date" type="text_general" indexed="true" stored="true" multiValued="true"/>
+   <field name="access" type="text_general" indexed="true" stored="true" multiValued="false"/>
+   <field name="access-type" type="text_general" indexed="true" stored="true" multiValued="false"/>
+   <field name="mpiwg-dri" type="text_general" indexed="true" stored="true" multiValued="false"/>
+   <field name="all-bib-data" type="text_general" indexed="true" stored="true" multiValued="true"/>
+
+   <field name="doc-type" type="string" indexed="true" stored="true" multiValued="true"/>
+
+   
+   <field name="content" type="text_general" indexed="true" stored="true" multiValued="true"/>
+   <field name="collectionid" type="string" indexed="true" stored="true" multiValued="true"/>
+   <field name="collection" type="string" indexed="true" stored="true" multiValued="true"/>
+
+
+   <!-- catchall field, containing all other searchable text fields (implemented
+        via copyField further on in this schema  -->
+   <field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>
+
+   <!-- catchall text field that indexes tokens both normally and in reverse for efficient
+        leading wildcard queries. -->
+   <field name="text_rev" type="text_general_rev" indexed="true" stored="false" multiValued="true"/>
+
+   <!-- non-tokenized version of manufacturer to make it easier to sort or group
+        results by manufacturer.  copied from "manu" via copyField -->
+
+   <field name="_version_" type="long" indexed="true" stored="true"/>
+
+   <!-- Uncommenting the following will create a "timestamp" field using
+        a default value of "NOW" to indicate when each document was indexed.
+     -->
+   <!--
+   <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false"/>
+     -->
+
+   <!-- Dynamic field definitions allow using convention over configuration
+       for fields via the specification of patterns to match field names. 
+       EXAMPLE:  name="*_i" will match any field ending in _i (like myid_i, z_i)
+       RESTRICTION: the glob-like pattern in the name attribute must have
+       a "*" only at the start or the end.  -->
+   
+   <dynamicField name="IM_*"  type="text_general"    indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="TT_*"  type="text_general"    indexed="true"  stored="true" multiValued="true"/>
+    <field name="text-url-path" type="string" indexed="true" stored="true" multiValued="false"/>
+   
+
+   <!-- String typed fields for better sorting-->
+   <dynamicField name="*_s"  type="string"    indexed="true"  stored="true" multiValued="true"/>
+
+   <!-- Type used to index the lat and lon components for the "location" FieldType -->
+
+   <!-- some trie-coded dynamic fields for faster range queries -->
+ 
+   <!-- uncomment the following to ignore any fields that don't already match an existing 
+        field name or dynamic field, rather than reporting them as an error. 
+        alternately, change the type="ignored" to some other type e.g. "text" if you want 
+        unknown fields indexed and/or stored by default --> 
+   <!--dynamicField name="*" type="ignored" multiValued="true" /-->
+ <field name="date2year" type="date2year"  indexed="true"  stored="true" multiValued="true"/>  
+ </fields>
+
+
+ <!-- Field to use to determine and enforce document uniqueness. 
+      Unless this field is marked with required="false", it will be a required field
+   -->
+ <uniqueKey>archive-path</uniqueKey>
+ <defaultSearchField>title</defaultSearchField>
+ <copyField source="IM_year" dest="year"/> 
+ <copyField source="IM_year" dest="date"/>
+
+ 
+ <copyField source="IM_date" dest="date2year"/>
+
+
+<!-- copy to author field (author is splitted by ';' -->
+ <copyField source="IM_author" dest="author"/>
+ <copyField source="IM_author" dest="author_c"/>
+ <copyField source="IM_title" dest="title"/>
+ <copyField source="subtitle" dest="title"/>
+<copyField source="IM_keyword" dest="keyword"/>
+
+ <copyField source="IM_title" dest="title_s"/>
+ <copyField source="subtitle" dest="title_s"/>
+
+<copyField source="IM_title" dest="maintitle_s"/>
+<copyField source="TT_text-url-path" dest="text-url-path"/>
+
+	
+   <!-- Above, multiple source fields are copied to the [text] field. 
+	  Another way to map multiple source fields to the same 
+	  destination field is to use the dynamic field syntax. 
+	  copyField also supports a maxChars to copy setting.  -->
+	   
+   <!-- <copyField source="*_t" dest="text" maxChars="3000"/> -->
+
+   <!-- copy name to alphaNameSort, a field designed for sorting by name -->
+   <!-- <copyField source="name" dest="alphaNameSort"/> -->
+ 
+  <types>
+    <!-- field type definitions. The "name" attribute is
+       just a label to be used by field definitions.  The "class"
+       attribute and any other attributes determine the real
+       behavior of the fieldType.
+         Class names starting with "solr" refer to java classes in a
+       standard package such as org.apache.solr.analysis
+    -->
+
+    <!-- The StrField type is not analyzed, but indexed/stored verbatim. -->
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
+
+    <!-- boolean type: "true" or "false" -->
+    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
+
+    <!-- sortMissingLast and sortMissingFirst attributes are optional attributes are
+         currently supported on types that are sorted internally as strings
+         and on numeric types.
+	     This includes "string","boolean", and, as of 3.5 (and 4.x),
+	     int, float, long, date, double, including the "Trie" variants.
+       - If sortMissingLast="true", then a sort on this field will cause documents
+         without the field to come after documents with the field,
+         regardless of the requested sort order (asc or desc).
+       - If sortMissingFirst="true", then a sort on this field will cause documents
+         without the field to come before documents with the field,
+         regardless of the requested sort order.
+       - If sortMissingLast="false" and sortMissingFirst="false" (the default),
+         then default lucene sorting will be used which places docs without the
+         field first in an ascending sort and last in a descending sort.
+    -->    
+
+    <!--
+      Default numeric field types. For faster range queries, consider the tint/tfloat/tlong/tdouble types.
+    -->
+    <fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
+    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
+    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
+    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>
+
+    <!--
+     Numeric field types that index each value at various levels of precision
+     to accelerate range queries when the number of values between the range
+     endpoints is large. See the javadoc for NumericRangeQuery for internal
+     implementation details.
+
+     Smaller precisionStep values (specified in bits) will lead to more tokens
+     indexed per value, slightly larger index size, and faster range queries.
+     A precisionStep of 0 disables indexing at different precision levels.
+    -->
+    <fieldType name="tint" class="solr.TrieIntField" precisionStep="8" positionIncrementGap="0"/>
+    <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" positionIncrementGap="0"/>
+    <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" positionIncrementGap="0"/>
+    <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" positionIncrementGap="0"/>
+
+    <!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
+         is a more restricted form of the canonical representation of dateTime
+         http://www.w3.org/TR/xmlschema-2/#dateTime    
+         The trailing "Z" designates UTC time and is mandatory.
+         Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
+         All other components are mandatory.
+
+         Expressions can also be used to denote calculations that should be
+         performed relative to "NOW" to determine the value, ie...
+
+               NOW/HOUR
+                  ... Round to the start of the current hour
+               NOW-1DAY
+                  ... Exactly 1 day prior to now
+               NOW/DAY+6MONTHS+3DAYS
+                  ... 6 months and 3 days in the future from the start of
+                      the current day
+                      
+         Consult the DateField javadocs for more information.
+
+         Note: For faster range queries, consider the tdate type
+      -->
+    <fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>
+
+    <!-- A Trie based date field for faster date range queries and date faceting. -->
+    <fieldType name="tdate" class="solr.TrieDateField" precisionStep="6" positionIncrementGap="0"/>
+
+
+    <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
+    <fieldtype name="binary" class="solr.BinaryField"/>
+
+    <!--
+      Note:
+      These should only be used for compatibility with existing indexes (created with lucene or older Solr versions).
+      Use Trie based fields instead. As of Solr 3.5 and 4.x, Trie based fields support sortMissingFirst/Last
+      
+      Plain numeric field types that store and index the text
+      value verbatim (and hence don't correctly support range queries, since the
+      lexicographic ordering isn't equal to the numeric ordering)
+    -->
+    <fieldType name="pint" class="solr.IntField"/>
+    <fieldType name="plong" class="solr.LongField"/>
+    <fieldType name="pfloat" class="solr.FloatField"/>
+    <fieldType name="pdouble" class="solr.DoubleField"/>
+    <fieldType name="pdate" class="solr.DateField" sortMissingLast="true"/>
+
+    <!-- The "RandomSortField" is not used to store or search any
+         data.  You can declare fields of this type it in your schema
+         to generate pseudo-random orderings of your docs for sorting 
+         or function purposes.  The ordering is generated based on the field
+         name and the version of the index. As long as the index version
+         remains unchanged, and the same field name is reused,
+         the ordering of the docs will be consistent.  
+         If you want different psuedo-random orderings of documents,
+         for the same version of the index, use a dynamicField and
+         change the field name in the request.
+     -->
+    <fieldType name="random" class="solr.RandomSortField" indexed="true" />
+
+    <!-- solr.TextField allows the specification of custom text analyzers
+         specified as a tokenizer and a list of token filters. Different
+         analyzers may be specified for indexing and querying.
+
+         The optional positionIncrementGap puts space between multiple fields of
+         this type on the same document, with the purpose of preventing false phrase
+         matching across fields.
+
+         For more info on customizing your analyzer chain, please see
+         http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters
+     -->
+
+    <!-- One can also specify an existing Analyzer class that has a
+         default constructor via the class attribute on the analyzer element.
+         Example:
+    <fieldType name="text_greek" class="solr.TextField">
+      <analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
+    </fieldType>
+    -->
+
+    <!-- A text field that only splits on whitespace for exact matching of words -->
+    <fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A general text field that has reasonable, generic
+         cross-language defaults: it tokenizes with StandardTokenizer,
+	 removes stop words from case-insensitive "stopwords.txt"
+	 (empty by default), and down cases.  At query time only, it
+	 also applies synonyms. -->
+	 
+	 
+	 
+	 
+	 
+	 
+    <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+         <filter class="solr.LengthFilterFactory" min="2" max="100"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+      </analyzer>
+    </fieldType>
+    
+    
+     <fieldType name="author" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+      <charFilter class="solr.PatternReplaceCharFilterFactory" 
+                    pattern="(\[Hrsg.\])" replacement=""/>
+        <charFilter class="solr.MappingCharFilterFactory" mapping="author_replace.txt"/>
+        <tokenizer class="solr.PatternTokenizerFactory" pattern="\s*[;\n\r]\s*"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+         <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+         <filter class="solr.LengthFilterFactory" min="2" max="100"/>
+         <filter class="solr.TrimFilterFactory"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.PatternTokenizerFactory" pattern="\s*;\s*"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.TrimFilterFactory"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+      </analyzer>
+    </fieldType>
+    
+     <fieldType name="author_c" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+      <charFilter class="solr.PatternReplaceCharFilterFactory" 
+                    pattern="(\[Hrsg.\])" replacement=""/>
+        <charFilter class="solr.MappingCharFilterFactory" mapping="author_replace.txt"/>
+        <tokenizer class="solr.PatternTokenizerFactory" pattern="\s*[;\n\r]\s*"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+         <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+         <filter class="solr.LengthFilterFactory" min="2" max="100"/>
+         <filter class="solr.TrimFilterFactory"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+       
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.PatternTokenizerFactory" pattern="\s*;\s*"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.TrimFilterFactory"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+      </analyzer>
+    </fieldType>
+    
+    
+     <fieldType name="date2year" class="solr.TextField" positionIncrementGap="100">
+      <!--<analyzer type="index">
+        <tokenizer class="solr.PatternTokenizerFactory" pattern="?!(.*\.)"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.PatternTokenizerFactory" pattern="?!(.*\.)"/>
+      </analyzer>-->
+    </fieldType>
+    
+
+    <!-- A text field with defaults appropriate for English: it
+         tokenizes with StandardTokenizer, removes English stop words
+         (lang/stopwords_en.txt), down cases, protects words from protwords.txt, and
+         finally applies Porter's stemming.  The query time analyzer
+         also applies synonyms from synonyms.txt. -->
+    <fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+        <!-- Case insensitive stop word removal.
+          add enablePositionIncrements=true in both the index and query
+          analyzers to leave a 'gap' for more accurate phrase queries.
+        -->
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.LowerCaseFilterFactory"/>
+	<filter class="solr.EnglishPossessiveFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+	<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+        <filter class="solr.EnglishMinimalStemFilterFactory"/>
+	-->
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.LowerCaseFilterFactory"/>
+	<filter class="solr.EnglishPossessiveFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+	<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+        <filter class="solr.EnglishMinimalStemFilterFactory"/>
+	-->
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A text field with defaults appropriate for English, plus
+	 aggressive word-splitting and autophrase features enabled.
+	 This field is just like text_en, except it adds
+	 WordDelimiterFilter to enable splitting and matching of
+	 words on case-change, alpha numeric boundaries, and
+	 non-alphanumeric chars.  This means certain compound word
+	 cases will work, for example query "wi fi" will match
+	 document "WiFi" or "wi-fi".
+        -->
+    <fieldType name="text_en_splitting" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
+      <analyzer type="index">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <!-- in this example, we will only use synonyms at query time
+        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
+        -->
+        <!-- Case insensitive stop word removal.
+          add enablePositionIncrements=true in both the index and query
+          analyzers to leave a 'gap' for more accurate phrase queries.
+        -->
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory"
+                ignoreCase="true"
+                words="lang/stopwords_en.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+        <filter class="solr.PorterStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Less flexible matching, but less false matches.  Probably not ideal for product names,
+         but may be good for SKUs.  Can insert dashes in the wrong place and still match. -->
+    <fieldType name="text_en_splitting_tight" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_en.txt"/>
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+        <filter class="solr.EnglishMinimalStemFilterFactory"/>
+        <!-- this filter can remove any duplicate tokens that appear at the same position - sometimes
+             possible with WordDelimiterFilter in conjuncton with stemming. -->
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Just like text_general except it reverses the characters of
+	 each token, to enable more efficient leading wildcard queries. -->
+    <fieldType name="text_general_rev" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.ReversedWildcardFilterFactory" withOriginal="true"
+           maxPosAsterisk="3" maxPosQuestion="2" maxFractionAsterisk="0.33"/>
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- charFilter + WhitespaceTokenizer  -->
+    <!--
+    <fieldType name="text_char_norm" class="solr.TextField" positionIncrementGap="100" >
+      <analyzer>
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+      </analyzer>
+    </fieldType>
+    -->
+
+    <!-- This is an example of using the KeywordTokenizer along
+         With various TokenFilterFactories to produce a sortable field
+         that does not include some properties of the source text
+      -->
+    <fieldType name="alphaOnlySort" class="solr.TextField" sortMissingLast="true" omitNorms="true">
+      <analyzer>
+        <!-- KeywordTokenizer does no actual tokenizing, so the entire
+             input string is preserved as a single token
+          -->
+        <tokenizer class="solr.KeywordTokenizerFactory"/>
+        <!-- The LowerCase TokenFilter does what you expect, which can be
+             when you want your sorting to be case insensitive
+          -->
+        <filter class="solr.LowerCaseFilterFactory" />
+        <!-- The TrimFilter removes any leading or trailing whitespace -->
+        <filter class="solr.TrimFilterFactory" />
+        <!-- The PatternReplaceFilter gives you the flexibility to use
+             Java Regular expression to replace any sequence of characters
+             matching a pattern with an arbitrary replacement string, 
+             which may include back references to portions of the original
+             string matched by the pattern.
+             
+             See the Java Regular Expression documentation for more
+             information on pattern and replacement string syntax.
+             
+             http://java.sun.com/j2se/1.6.0/docs/api/java/util/regex/package-summary.html
+          -->
+        <filter class="solr.PatternReplaceFilterFactory"
+                pattern="([^a-z])" replacement="" replace="all"
+        />
+      </analyzer>
+    </fieldType>
+    
+    <fieldtype name="phonetic" stored="false" indexed="true" class="solr.TextField" >
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.DoubleMetaphoneFilterFactory" inject="false"/>
+      </analyzer>
+    </fieldtype>
+
+    <fieldtype name="payloads" stored="false" indexed="true" class="solr.TextField" >
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <!--
+        The DelimitedPayloadTokenFilter can put payloads on tokens... for example,
+        a token of "foo|1.4"  would be indexed as "foo" with a payload of 1.4f
+        Attributes of the DelimitedPayloadTokenFilterFactory : 
+         "delimiter" - a one character delimiter. Default is | (pipe)
+	 "encoder" - how to encode the following value into a playload
+	    float -> org.apache.lucene.analysis.payloads.FloatEncoder,
+	    integer -> o.a.l.a.p.IntegerEncoder
+	    identity -> o.a.l.a.p.IdentityEncoder
+            Fully Qualified class name implementing PayloadEncoder, Encoder must have a no arg constructor.
+         -->
+        <filter class="solr.DelimitedPayloadTokenFilterFactory" encoder="float"/>
+      </analyzer>
+    </fieldtype>
+
+    <!-- lowercases the entire field value, keeping it as a single token.  -->
+    <fieldType name="lowercase" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.KeywordTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory" />
+      </analyzer>
+    </fieldType>
+
+    <!-- 
+      Example of using PathHierarchyTokenizerFactory at index time, so
+      queries for paths match documents at that path, or in descendent paths
+    -->
+    <fieldType name="descendent_path" class="solr.TextField">
+      <analyzer type="index">
+	<tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/" />
+      </analyzer>
+      <analyzer type="query">
+	<tokenizer class="solr.KeywordTokenizerFactory" />
+      </analyzer>
+    </fieldType>
+    <!-- 
+      Example of using PathHierarchyTokenizerFactory at query time, so
+      queries for paths match documents at that path, or in ancestor paths
+    -->
+    <fieldType name="ancestor_path" class="solr.TextField">
+      <analyzer type="index">
+	<tokenizer class="solr.KeywordTokenizerFactory" />
+      </analyzer>
+      <analyzer type="query">
+	<tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/" />
+      </analyzer>
+    </fieldType>
+
+    <!-- since fields of this type are by default not stored or indexed,
+         any data added to them will be ignored outright.  --> 
+    <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
+
+    <!-- This point type indexes the coordinates as separate fields (subFields)
+      If subFieldType is defined, it references a type, and a dynamic field
+      definition is created matching *___<typename>.  Alternately, if 
+      subFieldSuffix is defined, that is used to create the subFields.
+      Example: if subFieldType="double", then the coordinates would be
+        indexed in fields myloc_0___double,myloc_1___double.
+      Example: if subFieldSuffix="_d" then the coordinates would be indexed
+        in fields myloc_0_d,myloc_1_d
+      The subFields are an implementation detail of the fieldType, and end
+      users normally should not need to know about them.
+     -->
+    <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
+
+    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
+    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
+
+    <!-- An alternative geospatial field type new to Solr 4.  It supports multiValued and polygon shapes.
+      For more information about this and other Spatial fields new to Solr 4, see:
+      http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4
+    -->
+    <!-- <fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType"
+        geo="true" distErrPct="0.025" maxDistErr="0.000009" units="degrees" /> -->
+
+   <!-- Money/currency field type. See http://wiki.apache.org/solr/MoneyFieldType
+        Parameters:
+          defaultCurrency: Specifies the default currency if none specified. Defaults to "USD"
+          precisionStep:   Specifies the precisionStep for the TrieLong field used for the amount
+          providerClass:   Lets you plug in other exchange provider backend:
+                           solr.FileExchangeRateProvider is the default and takes one parameter:
+                             currencyConfig: name of an xml file holding exchange rates
+                           solr.OpenExchangeRatesOrgProvider uses rates from openexchangerates.org:
+                             ratesFileLocation: URL or path to rates JSON file (default latest.json on the web)
+                             refreshInterval: Number of minutes between each rates fetch (default: 1440, min: 60)
+   -->
+   <!-- <fieldType name="currency" class="solr.CurrencyField" precisionStep="8" defaultCurrency="USD" currencyConfig="currency.xml" /> -->
+
+   <!-- some examples for different languages (generally ordered by ISO code) -->
+
+    <!-- Arabic -->
+    <fieldType name="text_ar" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- for any non-arabic -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ar.txt" enablePositionIncrements="true"/>
+        <!-- normalizes ��� to ���, etc -->
+        <filter class="solr.ArabicNormalizationFilterFactory"/>
+        <filter class="solr.ArabicStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Bulgarian -->
+    <fieldType name="text_bg" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/> 
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_bg.txt" enablePositionIncrements="true"/>
+        <filter class="solr.BulgarianStemFilterFactory"/>       
+      </analyzer>
+    </fieldType>
+    
+    <!-- Catalan -->
+    <fieldType name="text_ca" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- removes l', etc -->
+        <filter class="solr.ElisionFilterFactory" ignoreCase="true" articles="lang/contractions_ca.txt"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ca.txt" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Catalan"/>       
+      </analyzer>
+    </fieldType>
+    
+    <!-- CJK bigram (see text_ja for a Japanese configuration using morphological analysis) -->
+    <fieldType name="text_cjk" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- normalize width before bigram, as e.g. half-width dakuten combine  -->
+        <filter class="solr.CJKWidthFilterFactory"/>
+        <!-- for any non-CJK -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.CJKBigramFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Czech -->
+    <fieldType name="text_cz" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_cz.txt" enablePositionIncrements="true"/>
+        <filter class="solr.CzechStemFilterFactory"/>       
+      </analyzer>
+    </fieldType>
+    
+    <!-- Danish -->
+    <fieldType name="text_da" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_da.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Danish"/>       
+      </analyzer>
+    </fieldType>
+    
+    <!-- German -->
+    <fieldType name="text_de" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_de.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.GermanNormalizationFilterFactory"/>
+        <filter class="solr.GermanLightStemFilterFactory"/>
+        <!-- less aggressive: <filter class="solr.GermanMinimalStemFilterFactory"/> -->
+        <!-- more aggressive: <filter class="solr.SnowballPorterFilterFactory" language="German2"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Greek -->
+    <fieldType name="text_el" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- greek specific lowercase for sigma -->
+        <filter class="solr.GreekLowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="false" words="lang/stopwords_el.txt" enablePositionIncrements="true"/>
+        <filter class="solr.GreekStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Spanish -->
+    <fieldType name="text_es" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_es.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SpanishLightStemFilterFactory"/>
+        <!-- more aggressive: <filter class="solr.SnowballPorterFilterFactory" language="Spanish"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Basque -->
+    <fieldType name="text_eu" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_eu.txt" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Basque"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Persian -->
+    <fieldType name="text_fa" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <!-- for ZWNJ -->
+        <charFilter class="solr.PersianCharFilterFactory"/>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.ArabicNormalizationFilterFactory"/>
+        <filter class="solr.PersianNormalizationFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_fa.txt" enablePositionIncrements="true"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Finnish -->
+    <fieldType name="text_fi" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_fi.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Finnish"/>
+        <!-- less aggressive: <filter class="solr.FinnishLightStemFilterFactory"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- French -->
+    <fieldType name="text_fr" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- removes l', etc -->
+        <filter class="solr.ElisionFilterFactory" ignoreCase="true" articles="lang/contractions_fr.txt"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_fr.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.FrenchLightStemFilterFactory"/>
+        <!-- less aggressive: <filter class="solr.FrenchMinimalStemFilterFactory"/> -->
+        <!-- more aggressive: <filter class="solr.SnowballPorterFilterFactory" language="French"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Irish -->
+    <fieldType name="text_ga" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- removes d', etc -->
+        <filter class="solr.ElisionFilterFactory" ignoreCase="true" articles="lang/contractions_ga.txt"/>
+        <!-- removes n-, etc. position increments is intentionally false! -->
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/hyphenations_ga.txt" enablePositionIncrements="false"/>
+        <filter class="solr.IrishLowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ga.txt" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Irish"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Galician -->
+    <fieldType name="text_gl" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_gl.txt" enablePositionIncrements="true"/>
+        <filter class="solr.GalicianStemFilterFactory"/>
+        <!-- less aggressive: <filter class="solr.GalicianMinimalStemFilterFactory"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Hindi -->
+    <fieldType name="text_hi" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <!-- normalizes unicode representation -->
+        <filter class="solr.IndicNormalizationFilterFactory"/>
+        <!-- normalizes variation in spelling -->
+        <filter class="solr.HindiNormalizationFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_hi.txt" enablePositionIncrements="true"/>
+        <filter class="solr.HindiStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Hungarian -->
+    <fieldType name="text_hu" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_hu.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Hungarian"/>
+        <!-- less aggressive: <filter class="solr.HungarianLightStemFilterFactory"/> -->   
+      </analyzer>
+    </fieldType>
+    
+    <!-- Armenian -->
+    <fieldType name="text_hy" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_hy.txt" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Armenian"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Indonesian -->
+    <fieldType name="text_id" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_id.txt" enablePositionIncrements="true"/>
+        <!-- for a less aggressive approach (only inflectional suffixes), set stemDerivational to false -->
+        <filter class="solr.IndonesianStemFilterFactory" stemDerivational="true"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Italian -->
+    <fieldType name="text_it" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- removes l', etc -->
+        <filter class="solr.ElisionFilterFactory" ignoreCase="true" articles="lang/contractions_it.txt"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_it.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.ItalianLightStemFilterFactory"/>
+        <!-- more aggressive: <filter class="solr.SnowballPorterFilterFactory" language="Italian"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Japanese using morphological analysis (see text_cjk for a configuration using bigramming)
+
+         NOTE: If you want to optimize search for precision, use default operator AND in your query
+         parser config with <solrQueryParser defaultOperator="AND"/> further down in this file.  Use 
+         OR if you would like to optimize for recall (default).
+    -->
+    <fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
+      <analyzer>
+      <!-- Kuromoji Japanese morphological analyzer/tokenizer (JapaneseTokenizer)
+
+           Kuromoji has a search mode (default) that does segmentation useful for search.  A heuristic
+           is used to segment compounds into its parts and the compound itself is kept as synonym.
+
+           Valid values for attribute mode are:
+              normal: regular segmentation
+              search: segmentation useful for search with synonyms compounds (default)
+            extended: same as search mode, but unigrams unknown words (experimental)
+
+           For some applications it might be good to use search mode for indexing and normal mode for
+           queries to reduce recall and prevent parts of compounds from being matched and highlighted.
+           Use <analyzer type="index"> and <analyzer type="query"> for this and mode normal in query.
+
+           Kuromoji also has a convenient user dictionary feature that allows overriding the statistical
+           model with your own entries for segmentation, part-of-speech tags and readings without a need
+           to specify weights.  Notice that user dictionaries have not been subject to extensive testing.
+
+           User dictionary attributes are:
+                     userDictionary: user dictionary filename
+             userDictionaryEncoding: user dictionary encoding (default is UTF-8)
+
+           See lang/userdict_ja.txt for a sample user dictionary file.
+
+           Punctuation characters are discarded by default.  Use discardPunctuation="false" to keep them.
+
+           See http://wiki.apache.org/solr/JapaneseLanguageSupport for more on Japanese language support.
+        -->
+        <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/>
+        <!--<tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_ja.txt"/>-->
+        <!-- Reduces inflected verbs and adjectives to their base/dictionary forms (���������) -->
+        <filter class="solr.JapaneseBaseFormFilterFactory"/>
+        <!-- Removes tokens with certain part-of-speech tags -->
+        <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" enablePositionIncrements="true"/>
+        <!-- Normalizes full-width romaji to half-width and half-width kana to full-width (Unicode NFKC subset) -->
+        <filter class="solr.CJKWidthFilterFactory"/>
+        <!-- Removes common tokens typically not useful for search, but have a negative effect on ranking -->
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" enablePositionIncrements="true" />
+        <!-- Normalizes common katakana spelling variations by removing any last long sound character (U+30FC) -->
+        <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
+        <!-- Lower-cases romaji characters -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Latvian -->
+    <fieldType name="text_lv" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_lv.txt" enablePositionIncrements="true"/>
+        <filter class="solr.LatvianStemFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Dutch -->
+    <fieldType name="text_nl" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_nl.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.StemmerOverrideFilterFactory" dictionary="lang/stemdict_nl.txt" ignoreCase="false"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Dutch"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Norwegian -->
+    <fieldType name="text_no" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_no.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Norwegian"/>
+        <!-- less aggressive: <filter class="solr.NorwegianLightStemFilterFactory"/> -->
+        <!-- singular/plural: <filter class="solr.NorwegianMinimalStemFilterFactory"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Portuguese -->
+    <fieldType name="text_pt" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_pt.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.PortugueseLightStemFilterFactory"/>
+        <!-- less aggressive: <filter class="solr.PortugueseMinimalStemFilterFactory"/> -->
+        <!-- more aggressive: <filter class="solr.SnowballPorterFilterFactory" language="Portuguese"/> -->
+        <!-- most aggressive: <filter class="solr.PortugueseStemFilterFactory"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Romanian -->
+    <fieldType name="text_ro" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ro.txt" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Romanian"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Russian -->
+    <fieldType name="text_ru" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ru.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Russian"/>
+        <!-- less aggressive: <filter class="solr.RussianLightStemFilterFactory"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Swedish -->
+    <fieldType name="text_sv" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_sv.txt" format="snowball" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Swedish"/>
+        <!-- less aggressive: <filter class="solr.SwedishLightStemFilterFactory"/> -->
+      </analyzer>
+    </fieldType>
+    
+    <!-- Thai -->
+    <fieldType name="text_th" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.ThaiWordFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_th.txt" enablePositionIncrements="true"/>
+      </analyzer>
+    </fieldType>
+    
+    <!-- Turkish -->
+    <fieldType name="text_tr" class="solr.TextField" positionIncrementGap="100">
+      <analyzer> 
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.TurkishLowerCaseFilterFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="false" words="lang/stopwords_tr.txt" enablePositionIncrements="true"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="Turkish"/>
+      </analyzer>
+    </fieldType>
+
+ </types>
+  
+  <!-- Similarity is the scoring routine for each document vs. a query.
+       A custom Similarity or SimilarityFactory may be specified here, but 
+       the default is fine for most applications.  
+       For more info: http://wiki.apache.org/solr/SchemaXml#Similarity
+    -->
+  <!--
+     <similarity class="com.example.solr.CustomSimilarityFactory">
+       <str name="paramkey">param value</str>
+     </similarity>
+    -->
+
+</schema>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solr/solrconfig.xml	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,1940 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- 
+     For more details about configurations options that may appear in
+     this file, see http://wiki.apache.org/solr/SolrConfigXml. 
+-->
+<config>
+  <!-- In all configuration below, a prefix of "solr." for class names
+       is an alias that causes solr to search appropriate packages,
+       including org.apache.solr.(search|update|request|core|analysis)
+
+       You may also specify a fully qualified Java classname if you
+       have your own custom plugins.
+    -->
+
+  <!-- Controls what version of Lucene various components of Solr
+       adhere to.  Generally, you want to use the latest version to
+       get all bug fixes and improvements. It is highly recommended
+       that you fully re-index after changing this setting as it can
+       affect both how text is indexed and queried.
+  -->
+  <luceneMatchVersion>LUCENE_40</luceneMatchVersion>
+
+  <!-- <lib/> directives can be used to instruct Solr to load an Jars
+       identified and use them to resolve any "plugins" specified in
+       your solrconfig.xml or schema.xml (ie: Analyzers, Request
+       Handlers, etc...).
+
+       All directories and paths are resolved relative to the
+       instanceDir.
+
+       Please note that <lib/> directives are processed in the order
+       that they appear in your solrconfig.xml file, and are "stacked" 
+       on top of each other when building a ClassLoader - so if you have 
+       plugin jars with dependencies on other jars, the "lower level" 
+       dependency jars should be loaded first.
+
+       If a "./lib" directory exists in your instanceDir, all files
+       found in it are included as if you had used the following
+       syntax...
+       
+              <lib dir="./lib" />
+    -->
+
+  <!-- A 'dir' option by itself adds any files found in the directory 
+       to the classpath, this is useful for including all jars in a
+       directory.
+
+       When a 'regex' is specified in addition to a 'dir', only the
+       files in that directory which completely match the regex
+       (anchored on both ends) will be included.
+
+       The examples below can be used to load some solr-contribs along 
+       with their external dependencies.
+    -->
+  <lib dir="../../../extension/" regex=".*\.jar" />
+  <lib dir="../../../dist/" regex="solr-dataimporthandler-\d.*\.jar" />
+  <lib dir="../../../contrib/extraction/lib" regex=".*\.jar" />
+  <lib dir="../../../dist/" regex="solr-cell-\d.*\.jar" />
+
+  <lib dir="../../../contrib/clustering/lib/" regex=".*\.jar" />
+  <lib dir="../../../dist/" regex="solr-clustering-\d.*\.jar" />
+
+  <lib dir="../../../contrib/langid/lib/" regex=".*\.jar" />
+  <lib dir="../../../dist/" regex="solr-langid-\d.*\.jar" />
+
+  <lib dir="../../../contrib/velocity/lib" regex=".*\.jar" />
+  <lib dir="../../../dist/" regex="solr-velocity-\d.*\.jar" />
+
+  <!-- If a 'dir' option (with or without a regex) is used and nothing
+       is found that matches, it will be ignored
+    -->
+  <lib dir="/total/crap/dir/ignored" /> 
+
+  <!-- an exact 'path' can be used instead of a 'dir' to specify a 
+       specific jar file.  This will cause a serious error to be logged 
+       if it can't be loaded.
+    -->
+  <!--
+     <lib path="../a-jar-that-does-not-exist.jar" /> 
+  -->
+  
+  <!-- Data Directory
+
+       Used to specify an alternate directory to hold all index data
+       other than the default ./data under the Solr home.  If
+       replication is in use, this should match the replication
+       configuration.
+    -->
+  <dataDir>${solr.data.dir:}</dataDir>
+
+
+  <!-- The DirectoryFactory to use for indexes.
+       
+       solr.StandardDirectoryFactory is filesystem
+       based and tries to pick the best implementation for the current
+       JVM and platform.  solr.NRTCachingDirectoryFactory, the default,
+       wraps solr.StandardDirectoryFactory and caches small files in memory
+       for better NRT performance.
+
+       One can force a particular implementation via solr.MMapDirectoryFactory,
+       solr.NIOFSDirectoryFactory, or solr.SimpleFSDirectoryFactory.
+
+       solr.RAMDirectoryFactory is memory based, not
+       persistent, and doesn't work with replication.
+    -->
+  <directoryFactory name="DirectoryFactory" 
+                    class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/> 
+
+  <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+       Index Config - These settings control low-level behavior of indexing
+       Most example settings here show the default value, but are commented
+       out, to more easily see where customizations have been made.
+       
+       Note: This replaces <indexDefaults> and <mainIndex> from older versions
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+  <indexConfig>
+    <!-- maxFieldLength was removed in 4.0. To get similar behavior, include a 
+         LimitTokenCountFilterFactory in your fieldType definition. E.g. 
+     <filter class="solr.LimitTokenCountFilterFactory" maxTokenCount="10000"/>
+    -->
+    <!-- Maximum time to wait for a write lock (ms) for an IndexWriter. Default: 1000 -->
+    <!-- <writeLockTimeout>1000</writeLockTimeout>  -->
+
+    <!-- Expert: Enabling compound file will use less files for the index, 
+         using fewer file descriptors on the expense of performance decrease. 
+         Default in Lucene is "true". Default in Solr is "false" (since 3.6) -->
+    <!-- <useCompoundFile>false</useCompoundFile> -->
+
+    <!-- ramBufferSizeMB sets the amount of RAM that may be used by Lucene
+         indexing for buffering added documents and deletions before they are
+         flushed to the Directory.
+         maxBufferedDocs sets a limit on the number of documents buffered
+         before flushing.
+         If both ramBufferSizeMB and maxBufferedDocs is set, then
+         Lucene will flush based on whichever limit is hit first.  -->
+    <!-- <ramBufferSizeMB>32</ramBufferSizeMB> -->
+    <!-- <maxBufferedDocs>1000</maxBufferedDocs> -->
+
+    <!-- Expert: Merge Policy 
+         The Merge Policy in Lucene controls how merging of segments is done.
+         The default since Solr/Lucene 3.3 is TieredMergePolicy.
+         The default since Lucene 2.3 was the LogByteSizeMergePolicy,
+         Even older versions of Lucene used LogDocMergePolicy.
+      -->
+    <!--
+        <mergePolicy class="org.apache.lucene.index.TieredMergePolicy">
+          <int name="maxMergeAtOnce">10</int>
+          <int name="segmentsPerTier">10</int>
+        </mergePolicy>
+      -->
+       
+    <!-- Merge Factor
+         The merge factor controls how many segments will get merged at a time.
+         For TieredMergePolicy, mergeFactor is a convenience parameter which
+         will set both MaxMergeAtOnce and SegmentsPerTier at once.
+         For LogByteSizeMergePolicy, mergeFactor decides how many new segments
+         will be allowed before they are merged into one.
+         Default is 10 for both merge policies.
+      -->
+    <!-- 
+    <mergeFactor>10</mergeFactor>
+      -->
+
+    <!-- Expert: Merge Scheduler
+         The Merge Scheduler in Lucene controls how merges are
+         performed.  The ConcurrentMergeScheduler (Lucene 2.3 default)
+         can perform merges in the background using separate threads.
+         The SerialMergeScheduler (Lucene 2.2 default) does not.
+     -->
+    <!-- 
+       <mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler"/>
+       -->
+
+    <!-- LockFactory 
+
+         This option specifies which Lucene LockFactory implementation
+         to use.
+      
+         single = SingleInstanceLockFactory - suggested for a
+                  read-only index or when there is no possibility of
+                  another process trying to modify the index.
+         native = NativeFSLockFactory - uses OS native file locking.
+                  Do not use when multiple solr webapps in the same
+                  JVM are attempting to share a single index.
+         simple = SimpleFSLockFactory  - uses a plain file for locking
+
+         Defaults: 'native' is default for Solr3.6 and later, otherwise
+                   'simple' is the default
+
+         More details on the nuances of each LockFactory...
+         http://wiki.apache.org/lucene-java/AvailableLockFactories
+    -->
+    <!-- <lockType>native</lockType> -->
+
+    <!-- Unlock On Startup
+
+         If true, unlock any held write or commit locks on startup.
+         This defeats the locking mechanism that allows multiple
+         processes to safely access a lucene index, and should be used
+         with care. Default is "false".
+
+         This is not needed if lock type is 'none' or 'single'
+     -->
+    <!--
+    <unlockOnStartup>false</unlockOnStartup>
+      -->
+    
+    <!-- Expert: Controls how often Lucene loads terms into memory
+         Default is 128 and is likely good for most everyone.
+      -->
+    <!-- <termIndexInterval>128</termIndexInterval> -->
+
+    <!-- If true, IndexReaders will be reopened (often more efficient)
+         instead of closed and then opened. Default: true
+      -->
+    <!-- 
+    <reopenReaders>true</reopenReaders>
+      -->
+
+    <!-- Commit Deletion Policy
+
+         Custom deletion policies can be specified here. The class must
+         implement org.apache.lucene.index.IndexDeletionPolicy.
+
+         http://lucene.apache.org/java/3_5_0/api/core/org/apache/lucene/index/IndexDeletionPolicy.html
+
+         The default Solr IndexDeletionPolicy implementation supports
+         deleting index commit points on number of commits, age of
+         commit point and optimized status.
+         
+         The latest commit point should always be preserved regardless
+         of the criteria.
+    -->
+    <!-- 
+    <deletionPolicy class="solr.SolrDeletionPolicy">
+    -->
+      <!-- The number of commit points to be kept -->
+      <!-- <str name="maxCommitsToKeep">1</str> -->
+      <!-- The number of optimized commit points to be kept -->
+      <!-- <str name="maxOptimizedCommitsToKeep">0</str> -->
+      <!--
+          Delete all commit points once they have reached the given age.
+          Supports DateMathParser syntax e.g.
+        -->
+      <!--
+         <str name="maxCommitAge">30MINUTES</str>
+         <str name="maxCommitAge">1DAY</str>
+      -->
+    <!-- 
+    </deletionPolicy>
+    -->
+
+    <!-- Lucene Infostream
+       
+         To aid in advanced debugging, Lucene provides an "InfoStream"
+         of detailed information when indexing.
+
+         Setting The value to true will instruct the underlying Lucene
+         IndexWriter to write its debugging info the specified file
+      -->
+     <!-- <infoStream file="INFOSTREAM.txt">false</infoStream> --> 
+  </indexConfig>
+
+
+  <!-- JMX
+       
+       This example enables JMX if and only if an existing MBeanServer
+       is found, use this if you want to configure JMX through JVM
+       parameters. Remove this to disable exposing Solr configuration
+       and statistics to JMX.
+
+       For more details see http://wiki.apache.org/solr/SolrJmx
+    -->
+  <jmx />
+  <!-- If you want to connect to a particular server, specify the
+       agentId 
+    -->
+  <!-- <jmx agentId="myAgent" /> -->
+  <!-- If you want to start a new MBeanServer, specify the serviceUrl -->
+  <!-- <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr"/>
+    -->
+
+  <!-- The default high-performance update handler -->
+  <updateHandler class="solr.DirectUpdateHandler2">
+
+    <!-- AutoCommit
+
+         Perform a hard commit automatically under certain conditions.
+         Instead of enabling autoCommit, consider using "commitWithin"
+         when adding documents. 
+
+         http://wiki.apache.org/solr/UpdateXmlMessages
+
+         maxDocs - Maximum number of documents to add since the last
+                   commit before automatically triggering a new commit.
+
+         maxTime - Maximum amount of time in ms that is allowed to pass
+                   since a document was added before automaticly
+                   triggering a new commit. 
+         openSearcher - if false, the commit causes recent index changes
+         to be flushed to stable storage, but does not cause a new
+         searcher to be opened to make those changes visible.
+      -->
+     <autoCommit> 
+       <maxTime>15000</maxTime> 
+       <openSearcher>false</openSearcher> 
+     </autoCommit>
+
+    <!-- softAutoCommit is like autoCommit except it causes a
+         'soft' commit which only ensures that changes are visible
+         but does not ensure that data is synced to disk.  This is
+         faster and more near-realtime friendly than a hard commit.
+      -->
+     <!--
+       <autoSoftCommit> 
+         <maxTime>1000</maxTime> 
+       </autoSoftCommit>
+      -->
+
+    <!-- Update Related Event Listeners
+         
+         Various IndexWriter related events can trigger Listeners to
+         take actions.
+
+         postCommit - fired after every commit or optimize command
+         postOptimize - fired after every optimize command
+      -->
+    <!-- The RunExecutableListener executes an external command from a
+         hook such as postCommit or postOptimize.
+         
+         exe - the name of the executable to run
+         dir - dir to use as the current working directory. (default=".")
+         wait - the calling thread waits until the executable returns. 
+                (default="true")
+         args - the arguments to pass to the program.  (default is none)
+         env - environment variables to set.  (default is none)
+      -->
+    <!-- This example shows how RunExecutableListener could be used
+         with the script based replication...
+         http://wiki.apache.org/solr/CollectionDistribution
+      -->
+    <!--
+       <listener event="postCommit" class="solr.RunExecutableListener">
+         <str name="exe">solr/bin/snapshooter</str>
+         <str name="dir">.</str>
+         <bool name="wait">true</bool>
+         <arr name="args"> <str>arg1</str> <str>arg2</str> </arr>
+         <arr name="env"> <str>MYVAR=val1</str> </arr>
+       </listener>
+      -->
+
+    <!-- Enables a transaction log, currently used for real-time get.
+         "dir" - the target directory for transaction logs, defaults to the
+            solr data directory.  --> 
+    <updateLog>
+      <str name="dir">${solr.data.dir:}</str>
+    </updateLog>
+   
+
+  </updateHandler>
+  
+  <!-- IndexReaderFactory
+
+       Use the following format to specify a custom IndexReaderFactory,
+       which allows for alternate IndexReader implementations.
+
+       ** Experimental Feature **
+
+       Please note - Using a custom IndexReaderFactory may prevent
+       certain other features from working. The API to
+       IndexReaderFactory may change without warning or may even be
+       removed from future releases if the problems cannot be
+       resolved.
+
+
+       ** Features that may not work with custom IndexReaderFactory **
+
+       The ReplicationHandler assumes a disk-resident index. Using a
+       custom IndexReader implementation may cause incompatibility
+       with ReplicationHandler and may cause replication to not work
+       correctly. See SOLR-1366 for details.
+
+    -->
+  <!--
+  <indexReaderFactory name="IndexReaderFactory" class="package.class">
+    <str name="someArg">Some Value</str>
+  </indexReaderFactory >
+  -->
+  <!-- By explicitly declaring the Factory, the termIndexDivisor can
+       be specified.
+    -->
+  <!--
+     <indexReaderFactory name="IndexReaderFactory" 
+                         class="solr.StandardIndexReaderFactory">
+       <int name="setTermIndexDivisor">12</int>
+     </indexReaderFactory >
+    -->
+
+  <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+       Query section - these settings control query time things like caches
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
+  <query>
+    <!-- Max Boolean Clauses
+
+         Maximum number of clauses in each BooleanQuery,  an exception
+         is thrown if exceeded.
+
+         ** WARNING **
+         
+         This option actually modifies a global Lucene property that
+         will affect all SolrCores.  If multiple solrconfig.xml files
+         disagree on this property, the value at any given moment will
+         be based on the last SolrCore to be initialized.
+         
+      -->
+    <maxBooleanClauses>1024</maxBooleanClauses>
+
+
+    <!-- Solr Internal Query Caches
+
+         There are two implementations of cache available for Solr,
+         LRUCache, based on a synchronized LinkedHashMap, and
+         FastLRUCache, based on a ConcurrentHashMap.  
+
+         FastLRUCache has faster gets and slower puts in single
+         threaded operation and thus is generally faster than LRUCache
+         when the hit ratio of the cache is high (> 75%), and may be
+         faster under other scenarios on multi-cpu systems.
+    -->
+
+    <!-- Filter Cache
+
+         Cache used by SolrIndexSearcher for filters (DocSets),
+         unordered sets of *all* documents that match a query.  When a
+         new searcher is opened, its caches may be prepopulated or
+         "autowarmed" using data from caches in the old searcher.
+         autowarmCount is the number of items to prepopulate.  For
+         LRUCache, the autowarmed items will be the most recently
+         accessed items.
+
+         Parameters:
+           class - the SolrCache implementation LRUCache or
+               (LRUCache or FastLRUCache)
+           size - the maximum number of entries in the cache
+           initialSize - the initial capacity (number of entries) of
+               the cache.  (see java.util.HashMap)
+           autowarmCount - the number of entries to prepopulate from
+               and old cache.  
+      -->
+    <filterCache class="solr.FastLRUCache"
+                 size="512"
+                 initialSize="512"
+                 autowarmCount="0"/>
+
+    <!-- Query Result Cache
+         
+         Caches results of searches - ordered lists of document ids
+         (DocList) based on a query, a sort, and the range of documents requested.  
+      -->
+    <queryResultCache class="solr.LRUCache"
+                     size="512"
+                     initialSize="512"
+                     autowarmCount="0"/>
+   
+    <!-- Document Cache
+
+         Caches Lucene Document objects (the stored fields for each
+         document).  Since Lucene internal document ids are transient,
+         this cache will not be autowarmed.  
+      -->
+    <documentCache class="solr.LRUCache"
+                   size="512"
+                   initialSize="512"
+                   autowarmCount="0"/>
+    
+    <!-- Field Value Cache
+         
+         Cache used to hold field values that are quickly accessible
+         by document id.  The fieldValueCache is created by default
+         even if not configured here.
+      -->
+    <!--
+       <fieldValueCache class="solr.FastLRUCache"
+                        size="512"
+                        autowarmCount="128"
+                        showItems="32" />
+      -->
+
+    <!-- Custom Cache
+
+         Example of a generic cache.  These caches may be accessed by
+         name through SolrIndexSearcher.getCache(),cacheLookup(), and
+         cacheInsert().  The purpose is to enable easy caching of
+         user/application level data.  The regenerator argument should
+         be specified as an implementation of solr.CacheRegenerator 
+         if autowarming is desired.  
+      -->
+    <!--
+       <cache name="myUserCache"
+              class="solr.LRUCache"
+              size="4096"
+              initialSize="1024"
+              autowarmCount="1024"
+              regenerator="com.mycompany.MyRegenerator"
+              />
+      -->
+
+
+    <!-- Lazy Field Loading
+
+         If true, stored fields that are not requested will be loaded
+         lazily.  This can result in a significant speed improvement
+         if the usual case is to not load all stored fields,
+         especially if the skipped fields are large compressed text
+         fields.
+    -->
+    <enableLazyFieldLoading>true</enableLazyFieldLoading>
+
+   <!-- Use Filter For Sorted Query
+
+        A possible optimization that attempts to use a filter to
+        satisfy a search.  If the requested sort does not include
+        score, then the filterCache will be checked for a filter
+        matching the query. If found, the filter will be used as the
+        source of document ids, and then the sort will be applied to
+        that.
+
+        For most situations, this will not be useful unless you
+        frequently get the same search repeatedly with different sort
+        options, and none of them ever use "score"
+     -->
+   <!--
+      <useFilterForSortedQuery>true</useFilterForSortedQuery>
+     -->
+
+   <!-- Result Window Size
+
+        An optimization for use with the queryResultCache.  When a search
+        is requested, a superset of the requested number of document ids
+        are collected.  For example, if a search for a particular query
+        requests matching documents 10 through 19, and queryWindowSize is 50,
+        then documents 0 through 49 will be collected and cached.  Any further
+        requests in that range can be satisfied via the cache.  
+     -->
+   <queryResultWindowSize>20</queryResultWindowSize>
+
+   <!-- Maximum number of documents to cache for any entry in the
+        queryResultCache. 
+     -->
+   <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
+
+   <!-- Query Related Event Listeners
+
+        Various IndexSearcher related events can trigger Listeners to
+        take actions.
+
+        newSearcher - fired whenever a new searcher is being prepared
+        and there is a current searcher handling requests (aka
+        registered).  It can be used to prime certain caches to
+        prevent long request times for certain requests.
+
+        firstSearcher - fired whenever a new searcher is being
+        prepared but there is no current registered searcher to handle
+        requests or to gain autowarming data from.
+
+        
+     -->
+    <!-- QuerySenderListener takes an array of NamedList and executes a
+         local query request for each NamedList in sequence. 
+      -->
+    <listener event="newSearcher" class="solr.QuerySenderListener">
+      <arr name="queries">
+        <!--
+           <lst><str name="q">solr</str><str name="sort">price asc</str></lst>
+           <lst><str name="q">rocks</str><str name="sort">weight asc</str></lst>
+          -->
+      </arr>
+    </listener>
+    <listener event="firstSearcher" class="solr.QuerySenderListener">
+      <arr name="queries">
+        <lst>
+          <str name="q">static firstSearcher warming in solrconfig.xml</str>
+        </lst>
+      </arr>
+    </listener>
+
+    <!-- Use Cold Searcher
+
+         If a search request comes in and there is no current
+         registered searcher, then immediately register the still
+         warming searcher and use it.  If "false" then all requests
+         will block until the first searcher is done warming.
+      -->
+    <useColdSearcher>false</useColdSearcher>
+
+    <!-- Max Warming Searchers
+         
+         Maximum number of searchers that may be warming in the
+         background concurrently.  An error is returned if this limit
+         is exceeded.
+
+         Recommend values of 1-2 for read-only slaves, higher for
+         masters w/o cache warming.
+      -->
+    <maxWarmingSearchers>2</maxWarmingSearchers>
+
+  </query>
+
+
+  <!-- Request Dispatcher
+
+       This section contains instructions for how the SolrDispatchFilter
+       should behave when processing requests for this SolrCore.
+
+       handleSelect is a legacy option that affects the behavior of requests
+       such as /select?qt=XXX
+
+       handleSelect="true" will cause the SolrDispatchFilter to process
+       the request and dispatch the query to a handler specified by the
+       "qt" param, assuming "/select" isn't already registered.
+
+       handleSelect="false" will cause the SolrDispatchFilter to
+       ignore "/select" requests, resulting in a 404 unless a handler
+       is explicitly registered with the name "/select"
+
+       handleSelect="true" is not recommended for new users, but is the default
+       for backwards compatibility
+    -->
+  <requestDispatcher handleSelect="false" >
+    <!-- Request Parsing
+
+         These settings indicate how Solr Requests may be parsed, and
+         what restrictions may be placed on the ContentStreams from
+         those requests
+
+         enableRemoteStreaming - enables use of the stream.file
+         and stream.url parameters for specifying remote streams.
+
+         multipartUploadLimitInKB - specifies the max size of
+         Multipart File Uploads that Solr will allow in a Request.
+         
+         *** WARNING ***
+         The settings below authorize Solr to fetch remote files, You
+         should make sure your system has some authentication before
+         using enableRemoteStreaming="true"
+
+      --> 
+    <requestParsers enableRemoteStreaming="true" 
+                    multipartUploadLimitInKB="2048000" />
+
+    <!-- HTTP Caching
+
+         Set HTTP caching related parameters (for proxy caches and clients).
+
+         The options below instruct Solr not to output any HTTP Caching
+         related headers
+      -->
+    <httpCaching never304="true" />
+    <!-- If you include a <cacheControl> directive, it will be used to
+         generate a Cache-Control header (as well as an Expires header
+         if the value contains "max-age=")
+         
+         By default, no Cache-Control header is generated.
+         
+         You can use the <cacheControl> option even if you have set
+         never304="true"
+      -->
+    <!--
+       <httpCaching never304="true" >
+         <cacheControl>max-age=30, public</cacheControl> 
+       </httpCaching>
+      -->
+    <!-- To enable Solr to respond with automatically generated HTTP
+         Caching headers, and to response to Cache Validation requests
+         correctly, set the value of never304="false"
+         
+         This will cause Solr to generate Last-Modified and ETag
+         headers based on the properties of the Index.
+
+         The following options can also be specified to affect the
+         values of these headers...
+
+         lastModFrom - the default value is "openTime" which means the
+         Last-Modified value (and validation against If-Modified-Since
+         requests) will all be relative to when the current Searcher
+         was opened.  You can change it to lastModFrom="dirLastMod" if
+         you want the value to exactly correspond to when the physical
+         index was last modified.
+
+         etagSeed="..." is an option you can change to force the ETag
+         header (and validation against If-None-Match requests) to be
+         different even if the index has not changed (ie: when making
+         significant changes to your config file)
+
+         (lastModifiedFrom and etagSeed are both ignored if you use
+         the never304="true" option)
+      -->
+    <!--
+       <httpCaching lastModifiedFrom="openTime"
+                    etagSeed="Solr">
+         <cacheControl>max-age=30, public</cacheControl> 
+       </httpCaching>
+      -->
+  </requestDispatcher>
+
+  <requestHandler name="/dataimport"
+		  class="org.apache.solr.handler.dataimport.DataImportHandler">
+    <lst name="defaults">
+      <str name="config">data-config.xml</str>
+    </lst>
+  </requestHandler> 
+
+
+
+
+  <!-- Request Handlers 
+
+       http://wiki.apache.org/solr/SolrRequestHandler
+
+       Incoming queries will be dispatched to a specific handler by name
+       based on the path specified in the request.
+
+       Legacy behavior: If the request path uses "/select" but no Request
+       Handler has that name, and if handleSelect="true" has been specified in
+       the requestDispatcher, then the Request Handler is dispatched based on
+       the qt parameter.  Handlers without a leading '/' are accessed this way
+       like so: http://host/app/[core/]select?qt=name  If no qt is
+       given, then the requestHandler that declares default="true" will be
+       used or the one named "standard".
+     
+       If a Request Handler is declared with startup="lazy", then it will
+       not be initialized until the first request that uses it
+
+    -->
+  <!-- SearchHandler
+
+       http://wiki.apache.org/solr/SearchHandler
+
+       For processing Search Queries, the primary Request Handler
+       provided with Solr is "SearchHandler" It delegates to a sequent
+       of SearchComponents (see below) and supports distributed
+       queries across multiple shards
+    -->
+  <requestHandler name="/select" class="solr.SearchHandler">
+    <!-- default values for query parameters can be specified, these
+         will be overridden by parameters in the request
+      -->
+     <lst name="defaults">
+       <str name="echoParams">explicit</str>
+       <int name="rows">10</int>
+       <str name="df">text</str>
+         <str name="defType">edismax</str>
+        <str name="qf">
+          title^10.0 IM_author^12.5 author^8.0 all-bib-data^3.0 author_c^1.0 content^1.0
+	</str>
+          <str name="facet">on</str>
+       <str name="facet.field">doc-type</str>
+       <str name="facet.field">access-type</str>
+       <str name="facet.field">author</str>
+       <str name="facet.field">keyword</str>
+       <str name="facet.field">title</str>
+       <str name="facet.field">title_s</str>
+       <str name="facet.field">collection</str>
+    
+     </lst>
+    <!-- In addition to defaults, "appends" params can be specified
+         to identify values which should be appended to the list of
+         multi-val params from the query (or the existing "defaults").
+      -->
+    <!-- In this example, the param "fq=instock:true" would be appended to
+         any query time fq params the user may specify, as a mechanism for
+         partitioning the index, independent of any user selected filtering
+         that may also be desired (perhaps as a result of faceted searching).
+
+         NOTE: there is *absolutely* nothing a client can do to prevent these
+         "appends" values from being used, so don't use this mechanism
+         unless you are sure you always want it.
+      -->
+    <!--
+       <lst name="appends">
+         <str name="fq">inStock:true</str>
+       </lst>
+      -->
+    <!-- "invariants" are a way of letting the Solr maintainer lock down
+         the options available to Solr clients.  Any params values
+         specified here are used regardless of what values may be specified
+         in either the query, the "defaults", or the "appends" params.
+
+         In this example, the facet.field and facet.query params would
+         be fixed, limiting the facets clients can use.  Faceting is
+         not turned on by default - but if the client does specify
+         facet=true in the request, these are the only facets they
+         will be able to see counts for; regardless of what other
+         facet.field or facet.query params they may specify.
+
+         NOTE: there is *absolutely* nothing a client can do to prevent these
+         "invariants" values from being used, so don't use this mechanism
+         unless you are sure you always want it.
+      -->
+    <!--
+       <lst name="invariants">
+         <str name="facet.field">cat</str>
+         <str name="facet.field">manu_exact</str>
+         <str name="facet.query">price:[* TO 500]</str>
+         <str name="facet.query">price:[500 TO *]</str>
+       </lst>
+      -->
+    <!-- If the default list of SearchComponents is not desired, that
+         list can either be overridden completely, or components can be
+         prepended or appended to the default list.  (see below)
+      -->
+    <!--
+       <arr name="components">
+         <str>nameOfCustomComponent1</str>
+         <str>nameOfCustomComponent2</str>
+       </arr>
+      -->
+    </requestHandler>
+
+  <!-- A request handler that returns indented JSON by default -->
+  <requestHandler name="/query" class="solr.SearchHandler">
+     <lst name="defaults">
+       <str name="echoParams">explicit</str>
+       <str name="wt">json</str>
+       <str name="indent">true</str>
+       <str name="df">text</str>
+     </lst>
+  </requestHandler>
+
+
+  <!-- realtime get handler, guaranteed to return the latest stored fields of
+       any document, without the need to commit or open a new searcher.  The
+       current implementation relies on the updateLog feature being enabled. -->
+  <requestHandler name="/get" class="solr.RealTimeGetHandler">
+     <lst name="defaults">
+       <str name="omitHeader">true</str>
+       <str name="wt">json</str>
+       <str name="indent">true</str>
+     </lst>
+  </requestHandler>
+
+ 
+  <!-- A Robust Example 
+       
+       This example SearchHandler declaration shows off usage of the
+       SearchHandler with many defaults declared
+
+       Note that multiple instances of the same Request Handler
+       (SearchHandler) can be registered multiple times with different
+       names (and different init parameters)
+    -->
+  <requestHandler name="/browse" class="solr.SearchHandler">
+     <lst name="defaults">
+       <str name="echoParams">explicit</str>
+
+       <!-- VelocityResponseWriter settings -->
+       <str name="wt">velocity</str>
+       <str name="v.template">browse</str>
+       <str name="v.layout">layout</str>
+       <str name="title">MPIWG - search</str>
+
+       <!-- Query settings -->
+       <str name="defType">edismax</str>
+       <str name="qf">
+          title^10.0 IM_author^8.5 author^8.0 all-bib-data^3.0 content^1.0
+       </str>
+       <str name="df">text</str>
+       <str name="mm">100%</str>
+       <str name="q.alt">*:*</str>
+       <str name="rows">10</str>
+       <str name="fl">*,score</str>
+
+       <str name="mlt.qf">
+          title^10.0 author^8.0 IM_author^8.5 ll-bib-data^3.0
+       </str>
+       <str name="mlt.fl">IM_title, IM_author,author, IM_year, all-bib-data</str>
+       <int name="mlt.count">3</int>
+
+       <!-- Faceting defaults -->
+       <str name="facet">on</str>
+       <str name="facet.field">doc-type</str>
+       <str name="facet.field">access-type</str>
+       <str name="facet.field">author</str>
+       <str name="facet.field">keyword</str>
+       <str name="facet.field">title</str>
+       <str name="facet.field">title_s</str>
+       <str name="facet.query">*:*</str>
+       <str name="facet.mincount">1</str>
+       <str name="facet.pivot">author,title_s</str>
+       <str name="facet.range.other">after</str>
+       <str name="facet.range">year</str>
+       <int name="f.year.facet.range.start">1500</int>
+       <int name="f.year.facet.range.end">2020</int>
+       <int name="f.year.facet.range.gap">30</int>
+      
+       <!-- Highlighting defaults -->
+       <str name="hl">on</str>
+       <str name="hl.fl">title author IM_author</str>
+       <str name="hl.encoder">html</str>
+       <str name="hl.simple.pre">&lt;b&gt;</str>
+       <str name="hl.simple.post">&lt;/b&gt;</str>
+       <str name="f.title.hl.fragsize">0</str>
+       <str name="f.title.hl.alternateField">title</str>
+       <str name="f.name.hl.fragsize">0</str>
+       <str name="f.name.hl.alternateField">title</str>
+       <str name="f.content.hl.snippets">3</str>
+       <str name="f.content.hl.fragsize">200</str>
+       <str name="f.content.hl.alternateField">content</str>
+       <str name="f.content.hl.maxAlternateFieldLength">750</str>
+
+       <!-- Spell checking defaults -->
+       <str name="spellcheck">on</str>
+       <str name="spellcheck.extendedResults">false</str>       
+       <str name="spellcheck.count">5</str>
+       <str name="spellcheck.alternativeTermCount">2</str>
+       <str name="spellcheck.maxResultsForSuggest">5</str>       
+       <str name="spellcheck.collate">true</str>
+       <str name="spellcheck.collateExtendedResults">true</str>  
+       <str name="spellcheck.maxCollationTries">5</str>
+       <str name="spellcheck.maxCollations">3</str>           
+     </lst>
+
+     <!-- append spellchecking to our list of components -->
+     <arr name="last-components">
+       <str>spellcheck</str>
+     </arr>
+  </requestHandler>
+
+  <!-- Author overview alphabetical -->
+  <requestHandler name="/browse2" class="solr.SearchHandler">
+     <lst name="defaults">
+       <str name="echoParams">explicit</str>
+
+       <!-- VelocityResponseWriter settings -->
+       <str name="wt">velocity</str>
+       <str name="v.template">authors</str>
+       <str name="v.layout">authors_layout</str>
+       <str name="title">MPIWG - search</str>
+
+       <!-- Query settings -->
+       <str name="defType">edismax</str>
+       <str name="qf">
+          title^10.0 IM_author^8.5 author^8.0 all-bib-data^3.0
+       </str>
+       <str name="df">text</str>
+       <str name="mm">100%</str>
+       <str name="q.alt">*:*</str>
+       <str name="rows">10</str>
+       <str name="fl">*,score</str>
+
+       <str name="mlt.qf">
+          title^10.0 author^8.0 IM_author^8.5 ll-bib-data^3.0
+       </str>
+       <str name="mlt.fl">IM_title, IM_author,author, IM_year, all-bib-data</str>
+       <int name="mlt.count">3</int>
+
+       <!-- Faceting defaults -->
+       <str name="facet">on</str>
+       <str name="facet.field">doc-type</str>
+       <str name="facet.field">author_c</str>
+       
+       
+       <str name="facet.field">title_s</str>
+       
+       <str name="facet.sort">index</str>
+       <str name="f.author_c.facet.limit">-1</str>
+       <str name="facet.query">*:*</str>
+       <str name="facet.mincount">1</str>
+       <str name="facet.range.other">after</str>
+       <str name="facet.range">year</str>
+       <int name="f.year.facet.range.start">1500</int>
+       <int name="f.year.facet.range.end">2020</int>
+       <int name="f.year.facet.range.gap">30</int>
+      
+       <!-- Highlighting defaults -->
+       <str name="hl">on</str>
+       <str name="hl.fl">title author IM_author</str>
+       <str name="hl.encoder">html</str>
+       <str name="hl.simple.pre">&lt;b&gt;</str>
+       <str name="hl.simple.post">&lt;/b&gt;</str>
+       <str name="f.title.hl.fragsize">0</str>
+       <str name="f.title.hl.alternateField">title</str>
+       <str name="f.name.hl.fragsize">0</str>
+       <str name="f.name.hl.alternateField">title</str>
+       <str name="f.content.hl.snippets">3</str>
+       <str name="f.content.hl.fragsize">200</str>
+       <str name="f.content.hl.alternateField">content</str>
+       <str name="f.content.hl.maxAlternateFieldLength">750</str>
+
+       <!-- Spell checking defaults -->
+       <str name="spellcheck">on</str>
+       <str name="spellcheck.extendedResults">false</str>       
+       <str name="spellcheck.count">5</str>
+       <str name="spellcheck.alternativeTermCount">2</str>
+       <str name="spellcheck.maxResultsForSuggest">5</str>       
+       <str name="spellcheck.collate">true</str>
+       <str name="spellcheck.collateExtendedResults">true</str>  
+       <str name="spellcheck.maxCollationTries">5</str>
+       <str name="spellcheck.maxCollations">3</str>           
+     </lst>
+
+     <!-- append spellchecking to our list of components -->
+     <arr name="last-components">
+       <str>spellcheck</str>
+     </arr>
+  </requestHandler>
+
+ <!-- Title overview alphabetical -->
+<requestHandler name="/browse3" class="solr.SearchHandler">
+     <lst name="defaults">
+       <str name="echoParams">explicit</str>
+
+       <!-- VelocityResponseWriter settings -->
+       <str name="wt">velocity</str>
+       <str name="v.template">titles</str>
+       <str name="v.layout">titles_layout</str>
+       <str name="title">MPIWG - search - titles</str>
+
+       <!-- Query settings -->
+       <str name="defType">edismax</str>
+       <str name="qf">
+          title^10.0 IM_author^8.5 author^8.0 all-bib-data^3.0
+       </str>
+       <str name="df">text</str>
+       <str name="mm">100%</str>
+       <str name="q.alt">*:*</str>
+       <str name="rows">10</str>
+       <str name="fl">*,score</str>
+
+       <str name="mlt.qf">
+          title^10.0 author^8.0 IM_author^8.5 ll-bib-data^3.0
+       </str>
+       <str name="mlt.fl">IM_title, IM_author,author, IM_year, all-bib-data</str>
+       <int name="mlt.count">3</int>
+
+       <!-- Faceting defaults -->
+       <str name="facet">on</str>
+       <str name="facet.field">doc-type</str>
+       <str name="facet.field">author_c</str>
+       
+       
+       <str name="facet.field">maintitle_s</str>
+       
+       <str name="facet.sort">index</str>
+       <str name="f.author_c.facet.limit">-1</str>
+       <str name="f.maintitle_s.facet.limit">500</str>
+       <str name="facet.query">*:*</str>
+       <str name="facet.mincount">1</str>
+       <str name="facet.range.other">after</str>
+       <str name="facet.range">year</str>
+       <int name="f.year.facet.range.start">1500</int>
+       <int name="f.year.facet.range.end">2020</int>
+       <int name="f.year.facet.range.gap">30</int>
+      
+       <!-- Highlighting defaults -->
+       <str name="hl">on</str>
+       <str name="hl.fl">title author IM_author</str>
+       <str name="hl.encoder">html</str>
+       <str name="hl.simple.pre">&lt;b&gt;</str>
+       <str name="hl.simple.post">&lt;/b&gt;</str>
+       <str name="f.title.hl.fragsize">0</str>
+       <str name="f.title.hl.alternateField">title</str>
+       <str name="f.name.hl.fragsize">0</str>
+       <str name="f.name.hl.alternateField">title</str>
+       <str name="f.content.hl.snippets">3</str>
+       <str name="f.content.hl.fragsize">200</str>
+       <str name="f.content.hl.alternateField">content</str>
+       <str name="f.content.hl.maxAlternateFieldLength">750</str>
+
+       <!-- Spell checking defaults -->
+       <str name="spellcheck">on</str>
+       <str name="spellcheck.extendedResults">false</str>       
+       <str name="spellcheck.count">5</str>
+       <str name="spellcheck.alternativeTermCount">2</str>
+       <str name="spellcheck.maxResultsForSuggest">5</str>       
+       <str name="spellcheck.collate">true</str>
+       <str name="spellcheck.collateExtendedResults">true</str>  
+       <str name="spellcheck.maxCollationTries">5</str>
+       <str name="spellcheck.maxCollations">3</str>           
+     </lst>
+
+     <!-- append spellchecking to our list of components -->
+     <arr name="last-components">
+       <str>spellcheck</str>
+     </arr>
+  </requestHandler>
+
+  <!-- Update Request Handler.  
+       
+       http://wiki.apache.org/solr/UpdateXmlMessages
+
+       The canonical Request Handler for Modifying the Index through
+       commands specified using XML, JSON, CSV, or JAVABIN
+
+       Note: Since solr1.1 requestHandlers requires a valid content
+       type header if posted in the body. For example, curl now
+       requires: -H 'Content-type:text/xml; charset=utf-8'
+       
+       To override the request content type and force a specific 
+       Content-type, use the request parameter: 
+         ?update.contentType=text/csv
+       
+       This handler will pick a response format to match the input
+       if the 'wt' parameter is not explicit
+    -->
+  <requestHandler name="/update" class="solr.UpdateRequestHandler">
+    <!-- See below for information on defining 
+         updateRequestProcessorChains that can be used by name 
+         on each Update Request
+      -->
+    <!--
+       <lst name="defaults">
+         <str name="update.chain">dedupe</str>
+       </lst>
+       -->
+  </requestHandler>
+
+  <!-- for back compat with clients using /update/json and /update/csv -->  
+  <requestHandler name="/update/json" class="solr.JsonUpdateRequestHandler">
+        <lst name="defaults">
+         <str name="stream.contentType">application/json</str>
+       </lst>
+  </requestHandler>
+  <requestHandler name="/update/csv" class="solr.CSVRequestHandler">
+        <lst name="defaults">
+         <str name="stream.contentType">application/csv</str>
+       </lst>
+  </requestHandler>
+
+  <!-- Solr Cell Update Request Handler
+
+       http://wiki.apache.org/solr/ExtractingRequestHandler 
+
+    -->
+  <requestHandler name="/update/extract" 
+                  startup="lazy"
+                  class="solr.extraction.ExtractingRequestHandler" >
+    <lst name="defaults">
+      <str name="lowernames">true</str>
+      <str name="uprefix">ignored_</str>
+
+      <!-- capture link hrefs but ignore div attributes -->
+      <str name="captureAttr">true</str>
+      <str name="fmap.a">links</str>
+      <str name="fmap.div">ignored_</str>
+    </lst>
+  </requestHandler>
+
+
+  <!-- Field Analysis Request Handler
+
+       RequestHandler that provides much the same functionality as
+       analysis.jsp. Provides the ability to specify multiple field
+       types and field names in the same request and outputs
+       index-time and query-time analysis for each of them.
+
+       Request parameters are:
+       analysis.fieldname - field name whose analyzers are to be used
+
+       analysis.fieldtype - field type whose analyzers are to be used
+       analysis.fieldvalue - text for index-time analysis
+       q (or analysis.q) - text for query time analysis
+       analysis.showmatch (true|false) - When set to true and when
+           query analysis is performed, the produced tokens of the
+           field value analysis will be marked as "matched" for every
+           token that is produces by the query analysis
+   -->
+  <requestHandler name="/analysis/field" 
+                  startup="lazy"
+                  class="solr.FieldAnalysisRequestHandler" />
+
+
+  <!-- Document Analysis Handler
+
+       http://wiki.apache.org/solr/AnalysisRequestHandler
+
+       An analysis handler that provides a breakdown of the analysis
+       process of provided documents. This handler expects a (single)
+       content stream with the following format:
+
+       <docs>
+         <doc>
+           <field name="id">1</field>
+           <field name="name">The Name</field>
+           <field name="text">The Text Value</field>
+         </doc>
+         <doc>...</doc>
+         <doc>...</doc>
+         ...
+       </docs>
+
+    Note: Each document must contain a field which serves as the
+    unique key. This key is used in the returned response to associate
+    an analysis breakdown to the analyzed document.
+
+    Like the FieldAnalysisRequestHandler, this handler also supports
+    query analysis by sending either an "analysis.query" or "q"
+    request parameter that holds the query text to be analyzed. It
+    also supports the "analysis.showmatch" parameter which when set to
+    true, all field tokens that match the query tokens will be marked
+    as a "match". 
+  -->
+  <requestHandler name="/analysis/document" 
+                  class="solr.DocumentAnalysisRequestHandler" 
+                  startup="lazy" />
+
+  <!-- Admin Handlers
+
+       Admin Handlers - This will register all the standard admin
+       RequestHandlers.  
+    -->
+  <requestHandler name="/admin/" 
+                  class="solr.admin.AdminHandlers" />
+  <!-- This single handler is equivalent to the following... -->
+  <!--
+     <requestHandler name="/admin/luke"       class="solr.admin.LukeRequestHandler" />
+     <requestHandler name="/admin/system"     class="solr.admin.SystemInfoHandler" />
+     <requestHandler name="/admin/plugins"    class="solr.admin.PluginInfoHandler" />
+     <requestHandler name="/admin/threads"    class="solr.admin.ThreadDumpHandler" />
+     <requestHandler name="/admin/properties" class="solr.admin.PropertiesRequestHandler" />
+     <requestHandler name="/admin/file"       class="solr.admin.ShowFileRequestHandler" >
+    -->
+  <!-- If you wish to hide files under ${solr.home}/conf, explicitly
+       register the ShowFileRequestHandler using: 
+    -->
+  <!--
+     <requestHandler name="/admin/file" 
+                     class="solr.admin.ShowFileRequestHandler" >
+       <lst name="invariants">
+         <str name="hidden">synonyms.txt</str> 
+         <str name="hidden">anotherfile.txt</str> 
+       </lst>
+     </requestHandler>
+    -->
+
+  <!-- ping/healthcheck -->
+  <requestHandler name="/admin/ping" class="solr.PingRequestHandler">
+    <lst name="invariants">
+      <str name="q">solrpingquery</str>
+    </lst>
+    <lst name="defaults">
+      <str name="echoParams">all</str>
+    </lst>
+    <!-- An optional feature of the PingRequestHandler is to configure the 
+         handler with a "healthcheckFile" which can be used to enable/disable 
+         the PingRequestHandler.
+         relative paths are resolved against the data dir 
+      -->
+    <!-- <str name="healthcheckFile">server-enabled.txt</str> -->
+  </requestHandler>
+
+  <!-- Echo the request contents back to the client -->
+  <requestHandler name="/debug/dump" class="solr.DumpRequestHandler" >
+    <lst name="defaults">
+     <str name="echoParams">explicit</str> 
+     <str name="echoHandler">true</str>
+    </lst>
+  </requestHandler>
+  
+  <!-- Solr Replication
+
+       The SolrReplicationHandler supports replicating indexes from a
+       "master" used for indexing and "slaves" used for queries.
+
+       http://wiki.apache.org/solr/SolrReplication 
+
+       It is also neccessary for SolrCloud to function (in Cloud mode, the 
+       replication handler is used to bulk transfer segments when nodes 
+       are added or need to recover).
+
+       https://wiki.apache.org/solr/SolrCloud/
+    -->
+  <requestHandler name="/replication" class="solr.ReplicationHandler" > 
+    <!--
+       To enable simple master/slave replication, uncomment one of the 
+       sections below, depending on wether this solr instance should be 
+       the "master" or a "slave".  If this instance is a "slave" you will 
+       also need to fill in the masterUrl to point to a real machine.
+    -->
+    <!--
+       <lst name="master">
+         <str name="replicateAfter">commit</str>
+         <str name="replicateAfter">startup</str>
+         <str name="confFiles">schema.xml,stopwords.txt</str>
+       </lst>
+    -->
+    <!--
+       <lst name="slave">
+         <str name="masterUrl">http://your-master-hostname:8983/solr</str>
+         <str name="pollInterval">00:00:60</str>
+       </lst>
+    -->
+  </requestHandler>
+
+  <!-- Search Components
+
+       Search components are registered to SolrCore and used by 
+       instances of SearchHandler (which can access them by name)
+       
+       By default, the following components are available:
+       
+       <searchComponent name="query"     class="solr.QueryComponent" />
+       <searchComponent name="facet"     class="solr.FacetComponent" />
+       <searchComponent name="mlt"       class="solr.MoreLikeThisComponent" />
+       <searchComponent name="highlight" class="solr.HighlightComponent" />
+       <searchComponent name="stats"     class="solr.StatsComponent" />
+       <searchComponent name="debug"     class="solr.DebugComponent" />
+   
+       Default configuration in a requestHandler would look like:
+
+       <arr name="components">
+         <str>query</str>
+         <str>facet</str>
+         <str>mlt</str>
+         <str>highlight</str>
+         <str>stats</str>
+         <str>debug</str>
+       </arr>
+
+       If you register a searchComponent to one of the standard names, 
+       that will be used instead of the default.
+
+       To insert components before or after the 'standard' components, use:
+    
+       <arr name="first-components">
+         <str>myFirstComponentName</str>
+       </arr>
+    
+       <arr name="last-components">
+         <str>myLastComponentName</str>
+       </arr>
+
+       NOTE: The component registered with the name "debug" will
+       always be executed after the "last-components" 
+       
+     -->
+  
+   <!-- Spell Check
+
+        The spell check component can return a list of alternative spelling
+        suggestions.  
+
+        http://wiki.apache.org/solr/SpellCheckComponent
+     -->
+  <searchComponent name="spellcheck" class="solr.SpellCheckComponent">
+
+    <str name="queryAnalyzerFieldType">textSpell</str>
+
+    <!-- Multiple "Spell Checkers" can be declared and used by this
+         component
+      -->
+
+    <!-- a spellchecker built from a field of the main index -->
+    <lst name="spellchecker">
+      <str name="name">default</str>
+      <str name="field">name</str>
+      <str name="classname">solr.DirectSolrSpellChecker</str>
+      <!-- the spellcheck distance measure used, the default is the internal levenshtein -->
+      <str name="distanceMeasure">internal</str>
+      <!-- minimum accuracy needed to be considered a valid spellcheck suggestion -->
+      <float name="accuracy">0.5</float>
+      <!-- the maximum #edits we consider when enumerating terms: can be 1 or 2 -->
+      <int name="maxEdits">2</int>
+      <!-- the minimum shared prefix when enumerating terms -->
+      <int name="minPrefix">1</int>
+      <!-- maximum number of inspections per result. -->
+      <int name="maxInspections">5</int>
+      <!-- minimum length of a query term to be considered for correction -->
+      <int name="minQueryLength">4</int>
+      <!-- maximum threshold of documents a query term can appear to be considered for correction -->
+      <float name="maxQueryFrequency">0.01</float>
+      <!-- uncomment this to require suggestions to occur in 1% of the documents
+      	<float name="thresholdTokenFrequency">.01</float>
+      -->
+    </lst>
+    
+    <!-- a spellchecker that can break or combine words.  See "/spell" handler below for usage -->
+    <lst name="spellchecker">
+      <str name="name">wordbreak</str>
+      <str name="classname">solr.WordBreakSolrSpellChecker</str>      
+      <str name="field">name</str>
+      <str name="combineWords">true</str>
+      <str name="breakWords">true</str>
+      <int name="maxChanges">10</int>
+    </lst>
+
+    <!-- a spellchecker that uses a different distance measure -->
+    <!--
+       <lst name="spellchecker">
+         <str name="name">jarowinkler</str>
+         <str name="field">spell</str>
+         <str name="classname">solr.DirectSolrSpellChecker</str>
+         <str name="distanceMeasure">
+           org.apache.lucene.search.spell.JaroWinklerDistance
+         </str>
+       </lst>
+     -->
+
+    <!-- a spellchecker that use an alternate comparator 
+
+         comparatorClass be one of:
+          1. score (default)
+          2. freq (Frequency first, then score)
+          3. A fully qualified class name
+      -->
+    <!--
+       <lst name="spellchecker">
+         <str name="name">freq</str>
+         <str name="field">lowerfilt</str>
+         <str name="classname">solr.DirectSolrSpellChecker</str>
+         <str name="comparatorClass">freq</str>
+      -->
+
+    <!-- A spellchecker that reads the list of words from a file -->
+    <!--
+       <lst name="spellchecker">
+         <str name="classname">solr.FileBasedSpellChecker</str>
+         <str name="name">file</str>
+         <str name="sourceLocation">spellings.txt</str>
+         <str name="characterEncoding">UTF-8</str>
+         <str name="spellcheckIndexDir">spellcheckerFile</str>
+       </lst>
+      -->
+  </searchComponent>
+
+  <!-- A request handler for demonstrating the spellcheck component.  
+
+       NOTE: This is purely as an example.  The whole purpose of the
+       SpellCheckComponent is to hook it into the request handler that
+       handles your normal user queries so that a separate request is
+       not needed to get suggestions.
+
+       IN OTHER WORDS, THERE IS REALLY GOOD CHANCE THE SETUP BELOW IS
+       NOT WHAT YOU WANT FOR YOUR PRODUCTION SYSTEM!
+       
+       See http://wiki.apache.org/solr/SpellCheckComponent for details
+       on the request parameters.
+    -->
+  <requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
+    <lst name="defaults">
+      <str name="df">text</str>
+      <!-- Solr will use suggestions from both the 'default' spellchecker
+           and from the 'wordbreak' spellchecker and combine them.
+           collations (re-written queries) can include a combination of
+           corrections from both spellcheckers -->
+      <str name="spellcheck.dictionary">default</str>
+      <str name="spellcheck.dictionary">wordbreak</str>
+      <str name="spellcheck">on</str>
+      <str name="spellcheck.extendedResults">true</str>       
+      <str name="spellcheck.count">10</str>
+      <str name="spellcheck.alternativeTermCount">5</str>
+      <str name="spellcheck.maxResultsForSuggest">5</str>       
+      <str name="spellcheck.collate">true</str>
+      <str name="spellcheck.collateExtendedResults">true</str>  
+      <str name="spellcheck.maxCollationTries">10</str>
+      <str name="spellcheck.maxCollations">5</str>         
+    </lst>
+    <arr name="last-components">
+      <str>spellcheck</str>
+    </arr>
+  </requestHandler>
+
+  <!-- Term Vector Component
+
+       http://wiki.apache.org/solr/TermVectorComponent
+    -->
+  <searchComponent name="tvComponent" class="solr.TermVectorComponent"/>
+
+  <!-- A request handler for demonstrating the term vector component
+
+       This is purely as an example.
+
+       In reality you will likely want to add the component to your 
+       already specified request handlers. 
+    -->
+  <requestHandler name="/tvrh" class="solr.SearchHandler" startup="lazy">
+    <lst name="defaults">
+      <str name="df">text</str>
+      <bool name="tv">true</bool>
+    </lst>
+    <arr name="last-components">
+      <str>tvComponent</str>
+    </arr>
+  </requestHandler>
+
+  <!-- Clustering Component
+
+       http://wiki.apache.org/solr/ClusteringComponent
+
+       You'll need to set the solr.cluster.enabled system property
+       when running solr to run with clustering enabled:
+
+            java -Dsolr.clustering.enabled=true -jar start.jar
+
+    -->
+  <searchComponent name="clustering"
+                   enable="${solr.clustering.enabled:false}"
+                   class="solr.clustering.ClusteringComponent" >
+    <!-- Declare an engine -->
+    <lst name="engine">
+      <!-- The name, only one can be named "default" -->
+      <str name="name">default</str>
+
+      <!-- Class name of Carrot2 clustering algorithm.
+
+           Currently available algorithms are:
+           
+           * org.carrot2.clustering.lingo.LingoClusteringAlgorithm
+           * org.carrot2.clustering.stc.STCClusteringAlgorithm
+           * org.carrot2.clustering.kmeans.BisectingKMeansClusteringAlgorithm
+           
+           See http://project.carrot2.org/algorithms.html for the
+           algorithm's characteristics.
+        -->
+      <str name="carrot.algorithm">org.carrot2.clustering.lingo.LingoClusteringAlgorithm</str>
+
+      <!-- Overriding values for Carrot2 default algorithm attributes.
+
+           For a description of all available attributes, see:
+           http://download.carrot2.org/stable/manual/#chapter.components.
+           Use attribute key as name attribute of str elements
+           below. These can be further overridden for individual
+           requests by specifying attribute key as request parameter
+           name and attribute value as parameter value.
+        -->
+      <str name="LingoClusteringAlgorithm.desiredClusterCountBase">20</str>
+
+      <!-- Location of Carrot2 lexical resources.
+
+           A directory from which to load Carrot2-specific stop words
+           and stop labels. Absolute or relative to Solr config directory.
+           If a specific resource (e.g. stopwords.en) is present in the
+           specified dir, it will completely override the corresponding
+           default one that ships with Carrot2.
+
+           For an overview of Carrot2 lexical resources, see:
+           http://download.carrot2.org/head/manual/#chapter.lexical-resources
+        -->
+      <str name="carrot.lexicalResourcesDir">clustering/carrot2</str>
+
+      <!-- The language to assume for the documents.
+
+           For a list of allowed values, see:
+           http://download.carrot2.org/stable/manual/#section.attribute.lingo.MultilingualClustering.defaultLanguage
+       -->
+      <str name="MultilingualClustering.defaultLanguage">GERMAN</str>
+    </lst>
+    <lst name="engine">
+      <str name="name">stc</str>
+      <str name="carrot.algorithm">org.carrot2.clustering.stc.STCClusteringAlgorithm</str>
+    </lst>
+  </searchComponent>
+
+  <!-- A request handler for demonstrating the clustering component
+
+       This is purely as an example.
+
+       In reality you will likely want to add the component to your 
+       already specified request handlers. 
+    -->
+  <requestHandler name="/clustering"
+                  startup="lazy"
+                  enable="${solr.clustering.enabled:false}"
+                  class="solr.SearchHandler">
+    <lst name="defaults">
+      <bool name="clustering">true</bool>
+      <str name="clustering.engine">default</str>
+      <bool name="clustering.results">true</bool>
+      <!-- The title field -->
+      <str name="carrot.title">name</str>
+      <str name="carrot.url">id</str>
+      <!-- The field to cluster on -->
+       <str name="carrot.snippet">features</str>
+       <!-- produce summaries -->
+       <bool name="carrot.produceSummary">true</bool>
+       <!-- the maximum number of labels per cluster -->
+       <!--<int name="carrot.numDescriptions">5</int>-->
+       <!-- produce sub clusters -->
+       <bool name="carrot.outputSubClusters">false</bool>
+       
+       <str name="defType">edismax</str>
+       <str name="qf">
+         title^2.0 author^3.0 year^1.2
+       </str>
+       <str name="q.alt">*:*</str>
+       <str name="rows">10</str>
+       <str name="fl">*,score</str>
+    </lst>     
+    <arr name="last-components">
+      <str>clustering</str>
+    </arr>
+  </requestHandler>
+  
+  <!-- Terms Component
+
+       http://wiki.apache.org/solr/TermsComponent
+
+       A component to return terms and document frequency of those
+       terms
+    -->
+  <searchComponent name="terms" class="solr.TermsComponent"/>
+
+  <!-- A request handler for demonstrating the terms component -->
+  <requestHandler name="/terms" class="solr.SearchHandler" startup="lazy">
+     <lst name="defaults">
+      <bool name="terms">true</bool>
+      <bool name="distrib">false</bool>
+    </lst>     
+    <arr name="components">
+      <str>terms</str>
+    </arr>
+  </requestHandler>
+
+
+  <!-- Query Elevation Component
+
+       http://wiki.apache.org/solr/QueryElevationComponent
+
+       a search component that enables you to configure the top
+       results for a given query regardless of the normal lucene
+       scoring.
+    -->
+  <searchComponent name="elevator" class="solr.QueryElevationComponent" >
+    <!-- pick a fieldType to analyze queries -->
+    <str name="queryFieldType">string</str>
+    <str name="config-file">elevate.xml</str>
+  </searchComponent>
+
+  <!-- A request handler for demonstrating the elevator component -->
+  <requestHandler name="/elevate" class="solr.SearchHandler" startup="lazy">
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+      <str name="df">text</str>
+    </lst>
+    <arr name="last-components">
+      <str>elevator</str>
+    </arr>
+  </requestHandler>
+
+  <!-- Highlighting Component
+
+       http://wiki.apache.org/solr/HighlightingParameters
+    -->
+  <searchComponent class="solr.HighlightComponent" name="highlight">
+    <highlighting>
+      <!-- Configure the standard fragmenter -->
+      <!-- This could most likely be commented out in the "default" case -->
+      <fragmenter name="gap" 
+                  default="true"
+                  class="solr.highlight.GapFragmenter">
+        <lst name="defaults">
+          <int name="hl.fragsize">100</int>
+        </lst>
+      </fragmenter>
+
+      <!-- A regular-expression-based fragmenter 
+           (for sentence extraction) 
+        -->
+      <fragmenter name="regex" 
+                  class="solr.highlight.RegexFragmenter">
+        <lst name="defaults">
+          <!-- slightly smaller fragsizes work better because of slop -->
+          <int name="hl.fragsize">70</int>
+          <!-- allow 50% slop on fragment sizes -->
+          <float name="hl.regex.slop">0.5</float>
+          <!-- a basic sentence pattern -->
+          <str name="hl.regex.pattern">[-\w ,/\n\&quot;&apos;]{20,200}</str>
+        </lst>
+      </fragmenter>
+
+      <!-- Configure the standard formatter -->
+      <formatter name="html" 
+                 default="true"
+                 class="solr.highlight.HtmlFormatter">
+        <lst name="defaults">
+          <str name="hl.simple.pre"><![CDATA[<em>]]></str>
+          <str name="hl.simple.post"><![CDATA[</em>]]></str>
+        </lst>
+      </formatter>
+
+      <!-- Configure the standard encoder -->
+      <encoder name="html" 
+               class="solr.highlight.HtmlEncoder" />
+
+      <!-- Configure the standard fragListBuilder -->
+      <fragListBuilder name="simple" 
+                       class="solr.highlight.SimpleFragListBuilder"/>
+      
+      <!-- Configure the single fragListBuilder -->
+      <fragListBuilder name="single" 
+                       class="solr.highlight.SingleFragListBuilder"/>
+      
+      <!-- Configure the weighted fragListBuilder -->
+      <fragListBuilder name="weighted" 
+                       default="true"
+                       class="solr.highlight.WeightedFragListBuilder"/>
+      
+      <!-- default tag FragmentsBuilder -->
+      <fragmentsBuilder name="default" 
+                        default="true"
+                        class="solr.highlight.ScoreOrderFragmentsBuilder">
+        <!-- 
+        <lst name="defaults">
+          <str name="hl.multiValuedSeparatorChar">/</str>
+        </lst>
+        -->
+      </fragmentsBuilder>
+
+      <!-- multi-colored tag FragmentsBuilder -->
+      <fragmentsBuilder name="colored" 
+                        class="solr.highlight.ScoreOrderFragmentsBuilder">
+        <lst name="defaults">
+          <str name="hl.tag.pre"><![CDATA[
+               <b style="background:yellow">,<b style="background:lawgreen">,
+               <b style="background:aquamarine">,<b style="background:magenta">,
+               <b style="background:palegreen">,<b style="background:coral">,
+               <b style="background:wheat">,<b style="background:khaki">,
+               <b style="background:lime">,<b style="background:deepskyblue">]]></str>
+          <str name="hl.tag.post"><![CDATA[</b>]]></str>
+        </lst>
+      </fragmentsBuilder>
+      
+      <boundaryScanner name="default" 
+                       default="true"
+                       class="solr.highlight.SimpleBoundaryScanner">
+        <lst name="defaults">
+          <str name="hl.bs.maxScan">10</str>
+          <str name="hl.bs.chars">.,!? &#9;&#10;&#13;</str>
+        </lst>
+      </boundaryScanner>
+      
+      <boundaryScanner name="breakIterator" 
+                       class="solr.highlight.BreakIteratorBoundaryScanner">
+        <lst name="defaults">
+          <!-- type should be one of CHARACTER, WORD(default), LINE and SENTENCE -->
+          <str name="hl.bs.type">WORD</str>
+          <!-- language and country are used when constructing Locale object.  -->
+          <!-- And the Locale object will be used when getting instance of BreakIterator -->
+          <str name="hl.bs.language">en</str>
+          <str name="hl.bs.country">US</str>
+        </lst>
+      </boundaryScanner>
+    </highlighting>
+  </searchComponent>
+
+  <!-- Update Processors
+
+       Chains of Update Processor Factories for dealing with Update
+       Requests can be declared, and then used by name in Update
+       Request Processors
+
+       http://wiki.apache.org/solr/UpdateRequestProcessor
+
+    --> 
+  <!-- Deduplication
+
+       An example dedup update processor that creates the "id" field
+       on the fly based on the hash code of some other fields.  This
+       example has overwriteDupes set to false since we are using the
+       id field as the signatureField and Solr will maintain
+       uniqueness based on that anyway.  
+       
+    -->
+  <!--
+     <updateRequestProcessorChain name="dedupe">
+       <processor class="solr.processor.SignatureUpdateProcessorFactory">
+         <bool name="enabled">true</bool>
+         <str name="signatureField">id</str>
+         <bool name="overwriteDupes">false</bool>
+         <str name="fields">name,features,cat</str>
+         <str name="signatureClass">solr.processor.Lookup3Signature</str>
+       </processor>
+       <processor class="solr.LogUpdateProcessorFactory" />
+       <processor class="solr.RunUpdateProcessorFactory" />
+     </updateRequestProcessorChain>
+    -->
+  
+  <!-- Language identification
+
+       This example update chain identifies the language of the incoming
+       documents using the langid contrib. The detected language is
+       written to field language_s. No field name mapping is done.
+       The fields used for detection are text, title, subject and description,
+       making this example suitable for detecting languages form full-text
+       rich documents injected via ExtractingRequestHandler.
+       See more about langId at http://wiki.apache.org/solr/LanguageDetection
+    -->
+    <!--
+     <updateRequestProcessorChain name="langid">
+       <processor class="org.apache.solr.update.processor.TikaLanguageIdentifierUpdateProcessorFactory">
+         <str name="langid.fl">text,title,subject,description</str>
+         <str name="langid.langField">language_s</str>
+         <str name="langid.fallback">en</str>
+       </processor>
+       <processor class="solr.LogUpdateProcessorFactory" />
+       <processor class="solr.RunUpdateProcessorFactory" />
+     </updateRequestProcessorChain>
+    -->
+
+  <!-- Script update processor
+
+    This example hooks in an update processor implemented using JavaScript.
+
+    See more about the script update processor at http://wiki.apache.org/solr/ScriptUpdateProcessor
+  -->
+  <!--
+    <updateRequestProcessorChain name="script">
+      <processor class="solr.StatelessScriptUpdateProcessorFactory">
+        <str name="script">update-script.js</str>
+        <lst name="params">
+          <str name="config_param">example config parameter</str>
+        </lst>
+      </processor>
+      <processor class="solr.RunUpdateProcessorFactory" />
+    </updateRequestProcessorChain>
+  -->
+ 
+  <!-- Response Writers
+
+       http://wiki.apache.org/solr/QueryResponseWriter
+
+       Request responses will be written using the writer specified by
+       the 'wt' request parameter matching the name of a registered
+       writer.
+
+       The "default" writer is the default and will be used if 'wt' is
+       not specified in the request.
+    -->
+  <!-- The following response writers are implicitly configured unless
+       overridden...
+    -->
+  <!--
+     <queryResponseWriter name="xml" 
+                          default="true"
+                          class="solr.XMLResponseWriter" />
+     <queryResponseWriter name="json" class="solr.JSONResponseWriter"/>
+     <queryResponseWriter name="python" class="solr.PythonResponseWriter"/>
+     <queryResponseWriter name="ruby" class="solr.RubyResponseWriter"/>
+     <queryResponseWriter name="php" class="solr.PHPResponseWriter"/>
+     <queryResponseWriter name="phps" class="solr.PHPSerializedResponseWriter"/>
+     <queryResponseWriter name="csv" class="solr.CSVResponseWriter"/>
+    -->
+
+  <queryResponseWriter name="json" class="solr.JSONResponseWriter">
+     <!-- For the purposes of the tutorial, JSON responses are written as
+      plain text so that they are easy to read in *any* browser.
+      If you expect a MIME type of "application/json" just remove this override.
+     -->
+    <str name="content-type">text/plain; charset=UTF-8</str>
+  </queryResponseWriter>
+  
+  <!--
+     Custom response writers can be declared as needed...
+    -->
+    <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
+  
+
+  <!-- XSLT response writer transforms the XML output by any xslt file found
+       in Solr's conf/xslt directory.  Changes to xslt files are checked for
+       every xsltCacheLifetimeSeconds.  
+    -->
+  <queryResponseWriter name="xslt" class="solr.XSLTResponseWriter">
+    <int name="xsltCacheLifetimeSeconds">5</int>
+  </queryResponseWriter>
+
+  <!-- Query Parsers
+
+       http://wiki.apache.org/solr/SolrQuerySyntax
+
+       Multiple QParserPlugins can be registered by name, and then
+       used in either the "defType" param for the QueryComponent (used
+       by SearchHandler) or in LocalParams
+    -->
+  <!-- example of registering a query parser -->
+  <!--
+     <queryParser name="myparser" class="com.mycompany.MyQParserPlugin"/>
+    -->
+
+  <!-- Function Parsers
+
+       http://wiki.apache.org/solr/FunctionQuery
+
+       Multiple ValueSourceParsers can be registered by name, and then
+       used as function names when using the "func" QParser.
+    -->
+  <!-- example of registering a custom function parser  -->
+  <!--
+     <valueSourceParser name="myfunc" 
+                        class="com.mycompany.MyValueSourceParser" />
+    -->
+    
+  
+  <!-- Document Transformers
+       http://wiki.apache.org/solr/DocTransformers
+    -->
+  <!--
+     Could be something like:
+     <transformer name="db" class="com.mycompany.LoadFromDatabaseTransformer" >
+       <int name="connection">jdbc://....</int>
+     </transformer>
+     
+     To add a constant value to all docs, use:
+     <transformer name="mytrans2" class="org.apache.solr.response.transform.ValueAugmenterFactory" >
+       <int name="value">5</int>
+     </transformer>
+     
+     If you want the user to still be able to change it with _value:something_ use this:
+     <transformer name="mytrans3" class="org.apache.solr.response.transform.ValueAugmenterFactory" >
+       <double name="defaultValue">5</double>
+     </transformer>
+
+      If you are using the QueryElevationComponent, you may wish to mark documents that get boosted.  The
+      EditorialMarkerFactory will do exactly that:
+     <transformer name="qecBooster" class="org.apache.solr.response.transform.EditorialMarkerFactory" />
+    -->
+    
+
+  <!-- Legacy config for the admin interface -->
+  <admin>
+    <defaultQuery>*:*</defaultQuery>
+  </admin>
+
+</config>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-author-block-form.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Displays the search form block.
+ *
+ * Available variables:
+ * - $search_form: The complete search form ready for print.
+ * - $search: Associative array of search elements. Can be used to print each
+ *   form element separately.
+ *
+ * Default elements within $search:
+ * - $search['search_block_form']: Text input area wrapped in a div.
+ * - $search['actions']: Rendered form buttons.
+ * - $search['hidden']: Hidden form elements. Used to validate forms when
+ *   submitted.
+ *
+ * Modules can add to the search form, so it is recommended to check for their
+ * existence before printing. The default keys will always exist. To check for
+ * a module-provided field, use code like this:
+ * @code
+ *   <?php if (isset($search['extra_field'])): ?>
+ *     <div class="extra-field">
+ *       <?php print $search['extra_field']; ?>
+ *     </div>
+ *   <?php endif; ?>
+ * @endcode
+ *
+ * @see template_preprocess_search_block_form()
+ */
+?>
+<div class="container-inline">
+  <?php if (empty($variables['form']['#block']->subject)): ?>
+    <h2 class="element-invisible"><?php print t('Search form'); ?></h2>
+  <?php endif; ?>
+  <h2> <?php print t('Search author');?></h2>
+  <div class="solrsearch_search_form">
+  <?php print $solrsearch_search_form; ?>
+  </div>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-block-form.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Displays the search form block.
+ *
+ * Available variables:
+ * - $search_form: The complete search form ready for print.
+ * - $search: Associative array of search elements. Can be used to print each
+ *   form element separately.
+ *
+ * Default elements within $search:
+ * - $search['search_block_form']: Text input area wrapped in a div.
+ * - $search['actions']: Rendered form buttons.
+ * - $search['hidden']: Hidden form elements. Used to validate forms when
+ *   submitted.
+ *
+ * Modules can add to the search form, so it is recommended to check for their
+ * existence before printing. The default keys will always exist. To check for
+ * a module-provided field, use code like this:
+ * @code
+ *   <?php if (isset($search['extra_field'])): ?>
+ *     <div class="extra-field">
+ *       <?php print $search['extra_field']; ?>
+ *     </div>
+ *   <?php endif; ?>
+ * @endcode
+ *
+ * @see template_preprocess_search_block_form()
+ */
+?>
+<div class="container-inline">
+  <?php if (empty($variables['form']['#block']->subject)): ?>
+    <h2 class="element-invisible"><?php print t('Search form'); ?></h2>
+  <?php endif; ?>
+  <h2> <?php print t('Search sources');?></h2>
+  <div class="solrsearch_search_form">
+  <?php print $solrsearch_search_form; ?>
+  </div>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-mpiwg.js	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,61 @@
+/*
+ * Javascript for MPIWG website
+ */
+
+(function ($) {
+$(document).ready(function() {
+    /*
+     * autosubmit forms
+     */
+	
+	
+    $('form.autosubmit').find('.autosubmit').change(function() {
+        this.form.submit();
+    });
+    // hide submit button
+    $('form.autosubmit input[type="submit"].autosubmit').hide();
+
+    /*
+     * foldout divs
+     */
+    $('.foldable').each(function() {
+        var $this = $(this);
+        var $head = $this.find('.fold_head');
+        var $img = $head.find('img');
+        var $body = $this.find('.fold_body');
+        $head.on('click', function() {
+            $body.slideToggle('fast');
+            $img.toggle();
+        }).css('cursor', 'pointer');
+        if (! $this.hasClass('initially_open')) {
+            // hide body initially
+            $body.hide();
+            $img.toggle();
+        }
+    });
+    
+
+/* the following block was uncommented. it was interfering with the code from collapsiblock (M. Trognitz) */    
+/*    $('.collapsiblock-processed').each(function() {
+        var $this = $(this);
+        var $head = $this.children('h2');
+        //var $img = $head.find('img');
+        
+        $head.each(function(){
+        	 var $body = $head.next();
+
+        	 $(this).on('click', function() {
+        		                 $body.slideToggle('fast');
+                 //$img.toggle();
+             }).css('cursor', 'pointer');
+             if (! $(this).hasClass('initially_open')) {
+                 // hide body initially
+                 $body.hide();
+                 //$img.toggle();
+             }
+        });
+    });*/
+
+}); 
+})(jQuery);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-term-list-author.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,5 @@
+<h2 class="resultoverview"><?php print $cnt ?> authors found for: <?php print $letter?></h2>
+
+<?php foreach ($authors as $author => $hits):?>
+<div><?php print l($author . ' ('. $hits .')','/solrsearch/author_c:"' . $author .'"') ?></div>
+<?php endforeach;?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-term-list-title.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,5 @@
+<h2 class="resultoverview"><?php print $cnt ?> titles found for: <?php print $letter?></h2>
+
+<?php foreach ($titles as $title => $hits):?>
+<div><?php print l($title . ' ('. $hits .')','/solrsearch/title_s:"' . $title .'")') ?></div>
+<?php endforeach;?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-term-selection-form.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,41 @@
+<?php
+drupal_add_js(drupal_get_path('module', 'solrsearch') .'/solrsearch-mpiwg.js');
+?>
+<h1>Browse all Sources</h1>
+
+      <div class="tool box">
+	<form class="autosubmit" action="../solrsearch-terms-change">
+	  <div class="searchbox">
+	   <div>
+	     <!--   <input class="text" type="text" name="search" placeholder="Search Sources"/>
+	      <input class="text" type="hidden" name="mode" value="display"/>
+	      <input class="submit" type="submit" value="&gt;"/> -->
+	    </div>
+	    <div class="extended">
+	    <!--   <a class="internal" href="extendedsearch">Extended Search</a>-->
+	    </div>
+	  </div>
+	  <div class="options">
+            <ul class="inline">
+              <li>
+                <input class="radio autosubmit" type="radio" <?php if ($field == "author_c"):?> checked="checked"<?php endif;?> name="browseField" value="author_c"/>
+                Author A-Z
+              </li>
+              <li>
+                <input class="radio autosubmit" type="radio" <?php if ($field == "title_s"):?> checked="checked"<?php endif;?>  name="browseField" value="title_s"/>
+                Title A-Z
+              </li>
+              <li>
+                <input class="autosubmit" type="submit" value="Change"/>
+              </li>
+            </ul>
+          </div>
+	  <div class="atoz">
+	    <ul class="inline"><?php print solrsearch_alphapager($field);?>
+	    <li><a href="search.html?startLetter_title_ascii=1" id="atoz-other">other</a>
+	      </li>
+	    </ul>
+        <p>Choose a character</p>
+	  </div>
+	</form>
+	</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch-title-block-form.tpl.php	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Displays the search form block.
+ *
+ * Available variables:
+ * - $search_form: The complete search form ready for print.
+ * - $search: Associative array of search elements. Can be used to print each
+ *   form element separately.
+ *
+ * Default elements within $search:
+ * - $search['search_block_form']: Text input area wrapped in a div.
+ * - $search['actions']: Rendered form buttons.
+ * - $search['hidden']: Hidden form elements. Used to validate forms when
+ *   submitted.
+ *
+ * Modules can add to the search form, so it is recommended to check for their
+ * existence before printing. The default keys will always exist. To check for
+ * a module-provided field, use code like this:
+ * @code
+ *   <?php if (isset($search['extra_field'])): ?>
+ *     <div class="extra-field">
+ *       <?php print $search['extra_field']; ?>
+ *     </div>
+ *   <?php endif; ?>
+ * @endcode
+ *
+ * @see template_preprocess_search_block_form()
+ */
+?>
+<div class="container-inline">
+  <?php if (empty($variables['form']['#block']->subject)): ?>
+    <h2 class="element-invisible"><?php print t('Search form'); ?></h2>
+  <?php endif; ?>
+  <h2> <?php print t('Search in title');?></h2>
+  <div class="solrsearch_search_form">
+  <?php print $solrsearch_search_form; ?>
+  </div>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.admin.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,817 @@
+<?php
+
+/**
+ * @file
+ *   Administrative pages for the Apache Solr framework.
+ */
+
+/**
+ * Form to delete a search environment
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return array output of confirm_form()
+ */
+function solrsearch_environment_delete_form(array $form, array &$form_state, array $environment) {
+  $form['env_id'] = array(
+    '#type' => 'value',
+    '#value' => $environment['env_id'],
+  );
+  if (isset($environment['export_type']) && $environment['export_type'] == 3) {
+    $verb = t('Revert');
+  }
+  else {
+    $verb = t('Delete');
+  }
+  return confirm_form(
+    $form,
+    t('Are you sure you want to !verb search environment %name?', array('%name' => $environment['name'], '!verb' => strtolower($verb))),
+    'admin/config/search/solrsearch',
+    t('This action cannot be undone.'),
+    $verb,
+    t('Cancel')
+  );
+}
+
+/**
+ * Submit handler for the delete form
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function solrsearch_environment_delete_form_submit(array $form, array &$form_state) {
+  if (solrsearch_environment_delete($form_state['values']['env_id'])) {
+    drupal_set_message(t('The search environment was deleted'));
+  }
+  $form_state['redirect'] = 'admin/config/search/solrsearch/settings';
+}
+
+function solrsearch_environment_edit_delete_submit($form, &$form_state) {
+  $form_state['redirect'] = 'admin/config/search/solrsearch/settings/' . $form_state['values']['env_id'] . '/delete';
+
+  // Regardlessly of the destination parameter we want to go to another page
+  unset($_GET['destination']);
+  drupal_static_reset('drupal_get_destination');
+  drupal_get_destination();
+}
+
+/**
+ * Settings page for a specific environment (or default one if not provided)
+ *
+ * @param array|bool $environment
+ *
+ * @return array Render array for a settings page
+ */
+function solrsearch_environment_settings_page(array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = solrsearch_default_environment();
+    $environment = solrsearch_environment_load($env_id);
+  }
+  $env_id = $environment['env_id'];
+
+ // Initializes output with information about which environment's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $output = array(
+    'solrsearch_environment' => array(
+      '#theme' => 'solrsearch_settings_title',
+      '#env_id' => $env_id,
+    ),
+  );
+  $output['form'] = drupal_get_form('solrsearch_environment_edit_form', $environment);
+  return $output;
+}
+
+/**
+ * Form to clone a certain environment
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param array $environment
+ *
+ * @return array output of confirm_form()
+ */
+function solrsearch_environment_clone_form(array $form, array &$form_state, array $environment) {
+  $form['env_id'] = array(
+    '#type' => 'value',
+    '#value' => $environment['env_id'],
+  );
+  return confirm_form(
+    $form,
+    t('Are you sure you want to clone search environment %name?', array('%name' => $environment['name'])),
+    'admin/config/search/solrsearch',
+    '',
+    t('Clone'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Submit handler for the clone form
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function solrsearch_environment_clone_form_submit(array $form, array &$form_state) {
+  if (solrsearch_environment_clone($form_state['values']['env_id'])) {
+    drupal_set_message(t('The search environment was cloned'));
+  }
+  $form_state['redirect'] = 'admin/config/search/solrsearch/settings';
+}
+
+/**
+ * Submit handler for the confirmation page of cloning an environment
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function solrsearch_environment_clone_submit(array $form, array &$form_state) {
+  $form_state['redirect'] = 'admin/config/search/solrsearch/settings/' . $form_state['values']['env_id'] . '/clone';
+}
+
+/**
+ * Form builder for adding/editing a Solr environment used as a menu callback.
+ */
+function solrsearch_environment_edit_form(array $form, array &$form_state, array $environment = array()) {
+  if (empty($environment)) {
+    $environment = array();
+  }
+  $environment += array('env_id' => '', 'name' => '', 'url' => '', 'service_class' => '', 'conf' => array());
+
+  $form['#environment'] = $environment;
+  $form['url'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Solr server URL'),
+    '#default_value' => $environment['url'],
+    '#description' => t('Example: http://localhost:8983/solr'),
+    '#required' => TRUE,
+  );
+  $is_default = $environment['env_id'] == solrsearch_default_environment();
+  $form['make_default'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Make this Solr search environment the default'),
+    '#default_value' => $is_default,
+    '#disabled' => $is_default,
+  );
+  $form['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Description'),
+    '#default_value' => $environment['name'],
+    '#required' => TRUE,
+  );
+  $form['env_id'] = array(
+    '#type' => 'machine_name',
+    '#title' => t('Environment id'),
+    '#machine_name' => array(
+      'exists' => 'solrsearch_environment_load',
+    ),
+    '#default_value' => $environment['env_id'],
+    '#disabled' => !empty($environment['env_id']), // Cannot change it once set.
+    '#description' => t('Unique, machine-readable identifier for this Solr environment.'),
+    '#required' => TRUE,
+  );
+  $form['service_class'] = array(
+    '#type' => 'value',
+    '#value' => $environment['service_class'],
+  );
+  $form['conf'] = array(
+    '#tree' => TRUE,
+  );
+  $form['conf']['solrsearch_read_only'] = array(
+    '#type' => 'radios',
+    '#title' => t('Index write access'),
+    '#default_value' => isset($environment['conf']['solrsearch_read_only']) ? $environment['conf']['solrsearch_read_only'] : solrsearch_READ_WRITE,
+    '#options' => array(solrsearch_READ_WRITE => t('Read and write (normal)'), solrsearch_READ_ONLY => t('Read only')),
+    '#description' => t('<em>Read only</em> stops this site from sending updates to this search environment. Useful for development sites.'),
+  );
+  $form['actions'] = array(
+    '#type' => 'actions',
+  );
+  $form['actions']['save'] = array(
+    '#type' => 'submit',
+    '#validate' => array('solrsearch_environment_edit_validate'),
+    '#submit' => array('solrsearch_environment_edit_submit'),
+    '#value' => t('Save'),
+  );
+  $form['actions']['save_edit'] = array(
+    '#type' => 'submit',
+    '#validate' => array('solrsearch_environment_edit_validate'),
+    '#submit' => array('solrsearch_environment_edit_submit'),
+    '#value' => t('Save and edit'),
+  );
+  $form['actions']['test'] = array(
+    '#type' => 'submit',
+    '#validate' => array('solrsearch_environment_edit_validate'),
+    '#submit' => array('solrsearch_environment_edit_test_submit'),
+    '#value' => t('Test connection'),
+  );
+  if (!empty($environment['env_id']) && !$is_default) {
+    $form['actions']['delete'] = array(
+      '#type' => 'submit',
+      '#submit' => array('solrsearch_environment_edit_delete_submit'),
+      '#value' => t('Delete'),
+    );
+  }
+
+  // Ensures destination is an internal URL, builds "cancel" link.
+  if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
+    $destination = $_GET['destination'];
+  }
+  else {
+    $destination = 'admin/config/search/solrsearch/settings';
+  }
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => $destination,
+  );
+
+  return $form;
+}
+
+/**
+ * Submit handler for the test button in the environment edit page
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function solrsearch_environment_edit_test_submit(array $form, array &$form_state) {
+  $ping = solrsearch_server_status($form_state['values']['url'], $form_state['values']['service_class']);
+  if ($ping) {
+    drupal_set_message(t('Your site has contacted the Apache Solr server.'));
+  }
+  else {
+    drupal_set_message(t('Your site was unable to contact the Apache Solr server.'), 'error');
+  }
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Validate handler for the environment edit page
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function solrsearch_environment_edit_validate(array $form, array &$form_state) {
+  $parts = parse_url($form_state['values']['url']);
+  foreach (array('scheme', 'host', 'path') as $key) {
+    if (empty($parts[$key])) {
+      form_set_error('url', t('The Solr server URL needs to include a !part', array('!part' => $key)));
+    }
+  }
+  if (isset($parts['port'])) {
+    // parse_url() should always give an integer for port. Since drupal_http_request()
+    // also uses parse_url(), we don't need to validate anything except the range.
+    $pattern = empty($parts['user']) ? '@://[^:]+:([^/]+)@' : '#://[^@]+@[^:]+:([^/]+)#';
+    preg_match($pattern, $form_state['values']['url'], $m);
+    if (empty($m[1]) || !ctype_digit($m[1]) || $m[1] < 1 || $m[1] > 65535) {
+      form_set_error('port', t('The port has to be an integer between 1 and 65535.'));
+    }
+    else {
+      // Normalize the url by removing extra slashes and whitespace.
+      $form_state['values']['url'] = trim($form_state['values']['url'], "/ \t\r\n\0\x0B");
+    }
+  }
+}
+
+/**
+ * Submit handler for the environment  edit page
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function solrsearch_environment_edit_submit(array $form, array &$form_state) {
+  solrsearch_environment_save($form_state['values']);
+  if (!empty($form_state['values']['make_default'])) {
+    solrsearch_set_default_environment($form_state['values']['env_id']);
+  }
+  cache_clear_all('solrsearch:environments', 'cache_solrsearch');
+  drupal_set_message(t('The %name search environment has been saved.', array('%name' => $form_state['values']['name'])));
+  if ($form_state['values']['op'] == t('Save')) {
+    $form_state['redirect'] = 'admin/config/search/solrsearch/settings';
+  }
+  else {
+    $form_state['redirect'] = current_path();
+  }
+  // Regardlessly of the destination parameter we want to go to another page
+  unset($_GET['destination']);
+  drupal_static_reset('drupal_get_destination');
+  drupal_get_destination();
+}
+
+/**
+ * Check to see if the facetapi module is installed, and if not put up
+ * a message.
+ *
+ * Only call this function if the user is already in a position for this to
+ * be useful.
+ */
+function solrsearch_check_facetapi() {
+  if (!module_exists('facetapi')) {
+    $filename = db_query_range("SELECT filename FROM {system} WHERE type = 'module' AND name = 'facetapi'", 0, 1)
+      ->fetchField();
+    if ($filename && file_exists($filename)) {
+      drupal_set_message(t('If you <a href="@modules">enable the facetapi module</a>, Apache Solr Search will provide you with configurable facets.', array('@modules' => url('admin/modules'))));
+    }
+    else {
+      drupal_set_message(t('If you install the facetapi module from !href, Apache Solr Search will provide you with configurable facets.', array('!href' => url('http://drupal.org/project/facetapi'))));
+    }
+  }
+}
+
+/**
+ * Form builder for general settings used as a menu callback.
+ *
+ * @param array $form
+ * @param array $form_state
+ *
+ * @return array Output of the system_settings_form()
+ */
+function solrsearch_settings(array $form, array &$form_state) {
+  $form = array();
+  $rows = array();
+
+  // Environment settings // Take environment settings form solrsearch
+  $id = solrsearch_default_environment();
+
+  $environments = solrsearch_load_all_environments();
+  $default_environment = solrsearch_default_environment();
+  solrsearch_check_facetapi();
+
+  // Reserve a row for the default one
+  $rows[$default_environment] = array();
+
+  foreach ($environments as $environment_id => $data) {
+    // Define all the Operations
+    $confs = array();
+    $ops = array();
+    // Whenever facetapi is enabled we also enable our operation link
+    if (module_exists('facetapi')) {
+      $confs['facets'] = array(
+        'class' => 'operation',
+        'data' => l(t('Facets'),
+          'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/facets',
+          array('query' => array('destination' => current_path()))
+        ),
+      );
+    }
+    // These are our result and bias settings
+    if (module_exists('solrsearch_search')) {
+      $confs['result_bias'] = array(
+        'class' => 'operation',
+        'data' => l(t('Bias'),
+          'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/bias',
+          array('query' => array('destination' => current_path()))
+        ),
+      );
+    }
+
+    $ops['edit'] = array(
+      'class' => 'operation',
+      'data' => l(t('Edit'),
+        'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/edit',
+        array('query' => array('destination' => current_path()))
+      ),
+    );
+
+    $ops['clone'] = array(
+      'class' => 'operation',
+      'data' => l(t('Clone'),
+        'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/clone',
+        array('query' => array('destination' => $_GET['q']))
+      ),
+    );
+    $env_name = l($data['name'], 'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/edit', array('query' => array('destination' => $_GET['q'])));
+
+    // Is this row our default environment?
+    if ($environment_id == $default_environment) {
+      $env_name = t('!environment <em>(Default)</em>', array('!environment' => $env_name));
+      $env_class_row = 'default-environment';
+    }
+    else {
+      $env_class_row = '';
+    }
+    // For every non-default we add a delete link
+    // Allow to revert a search environment or to delete it
+    $delete_value = '';
+    if (!isset($data['in_code_only'])) {
+      if ((isset($data['type']) && $data['type'] == 'Overridden')) {
+        $delete_value = array(
+          'class' => 'operation',
+          'data' => l(t('Revert'), 'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/delete'),
+        );
+      }
+      // don't allow the deletion of the default environment
+      elseif ($environment_id != $default_environment) {
+        $delete_value = array(
+          'class' => 'operation',
+          'data' => l(t('Delete'), 'admin/config/search/solrsearch/settings/' . $data['env_id'] . '/delete'),
+        );
+      }
+    }
+    $ops['delete'] = $delete_value;
+
+    // When we are receiving a http POST (so the page does not show) we do not
+    // want to check the statusses of any environment
+    $class = '';
+    if (empty($form_state['input'])) {
+      $class = solrsearch_server_status($data['url'], $data['service_class']) ? 'ok' : 'error';
+    }
+
+    $headers = array(
+      array('data' => t('Name'), 'colspan' => 2),
+      t('URL'),
+      array('data' => t('Configuration'), 'colspan' => count($confs)),
+      array('data' => t('Operations'), 'colspan' => count($ops)),
+    );
+
+    $rows[$environment_id] = array('data' =>
+      array(
+        // Cells
+        array(
+          'class' => 'status-icon',
+          'data' => '<div title="' . $class . '"><span class="element-invisible">' . $class . '</span></div>',
+        ),
+        array(
+          'class' => $env_class_row,
+          'data' => $env_name,
+        ),
+        check_plain($data['url']),
+      ),
+      'class' => array(drupal_html_class($class)),
+    );
+    // Add the links to the page
+    $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $confs);
+    $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $ops);
+  }
+
+  $form['solrsearch_host_settings']['actions'] = array(
+    '#markup' => '<ul class="action-links">' . drupal_render($actions) . '</ul>',
+  );
+  $form['solrsearch_host_settings']['table'] = array(
+    '#theme' => 'table',
+    '#header' => $headers,
+    '#rows' => array_values($rows),
+    '#attributes' => array('class' => array('admin-solrsearch')),
+  );
+
+  $form['advanced'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Advanced configuration'),
+    '#collapsed' => TRUE,
+    '#collapsible' => TRUE,
+  );
+  $form['advanced']['solrsearch_set_nodeapi_messages'] = array(
+    '#type' => 'radios',
+    '#title' => t('Extra help messages for administrators'),
+    '#description' => t('Adds notices to a page whenever Drupal changed content that needs reindexing'),
+    '#default_value' => variable_get('solrsearch_set_nodeapi_messages', 1),
+    '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
+  );
+
+  // Number of Items to index
+  $numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
+  $default_cron_limit = variable_get('solrsearch_cron_limit', 50);
+
+  // solrsearch_cron_limit may be overridden in settings.php. If its current
+  // value is not among the default set of options, add it.
+  if (!isset($numbers[$default_cron_limit])) {
+    $numbers[$default_cron_limit] = $default_cron_limit;
+  }
+  $form['advanced']['solrsearch_cron_limit'] = array(
+    '#type' => 'select',
+    '#title' => t('Number of items to index per cron run'),
+    '#default_value' => $default_cron_limit,
+    '#options' => $numbers,
+    '#description' => t('Reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status')))
+  );
+
+  $options = array('solrsearch:show_error' => t('Show error message'));
+  $system_info = system_get_info('module');
+  if (module_exists('search')) {
+    foreach (search_get_info() as $module => $search_info) {
+      // Don't allow solrsearch to return results on failure of solrsearch.
+      if ($module == 'solrsearch_search') {
+        continue;
+      }
+      $options[$module] = t('Show @name search results', array('@name' => $system_info[$module]['name']));
+    }
+  }
+
+  $options['solrsearch:show_no_results'] = t('Show no results');
+  $form['advanced']['solrsearch_failure'] = array(
+    '#type' => 'select',
+    '#title' => t('On failure'),
+    '#options' => $options,
+    '#default_value' => variable_get('solrsearch_failure', 'solrsearch:show_error'),
+  );
+
+  return system_settings_form($form);
+}
+
+/**
+ * Gets information about the fields already in solr index.
+ *
+ * @param array $environment
+ *   The environment for which we need to ask the status from
+ *
+ * @return array page render array
+ */
+function solrsearch_status_page($environment = array()) {
+
+  if (empty($environment)) {
+
+    $env_id = solrsearch_default_environment();
+    $environment = solrsearch_environment_load($env_id);
+  }
+  else {
+    $env_id = $environment['env_id'];
+  }
+
+  // Check for availability
+  if (!solrsearch_server_status($environment['url'], $environment['service_class'])) {
+
+    drupal_set_message(t('The server seems to be unavailable. Please verify the server settings at the <a href="!settings_page">settings page</a>', array('!settings_page' => url("admin/config/search/solrsearch/settings/{$environment['env_id']}/edit", array('query' =>  drupal_get_destination())))), 'warning');
+    return '';
+  }
+
+  try {
+    $solr = solrsearch_get_solr($environment["env_id"]);
+    $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 = new stdClass;
+    $data->fields = array();
+  }
+
+  $messages = array();
+
+  // Initializes output with information about which server's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $output['solrsearch_index_action_status'] = array(
+    '#prefix' => '<h3>' . t('@environment: Search Index Content', array('@environment' => $environment['name'])) . '</h3>',
+    '#theme' => 'table',
+    '#header' => array(t('Type'), t('Value')),
+    '#rows' => $messages,
+  );
+
+  $output['viewmore'] = array(
+    '#markup' => l(t('View more details on the search index contents'), 'admin/reports/solrsearch'),
+  );
+
+  $write_status = solrsearch_environment_variable_get($env_id, 'solrsearch_read_only', solrsearch_READ_WRITE);
+  if ($write_status == solrsearch_READ_WRITE) {
+     /*$output['index_action_form'] = drupal_get_form('solrsearch_index_action_form', $env_id);
+     $output['index_config_form'] = drupal_get_form('solrsearch_index_config_form', $env_id);*/
+  }
+  else {
+    drupal_set_message(t('Options for deleting and re-indexing are not available because the index is read-only. This can be changed on the <a href="!settings_page">settings page</a>', array('!settings_page' => url('admin/config/search/solrsearch/settings/' . $env_id . '/edit', array('query' =>  drupal_get_destination())))), 'warning');
+  }
+
+  return $output;
+}
+
+/**
+ * Get the report, eg.: some statistics and useful data from the Apache Solr index
+ *
+ * @param array $environment
+ *
+ * @return array page render array
+ */
+function solrsearch_index_report(array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = solrsearch_default_environment();
+    drupal_goto('admin/reports/solrsearch/' . $env_id);
+  }
+  $environments = solrsearch_load_all_environments();
+  $environments_list = array();
+  foreach ($environments as $env) {
+    $var_status = array('!name' =>$env['name']);
+    $environments_list[] = l(t('Statistics for !name', $var_status), 'admin/reports/solrsearch/' . $env['env_id']);
+  }
+  $output['environments_list'] = array(
+    '#theme' => 'item_list',
+    '#items' => $environments_list,
+  );
+
+  try {
+    $solr = solrsearch_get_solr($environment['env_id']);
+    $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");
+    return $output;
+  }
+
+  $messages = array();
+  $messages[] = array(t('Number of documents in index'), $data->index->numDocs);
+
+  $limit = variable_get('solrsearch_luke_limit', 20000);
+  if (isset($data->index->numDocs) && $data->index->numDocs > $limit) {
+    $messages[] = array(t('Limit'), t('You have more than @limit documents, so term frequencies are being omitted for performance reasons.', array('@limit' => $limit)));
+    $not_found = t('<em>Omitted</em>');
+  }
+  elseif (isset($data->index->numDocs)) {
+    $not_found = t('Not indexed');
+    try {
+      $solr = solrsearch_get_solr($environment['env_id']);
+      // Note: we use 2 since 1 fails on Ubuntu Hardy.
+      $data = $solr->getLuke(2);
+      if (isset($data->index->numTerms)) {
+        $messages[] = array(t('# of terms in index'), $data->index->numTerms);
+      }
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      $data->fields = array();
+    }
+  }
+  // Initializes output with information about which server's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $fields = (array)$data->fields;
+  if ($fields) {
+    $messages[] = array(t('# of fields in index'), count($fields));
+  }
+
+  // Output the messages we have for this page
+  $output['solrsearch_index_report'] = array(
+    '#theme' => 'table',
+    '#header' => array('type', 'value'),
+    '#rows' => $messages,
+  );
+
+  if ($fields) {
+    // Initializes table header.
+    $header = array(
+      'name' => t('Field name'),
+      'type' => t('Index type'),
+      'terms' => t('Distinct terms'),
+    );
+
+    // Builds table rows.
+    $rows = array();
+    foreach ($fields as $name => $field) {
+      // TODO: try to map the name to something more meaningful.
+      $rows[$name] = array(
+        'name' => $name,
+        'type' => $field->type,
+        'terms' => isset($field->distinct) ? $field->distinct : $not_found
+      );
+    }
+    ksort($rows);
+    // Output the fields we found for this environment
+    $output['field_table'] = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+    );
+  }
+  else {
+    $output['field_table'] = array('#markup' => t('No data on indexed fields.'));
+  }
+  return $output;
+}
+
+/**
+ * Page callback to show available conf files.
+ *
+ * @param array $environment
+ *
+ * @return string
+ *   A non-render array but plain theme output for the config files overview. Could be done better probably
+ */
+function solrsearch_config_files_overview(array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = solrsearch_default_environment();
+  }
+  else {
+    $env_id = $environment['env_id'];
+  }
+
+  $xml = NULL;
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    $response = $solr->makeServletRequest('admin/file', array('wt' => 'xml'));
+    $xml = simplexml_load_string($response->data);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
+  }
+
+  if ($xml) {
+    // Retrieve our items from the xml using xpath
+    $items = $xml->xpath('//lst[@name="files"]/lst');
+
+    // Add all the data of the file in a files array
+    $files = array();
+    foreach ($items as $item_id => $item) {
+      // Do not list directories. Always a bool
+      if (isset($item->bool)) {
+        break;
+      }
+      // Get data from the files.
+      $name =  $item->attributes();
+      $name = ((string)$item->attributes()) ? (string)$item->attributes() : t('No name found');
+      $files[$item_id]['name'] = l($name, 'admin/reports/solrsearch/' . $env_id . '/conf/' . $name);
+
+      // Retrieve the date attribute
+      if (isset($item->date)) {
+        $modified = ((string)$item->date->attributes() == 'modified') ? (string) $item->date : t('No date found');
+        $files[$item_id]['modified'] = format_date(strtotime($modified));
+      }
+
+      // Retrieve the size attribute
+      if (isset($item->long)) {
+        $size = ((string)$item->long->attributes() == 'size') ? (string) $item->long : t('No size found');
+        $files[$item_id]['size'] = t('Size (bytes): @bytes', array('@bytes' => $size));
+      }
+    }
+    // Sort our files alphabetically
+    ksort($files);
+
+    // Initializes table header.
+    $header = array(
+      'name' => t('File name'),
+      'date' => t('Modified'),
+      'size' => t('Size'),
+    );
+
+    // Display the table of field names, index types, and term counts.
+    $variables = array(
+      'header' => $header,
+      'rows' => $files,
+    );
+    $output = theme('table', $variables);
+  }
+  else {
+    $output = '<p>' . t('No data about any file found.') . "</p>\n";
+  }
+  return $output;
+}
+
+/**
+ * Page callback to show one conf file.
+ *
+ * @param string $name
+ * @param array $environment
+ *
+ * @return string
+ *   the requested config file
+ */
+function solrsearch_config_file($name, array $environment = array()) {
+  if (empty($environment)) {
+    $env_id = solrsearch_default_environment();
+  }
+  else {
+    $env_id = $environment['env_id'];
+  }
+
+  $output = '';
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    $response = $solr->makeServletRequest('admin/file', array('file' => $name));
+    $raw_file = $response->data;
+    $output = '<pre>' . check_plain($raw_file) . '</pre>';
+    drupal_set_title(check_plain($name));
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
+  }
+  return $output;
+}
+
+
+
+/**
+ * Page callback for node/%node/devel/solrsearch.
+ *
+ * @param object $node
+ * @return string debugging information
+ */
+function solrsearch_devel($node) {
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  $item = new stdClass();
+  $item->entity_type = 'node';
+  $item->entity_id = $node->nid;
+  $output = '';
+  foreach (solrsearch_load_all_environments() as $env_id => $environment) {
+    $documents = solrsearch_index_entity_to_documents($item, $env_id);
+    $output .= '<h1>' . t('Environment %name (%env_id)', array('%name' => $environment['name'], '%env_id' => $env_id)). '</h1>';
+    foreach ($documents as $document) {
+      $debug_data = array();
+      foreach ($document as $key => $value) {
+        $debug_data[$key] = $value;
+      }
+      $output .= kdevel_print_object($debug_data);
+    }
+  }
+  return $output;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.css	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,22 @@
+p.solrext-help,
+div.solrext {
+display: block;
+color: white;
+padding: 5px;
+background-color: black;
+border: 4px solid white;
+-webkit-border-radius: 8px;
+-moz-border-radius: 8px;
+border-radius: 8px;
+}
+
+
+div.solrsearch_search_form div input.form-submit {
+
+background: url("http://localhost/drupal-dev/themes/bartik/images/search-button.png") no-repeat scroll center top transparent;
+text-indent: -9999px;
+ }
+
+.solrsearch_internal{
+background-color: red;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.index.inc_unused	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,1469 @@
+<?php
+
+/**
+ * @file
+ * Functions related to Apache Solr indexing operations.
+ */
+
+/**
+ * Processes all index queues associated with the passed environment.
+ *
+ * An environment usually indexes one or more entity types. Each entity type
+ * stores its queue in a database table that is defined in the entity type's
+ * info array. This function processes N number of items in each queue table,
+ * where N is the limit passed as the second argument.
+ *
+ * The indexing routine allows developers to selectively bypass indexing on a
+ * per-entity basis by implementing the following hooks:
+ * - hook_solrsearch_exclude()
+ * - hook_solrsearch_ENTITY_TYPE_exclude()
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param int $limit
+ *   The number of items to process per queue table. For example, if there are
+ *   two entities that are being indexed in this environment and they each have
+ *   their own queue table, setting a limit of 50 will send a maximum number of
+ *   100 documents to the Apache Solr server.
+ *
+ * @return int
+ *   The total number of documents sent to the Apache Solr server for indexing.
+ *
+ * @see solrsearch_index_get_entities_to_index()
+ * @see solrsearch_index_entity_to_documents()
+ * @see solrsearch_index_send_to_solr()
+ */
+function solrsearch_index_entities($env_id, $limit) {
+  $documents_submitted = 0;
+  foreach (entity_get_info() as $entity_type => $info) {
+    // With each pass through the callback, retrieve the next group of nids.
+    $rows = solrsearch_index_get_entities_to_index($env_id, $entity_type, $limit);
+    $documents = array();
+    foreach ($rows as $row) {
+      $row_documents = solrsearch_index_entities_document($row, $entity_type, $env_id);
+      $documents = array_merge($documents, $row_documents);
+    }
+
+    $indexed = solrsearch_index_send_to_solr($env_id, $documents);
+    if ($indexed !== FALSE) {
+      $documents_submitted += count($documents);
+      $index_position = solrsearch_get_last_index_position($env_id, $entity_type);
+      $max_changed = $index_position['last_changed'];
+      $max_entity_id = $index_position['last_entity_id'];
+      foreach ($rows as $row) {
+        if (!empty($row->status)) {
+          if ($row->changed > $max_changed) {
+            $max_changed = $row->changed;
+          }
+          if ($row->entity_id > $max_entity_id) {
+            $max_entity_id = $row->entity_id;
+          }
+        }
+      }
+      solrsearch_set_last_index_position($env_id, $entity_type, $max_changed, $max_entity_id);
+      solrsearch_set_last_index_updated($env_id, REQUEST_TIME);
+    }
+  }
+  return $documents_submitted;
+}
+
+/**
+ * Convert a certain entity from the solrsearch index table to a set of documents. 1 entity
+ * can be converted in multiple documents if the solrsearch_index_entity_to_documents decides to do so.
+ *
+ * @param array $row
+ *   A row from the indexing table
+ * @param string $entity_type
+ *   The type of the entity
+ * @param string $env_id
+ *   The machine name of the environment.
+ *
+ * @return array of solrsearchDocument(s)
+ */
+function solrsearch_index_entities_document($row, $entity_type, $env_id) {
+  $documents = array();
+  if (!empty($row->status)) {
+    // Let any module exclude this entity from the index.
+    $build_document = TRUE;
+    foreach (module_implements('solrsearch_exclude') as $module) {
+      $exclude = module_invoke($module, 'solrsearch_exclude', $row->entity_id, $entity_type, $row, $env_id);
+      // If the hook returns TRUE we should exclude the entity
+      if (!empty($exclude)) {
+        $build_document = FALSE;
+      }
+    }
+    foreach (module_implements('solrsearch_' . $entity_type . '_exclude') as $module) {
+      $exclude = module_invoke($module, 'solrsearch_' . $entity_type . '_exclude', $row->entity_id, $row, $env_id);
+      // If the hook returns TRUE we should exclude the entity
+      if (!empty($exclude)) {
+        $build_document = FALSE;
+      }
+    }
+    if ($build_document) {
+      $documents = array_merge($documents, solrsearch_index_entity_to_documents($row, $env_id));
+    }
+  }
+  else {
+    // Delete the entity from our index if the status callback returned 0
+    solrsearch_remove_entity($env_id, $row->entity_type, $row->entity_id);
+  }
+  // Clear entity cache for this specific entity
+  entity_get_controller($row->entity_type)->resetCache(array($row->entity_id));
+  return $documents;
+}
+/**
+ * Returns the total number of documents that are able to be indexed and the
+ * number of documents left to be indexed.
+ *
+ * This is a helper function for modules that implement hook_search_status().
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ *
+ * @return array
+ *   An associative array with the key-value pairs:
+ *   - remaining: The number of items left to index.
+ *   - total: The total number of items to index.
+ *
+ * @see hook_search_status()
+ */
+function solrsearch_index_status($env_id) {
+  $remaining = 0;
+  $total = 0;
+
+  foreach (entity_get_info() as $entity_type => $info) {
+    $bundles = solrsearch_get_index_bundles($env_id, $entity_type);
+    if (empty($bundles)) {
+      continue;
+    }
+
+    $table = solrsearch_get_indexer_table($entity_type);
+    $query = db_select($table, 'asn')->condition('asn.status', 1)->condition('asn.bundle', $bundles);
+    $total += $query->countQuery()->execute()->fetchField();
+
+    // Get $last_entity_id and $last_changed.
+    $last_index_position = solrsearch_get_last_index_position($env_id, $entity_type);
+    $last_entity_id = $last_index_position['last_entity_id'];
+    $last_changed = $last_index_position['last_changed'];
+
+    // Find the remaining entities to index for this entity type.
+    $query = db_select($table, 'aie')
+      ->condition('aie.bundle', $bundles)
+      ->condition('aie.status', 1)
+      ->condition(db_or()
+        ->condition('aie.changed', $last_changed, '>')
+        ->condition(db_and()
+          ->condition('aie.changed', $last_changed, '<=')
+          ->condition('aie.entity_id', $last_entity_id, '>')))
+      ->addTag('solrsearch_index_' . $entity_type);
+
+
+    if ($table == 'solrsearch_index_entities') {
+      // Other, entity-specific tables don't need this condition.
+      $query->condition('aie.entity_type', $entity_type);
+    }
+    $remaining += $query->countQuery()->execute()->fetchField();
+  }
+  return array('remaining' => $remaining, 'total' => $total);
+}
+
+/**
+ * Worker callback for solrsearch_index_entities().
+ *
+ * Loads and proccesses the entity queued for indexing and converts into one or
+ * more documents that are sent to the Apache Solr server for indexing.
+ *
+ * The entity is loaded as the user specified in the "solrsearch_index_user"
+ * system variable in order to prevent sentive data from being indexed and
+ * displayed to underprivileged users in search results. The index user defaults
+ * to a user ID of "0", which is the anonymous user.
+ *
+ * After the entity is loaded, it is converted to an array via the callback
+ * specified in the entity type's info array. The array that the entity is
+ * converted to is the model of the document sent to the Apache Solr server for
+ * indexing. This function allows develoeprs to modify the document by
+ * implementing the following hooks:
+ * - solrsearch_index_document_build()
+ * - solrsearch_index_document_build_ENTITY_TYPE()
+ * - solrsearch_index_documents_alter()
+ *
+ * @param stdClass $item
+ *   The data returned by the queue table containing:
+ *   - entity_id: An integer containing the unique identifier of the entity, for
+ *     example a node ID or comment ID.
+ *   - entity_type: The unique identifier for the entity, i.e. "node", "file".
+ *   - bundle: The machine-readable name of the bundle the passed entity is
+ *     associated with.
+ *   - status: The "published" status of the entity. The status will also be set
+ *     to "0" when entity is deleted but the Apache Solr server is unavailable.
+ *   - changed: A timestamp flagging when the entity was last modified.
+ * @param string $env_id
+ *   The machine name of the environment.
+ *
+ * @return array
+ *   An associative array of documents that are sent to the Apache Solr server
+ *   for indexing.
+ *
+ * @see solrsearch_index_nodes() for the old-skool version.
+ */
+function solrsearch_index_entity_to_documents($item, $env_id) {
+  global $user;
+  drupal_save_session(FALSE);
+  $saved_user = $user;
+  // build the content for the index as an anonymous user to avoid exposing restricted fields and such.
+  // By setting a variable, indexing can take place as a different user
+  $uid = variable_get('solrsearch_index_user', 0);
+  if ($uid == 0) {
+    $user = drupal_anonymous_user();
+  }
+  else {
+    $user = user_load($uid);
+  }
+  // Pull out all of our pertinent data.
+  $entity_type = $item->entity_type;
+
+  // Entity cache will be reset at the end of the indexing algorithm, to use the cache properly whenever
+  // the code does another entity_load
+  $entity = entity_load($entity_type, array($item->entity_id));
+  $entity = $entity ? reset($entity) : FALSE;
+
+  if (empty($entity)) {
+    // If the object failed to load, just stop.
+    return FALSE;
+  }
+
+  list($entity_id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+  // Create a new document, and do the bare minimum on it.
+  $document = _solrsearch_index_process_entity_get_document($entity, $entity_type);
+
+  //Get the callback array to add stuff to the document
+  $callbacks = solrsearch_entity_get_callback($entity_type, 'document callback', $bundle);
+  $documents = array();
+  foreach ($callbacks as $callback) {
+    // Call a type-specific callback to add stuff to the document.
+    $documents = array_merge($documents, $callback($document, $entity, $entity_type, $env_id));
+  }
+
+  //do this for all possible documents that were returned by the callbacks
+  foreach ($documents as $document) {
+    // Call an all-entity hook to add stuff to the document.
+    module_invoke_all('solrsearch_index_document_build', $document, $entity, $entity_type, $env_id);
+
+    // Call a type-specific hook to add stuff to the document.
+    module_invoke_all('solrsearch_index_document_build_' . $entity_type, $document, $entity, $env_id);
+
+    // Final processing to ensure that the document is properly structured.
+    // All records must have a label field, which is used for user-friendly labeling.
+    if (empty($document->label)) {
+      $document->label = '';
+    }
+
+    // All records must have a "content" field, which is used for fulltext indexing.
+    // If we don't have one, enter an empty value.  This does mean that the entity
+    // will not be fulltext searchable.
+    if (empty($document->content)) {
+      $document->content = '';
+    }
+
+    // All records must have a "teaser" field, which is used for abbreviated
+    // displays when no highlighted text is available.
+    if (empty($document->teaser)) {
+      $document->teaser = truncate_utf8($document->content, 300, TRUE);
+    }
+
+    // Add additional indexing based on the body of each record.
+    solrsearch_index_add_tags_to_document($document, $document->content);
+  }
+
+  // Now allow modules to alter each other's additions for maximum flexibility.
+
+  // Hook to allow modifications of the retrieved results
+  foreach (module_implements('solrsearch_index_documents_alter') as $module) {
+    $function = $module . '_solrsearch_index_documents_alter';
+    $function($documents, $entity, $entity_type, $env_id);
+  }
+
+  // Restore the user.
+  $user = $saved_user;
+  drupal_save_session(TRUE);
+
+  return $documents;
+}
+
+/**
+ * Index an array of documents to solr.
+ *
+ * @param $env_id
+ * @param array $documents
+ *
+ * @return bool|int number indexed, or FALSE on failure.
+ * @throws Exception
+ */
+function solrsearch_index_send_to_solr($env_id, array $documents) {
+  try {
+    // Get the $solr object
+    $solr = solrsearch_get_solr($env_id);
+    // If there is no server available, don't continue.
+    if (!$solr->ping(variable_get('solrsearch_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;
+  }
+  // Do not index when we do not have any documents to send
+  // Send TRUE because this is not an error
+  if (empty($documents)) {
+    return TRUE;
+  }
+  // Send the document off to Solr.
+  watchdog('Apache Solr', 'Adding @count documents.', array('@count' => count($documents)));
+  try {
+    $docs_chunk = array_chunk($documents, 20);
+    foreach ($docs_chunk as $docs) {
+      $solr->addDocuments($docs);
+    }
+    watchdog('Apache Solr', 'Indexing succeeded on @count documents', array(
+      '@count' => count($documents),
+    ), WATCHDOG_INFO);
+    return count($documents);
+  }
+  catch (Exception $e) {
+    if (!empty($docs)) {
+      foreach ($docs as $doc) {
+        $eids[] = $doc->entity_type . '/' . $doc->entity_id;
+      }
+    }
+    watchdog('Apache Solr', 'Indexing failed on one of the following entity ids: @eids <br /> !message', array(
+      '@eids' => implode(', ', $eids),
+      '!message' => nl2br(strip_tags($e->getMessage())),
+    ), WATCHDOG_ERROR);
+    return FALSE;
+  }
+}
+
+/**
+ * Extract HTML tag contents from $text and add to boost fields.
+ *
+ * @param solrsearchDocument $document
+ * @param string $text
+ *   must be stripped of control characters before hand.
+ *
+ */
+function solrsearch_index_add_tags_to_document(solrsearchDocument $document, $text) {
+  $tags_to_index = variable_get('solrsearch_tags_to_index', array(
+    'h1' => 'tags_h1',
+    'h2' => 'tags_h2_h3',
+    'h3' => 'tags_h2_h3',
+    'h4' => 'tags_h4_h5_h6',
+    'h5' => 'tags_h4_h5_h6',
+    'h6' => 'tags_h4_h5_h6',
+    'u' => 'tags_inline',
+    'b' => 'tags_inline',
+    'i' => 'tags_inline',
+    'strong' => 'tags_inline',
+    'em' => 'tags_inline',
+    'a' => 'tags_a'
+  ));
+
+  // Strip off all ignored tags.
+  $text = strip_tags($text, '<' . implode('><', array_keys($tags_to_index)) . '>');
+
+  preg_match_all('@<(' . implode('|', array_keys($tags_to_index)) . ')[^>]*>(.*)</\1>@Ui', $text, $matches);
+  foreach ($matches[1] as $key => $tag) {
+    $tag = drupal_strtolower($tag);
+    // We don't want to index links auto-generated by the url filter.
+    if ($tag != 'a' || !preg_match('@(?:http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://|www\.)[a-zA-Z0-9]+@', $matches[2][$key])) {
+      if (!isset($document->{$tags_to_index[$tag]})) {
+        $document->{$tags_to_index[$tag]} = '';
+      }
+      $document->{$tags_to_index[$tag]} .= ' ' . solrsearch_clean_text($matches[2][$key]);
+    }
+  }
+}
+
+/**
+ * Returns a generic Solr document object for this entity.
+ *
+ * This function will do the basic processing for the document that is common
+ * to all entities, but virtually all entities will need their own additional
+ * processing.
+ *
+ * @param object $entity
+ *   The entity for which we want a document.
+ * @param string $entity_type
+ *   The type of entity we're processing.
+ * @return solrsearchDocument
+ */
+function _solrsearch_index_process_entity_get_document($entity, $entity_type) {
+  list($entity_id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+  $document = new solrsearchDocument();
+
+  // Define our url options in advance. This differs depending on the
+  // language
+  $languages = language_list();
+  $url_options = array('absolute' => TRUE);
+  if (isset($entity->language) && isset($languages[$entity->language])) {
+    $url_options = $url_options + array('language' => $languages[$entity->language]);
+  }
+
+  $document->id = solrsearch_document_id($entity_id, $entity_type);
+  $document->site = url(NULL, $url_options);
+  $document->hash = solrsearch_site_hash();
+
+  $document->entity_id = $entity_id;
+  $document->entity_type = $entity_type;
+  $document->bundle = $bundle;
+  $document->bundle_name = entity_bundle_label($entity_type, $bundle);
+
+  if (empty($entity->language)) {
+    // 'und' is the language-neutral code in Drupal 7.
+    $document->language = LANGUAGE_NONE;
+  }
+  else {
+    $document->language = $entity->language;
+  }
+
+  $path = entity_uri($entity_type, $entity);
+  // A path is not a requirement of an entity
+  if (!empty($path)) {
+    $document->path = $path['path'];
+    $document->url = url($path['path'], $path['options'] + $url_options);
+    // Path aliases can have important information about the content.
+    // Add them to the index as well.
+    if (function_exists('drupal_get_path_alias')) {
+      // Add any path alias to the index, looking first for language specific
+      // aliases but using language neutral aliases otherwise.
+      $output = drupal_get_path_alias($document->path, $document->language);
+      if ($output && $output != $document->path) {
+        $document->path_alias = $output;
+      }
+    }
+  }
+  return $document;
+}
+
+/**
+ * Returns an array of rows from a query based on an indexing environment.
+ * @todo Remove the read only because it is not environment specific
+ *
+ * @param $env_id
+ * @param $entity_type
+ * @param $limit
+ *
+ * @return array list of row to index
+ */
+function solrsearch_index_get_entities_to_index($env_id, $entity_type, $limit) {
+  $rows = array();
+  if (variable_get('solrsearch_read_only', 0)) {
+    return $rows;
+  }
+  $bundles = solrsearch_get_index_bundles($env_id, $entity_type);
+  if (empty($bundles)) {
+    return $rows;
+  }
+
+  $table = solrsearch_get_indexer_table($entity_type);
+  // Get $last_entity_id and $last_changed.
+  $last_index_position = solrsearch_get_last_index_position($env_id, $entity_type);
+  $last_entity_id = $last_index_position['last_entity_id'];
+  $last_changed = $last_index_position['last_changed'];
+
+  // Find the next batch of entities to index for this entity type.  Note that
+  // for ordering we're grabbing the oldest first and then ordering by ID so
+  // that we get a definitive order.
+  // Also note that we fetch ALL fields from the indexer table
+  $query = db_select($table, 'aie')
+    ->fields('aie')
+    ->condition('aie.bundle', $bundles)
+    ->condition(db_or()
+      ->condition('aie.changed', $last_changed, '>')
+      ->condition(db_and()
+        ->condition('aie.changed', $last_changed, '<=')
+        ->condition('aie.entity_id', $last_entity_id, '>')))
+    ->orderBy('aie.changed', 'ASC')
+    ->orderBy('aie.entity_id', 'ASC')
+    ->addTag('solrsearch_index_' . $entity_type);
+
+  if ($table == 'solrsearch_index_entities') {
+    // Other, entity-specific tables don't need this condition.
+    $query->condition('aie.entity_type', $entity_type);
+  }
+  $query->range(0, $limit);
+  $records = $query->execute();
+
+  $status_callbacks = solrsearch_entity_get_callback($entity_type, 'status callback');
+  foreach ($records as $record) {
+    // Check status and status callbacks before sending to the index
+    if (is_array($status_callbacks)) {
+      foreach($status_callbacks as $status_callback) {
+        if (is_callable($status_callback)) {
+          // by placing $status in front we prevent calling any other callback
+          // after one status callback returned false
+          $record->status = $record->status && $status_callback($record->entity_id, $record->entity_type);
+        }
+      }
+    }
+    $rows[] = $record;
+  }
+  return $rows;
+}
+
+/**
+ * Delete the whole index for an environment.
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param string $entity_type
+ *   (optional) specify to remove just this entity_type from the index.
+ * @param string $bundle
+ *   (optional) also specify a bundle to remove just the bundle from
+ *   the index.
+ */
+function solrsearch_index_delete_index($env_id, $entity_type = NULL, $bundle = NULL) {
+  // Instantiate a new Solr object.
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    $query = '*:*';
+
+    if (!empty($entity_type) && !empty($bundle)) {
+      $query = "(bundle:$bundle AND entity_type:$entity_type) OR sm_parent_entity_bundle:{$entity_type}-{$bundle}";
+    }
+    elseif (!empty($bundle)) {
+      $query = "(bundle:$bundle)";
+    }
+
+    // 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:' . solrsearch_site_hash()
+    drupal_alter('solrsearch_delete_by_query', $query);
+    $solr->deleteByQuery($query);
+    $solr->commit();
+
+    if (!empty($entity_type)) {
+      $rebuild_callback = solrsearch_entity_get_callback($entity_type, 'reindex callback');
+      if (is_callable($rebuild_callback)) {
+        $rebuild_callback($env_id, $bundle);
+      }
+    }
+    else {
+      solrsearch_index_mark_for_reindex($env_id);
+    }
+
+    solrsearch_set_last_index_updated($env_id, REQUEST_TIME);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Delete from the index documents with the entity type and any of the excluded bundles.
+ *
+ * Also deletes all documents that have the entity type and bundle as a parent.
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param string $entity_type
+ * @param array $excluded_bundles
+ *
+ * @return true on success, false on failure.
+ */
+function solrsearch_index_delete_bundles($env_id, $entity_type, array $excluded_bundles) {
+  // Remove newly omitted bundles.
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    foreach ($excluded_bundles as $bundle) {
+      $query = "(bundle:$bundle AND entity_type:$entity_type) OR sm_parent_entity_bundle:{$entity_type}-{$bundle}";
+
+      // 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:' . solrsearch_site_hash()
+      drupal_alter('solrsearch_delete_by_query', $query);
+      $solr->deleteByQuery($query);
+    }
+    if ($excluded_bundles) {
+      $solr->commit();
+    }
+    return TRUE;
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    return FALSE;
+  }
+}
+
+/**
+ * Delete an entity from the index.
+ *
+ * Also deletes all documents that have the deleted document as a parent.
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param string $entity_type
+ * @param string $entity_id
+ *
+ * @return true on success, false on failure.
+ */
+function solrsearch_index_delete_entity_from_index($env_id, $entity_type, $entity_id) {
+  static $failed = FALSE;
+  if ($failed) {
+    return FALSE;
+  }
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    $document_id = solrsearch_document_id($entity_id, $entity_type);
+    $query = "id:\"$document_id\" OR sm_parent_document_id:\"$document_id\"";
+    $solr->deleteByQuery($query);
+    solrsearch_set_last_index_updated($env_id, REQUEST_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;
+  }
+}
+
+/**
+ * Mark a certain entity type for a specific environment for reindexing.
+ *
+ * @param $env_id
+ * @param null $entity_type
+ */
+function solrsearch_index_mark_for_reindex($env_id, $entity_type = NULL) {
+  foreach (entity_get_info() as $type => $entity_info) {
+    if (($type == $entity_type) || ($entity_type == NULL)) {
+      if (isset($entity_info['solrsearch']) && ($entity_info['solrsearch']['indexable'])) {
+        $reindex_callback = solrsearch_entity_get_callback($type, 'reindex callback');
+        if (!empty($reindex_callback)) {
+          call_user_func($reindex_callback, $env_id);
+        }
+      }
+    }
+  }
+  solrsearch_clear_last_index_position($env_id, $entity_type);
+  cache_clear_all('*', 'cache_solrsearch', TRUE);
+}
+
+/**
+ * Sets what bundles on the specified entity type should be indexed.
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param string $entity_type
+ *   The entity type to index.
+ * @param array $bundles
+ *   The machine names of the bundles to index.
+ *
+ * @throws Exception
+ */
+function solrsearch_index_set_bundles($env_id, $entity_type, array $bundles) {
+  $transaction = db_transaction();
+  try {
+    db_delete('solrsearch_index_bundles')
+      ->condition('env_id', $env_id)
+      ->condition('entity_type', $entity_type)
+      ->execute();
+
+    if ($bundles) {
+      $insert = db_insert('solrsearch_index_bundles')
+        ->fields(array('env_id', 'entity_type', 'bundle'));
+
+      foreach ($bundles as $bundle) {
+        $insert->values(array(
+          'env_id' => $env_id,
+          'entity_type' => $entity_type,
+          'bundle' => $bundle,
+        ));
+      }
+      $insert->execute();
+    }
+  }
+  catch (Exception $e) {
+    $transaction->rollback();
+    // Re-throw the exception so we are aware of the failure.
+    throw $e;
+  }
+}
+
+// This really should be in core, but it isn't yet.  When it gets added to core,
+// we can remove this version.
+// @see http://drupal.org/node/969180
+if (!function_exists('entity_bundle_label')) {
+
+/**
+ * Returns the label of a bundle.
+ *
+ * @param string $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param string $bundle_name
+ *   The bundle for which we want the label from
+ *
+ * @return
+ *   A string with the human-readable name of the bundle, or FALSE if not specified.
+ */
+function entity_bundle_label($entity_type, $bundle_name) {
+  $labels = &drupal_static(__FUNCTION__, array());
+
+  if (empty($labels)) {
+    foreach (entity_get_info() as $type => $info) {
+      foreach ($info['bundles'] as $bundle => $bundle_info) {
+        $labels[$type][$bundle] = !empty($bundle_info['label']) ? $bundle_info['label'] : FALSE;
+      }
+    }
+  }
+
+  return $labels[$entity_type][$bundle_name];
+}
+
+}
+
+/**
+ * Builds the node-specific information for a Solr document.
+ *
+ * @param solrsearchDocument $document
+ *   The Solr document we are building up.
+ * @param object $node
+ *   The entity we are indexing.
+ * @param string $entity_type
+ *   The type of entity we're dealing with.
+ * @param string $env_id
+ *   The type of entity we're dealing with.
+ *
+ * @return array A set of solrsearchDocument documents
+ */
+function solrsearch_index_node_solr_document(solrsearchDocument $document, $node, $entity_type, $env_id) {
+  // None of these get added unless they are explicitly in our schema.xml
+  $document->label = solrsearch_clean_text($node->title);
+
+  // Build the node body.
+  $build = node_view($node, 'search_index', !empty($node->language) ? $node->language : LANGUAGE_NONE);
+  // Remove useless html crap out of the render.
+  unset($build['#theme']);
+  $text = drupal_render($build);
+  $document->content = solrsearch_clean_text($text);
+
+  // Adding the teaser
+  if (isset($node->teaser)) {
+    $document->teaser = solrsearch_clean_text($node->teaser);
+  }
+  else {
+    $document->teaser = truncate_utf8($document->content, 300, TRUE);
+  }
+
+  // Path aliases can have important information about the content.
+  // Add them to the index as well.
+  if (function_exists('drupal_get_path_alias')) {
+    // Add any path alias to the index, looking first for language specific
+    // aliases but using language neutral aliases otherwise.
+    $language = empty($node->language) ? NULL : $node->language;
+    $path = 'node/' . $node->nid;
+    $output = drupal_get_path_alias($path, $language);
+    if ($output && $output != $path) {
+      $document->path_alias = $output;
+    }
+  }
+
+  // Author information
+  $document->ss_name = $node->name;
+  // We want the name to be searchable for keywords.
+  $document->tos_name = $node->name;
+
+  // Index formatted username so it can be searched and sorted on.
+  $account = (object) array('uid' => $node->uid, 'name' => $node->name);
+  $username = format_username($account);
+  $document->ss_name_formatted = $username;
+  $document->tos_name_formatted = $username;
+  $document->is_uid = $node->uid;
+  $document->bs_status = $node->status;
+  $document->bs_sticky = $node->sticky;
+  $document->bs_promote = $node->promote;
+  $document->is_tnid = $node->tnid;
+  $document->bs_translate = $node->translate;
+
+  // Language specific checks
+  if (empty($node->language)) {
+    // 'und' is the language-neutral code in Drupal 7.
+    $document->ss_language = LANGUAGE_NONE;
+  }
+  else {
+    $document->ss_language = $node->language;
+  }
+
+  // Timestamp of the node
+  $document->ds_created = solrsearch_date_iso($node->created);
+  $document->ds_changed = solrsearch_date_iso($node->changed);
+
+  // Comment counts + time
+  if (isset($node->last_comment_timestamp) && !empty($node->comment_count)) {
+    $document->ds_last_comment_timestamp = solrsearch_date_iso($node->last_comment_timestamp);
+    $document->ds_last_comment_or_change = solrsearch_date_iso(max($node->last_comment_timestamp, $node->changed));
+    $document->is_comment_count = $node->comment_count;
+  }
+  else {
+    $document->ds_last_comment_or_change = solrsearch_date_iso($node->changed);
+  }
+
+  // Fetch extra data normally not visible, including comments.
+  // We do this manually (with module_implements instead of node_invoke_nodeapi)
+  // because we want a keyed array to come back. Only in this way can we decide
+  // whether to index comments or not.
+  $extra = array();
+  $excludes = variable_get('solrsearch_exclude_nodeapi_types', array());
+  $exclude_nodeapi = isset($excludes[$node->type]) ? $excludes[$node->type] : array();
+
+  foreach (module_implements('node_update_index') as $module) {
+    // Invoke nodeapi if this module has not been excluded, for example,
+    // exclude 'comment' for a type to skip indexing its comments.
+    if (empty($exclude_nodeapi[$module])) {
+      $function = $module . '_node_update_index';
+      if ($output = $function($node)) {
+        $extra[$module] = $output;
+      }
+    }
+  }
+
+  // Adding the text of the comments
+  if (isset($extra['comment'])) {
+    $comments = $extra['comment'];
+    // Remove comments from the extra fields
+    unset($extra['comment']);
+    $document->ts_comments = solrsearch_clean_text($comments);
+    // @todo: do we want to reproduce solrsearch_add_tags_to_document() for comments?
+  }
+  // If there are other extra fields, add them to the document
+  if (!empty($extra)) {
+    // Use an omit-norms text field since this is generally going to be short; not
+    // really a full-text field.
+    $document->tos_content_extra = solrsearch_clean_text(implode(' ', $extra));
+  }
+
+  //  Generic use case for future reference. Callbacks can
+  //  allow you to send back multiple documents
+  $documents = array();
+  $documents[] = $document;
+  return $documents;
+}
+
+/**
+ * Function that will be executed if the node bundles were updated.
+ * Currently it does nothing, but it could potentially do something later on.
+ *
+ * @param $env_id
+ * @param $existing_bundles
+ * @param $new_bundles
+ */
+function solrsearch_index_node_bundles_changed($env_id, $existing_bundles, $new_bundles) {
+  // Nothing to do for now.
+}
+
+/**
+ * Reindexing callback for solrsearch, for nodes.
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param string|null $bundle
+ *   (optional) The bundle type to reindex. If not used
+ *   all bundles will be re-indexed.
+ *
+ * @return null
+ *   returns NULL if the specified bundle is not in the indexable bundles list
+ *
+ * @throws Exception
+ */
+function solrsearch_index_node_solr_reindex($env_id, $bundle = NULL) {
+  $indexer_table = solrsearch_get_indexer_table('node');
+  $transaction = db_transaction();
+  try {
+    $indexable_bundles = solrsearch_get_index_bundles($env_id, 'node');
+
+    if ($bundle && !empty($indexable_bundles) && !in_array($bundle, $indexable_bundles)) {
+      // The bundle specified is not in the indexable bundles list.
+      return NULL;
+    }
+
+    // Leave status 0 rows - those need to be
+    // removed from the index later.
+    $delete = db_delete($indexer_table);
+    $delete->condition('status', 1);
+
+    if (!empty($bundle)) {
+      $delete->condition('bundle', $bundle);
+    }
+    elseif (!empty($indexable_bundles)) {
+      $delete->condition('bundle', $indexable_bundles, 'IN');
+    }
+
+    $delete->execute();
+
+    $select = db_select('node', 'n');
+    $select->condition('status', 1);
+    $select->addExpression("'node'", 'entity_type');
+    $select->addField('n', 'nid', 'entity_id');
+    $select->addField('n', 'type', 'bundle');
+    $select->addField('n', 'status', 'status');
+    $select->addExpression(REQUEST_TIME, 'changed');
+
+    if ($bundle) {
+      // Mark all nodes of the specified content type for reindexing.
+      $select->condition('n.type', $bundle);
+    }
+    elseif (!empty($indexable_bundles)) {
+      // Restrict reindex to content types in the indexable bundles list.
+      $select->condition('n.type', $indexable_bundles, 'IN');
+    }
+
+    $insert = db_insert($indexer_table)
+      ->fields(array('entity_id', 'bundle', 'status', 'entity_type', 'changed'))
+      ->from($select)
+      ->execute();
+  }
+  catch (Exception $e) {
+    $transaction->rollback();
+    throw $e;
+  }
+}
+
+/**
+ * Status callback for solrsearch, for nodes.
+ * after indexing a certain amount of nodes
+ *
+ * @param $entity_id
+ * @param $entity_type
+ *
+ * @return int
+ *   The status of the node
+ */
+function solrsearch_index_node_status_callback($entity_id, $entity_type) {
+  // Make sure we have a boolean value.
+  // Anything different from 1 becomes zero
+  $entity = entity_load($entity_type, array($entity_id));
+  $entity = $entity ? reset($entity) : FALSE;
+
+  if (empty($entity)) {
+    // If the object failed to load, just stop.
+    return FALSE;
+  }
+  $status = ($entity->status == 1 ? 1 : 0);
+  return $status;
+}
+
+/**
+ * Callback that converts term_reference field into an array
+ *
+ * @param object $node
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ *   fields that will be indexed for this term reference
+ */
+function solrsearch_term_reference_indexing_callback($node, $field_name, $index_key, array $field_info) {
+  // Keep ancestors cached
+  $ancestors = &drupal_static(__FUNCTION__, array());
+
+  $fields = array();
+  $vocab_names = array();
+  if (!empty($node->{$field_name}) && function_exists('taxonomy_get_parents_all')) {
+    $field = $node->$field_name;
+    list($lang, $items) = each($field);
+    foreach ($items as $item) {
+      // Triple indexing of tids lets us do efficient searches (on tid)
+      // and do accurate per field or per-vocabulary faceting.
+
+      // By including the ancestors to a term in the index we make
+      // sure that searches for general categories match specific
+      // categories, e.g. Fruit -> apple, a search for fruit will find
+      // content categorized with apple.
+      if (!isset($ancestors[$item['tid']])) {
+        $ancestors[$item['tid']] = taxonomy_get_parents_all($item['tid']);
+      }
+      foreach ($ancestors[$item['tid']] as $ancestor) {
+        // Index parent term against the field. Note that this happens
+        // regardless of whether the facet is set to show as a hierarchy or not.
+        // We would need a separate field if we were to index terms without any
+        // hierarchy at all.
+        // If the term is singular, then we cannot add another value to the
+        // document as the field is single
+        if ($field_info['multiple'] == true) {
+          $fields[] = array(
+            'key' => $index_key,
+            'value' => $ancestor->tid,
+          );
+        }
+        $fields[] = array(
+          'key' => 'tid',
+          'value' => $ancestor->tid,
+        );
+        $fields[] = array(
+          'key' => 'im_vid_' . $ancestor->vid,
+          'value' => $ancestor->tid,
+        );
+        $name = solrsearch_clean_text($ancestor->name);
+        $vocab_names[$ancestor->vid][] = $name;
+        // We index each name as a string for cross-site faceting
+        // using the vocab name rather than vid in field construction .
+        $fields[] = array(
+          'key' => 'sm_vid_' . solrsearch_vocab_name($ancestor->vid),
+          'value' => $name,
+        );
+      }
+    }
+    // Index the term names into a text field for MLT queries and keyword searching.
+    foreach ($vocab_names as $vid => $names) {
+      $fields[] = array(
+        'key' => 'tm_vid_' . $vid . '_names',
+        'value' => implode(' ', $names),
+      );
+    }
+  }
+  return $fields;
+}
+
+/**
+ * Helper function - return a safe (PHP identifier) vocabulary name.
+ *
+ * @param integer $vid
+ * @return string
+ */
+function solrsearch_vocab_name($vid) {
+  $names = &drupal_static(__FUNCTION__, array());
+
+  if (!isset($names[$vid])) {
+    $vocab_name = db_query('SELECT v.name FROM {taxonomy_vocabulary} v WHERE v.vid = :vid', array(':vid' => $vid))->fetchField();
+    $names[$vid] = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $vocab_name);
+    // Fallback for names ending up all as '_'.
+    $check = rtrim($names[$vid], '_');
+    if (!$check) {
+      $names[$vid] = '_' . $vid . '_';
+    }
+  }
+  return $names[$vid];
+}
+
+/**
+ * Callback that converts list module field into an array
+ * For every multivalued value we also add a single value to be able to
+ * use the stats
+ *
+ * @param object $entity
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ */
+function solrsearch_fields_default_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  $numeric = TRUE;
+  if (!empty($entity->{$field_name})) {
+    $field = $entity->$field_name;
+    list($lang, $values) = each($field);
+    switch ($field_info['index_type']) {
+      case 'integer':
+      case 'half-int':
+      case 'sint':
+      case 'tint':
+      case 'thalf-int':
+      case 'boolean':
+        $function = 'intval';
+        break;
+      case 'float':
+      case 'double':
+      case 'sfloat':
+      case 'sdouble':
+      case 'tfloat':
+      case 'tdouble':
+        $function = 'solrsearch_floatval';
+        break;
+      default:
+        $numeric = FALSE;
+        $function = 'solrsearch_clean_text';
+    }
+    for ($i = 0; $i < count($values); $i++) {
+      $fields[] = array(
+        'key' => $index_key,
+        'value' => $function($values[$i]['value']),
+      );
+    }
+    // Also store the first value of the field in a singular index for multi value fields
+    if ($field_info['multiple'] && $numeric && !empty($values[0])) {
+      $singular_field_info = $field_info;
+      $singular_field_info['multiple'] = FALSE;
+      $single_key = solrsearch_index_key($singular_field_info);
+      $fields[] = array(
+        'key' => $single_key,
+        'value' => $function($values[0]['value']),
+      );
+    }
+  }
+  return $fields;
+}
+
+/**
+ * This function is used during indexing to normalize the DATE and DATETIME
+ * fields into the appropriate format for Apache Solr.
+ *
+ * @param object $entity
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ */
+function solrsearch_date_default_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  if (!empty($entity->{$field_name})) {
+    $field = $entity->$field_name;
+    list($lang, $values) = each($field);
+    // Construct a Solr-ready date string in UTC time zone based on the field's date string and time zone.
+    $tz = new DateTimeZone(isset($field['timezone']) ? $field['timezone'] : 'UTC');
+
+    // $fields may end up having two values; one for the start date
+    // and one for the end date.
+    foreach ($values as $value) {
+      if ($date = date_create($value['value'], $tz)) {
+        $index_value = solrsearch_date_iso($date->format('U'));
+        $fields[] = array(
+          'key' => $index_key,
+          'value' => $index_value,
+        );
+      }
+
+      if (isset($value['value2'])) {
+        if ($date = date_create($value['value2'], $tz)) {
+          $index_value = solrsearch_date_iso($date->format('U'));
+          $fields[] = array(
+            // The value2 element is the end date. Therefore it gets indexed
+            // into its own Solr field.
+            'key' => $index_key . '_end',
+            'value' => $index_value,
+          );
+        }
+      }
+    }
+  }
+  return $fields;
+}
+
+/**
+ * This function is used during indexing to normalize the DATESTAMP fields
+ * into the appropriate format for Apache Solr.
+ *
+ * @param object $entity
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ */
+function solrsearch_datestamp_default_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  if (!empty($entity->{$field_name})) {
+    // $fields may end up having two values; one for the start date
+    // and one for the end date.
+    $field = $entity->$field_name;
+    list($lang, $values) = each($field);
+
+    foreach ($values as $value) {
+      if (isset($value['value']) && $value['value'] != 0) {
+        $index_value = solrsearch_date_iso($value['value']);
+        $fields[] = array(
+          'key' => $index_key,
+          'value' => $index_value,
+        );
+      }
+      if (isset($value['value2']) && $value['value'] != 0) {
+        $index_value = solrsearch_date_iso($value['value2']);
+        $fields[] = array(
+          // The value2 element is the end date. Therefore it gets indexed
+          // into its own Solr field.
+          'key' => $index_key . '_end',
+          'value' => $index_value,
+        );
+      }
+    }
+  }
+  return $fields;
+}
+
+function solrsearch_floatval($value) {
+  return sprintf('%0.20f', $value);
+}
+
+/**
+ *  Indexing callback for the node_reference module
+ *  by the references module
+ *
+ * @param object $entity
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ */
+function solrsearch_nodereference_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  if (!empty($entity->{$field_name})) {
+    $index_key = solrsearch_index_key($field_info);
+    foreach ($entity->$field_name as $field_references) {
+      foreach ($field_references as $reference) {
+        if ($index_value = (!empty($reference['nid'])) ? $reference['nid'] : FALSE) {
+          $fields[] = array(
+            'key' => $index_key,
+            'value' => $index_value,
+          );
+        }
+      }
+    }
+  }
+  return $fields;
+}
+
+/**
+ *  Indexing callback for the user_reference module
+ *  by the references module
+ *
+ * @param object $entity
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ */
+function solrsearch_userreference_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  if (!empty($entity->$field_name)) {
+    $index_key = solrsearch_index_key($field_info);
+    foreach ($entity->$field_name as $field_references) {
+      foreach ($field_references as $reference) {
+        if ($index_value = (isset($reference['uid']) && strlen($reference['uid'])) ? $reference['uid'] : FALSE) {
+          $fields[] = array(
+            'key' => $index_key,
+            'value' => $index_value,
+          );
+        }
+      }
+    }
+  }
+  return $fields;
+}
+
+/**
+ * Indexing callback for entityreference fields.
+ *
+ * @param object $entity
+ * @param string $field_name
+ * @param string $index_key
+ * @param array $field_info
+ * @return array $fields
+ *
+ */
+function solrsearch_entityreference_indexing_callback($entity, $field_name, $index_key, $field_info) {
+  $fields = array();
+  if (!empty($entity->{$field_name})) {
+
+    // Gets entity type and index key. We need to prefix the ID with the entity
+    // type so we know what entity we are dealing with in the mapping callback.
+    $entity_type = $field_info['field']['settings']['target_type'];
+    $index_key = solrsearch_index_key($field_info);
+
+    // Iterates over all references and adds them to the fields.
+    foreach ($entity->$field_name as $entity_references) {
+      foreach ($entity_references as $reference) {
+        if ($id = (!empty($reference['target_id'])) ? $reference['target_id'] : FALSE) {
+          $fields[] = array(
+            'key' => $index_key,
+            'value' => $entity_type . ':' . $id,
+          );
+        }
+      }
+    }
+  }
+  return $fields;
+}
+
+/**
+ * Extract HTML tag contents from $text and add to boost fields.
+ *
+ * $text must be stripped of control characters before hand.
+ *
+ * @param solrsearchDocument $document
+ * @param type $text
+ */
+function solrsearch_add_tags_to_document(solrsearchDocument $document, $text) {
+  $tags_to_index = variable_get('solrsearch_tags_to_index', array(
+    'h1' => 'tags_h1',
+    'h2' => 'tags_h2_h3',
+    'h3' => 'tags_h2_h3',
+    'h4' => 'tags_h4_h5_h6',
+    'h5' => 'tags_h4_h5_h6',
+    'h6' => 'tags_h4_h5_h6',
+    'u' => 'tags_inline',
+    'b' => 'tags_inline',
+    'i' => 'tags_inline',
+    'strong' => 'tags_inline',
+    'em' => 'tags_inline',
+    'a' => 'tags_a'
+  ));
+
+  // Strip off all ignored tags.
+  $text = strip_tags($text, '<' . implode('><', array_keys($tags_to_index)) . '>');
+
+  preg_match_all('@<(' . implode('|', array_keys($tags_to_index)) . ')[^>]*>(.*)</\1>@Ui', $text, $matches);
+  foreach ($matches[1] as $key => $tag) {
+    $tag = strtolower($tag);
+    // We don't want to index links auto-generated by the url filter.
+    if ($tag != 'a' || !preg_match('@(?:http://|https://|ftp://|mailto:|smb://|afp://|file://|gopher://|news://|ssl://|sslv2://|sslv3://|tls://|tcp://|udp://|www\.)[a-zA-Z0-9]+@', $matches[2][$key])) {
+      if (!isset($document->{$tags_to_index[$tag]})) {
+        $document->{$tags_to_index[$tag]} = '';
+      }
+      $document->{$tags_to_index[$tag]} .= ' ' . solrsearch_clean_text($matches[2][$key]);
+    }
+  }
+}
+
+/**
+ * hook_cron() helper to try to make the index table consistent with their
+ * respective entity table.
+ */
+function solrsearch_index_node_check_table() {
+  // Check for unpublished content that wasn't deleted from the index.
+  $table = solrsearch_get_indexer_table('node');
+  // We do not check more nodes than double the cron limit per time
+  // Update or delete at most this many in each Solr query.
+  $limit = variable_get('solrsearch_cron_mass_limit', 500);
+  $query = db_select($table, 'aien')
+    ->fields('n', array('nid', 'status'))
+    ->where('aien.status <> n.status')
+    ->range(0, ($limit * 2))
+    ->addTag('solrsearch_index_node');
+  $query->innerJoin('node', 'n', 'n.nid = aien.entity_id');
+  $nodes = $query->execute()->fetchAllAssoc('nid');
+
+  $node_lists = array_chunk($nodes, $limit, TRUE);
+  foreach ($node_lists as $nodes) {
+    watchdog('Apache Solr', 'On cron running solrsearch_nodeapi_mass_update() on nids @nids', array('@nids' => implode(',', array_keys($nodes))), WATCHDOG_NOTICE);
+    if (!solrsearch_index_nodeapi_mass_update($nodes, $table)) {
+      // Solr query failed - so stop trying.
+      break;
+    }
+  }
+
+  // Check for deleted content that wasn't deleted from the index.
+  $query = db_select($table, 'aien')
+    ->isNull('n.nid')
+    ->range(0, ($limit*2));
+  $query->addExpression('aien.entity_id', 'nid');
+  $query->leftJoin('node', 'n', 'n.nid = aien.entity_id');
+  $nodes = $query->execute()->fetchAllAssoc('nid');
+  $node_lists = array_chunk($nodes, $limit, TRUE);
+
+  foreach ($node_lists as $nodes) {
+    watchdog('Apache Solr', 'On cron running solrsearch_nodeapi_mass_delete() on nids @nids', array('@nids' => implode(',', array_keys($nodes))), WATCHDOG_NOTICE);
+    if (!solrsearch_index_nodeapi_mass_delete($nodes, $table)) {
+      // Solr query failed - so stop trying.
+      break;
+    }
+  }
+}
+
+/**
+ * Mass Update nodes from the solr indexer table
+ *
+ * @param array $nodes
+ * @param string $table
+ * @return boolean
+ *   true if we mass updated, false if failed
+ */
+function solrsearch_index_nodeapi_mass_update(array $nodes, $table = NULL) {
+  if (empty($nodes)) {
+    return TRUE;
+  }
+  if (empty($table)) {
+    $table = solrsearch_get_indexer_table('node');
+  }
+
+  if (solrsearch_environment_variable_get(solrsearch_default_environment(), 'solrsearch_read_only', solrsearch_READ_WRITE) == solrsearch_READ_ONLY) {
+    return TRUE;
+  }
+
+  $published_ids = array();
+  $unpublished_ids = array();
+  foreach ($nodes as $node) {
+    if ($node->status) {
+      $published_ids[$node->nid] = solrsearch_document_id($node->nid);
+    }
+    else {
+      $unpublished_ids[$node->nid] = solrsearch_document_id($node->nid);
+    }
+  }
+  try {
+    $env_id = solrsearch_default_environment();
+    $solr = solrsearch_get_solr($env_id);
+    $solr->deleteByMultipleIds($unpublished_ids);
+    solrsearch_set_last_index_updated($env_id, REQUEST_TIME);
+
+      // There was no exception, so update the table.
+    if ($published_ids) {
+      db_update($table)
+        ->fields(array('changed' => REQUEST_TIME, 'status' => 1))
+        ->condition('entity_id', array_keys($published_ids), 'IN')
+        ->execute();
+    }
+    if ($unpublished_ids) {
+      db_update($table)
+        ->fields(array('changed' => REQUEST_TIME, 'status' => 0))
+        ->condition('entity_id', array_keys($unpublished_ids), 'IN')
+        ->execute();
+    }
+    return TRUE;
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    return FALSE;
+  }
+}
+
+/**
+ * Mass delete nodes from the solr indexer tables.
+ *
+ * @param array $nodes
+ * @param string $table
+ * @return boolean
+ *   true if we mass updated, false if failed
+ */
+function solrsearch_index_nodeapi_mass_delete(array $nodes, $table = NULL) {
+  if (empty($nodes)) {
+    return TRUE;
+  }
+  if (empty($table)) {
+    $table = solrsearch_get_indexer_table('node');
+  }
+
+  if (solrsearch_environment_variable_get(solrsearch_default_environment(), 'solrsearch_read_only', solrsearch_READ_WRITE) == solrsearch_READ_ONLY) {
+    return TRUE;
+  }
+
+  $ids = array();
+  $nids = array();
+  foreach ($nodes as $node) {
+    $ids[] = solrsearch_document_id($node->nid);
+    $nids[] = $node->nid;
+  }
+  try {
+    $env_id = solrsearch_default_environment();
+    $solr = solrsearch_get_solr($env_id);
+    $solr->deleteByMultipleIds($ids);
+    solrsearch_set_last_index_updated($env_id, REQUEST_TIME);
+    // There was no exception, so update the table.
+    db_delete($table)
+      ->condition('entity_id', $nids, 'IN')
+      ->execute();
+    return TRUE;
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    return FALSE;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.info	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,25 @@
+name = solrsearch
+description = Frontend for searching Solr
+core = 7.x
+version = 0.5
+stylesheets[all][] = solrsearch.css
+package = SolrSearch
+dependencies[] = digitalobjects
+
+
+
+configure = admin/config/search/searchsolr/settings
+files[] = solrsearch.install
+files[] = solrsearch.module
+files[] = solrsearch.admin.inc
+files[] = solrsearch.interface.inc
+files[] = Drupal_Apache_Solr_Service.php
+files[] = Apache_Solr_Document.php
+files[] = Solr_Base_Query.php
+files[] = plugins/facetapi/adapter.inc
+files[] = plugins/facetapi/query_type_date.inc
+files[] = plugins/facetapi/query_type_term.inc
+files[] = plugins/facetapi/query_type_numeric_range.inc
+files[] = plugins/facetapi/query_type_integer.inc
+files[] = plugins/facetapi/query_type_geo.inc
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.install	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,899 @@
+<?php
+
+/**
+ * @file
+ *   Install and related hooks for solrsearch_search.
+ */
+
+/**
+ * Implements hook_requirements().
+ */
+function solrsearch_requirements($phase) {
+  $requirements = array();
+  if ($phase != 'runtime') {
+    return $requirements;
+  }
+  // Ensure translations don't break at install time
+  $t = get_t();
+  $has_settings = FALSE;
+  $id = solrsearch_default_environment();
+  $environment = solrsearch_environment_load($id);
+  if (!$environment || empty($environment['url'])) {
+    $requirements['solrsearch'] = array(
+      'title' => $t('Solr search'),
+      'value' => $t('Missing environment configuration'),
+      'description' => $t('Missing or invalid Solr environment record for the default environment ID %id.', array('%id' => $id)),
+      'severity' => REQUIREMENT_ERROR,
+    );
+  }
+  else {
+    $has_settings = TRUE;
+  }
+
+  if ($has_settings) {
+    $ping = FALSE;
+    try {
+      $solr = solrsearch_get_solr($id);
+      $ping = @$solr->ping(variable_get('solrsearch_ping_timeout', 4));
+      // If there is no $solr object, there is no instance 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;
+    $requirements['solrsearch'] = array(
+      'title' => $t('Apache Solr'),
+      'value' => $value,
+      'description' => $t('Default environment url: <br/> %url',  array('%url' => $environment['url'])),
+      'severity' => $severity,
+    );
+  }
+
+  return $requirements;
+}
+
+/**
+ * Implements hook_install().
+ */
+function solrsearch_install() {
+  module_load_include('inc', 'solrsearch', 'solrsearch_search.admin');
+  /*module_load_include('inc', 'solrsearch', 'solrsearch.index');*/
+  // Create one MLT block.
+  solrsearch_search_mlt_save_block(array('name' => st('More like this')));
+  db_insert('solrsearch_environment')->fields(array('env_id' => 'echosearch', 'name' => 'localhost server', 'url' => 'http://localhost:8983/solr'))->execute();
+
+  // Initialize the entities to index. We enable all node types by default
+  $info = entity_get_info('node');
+  $bundles = array_keys($info['bundles']);
+  $env_id = solrsearch_default_environment();
+  /*solrsearch_index_set_bundles($env_id, 'node', $bundles);*/
+
+  drupal_set_message(st('Apache Solr is enabled. Visit the <a href="@settings_link">settings page</a>.', array('@settings_link' => url('admin/config/search/solrsearch'))));
+}
+
+/**
+ * Implements hook_enable().
+ */
+function solrsearch_enable() {
+  // Completely build the index table.
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  $env_id = solrsearch_default_environment();
+  /*solrsearch_index_mark_for_reindex($env_id);*/
+}
+
+/**
+ * Implements hook_schema().
+ */
+function solrsearch_schema() {
+
+  $table = drupal_get_schema_unprocessed('system', 'cache');
+  $table['description'] = 'Cache table for solrsearch to store Luke data and indexing information.';
+  $schema['cache_solrsearch'] = $table;
+
+  $schema['solrsearch_environment'] = array(
+    'description' => 'The Solr search environment table.',
+    // Enable CTools exportables based on this table.
+    'export' => array(
+      // Environment machine name.
+      'key' => 'env_id',
+      // Description of key.
+      'key name' => 'Environment machine name',
+      // Apache Solr doesn't allow disabling environments.
+      'can disable' => FALSE,
+      // Variable name to use in exported code.
+      'identifier' => 'environment',
+      // Thin wrapper for the environment save callback.
+      'save callback' => 'solrsearch_ctools_environment_save',
+      // Thin wrapper for the environment delete callback.
+      'delete callback' => 'solrsearch_ctools_environment_delete',
+      // Includes the environment variables in 'conf' as well as the fields in this table.
+      'export callback' => 'solrsearch_ctools_environment_export',
+      // Use the same hook as the API name below.
+      'default hook' => 'solrsearch_environments',
+      // CTools API implementation.
+      'api' => array(
+        'owner' => 'solrsearch',
+        'api' => 'solrsearch_environments',
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
+    'fields' => array(
+      'env_id' => array(
+        'description' => 'Unique identifier for the environment',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'Human-readable name for the server',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'url' => array(
+        'description' => 'Full url for the server',
+        'type' => 'varchar',
+        'length' => 1000,
+        'not null' => TRUE,
+      ),
+      'service_class' => array(
+        'description' => 'Optional class name to use for connection',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+    ),
+    'primary key' => array('env_id'),
+  );
+  $schema['solrsearch_environment_variable'] = array(
+    'description' => 'Variable values for each Solr search environment.',
+    'fields' => array(
+      'env_id' => array(
+        'description' => 'Unique identifier for the environment',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'The name of the variable.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'value' => array(
+        'description' => 'The value of the variable.',
+        'type' => 'blob',
+        'not null' => TRUE,
+        'size' => 'big',
+      ),
+    ),
+    'primary key' => array('env_id', 'name'),
+  );
+
+  // Technically the entity system does not require an integer ID.
+  // However, documentation mentions :
+  // id: The name of the property that contains the primary id of the
+  // entity. Every entity object passed to the Field API must have this
+  // property and its value must be numeric.
+
+  //Predefine an amount of types that get their own table
+  $types = array(
+      'other' => 'solrsearch_index_entities',
+      'node' => 'solrsearch_index_entities_node',
+  );
+  foreach ($types as $type => $table) {
+    $schema[$table] = array(
+      'description' => 'Stores a record of when an entity changed to determine if it needs indexing by Solr.',
+      'fields' => array(
+        'entity_type' => array(
+          'description' => 'The type of entity.',
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+        ),
+        'entity_id' => array(
+          'description' => 'The primary identifier for an entity.',
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+        ),
+        'bundle' => array(
+          'description' => 'The bundle to which this entity belongs.',
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+        ),
+        'status' => array(
+          'description' => 'Boolean indicating whether the entity should be in the index.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 1,
+        ),
+        'changed' => array(
+          'description' => 'The Unix timestamp when an entity was changed.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+      ),
+      'indexes' => array(
+        'bundle_changed' => array('bundle', 'changed'),
+      ),
+      'primary key' => array('entity_id'),
+    );
+    if ($type == 'other') {
+      // Need the entity type also in the pkey for multiple entities in one table.
+      $schema[$table]['primary key'][] = 'entity_type';
+    }
+  }
+
+  $schema['solrsearch_index_bundles'] = array(
+    'description' => 'Records what bundles we should be indexing for a given environment.',
+    'fields' => array(
+      'env_id' => array(
+        'description' => 'The name of the environment.',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+      ),
+      'entity_type' => array(
+        'description' => 'The type of entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+      ),
+      'bundle' => array(
+        'description' => 'The bundle to index.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+      ),
+    ),
+    'primary key' => array('env_id', 'entity_type', 'bundle'),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function solrsearch_uninstall() {
+  // Remove variables.
+  variable_del('solrsearch_default_environment');
+  variable_del('solrsearch_rows');
+  variable_del('solrsearch_site_hash');
+  variable_del('solrsearch_index_last');
+  variable_del('solrsearch_search_mlt_blocks');
+  variable_del('solrsearch_cron_limit');
+  variable_del('solrsearch_exclude_nodeapi_types');
+  variable_del('solrsearch_failure');
+  variable_del('solrsearch_index_updated');
+  variable_del('solrsearch_read_only');
+  variable_del('solrsearch_set_nodeapi_messages');
+  variable_del('solrsearch_last_optimize');
+  variable_del('solrsearch_update_from_6303');
+  // Remove blocks.
+  db_delete('block')->condition('module', 'solrsearch')->execute();
+}
+
+/**
+ * Add a table to track Solr servers.
+ */
+function solrsearch_update_7000() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $schema['solrsearch_server'] = array(
+    'description' => 'The Solr server table.',
+    'fields' => array(
+     'server_id' => array(
+        'description' => 'Unique identifier for the server',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'Human-readable name for the server',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'scheme' => array(
+        'description' => 'Preferred scheme for the registered server',
+        'type' => 'varchar',
+        'length' => 10,
+        'not null' => TRUE,
+        'default' => 'http'
+      ),
+      'host' => array(
+        'description' => 'Host name for the registered server',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'port' => array(
+        'description' => 'Port number for the registered server',
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'path' => array(
+        'description' => 'Path to the registered server',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'service_class' => array(
+        'description' => 'Optional class name to use for connection',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+    ),
+    'primary key' => array('server_id'),
+  );
+  db_create_table('solrsearch_server', $schema['solrsearch_server']);
+  // Insert into the table the current single server record.
+  $host = variable_get('solrsearch_host', 'localhost');
+  $port = variable_get('solrsearch_port', '8983');
+  $path = variable_get('solrsearch_path', '/solr');
+  db_insert('solrsearch_server')->fields(array('server_id' => 'solr', 'name' => 'Apache Solr server', 'host' => $host, 'port' => $port, 'path' => $path))->execute();
+  variable_set('solrsearch_default_server', 'solr');
+  variable_del('solrsearch_host');
+  variable_del('solrsearch_port');
+  variable_del('solrsearch_path');
+  $value = variable_get('solrsearch_service_class', NULL);
+  if (is_array($value)) {
+    list($module, $filepath, $class) = $value;
+    variable_set('solrsearch_service_class', $class);
+  }
+  variable_del('solrsearch_logging');
+}
+
+
+/**
+ * Re-jigger the schema to use fewer, shorter keys.
+ */
+function solrsearch_update_7001() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  if (db_field_exists('solrsearch_server', 'asid')) {
+    // You installed the beta1 and need to be fixed up.
+    db_drop_field('solrsearch_server', 'asid');
+    db_drop_unique_key('solrsearch_server', 'server_id');
+    db_add_primary_key('solrsearch_server', array('server_id'));
+    db_drop_unique_key('solrsearch_server', 'host_post_path');
+    db_change_field('solrsearch_server', 'port', 'port',
+      array(
+        'description' => 'Port number for the registered server',
+        'type' => 'int',
+        'not null' => TRUE,
+      )
+    );
+  }
+}
+
+/**
+ * Create the per-server variable table.
+ */
+function solrsearch_update_7002() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $schema['solrsearch_server_variable'] = array(
+    'description' => 'Variable values for each Solr server.',
+    'fields' => array(
+     'server_id' => array(
+        'description' => 'Unique identifier for the server',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'The name of the variable.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'value' => array(
+        'description' => 'The value of the variable.',
+        'type' => 'blob',
+        'not null' => TRUE,
+        'size' => 'big',
+      ),
+    ),
+    'primary key' => array('server_id', 'name'),
+  );
+  db_create_table('solrsearch_server_variable', $schema['solrsearch_server_variable']);
+  $server_id = variable_get('solrsearch_default_server', 'solr');
+  // Variables to be migrated:
+  $conf['solrsearch_enabled_facets'] = variable_get('solrsearch_enabled_facets', NULL);
+  $conf['solrsearch_search_query_fields'] = variable_get('solrsearch_search_query_fields', NULL);
+  $conf['solrsearch_search_type_boosts'] = variable_get('solrsearch_search_type_boosts', NULL);
+  $conf['solrsearch_search_comment_boost'] = variable_get('solrsearch_search_comment_boost', NULL);
+  $conf['solrsearch_search_changed_boost'] = variable_get('solrsearch_search_changed_boost', NULL);
+  $conf['solrsearch_search_sticky_boost'] = variable_get('solrsearch_search_sticky_boost', NULL);
+  $conf['solrsearch_search_promote_boost'] = variable_get('solrsearch_search_promote_boost', NULL);
+  $conf['solrsearch_search_excluded_types'] = variable_get('solrsearch_search_excluded_types', NULL);
+  foreach ($conf as $name => $value) {
+    if ($value !== NULL) {
+      db_merge('solrsearch_server_variable')
+        ->key(array('server_id' => $server_id, 'name' => $name))
+        ->fields(array('value' => serialize($value)))
+        ->execute();
+    }
+    variable_del($name);
+  }
+}
+
+/**
+ * Move excluded comment types into a new variable.
+ */
+function solrsearch_update_7003() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // Same as solrsearch_update_6006()
+  $exclude_comment_types = variable_get('solrsearch_exclude_comments_types', NULL);
+  if (is_array($exclude_comment_types)) {
+    $exclude = array();
+    foreach ($exclude_comment_types as $type) {
+      $exclude[$type]['comment'] = TRUE;
+    }
+    variable_set('solrsearch_exclude_nodeapi_types', $exclude);
+  }
+  variable_del('solrsearch_exclude_comments_types');
+}
+
+/**
+ * Update solrsearch_failure variable.
+ */
+function solrsearch_update_7004() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $failure = variable_get('solrsearch_failure', NULL);
+  switch ($failure) {
+    case 'show_error':
+      variable_set('solrsearch_failure', 'solrsearch:show_error');
+      break;
+    case 'show_drupal_results':
+      variable_set('solrsearch_failure', 'node');
+      break;
+    case 'show_no_results':
+      variable_set('solrsearch_failure', 'solrsearch:show_no_results');
+      break;
+  }
+}
+
+/**
+ * Re-jigger the schema to use just a url column.
+ */
+function solrsearch_update_7005() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  if (db_field_exists('solrsearch_server', 'port')) {
+    // You installed the beta3 and need to be fixed up.
+    $servers = db_query('SELECT * FROM {solrsearch_server}')->fetchAllAssoc('server_id', PDO::FETCH_ASSOC);
+    db_drop_field('solrsearch_server', 'scheme');
+    db_drop_field('solrsearch_server', 'port');
+    db_drop_field('solrsearch_server', 'path');
+    db_change_field('solrsearch_server', 'host', 'url',
+      array(
+        'description' => 'Full url for the server',
+        'type' => 'varchar',
+        'length' => 1000,
+        'not null' => TRUE,
+      )
+    );
+    foreach ($servers as $id => $server) {
+      $port = $server['port'] ? ':' . $server['port'] : '';
+      $url = $server['scheme'] . '://' . $server['host'] . $port . $server['path'];
+    db_update('solrsearch_server')
+      ->fields(array('url' => $url))
+      ->condition('server_id', $id)
+      ->execute();
+    }
+  }
+}
+
+/**
+ * Remove facet-related variable deprecated by the Facet API integration.
+ */
+function solrsearch_update_7006() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  variable_del('solrsearch_facetstyle');
+  variable_del('solrsearch_facet_show_children');
+  variable_del('solrsearch_facet_query_limits');
+  variable_del('solrsearch_facet_query_limit_default');
+}
+
+/**
+ * Rename tables to make them more generic.
+ */
+function solrsearch_update_7007() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  db_drop_primary_key('solrsearch_server');
+  db_drop_primary_key('solrsearch_server_variable');
+  db_rename_table('solrsearch_server', 'solrsearch_environment');
+  db_rename_table('solrsearch_server_variable', 'solrsearch_environment_variable');
+  db_change_field('solrsearch_environment', 'server_id', 'env_id', array(
+    'description' => 'Unique identifier for the environment',
+    'type' => 'varchar',
+    'length' => 64,
+    'not null' => TRUE)
+  );
+  db_change_field('solrsearch_environment_variable', 'server_id', 'env_id', array(
+    'description' => 'Unique identifier for the environment',
+    'type' => 'varchar',
+    'length' => 64,
+    'not null' => TRUE)
+  );
+  db_add_primary_key('solrsearch_environment', array('env_id'));
+  db_add_primary_key('solrsearch_environment_variable', array('env_id', 'name'));
+  $id = variable_get('solrsearch_default_server', NULL);
+  if (isset($id)) {
+    variable_set('solrsearch_default_environment', $id);
+  }
+  variable_del('solrsearch_default_server');
+}
+
+/**
+ * Remove more facet-related variable deprecated by the Facet API integration.
+ */
+function solrsearch_update_7008() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  variable_del('solrsearch_facet_missing');
+  variable_del('solrsearch_facet_query_initial_limits');
+  variable_del('solrsearch_facet_query_sorts');
+  variable_del('solrsearch_facet_sort_active');
+  variable_del('solrsearch_operator');
+}
+
+/**
+ * Update Facet API block deltas to account for removal of numeric ID from field names.
+ */
+function solrsearch_update_7009() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // Only run when facetapi is available and/or installed
+  if (module_exists('facetapi')) {
+    module_load_include('inc', 'facetapi', 'facetapi.block');
+    // Get all searchers
+    $searchers = facetapi_get_searcher_info();
+    $realms = facetapi_get_realm_info();
+    foreach ($searchers as $searcher_id => $searcher) {
+      foreach ($realms as $realm_id => $realm) {
+        foreach (field_info_fields() as $field_name => $field) {
+          // Generate the old delta
+          $facet_name_old = $field['id'] . '_' . $field['field_name'];
+          $delta_old = facetapi_build_delta($searcher['name'], $realm['name'], $facet_name_old);
+          $delta_old = substr(drupal_hash_base64($delta_old), 0, 32);
+          // Generate the new delta
+          $facet_name = $field['field_name'];
+          $delta = facetapi_build_delta($searcher['name'], $realm['name'], $facet_name);
+          $delta = substr(drupal_hash_base64($delta), 0, 32);
+          db_update('block')
+            ->fields(array('delta' => $delta))
+            ->condition('module', 'facetapi')
+            ->condition('delta', $delta_old)
+            ->execute();
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Update cache table schema for Drupal 7.
+ */
+function solrsearch_update_7010() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  db_drop_field('cache_solrsearch', 'headers');
+  return 'Updated cache table schema for Drupal 7.';
+}
+
+/**
+ * Change the namespace for the indexer from solrsearch_search to solrsearch
+ */
+function solrsearch_update_7011() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $stored = variable_get('solrsearch_index_last', array());
+  if (isset($stored['solrsearch_search'])) {
+    $stored['solrsearch'] = $stored['solrsearch_search'];
+    unset($stored['solrsearch_search']);
+    variable_set('solrsearch_index_last', $stored);
+  }
+  return 'Updated the namespace variable for the index process.';
+}
+
+/**
+ * Rename some variables and update the database tables
+ */
+function solrsearch_update_7012() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // @see: drupal_load()
+  if (!function_exists('solrsearch_default_environment')) {
+    include_once dirname(__FILE__) . '/solrsearch.module';
+  }
+
+  $env_id = solrsearch_default_environment();
+
+  // Variable changed from integer to array with environment integers
+  $stored = variable_get('solrsearch_index_last', array());
+  if (isset($stored['solrsearch'])) {
+    $stored[$env_id]['node']['last_entity_id'] = $stored['solrsearch']['last_nid'];
+    $stored[$env_id]['node']['last_changed'] = $stored['solrsearch']['last_change'];
+    unset($stored['solrsearch']);
+    variable_set('solrsearch_index_last', $stored);
+  }
+  $last = variable_get('solrsearch_index_updated', NULL);
+  if (isset($last)) {
+    variable_set('solrsearch_index_updated', array($env_id => (int) $last));
+  }
+
+  // Change namespace to environment id
+  $excluded_types = solrsearch_environment_variable_get('solrsearch', 'solrsearch_search_excluded_types', array());
+  if (!empty($excluded_types)) {
+    solrsearch_environment_variable_set($env_id, 'solrsearch_search_excluded_types', $excluded_types);
+    solrsearch_environment_variable_del('solrsearch', 'solrsearch_search_excluded_types');
+  }
+
+  // Install the new schema
+  //Predefine an amount of types that get their own table
+  $types = array(
+      'other' => 'solrsearch_index_entities',
+      'node' => 'solrsearch_index_entities_node',
+  );
+  foreach ($types as $type => $table) {
+    $schema[$table] = array(
+      'description' => 'Stores a record of when an entity changed to determine if it needs indexing by Solr.',
+      'fields' => array(
+        'entity_type' => array(
+          'description' => 'The type of entity.',
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+        ),
+        'entity_id' => array(
+          'description' => 'The primary identifier for an entity.',
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+        ),
+        'bundle' => array(
+          'description' => 'The bundle to which this entity belongs.',
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+        ),
+        'status' => array(
+          'description' => 'Boolean indicating whether the entity is visible to non-administrators (eg, published for nodes).',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 1,
+        ),
+        'changed' => array(
+          'description' => 'The Unix timestamp when an entity was changed.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+      ),
+      'indexes' => array(
+        'changed' => array('bundle', 'status', 'changed'),
+      ),
+      'primary key' => array('entity_id'),
+    );
+    if ($type == 'other') {
+      // Need the entity type also in the pkey for multiple entities in one table.
+      $schema[$table]['primary key'][] = 'entity_type';
+    }
+    // Create the table
+    db_create_table($table, $schema[$table]);
+  }
+
+  $schema['solrsearch_index_bundles'] = array(
+    'description' => 'Records what bundles we should be indexing for a given environment.',
+    'fields' => array(
+      'env_id' => array(
+        'description' => 'The name of the environment.',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+      ),
+      'entity_type' => array(
+        'description' => 'The type of entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+      ),
+      'bundle' => array(
+        'description' => 'The bundle to index.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+      ),
+    ),
+    'primary key' => array('env_id', 'entity_type', 'bundle'),
+  );
+  db_create_table('solrsearch_index_bundles', $schema['solrsearch_index_bundles']);
+
+
+  // Move the data from solrsearch_search_node to solrsearch_index_entities_node
+  $select = db_select('solrsearch_search_node', 'asn');
+  $select->join('node', 'n', 'asn.nid = n.nid');
+  $select->addField('n', 'nid', 'entity_id');
+  $select->addField('n', 'type', 'bundle');
+  $select->addField('asn', 'status', 'status');
+  $select->addField('asn', 'changed', 'changed');
+  $select->addExpression("'node'", 'entity_type');
+  $return_value = db_insert('solrsearch_index_entities_node')
+    ->fields(array('entity_id', 'bundle', 'status', 'changed', 'entity_type'))
+    ->from($select)
+    ->execute();
+  // Drop the table solrsearch_search_node
+  db_drop_table('solrsearch_search_node');
+
+  $environments = solrsearch_load_all_environments();
+  foreach ($environments as $env_id => $environment) {
+    $excluded_types = solrsearch_environment_variable_get($env_id, 'solrsearch_search_excluded_types', array());
+    // Get indexable entity types
+    $options = array();
+    foreach (entity_get_info() as $entity_type => $entity_info) {
+      if ($entity_type == 'node') {
+        foreach ($entity_info['bundles'] as $key => $info) {
+          // See if it was excluded & only of entity node. We will not enable
+          // other entity types by default
+          if (empty($excluded_types[$key])) {
+            $options[$entity_type][$key] = $key;
+          }
+        }
+      }
+    }
+    // Set all except the excluded types
+    // @see solrsearch_index_set_bundles()
+    foreach ($options as $entity_type => $bundles) {
+      db_delete('solrsearch_index_bundles')
+        ->condition('env_id', $env_id)
+        ->condition('entity_type', $entity_type)
+        ->execute();
+
+      if ($bundles) {
+        $insert = db_insert('solrsearch_index_bundles')
+          ->fields(array('env_id', 'entity_type', 'bundle'));
+
+        foreach ($bundles as $bundle) {
+          $insert->values(array(
+            'env_id' => $env_id,
+            'entity_type' => $entity_type,
+            'bundle' => $bundle,
+          ));
+        }
+        $insert->execute();
+      }
+    }
+    // Remove the excluded types
+    solrsearch_environment_variable_del($env_id, 'solrsearch_search_excluded_types');
+  }
+}
+
+/**
+ * Make consistent (and reduce) field lengths which cause excess pkey length.
+ */
+function solrsearch_update_7013() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  db_drop_primary_key('solrsearch_index_entities');
+  db_change_field('solrsearch_index_entities', 'entity_type', 'entity_type', array(
+    'description' => 'The type of entity.',
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE)
+  );
+  db_add_primary_key('solrsearch_index_entities', array('entity_id', 'entity_type'));
+  db_change_field('solrsearch_index_entities_node', 'entity_type', 'entity_type', array(
+    'description' => 'The type of entity.',
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE)
+  );
+  db_drop_primary_key('solrsearch_index_bundles');
+  db_change_field('solrsearch_index_bundles', 'env_id', 'env_id', array(
+    'description' => 'Unique identifier for the environment',
+    'type' => 'varchar',
+    'length' => 64,
+    'not null' => TRUE)
+  );
+  db_change_field('solrsearch_index_bundles', 'entity_type', 'entity_type', array(
+    'description' => 'The type of entity.',
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE)
+  );
+  db_add_primary_key('solrsearch_index_bundles', array('env_id', 'entity_type', 'bundle'));
+}
+
+/**
+ * Remove status from the key.
+ */
+function solrsearch_update_7014() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $types = array(
+    'other' => 'solrsearch_index_entities',
+    'node' => 'solrsearch_index_entities_node',
+  );
+  foreach ($types as $type => $table) {
+    db_drop_index($table, 'changed');
+    db_add_index($table, 'bundle_changed', array('bundle', 'changed'));
+  }
+}
+
+
+/**
+ * Fix primary key schema mismatch for those who cleanly installed with beta16.
+ */
+function solrsearch_update_7015() {
+  if (variable_get('solrsearch_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // Brand new installations since update_7013 have the wrong primary key.
+  db_drop_primary_key('solrsearch_index_entities');
+  db_add_primary_key('solrsearch_index_entities', array('entity_id', 'entity_type'));
+}
+
+/**
+ * Clean up solrsearch_update_from_6303.
+ *
+ * This variable had been used to bypass 7.x-1.x updates which are redundant
+ * with 6.x-3.x.
+ */
+function solrsearch_update_7016() {
+  variable_del('solrsearch_update_from_6303');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.interface.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,544 @@
+<?php
+
+/**
+ * The interface for all 'query' objects.
+ */
+interface DrupalSolrQueryInterface {
+
+  /**
+   * Get query name.
+   */
+  function getName();
+
+  /**
+   * Get query searcher name (for facetapi, views, pages, etc).
+   */
+  function getSearcher();
+
+  /**
+   * Get context values.
+   */
+  function getContext();
+
+  /**
+   * Set context value.
+   */
+  function addContext(array $context);
+
+  /**
+   * Returns all filters matching $name, if set; otherwise, returns all filters.
+   *
+   * @param string $name
+   *   The facet field name to match. If NULL, all filters will be returned.
+   *
+   * @return array
+   *   All matching filters.
+   */
+  function getFilters($name = NULL);
+
+  /**
+   * Tests whether a filter is already present in the query.
+   *
+   * @param string $name
+   *   The facet field name to check.
+   * @param string $value
+   *   The facet value to check.
+   * @param boolean $exclude
+   *   Optional, defaults to FALSE, must match the filter.
+   *
+   * @return boolean
+   *   TRUE or FALSE.
+   */
+  function hasFilter($name, $value, $exclude = FALSE);
+
+  /**
+   * Adds a filter to the query.
+   *
+   * @param string $name
+   *   The facet field name.
+   * @param string $value
+   *   The facet field value.
+   * @param boolean $exclude
+   *   Set to TRUE to filter out documents matching $value.
+   * @param string $local
+   *   Solr LocalParams.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function addFilter($name, $value, $exclude = FALSE, $local = '');
+
+  /**
+   * Removes a filter from the query.
+   *
+   * @param string $name
+   *   The name of the facet field to remove.
+   * @param string $value
+   *   The value of the facet field to remove. If NULL, all filters matching
+   *   $name are removed.
+   * @param boolean $exclude
+   *   If $value is not NULL, only filters matching both $value and $exclude are
+   *   removed. Ignored if $value is NULL.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function removeFilter($name, $value = NULL, $exclude = FALSE);
+
+  /**
+   * Returns all subqueries to the query.
+   *
+   * @return array
+   *   All subqueries to the query.
+   */
+  function getFilterSubQueries();
+
+  /**
+   * Adds a subquery to the query.
+   *
+   * @param SolrFilterSubQuery $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.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function addFilterSubQuery(SolrFilterSubQuery $query);
+
+  /**
+   * Removes a specific subquery.
+   *
+   * @param DrupalSolrQueryInterface $query
+   *   The query to remove.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function removeFilterSubQuery(SolrFilterSubQuery $query);
+
+  /**
+   * Removes all subqueries.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function removeFilterSubQueries();
+
+  /**
+   * Transforms a single filter in a form suitable for use in a Solr query.
+   *
+   * @param array $filter
+   *   A filter as an array with the keys '#name', for the facet field name,
+   *   '#value', for the facet field value, '#local', for Solr LocalParams, and
+       '#exclude' set to TRUE if it is an exclusion filter.
+   *
+   * @return string
+   *   A Solr fq parameter value.
+   */
+  function makeFilterQuery(array $filter);
+
+  /**
+   * Gets the value of a parameter.
+   *
+   * @param string $name
+   *   The parameter name.
+   *
+   * @return
+   *   The value of the parameter.
+   */
+  function getParam($name);
+
+  /**
+   * Gets all parameters in normalized form.
+   *
+   * @return array
+   *   All parameters as key-value pairs.
+   */
+  function getParams();
+
+  /**
+   * Gets parameters in a form suitable for use in a Solr query.
+   *
+   * @return array
+   *   All parameters as key-value pairs, where values have been transformed
+   *   into Solr parameter values.
+   */
+  function getSolrParams();
+
+  /**
+   * Adds a param to be sent when running the Solr search.
+   *
+   * If the param is single-valued, this will replace rather than add the value.
+   *
+   * @param string $name
+   *   A Solr param name, e.g. 'q' or 'fl'.
+   * @param $value
+   *   A Solr param value: an array of values, or a string for a single value.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function addParam($name, $value);
+
+  /**
+   * Adds multiple params to be sent when running the Solr search.
+   *
+   * If the param is single-valued, this will replace rather than add the value.
+   *
+   * @param $params
+   *   An array where the keys are param names, and the values may be strings or
+   *   arrays of strings.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function addParams(array $params);
+
+  /**
+   * Removes all values for one Solr param.
+   *
+   * @param string $name
+   *   A Solr param name, e.g. 'q' or 'fl'.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function removeParam($name);
+
+  /**
+   * Replaces a param to be sent when running the Solr search.
+   *
+   * Basically a shortcut for removeParam() plus addParam().
+   *
+   * @param string $name
+   *   A Solr param name, e.g. 'q' or 'fl'.
+   * @param $value
+   *   A Solr param value: an array of values, or a string for a single value.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function replaceParam($name, $value);
+
+  /**
+   * Handles aliases for field to make nicer URLs.
+   *
+   * @param $field_map
+   *   An array keyed with real Solr index field names with the alias as value.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function addFieldAliases($field_map);
+
+  function getFieldAliases();
+
+  function clearFieldAliases();
+
+  function getAvailableSorts();
+
+  /**
+   * Adds an available sort.
+   *
+   * @param string $name
+   *  The name of the field in the Solr index to sort on.
+   * @param array $sort
+   *  An array with the keys 'title', for the human name of the sort, and
+   *  'default', for the default sort direction ('asc' or 'desc').
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function setAvailableSort($name, $sort);
+
+  /**
+   * Removes an available sort.
+   *
+   * @param string $name
+   *  The name of the field in the Solr index to sort on.
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function removeAvailableSort($name);
+
+  /**
+   * Gets the current sort.
+   *
+   * @return array
+   *   The current sort as an array with the keys '#name', for the name of
+   *   the field, and '#direction', for the sort direction ('asc' or 'desc').
+   */
+  function getSolrsort();
+
+  /**
+   * Sets the sort.
+   *
+   * @param string $field
+   *  The name of the field in the Solr index to sort on.
+   * @param string $direction
+   *  'asc' or 'desc'
+   *
+   * @return DrupalSolrQueryInterface
+   *   The called object.
+   */
+  function setSolrsort($name, $direction);
+
+  /**
+   * Returns an array representing the URL query string for the current sort.
+   *
+   * @return array
+   *   The URL query string for the current sort.
+   */
+  function getSolrsortUrlQuery();
+
+  /**
+   * Returns the search path (including the search keywords).
+   *
+   * @param string $new_keywords
+   *   Optional. When set, this string overrides the query's current keywords.
+   *
+   * @return string
+   *   The search path.
+   */
+  function getPath($new_keywords = NULL);
+
+  /**
+   * Sends the search request to Solr, unless $query->abort_search is TRUE.
+   *
+   * @param string $keys
+   *   The search keys.
+   *
+   * @return
+   *   A stdClass response object.
+   */
+  function search($keys = NULL);
+
+  /**
+   * Calls a method, without arguments, on the Solr object with which the query
+   * object was initialized.
+   *
+   * @param string $method
+   *   The method to call on the Solr object.
+   *
+   * @return
+   *   Any method return.
+   */
+  function solr($method);
+}
+
+/**
+ * The interface for all 'Service' objects.
+ */
+interface DrupalApacheSolrServiceInterface {
+  /**
+   * Call the /admin/ping servlet, to test the connection to the server.
+   *
+   * @param $timeout
+   *   maximum time to wait for ping in seconds, -1 for unlimited (default 2).
+   * @return
+   *   (float) seconds taken to ping the server, FALSE if timeout occurs.
+   */
+  function ping($timeout = 2);
+
+  /**
+   * Get information about the Solr Core.
+   *
+   * @return
+   *   (string) system info encoded in json
+   */
+  function getSystemInfo();
+
+  /**
+   * Get just the field meta-data about the index.
+   */
+  function getFields($num_terms = 0);
+
+  /**
+   * Get meta-data about the index.
+   */
+  function getLuke($num_terms = 0);
+
+  /**
+   * Get information about the Solr Core.
+   *
+   * Returns a Simple XMl document
+   */
+  function getStats();
+
+  /**
+   * Get summary information about the Solr Core.
+   */
+  function getStatsSummary();
+
+  /**
+   * Clear cached Solr data.
+   */
+  function clearCache();
+
+  /**
+   * Constructor
+   *
+   * @param $url
+   *   The URL to the Solr server, possibly including a core name.  E.g. http://localhost:8983/solr/
+   *   or https://search.example.com/solr/core99/
+   * @param $env_id
+   *   The machine name of a corresponding saved configuration used for loading
+   *   data like which facets are enabled.
+   */
+  function __construct($url, $env_id = NULL);
+
+  function getId();
+
+  /**
+   * Make a request to a servlet (a path) that's not a standard path.
+   *
+   * @param string $servlet
+   *   A path to be added to the base Solr path. e.g. 'extract/tika'
+   *
+   * @param array $params
+   *   Any request parameters when constructing the URL.
+   *
+   * @param array $options
+   *  @see drupal_http_request() $options.
+   *
+   * @return
+   *  response object
+   *
+   * @thows Exception
+   */
+  function makeServletRequest($servlet, $params = array(), $options = array());
+
+  /**
+   * Get the Solr url
+   *
+   * @return string
+   */
+  function getUrl();
+
+  /**
+   * Set the Solr url.
+   *
+   * @param $url
+   *
+   * @return $this
+   */
+  function setUrl($url);
+
+  /**
+   * Raw update Method. Takes a raw post body and sends it to the update service. Post body
+   * should be a complete and well formed xml document.
+   *
+   * @param string $rawPost
+   * @param float $timeout Maximum expected duration (in seconds)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function update($rawPost, $timeout = FALSE);
+
+  /**
+   * Add an array of Solr Documents to the index all at once
+   *
+   * @param array $documents Should be an array of ApacheSolrDocument instances
+   * @param boolean $allowDups
+   * @param boolean $overwritePending
+   * @param boolean $overwriteCommitted
+   *
+   * @return response objecte
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function addDocuments($documents, $overwrite = NULL, $commitWithin = NULL);
+
+  /**
+   * Send a commit command.  Will be synchronous unless both wait parameters are set to false.
+   *
+   * @param boolean $optimize Defaults to true
+   * @param boolean $waitFlush Defaults to true
+   * @param boolean $waitSearcher Defaults to true
+   * @param float $timeout Maximum expected duration (in seconds) of the commit operation on the server (otherwise, will throw a communication exception). Defaults to 1 hour
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function commit($optimize = TRUE, $waitFlush = TRUE, $waitSearcher = TRUE, $timeout = 3600);
+
+  /**
+   * Create a delete document based on document ID
+   *
+   * @param string $id Expected to be utf-8 encoded
+   * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function deleteById($id, $timeout = 3600);
+
+  /**
+   * Create and post a delete document based on multiple document IDs.
+   *
+   * @param array $ids Expected to be utf-8 encoded strings
+   * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function deleteByMultipleIds($ids, $timeout = 3600);
+
+  /**
+   * Create a delete document based on a query and submit it
+   *
+   * @param string $rawQuery Expected to be utf-8 encoded
+   * @param float $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
+   * @return stdClass response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function deleteByQuery($rawQuery, $timeout = 3600);
+
+  /**
+   * Send an optimize command.  Will be synchronous unless both wait parameters are set
+   * to false.
+   *
+   * @param boolean $waitFlush
+   * @param boolean $waitSearcher
+   * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function optimize($waitFlush = TRUE, $waitSearcher = TRUE, $timeout = 3600);
+
+  /**
+   * Simple Search interface
+   *
+   * @param string $query The raw query string
+   * @param array $params key / value pairs for other query parameters (see Solr documentation), use arrays for parameter keys used more than once (e.g. facet.field)
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function search($query = '', array $params = array(), $method = 'GET');
+
+  /**
+   * Get the current solr version. This could be 1, 3 or 4
+   *
+   * @return int
+   *   1, 3 or 4. Does not give a more details version, for that you need
+   *   to get the system info.
+   */
+  function getSolrVersion();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.module	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,2788 @@
+<?php
+
+/**
+ * @file
+ *   Integration with the Solr Search search application.
+ */
+
+define('solrsearch_READ_WRITE', 0);
+define('solrsearch_READ_ONLY', 1);
+define('solrsearch_API_VERSION', '3.0');
+
+/**
+ * Implements hook_init().
+ */
+function solrsearch_init() {
+  if (arg(0) == 'admin') {
+    // Add the CSS for this module
+    drupal_add_css(drupal_get_path('module', 'solrsearch') . '/solrsearch.css');
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function solrsearch_menu() {
+  //$items = array();
+
+  $items['solrsearch-terms-change'] =array(
+      'page callback' => 'solrsearch_term_select_field',
+      'access arguments'   => array('access content'),
+      'file' => 'solrsearch_terms.inc',
+  );
+
+  $items['solrsearch-terms'] =array(
+      'page callback' => 'solrsearch_term_list',
+      'access arguments'   => array('access content'),
+      'file' => 'solrsearch_terms.inc',
+  );
+  $items['solrsearch/node'] = array(
+      'title' => 'Search',
+      'page callback' => 'solrsearch_view',
+      'access arguments'   => array('access content'),
+      //'access callback' => 'solrsearch_is_active',
+      'type' => MENU_SUGGESTED_ITEM,
+      'file' => 'solrsearch.pages.inc',
+  );
+  $items['solrsearch/site'] = array(
+      'title' => 'Search',
+      'page callback' => 'solrsearch_view',
+      'access arguments'   => array('access content'),
+      //'access callback' => 'solrsearch_is_active',
+      'type' => MENU_SUGGESTED_ITEM,
+      'file' => 'solrsearch.pages.inc',
+  );
+  $items['solrsearch'] = array(
+      'title' => 'Search',
+      'page callback' => 'solrsearch_view',
+      'access arguments'   => array('access content'),
+      //'access callback' => 'solrsearch_is_active',
+      'type' => MENU_SUGGESTED_ITEM,
+      'file' => 'solrsearch.pages.inc',
+  );
+
+  $items['solrsearchsimple'] = array(
+      'title' => 'Simple Search',
+      'page callback' => 'solrsearch_view',
+      'access arguments'   => array('access content'),
+      //'access callback' => 'solrsearch_is_active',
+      'type' => MENU_SUGGESTED_ITEM,
+      'file' => 'solrsearch.pages.inc',
+  );
+
+
+  $items['solrsearchsave'] = array(
+      'title' => 'Search',
+      'page callback' => 'solrsearch_save',
+      'access arguments'   => array('access content'),
+      //'access callback' => 'solrsearch_is_active',
+      'type' => MENU_SUGGESTED_ITEM,
+      'file' => 'solrsearch.pages.inc',
+  );
+  $items['admin/config/search/solrsearch'] = array(
+    'title'              => 'Solr Search search',
+    'description'        => 'Administer Solr Search.',
+    'page callback'      => 'solrsearch_status_page',
+    'access arguments'   => array('administer search'),
+    'weight'             => -8,
+    'file'               => 'solrsearch.admin.inc',
+  );
+
+
+  $items['admin/config/search/solrsearch/settings'] = array(
+    'title'              => 'Settings',
+    'weight'             => 10,
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('solrsearch_settings'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'solrsearch.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+
+  $settings_path = 'admin/config/search/solrsearch/settings/';
+
+  $items[$settings_path . '%solrsearch_environment/edit'] = array(
+    'title'              => 'Edit',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('solrsearch_environment_edit_form', 5),
+    'description'        => 'Edit Solr Search search environment.',
+    'access arguments'   => array('administer search'),
+    'weight'             => 10,
+    'file'               => 'solrsearch.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items[$settings_path . '%solrsearch_environment/clone'] = array(
+    'title'              => 'Solr Search search environment clone',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('solrsearch_environment_clone_form', 5),
+    'access arguments'   => array('administer search'),
+    'file'               => 'solrsearch.admin.inc',
+  );
+  $items[$settings_path . '%solrsearch_environment/delete'] = array(
+    'title'              => 'Solr Search search environment delete',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('solrsearch_environment_delete_form', 5),
+    'access callback'    => 'solrsearch_environment_delete_page_access',
+    'access arguments'   => array('administer search', 5),
+    'file'               => 'solrsearch.admin.inc',
+  );
+  $items[$settings_path . 'add'] = array(
+    'title'              => 'Add search environment',
+    'description'        => 'Add Solr Search environment.',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('solrsearch_environment_edit_form'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'solrsearch.admin.inc',
+    'type'               => MENU_LOCAL_ACTION,
+  );
+
+
+  $reports_path = 'admin/reports/solrsearch';
+  $items[$reports_path] = array(
+    'title'              => 'Solr Search search index',
+    'description'        => 'Information about the contents of the index on the server',
+    'page callback'      => 'solrsearch_index_report',
+    'access arguments'   => array('access site reports'),
+    'file'               => 'solrsearch.admin.inc',
+  );
+  $items[$reports_path . '/%solrsearch_environment'] = array(
+    'title'              => 'Solr Search search index',
+    'description'        => 'Information about the contents of the index on the server',
+    'page callback'      => 'solrsearch_index_report',
+    'page arguments'     => array(3),
+    'access arguments'   => array('access site reports'),
+    'file'               => 'solrsearch.admin.inc',
+  );
+  $items[$reports_path . '/%solrsearch_environment/index'] = array(
+    'title'              => 'Search index',
+    'file'               => 'solrsearch.admin.inc',
+    'type'               => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items[$reports_path . '/%solrsearch_environment/conf'] = array(
+    'title'              => 'Configuration files',
+    'page callback'      => 'solrsearch_config_files_overview',
+    'access arguments'   => array('access site reports'),
+    'file'               => 'solrsearch.admin.inc',
+    'weight'             => 5,
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items[$reports_path . '/%solrsearch_environment/conf/%'] = array(
+    'title'              => 'Configuration file',
+    'page callback'      => 'solrsearch_config_file',
+    'page arguments'     => array(5, 3),
+    'access arguments'   => array('access site reports'),
+    'file'               => 'solrsearch.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  if (module_exists('devel')) {
+    $items['node/%node/devel/solrsearch'] = array(
+      'title'              => 'Solr Search',
+      'page callback'      => 'solrsearch_devel',
+      'page arguments'     => array(1),
+      'access arguments'   => array('access devel information'),
+      'file'               => 'solrsearch.admin.inc',
+      'type'               => MENU_LOCAL_TASK,
+    );
+  }
+
+  // We handle our own menu paths for facets
+  if (module_exists('facetapi')) {
+    $file_path = drupal_get_path('module', 'facetapi');
+    $first = TRUE;
+    foreach (facetapi_get_realm_info() as $realm_name => $realm) {
+      if ($first) {
+        $first = FALSE;
+        $items[$settings_path . '%solrsearch_environment/facets'] = array(
+          'title'            => 'Facets',
+          'page callback'    => 'solrsearch_enabled_facets_page',
+          'page arguments'   =>  array($realm_name, 5),
+          'weight'           => -5,
+          'access arguments' => array('administer search'),
+          'file path'        => $file_path,
+          'file'             => 'facetapi.admin.inc',
+          'type'             => MENU_LOCAL_TASK,
+          'context'          => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+        );
+      }
+      else {
+        $items[$settings_path . '%solrsearch_environment/facets/' . $realm_name] = array(
+          'title'            => $realm['label'],
+          'page callback'    => 'solrsearch_enabled_facets_page',
+          'page arguments'   => array($realm_name, 5),
+          'weight'           => -5,
+          'access arguments' => array('administer search'),
+          'type'             => MENU_LOCAL_TASK,
+          'context'          => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+          'file path'        => $file_path,
+          'file'             => 'facetapi.admin.inc',
+        );
+      }
+    }
+  }
+  return $items;
+}
+
+/**
+ * Wrapper for facetapi settings forms.
+ */
+function solrsearch_enabled_facets_page($realm_name, $environment = NULL) {
+  $page = array();
+
+  if (isset($environment['env_id'])) {
+    $env_id = $environment['env_id'];
+  }
+  else {
+    $env_id = solrsearch_default_environment();
+  }
+  $searcher = 'solrsearch@' . $env_id;
+
+  // Initializes output with information about which environment's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $page['solrsearch_environment'] = array(
+    '#theme' => 'solrsearch_settings_title',
+    '#env_id' => $env_id,
+  );
+
+  $page['settings'] = drupal_get_form('facetapi_realm_settings_form', $searcher, $realm_name);
+  return $page;
+}
+
+/**
+ * Implements hook_facetapi_searcher_info().
+ */
+function solrsearch_facetapi_searcher_info() {
+  $info = array();
+  // TODO: is it needed to return all of them here?
+  foreach (solrsearch_load_all_environments() as $id => $environment) {
+    $info['solrsearch@' . $id] = array(
+      'label' => t('Solr Search environment: @environment', array('@environment' => $environment['name'])),
+      'adapter' => 'solrsearch',
+      'instance' => $id,
+      'path' => '',
+      'supports facet mincount' => TRUE,
+      'supports facet missing' => TRUE,
+      'include default facets' => FALSE,
+    );
+  }
+  return $info;
+}
+
+/**
+ * Implements hook_facetapi_adapters().
+ */
+function solrsearch_facetapi_adapters() {
+  return array(
+    'solrsearch' => array(
+      'handler' => array(
+        'class' => 'solrsearchFacetapiAdapter',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_facetapi_query_types().
+ */
+function solrsearch_facetapi_query_types() {
+  return array(
+    'solrsearch_term' => array(
+      'handler' => array(
+        'class' => 'solrsearchFacetapiTerm',
+        'adapter' => 'solrsearch',
+      ),
+    ),
+    'solrsearch_date' => array(
+      'handler' => array(
+        'class' => 'solrsearchFacetapiDate',
+        'adapter' => 'solrsearch',
+      ),
+    ),
+    'solrsearch_numeric_range' => array(
+      'handler' => array(
+        'class' => 'solrsearchFacetapiNumericRange',
+        'adapter' => 'solrsearch',
+      ),
+    ),
+    'solrsearch_integer' => array(
+          'handler' => array(
+              'class' => 'solrsearchFacetapiInteger',
+              'adapter' => 'solrsearch',
+          ),
+      ),
+    'solrsearch_geo' => array(
+      'handler' => array(
+        'class' => 'solrsearchFacetapiGeo',
+        'adapter' => 'solrsearch',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_facetapi_facet_info().
+ * Currently it only supports the node entity type
+ */
+function solrsearch_facetapi_facet_info($searcher_info) {
+  $facets = array();
+  //dpm("sorlsearch_facetapi");
+  if ('solrsearch' == $searcher_info['adapter']) {
+    $environment = solrsearch_environment_load($searcher_info['instance']);
+
+    if (!empty($environment['conf']['facet callbacks'])) {
+      foreach ($environment['conf']['facet callbacks'] as $callback) {
+        if (is_callable($callback)) {
+          $facets = array_merge($facets, call_user_func($callback, $searcher_info));
+        }
+      }
+    }
+    elseif (isset($searcher_info['types']['node'])) {
+      $facets = solrsearch_default_node_facet_info();
+    }
+  }
+
+  return $facets;
+}
+
+/**
+ * Returns an array of facets for node fields and attributes.
+ *
+ * @return
+ *   An array of node facets.
+ */
+function solrsearch_default_node_facet_info() {
+  //dpm("sorlsearch_default_facetapi");
+  return array_merge(solrsearch_common_node_facets(), solrsearch_entity_field_facets('node'));
+}
+
+/**
+ * Returns an array of facets for the provided entity type's fields.
+ *
+ * @param string $entity_type
+ *   An entity type machine name.
+ * @return
+ *   An array of facets for the fields of the requested entity type.
+ */
+function solrsearch_entity_field_facets($entity_type) {
+  $facets = array();
+
+  foreach (solrsearch_entity_fields($entity_type) as $field_nm => $entity_fields) {
+    foreach ($entity_fields as $field_info) {
+      if (!empty($field_info['facets'])) {
+        $field = solrsearch_index_key($field_info);
+        $facets[$field] = array(
+          'label' => check_plain($field_info['display_name']),
+          'dependency plugins' => $field_info['dependency plugins'],
+          'field api name' => $field_info['field']['field_name'],
+          'description' => t('Filter by field of type @type.', array('@type' => $field_info['field']['type'])),
+          'map callback' => $field_info['map callback'],
+          'map options' => $field_info,
+          'hierarchy callback' => $field_info['hierarchy callback'],
+        );
+        if (!empty($field_info['facet mincount allowed'])) {
+          $facets[$field]['facet mincount allowed'] = $field_info['facet mincount allowed'];
+        }
+        if (!empty($field_info['facet missing allowed'])) {
+          $facets[$field]['facet missing allowed'] = $field_info['facet missing allowed'];
+        }
+        if (!empty($field_info['query types'])) {
+          $facets[$field]['query types'] = $field_info['query types'];
+        }
+        if (!empty($field_info['allowed operators'])) {
+          $facets[$field]['allowed operators'] = $field_info['allowed operators'];
+        }
+        // TODO : This is actually deprecated but we should still support
+        // older versions of facetapi. We should remove once facetapi has RC1
+        // For reference : http://drupal.org/node/1161444
+        if (!empty($field_info['query type'])) {
+          $facets[$field]['query type'] = $field_info['query type'];
+        }
+        if (!empty($field_info['min callback'])) {
+          $facets[$field]['min callback'] = $field_info['min callback'];
+        }
+        if (!empty($field_info['max callback'])) {
+          $facets[$field]['max callback'] = $field_info['max callback'];
+        }
+        if (!empty($field_info['map callback'])) {
+          $facets[$field]['map callback'] = $field_info['map callback'];
+        }
+        if (!empty($field_info['alter callbacks'])) {
+          $facets[$field]['alter callbacks'] = $field_info['alter callbacks'];
+        }
+      }
+    }
+  }
+
+  return $facets;
+}
+
+/**
+ * Helper function returning common facet definitions.
+ */
+function solrsearch_common_node_facets() {
+
+
+
+   $facets['author'] = array(
+    'label' => t('Author'),
+    'description' => t('Filter by author.'),
+    'field' => 'author_c',
+    'facet mincount allowed' => TRUE,
+
+  );
+
+
+  $facets['title'] = array(
+      'label' => t('Title'),
+      'description' => t('Filter by title.'),
+      'field' => 'title_s',
+      'facet mincount allowed' => TRUE,
+  );
+
+
+  $facets['doc-type'] = array(
+      'label' => t('Type of item'),
+      'description' => t('Filter by the type of item (field: doc-type).'),
+      'field' => 'doc-type',
+      'facet mincount allowed' => TRUE,
+      'map callback' => 'solrsearch_map_doc_type',
+  );
+
+  $facets['access-type'] = array(
+      'label' => t('Access restrictions'),
+      'description' => t('Filter by Access Type.'),
+      'field' => 'access-type',
+      'facet mincount allowed' => TRUE,
+  );
+
+
+  $facets['collection'] = array(
+      'label' => t('Filter by collection'),
+      'description' => t('Filter by Collection (field:collection).'),
+      'field' => 'collection',
+      'facet mincount allowed' => TRUE,
+  );
+
+ $facets['provider'] = array(
+      'label' => t('Filter by provider'),
+      'description' => t('Filter by Provider.'),
+      'field' => 'provider',
+      'facet mincount allowed' => TRUE,
+  );
+
+ $facets['data_provider'] = array(
+      'label' => t('Filter by data provider'),
+      'description' => t('Filter by Data provider .'),
+      'field' => 'data_provider',
+      'facet mincount allowed' => TRUE,
+  );
+
+
+ $facets['type'] = array(
+      'label' => t('Filter bytype'),
+      'description' => t('Filter by type .'),
+      'field' => 'type',
+      'facet mincount allowed' => TRUE,
+  );
+
+
+  $facets['year'] = array(
+    'label' => t('Year'),
+    'description' => t('Filter by the year.'),
+    'field' => 'year',
+    'query types' => array('integer'),
+    'allowed operators' => array(FACETAPI_OPERATOR_AND => TRUE),
+      'min callback' => 'solrsearch_get_min_integer',
+      'max callback' => 'solrsearch_get_max_integer',
+      'gap callback' => 'solrsearch_get_gap_integer',
+      'default sorts' => array(
+      array('active', SORT_DESC),
+      array('indexed', SORT_ASC),
+    ),
+  );
+
+
+  //dpm($facets);
+  return $facets;
+}
+
+
+/*
+ * solrsearch_map_doc_type
+ * map doc type to human readable
+ */
+
+function solrsearch_map_doc_type($facets, $options) {
+  $map = array();
+  $allowed_values = array();
+  // @see list_field_formatter_view()
+
+  $mapping = array(
+      'institutesLibrary' => 'Printed Catalog',
+      'indexMeta' => 'Digital Library',
+      'echo2_collection' => 'Collections (ECHO)',
+  );
+  foreach ($facets as $key) {
+
+    if (isset( $mapping[$key])) {
+      $map[$key]['#markup'] = $mapping[$key];
+    } else {
+      $map[$key]['#markup'] = $key;
+    }
+
+    $map[$key]['#html'] = TRUE;
+  }
+  return $map;
+}
+/**
+ * Callback that returns the gap
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *
+ *
+ * @todo Cache this value.
+ */
+function solrsearch_get_gap_integer(array $facet) {
+  //TODO: should be dynamic or configurable
+  return 20;
+}
+
+/**
+ * Callback that returns the minimum integer
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *
+ *
+ * @todo Cache this value.
+ */
+function solrsearch_get_min_integer(array $facet) {
+  //TODO: should be dynamic or configurable
+  return 1;
+}
+
+/**
+ * Callback that returns the maximum integer
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *
+ *
+ * @todo Cache this value.
+ */
+function solrsearch_get_max_integer(array $facet) {
+  //TODO: should be dynamic or configurable
+  return 2020;
+}
+
+
+/**
+ * Determines Solr Search'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 solrsearch_failure($search_name, $querystring) {
+  $fail_rule = variable_get('solrsearch_failure', 'solrsearch:show_error');
+
+  switch ($fail_rule) {
+    case 'solrsearch:show_error':
+      // drupal_set_message(t('Search is temporarily unavailable. If the problem persists, please contact the site administrator.'), 'error');
+      break;
+    case 'solrsearch:show_no_results':
+      // Do nothing.
+      break;
+    default:
+      // If we're failing over to another module make sure the search is available.
+      if (module_exists('search')) {
+        $search_info = search_get_info();
+        if (isset($search_info[$fail_rule])) {
+          $search_info = $search_info[$fail_rule];
+          drupal_set_message(t("%search_name is not available. Your search is being redirected.", array('%search_name' => $search_name)));
+          drupal_goto('search/' . $search_info['path'] . '/' . rawurlencode($querystring));
+        }
+      }
+      // if search is not enabled, break and do nothing
+      break;
+  }
+}
+
+/**
+ * Like $site_key in _update_refresh() - returns a site-specific hash.
+ */
+function solrsearch_site_hash() {
+  if (!($hash = variable_get('solrsearch_site_hash', FALSE))) {
+    global $base_url;
+    // Set a random 6 digit base-36 number as the hash.
+    $hash = substr(base_convert(sha1(uniqid($base_url, TRUE)), 16, 36), 0, 6);
+    variable_set('solrsearch_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 solrsearch_document_id($id, $entity_type = 'node') {
+  return solrsearch_site_hash() . "/{$entity_type}/" . $id;
+}
+
+/**
+ * Mark one entity as needing re-indexing.
+ */
+function solrsearch_mark_entity($entity_type, $entity_id) {
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  $table = solrsearch_get_indexer_table($entity_type);
+  if (!empty($table)) {
+    db_update($table)
+      ->condition('entity_id', $entity_id)
+      ->fields(array('changed' => REQUEST_TIME))
+      ->execute();
+  }
+}
+
+/**
+ * Implements hook_user_update().
+ *
+ * Mark nodes as needing re-indexing if the author name changes.
+ *
+ * @see http://drupal.org/node/592522
+ *   Performance issue with Mysql
+ * @see http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_update/7#comment-15459
+ *   To know why PDO in drupal does not support UPDATE and JOIN at once.
+ */
+function solrsearch_user_update(&$edit, $account, $category) {
+  if (isset($account->name) && isset($account->original) && isset($account->original->name) && $account->name != $account->original->name) {
+    $table = solrsearch_get_indexer_table('node');
+    switch (db_driver()) {
+      case 'mysql' :
+        $table = db_escape_table($table);
+        $query = "UPDATE {{$table}} asn
+          INNER JOIN {node} n ON asn.entity_id = n.nid SET asn.changed = :changed
+          WHERE n.uid = :uid";
+        $result = db_query($query, array(':changed' => REQUEST_TIME,
+          ':uid' => $account->uid,
+        ));
+        break;
+      default :
+        $nids = db_select('node')
+          ->fields('node', array('nid'))
+          ->where("uid = :uid", array(':uid' => $account->uid));
+        $update = db_update($table)
+          ->condition('entity_id', $nids, 'IN')
+          ->fields(array('changed' => REQUEST_TIME))
+          ->execute();
+    }
+  }
+}
+
+/**
+ * Implements hook_term_update().
+ *
+ * Mark nodes as needing re-indexing if a term name changes.
+ *
+ * @see http://drupal.org/node/592522
+ *   Performance issue with Mysql
+ * @see http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_update/7#comment-15459
+ *   To know why PDO in drupal does not support UPDATE and JOIN at once.
+ * @todo the rest, such as term deletion.
+ */
+function solrsearch_taxonomy_term_update($term) {
+  $table = solrsearch_get_indexer_table('node');
+  switch (db_driver()) {
+    case 'mysql' :
+      $table = db_escape_table($table);
+      $query = "UPDATE {{$table}} asn
+        INNER JOIN {taxonomy_index} ti ON asn.entity_id = ti.nid SET asn.changed = :changed
+        WHERE ti.tid = :tid";
+      $result = db_query($query, array(':changed' => REQUEST_TIME,
+        ':tid' => $term->tid,
+      ));
+      break;
+    default :
+      $nids = db_select('taxonomy_index')
+        ->fields('taxonomy_index', array('nid'))
+        ->where("tid = :tid", array(':tid' => $term->tid));
+      $update = db_update($table)
+        ->condition('entity_id', $nids, 'IN')
+        ->fields(array('changed' => REQUEST_TIME))
+        ->execute();
+  }
+}
+
+
+
+/**
+ * Convert date from timestamp into ISO 8601 format.
+ * http://lucene.apache.org/solr/api/org/apache/solr/schema/DateField.html
+ */
+function solrsearch_date_iso($date_timestamp) {
+  return gmdate('Y-m-d\TH:i:s\Z', $date_timestamp);
+}
+
+/**
+ * Function to flatten documents array recursively.
+ *
+ * @param array $documents
+ *   The array of documents being indexed.
+ * @param array &$tmp
+ *   A container variable that will contain the flattened array.
+ */
+function solrsearch_flatten_documents_array($documents, &$tmp) {
+  foreach ($documents AS $index => $item) {
+    if (is_array($item)) {
+      solrsearch_flatten_documents_array($item, $tmp);
+    }
+    elseif (is_object($item)) {
+      $tmp[] = $item;
+    }
+  }
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function solrsearch_flush_caches() {
+  return array('cache_solrsearch');
+}
+
+/**
+ * A wrapper for cache_clear_all to be used as a submit handler on forms that
+ * require clearing Luke cache etc.
+ */
+function solrsearch_clear_cache($env_id) {
+  // Reset $env_id to NULL if call originates from a form submit handler.
+  if (is_array($env_id)) {
+    $env_id = NULL;
+  }
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    $solr->clearCache();
+  }
+  catch (Exception $e) {
+    watchdog('Solr Search', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), 'warning');
+  }
+}
+
+/**
+ * Call drupal_set_message() with the text.
+ *
+ * The text is translated with t() and substituted using Solr stats.
+ * @todo This is not according to drupal code standards
+ */
+function solrsearch_set_stats_message($text, $type = 'status', $repeat = FALSE) {
+  try {
+    $solr = solrsearch_get_solr();
+    $stats_summary = $solr->getStatsSummary();
+    drupal_set_message(check_plain(t($text, $stats_summary)), $type, FALSE);
+  }
+  catch (Exception $e) {
+    watchdog('Solr Search', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Returns last changed and last ID for an environment and entity type.
+ */
+function solrsearch_get_last_index_position($env_id, $entity_type) {
+  $stored = variable_get('solrsearch_index_last', array());
+  return isset($stored[$env_id][$entity_type]) ? $stored[$env_id][$entity_type] : array('last_changed' => 0, 'last_entity_id' => 0);
+}
+
+/**
+ * Sets last changed and last ID for an environment and entity type.
+ */
+function solrsearch_set_last_index_position($env_id, $entity_type, $last_changed, $last_entity_id) {
+  $stored = variable_get('solrsearch_index_last', array());
+  $stored[$env_id][$entity_type] = array('last_changed' => $last_changed, 'last_entity_id' => $last_entity_id);
+  variable_set('solrsearch_index_last', $stored);
+}
+
+/**
+ * Clear a specific environment, or clear all.
+ */
+function solrsearch_clear_last_index_position($env_id = NULL, $entity_type = NULL) {
+  $stored = variable_get('solrsearch_index_last', array());
+  if (empty($env_id)) {
+    $stored = array();
+  }
+  elseif ($entity_type) {
+    unset($stored[$env_id][$entity_type]);
+  }
+  else {
+    unset($stored[$env_id]);
+  }
+  variable_set('solrsearch_index_last', $stored);
+}
+
+/**
+ * Set the timestamp of the last index update
+ * @param $timestamp
+ *   A timestamp or zero. If zero, the variable is deleted.
+ */
+function solrsearch_set_last_index_updated($env_id, $timestamp = 0) {
+  $updated = variable_get('solrsearch_index_updated', array());
+  if ($timestamp > 0) {
+    $updated[$env_id] = $timestamp;
+  }
+  else {
+    unset($updated[$env_id]);
+  }
+  variable_set('solrsearch_index_updated', $updated);
+}
+
+/**
+ * Get the timestamp of the last index update.
+ * @return integer (timestamp)
+ */
+function solrsearch_get_last_index_updated($env_id) {
+  $updated = variable_get('solrsearch_index_updated', array());
+  return isset($updated[$env_id]) ? $updated[$env_id] : 0;
+}
+
+
+
+/**
+ * Implements hook_form_[form_id]_alter().
+ *
+ * Make sure to flush cache when content types are changed.
+ */
+function solrsearch_form_node_type_form_alter(&$form, $form_state) {
+  $form['#submit'][] = 'solrsearch_clear_cache';
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ *
+ * Make sure to flush cache when fields are added.
+ */
+function solrsearch_form_field_ui_field_overview_form_alter(&$form, $form_state) {
+  $form['#submit'][] = 'solrsearch_clear_cache';
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ *
+ * Make sure to flush cache when fields are updated.
+ */
+function solrsearch_form_field_ui_field_edit_form_alter(&$form, $form_state) {
+  $form['#submit'][] = 'solrsearch_clear_cache';
+}
+
+/**
+ * Sets breadcrumb trails for Facet API settings forms.
+ *
+ * @param FacetapiAdapter $adapter
+ *   The Facet API adapter object.
+ * @param array $realm
+ *   The realm definition.
+ */
+function solrsearch_set_facetapi_breadcrumb(FacetapiAdapter $adapter, array $realm) {
+  if ('solrsearch' == $adapter->getId()) {
+    // Hack here that depnds on our construction of the searcher name in this way.
+    list(, $env_id) = explode('@', $adapter->getSearcher());
+    // Appends additional breadcrumb items.
+    $breadcrumb = drupal_get_breadcrumb();
+    $breadcrumb[] = l(t('Solr Search search environment edit'), 'admin/config/search/solrsearch/settings/' . $env_id);
+    $breadcrumb[] = l($realm['label'], 'admin/config/search/solrsearch/settings/' . $env_id . '/facets/' . $realm['name']);
+    drupal_set_breadcrumb($breadcrumb);
+  }
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ */
+function solrsearch_form_facetapi_facet_settings_form_alter(&$form, $form_state) {
+  solrsearch_set_facetapi_breadcrumb($form['#facetapi']['adapter'], $form['#facetapi']['realm']);
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ */
+function solrsearch_form_facetapi_facet_dependencies_form_alter(&$form, $form_state) {
+  solrsearch_set_facetapi_breadcrumb($form['#facetapi']['adapter'], $form['#facetapi']['realm']);
+}
+
+/**
+ * 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 solrsearch_has_searched($env_id, $searched = NULL) {
+  $_searched  = &drupal_static(__FUNCTION__, FALSE);
+  if (is_bool($searched)) {
+    $_searched[$env_id] = $searched;
+  }
+  // Return false if the search environment is not available in our array
+  if (!isset($_searched[$env_id])) {
+    return FALSE;
+  }
+  return $_searched[$env_id];
+}
+
+/**
+ * Semaphore that indicates whether Blocks should be suppressed regardless
+ * of whether a search has run.
+ *
+ * @param $suppress
+ *   A boolean indicating whether to suppress.
+ *
+ * @return
+ *   TRUE if a search has been executed.
+ *   FALSE otherwise.
+ */
+function solrsearch_suppress_blocks($env_id, $suppress = NULL) {
+  $_suppress = &drupal_static(__FUNCTION__, FALSE);
+  if (is_bool($suppress)) {
+    $_suppress[$env_id] = $suppress;
+  }
+  // Return false if the search environment is not available in our array
+  if (!isset($_suppress[$env_id])) {
+    return FALSE;
+  }
+  return $_suppress[$env_id];
+}
+
+/**
+ * Get or set the default environment ID for the current page.
+ */
+function solrsearch_default_environment($env_id = NULL) {
+  $default_env_id = &drupal_static(__FUNCTION__, NULL);
+
+  if (isset($env_id)) {
+    $default_env_id = $env_id;
+  }
+  if (empty($default_env_id)) {
+    $default_env_id = variable_get('solrsearch_default_environment', 'echosearch');
+  }
+  return $default_env_id;
+}
+
+/**
+ * Set the default environment and let other modules know about the change.
+ */
+function solrsearch_set_default_environment($env_id) {
+  $old_env_id = variable_get('solrsearch_default_environment', 'solr');
+  variable_set('solrsearch_default_environment', $env_id);
+  module_invoke_all('solrsearch_default_environment', $env_id, $old_env_id);
+}
+
+/**
+ * Factory method for solr singleton objects. Structure allows for an arbitrary
+ * number of solr objects to be used based on a name whie maps to
+ * the host, port, path combination.
+ * Get an instance like this:
+ *   try {
+ *     $solr = solrsearch_get_solr();
+ *   }
+ *   catch (Exception $e) {
+ *     watchdog('Solr Search', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+ *   }
+ *
+ *
+ * @param string $env_id
+ *
+ * @return DrupalApacheSolrServiceInterface $solr
+ *
+ * @throws Exception
+ */
+function solrsearch_get_solr($env_id = NULL) {
+  $solr_cache = &drupal_static(__FUNCTION__);
+  $environments = solrsearch_load_all_environments();
+
+  if (!interface_exists('DrupalApacheSolrServiceInterface')) {
+    require_once(dirname(__FILE__) . '/solrsearch.interface.inc');
+  }
+
+  if (empty($env_id)) {
+    $env_id = solrsearch_default_environment();
+  }
+  elseif (empty($environments[$env_id])) {
+    throw new Exception(t('Invalid Solr Search environment: @env_id.', array('@env_id' => $env_id)));
+  }
+
+  if (isset($environments[$env_id])) {
+    $class = $environments[$env_id]['service_class'];
+
+    if (empty($solr_cache[$env_id])) {
+      // Use the default class if none is specified.
+      if (empty($class)) {
+        $class = variable_get('solrsearch_service_class', 'DrupalsolrsearchService');
+      }
+      // Takes advantage of auto-loading.
+      $solr = new $class($environments[$env_id]['url'], $env_id);
+      $solr_cache[$env_id] = $solr;
+    }
+    return $solr_cache[$env_id];
+  }
+  else {
+    throw new Exception('No default Solr Search environment.');
+  }
+}
+
+/**
+ * Function that loads all the environments
+ *
+ * @return $environments
+ *   The environments in the database
+ */
+function solrsearch_load_all_environments() {
+  $environments = &drupal_static(__FUNCTION__);
+
+  if (isset($environments)) {
+    return $environments;
+  }
+  // Use cache_get to avoid DB when using memcache, etc.
+  $cache = cache_get('solrsearch:environments', 'cache_solrsearch');
+  if (isset($cache->data)) {
+    $environments = $cache->data;
+  }
+  elseif (!db_table_exists('solrsearch_index_bundles') || !db_table_exists('solrsearch_environment')) {
+    // Sometimes this function is called when the 'solrsearch_index_bundles' is
+    // not created yet.
+    $environments = array();
+  }
+  else {
+    // If ctools is available use its crud functions to load the environments.
+    if (module_exists('ctools')) {
+      ctools_include('export');
+      $environments = ctools_export_load_object('solrsearch_environment', 'all');
+      // Convert environments to array.
+      foreach ($environments as &$environment) {
+        $environment = (array) $environment;
+      }
+    }
+    else {
+      $environments = db_query('SELECT * FROM {solrsearch_environment}')->fetchAllAssoc('env_id', PDO::FETCH_ASSOC);
+    }
+
+    // Load conf and index bundles. We don't use 'subrecords callback' property
+    // of ctools export API.
+    solrsearch_environment_load_subrecords($environments);
+
+    cache_set('solrsearch:environments', $environments, 'cache_solrsearch');
+  }
+
+  // Allow overrides of environments from settings.php
+  $conf_environments = variable_get('solrsearch_environments', array());
+  if (!empty($conf_environments)) {
+    $environments = drupal_array_merge_deep($environments, $conf_environments);
+  }
+
+  return $environments;
+}
+
+/**
+ * Function that loads an environment
+ *
+ * @param $env_id
+ *   The environment ID it needs to load.
+ *
+ * @return $environment
+ *   The environment that was requested or FALSE if non-existent
+ */
+function solrsearch_environment_load($env_id) {
+  $environments = solrsearch_load_all_environments();
+  return isset($environments[$env_id]) ? $environments[$env_id] : FALSE;
+}
+
+/**
+ * Access callback for the delete page of an environment.
+ *
+ * @param $permission
+ *   The permission that you allow access to
+ * @param $environment
+ *   The environment you want to delete. Core environment cannot be deleted
+ */
+function solrsearch_environment_delete_page_access($permission, $environment) {
+  $is_default = $environment['env_id'] == solrsearch_default_environment();
+  if ($is_default && !user_access($permission)) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Function that deletes an environment
+ *
+ * @param $env_id
+ *   The environment ID it needs to delete.
+ *
+ */
+function solrsearch_environment_delete($env_id) {
+  $environment = solrsearch_environment_load($env_id);
+  if ($environment) {
+    db_delete('solrsearch_environment')
+      ->condition('env_id', $env_id)
+      ->execute();
+    db_delete('solrsearch_environment_variable')
+      ->condition('env_id', $env_id)
+      ->execute();
+    db_delete('solrsearch_index_bundles')
+      ->condition('env_id', $env_id)
+      ->execute();
+
+    module_invoke_all('solrsearch_environment_delete', $environment);
+    solrsearch_environments_clear_cache();
+  }
+}
+
+/**
+ * Function that clones an environment
+ *
+ * @param $env_id
+ *   The environment ID it needs to clone.
+ *
+ */
+function solrsearch_environment_clone($env_id) {
+  $environment = solrsearch_environment_load($env_id);
+  $environments = solrsearch_load_all_environments();
+  $environment['env_id'] = solrsearch_create_unique_id($environments, $env_id);
+  $environment['name'] = $environment['name'] . ' [cloned]';
+  solrsearch_environment_save($environment);
+}
+
+/**
+ * Generator for an unique ID of an environment
+ *
+ * @param $environments
+ *   The environments that are available
+ * @param $original_environment
+ *   The environment it needs to replicate an ID for.
+ *
+ * @return
+ *   The new environment ID
+ */
+function solrsearch_create_unique_id($existing, $id) {
+  $count = 0;
+  $cloned_env_int = 0;
+  do {
+    $new_id = $id . '_' . $count;
+    $count++;
+  } while (isset($existing[$new_id]));
+  return $new_id;
+}
+
+/**
+ * Function that saves an environment
+ *
+ * @param $environment
+ *   The environment it needs to save.
+ *
+ */
+function solrsearch_environment_save($environment) {
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  $default = array('env_id' => '', 'name' => '', 'url' => '', 'service_class' => '');
+
+  $conf = isset($environment['conf']) ? $environment['conf'] : array();
+  $index_bundles = isset($environment['index_bundles']) ? $environment['index_bundles'] : array();
+  // Remove any unexpected fields.
+  // @todo - get this from the schema?.
+  $environment = array_intersect_key($environment, $default);
+  db_merge('solrsearch_environment')
+    ->key(array('env_id' => $environment['env_id']))
+    ->fields($environment)
+    ->execute();
+  // Update the environment variables (if any).
+  foreach ($conf as $name => $value) {
+    db_merge('solrsearch_environment_variable')
+      ->key(array('env_id' => $environment['env_id'], 'name' => $name))
+      ->fields(array('value' => serialize($value)))
+      ->execute();
+  }
+  // Update the index bundles (if any).
+  foreach ($index_bundles as $entity_type => $bundles) {
+    solrsearch_index_set_bundles($environment['env_id'], $entity_type, $bundles);
+  }
+  solrsearch_environments_clear_cache();
+}
+
+/**
+ * Clear all caches for environments.
+ */
+function solrsearch_environments_clear_cache() {
+  cache_clear_all('solrsearch:environments', 'cache_solrsearch');
+  drupal_static_reset('solrsearch_load_all_environments');
+  drupal_static_reset('solrsearch_get_solr');
+  if (module_exists('ctools')) {
+    ctools_include('export');
+    ctools_export_load_object_reset('solrsearch_environment');
+  }
+}
+
+/**
+ * Get a named variable, or return the default.
+ *
+ * @see variable_get()
+ */
+function solrsearch_environment_variable_get($env_id, $name, $default = NULL) {
+  $environment = solrsearch_environment_load($env_id);
+  if (isset($environment['conf'][$name])) {
+    return $environment['conf'][$name];
+  }
+  return $default;
+}
+
+/**
+ * Set a named variable, or return the default.
+ *
+ * @see variable_set()
+ */
+function solrsearch_environment_variable_set($env_id, $name, $value) {
+  db_merge('solrsearch_environment_variable')
+    ->key(array('env_id' => $env_id, 'name' => $name))
+    ->fields(array('value' => serialize($value)))
+    ->execute();
+  solrsearch_environments_clear_cache();
+}
+
+/**
+ * Get a named variable, or return the default.
+ *
+ * @see variable_del()
+ */
+function solrsearch_environment_variable_del($env_id, $name) {
+  db_delete('solrsearch_environment_variable')
+    ->condition('env_id', $env_id)
+    ->condition('name', $name)
+    ->execute();
+  solrsearch_environments_clear_cache();
+}
+
+/**
+ * Checks if a specific Solr Search server is available.
+ *
+ * @return boolean TRUE if the server can be pinged, FALSE otherwise.
+ */
+function solrsearch_server_status($url, $class = NULL) {
+
+  $status = &drupal_static(__FUNCTION__, array());
+
+  if (!interface_exists('DrupalApacheSolrServiceInterface')) {
+    require_once(dirname(__FILE__) . '/solrsearch.interface.inc');
+  }
+
+  if (empty($class)) {
+    $class = variable_get('solrsearch_service_class', 'DrupalsolrsearchService');
+  }
+
+  $key = $url . '|' . $class;
+  // Static store insures we don't ping the server more than once per page load.
+  if (!isset($status[$key])) {
+    $ping = FALSE;
+    try {
+      // Takes advantage of auto-loading.
+      // @Todo : Do we have to specify the env_id?
+      $solr = new $class($url);
+      $ping = @$solr->ping(variable_get('solrsearch_ping_timeout', 4));
+    }
+    catch (Exception $e) {
+      watchdog('Solr Search', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    }
+    $status[$key] = $ping;
+  }
+  return $status[$key];
+}
+
+/**
+ * Execute a keyword search based on a query object.
+ *
+ * Normally this function is used with the default (dismax) handler for keyword
+ * searches. The $final_query that's returned will have been modified by
+ * both hook_solrsearch_query_prepare() and hook_solrsearch_query_alter().
+ *
+ * @param $current_query
+ *   A query object from solrsearch_drupal_query().  It will be modified by
+ *   hook_solrsearch_query_prepare() and then cached in solrsearch_current_query().
+ * @param $page
+ *   For paging into results, using $current_query->params['rows'] results per page.
+ *
+ * @return array($final_query, $response)
+ *
+ * @throws Exception
+ */
+function solrsearch_do_query(DrupalSolrQueryInterface $current_query) {
+
+  if (!is_object($current_query)) {
+    throw new Exception(t('NULL query object in function solrsearch_do_query()'));
+  }
+
+  // Allow modules to alter the query prior to statically caching it.
+  // This can e.g. be used to add available sorts.
+  $searcher = $current_query->getSearcher();
+
+  if (module_exists('facetapi')) {
+
+    // Gets enabled facets, adds filter queries to $params.
+
+    $adapter = facetapi_adapter_load($searcher);
+    if ($adapter) {
+
+      // Realm could be added but we want all the facets
+      $adapter->addActiveFilters($current_query);
+    }
+  }
+
+  foreach (module_implements('solrsearch_query_prepare') as $module) {
+    $function_name = $module . '_solrsearch_query_prepare';
+    $function_name($current_query);
+  }
+
+  // Cache the original query. Since all the built queries go through
+  // this process, all the hook_invocations will happen later
+  $env_id = $current_query->solr('getId');
+
+  // Add our defType setting here. Normally this would be dismax or the setting
+  // from the solrconfig.xml. This allows the setting to be overridden.
+  $defType = solrsearch_environment_variable_get($env_id, 'solrsearch_query_type');
+  if (!empty($defType)) {
+    $current_query->addParam('defType', $defType);
+  }
+
+  $query = solrsearch_current_query($env_id, $current_query);
+
+  // Verify if this query was already executed in the same page load
+  if ($response = solrsearch_static_response_cache($searcher)) {
+    // Return cached query object
+
+    return array($query, $response);
+  }
+
+  $query->addParam('start', $query->page * $query->getParam('rows'));
+
+  // This hook allows modules to modify the query and params objects.
+  drupal_alter('solrsearch_query', $query);
+
+  if ($query->abort_search) {
+    // A module implementing HOOK_solrsearch_query_alter() aborted the search.
+    return array(NULL, array());
+  }
+
+
+  $keys = $query->getParam('q');
+
+
+  //dont want this because not supported by my search module in drupal dwinter
+  /* if (strlen($keys) == 0 && ($filters = $query->getFilters())) { */
+  /*   // Move the fq params to q.alt for better performance. Only suitable */
+  /*   // when using dismax or edismax, so we keep this out of the query class itself */
+  /*   // for now. */
+  /*   $qalt = array(); */
+  /*   foreach ($filters as $delta => $filter) { */
+  /*     // Move the fq param if it has no local params and is not negative. */
+  /*     if (!$filter['#exclude'] && !$filter['#local']) { */
+  /*       $qalt[] = '(' . $query->makeFilterQuery($filter) . ')'; */
+  /*       $query->removeFilter($filter['#name'], $filter['#value'], $filter['#exclude']); */
+  /*     } */
+  /*   } */
+  /*   if ($qalt) { */
+  /*     $query->addParam('q.alt', implode(' ', $qalt)); */
+  /*   } */
+  /* } */
+  // We must run htmlspecialchars() here since converted entities are in the index.
+  // and thus bare entities &, > or < won't match. Single quotes are converted
+  // too, but not double quotes since the dismax parser looks at them for
+  // phrase queries.
+  $keys = htmlspecialchars($keys, ENT_NOQUOTES, 'UTF-8');
+  $keys = str_replace("'", '&#039;', $keys);
+
+
+  $response = $query->search($keys);
+
+
+  // The response is cached so that it is accessible to the blocks and anything
+  // else that needs it beyond the initial search.
+  solrsearch_static_response_cache($searcher, $response);
+  return array($query, $response);
+}
+
+/**
+ * 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.
+ *
+ * @param $searcher
+ *   Name of the searcher - e.g. from $query->getSearcher().
+ */
+function solrsearch_static_response_cache($searcher, $response = NULL) {
+  $_response = &drupal_static(__FUNCTION__, array());
+
+  if (is_object($response)) {
+    $_response[$searcher] = clone $response;
+  }
+  if (!isset($_response[$searcher])) {
+    $_response[$searcher] = NULL;
+  }
+  return $_response[$searcher];
+}
+
+/**
+ * Factory function for query objects.
+ *
+ * @param string $name
+ *   The search name, used for finding the correct blocks and other config.
+ *   Typically "solrsearch".
+ * @param array $params
+ *   Array of params , such as 'q', 'fq' to be applied.
+ * @param string $solrsort
+ *   Visible string telling solr how to sort.
+ * @param string $base_path
+ *   The search base path (without the keywords) for this query.
+ * @param DrupalApacheSolrServiceInterface $solr
+ *   An instance of DrupalApacheSolrServiceInterface.
+ *
+ * @return DrupalSolrQueryInterface
+ *   DrupalSolrQueryInterface object.
+ *
+ * @throws Exception
+ */
+function solrsearch_drupal_query($name, array $params = array(), $solrsort = '', $base_path = '', DrupalApacheSolrServiceInterface $solr = NULL, $context = array()) {
+
+
+  if (!interface_exists('DrupalSolrQueryInterface')) {
+    require_once(dirname(__FILE__) . '/solrsearch.interface.inc');
+  }
+  $class_info = variable_get('solrsearch_query_class', array(
+    'file' => 'Solr_Base_Query',
+    'module' => 'solrsearch',
+    'class' => 'SolrBaseQuery'));
+  $class = $class_info['class'];
+  if (!class_exists($class_info['class']) && isset($class_info['file']) && isset($class_info['module'])) {
+    module_load_include('php', $class_info['module'], $class_info['file']);
+  }
+  if (empty($solr)) {
+    $solr = solrsearch_get_solr();
+  }
+
+  return new $class($name, $solr, $params, $solrsort, $base_path, $context);
+}
+
+/**
+ * Factory function for query objects.
+ *
+ * @param $operator
+ *   Whether the subquery should be added to another query as OR or AND
+ *
+ * @return DrupalSolrQueryInterface|false
+ *   Subquery or error.
+ *
+ * @throws Exception
+ */
+function solrsearch_drupal_subquery($operator = 'OR') {
+  if (!interface_exists('DrupalSolrQueryInterface')) {
+    require_once(dirname(__FILE__) . '/solrsearch.interface.inc');
+  }
+
+  $class_info = variable_get('solrsearch_subquery_class', array(
+    'file' => 'Solr_Base_Query',
+    'module' => 'solrsearch',
+    'class' => 'SolrFilterSubQuery'));
+  $class = $class_info['class'];
+  if (!class_exists($class_info['class']) && isset($class_info['file']) && isset($class_info['module'])) {
+    module_load_include('php', $class_info['module'], $class_info['file']);
+  }
+  $query = new $class($operator);
+  return $query;
+}
+
+/**
+ * Static getter/setter for the current query. Only set once per page.
+ *
+ * @param $env_id
+ *   Environment from which to save or get the current query
+ * @param DrupalSolrQueryInterface $query
+ *   $query object to save in the static
+ *
+ * @return DrupalSolrQueryInterface|null
+ *   return the $query object if it is available in the drupal_static or null otherwise
+ */
+function solrsearch_current_query($env_id, DrupalSolrQueryInterface $query = NULL) {
+  $saved_query = &drupal_static(__FUNCTION__, NULL);
+  if (is_object($query)) {
+    $saved_query[$env_id] = clone $query;
+  }
+  if (empty($saved_query[$env_id])) {
+    return NULL;
+  }
+  return is_object($saved_query[$env_id]) ? clone $saved_query[$env_id] : NULL;
+}
+
+/**
+ *
+ */
+
+/**
+ * Construct a dynamic index name based on information about a field.
+ *
+ * @param array $field
+ *   array(
+ *     'index_type' => 'integer',
+ *     'multiple' => TRUE,
+ *     'name' => 'fieldname',
+ *   ),
+ * @return string
+ *   Fieldname as it appears in the solr index
+ */
+function solrsearch_index_key($field) {
+  $index_type = !empty($field['index_type']) ? $field['index_type'] : NULL;
+  switch ($index_type) {
+    case 'text':
+      $type_prefix = 't';
+      break;
+    case 'text-omitNorms':
+      $type_prefix = 'to';
+      break;
+    case 'text-unstemmed':
+      $type_prefix = 'tu';
+      break;
+    case 'text-edgeNgram':
+      $type_prefix = 'te';
+      break;
+    case 'text-whiteSpace':
+      $type_prefix = 'tw';
+      break;
+    case 'integer':
+      $type_prefix = 'i'; // long integer
+      break;
+    case 'half-int':
+      $type_prefix = 'h'; // 32 bit integer
+      break;
+    case 'float':
+      $type_prefix = 'f'; // float; sortable.
+      break;
+    case 'double':
+      $type_prefix = 'p'; // double; sortable d was used for date.
+      break;
+    case 'boolean':
+      $type_prefix = 'b';
+      break;
+    case 'tint':
+      $type_prefix = 'it'; // long integer trie; sortable, best for range queries
+      break;
+    case 'thalf-int':
+      $type_prefix = 'ht'; // 32 bit integer trie (sortable)
+      break;
+    case 'tfloat':
+      $type_prefix = 'ft'; // float trie; sortable, best for range queries.
+      break;
+    case 'tdouble':
+      $type_prefix = 'pt'; // double trie;
+      break;
+    case 'sint':
+      $type_prefix = 'is'; // long integer sortable (deprecated)
+      break;
+    case 'half-sint':
+      $type_prefix = 'hs'; // 32 bit integer long sortable (deprecated)
+      break;
+    case 'sfloat':
+      $type_prefix = 'fs'; // float, sortable (use for sorting missing last) (deprecated).
+      break;
+    case 'sdouble':
+      $type_prefix = 'ps'; // double sortable; (use for sorting missing last) (deprecated).
+      break;
+    case 'date':
+      $type_prefix = 'd'; // date trie (sortable)
+      break;
+    case 'date-deprecated':
+      $type_prefix = 'dd'; // date (regular)
+      break;
+    case 'binary':
+      $type_prefix = 'x'; // Anything that is base64 encoded
+      break;
+    case 'storage':
+      $type_prefix = 'z'; // Anything that just need to be stored, not indexed
+      break;
+    case 'point':
+      $type_prefix = 'point'; // PointType. "52.3672174,4.9126891"
+      break;
+    case 'location':
+      $type_prefix = 'loc'; // LatLonType. "52.3672174,4.9126891"
+      break;
+    case 'geohash':
+      $type_prefix = 'geo'; // GeohashField. "42.6" http://en.wikipedia.org/wiki/Geohash
+      break;
+    case 'string':
+    default:
+      $type_prefix = 's'; // String
+  }
+  $sm = !empty($field['multiple']) ? 'm_' : 's_';
+  // Block deltas are limited to 32 chars.
+  return substr($type_prefix . $sm . $field['name'], 0, 32);
+}
+
+/**
+ * Try to map a schema field name to a human-readable description.
+ */
+function solrsearch_field_name_map($field_name) {
+  $map = &drupal_static(__FUNCTION__);
+
+  if (!isset($map)) {
+    $map = array(
+      'content' => t('The full, rendered content (e.g. the rendered node body)'),
+      'ts_comments' => t('The rendered comments associated with a node'),
+      'tos_content_extra' => t('Extra rendered content or keywords'),
+      'tos_name_formatted' => t('Author name (Formatted)'),
+      'label' => t('Title or label'),
+      'teaser' => t('Teaser or preview'),
+      'tos_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, H5, 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'),
+      'is_uid' => t('User IDs'),
+      'bundle' => t('Content type names eg. article'),
+      'entity_type' => t('Entity type names eg. node'),
+      'ss_language' => t('Language type eg. en or und (undefinded)'),
+    );
+    if (module_exists('taxonomy')) {
+      foreach (taxonomy_get_vocabularies() as $vocab) {
+        $map['tm_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 (solrsearch_entity_fields('node') as $field_nm => $nodefields) {
+      foreach ($nodefields as $field_info) {
+        $map[solrsearch_index_key($field_info)] = t('Field of type @type: %label', array('@type' => $field_info['field']['type'], '%label' => $field_info['display_name']));
+      }
+    }
+    drupal_alter('solrsearch_field_name_map', $map);
+  }
+  return isset($map[$field_name]) ? $map[$field_name] : $field_name;
+}
+
+/**
+ * Validation function for the Facet API facet settings form.
+ *
+ * Solr Search does not support the combination of OR facets
+ * and facet missing, so catch that at validation.
+ */
+function solrsearch_facet_form_validate($form, &$form_state) {
+  if (($form_state['values']['global']['operator'] == FACETAPI_OPERATOR_OR) && $form_state['values']['global']['facet_missing']) {
+    form_set_error('operator', t('Solr Search does not support <em>facet missing</em> in combination with the OR operator.'));
+  }
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function solrsearch_entity_info_alter(&$entity_info) {
+  // Load all environments
+  $environments = solrsearch_load_all_environments();
+
+  // Set those values that we know.  Other modules can do so
+  // for their own entities if they want.
+  $default_entity_info = array();
+  $default_entity_info['node']['indexable'] = TRUE;
+  $default_entity_info['node']['status callback'][] = 'solrsearch_index_node_status_callback';
+  $default_entity_info['node']['document callback'][] = 'solrsearch_index_node_solr_document';
+  $default_entity_info['node']['reindex callback'] = 'solrsearch_index_node_solr_reindex';
+  $default_entity_info['node']['bundles changed callback'] = 'solrsearch_index_node_bundles_changed';
+  $default_entity_info['node']['index_table'] = 'solrsearch_index_entities_node';
+  $default_entity_info['node']['cron_check'] = 'solrsearch_index_node_check_table';
+  // solrsearch_search implements a new callback for every entity type
+  // $default_entity_info['node']['solrsearch']['result callback'] = 'solrsearch_search_node_result';
+  //Allow implementations of HOOK_solrsearch_entity_info to modify these default indexers
+  drupal_alter('solrsearch_entity_info', $default_entity_info);
+
+  // First set defaults so that we don't need to worry about NULL keys.
+  foreach (array_keys($entity_info) as $type) {
+    if (!isset($entity_info[$type]['solrsearch'])) {
+      $entity_info[$type]['solrsearch'] = array();
+    }
+    if (isset($default_entity_info[$type])) {
+      $entity_info[$type]['solrsearch'] += $default_entity_info[$type];
+    }
+    $default = array(
+      'indexable' => FALSE,
+      'status callback' => '',
+      'document callback' => '',
+      'reindex callback' => '',
+      'bundles changed callback' => '',
+    );
+    $entity_info[$type]['solrsearch'] += $default;
+  }
+
+  // For any supported entity type and bundle, flag it for indexing.
+  foreach ($entity_info as $entity_type => $info) {
+    if ($info['solrsearch']['indexable']) {
+      // Loop over each environment and check if any of them have other entity
+      // bundles of any entity type enabled and set the index value to TRUE
+      foreach ($environments as $env) {
+        // Skip if the environment is set to read only
+        if (empty($env['env_id']['conf']['solrsearch_read_only'])) {
+          // Get the supported bundles
+          $supported = solrsearch_get_index_bundles($env['env_id'], $entity_type);
+          // For each bundle in drupal, compare to the supported solrsearch
+          // bundles and enable where possible
+          foreach (array_keys($info['bundles']) as $bundle) {
+            if (in_array($bundle, $supported)) {
+              $entity_info[$entity_type]['bundles'][$bundle]['solrsearch']['index'] = TRUE;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Gets a list of the bundles on the specified entity type that should be indexed.
+ *
+ * @param string $core
+ *   The Solr environment for which to index entities.
+ * @param string $entity_type
+ *   The entity type to index.
+ * @return array
+ *   The bundles that should be indexed.
+ */
+function solrsearch_get_index_bundles($env_id, $entity_type) {
+  $environment = solrsearch_environment_load($env_id);
+  return !empty($environment['index_bundles'][$entity_type]) ? $environment['index_bundles'][$entity_type] : array();
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function solrsearch_entity_insert($entity, $type) {
+  // For our purposes there's really no difference between insert and update.
+  return solrsearch_entity_update($entity, $type);
+}
+
+/**
+ * Determines if we should index the provided entity.
+ *
+ * Whether or not a given entity is indexed is determined on a per-bundle basis.
+ * Entities/Bundles that have no index flag are presumed to not get indexed.
+ *
+ * @param stdClass $entity
+ *   The entity we may or may not want to index.
+ * @param string $type
+ *   The type of entity.
+ * @return boolean
+ *   TRUE if this entity should be indexed, FALSE otherwise.
+ */
+function solrsearch_entity_should_index($entity, $type) {
+  $info = entity_get_info($type);
+  list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
+
+  if ($bundle && isset($info['bundles'][$bundle]['solrsearch']['index']) && $info['bundles'][$bundle]['solrsearch']['index']) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function solrsearch_entity_update($entity, $type) {
+  // Include the index file for the status callback
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  if (solrsearch_entity_should_index($entity, $type)) {
+    $info = entity_get_info($type);
+    list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
+
+    // Check status callback before sending to the index
+    $status_callbacks = solrsearch_entity_get_callback($type, 'status callback', $bundle);
+
+    $status = TRUE;
+    if (is_array($status_callbacks)) {
+      foreach($status_callbacks as $status_callback) {
+        if (is_callable($status_callback)) {
+          // by placing $status in front we prevent calling any other callback
+          // after one status callback returned false
+          $status = $status && $status_callback($id, $type);
+        }
+      }
+    }
+
+    // Delete the entity from our index if the status callback returns FALSE
+    if (!$status) {
+      solrsearch_entity_delete($entity, $type);
+      return NULL;
+    }
+
+    $indexer_table = solrsearch_get_indexer_table($type);
+
+    // If we haven't seen this entity before it may not be there, so merge
+    // instead of update.
+    db_merge($indexer_table)
+      ->key(array(
+      'entity_type' => $type,
+      'entity_id' => $id,
+      ))
+      ->fields(array(
+        'bundle' => $bundle,
+        'status' => 1,
+        'changed' => REQUEST_TIME,
+      ))
+      ->execute();
+  }
+}
+
+/**
+ * Retrieve the indexer table for an entity type.
+ */
+function solrsearch_get_indexer_table($type) {
+  $entity_info = entity_get_info();
+  if (isset($entity_info[$type]['solrsearch']['index_table'])) {
+    $indexer_table = $entity_info[$type]['solrsearch']['index_table'];
+  }
+  else {
+    $indexer_table = 'solrsearch_index_entities';
+  }
+  return $indexer_table;
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function solrsearch_entity_delete($entity, $entity_type) {
+  $env_id = solrsearch_default_environment();
+
+  // Delete the entity's entry from a fictional table of all entities.
+  $info = entity_get_info($entity_type);
+  list($entity_id) = entity_extract_ids($entity_type, $entity);
+  solrsearch_remove_entity($env_id, $entity_type, $entity_id);
+}
+
+function solrsearch_remove_entity($env_id, $entity_type, $entity_id) {
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+
+  $indexer_table = solrsearch_get_indexer_table($entity_type);
+  if (solrsearch_index_delete_entity_from_index($env_id, $entity_type, $entity_id)) {
+    // There was no exception, so delete from the table.
+    db_delete($indexer_table)
+      ->condition('entity_type', $entity_type)
+      ->condition('entity_id', $entity_id)
+      ->execute();
+  }
+  else {
+    // Set status 0 so we try to delete from the index again in the future.
+    db_update($indexer_table)
+      ->condition('entity_id', $entity_id)
+      ->fields(array('changed' => REQUEST_TIME, 'status' => 0))
+      ->execute();
+  }
+}
+
+/**
+ * Returns array containing information about node fields that should be indexed
+ */
+function solrsearch_entity_fields($entity_type = 'node') {
+  $fields = &drupal_static(__FUNCTION__, array());
+
+  if (!isset($fields[$entity_type])) {
+    $fields[$entity_type] = array();
+
+    $mappings = module_invoke_all('solrsearch_field_mappings');
+    foreach (array_keys($mappings) as $key) {
+      // Set all values with defaults.
+      $defaults = array(
+          'dependency plugins' => array('bundle', 'role'),
+          'map callback' => FALSE,
+          'name callback' => '',
+          'hierarchy callback' => FALSE,
+          'indexing_callback' => '',
+          'index_type' => 'string',
+          'facets' => FALSE,
+          'facet missing allowed' => FALSE,
+          'facet mincount allowed' => FALSE,
+          // Field API allows any field to be multi-valued.
+          'multiple' => FALSE,
+        );
+      if ($key !== 'per-field') {
+        $mappings[$key] += $defaults;
+      }
+      else {
+        foreach (array_keys($mappings[$key]) as $field_key) {
+          $mappings[$key][$field_key] += $defaults;
+        }
+      }
+    }
+
+    // Allow other modules to add or alter mappings.
+    drupal_alter('solrsearch_field_mappings', $mappings, $entity_type);
+
+    $modules = system_get_info('module');
+    $instances = field_info_instances($entity_type);
+    foreach (field_info_fields() as $field_name => $field) {
+      $row = array();
+      if (isset($field['bundles'][$entity_type]) && (isset($mappings['per-field'][$field_name]) || isset($mappings[$field['type']]))) {
+        // Find the mapping.
+        if (isset($mappings['per-field'][$field_name])) {
+          $row = $mappings['per-field'][$field_name];
+        }
+        else {
+          $row = $mappings[$field['type']];
+        }
+        // The field info array.
+        $row['field'] = $field;
+
+        // Cardinality: The number of values the field can hold. Legal values
+        // are any positive integer or FIELD_CARDINALITY_UNLIMITED.
+        if ($row['field']['cardinality'] != 1) {
+          $row['multiple'] = TRUE;
+        }
+
+        // @todo: for fields like taxonomy we are indexing multiple Solr fields
+        // per entity field, but are keying on a single Solr field name here.
+        $function = !empty($row['name callback']) ? $row['name callback'] : NULL;
+        if ($function && is_callable($function)) {
+          $row['name'] = $function($field);
+        }
+        else {
+          $row['name'] = $field['field_name'];
+        }
+        $row['module_name'] = $modules[$field['module']]['name'];
+        // Set display name
+        $display_name = array();
+        foreach ($field['bundles'][$entity_type] as $bundle) {
+          if (empty($instances[$bundle][$field_name]['display']['search_index']) || $instances[$bundle][$field_name]['display']['search_index'] != 'hidden') {
+            $row['display_name'] = $instances[$bundle][$field_name]['label'];
+            $row['bundles'][] = $bundle;
+          }
+        }
+        // Only add to the $fields array if some instances are displayed for the search index.
+        if (!empty($row['bundles'])) {
+          // Use the Solr index key as the array key.
+          $fields[$entity_type][solrsearch_index_key($row)][] = $row;
+        }
+      }
+    }
+  }
+  return $fields[$entity_type];
+}
+
+/**
+ * Implements hook_solrsearch_index_document_build().
+ */
+function field_solrsearch_index_document_build(solrsearchDocument $document, $entity, $entity_type) {
+  $info = entity_get_info($entity_type);
+  if ($info['fieldable']) {
+    // Handle fields including taxonomy.
+    $indexed_fields = solrsearch_entity_fields($entity_type);
+    foreach ($indexed_fields as $index_key => $nodefields) {
+      foreach ($nodefields as $field_info) {
+        $field_name = $field_info['field']['field_name'];
+        // See if the node has fields that can be indexed
+        if (isset($entity->{$field_name})) {
+          // Got a field.
+          $function = $field_info['indexing_callback'];
+          if ($function && function_exists($function)) {
+            // NOTE: This function should always return an array.  One
+            // entity field may be indexed to multiple Solr fields.
+            $fields = $function($entity, $field_name, $index_key, $field_info);
+            foreach ($fields as $field) {
+              // It's fine to use this method also for single value fields.
+              $document->setMultiValue($field['key'], $field['value']);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_solrsearch_index_document_build_node().
+ *
+ * Adds book module support
+ */
+function solrsearch_solrsearch_index_document_build_node(solrsearchDocument $document, $entity, $env_id) {
+  // Index book module data.
+  if (!empty($entity->book['bid'])) {
+    // Hard-coded - must change if solrsearch_index_key() changes.
+    $document->is_book_bid = (int) $entity->book['bid'];
+  }
+}
+
+/**
+ * Strip html tags and also control characters that cause Jetty/Solr to fail.
+ */
+function solrsearch_clean_text($text) {
+  // Remove invisible content.
+  $text = preg_replace('@<(applet|audio|canvas|command|embed|iframe|map|menu|noembed|noframes|noscript|script|style|svg|video)[^>]*>.*</\1>@siU', ' ', $text);
+  // Add spaces before stripping tags to avoid running words together.
+  $text = filter_xss(str_replace(array('<', '>'), array(' <', '> '), $text), array());
+  // Decode entities and then make safe any < or > characters.
+  $text = htmlspecialchars(html_entity_decode($text, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8');
+  // Remove extra spaces.
+  $text = preg_replace('/\s+/s', ' ', $text);
+  // Remove white spaces around punctuation marks probably added
+  // by the safety operations above. This is not a world wide perfect solution,
+  // but a rough attempt for at least US and Western Europe.
+  // Pc: Connector punctuation
+  // Pd: Dash punctuation
+  // Pe: Close punctuation
+  // Pf: Final punctuation
+  // Pi: Initial punctuation
+  // Po: Other punctuation, including ¿?¡!,.:;
+  // Ps: Open punctuation
+  $text = preg_replace('/\s(\p{Pc}|\p{Pd}|\p{Pe}|\p{Pf}|!|\?|,|\.|:|;)/s', '$1', $text);
+  $text = preg_replace('/(\p{Ps}|¿|¡)\s/s', '$1', $text);
+  return $text;
+}
+
+/**
+ * Use the list.module's list_allowed_values() 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 solrsearch_fields_list_facet_map_callback($facets, $options) {
+  $map = array();
+  $allowed_values = array();
+  // @see list_field_formatter_view()
+  $fields = field_info_fields();
+  $field_name = $options['field']['field_name'];
+  if (isset($fields[$field_name])) {
+    $allowed_values = list_allowed_values($fields[$field_name]);
+  }
+  foreach ($facets as $key) {
+    if (isset($allowed_values[$key])) {
+      $map[$key]['#markup'] = field_filter_xss($allowed_values[$key]);
+    }
+    elseif ($key == '_empty_' && $options['facet missing allowed']) {
+      // Facet missing.
+      $map[$key]['#markup'] = theme('facetapi_facet_missing', array('field_name' => $options['display_name']));
+    }
+    else {
+      $map[$key]['#markup'] = field_filter_xss($key);
+    }
+    // The value has already been filtered.
+    $map[$key]['#html'] = TRUE;
+  }
+  return $map;
+}
+
+/**
+ *  @param $facet string
+ *    The indexed value
+ *  @param $options
+ *    An array of options including the hook_block $delta.
+ *  @see http://drupal.org/node/1059372
+ */
+function solrsearch_nodereference_map_callback($facets, $options) {
+  $map = array();
+  $allowed_values = array();
+  // @see list_field_formatter_view()
+  $fields = field_info_fields();
+  $field_name = $options['field']['field_name'];
+  if (isset($fields[$field_name])) {
+    $allowed_values = node_reference_potential_references($fields[$field_name]);
+  }
+  foreach ($facets as $key) {
+    if (isset($allowed_values[$key])) {
+      $map[$key]['#markup'] = field_filter_xss($allowed_values[$key]['title']);
+    }
+    elseif ($key == '_empty_' && $options['facet missing allowed']) {
+      // Facet missing.
+      $map[$key]['#markup'] = theme('facetapi_facet_missing', array('field_name' => $options['display_name']));
+    }
+    else {
+      $map[$key]['#markup'] = field_filter_xss($key);
+    }
+    // The value has already been filtered.
+    $map[$key]['#html'] = TRUE;
+  }
+  return $map;
+}
+
+/**
+ *  @param $facet string
+ *    The indexed value
+ *  @param $options
+ *    An array of options including the hook_block $delta.
+ *  @see http://drupal.org/node/1059372
+ */
+function solrsearch_userreference_map_callback($facets, $options) {
+  $map = array();
+  $allowed_values = array();
+  // @see list_field_formatter_view()
+  $fields = field_info_fields();
+  $field_name = $options['field']['field_name'];
+  if (isset($fields[$field_name])) {
+    $allowed_values = user_reference_potential_references($fields[$field_name]);
+  }
+  foreach ($facets as $key) {
+    if (isset($allowed_values[$key])) {
+      $map[$key]['#markup'] = field_filter_xss($allowed_values[$key]['title']);
+    }
+    elseif ($key == '_empty_' && $options['facet missing allowed']) {
+      // Facet missing.
+      $map[$key]['#markup'] = theme('facetapi_facet_missing', array('field_name' => $options['display_name']));
+    }
+    else {
+      $map[$key]['#markup'] = field_filter_xss($key);
+    }
+    // The value has already been filtered.
+    $map[$key]['#html'] = TRUE;
+  }
+  return $map;
+}
+
+/**
+ * Mapping callback for entity references.
+ */
+function solrsearch_entityreference_facet_map_callback(array $values, array $options) {
+  $map = array();
+  // Gathers entity ids so we can load multiple entities at a time.
+  $entity_ids = array();
+  foreach ($values as $value) {
+    list($entity_type, $id) = explode(':', $value);
+    $entity_ids[$entity_type][] = $id;
+  }
+  // Loads and maps entities.
+  foreach ($entity_ids as $entity_type => $ids) {
+    $entities = entity_load($entity_type, $ids);
+    foreach ($entities as $id => $entity) {
+      $key = $entity_type . ':' . $id;
+      $map[$key] = entity_label($entity_type, $entity);
+    }
+  }
+  return $map;
+}
+
+/**
+ * Returns the callback function appropriate for a given entity type/bundle.
+ *
+ * @param string $entity_type
+ *   The entity type for which we want to know the approprite callback.
+ * @param string $callback
+ *   The callback for which we want the appropriate function.
+ * @param string $bundle
+ *   If specified, the bundle of the entity in question.  Some callbacks may
+ *   be overridden on a bundle-level.  Not specified only the entity-level
+ *   callback will be checked.
+ * @return string
+ *   The function name for this callback, or NULL if not specified.
+ */
+function solrsearch_entity_get_callback($entity_type, $callback, $bundle = NULL) {
+  $info = entity_get_info($entity_type);
+
+  // A bundle-specific callback takes precedence over the generic one for the
+  // entity type.
+  if ($bundle && isset($info['bundles'][$bundle]['solrsearch'][$callback])) {
+    $callback_function = $info['bundles'][$bundle]['solrsearch'][$callback];
+  }
+  elseif (isset($info['solrsearch'][$callback])) {
+    $callback_function = $info['solrsearch'][$callback];
+  }
+  else {
+    $callback_function = NULL;
+  }
+  return $callback_function;
+}
+
+
+/**
+ * Function to retrieve all the nodes to index.
+ * Deprecated but kept for backwards compatibility
+ * @param String $namespace
+ * @param type $limit
+ */
+function solrsearch_get_nodes_to_index($namespace, $limit) {
+  $env_id = solrsearch_default_environment();
+  // Hardcode node as an entity type
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  solrsearch_index_get_entities_to_index($env_id, 'node', $limit);
+}
+
+
+/**
+ * Implements hook_theme().
+ */
+function solrsearch_theme() {
+  return array(
+    'solrsearch_result'  => array(
+      'variables' => array('result' => NULL, 'module' => NULL),
+      'file' => 'solrsearch.pages.inc',
+      'template' => 'solr-search-result',
+    ),
+      'solrsearch_results' => array(
+          'variables' => array('results' => NULL, 'module' => NULL),
+          'file' => 'solrsearch.pages.inc',
+          'template' => 'solr-search-results',
+      ),
+      'solrsearch_term_list_author' => array(
+          'variables' => array('authors' => null),
+          'file' => 'solrsearch_terms.inc',
+          'template' => 'solrsearch-term-list-author',
+      ),
+
+      'solrsearch_term_list_title' => array(
+          'variables' => array('authors' => null),
+          'file' => 'solrsearch_terms.inc',
+          'template' => 'solrsearch-term-list-title',
+      ),
+
+      'solrsearch_term_selection_form' => array(
+          'variables' => array('authors' => null,'cnt' => null, 'letter' => null),
+          'file' => 'solrsearch_terms.inc',
+          'template' => 'solrsearch-term-selection-form',
+      ),
+
+
+    /**
+     * Returns a list of links generated by solrsearch_sort_link
+     */
+    'solrsearch_sort_list' => array(
+      'variables' => array('items' => NULL),
+    ),
+    /**
+     * Returns a link which can be used to search the results.
+     */
+    'solrsearch_sort_link' => array(
+      'variables' => array('text' => NULL, 'path' => NULL, 'options' => NULL, 'active' => FALSE, 'direction' => ''),
+    ),
+    /**
+     * Themes the title links in admin settings pages.
+     */
+    'solrsearch_settings_title' => array(
+      'variables' => array('env_id' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function solrsearch_hook_info() {
+  $hooks = array(
+    'solrsearch_field_mappings' => array(
+      'group' => 'solrsearch',
+    ),
+    'solrsearch_field_mappings_alter' => array(
+      'group' => 'solrsearch',
+    ),
+    'solrsearch_query_prepare' => array(
+      'group' => 'solrsearch',
+    ),
+    'solrsearch_query_alter' => array(
+      'group' => 'solrsearch',
+    ),
+    'solrsearch_search_result_alter' => array(
+      'group' => 'solrsearch',
+    ),
+    'solrsearch_environment_delete' => array(
+      'group' => 'solrsearch',
+    )
+  );
+  $hooks['solrsearch_index_document_build'] = array(
+    'group' => 'solrsearch',
+  );
+  return $hooks;
+}
+
+/**
+ * Implements hook_solrsearch_field_mappings().
+ */
+function field_solrsearch_field_mappings() {
+  $mappings = array(
+    'list_integer' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'map callback' => 'solrsearch_fields_list_facet_map_callback',
+      'index_type' => 'integer',
+      'facets' => TRUE,
+      'query types' => array('term', 'numeric_range'),
+      'query type' => 'term',
+      'facet missing allowed' => TRUE,
+    ),
+    'list_float' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'map callback' => 'solrsearch_fields_list_facet_map_callback',
+      'index_type' => 'float',
+      'facets' => TRUE,
+      'query types' => array('term', 'numeric_range'),
+      'query type' => 'term',
+      'facet missing allowed' => TRUE,
+    ),
+    'list_text' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'map callback' => 'solrsearch_fields_list_facet_map_callback',
+      'index_type' => 'string',
+      'facets' => TRUE,
+      'facet missing allowed' => TRUE,
+    ),
+    'list_boolean' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'map callback' => 'solrsearch_fields_list_facet_map_callback',
+      'index_type' => 'boolean',
+      'facets' => TRUE,
+      'facet missing allowed' => TRUE,
+    ),
+    'number_integer' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'index_type' => 'tint',
+      'facets' => TRUE,
+      'query types' => array('term', 'numeric_range'),
+      'query type' => 'term',
+      'facet mincount allowed' => TRUE,
+    ),
+    'number_decimal' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'index_type' => 'tfloat',
+      'facets' => TRUE,
+      'query types' => array('term', 'numeric_range'),
+      'query type' => 'term',
+      'facet mincount allowed' => TRUE,
+    ),
+    'number_float' => array(
+      'indexing_callback' => 'solrsearch_fields_default_indexing_callback',
+      'index_type' => 'tfloat',
+      'facets' => TRUE,
+      'query types' => array('term', 'numeric_range'),
+      'query type' => 'term',
+      'facet mincount allowed' => TRUE,
+    ),
+    'taxonomy_term_reference' => array(
+      'map callback' => 'facetapi_map_taxonomy_terms',
+      'hierarchy callback' => 'facetapi_get_taxonomy_hierarchy',
+      'indexing_callback' => 'solrsearch_term_reference_indexing_callback',
+      'index_type' => 'integer',
+      'facet_block_callback' => 'solrsearch_search_taxonomy_facet_block',
+      'facets' => TRUE,
+      'query types' => array('term'),
+      'query type' => 'term',
+      'facet mincount allowed' => TRUE,
+    ),
+  );
+
+  return $mappings;
+}
+
+/**
+ * Implements hook_solrsearch_field_mappings() on behalf of date module.
+ */
+function date_solrsearch_field_mappings() {
+  $mappings = array();
+  $default = array(
+    'indexing_callback' => 'solrsearch_date_default_indexing_callback',
+    'index_type' => 'date',
+    'facets' => TRUE,
+    'query types' => array('date'),
+    'query type' => 'date',
+    'min callback' => 'solrsearch_get_min_date',
+    'max callback' => 'solrsearch_get_max_date',
+    'map callback' => 'facetapi_map_date',
+  );
+
+  // DATE and DATETIME fields can use the same indexing callback.
+  $mappings['date'] = $default;
+  $mappings['datetime'] = $default;
+
+  // DATESTAMP fields need a different callback.
+  $mappings['datestamp'] = $default;
+  $mappings['datestamp']['indexing_callback'] = 'solrsearch_datestamp_default_indexing_callback';
+
+  return $mappings;
+}
+
+
+/**
+ * Callback that returns the minimum date of the facet's datefield.
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *   The minimum time in the node table.
+ *
+ * @todo Cache this value.
+ */
+function solrsearch_get_min_date(array $facet) {
+  // FieldAPI date fields.
+  $table = 'field_data_' . $facet['field api name'];
+  $column = $facet['field api name'] . '_value';
+  $query = db_select($table, 't');
+  $query->addExpression('MIN(' . $column . ')', 'min');
+  $query_min = $query->execute()->fetch()->min;
+  // Update to unix timestamp if this is an ISO or other format.
+  if (!is_int($query_min)) {
+    $return = strtotime($query_min);
+    if ($return === FALSE) {
+      // Not a string that strtotime accepts (ex. '0000-00-00T00:00:00').
+      // Return default start date of 1 as the date query type getDateRange()
+      // function expects a non-0 integer.
+      $return = 1;
+    }
+  }
+  return $return;
+}
+
+/**
+ * Callback that returns the maximum value of the facet's date field.
+ *
+ * @param $facet
+ *   An array containing the facet definition.
+ *
+ * @return
+ *   The maximum time of the field.
+ *
+ * @todo Cache this value.
+ */
+function solrsearch_get_max_date(array $facet) {
+
+  // FieldAPI date fields.
+  $table = 'field_data_' . $facet['field api name'];
+  $column = $facet['field api name'] . '_value';
+  $query = db_select($table, 't');
+  $query->addExpression('MAX(' . $column . ')', 'max');
+  $query_max = $query->execute()->fetch()->max;
+  // Update to unix timestamp if this is an ISO or other format.
+  if (!is_int($query_max)) {
+    $return = strtotime($query_max);
+    if ($return === FALSE) {
+      // Not a string that strtotime accepts (ex. '0000-00-00T00:00:00').
+      // Return default end date of 1 year from now.
+      $return = time() + (52 * 7 * 24 * 60 * 60);
+    }
+  }
+  return $return;
+}
+
+/**
+ * Implements hook_solrsearch_field_mappings() on behalf of References (node_reference).
+ * @see http://drupal.org/node/1059372
+ */
+function node_reference_solrsearch_field_mappings() {
+  $mappings = array(
+    'node_reference' => array(
+      'indexing_callback' => 'solrsearch_nodereference_indexing_callback',
+      'index_type' => 'integer',
+      'map callback' => 'solrsearch_nodereference_map_callback',
+      'facets' => TRUE,
+    )
+  );
+
+  return $mappings;
+}
+
+/**
+ * Implements hook_solrsearch_field_mappings() on behalf of References (user_reference).
+ * @see http://drupal.org/node/1059372
+ */
+function user_reference_solrsearch_field_mappings() {
+  $mappings = array(
+    'user_reference' => array(
+      'indexing_callback' => 'solrsearch_userreference_indexing_callback',
+      'index_type' => 'integer',
+      'map callback' => 'solrsearch_userreference_map_callback',
+      'facets' => TRUE,
+    ),
+  );
+
+  return $mappings;
+}
+/**
+ * Implements hook_solrsearch_field_mappings() on behalf of EntityReferences (entityreference)
+ * @see http://drupal.org/node/1572722
+ */
+function entityreference_solrsearch_field_mappings() {
+  $mappings = array(
+    'entityreference' => array(
+      'indexing_callback' => 'solrsearch_entityreference_indexing_callback',
+      'map callback' => 'solrsearch_entityreference_facet_map_callback',
+      'index_type' => 'string',
+      'facets' => TRUE,
+      'query types' => array('term'),
+      'facet missing allowed' => TRUE,
+    ),
+  );
+
+  return $mappings;
+}
+
+/**
+ * A replacement for l()
+ *  - doesn't add the 'active' class
+ *  - retains all $_GET parameters that solrsearch 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 solrsearch_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']);
+
+  // Retain GET parameters that Solr Search knows nothing about.
+  $get = array_diff_key($_GET, array('q' => 1, 'page' => 1, 'solrsort' => 1), $options['query']);
+  $options['query'] += $get;
+
+  return '<a href="' . check_url(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain(html_entity_decode($text))) . '</a>';
+}
+
+function theme_solrsearch_sort_link($vars) {
+  $icon = '';
+  if ($vars['direction']) {
+    $icon = ' ' . theme('tablesort_indicator', array('style' => $vars['direction']));
+  }
+  if ($vars['active']) {
+    if (isset($vars['options']['attributes']['class'])) {
+      $vars['options']['attributes']['class'] .= ' active';
+    }
+    else {
+      $vars['options']['attributes']['class'] = 'active';
+    }
+  }
+  return $icon . solrsearch_l($vars['text'], $vars['path'], $vars['options']);
+}
+
+function theme_solrsearch_sort_list($vars) {
+  // theme('item_list') expects a numerically indexed array.
+  $vars['items'] = array_values($vars['items']);
+  return theme('item_list', array('items' => $vars['items']));
+}
+
+/**
+ * Themes the title for settings pages.
+ */
+function theme_solrsearch_settings_title($vars) {
+  $output = '';
+
+  // Gets environment information, builds header with nested link to the environment's
+  // edit page. Skips building title if environment info could not be retrieved.
+  if ($environment = solrsearch_environment_load($vars['env_id'])) {
+    $url = url(
+      'admin/config/search/solrsearch/settings/',
+      array('query' => array('destination' => current_path()))
+    );
+    $output .= '<h3>';
+    $output .= t(
+      'Settings for: @environment (<a href="@url">Overview</a>)',
+      array('@url' => $url, '@environment' => $environment['name'])
+    );
+    $output .= "</h3>\n";
+  }
+
+  return $output;
+}
+
+/**
+ * Export callback to load the view subrecords, which are the index bundles.
+ */
+function solrsearch_environment_load_subrecords(&$environments) {
+  if (empty($environments)) {
+    // Nothing to do.
+    return NULL;
+  }
+
+  $all_index_bundles = db_select('solrsearch_index_bundles', 'ib')
+    ->fields('ib', array('env_id', 'entity_type', 'bundle'))
+    ->condition('env_id', array_keys($environments), 'IN')
+    ->orderBy('env_id')
+    ->orderBy('entity_type')
+    ->orderBy('bundle')
+    ->execute()
+    ->fetchAll(PDO::FETCH_ASSOC);
+
+  $all_index_bundles_keyed = array();
+  foreach ($all_index_bundles as $env_info) {
+    extract($env_info);
+    $all_index_bundles_keyed[$env_id][$entity_type][] = $bundle;
+  }
+
+  $all_variables = db_select('solrsearch_environment_variable', 'v')
+    ->fields('v', array('env_id', 'name', 'value'))
+    ->condition('env_id', array_keys($environments), 'IN')
+    ->orderBy('env_id')
+    ->orderBy('name')
+    ->orderBy('value')
+    ->execute()
+    ->fetchAll(PDO::FETCH_ASSOC);
+
+  $variables = array();
+  foreach ($all_variables as $variable) {
+    extract($variable);
+    $variables[$env_id][$name] = unserialize($value);
+  }
+
+  foreach ($environments as $env_id => &$environment) {
+    $index_bundles = !empty($all_index_bundles_keyed[$env_id]) ? $all_index_bundles_keyed[$env_id] : array();
+    $conf = !empty($variables[$env_id]) ? $variables[$env_id] : array();
+    if (is_array($environment)) {
+      // Environment is an array.
+      // If we have different values in the database compared with what we
+      // have in the given environment argument we allow the admin to revert
+      // the db values so we can stick with a consistent system
+      if (!empty($environment['index_bundles']) && !empty($index_bundles) && $environment['index_bundles'] !== $index_bundles) {
+        unset($environment['in_code_only']);
+        $environment['type'] = 'Overridden';
+      }
+      if (!empty($environment['conf']) && !empty($conf) && $environment['conf'] !== $conf) {
+        unset($environment['in_code_only']);
+        $environment['type'] = 'Overridden';
+      }
+      $environment['index_bundles'] = (empty($environment['index_bundles']) || !empty($index_bundles)) ? $index_bundles : $environment['index_bundles'];
+      $environment['conf'] = (empty($environment['conf']) || !empty($conf)) ? $conf : $environment['conf'];
+    }
+    elseif (is_object($environment)) {
+      // Environment is an object.
+      if ($environment->index_bundles !== $index_bundles && !empty($index_bundles)) {
+        unset($environment->in_code_only);
+        $environment->type = 'Overridden';
+      }
+      if ($environment->conf !== $conf && !empty($conf)) {
+        unset($environment->in_code_only);
+        $environment->type = 'Overridden';
+      }
+      $environment->index_bundles = (empty($environment->index_bundles) || !empty($index_bundles)) ? $index_bundles : $environment->index_bundles;
+      $environment->conf = (empty($environment->conf) || !empty($conf)) ? $conf : $environment->conf;
+    }
+  }
+}
+
+/**
+ * Callback for saving Solr Search environment CTools exportables.
+ *
+ * CTools uses objects, while Solr Search uses arrays; turn CTools value into an
+ * array, then call the normal save function.
+ *
+ * @param stdclass $environment
+ *   An environment object.
+ */
+function solrsearch_ctools_environment_save($environment) {
+  solrsearch_environment_save((array) $environment);
+}
+
+/**
+ * Callback for reverting Solr Search environment CTools exportables.
+ *
+ * @param mixed $env_id
+ *   An environment machine name. CTools may provide an id OR a complete
+ *   environment object; Since Solr Search loads environments as arrays, this
+ *   may also be an environment array.
+ */
+function solrsearch_ctools_environment_delete($env_id) {
+  if (is_object($env_id) || is_array($env_id)) {
+    $env_id = (object) $env_id;
+    $env_id = $env_id->env_id;
+  }
+  solrsearch_environment_delete($env_id);
+}
+
+/**
+ * Callback for exporting Solr Search environments as CTools exportables.
+ *
+ * @param array $environment
+ *   An environment array from Solr Search.
+ * @param string $indent
+ *   White space for indentation from CTools.
+ */
+function solrsearch_ctools_environment_export($environment, $indent) {
+  ctools_include('export');
+  $environment = (object) $environment;
+  // Re-load the enviroment, since in some cases the conf
+  // is stripped since it's not in the actual schema.
+  $environment = (object) solrsearch_environment_load($environment->env_id);
+
+  $index_bundles = array();
+  foreach (entity_get_info() as $type => $info) {
+    if ($bundles = solrsearch_get_index_bundles($environment->env_id, $type)) {
+      $index_bundles[$type] = $bundles;
+    }
+  }
+  $additions_top = array();
+  $additions_bottom = array('conf' => $environment->conf, 'index_bundles' => $index_bundles);
+  return ctools_export_object('solrsearch_environment', $environment, $indent, NULL, $additions_top, $additions_bottom);
+}
+
+
+
+
+/**
+* Performs a search by calling hook_search_execute().
+*
+* @param $keys
+*   Keyword query to search on.
+* @param $module
+*   Search module to search.
+* @param $conditions
+*   Optional array of additional search conditions.
+*
+* @return
+*   Renderable array of search results. No return value if $keys are not
+*   supplied or if the given search module is not active.
+*/
+function solrsearch_data($keys, $module, $conditions = NULL) {
+  if (module_hook($module, 'search_execute')) {
+
+
+    $results = module_invoke($module, 'search_execute', $keys, $conditions);
+
+      #$params = $query ->getParams();
+    #$description = t('Showing items @start through @end of @total.', array(
+    #    '@start' => $params['start'] + 1,
+    #    '@end' => $params['start'] + $params['rows'] - 1,
+    #    '@total' => $total,
+    #));
+
+
+    if (module_hook($module, 'search_page')) {
+      return module_invoke($module, 'search_page', $results);
+    }
+    else {
+      return array(
+          '#theme' => 'solrsearch_results',
+          '#results' => $results,
+          '#module' => $module,
+      );
+    }
+  }
+}
+
+function solrsearch_callback_user_values(array $facet){
+ #ToDO is this used?
+}
+
+
+function solrsearch_permission() {
+  return array(
+      'view restricted content' => array(
+          'title' => t('View restricted content'),
+          'description' => t('Allow users to view content which is restricted.'),
+      ),
+  );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch.pages.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,349 @@
+<?php
+
+/**
+ * @file
+ * User page callbacks for the search module.
+ */
+/**
+ * Menu callback;saves search in current collection
+ *
+ * @param $module
+ *   Search module to use for the search.
+ * @param $keys
+ *   Keywords to use for the search.
+ */
+function solrsearch_save($keys = '') {
+  $keys = trim($keys);
+  // Also try to pull search keywords out of the $_REQUEST variable to
+  // support old GET format of searches for existing links.
+  if (!$keys && !empty($_REQUEST['keys'])) {
+    $keys = trim($_REQUEST['keys']);
+  }
+
+
+  $conditions =  NULL;
+
+  $conditions = solrsearch_search_conditions($keys);
+  $conditions['rows']=500; //TODO make this configurable
+
+  // Only search if there are keywords or non-empty conditions.
+  if ($keys || !empty($conditions)) {
+
+    // Log the search keys.
+    //watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys));
+
+    // Collect the search results.
+    $results = module_invoke('solrsearch_search', 'search_execute', $keys, $conditions);
+
+
+  }
+  //dpm($keys);
+  //dpm($conditions);
+  //dpm($results);
+
+  foreach($results as $result){
+    $data['digitalobjects_items_select'][]=$result['mpiwg-dri'];
+  }
+
+  digitalobjects_digitalcollectionsManage($data);
+  return "TEST";
+}
+
+
+
+
+
+
+
+/**
+ * Menu callback; presents the search form and/or search results.
+ *
+ * @param $module
+ *   Search module to use for the search.
+ * @param $keys
+ *   Keywords to use for the search.
+ */
+function solrsearch_view($keys = '') {
+  $keys = trim($keys);
+  // Also try to pull search keywords out of the $_REQUEST variable to
+  // support old GET format of searches for existing links.
+  if (!$keys && !empty($_REQUEST['keys'])) {
+    $keys = trim($_REQUEST['keys']);
+  }
+
+
+
+
+  // Default results output is an empty string.
+  $results = array('#markup' => '');
+  // Process the search form. Note that if there is $_POST data,
+  // search_form_submit() will cause a redirect to search/[module path]/[keys],
+  // which will get us back to this page callback. In other words, the search
+  // form submits with POST but redirects to GET. This way we can keep
+  // the search query URL clean as a whistle.
+
+  if (empty($_POST['form_id']) || $_POST['form_id'] != 'solrsearch_form') {
+
+    $conditions =  NULL;
+
+    $conditions = solrsearch_search_conditions($keys);
+
+    // Only search if there are keywords or non-empty conditions.
+    if ($keys || !empty($conditions)) {
+
+      // Log the search keys.
+      //watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys));
+
+      // Collect the search results.
+      $results = solrsearch_data($keys, 'solrsearch_search', $conditions);
+
+    }
+  }
+
+  // The form may be altered based on whether the search was run.
+
+  $wrapper = array(
+      '#type' => 'container',
+      '#attributes' => array(
+          'class' => array('tool','box'),
+      ),
+  );
+
+
+
+  $wrapper['form'] = drupal_get_form('solrsearch_form', NULL, $keys);
+
+  $build['search_form'] = $wrapper;
+
+
+  $build['search_results'] = $results;
+
+
+
+  return $build;
+}
+
+/**
+ * Process variables for search-results.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $results: Search results array.
+ * - $module: Module the search results came from (module implementing
+ *   hook_search_info()).
+ *
+ * @see search-results.tpl.php
+ */
+function template_preprocess_solrsearch_results(&$variables) {
+
+
+  $variables['search_results'] = '';
+  if (!empty($variables['module'])) {
+    $variables['module'] = check_plain($variables['module']);
+  }
+
+
+
+
+  if (user_access("manage private collections")){
+    $variables['digitalobjects_items_select']=base_path()."digitalcollections/manageCurrent";
+    $variables['search_results'] .='<tr><td colspan="5"><input type="submit" value="add selected to current collection">';
+    $variables['search_results'] .='<input type="hidden" name="redirect" value="' . urlencode(current_path() .'?' . $_SERVER['QUERY_STRING']) . '"></td></tr>';
+
+
+    $sw = explode("/",current_path());
+    $key= $sw[sizeof($sw)-1];
+
+    $variables['search_results'] .='<tr><td colspan="5"><a href="../solrsearchsave/' . $key  .'?' . $_SERVER['QUERY_STRING']. '">Save all (max 500) to the current collection</a>';
+  }
+
+  foreach ($variables['results'] as $result) {
+
+
+
+
+    $variables['search_results'] .= theme('solrsearch_result', array('result' => $result, 'module' => $variables['module']));
+
+    /* add checkboxes for the item */
+    $variables['search_results'] .= '<div class="tools">';
+
+    if (user_access("manage private collections")){
+    $variables['search_results'] .= '<input type="checkbox" name="digitalobjects_items_select[]" value="'.$result['mpiwg-dri'].'"/>';
+    }
+
+    /* add tools for the item */
+      $variables['search_results'] .= theme('digitalobjects_item_tools', array('objid' => $result['mpiwg-dri'])) . "</div></div>";
+
+
+
+  }
+  $variables['pager'] = theme('pager', array('tags' => array('<<','<','...','>','>>'),'quantity' => 5));
+
+  $variables['description'] = '';
+  $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module'];
+}
+
+
+function check_array_plain($result,$field){
+  if (!isset($result[$field])){
+    return null;
+  }
+
+   $string = $result[$field];
+
+    if (is_array($string)){
+      return check_plain(implode($string));
+    } else {
+      return check_plain($string);
+  }
+}
+/**
+ * Process variables for search-result.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $result
+ * - $module
+ *
+ * @see search-result.tpl.php
+ */
+function template_preprocess_solrsearch_result(&$variables) {
+  
+
+  $result = $variables['result'];
+  //dpm($variables);
+  $variables['url'] = create_url_from_dri($result['mpiwg-dri'],$result['access-type']);
+  $variables['access_type'] = $result['access-type']!="free" ? "restricted" :$result['access-type'];
+  $variables['title'] = check_plain($result['title']);
+  $variables['author'] = check_plain($result['author']);
+  $variables['date'] = check_array_plain($result,'date');
+  $variables['signature'] = check_array_plain($result,'IM_call-number');
+  $variables['doc_type'] = check_array_plain($result,'doc-type');
+  $variables['author'] = check_plain($result['author']);
+  $variables['year'] = is_array($result['year']) ? implode($result['year']) :$result['year'];
+  $variables['thumburl'] = create_thumburl_from_dri($result['mpiwg-dri']);
+  // Check for existence. User search does not include snippets.
+  $variables['snippet'] = isset($result['snippet']) ? $result['snippet'] : '';
+
+  $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module'];
+
+  $idstring = str_replace("INST:", "", $result['archive-path']);
+  $path = create_library_path($idstring);
+
+  $variables['librarySearchPath'] = $path; //archive- path enthaelt die ID im Inst. Katalog
+
+  $variables['collectionPath'] = $GLOBALS['base_url'] . str_replace("ECHOCOLL:", "/node/", $result['archive-path']);
+  $variables['content'] = $result['content'];
+
+  $variables['external_url']=$result['url'][0];
+  $variables['external_image']=$result['image'][0];
+  $variables['provider']=$result['provider'][0];
+  $variables['dataSource']=$result['dataSource'];
+
+
+}
+
+
+
+/**
+ * As the search form collates keys from other modules hooked in via
+ * hook_form_alter, the validation takes place in _submit.
+ * search_form_validate() is used solely to set the 'processed_keys' form
+ * value for the basic search form.
+ */
+function solrsearch_form_validate($form, &$form_state) {
+  form_set_value($form['basic']['processed_keys'], trim($form_state['values']['keys']), $form_state);
+}
+
+/**
+ * Process a search form submission.
+ */
+function solrsearch_form_submit($form, &$form_state) {
+
+
+  $keys = $form_state['values']['processed_keys'];
+  if ($keys == '') {
+    form_set_error('keys', t('Please enter some keywords.'));
+    // Fall through to the form redirect.
+  }
+
+  $exact = $form_state['values']['exact'];
+  if ($exact==0) { #nicht genaue suche dann trunkiere das wort
+   $keys .="*";
+  }
+  $form_state['redirect'] = $form_state['action'] . '/' . $keys;
+}
+
+
+/**
+ * Builds a search form.
+ *
+ * @param $action
+ *   Form action. Defaults to "search/$path", where $path is the search path
+ *   associated with the module in its hook_search_info(). This will be
+ *   run through url().
+ * @param $keys
+ *   The search string entered by the user, containing keywords for the search.
+ * @param $module
+ *   The search module to render the form for: a module that implements
+ *   hook_search_info(). If not supplied, the default search module is used.
+ * @param $prompt
+ *   Label for the keywords field. Defaults to t('Enter your keywords') if NULL.
+ *   Supply '' to omit.
+ *
+ * @return
+ *   A Form API array for the search form.
+ */
+function solrsearch_form($form, &$form_state, $action = '', $keys = '', $module = NULL, $prompt = NULL) {
+  $module_info = FALSE;
+
+  if (!$action) {
+    $action = 'solrsearch';
+  }
+  if (!isset($prompt)) {
+    $prompt = t('Enter your keywords');
+
+  }
+
+  if ($keys == ''){
+    $keys=$prompt;
+  }
+
+  $form['#action'] = url($action);
+
+  // Record the $action for later use in redirecting.
+  $form_state['action'] = $action;
+  $form['#attributes']['class'][] = 'search-form';
+  $form['module'] = array('#type' => 'value', '#value' => $module);
+  $form['basic'] = array('#type' => 'container', '#attributes' => array('class' => array('container-inline','searchbox')));
+
+  $form['basic']['keys'] = array(
+      '#type' => 'textfield',
+      '#size' => $prompt ? 40 : 20,
+      '#attributes' =>array('placeholder' => t($keys),'class' => array('text')),
+      '#maxlength' => 255,
+  );
+  // processed_keys is used to coordinate keyword passing between other forms
+  // that hook into the basic search form.
+  $form['basic']['processed_keys'] = array('#type' => 'value', '#value' => '');
+
+
+  $form['basic']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('>'),
+      '#attributes' =>array('class' => array('submit')),
+
+  );
+
+  $form['basic']['exact'] = array(
+      '#type' => 'checkbox',
+      '#title' => 'whole word',
+
+
+
+  );
+
+
+  return $form;
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search.admin.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,1197 @@
+<?php
+
+/**
+ * @file
+ *   Administrative settings for searching.
+ */
+
+/**
+ * Helper function for empty search configuration.
+ */
+function _solrsearch_search_browse_form($default_value) {
+  $description = t('This is what is shown when the user enters an empty search, or removes all filters from an active search.') . ' ';
+  if (!module_exists('facetapi')) {
+    $description .= t('<strong>Facets will not be shown until you enable Facet API module.</strong>');
+  }
+  else {
+    $description .= t('Remember to configure the facets on the <a href="!facetslink">search environment page</a> and assign blocks to regions on the <a href="!blocklink">block settings page</a>', array(
+      '!facetslink' => url('admin/config/search/solrsearch/settings/'),
+      '!blocklink' => url('admin/structure/block'),
+    ));
+  }
+  return array(
+    '#type' => 'radios',
+    '#title' => t('Behavior on empty search'),
+    '#options' => array(
+      'none' => t("Show search box"),
+      'browse'  => t("Show enabled facets' blocks under the search box"),
+      'blocks'  => t("Show enabled facets' blocks in their configured regions"),
+      'results' => t("Show enabled facets' blocks in their configured regions and first page of all available results"),
+    ),
+    '#default_value' => $default_value,
+    '#description' => $description,
+  );
+}
+
+/**
+ * Menu callback for the overview page showing custom search pages and blocks.
+ * @return array $build
+ */
+function solrsearch_search_page_list_all() {
+
+  $build['pages'] = solrsearch_search_page_list_pages();
+  $build['blocks'] = solrsearch_search_page_list_blocks();
+  return $build;
+
+}
+
+
+/**
+ * Listing of all the search pages
+ * @return array $build
+ */
+function solrsearch_search_page_list_pages() {
+  $build = array();
+  $rows = array();
+  $rows['core_solr_search'] = array();
+
+  // Build the sortable table header.
+  $header = array(
+    'label' => array('data' => t('Name'), 'field' => 's.label'),
+    'path' => array('data' => t('Path'), 'field' => 's.search_path'),
+    'environment' => array('data' => t('Search environment')),
+    'operations' => array('data' => t('Operations')),
+  );
+
+  $search_pages = solrsearch_search_load_all_search_pages();
+  $default_search_page = solrsearch_search_default_search_page();
+  foreach ($search_pages as $search_page) {
+    $row = array();
+
+    // Add the label
+    $label = check_plain($search_page['label']);
+    // Is this row our default environment?
+    if ($search_page['page_id'] == $default_search_page) {
+      $label = t('!search_page <em>(Default)</em>', array('!search_page' => $label));
+    }
+
+    $row[] = $label;
+    // Add the link
+    $row[] = array(
+      'data' => array(
+        '#type' => 'link',
+        '#title' => $search_page['search_path'],
+        '#href' => $search_page['search_path'],
+      ),
+    );
+
+    // Add the search environment
+    $environment = solrsearch_environment_load($search_page['env_id']);
+    $row[] = $environment ? check_plain($environment['name']) : check_plain(t('<Disabled>'));
+    // Operations
+    $row[] = array('data' => l(t('Edit'), 'admin/config/search/solrsearch/search-pages/' . $search_page['page_id'] . '/edit'));
+    $row[] = array('data' => l(t('Clone'), 'admin/config/search/solrsearch/search-pages/' . $search_page['page_id'] . '/clone'));
+
+    // Allow to revert a search page or to delete it
+    if (!isset($search_page['settings']['solrsearch_search_not_removable']) && !isset($search_page['in_code_only'])) {
+      if ((isset($search_page['type']) && $search_page['type'] == 'Overridden')) {
+        $row[] = array('data' => l(t('Revert'), 'admin/config/search/solrsearch/search-pages/' . $search_page['page_id'] . '/delete'));
+      } else {
+        $row[] = array('data' => l(t('Delete'), 'admin/config/search/solrsearch/search-pages/' . $search_page['page_id'] . '/delete'));
+      }
+    }
+    else {
+      $row[] = '';
+    }
+    $rows[$search_page['page_id']] = $row;
+  }
+
+  // Automatically enlarge our header with the operations size
+  $header['operations']['colspan'] = count(reset($rows)) - 3;
+
+  $build['list'] = array(
+    '#prefix' => '<h3>Pages</h3>',
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => array_values($rows),
+    '#empty' => t('No available search pages.'),
+  );
+  $build['pager'] = array(
+    '#theme' => 'pager',
+    '#quantity' => 20,
+    '#weight' => 10,
+  );
+
+  return $build;
+}
+
+/**
+ * Listing of all the search blocks
+ * @return array $build
+ */
+function solrsearch_search_page_list_blocks() {
+  $build = array();
+  $rows = array();
+
+  // Build the sortable table header.
+  $header = array(
+    'label' => array('data' => t('Name'), 'field' => 's.label'),
+    'environment' => array('data' => t('Search environment')),
+    'operations' => array('data' => t('Operations')),
+  );
+
+  $search_blocks = variable_get('solrsearch_search_mlt_blocks', array());
+  foreach ($search_blocks as $search_block_id => $search_block) {
+    $row = array();
+
+    // Add the label
+    $label = check_plain($search_block['name']);
+    $row[] = $label;
+
+    // Add the search environment
+    $environment = solrsearch_environment_load($search_block['mlt_env_id']);
+    $row[] = $environment ? check_plain($environment['name']) : check_plain(t('<Disabled>'));
+    // Operations
+    if (module_exists('block')) {
+      $row[] = array('data' => l(t('Configure'), 'admin/structure/block/manage/solrsearch_search/' . $search_block_id . '/configure', array('query' => array('destination' => current_path()))));
+    }
+    $row[] = array('data' => l(t('Delete'), 'admin/config/search/solrsearch/search-pages/block/' . $search_block_id . '/delete'));
+    $rows[$search_block_id] = $row;
+  }
+
+  // Automatically enlarge our header with the operations size
+  $header['operations']['colspan'] = count(reset($rows)) - 2;
+
+  $build['list'] = array(
+    '#prefix' => '<h3>Blocks "More Like This"</h3>',
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => array_values($rows),
+    '#empty' => t('No available search blocks.'),
+  );
+  $build['pager'] = array(
+    '#theme' => 'pager',
+    '#quantity' => 20,
+    '#weight' => 10,
+  );
+
+  return $build;
+}
+
+/**
+ * Menu callback/form-builder for the form to create or edit a search page.
+ */
+function solrsearch_search_page_settings_form($form, &$form_state, $search_page = NULL) {
+  $environments = solrsearch_load_all_environments();
+  $options = array('' => t('<Disabled>'));
+  foreach ($environments as $id => $environment) {
+    $options[$id] = $environment['name'];
+  }
+  // Validate the env_id.
+  if (!empty($search_page['env_id']) && !solrsearch_environment_load($search_page['env_id'])) {
+    $search_page['env_id'] = '';
+  }
+
+  // Initializes form with common settings.
+  $form['search_page'] = array(
+      '#type' => 'value',
+      '#value' => $search_page,
+  );
+
+  $form['label'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Label'),
+    '#description' => '',
+    '#required' => TRUE,
+    '#size' => 30,
+    '#maxlength' => 32,
+    '#default_value' => !empty($search_page['label']) ? $search_page['label'] : '',
+    '#description' => t('The human-readable name of the search page configuration.'),
+  );
+
+  $form['page_id'] = array(
+    '#type' => 'machine_name',
+    '#maxlength' => 32,
+    '#required' => TRUE,
+    '#machine_name' => array(
+      'exists' => 'solrsearch_search_page_exists',
+      'source' => array('label'),
+    ),
+    '#description' => '',
+    '#default_value' => !empty($search_page['page_id']) ? $search_page['page_id'] : '',
+    '#disabled' => !empty($search_page),
+    '#description' => t('A unique machine-readable identifier for the search page configuration. It must only contain lowercase letters, numbers, and underscores.'),
+  );
+
+  $form['description_enable'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Description'),
+    '#default_value' => !empty($search_page['description'])  ? TRUE : FALSE
+  );
+
+  $form['description'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Provide description'),
+    '#title_display' => 'invisible',
+    '#size' => 64,
+    '#default_value' => !empty($search_page['description']) ? $search_page['description'] : '',
+    '#dependency' => array(
+      'edit-description-enable' => array(1),
+    ),
+  );
+
+  $is_default = FALSE;
+  if (!empty($search_page)) {
+    $is_default = $search_page['page_id'] == solrsearch_search_default_search_page();
+  }
+  $form['make_default'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Make this Solr Search Page the default'),
+    '#description' => t('Useful for eg. making facets to link to this page when they are shown on non-search pages'),
+    '#default_value' => $is_default,
+    '#disabled' => $is_default,
+  );
+
+  $form['info'] = array(
+    '#title' => t('Search Page Information'),
+    '#type' => 'fieldset',
+    '#collapsible' => FALSE,
+    '#prefix' => '<div id="dynamic-search-page">',
+    '#suffix' => '</div>',
+  );
+
+  $core_solr_search = FALSE;
+  if (!empty($search_page['page_id']) && ($search_page['page_id'] == 'core_solr_search')) {
+    $core_solr_search = TRUE;
+  }
+  if ($core_solr_search) {
+    $description = t('This page always uses the current default search environment');
+  }
+  else {
+    $description = t('The environment that is used by this search page. If no environment is selected, this page will be disabled.');
+  }
+
+  $form['info']['env_id'] = array(
+    '#title' => t('Search environment'),
+    '#type' => 'select',
+    '#options' => $options,
+    '#default_value' => !empty($search_page['env_id']) ? $search_page['env_id'] : '',
+    '#disabled' => $core_solr_search,
+    '#description' => $description,
+  );
+
+  $form['info']['page_title'] = array(
+    '#title' => t('Title'),
+    '#type' => 'textfield',
+    '#required' => TRUE,
+    '#maxlength' => 255,
+    '#description' => 'You can use %value to place the search term in the title',
+    '#default_value' => !empty($search_page['page_title']) ? $search_page['page_title'] : '',
+  );
+
+  $search_types = solrsearch_search_load_all_search_types();
+  $options = array('custom' => t('Custom Field'));
+  foreach ($search_types as $id => $search_type) {
+    $options[$id] = $search_type['name'];
+  }
+
+  $form['info']['search_type'] = array(
+    '#title' => t('Search Type'),
+    '#type' => 'select',
+    '#options' => $options,
+    '#default_value' => !empty($search_page['settings']['solrsearch_search_search_type']) ? $search_page['settings']['solrsearch_search_search_type'] : '',
+    '#access' => !$core_solr_search,
+    '#description' => t('Use this only when filtering on a value from the search path.
+      For example, select Taxonomy Term to filter on a term ID (search/taxonomy/%).'),
+    '#ajax' => array(
+      'callback' => 'solrsearch_search_ajax_search_page_default',
+      'wrapper' => 'dynamic-search-page',
+      'method' => 'replace',
+    ),
+  );
+
+  // Token element validate is added to validate the specific
+  // tokens that are allowed
+  $form['info']['search_path'] = array(
+    '#title' => t('Path'),
+    '#type' => 'textfield',
+    '#required' => TRUE,
+    '#maxlength' => 255,
+    '#description' => t('For example: search/my-search-page. Search keywords will appear at the end of the path.'),
+    '#default_value' => !empty($search_page['search_path']) ? $search_page['search_path'] : '',
+  );
+  if (!$core_solr_search) {
+    $form['info']['search_path']['#description'] .= ' ' . t('You can use one % to make the search page dynamic.');
+  }
+
+  $form['info']['custom_filter_enable'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Custom Filter'),
+    '#default_value' => !empty($search_page['settings']['solrsearch_search_custom_enable'])  ? TRUE : FALSE
+  );
+
+  $form['info']['filters'] = array(
+    '#title' => t('Custom filters'),
+    '#type' => 'textfield',
+    '#required' => FALSE,
+    '#maxlength' => 255,
+    '#description' => t('A comma-separated list of lucene filter queries to apply by default.'),
+    '#default_value' => !empty($search_page['settings']['fq'])  ? implode(', ', $search_page['settings']['fq']) : '',
+    '#dependency' => array(
+      'edit-custom-filter-enable' => array(1),
+      'edit-search-type' => array('custom'),
+    ),
+  );
+  if (!$core_solr_search) {
+    $form['info']['filters']['#description'] .= ' ' . t('E.g. "bundle:blog, is_uid:(1 OR 2 OR %). % will be replaced by the value of % in the path"');
+  }
+
+  $form['advanced'] = array(
+    '#title' => t('Advanced Search Page Options'),
+    '#type' => 'fieldset',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#tree' => TRUE,
+  );
+
+  // Results per page per search page
+  $default_value = isset($search_page['settings']['solrsearch_search_per_page']) ? $search_page['settings']['solrsearch_search_per_page'] : '10';
+  $form['advanced']['solrsearch_search_per_page'] = array(
+    '#type' => 'textfield',
+    '#size' => 3,
+    '#title' => t('Results per page'),
+    '#description' => t('How many items will be displayed on one page of the search result.'),
+    '#default_value' => $default_value,
+  );
+
+  // Enable/disable spellcheck on pages
+  $default_value = isset($search_page['settings']['solrsearch_search_spellcheck']) ? $search_page['settings']['solrsearch_search_spellcheck'] : TRUE;
+  $form['advanced']['solrsearch_search_spellcheck'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable spell check'),
+    '#description' => t('Display "Did you mean … ?" above search results.'),
+    '#default_value' => $default_value,
+  );
+
+  // Enable/disable search form on search page (replaced by a block perhaps)
+  $default_value = isset($search_page['settings']['solrsearch_search_search_box']) ? $search_page['settings']['solrsearch_search_search_box'] : TRUE;
+  $form['advanced']['solrsearch_search_search_box'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable the search box on the page'),
+    '#description' => t('Display a search box on the page.'),
+    '#default_value' => $default_value,
+  );
+
+  // Enable/disable search form on search page (replaced by a block perhaps)
+  $default_value = isset($search_page['settings']['solrsearch_search_allow_user_input']) ? $search_page['settings']['solrsearch_search_allow_user_input'] : FALSE;
+  $form['advanced']['solrsearch_search_allow_user_input'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Allow user input using the URL'),
+    '#description' => t('Allow users to use the URL for manual facetting via fq[] params (e.g. http://example.com/search/site/test?fq[]=uid:1&fq[]=tid:99). This will only work in combination with a keyword search. The recommended value is unchecked'),
+    '#default_value' => $default_value,
+  );
+
+  // Use the main search page setting as the default for new pages.
+  $default_value = isset($search_page['settings']['solrsearch_search_browse']) ? $search_page['settings']['solrsearch_search_browse'] : 'browse';
+  $form['advanced']['solrsearch_search_browse'] = _solrsearch_search_browse_form($default_value);
+
+  // Button for the corresponding actions
+  $form['actions'] = array(
+    '#type' => 'actions',
+  );
+
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#redirect' => 'admin/config/search/solrsearch/search-pages',
+    '#value' => t('Save'),
+  );
+  $form['actions']['submit_edit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save and edit'),
+  );
+
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => t('Cancel'),
+    '#href' => 'admin/config/search/solrsearch/search-pages',
+  );
+
+  $form['#submit'][] = 'solrsearch_search_page_settings_form_submit';
+
+  return $form;
+}
+
+/**
+ * Callback element needs only select the portion of the form to be updated.
+ * Since #ajax['callback'] return can be HTML or a renderable array (or an
+ * array of commands), we can just return a piece of the form.
+ */
+function solrsearch_search_ajax_search_page_default($form, $form_state, $search_page = NULL) {
+
+  $search_page = $form_state['values']['search_page'];
+  $search_types = solrsearch_search_load_all_search_types();
+
+  // Helping with sensible defaults for the search path
+  $default_search_path = '';
+  if (!empty($form_state['values']['search_type']) && $form_state['values']['search_type'] != 'custom') {
+    $default_search_path = $search_types[$form_state['values']['search_type']]['default menu'];
+    $form['info']['search_path']['#value'] = $default_search_path;
+  }
+
+  // Helping with sensible defaults for the search title
+  $default_search_title = '';
+
+  if (empty($form_state['values']['page_title']) && $form_state['values']['search_type'] != 'custom') {
+    $default_search_title_callback = $search_types[$form_state['values']['search_type']]['title callback'];
+    $default_search_title = $default_search_title_callback();
+    $form['info']['page_title']['#value'] = $default_search_title;
+  }
+  return $form['info'];
+}
+
+function solrsearch_search_page_settings_form_validate($form, &$form_state) {
+  // Performs basic validation of the menu path.
+  if (url_is_external($form_state['values']['search_path'])) {
+    form_set_error('search_path', t('Path must be local.'));
+  }
+  $form_state['values']['search_path'] = trim($form_state['values']['search_path'], '/');
+  if (empty($form_state['values']['search_path'])) {
+    form_set_error('search_path', t('Path required.'));
+  }
+  if (!is_numeric($form_state['values']['advanced']['solrsearch_search_per_page'])) {
+    form_set_error('advanced][solrsearch_search_per_page', t('The amount of search results must be an integer.'));
+  }
+  $form_state['values']['advanced']['solrsearch_search_per_page'] = (int) $form_state['values']['advanced']['solrsearch_search_per_page'];
+  if (empty($form_state['values']['advanced']['solrsearch_search_per_page'])) {
+    form_set_error('advanced][solrsearch_search_per_page', t('The amount of search results cannot be empty.'));
+  }
+  if (count(explode('%', $form_state['values']['search_path'])) > 2) {
+    form_set_error('search_path', t('Only one % placeholder is allowed.'));
+  }
+}
+
+/**
+ * Processes solrsearch_search_page_settings_form form submissions.
+ */
+function solrsearch_search_page_settings_form_submit($form, &$form_state) {
+  $settings = array();
+  $settings['fq'] = array();
+  if ($form_state['values']['filters']) {
+    foreach (explode(',', $form_state['values']['filters']) as $string) {
+      $string = trim($string);
+      // Minimal validation.  ':' must exist and can't be the 1st char..
+      if (strpos($string, ':')) {
+        $settings['fq'][] = $string;
+      }
+    }
+  }
+  $settings['solrsearch_search_custom_enable'] = $form_state['values']['custom_filter_enable'];
+  $settings['solrsearch_search_search_type'] = $form_state['values']['search_type'];
+  // Add all advanced settings.
+  $settings += $form_state['values']['advanced'];
+
+  // Set the default search page settings
+  if (!empty($form_state['values']['make_default']) && isset($form_state['values']['page_id'])) {
+    variable_set('solrsearch_search_default_search_page', $form_state['values']['page_id']);
+  }
+
+  $search_page = array();
+  $search_page['page_id'] = $form_state['values']['page_id'];
+  $search_page['label'] = $form_state['values']['label'];
+  $search_page['description'] = $form_state['values']['description'];
+  $search_page['env_id'] = $form_state['values']['env_id'];
+  $search_page['search_path'] = $form_state['values']['search_path'];
+  $search_page['page_title'] = $form_state['values']['page_title'];
+  $search_page['settings'] = $settings;
+  solrsearch_search_page_save($search_page);
+
+  // Saves our values in the database, sets redirect path on success.
+  drupal_set_message(t('The configuration options have been saved for %page.', array('%page' => $form_state['values']['label'])));
+  if (isset($form_state['clicked_button']['#redirect'])) {
+    $form_state['redirect'] = $form_state['clicked_button']['#redirect'];
+  }
+  else {
+    $form_state['redirect'] = current_path();
+  }
+  // Regardlessly of the destination parameter we want to go to another page
+  unset($_GET['destination']);
+  drupal_static_reset('drupal_get_destination');
+  drupal_get_destination();
+  // Menu rebuild needed to pick up search path.
+  menu_rebuild();
+}
+
+/**
+ * Deletes a single search page configuration.
+ */
+function solrsearch_search_delete_search_page_confirm($form, &$form_state, $search_page) {
+
+  // Sets values required for deletion.
+  $form['page_id'] = array('#type' => 'value', '#value' => $search_page['page_id']);
+  $form['label'] = array('#type' => 'value', '#value' => $search_page['label']);
+
+  if (isset($search_page['export_type']) && $search_page['export_type'] == '3') {
+    $verb = t('Revert');
+  }
+  else {
+    $verb = t('Delete');
+  }
+
+  // Sets the message, or the title of the page.
+  $message = t(
+    'Are you sure you want to !verb the %label search page configuration?',
+    array('%label' => $form['label']['#value'], '!verb' => strtolower($verb))
+  );
+
+
+  // Builds caption.
+  $caption = '<p>';
+  $caption .= t(
+    'The %label search page configuration will be deleted.',
+    array('%label' => $form['label']['#value'])
+  );
+  $caption .= '</p>';
+  $caption .= '<p><strong>' . t('This action cannot be undone.') . '</strong></p>';
+
+  // Finalizes and returns the confirmation form.
+  $return_path = 'admin/config/search/solrsearch/search-pages';
+  $button_text = $verb;
+  if (!isset($search_page['settings']['solrsearch_search_not_removable'])) {
+    return confirm_form($form, filter_xss($message), $return_path, filter_xss($caption), check_plain($button_text));
+  }
+  else {
+    // Maybe this should be solved somehow else
+    drupal_access_denied();
+  }
+}
+
+/**
+ * Process content type delete confirm submissions.
+ */
+function solrsearch_search_delete_search_page_confirm_submit($form, &$form_state) {
+  // Deletes the index configuration settings.
+  // @todo Invoke a hook that allows backends and indexers to delete their stuff.
+  db_delete('solrsearch_search_page')
+    ->condition('page_id', $form_state['values']['page_id'])
+    ->execute();
+
+  // Sets message, logs action.
+  drupal_set_message(t(
+    'The %label search page configuration has been deleted.',
+    array('%label' => $form_state['values']['label'])
+  ));
+  watchdog('solrsearch_search', 'Deleted search page configuration "@page_id".', array('@page_id' => $form_state['values']['page_id']), WATCHDOG_NOTICE);
+
+  // Rebuilds the menu cache.
+  menu_rebuild();
+
+  // Returns back to search page list page.
+  $form_state['redirect'] = 'admin/config/search/solrsearch/search-pages';
+}
+
+/**
+ * Clones a single search page configuration
+ * @param $search_page
+ *   The search page that needs to be cloned
+ */
+function solrsearch_search_clone_search_page_confirm($form, &$form_state, $search_page) {
+  $form['page_id'] = array(
+    '#type' => 'value',
+    '#value' => $search_page['page_id'],
+  );
+  return confirm_form(
+    $form,
+    t('Are you sure you want to clone search page %name?', array('%name' => $search_page['label'])),
+    'admin/config/search/solrsearch',
+    '',
+    t('Clone'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Submits the confirmations of the cloning of a search page
+ */
+function solrsearch_search_clone_search_page_confirm_submit($form, &$form_state) {
+  if (solrsearch_search_page_clone($form_state['values']['page_id'])) {
+    drupal_set_message(t('The search page was cloned'));
+  }
+  $form_state['redirect'] = 'admin/config/search/solrsearch/search-pages';
+}
+
+/**
+ * Menu callback - the settings form.
+ */
+function solrsearch_search_get_fields($environment = NULL) {
+  if (empty($environment)) {
+    $env_id = solrsearch_default_environment();
+    $environment = solrsearch_environment_load($env_id);
+  }
+  $env_id = $environment['env_id'];
+
+  // Try to fetch the schema fields.
+  try {
+    $solr = solrsearch_get_solr($env_id);
+    $fields = $solr->getFields();
+    return $fields;
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    drupal_set_message(nl2br(check_plain($e->getMessage())), 'warning');
+    drupal_set_message(t('Cannot get information about the fields in the index.'), 'warning');
+  }
+}
+
+/**
+ * Menu callback - Bias settings form.
+ */
+function solrsearch_bias_settings_page($environment = NULL) {
+  if (empty($environment)) {
+    $env_id = solrsearch_default_environment();
+    $environment = solrsearch_environment_load($env_id);
+  }
+  $env_id = $environment['env_id'];
+
+  // Initializes output with information about which environment's setting we are
+  // editing, as it is otherwise not transparent to the end user.
+  $output = array(
+    'solrsearch_environment' => array(
+      '#theme' => 'solrsearch_settings_title',
+      '#env_id' => $env_id,
+    ),
+  );
+
+  // Adds content bias and type boost forms.
+  $fields = solrsearch_search_get_fields($environment);
+  $form = array();
+  $form = drupal_get_form('solrsearch_search_bias_form', $env_id, $fields);
+  $output['bias_forms'] = $form;
+  return $output;
+}
+
+function solrsearch_search_bias_form($form, &$form_state, $env_id, $fields) {
+  $form['#env_id'] = $env_id;
+  $form['bias_tabs'] = array(
+    '#type' => 'vertical_tabs',
+  );
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save configuration'),
+    '#submit' => array('solrsearch_search_bias_form_submit'),
+  );
+  $form['actions']['reset'] = array(
+    '#type' => 'submit',
+    '#value' => t('Reset to defaults'),
+    '#submit' => array('solrsearch_search_bias_form_reset'),
+  );
+  $form += solrsearch_search_result_bias_form($env_id);
+  $form += solrsearch_search_type_boost_form($env_id);
+  $form += solrsearch_search_field_bias_form($fields, $env_id);
+  return $form;
+}
+
+function solrsearch_search_bias_form_submit(&$form, &$form_state) {
+  // Exclude unnecessary elements.
+  form_state_values_clean($form_state);
+  foreach ($form_state['values'] as $key => $value) {
+    if (is_array($value) && isset($form_state['values']['array_filter'])) {
+      $value = array_keys(array_filter($value));
+    }
+    // There is no need to set default variable values.
+    if (!isset($form[$key]['#default_value']) || $form[$key]['#default_value'] != $value) {
+      switch ($key) {
+        case 'solrsearch_search_sticky_boost' :
+        case 'solrsearch_search_promote_boost' :
+        case 'solrsearch_search_date_boost' :
+        case 'solrsearch_search_comment_boost' :
+        case 'solrsearch_search_changed_boost' :
+        case 'solrsearch_search_type_boosts' :
+        case 'field_bias' :
+          solrsearch_environment_variable_set($form['#env_id'], $key, $value);
+      }
+    }
+  }
+  drupal_set_message(t('The configuration options have been saved.'));
+}
+
+function solrsearch_search_bias_form_reset($form, &$form_state) {
+  // Exclude unnecessary elements.
+  form_state_values_clean($form_state);
+
+  foreach ($form_state['values'] as $key => $value) {
+    solrsearch_environment_variable_del($form['#env_id'], $key);
+  }
+  drupal_set_message(t('The configuration options have been reset to their default values.'));
+}
+
+/**
+ * Form builder function to set date, comment, etc biases.
+ */
+function solrsearch_search_result_bias_form($env_id) {
+
+  $date_settings = solrsearch_environment_variable_get($env_id, 'solrsearch_search_date_boost', '0:0');
+  $comment_settings = solrsearch_environment_variable_get($env_id, 'solrsearch_search_comment_boost', '0:0');
+  $changed_settings = solrsearch_environment_variable_get($env_id, 'solrsearch_search_changed_boost', '0:0');
+  $sticky_boost = solrsearch_environment_variable_get($env_id, 'solrsearch_search_sticky_boost', '0');
+  $promote_boost = solrsearch_environment_variable_get($env_id, 'solrsearch_search_promote_boost', '0');
+
+  $options = array(
+    '10:2000.0' => '10',
+    '8:1000.0' => '9',
+    '8:700.0' => '8',
+    '8:500.0' => '7',
+    '4:300.0' => '6',
+    '4:200.0' => '5',
+    '4:150.0' => '4',
+    '2:150.0' => '3',
+    '2:100.0' => '2',
+    '1:100.0' => '1',
+    '0:0' => t('Ignore'),
+  );
+
+  $weights = drupal_map_assoc(array('21.0', '13.0', '8.0', '5.0', '3.0', '2.0', '1.0', '0.8', '0.5', '0.3', '0.2', '0.1'));
+  $weights['0'] = t('Ignore');
+
+  $form = array();
+  $form['result_bias'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Result biasing'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#description' => t('Give bias to certain properties when ordering the search results. Any value except <em>Ignore</em> will increase the score of the given type in search results. Choose <em>Ignore</em> to ignore any given property.'),
+    '#group' => 'bias_tabs',
+  );
+  $form['result_bias']['solrsearch_search_sticky_boost'] = array(
+    '#type' => 'select',
+    '#options' => $weights,
+    '#title' => t("Sticky at top of lists"),
+    '#default_value' => $sticky_boost,
+    '#description' => t("Select additional bias to give to nodes that are set to be 'Sticky at top of lists'."),
+  );
+  $form['result_bias']['solrsearch_search_promote_boost'] = array(
+    '#type' => 'select',
+    '#options' => $weights,
+    '#title' => t("Promoted to home page"),
+    '#default_value' => $promote_boost,
+    '#description' => t("Select additional bias to give to nodes that are set to be 'Promoted to home page'."),
+  );
+  $form['result_bias']['solrsearch_search_date_boost'] = array(
+    '#type' => 'select',
+    '#options' => $options,
+    '#title' => t("More recently created"),
+    '#default_value' => $date_settings,
+    '#description' => t('This setting will change the result scoring so that nodes created more recently may appear before those with higher keyword matching.'),
+  );
+  $form['result_bias']['solrsearch_search_comment_boost'] = array(
+    '#type' => 'select',
+    '#options' => $options,
+    '#title' => t("More comments"),
+    '#default_value' => $comment_settings,
+    '#description' => t('This setting will change the result scoring so that nodes with more comments may appear before those with higher keyword matching.'),
+  );
+  $form['result_bias']['solrsearch_search_changed_boost'] = array(
+    '#type' => 'select',
+    '#options' => $options,
+    '#title' => t("More recent comments"),
+    '#default_value' => $changed_settings,
+    '#description' => t('This setting will change the result scoring so that nodes with the most recent comments (or most recent updates to the node itself) may appear before those with higher keyword matching.'),
+  );
+  return $form;
+}
+
+/**
+ * Form builder function to set query field weights.
+ */
+function solrsearch_search_field_bias_form($fields, $env_id) {
+  $form = array();
+  // get the current weights
+  $defaults = array(
+    'content' => '1.0',
+    'ts_comments' => '0.5',
+    'tos_content_extra' => '0.1',
+    'label' => '5.0',
+    'tos_name' => '3.0',
+    'taxonomy_names' => '2.0',
+    'tags_h1' => '5.0',
+    'tags_h2_h3' => '3.0',
+    'tags_h4_h5_h6' => '2.0',
+    'tags_inline' => '1.0',
+    'tags_a' => '0',
+  );
+  $qf = solrsearch_environment_variable_get($env_id, 'field_bias', $defaults);
+  $weights = drupal_map_assoc(array('21.0', '13.0', '8.0', '5.0', '3.0', '2.0', '1.0', '0.8', '0.5', '0.3', '0.2', '0.1'));
+  $weights['0'] = t('Omit');
+  if (!$qf) {
+    $qf = $defaults;
+  }
+  if ($fields) {
+    $form['field_bias'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Field biases'),
+      '#collapsible' => TRUE,
+      '#collapsed' => FALSE,
+      '#tree' => TRUE,
+      '#description' => t('Specify here which fields are more important when searching. Give a field a greater numeric value to make it more important. If you omit a field, it will not be searched.'),
+      '#group' => 'bias_tabs',
+    );
+    foreach ($fields as $field_name => $field) {
+      // Only indexed feids are searchable.
+      if ($field->schema{0} == 'I') {
+        // By default we only show text fields.  Use hook_form_alter to change.
+        // We use filter_xss to make sure links are allowed
+        $form['field_bias'][$field_name] = array(
+          '#access' => ($field->type == 'text' || $field->type == 'text_und'),
+          '#type' => 'select',
+          '#options' => $weights,
+          '#title' => filter_xss(solrsearch_field_name_map($field_name)),
+          '#default_value' => isset($qf[$field_name]) ? $qf[$field_name] : '0',
+        );
+      }
+    }
+
+    // Make sure all the default fields are included, even if they have
+    // no indexed content.
+    foreach ($defaults as $field_name => $weight) {
+      $form['field_bias'][$field_name] = array(
+        '#type' => 'select',
+        '#options' => $weights,
+        '#title' => check_plain(solrsearch_field_name_map($field_name)),
+        '#default_value' => isset($qf[$field_name]) ? $qf[$field_name] : $defaults[$field_name],
+      );
+    }
+
+    ksort($form['field_bias']);
+  }
+  return $form;
+}
+
+/**
+ * Form builder function to set query type weights.
+ */
+function solrsearch_search_type_boost_form($env_id) {
+
+  $form['type_boost'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Type biasing'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+    '#group' => 'bias_tabs',
+  );
+  $form['type_boost']['solrsearch_search_type_boosts'] = array(
+    '#type' => 'item',
+    '#description' => t("Specify here which node types should get a higher relevancy score in searches. Any value except <em>Ignore</em> will increase the score of the given type in search results."),
+    '#tree' => TRUE,
+  );
+
+  $weights = drupal_map_assoc(array('21.0', '13.0', '8.0', '5.0', '3.0', '2.0', '1.0', '0.8', '0.5', '0.3', '0.2', '0.1'));
+  $weights['0'] = t('Ignore');
+
+  // Get the current boost values.
+  $type_boosts = solrsearch_environment_variable_get($env_id, 'solrsearch_search_type_boosts', array());
+  $names = array();
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    if (!empty($entity_info['solrsearch']['indexable'])) {
+      foreach ($entity_info['bundles'] as $key => $info) {
+        $names[$key] = $info['label'];
+      }
+    }
+  }
+  asort($names);
+
+  foreach ($names as $type => $name) {
+    $form['type_boost']['solrsearch_search_type_boosts'][$type] = array(
+      '#type' => 'select',
+      '#title' => t('%type type content bias', array('%type' => $name)),
+      '#options' => $weights,
+      '#default_value' => isset($type_boosts[$type]) ? $type_boosts[$type] : 0,
+    );
+  }
+
+  return $form;
+}
+
+/**
+ * MoreLikeThis administration and utility functions.
+ */
+function solrsearch_search_mlt_add_block_form() {
+  $form = solrsearch_search_mlt_block_form();
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => '5',
+  );
+  return $form;
+}
+
+function solrsearch_search_mlt_add_block_form_submit($form, &$form_state) {
+  solrsearch_search_mlt_save_block($form_state['values']);
+  $block_message = t('New More like this block created. <a href="!configure">Configure</a> this block in the Block administration', array('!configure' => url('admin/structure/block')));
+  drupal_set_message($block_message);
+  $form_state['redirect'] = 'admin/config/search/solrsearch/search-pages';
+}
+
+/**
+ * Merge supplied settings with the standard defaults..
+ */
+function solrsearch_search_mlt_block_defaults($block = array()) {
+  return $block + array(
+    'name' => '',
+    'num_results' => '5',
+    'mlt_fl' => array(
+      'label' => 'label',
+      'taxonomy_names' => 'taxonomy_names',
+    ),
+    'mlt_env_id' => 'solr',
+    'mlt_mintf' => '1',
+    'mlt_mindf' => '1',
+    'mlt_minwl' => '3',
+    'mlt_maxwl' => '15',
+    'mlt_maxqt' => '20',
+    'mlt_type_filters' => array(),
+    '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 solrsearch_search_mlt_get_fields() {
+  $rows = array();
+
+  try {
+    $solr = solrsearch_get_solr();
+    $fields = $solr->getFields();
+    foreach ($fields as $field_name => $field) {
+      if ($field->schema{4} == 'V') {
+        $rows[$field_name] = solrsearch_field_name_map($field_name);
+      }
+    }
+    ksort($rows);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+
+  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 solrsearch_search_mlt_save_block($block_settings = array(), $delta = NULL) {
+  $blocks = variable_get('solrsearch_search_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 = solrsearch_search_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]['delta'] = $delta;
+  $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('solrsearch_search_mlt_blocks', $blocks);
+}
+
+function solrsearch_search_mlt_delete_block_form($form, &$form_state, $block) {
+  if ($block) {
+    // Backwards compatibility for the block deltas
+    if (isset($block['delta'])) {
+      $delta = $block['delta'];
+    }
+    else {
+      $delta = arg(6);
+    }
+    // Add our delta to the delete form
+    $form['delta'] = array(
+      '#type' => 'value',
+      '#value' => $delta,
+    );
+    $question = t('Are you sure you want to delete the "More Like this" block %name?', array('%name' => $block['name']));
+    $path = 'admin/structure/block';
+    $description = t('The block will be deleted. This action cannot be undone.');
+    $yes = t('Delete');
+    $no = t('Cancel');
+    return confirm_form($form, filter_xss($question), $path, $description, $yes, $no);
+  }
+}
+
+function solrsearch_search_mlt_delete_block_form_submit($form, &$form_state) {
+  $blocks = solrsearch_search_load_all_mlt_blocks();
+
+  unset($blocks[$form_state['values']['delta']]);
+  variable_set('solrsearch_search_mlt_blocks', $blocks);
+  drupal_set_message(t('The block has been deleted.'));
+  $form_state['redirect'] = 'admin/config/search/solrsearch/search-pages';
+}
+
+/**
+ * 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.
+ * @todo Enable the user to specify a query, rather then forcing suggestions
+ *  based on the node id.
+ */
+function solrsearch_search_mlt_block_form($block_id = NULL) {
+
+  if (!empty($block_id)) {
+    $block = solrsearch_search_mlt_block_load($block_id);
+    if (!$block) {
+      return array();
+    }
+  }
+  else {
+    $block = solrsearch_search_mlt_block_defaults();
+  }
+
+  $form['delta'] = array(
+    '#type' => 'value',
+    '#default_value' => isset($block['delta']) ? $block['delta'] : '',
+    '#weight' => '-2',
+  );
+
+  $form['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Block name'),
+    '#description' => t('The block name displayed to site users.'),
+    '#required' => TRUE,
+    '#default_value' => isset($block['name']) ? $block['name'] : '',
+    '#weight' => '-2',
+  );
+
+  $environments = solrsearch_load_all_environments();
+  $options = array('' => t('<Disabled>'));
+  foreach ($environments as $id => $environment) {
+    $options[$id] = $environment['name'];
+  }
+  $form['mlt_env_id'] = array(
+    '#title' => t('Search environment'),
+    '#type' => 'select',
+    '#options' => $options,
+    '#default_value' => isset($block['mlt_env_id']) ? $block['mlt_env_id'] : solrsearch_default_environment(),
+  );
+
+  $form['num_results'] = array(
+    '#type' => 'select',
+    '#title' => t('Maximum number of related items to display'),
+    '#default_value' => isset($block['num_results']) ? $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" => solrsearch_field_name_map("taxonomy_names"), "%title" => solrsearch_field_name_map("label"))),
+    '#options' => solrsearch_search_mlt_get_fields(),
+    '#required' => TRUE,
+    '#default_value' => isset($block['mlt_fl']) ? $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' => isset($block['mlt_mintf']) ? $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' => isset($block['mlt_mindf']) ? $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' => isset($block['mlt_minwl']) ? $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' => isset($block['mlt_maxwl']) ? $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' => isset($block['mlt_maxqt']) ? $block['mlt_maxqt'] : '',
+  );
+*/
+  /*
+  $form['restrictions'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Filters'),
+    '#weight' => '1',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $type_options = array();
+  foreach (node_type_get_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(),
+    '#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. If none are selected, no filter will be applied.'),
+    '#weight' => '-2',
+  );
+
+  $form['restrictions']['mlt_custom_filters'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Additional Query'),
+    '#description' => t("A query, in Lucene syntax, which will further filter the similarity suggestions. For example, 'label:strategy' will filter related content further to only those with strategy in the title. Here are some more examples:") .
+                        '<ul>
+                            <li>ss_language:fr</li>
+                            <li>tid:(5 OR 7)</li>
+                            <li>ds_created:[2009-05-01T23:59:59Z TO 2009-07-28T12:30:00Z]</li>
+                            <li>-is_uid:0, -is_uid:1</li>
+                        </ul>',
+    '#required' => FALSE,
+    '#default_value' => isset($block['mlt_custom_filters']) ? $block['mlt_custom_filters'] : '',
+    '#weight' => '-1',
+  );
+*/
+  return $form;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search.info	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,21 @@
+name = solrsearch_search
+description = Frontend for solr II
+dependencies[] = search
+dependencies[] = solrsearch
+package = SolrSearch
+core = 7.x
+configure = admin/config/search/solrsearch/search-pages
+
+files[] = solrsearch_search.install
+files[] = solrsearch_search.module
+files[] = solrsearch_search_blocks.inc
+files[] = solrsearch_search.admin.inc
+files[] = solrsearch_search.pages.inc
+
+
+; Information added by drupal.org packaging script on 2013-03-15
+version = "7.x-1.1+34-dev"
+core = "7.x"
+project = "solrsearch"
+datestamp = "1363307665"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search.install	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,390 @@
+<?php
+
+/**
+ * @file
+ *   Install and related hooks for solrsearch_search.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function solrsearch_search_install() {
+  // Add a taxonomy search page to the database
+  $settings = array(
+    'solrsearch_search_search_type' => 'tid',
+    'solrsearch_search_per_page' => 10,
+    'solrsearch_search_browse' => 'results',
+    'solrsearch_search_spellcheck' => FALSE,
+    'solrsearch_search_search_box' => FALSE,
+  );
+  $settings = serialize($settings);
+
+  $fields = array(
+    'page_id' => 'taxonomy_search',
+    'label' => 'Taxonomy Search',
+    'description' => 'Search all items with given term',
+    'search_path' => 'taxonomy/term/%',
+    'env_id' => '',
+    'page_title' => '%value',
+    'settings' => $settings,
+  );
+  db_insert('solrsearch_search_page')->fields($fields)->execute();
+}
+
+/**
+ * Implements hook_enable().
+ */
+function solrsearch_search_enable() {
+  // Make sure the default core search page is installed.
+  //dpm("ENABLE");
+  $search_page = solrsearch_search_page_load('core_solr_search');
+  //dpm($search_page);
+  if (empty($search_page)) {
+    // Add Default search page (core search)
+    // This is a duplication from update_7004 but it is intended
+    // so future changes are possible without breaking the update
+    $settings = array(
+      'solrsearch_search_search_type' => 'custom',
+      'solrsearch_search_per_page' => 10,
+      'solrsearch_search_browse' => 'browse',
+      'solrsearch_search_spellcheck' => TRUE,
+      'solrsearch_search_not_removable' => TRUE,
+      'solrsearch_search_search_box' => TRUE,
+    );
+    $settings = serialize($settings);
+
+    $fields = array(
+      'page_id' => 'core_solr_search',
+      'label' => 'Core Solr Search',
+      'description' => 'Core SolrSearch',
+      'search_path' => 'solrsearch/site',
+      'env_id' => 'searchecho',
+      'page_title' => 'echo',
+      'settings' => $settings,
+    );
+    db_insert('solrsearch_search_page')->fields($fields)->execute();
+  }
+
+
+  #$active = variable_get('search_active_modules', array('node', 'user'));
+  #$active[] = 'solrsearch_search';
+  #variable_set('search_active_modules', array_unique($active));
+}
+
+/**
+ * Implements hook_schema().
+ */
+function solrsearch_search_schema() {
+  $schema = array();
+
+  $schema['solrsearch_search_page'] = array(
+    'description' => 'Apache Solr Search search page settings.',
+    'export' => array(
+      // Environment machine name.
+      'key' => 'page_id',
+      // Description of key.
+      'key name' => 'search page machine name',
+      // Variable name to use in exported code.
+      'identifier' => 'searcher',
+      // Use the same hook as the API name below.
+      'default hook' => 'solrsearch_search_default_searchers',
+      'status' => 'solrsearch_search_page_status',
+      // CTools API implementation.
+      'api' => array(
+        'owner' => 'solrsearch_search',
+        'api' => 'solrsearch_search_defaults',
+        'minimum_version' => 3,
+        'current_version' => 3,
+      ),
+      // Includes all search page specific configurations.
+      'export callback' => 'solrsearch_search_page_settings_export',
+    ),
+    'fields' => array(
+      'page_id' => array(
+        'description' => 'The machine readable name of the search page.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'label' => array(
+        'description' => 'The human readable name of the search page.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'description' => array(
+        'description' => 'The description of the search page.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'search_path' => array(
+        'description' => 'The path to the search page.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'page_title' => array(
+        'description' => 'The title of the search page.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'env_id' => array(
+        'description' => 'The machine name of the search environment.',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'settings' => array(
+        'description' => 'Serialized storage of general settings.',
+        'type' => 'text',
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('page_id'),
+    'indexes' => array(
+      'env_id' => array('env_id'),
+    ),
+  );
+
+  return $schema;
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function solrsearch_search_uninstall() {
+  $stored = variable_get('solrsearch_index_last', array());
+  unset($stored['solrsearch_search']);
+  variable_set('solrsearch_index_last', $stored);
+
+  $active = variable_get('search_active_modules', array('node', 'user'));
+  $idx = array_search('solrsearch_search', $active);
+  if ($idx !== FALSE) {
+    unset($active[$idx]);
+    variable_set('search_active_modules', $active);
+  }
+  // Remove variables.
+  variable_del('solrsearch_search_spellcheck');
+  variable_del('solrsearch_search_mlt_blocks');
+  variable_del('solrsearch_search_default_search_page');
+  // Remove blocks.
+  db_delete('block')->condition('module', 'solrsearch_search')->execute();
+}
+
+/**
+ * Various updates for Drupal 7.
+ */
+function solrsearch_search_update_7000() {
+  $taxo_links = variable_get('solrsearch_search_taxonomy_links', 0);
+  // TODO - enable the new contrib module?
+  variable_del('solrsearch_search_taxonomy_links');
+  // TODO - possibly rename block deltas, etc.
+  $active = variable_get('search_active_modules', array('node', 'user'));
+  $active[] = 'solrsearch_search';
+  variable_set('search_active_modules', array_unique($active));
+  if (variable_get('solrsearch_search_make_default', 0)) {
+    variable_set('search_default_module', 'solrsearch_search');
+  }
+  variable_del('solrsearch_search_make_default');
+}
+
+/**
+ * Add solrsearch_search_page table.
+ */
+function solrsearch_search_update_7001() {
+  // Moved to 7002
+}
+
+/**
+ * Add solrsearch_search_page table for real.
+ */
+function solrsearch_search_update_7002() {
+
+  $schema['solrsearch_search_page'] = array(
+    'description' => 'Apache Solr Search search page settings.',
+    'export' => array(
+      'key' => 'page_id',
+      'identifier' => 'searcher',
+      'default hook' => 'solrsearch_search_default_searchers',
+      'status' => 'solrsearch_search_page_status',
+      'api' => array(
+        'owner' => 'solrsearch_search',
+        'api' => 'solrsearch_search_defaults',
+        'minimum_version' => 3,
+        'current_version' => 3,
+      ),
+      'export callback' => 'solrsearch_search_page_settings_export',
+    ),
+    'fields' => array(
+      'page_id' => array(
+        'description' => 'The machine readable name of the search page.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'label' => array(
+        'description' => 'The human readable name of the search page.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'description' => array(
+        'description' => 'The description of the search page.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'search_path' => array(
+        'description' => 'The path to the search page.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'page_title' => array(
+        'description' => 'The title of the search page.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'env_id' => array(
+        'description' => 'The machine name of the search environment.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'settings' => array(
+        'description' => 'Serialized storage of general settings.',
+        'type' => 'text',
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('page_id'),
+    'indexes' => array(
+      'env_id' => array('env_id'),
+    ),
+  );
+  if (db_table_exists('solrsearch_search_page')) {
+    // Just in case you are chasing HEAD.
+    db_drop_table('solrsearch_search_page');
+  }
+  db_create_table('solrsearch_search_page', $schema['solrsearch_search_page']);
+}
+
+
+/**
+ * Delete all Apache Solr Search blocks - they moved to Facet API.
+ */
+function solrsearch_search_update_7003() {
+  // Remove blocks.
+  db_delete('block')->condition('module', 'solrsearch_search')->execute();
+}
+
+/**
+ * Add a default search page for core
+ * Add a taxonomy page if the taxonomy module was ever active
+ */
+function solrsearch_search_update_7004() {
+  // Add Default search page (core search)
+  $settings = array(
+    'solrsearch_search_search_type' => 'custom',
+    'solrsearch_search_per_page' => variable_get('solrsearch_rows', 10),
+    'solrsearch_search_browse' => variable_get('solrsearch_search_browse', 'browse'),
+    'solrsearch_search_spellcheck' => variable_get('solrsearch_search_spellcheck', TRUE),
+    'solrsearch_search_not_removable' => TRUE,
+    'solrsearch_search_search_box' => TRUE,
+  );
+  $settings = serialize($settings);
+
+  $fields = array(
+
+      'page_id' => 'core_solr_search',
+      'label' => 'Core Solr Search',
+      'description' => 'Core SolrSearch',
+      'search_path' => 'solrsearch/site',
+      'env_id' => 'searchecho',
+      'page_title' => 'echo',
+      'settings' => $settings,
+  );
+  db_insert('solrsearch_search_page')->fields($fields)->execute();
+  // Remove variables.
+  variable_del('solrsearch_search_spellcheck');
+  variable_del('solrsearch_search_browse');
+
+  // Add this taxonomy search page to the database
+  $settings = array(
+    'solrsearch_search_search_type' => 'tid',
+    'solrsearch_search_per_page' => 10,
+    'solrsearch_search_browse' => 'results',
+    'solrsearch_search_spellcheck' => FALSE,
+    'solrsearch_search_search_box' => FALSE,
+  );
+  $settings = serialize($settings);
+
+  $fields = array(
+    'page_id' => 'taxonomy_search',
+    'label' => 'Taxonomy Search',
+    'description' => 'Search all items with given term',
+    'search_path' => 'taxonomy/term/%',
+    'env_id' => '',
+    'page_title' => '%value',
+    'settings' => $settings,
+  );
+  db_insert('solrsearch_search_page')->fields($fields)->execute();
+
+  // Check if the taxonomy module was ever present
+  $status = db_query("SELECT 1 FROM {system} WHERE name = 'solrsearch_taxonomy'")->fetchField();
+  if ($status) {
+    $message  = t('If you had the solrsearch_taxonomy module enabled please go to the !link and enable the Taxonomy Term page', array('!link' => l('Apache Solr custom pages', 'admin/config/search/solrsearch/search-pages')));
+    drupal_set_message($message, 'warning');
+  }
+}
+
+/**
+ * Make the env_id length on the solrsearch_search_page table 64 characters
+ * to match the length of the env_id on all other tables
+ */
+function solrsearch_search_update_7005(&$sandbox) {
+  db_drop_index('solrsearch_search_page', 'env_id');
+  db_change_field('solrsearch_search_page', 'env_id', 'env_id',
+    array(
+        'description' => 'The machine name of the search environment.',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      array(
+        'indexes' => array(
+          'env_id' => array('env_id'),
+        )
+      )
+    );
+}
+
+/**
+ * Remove all solrsearch_search env variables for show_facets if it is zero
+ */
+function solrsearch_search_update_7006() {
+  module_load_include('module', 'solrsearch');
+  $environments = solrsearch_load_all_environments();
+  foreach ($environments as $environment) {
+    $show_facets = solrsearch_environment_variable_get($environment['env_id'], 'solrsearch_search_show_facets', 0);
+    if ($show_facets === 0) {
+      solrsearch_environment_variable_del($environment['env_id'], 'solrsearch_search_show_facets');
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search.module	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,1886 @@
+<?php
+
+include('solrsearch_search_blocks.inc');
+include('solrsearch_search_author_block.inc');
+include('solrsearch_search_title_block.inc');
+include('solrsearch_terms.inc');
+
+/**
+ * @file
+ *   Provides a content search implementation for node content for use with the
+ *   Apache Solr search application.
+ */
+
+#todo loesche diese funktoe finde nicht von wo aus dieaufgerufen wird!!
+function solrsearch_index_delete_entity_from_index($env_id, $entity_type, $entity_id) {
+
+  return true;
+}
+/**
+ * Implements hook_init().
+ *
+ * Checks if we should run an empty facet query so the facet blocks can be
+ * displayed.
+ */
+function solrsearch_search_init() {
+  // Useless without facetapi
+  //dpm("solrserch_init");
+  if (!module_exists('facetapi')) {
+    return NULL;
+  }
+
+  // Using a simple query we will figure out if we have to execute this snippet
+  // on every page or exit as fast as possible.
+  $query = "SELECT count(env_id)
+    FROM {solrsearch_environment_variable}
+    WHERE name = 'solrsearch_search_show_facets'";
+  $count = db_query($query)->fetchField();
+
+  //dpm($query);
+  //dpm($count);
+  if ($count == 0) {
+    return NULL;
+  }
+
+  // Load the default search page, we only support facets to link to this
+  // search page due to complexity and slow downs
+  //dpm("SEARCH PAGE I");
+
+  $search_page_id = solrsearch_search_default_search_page();
+  //dpm("SP".$search_page_id);
+  $search_page = solrsearch_search_page_load($search_page_id);
+  // Do not continue if our search page is not valid
+  if (empty($search_page)) {
+    return NULL;
+  }
+  //dpm("SEARCH PAGE II");
+  //dpm($search_page);
+  $show_facets = solrsearch_environment_variable_get($search_page['env_id'], 'solrsearch_search_show_facets', 0);
+  //dpm($show_facets);
+  if ($show_facets) {
+
+    // Converts current path to lowercase for case insensitive matching.
+    $paths = array();
+    $paths[] = drupal_strtolower(drupal_get_path_alias(current_path()));
+    $paths[] = drupal_strtolower(current_path());
+
+    $facet_pages = solrsearch_environment_variable_get($search_page['env_id'], 'solrsearch_search_facet_pages', '');
+    //dpm("FACET_PAGES");
+    //dpm($facet_pages);
+    //dpm($paths);
+    //dpm("FACET_PAGES_PATJS");
+    // Iterates over each environment to check if an empty query should be run.
+    if (!empty($facet_pages)) {
+      // Compares path with settings, runs query if there is a match.
+      $patterns = drupal_strtolower($facet_pages);
+      foreach ($paths as $path) {
+        //dpm($path."===".$patterns);
+        if (drupal_match_path($path, $patterns)) {
+          //dpm("MATCH");
+          try {
+            if (!empty($search_page['search_path'])) {
+
+              $solr = solrsearch_get_solr($search_page['env_id']);
+              // Initializes params for empty query.
+              //dpm("solr");
+              //dpm($solr);
+              $params = array(
+                'spellcheck' => 'false',
+                'fq' => array(),
+                'rows' => 1,
+              );
+              $context['page_id'] = $search_page_id;
+              $context['search_type'] = 'solrsearch_search_show_facets';
+              solrsearch_search_run_empty('solrsearch', $params, $search_page['search_path'], $solr, $context);
+            }
+          }
+          catch (Exception $e) {
+            watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function solrsearch_search_menu() {
+  $items['admin/config/search/solrsearch/search-pages'] = array(
+    'title'            => 'Pages/Blocks',
+    'description'      => 'Configure search pages',
+    'page callback'    => 'solrsearch_search_page_list_all',
+    'access arguments' => array('administer search'),
+    'type'             => MENU_LOCAL_TASK,
+    'file'             => 'solrsearch_search.admin.inc',
+  );
+  $items['admin/config/search/solrsearch/search-pages/add'] = array(
+    'title'            => 'Add search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('solrsearch_search_page_settings_form'),
+    'access arguments' => array('administer search'),
+    'type'             => MENU_LOCAL_ACTION,
+    'weight'           => 1,
+    'file'             => 'solrsearch_search.admin.inc',
+  );
+  $items['admin/config/search/solrsearch/search-pages/%solrsearch_search_page/edit'] = array(
+    'title' => 'Edit search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('solrsearch_search_page_settings_form', 5),
+    'access arguments' => array('administer search'),
+    'file'             => 'solrsearch_search.admin.inc',
+  );
+  $items['admin/config/search/solrsearch/search-pages/%solrsearch_search_page/delete'] = array(
+    'title'            => 'Delete search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('solrsearch_search_delete_search_page_confirm', 5),
+    'access arguments' => array('administer search'),
+    'file'             => 'solrsearch_search.admin.inc',
+  );
+  $items['admin/config/search/solrsearch/search-pages/%solrsearch_search_page/clone'] = array(
+    'title' => 'Clone search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('solrsearch_search_clone_search_page_confirm', 5),
+    'access arguments' => array('administer search'),
+    'file'             => 'solrsearch_search.admin.inc',
+  );
+  $items['admin/config/search/solrsearch/search-pages/addblock'] = array(
+    'title'            => 'Add search block "More Like This"',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('solrsearch_search_mlt_add_block_form'),
+    'access arguments' => array('administer search'),
+    'type'             => MENU_LOCAL_ACTION,
+    'weight'           => 2,
+    'file'             => 'solrsearch_search.admin.inc',
+  );
+
+
+  return $items;
+}
+
+function UNUSED_solrsearch_search_menu_alter(&$items) {
+  // Gets default search information.
+  $default_info = search_get_default_module_info();
+  $search_types = solrsearch_search_load_all_search_types();
+  $search_pages = solrsearch_search_load_all_search_pages();
+
+  // Iterates over search pages, builds menu items.
+  foreach ($search_pages as $search_page) {
+    // Validate the environemnt ID in case of import or missed deletion.
+    $environment = solrsearch_environment_load($search_page['env_id']);
+    if (!$environment) {
+      continue;
+    }
+
+    // Parses search path into it's various parts, builds menu items dependent
+    // on whether %keys is in the path.
+    $parts = explode('/', $search_page['search_path']);
+    $keys_pos = count($parts);
+    // Tests whether we are simulating a core search tab.
+    $core_solr_search = ($parts[0] == 'search');
+    $position = array_search('%', $parts);
+    $page_title = isset($search_page['page_title']) ? $search_page['page_title'] : 'Search Results';
+
+
+    // Replace possible tokens [term:tid], [node:nid], [user:uid] with their
+    // menu-specific variant
+    $items[$search_page['search_path']] = array(
+      'title' => $page_title,
+      'page callback' => 'solrsearch_search_custom_page',
+      'page arguments' => array($search_page['page_id'], '', $position),
+      'access arguments' => array('search content'),
+      'type' => ($core_solr_search) ? MENU_LOCAL_TASK : MENU_SUGGESTED_ITEM,
+      'file' => 'solrsearch_search.pages.inc',
+      'file path' => drupal_get_path('module', 'solrsearch_search'),
+    );
+
+    // Not using menu tail because of inflexibility with clean urls
+    $items[$search_page['search_path'] . '/%'] = array(
+      'title' => $page_title,
+      'page callback' => 'solrsearch_search_custom_page',
+      'page arguments' => array($search_page['page_id'], $keys_pos, $position),
+      'access arguments' => array('search content'),
+      'type' => !($core_solr_search) ? MENU_CALLBACK : MENU_LOCAL_TASK,
+      'file' => 'solrsearch_search.pages.inc',
+      'file path' => drupal_get_path('module', 'solrsearch_search'),
+    );
+
+    // If title has a certain callback for the selected type we use it
+    $search_type_id = !empty($search_page['settings']['solrsearch_search_search_type']) ? $search_page['settings']['solrsearch_search_search_type'] : FALSE;
+    $search_type = !empty($search_types[$search_type_id]) ? $search_types[$search_type_id] : FALSE;
+
+    if ($search_type && !empty($position)) {
+      $title_callback = $search_type['title callback'];
+      $items[$search_page['search_path']]['title callback'] = $title_callback;
+      $items[$search_page['search_path']]['title arguments'] = array($search_page['page_id'], $position);
+      $items[$search_page['search_path'] . '/%']['title callback'] = $title_callback;
+      $items[$search_page['search_path'] . '/%']['title arguments'] = array($search_page['page_id'], $position);
+    }
+    // If we have additional searches in the search/* path
+    if ($core_solr_search) {
+      $items[$search_page['search_path'] . '/%']['tab_root'] = 'search/' . $default_info['path'] . '/%';
+      $items[$search_page['search_path'] . '/%']['tab_parent'] = 'search/' . $default_info['path'];
+    }
+  }
+}
+
+/**
+ * Function that loads all the search types
+ *
+ * @return array $search_types
+ */
+function solrsearch_search_load_all_search_types() {
+  $search_types = &drupal_static(__FUNCTION__);
+
+  if (isset($search_types)) {
+    return $search_types;
+  }
+  // Use cache_get to avoid DB when using memcache, etc.
+  $cache = cache_get('solrsearch_search:search_types', 'cache_solrsearch');
+  if (isset($cache->data)) {
+    $search_types = $cache->data;
+  }
+  else {
+    $search_types = array(
+        'bundle' => array(
+        'name' => solrsearch_field_name_map('bundle'),
+        'default menu' => 'search/type/%',
+        'title callback' => 'solrsearch_search_get_value_title',
+      ),
+    );
+    drupal_alter('solrsearch_search_types', $search_types);
+    cache_set('solrsearch_search:search_types', $search_types, 'cache_solrsearch');
+  }
+  return $search_types;
+}
+
+
+
+/**
+ * Used as a callback function to generate a title for a node/page depending
+ * on the input in the configuration screen
+ * @param integer $search_page_id
+ * @param integer $value
+ * @return String
+ */
+/*
+function solrsearch_search_get_value_title($search_page_id = NULL, $value = NULL) {
+  $page_title = 'Search results for %value';
+  if (isset($value)  && isset($search_page_id)) {
+    $search_page = solrsearch_search_page_load($search_page_id);
+    $page_title = str_replace('%value', '!value', $search_page['page_title']);
+    $title = $value;
+  }
+  return t($page_title, array('!value' => $title));
+}
+*/
+
+/**
+ * Get or set the default search page id for the current page.
+ */
+function solrsearch_search_default_search_page($page_id = NULL) {
+  $default_page_id = &drupal_static(__FUNCTION__, NULL);
+
+  if (isset($page_id)) {
+    $default_page_id = $page_id;
+  }
+  if (empty($default_page_id)) {
+    $default_page_id = variable_get('solrsearch_search_default_search_page', 'core_solr_search');
+  }
+  return $default_page_id;
+}
+
+/**
+ * Implements hook_solrsearch_default_environment()
+ *
+ * Make sure the core search page is using the default environment.
+ */
+function solrsearch_search_solrsearch_default_environment($env_id, $old_env_id) {
+  $page = solrsearch_search_page_load('core_solr_search');
+  if ($page && $page['env_id'] != $env_id) {
+    $page['env_id'] = $env_id;
+    solrsearch_search_page_save($page);
+  }
+}
+
+/**
+ * Load a search page
+ * @param string $page_id
+ * @return array
+ */
+function solrsearch_search_page_load($page_id) {
+  //dpm(" solrsearch_search_page_load");
+
+  $search_pages = solrsearch_search_load_all_search_pages();
+  //dpm($search_pages);
+  if (!empty($search_pages[$page_id])) {
+    return $search_pages[$page_id];
+  }
+  return FALSE;
+}
+
+function solrsearch_search_page_save($search_page) {
+  if (!empty($search_page)) {
+    db_merge('solrsearch_search_page')
+      ->key(array('page_id' => $search_page['page_id']))
+      ->fields(array(
+        'label' => $search_page['label'],
+        'page_id' => $search_page['page_id'],
+        'description' => $search_page['description'],
+        'env_id' => $search_page['env_id'],
+        'search_path' => $search_page['search_path'],
+        'page_title' => $search_page['page_title'],
+        'settings' => serialize($search_page['settings']),
+      ))
+      ->execute();
+  }
+}
+
+ /**
+ * Function that clones a search page
+ *
+ * @param $page_id
+ *   The page identifier it needs to clone.
+ *
+ */
+function solrsearch_search_page_clone($page_id) {
+  $search_page = solrsearch_search_page_load($page_id);
+  // Get all search_pages
+  $search_pages = solrsearch_search_load_all_search_pages();
+  // Create an unique ID
+  $new_search_page_id = solrsearch_create_unique_id($search_pages, $search_page['page_id']);
+  // Set this id to the new search page
+  $search_page['page_id'] = $new_search_page_id;
+  $search_page['label'] = $search_page['label'] . ' [cloned]';
+  // All cloned search pages should be removable
+  if (isset($search_page['settings']['solrsearch_search_not_removable'])) {
+    unset($search_page['settings']['solrsearch_search_not_removable']);
+  }
+  // Save our new search page in the database
+  solrsearch_search_page_save($search_page);
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function solrsearch_search_block_info() {
+  // Get all of the moreLikeThis blocks that the user has created
+  $blocks = solrsearch_search_load_all_mlt_blocks();
+  foreach ($blocks as $delta => $settings) {
+    $blocks[$delta] += array('info' => t('Solr Search recommendations: !name', array('!name' => $settings['name'])) , 'cache' => DRUPAL_CACHE_PER_PAGE);
+  }
+  // Add the sort block.
+  $blocks['sort'] = array(
+    'info' => t('Solr Search Core: Sorting'),
+    'cache' => DRUPAL_CACHE_CUSTOM,
+  );
+
+  $blocks['solrsearch'] = array(
+      'info' => t('Solr Search Core: Search'),
+      'cache' => DRUPAL_CACHE_CUSTOM,
+  );
+
+
+  $blocks['solrsearch_author'] = array(
+      'info' => t('Solr Search Core: Search - author'),
+      'cache' => DRUPAL_CACHE_CUSTOM,
+  );
+
+  $blocks['solrsearch_title'] = array(
+      'info' => t('Solr Search Core: Search - title'),
+      'cache' => DRUPAL_CACHE_CUSTOM,
+  );
+
+
+
+  return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function solrsearch_search_block_view($delta = '') {
+
+  if ($delta == 'sort') {
+    $environments = solrsearch_load_all_environments();
+    foreach ($environments as $env_id => $environment) {
+      if (solrsearch_has_searched($env_id) && !solrsearch_suppress_blocks($env_id) && $delta == 'sort') {
+        $response = NULL;
+        $query = solrsearch_current_query($env_id);
+        $solrsort = NULL;
+        if ($query) {
+          // Get the query and response. Without these no blocks make sense.
+          $response = solrsearch_static_response_cache($query->getSearcher());
+        }
+
+        // If there are less than two results, do not return the sort block
+        if (empty($response) || ($response->response->numFound < 2)) {
+          return NULL;
+        }
+
+        // Check if we have to return a cached version of this block
+        if ($query) {
+          // Get the URI without any query parameter.
+          $uri = parse_url(request_uri());
+          // Get the current sort as an array.
+          $solrsort = $query->getSolrsort();
+          $cache_id = $uri['path'] . ':' . implode(':', $solrsort);
+          // Do we have something in cache ?
+          if ($cache = cache_get($cache_id, 'cache_block')) {
+            $block = $cache->data;
+            return $block;
+          }
+        }
+
+        $sorts = $query->getAvailableSorts();
+        $sort_links = array();
+        $path = $query->getPath();
+        $new_query = clone $query;
+        $toggle = array('asc' => 'desc', 'desc' => 'asc');
+        foreach ($sorts as $name => $sort) {
+          $active = $solrsort['#name'] == $name;
+          if ($name == 'score') {
+            $direction = '';
+            $new_direction = 'desc'; // We only sort by descending score.
+          }
+          elseif ($active) {
+            $direction = $toggle[$solrsort['#direction']];
+            $new_direction = $toggle[$solrsort['#direction']];
+          }
+          else {
+            $direction = '';
+            $new_direction = $sort['default'];
+          }
+          $new_query->setSolrsort($name, $new_direction);
+          $sort_links[$name] = array(
+            'text' => $sort['title'],
+            'path' => $path,
+            'options' => array('query' => $new_query->getSolrsortUrlQuery()),
+            'active' => $active,
+            'direction' => $direction,
+          );
+        }
+        foreach ($sort_links as $name => $link) {
+          $themed_links[$name] = theme('solrsearch_sort_link', $link);
+        }
+        $block = array(
+        'subject' => t('Sort by'),
+          'content' => theme('solrsearch_sort_list', array('items' => $themed_links))
+        );
+        // Cache the block
+        cache_set($cache_id, $block, 'cache_block');
+        return $block;
+      }
+    }
+  }
+  elseif ($delta == 'solrsearch') {
+
+    return solrsearch_search_block();
+  }
+
+  elseif($delta == 'solrsearch_author'){
+
+    return solrsearch_search_author_block();
+  }
+
+  elseif($delta == 'solrsearch_title'){
+
+    return solrsearch_search_title_block();
+  }
+
+
+
+  elseif (($node = menu_get_object()) && (!arg(2) || arg(2) == 'view')) {
+    $suggestions = array();
+    // Determine whether the user can view the current node. Probably not necessary.
+    $block = solrsearch_search_mlt_block_load($delta);
+    if ($block && node_access('view', $node)) {
+      // Get our specific environment for the MLT block
+      $env_id = (!empty($block['mlt_env_id'])) ? $block['mlt_env_id'] : '';
+      try {
+        $solr = solrsearch_get_solr($env_id);
+        $context['search_type'] = 'solrsearch_search_mlt';
+        $context['block_id'] = $delta;
+        $docs = solrsearch_search_mlt_suggestions($block, solrsearch_document_id($node->nid), $solr, $context);
+        if (!empty($docs)) {
+          $suggestions['subject'] = check_plain($block['name']);
+          $suggestions['content'] = array(
+            '#theme' => 'solrsearch_search_mlt_recommendation_block',
+            '#docs' => $docs,
+            '#delta' => $delta
+          );
+        }
+      }
+      catch (Exception $e) {
+        watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      }
+    }
+    return $suggestions;
+  }
+}
+
+/**
+ * Implements hook_form_[form_id]_alter().
+ */
+function solrsearch_search_form_block_admin_display_form_alter(&$form) {
+  foreach ($form['blocks'] as $key => $block) {
+    if ((strpos($key, "solrsearch_search_mlt-") === 0) && $block['module']['#value'] == 'solrsearch_search') {
+      $form['blocks'][$key]['delete'] = array(
+        '#type' => 'link',
+        '#title' => 'delete',
+        '#href' => 'admin/config/search/solrsearch/search-pages/block/' . $block['delta']['#value'] . '/delete',
+      );
+    }
+  }
+}
+
+/**
+ * Implements hook_block_configure().
+ */
+function solrsearch_search_block_configure($delta = '') {
+  if ($delta != 'sort') {
+    require_once(drupal_get_path('module', 'solrsearch') . '/solrsearch_search.admin.inc');
+    return solrsearch_search_mlt_block_form($delta);
+  }
+}
+
+/**
+ * Implements hook_block_save().
+ */
+function solrsearch_search_block_save($delta = '', $edit = array()) {
+  if ($delta != 'sort') {
+    require_once(drupal_get_path('module', 'solrsearch') . '/solrsearch_search.admin.inc');
+    solrsearch_search_mlt_save_block($edit, $delta);
+  }
+}
+
+
+/**
+ * Return all the saved search pages
+ * @return array $search_pages
+ *   Array of all search pages
+ */
+function solrsearch_search_load_all_search_pages() {
+  $search_pages = &drupal_static(__FUNCTION__, array());
+  //dpm("solrsearch_search_load_all_search_pages");
+  if (!empty($search_pages)) {
+    return $search_pages;
+  }
+
+  // If ctools module is enabled, add search pages from code, e.g. from a
+  // feature module.
+  if (module_exists('ctools')) {
+    ctools_include('export');
+    $defaults = ctools_export_load_object('solrsearch_search_page', 'all');
+    foreach ($defaults as $page_id => $default) {
+      $search_pages[$page_id] = (array) $default;
+    }
+  }
+
+  // Get all search_pages and their id
+  $search_pages_db = db_query('SELECT * FROM {solrsearch_search_page}')->fetchAllAssoc('page_id', PDO::FETCH_ASSOC);
+  //$search_pages_db = db_query('SELECT * FROM {apachesolr_search_page}')->fetchAllAssoc('page_id', PDO::FETCH_ASSOC);
+  //dpm($search_pages);
+  //dpm("QUERY");
+  $search_pages = $search_pages + $search_pages_db;
+
+  // Ensure that the core search page uses the default environment. In some
+  // instances, for example when unit testing, this search page isn't defined.
+  if (isset($search_pages['core_solr_search'])) {
+    $search_pages['core_solr_search']['env_id'] = solrsearch_default_environment();
+  }
+
+  // convert settings to an array
+  foreach ($search_pages as $id => $search_page) {
+    if (is_string($search_pages[$id]['settings'])) {
+      $search_pages[$id]['settings'] = unserialize($search_pages[$id]['settings']);
+      // Prevent false outcomes for the following search page
+      $settings = 0;
+    }
+  }
+  //dpm($search_pages);
+  //dpm("QUERY II");
+  return $search_pages;
+}
+
+function solrsearch_search_load_all_mlt_blocks() {
+  $search_blocks = variable_get('solrsearch_search_mlt_blocks', array());
+  return $search_blocks;
+}
+
+function solrsearch_search_mlt_block_load($block_id) {
+  $search_blocks = variable_get('solrsearch_search_mlt_blocks', array());
+  return isset($search_blocks[$block_id]) ? $search_blocks[$block_id] : FALSE;
+}
+
+/**
+ * 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 solrsearch_document_id($node->nid)
+ * @param $solr
+ *   The solr environment you want to query against
+ *
+ * @return An array of response documents, or NULL
+ */
+function solrsearch_search_mlt_suggestions($settings, $id, $solr = NULL, $context = array()) {
+
+  try {
+    $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(
+      'q' => 'id:' . $id,
+      'qt' => 'mlt',
+      'fl' => array('entity_id', 'entity_type', 'label', 'path', 'url'),
+      'mlt.fl' => $settings['mlt_fl'],
+      'start' => 0,
+      'rows' => $settings['num_results'],
+    );
+    // We can optionally specify a Solr object.
+    $query = solrsearch_drupal_query('solrsearch_mlt', $params, '', '', $solr, $context);
+
+    foreach ($fields as $form_key => $name) {
+      if (!empty($settings[$form_key])) {
+        $query->addParam($name, $settings[$form_key]);
+      }
+    }
+
+    $type_filters = array();
+    if (is_array($settings['mlt_type_filters']) && !empty($settings['mlt_type_filters'])) {
+      $query->addFilter('bundle', '(' . implode(' OR ', $settings['mlt_type_filters']) . ') ');
+    }
+
+    if ($custom_filters = $settings['mlt_custom_filters']) {
+      // @todo - fix the settings form to take a comma-delimited set of filters.
+      $query->addFilter('', $custom_filters);
+    }
+
+    // This hook allows modules to modify the query object.
+    drupal_alter('solrsearch_query', $query);
+    if ($query->abort_search) {
+      return NULL;
+    }
+
+    $response = $query->search();
+
+    if (isset($response->response->docs)) {
+      return (array) $response->response->docs;
+    }
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+function theme_solrsearch_search_mlt_recommendation_block($vars) {
+  $docs = $vars['docs'];
+  $links = array();
+  foreach ($docs as $result) {
+    // Suitable for single-site mode. Label is already safe.
+    $links[] = l($result->label, $result->path, array('html' => TRUE));
+  }
+  $links = array(
+    '#theme' => 'item_list',
+    '#items' => $links,
+  );
+  return render($links);
+}
+
+/**
+ * Implements hook_search_info().
+ */
+function UNUSED_solrsearch_search_search_info() {
+  // Load our core search page
+  // This core search page is assumed to always be there. It cannot be deleted.
+  $search_page = solrsearch_search_page_load('core_solr_search');
+
+  // This can happen during install, or if the DB was manually changed.
+  if (empty($search_page)) {
+    $search_page = array();
+    $search_page['page_title'] = 'echo';
+    $search_page['search_path'] = 'solrsearch/site';
+  }
+
+  return array(
+    'title' => $search_page['page_title'],
+    'path' => str_replace('solrsearch/', '', $search_page['search_path']),
+    'conditions_callback' => variable_get('solrsearch_search_conditions_callback', 'solrsearch_search_conditions'),
+  );
+}
+
+/**
+ * Implements hook_search_reset().
+ */
+function UNUSED_solrsearch_search_search_reset() {
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  $env_id = solrsearch_default_environment();
+  solrsearch_index_mark_for_reindex($env_id);
+}
+
+/**
+ * Implements hook_search_status().
+ */
+function UNUSED_solrsearch_search_search_status() {
+  module_load_include('inc', 'solrsearch', 'solrsearch.index');
+  $env_id = solrsearch_default_environment();
+  return solrsearch_index_status($env_id);
+}
+
+/**
+ * Implements hook_search_execute().
+ * @param $keys
+ *   The keys that are available after the path that is defined in
+ *   hook_search_info
+ * @param $conditions
+ *   Conditions that are coming from solrsearch_search_conditions
+ */
+function solrsearch_search_search_execute($keys = NULL, $conditions = NULL) {
+
+  $search_page = solrsearch_search_page_load('core_solr_search');
+  $results = solrsearch_search_search_results($keys, $conditions, $search_page);
+  return $results;
+}
+
+/**
+ * Implementation of a search_view() conditions callback.
+ */
+function solrsearch_search_conditions() {
+  //get default conditions from the core_solr_search
+  $search_page = solrsearch_search_page_load('core_solr_search');
+  $conditions = solrsearch_search_conditions_default($search_page);
+  return $conditions;
+}
+
+/**
+ * Implements hook_search_page().
+ * @param $results
+ *   The results that came from apache solr
+ */
+function solrsearch_search_search_page($results) {
+  $search_page = solrsearch_search_page_load('core_solr_search');
+  $build = solrsearch_search_search_page_custom($results, $search_page);
+  return $build;
+}
+
+/**
+ * Mimics solrsearch_search_search_page() but is used for custom search pages
+ * We prefer to keep them seperate so we are not dependent from core search
+ * when someone tries to disable the core search
+ * @param $results
+ *   The results that came from apache solr
+ * @param $build
+ *   the build array from where this function was called. Good to append output
+ *   to the build array
+ * @param $search_page
+ *   the search page that is requesting an output
+ */
+function solrsearch_search_search_page_custom($results, $search_page, $build = array()) {
+  if (!empty($search_page['settings']['solrsearch_search_spellcheck'])) {
+
+    // Retrieve suggestion
+    $suggestions = solrsearch_search_get_search_suggestions($search_page['env_id']);
+
+    if ($search_page && !empty($suggestions)) {
+      $build['suggestions'] = array(
+        '#theme' => 'solrsearch_search_suggestions',
+        '#links' => array(l($suggestions[0], $search_page['search_path'] . '/' . $suggestions[0])),
+      );
+    }
+  }
+  // Retrieve expected results from searching
+  if (!empty($results['solrsearch_search_browse'])) {
+    // Show facet browsing blocks.
+   $build['search_results'] = solrsearch_search_page_browse($results['solrsearch_search_browse'], $search_page['env_id']);
+
+  }
+  elseif ($results) {
+
+    $build['search_results'] = array(
+      '#theme' => 'solrsearch_results',
+      '#results' => $results,
+      '#module' => 'solrsearch_search',
+      '#search_page' => $search_page,
+    );
+  }
+  else {
+    // Give the user some custom help text.
+    $build['search_results'] = array('#markup' => theme('solrsearch_search_noresults'));
+  }
+
+  // Allows modules to alter the render array before returning.
+  drupal_alter('solrsearch_search_page', $build, $search_page);
+
+  return $build;
+}
+
+/**
+ * Executes search depending on the conditions given.
+ * See solrsearch_search.pages.inc for another use of this function
+ */
+function solrsearch_search_search_results($keys = NULL, $conditions = NULL, $search_page = NULL) {
+
+  $params = array();
+  $results = array();
+  // Process the search form. Note that if there is $_POST data,
+  // search_form_submit() will cause a redirect to search/[module path]/[keys],
+  // which will get us back to this page callback. In other words, the search
+  // form submits with POST but redirects to GET. This way we can keep
+  // the search query URL clean as a whistle.
+  if (empty($_POST['form_id'])
+      || ($_POST['form_id'] != 'solrsearch_search_custom_page_search_form')
+      && ($_POST['form_id'] != 'search_form')
+      && ($_POST['form_id'] != 'search_block_form') ) {
+
+    // Check input variables
+    if (empty($search_page)) {
+      $search_page = solrsearch_search_page_load('core_solr_search');
+      // Verify if it actually loaded
+      if (empty($search_page)) {
+        // Something must have been really messed up.
+        solrsearch_failure(t('Solr search'), $keys);
+        return array();
+      }
+    }
+    if (empty($conditions)) {
+      $conditions = solrsearch_search_conditions_default($search_page);
+    }
+
+    // Sort options from the conditions array.
+    // @see solrsearch_search_conditions_default()
+    //   See This condition callback to find out how.
+    $solrsort = isset($conditions['solrsearch_search_sort']) ? $conditions['solrsearch_search_sort'] : '';
+    // What to do when we have an initial empty search
+    $empty_search_behavior = isset($search_page['settings']['solrsearch_search_browse']) ? $search_page['settings']['solrsearch_search_browse'] : '';
+
+    try {
+
+      $solr = solrsearch_get_solr($search_page['env_id']);
+      // Default parameters
+      $params['fq'] = isset($conditions['fq']) ? $conditions['fq'] : array();
+      $params['rows'] = isset($conditions['rows']) ? $conditions['rows'] : $search_page['settings']['solrsearch_search_per_page'];
+
+      if (empty($search_page['settings']['solrsearch_search_spellcheck'])) {
+        // Spellcheck needs to have a string as false/true
+        $params['spellcheck'] = 'false';
+      }
+      else {
+        $params['spellcheck'] = 'true';
+      }
+
+      // Empty text Behavior
+      if (!$keys && !isset($conditions['f']) && ($empty_search_behavior == 'browse' || $empty_search_behavior == 'blocks')) {
+        // Pass empty search behavior as string on to solrsearch_search_search_page()
+        // Hardcoded solrsearch name since we rely on this for the facets
+
+        $context['page_id'] = $search_page['page_id'];
+        $context['search_type'] = 'solrsearch_search_browse';
+        solrsearch_search_run_empty('solrsearch', $params, $search_page['search_path'], $solr, $context);
+        $results['solrsearch_search_browse'] = $empty_search_behavior;
+
+        if ($empty_search_behavior == 'browse') {
+          // Hide sidebar blocks for content-area browsing instead.
+          solrsearch_suppress_blocks($search_page['env_id'], TRUE);
+        }
+      }
+      // Full text behavior. Triggers with a text search or a facet
+      elseif (($keys || isset($conditions['f'])) || ($empty_search_behavior == 'results')) {
+
+        $params['q'] = $keys;
+        // Hardcoded solrsearch name since we rely on this for the facets
+        $context['page_id'] = $search_page['page_id'];
+        $context['search_type'] = 'solrsearch_search_results';
+
+        $results = solrsearch_search_run('solrsearch', $params, $solrsort, $search_page['search_path'], pager_find_page(), $solr, $context);
+
+      }
+    }
+    catch (Exception $e) {
+
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+      solrsearch_failure(t('Solr search'), $keys);
+    }
+  }
+  return $results;
+}
+
+function solrsearch_search_conditions_default($search_page) {
+  $conditions = array();
+  $search_type = isset($search_page['settings']['solrsearch_search_search_type']) ? $search_page['settings']['solrsearch_search_search_type'] : '';
+  $allow_user_input = isset($search_page['settings']['solrsearch_search_allow_user_input']) ? $search_page['settings']['solrsearch_search_allow_user_input'] : FALSE;
+  $path_replacer = isset($search_page['settings']['solrsearch_search_path_replacer']) ? $search_page['settings']['solrsearch_search_path_replacer'] : '';
+  $set_custom_filter = isset($search_page['settings']['solrsearch_search_custom_enable']) ? $search_page['settings']['solrsearch_search_custom_enable'] : '';
+  $search_page_fq = !empty($search_page['settings']['fq']) ? $search_page['settings']['fq'] : '';
+
+
+
+
+
+  $conditions['fq'] = array();
+  // We only allow this to happen if the search page explicitely allows it
+  if ($allow_user_input) {
+    // Get the filterQueries from the url
+    if (!empty($_GET['fq']) && is_array($_GET['fq'])) {
+      // Reset the array so that we have one level lower to go through
+      $conditions['fq'] = $_GET['fq'];
+    }
+    foreach($conditions['fq'] as $condition_id => $condition) {
+      // If the user input does not pass our validation we do not allow
+      // it to query solr
+      $test_query = solrsearch_drupal_subquery('Test');
+      if (!$test_query->validFilterValue($condition)) {
+        unset($conditions['fq'][$condition_id]);
+      }
+    }
+  }
+
+  // Custom filters added in search pages
+  if (!empty($search_page_fq) && !empty($set_custom_filter)) {
+    if (!empty($path_replacer)) {
+      // If the manual filter has a % in it, replace it with $value
+      $conditions['fq'][] = str_replace('%', $path_replacer, $search_page_fq);
+    }
+    else {
+      // Put the complete filter in the filter query
+      $conditions['fq'][] = $search_page_fq;
+    }
+  }
+
+  // Search type filters (such as taxonomy)
+  if (!empty($path_replacer) && !empty($search_type) && $search_type != 'custom') {
+    $conditions['fq'][] = $search_type . ':' . $path_replacer;
+  }
+
+  // We may also have filters added by facet API module. The 'f'
+  // is determined by variable FacetapiUrlProcessor::$filterKey. Hard
+  // coded here to avoid extra class loading.
+  if (!empty($_GET['f']) && is_array($_GET['f'])) {
+    if (module_exists('facetapi')) {
+      $conditions['f'] = $_GET['f'];
+    }
+  }
+  // Add the sort from the page to our conditions
+  $sort = isset($_GET['solrsort']) ? $_GET['solrsort'] : '';
+  $conditions['solrsearch_search_sort'] = $sort;
+
+
+  //$conditions['fq'][] = "doc-type:indexMeta";
+  //$conditions['fq'][] = "doc-type:institutesLibrary";
+  $conditions['fq'][] = "doc-type:*";
+
+  return $conditions;
+}
+
+/**
+ * Handle browse results for empty searches.
+ */
+function solrsearch_search_page_browse($empty_search_behavior, $env_id) {
+  $build = array();
+  //dpm("solrsearch_search_page_browse");
+  //dpm($empty_search_behavior);
+
+  // Switch in case we come up with new flags.
+  switch ($empty_search_behavior) {
+    case 'browse':
+      drupal_add_js(drupal_get_path('module', 'solrsearch') .'/solrsearch-mpiwg.js');
+
+      if (module_exists('facetapi') && $query = solrsearch_current_query($env_id)) {
+        module_load_include('inc', 'facetapi', 'facetapi.block');
+        // Get facet render elements.
+        $searcher = $query->getSearcher();
+        $elements = facetapi_build_realm($searcher, 'block');
+        $build = array();
+        //dpm($searcher);
+        //dpm($elements);
+        foreach (element_children($elements) as $key) {
+          $delta = "facetapi_{$key}";
+          // @todo: order/filter these pseudo-blocks according to block.module weight, visibility (see 7.x-1beta4)
+          $block = new stdClass();
+          $block->visibility = TRUE;
+          $block->enabled = TRUE;
+          $block->module = 'facetapi';
+          $block->subject = theme('facetapi_title', array('title' => $elements[$key]['#title']));
+          $build[$delta] = $elements[$key];
+          $block->region = NULL;
+          $block->delta = 'solrsearch-' . $key;
+          // @todo: the final themed block's div id attribute does not coincide with "real" block's id (see facetapi_get_delta_map())
+          $build[$delta]['#block'] = $block;
+          $build[$delta]['#theme_wrappers'][] = 'block';
+          $build['#sorted'] = TRUE;
+        }
+        //$build['#theme_wrappers'][] = 'solrsearch_search_browse_blocks';
+      }
+      break;
+  }
+  return $build;
+}
+
+/**
+ * Shows a groups of blocks for starting a search from a filter.
+ */
+function theme_solrsearch_search_browse_blocks($vars) {
+  $result = '';
+  if ($vars['content']['#children']) {
+    $result .= "<div class='solrsearch-browse-blocks'>\n<h2>" . t('Browse available categories') . "</h2>\n";
+    $result .= '<p>' . t('Pick a category to launch a search.') . "</p>\n";
+    $result .= $vars['content']['#children'] . "\n</div>\n";
+  }
+
+  return $result;
+}
+
+/**
+ * Execute a search with zero results rows so as to populate facets.
+ */
+function solrsearch_search_run_empty($name, array $params = array(), $base_path = '', $solr = NULL, $context = array()) {
+  $query = solrsearch_drupal_query($name, $params, '', $base_path, $solr, $context);
+  $query->addParam('rows', '0');
+  $solr_id = $query->solr('getId');
+  list($final_query, $response) = solrsearch_do_query($query);
+  solrsearch_has_searched($solr_id, TRUE);
+}
+
+/**
+ * Execute a search results based on keyword, filter, and sort strings.
+ *
+ * @param $name
+ * @param $params
+ *   Array - 'q' is the keywords to search.
+ * @param $solrsort
+ * @param $base_path
+ *   For constructing filter and sort links. Leave empty unless the links need to point somewhere
+ *   other than the base path of the current request.
+ * @param integer $page
+ *   For pagination.
+ * @param DrupalApacheSolrServiceInterface $solr
+ *   The solr server resource to execute the search on.
+ *
+ * @return stdClass $response
+ *
+ * @throws Exception
+ */
+function solrsearch_search_run($name, array $params = array(), $solrsort = '', $base_path = '', $page = 0, DrupalApacheSolrServiceInterface $solr = NULL, $context = array()) {
+  // Merge the default params into the params sent in.
+
+
+  $params += solrsearch_search_basic_params();
+  // This is the object that knows about the query coming from the user.
+  $query = solrsearch_drupal_query($name, $params, $solrsort, $base_path, $solr, $context);
+
+
+
+  if ($query->getParam('q')) {
+    solrsearch_search_add_spellcheck_params($query);
+  }
+
+  // Add the paging parameters
+  $query->page = $page;
+
+
+  //solrsearch_search_add_boost_params($query);
+  if ($query->getParam('q')) {
+    solrsearch_search_highlighting_params($query);
+    if (!$query->getParam('hl.fl')) {
+      $qf = array();
+      //foreach ($query->getParam('qf') as $field) {
+        // Truncate off any boost so we get the simple field name.
+        //$parts = explode('^', $field, 2);
+        //$qf[$parts[0]] = TRUE;
+      //}
+      //foreach (array('content', 'ts_comments') as $field) {
+        //if (isset($qf[$field])) {
+          //$query->addParam('hl.fl', $field);
+        //}
+      //}
+    }
+  }
+  else {
+    // No highlighting, use the teaser as a snippet.
+    $query->addParam('fl', 'teaser');
+  }
+
+
+
+  list($final_query, $response) = solrsearch_do_query($query);
+
+
+  $env_id = $query->solr('getId');
+  solrsearch_has_searched($env_id, TRUE);
+  $process_response_callback = solrsearch_environment_variable_get($env_id, 'process_response_callback', 'solrsearch_search_process_response');
+
+
+  if (function_exists($process_response_callback)) {
+    return call_user_func($process_response_callback, $response, $final_query);
+  }
+  else {
+    return solrsearch_search_process_response($response, $final_query);
+  }
+}
+
+function solrsearch_search_basic_params(DrupalSolrQueryInterface $query = NULL) {
+  $params = array(
+    'fl' => array(
+        'id',
+        'entity_type',
+        'author_c',
+        'author',
+        'title',
+        'title_s',
+        'keyword',
+        'year',
+        'IM_date',
+        'IM_signature',
+        'IM_call-number',
+        'archive-path',
+        'doc-type',
+        'mpiwg-dri',
+        'access-type',
+        'content',
+      ),
+    'mm' => 1,
+    'rows' => 10,
+    'pf' => 'title^2.0 author^1.0',
+    'ps' => 15,
+    'hl' => 'true',
+    'hl.fl' => 'title',
+    'hl.snippets' => 3,
+    'hl.mergeContigious' => 'true',
+    'f.content.hl.alternateField' => 'teaser',
+    'f.content.hl.maxAlternateFieldLength' => 256,
+  );
+  if ($query) {
+    $query->addParams($params);
+  }
+  return $params;
+}
+
+/**
+ * Add highlighting settings to the search params.
+ *
+ * These settings are set in solrconfig.xml.
+ * See the defaults there.
+ * If you wish to override them, you can via settings.php or drush
+ */
+function solrsearch_search_highlighting_params(DrupalSolrQueryInterface $query = NULL) {
+  $params['hl'] = variable_get('solrsearch_hl_active', NULL);
+  $params['hl.fragsize']= variable_get('solrsearch_hl_textsnippetlength', NULL);
+  $params['hl.simple.pre'] = variable_get('solrsearch_hl_pretag', NULL);
+  $params['hl.simple.post'] = variable_get('solrsearch_hl_posttag', NULL);
+  $params['hl.snippets'] = variable_get('solrsearch_hl_numsnippets', NULL);
+  // This should be an array of possible field names.
+  $params['hl.fl'] = variable_get('solrsearch_hl_fieldtohighlight', NULL);
+  $params = array_filter($params);
+  if ($query) {
+    $query->addParams($params);
+  }
+  return $params;
+}
+
+function solrsearch_search_add_spellcheck_params(DrupalSolrQueryInterface $query) {
+  $params = array();
+
+  // Add new parameter to the search request
+  //$params['spellcheck.q'] = $query->getParam('q');
+  //$params['spellcheck'] = 'true';
+  //$query->addParams($params);
+}
+
+function solrsearch_search_add_boost_params(DrupalSolrQueryInterface $query) {
+  $env_id = $query->solr('getId');
+  $params = array();
+
+  $defaults = array(
+    'content' => '1.0',
+    'ts_comments' => '0.5',
+    'tos_content_extra' => '0.1',
+    'label' => '5.0',
+    'tos_name' => '3.0',
+    'taxonomy_names' => '2.0',
+    'tags_h1' => '5.0',
+    'tags_h2_h3' => '3.0',
+    'tags_h4_h5_h6' => '2.0',
+    'tags_inline' => '1.0',
+    'tags_a' => '0',
+  );
+  $qf = solrsearch_environment_variable_get($env_id, 'field_bias', $defaults);
+  $fields = $query->solr('getFields');
+  if ($qf && $fields) {
+    foreach ($fields as $field_name => $field) {
+      if (!empty($qf[$field_name])) {
+        $prefix = substr($field_name, 0, 3);
+        if ($field_name == 'content' || $prefix == 'IM_' || $prefix == 'TT_') {
+          // Normed fields tend to have a lower score. Multiplying by 40 is
+          // a rough attempt to bring the score in line with fields that are
+          // not normed.
+          $qf[$field_name] *= 40.0;
+        }
+        $params['qf'][$field_name] = $field_name . '^' . $qf[$field_name];
+      }
+    }
+  }
+
+  $date_settings = solrsearch_environment_variable_get($env_id, 'solrsearch_search_date_boost', '0:0');
+  $comment_settings = solrsearch_environment_variable_get($env_id, 'solrsearch_search_comment_boost', '0:0');
+  $changed_settings = solrsearch_environment_variable_get($env_id, 'solrsearch_search_changed_boost', '0:0');
+  $sticky_boost = solrsearch_environment_variable_get($env_id, 'solrsearch_search_sticky_boost', '0');
+  $promote_boost = solrsearch_environment_variable_get($env_id, 'solrsearch_search_promote_boost', '0');
+  // For the boost functions for the created timestamp, etc we use the
+  // standard date-biasing function, as suggested (but steeper) at
+  // http://wiki.apache.org/solr/SolrRelevancyFAQ#How_can_I_boost_the_score_of_newer_documents
+  // ms() returns the time difference in ms between now and the date
+  // The function is thus: $ab/(ms(NOW,date)*$steepness + $ab).
+  list($date_steepness, $date_boost) = explode(':', $date_settings);
+  if ($date_boost) {
+    $ab = 4 / $date_steepness;
+    $params['bf'][] = "recip(ms(NOW,ds_created),3.16e-11,$ab,$ab)^$date_boost";
+  }
+  // Boost on comment count.
+  list($comment_steepness, $comment_boost) = explode(':', $comment_settings);
+  if ($comment_boost) {
+    $params['bf'][] = "recip(div(1,max(is_comment_count,1)),$comment_steepness,10,10)^$comment_boost";
+  }
+  // Boost for a more recent comment or node edit.
+  list($changed_steepness, $changed_boost) = explode(':', $changed_settings);
+  if ($changed_boost) {
+    $ab = 4 / $changed_steepness;
+    $params['bf'][] = "recip(ms(NOW,ds_created),3.16e-11,$ab,$ab)^$changed_boost";
+  }
+  // Boost for nodes with sticky bit set.
+  if ($sticky_boost) {
+    $params['bq'][] = "bs_sticky:true^$sticky_boost";
+  }
+  // Boost for nodes with promoted bit set.
+  if ($promote_boost) {
+    $params['bq'][] = "bs_promote:true^$promote_boost";
+  }
+  // Modify the weight of results according to the node types.
+  $type_boosts = solrsearch_environment_variable_get($env_id, 'solrsearch_search_type_boosts', array());
+  if (!empty($type_boosts)) {
+    foreach ($type_boosts as $type => $boost) {
+      // Only add a param if the boost is != 0 (i.e. > "Normal").
+      if ($boost) {
+        $params['bq'][] = "bundle:$type^$boost";
+      }
+    }
+  }
+  $query->addParams($params);
+
+}
+
+function solrsearch_search_process_response($response, DrupalSolrQueryInterface $query) {
+  $results = array();
+  // We default to getting snippets from the body content and comments.
+  $hl_fl = $query->getParam('hl.fl');
+  if (!$hl_fl) {
+    //TODO: make highlighitn configurabel
+    $hl_fl = array('title','author');
+  }
+  $total = $response->response->numFound;
+
+
+  pager_default_initialize($total, $query->getParam('rows'));
+  if ($total > 0) {
+    $fl = $query->getParam('fl');
+    // 'id' and 'entity_type' are the only required fields in the schema, and
+    // 'score' is generated by solr.
+
+
+    foreach ($response->response->docs as $doc) {
+      $extra = array();
+      // Allow modules to alter each document and its extra information.
+      drupal_alter('solrsearch_search_result', $doc, $extra, $query);
+
+      // Start with an empty snippets array.
+      $snippets = array();
+
+      //TODO mappe das irgendwo allgemein?
+      $doc->id=$doc->{'archive-path'};
+
+          // Find the nicest available snippet.
+      foreach ($hl_fl as $hl_param) {
+        if (isset($response->highlighting->{$doc->id}->$hl_param)) {
+          // Merge arrays preserving keys.
+          foreach ($response->highlighting->{$doc->id}->$hl_param as $value) {
+            $snippets[$hl_param][] = $value;
+          }
+        }
+      }
+      // If there's no snippet at this point, add the teaser.
+      if (!$snippets) {
+        if (isset($doc->teaser)) {
+          $snippets[] = truncate_utf8($doc->teaser, 256, TRUE);
+        }
+      }
+
+
+      $hook = 'solrsearch_search_snippets__' . $doc->{'doc-type'}[0];
+      /*$bundle = !empty($doc->bundle) ? $doc->bundle : NULL;
+      if ($bundle) {
+        $hook .= '__' . $bundle;
+      }*/
+      $snippet = theme($hook, array('doc' => $doc, 'snippets' => $snippets));
+
+      if (!isset($doc->content)) {
+        $doc->content = $snippet;
+      }
+
+      /*
+      // Normalize common dates so that we can use Drupal's normal date and
+      // time handling.
+      if (isset($doc->ds_created)) {
+        $doc->created = strtotime($doc->ds_created);
+      }
+      else {
+        $doc->created = NULL;
+      }
+
+      if (isset($doc->ds_changed)) {
+        $doc->changed = strtotime($doc->ds_changed);
+      }
+      else {
+        $doc->changed = NULL;
+      }
+
+      if (isset($doc->tos_name)) {
+        $doc->name = $doc->tos_name;
+      }
+      else {
+        $doc->name = NULL;
+      }
+      */
+      // Set all expected fields from fl to NULL if they are missing so
+      // as to prevent Notice: Undefined property.
+      $fl = array_merge($fl, array('path', 'label', 'score'));
+      foreach ($fl as $field) {
+        if (!isset($doc->{$field})) {
+          $doc->{$field} = NULL;
+        }
+      }
+
+      $fields = (array) $doc;
+
+      // a path is not a requirement of entity (see entity_uri() ), so we check if we
+      // can show it and fallback to the main page of the site if we don't
+      // have it.
+      if (!isset($doc->url)) {
+        $path = '';
+      }
+      else {
+        $path = $doc->url;
+      }
+
+      $result = array(
+
+        // template_preprocess_search_result() runs check_plain() on the title
+        // again.  Decode to correct the display.
+          'title' => htmlspecialchars_decode($doc->title[0], ENT_QUOTES),
+          'author' => htmlspecialchars_decode($doc->author[0], ENT_QUOTES),
+        // These values are not required by the search module but are provided
+        // to give entity callbacks and themers more flexibility.
+        'score' => $doc->score,
+        'snippets' => $snippets,
+        'snippet' => $snippet,
+        'fields' => $fields,
+        'doc-type' => $doc->{'doc-type'},
+        'mpiwg-dri' => $doc->{'mpiwg-dri'},
+        'access-type'=> $doc->{'access-type'},
+        'year' => $doc->{'year'},
+        'archive-path' => $doc->{'archive-path'},
+
+      );
+
+      if (isset($doc->{'url'})){
+        $result['url']=$doc->{'url'};
+      } else {
+      	$result['url']=array("");
+      }
+
+      if (isset($doc->{'image'})){
+        $result['image']=$doc->{'image'};
+      } else {
+       $result['image']=array("");
+      }
+      
+      if (isset($doc->{'provider'})){
+        $result['provider']=$doc->{'provider'};
+      } else {
+       $result['provider']=array("");
+      }
+
+      if (isset($doc->{'dataSource'})){
+        $result['dataSource']=$doc->{'dataSource'};
+      } else {
+       $result['dataSource']="mpiwg";
+      }
+
+
+
+      if (is_array($doc->{'content'})){
+        $result['content'] = check_plain(implode(" ",$doc->{'content'}));
+      } else {
+        $result['content'] = check_plain($doc->{'content'});
+      }
+
+      if (isset($doc->{'IM_date'})){
+        $result['date']=$doc->{'IM_date'};
+      }
+
+      if (isset($doc->{'IM_call-number'})){
+        $result['IM_call-number']=$doc->{'IM_call-number'};
+      }
+
+      if (isset($doc->{'IM_signature'})){
+        $result['signature']=$doc->{'IM_signature'};
+      }
+
+
+      // Call entity-type-specific callbacks for extra handling.
+      /*$function = solrsearch_entity_get_callback($doc->{'doc-type'}, 'result callback', '');
+      if (is_callable($function)) {
+        $function($doc, $result, $extra);
+      }
+      */
+      $result['extra'] = $extra;
+
+      $results[] = $result;
+    }
+  }
+  // Hook to allow modifications of the retrieved results
+  foreach (module_implements('solrsearch_process_results') as $module) {
+    $function = $module . '_solrsearch_process_results';
+    $function($results, $query);
+  }
+  return $results;
+}
+
+/**
+ * Used as a callback function to generate a title for a node/page depending
+ * on the input in the configuration screen
+ * @param integer $search_page_id
+ * @param integer $value
+ * @return String
+ */
+function searchsolr_search_get_value_title($search_page_id = NULL, $value = NULL) {
+  $page_title = 'Search results for %value';
+  if (isset($value)  && isset($search_page_id)) {
+    $search_page = apachesolr_search_page_load($search_page_id);
+    $page_title = str_replace('%value', '!value', $search_page['page_title']);
+    $title = $value;
+  }
+  return t($page_title, array('!value' => $title));
+}
+
+/**
+ * Retrieve all of the suggestions that were given after a certain search
+ * @return array()
+ */
+function solrsearch_search_get_search_suggestions($env_id) {
+  $suggestions_output = array();
+  if (solrsearch_has_searched($env_id)) {
+    $query = solrsearch_current_query($env_id);
+    $keyword = $query->getParam('q');
+    $searcher = $query->getSearcher();
+    $response = solrsearch_static_response_cache($searcher);
+    // Get spellchecker suggestions into an array.
+    if (!empty($response->spellcheck->suggestions)) {
+      $suggestions = get_object_vars($response->spellcheck->suggestions);
+      if ($suggestions) {
+        $replacements = array();
+        // Get the original query and retrieve all words with suggestions.
+        foreach ($suggestions as $word => $value) {
+          $replacements[$word] = $value->suggestion[0];
+        }
+        // Replace the keyword with the suggested keyword.
+        $suggested_keyword = strtr($keyword, $replacements);
+        // Show only if suggestion is different than current query.
+        if ($keyword != $suggested_keyword) {
+          $suggestions_output[] = $suggested_keyword;
+        }
+      }
+    }
+  }
+  return $suggestions_output;
+}
+
+/**
+ * Implements hook_solrsearch_entity_info_alter().
+ */
+function solrsearch_search_solrsearch_entity_info_alter(&$entity_info) {
+  // First set defaults so that we don't need to worry about NULL keys.
+  foreach (array_keys($entity_info) as $type) {
+    $entity_info[$type]['result callback'] = '';
+  }
+  // Now set those values that we know.  Other modules can do so
+  // for their own entities if they want.
+  $entity_info['node']['result callback'] = 'solrsearch_search_node_result';
+}
+
+/**
+ * Callback function for node search results.
+ *
+ * @param stdClass $doc
+ *   The result document from Apache Solr.
+ * @param array $result
+ *   The result array for this record to which to add.
+ */
+function solrsearch_search_node_result($doc, &$result, &$extra) {
+  $doc->uid = $doc->is_uid;
+  $result += array(
+    'type' => node_type_get_name($doc->bundle),
+    'user' => theme('username', array('account' => $doc)),
+    'date' => isset($doc->changed) ? $doc->changed : 0,
+    'node' => $doc,
+    'uid' => $doc->is_uid,
+  );
+
+  if (isset($doc->is_comment_count)) {
+    $extra['comments'] = format_plural($doc->is_comment_count, '1 comment', '@count comments');
+  }
+}
+
+/**
+ * Returns whether a search page exists.
+ */
+function solrsearch_search_page_exists($search_page_id) {
+  return db_query('SELECT 1 FROM {solrsearch_search_page} WHERE page_id = :page_id', array(':page_id' => $search_page_id))->fetchField();
+}
+
+/**
+ * Template preprocess for solrsearch search results.
+ *
+ * We need to add additional entity/bundle-based templates
+ */
+function solrsearch_search_preprocess_search_result(&$variables) {
+  // If this search result is coming from our module, we want to improve the
+  // template potential to make life easier for themers.
+
+
+  if ($variables['module'] == 'solrsearch_search') {
+    $result = $variables['result'];
+
+    //info in display should display the author.
+    $variables['info']=$result['author'];
+    if (!empty($result['entity_type'])) {
+      $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module'] . '__' . $result['entity_type'];
+      if (!empty($result['bundle'])) {
+        $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module'] . '__' . $result['entity_type'] . '__' . $result['bundle'];
+      }
+    }
+  }
+}
+
+function solrsearch_search_preprocess_search_results(&$variables) {
+  // Initialize variables
+  $env_id = NULL;
+
+  // If this is a solr search, expose more data to themes to play with.
+  if ($variables['module'] == 'solrsearch_search') {
+    // Fetch our current query
+    if (!empty($variables['search_page']['env_id'])) {
+      $env_id = $variables['search_page']['env_id'];
+    }
+    $query = solrsearch_current_query($env_id);
+
+    if ($query) {
+      $variables['query'] = $query;
+      $variables['response'] = solrsearch_static_response_cache($query->getSearcher());
+    }
+    if (empty($variables['response'])) {
+      $variables['description'] = '';
+      return NULL;
+    }
+    $total = $variables['response']->response->numFound;
+    $params = $variables['query']->getParams();
+
+    $variables['description'] = t('Showing items @start through @end of @total.', array(
+      '@start' => $params['start'] + 1,
+      '@end' => $params['start'] + $params['rows'] - 1,
+      '@total' => $total,
+    ));
+    // Redefine the pager if it was missing
+    pager_default_initialize($total, $params['rows']);
+    $variables['pager'] = theme('pager', array('tags' => NULL));
+
+    // Add template hints for environments
+    if (!empty($env_id)) {
+      $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module'] . '__' . $env_id;
+      // Add template hints for search pages
+      if (!empty($variables['search_page']['page_id'])) {
+        $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module'] . '__' . $variables['search_page']['page_id'];
+        // Add template hints for both
+        $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module'] . '__' . $env_id . '__' . $variables['search_page']['page_id'];
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_solrsearch_environment_delete().
+ */
+function solrsearch_search_solrsearch_environment_delete($server) {
+  db_update('solrsearch_search_page')
+    ->fields(array(
+      'env_id' => '',
+    ))
+    ->condition('env_id', $server['env_id'])
+    ->execute();
+  solrsearch_environment_variable_del($server['env_id'], 'solrsearch_search_show_facets');
+  solrsearch_environment_variable_del($server['env_id'], 'solrsearch_search_facet_pages');
+  menu_rebuild();
+}
+
+/*function solrsearch_search_form_search_block_form_alter(&$form, $form_state) {
+  if (variable_get('search_default_module') == 'solrsearch_search') {
+    $form['#submit'][] = 'solrsearch_search_form_search_submit';
+  }
+}
+*/
+
+/**
+ * Default theme function for spelling suggestions.
+ */
+function theme_solrsearch_search_suggestions($variables) {
+  $output = '<div class="spelling-suggestions">';
+  $output .= '<dl class="form-item"><dt><strong>' . t('Did you mean') . '</strong></dt>';
+  foreach ((array) $variables['links'] as $link) {
+    $output .= '<dd>' . $link . '</dd>';
+  }
+  $output .= '</dl></div>';
+  return $output;
+}
+
+/**
+ * Added form submit function to retain filters.
+ *
+ * @see solrsearch_search_form_search_form_alter()
+ */
+function solrsearch_search_form_search_submit($form, &$form_state) {
+  $fv = $form_state['values'];
+  // Replace keys with their rawurlencoded value
+  if (isset($fv['search_block_form'])) {
+    $raw_keys = str_replace("/","%2f",$fv['search_block_form']);
+    $form_state['redirect'] = str_replace($fv['search_block_form'], $raw_keys, $form_state['redirect']);
+  }
+}
+
+
+/**
+ * submit function for the delete_index form.
+ *
+ */
+function solrsearch_search_build_spellcheck($form, &$form_state) {
+  try {
+    $solr = solrsearch_get_solr();
+    $params['spellcheck'] = 'true';
+    $params['spellcheck.build'] = 'true';
+    $response = $solr->search('solr', 0, 0, $params);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Implements hook_form_[form_id]_alter().
+ *
+ * Adds settings to show facet blocks on non-search pages.
+ */
+function solrsearch_search_form_facetapi_realm_settings_form_alter(&$form, &$form_state) {
+
+  if ('solrsearch' == $form['#facetapi']['adapter']->getId() && 'block' == $form['#facetapi']['realm']['name']) {
+    // Gets the environment ID from the searcher, stores in #facetapi property.
+    $env_id = ltrim(strstr($form['#facetapi']['adapter']->getSearcher(), '@'), '@');
+
+    $show_facets = solrsearch_environment_variable_get($env_id, 'solrsearch_search_show_facets', 0);
+    $facet_pages = solrsearch_environment_variable_get($env_id, 'solrsearch_search_facet_pages', '');
+
+    $form['#facetapi']['env_id'] = $env_id;
+
+    $form['solrsearch_search_show_facets'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show facets on non-search pages.'),
+      '#default_value' => $show_facets,
+      '#weight' => '-10',
+    );
+
+    $form['solrsearch_search_facet_pages'] = array(
+      '#title' => t('Non-search paths'),
+      '#type' => 'textarea',
+      '#default_value' => $facet_pages,
+      '#weight' => '-10',
+      '#dependency' => array(
+        'edit-solrsearch-search-show-facets' => array(1),
+      ),
+    );
+
+    $form['#submit'][] = 'solrsearch_search_facetapi_realm_settings_form_submit';
+  }
+}
+
+/**
+ * Form submission handler for facetapi_realm_settings_form().
+ */
+function solrsearch_search_facetapi_realm_settings_form_submit(&$form, &$form_state) {
+  $env_id = $form['#facetapi']['env_id'];
+
+  // Adds the settings to the array keyed by environment ID, saves variables.
+  $show_facets = $form_state['values']['solrsearch_search_show_facets'];
+  $facet_pages = $form_state['values']['solrsearch_search_facet_pages'];
+  if ($show_facets) {
+    solrsearch_environment_variable_set($env_id, 'solrsearch_search_show_facets', $show_facets);
+  }
+  else {
+    // Due to performance reasons, we delete it from the vars so that our init
+    // process can react on environments that hae it set and not unset.
+    // See solrsearch_search_init().
+    solrsearch_environment_variable_del($env_id, 'solrsearch_search_show_facets');
+  }
+  solrsearch_environment_variable_set($env_id, 'solrsearch_search_facet_pages', $facet_pages);
+}
+
+/**
+ * Implements hook_theme().
+ */
+function solrsearch_search_theme() {
+  return array(
+    /**
+     * Shows the facets in blocks in the search result area
+     */
+    'solrsearch_search_browse_blocks' => array(
+      'render element' => 'content',
+    ),
+    /**
+     * Shows the search snippet
+     */
+    'solrsearch_search_snippets' => array(
+      'variables' => array('doc' => NULL, 'snippets' => array()),
+    ),
+    /**
+     * Shows a message when the search does not return any result
+     */
+    'solrsearch_search_noresults' => array(
+      'variables' => array(),
+    ),
+    /**
+     * Shows a list of suggestions
+     */
+    'solrsearch_search_suggestions' => array(
+      'variables' => array('links' => NULL),
+    ),
+    /**
+     * Shows a list of results (docs) in content recommendation block
+     */
+    'solrsearch_search_mlt_recommendation_block' => array(
+      'variables' => array('docs' => NULL, 'delta' => NULL),
+    ),
+    'solrsearch_search_block_form' => array(
+          'render element' => 'form',
+          'template' => 'solrsearch-block-form',
+     ),
+
+     'solrsearch_search_author_block_form' => array(
+          'render element' => 'form',
+          'template' => 'solrsearch-author-block-form',
+      ),
+
+      'solrsearch_search_title_block_form' => array(
+          'render element' => 'form',
+          'template' => 'solrsearch-title-block-form',
+      ),
+  );
+}
+
+
+
+/**
+ * Implements hook_theme_registry_alter().
+ */
+function solrsearch_search_theme_registry_alter(&$theme_registry) {
+
+  if (isset($theme_registry['search_results'])) {
+    $theme_registry['search_results']['variables']['search_page'] = NULL;
+  }
+}
+
+/**
+ * Theme the highlighted snippet text for a search entry.
+ *
+ * @param array $vars
+ *
+ */
+function theme_solrsearch_search_snippets($vars) {
+  $result = '';
+  if (is_array($vars['snippets'])) {
+    $snippets = $vars['snippets'];
+    if (isset($snippets['content'])) {
+      $result .= implode(' ... ', $snippets['content']);
+      unset($snippets['content']);
+    }
+    if (isset($snippets['teaser'])) {
+      $result .= (strlen($result) > 0) ? ' ... ' : '';
+      $result .= implode(' ... ', $snippets['teaser']);
+      unset($snippets['teaser']);
+    }
+    if (count($snippets)) {
+      $result .= (strlen($result) > 0) ? ' ... ' : '';
+      foreach ($snippets as $snippet) {
+        $result .= implode(' ... ', $snippet);
+      }
+    }
+  }
+  return $result . ' ...';
+}
+
+/**
+ * Brief message to display when no results match the query.
+ *
+ * @see search_help()
+ */
+function theme_solrsearch_search_noresults() {
+  return t('<ul>
+<li>Check if your spelling is correct, or try removing filters.</li>
+<li>Remove quotes around phrases to match each word individually: <em>"blue drop"</em> will match less than <em>blue drop</em>.</li>
+<li>You can require or exclude terms using + and -: <em>big +blue drop</em> will require a match on <em>blue</em> while <em>big blue -drop</em> will exclude results that contain <em>drop</em>.</li>
+</ul>');
+}
+
+
+
+
+/**
+ * Implements hook_forms().
+ */
+function solrsearch_search_forms() {
+  $forms['solrsearch_search_block_form']= array(
+      'callback' => 'solrsearch_search_box',
+      'callback arguments' => array('solrsearch_search_block_form'),
+  );
+
+  $forms['solrsearch_search_author_block_form']= array(
+      'callback' => 'solrsearch_search_author_box',
+      'callback arguments' => array('solrsearch_search_author_block_form'),
+  );
+
+  $forms['solrsearch_search_title_block_form']= array(
+      'callback' => 'solrsearch_search_title_box',
+      'callback arguments' => array('solrsearch_search_title_block_form'),
+  );
+  return $forms;
+}
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search.pages.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * @file
+ *   Provides the page callback for user defined search pages.
+ */
+
+/**
+ * Returns search results on user defined search pages.
+ */
+function UNUSED_solrsearch_search_custom_page($page_id, $keys = '', $path_replacer = NULL) {
+  $search_page = solrsearch_search_page_load($page_id);
+  if (empty($search_page)) {
+    drupal_set_message(t('This search page cannot be found'), 'error');
+    return drupal_not_found();
+  }
+  // Add our replacement value in the conditions array
+  if (!empty($path_replacer)) {
+    $search_page['settings']['solrsearch_search_path_replacer'] = $path_replacer;
+  }
+  // Replace dynamic path with current path
+  $search_page['search_path'] = str_replace('%', $path_replacer, $search_page['search_path']);
+  // Retrieve the conditions that apply to this page
+  $conditions = solrsearch_search_conditions_default($search_page);
+  // Process our keys so they are clean
+  $keys = rawurldecode($keys);
+  // Retrieve the results of the search
+
+  $results = solrsearch_search_search_results($keys, $conditions, $search_page);
+  // Initiate our build array
+  $build = array();
+  // Add a custom search form if required
+
+  if (!empty($search_page['settings']['solrsearch_search_search_box'])) {
+    // Adds the search form to the page.
+    $build['search_form'] = drupal_get_form('solrsearch_search_custom_page_search_form', $search_page, $keys);
+  }
+  // Build our page and allow modification.
+  $build_results = solrsearch_search_search_page_custom($results, $search_page, $build);
+  return $build_results;
+}
+
+/**
+ * Search for placed on user defined search pages.
+ */
+function UNUSED_solrsearch_search_custom_page_search_form($form, &$form_state, $search_page, $keys = '') {
+  // Loads the core Search CSS file, use the core search module's classes.
+  drupal_add_css(drupal_get_path('module', 'search') . '/search.css');
+  $form = array();
+  $form['#id'] = 'search-form';
+  $form['#attributes']['class'][] = 'search-form';
+  $form['#search_page'] = $search_page;
+  $form['basic'] = array(
+    '#type' => 'container',
+    '#attributes' => array('class' => array('container-inline')),
+  );
+  $form['basic']['keys'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Enter terms'),
+    '#default_value' => $keys,
+    '#size' => 20,
+    '#maxlength' => 255,
+  );
+  $form['basic']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Search'),
+  );
+
+  $form['basic']['get'] = array(
+    '#type' => 'hidden',
+    '#default_value' => json_encode(array_diff_key($_GET, array('q' => 1, 'page' => 1, 'solrsort' => 1, 'retain-filters' => 1))),
+  );
+
+  $fq = NULL;
+
+  if (solrsearch_has_searched($search_page['env_id'])) {
+    $query = solrsearch_current_query($search_page['env_id']);
+    // We use the presence of filter query params as a flag for the retain filters checkbox.
+    $fq = $query->getParam('fq');
+  }
+
+  if ($fq || isset($form_state['input']['retain-filters'])) {
+    $form['basic']['retain-filters'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Retain current filters'),
+      '#default_value' => (int) !empty($_GET['retain-filters']),
+    );
+  }
+
+  return $form;
+}
+
+/**
+ * Processes solrsearch_search_custom_page_search_form submissions.
+ */
+function UNUSED_solrsearch_search_custom_page_search_form_submit(&$form, &$form_state) {
+  $search_page = $form['#search_page'];
+  $redirect = $search_page['search_path'];
+
+  // Also encode slashes so we don't get akward situations when obtaining the
+  // search key. We can't use drupal_encode_path because for "aestetic" reasons
+  // they don't encode slashes...
+  $redirect_value = rawurlencode($form_state['values']['keys']);
+
+  if (strlen($form_state['values']['keys'])) {
+    $redirect .= '/' . $redirect_value;
+  }
+
+  $get = array();
+  if (isset($form_state['values']['get'])) {
+    $get = json_decode($form_state['values']['get'], TRUE);
+  }
+  if (!empty($form_state['values']['retain-filters'])) {
+    // Add our saved values
+    $get['retain-filters'] = '1';
+  }
+  else {
+    // Remove all filters
+    if (!empty($search_page['settings']['solrsearch_search_allow_user_input'])) {
+      unset($get['fq']);
+    }
+    if (module_exists('facetapi')) {
+      unset($get['f']);
+    }
+  }
+
+  // Add the query values into the redirect.
+  $form_state['redirect'] = array($redirect, array('query' => $get));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search_author_block.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Form builder; Output a search form for the search block's search box.
+ *
+ * @ingroup forms
+ * @see search_box_form_submit()
+ * @see search-block-form.tpl.php
+ */
+function solrsearch_search_author_box($form, &$form_state, $form_id) {
+  $form[$form_id] = array(
+      '#type' => 'textfield',
+      '#title' => t('Search'),
+      '#title_display' => 'invisible',
+      '#size' => 15,
+      '#default_value' => '',
+      '#attributes' => array('title' => t('Enter the terms you wish to search for.')),
+  );
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Search'));
+  $form['#submit'][] = 'solrsearch_search_author_box_form_submit';
+
+  return $form;
+}
+
+/**
+ * Process a block search form submission.
+ */
+function solrsearch_search_author_box_form_submit($form, &$form_state) {
+  // The search form relies on control of the redirect destination for its
+  // functionality, so we override any static destination set in the request,
+  // for example by drupal_access_denied() or drupal_not_found()
+  // (see http://drupal.org/node/292565).
+  if (isset($_GET['destination'])) {
+    unset($_GET['destination']);
+  }
+
+  // Check to see if the form was submitted empty.
+  // If it is empty, display an error message.
+  // (This method is used instead of setting #required to TRUE for this field
+  // because that results in a confusing error message.  It would say a plain
+  // "field is required" because the search keywords field has no title.
+  // The error message would also complain about a missing #title field.)
+  if ($form_state['values']['solrsearch_search_author_block_form'] == '') {
+    form_set_error('keys', t('Please enter some keywords.'));
+  }
+
+  $form_id = $form['form_id']['#value'];
+  $info = search_get_default_module_info();
+  if ($info) {
+    $form_state['redirect'] = 'solrsearch/' . $info['path'] . '/IM_author:' . trim($form_state['values'][$form_id]);
+  }
+  else {
+    form_set_error(NULL, t('Search is currently disabled.'), 'error');
+  }
+}
+
+/**
+ * Process variables for search-block-form.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $form
+ *
+ * @see search-block-form.tpl.php
+ */
+function template_preprocess_solrsearch_search_author_block_form(&$variables) {
+  $variables['search'] = array();
+  $hidden = array();
+  // Provide variables named after form keys so themers can print each element independently.
+  foreach (element_children($variables['form']) as $key) {
+    $type = $variables['form'][$key]['#type'];
+    if ($type == 'hidden' || $type == 'token') {
+      $hidden[] = drupal_render($variables['form'][$key]);
+    }
+    else {
+      $variables['search'][$key] = drupal_render($variables['form'][$key]);
+    }
+  }
+  // Hidden form elements have no value to themers. No need for separation.
+  $variables['search']['hidden'] = implode($hidden);
+  // Collect all form elements to make it easier to print the whole form.
+  $variables['solrsearch_search_form'] = implode($variables['search']);
+}
+
+
+
+function solrsearch_search_author_block(){
+
+  $block['content'] = drupal_get_form('solrsearch_search_author_block_form');
+  return $block;
+
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search_blocks.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * Process variables for search-block-form.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $form
+ *
+ * @see search-block-form.tpl.php
+ */
+function template_preprocess_solrsearch_search_block_form(&$variables) {
+  $variables['search'] = array();
+  $hidden = array();
+  // Provide variables named after form keys so themers can print each element independently.
+  foreach (element_children($variables['form']) as $key) {
+    $type = $variables['form'][$key]['#type'];
+    if ($type == 'hidden' || $type == 'token') {
+      $hidden[] = drupal_render($variables['form'][$key]);
+    }
+    else {
+      $variables['search'][$key] = drupal_render($variables['form'][$key]);
+    }
+  }
+  // Hidden form elements have no value to themers. No need for separation.
+  $variables['search']['hidden'] = implode($hidden);
+  // Collect all form elements to make it easier to print the whole form.
+  $variables['solrsearch_search_form'] = implode($variables['search']);
+}
+
+/**
+ * Form builder; Output a search form for the search block's search box.
+ *
+ * @ingroup forms
+ * @see search_box_form_submit()
+ * @see search-block-form.tpl.php
+ */
+function solrsearch_search_box($form, &$form_state, $form_id) {
+  $form[$form_id] = array(
+      '#type' => 'textfield',
+      '#title' => t('Search'),
+      '#title_display' => 'invisible',
+      '#size' => 15,
+      '#default_value' => '',
+      '#attributes' => array('title' => t('Enter the terms you wish to search for.')),
+  );
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Search'));
+  $form['#submit'][] = 'solrsearch_search_box_form_submit';
+
+  return $form;
+}
+
+/**
+ * Process a block search form submission.
+ */
+function solrsearch_search_box_form_submit($form, &$form_state) {
+  // The search form relies on control of the redirect destination for its
+  // functionality, so we override any static destination set in the request,
+  // for example by drupal_access_denied() or drupal_not_found()
+  // (see http://drupal.org/node/292565).
+  if (isset($_GET['destination'])) {
+    unset($_GET['destination']);
+  }
+
+  // Check to see if the form was submitted empty.
+  // If it is empty, display an error message.
+  // (This method is used instead of setting #required to TRUE for this field
+  // because that results in a confusing error message.  It would say a plain
+  // "field is required" because the search keywords field has no title.
+  // The error message would also complain about a missing #title field.)
+  if ($form_state['values']['solrsearch_search_block_form'] == '') {
+    form_set_error('keys', t('Please enter some keywords.'));
+  }
+
+  $form_id = $form['form_id']['#value'];
+  $info = search_get_default_module_info();
+  if ($info) {
+    $form_state['redirect'] = 'solrsearch/' . $info['path'] . '/' . trim($form_state['values'][$form_id]);
+  }
+  else {
+    form_set_error(NULL, t('Search is currently disabled.'), 'error');
+  }
+}
+
+
+
+
+function solrsearch_search_block(){
+
+  $block['content'] = drupal_get_form('solrsearch_search_block_form');
+  return $block;
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_search_title_block.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Form builder; Output a search form for the search block's search box.
+ *
+ * @ingroup forms
+ * @see search_box_form_submit()
+ * @see search-block-form.tpl.php
+ */
+function solrsearch_search_title_box($form, &$form_state, $form_id) {
+  $form[$form_id] = array(
+      '#type' => 'textfield',
+      '#title' => t('Search'),
+      '#title_display' => 'invisible',
+      '#size' => 15,
+      '#default_value' => '',
+      '#attributes' => array('title' => t('Enter the terms you wish to search for.')),
+  );
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Search'));
+  $form['#submit'][] = 'solrsearch_search_title_box_form_submit';
+
+  return $form;
+}
+
+/**
+ * Process a block search form submission.
+ */
+function solrsearch_search_title_box_form_submit($form, &$form_state) {
+  // The search form relies on control of the redirect destination for its
+  // functionality, so we override any static destination set in the request,
+  // for example by drupal_access_denied() or drupal_not_found()
+  // (see http://drupal.org/node/292565).
+  if (isset($_GET['destination'])) {
+    unset($_GET['destination']);
+  }
+
+  // Check to see if the form was submitted empty.
+  // If it is empty, display an error message.
+  // (This method is used instead of setting #required to TRUE for this field
+  // because that results in a confusing error message.  It would say a plain
+  // "field is required" because the search keywords field has no title.
+  // The error message would also complain about a missing #title field.)
+  if ($form_state['values']['solrsearch_search_title_block_form'] == '') {
+    form_set_error('keys', t('Please enter some keywords.'));
+  }
+
+  $form_id = $form['form_id']['#value'];
+  $info = search_get_default_module_info();
+  if ($info) {
+    $form_state['redirect'] = 'solrsearch/' . $info['path'] . '/IM_title:' . trim($form_state['values'][$form_id]);
+  }
+  else {
+    form_set_error(NULL, t('Search is currently disabled.'), 'error');
+  }
+}
+
+/**
+ * Process variables for search-block-form.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $form
+ *
+ * @see search-block-form.tpl.php
+ */
+function template_preprocess_solrsearch_search_title_block_form(&$variables) {
+  $variables['search'] = array();
+  $hidden = array();
+  // Provide variables named after form keys so themers can print each element independently.
+  foreach (element_children($variables['form']) as $key) {
+    $type = $variables['form'][$key]['#type'];
+    if ($type == 'hidden' || $type == 'token') {
+      $hidden[] = drupal_render($variables['form'][$key]);
+    }
+    else {
+      $variables['search'][$key] = drupal_render($variables['form'][$key]);
+    }
+  }
+  // Hidden form elements have no value to themers. No need for separation.
+  $variables['search']['hidden'] = implode($hidden);
+  // Collect all form elements to make it easier to print the whole form.
+  $variables['solrsearch_search_form'] = implode($variables['search']);
+}
+
+
+
+function solrsearch_search_title_block(){
+
+  $block['content'] = drupal_get_form('solrsearch_search_title_block_form');
+  return $block;
+
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solrsearch_terms.inc	Mon Jun 08 10:21:54 2015 +0200
@@ -0,0 +1,111 @@
+<?php
+
+
+function solrsearch_load_data_x($field,$letter){
+  #todo cache this!
+  $env = solrsearch_environment_load('echosearch');
+  //$searchStr = "/terms?q=doc-type:indexMeta%20and%20access-type:free&json.nl=map&wt=json&terms.fl=" . $field . "&terms.limit=-1&terms.sort=index&term.mincount=1&terms.prefix=" . $letter;
+  $searchStr = "/terms?q=access-type:free&json.nl=map&wt=json&terms.fl=" . $field . "&terms.limit=-1&terms.sort=index&term.mincount=1&terms.prefix=" . $letter;
+
+
+  $url = $env['url'];
+  $result = drupal_http_request($url . $searchStr);
+  $data = drupal_json_decode($result->data);
+  $authorsAll=$data['terms'][$field];
+  return $authorsAll;
+}
+
+function solrsearch_load_data($field,$letter,$accessType="free"){
+  #todo cache this!
+  $env = solrsearch_environment_load('echosearch');
+ // $searchStr = "/select?q=". $field. ":". $letter . "*%20and%20doc-type:indexMeta%20&json.nl=map&facet.sort=index&facet.mincount=1&wt=json&facet=true&facet.field=" . $field . "&facet.limit=100000&start=0&rows=9";
+  $searchStr = "/select?q=". $field. ":". $letter . "*&json.nl=map&facet.sort=index&facet.mincount=1&wt=json&facet=true&facet.field=" . $field . "&facet.limit=100000&start=0&rows=9";
+
+
+  if ($accessType=="free")
+  {
+    $searchStr .="&fq=access-type:free";
+  }
+
+  $url = $env['url'];
+  $result = drupal_http_request($url . $searchStr);
+  $data = drupal_json_decode($result->data);
+  #return $data;
+
+  $resultsAll=$data['facet_counts']['facet_fields'][$field];
+
+  #result list ist zu lang, da hier alle werke gefunden werden, bei denene ein Autor mit dem Buchstaben beginnt.
+  $res=array();
+  foreach ($resultsAll as $author => $val){
+    if (drupal_substr($author, 0,1) == $letter){
+      $res[$author]=$val;
+    }
+
+  }
+
+  return $res;
+}
+
+#
+
+function solrsearch_term_select_field(){
+  $query = $_GET;
+  if (isset($query['browseField'])){
+    drupal_goto("solrsearch-terms/" .$query['browseField'] );
+  }
+
+}
+function solrsearch_term_list($field="",$letter="A",$numperpage=20){
+
+  if ($field==""){
+    return '';
+  }
+
+
+
+ #field z.b. author_c
+
+  if (!user_access("view restricted content")){
+    $accessType="free";
+  } else {
+    $accessType="";
+  }
+
+  $authorsAll=solrsearch_load_data($field,$letter,$accessType);
+  $cnt=count($authorsAll);
+
+  $page  = pager_find_page();
+  $offset = $numperpage * $page;
+  $authors = array_slice($authorsAll, $offset,$slice_lenght=$numperpage);
+
+  pager_default_initialize($cnt, 10);
+
+  $rs =  theme('solrsearch_term_selection_form',array('field' => $field));
+  $rs .=theme('pager', array('tags' => array('<<','<','..','>','>>'),'quantity' => 3));
+
+  if ($field=='author_c'){
+    $rs .=  theme('solrsearch_term_list_author',array('authors' => $authors,'cnt' => $cnt,'letter' => $letter));
+  } else {
+    $rs .=  theme('solrsearch_term_list_title',array('titles' => $authors,'cnt' => $cnt,'letter' => $letter));
+  }
+
+
+  $rs .=theme('pager', array('tags' => array('<<','<','..','>','>>'),'quantity' => 3));
+
+  return $rs;
+  }
+
+
+  function solrsearch_alphapager($field) {
+    $attributes = array( 'class' => 'alpha-page' );
+    $output = "";
+
+    foreach(range('A', 'Z') as $letter) {
+      $output .= "
+          <li>" . l($letter,  "solrsearch-terms/" . $field . "/" . $letter, $attributes). "</li>";
+    }
+
+
+
+    return $output;
+  }