changeset 0:015d06b10d37 default tip

initial
author dwinter
date Wed, 31 Jul 2013 13:49:13 +0200
parents
children
files sites/all/modules/custom/digitalobjects/Copy of digitalobjects-item.tpl.php sites/all/modules/custom/digitalobjects/digitalobject_reader.php sites/all/modules/custom/digitalobjects/digitalobjects-current-collection.tpl.php sites/all/modules/custom/digitalobjects/digitalobjects-item-short.tpl.php sites/all/modules/custom/digitalobjects/digitalobjects-item-tools.tpl.php sites/all/modules/custom/digitalobjects/digitalobjects-item-usage.tpl.php sites/all/modules/custom/digitalobjects/digitalobjects-item.tpl.php sites/all/modules/custom/digitalobjects/digitalobjects-user-admin-form.tpl.php sites/all/modules/custom/digitalobjects/digitalobjects.admin.inc sites/all/modules/custom/digitalobjects/digitalobjects.collections.inc sites/all/modules/custom/digitalobjects/digitalobjects.css sites/all/modules/custom/digitalobjects/digitalobjects.import.php sites/all/modules/custom/digitalobjects/digitalobjects.info sites/all/modules/custom/digitalobjects/digitalobjects.install sites/all/modules/custom/digitalobjects/digitalobjects.item.inc sites/all/modules/custom/digitalobjects/digitalobjects.module sites/all/modules/custom/digitalobjects/digitalobjects.user.inc sites/all/modules/custom/mediathek/mediathek-theme-mediathek-mediaviewer.tpl.php sites/all/modules/custom/mediathek/mediathek.info sites/all/modules/custom/mediathek/mediathek.install sites/all/modules/custom/mediathek/mediathek.module sites/all/modules/custom/solrconnect/CHANGELOG.txt sites/all/modules/custom/solrconnect/Drupal_Apache_Solr_Service.php sites/all/modules/custom/solrconnect/LICENSE.txt sites/all/modules/custom/solrconnect/README.txt sites/all/modules/custom/solrconnect/Solr_Base_Query.php sites/all/modules/custom/solrconnect/apachesolr.admin.inc sites/all/modules/custom/solrconnect/apachesolr.api.php sites/all/modules/custom/solrconnect/apachesolr.css sites/all/modules/custom/solrconnect/apachesolr.index.inc sites/all/modules/custom/solrconnect/apachesolr.info sites/all/modules/custom/solrconnect/apachesolr.install sites/all/modules/custom/solrconnect/apachesolr.interface.inc sites/all/modules/custom/solrconnect/apachesolr.module sites/all/modules/custom/solrconnect/apachesolr_access/apachesolr_access.info sites/all/modules/custom/solrconnect/apachesolr_access/apachesolr_access.module sites/all/modules/custom/solrconnect/apachesolr_access/tests/apachesolr_access.test sites/all/modules/custom/solrconnect/apachesolr_search.admin.inc sites/all/modules/custom/solrconnect/apachesolr_search.info sites/all/modules/custom/solrconnect/apachesolr_search.install sites/all/modules/custom/solrconnect/apachesolr_search.module sites/all/modules/custom/solrconnect/apachesolr_search.pages.inc sites/all/modules/custom/solrconnect/drush/apachesolr.drush.inc sites/all/modules/custom/solrconnect/plugins/facetapi/adapter.inc sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_date.inc sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_geo.inc sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_numeric_range.inc sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_term.inc sites/all/modules/custom/solrconnect/solr-conf/solr-1.4/protwords.txt sites/all/modules/custom/solrconnect/solr-conf/solr-1.4/schema.xml sites/all/modules/custom/solrconnect/solr-conf/solr-1.4/solrconfig.xml sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/protwords.txt sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/schema.xml sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/schema_extra_fields.xml sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/schema_extra_types.xml sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/solrconfig.xml sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/solrconfig_extra.xml sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/solrcore.properties sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/protwords.txt sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/schema.xml sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/schema_extra_fields.xml sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/schema_extra_types.xml sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/solrconfig.xml sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/solrconfig_extra.xml sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/solrcore.properties sites/all/modules/custom/solrconnect/task-check.png sites/all/modules/custom/solrconnect/tests/Dummy_Solr.php sites/all/modules/custom/solrconnect/tests/apachesolr_base.test sites/all/modules/custom/solrconnect/tests/apachesolr_test/apachesolr_test.info sites/all/modules/custom/solrconnect/tests/apachesolr_test/apachesolr_test.module sites/all/modules/custom/solrconnect/tests/conf/README.txt sites/all/modules/custom/solrconnect/tests/conf/elevate.xml sites/all/modules/custom/solrconnect/tests/conf/mapping-ISOLatin1Accent.txt sites/all/modules/custom/solrconnect/tests/conf/protwords.txt sites/all/modules/custom/solrconnect/tests/conf/stopwords.txt sites/all/modules/custom/solrconnect/tests/conf/synonyms.txt sites/all/modules/custom/solrconnect/tests/solr_base_query.test sites/all/modules/custom/solrconnect/tests/solr_base_subquery.test sites/all/modules/custom/solrconnect/tests/solr_document.test sites/all/modules/custom/solrconnect/tests/solr_index_and_search.test sites/all/modules/custom/solrext/solrext.css sites/all/modules/custom/solrext/solrext.info sites/all/modules/custom/solrext/solrext.module sites/all/modules/custom/solrsearch/Apache_Solr_Document.php sites/all/modules/custom/solrsearch/Drupal_Apache_Solr_Service.php sites/all/modules/custom/solrsearch/Solr_Base_Query.php sites/all/modules/custom/solrsearch/apachesolr.admin.inc sites/all/modules/custom/solrsearch/facetapi.callbacks.inc sites/all/modules/custom/solrsearch/plugins/facetapi/adapter.inc sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_date.inc sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_geo.inc sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_integer.inc sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_numeric_range.inc sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_term.inc sites/all/modules/custom/solrsearch/solr-search-result.tpl.php sites/all/modules/custom/solrsearch/solr-search-results.tpl.php sites/all/modules/custom/solrsearch/solrsearch-author-block-form.tpl.php sites/all/modules/custom/solrsearch/solrsearch-block-form.tpl.php sites/all/modules/custom/solrsearch/solrsearch-mpiwg.js sites/all/modules/custom/solrsearch/solrsearch-term-list-author.tpl.php sites/all/modules/custom/solrsearch/solrsearch-term-list-title.tpl.php sites/all/modules/custom/solrsearch/solrsearch-term-selection-form.tpl.php sites/all/modules/custom/solrsearch/solrsearch-title-block-form.tpl.php sites/all/modules/custom/solrsearch/solrsearch.admin.inc sites/all/modules/custom/solrsearch/solrsearch.css sites/all/modules/custom/solrsearch/solrsearch.index.inc_unused sites/all/modules/custom/solrsearch/solrsearch.info sites/all/modules/custom/solrsearch/solrsearch.install sites/all/modules/custom/solrsearch/solrsearch.interface.inc sites/all/modules/custom/solrsearch/solrsearch.module sites/all/modules/custom/solrsearch/solrsearch.pages.inc sites/all/modules/custom/solrsearch/solrsearch_search.admin.inc sites/all/modules/custom/solrsearch/solrsearch_search.info sites/all/modules/custom/solrsearch/solrsearch_search.install sites/all/modules/custom/solrsearch/solrsearch_search.module sites/all/modules/custom/solrsearch/solrsearch_search.pages.inc sites/all/modules/custom/solrsearch/solrsearch_search_author_block.inc sites/all/modules/custom/solrsearch/solrsearch_search_blocks.inc sites/all/modules/custom/solrsearch/solrsearch_search_title_block.inc sites/all/modules/custom/solrsearch/solrsearch_terms.inc sites/all/modules/custom/solrsearch_autocomplete/LICENSE.txt sites/all/modules/custom/solrsearch_autocomplete/README.txt sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/changelog.txt sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/jquery.autocomplete.css sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/jquery.autocomplete.js sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/todo sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.css sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.info sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.install sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.js sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.module sites/all/themes/mpiwgDev/README.txt sites/all/themes/mpiwgDev/block.tpl.php sites/all/themes/mpiwgDev/color/base.png sites/all/themes/mpiwgDev/color/color.inc sites/all/themes/mpiwgDev/color/preview.css sites/all/themes/mpiwgDev/color/preview.html sites/all/themes/mpiwgDev/color/preview.js sites/all/themes/mpiwgDev/color/preview.png sites/all/themes/mpiwgDev/icons/blue/Download.png sites/all/themes/mpiwgDev/icons/blue/Email.png sites/all/themes/mpiwgDev/icons/blue/IconsSatellitenseiten.psd sites/all/themes/mpiwgDev/icons/blue/LinkExtern.png sites/all/themes/mpiwgDev/icons/blue/LinkIntern.png sites/all/themes/mpiwgDev/layout.css sites/all/themes/mpiwgDev/layout_orig.css sites/all/themes/mpiwgDev/logo.png sites/all/themes/mpiwgDev/logo_BW.png sites/all/themes/mpiwgDev/logo_old.png sites/all/themes/mpiwgDev/main_template.zpt sites/all/themes/mpiwgDev/mpiwgDev.info sites/all/themes/mpiwgDev/node.tpl.php sites/all/themes/mpiwgDev/page.tpl.php sites/all/themes/mpiwgDev/page.tpl_orig.php sites/all/themes/mpiwgDev/screenshot.png sites/all/themes/mpiwgDev/screenshot_old.png sites/all/themes/mpiwgDev/template.php sites/all/themes/mpiwgDev/theme-settings.php
diffstat 157 files changed, 46529 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/Copy of digitalobjects-item.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,105 @@
+
+
+<h2> Hello </h2>
+
+<?php print $objid ?>
+  <h3 class="title"<?php print $title_attributes; ?>>
+    <a href="<?php print $objdata['viewerurl']; ?>"><?php print $title ?> </a>
+  </h3>
+<a href="<?php print $objdata['viewerurl']; ?>"><img src="<?php print $objdata['thumburl']; ?>"/></a>
+
+
+ <div class="col main">
+      <!-- main content column -->
+      <div class="index-info">
+        <h2>Bibliographic information</h2>
+        <table border="0">
+          <tal:x condition="python:formattedData">
+            <!-- wenn es bibinfo in docinfo gibt -->
+            <tr tal:replace="structure python:formattedData" />
+          </tal:x>
+
+          <tal:x condition="not:formattedData">
+            <!-- kein template fuer die daten -->
+            <tal:block condition="exists:docinfo/bib">
+              <tal:block tal:define="bibinfo docinfo/bib" tal:repeat="bib bibinfo">
+                <tr tal:condition="python:bib[0]!='@'">
+                  <td class="type" tal:content="python:bib.capitalize().replace('_',' ') + ':'" />
+                  <td class="content" tal:content="python:bibinfo[bib]" />
+                </tr>
+              </tal:block>
+            </tal:block>
+
+            <tal:y condition="not:exists:docinfo/bib">
+              <!-- wenn es kein bibinfo gibt (archimedes-texte) -->
+              <tr>
+                <td class="type">Author:</td>
+                <td class="content" tal:content="docinfo/creator" />
+              </tr>
+              <tr>
+                <td class="type">Title:</td>
+                <td class="content" tal:content="docinfo/title" />
+              </tr>
+              <tr>
+                <td class="type">Date:</td>
+                <td class="content" tal:content="docinfo/date" />
+              </tr>
+            </tal:y>
+          </tal:x>
+          <!-- ende kein template fuer die daten -->
+        </table>
+
+        <tal:block tal:define="dri docinfo/DRI | nothing" tal:condition="dri">
+          <h2>Permanent URL</h2>
+          <table>
+            <tr>
+              <td class="type">Document ID:</td>
+              <td class="content" tal:content="dri" />
+            </tr>
+            <tr>
+              <td class="type">Permanent URL:</td>
+              <td class="content"><a target="_blank" tal:attributes="href string:http://echo.mpiwg-berlin.mpg.de/$dri"
+                tal:content="string:http://echo.mpiwg-berlin.mpg.de/$dri" /></td>
+            </tr>
+          </table>
+        </tal:block>
+
+        <tal:block tal:define="ctxs docinfo/presentationContext | nothing" tal:condition="ctxs">
+          <h2>Presentation context</h2>
+          <ul>
+            <li tal:repeat="ctx ctxs"><a tal:define="link ctx/link | nothing; name ctx/name | link;" tal:content="name"
+              tal:attributes="href link" target="_blank" tal:omit-tag="not:link" /></li>
+          </ul>
+        </tal:block>
+
+        <tal:block tal:define="attribution docinfo/attribution | nothing; copyright docinfo/copyright | nothing">
+          <h2>Copyright information</h2>
+          <table border="0" tal:condition="attribution | copyright">
+            <!-- attribution -->
+            <tr tal:condition="attribution"
+              tal:replace="structure python:here.metadataService.getAttributionFormatted('metadata_template', data=attribution)" />
+            <!-- copyright -->
+            <tr tal:condition="copyright"
+              tal:replace="structure python:here.metadataService.getCopyrightFormatted('metadata_template', data=copyright)" />
+          </table>
+          <table border="0" tal:condition="not:attribution | copyright">
+            <tr>
+              <td class="type">Copyright:</td>
+              <td class="content"><a target="_blank" href="http://www.mpiwg-berlin.mpg.de">Max Planck Institute for the
+                  History of Science</a> (unless stated otherwise)</td>
+            </tr>
+            <tr tal:define="accType python:docinfo.get('accessType', None)">
+              <td class="type">License:</td>
+              <td tal:condition="python:accType == 'free'" class="content"><a target="_blank"
+                href="http://creativecommons.org/licenses/by-sa/3.0/de/">CC-BY-SA</a> (unless stated otherwise)</td>
+              <td tal:condition="python:accType != 'free'" class="content">Internal use only<span tal:condition="accType"
+                tal:content="string: ($accType)" />, please contact <a href="mailto:library@mpiwg-berlin.mpg.de">library@mpiwg-berlin.mpg.de</a>
+                (unless stated otherwise)
+              </td>
+            </tr>
+          </table>
+        </tal:block>
+      </div>
+    </div>
+    <!-- /main content column -->
+  </div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobject_reader.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,130 @@
+<?php
+
+/* function reads the metadata from index meta and gives it back to the
+ * template.
+ */
+
+#http://localhost:18080/ECHOdocuView/getDocinfo?mode=imagepath&url=http://md.mpiwg-berlin.mpg.de/indexMeta/MPIWG:WEBVEZ22
+function digitalobject_getMetadata($objid){
+
+  $base = variable_get('digitalobjects_docuviewer_path');
+  #$path="http://localhost:18080/ECHOdocuView/getDocinfoJSON?mode=texttool&url=http://md.mpiwg-berlin.mpg.de/indexMeta/" . $objid;
+  $path = $base . "/getDocinfoJSON?mode=texttool&url=http://md.mpiwg-berlin.mpg.de/indexMeta/" . $objid;
+  $request = drupal_http_request($path);
+  #TODO give out a status message  reports if servers is not available
+
+  if (isset($request->data)){
+    return drupal_json_decode($request->data);
+  }
+  else {
+    return null;
+  }
+
+}
+
+function digitalobjects_getBibFormattedMetaData($bibdata){
+  $bd = drupal_json_encode($bibdata);
+
+  $base = variable_get('digitalobjects_bibdata_path');
+  #$path="http://localhost:18080/metadata/getBibFormattedMetaDataJSON";
+  $path = $base ."/getBibFormattedMetaDataJSON";
+  $options = array(
+
+      'method' => 'POST',
+      'data' => 'bibdata=' . $bd
+  );
+  $request = drupal_http_request($path,  $options);
+  return drupal_json_decode($request->data);
+
+}
+
+function digitalobjects_getBibFormattedLabel($bibdata){
+  $bd = drupal_json_encode($bibdata);
+
+  $base = variable_get('digitalobjects_bibdata_path');
+  #$path="http://localhost:18080/metadata/getBibFormattedMetaDataJSON";
+  $path = $base ."/getBibFormattedLabelJSON";
+  $options = array(
+
+      'method' => 'POST',
+      'data' => 'bibdata=' . $bd
+  );
+  $request = drupal_http_request($path,  $options);
+  return drupal_json_decode($request->data);
+
+}
+
+
+
+function digitalobjects_getBibMappedData($bibdata){
+  $bd = drupal_json_encode($bibdata);
+
+  $base = variable_get('digitalobjects_bibdata_path');
+  #$path="http://localhost:18080/metadata/getBibFormattedMetaDataJSON";
+  $path = $base ."/getBibMappedDataJSON";
+
+  $options = array(
+
+      'method' => 'POST',
+      'data' => 'bibdata=' . $bd
+  );
+  $request = drupal_http_request($path,  $options);
+  return drupal_json_decode($request->data);
+
+}
+#here.metadata.getBibFormattedMetaData(bibdata=docinfo.get('bib', None))
+
+
+
+function digitalobjects_readMetadata($objid,$format="long"){
+
+  $md = digitalobject_getMetadata($objid);
+
+  switch($format){
+    case "long":
+      $bib = isset($md['bib']) ?digitalobjects_getBibFormattedMetaData($md['bib']): null;
+    break;
+
+    case "short":
+      $bib = isset($md['bib']) ?digitalobjects_getBibFormattedLabel($md['bib']): null;
+  }
+  if ($bib == null) {
+    $bibdata = array (
+        "Author" => $md['creator'],
+        "Title" => $md['title'],
+        "Date" => $md['date']
+    );
+    $bib="";
+    foreach ($bibdata as $key => $content){
+      $bib = $bib . '<tr><td class="type">' . $key . '</td>
+                <td class="content">' . $content . '</td></tr>';
+    }
+
+  }
+
+  $access_type = isset($md['accessType']) ? $md['accessType'] : 'mpiwg';
+  $titlepage = isset($md['titlePage']) ? $md['titlePage'] : 1;
+  $data =  array(
+      "thumburl" => create_thumburl_from_dri($objid,$titlepage=$titlepage),
+      "viewerurl" => create_url_from_dri($objid,$access_type = $access_type),
+      "md" => $md,
+      "bibdata" => $bib,
+      "access_type" => $access_type,
+  );
+
+  return $data;
+}
+
+
+function create_url_from_dri($dri,$access_type = "closed"){
+
+  if ($access_type=="free"){
+    return "http://echo.mpiwg-berlin.mpg.de/".$dri;
+  }
+  else
+    return "http://libcoll.mpiwg-berlin.mpg.de/".$dri;
+}
+
+function create_thumburl_from_dri($dri,$titlepage=1){
+  return "http://md.mpiwg-berlin.mpg.de/purls/image/".$dri."?pn=" . $titlepage;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects-current-collection.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,2 @@
+<h3>Current collection</h3>
+<p><?php print l($node->title,"node/".$node->nid) ?></p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects-item-short.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,26 @@
+
+
+<?php if($access): ?>
+
+<div class="digitalobjects_item">
+<table class="digitalobjects_item_short">
+<tr>
+<td class="digitalobjects_item_image">
+<?php if ($objdata['access_type'] !="free"):?>
+<span class="solrsearch_internal" >
+intern
+</span>
+<?php endif ;?>
+<a href="<?php print $objdata['viewerurl']; ?>"><img src="<?php print $objdata['thumburl']; ?>"/></a>
+
+</td>
+<td align="top">
+<?php print $objdata['bibdata']; ?>
+</td>
+</tr>
+</table>
+</div>
+<?php else: ?>
+<p> <?php print $objid ?>: restricted - do to copyright reasons </p>
+
+<?php endif; ?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects-item-tools.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,17 @@
+<div class="digitalobjects_item_tools">
+<span>
+<?php print l(t("more"),"digitalobject/". $variables['objid']) ?>
+</span>
+<?php if (user_access('administrate digitalobjects')):?>
+<span><?php print l(t("edit"),"digitalobject/". $variables['objid'] ."/edit") ?>
+</span>
+<?php endif;?>
+<span>
+<?php print l(t("usage"),"digitalobject/". $variables['objid'] ."/usage") ?>
+</span>
+<?php if (user_access('manage private collections')):?>
+<span>
+<?php print l(t("add"),"digitalobject/". $variables['objid'] ."/add") ?>
+</span>
+<?php endif;?>
+</div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects-item-usage.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,12 @@
+<h2>Collections where the object is used</h2>
+<table>
+<?php foreach ($variables['data'] as $nid => $title):?>
+<tr>
+<td><?php print l(t($title), "node/$nid");?></td>
+</tr>
+
+
+<?php endforeach ?>
+
+
+</table>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects-item.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,40 @@
+
+
+<?php if($access): ?>
+
+<div class="digitalobjects_item">
+<table class="digitalobjects_item_full>
+<tr>
+<td class="digitalobjects_item_image">
+<?php if ($objdata['access_type'] !="free"):?>
+<span class="solrsearch_internal" >
+intern
+</span>
+<?php endif ;?>
+<a href="<?php print $objdata['viewerurl']; ?>"><img src="<?php print $objdata['thumburl']; ?>"/></a>
+
+</td>
+<td>
+<table><?php print $objdata['bibdata']; ?></table>
+
+
+          <table>
+              <tr>
+                <td class="type">Document ID:</td>
+              <td class="content"> <?php print $objid ?></td>
+            </tr>
+            <tr>
+              <td   class="type">Permanent URL:</td>
+              <td class="content"> <a href="<?php print $objdata['viewerurl']; ?>"><?php print $objdata['viewerurl']; ?> </a></td>
+            </tr>
+          </table>
+
+</table>
+</td>
+</tr>
+</table>
+</div>
+<?php else: ?>
+<p> <?php print $objid ?>: restricted - do to copyright reasons </p>
+
+<?php endif; ?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects-user-admin-form.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,7 @@
+
+  <h2> <?php print t('Set current collection');?></h2>
+  <div class="solrsearch_search_form">
+  <?php print $digitalobjects_user_admin ?>
+  </div>
+  <div><?php print $uid ?>
+</div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.admin.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,35 @@
+<?php
+/**
+ * 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 digitalobjects_settings(array $form, array &$form_state) {
+
+
+  $form['digitalobjects_bibdata_path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('bibdataprovider URL'),
+      '#default_value' => variable_get('digitalobjects_bibdata_path',"http://localhost:18080/metadata"),
+      '#maxlength' => 255,
+      '#description' => t('URL to the bibdataprovider  (no "/" at the end!)'),
+
+  );
+
+  $form['digitalobjects_docuviewer_path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('docuviewer URL'),
+      '#default_value' => variable_get('digitalobjects_docuviewer_path',"http://localhost:18080/ECHOdocuView"),
+      '#maxlength' => 255,
+      '#description' => t('URL to the docuviewer (no "/" at the end!)'),
+
+  );
+
+  return system_settings_form($form);
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.collections.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,239 @@
+<?php
+
+ctools_add_js('auto-submit');
+
+function digitalobjects_user_admin_collection_submit($form,$formstate){
+  $userid = $formstate['values']['userid'];
+
+
+
+  drupal_goto("user/".$userid.'/collections');
+
+}
+function digitalobjects_user_admin_collection($key){
+
+
+
+
+  $form['digitalobjects_user_admin_form'] = drupal_get_form("digitalobjects_user_admin_form");
+  #$form = drupal_get_form("digitalobjects_user_admin_form");
+
+  $user_full=user_load($key);
+  $val = $user_full->field_digitalobjects_cid['und'][0]['objid'];
+
+  $form['digitalobjects_user_admin_form']['digitalcollection']['#default_value']="";
+  $form['digitalobjects_user_admin_form']['digitalcollection']['#value']=$val;
+  $form['digitalobjects_user_admin_form']['userid']['#value']=$key;
+
+  //$form['digitalcollection_chooser'] =  drupal_get_form("digitalcollection_chooser");
+
+
+ # $element = $form;
+
+  $form['chooser']=drupal_get_form("digitalcollection_chooser");
+
+  $form['chooser']['pager'] = array('#markup' => theme('pager'));
+  $form['chooser']['userid']['#value']=$key;
+
+  #$element += $form3;
+
+ #
+  #$element += $form2;
+
+  return $form;
+}
+
+function digitalcollection_chooser(array $form){
+  // Build the sortable table header.
+  $header = array(
+      'title' => array('data' => t('Title'), 'field' => 'n.title'),
+      'nid' => array('data' => t('nid'), 'field' => 'n.nid')
+  );
+
+
+  #$result = db_query("SELECT nid FROM node WHERE type = :ntype ", array ('ntype' => 'digitalcollection'));
+  $result = db_query("SELECT n.nid FROM {node} n ");
+
+  $nids = array();
+  foreach ($result as $record) {
+    $nids[] = $record->nid;
+  }
+  //Get the node data.
+  //$nids = $query
+  //->fields('n',array('nid'))
+  //->limit(50)
+  //->orderByHeader($header)
+  //->execute()
+  //->fetchCol();
+
+
+  $nodes = node_load_multiple($nids);
+
+  //Build the rows.
+  $options = array();
+  $l_options=array("");
+  $destination = drupal_get_destination();
+  $languages = language_list();
+  foreach ($nodes as $node) {
+    $langcode = entity_language('node', $node);
+    $l_options = $langcode != LANGUAGE_NONE && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array();
+
+    $options[$node->nid] = array(
+        'title' => array(
+            'data' => array(
+                '#type' => 'link',
+                '#title' => $node->title,
+                '#href' => 'node/' . $node->nid,
+                '#options' => $l_options,
+                '#suffix' => ' ' ,
+            ),
+        ),
+        'nid' => $node->nid,
+    );
+
+  }
+
+  $destination = drupal_get_destination();
+
+  /*
+   $operations = array();
+  if (node_access('update', $node)) {
+  $operations['edit'] = array(
+      'title' => t('edit'),
+      'href' => 'node/' . $node->nid . '/edit',
+      'query' => $destination,
+  );
+  }
+  if (node_access('delete', $node)) {
+  $operations['delete'] = array(
+      'title' => t('delete'),
+      'href' => 'node/' . $node->nid . '/delete',
+      'query' => $destination,
+  );
+  }
+  $options[$node->nid]['operations'] = array();
+  $options[$node->nid]['operations'] = array(
+      'data' => array(
+          '#theme' => 'links__node_operations',
+          '#links' => $operations,
+          '#attributes' => array('class' => array('links', 'inline')),
+      ),
+  );
+
+
+  */
+
+
+
+
+  $form['digitalcollection_chooser'] = array(
+      '#type' => 'tableselect',
+      '#header' => $header,
+      '#options' => $options,
+      '#empty' => t('No content available.'),
+      '#attributes' => array('class' => array('search-form', 'ctools-auto-submit-full-form')),
+      '#multiple' => False,
+
+  );
+
+  #form['chooser']['change'] = array('#type' => 'actions');
+  $form+= $form['submit2'] = array('#type' => 'submit', '#value' => t('Choose'));
+  #$form['chooser']['#submit'][] = 'digitalobjects_user_change_form_submit';
+  $form['submit2']['#submit'][] = 'digitalobjects_user_change_form_submit';
+  #$form['chooser']['#attributes']= array('class' => array('ctools-use-ajax', 'ctools-auto-submit-click'));
+
+  $form['userid'] = array(
+      '#type' => 'hidden',
+  );
+
+  return $form;
+}
+
+
+function digitalobjects_user_change_form_submit($form,$formstate){
+  $input = $formstate['input']['digitalcollection_chooser'];
+  $userid = $formstate['values']['userid'];
+
+  $user=user_load($userid);
+
+
+  $user->field_digitalobjects_cid['und'][0]['objid'] = $input;
+
+  user_save($user);
+
+}
+#füge objid zur aktuelle sammlung hinzu
+function digitalobjects_addToCurrentCollection($objid){
+  global $user;
+  #hole current collection
+  $user_full = user_load($user->uid);
+  $val = $user_full->field_digitalobjects_cid['und'][0]['objid'];
+
+  #hole sammlung
+
+  $node = node_load($val);
+
+  foreach ( $node->field_objid['und'] as $obj){
+    if ($obj['objid']==$objid){
+      return drupal_set_message(t("Already in the current collection"),"error");
+    }
+
+  }
+
+  $node->field_objid['und'][]['objid']=$objid;
+
+  $node = node_submit($node);
+
+  node_save($node);
+
+  drupal_goto("node/".$node->nid);
+}
+
+
+
+function digitalobjects_digitalcollectionsManage(){
+
+  $data = $_GET;
+
+
+  global $user;
+  #hole current collection
+  $user_full = user_load($user->uid);
+  $val = $user_full->field_digitalobjects_cid['und'][0]['objid'];
+
+  #hole sammlung
+
+  $node = node_load($val);
+
+  foreach ($data['digitalobjects_items_select'] as $objid){
+    /*foreach ( $node->field_objid['und'] as $obj){
+      if ($obj['objid']==$objid){
+        return drupal_set_message(t("Already in the current collection"),"error");
+      }
+
+    }
+  */
+    $node->field_objid['und'][]['objid']=$objid;
+
+  }
+
+
+  $node = node_submit($node);
+
+  node_save($node);
+
+  if (isset($data['redirect'])){
+
+  drupal_goto(urldecode($data['redirect']));
+  } else {
+    drupal_goto("node/".$node->nid);
+  }
+
+/*
+  $output['digitalobjects_item']= array(
+      '#theme' => 'digitalobjects_item',
+      '#objid' => '');
+  return $output;
+  */
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,23 @@
+/*
+Cascading Style Sheets, level 2 revision 1. 
+CSS 2.1 Specification is available at 
+http://www.w3.org/TR/2006/WD-CSS21-20061106/
+*/
+
+td.digitalobjects_item_image {
+vertical-align:top;
+}
+
+div.digitalobjects_item {
+	padding-top: 10px;
+	padding-bottom: 10px;
+}
+
+table.digitalobjects_item_short td{
+	vertical-align:top;
+}
+
+
+div.digitalobjects_item_tools{
+	display:inline;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.import.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,159 @@
+<?php
+
+
+
+
+
+function digitalobjects_importPagesFromFile(){
+  $filename = "/tmp/maps.json";
+  # $filename = "/tmp/echo.json";
+  $handle = fopen($filename, "r");
+  $contents = fread($handle, filesize($filename));
+  fclose($handle);
+
+  $pages = drupal_json_decode($contents);
+
+  $cnt=0;
+  foreach ($pages as $key => $page){
+
+    $page = base64_decode($page);
+    digitalobjects_importPage($page,$key);
+    $cnt++;
+
+  }
+
+
+}
+
+
+function digitalobjects_importPage($page,$key){
+
+  $baseUrl = "http://localhost:18080/echo_nav/echo_pages/content/scientific_revolution/harriot/maps/";
+
+  $fnames = explode("/",$key);
+
+  if ($fnames[1] != "maps"){
+    dpm($fnames[0]);
+    return;
+  }
+
+  $fname = str_replace(".pt","1_1.png",$fnames[sizeof($fnames)-1]);
+
+  $image = file_get_contents($baseUrl . $fname); // string
+  $file = file_save_data($image, 'public://' . $fname,FILE_EXISTS_REPLACE);
+
+
+
+
+  $node = new stdClass();
+  $node->type = 'map_page';
+  node_object_prepare($node);
+
+  $node->body['und'][0]['value'] = check_markup($page,'full_html');
+
+  $node->body['und'][0]['format']  = 'full_html';
+
+  $node->path['alias']="harriot/maps/" . $fnames[sizeof($fnames)-1];
+
+  $node->title = "_".$key;
+
+
+  $node->language = LANGUAGE_NONE;
+
+
+  $node->field_echopath['und'][0]['value']=$key;
+
+  $node->field_image[LANGUAGE_NONE]['0']['fid'] = $file->fid;
+  $node = node_submit($node);
+
+
+
+
+  /*$node->field_admintag['und'][0] = array (
+      'tid' => $tid,
+
+  );*/
+  // Try to set your custom field
+
+  $node = node_submit($node);
+
+  node_save($node);
+
+  }
+
+
+
+function digitalobjects_importCollectionFromFile() {
+
+
+  $filename = "/tmp/china.json";
+  $handle = fopen($filename, "r");
+  $contents = fread($handle, filesize($filename));
+  fclose($handle);
+
+  $collection = drupal_json_decode($contents);
+  digitalobjects_importCollection($collection);
+}
+
+function digitalobjects_importCollection($collection,$tid){
+
+  $node = new stdClass();
+  $node->type = 'digitalcollection';
+  node_object_prepare($node);
+
+  $node->body['und'][0]['value'] = check_markup($collection['description'],'full_html');
+
+  $node->body['und'][0]['format']  = 'full_html';
+
+
+  $node->title = $collection['title'];
+
+
+  $node->language = LANGUAGE_NONE;
+
+
+  $node->field_echopath['und'][0]['value']=$collection['echo_path'];
+  $node->field_label['und'][0]['value']=$collection['label'];
+  $node = node_submit($node);
+
+
+  $cnt=0;
+  foreach ($collection['content'] as $cont){
+    print $cont;
+    if (($cont != "") && ($cont !=null)){
+      $node->field_objid['und'][$cnt]['objid']=$cont;
+      $cnt++;
+
+
+    }
+  }
+
+  $node->field_admintag['und'][0] = array (
+      'tid' => $tid,
+
+  );
+  // Try to set your custom field
+
+  $node = node_submit($node);
+
+  node_save($node);
+
+}
+
+function digitalobjects_importCollections($tid) {
+  $filename = "/tmp/export.json";
+ # $filename = "/tmp/echo.json";
+  $handle = fopen($filename, "r");
+  $contents = fread($handle, filesize($filename));
+  fclose($handle);
+
+  $collections = drupal_json_decode($contents);
+
+  $cnt=0;
+  foreach ($collections as $key => $collection){
+    digitalobjects_importCollection($collection,$tid);
+    $cnt++;
+
+  }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,18 @@
+name = digitalobjects
+description = Allows access to the digital library
+core = 7.x
+
+
+configure = admin/config/media/digitalobjects/settings
+
+files[] = 
+files[] = digitalobjects.module
+files[] = digitalobjects.install
+files[] = digitalobjects.item.inc
+files[] = digitalobjects.admin.inc
+files[] = digitalobjects-item.tpl.php
+files[] = digitalobject_reader.php
+files[] = digitalobjects.collections.inc
+
+
+stylesheets[all][] = digitalobjects.css
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.install	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,35 @@
+<?php
+/**
+ * @file
+ * Install, update, and uninstall functions for the field_example module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ *
+ * Defines the database schema of the field, using the format used by the
+ * Schema API.
+ *
+ *
+ * All implementations of hook_field_schema() must be in the module's
+ * .install file.
+ *
+ * @see http://drupal.org/node/146939
+ * @see schemaapi
+ * @see hook_field_schema()
+ * @ingroup field_example
+ */
+
+function digitalobjects_field_schema($field) {
+
+  $columns = array(
+      'objid' => array('type' => 'varchar', 'length' => 20, 'not null' => FALSE),
+  );
+  $indexes = array(
+      'objid' => array('objid'),
+  );
+  return array(
+      'columns' => $columns,
+      'indexes' => $indexes,
+  );
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.item.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,42 @@
+<?php
+
+
+
+/**
+ * Process variables for digitalobject-item.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_digitalobjects_item(&$variables) {
+
+  $variables['objdata'] = digitalobjects_readMetadata($variables['objid']);
+
+  $accessType=isset($variables['objdata']['md']['accessType']) ?$variables['objdata']['md']['accessType']: 'mpiwg';
+  if (!user_access("view restricted content") && $accessType!="free"){
+    $variables['access']=false;
+  } else {
+    $variables['access']=true;
+  }
+
+
+}
+
+function template_preprocess_digitalobjects_item_short(&$variables) {
+
+  $variables['objdata'] = digitalobjects_readMetadata($variables['objid'],$format="short");
+
+
+  $accessType=isset($variables['objdata']['md']['accessType']) ?$variables['objdata']['md']['accessType']: 'mpiwg';
+  if (!user_access("view restricted content") && $accessType!="free"){
+    $variables['access']=false;
+  } else {
+    $variables['access']=true;
+  }
+
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/digitalobjects/digitalobjects.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,602 @@
+<?php
+/* Implements hook_menu */
+
+
+/* TODOS
+ *
+ *
+ */
+include('digitalobject_reader.php');
+include('digitalobjects.import.php');
+
+/**
+ * Implementation of hook_enable().
+ */
+function digitalobjects_enable() {
+
+  #adds a field for the current collection
+  // Check if our field is not already created.
+  if (!field_info_field('field_digitalobjects_cid')) {
+    $field = array(
+        'field_name' => 'field_digitalobjects_cid',
+        'type' => 'digitalobjects_digitalcollection',
+    );
+    field_create_field($field);
+
+    // Create the instance on the bundle.
+    $instance = array(
+        'field_name' => 'field_digitalobjects_cid',
+        'entity_type' => 'user',
+        'label' => 'Current collection',
+        'bundle' => 'user',
+        // If you don't set the "required" property then the field wont be required by default.
+        'required' => False,
+        'settings' => array(
+            // Here you inform either or not you want this field showing up on the registration form.
+            'user_register_form' => 1,
+        ),
+        'widget' => array(
+            'type' => 'digitalcollection_default_textfield',
+        ),
+    );
+    field_create_instance($instance);
+  }
+}
+
+function digitalobjects_menu(){
+
+    $items['digitalcollections/manageCurrent'] = array(
+        'description' => 'show an object.',
+        'page callback' => 'digitalobjects_digitalcollectionsManage',
+        'access arguments'   => array('access content'),
+        'file'               => 'digitalobjects.collections.inc',
+
+    );
+
+
+    $items['digitalobject'] = array(
+        'description' => 'show an object.',
+        'page callback' => 'digitalobjects_page',
+        'access arguments'   => array('access content'),
+
+    );
+     $items['admin/config/media/digitalobjects'] = array(
+            'title'              => 'Digitalobject',
+            'description'        => 'Set path to the metadaprovider for digitalobjects',
+            'weight'             => 10,
+            'page callback'      => 'drupal_get_form',
+            'page arguments'     => array('digitalobjects_settings'),
+            'access arguments'   => array('administrate digitalobjects'),
+            'file'               => 'digitalobjects.admin.inc',
+
+        );
+
+
+
+     $items['user/%/collections'] = array(
+         'title'              => 'Collections',
+         'description'        => 'Set path to the metadaprovider for digitalobjects',
+         'weight'             => 10,
+         'page callback'      => 'digitalobjects_user_admin_collection',
+         'page arguments'     => array(1),
+         'access arguments'   => array('manage private collections'),
+         'file'               => 'digitalobjects.collections.inc',
+         'type' => MENU_LOCAL_TASK,
+     );
+
+
+
+     $items['digitalobject/%/view'] = array(
+        'title'              => 'View',
+               'description' => 'show an object.',
+        'page callback' => 'digitalobjects_page',
+        'access arguments'   => array('access content'),
+            'page arguments'     => array(1),
+
+         'access arguments'   => array('access content'),
+          'type' => MENU_LOCAL_TASK,
+
+     );
+
+     $items['digitalobject/%/edit'] = array(
+         'title'              => 'Edit',
+         'description'        => 'Set path to the metadaprovider for digitalobjects',
+         'weight'             => 10,
+         'page callback'      => 'digitalobjects_edit',
+         'page arguments'     => array(1),
+
+         'access arguments'   => array('administrate digitalobjects'),
+         'type' => MENU_LOCAL_TASK,
+
+     );
+
+     $items['digitalobject/%/usage'] = array(
+         'title'              => 'Usage',
+         'description'        => 'Set path to the metadaprovider for digitalobjects',
+         'weight'             => 10,
+         'page callback'      => 'digitalobjects_show_usage',
+         'page arguments'     => array(1),
+
+         'access arguments'   => array('access content'),
+         'type' => MENU_LOCAL_TASK,
+
+     );
+
+     $items['digitalobject/%/add'] = array(
+         'title'              => 'Add',
+         'description'        => 'Add an object to the current collection',
+         'weight'             => 10,
+         'page callback'      => 'digitalobjects_addToCurrentCollection',
+         'page arguments'     => array(1),
+
+         'access arguments'   => array('administrate digitalobjects'),
+         'file'               => 'digitalobjects.collections.inc',
+         'type' => MENU_LOCAL_TASK,
+
+     );
+    return $items;
+
+}
+
+
+function  digitalobjects_create_primary_node($objid){
+$node = new stdClass();
+$node->type = 'digitalobject';
+node_object_prepare($node);
+global $user;
+#dpm($user);
+#$node->uid=$user->uid;
+$node->field_single_objid['und'][0]['objid']=$objid;
+
+$node->title = $objid;
+
+$node->language = LANGUAGE_NONE;
+#$node = node_submit($node);
+
+#$node->field_object_type['und'][0]=array('tid'=> '6');
+
+$foo = taxonomy_get_term_by_name('primary');
+foreach($foo as $term) {
+
+  if(($term->vocabulary_machine_name) == 'digitalobject_types') {
+
+    $node->field_object_type['und'][]['tid'] = $term->tid;
+  }
+}
+$node = node_submit($node);
+
+node_save($node);
+
+return $node;
+}
+
+function digitalobjects_edit($objid){
+  #suche ob es schon ein locales object zu dieser id gibt.
+  $query = new EntityFieldQuery();
+  $entities = $query->entityCondition('entity_type', 'node')
+  ->entityCondition('bundle', 'digitalobject')
+  ->fieldCondition('field_single_objid', 'objid', $objid,"=")
+  ->execute();
+
+  if ($entities == null) #existiert noch nicht, dann anlegen
+  {
+    $node =digitalobjects_create_primary_node($objid);
+    #$node->field_object_type['und'][0]=array('tid'=> 'autocreate',
+    #'name' => "primary",
+    #'vocabulary_machine_name' => 'digitialobject_types');
+
+  } else {
+
+
+  $nodes = node_load_multiple(array_keys($entities['node']));
+
+  #gibt es eins, dann gib dieses zum editieren zurück
+  #TODO falls mehrere existieren, was dann??
+
+  #gehe durch alle gefundene
+  foreach ($nodes as $node){
+    #$typeTag = $entity->object_type['und']
+
+    if (isset($node->field_object_type['und'])){
+    $tid = $node->field_object_type['und'][0]['tid'];
+    $tax =taxonomy_term_load($tid);
+    if ($tax->name == "primary"){ #'primary gefunden'
+      module_load_include('inc', 'node', 'node.pages');
+      return drupal_get_form('digitalobject_node_form', $node);
+    }}
+  } # keine primary dann lege diese an:
+
+  $node =digitalobjects_create_primary_node($objid);
+
+  #$vals = array_values($nodes);
+  #$node= array_shift($vals);
+  }
+
+  $node = node_submit($node);
+
+  #node_save($node);
+  module_load_include('inc', 'node', 'node.pages');
+
+  return drupal_get_form('digitalobject_node_form', $node);
+
+}
+
+
+function digitalobjects_show_usage($objid){
+  $query = new EntityFieldQuery();
+  $entities = $query->entityCondition('entity_type', 'node')
+  ->fieldCondition('field_objid', 'objid', $objid,"=")
+  ->execute();
+
+
+  $data = array();
+
+  #treffer gefunden
+  if (isset($entities['node'])){
+
+  $nodes = node_load_multiple(array_keys($entities['node']));
+
+
+  foreach ($nodes as $node){
+    $data[$node->nid] = $node->title;
+
+  }
+
+
+  $output['digitalobjects_item_usage']= array(
+      '#theme' => 'digitalobjects_item_usage',
+      '#data' => $data);
+
+  return $output;
+  }
+
+  return "<h2> object not used in a collection </h2>";
+  }
+
+
+function digitalobjects_page($keys = ''){
+
+
+  #suche ob es schon ein locales object zu dieser id gibt.
+  $query = new EntityFieldQuery();
+  $entities = $query->entityCondition('entity_type', 'node')
+  ->entityCondition('bundle', 'digitalobject')
+  ->fieldCondition('field_single_objid', 'objid', $keys,"=")
+  ->execute();
+
+  if ($entities == null) #existiert noch nicht, dann generische Anzeige
+  {
+
+  $output['digitalobjects_item']= array(
+  '#theme' => 'digitalobjects_item',
+  '#objid' => $keys);
+
+  return $output;
+  } else {
+    $nodes = node_load_multiple(array_keys($entities['node']));
+
+    #gibt es eins, dann gib dieses zum editieren zurück
+    #TODO falls mehrere existieren, was dann??
+
+    #gehe durch alle gefundene
+    foreach ($nodes as $node){
+      if (isset($node->field_object_type['und'])){
+        $tid = $node->field_object_type['und'][0]['tid'];
+        $tax =taxonomy_term_load($tid);
+
+        if ($tax->name == "primary"){ #'primary gefunden'
+          return node_view($node, $view_mode = 'full');
+        }
+      }
+    }
+    #keine primary node gefunden, generische ausgabe
+    $output['digitalobjects_item']= array(
+        '#theme' => 'digitalobjects_item',
+        '#objid' => $keys);
+    return $output;
+  }
+}
+
+/* implements digitalobject_theme */
+
+function digitalobjects_theme(){
+
+    return array(
+        'digitalobjects_item'  => array(
+            'variables' => array('objid' => NULL),
+            'file' => 'digitalobjects.item.inc',
+            'template' => 'digitalobjects-item',
+        ),
+        'digitalobjects_item_short'  => array(
+            'variables' => array('objid' => NULL),
+            'file' => 'digitalobjects.item.inc',
+            'template' => 'digitalobjects-item-short',
+        ),
+        'digitalobjects_item_tools'  => array(
+            'variables' => array('objid' => NULL),
+            'template' => 'digitalobjects-item-tools',
+        ),
+        'digitalobjects_item_usage'  => array(
+            'variables' => array('data' => NULL),
+            'template' => 'digitalobjects-item-usage',
+        ),
+
+        'digitalobjects_currentCollection_block'  => array(
+            'variables' => array('node' => NULL),
+            'template' => 'digitalobjects-current-collection',
+        ),
+     /*   'digitalobjects_user_admin_page' => array(
+         'variables' => array('digitalobjects_user_admin','uid' =>1),
+        'template'  => 'digitalobjects-user-admin-form'
+         ),*/
+            );
+}
+
+function digitalobjects_forms($form_id, $args) {
+
+  $forms['digitalobjects_user_admin_form']= array(
+      'callback' => 'digitalobjects_user_admin',
+      'callback arguments' => array('digitalobjects_user_admin_page'),
+  );
+  return $forms;
+};
+
+
+
+
+function digitalobjects_user_admin(array $form, array &$form_state) {
+
+  $form['digitalcollection'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Active collection'),
+      '#size' => 15,
+      '#default_value' => 'xxxx',
+      '#attributes' => array('title' => t('Enter the terms id of the current collection.')),
+  );
+
+
+  $form['userid'] = array(
+      '#type' => 'hidden',
+  );
+  #$form['actions'] = array('#type' => 'actions');
+  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
+  $form['submit']['#submit'][] = 'digitalobjects_user_admin_form_submit';
+
+  return $form;
+}
+/* implements a new field type for the digitalobject id */
+
+function digitalobjects_field_info(){
+
+  return array('digitalobjects_digitalobject' => array (
+      'label' => t('Digital Object ID'),
+      'description' => t('This field stores a digital object ID'),
+      'settings' => array('max_length' =>20),
+     'instance_settings' => array('text_processing' => 0),
+      'default_widget' => 'digitalobject_default_textfield',
+      'default_formatter' => 'digitalobjects_fullmetadata',
+    ),
+      'digitalobjects_digitalcollection' => array (
+          'label' => t('Digital Collection ID'),
+          'description' => t('This field stores a digital object ID'),
+          'settings' => array('max_length' =>20),
+          'instance_settings' => array('text_processing' => 0),
+          'default_widget' => 'digitalcollection_default_textfield',
+          'default_formatter' => 'digitalcollection_fullmetadata',
+          ),
+
+  );
+}
+
+function digitalobjects_field_formatter_info() {
+
+  return array(
+      // This formatter shows the obejct with fullmetadata
+      'digitalobjects_fullmetadata' => array(
+          'label' => t('Fullmetadata formatter'),
+          'field types' => array('digitalobjects_digitalobject'),
+      ),
+      'digitalobjects_label' => array(
+          'label' => t('Lable formatter'),
+          'field types' => array('digitalobjects_digitalobject'),
+      )
+  );
+  }
+
+
+
+
+
+
+ function digitalobjects_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+    $element = array();
+
+
+    switch ($display['type']) {
+      // This formatter simply outputs the field as text and with a color.
+      case 'digitalobjects_fullmetadata':
+        foreach ($items as $delta => $item) {
+          if ($item['objid']!=""){
+            $element[$delta]['#markup'] = theme('digitalobjects_item_tools', array("objid" => $item['objid']));
+            $element[$delta]['#markup'] .= theme('digitalobjects_item', array("objid" => $item['objid']));
+
+          }
+        }
+        break;
+
+      case 'digitalobjects_label':
+        foreach ($items as $delta => $item) {
+          if ($item['objid']!=""){
+
+            $element[$delta]['#markup'] = theme('digitalobjects_item_short', array("objid" => $item['objid']));
+            $element[$delta]['#markup'] .= theme('digitalobjects_item_tools', array("objid" => $item['objid']));
+          }
+        }
+        break;
+    }
+    return $element;
+ }
+
+
+function digitalobjects_field_widget_info() {
+  #TODO:both types should check if field is valid
+   return array(
+       'digitalobject_default_textfield' => array(
+           'label' => t('digitalobject id'),
+           'field types' => array('digitalobjects_digitalobject'),
+       ),
+       'digitalcollection_default_textfield' => array(
+           'label' => t('digitalobject id'),
+           'field types' => array('digitalobjects_digitalcollection'),
+       )
+       );
+  }
+
+ function digitalobjects_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+
+
+   $field_name = $field['field_name'];
+   $field_type = $field['type'];
+
+
+   $value = isset($items[$delta]['objid']) ? $items[$delta]['objid'] : '';
+
+   $widget = $element;
+   $widget['#delta'] = $delta;
+
+   switch ($instance['widget']['type']) {
+
+
+
+     case 'digitalobject_default_textfield':
+       $widget += array(
+       '#type' => 'textfield',
+       '#default_value' => $value,
+       '#size' => 20,
+       '#maxlength' => 20,
+           );
+       $element['objid'] = $widget;
+           break;
+
+     case 'digitalcollection_default_textfield':
+             $widget += array(
+             '#type' => 'textfield',
+             '#default_value' => $value,
+             '#size' => 20,
+             '#maxlength' => 20,
+             );
+             break;
+
+
+   }
+
+
+
+
+   return $element;
+ }
+
+
+ function digitalobjects_field_is_empty($item, $field){
+   return empty($item['objid']);
+ }
+
+
+
+function digitalobjects_user_admin_form_submit($form,$formstate){
+  $value = $formstate['values']['digitalcollection'];
+  $userid = $formstate['values']['userid'];
+
+  $user=user_load($userid);
+
+
+  $user->field_digitalobjects_cid['und'][0]['objid'] = $value;
+
+  user_save($user);
+
+}
+
+function digitalobjects_block_info(){
+  $blocks['digitalobjects_currentCollection'] = array(
+      'info' => t('Current Collection'),
+      'visibility' => BLOCK_VISIBILITY_PHP,
+      'pages' => '<?php global $user; if ($user->uid == 0) return FALSE; else return TRUE; ?>'
+  );
+  return $blocks;
+}
+
+function digitalobjects_block_view($delta){
+
+  switch($delta){
+
+  case 'digitalobjects_currentCollection':
+    /* display current cullection */
+
+    global $user;
+
+    $user_full=user_load($user->uid);
+
+    if (isset($user_full->field_digitalobjects_cid['und'][0]['objid'])){
+    $val = $user_full->field_digitalobjects_cid['und'][0]['objid'];
+    } else {
+      return;
+    }
+    $node = node_load($val);
+
+    #$title = $node=>title;
+
+   $block['content'] = theme('digitalobjects_currentCollection_block',array('node' => $node));
+
+   $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
+   $form['submit']['#submit'][] = 'digitalobjects_user_admin_collection';
+
+   $submitForm=drupal_get_form('digitalobjects_change_collection_button_form');
+   $block['content'] .= drupal_render($submitForm);
+  }
+
+
+  return $block;
+}
+
+function digitalobjects_change_collection_button_form(){
+  include_once 'digitalobjects.collections.inc';
+  global $user;
+  $form['userid'] = array(
+
+      '#type' => 'hidden',
+      '#value' => $user->uid
+  );
+
+  $form['submit'] = array('#type' => 'submit', '#value' => t('change'));
+  $form['submit']['#submit'][] = 'digitalobjects_user_admin_collection_submit';
+
+  return $form;
+}
+
+function digitalobjects_permission() {
+  return array(
+      'manage private collections' => array(
+          'title' => t('Manage private collections'),
+          'description' => t('Allow users create and manage private collections.'),
+      ),
+      'administrate digitalobjects' => array(
+          'title' => t('Administrate digitalobjects'),
+          'description' => t('Can add commentaries to digital objects.'),
+      ),
+  );
+}
+
+
+function digitalobjects_pathologic_alter(&$url_params, $parts, $settings){
+#bilder ohne jegliche pfad angaben werden auf sites/default/files/..
+  if (preg_match('~^([^/]*)\.(png|gif|jpe?g)$~', $url_params['path'])){
+
+    $url_params['path'] = 'sites/default/files/' . $url_params['path'];
+  }
+
+  if (preg_match('~^./([^/]*)\.pt$~', $url_params['path'],$matches)){
+
+    $url_params['path'] = 'harriot/maps/' . $matches[1] . '.pt';
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/mediathek/mediathek-theme-mediathek-mediaviewer.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,12 @@
+
+
+<div class="field field-type-iframe field-field-iframe">
+    <div class="field-items">
+            <div class="field-item odd">
+                     <iframe src="<?php  print $item['url']?>" width="<?php  print $item['width']?>" height="<?php print $item['height']?>"
+                    frameborder="0" scrolling="auto" transparency="0" class=" iframe-delta-0" id="iframe-0"
+                    name="iframe-0">Your browser does not support iframes. But You can use the following link.
+                    <a href="<?php  print $item['url']?>" title="">Link</a></iframe>
+                     </div>
+        </div>
+</div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/mediathek/mediathek.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,7 @@
+name = mediathek
+description = Allows access to the mediathek
+core = 7.x
+
+files[] = mediathek.module
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/mediathek/mediathek.install	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,38 @@
+<?php
+/**
+ * @file
+ * Install, update, and uninstall functions for the field_example module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ *
+ * Defines the database schema of the field, using the format used by the
+ * Schema API.
+ *
+ * The data we will store here is just one 7-character element, even
+ * though the widget presents the three portions separately.
+ *
+ * All implementations of hook_field_schema() must be in the module's
+ * .install file.
+ *
+ * @see http://drupal.org/node/146939
+ * @see schemaapi
+ * @see hook_field_schema()
+ * @ingroup field_example
+ */
+function mediathek_field_schema($field) {
+
+  $columns = array(
+      'url' => array('type' => 'varchar', 'length' => 600, 'not null' => FALSE),
+      'width' => array('type' => 'varchar', 'length' => 10, 'not null' => FALSE),
+      'height' => array('type' => 'varchar', 'length' => 10, 'not null' => FALSE),
+  );
+  $indexes = array(
+    'url' => array('url'),
+  );
+  return array(
+    'columns' => $columns,
+    'indexes' => $indexes,
+  );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/mediathek/mediathek.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,198 @@
+<?php
+/**
+ * Implementation of hook_enable().
+ */
+function mediathek_enable() {
+
+  #adds a field for the current collection
+  // Check if our field is not already created.
+  if (!field_info_field('field_mediathek_entry')) {
+    $field = array(
+        'field_name' => 'field_mediathek_entry',
+        'type' => 'mediathek_mediathek_entry',
+    );
+    field_create_field($field);
+
+    // Create the instance on the bundle.
+    $instance = array(
+        'field_name' => 'field_mediathek_entry',
+        'entity_type' => 'node',
+        'label' => 'Mediathek Entry',
+        'bundle' => 'node',
+        // If you don't set the "required" property then the field wont be required by default.
+        'required' => False,
+        'settings' => array(
+        ),
+        'widget' => array(
+            'type' => 'mediathek_default_mediathek_entry',
+        ),
+    );
+    field_create_instance($instance);
+  }
+}
+
+/* implements a new field type for the medithek id */
+
+function mediathek_field_info(){
+
+  return array('mediathek_mediathek_entry' => array (
+      'label' => t('Mediathek Item'),
+      'description' => t('This field stores an mediathek Item'),
+      'settings' => array('max_length' =>20),
+      'instance_settings' => array('text_processing' => 0),
+      'default_widget' => 'mediathek_default_mediathek',
+      'default_formatter' => 'mediathek_mediaviewer',
+  ),
+
+  );
+}
+
+function mediathek_field_is_empty($item, $field){
+  return empty($item['url']);
+}
+
+function mediathek_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+
+
+  $field_name = $field['field_name'];
+  $field_type = $field['type'];
+
+
+  $url = isset($items[$delta]['url']) ? $items[$delta]['url'] : '';
+  $height = isset($items[$delta]['url']) ? $items[$delta]['height'] : '';
+  $width = isset($items[$delta]['url']) ? $items[$delta]['width'] : '';
+
+  $widget = $element;
+  $widget['#delta'] = $delta;
+
+  switch ($instance['widget']['type']) {
+
+
+
+    case 'mediathek_default_mediathek':
+
+      $fieldset_info = element_info('fieldset');
+      $process = array_merge($fieldset_info['#process'], array('mediathek_media_entry_process'));
+
+
+      $widget+=
+          array(
+              '#type' => 'fieldset',
+              '#title' => t('Contact settings'),
+              '#weight' => 5,
+              '#collapsible' => TRUE,
+              '#collapsed' => FALSE,
+              '#tree' => TRUE,
+              '#process' => $process,
+          );
+
+
+      $widget['url'] = array(
+              '#title' => t('Url'),
+              '#type' => 'textfield',
+              '#default_value' => $url,
+              '#size' => 150,
+              '#maxlength' => 500,
+          );
+
+      $widget['height'] = array(
+          '#title' => t('Height'),
+          '#type' => 'textfield',
+          '#default_value' => $height,
+          '#size' => 20,
+          '#maxlength' => 20,
+      );
+
+
+
+      $widget['width'] = array(
+          '#title' => t('Width'),
+          '#type' => 'textfield',
+          '#default_value' => $width,
+          '#size' => 20,
+          '#maxlength' => 20,
+      );
+
+
+
+
+
+      $element['media'] = $widget;
+      break;
+
+
+
+  }
+
+
+
+
+  return $element;
+}
+
+function mediathek_media_entry_process(&$form, &$form_state, $complete){
+
+
+  array_pop($form['#parents']);
+  return $form;
+}
+
+function mediathek_field_widget_info() {
+  #TODO:both types should check if field is valid
+  return array(
+  'mediathek_default_mediathek' => array(
+  'label' => t('Media Item'),
+  'field types' => array('mediathek_mediathek_entry'),
+  ),
+
+  );
+}
+
+
+function mediathek_field_formatter_info() {
+
+  return array(
+      // This formatter shows the obejct with fullmetadata
+      'mediathek_mediaviewer' => array(
+          'label' => t('Mediathek viewer'),
+          'field types' => array('mediathek_mediathek_entry'),
+      )
+  );
+}
+
+
+function mediathek_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $element = array();
+
+
+  switch ($display['type']) {
+    // This formatter simply outputs the field as text and with a color.
+    case 'mediathek_mediaviewer':
+
+      foreach ($items as $delta => $item) {
+
+        $url = $item['url'];
+        $width = $item['width'];
+        $height= $item['height'];
+
+        if ($url!=""){
+        $element[$delta]['#markup'] = theme('mediathek_theme_mediathek_mediaviewer',array('item' => $item ));
+        }
+
+        }
+
+      break;
+  }
+
+  return $element;
+}
+
+function mediathek_theme(){
+return array(
+    'mediathek_theme_mediathek_mediaviewer'  => array(
+        'variables' => array('item' => NULL),
+        'template' => 'mediathek-theme-mediathek-mediaviewer',
+        )
+);
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/CHANGELOG.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,820 @@
+Apache Solr integration 7.x-1.x, xxxx-xx-xx
+-----------------------------
+#1249616 by agentrickard, pwolanin, Nick_vh | R.Muilwijk: Fixed Apachesolr access makes assumptions that don't apply to modules like Domain Access.
+#1926896 by mkalkbrenner: Fixed missing highlighted snippets.
+#1927032 by mkalkbrenner: Fixed Avoid spaces between words and punctution marks in search result snippets.
+#1915614 by mkalkbrenner, Nick_vh: Added Complete admin settings for non-default environments.
+#1918012 by mkalkbrenner, Nick_vh, pwolanin: Added Solr query needs to be aware of more context such as Search Page ID.
+#1896470 by Nick_vh, mkalkbrenner: Fixed Integrate Apache Solr Common Configurations for Solr 3.x and 4.x (Highlighting broken).
+#1918030 by mkalkbrenner, pwolanin: Allow contrib modules to add custom settings to search pages.
+#1918566 by mkalkbrenner: Added Devel integration: debug index documents on all environments / indexes.
+#1915418 by mkalkbrenner, Nick_vh: Fixed Unable to index a node bundle in a dedicated index / environment.
+#1900036 by remimikalsen, swentel | albert78: Fixed node reference with multiple values.
+#1900006 by Nick_vh | msellers: Fixed Undefined variable causes warning on node type uninstall.
+#1874420 by Nick_vh, j0rd | dirtabulous: Fixed Solr4 Entites Not Being Removed with deleteByQuery.
+#1871866 by Nick_vh, grom358: Fixed Type bias for all entities.
+#1861544 by fizk: Fixed Incorrect use of $field_map() in SolrBaseQuery::getSolrsortUrlQuery.
+#1897556 by Nick_vh: Update documentation for all the functions and make sure it reflects reality.
+#1836262 by ianmthomasuk, Nick_vh: Added Allow indexing of a set number of items using drush.
+#1898166 by Nick_vh: Fixed Make better use of the entity cache.
+#1868870 by cpliakas | Nick_vh: Revert changes to interface cause fatal errors for modules that implement the interface.
+#1803512 by David_Rothstein | amanire: Fixed The OR operator returns no results for facets with colons in the value (such as entity reference facets).
+#1679392 by David_Rothstein, brianV: Move logic from apachesolr_default_node_facet_info() into a separate function so other modules can use it for non-node entities.
+#1814080 by Nick_vh, JordanMagnuson: Fixed Apachesolr sort block fills cache_block() table without stopping.
+#1550964 by Nick_vh, drzraf, rajivk, rjbrown99, j0rd | tinflute: Support Solr 4.0 schema.
+#1840430 by Nick_vh: Fixed add alter callbacks from facetapi to the mappings array.
+#1827320 by drumm: Fixed Upgrading from 6.x-3.x does not need apachesolr_update_7000-15.
+#1828040 by david.gil, Nick_vh: Fixed Cannot access empty property in query_type_numeric_range().inc on line 77.
+#1828014 by DeFr, Nick_vh: Fixed Mass re-indexation can miss (a lot of) content.
+#1823590 by Nick_vh, Josh Waihi: Added Tag Apachesolr index table select queries to allow other modules to alter them.
+#1825840 by firebird: Fixed Typo in menu description.
+
+Apache Solr integration 7.x-1.1, 2012-10-15
+-----------------------------
+#1780200 by pwolanin: document basic auth in README.
+#1226274 by pwolanin: Fixes for schema.xml version numbering and organization.
+
+Apache Solr integration 7.x-1.0, 2012-10-13
+-----------------------------
+#761990 by pwolanin, jhedstrom, Nick_vh, jpmckinney | morningtime: Fixed 400 Bad Status if URL length limit exceeded.
+#1811364 by Nick_vh: Fixed Add newly created content type to the indexed bundles for all environments.
+#1811456 by rupl: Fixed Improve documentation of addFilter in apachesolr.api.php.
+#1773506 by drzraf: Fixed drush solr-search notice when file entity are part of the result.
+#1688150 by HalfChem, cam8001, jhedstrom, Nick_vh: Fixed Search query string gets double encoded when core Search block form is submitted.
+#1794602 by pwolanin, Nick_vh | ppmr: Fixed Undefined index: path in ApacheSolrDocument->__get() (line 304 of ...\apachesolr\Apache_Solr_Document.
+#1773506 by drzraf: Fixed drush solr-search notice when file entity are part of the result.
+#1764334 by cpliakas: Fixed Negative percentage of documents sent to the server for indexing.
+#1806300 by Nick_vh: Separate out the creation of the document from the index process.
+#1807552 by Nick_vh, wimvds: Fixed Site and url wrong when indexing multilingual content (using i18n module).
+#1759190 by mkalkbrenner: Fixed obsolete form code?.
+#1802288 by Nick_vh: Fixed Improve testing of node deletion.
+
+Apache Solr integration 7.x-1.0-rc5, 2012-10-01
+-----------------------------
+#1698050 by duellj: fix for apachesolr_clean_text() should strip extra spaces.
+#1764450 by cpliakas: Improve the docblocks in apachesolr.index.inc.
+#1800324 by pwolanin: Clean up and align node access tests.
+#1799330 by pwolanin: fix for Stats table missing t() calls.
+#1787370 by pwolanin: Don't complain about schema version if it doesn't match expected naming pattern.
+#1790590 by pwolanin: Fix DrupalSolrOnlineWebTestCase  so it works with any multicore setup.
+#1799032 by pwolanin: Remove uneeded object reference in indexing function.
+#1793610 by zengenuity: Fix for regession - spelling suggestions missing.
+
+Apache Solr integration 7.x-1.0-rc4, 2012-09-21
+-----------------------------
+#1790894 by pwolanin: fix for Cloning an environment doesn't clone the bundles to be indexed.
+#1778050 by Nick_vh, pwolanin: fix for stale cache when CTools is already enabled.
+#1778266 by Nick_vh, mkalkbrenner: Refactoring of DrupalSolrOnlineWebTestCase to be used by Others.
+#1778266 by mkalkbrenner: Refactoring of DrupalSolrOnlineWebTestCase to be used by Others.
+#1789526 by pwolanin: Clarify lack of upgrade path from 6.x-1.x.
+#1765938 by cpliakas: Added Move the variable_get() for 'apachesolr_environments()" after the cache_set() so that URLs can be modified dynamically.
+#1782436 by cspitzlay: Fixed Error and obsolete hint in code comment.
+#1732900 by cpliakas: Added Change the wording of the generic 'Apache Solr server unavailable" error message.
+#1781040 by mkalkbrenner: Fixed Switch 'Enable spell check" does not work - spell check is allways on.
+#1786450 by Nick_vh: Fixed apachesolr.interface.inc is not always loaded.
+#1760592 by cpliakas, pwolanin: fix for core_search page does not use the current default search environment.
+#1759004 by mkalkbrenner, pwolanin: fix for apachesolr_search_custom_page_search_form_submit() kills all $_GET parameters.
+#1778150 by cpliakas, pwolanin: fix for SQL error in apachesolr_environment_load_subrecords().
+#1743138 by mundanity: Fixed Invalid argument when running Drush.
+#1751640 by cpliakas: Added drush commands to see the ID of the last item indexes as well as the next item queued for indexing.
+#1708150 by ianmthomasuk, Nick_vh, pwolanin: Added additional typing to function definitions.
+#1759110 by Nick_vh: Fixed process response fails if variable was set but never removed.
+#1741066 by Nick_vh: Added geo query type to make contrib's modules life easier.
+#1730840 by pwolanin: Normalize boolean and other values for parameters like hl.
+#1729836 by pwolanin: fix for "Results per page" option for a search page is broken.
+#1719302 by Nick_vh, msti: Fixed Running an empty facet query so the facet blocks can be displayed.
+#1722966 by greggles: Fixed make apachesolr_index_batch_index_finished() message more translateable.
+#1717628 by Nick_vh, pwolanin: Added 'Save and Edit" functionality for configuration pages - Follow ups.
+
+Apache Solr integration 7.x-1.0-rc3, 2012-08-10
+------------------------------
+#1682004 by Nick_vh: Add template hints for search results based on search page.
+#1722012 by mkalkbrenner, Nick_vh: fix for Missing argument 4 for apachesolr_multilingual_apachesolr_index_documents_alter().
+#1717490 by pwolanin: fix for Search result template suggestions relies on non-required fields.
+#1708212 by ianmthomasuk, Nick_vh: Fixed Duplicate field error when indexing single value fields.
+#1702788 by mkalkbrenner: Fixed content and teaser are empty if node->language is not default.
+#1708166 by ianmthomasuk: Fixed Thow exception if asked for invalid solr environment.
+#1717628 by Nick_vh | cpliakas: Added 'Save and Edit" functionality for configuration pages.
+#1718172 by Nick_vh, pwolanin: Fixed Move search result alter hook earlier, and make sure needed data is set.
+#1702526 by Nick_vh: Fixed apachesolr_search_page_load() is quirky.
+#1717958 by pwolanin: Fix for text_und fields should be exposed for searching in the admin section.
+#1708538 by Nick_vh: Fixed Move the start param above the query alter.
+#1700472 by pwolanin: Make 'apachesolr_search_process_response_callback' a search environment variable.
+#1439564 by recidive, neochief, Nick_vh, dawehner, jhedstrom: Added Index bundles Export/Import.
+#1652746 by martins.bertins | chrisssi: Fixed Notice: Undefined offset: 3 in _menu_translate().
+#1670198 by alanmackenzie | ruedu: Fixed Unable to export/import settings using features module.
+#1681946 by Josh Waihi: Fixed apachesolr_index_nodeapi_mass_delete() doesn't work.
+#1650096 by Nick_vh | cappadona: Fixed addParam() creates duplicate filters when dealing with fq.
+#1677050 by cpliakas: Fix for Cancel link when creating a new environment.
+#1673086 by pwolanin: Add drush commands to get, set, del environment variables.
+#1682004 by cpliakas: Added template hints for search results based on search page.
+#1672738 by Nick_vh: Added Allow a dynamic apachesolr_process_results() function.
+#1473722 by levialliance: Added Bundle specific overrides.
+#1408844 by Nick_vh, jrbeeman: Added As a site builder I want to override the environment settings in settings.php.
+#1652746 by chrisssi: Fixed Notice: Undefined offset: 3 in _menu_translate() (Zeile 775 von /var/www/includes/menu.inc).
+#1402748 by Nick_vh, killua99: Fixed Check in apachesolr_do_query() if the static of the query with that name already exists, and if so return it.
+
+Apache Solr integration 7.x-1.0-rc2, 2012-06-21
+------------------------------
+Renamed schema.xml and solrconfig.xml to rc2
+
+Apache Solr integration 7.x-1.0-rc1, 2012-06-21
+------------------------------
+#1642140 by Nick_vh: Fixed Make the search query also escape the slash and move away from menu_tail() to just 1 argument.
+#1518108 by grendzy: Added Devel interface for inspecting solr documents.
+#1402748 by Nick_vh: Fixed Check in apachesolr_do_query() if the static of the query with that name already exists, and if so return it.
+#1631702 by Nick_vh | khaled.zaidan: Fixed Report at admin/reports/apachesolr doesn't always display data from the current default solr server.
+#1647668 by paulmckibben: Fixed Error messages when indexing - Notice: Undefined property: stdClass::$nid in DatabaseStatementBase->fetchAllAssoc().
+#1475014 by ceng, Nick_vh: Fixed Drush command solr-delete-index ignores type arguments.
+#1588256 by InternetDevels.Com, Nick_vh | RaulMuroc: Fixed Warning: Invalid argument supplied for foreach() in apachesolr_entity_update() (line 1766 of /apachesolr/apachesolr.module).
+#1612530 by Nick_vh, willmoy: Added Results say '0 comments' when comment.module is disabled.
+#1522174 by pwolanin: Fixed hook_node_type_delete() is wrong.
+#1576710 by skwashd: Added Indexing with drush cron generates a lot of invalid cron notifications.
+#1543754 by iamEAP: Fixed Schema inconsistency introduced by apachesolr_update_7013.
+#1481564 by milesw: Added Allow other modules to react when there are no results.
+#1470794 by pwolanin: Added Show size of index on disk in index report.
+#1586320 by cpliakas, pwolanin: Added support for the ExternalFileField field type.
+#1613328 by cpliakas: Fixed The service_class() is not passed to apachesolr_server_status() when checking each server's status on the settings page.
+#1621142 by pwolanin: Fixed Broken logic in indexing of extra data.
+#1627484 by gnucifer: Fixed 'allowed operator" not transfered from field definitions.
+#1627604 by Nick_vh: Fixed Add query type for taxonomy_term_reference().
+#1609184 by pwolanin: Minor code cleanup in MLT response handling .
+#1611338 by remimikalsen: Added Allowing several bundles to share the same field name within a given entity type after field_mapping_alter().
+#1568126 by fearlsgroove, pwolanin: Fixed Apachesolr causes all simpletests to fail.
+#1609030 by pwolanin: Fix for Missing search page still executes search.
+#1572722 by eporama, cpliakas, wonder95: Added integration with the Entity References module.
+#1567614 by pwolanin: fix for Uninstall is not complete.
+#1559880 by pwolanin: fix for 'retain filters' checkbox does not work.
+#1558626 by pwolanin: fix for broken OR facet when value has spaces (committed in 16ef187).
+#1538244 by by julianmancera: Fix for Solr base query adds an extra slash in getPath() when there is no 'q' param.
+#1532214 by setvik, pwolanin: Fox for mergePolicy syntax has changed and throws error in Apache Solr 3.6, update config versions.
+#1536936 by pwolanin: Fix for Use json.nl=map for Luke and other json data.
+#1536628 by Nick_vh: Added exclude hook that allows to skip content without removing it from the table.
+#1536600 by Nick_vh: Fixed Performance optimizations when doing hundreds of deletes.
+#1515822 by pwolanin, Nick_vh: Added Support notion of parent entity for derived documents.
+#1519900 by pwolanin, Nick_vh, Georgique: Fixed Error in apachesolr_index_get_entities_to_index() function.
+
+Apache Solr integration 7.x-1.0-beta19, 2012-04-05
+------------------------------
+#1515580 by Nick_vh | yannou: Fixed Deleting node is impossible, stdClass problem.
+
+Apache Solr integration 7.x-1.0-beta18, 2012-04-04
+------------------------------
+#1514314 by Nick_vh, Georgique: Fixed Error in apachesolr_entity_update() Follow-up.
+#1514314 by Nick_vh, Georgique: Fixed Error in apachesolr_entity_update().
+
+Apache Solr integration 7.x-1.0-beta17, 2012-04-03
+------------------------------
+#1509790 by Nick_vh: Fixed Status is not checked when a reindex is initiated.
+#1476228 by ygerasimov: Fixed Items to index determined not correct.
+#1477732 by ygerasimov: Fixed apachesolr_index_entities() sets index position that leads to cycle.
+#1508434 by lazysoundsystem, Nick_vh: Fixed whitespace problem.
+#1481326 by Nick_vh | milesw: Fixed Queries run twice after submitting search forms on a custom search page.
+#1502088 by Nick_vh | sanpi: Fixed Missing Search dependency for Apache Solr framework.
+#1498490 by Nick_vh, milesw: Fixed Menu paths for custom search pages are broken when exported as features.
+#1475010 by Nick_vh: Fixed Date filtering to minute returns faulty results.
+#1469484 by Nick_vh, Josh Waihi: Fixed Limit select queries when checking index node table to prevent memory overload.
+#1442358 by johnennew, Nick_vh: Fixed Nodes not queued to be removed from index when unpublished.
+#1456368 by Nick_vh, pwolanin: Fixed Entities not removed when excluded, unnecessary re-indexing.
+#1470042 by klaasvw: Fixed Existing node table error on drupal update.
+#1458696 by Nick_vh | m1r1k: Fixed Apache Solr bug with re-index.
+#1418834 by levialliance | drewmacphee: Beta15+ problems with i18n and entity_translation().
+#1404284 by pwolanin: Fix for possible undefined function apachesolr_default_environment() during update.
+#1441066 by pwolanin: Fix for incorrect hook_schema() definition for {apachesolr_index_entities}.
+#1411066 by Nick_vh | heacu: Fixed Unchecking 'Custom filter" has no effect if custom filter has been entered on Edit Search Page.
+#1424578 by Nick_vh | cpliakas: Fixed Errors thrown on admin/reports/apachesolr when the server is unavailable.
+#1439492 by recidive: Fixed Features importing of search page not working.
+#1446694 by edb: Fixed env_id() length is only 32 characters on apachesolr_search_page() table.
+#1437842 by duellj: Added Facetapi block titles on empty search page should use theme_facetapi_title().
+#1442358 by johnennew, Nick_vh: Fixed Nodes not queued to be removed from index when unpublished.
+#1440394 by grendzy: fix for Field bias setting has no effect.
+#1399584 by halcyonCorsair | Nick_vh: Added As a developer I want to change environment variables with drush.
+#1423130 by pwolanin | rickmanelius: Fixed apachesolr_user_update() Issues.
+#1403810 by Nick_vh | SangersDrupalDude: Fixed some warning in admin area.
+#1419290 by Nick_vh, paulmckibben: Fixed When $conditions is empty, search_data() results in 'The Apache Solr search engine is not available" (but results are returned).
+#1429230 by Nick_vh: Fixed Default values for for mapping per-field is not added correctly.
+#1427288 by Nick_vh: Fixed Change the MergePolicy for all the solrconfigs to use LogByteMergePolicy.
+
+Apache Solr integration 7.x-1.0-beta16, 2012-02-02
+------------------------------
+#1408190 by DeFr: Fix port check for url with credentials.
+#1411354 by Nick_vh: fix for Incorrect reporting of documents indexed.
+#1357588 by becw, Bußmeyer, pwolanin: improved ctools export/import support.
+#1418136 by zengenuity, pwolanin: fix for undefined function apachesolr_index_set_last_updated() in apachesolr_access.
+#995526  by chx, pwolanin: another fix for Key length greater than 1000 bytes causes install error on MyISAM.
+#1409878 by becw: Fixed Uncaught exception when the default environment does not exist (patch).
+#1398100 by thecarlhall: Fixed Configuration throws exception when starting Solr. Follow up for 7.x-1.x
+#1409076 by Nick_vh, sdrycroft: Fixed Entities that use the 'apachesolr_index_entities()" table are not indexed.
+#1409146 by Nick_vh: Fixed Add all possible index_types() to apachesolr_index_key().
+#1408856 by Nick_vh: Remove DrupalQueue from the install.php.
+#1402746 by Nick_vh: Clean up apachesolr_do_query() so the page variable will be inside the query object.
+#1408192 by becw: Fixed Add apachesolr.interface.inc to the .info file (patch).
+#1405040 by Nick_vh | chrisssi: Fixed SQLSTATE[42S22]: Column not found: 1054.
+
+Apache Solr integration 7.x-1.0-beta15, 2012-01-12
+------------------------------
+#1397526 by Nick_vh, cpliakas: Add an option to select which search page facets link to when displayed on non-search pages. Follow-Up small bugfix
+#1397138 by Nick_vh: Create Simpletests to verify base functionality of the module.
+#1402688 by Nick_vh: Fixed Upgrade from Beta13 to Beta14 does not take the excluded types into account properly.
+
+Apache Solr integration 7.x-1.0-beta14, 2012-01-12
+------------------------------
+#1397526 by Nick_vh, cpliakas: Add an option to select which search page facets link to when displayed on non-search pages.
+#713142 by ygerasimov, pwolanin, Nick_vh: Added configuration option to a search environment if we want to use Dismax or EDismax.
+#1379128 by Nick_vh, bdragon: Fixed Treat cardinality=1 fields as single-value + caching stats response - Follow up.
+#370855 by Damien Tournoud, Nick_vh: Add configuration option to a search page if we allow user input using the url or not.
+#1392940 by Nick_vh | pwolanin: Create a 3.x solrconfig.xml that sets luceneMatchVersion.
+#1031250 by EugenMayer, Nick_vh: Added an option to index as another user instead of only anonymous.
+#1388498 by Nick_vh | pwolanin: Remove s (sortable) fields from schema.xml.
+#1379128 by Nick_vh, bdragon: Fixed Treat cardinality=1 fields as single-value + caching stats response.
+#1361422 by Nick_vh: Fixed coder issues as mentioned by the drupal testing bot.
+#1398122 by Nick_vh | thecarlhall: Fixed Trying to get property of non-object in apachesolr_search_block_view().
+#1398100 by thecarlhall: Fixed Configuration throws exception when starting Solr.
+#1396606 by cpliakas: Fixed Fatal error 'Call to undefined function apachesolr_index_status()" when visiting admin/config/search/settings.
+#1392742 by Nick_vh, pwolanin: Fixed Add solr.LengthFilterFactory to the text fields.
+#592522 by Nick_vh, pwolanin | quaoar: Fixed Hooks node_type(), taxonomy and user knocks out our database server.
+#1394276 by pwolanin, Nick_vh, Fix for logic for last indexed timestamp.
+#1388916 by rwohleb, pwolanin: Fix for ctype_digit() port validation fails for ports < 256.
+#1391850 by cpliakas: Fix for Fatal error when Solr server is not available on non-search pages.
+#1389306 by Nick_vh: Fixed Wrong helptext link if facetapi is not installed.
+#1252648 by cpliakas, Nick_vh: Added Allow for enabling facet blocks on non-search pages.
+#1161608 by scor, Nick_vh: Added Index format_username()($account).
+#1380448 by Nick_vh | longwave: Fixed Menu item description for /admin/reports/apachesolr.
+#1387272 by cpliakas: Fixed The cancel links and button submission redirects in facet configuration forms are broken.
+#1387088 by swentel, Nick_vh: Fixed apachesolr_index_mark_for_reindex() not found during enable/disable and drush can't load apachesolr.index.inc.
+#1313698 by Nick_vh, denikin: Fixed Support for search of multiword content in facets/fields .
+#966796 by Nick_vh, scor, BarisW, wesnick, swentel, LSU_JBob | Crell: Added Separate indexer for multiple entity types.
+
+Apache Solr integration 7.x-1.x-beta13, 2011-12-21
+------------------------------
+#1376278 by Nick_vh: Fixed Inconsistent behavior of data facets producing unlimited filtering, inconsistent counts, or gap mismatches.
+#1372952 by pwolanin | albertosouza: Fixed Wrong variable in apachesolr.module.
+
+Apache Solr integration 7.x-1.0-beta12, 2011-12-12
+------------------------------
+#1369208 by pwolanin: Add configure links in .info files.
+#1369184 by pwolanin: Restore MLT block edit use of block module configure form.
+#1369144 by pwolanin: Fix for cleanup settings table and restore cron index operation.
+#1353422 by Nick_vh | Marty2081: Added Using view modes in 'more like this" block.
+#1212610 by Nick_vh | jeff.maes: Fixed Notice: Undefined index: module in apachesolr_search_form_search_form_alter(). - follow up
+#1365304 by brianV: Fixed Date field minimum/maximum range callbacks don't handle some cases; cause Solr exceptions.
+#1365940 by Nick_vh: Fixed Move all indexing functions to apachesolr.module.
+#1364564 by Nick_vh, pwolanin: Clarify % on the index page.
+#1357820 by Nick_vh | vrc3: Fixed Titles for taxonomy pages are double encoded.
+#1358730 by Nick_vh | rooby: Fixed Facet only searching isn't working.
+#1362294 by pwolanin: Fix for undefined variable: title in apachesolr_get_user_title().
+#1059372 by jpmckinney, Georgique, Nick_vh: Fixed References integration is broken.
+#1323676 by Nick_vh: Global functions should be context driven.
+#1359294 by Nick_vh: Follow-up : Fixed Test is not enabling the correct modules.
+#1359386 by Nick_vh: Replace all instances of $_GET['q'] with current_path().
+#1359294 by Nick_vh: Fixed Test is not enabling the correct modules.
+#1358158 by pwolanin | SangersDrupalDude: Fixed Schema issues on update to D7.
+#1351908 by Nick_vh: Added UI comments and improvements.
+#1349532 by Nick_vh: Added UI remake of the bias pages.
+#1357548 by Nick_vh: Make the unit tests for solr succeed if there is no solr environment available.
+#1356038 by Nick_vh: Added More like this / Blocks Admin UI rework.
+#1356018 by Nick_vh: Fixed Remove admin/system from solrconfig.xlm.
+#1344690 by Nick_vh: Added Search page should be cloneable.
+#1333904 by Nick_vh | vrc3: Added Please restore flexible 'results per page" option - follow up.
+#1292364 by Nick_vh, scor: Added UI redesign.
+#1336324 by Nick_vh | tinker: Added Get Solr version number to determine feature sets like facet ranges and location search.
+#1347092 by Nick_vh | cfuller12: Fixed Solrsort appears to be broken in beta11.
+#1344576 by Nick_vh: Fixed More like this should be environment dependent.
+#1344570 by Nick_vh: Fixed When installing solr the 'visit the admin page" link is not working.
+
+Apache Solr integration 7.x-1.0-beta11, 2011-11-16
+------------------------------
+#1328886 by drasgardian, Nick_vh, pwolanin: fix for access module fails for realm names with spaces.
+#1340232 by milesw, Nick_vh: Added Did You Mean suggestions should be broken out from core search form.
+#1343646 by Nick_vh: Fixed Empty Search behavior is not working.
+#1301646 by Nick_vh: Fixed Coder Review + Drupal coding standard.
+#1097976 by Nick_vh, jpmckinney: Fixed Use ShowFileRequestHandler, gettableFiles is deprecated.
+#1342134 by Nick_vh: Fixed 'Creating default object from empty value" Notice in node access and indexing test.
+#1341860 by pwolanin: Fixed Notice: Undefined index: fq in apachesolr_search_conditions_default() (line 401 of apachesolr_search().module).
+#1053126 by jpmckinney, Nick_vh | pwolanin: Remove duplicate sort hooks in 7.x? follow-up.
+#1053126 by jpmckinney, Nick_vh | pwolanin: Remove duplicate sort hooks in 7.x?.
+#1341854 by pwolanin: Pass the query object into hooks altering search results.
+#1340552 by pwolanin: Make facet generation more flexible/overrideable.
+#1341840 by pwolanin: Fix for undefined index 'core_search' while running tests.
+#1271964 by Nick_vh | wmostrey: Fix for no way to delete content recommendation blocks.
+#1314406 by Nick_vh, scor: Fixed De-duplication of the apachesolr_search_execute() and apachesolr_search_user_defined_search_page().
+#1161538 by Nick_vh, pwolanin | domidc: Fixed The numeric field id should not be used for Solr index field names.
+#1333904 by Nick_vh | vrc3: Restore flexible 'results per page' option.
+#1161444 by Nick_vh | cpliakas: Modify Facet API field definitions to reflect API change for the 'query type" key.
+#1334216 by Nick_vh: Fixed Convert all static parameters to drupal_static().
+#1328854 by Josh Waihi: Added Tag cron database queries to allow modules to alter conditions of updating index table.
+#1323758 by Nick_vh | Frippuz: Fixed hook_apachesolr_process_results() make no impact on results.
+#1265124 by InternetDevels.Com: Fixed Not output pager in search results.
+#1324842 by Nick_vh: Added setAvailableSorts to the api.
+#1000532 by craigmc, jpmckinney: Fixed Non-current/valid Node Types not excluded from index.
+#1320906 by Nick_vh | brianV: Fixed Taxonomy Search page title should be dynamic based on term.
+#1320076 by Nick_vh | egarias: Fixed Results per page should be non-negative integer less than 200.
+#1320634 by Nick_vh | jummonk: Fixed Search pages saved in static array before they are created in DB => PDO exception on creation of menu_router() records.
+#1319542 by jgalletta: Fixed Undefined index: get in apachesolr_search_user_defined_search_form_submit().
+#1313698 by Nick_vh, denikin: Fixed Support for search of date content in facets/fields
+#1316578 by Nick_vh | rooby: Added some documentation somewhere that says you need the facetapi module for facets.
+
+
+Apache Solr integration 7.x-1.0-beta10, 2011-10-19
+------------------------------
+#1312718 by pwolanin: fix for menu rebuild problems when installing apachesolr_search.
+#1134610 by pcambra, JoeMcGuire, Ravi.J: schema support for ctools export of settings.
+#1314664 by Nick_vh, brianV: fix for Search pages completely ignore the title set in the configuration.
+#1314260 by Nick_vh: Fixed hook_apachesolr_query_prepare() not also correctly documented.
+#1313698 by Nick_vh, denikin: Fixed Support for search of multiword content in facets/fields .
+#1309572 by Nick_vh: Fixed Creating new search page gives error when trying to find the search page variable.
+#1204480 by Nick_vh | chriscalip: Fixed Please update apachesolr.api.php naming convention from HOOK_ to hook_.
+#1212610 by Nick_vh: Fixed Notice: Undefined index: module in apachesolr_search_form_search_form_alter().
+#1309564 by Nick_vh: Fixed Simplifying syntax between isset and empty for search_box() in custom page.
+
+Apache Solr integration 7.x-1.0-beta9, 2011-10-13
+------------------------------
+#1307526 by pwolanin, Nick_vh: Fixes for update path, core search functionality.
+#1264786 by grndlvl: fix for double ellipses on search snippets.
+#1305282 by pwolanin, Nick_vh: Fixed Search pages problems.
+#1283924 by rjmackay, lazysoundsystem, jweowu: fix for notice undefined index errors.
+#1305052 by cpliakas: fix for Negative facets not displayed when the mincount is 0.
+#1279164 by pwolanin, fix for 'bundle' is not a required field, but apachesolr treats it as such.
+#1300380 by Nick_vh, pwolanin: Search environments not clear about being active or online.
+#1294846 by Nick_vh: Added Refactoring of the search pages.
+#1188824 by pwolanin: fix for very large watchdog entries when index is in read-only mode.
+#1270826 by pwolanin, brianV: fix for search page regex fails for complex queries.
+#989398  by pwolanin, Nick_vh: Fix tests after moving conf files.
+#1167136 by cpliakas: fix for fields attached to nodes added as facets regardless off the collection type.
+#1225554 by pwolanin: fix for pagination missing in the search page administration.
+#817286  by pfrenssen: API docs cleanup.
+#901376  by LiuShaz, pwolanin: insure UTF-8 encoding is used for POST searches.
+#1288080 by Nick_vh, brianV: make facets based on date fields work.
+#1201534 by BrianV, pwolanin: restore date field indexing.
+#1230380 by Shawn_Smiley: fix for Undefined index: 'facet mincount allowed'.
+#1292328 by Nick_vh: add clone environment feature, tweak the UI.
+#1187888 by pwolanin: move conf to a subdirectory and start supporting solr 3.3+.
+#1237472 by DeFr, sfyn: Fix for _constructURL method misforms urls with username/password.
+#1258658 by pwolanin: Fix failing node access test due to core 7.3 change.
+#1248366 by Dave Reid: Fix declaration of getInfo() test functions.
+#1219178 by cpliakas: Added support for customizable minimum facet counts.
+#1216184 by cpliakas: remove unneeded static variable.
+#1204450 by blazey: fix for parameter error in apachesolr_taxonomy module.
+#1150174 by mr.andrey, pwolanin: Strip content of script and similar tags when indexing.
+#1183742 by MrHaroldA, pwolanin: Index all numeric field API fields by default.
+#1188614 by ASupinski: Expand Hook_Hook_Info to include other hooks.
+
+Apache Solr integration 7.x-1.0-beta8, 2011-06-13
+------------------------------
+#1174960 by pwolanin: fix critical indexing bug from DBTNG error in apachesolr_cron_check_node_table().
+#1148612 by pwolanin: fix regression; Clicking "Relevancy" has no effect after choosing another sort.
+
+Apache Solr integration 7.x-1.0-beta7, 2011-05-24
+------------------------------
+#1162600 by pwolanin: Display an error if the schema version in use is incompatible.
+#1162078 by jpmckinney: fix for Undefined variable: stats_summary.
+#1159172 by jpmckinney: Remove unused facet functions.
+#1167172 by cpliakas: Improve the breadcrumb handling for facet settings forms.
+#1157864 by cpliakas: Integrate with Facet API's depencency plugin system.
+#912758  by pwolanin: use Facet API's support for faceting missing values.
+#1098860 by jpmckinney: Add apachesolr_cron_check_node_table back to cron.
+#1159172 by cpliakas: Remove unused facet functions.
+#926564  by jpmckinney: Add get_subqueries().
+#1064972 by jpmckinney: Use is_callable not function_exists, where applicable.
+#1097988 by pwolanin: Add omitHeader to save on bandwidth.
+#1154770 by jpmckinney, pwolanin: Must double quote filter query values if containing space or colon.
+#1152382 by cpliakas, pwolanin: Modify Facet API adapter for api changes.
+#1131288 by jpmckinney: Fix install and other follow-ups for renaming "server" to "search environment".
+#1150988 by pwolanin: Fix for facet blocks based on custom fields displaying field keys instead of labels.
+#1150220 by pwolanin: Fix for MLT requests are not going through query alter.
+#1150306 by pwolanin: fix for double URL encoding of plus sign (+) on search, after changing sort filters.
+
+Apache Solr integration 7.x-1.0-beta6, 2011-05-06
+------------------------------
+#1148768 by pwolanin: move the read-only index setting to the environment edit page.
+#1146976 by pwolanin: Rename hook_apachesolr_modify_query to hook_apachesolr_query_alter and other API clean up.
+#1146296 by pwolanin: integrate with Facet API current search block code.
+#1145036 by slip, pwolanin: Add support for facet browsing to custom search pages.
+#1131288 by pwolanin: Rename "server" to "search environment" for better conceptual clarity.
+#1122186 by slip, pwolanin: Allow custom user-specified search pages, supporting API changes.
+#1127520 by cpliakas, pwolanin: make the breadcrumb look reasonable on facet form.
+#1127302 by Janusman: some variables were not removed on uninstall.
+#1126806 by pwolanin, Janusman: restore facet browsing functionality.
+#1126488 by cpliakas: Implemented hierarchical taxonomy facets.
+#1126284 by pwolanin: add enabled filters tab on every server edit page.
+#1126282 by pwolanin: fix facet mincount param.
+#1124844 by pwolanin: Port over facet API adapter and hooks.
+#1121170 by pwolanin: remove facet-related code to prep for Facet API integration.
+#1122348 by pwolanin: Rework query class to provide uniform methods of getting and setting params.
+
+Apache Solr integration 7.x-1.0-beta-5, 2011-04-07
+------------------------------
+#799970  by pwolanin: update README for config changes.
+#1118646 by cpliakas, pwolanin: Fields now displayed on search index report page.
+#1117152 by cpliakas: Added a cancel link to the server edit page.
+#1117606 by cpliakas: Resolved inconsistencies with the caller parameter.
+#1118508 by pwolanin: Make the Apache Solr config link show up next to Search module.
+#1117128 by jpmckinney: Follow-up to #1088208
+
+Apache Solr integration 7.x-1.0-beta4, 2011-04-04
+------------------------------
+#1116030 by cpliakas, pwolanin: Added titles to settings pages to add transparency as to which server's settings are being edited.
+#1112022 by pwolanin, elliotttf: index node last_comment_timestamp too.
+#1114798 by pwolanin: enhancements to config based on Solr 3.1 examples.
+#1108618 by pwolanin: make numeric fields in 7.x use a sortable data type by default.
+#1103602 by pwolanin: prevent PHP Notice when there is an invalid term reference field.
+#1097988 by jpmckinney: avoid 'using default converter' warning on Solr startup.
+#920482  by jpmckinney: $info_split['date'] template variable should contain changed, not created, date.
+#379512  by pwolanin: separate schema field for indexing comments and "extra" information.
+#871440  by jpmckinney: Solr taxonomy page displays search form and blocks when the user has no access.
+#761990  by jhedstrom, pwolanin: switch to POST for long search ULRs.
+#1112362 by pwolanin: cleanup following #1107502 to better use drupal_http_request.
+#1107502 by pwolanin: Merge relevant parts of php client into DrupalApacheSolrService.
+#899590  by jpmckinney, elliotttf: Support indexing of attached fields that are not facets.
+#993476  by jpmckinney, pwolanin: allow arbitrary results per page in the 0-200 range.
+#1080652 by elliotttf, pwolanin: Allow other modules to return search results on solr failure.
+#562214  by ecofinn, wmostry, jpmckinney: Problems with double-encoded ampersands.
+#997480  by jpmckinney | davidwhthomas: Facet checkboxes are duplicated following other javascript activity on page.
+#1098038 by pwolanin, mgifford: fix spelling suggestion incorrect use of LABEL tag.
+#1099390 by jpmckinney: Fatal error: Call to undefined function apachesolr_nodeaccess_build_subquery().
+#1098222 by pwolanin: Rename and make the nodeaccess module more generic.
+#1092910 by pwolanin: missing date field conversions from #1088208.
+#1090530 by pwolanin: Further schema-related code fixes following from #1088208.
+#783366  by elliotttf: Invoke hook_apachesolr_prepare_query() in apachesolr_search_browse
+#1078766 by elliotttf: code-style cleanup according Coder module.
+#1049114 by Steven Jones, james.williams, Janusman: restore hierarchical taxonomy facets.
+#1088208 by pwolanin | Janusman: simplify the schmea to eliminate node-specific fields.
+#1089342 by elliotttf: Content Biasing not working.
+#996800  by elliotttf: trim host, port, and path strings to prevent connection errors.
+#1020780 by jpmckinney, pwolanin: cleanup of variable_get to apachesolr_server_variable_get conversion.
+#1085630 by pwolanin: Index taxonomy term ancestors into term reference-based field.
+#1050000 by pwolanin: More generically prevent calling nodeapi update_index when indexing.
+#1060536 by jpmckinney: Long title and Default shortcut link obscured in overlay.
+#1060550 by jpmckinney: admin/build/block should be admin/structure/block.
+#704190  by pwolanin | robertDouglass | jpmckinney: Add page callbacks to display conf files in the index in reports.
+#925608  by jpmckinney | janusman: Quick perf improvement: cache term ancestors on indexing.
+#1059380 by jpmckinney: apachesolr_fields_list_display_callback doesn't return if $facet in list_allowed_values.
+#1059368 by jpmckinney: apachesolr_clear_cache as a #submit callback doesn't work.
+#1072884 by justinrandell: incorrect use of query->condition() in apachesolr_cron().
+#901720  by jpmckinney | robertDouglass: Highlighting snippets in search results not flexible enough, and logic improvement.
+#1064782 by jpmckinney: Use module_load_include, module_load_install instead of include_once.
+#791916  by Network | jpmckinney: Allow facet search block to have children always show.
+#937328  by Davy Van Den Bremt | pwolanin: Drush support for indexing remaining nodes.
+#1060698 by dww | jpmckinney: Always display the current value of apachesolr_cron_limit in the admin UI.
+#904312  by pounard: Use drupal_get_breadcrumb() not menu_get_active_breadcrumb().
+#616888  by bangpound: Pass delta to MLT blocks' theme function.
+#864146  by pwolanin | jpmckinney: Fixed When moving fq to q.alt, we should parenthesize each fq.
+#896324  by ahankinson | jpmckinney: Fixed module_invoke() called too late.
+#840358  by pwolanin | torstenzenk: Fixed Error searching Taxonomies.
+#991444  by Nick_vh: None of start, end, gap should show up as date facets.
+#835674  by pwolanin: Remove search module dependency from apachesolr.
+#961570  by jpmckinney: if apachesolr_search was the default search module in D6, make it so in D7.
+#878996  by pwolanin | weri: Added Don't break the loop.
+#1050044 by pwolanin: hook_theme API fix, and revert to using core theme('search_results').
+#1026916 by dmitry_bezer: apachesolr_get_enabled_facets() mandatory parameter was omitted.
+#528086  by pwolanin: Fix for special html entity search and display bugs.
+#1020780 by pwolanin: Store variable settings per server for better flexibility.
+#891962  by jurcello, pwolanin: avoid incorrect filter substring matches, emit correct query string.
+#1018768 by pwolanin: fix notices during failed search request.
+
+Apache Solr integration 7.x-1.0-BETA3, 2011-01-06
+------------------------------
+#1017836 by Janusman, pwolanin: fix empty search behavior.
+#1017624 by pwolanin: Fix for "Did you mean" suggestion does not show.
+#1007848 by pwolanin: Fixes for apachesolr_nodeaccess for Drupal 7.
+#1017258 by pwolanin: Fix help text when there are no search results.
+#1013122 by pwolanin: some JS cleanup to use jQuery proxy.
+#1013136 by pwolanin: fix retain filters checkbox.
+#1009398 by pwolanin: fix content bias forms.
+#1007860 by pwolanin: API fix for hook_apachesolr_modify_query, add type hinting.
+#1007824 by pwolanin: use contextual links to avoid block caching mode issues.
+#1003500 fix icon and icon path.
+#957652  by aegnor, pwolanin: fix str_replace may remove a substring in filter_extract.
+
+Apache Solr integration 7.x-1.0-BETA2, 2010-12-18
+------------------------------
+#983458  by craig_ : mass update and mass delete ignore setting apachesolr_read_only.
+#996976  by scor: list item class should be an array, not a string.
+#995526  by pwolanin: Alter server schema so we don't exceed the MyISAM key size limit.
+#1000396 by dmitry_bezer: Edit server form ignored "Make this server the default" checkbox.
+#997240  by VladGh: fix parameters for apachesolr_server_edit_form for PHP 5.3.
+#993448  by pwolanin, scor: get Solr result docs as stdClass instead of Apache_Solr_Document.
+
+Apache Solr integration 7.x-1.0-BETA1, 2010-12-09
+------------------------------
+#979198 by pwolanin, janusman: Missing 'module' property on facet $block objects.
+#992860 by pwolanin, davereid: Taxonomy indexing and faceting has to be per field, not per vocab.
+#991590 by larskleiner: fix for API change to timezone param in format_date().
+#989730 by pwolanin: Using tdate instead of data cases java exception in using rord().
+#989658 by pwolanin: Drupal 7 allows multiple term refernece fields for the same vocabulary.
+#989398 by pwolanin: get some actual tests working.
+#983892 by pwolanin: update schema to use long instead of int, plus tdate fields.
+#983894 by pwolanin: add a set of conf files that can be used to create a test core.
+#983572 by amateescu, pwolanin: fix for empty filter values causing Solr error.
+#795912 by pwolanin patch 64: fixes various settings forms and variables, avoids a notice, removes use of md5() in favor of drupal_hash_base64().
+#982846 by pwolanin: split out (and deprecate) the taxonomy path hijack feature.
+#982840 by pwolanin: OO cleanup for Drupal 7 coding standards.
+#982490 by pwolanin: rip out Drupal 6.x update functions, update README.
+#904100 by das-peter, pwolanin: prevent missing table error when comment module not enabled.
+#795912 by pwolanin patch 62: requirements fixes, add a button to test server settings.
+#795912 by pwolanin patch 61: fix up server add/edit/delete funcitonality.
+#795912 by pwolanin, crell patch 60: - makes Field API handling more generic, adds a framework
+        for handling multiple Solr servers, and moves the nodeaccess module out of the contrib dir.
+#885950 by pwolanin, csevb10: preserve added/removed filters when filterstring is re-parsed.
+#864160 by pwolanin: Allow the caller a last chance to modify the query and params.
+#536990 by pwolanin | jpmckinney, janusman: always index content as an anonymous user.
+#835850 by pwolanin: add more replicated files to solrconfig.xml master section.
+#830976 by eosrei, pwolanin: make sure we return a non-zero ping time on success.
+#795912 by tjwallace, dmitry_bezer, jpmckinney, pwolanin: inital Drupal 7 port.
+
+Apache Solr integration 6.x-2.0-BETA1, 2010-04-08
+------------------------------
+#660754 by jhedstrom: Added Allow key sorting of facets.
+#614644 by netsensei | robertDouglass: Fixed Forms attached to Apachesolr search results won't work.
+#747346 by robertDouglass, pwolanin | lazysoundsystem: Fixed Typo in apachesolr_search().module.
+#763072 by robertDouglass, justinrandell | pwolanin: Fixed warnings when indexing old, crappy html.
+#658278 by cpliakas | JThan: Fixed Errors when building the search index in PHP 5.3.
+#765486 by robertDouglass: Fixed Several cases where Luke cache not getting cleared and resulting in errors.
+#765448 by robertDouglass: Fixed Facet blocks for hierarchical taxonomy broken.
+#751420 by pwolanin, skwashd | Damien Tournoud, Scott Reynolds: Fixed apachesolr_site_hash() calls md5() twice.
+#750426 by mkalkbrenner, pwolanin | robertDouglass: Fixed fieldType textTight conficts with fieldType text and textSpell.
+
+Apache Solr integration 6.x-2.0-BETA1, 2010-03-24
+------------------------------
+#649038 by brunodbo, slip | robertDouglass: Fixed Search not working on 404 page.
+
+Apache Solr integration 6.x-2.0-ALPHA3, 2010-03-22
+------------------------------
+#610656 by pwolanin, claudiu.cristea | Scott Reynolds: Fixed Facets requests for non-enabled modules.
+#686390 by pwolanin | rjbrown99: Fixed Wrong number of initial items in taxonomy facet under certain conditions.
+#573734 by drewish | robertDouglass: Added Index controls should be radio buttons with one form submission button.
+#736540 by drewish | Scott Reynolds: Changed Minimize UPDATE queries in apachesolr_nodeapi_mass_update().
+#687738 by David Lesieur | anantagati: Fixed Avoid introducing empty 'filters' query string.
+#733116 by pwolanin | drewish: Changed Implement hook_flush_caches().
+#719356 by robertDouglass, mathieu | flk: Fixed Indexing cron triggers sigsegv in apachesolr.module line 387.
+#744038 by siliconmeadow: Changed Change of Drush extension command naming conventions.
+#558160 by robertDouglass, mihha | DenRaf, mcarbone, haxney: Added date facet for cck fields.
+#666936 by pwolanin, robertDouglass, claudiu.cristea | justindodge: Fixed apachesolr.js - Drupal.behaviors.apachesolr does not respect context.
+#708424 by janusman: Changed Change gmdate() to Drupal format_date() in date facets to support localization.
+
+Apache Solr integration 6.x-2.0-ALPHA2, 2010-01-08
+------------------------------
+#679522 by pwolanin, Add gettableFiles to solr admin interface config.
+http://drupal.org/cvs?commit=309746 by robertDouglass, add entity='comment' to comments on indexing.
+#672882 by David Lesieur: Fixed Broken 'Show more' link on taxonomy facets.
+#604566 by robertDouglass | jhedstrom: Fixed index_value() never set for CCK fields that aren't of type text, node or user referrence.
+#672530 by robertDouglass: Fixed Change array key names from display callback to display_callback() and indexing callback to indexing_callback().
+#672518 by robertDouglass: Fixed Add new trie prefixes to helper function .
+#551582 by drewish: Fixed Show value instead of key in CCK facets.
+#668396 by pwolanin, closer to fix from #655006 for PHP notices.
+#664818 by robertDouglass, pounard, pwolanin | Scott Reynolds: Fixed Wrong watchdog() usage.
+#657648 by kcoop: Added Add Smaller Limit Options to Apache Solr Cron Indexing.
+
+Apache Solr integration 6.x-2.0-ALPHA1, 2009-12-26
+------------------------------
+#664818 by robertDouglass, pounard, pwolanin | Scott Reynolds: Fixed Wrong watchdog() usage.
+#662232 by pwolanin | anarchivist: Changed Use language-neutral code like D7.
+#666648 by pwolanin: Changed Make hook_apachesolr_update_index() more generic by taking a namespace param also.
+#667110 by pwolanin: Fixed Replace bogus use of pager_query().
+#667124 by pwolanin: Fixed Use current query not altered query for the breadcrumb.
+#667650 by Dave Reid: Fixed Results of apachesolr_process_response() should return absolute URLs.
+#664572 by pwolanin: Added Add schema and core name to admin screen.
+#664860 by pounard: Fixed Wrong t() usage in apachesolr_field_name_map().
+#528086 by pwolanin, better (but still problematic) handling of entities.
+#662232 by pwolanin, index zxx as the Language neutral code.
+#401234 by mkalkbrenner, janusman, and pwolanin, reflect hierarchical taxonomy vocabulary in facets.
+#661952 by pwolanin, fix no results help text for dismax syntax.
+#348668 by pwolanin, add indexing of the 'node' entity string.
+#641954 by anarchivist, swentel, pwolanin, update schema.xml.
+#651044 by kcoop use node title for comment title when comment has no title.
+#655006 by Scott Reynolds, pwolanin fix warnings on constants.
+#652512 by robertDouglass enable use of more than just the default solr server.
+#642602 by robertDouglass, change 'content type' to 'content_type' in facet definitions.
+#641452 by robertDouglass, prevent admin from trying to re-index when in read-only mode.
+#372767 by socki, robertDouglass, pwolanin allow MLT blocks to be filtered by type and custom filters.
+#372336 by der, janusman, robertDouglass, allow name sorting of facet links.
+#611670 by pwolanin, allow modules to abort the building of documents for indexing.
+#628080 by pwolanin, update to use Solr PHP library r22 and check for it in hook_requirements.
+#638236 by mkalkbrenner and robertDouglass, undocumented dependency on taxonomy module.
+#562458 by janusman, fix typo preventing menu_rebuild regarding taxonomy hijack.
+#630798 by joshk, robertDouglass make cache_apachesolr table to facilitate better memcache utilization.
+#623046 by robertDouglass make the results that come back from a search more useful.
+#622120 by robertDouglass make the "Show more" block selection dynamic to accommodate other modules.
+#621922 by robertDouglass make the "Show more" js more robust.
+#612024 by pwolanin, Add method to allow requests to additional Solr servelets.
+#561082 by pwolanin, consolidate Solr delete queries on cron.
+#580404 by pwolanin per content type comment exclusion.
+#597174 by Frando, add hook_apachesolr_prepare_query() to enable custom sorts.
+#591278 by robertDouglass fix bug that was preventing hook_apachesolr_modify_query from working correctly.
+#590982 by swentel fix warnings on indexing.
+#554136 by emackn, Jaza make results-per-page alterable.
+#580764 by robertDougalss Add a new contrib module that allows searching on just comments.
+#548160 by robertDouglass get rid of functions that begin with underscore. Yuck.
+#580404 by robertDouglass make indexing of comments optional. New variable, apachesolr_index_comments_with_node.
+#538636 by robertDouglass allow modules to register document handlers so that multiple documents can be indexed per entity.
+#557382 by Josh Waihi, Scott Reynolds mlt blocks were double encoding titles.
+#578008 by robertDouglass improve performance by not including unused facet queries.
+#552152 by robertDouglass OR operator for facet blocks.
+#576092 by robertDouglass use Drush to search the site using Solr.
+#576040 by robertDouglass use Drush to download the SolrPhpClient: drush solr phpclient
+#457826 by janusman Make the behavior of empty searches configurable.
+#573038 by robertDouglass Automatically create facets for user and node reference CCK fields.
+#570476 by robertDouglass add initial Drush support with commands drush solr delete index and drush solr reindex.
+#570476 by robertDouglass allow for deleting or reindexing single content types.
+#456420 by anarchivist, janusman, robertDouglass Reindex using Batch API.
+#551510 by Scott Reynolds Add in ability to theme different facet blocks differently.
+#551620 by robertDouglass Type dependent facet blocks.
+#549664 by Scott Reynolds Ignore node_access for Solr Views queries.
+#551582 by robertDouglass make CCK breadcrumbs, facets, and current search show the value, not the key.
+#551278 by robertDouglass CCK mappings don't respect shared fields
+#535654 by drunken monkey Add apachesolr_server_status() function
+#543226 by drunken monkey validate port on settings form.
+#502976 by Scott Reynolds followup to Other GET parameters ignored by Apache Solr Facet Blocks
+#473554 by janusman Add an "unclick" link to search keys
+#545094 by loganfsmyth add getter and setter methods for a query's keys.
+#530910 by Damien Tournoud fix offset problem in field settings administration.
+#526344 by drunken monkey Remove apachesolr_read_only check from Drupal_ Apache_Solr_Service::_sendRawPost().
+#525980 by robertDouglass Clarify the API of apachesolr_index_updated.
+#530196 by pwolanin, fix facecount form function calls in apachesolr_og.
+#548102 by robertDouglass change wording on enabled filters page to improve usability.
+#529606 by Damien Tournoud update schema.xml with WhitespaceTokenizerFactory and SnowballPorterFilterFactory. Note that you need to stop solr, replace schema.xml, delete your index, and re-index your site.
+#528002 by janusman, Add RSS discovery to taxonomy hijack page
+#528888 by robertDouglass turn spellchecker on by default
+#528596 part 1 by robertDouglass add JS enabled checkboxes to facet and unclick links
+#525918 by robertDouglass be more forceful when reindexing; rebuild the apachesolr_search_node table completely.
+#528516 by robertDouglass add apachesolr-facet and apachesolr-unclick CSS classes to unclick and facet links.
+#528484 by robertDouglass switch to Drupal.behaviors in apachesolr.js
+#515682 by robertDouglass, add confirmation form to re-index button.
+#509526 by pwolanin, {apachesolr_search_node} table should be rebuilt when index is deleted.
+#457826 by janusman, robertDouglass - show browsable facet blocks in the search well when no search term is present to allow browsing. Hello 6.2 branch of ApacheSolr :D
+
+Apache Solr integration 6.x-1.x, xxxx-xx-xx
+------------------------------
+#508364 by pwolanin, Don't offer non-indexed fields as search options.
+#508548 by pwolanin, Don't implode params['fq'] if it's not set.
+#708424 by janusman: Changed Change gmdate() to Drupal format_date() in date facets to support localization.
+
+Apache Solr integration 6.x-1.0-RC1, 2009-07-02
+------------------------------
+#502976 by pwolanin, Scott Reynolds, robertDouglass facet links and form submissions respect non ApacheSolr $_GET parameters. Note that this changes the interface API: get_url_querystring is now get_url_queryvalues and returns and array instead of a string.
+#507708 by pwolanin, fix sort parameters to use field aliases, validate in query object.
+#299539 by kleung11 and pwolanin, use 'administer search' for permission checking.
+#503644 by pwolanin and Jeremy, make sure we strip ctrl chars last, add logging.
+#505652 by bdurbin, add apachesolr-showhide class to Show more link.
+#472600 by janusman, JacobSingh, and pwolanin, optionally hijack taxonomy pages.
+#496650 by mkalkbrenner, make unclick links work after #463900.
+#495258 comment out timeAllowed params (partial roll-back of #490076)
+#495012 by pwolanin, fix Anonymous user and other 0-value facets.
+#463900 by pwolanin and JacobSingh, facet theme function clean-up.
+#405206 by pwolanin, allow Apache Solr to be the default, let core search index 0 nodes.
+#453310 by pwolanin, allow easier theming of username display.
+#490076 by pwolanin, spellcheck more popular, maxqt to 20, limit search time.
+#358166 by David Lesieur, janusman, cptnCauliflower, and pwolanin, search for just facet(s).
+#489654 by JacobSingh, and pwolanin, allow users to set their index as "read only".
+
+Apache Solr integration 6.x-1.0-beta11, 2009-06-11
+------------------------------
+#348218 by David Lesieur, janusman, and pwolanin, retain filters for next search.
+#401046 by pwolanin, revist urlencoding of query strings.
+#467810 by aufumy, Pass in page number and caller to apachesolr_search_execute.
+#481838 by JacobSingh and pwolanin, enable plus sign in search when using clean URLs.
+#480814 by mkalkbrenner and pwolanin, add more detail to logging on errors.
+#464758 by pwolanin, 4th param to htmlspecialchars breaks PHP < 5.2.3.
+#466328 by pwolanin, fix classes for sort links.
+
+Apache Solr integration 6.x-1.0-beta10, 2009-05-14
+------------------------------
+#449414 by pwolanin, aufumy, & Scott Reynolds, refactor apachesolr_search_search().
+#462836 by pwolanin, catch fatal error in _nodeaccess if no solr.
+#461506 by pwolanin, do nothing if there are no nodes to index.
+#459930 by Scott Reynolds and pwolanin, clean up hook_enable(), uninstall, update_6004
+#453338 by pwolanin and JacobSingh, move mlt functionality into the framework module.
+#365495 by pwolanin, improve admin screens and usability of field weights.
+#454608 by pwolanin, fix current search block.
+#453182 by pwolanin, use stored path rather than forcing node/$nid.
+#448298 by JacobSingh and pwolanin, use a confirm form for index deletion.
+#454352 by Damien Tournoud, make optimize interval configurable, document variables.
+
+Apache Solr integration 6.x-1.0-beta9, 2009-04-30
+------------------------------
+#435924 by pwolanin, only clear cache on cron after updates and if the server is available.
+#405780 by blackdog and pwolanin, skip excluded node types during counting and indexing.
+#441628 by aufumy and pwolanin, update _og for negative facets, minor fixes, install/enable/update hooks.
+#447622 by pwolanin and mkalkbrenner, better encoding of html entities and CCK facets.
+#447890 by pwolanin, properly respect 'access content' permission in _nodeaccess.
+#271753 by pwolanin, more granular CCK field mappings via _alter hook.
+#436074 by pwolanin, better query class handling of negative queries.
+#442198 by Scott Reynolds and pwolanin, update the Drupal_Solr_Query_Interface interface.
+#443252 by Scott Reynolds, (bugfix for regression) make protected id public again.
+#337737 by David Lesieur, mikejoconnor and Scott Reynolds, localize arg(1) dependence.
+        Changes query get_path() to facilitate generating facets outside the search page.
+
+Apache Solr integration 6.x-1.0-beta8, 2009-04-16
+------------------------------
+#343252 by pwolanin, fix nodeaccess for method name changes, make multi-site aware.
+#432946 by pwolanin, query class and sort cleanups.
+#393480 by pwolanin and Jody Lynn, provide a book facet and facets for missing fields.
+#432140 by Damien Tournoud, use format_interval() for more attractive, localizable time intervals.
+#348029 by pwolanin, Handle negative filters and improve date facet block code.
+#254565 by drunken monkey and Scott Reynolds, change the query class to enable Views integration.
+
+Apache Solr integration 6.x-1.0-beta7, 2009-04-03
+------------------------------
+#410330 by pwolanin and bhuga, return more information for error 0.
+#293989 by bjaspan and vladimir.dolgopolov, add date facets for created and changed dates.
+#420290 by mkalkbrenner and pwolanin, add spaces around tags to avoid running words together.
+#368688 by hurleyit and pwolanin, send MLT docs instead of processed links to theme function.
+#383478 by pwolanin and JacobSingh, provide more information about autocommit lag, pending deletes.
+#339490 by aufumy, pwolanin, and JacobSingh, Organic groups Apachesolr integration, new _alter hook.
+
+Apache Solr integration 6.x-1.0-beta6, 2009-03-20
+------------------------------
+#305370 by pwolanin, Handle failed delete requests so unpublished/deleted content doesn't stay in the index.
+#407570 by pwolanin and moshe weitzman, _alter for sort links, hide for < 2 results.
+#392978 by pwolanin and ncameron, workaround for those using php 5.1, update README.
+#402984 by JacobSingh and pwolanin, put MLT menu under the general ApacheSolr settings.
+#401442 by Janusman and pwolanin, no sort block when 0 results.
+#405732 by JacobSingh, pwolanin: Update to new SolrPhpClient (r6) and make ping() use drupal_http_request.
+#405722 by JacobSingh, increase ping timeout and make it variable.
+#400882 by mkalkbrenner, fix faceting bug due to static counter in method add_field.
+#382358 by pwolanin, use tokenizer solr.CharStreamAwareWhitespaceTokenizerFactory to fix highlighting.
+
+Apache Solr integration 6.x-1.0-beta5, 2009-02-27
+------------------------------
+#305370 by pwolanin, don't delete from apachesolr table if Solr query fails.
+#385348 by moshe weitzman, use key in sort links array.
+#385362 by pwolanin, Shorten hash from 32 chars to 12.
+#383804 by JacobSingh, fix query building that broke nodeaccess.
+
+Apache Solr integration 6.x-1.0-beta4, 2009-02-23
+------------------------------
+#380670 by pwolanin, only add a bq param for a node-type boost > 'Normal'.
+#379518 by pwolanin, correct mismatch in default boost between
+        solrconfig.xml and apachesolr_search.
+#380594 by pwolanin, empty the spellcheck dictionary if the index is deleted.
+#380644 by JacobSingh, Backwards compatability for old sort fields.
+#380538 by pwolanin, fix code to find vid for taxonomy facet blocks.
+
+Apache Solr integration 6.x-1.0-beta3, 2009-02-20
+------------------------------
+#378222 by janusman and pwolanin, add boost settings for "sticky" and "promote".
+#378566 by pwolanin, nodeaccess not correctly marking single nodes for re-indexing.
+#378270 by pwolanin, suppress MLT admin link when there is no block content.
+#378196 by pwolanin, remove PHP client from CVS per Drupal.org policy.
+#231200 by janusman and pwolanin, turn on mapping of accented to non-accented
+        ISO characters for indexing and search.
+#377494 by pwolanin, Update text type in schema to new example.
+#376270 by pwolanin, also add option to bias on recent comments/changes.
+#337879 by pwolanin and blackdog, Store relative not absolute paths.
+#376255 by pwolanin, Index more node fields, use boolean fields.
+#376966 by JacobSingh and pwolanin, requesting the top terms from luke is very
+        expensive, so normally request 0, and only get them for smaller indexes.
+#375991 by pwolanin, build spellcheck index on optimize.
+#370707 by pwolanin, make sort field names consistent, make ignored multiValued.
+#375723 by pwolanin, prevent fatal error if available facet list changes.
+#373921 by JacobSingh, show pending docs on index page from autocommit.
+#371858 by pwolanin, re-fill the Luke cache after we empty it on cron.
+#372120 by pwolanin, create one MLT block by default on install of apachesolr_mlt.
+#370707 compact field names, create "order by" fields in schema.xml
+        by pwolanin and Damien Tournoud.
+#370796 avoid repeated looping/indexing in apachesolr_index_nodes() by Damien Tournoud.
+#369944 Add field aliases and further clean-up the query class, by pwolanin.
+#366959 make usage of solrsort consistent in Solr_Base_Query::solrsort by Damien Tournoud.
+#       Update errant watchdog calls that were using D5 signature by robertDouglass.
+#369780 Rearrange code for better organization and performance by robertDouglass.
+#365901 Fix bug where indexing might hang & improved API by adding a separate
+        hook for modules to indicate that a node is excluded, by pwolanin.
+#367361 Use the machine-readable name for disabled node types by pwolanin.
+#366957 Add a "configure" link to the more like this block by JacobSingh.
+#365901 Add a bias on node type (and node-type exclusion) by Damien Tournoud and pwolanin.
+
+Apache Solr integration 6.x-1.0-beta2, 2009-01-28 (changes since 6.x-1.0-alpha6 2009-Jan-08)
+------------------------------
+#365684 Get PHP library from new svn home by pwolanin
+#365620 clear stale data on hook_enable by pwolanin
+#365312 don't redirect after enabling filters by pwolanin
+#365245 invalid foreach when no facets available reported by Damien Tournoud
+#        don't let attachements be enabled since it seems be broken by pwolanin
+#363972 fix ApacheSolr to Apache Solr
+#363972 Text improvements for UI by horncologne and pwolanin
+#365063 fix module name in admin screen by pwolanin
+#365022 fatal error in MLT when no Solr server by pwolanin
+#364446 fix space problem and clean up query class by pwolanin
+#355525 fix mis-named variables, patch by pwolanin,  bug reported by flexer
+#339467 centralize/register facets blocks by paul.lovvik, JacobSingh and
+        pwolanin
+#362389 make the _image module work by pwolanin
+#364140 fix mlt schema bug, thanks to webrascal
+#364384 reorder selects to have bigger numbers at the top by pwolanin, suggested
+        by horncologne
+#350330 make sure to index dates as GMT, thanks to webrascal
+#363416 reindex without blowing away either the solr index or the core search index by pwolanin
+#360227 strip ctl chars() also on path, reported by flexer
+#348215 cleanup - simplify branching, numerically index array by pwolanin
+#348215 add missing js file with minor text chenges (js file by vladimir.dolgopolov)
+#359923 separate cron limit for apachesolr by pwolanin
+#       remove lang module - code was already added to apachesolr_search
+#292662 commit after we delete the index by pwolanin
+#356696 by pwolanin: copies the author's name to a string field for use in
+        multisite search or sorting by author.  Also snuck in non-compression on
+        the body for performance.
+#348215 by vladimir.dolgopolov: More link for additional facets.
+#292662 update README by pwolanin
+#344249 obey 32 char limit for block deltas by pwolanin
+#355479 fix PHP warning when request fails by pwolanin
+#355544 Add a ->clearCache(); in apachesolr_index_page(), by flexer and pwolanin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/Drupal_Apache_Solr_Service.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,903 @@
+<?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 DrupalApacheSolrService 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_apachesolr');
+      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_apachesolr');
+      }
+    }
+  }
+
+  /**
+   * 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_apachesolr');
+        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_apachesolr');
+      }
+    }
+  }
+
+  /**
+   * 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_apachesolr');
+        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_apachesolr');
+        }
+      }
+    }
+  }
+
+  /**
+   * 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_apachesolr', TRUE);
+      cache_clear_all($this->env_id . ":luke:", 'cache_apachesolr', 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 DrupalApacheSolrService::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 ApacheSolrDocument 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 ApacheSolrDocument)) {
+        $rawPost .= ApacheSolrDocument::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
+    $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 = apachesolr_environment_variable_get($this->env_id, 'apachesolr_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/sites/all/modules/custom/solrconnect/LICENSE.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/README.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,194 @@
+
+This module integrates Drupal with the Apache Solr search platform. Solr search
+can be used as a replacement for core content search and boasts both extra
+features and better performance. Among the extra features is the ability to have
+faceted search on facets ranging from content author to taxonomy to arbitrary
+Field API fields.
+
+The module comes with a schema.xml, solrconfig.xml, and protwords.txt file which
+must be used in your Solr installation.
+
+This module depends on the search framework in core.  When used in combination
+with core search module, Apache Solr is not the default search. Access it via a
+new tab on the default search page, called "Site".  You may configure it
+to be default at ?q=admin/config/search/settings
+
+Updating from 6.x
+-----------------
+
+IMPORTANT: there is no upgrade path from 6.x-1.x or 6.x-2.x. If you previously
+installed those modules you must disable and uninstall them prior to
+installing 7.x-1.x.
+
+You will have to install the new schema.xml and solrconfig.xml files, and restart
+the Solr server (or core) and delete your index and reindex all content.
+
+Installation
+------------
+
+Prerequisite: Java 5 or higher (a.k.a. 1.5.x).  PHP 5.2.4 or higher.
+
+Install the Apache Solr Drupal module as you would any Drupal module. Note
+that the Drupal 7.x-1.x branch does not require the SolrPhpClient to
+be installed. All necessary code is now included with this module.
+
+Before enabling the module, you must have a working Solr server, or be
+subscribed to a service like Acquia Search.
+
+The Debian/Ubuntu packages for Solr should NOT be used to install Solr.
+For example, do NOT install the solr or solr-jetty packages.
+
+Download the latest Solr 1.4.x or 3.x release (e.g. 1.4.1 or 3.6.1) from:
+http://www.apache.org/dyn/closer.cgi/lucene/solr/
+
+Apache Lucene 3.1, 3.2 or 3.3, have a possible index corruption bug on
+server crash or power loss (LUCENE-3418) and have bugs that interfere
+with the Drupal admin reports. Solr 3.4 has a problem with
+SortMissingLast so Solr 3.5.0 or later is strongly preferred.
+
+Unpack the Solr tarball somewhere not visible to the web (not in your
+webserver docroot and not inside of your Drupal directory).
+
+The Solr download comes with an example application that you can use for
+testing, development, and even for smaller production sites. This
+application is found at apache-solr-1.4.1/example.
+
+You must use 3 Solr configuration files that come with the Drupal
+module or the integration will not work correctly.
+
+For Solr 1.4 use the ones found in:
+solr-conf/solr-1.4/
+
+for Solr 3.5.0 or 3.6.1 use:
+solr-conf/solr-3.x/
+
+While the Solr 1.4 files will work for Solr 3.5+, they are not optimal
+and you will be missing important new features.
+
+For example, when deploying solr 1.4:
+
+Move apache-solr-1.4.1/example/solr/conf/schema.xml and rename it to
+something like schema.bak. Then move the solr-conf/solr-1.4/schema.xml
+that comes with this Drupal module to take its place.
+
+Similarly, move apache-solr-1.4.1/example/solr/conf/solrconfig.xml and rename
+it like solrconfig.bak. Then move the solr-conf/solr-1.4/solrconfig.xml
+that comes with this module to take its place.
+
+Finally, move apache-solr-1.4.1/example/solr/conf/protwords.txt and rename it
+protwords.bak. Then move the solr-conf/solr-1.4/protwords.txt that comes
+with this module to take its place.
+
+Make sure that the conf directory includes the following files - the Solr core
+may not load if you don't have at least an empty file present:
+solrconfig.xml
+schema.xml
+elevate.xml
+mapping-ISOLatin1Accent.txt
+protwords.txt
+stopwords.txt
+synonyms.txt
+
+Now start the solr application by opening a shell, changing directory to
+apache-solr-1.4.1/example, and executing the command java -jar start.jar
+
+Test that your solr server is now available by visiting
+http://localhost:8983/solr/admin/
+
+Now, you should enable the "Apache Solr framework" and "Apache Solr search"
+modules. Check that you can connect to Solr at ?q=admin/setting/apachesolr
+Now run cron on your Drupal site until your content is indexed. You
+can monitor the index at ?q=admin/settings/apachesolr/index
+
+The solrconfig.xml that comes with this modules defines auto-commit, so
+it may take a few minutes between running cron and when the new content
+is visible in search.
+
+To use facets you should download facetapi http://drupal.org/project/facetapi
+This module will allow you to define and set facets next to your search pages.
+Once this module is enabled, enable blocks for facets first at
+Administer > Site configuration > Apache Solr > Enabled filters
+then position them as you like at Administer > Site building > Blocks.
+
+Settings.php
+------------
+You can override environment settings using the following syntax in your
+settings.php
+
+$conf['apachesolr_environments']['my_env_id']['url'] = 'http://localhost:8983';
+
+Configuration variables
+-----------------------
+
+The module provides some (hidden) variables that can be used to tweak its
+behavior:
+
+ - apachesolr_luke_limit: the limit (in terms of number of documents in the
+   index) above which the module will not retrieve the number of terms per field
+   when performing LUKE queries (for performance reasons).
+
+ - apachesolr_tags_to_index: the list of HTML tags that the module will index
+   (see apachesolr_add_tags_to_document()).
+
+ - apachesolr_exclude_nodeapi_types: an array of node types each of which is
+   an array of one or more module names, such as 'comment'.  Any type listed
+   will have any listed modules' hook_node_update_index() implementation skipped
+   when indexing. This can be useful for excluding comments or taxonomy links.
+
+ - apachesolr_ping_timeout: the timeout (in seconds) after which the module will
+   consider the Apache Solr server unavailable.
+
+ - apachesolr_optimize_interval: the interval (in seconds) between automatic
+   optimizations of the Apache Solr index. Set to 0 to disable.
+
+ - apachesolr_cache_delay: the interval (in seconds) after an update after which
+   the module will requery the Apache Solr for the index structure. Set it to
+   your autocommit delay plus a few seconds.
+
+ - apachesolr_query_class: the default query class to use.
+
+ - apachesolr_index_comments_with_node: TRUE | FALSE. Whether to index comments
+   along with each node.
+
+ - apachesolr_cron_mass_limit: update or delete at most this many documents in
+   each Solr request, such as when making {apachesolr_search_node} consistent
+   with {node}.
+
+ - apachesolr_index_user: Define with which user you want the index process to
+   happen.
+
+Troubleshooting
+---------------
+Problem:
+You use http basic auth to limit access to your Solr server.
+
+Solution:
+Set the Server URL to include the username and password like
+http://username:password@example.com:8080/solr
+
+Problem:
+Links to nodes appear in the search results with a different host name or
+subdomain than is preferred.  e.g. sometimes at http://example.com
+and sometimes at http://www.example.com
+
+Solution:
+Set $base_url in settings.php to insure that an identical absolute url is
+generated at all times when nodes are indexed.  Alternately, set up a re-direct
+in .htaccess to prevent site visitors from accessing the site via more than one
+site address.
+
+Problem:
+The 'Solr Index Queries' test fails with file permission errors.
+
+Solution:
+When running this test you should have your tomcat/jetty running as the same user
+as the user under which PHP runs (often the same as the webserver). This is
+important because of the on-the-fly folder creation within PHP.
+
+
+Themers
+----------------
+
+See inline docs in apachesolr_theme and apachesolr_search_theme functions
+within apachesolr.module and apachesolr_search.module.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/Solr_Base_Query.php	Wed Jul 31 13:49:13 2013 +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'),
+      'sort_label' => array('title' => t('Title'), 'default' => 'asc'),
+      'bundle' => array('title' => t('Type'), 'default' => 'asc'),
+      'sort_name' => array('title' => t('Author'), 'default' => 'asc'),
+      'ds_created' => 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/sites/all/modules/custom/solrconnect/apachesolr.admin.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrconnect/apachesolr.api.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,426 @@
+<?php
+/**
+ * @file
+ *   Exposed Hooks in 7.x:
+ */
+
+/**
+ * Lets modules know when the default environment is changed.
+ *
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @param string $old_env_id
+ *   The old machine name of the environment.
+ */
+function hook_apachesolr_default_environment($env_id, $old_env_id) {
+  $page = apachesolr_search_page_load('core_search');
+  if ($page && $page['env_id'] != $env_id) {
+    $page['env_id'] = $env_id;
+    apachesolr_search_page_save($page);
+  }
+}
+
+/**
+ * Add index mappings for Field API types. The default mappings array
+ * handles just list fields and taxonomy term reference fields, such as:
+ *
+ * $mappings['list_text'] = array(
+ *   'indexing_callback' => 'apachesolr_fields_list_indexing_callback',
+ *   'index_type' => 'string',
+ *   'map callback' => 'apachesolr_fields_list_display_callback',
+ *   'facets' => TRUE,
+ * ),
+ *
+ * In your implementation you can add additional field types such as:
+ * $mappings['number_integer']['number'] = array(...);
+ *
+ * You can also add mapping for a specific field. This will take precedence
+ * over any mapping for a general field type. A field-specific mapping would
+ * looks like:
+ * $mappings['per-field']['field_model_name'] = array(...);
+ *
+ * Much more information can be found below in the example implementation or in
+ * facetapi.api.php. If you feel restricted with the options as set below
+ * there is nothing that stops you from implementing facetapi directly. However
+ * it is recommended to not directly talk to solr fields since this could break
+ * in the future.
+ *
+ * @return array $mappings
+ *   An associative array of mappings as defined by modules that implement
+ *   hook_apachesolr_field_mappings().
+ */
+function hook_apachesolr_field_mappings() {
+  $mappings = array(
+    // Example for a field API type. See extensive documentation below
+    'number_float' => array(
+      'indexing_callback' => 'apachesolr_fields_default_indexing_callback',
+      'index_type' => 'tfloat',
+      'facets' => TRUE,
+      'query types' => array('term', 'numeric_range'),
+      'query type' => 'term',
+      'facet mincount allowed' => TRUE,
+    ),
+    // Example for a field API field
+    'per-field' => array(
+      // machine name of the field in Field API
+      'field_price' => array(
+        // REQUIRED FIELDS //
+        // Function callback to return the value that will be put in to
+        // the solr index
+        'indexing_callback' => 'apachesolr_fields_default_indexing_callback',
+
+        // NON REQUIRED FIELDS //
+        // See apachesolr_index_key() for the correct type. Defaults string
+        'index_type' => 'string',
+        // How to display the values when they return as a facet
+        'map callback' => 'apachesolr_fields_list_facet_map_callback',
+        // Does your facet have a dynamic name? Add function call here and will
+        // have the name of the return value
+        'name callback' => FALSE,
+        // If a custom field needs to be searchable but does not need to be faceted you
+        // can change the 'facets' parameter to FALSE.
+        'facets' => FALSE,
+        // Do you want to allow items without value
+        'facet missing allowed' => FALSE,
+        // (optional)  Whether or not the facet supports the
+        //    "minimum facet count" setting. Defaults to TRUE.
+        'facet mincount allowed' => FALSE,
+        // Field API allows any field to be multi-valued.
+        // If we set this to false we are able to sort
+        'dependency plugins' => array('bundle', 'role'),
+        // Does your solr index has a hierarchy?
+        // See facetapi_get_taxonomy_hierarchy for details or
+        // view the mapping of taxonomy_term_reference
+        'hierarchy callback' => FALSE,
+        // There are different query types to return information from Solr
+        // term : Regular strings
+        // date : Everything regarding dates
+        // numeric_range : Useful when you have widgets that depend
+        //   on statistics coming from Solr
+        'query types' => array('term', 'numeric_range'),
+        // Backwards compatible with previous facetapi versions.
+        // Pick the main query type
+        'query type' => 'term',
+        // What dependencies do you have (see facetapi)
+        'multiple' => TRUE,
+      ),
+    ),
+  );
+  return $mappings;
+}
+
+/**
+ * Alter hook for apachesolr_field_mappings().
+ *
+ * Add or alter index mappings for Field API types. The default mappings array
+ * handles just list fields and taxonomy term reference fields, in the same way
+ * as documented in hook_apachesolr_field_mappings.
+ *
+ * @param array $mappings
+ *   An associative array of mappings as defined by modules that implement
+ *   hook_apachesolr_field_mappings().
+ * @param string $entity_type
+ *   The entity type for which you want to alter the field mappings
+ */
+function hook_apachesolr_field_mappings_alter(array &$mappings, $entity_type) {
+  // Enable indexing for text fields
+  $mappings['text'] = array(
+    'indexing_callback' => 'apachesolr_fields_default_indexing_callback',
+    'map callback' => '',
+    'index_type' => 'string',
+    'facets' => TRUE,
+    'facet missing allowed' => TRUE,
+    'dependency plugins' => array('bundle', 'role'),
+    'hierarchy callback' => FALSE,
+    'name callback' => '',
+    'facet mincount allowed' => FALSE,
+    'multiple' => FALSE,
+  );
+
+  // Add our per field mapping here so we can sort on the
+  // price by making it single. Solr cannot sort on multivalued fields
+  // field_price is our identifier of a custom field, and it was decided to
+  // index in the same way as a number_float field.
+  $mappings['per-field']['field_price'] = $mappings['number_float'];
+  $mappings['per-field']['field_price']['multiple'] = FALSE;
+}
+
+/**
+ * Prepare the query by adding parameters, sorts, etc.
+ *
+ * This hook is invoked before the query is cached. The cached query is used
+ * after the search such as for building facet and sort blocks, so parameters
+ * added during this hook may be visible to end users.
+ *
+ * This is otherwise the same as HOOK_apachesolr_query_alter(), but runs before
+ * it.
+ *
+ * @param DrupalSolrQueryInterface $query
+ *  An object implementing DrupalSolrQueryInterface. No need for &.
+ */
+function hook_apachesolr_query_prepare(DrupalSolrQueryInterface $query) {
+  // Add a sort on the node ID.
+  $query->setAvailableSort('entity_id', array(
+    'title' => t('Node ID'),
+    'default' => 'asc',
+  ));
+}
+
+/**
+ * Assigns a readable name to your custom solr field
+ *
+ * @param array $map
+ */
+function hook_apachesolr_field_name_map_alter(array &$map) {
+  $map['xs_node'] = t('The full node object');
+}
+
+/**
+ * Alter the query after it's prepared and cached.
+ *
+ * Any module performing a search should call
+ * drupal_alter('apachesolr_query', $query). That function then invokes this
+ * hook. It allows modules to modify the query object and its parameters.
+ *
+ * A module implementing HOOK_apachesolr_query_alter() may set
+ * $query->abort_search to TRUE to flag the query to be aborted.
+ *
+ * @param DrupalSolrQueryInterface $query
+ *   An object implementing DrupalSolrQueryInterface. No need for &.
+ */
+function hook_apachesolr_query_alter(DrupalSolrQueryInterface $query) {
+  // I only want to see articles by the admin.
+  //
+  // NOTE: this "is_uid" filter does NOT refer to the English word "is"
+  // It is a combination of flags representing Integer-Single, which is
+  // abbreviated with the letters i and s.
+  //
+  // @see the <dynamicField> definitions in schema.xml or schema-solr3.xml
+  $query->addFilter("is_uid", 1);
+
+  // Only search titles.
+  $query->replaceParam('qf', 'label');
+}
+
+/**
+ * Allows a module to modify the delete query.
+ *
+ * @param string $query
+ *   Defaults to *:*
+ *   This is not an instance of DrupalSolrQueryInterface, it is the raw query that is being sent to Solr
+ */
+function hook_apachesolr_delete_by_query_alter($query) {
+  // use the site hash so that you only delete this site's content
+  if ($query == '*:*') {
+    $query = 'hash:' . apachesolr_site_hash();
+  }
+  else {
+    $query .= ' AND hash:' . apachesolr_site_hash();
+  }
+}
+
+/**
+ * This is the place to look for the replacement to hook_apachesolr_node_exclude
+ * You should define a replacement for the status callback and return
+ * FALSE for entities which you do not want to appear in the index and TRUE for
+ * those that you want to include
+ */
+
+/**
+ * This is invoked for each entity that is being inspected to be added to the
+ * index. if any module returns TRUE, the entity is skipped for indexing.
+ *
+ * @param string $entity_id
+ * @param string $entity_type
+ * @param object $row
+ *   A complete set of data from the indexing table.
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @return boolean
+ */
+function hook_apachesolr_exclude($entity_id, $entity_type, $row, $env_id) {
+  // Never index media entities to core_1
+  if ($entity_type == 'media' && $env_id == 'core_1') {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * This is invoked for each entity from the type of ENTITY_TYPE that is being
+ * inspected to be added to the index. if any module returns TRUE, 
+ * the entity is skipped for indexing.
+ *
+ * @param string $entity_id
+ * @param object $row
+ *   A complete set of data from the indexing table.
+ * @param string $env_id
+ *   The machine name of the environment.
+ * @return boolean
+ */
+function hook_apachesolr_ENTITY_TYPE_exclude($entity_id, $row, $env_id) {
+  // Never index ENTITY_TYPE to core_1
+  if ($env_id == 'core_1') {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Add information to index other entities.
+ * There are some modules in http://drupal.org that can give a good example of
+ * custom entity indexing such as apachesolr_user, apachesolr_term
+ *
+ * @param array $entity_info
+ */
+function hook_apachesolr_entity_info_alter(array &$entity_info) {
+  // REQUIRED VALUES
+  // myentity should be replaced with user/node/custom entity
+  $entity_info['node'] = array();
+  // Set this entity as indexable
+  $entity_info['node']['indexable'] = TRUE;
+  // Validate each entity if it can be indexed or not. Multiple callbacks are
+  // allowed. If one of them returns false it won't be indexed
+  $entity_info['node']['status callback'][] = 'apachesolr_index_node_status_callback';
+  // Build up a custom document.
+  $entity_info['node']['document callback'][] = 'apachesolr_index_node_solr_document';
+  // What to do when a reindex is issued. Most probably this will reset all the
+  // items in the index_table
+  $entity_info['node']['reindex callback'] = 'apachesolr_index_node_solr_reindex';
+
+  // OPTIONAL VALUES
+  // Index in a separate table? Useful for huge datasets.
+  $entity_info['node']['index_table'] = 'apachesolr_index_entities_node';
+  // Execute custom callback on each cron run.
+  // See apachesolr_index_node_check_table
+  $entity_info['node']['cron_check'] = 'apachesolr_index_node_check_table';
+  // Specific output processing for the results
+  $entity_info['node']['apachesolr']['result callback'] = 'apachesolr_search_node_result';
+
+  // BUNDLE SPECIFIC OVERRIDES
+  // The following can be overridden on a per-bundle basis.
+  // The bundle-specific settings will take precedence over the entity settings.
+  $entity_info['node']['bundles']['page']['apachesolr']['result callback'] = 'apachesolr_search_node_result';
+  $entity_info['node']['bundles']['page']['apachesolr']['status callback'][] = 'apachesolr_index_node_status_callback';
+  $entity_info['node']['bundles']['page']['apachesolr']['document callback'][] = 'apachesolr_index_node_solr_document';
+}
+
+
+/**
+ * The is invoked by apachesolr_search.module for each document returned in a
+ * search. This has been introduced in 6.x-beta7 as a replacement for the call
+ * to HOOK_nodeapi().
+ *
+ * @param ApacheSolrDocument $document
+ *   The ApacheSolrDocument instance.
+ * @param array $extra
+ * @param DrupalSolrQueryInterface $query
+ */
+function hook_apachesolr_search_result_alter(ApacheSolrDocument $document, array &$extra, DrupalSolrQueryInterface $query) {
+}
+
+/**
+ * This is invoked by apachesolr_search.module for the whole resultset returned
+ * in a search.
+ *
+ * @param array $results
+ *   The returned search results.
+ * @param DrupalSolrQueryInterface $query
+ *   The query for which we want to process the results from
+ */
+function hook_apachesolr_process_results(array &$results, DrupalSolrQueryInterface $query) {
+  foreach ($results as $id => $result) {
+    $results[$id]['title'] = t('[Result] !title', array('!title' => $result['title']));
+  }
+}
+
+/**
+ * Respond to search environment deletion.
+ *
+ * This hook is invoked from apachesolr_environment_delete() after the
+ * environment is removed from the database.
+ *
+ * @param array $environment
+ *   The environment object that is being deleted.
+ */
+function hook_apachesolr_environment_delete(array $environment) {
+}
+
+/**
+ *
+ * Modify the build array for any search output build by Apache Solr
+ * This includes core and custom pages and makes it very easy to modify both
+ * of them at once
+ *
+ * @param array $build
+ * @param array $search_page
+ */
+function hook_apachesolr_search_page_alter(array &$build, array $search_page) {
+  // Adds a text to the top of the page
+  $info = array('#markup' => t('Add information to every search page'));
+  array_unshift($build, $info);
+}
+
+/**
+ * Modify the search types as found in the search pages administration
+ *
+ * @param array $search_types
+ */
+function hook_apachesolr_search_types_alter(&$search_types) {
+  $search_types['ss_language'] = array(
+    'name' => apachesolr_field_name_map('ss_language'),
+    'default menu' => 'search/language/%',
+    'title callback' => 'custom_title_callback',
+  );
+}
+
+/**
+ * Build the documents before sending them to Solr.
+ * The function is the follow-up for apachesolr_update_index
+ *
+ * @param ApacheSolrDocument $document
+ * @param object $entity
+ * @param string $entity_type
+ * @param string $env_id
+ *   The machine name of the environment.
+ */
+function hook_apachesolr_index_document_build(ApacheSolrDocument $document, $entity, $entity_type, $env_id) {
+
+}
+
+/**
+ * Build the documents before sending them to Solr.
+ *
+ * Supports all types of
+ * hook_apachesolr_index_document_build_' . $entity_type($documents[$id], $entity, $env_id);
+ *
+ * The function is the follow-up for apachesolr_update_index but then for
+ * specific entity types
+ *
+ * @param ApacheSolrDocument $document
+ * @param object $entity
+ * @param string $env_id
+ *   The machine name of the environment.
+ */
+function hook_apachesolr_index_document_build_ENTITY_TYPE(ApacheSolrDocument $document, $entity, $env_id) {
+  // Index field_main_image as a separate field
+  if ($entity->type == 'profile') {
+    $user = user_load(array('uid' => $entity->uid));
+    // Hard coded field, not recommended for inexperienced users.
+    $document->setMultiValue('sm_field_main_image', $user->picture);
+  }
+}
+
+/**
+ * Alter the prepared documents from one entity before sending them to Solr.
+ *
+ * @param $documents
+ *   Array of ApacheSolrDocument objects.
+ * @param object $entity
+ * @param string $entity_type
+ * @param string $env_id
+ *   The machine name of the environment.
+ */
+function hook_apachesolr_index_documents_alter(array &$documents, $entity, $entity_type, $env_id) {
+  // Do whatever altering you need here
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,36 @@
+
+table.admin-apachesolr td.icon {
+  background: no-repeat center;
+  width: 16px;
+}
+table.admin-apachesolr td.operation {
+  width: 16px;
+}
+table.admin-apachesolr tr.ok {
+  background-color : #E5FFE2;
+}
+table.admin-apachesolr tr.error {
+  color: #8C2E0B;
+  background-color : #FEF5F1;
+}
+
+table.admin-apachesolr td.status-icon {
+  width: 16px;
+  padding-right: 0;
+}
+table.admin-apachesolr td.status-icon div {
+  background-repeat: no-repeat;
+  height: 16px;
+  width: 16px;
+}
+table.admin-apachesolr tr {
+  border-bottom: 1px solid #ccc;
+}
+
+table.admin-apachesolr tr td.default-environment {
+  font-weight: bold;
+}
+
+table.admin-apachesolr tr.error td.status-icon div {
+  background-image: url(/misc/message-16-error.png);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr.index.inc	Wed Jul 31 13:49:13 2013 +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_apachesolr_exclude()
+ * - hook_apachesolr_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 apachesolr_index_get_entities_to_index()
+ * @see apachesolr_index_entity_to_documents()
+ * @see apachesolr_index_send_to_solr()
+ */
+function apachesolr_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 = apachesolr_index_get_entities_to_index($env_id, $entity_type, $limit);
+    $documents = array();
+    foreach ($rows as $row) {
+      $row_documents = apachesolr_index_entities_document($row, $entity_type, $env_id);
+      $documents = array_merge($documents, $row_documents);
+    }
+
+    $indexed = apachesolr_index_send_to_solr($env_id, $documents);
+    if ($indexed !== FALSE) {
+      $documents_submitted += count($documents);
+      $index_position = apachesolr_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;
+          }
+        }
+      }
+      apachesolr_set_last_index_position($env_id, $entity_type, $max_changed, $max_entity_id);
+      apachesolr_set_last_index_updated($env_id, REQUEST_TIME);
+    }
+  }
+  return $documents_submitted;
+}
+
+/**
+ * Convert a certain entity from the apachesolr index table to a set of documents. 1 entity
+ * can be converted in multiple documents if the apachesolr_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 ApacheSolrDocument(s)
+ */
+function apachesolr_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('apachesolr_exclude') as $module) {
+      $exclude = module_invoke($module, 'apachesolr_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('apachesolr_' . $entity_type . '_exclude') as $module) {
+      $exclude = module_invoke($module, 'apachesolr_' . $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, apachesolr_index_entity_to_documents($row, $env_id));
+    }
+  }
+  else {
+    // Delete the entity from our index if the status callback returned 0
+    apachesolr_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 apachesolr_index_status($env_id) {
+  $remaining = 0;
+  $total = 0;
+
+  foreach (entity_get_info() as $entity_type => $info) {
+    $bundles = apachesolr_get_index_bundles($env_id, $entity_type);
+    if (empty($bundles)) {
+      continue;
+    }
+
+    $table = apachesolr_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 = apachesolr_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('apachesolr_index_' . $entity_type);
+
+
+    if ($table == 'apachesolr_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 apachesolr_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 "apachesolr_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:
+ * - apachesolr_index_document_build()
+ * - apachesolr_index_document_build_ENTITY_TYPE()
+ * - apachesolr_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 apachesolr_index_nodes() for the old-skool version.
+ */
+function apachesolr_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('apachesolr_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 = _apachesolr_index_process_entity_get_document($entity, $entity_type);
+
+  //Get the callback array to add stuff to the document
+  $callbacks = apachesolr_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('apachesolr_index_document_build', $document, $entity, $entity_type, $env_id);
+
+    // Call a type-specific hook to add stuff to the document.
+    module_invoke_all('apachesolr_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.
+    apachesolr_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('apachesolr_index_documents_alter') as $module) {
+    $function = $module . '_apachesolr_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 apachesolr_index_send_to_solr($env_id, array $documents) {
+  try {
+    // Get the $solr object
+    $solr = apachesolr_get_solr($env_id);
+    // If there is no server available, don't continue.
+    if (!$solr->ping(variable_get('apachesolr_ping_timeout', 4))) {
+      throw new Exception(t('No Solr instance available during indexing.'));
+    }
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+    return FALSE;
+  }
+  // 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 ApacheSolrDocument $document
+ * @param string $text
+ *   must be stripped of control characters before hand.
+ *
+ */
+function apachesolr_index_add_tags_to_document(ApacheSolrDocument $document, $text) {
+  $tags_to_index = variable_get('apachesolr_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]} .= ' ' . apachesolr_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 ApacheSolrDocument
+ */
+function _apachesolr_index_process_entity_get_document($entity, $entity_type) {
+  list($entity_id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+  $document = new ApacheSolrDocument();
+
+  // 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 = apachesolr_document_id($entity_id, $entity_type);
+  $document->site = url(NULL, $url_options);
+  $document->hash = apachesolr_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 apachesolr_index_get_entities_to_index($env_id, $entity_type, $limit) {
+  $rows = array();
+  if (variable_get('apachesolr_read_only', 0)) {
+    return $rows;
+  }
+  $bundles = apachesolr_get_index_bundles($env_id, $entity_type);
+  if (empty($bundles)) {
+    return $rows;
+  }
+
+  $table = apachesolr_get_indexer_table($entity_type);
+  // Get $last_entity_id and $last_changed.
+  $last_index_position = apachesolr_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('apachesolr_index_' . $entity_type);
+
+  if ($table == 'apachesolr_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 = apachesolr_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 apachesolr_index_delete_index($env_id, $entity_type = NULL, $bundle = NULL) {
+  // Instantiate a new Solr object.
+  try {
+    $solr = apachesolr_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:' . apachesolr_site_hash()
+    drupal_alter('apachesolr_delete_by_query', $query);
+    $solr->deleteByQuery($query);
+    $solr->commit();
+
+    if (!empty($entity_type)) {
+      $rebuild_callback = apachesolr_entity_get_callback($entity_type, 'reindex callback');
+      if (is_callable($rebuild_callback)) {
+        $rebuild_callback($env_id, $bundle);
+      }
+    }
+    else {
+      apachesolr_index_mark_for_reindex($env_id);
+    }
+
+    apachesolr_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 apachesolr_index_delete_bundles($env_id, $entity_type, array $excluded_bundles) {
+  // Remove newly omitted bundles.
+  try {
+    $solr = apachesolr_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:' . apachesolr_site_hash()
+      drupal_alter('apachesolr_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 apachesolr_index_delete_entity_from_index($env_id, $entity_type, $entity_id) {
+  static $failed = FALSE;
+  if ($failed) {
+    return FALSE;
+  }
+  try {
+    $solr = apachesolr_get_solr($env_id);
+    $document_id = apachesolr_document_id($entity_id, $entity_type);
+    $query = "id:\"$document_id\" OR sm_parent_document_id:\"$document_id\"";
+    $solr->deleteByQuery($query);
+    apachesolr_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 apachesolr_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['apachesolr']) && ($entity_info['apachesolr']['indexable'])) {
+        $reindex_callback = apachesolr_entity_get_callback($type, 'reindex callback');
+        if (!empty($reindex_callback)) {
+          call_user_func($reindex_callback, $env_id);
+        }
+      }
+    }
+  }
+  apachesolr_clear_last_index_position($env_id, $entity_type);
+  cache_clear_all('*', 'cache_apachesolr', 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 apachesolr_index_set_bundles($env_id, $entity_type, array $bundles) {
+  $transaction = db_transaction();
+  try {
+    db_delete('apachesolr_index_bundles')
+      ->condition('env_id', $env_id)
+      ->condition('entity_type', $entity_type)
+      ->execute();
+
+    if ($bundles) {
+      $insert = db_insert('apachesolr_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 ApacheSolrDocument $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 ApacheSolrDocument documents
+ */
+function apachesolr_index_node_solr_document(ApacheSolrDocument $document, $node, $entity_type, $env_id) {
+  // None of these get added unless they are explicitly in our schema.xml
+  $document->label = apachesolr_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 = apachesolr_clean_text($text);
+
+  // Adding the teaser
+  if (isset($node->teaser)) {
+    $document->teaser = apachesolr_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 = apachesolr_date_iso($node->created);
+  $document->ds_changed = apachesolr_date_iso($node->changed);
+
+  // Comment counts + time
+  if (isset($node->last_comment_timestamp) && !empty($node->comment_count)) {
+    $document->ds_last_comment_timestamp = apachesolr_date_iso($node->last_comment_timestamp);
+    $document->ds_last_comment_or_change = apachesolr_date_iso(max($node->last_comment_timestamp, $node->changed));
+    $document->is_comment_count = $node->comment_count;
+  }
+  else {
+    $document->ds_last_comment_or_change = apachesolr_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('apachesolr_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 = apachesolr_clean_text($comments);
+    // @todo: do we want to reproduce apachesolr_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 = apachesolr_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 apachesolr_index_node_bundles_changed($env_id, $existing_bundles, $new_bundles) {
+  // Nothing to do for now.
+}
+
+/**
+ * Reindexing callback for ApacheSolr, 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 apachesolr_index_node_solr_reindex($env_id, $bundle = NULL) {
+  $indexer_table = apachesolr_get_indexer_table('node');
+  $transaction = db_transaction();
+  try {
+    $indexable_bundles = apachesolr_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 ApacheSolr, for nodes.
+ * after indexing a certain amount of nodes
+ *
+ * @param $entity_id
+ * @param $entity_type
+ *
+ * @return int
+ *   The status of the node
+ */
+function apachesolr_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 apachesolr_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 = apachesolr_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_' . apachesolr_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 apachesolr_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 apachesolr_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 = 'apachesolr_floatval';
+        break;
+      default:
+        $numeric = FALSE;
+        $function = 'apachesolr_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 = apachesolr_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 apachesolr_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 = apachesolr_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 = apachesolr_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 apachesolr_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 = apachesolr_date_iso($value['value']);
+        $fields[] = array(
+          'key' => $index_key,
+          'value' => $index_value,
+        );
+      }
+      if (isset($value['value2']) && $value['value'] != 0) {
+        $index_value = apachesolr_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 apachesolr_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 apachesolr_nodereference_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  if (!empty($entity->{$field_name})) {
+    $index_key = apachesolr_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 apachesolr_userreference_indexing_callback($entity, $field_name, $index_key, array $field_info) {
+  $fields = array();
+  if (!empty($entity->$field_name)) {
+    $index_key = apachesolr_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 apachesolr_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 = apachesolr_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 ApacheSolrDocument $document
+ * @param type $text
+ */
+function apachesolr_add_tags_to_document(ApacheSolrDocument $document, $text) {
+  $tags_to_index = variable_get('apachesolr_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]} .= ' ' . apachesolr_clean_text($matches[2][$key]);
+    }
+  }
+}
+
+/**
+ * hook_cron() helper to try to make the index table consistent with their
+ * respective entity table.
+ */
+function apachesolr_index_node_check_table() {
+  // Check for unpublished content that wasn't deleted from the index.
+  $table = apachesolr_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('apachesolr_cron_mass_limit', 500);
+  $query = db_select($table, 'aien')
+    ->fields('n', array('nid', 'status'))
+    ->where('aien.status <> n.status')
+    ->range(0, ($limit * 2))
+    ->addTag('apachesolr_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 apachesolr_nodeapi_mass_update() on nids @nids', array('@nids' => implode(',', array_keys($nodes))), WATCHDOG_NOTICE);
+    if (!apachesolr_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 apachesolr_nodeapi_mass_delete() on nids @nids', array('@nids' => implode(',', array_keys($nodes))), WATCHDOG_NOTICE);
+    if (!apachesolr_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 apachesolr_index_nodeapi_mass_update(array $nodes, $table = NULL) {
+  if (empty($nodes)) {
+    return TRUE;
+  }
+  if (empty($table)) {
+    $table = apachesolr_get_indexer_table('node');
+  }
+
+  if (apachesolr_environment_variable_get(apachesolr_default_environment(), 'apachesolr_read_only', APACHESOLR_READ_WRITE) == APACHESOLR_READ_ONLY) {
+    return TRUE;
+  }
+
+  $published_ids = array();
+  $unpublished_ids = array();
+  foreach ($nodes as $node) {
+    if ($node->status) {
+      $published_ids[$node->nid] = apachesolr_document_id($node->nid);
+    }
+    else {
+      $unpublished_ids[$node->nid] = apachesolr_document_id($node->nid);
+    }
+  }
+  try {
+    $env_id = apachesolr_default_environment();
+    $solr = apachesolr_get_solr($env_id);
+    $solr->deleteByMultipleIds($unpublished_ids);
+    apachesolr_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 apachesolr_index_nodeapi_mass_delete(array $nodes, $table = NULL) {
+  if (empty($nodes)) {
+    return TRUE;
+  }
+  if (empty($table)) {
+    $table = apachesolr_get_indexer_table('node');
+  }
+
+  if (apachesolr_environment_variable_get(apachesolr_default_environment(), 'apachesolr_read_only', APACHESOLR_READ_WRITE) == APACHESOLR_READ_ONLY) {
+    return TRUE;
+  }
+
+  $ids = array();
+  $nids = array();
+  foreach ($nodes as $node) {
+    $ids[] = apachesolr_document_id($node->nid);
+    $nids[] = $node->nid;
+  }
+  try {
+    $env_id = apachesolr_default_environment();
+    $solr = apachesolr_get_solr($env_id);
+    $solr->deleteByMultipleIds($ids);
+    apachesolr_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/sites/all/modules/custom/solrconnect/apachesolr.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,32 @@
+name = Apache Solr framework
+description = Framework for searching with Solr
+package = Search Toolkit
+core = 7.x
+configure = admin/config/search/apachesolr/settings
+
+files[] = apachesolr.install
+files[] = apachesolr.module
+files[] = apachesolr.admin.inc
+files[] = apachesolr.index.inc
+files[] = apachesolr.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_geo.inc
+files[] = tests/Dummy_Solr.php
+files[] = tests/apachesolr_base.test
+files[] = tests/solr_index_and_search.test
+files[] = tests/solr_base_query.test
+files[] = tests/solr_base_subquery.test
+files[] = tests/solr_document.test
+
+; Information added by drupal.org packaging script on 2013-03-15
+version = "7.x-1.1+34-dev"
+core = "7.x"
+project = "apachesolr"
+datestamp = "1363307665"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr.install	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,899 @@
+<?php
+
+/**
+ * @file
+ *   Install and related hooks for apachesolr_search.
+ */
+
+/**
+ * Implements hook_requirements().
+ */
+function apachesolr_requirements($phase) {
+  $requirements = array();
+  if ($phase != 'runtime') {
+    return $requirements;
+  }
+  // Ensure translations don't break at install time
+  $t = get_t();
+  $has_settings = FALSE;
+  $id = apachesolr_default_environment();
+  $environment = apachesolr_environment_load($id);
+  if (!$environment || empty($environment['url'])) {
+    $requirements['apachesolr'] = array(
+      'title' => $t('Apache Solr'),
+      '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 = apachesolr_get_solr($id);
+      $ping = @$solr->ping(variable_get('apachesolr_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['apachesolr'] = 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 apachesolr_install() {
+  module_load_include('inc', 'apachesolr', 'apachesolr_search.admin');
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  // Create one MLT block.
+  apachesolr_search_mlt_save_block(array('name' => st('More like this')));
+  db_insert('apachesolr_environment')->fields(array('env_id' => 'solr', '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 = apachesolr_default_environment();
+  apachesolr_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/apachesolr'))));
+}
+
+/**
+ * Implements hook_enable().
+ */
+function apachesolr_enable() {
+  // Completely build the index table.
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $env_id = apachesolr_default_environment();
+  apachesolr_index_mark_for_reindex($env_id);
+}
+
+/**
+ * Implements hook_schema().
+ */
+function apachesolr_schema() {
+
+  $table = drupal_get_schema_unprocessed('system', 'cache');
+  $table['description'] = 'Cache table for apachesolr to store Luke data and indexing information.';
+  $schema['cache_apachesolr'] = $table;
+
+  $schema['apachesolr_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' => 'apachesolr_ctools_environment_save',
+      // Thin wrapper for the environment delete callback.
+      'delete callback' => 'apachesolr_ctools_environment_delete',
+      // Includes the environment variables in 'conf' as well as the fields in this table.
+      'export callback' => 'apachesolr_ctools_environment_export',
+      // Use the same hook as the API name below.
+      'default hook' => 'apachesolr_environments',
+      // CTools API implementation.
+      'api' => array(
+        'owner' => 'apachesolr',
+        'api' => 'apachesolr_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['apachesolr_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' => 'apachesolr_index_entities',
+      'node' => 'apachesolr_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['apachesolr_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 apachesolr_uninstall() {
+  // Remove variables.
+  variable_del('apachesolr_default_environment');
+  variable_del('apachesolr_rows');
+  variable_del('apachesolr_site_hash');
+  variable_del('apachesolr_index_last');
+  variable_del('apachesolr_search_mlt_blocks');
+  variable_del('apachesolr_cron_limit');
+  variable_del('apachesolr_exclude_nodeapi_types');
+  variable_del('apachesolr_failure');
+  variable_del('apachesolr_index_updated');
+  variable_del('apachesolr_read_only');
+  variable_del('apachesolr_set_nodeapi_messages');
+  variable_del('apachesolr_last_optimize');
+  variable_del('apachesolr_update_from_6303');
+  // Remove blocks.
+  db_delete('block')->condition('module', 'apachesolr')->execute();
+}
+
+/**
+ * Add a table to track Solr servers.
+ */
+function apachesolr_update_7000() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $schema['apachesolr_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('apachesolr_server', $schema['apachesolr_server']);
+  // Insert into the table the current single server record.
+  $host = variable_get('apachesolr_host', 'localhost');
+  $port = variable_get('apachesolr_port', '8983');
+  $path = variable_get('apachesolr_path', '/solr');
+  db_insert('apachesolr_server')->fields(array('server_id' => 'solr', 'name' => 'Apache Solr server', 'host' => $host, 'port' => $port, 'path' => $path))->execute();
+  variable_set('apachesolr_default_server', 'solr');
+  variable_del('apachesolr_host');
+  variable_del('apachesolr_port');
+  variable_del('apachesolr_path');
+  $value = variable_get('apachesolr_service_class', NULL);
+  if (is_array($value)) {
+    list($module, $filepath, $class) = $value;
+    variable_set('apachesolr_service_class', $class);
+  }
+  variable_del('apachesolr_logging');
+}
+
+
+/**
+ * Re-jigger the schema to use fewer, shorter keys.
+ */
+function apachesolr_update_7001() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  if (db_field_exists('apachesolr_server', 'asid')) {
+    // You installed the beta1 and need to be fixed up.
+    db_drop_field('apachesolr_server', 'asid');
+    db_drop_unique_key('apachesolr_server', 'server_id');
+    db_add_primary_key('apachesolr_server', array('server_id'));
+    db_drop_unique_key('apachesolr_server', 'host_post_path');
+    db_change_field('apachesolr_server', 'port', 'port',
+      array(
+        'description' => 'Port number for the registered server',
+        'type' => 'int',
+        'not null' => TRUE,
+      )
+    );
+  }
+}
+
+/**
+ * Create the per-server variable table.
+ */
+function apachesolr_update_7002() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $schema['apachesolr_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('apachesolr_server_variable', $schema['apachesolr_server_variable']);
+  $server_id = variable_get('apachesolr_default_server', 'solr');
+  // Variables to be migrated:
+  $conf['apachesolr_enabled_facets'] = variable_get('apachesolr_enabled_facets', NULL);
+  $conf['apachesolr_search_query_fields'] = variable_get('apachesolr_search_query_fields', NULL);
+  $conf['apachesolr_search_type_boosts'] = variable_get('apachesolr_search_type_boosts', NULL);
+  $conf['apachesolr_search_comment_boost'] = variable_get('apachesolr_search_comment_boost', NULL);
+  $conf['apachesolr_search_changed_boost'] = variable_get('apachesolr_search_changed_boost', NULL);
+  $conf['apachesolr_search_sticky_boost'] = variable_get('apachesolr_search_sticky_boost', NULL);
+  $conf['apachesolr_search_promote_boost'] = variable_get('apachesolr_search_promote_boost', NULL);
+  $conf['apachesolr_search_excluded_types'] = variable_get('apachesolr_search_excluded_types', NULL);
+  foreach ($conf as $name => $value) {
+    if ($value !== NULL) {
+      db_merge('apachesolr_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 apachesolr_update_7003() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // Same as apachesolr_update_6006()
+  $exclude_comment_types = variable_get('apachesolr_exclude_comments_types', NULL);
+  if (is_array($exclude_comment_types)) {
+    $exclude = array();
+    foreach ($exclude_comment_types as $type) {
+      $exclude[$type]['comment'] = TRUE;
+    }
+    variable_set('apachesolr_exclude_nodeapi_types', $exclude);
+  }
+  variable_del('apachesolr_exclude_comments_types');
+}
+
+/**
+ * Update apachesolr_failure variable.
+ */
+function apachesolr_update_7004() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $failure = variable_get('apachesolr_failure', NULL);
+  switch ($failure) {
+    case 'show_error':
+      variable_set('apachesolr_failure', 'apachesolr:show_error');
+      break;
+    case 'show_drupal_results':
+      variable_set('apachesolr_failure', 'node');
+      break;
+    case 'show_no_results':
+      variable_set('apachesolr_failure', 'apachesolr:show_no_results');
+      break;
+  }
+}
+
+/**
+ * Re-jigger the schema to use just a url column.
+ */
+function apachesolr_update_7005() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  if (db_field_exists('apachesolr_server', 'port')) {
+    // You installed the beta3 and need to be fixed up.
+    $servers = db_query('SELECT * FROM {apachesolr_server}')->fetchAllAssoc('server_id', PDO::FETCH_ASSOC);
+    db_drop_field('apachesolr_server', 'scheme');
+    db_drop_field('apachesolr_server', 'port');
+    db_drop_field('apachesolr_server', 'path');
+    db_change_field('apachesolr_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('apachesolr_server')
+      ->fields(array('url' => $url))
+      ->condition('server_id', $id)
+      ->execute();
+    }
+  }
+}
+
+/**
+ * Remove facet-related variable deprecated by the Facet API integration.
+ */
+function apachesolr_update_7006() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  variable_del('apachesolr_facetstyle');
+  variable_del('apachesolr_facet_show_children');
+  variable_del('apachesolr_facet_query_limits');
+  variable_del('apachesolr_facet_query_limit_default');
+}
+
+/**
+ * Rename tables to make them more generic.
+ */
+function apachesolr_update_7007() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  db_drop_primary_key('apachesolr_server');
+  db_drop_primary_key('apachesolr_server_variable');
+  db_rename_table('apachesolr_server', 'apachesolr_environment');
+  db_rename_table('apachesolr_server_variable', 'apachesolr_environment_variable');
+  db_change_field('apachesolr_environment', 'server_id', 'env_id', array(
+    'description' => 'Unique identifier for the environment',
+    'type' => 'varchar',
+    'length' => 64,
+    'not null' => TRUE)
+  );
+  db_change_field('apachesolr_environment_variable', 'server_id', 'env_id', array(
+    'description' => 'Unique identifier for the environment',
+    'type' => 'varchar',
+    'length' => 64,
+    'not null' => TRUE)
+  );
+  db_add_primary_key('apachesolr_environment', array('env_id'));
+  db_add_primary_key('apachesolr_environment_variable', array('env_id', 'name'));
+  $id = variable_get('apachesolr_default_server', NULL);
+  if (isset($id)) {
+    variable_set('apachesolr_default_environment', $id);
+  }
+  variable_del('apachesolr_default_server');
+}
+
+/**
+ * Remove more facet-related variable deprecated by the Facet API integration.
+ */
+function apachesolr_update_7008() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  variable_del('apachesolr_facet_missing');
+  variable_del('apachesolr_facet_query_initial_limits');
+  variable_del('apachesolr_facet_query_sorts');
+  variable_del('apachesolr_facet_sort_active');
+  variable_del('apachesolr_operator');
+}
+
+/**
+ * Update Facet API block deltas to account for removal of numeric ID from field names.
+ */
+function apachesolr_update_7009() {
+  if (variable_get('apachesolr_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 apachesolr_update_7010() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  db_drop_field('cache_apachesolr', 'headers');
+  return 'Updated cache table schema for Drupal 7.';
+}
+
+/**
+ * Change the namespace for the indexer from apachesolr_search to apachesolr
+ */
+function apachesolr_update_7011() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $stored = variable_get('apachesolr_index_last', array());
+  if (isset($stored['apachesolr_search'])) {
+    $stored['apachesolr'] = $stored['apachesolr_search'];
+    unset($stored['apachesolr_search']);
+    variable_set('apachesolr_index_last', $stored);
+  }
+  return 'Updated the namespace variable for the index process.';
+}
+
+/**
+ * Rename some variables and update the database tables
+ */
+function apachesolr_update_7012() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // @see: drupal_load()
+  if (!function_exists('apachesolr_default_environment')) {
+    include_once dirname(__FILE__) . '/apachesolr.module';
+  }
+
+  $env_id = apachesolr_default_environment();
+
+  // Variable changed from integer to array with environment integers
+  $stored = variable_get('apachesolr_index_last', array());
+  if (isset($stored['apachesolr'])) {
+    $stored[$env_id]['node']['last_entity_id'] = $stored['apachesolr']['last_nid'];
+    $stored[$env_id]['node']['last_changed'] = $stored['apachesolr']['last_change'];
+    unset($stored['apachesolr']);
+    variable_set('apachesolr_index_last', $stored);
+  }
+  $last = variable_get('apachesolr_index_updated', NULL);
+  if (isset($last)) {
+    variable_set('apachesolr_index_updated', array($env_id => (int) $last));
+  }
+
+  // Change namespace to environment id
+  $excluded_types = apachesolr_environment_variable_get('apachesolr', 'apachesolr_search_excluded_types', array());
+  if (!empty($excluded_types)) {
+    apachesolr_environment_variable_set($env_id, 'apachesolr_search_excluded_types', $excluded_types);
+    apachesolr_environment_variable_del('apachesolr', 'apachesolr_search_excluded_types');
+  }
+
+  // Install the new schema
+  //Predefine an amount of types that get their own table
+  $types = array(
+      'other' => 'apachesolr_index_entities',
+      'node' => 'apachesolr_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['apachesolr_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('apachesolr_index_bundles', $schema['apachesolr_index_bundles']);
+
+
+  // Move the data from apachesolr_search_node to apachesolr_index_entities_node
+  $select = db_select('apachesolr_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('apachesolr_index_entities_node')
+    ->fields(array('entity_id', 'bundle', 'status', 'changed', 'entity_type'))
+    ->from($select)
+    ->execute();
+  // Drop the table apachesolr_search_node
+  db_drop_table('apachesolr_search_node');
+
+  $environments = apachesolr_load_all_environments();
+  foreach ($environments as $env_id => $environment) {
+    $excluded_types = apachesolr_environment_variable_get($env_id, 'apachesolr_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 apachesolr_index_set_bundles()
+    foreach ($options as $entity_type => $bundles) {
+      db_delete('apachesolr_index_bundles')
+        ->condition('env_id', $env_id)
+        ->condition('entity_type', $entity_type)
+        ->execute();
+
+      if ($bundles) {
+        $insert = db_insert('apachesolr_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
+    apachesolr_environment_variable_del($env_id, 'apachesolr_search_excluded_types');
+  }
+}
+
+/**
+ * Make consistent (and reduce) field lengths which cause excess pkey length.
+ */
+function apachesolr_update_7013() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  db_drop_primary_key('apachesolr_index_entities');
+  db_change_field('apachesolr_index_entities', 'entity_type', 'entity_type', array(
+    'description' => 'The type of entity.',
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE)
+  );
+  db_add_primary_key('apachesolr_index_entities', array('entity_id', 'entity_type'));
+  db_change_field('apachesolr_index_entities_node', 'entity_type', 'entity_type', array(
+    'description' => 'The type of entity.',
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE)
+  );
+  db_drop_primary_key('apachesolr_index_bundles');
+  db_change_field('apachesolr_index_bundles', 'env_id', 'env_id', array(
+    'description' => 'Unique identifier for the environment',
+    'type' => 'varchar',
+    'length' => 64,
+    'not null' => TRUE)
+  );
+  db_change_field('apachesolr_index_bundles', 'entity_type', 'entity_type', array(
+    'description' => 'The type of entity.',
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE)
+  );
+  db_add_primary_key('apachesolr_index_bundles', array('env_id', 'entity_type', 'bundle'));
+}
+
+/**
+ * Remove status from the key.
+ */
+function apachesolr_update_7014() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  $types = array(
+    'other' => 'apachesolr_index_entities',
+    'node' => 'apachesolr_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 apachesolr_update_7015() {
+  if (variable_get('apachesolr_update_from_6303', FALSE)) {
+    return NULL;
+  }
+
+  // Brand new installations since update_7013 have the wrong primary key.
+  db_drop_primary_key('apachesolr_index_entities');
+  db_add_primary_key('apachesolr_index_entities', array('entity_id', 'entity_type'));
+}
+
+/**
+ * Clean up apachesolr_update_from_6303.
+ *
+ * This variable had been used to bypass 7.x-1.x updates which are redundant
+ * with 6.x-3.x.
+ */
+function apachesolr_update_7016() {
+  variable_del('apachesolr_update_from_6303');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr.interface.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrconnect/apachesolr.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,2793 @@
+<?php
+
+/**
+ * @file
+ *   Integration with the Apache Solr search application.
+ */
+
+define('APACHESOLR_READ_WRITE', 0);
+define('APACHESOLR_READ_ONLY', 1);
+define('APACHESOLR_API_VERSION', '3.0');
+
+/**
+ * Implements hook_init().
+ */
+function apachesolr_init() {
+  if (arg(0) == 'admin') {
+    // Add the CSS for this module
+    drupal_add_css(drupal_get_path('module', 'apachesolr') . '/apachesolr.css');
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function apachesolr_menu() {
+  $items = array();
+  $items['admin/config/search/apachesolr'] = array(
+    'title'              => 'Apache Solr search',
+    'description'        => 'Administer Apache Solr.',
+    'page callback'      => 'apachesolr_status_page',
+    'access arguments'   => array('administer search'),
+    'weight'             => -8,
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/index'] = array(
+    'title'              => 'Default index',
+    'description'        => 'Administer Apache Solr.',
+    'page callback'      => 'apachesolr_status_page',
+    'access arguments'   => array('administer search'),
+    'weight'             => -8,
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_DEFAULT_LOCAL_TASK,
+  );
+
+  $items['admin/config/search/apachesolr/settings'] = array(
+    'title'              => 'Settings',
+    'weight'             => 10,
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_settings'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+
+  $settings_path = 'admin/config/search/solrsearch/settings/';
+  $items[$settings_path . '%apachesolr_environment/index'] = array(
+    'title'              => 'Index',
+    'page callback'      => 'apachesolr_status_page',
+    'page arguments'     => array(5),
+    'access arguments'   => array('administer search'),
+    'weight'             => 0,
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items[$settings_path . '%apachesolr_environment/index/remaining'] = array(
+    'title'              => 'Remaining',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_index_action_form_remaining_confirm', 5),
+    'file'               => 'apachesolr.admin.inc',
+    'access arguments'   => array('administer search'),
+    'type'               => MENU_CALLBACK,
+  );
+  $items[$settings_path . '%apachesolr_environment/index/delete'] = array(
+    'title'              => 'Reindex',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_index_action_form_delete_confirm', 5),
+    'file'               => 'apachesolr.admin.inc',
+    'access arguments'   => array('administer search'),
+    'type'               => MENU_CALLBACK,
+  );
+  $items[$settings_path . '%apachesolr_environment/index/reset'] = array(
+    'title'              => 'Reindex',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_index_action_form_reset_confirm', 5),
+    'file'               => 'apachesolr.admin.inc',
+    'access arguments'   => array('administer search'),
+    'type'               => MENU_CALLBACK,
+  );
+  $items[$settings_path . '%apachesolr_environment/index/reset/confirm'] = array(
+    'title'              => 'Confirm the re-indexing of all content',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_clear_index_confirm', 5),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items[$settings_path . '%apachesolr_environment/index/delete/confirm'] = array(
+    'title'              => 'Confirm index deletion',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_delete_index_confirm', 5),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items[$settings_path . '%apachesolr_environment/edit'] = array(
+    'title'              => 'Edit',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_environment_edit_form', 5),
+    'description'        => 'Edit Apache Solr search environment.',
+    'access arguments'   => array('administer search'),
+    'weight'             => 10,
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items[$settings_path . '%apachesolr_environment/clone'] = array(
+    'title'              => 'Apache Solr search environment clone',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_environment_clone_form', 5),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items[$settings_path . '%apachesolr_environment/delete'] = array(
+    'title'              => 'Apache Solr search environment delete',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_environment_delete_form', 5),
+    'access callback'    => 'apachesolr_environment_delete_page_access',
+    'access arguments'   => array('administer search', 5),
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items[$settings_path . 'add'] = array(
+    'title'              => 'Add search environment',
+    'description'        => 'Add Apache Solr environment.',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_environment_edit_form'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_LOCAL_ACTION,
+  );
+  $items['admin/config/search/apachesolr/index/confirm/clear'] = array(
+    'title'              => 'Confirm the re-indexing of all content',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_clear_index_confirm'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  $items['admin/config/search/apachesolr/index/confirm/delete'] = array(
+    'title'              => 'Confirm index deletion',
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_delete_index_confirm'),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+
+  $reports_path = 'admin/reports/apachesolr';
+  $items[$reports_path] = array(
+    'title'              => 'Apache Solr search index',
+    'description'        => 'Information about the contents of the index on the server',
+    'page callback'      => 'apachesolr_index_report',
+    'access arguments'   => array('access site reports'),
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items[$reports_path . '/%apachesolr_environment'] = array(
+    'title'              => 'Apache Solr search index',
+    'description'        => 'Information about the contents of the index on the server',
+    'page callback'      => 'apachesolr_index_report',
+    'page arguments'     => array(3),
+    'access arguments'   => array('access site reports'),
+    'file'               => 'apachesolr.admin.inc',
+  );
+  $items[$reports_path . '/%apachesolr_environment/index'] = array(
+    'title'              => 'Search index',
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items[$reports_path . '/%apachesolr_environment/conf'] = array(
+    'title'              => 'Configuration files',
+    'page callback'      => 'apachesolr_config_files_overview',
+    'access arguments'   => array('access site reports'),
+    'file'               => 'apachesolr.admin.inc',
+    'weight'             => 5,
+    'type'               => MENU_LOCAL_TASK,
+  );
+  $items[$reports_path . '/%apachesolr_environment/conf/%'] = array(
+    'title'              => 'Configuration file',
+    'page callback'      => 'apachesolr_config_file',
+    'page arguments'     => array(5, 3),
+    'access arguments'   => array('access site reports'),
+    'file'               => 'apachesolr.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+  if (module_exists('devel')) {
+    $items['node/%node/devel/apachesolr'] = array(
+      'title'              => 'Apache Solr',
+      'page callback'      => 'apachesolr_devel',
+      'page arguments'     => array(1),
+      'access arguments'   => array('access devel information'),
+      'file'               => 'apachesolr.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 . '%apachesolr_environment/facets'] = array(
+          'title'            => 'Facets',
+          'page callback'    => 'apachesolr_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 . '%apachesolr_environment/facets/' . $realm_name] = array(
+          'title'            => $realm['label'],
+          'page callback'    => 'apachesolr_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 apachesolr_enabled_facets_page($realm_name, $environment = NULL) {
+  $page = array();
+
+  if (isset($environment['env_id'])) {
+    $env_id = $environment['env_id'];
+  }
+  else {
+    $env_id = apachesolr_default_environment();
+  }
+  $searcher = 'apachesolr@' . $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['apachesolr_environment'] = array(
+    '#theme' => 'apachesolr_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 apachesolr_facetapi_searcher_info() {
+  $info = array();
+  // TODO: is it needed to return all of them here?
+  foreach (apachesolr_load_all_environments() as $id => $environment) {
+    $info['apachesolr@' . $id] = array(
+      'label' => t('Apache Solr environment: @environment', array('@environment' => $environment['name'])),
+      'adapter' => 'apachesolr',
+      'instance' => $id,
+      'path' => '',
+      'supports facet mincount' => TRUE,
+      'supports facet missing' => TRUE,
+      'include default facets' => FALSE,
+    );
+  }
+  return $info;
+}
+
+/**
+ * Implements hook_facetapi_adapters().
+ */
+function apachesolr_facetapi_adapters() {
+  return array(
+    'apachesolr' => array(
+      'handler' => array(
+        'class' => 'ApacheSolrFacetapiAdapter',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_facetapi_query_types().
+ */
+function apachesolr_facetapi_query_types() {
+  return array(
+    'apachesolr_term' => array(
+      'handler' => array(
+        'class' => 'ApacheSolrFacetapiTerm',
+        'adapter' => 'apachesolr',
+      ),
+    ),
+    'apachesolr_date' => array(
+      'handler' => array(
+        'class' => 'ApacheSolrFacetapiDate',
+        'adapter' => 'apachesolr',
+      ),
+    ),
+    'apachesolr_numeric_range' => array(
+      'handler' => array(
+        'class' => 'ApacheSolrFacetapiNumericRange',
+        'adapter' => 'apachesolr',
+      ),
+    ),
+    'apachesolr_geo' => array(
+      'handler' => array(
+        'class' => 'ApacheSolrFacetapiGeo',
+        'adapter' => 'apachesolr',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_facetapi_facet_info().
+ * Currently it only supports the node entity type
+ */
+function apachesolr_facetapi_facet_info($searcher_info) {
+  $facets = array();
+  if ('apachesolr' == $searcher_info['adapter']) {
+    $environment = apachesolr_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 = apachesolr_default_node_facet_info();
+    }
+  }
+
+  return $facets;
+}
+
+/**
+ * Returns an array of facets for node fields and attributes.
+ *
+ * @return
+ *   An array of node facets.
+ */
+function apachesolr_default_node_facet_info() {
+  return array_merge(apachesolr_common_node_facets(), apachesolr_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 apachesolr_entity_field_facets($entity_type) {
+  $facets = array();
+
+  foreach (apachesolr_entity_fields($entity_type) as $field_nm => $entity_fields) {
+    foreach ($entity_fields as $field_info) {
+      if (!empty($field_info['facets'])) {
+        $field = apachesolr_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 apachesolr_common_node_facets() {
+
+  $facets['bundle'] = array(
+    'label' => t('Content type'),
+    'description' => t('Filter by content type.'),
+    'field api bundles' => array('node'),
+    'map callback' => 'facetapi_map_bundle',
+    'values callback' => 'facetapi_callback_type_values',
+    'facet mincount allowed' => TRUE,
+    'dependency plugins' => array('role'),
+  );
+
+  $facets['author'] = array(
+    'label' => t('Author'),
+    'description' => t('Filter by author.'),
+    'field' => 'is_uid',
+    'map callback' => 'facetapi_map_author',
+    'values callback' => 'facetapi_callback_user_values',
+    'facet mincount allowed' => TRUE,
+    'dependency plugins' => array('bundle', 'role'),
+  );
+
+  $facets['language'] = array(
+    'label' => t('Language'),
+    'description' => t('Filter by language.'),
+    'field' => 'ss_language',
+    'map callback' => 'facetapi_map_language',
+    'values callback' => 'facetapi_callback_language_values',
+    'facet mincount allowed' => TRUE,
+    'dependency plugins' => array('bundle', 'role'),
+  );
+
+  $facets['created'] = array(
+    'label' => t('Post date'),
+    'description' => t('Filter by the date the node was posted.'),
+    'field' => 'ds_created',
+    'query types' => array('date'),
+    'allowed operators' => array(FACETAPI_OPERATOR_AND => TRUE),
+    'map callback' => 'facetapi_map_date',
+    'min callback' => 'facetapi_get_min_date',
+    'max callback' => 'facetapi_get_max_date',
+    'dependency plugins' => array('bundle', 'role'),
+    'default sorts' => array(
+      array('active', SORT_DESC),
+      array('indexed', SORT_ASC),
+    ),
+  );
+
+  $facets['changed'] = array(
+    'label' => t('Updated date'),
+    'description' => t('Filter by the date the node was last modified.'),
+    'field' => 'ds_changed',
+    'query types' => array('date'),
+    'allowed operators' => array(FACETAPI_OPERATOR_AND => TRUE),
+    'map callback' => 'facetapi_map_date',
+    'min callback' => 'facetapi_get_min_date',
+    'max callback' => 'facetapi_get_max_date',
+    'dependency plugins' => array('bundle', 'role'),
+    'default sorts' => array(
+      array('active', SORT_DESC),
+      array('indexed', SORT_ASC),
+    ),
+  );
+
+  return $facets;
+}
+
+/**
+ * Determines Apache Solr's behavior when searching causes an exception (e.g. Solr isn't available.)
+ * Depending on the admin settings, possibly redirect to Drupal's core search.
+ *
+ * @param $search_name
+ *   The name of the search implementation.
+ *
+ * @param $querystring
+ *   The search query that was issued at the time of failure.
+ */
+function apachesolr_failure($search_name, $querystring) {
+  $fail_rule = variable_get('apachesolr_failure', 'apachesolr:show_error');
+
+  switch ($fail_rule) {
+    case 'apachesolr:show_error':
+      drupal_set_message(t('Search is temporarily unavailable. If the problem persists, please contact the site administrator.'), 'error');
+      break;
+    case 'apachesolr: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 apachesolr_site_hash() {
+  if (!($hash = variable_get('apachesolr_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('apachesolr_site_hash', $hash);
+  }
+  return $hash;
+}
+
+/**
+ * Generate a unique ID for an entity being indexed.
+ *
+ * @param $id
+ *   An id number (or string) unique to this site, such as a node ID.
+ * @param $entity
+ *   A string like 'node', 'file', 'user', or some other Drupal object type.
+ *
+ * @return
+ *   A string combining the parameters with the site hash.
+ */
+function apachesolr_document_id($id, $entity_type = 'node') {
+  return apachesolr_site_hash() . "/{$entity_type}/" . $id;
+}
+
+/**
+ * Mark one entity as needing re-indexing.
+ */
+function apachesolr_mark_entity($entity_type, $entity_id) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $table = apachesolr_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 apachesolr_user_update(&$edit, $account, $category) {
+  if (isset($account->name) && isset($account->original) && isset($account->original->name) && $account->name != $account->original->name) {
+    $table = apachesolr_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 apachesolr_taxonomy_term_update($term) {
+  $table = apachesolr_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();
+  }
+}
+
+/**
+ * Implement hook_comment_*().
+ *
+ * Mark nodes as needing re-indexing if comments are added or changed.
+ * Like search_comment().
+ */
+
+/**
+ * Implements hook_comment_insert().
+ */
+function apachesolr_comment_insert($comment) {
+  apachesolr_mark_entity('node', $comment->nid);
+}
+
+/**
+ * Implements hook_comment_update().
+ */
+function apachesolr_comment_update($comment) {
+  apachesolr_mark_entity('node', $comment->nid);
+}
+
+/**
+ * Implements hook_comment_delete().
+ */
+function apachesolr_comment_delete($comment) {
+  apachesolr_mark_entity('node', $comment->nid);
+}
+
+/**
+ * Implements hook_comment_publish().
+ */
+function apachesolr_comment_publish($comment) {
+  apachesolr_mark_entity('node', $comment->nid);
+}
+
+/**
+ * Implements hook_comment_unpublish().
+ */
+function apachesolr_comment_unpublish($comment) {
+  apachesolr_mark_entity('node', $comment->nid);
+}
+
+/**
+ * Implements hook_node_type_delete().
+ */
+function apachesolr_node_type_delete($info) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $env_id = apachesolr_default_environment();
+  $existing_bundles = apachesolr_get_index_bundles($env_id, 'node');
+  $new_bundles = $existing_bundles;
+  $index = array_search($info->type, $existing_bundles);
+  if ($index !== FALSE) {
+    unset($new_bundles[$index]);
+    $new_bundles = array_values($new_bundles);
+    apachesolr_index_set_bundles($env_id, 'node', $new_bundles);
+  }
+  apachesolr_index_delete_bundles($env_id, 'node', array($info->type));
+  $callback = apachesolr_entity_get_callback('node', 'bundles changed callback');
+  if (!empty($callback)) {
+    call_user_func($callback, $env_id, $existing_bundles, $new_bundles);
+  }
+  apachesolr_environments_clear_cache();
+}
+
+/**
+ * Implements hook_node_type_update().
+ *
+ * @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 Support backwards compatibility
+ */
+function apachesolr_node_type_update($info) {
+  if (!empty($info->old_type) && $info->old_type != $info->type) {
+    // We cannot be sure we are going before or after node module.
+    $table = apachesolr_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.type = :type OR n.type = :old_type)";
+        $result = db_query($query, array(':changed' => REQUEST_TIME,
+          ':type' => $info->type,
+          ':old_type' => $info->old_type,
+        ));
+        break;
+      default :
+        $nids = db_select('node')
+          ->fields('node', array('nid'))
+          ->where("type = :new OR type = :old", array(':new' => $info->type, ':old' => $info->old_type));
+        $update = db_update($table)
+          ->condition('entity_id', $nids, 'IN')
+          ->fields(array('changed' => REQUEST_TIME))
+          ->execute();
+    }
+    db_update('apachesolr_index_bundles')
+      ->condition('bundle', $info->old_type)
+      ->condition('entity_type', 'node')
+      ->fields(array('bundle' => $info->type))
+      ->execute();
+    apachesolr_environments_clear_cache();
+  }
+}
+
+/**
+ * Implements hook_node_type_insert().
+ *
+ * Insert our new type into all the environments as indexable bundle type
+ * @param array $info
+ */
+function apachesolr_node_type_insert($info) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  // Get all our environments
+  $envs = apachesolr_load_all_environments();
+  $bundles = array();
+  foreach($envs as $env) {
+    if (isset($env['index_bundles']['node'])) {
+      $bundles = $env['index_bundles']['node'];
+    }
+    // Is the bundle already marked?
+    if (!in_array($info->type, $bundles)) {
+      $bundles[] = $info->type;
+      // Set the new bundle as indexable for all environments
+      apachesolr_index_set_bundles($env['env_id'], 'node', $bundles);
+    }
+  }
+}
+
+/**
+ * Convert date from timestamp into ISO 8601 format.
+ * http://lucene.apache.org/solr/api/org/apache/solr/schema/DateField.html
+ */
+function apachesolr_date_iso($date_timestamp) {
+  return gmdate('Y-m-d\TH:i:s\Z', $date_timestamp);
+}
+
+/**
+ * Function 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 apachesolr_flatten_documents_array($documents, &$tmp) {
+  foreach ($documents AS $index => $item) {
+    if (is_array($item)) {
+      apachesolr_flatten_documents_array($item, $tmp);
+    }
+    elseif (is_object($item)) {
+      $tmp[] = $item;
+    }
+  }
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function apachesolr_flush_caches() {
+  return array('cache_apachesolr');
+}
+
+/**
+ * A wrapper for cache_clear_all to be used as a submit handler on forms that
+ * require clearing Luke cache etc.
+ */
+function apachesolr_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 = apachesolr_get_solr($env_id);
+    $solr->clearCache();
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', 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 apachesolr_set_stats_message($text, $type = 'status', $repeat = FALSE) {
+  try {
+    $solr = apachesolr_get_solr();
+    $stats_summary = $solr->getStatsSummary();
+    drupal_set_message(check_plain(t($text, $stats_summary)), $type, FALSE);
+  }
+  catch (Exception $e) {
+    watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+  }
+}
+
+/**
+ * Returns last changed and last ID for an environment and entity type.
+ */
+function apachesolr_get_last_index_position($env_id, $entity_type) {
+  $stored = variable_get('apachesolr_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 apachesolr_set_last_index_position($env_id, $entity_type, $last_changed, $last_entity_id) {
+  $stored = variable_get('apachesolr_index_last', array());
+  $stored[$env_id][$entity_type] = array('last_changed' => $last_changed, 'last_entity_id' => $last_entity_id);
+  variable_set('apachesolr_index_last', $stored);
+}
+
+/**
+ * Clear a specific environment, or clear all.
+ */
+function apachesolr_clear_last_index_position($env_id = NULL, $entity_type = NULL) {
+  $stored = variable_get('apachesolr_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('apachesolr_index_last', $stored);
+}
+
+/**
+ * Set the timestamp of the last index update
+ * @param $timestamp
+ *   A timestamp or zero. If zero, the variable is deleted.
+ */
+function apachesolr_set_last_index_updated($env_id, $timestamp = 0) {
+  $updated = variable_get('apachesolr_index_updated', array());
+  if ($timestamp > 0) {
+    $updated[$env_id] = $timestamp;
+  }
+  else {
+    unset($updated[$env_id]);
+  }
+  variable_set('apachesolr_index_updated', $updated);
+}
+
+/**
+ * Get the timestamp of the last index update.
+ * @return integer (timestamp)
+ */
+function apachesolr_get_last_index_updated($env_id) {
+  $updated = variable_get('apachesolr_index_updated', array());
+  return isset($updated[$env_id]) ? $updated[$env_id] : 0;
+}
+
+/**
+ * Implements hook_cron().
+ * Runs the indexing process on all writable environments or just a given environment.
+ */
+function apachesolr_cron($env_id = NULL) {
+  $environments = array();
+  if (empty($env_id)) {
+    $environments = array_keys(apachesolr_load_all_environments());
+  }
+  else {
+    $environments[] = $env_id;
+  }
+
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+
+  // Optimize the index (by default once a day).
+  $optimize_interval = variable_get('apachesolr_optimize_interval', 60 * 60 * 24);
+  $last = variable_get('apachesolr_last_optimize', 0);
+  $time = REQUEST_TIME;
+
+  foreach($environments as $env_id) {
+    // Indexes in read-only mode do not change the index, so will not update, delete, or optimize during cron.
+    if (apachesolr_environment_variable_get($env_id, 'apachesolr_read_only', APACHESOLR_READ_WRITE) == APACHESOLR_READ_ONLY) {
+      continue;
+    }
+
+    // For every entity type that requires extra validation
+    foreach (entity_get_info() as $type => $info) {
+      $bundles = apachesolr_get_index_bundles($env_id, $type);
+
+      // If we're not checking any bundles of this entity type, just skip them all.
+      if (empty($bundles)) {
+        continue;
+      }
+
+      if (isset($info['apachesolr']['cron_check'])) {
+        $callback = $info['apachesolr']['cron_check'];
+        call_user_func($callback);
+      }
+    }
+
+    try {
+      $solr = apachesolr_get_solr($env_id);
+      if ($optimize_interval && ($time - $last > $optimize_interval)) {
+        $solr->optimize(FALSE, FALSE);
+        variable_set('apachesolr_last_optimize', $time);
+        apachesolr_set_last_index_updated($env_id, $time);
+      }
+      // Only clear the cache if the index changed.
+      // TODO: clear on some schedule if running multi-site.
+      $updated = apachesolr_get_last_index_updated($env_id);
+      if ($updated > 0) {
+        $solr->clearCache();
+        // Re-populate the luke cache.
+        $solr->getLuke();
+        // TODO: an admin interface for setting this.  Assume for now 5 minutes.
+        if ($time - $updated >= variable_get('apachesolr_cache_delay', 300)) {
+          // Clear the updated flag.
+          apachesolr_set_last_index_updated($env_id);
+        }
+      }
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', nl2br(check_plain($e->getMessage())) . ' in apachesolr_cron', NULL, WATCHDOG_ERROR);
+    }
+
+    // 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);
+    apachesolr_index_entities($env_id, $limit);
+  }
+}
+
+/**
+ * Implements hook_form_[form_id]_alter().
+ *
+ * Make sure to flush cache when content types are changed.
+ */
+function apachesolr_form_node_type_form_alter(&$form, $form_state) {
+  $form['#submit'][] = 'apachesolr_clear_cache';
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ *
+ * Make sure to flush cache when fields are added.
+ */
+function apachesolr_form_field_ui_field_overview_form_alter(&$form, $form_state) {
+  $form['#submit'][] = 'apachesolr_clear_cache';
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ *
+ * Make sure to flush cache when fields are updated.
+ */
+function apachesolr_form_field_ui_field_edit_form_alter(&$form, $form_state) {
+  $form['#submit'][] = 'apachesolr_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 apachesolr_set_facetapi_breadcrumb(FacetapiAdapter $adapter, array $realm) {
+  if ('apachesolr' == $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('Apache Solr search environment edit'), 'admin/config/search/apachesolr/settings/' . $env_id);
+    $breadcrumb[] = l($realm['label'], 'admin/config/search/apachesolr/settings/' . $env_id . '/facets/' . $realm['name']);
+    drupal_set_breadcrumb($breadcrumb);
+  }
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ */
+function apachesolr_form_facetapi_facet_settings_form_alter(&$form, $form_state) {
+  apachesolr_set_facetapi_breadcrumb($form['#facetapi']['adapter'], $form['#facetapi']['realm']);
+}
+
+/**
+ * Implements hook_form_[form_id]_alter(). (D7)
+ */
+function apachesolr_form_facetapi_facet_dependencies_form_alter(&$form, $form_state) {
+  apachesolr_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 apachesolr_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 apachesolr_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 apachesolr_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('apachesolr_default_environment', 'solr');
+  }
+  return $default_env_id;
+}
+
+/**
+ * Set the default environment and let other modules know about the change.
+ */
+function apachesolr_set_default_environment($env_id) {
+  $old_env_id = variable_get('apachesolr_default_environment', 'solr');
+  variable_set('apachesolr_default_environment', $env_id);
+  module_invoke_all('apachesolr_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 = apachesolr_get_solr();
+ *   }
+ *   catch (Exception $e) {
+ *     watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
+ *   }
+ *
+ *
+ * @param string $env_id
+ *
+ * @return DrupalApacheSolrServiceInterface $solr
+ *
+ * @throws Exception
+ */
+function apachesolr_get_solr($env_id = NULL) {
+  $solr_cache = &drupal_static(__FUNCTION__);
+  $environments = apachesolr_load_all_environments();
+
+  if (!interface_exists('DrupalApacheSolrServiceInterface')) {
+    require_once(dirname(__FILE__) . '/apachesolr.interface.inc');
+  }
+
+  if (empty($env_id)) {
+    $env_id = apachesolr_default_environment();
+  }
+  elseif (empty($environments[$env_id])) {
+    throw new Exception(t('Invalid Apache Solr 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('apachesolr_service_class', 'DrupalApacheSolrService');
+      }
+      // 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 Apache Solr environment.');
+  }
+}
+
+/**
+ * Function that loads all the environments
+ *
+ * @return $environments
+ *   The environments in the database
+ */
+function apachesolr_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('apachesolr:environments', 'cache_apachesolr');
+  if (isset($cache->data)) {
+    $environments = $cache->data;
+  }
+  elseif (!db_table_exists('apachesolr_index_bundles') || !db_table_exists('apachesolr_environment')) {
+    // Sometimes this function is called when the 'apachesolr_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('apachesolr_environment', 'all');
+      // Convert environments to array.
+      foreach ($environments as &$environment) {
+        $environment = (array) $environment;
+      }
+    }
+    else {
+      $environments = db_query('SELECT * FROM {apachesolr_environment}')->fetchAllAssoc('env_id', PDO::FETCH_ASSOC);
+    }
+
+    // Load conf and index bundles. We don't use 'subrecords callback' property
+    // of ctools export API.
+    apachesolr_environment_load_subrecords($environments);
+
+    cache_set('apachesolr:environments', $environments, 'cache_apachesolr');
+  }
+
+  // Allow overrides of environments from settings.php
+  $conf_environments = variable_get('apachesolr_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 apachesolr_environment_load($env_id) {
+  $environments = apachesolr_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 apachesolr_environment_delete_page_access($permission, $environment) {
+  $is_default = $environment['env_id'] == apachesolr_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 apachesolr_environment_delete($env_id) {
+  $environment = apachesolr_environment_load($env_id);
+  if ($environment) {
+    db_delete('apachesolr_environment')
+      ->condition('env_id', $env_id)
+      ->execute();
+    db_delete('apachesolr_environment_variable')
+      ->condition('env_id', $env_id)
+      ->execute();
+    db_delete('apachesolr_index_bundles')
+      ->condition('env_id', $env_id)
+      ->execute();
+
+    module_invoke_all('apachesolr_environment_delete', $environment);
+    apachesolr_environments_clear_cache();
+  }
+}
+
+/**
+ * Function that clones an environment
+ *
+ * @param $env_id
+ *   The environment ID it needs to clone.
+ *
+ */
+function apachesolr_environment_clone($env_id) {
+  $environment = apachesolr_environment_load($env_id);
+  $environments = apachesolr_load_all_environments();
+  $environment['env_id'] = apachesolr_create_unique_id($environments, $env_id);
+  $environment['name'] = $environment['name'] . ' [cloned]';
+  apachesolr_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 apachesolr_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 apachesolr_environment_save($environment) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.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('apachesolr_environment')
+    ->key(array('env_id' => $environment['env_id']))
+    ->fields($environment)
+    ->execute();
+  // Update the environment variables (if any).
+  foreach ($conf as $name => $value) {
+    db_merge('apachesolr_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) {
+    apachesolr_index_set_bundles($environment['env_id'], $entity_type, $bundles);
+  }
+  apachesolr_environments_clear_cache();
+}
+
+/**
+ * Clear all caches for environments.
+ */
+function apachesolr_environments_clear_cache() {
+  cache_clear_all('apachesolr:environments', 'cache_apachesolr');
+  drupal_static_reset('apachesolr_load_all_environments');
+  drupal_static_reset('apachesolr_get_solr');
+  if (module_exists('ctools')) {
+    ctools_include('export');
+    ctools_export_load_object_reset('apachesolr_environment');
+  }
+}
+
+/**
+ * Get a named variable, or return the default.
+ *
+ * @see variable_get()
+ */
+function apachesolr_environment_variable_get($env_id, $name, $default = NULL) {
+  $environment = apachesolr_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 apachesolr_environment_variable_set($env_id, $name, $value) {
+  db_merge('apachesolr_environment_variable')
+    ->key(array('env_id' => $env_id, 'name' => $name))
+    ->fields(array('value' => serialize($value)))
+    ->execute();
+  apachesolr_environments_clear_cache();
+}
+
+/**
+ * Get a named variable, or return the default.
+ *
+ * @see variable_del()
+ */
+function apachesolr_environment_variable_del($env_id, $name) {
+  db_delete('apachesolr_environment_variable')
+    ->condition('env_id', $env_id)
+    ->condition('name', $name)
+    ->execute();
+  apachesolr_environments_clear_cache();
+}
+
+/**
+ * Checks if a specific Apache Solr server is available.
+ *
+ * @return boolean TRUE if the server can be pinged, FALSE otherwise.
+ */
+function apachesolr_server_status($url, $class = NULL) {
+  $status = &drupal_static(__FUNCTION__, array());
+
+  if (!interface_exists('DrupalApacheSolrServiceInterface')) {
+    require_once(dirname(__FILE__) . '/apachesolr.interface.inc');
+  }
+
+  if (empty($class)) {
+    $class = variable_get('apachesolr_service_class', 'DrupalApacheSolrService');
+  }
+
+  $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('apachesolr_ping_timeout', 4));
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', 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_apachesolr_query_prepare() and hook_apachesolr_query_alter().
+ *
+ * @param $current_query
+ *   A query object from apachesolr_drupal_query().  It will be modified by
+ *   hook_apachesolr_query_prepare() and then cached in apachesolr_current_query().
+ * @param $page
+ *   For paging into results, using $current_query->params['rows'] results per page.
+ *
+ * @return array($final_query, $response)
+ *
+ * @throws Exception
+ */
+function apachesolr_do_query(DrupalSolrQueryInterface $current_query) {
+  if (!is_object($current_query)) {
+    throw new Exception(t('NULL query object in function apachesolr_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('apachesolr_query_prepare') as $module) {
+    $function_name = $module . '_apachesolr_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 = apachesolr_environment_variable_get($env_id, 'apachesolr_query_type');
+  if (!empty($defType)) {
+    $current_query->addParam('defType', $defType);
+  }
+
+  $query = apachesolr_current_query($env_id, $current_query);
+
+  // Verify if this query was already executed in the same page load
+  if ($response = apachesolr_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('apachesolr_query', $query);
+
+  if ($query->abort_search) {
+    // A module implementing HOOK_apachesolr_query_alter() aborted the search.
+    return array(NULL, array());
+  }
+
+
+  $keys = $query->getParam('q');
+
+  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.
+  apachesolr_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 apachesolr_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 "apachesolr".
+ * @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 apachesolr_drupal_query($name, array $params = array(), $solrsort = '', $base_path = '', DrupalApacheSolrServiceInterface $solr = NULL, $context = array()) {
+  if (!interface_exists('DrupalSolrQueryInterface')) {
+    require_once(dirname(__FILE__) . '/apachesolr.interface.inc');
+  }
+  $class_info = variable_get('apachesolr_query_class', array(
+    'file' => 'Solr_Base_Query',
+    'module' => 'apachesolr',
+    '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 = apachesolr_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 apachesolr_drupal_subquery($operator = 'OR') {
+  if (!interface_exists('DrupalSolrQueryInterface')) {
+    require_once(dirname(__FILE__) . '/apachesolr.interface.inc');
+  }
+
+  $class_info = variable_get('apachesolr_subquery_class', array(
+    'file' => 'Solr_Base_Query',
+    'module' => 'apachesolr',
+    '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 apachesolr_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 apachesolr_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 apachesolr_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 (apachesolr_entity_fields('node') as $field_nm => $nodefields) {
+      foreach ($nodefields as $field_info) {
+        $map[apachesolr_index_key($field_info)] = t('Field of type @type: %label', array('@type' => $field_info['field']['type'], '%label' => $field_info['display_name']));
+      }
+    }
+    drupal_alter('apachesolr_field_name_map', $map);
+  }
+  return isset($map[$field_name]) ? $map[$field_name] : $field_name;
+}
+
+/**
+ * Validation function for the Facet API facet settings form.
+ *
+ * Apache Solr does not support the combination of OR facets
+ * and facet missing, so catch that at validation.
+ */
+function apachesolr_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('Apache Solr does not support <em>facet missing</em> in combination with the OR operator.'));
+  }
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function apachesolr_entity_info_alter(&$entity_info) {
+  // Load all environments
+  $environments = apachesolr_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'][] = 'apachesolr_index_node_status_callback';
+  $default_entity_info['node']['document callback'][] = 'apachesolr_index_node_solr_document';
+  $default_entity_info['node']['reindex callback'] = 'apachesolr_index_node_solr_reindex';
+  $default_entity_info['node']['bundles changed callback'] = 'apachesolr_index_node_bundles_changed';
+  $default_entity_info['node']['index_table'] = 'apachesolr_index_entities_node';
+  $default_entity_info['node']['cron_check'] = 'apachesolr_index_node_check_table';
+  // apachesolr_search implements a new callback for every entity type
+  // $default_entity_info['node']['apachesolr']['result callback'] = 'apachesolr_search_node_result';
+  //Allow implementations of HOOK_apachesolr_entity_info to modify these default indexers
+  drupal_alter('apachesolr_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]['apachesolr'])) {
+      $entity_info[$type]['apachesolr'] = array();
+    }
+    if (isset($default_entity_info[$type])) {
+      $entity_info[$type]['apachesolr'] += $default_entity_info[$type];
+    }
+    $default = array(
+      'indexable' => FALSE,
+      'status callback' => '',
+      'document callback' => '',
+      'reindex callback' => '',
+      'bundles changed callback' => '',
+    );
+    $entity_info[$type]['apachesolr'] += $default;
+  }
+
+  // For any supported entity type and bundle, flag it for indexing.
+  foreach ($entity_info as $entity_type => $info) {
+    if ($info['apachesolr']['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']['apachesolr_read_only'])) {
+          // Get the supported bundles
+          $supported = apachesolr_get_index_bundles($env['env_id'], $entity_type);
+          // For each bundle in drupal, compare to the supported apachesolr
+          // bundles and enable where possible
+          foreach (array_keys($info['bundles']) as $bundle) {
+            if (in_array($bundle, $supported)) {
+              $entity_info[$entity_type]['bundles'][$bundle]['apachesolr']['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 apachesolr_get_index_bundles($env_id, $entity_type) {
+  $environment = apachesolr_environment_load($env_id);
+  return !empty($environment['index_bundles'][$entity_type]) ? $environment['index_bundles'][$entity_type] : array();
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function apachesolr_entity_insert($entity, $type) {
+  // For our purposes there's really no difference between insert and update.
+  return apachesolr_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 apachesolr_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]['apachesolr']['index']) && $info['bundles'][$bundle]['apachesolr']['index']) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function apachesolr_entity_update($entity, $type) {
+  // Include the index file for the status callback
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  if (apachesolr_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 = apachesolr_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) {
+      apachesolr_entity_delete($entity, $type);
+      return NULL;
+    }
+
+    $indexer_table = apachesolr_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 apachesolr_get_indexer_table($type) {
+  $entity_info = entity_get_info();
+  if (isset($entity_info[$type]['apachesolr']['index_table'])) {
+    $indexer_table = $entity_info[$type]['apachesolr']['index_table'];
+  }
+  else {
+    $indexer_table = 'apachesolr_index_entities';
+  }
+  return $indexer_table;
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function apachesolr_entity_delete($entity, $entity_type) {
+  $env_id = apachesolr_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);
+  apachesolr_remove_entity($env_id, $entity_type, $entity_id);
+}
+
+function apachesolr_remove_entity($env_id, $entity_type, $entity_id) {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+
+  $indexer_table = apachesolr_get_indexer_table($entity_type);
+  if (apachesolr_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 apachesolr_entity_fields($entity_type = 'node') {
+  $fields = &drupal_static(__FUNCTION__, array());
+
+  if (!isset($fields[$entity_type])) {
+    $fields[$entity_type] = array();
+
+    $mappings = module_invoke_all('apachesolr_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('apachesolr_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][apachesolr_index_key($row)][] = $row;
+        }
+      }
+    }
+  }
+  return $fields[$entity_type];
+}
+
+/**
+ * Implements hook_apachesolr_index_document_build().
+ */
+function field_apachesolr_index_document_build(ApacheSolrDocument $document, $entity, $entity_type) {
+  $info = entity_get_info($entity_type);
+  if ($info['fieldable']) {
+    // Handle fields including taxonomy.
+    $indexed_fields = apachesolr_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_apachesolr_index_document_build_node().
+ *
+ * Adds book module support
+ */
+function apachesolr_apachesolr_index_document_build_node(ApacheSolrDocument $document, $entity, $env_id) {
+  // Index book module data.
+  if (!empty($entity->book['bid'])) {
+    // Hard-coded - must change if apachesolr_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 apachesolr_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 apachesolr_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 apachesolr_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 apachesolr_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 apachesolr_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 apachesolr_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]['apachesolr'][$callback])) {
+    $callback_function = $info['bundles'][$bundle]['apachesolr'][$callback];
+  }
+  elseif (isset($info['apachesolr'][$callback])) {
+    $callback_function = $info['apachesolr'][$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 apachesolr_get_nodes_to_index($namespace, $limit) {
+  $env_id = apachesolr_default_environment();
+  // Hardcode node as an entity type
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  apachesolr_index_get_entities_to_index($env_id, 'node', $limit);
+}
+
+
+/**
+ * Implements hook_theme().
+ */
+function apachesolr_theme() {
+  return array(
+    /**
+     * Returns a list of links generated by apachesolr_sort_link
+     */
+    'apachesolr_sort_list' => array(
+      'variables' => array('items' => NULL),
+    ),
+    /**
+     * Returns a link which can be used to search the results.
+     */
+    'apachesolr_sort_link' => array(
+      'variables' => array('text' => NULL, 'path' => NULL, 'options' => NULL, 'active' => FALSE, 'direction' => ''),
+    ),
+    /**
+     * Themes the title links in admin settings pages.
+     */
+    'apachesolr_settings_title' => array(
+      'variables' => array('env_id' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function apachesolr_hook_info() {
+  $hooks = array(
+    'apachesolr_field_mappings' => array(
+      'group' => 'apachesolr',
+    ),
+    'apachesolr_field_mappings_alter' => array(
+      'group' => 'apachesolr',
+    ),
+    'apachesolr_query_prepare' => array(
+      'group' => 'apachesolr',
+    ),
+    'apachesolr_query_alter' => array(
+      'group' => 'apachesolr',
+    ),
+    'apachesolr_search_result_alter' => array(
+      'group' => 'apachesolr',
+    ),
+    'apachesolr_environment_delete' => array(
+      'group' => 'apachesolr',
+    )
+  );
+  $hooks['apachesolr_index_document_build'] = array(
+    'group' => 'apachesolr',
+  );
+  return $hooks;
+}
+
+/**
+ * Implements hook_apachesolr_field_mappings().
+ */
+function field_apachesolr_field_mappings() {
+  $mappings = array(
+    'list_integer' => array(
+      'indexing_callback' => 'apachesolr_fields_default_indexing_callback',
+      'map callback' => 'apachesolr_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' => 'apachesolr_fields_default_indexing_callback',
+      'map callback' => 'apachesolr_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' => 'apachesolr_fields_default_indexing_callback',
+      'map callback' => 'apachesolr_fields_list_facet_map_callback',
+      'index_type' => 'string',
+      'facets' => TRUE,
+      'facet missing allowed' => TRUE,
+    ),
+    'list_boolean' => array(
+      'indexing_callback' => 'apachesolr_fields_default_indexing_callback',
+      'map callback' => 'apachesolr_fields_list_facet_map_callback',
+      'index_type' => 'boolean',
+      'facets' => TRUE,
+      'facet missing allowed' => TRUE,
+    ),
+    'number_integer' => array(
+      'indexing_callback' => 'apachesolr_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' => 'apachesolr_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' => 'apachesolr_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' => 'apachesolr_term_reference_indexing_callback',
+      'index_type' => 'integer',
+      'facet_block_callback' => 'apachesolr_search_taxonomy_facet_block',
+      'facets' => TRUE,
+      'query types' => array('term'),
+      'query type' => 'term',
+      'facet mincount allowed' => TRUE,
+    ),
+  );
+
+  return $mappings;
+}
+
+/**
+ * Implements hook_apachesolr_field_mappings() on behalf of date module.
+ */
+function date_apachesolr_field_mappings() {
+  $mappings = array();
+  $default = array(
+    'indexing_callback' => 'apachesolr_date_default_indexing_callback',
+    'index_type' => 'date',
+    'facets' => TRUE,
+    'query types' => array('date'),
+    'query type' => 'date',
+    'min callback' => 'apachesolr_get_min_date',
+    'max callback' => 'apachesolr_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'] = 'apachesolr_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 apachesolr_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 apachesolr_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_apachesolr_field_mappings() on behalf of References (node_reference).
+ * @see http://drupal.org/node/1059372
+ */
+function node_reference_apachesolr_field_mappings() {
+  $mappings = array(
+    'node_reference' => array(
+      'indexing_callback' => 'apachesolr_nodereference_indexing_callback',
+      'index_type' => 'integer',
+      'map callback' => 'apachesolr_nodereference_map_callback',
+      'facets' => TRUE,
+    )
+  );
+
+  return $mappings;
+}
+
+/**
+ * Implements hook_apachesolr_field_mappings() on behalf of References (user_reference).
+ * @see http://drupal.org/node/1059372
+ */
+function user_reference_apachesolr_field_mappings() {
+  $mappings = array(
+    'user_reference' => array(
+      'indexing_callback' => 'apachesolr_userreference_indexing_callback',
+      'index_type' => 'integer',
+      'map callback' => 'apachesolr_userreference_map_callback',
+      'facets' => TRUE,
+    ),
+  );
+
+  return $mappings;
+}
+/**
+ * Implements hook_apachesolr_field_mappings() on behalf of EntityReferences (entityreference)
+ * @see http://drupal.org/node/1572722
+ */
+function entityreference_apachesolr_field_mappings() {
+  $mappings = array(
+    'entityreference' => array(
+      'indexing_callback' => 'apachesolr_entityreference_indexing_callback',
+      'map callback' => 'apachesolr_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 ApacheSolr may not be aware of
+ *  - if set, $options['query'] MUST be an array
+ *
+ * @see http://api.drupal.org/api/function/l/6
+ *   for parameters and options.
+ *
+ * @return
+ *   an HTML string containing a link to the given path.
+ */
+function apachesolr_l($text, $path, $options = array()) {
+  // Merge in defaults.
+  $options += array(
+    'attributes' => array(),
+    'html' => FALSE,
+    'query' => array(),
+  );
+
+  // Don't need this, and just to be safe.
+  unset($options['attributes']['title']);
+
+  // Retain GET parameters that Apache Solr 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_apachesolr_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 . apachesolr_l($vars['text'], $vars['path'], $vars['options']);
+}
+
+function theme_apachesolr_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_apachesolr_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 = apachesolr_environment_load($vars['env_id'])) {
+    $url = url(
+      'admin/config/search/apachesolr/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 apachesolr_environment_load_subrecords(&$environments) {
+  if (empty($environments)) {
+    // Nothing to do.
+    return NULL;
+  }
+
+  $all_index_bundles = db_select('apachesolr_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('apachesolr_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 Apache Solr environment CTools exportables.
+ *
+ * CTools uses objects, while Apache Solr uses arrays; turn CTools value into an
+ * array, then call the normal save function.
+ *
+ * @param stdclass $environment
+ *   An environment object.
+ */
+function apachesolr_ctools_environment_save($environment) {
+  apachesolr_environment_save((array) $environment);
+}
+
+/**
+ * Callback for reverting Apache Solr environment CTools exportables.
+ *
+ * @param mixed $env_id
+ *   An environment machine name. CTools may provide an id OR a complete
+ *   environment object; Since Apache Solr loads environments as arrays, this
+ *   may also be an environment array.
+ */
+function apachesolr_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;
+  }
+  apachesolr_environment_delete($env_id);
+}
+
+/**
+ * Callback for exporting Apache Solr environments as CTools exportables.
+ *
+ * @param array $environment
+ *   An environment array from Apache Solr.
+ * @param string $indent
+ *   White space for indentation from CTools.
+ */
+function apachesolr_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) apachesolr_environment_load($environment->env_id);
+
+  $index_bundles = array();
+  foreach (entity_get_info() as $type => $info) {
+    if ($bundles = apachesolr_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('apachesolr_environment', $environment, $indent, NULL, $additions_top, $additions_bottom);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_access/apachesolr_access.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,15 @@
+name = Apache Solr Access
+description = Integrates node access and other permissions with Apache Solr search
+dependencies[] = apachesolr
+package = Search Toolkit
+core = 7.x
+
+files[] = apachesolr_access.module
+files[] = tests/apachesolr_access.test
+
+; Information added by drupal.org packaging script on 2013-03-15
+version = "7.x-1.1+34-dev"
+core = "7.x"
+project = "apachesolr"
+datestamp = "1363307665"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_access/apachesolr_access.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * Implements hook_apachesolr_index_document_build_node()
+ *
+ * Add node access grants of generic view grants if node access is not used.
+ *
+ * @param $document
+ *   The document to add our node access information to
+ * @param $node
+ *   The node which is used to built the document from
+ * @param $env_id
+ *   The environment for which we are building the document. This parameter does not have any effect in
+ *   this code so it can be ignored
+ */
+function apachesolr_access_apachesolr_index_document_build_node(ApacheSolrDocument $document, $node, $env_id) {
+  $account = &drupal_static(__FUNCTION__);
+
+  if (!isset($account)) {
+    // Load the anonymous user.
+    $account = drupal_anonymous_user();
+  }
+
+  // When using a node access module like Domain Access which has
+  // access grants that vary for anonymous users for the same content,
+  // this variable should be set to 1.  Note that doing so will prevent
+  // any results from being returned if using apachesolr_multisitesearch
+  // from a different site.
+  $always_add = apachesolr_environment_variable_get($env_id, 'apachesolr_access_always_add_grants', 0);
+  if ($always_add || !node_access('view', $node, $account)) {
+    // Get node access grants.
+    $result = db_query('SELECT * FROM {node_access} WHERE (nid = 0 OR nid = :nid) AND grant_view = 1', array(':nid' => $node->nid));
+    foreach ($result as $grant) {
+      $grant_realm = apachesolr_access_clean_realm_name($grant->realm);
+      $key = 'access_node_' . apachesolr_site_hash() . '_' . $grant_realm;
+      $document->addField($key, $grant->gid);
+    }
+  }
+  else {
+    // Add the generic view grant if we are not using
+    // node access or the node is viewable by anonymous users.
+    // We assume we'll never have an entity with the name '__all'.
+    $document->addField('access__all', 0);
+  }
+}
+
+/**
+ * Creates a Solr query for a given user
+ *
+ * @param $account
+ *   an account to get grants for and build a solr query
+ *
+ * @throws Exception
+ *
+ * @return SolrFilterSubQuery
+ *   Instance of SolrFilterSubQuery
+ */
+function apachesolr_access_build_subquery($account) {
+  if (!user_access('access content', $account)) {
+    throw new Exception('No access');
+  }
+  $node_access_query = apachesolr_drupal_subquery();
+  if (user_access('bypass node access', $account)) {
+    // Access all content from the current site.
+    $node_access_query->addFilter('hash', apachesolr_site_hash());
+  }
+  else {
+    // Get node access grants.
+    $grants = node_access_grants('view', $account);
+    foreach ($grants as $realm => $gids) {
+      $realm = apachesolr_access_clean_realm_name($realm);
+      foreach ($gids as $gid) {
+        $node_access_query->addFilter('access_node_' . apachesolr_site_hash() . '_' . $realm, $gid);
+      }
+    }
+  }
+  // Everyone can access public content. Note that if the variable
+  // 'apachesolr_access_always_add_grants' is TRUE, no content from this site
+  // is considered "public". However, this condition may match documents in
+  // the Solr index supplied by other sites when multiple sites are indexing
+  // into the same index , i.e. multisite search.
+  $node_access_query->addFilter('access__all', 0);
+  return $node_access_query;
+}
+
+/**
+ * Implements hook_apachesolr_query_alter().
+ *
+ * Alter the query to include the access subquery
+ *
+ * @param DrupalSolrQueryInterface $query
+ *
+ */
+function apachesolr_access_apachesolr_query_alter(DrupalSolrQueryInterface $query) {
+  global $user;
+  try {
+    $subquery = apachesolr_access_build_subquery($user);
+    $query->addFilterSubQuery($subquery);
+  }
+  catch (Exception $e) {
+    watchdog("apachesolr_access", 'User %name (UID:!uid) cannot search: @message', array('%name' => $user->name, '!uid' => $user->uid, '@message' => $e->getMessage()));
+    $query->abort_search = TRUE;
+  }
+}
+
+/**
+ * Implements hook_node_insert().
+ *
+ * hook_node_ACTION() is called before hook_node_access_records() in node_save().
+ *
+ * @param object $node
+ */
+function apachesolr_access_node_insert($node) {
+  $node->apachesolr_access_node_ignore = 1;
+}
+
+/**
+ * Implements hook_node_update().
+ *
+ * hook_node_ACTION() is called before hook_node_access_records() in node_save().
+ *
+ * @param object $node
+ */
+function apachesolr_access_node_update($node) {
+  $node->apachesolr_access_node_ignore = 1;
+}
+
+/**
+ * Implements hook_node_access_records().
+ *
+ * Listen to this hook to find out when a node needs to be re-indexed
+ * for its node access grants.
+ *
+ * @param object $node
+ */
+function apachesolr_access_node_access_records($node) {
+  // node_access_needs_rebuild() will usually be TRUE during a
+  // full rebuild.
+  if (empty($node->apachesolr_access_node_ignore) && !node_access_needs_rebuild()) {
+    // Only one node is being changed - mark for re-indexing.
+    apachesolr_mark_entity('node', $node->nid);
+  }
+}
+
+/**
+ * Implements hook_form_alter().
+ *
+ * @param array $form
+ * @param array $form_state
+ * @param string $form_id
+ *
+ */
+function apachesolr_access_form_alter(&$form, $form_state, $form_id) {
+  $form['#submit'][] = 'apachesolr_access_rebuild_nodeaccess';
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function apachesolr_access_form_apachesolr_environment_edit_form_alter(&$form, $form_state) {
+  $form['conf']['apachesolr_access_always_add_grants'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Add access grants even for public content'),
+    '#default_value' => empty($form['#environment']['conf']['apachesolr_access_always_add_grants']) ? 0 : 1,
+    '#description' => t('Normally should be disabled. Changing this value requires all content to be re-indexed. Useful for sites using Domamin Access or simliar node acess modules with grants that vary for anonymous users.'),
+  );
+  $form['actions']['save']['#submit'][] = 'apachesolr_access_environment_edit_form_submit';
+  $form['actions']['save_edit']['#submit'][] = 'apachesolr_access_environment_edit_form_submit';
+}
+
+/**
+ * Added button-level form submit function for apachesolr_environment_edit_form.
+ */
+function apachesolr_access_environment_edit_form_submit($form, &$form_state) {
+  $prior = empty($form['#environment']['conf']['apachesolr_access_always_add_grants']) ? 0 : 1;
+  if ($form_state['values']['conf']['apachesolr_access_always_add_grants'] != $prior) {
+    apachesolr_access_enable();
+  }
+}
+
+/**
+ * Force Solr to do a total re-index when node access rules change.
+ *
+ * This is unfortunate because not every node is going to be affected, but
+ * there is little we can do.
+ *
+ * @param $form
+ * @param $form_state
+ *
+ */
+function apachesolr_access_rebuild_nodeaccess($form, $form_state) {
+  drupal_set_message(t('Solr search index will be rebuilt.'));
+  // Clear last updated
+  apachesolr_clear_last_index_position();
+}
+
+/**
+ * Implements hook_enable().
+ *
+ * On enabling the module, tell the user to reindex
+ */
+function apachesolr_access_enable() {
+  drupal_set_message(t('Your content <a href="@url">must be re-indexed</a> before Apache Solr Access will be functional on searches.', array('@url' => url('admin/config/search/apachesolr/index'))), 'warning');
+}
+
+/**
+ * Helper function - return a safe (PHP identifier) realm name.
+ *
+ * @todo See if we can replace this with a native php function
+ *
+ * @param string $realm
+ *
+ * @return string
+ *   Clean string without bad characters
+ */
+function apachesolr_access_clean_realm_name($realm) {
+  return preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $realm);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_access/tests/apachesolr_access.test	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,157 @@
+<?php
+/**
+ * @file
+ *   Unit tests for the access control functionalities that are added by
+ *   apachesolr_access.
+ */
+class DrupalApacheSolrNodeAccess extends DrupalWebTestCase {
+
+  /**
+   * Gets Information about the DrupalApacheSolrNodeAccess test
+   *
+   * @return array
+   *   Information such as name, description and group it belongs to
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Node Access',
+      'description' => 'Test Access Control',
+      'group' => 'ApacheSolr'
+    );
+  }
+
+  /**
+   * Defines what is required to start the DrupalApacheSolrNodeAccess test.
+   */
+  function setUp() {
+    parent::setUp('node_access_test', 'apachesolr', 'apachesolr_search', 'apachesolr_access');
+
+    // Create a basic user, which is subject to moderation.
+    $permissions = array(
+      'access content',
+      'create page content',
+      'edit own page content',
+      'create article content',
+      'edit own article content',
+    );
+    $this->basic_user = $this->drupalCreateUser($permissions);
+    // Create an admin user.
+    $permissions = array(
+      'access content',
+      'search content',
+      'administer nodes',
+      'administer search',
+      'access administration pages',
+    );
+    $this->admin_user = $this->drupalCreateUser($permissions);
+  }
+
+  /**
+   * Tests indexing and check if it adds the correct grants for those specific users
+   */
+  function testIndexing() {
+    $basic_user = $this->basic_user;
+    // Login as basic user to perform initial content creation.
+
+    //Create 2 nodes
+    $edit = array();
+    $edit['uid'] = $basic_user->uid;
+    $role_restricted_node = $this->drupalCreateNode($edit);
+
+    $edit = array();
+    $edit['uid'] = $basic_user->uid;
+    $author_restricted_node = $this->drupalCreateNode($edit);
+    // Delete the generic node access grant for all nodes.
+    db_delete('node_access')->condition('nid', '0')->execute();
+
+    $roles = array_keys($basic_user->roles);
+    // The assigned role will be the last in the array.
+    $assigned_role = end($roles);
+    $role_grant = array(
+        'gid' => $assigned_role,
+        'realm' => 'nodeaccess_rid',
+        'grant_view' => '1',
+        'grant_update' => '0',
+        'grant_delete' => '0',
+    );
+    node_access_write_grants($role_restricted_node, array($role_grant));
+
+    $author_grant = array(
+        'gid' => $basic_user->uid,
+        'realm' => 'nodeaccess_author',
+        'grant_view' => '1',
+        'grant_update' => '0',
+        'grant_delete' => '0',
+    );
+
+    node_access_write_grants($author_restricted_node, array($author_grant));
+
+    // This loads the document class too.
+    $env_id = apachesolr_default_environment();
+    $solr = apachesolr_get_solr($env_id);
+
+    $document = new ApacheSolrDocument();
+    apachesolr_access_apachesolr_index_document_build_node($document, $role_restricted_node, $env_id);
+    $field = 'access_node_' . apachesolr_site_hash() . '_nodeaccess_rid';
+    $this->assertEqual($document->{$field}[0], $assigned_role, 'Solr Document being indexed is restricted by the proper role' . print_r(db_query('SELECT * FROM {node_access}')->fetchAllAssoc('nid'), 1));
+    $this->drupalGet('node');
+
+    $document = new ApacheSolrDocument();
+    apachesolr_access_apachesolr_index_document_build_node($document, $author_restricted_node, $env_id);
+    $field = 'access_node_' . apachesolr_site_hash() . '_nodeaccess_author';
+    $this->assertEqual($document->{$field}[0], $basic_user->uid, 'Solr Document being indexed is restricted by the proper author');
+
+    $expected_criterion = array(
+      'access__all' => 0,
+      'access_node_' . apachesolr_site_hash() . '_all' => 0,
+      // The node_access_test module writes this as of core 7.3.
+      'access_node_' . apachesolr_site_hash() . '_node_access_test_author' => $basic_user->uid,
+    );
+
+    // Test addition of filters to query.
+    $subquery = apachesolr_access_build_subquery($basic_user);
+    $fields = $subquery->getFilters();
+
+    foreach ($fields as $field) {
+      if (is_array($expected_criterion[$field['#name']])) {
+        $this->assertTrue(in_array($field['#value'], $expected_criterion[$field['#name']]), t('Expected node access grant @name == @value found', array('@name' => $field['#name'], '@value' => $field['#value'])));
+        //This is sorta a bug
+        $found_criterion[$field['#name']] = $expected_criterion[$field['#name']];
+      }
+      else {
+        $this->assertEqual($field['#value'], $expected_criterion[$field['#name']], t('Expected node access grant @name == @value found', array('@name' => $field['#name'], '@value' => $field['#value'])));
+        $found_criterion[$field['#name']] = $expected_criterion[$field['#name']];
+      }
+    }
+
+    $this->assertEqual($expected_criterion, $found_criterion, 'All Criteria was accounted for in fields. If not accounted for, Unaccounted Criteria [' . var_export(array_diff($expected_criterion, $found_criterion), 1) . ']');
+    // Run a query through the MLT code to be sure access filters are added.
+    $solr = new DummySolr($url = NULL, $env_id);
+    $settings = apachesolr_search_mlt_block_defaults();
+    // Dummy value
+    $id = apachesolr_document_id($author_restricted_node->nid);
+    drupal_save_session(false);
+    $GLOBALS['user'] = $basic_user;
+    $response = apachesolr_search_mlt_suggestions($settings, $id, $solr);
+    $search = $solr->getLastSearch();
+    // Should only be one fq
+    $this->assertEqual(count($search['params']['fq']), 1, 'One fq param found');
+    // Do some manipulation to avoid having to guess the order.
+    $filter = trim(end($search['params']['fq']), ')(');
+    $parts = explode(' OR ', $filter);
+    $this->assertEqual(count($expected_criterion), count($parts), 'Number of parts is the same as the number of critera');
+    foreach ($expected_criterion as $k => $v) {
+      $this->assertTrue(in_array("$k:$v", $parts), "Filter $k:$v found in the parts");
+    }
+    // Test reset of index position.
+    $this->drupalLogin($this->admin_user);
+    $env_id = apachesolr_default_environment();
+    apachesolr_set_last_index_position($env_id, 'node', 1, 1);
+    $empty = serialize(array());
+    $value = db_query('SELECT value FROM {variable} WHERE name = :name', array(':name' => 'apachesolr_index_last'))->fetchField();
+    $this->assertNotEqual($value, $empty, 'value is not empty array');
+    $this->drupalPost('admin/reports/status/rebuild', array(), t('Rebuild permissions'));
+    $value = db_query('SELECT value FROM {variable} WHERE name = :name', array(':name' => 'apachesolr_index_last'))->fetchField();
+    $this->assertEqual($value, $empty, 'value is empty array');
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_search.admin.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1196 @@
+<?php
+
+/**
+ * @file
+ *   Administrative settings for searching.
+ */
+
+/**
+ * Helper function for empty search configuration.
+ */
+function _apachesolr_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/apachesolr/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 apachesolr_search_page_list_all() {
+  $build['pages'] = apachesolr_search_page_list_pages();
+  $build['blocks'] = apachesolr_search_page_list_blocks();
+  return $build;
+
+}
+
+/**
+ * Listing of all the search pages
+ * @return array $build
+ */
+function apachesolr_search_page_list_pages() {
+  $build = array();
+  $rows = array();
+  $rows['core_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 = apachesolr_search_load_all_search_pages();
+  $default_search_page = apachesolr_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 = apachesolr_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/apachesolr/search-pages/' . $search_page['page_id'] . '/edit'));
+    $row[] = array('data' => l(t('Clone'), 'admin/config/search/apachesolr/search-pages/' . $search_page['page_id'] . '/clone'));
+
+    // Allow to revert a search page or to delete it
+    if (!isset($search_page['settings']['apachesolr_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/apachesolr/search-pages/' . $search_page['page_id'] . '/delete'));
+      } else {
+        $row[] = array('data' => l(t('Delete'), 'admin/config/search/apachesolr/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 apachesolr_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('apachesolr_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 = apachesolr_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/apachesolr_search/' . $search_block_id . '/configure', array('query' => array('destination' => current_path()))));
+    }
+    $row[] = array('data' => l(t('Delete'), 'admin/config/search/apachesolr/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 apachesolr_search_page_settings_form($form, &$form_state, $search_page = NULL) {
+  $environments = apachesolr_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']) && !apachesolr_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' => 'apachesolr_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'] == apachesolr_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_search = FALSE;
+  if (!empty($search_page['page_id']) && ($search_page['page_id'] == 'core_search')) {
+    $core_search = TRUE;
+  }
+  if ($core_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_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 = apachesolr_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']['apachesolr_search_search_type']) ? $search_page['settings']['apachesolr_search_search_type'] : '',
+    '#access' => !$core_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' => 'apachesolr_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_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']['apachesolr_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_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']['apachesolr_search_per_page']) ? $search_page['settings']['apachesolr_search_per_page'] : '10';
+  $form['advanced']['apachesolr_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']['apachesolr_search_spellcheck']) ? $search_page['settings']['apachesolr_search_spellcheck'] : TRUE;
+  $form['advanced']['apachesolr_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']['apachesolr_search_search_box']) ? $search_page['settings']['apachesolr_search_search_box'] : TRUE;
+  $form['advanced']['apachesolr_search_search_box'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable the search box on the page'),
+    '#description' => t('Display a search box on the page.'),
+    '#access' => !$core_search,
+    '#default_value' => $default_value,
+  );
+
+  // Enable/disable search form on search page (replaced by a block perhaps)
+  $default_value = isset($search_page['settings']['apachesolr_search_allow_user_input']) ? $search_page['settings']['apachesolr_search_allow_user_input'] : FALSE;
+  $form['advanced']['apachesolr_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']['apachesolr_search_browse']) ? $search_page['settings']['apachesolr_search_browse'] : 'browse';
+  $form['advanced']['apachesolr_search_browse'] = _apachesolr_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/apachesolr/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/apachesolr/search-pages',
+  );
+
+  $form['#submit'][] = 'apachesolr_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 apachesolr_search_ajax_search_page_default($form, $form_state, $search_page = NULL) {
+
+  $search_page = $form_state['values']['search_page'];
+  $search_types = apachesolr_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 apachesolr_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']['apachesolr_search_per_page'])) {
+    form_set_error('advanced][apachesolr_search_per_page', t('The amount of search results must be an integer.'));
+  }
+  $form_state['values']['advanced']['apachesolr_search_per_page'] = (int) $form_state['values']['advanced']['apachesolr_search_per_page'];
+  if (empty($form_state['values']['advanced']['apachesolr_search_per_page'])) {
+    form_set_error('advanced][apachesolr_search_per_page', t('The amount of search results cannot be empty.'));
+  }
+  if ($form_state['values']['page_id'] == 'core_search') {
+    if (!preg_match('@^search/[^/%]+$@', $form_state['values']['search_path'])) {
+      form_set_error('search_path', t('The core Search page path must start with search/ and only have one /'));
+    }
+  }
+  elseif (count(explode('%', $form_state['values']['search_path'])) > 2) {
+    form_set_error('search_path', t('Only one % placeholder is allowed.'));
+  }
+}
+
+/**
+ * Processes apachesolr_search_page_settings_form form submissions.
+ */
+function apachesolr_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['apachesolr_search_custom_enable'] = $form_state['values']['custom_filter_enable'];
+  $settings['apachesolr_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('apachesolr_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;
+  apachesolr_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 apachesolr_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/apachesolr/search-pages';
+  $button_text = $verb;
+  if (!isset($search_page['settings']['apachesolr_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 apachesolr_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('apachesolr_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('apachesolr_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/apachesolr/search-pages';
+}
+
+/**
+ * Clones a single search page configuration
+ * @param $search_page
+ *   The search page that needs to be cloned
+ */
+function apachesolr_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/apachesolr',
+    '',
+    t('Clone'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Submits the confirmations of the cloning of a search page
+ */
+function apachesolr_search_clone_search_page_confirm_submit($form, &$form_state) {
+  if (apachesolr_search_page_clone($form_state['values']['page_id'])) {
+    drupal_set_message(t('The search page was cloned'));
+  }
+  $form_state['redirect'] = 'admin/config/search/apachesolr/search-pages';
+}
+
+/**
+ * Menu callback - the settings form.
+ */
+function apachesolr_search_get_fields($environment = NULL) {
+  if (empty($environment)) {
+    $env_id = apachesolr_default_environment();
+    $environment = apachesolr_environment_load($env_id);
+  }
+  $env_id = $environment['env_id'];
+
+  // Try to fetch the schema fields.
+  try {
+    $solr = apachesolr_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 apachesolr_bias_settings_page($environment = NULL) {
+  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,
+    ),
+  );
+
+  // Adds content bias and type boost forms.
+  $fields = apachesolr_search_get_fields($environment);
+  $form = array();
+  $form = drupal_get_form('apachesolr_search_bias_form', $env_id, $fields);
+  $output['bias_forms'] = $form;
+  return $output;
+}
+
+function apachesolr_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('apachesolr_search_bias_form_submit'),
+  );
+  $form['actions']['reset'] = array(
+    '#type' => 'submit',
+    '#value' => t('Reset to defaults'),
+    '#submit' => array('apachesolr_search_bias_form_reset'),
+  );
+  $form += apachesolr_search_result_bias_form($env_id);
+  $form += apachesolr_search_type_boost_form($env_id);
+  $form += apachesolr_search_field_bias_form($fields, $env_id);
+  return $form;
+}
+
+function apachesolr_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 'apachesolr_search_sticky_boost' :
+        case 'apachesolr_search_promote_boost' :
+        case 'apachesolr_search_date_boost' :
+        case 'apachesolr_search_comment_boost' :
+        case 'apachesolr_search_changed_boost' :
+        case 'apachesolr_search_type_boosts' :
+        case 'field_bias' :
+          apachesolr_environment_variable_set($form['#env_id'], $key, $value);
+      }
+    }
+  }
+  drupal_set_message(t('The configuration options have been saved.'));
+}
+
+function apachesolr_search_bias_form_reset($form, &$form_state) {
+  // Exclude unnecessary elements.
+  form_state_values_clean($form_state);
+
+  foreach ($form_state['values'] as $key => $value) {
+    apachesolr_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 apachesolr_search_result_bias_form($env_id) {
+
+  $date_settings = apachesolr_environment_variable_get($env_id, 'apachesolr_search_date_boost', '0:0');
+  $comment_settings = apachesolr_environment_variable_get($env_id, 'apachesolr_search_comment_boost', '0:0');
+  $changed_settings = apachesolr_environment_variable_get($env_id, 'apachesolr_search_changed_boost', '0:0');
+  $sticky_boost = apachesolr_environment_variable_get($env_id, 'apachesolr_search_sticky_boost', '0');
+  $promote_boost = apachesolr_environment_variable_get($env_id, 'apachesolr_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']['apachesolr_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']['apachesolr_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']['apachesolr_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']['apachesolr_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']['apachesolr_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 apachesolr_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 = apachesolr_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(apachesolr_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(apachesolr_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 apachesolr_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']['apachesolr_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 = apachesolr_environment_variable_get($env_id, 'apachesolr_search_type_boosts', array());
+  $names = array();
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    if (!empty($entity_info['apachesolr']['indexable'])) {
+      foreach ($entity_info['bundles'] as $key => $info) {
+        $names[$key] = $info['label'];
+      }
+    }
+  }
+  asort($names);
+
+  foreach ($names as $type => $name) {
+    $form['type_boost']['apachesolr_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 apachesolr_search_mlt_add_block_form() {
+  $form = apachesolr_search_mlt_block_form();
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => '5',
+  );
+  return $form;
+}
+
+function apachesolr_search_mlt_add_block_form_submit($form, &$form_state) {
+  apachesolr_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/apachesolr/search-pages';
+}
+
+/**
+ * Merge supplied settings with the standard defaults..
+ */
+function apachesolr_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 apachesolr_search_mlt_get_fields() {
+  $rows = array();
+
+  try {
+    $solr = apachesolr_get_solr();
+    $fields = $solr->getFields();
+    foreach ($fields as $field_name => $field) {
+      if ($field->schema{4} == 'V') {
+        $rows[$field_name] = apachesolr_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 apachesolr_search_mlt_save_block($block_settings = array(), $delta = NULL) {
+  $blocks = variable_get('apachesolr_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 = apachesolr_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('apachesolr_search_mlt_blocks', $blocks);
+}
+
+function apachesolr_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 apachesolr_search_mlt_delete_block_form_submit($form, &$form_state) {
+  $blocks = apachesolr_search_load_all_mlt_blocks();
+
+  unset($blocks[$form_state['values']['delta']]);
+  variable_set('apachesolr_search_mlt_blocks', $blocks);
+  drupal_set_message(t('The block has been deleted.'));
+  $form_state['redirect'] = 'admin/config/search/apachesolr/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 apachesolr_search_mlt_block_form($block_id = NULL) {
+  if (!empty($block_id)) {
+    $block = apachesolr_search_mlt_block_load($block_id);
+    if (!$block) {
+      return array();
+    }
+  }
+  else {
+    $block = apachesolr_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 = apachesolr_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'] : apachesolr_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" => apachesolr_field_name_map("taxonomy_names"), "%title" => apachesolr_field_name_map("label"))),
+    '#options' => apachesolr_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/sites/all/modules/custom/solrconnect/apachesolr_search.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,19 @@
+name = Apache Solr search
+description = Search with Solr
+dependencies[] = search
+dependencies[] = apachesolr
+package = Search Toolkit
+core = 7.x
+configure = admin/config/search/apachesolr/search-pages
+
+files[] = apachesolr_search.install
+files[] = apachesolr_search.module
+files[] = apachesolr_search.admin.inc
+files[] = apachesolr_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 = "apachesolr"
+datestamp = "1363307665"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_search.install	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,387 @@
+<?php
+
+/**
+ * @file
+ *   Install and related hooks for apachesolr_search.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function apachesolr_search_install() {
+  // Add a taxonomy search page to the database
+  $settings = array(
+    'apachesolr_search_search_type' => 'tid',
+    'apachesolr_search_per_page' => 10,
+    'apachesolr_search_browse' => 'results',
+    'apachesolr_search_spellcheck' => FALSE,
+    'apachesolr_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('apachesolr_search_page')->fields($fields)->execute();
+}
+
+/**
+ * Implements hook_enable().
+ */
+function apachesolr_search_enable() {
+  // Make sure the default core search page is installed.
+  $search_page = apachesolr_search_page_load('core_search');
+  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(
+      'apachesolr_search_search_type' => 'custom',
+      'apachesolr_search_per_page' => 10,
+      'apachesolr_search_browse' => 'browse',
+      'apachesolr_search_spellcheck' => TRUE,
+      'apachesolr_search_not_removable' => TRUE,
+      'apachesolr_search_search_box' => TRUE,
+    );
+    $settings = serialize($settings);
+
+    $fields = array(
+      'page_id' => 'core_search',
+      'label' => 'Core Search',
+      'description' => 'Core Search',
+      'search_path' => 'search/site',
+      'env_id' => 'solr',
+      'page_title' => 'Site',
+      'settings' => $settings,
+    );
+    db_insert('apachesolr_search_page')->fields($fields)->execute();
+  }
+
+
+  $active = variable_get('search_active_modules', array('node', 'user'));
+  $active[] = 'apachesolr_search';
+  variable_set('search_active_modules', array_unique($active));
+}
+
+/**
+ * Implements hook_schema().
+ */
+function apachesolr_search_schema() {
+  $schema = array();
+
+  $schema['apachesolr_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' => 'apachesolr_search_default_searchers',
+      'status' => 'apachesolr_search_page_status',
+      // CTools API implementation.
+      'api' => array(
+        'owner' => 'apachesolr_search',
+        'api' => 'apachesolr_search_defaults',
+        'minimum_version' => 3,
+        'current_version' => 3,
+      ),
+      // Includes all search page specific configurations.
+      'export callback' => 'apachesolr_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 apachesolr_search_uninstall() {
+  $stored = variable_get('apachesolr_index_last', array());
+  unset($stored['apachesolr_search']);
+  variable_set('apachesolr_index_last', $stored);
+
+  $active = variable_get('search_active_modules', array('node', 'user'));
+  $idx = array_search('apachesolr_search', $active);
+  if ($idx !== FALSE) {
+    unset($active[$idx]);
+    variable_set('search_active_modules', $active);
+  }
+  // Remove variables.
+  variable_del('apachesolr_search_spellcheck');
+  variable_del('apachesolr_search_mlt_blocks');
+  variable_del('apachesolr_search_default_search_page');
+  // Remove blocks.
+  db_delete('block')->condition('module', 'apachesolr_search')->execute();
+}
+
+/**
+ * Various updates for Drupal 7.
+ */
+function apachesolr_search_update_7000() {
+  $taxo_links = variable_get('apachesolr_search_taxonomy_links', 0);
+  // TODO - enable the new contrib module?
+  variable_del('apachesolr_search_taxonomy_links');
+  // TODO - possibly rename block deltas, etc.
+  $active = variable_get('search_active_modules', array('node', 'user'));
+  $active[] = 'apachesolr_search';
+  variable_set('search_active_modules', array_unique($active));
+  if (variable_get('apachesolr_search_make_default', 0)) {
+    variable_set('search_default_module', 'apachesolr_search');
+  }
+  variable_del('apachesolr_search_make_default');
+}
+
+/**
+ * Add apachesolr_search_page table.
+ */
+function apachesolr_search_update_7001() {
+  // Moved to 7002
+}
+
+/**
+ * Add apachesolr_search_page table for real.
+ */
+function apachesolr_search_update_7002() {
+
+  $schema['apachesolr_search_page'] = array(
+    'description' => 'Apache Solr Search search page settings.',
+    'export' => array(
+      'key' => 'page_id',
+      'identifier' => 'searcher',
+      'default hook' => 'apachesolr_search_default_searchers',
+      'status' => 'apachesolr_search_page_status',
+      'api' => array(
+        'owner' => 'apachesolr_search',
+        'api' => 'apachesolr_search_defaults',
+        'minimum_version' => 3,
+        'current_version' => 3,
+      ),
+      'export callback' => 'apachesolr_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('apachesolr_search_page')) {
+    // Just in case you are chasing HEAD.
+    db_drop_table('apachesolr_search_page');
+  }
+  db_create_table('apachesolr_search_page', $schema['apachesolr_search_page']);
+}
+
+
+/**
+ * Delete all Apache Solr Search blocks - they moved to Facet API.
+ */
+function apachesolr_search_update_7003() {
+  // Remove blocks.
+  db_delete('block')->condition('module', 'apachesolr_search')->execute();
+}
+
+/**
+ * Add a default search page for core
+ * Add a taxonomy page if the taxonomy module was ever active
+ */
+function apachesolr_search_update_7004() {
+  // Add Default search page (core search)
+  $settings = array(
+    'apachesolr_search_search_type' => 'custom',
+    'apachesolr_search_per_page' => variable_get('apachesolr_rows', 10),
+    'apachesolr_search_browse' => variable_get('apachesolr_search_browse', 'browse'),
+    'apachesolr_search_spellcheck' => variable_get('apachesolr_search_spellcheck', TRUE),
+    'apachesolr_search_not_removable' => TRUE,
+    'apachesolr_search_search_box' => TRUE,
+  );
+  $settings = serialize($settings);
+
+  $fields = array(
+    'page_id' => 'core_search',
+    'label' => 'Core Search',
+    'description' => 'Site search',
+    'search_path' => 'search/site',
+    'env_id' => 'solr',
+    'page_title' => 'Site',
+    'settings' => $settings,
+  );
+  db_insert('apachesolr_search_page')->fields($fields)->execute();
+  // Remove variables.
+  variable_del('apachesolr_search_spellcheck');
+  variable_del('apachesolr_search_browse');
+
+  // Add this taxonomy search page to the database
+  $settings = array(
+    'apachesolr_search_search_type' => 'tid',
+    'apachesolr_search_per_page' => 10,
+    'apachesolr_search_browse' => 'results',
+    'apachesolr_search_spellcheck' => FALSE,
+    'apachesolr_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('apachesolr_search_page')->fields($fields)->execute();
+
+  // Check if the taxonomy module was ever present
+  $status = db_query("SELECT 1 FROM {system} WHERE name = 'apachesolr_taxonomy'")->fetchField();
+  if ($status) {
+    $message  = t('If you had the apachesolr_taxonomy module enabled please go to the !link and enable the Taxonomy Term page', array('!link' => l('Apache Solr custom pages', 'admin/config/search/apachesolr/search-pages')));
+    drupal_set_message($message, 'warning');
+  }
+}
+
+/**
+ * Make the env_id length on the apachesolr_search_page table 64 characters
+ * to match the length of the env_id on all other tables
+ */
+function apachesolr_search_update_7005(&$sandbox) {
+  db_drop_index('apachesolr_search_page', 'env_id');
+  db_change_field('apachesolr_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 apachesolr_search env variables for show_facets if it is zero
+ */
+function apachesolr_search_update_7006() {
+  module_load_include('module', 'apachesolr');
+  $environments = apachesolr_load_all_environments();
+  foreach ($environments as $environment) {
+    $show_facets = apachesolr_environment_variable_get($environment['env_id'], 'apachesolr_search_show_facets', 0);
+    if ($show_facets === 0) {
+      apachesolr_environment_variable_del($environment['env_id'], 'apachesolr_search_show_facets');
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_search.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1726 @@
+<?php
+
+/**
+ * @file
+ *   Provides a content search implementation for node content for use with the
+ *   Apache Solr search application.
+ */
+
+/**
+ * Implements hook_init().
+ *
+ * Checks if we should run an empty facet query so the facet blocks can be
+ * displayed.
+ */
+function apachesolr_search_init() {
+  // Useless without facetapi
+  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 {apachesolr_environment_variable}
+    WHERE name = 'apachesolr_search_show_facets'";
+  $count = db_query($query)->fetchField();
+  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
+  $search_page_id = apachesolr_search_default_search_page();
+  $search_page = apachesolr_search_page_load($search_page_id);
+  // Do not continue if our search page is not valid
+  if (empty($search_page)) {
+    return NULL;
+  }
+
+  $show_facets = apachesolr_environment_variable_get($search_page['env_id'], 'apachesolr_search_show_facets', 0);
+  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 = apachesolr_environment_variable_get($search_page['env_id'], 'apachesolr_search_facet_pages', '');
+
+    // 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) {
+        if (drupal_match_path($path, $patterns)) {
+          try {
+            if (!empty($search_page['search_path'])) {
+              $solr = apachesolr_get_solr($search_page['env_id']);
+              // Initializes params for empty query.
+              $params = array(
+                'spellcheck' => 'false',
+                'fq' => array(),
+                'rows' => 1,
+              );
+              $context['page_id'] = $search_page_id;
+              $context['search_type'] = 'apachesolr_search_show_facets';
+              apachesolr_search_run_empty('apachesolr', $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 apachesolr_search_menu() {
+  $items['admin/config/search/apachesolr/search-pages'] = array(
+    'title'            => 'Pages/Blocks',
+    'description'      => 'Configure search pages',
+    'page callback'    => 'apachesolr_search_page_list_all',
+    'access arguments' => array('administer search'),
+    'type'             => MENU_LOCAL_TASK,
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/search-pages/add'] = array(
+    'title'            => 'Add search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('apachesolr_search_page_settings_form'),
+    'access arguments' => array('administer search'),
+    'type'             => MENU_LOCAL_ACTION,
+    'weight'           => 1,
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/search-pages/%apachesolr_search_page/edit'] = array(
+    'title' => 'Edit search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('apachesolr_search_page_settings_form', 5),
+    'access arguments' => array('administer search'),
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/search-pages/%apachesolr_search_page/delete'] = array(
+    'title'            => 'Delete search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('apachesolr_search_delete_search_page_confirm', 5),
+    'access arguments' => array('administer search'),
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/search-pages/%apachesolr_search_page/clone'] = array(
+    'title' => 'Clone search page',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('apachesolr_search_clone_search_page_confirm', 5),
+    'access arguments' => array('administer search'),
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/search-pages/addblock'] = array(
+    'title'            => 'Add search block "More Like This"',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('apachesolr_search_mlt_add_block_form'),
+    'access arguments' => array('administer search'),
+    'type'             => MENU_LOCAL_ACTION,
+    'weight'           => 2,
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  $items['admin/config/search/apachesolr/search-pages/block/%apachesolr_search_mlt_block/delete'] = array(
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('apachesolr_search_mlt_delete_block_form', 6),
+    'access arguments'   => array('administer search'),
+    'file'               => 'apachesolr_search.admin.inc',
+    'type'               => MENU_CALLBACK,
+  );
+
+  // Environment specific settings
+  $settings_path = 'admin/config/search/apachesolr/settings/';
+  $items[$settings_path . '%apachesolr_environment/bias'] = array(
+    'title'            => 'Bias',
+    'page callback'    => 'apachesolr_bias_settings_page',
+    'page arguments'   => array(5),
+    'access arguments' => array('administer search'),
+    'weight'           => 4,
+    'type'             => MENU_LOCAL_TASK,
+    'file'             => 'apachesolr_search.admin.inc',
+  );
+  return $items;
+}
+
+function apachesolr_search_menu_alter(&$items) {
+  // Gets default search information.
+  $default_info = search_get_default_module_info();
+  $search_types = apachesolr_search_load_all_search_types();
+  $search_pages = apachesolr_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 = apachesolr_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_search = ($parts[0] == 'search');
+    $position = array_search('%', $parts);
+    $page_title = isset($search_page['page_title']) ? $search_page['page_title'] : 'Search Results';
+
+    // If we have a taxonomy search, remove existing menu paths
+    if ($search_page['search_path'] == 'taxonomy/term/%') {
+      unset($items['taxonomy/term/%taxonomy_term']);
+      unset($items['taxonomy/term/%taxonomy_term/view']);
+    }
+
+    // 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' => 'apachesolr_search_custom_page',
+      'page arguments' => array($search_page['page_id'], '', $position),
+      'access arguments' => array('search content'),
+      'type' => ($core_search) ? MENU_LOCAL_TASK : MENU_SUGGESTED_ITEM,
+      'file' => 'apachesolr_search.pages.inc',
+      'file path' => drupal_get_path('module', 'apachesolr_search'),
+    );
+
+    // Not using menu tail because of inflexibility with clean urls
+    $items[$search_page['search_path'] . '/%'] = array(
+      'title' => $page_title,
+      'page callback' => 'apachesolr_search_custom_page',
+      'page arguments' => array($search_page['page_id'], $keys_pos, $position),
+      'access arguments' => array('search content'),
+      'type' => !($core_search) ? MENU_CALLBACK : MENU_LOCAL_TASK,
+      'file' => 'apachesolr_search.pages.inc',
+      'file path' => drupal_get_path('module', 'apachesolr_search'),
+    );
+
+    // If title has a certain callback for the selected type we use it
+    $search_type_id = !empty($search_page['settings']['apachesolr_search_search_type']) ? $search_page['settings']['apachesolr_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_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 apachesolr_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('apachesolr_search:search_types', 'cache_apachesolr');
+  if (isset($cache->data)) {
+    $search_types = $cache->data;
+  }
+  else {
+    $search_types = array(
+      'tid' => array(
+        'name' => apachesolr_field_name_map('tid'),
+        'default menu' => 'taxonomy/term/%',
+        'title callback' => 'apachesolr_search_get_taxonomy_term_title',
+      ),
+      'is_uid' => array(
+        'name' => apachesolr_field_name_map('is_uid'),
+        'default menu' => 'user/%/search',
+        'title callback' => 'apachesolr_search_get_user_title',
+      ),
+      'bundle' => array(
+        'name' => apachesolr_field_name_map('bundle'),
+        'default menu' => 'search/type/%',
+        'title callback' => 'apachesolr_search_get_value_title',
+      ),
+      'ss_language' => array(
+        'name' => apachesolr_field_name_map('ss_language'),
+        'default menu' => 'search/language/%',
+        'title callback' => 'apachesolr_search_get_value_title',
+      ),
+    );
+    drupal_alter('apachesolr_search_types', $search_types);
+    cache_set('apachesolr_search:search_types', $search_types, 'cache_apachesolr');
+  }
+  return $search_types;
+}
+
+/**
+ * Used as a callback function to generate a title for the taxonomy term
+ * depending on the input in the configuration screen
+ * @param integer $search_page_id
+ * @param integer $value
+ * @return String
+ */
+function apachesolr_search_get_taxonomy_term_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']);
+     $term = taxonomy_term_load($value);
+     if (!$term) {
+       return NULL;
+     }
+     $title = $term->name;
+   }
+   return t($page_title, array('!value' => $title));
+}
+
+/**
+ * Used as a callback function to generate a title for a user name depending
+ * on the input in the configuration screen
+ * @param integer $search_page_id
+ * @param integer $value
+ * @return String
+ */
+function apachesolr_search_get_user_title($search_page_id = NULL, $value = NULL) {
+  $page_title = 'Search results for %value';
+  $title = '';
+  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']);
+    $user = user_load($value);
+    if (!$user) {
+       return NULL;
+     }
+    $title = $user->name;
+  }
+  return t($page_title, array('!value' => $title));
+}
+
+/**
+ * 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 apachesolr_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));
+}
+
+/**
+ * Get or set the default search page id for the current page.
+ */
+function apachesolr_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('apachesolr_search_default_search_page', 'core_search');
+  }
+  return $default_page_id;
+}
+
+/**
+ * Implements hook_apachesolr_default_environment()
+ *
+ * Make sure the core search page is using the default environment.
+ */
+function apachesolr_search_apachesolr_default_environment($env_id, $old_env_id) {
+  $page = apachesolr_search_page_load('core_search');
+  if ($page && $page['env_id'] != $env_id) {
+    $page['env_id'] = $env_id;
+    apachesolr_search_page_save($page);
+  }
+}
+
+/**
+ * Load a search page
+ * @param string $page_id
+ * @return array
+ */
+function apachesolr_search_page_load($page_id) {
+  $search_pages = apachesolr_search_load_all_search_pages();
+  if (!empty($search_pages[$page_id])) {
+    return $search_pages[$page_id];
+  }
+  return FALSE;
+}
+
+function apachesolr_search_page_save($search_page) {
+  if (!empty($search_page)) {
+    db_merge('apachesolr_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 apachesolr_search_page_clone($page_id) {
+  $search_page = apachesolr_search_page_load($page_id);
+  // Get all search_pages
+  $search_pages = apachesolr_search_load_all_search_pages();
+  // Create an unique ID
+  $new_search_page_id = apachesolr_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']['apachesolr_search_not_removable'])) {
+    unset($search_page['settings']['apachesolr_search_not_removable']);
+  }
+  // Save our new search page in the database
+  apachesolr_search_page_save($search_page);
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function apachesolr_search_block_info() {
+  // Get all of the moreLikeThis blocks that the user has created
+  $blocks = apachesolr_search_load_all_mlt_blocks();
+  foreach ($blocks as $delta => $settings) {
+    $blocks[$delta] += array('info' => t('Apache Solr recommendations: !name', array('!name' => $settings['name'])) , 'cache' => DRUPAL_CACHE_PER_PAGE);
+  }
+  // Add the sort block.
+  $blocks['sort'] = array(
+    'info' => t('Apache Solr Core: Sorting'),
+    'cache' => DRUPAL_CACHE_CUSTOM,
+  );
+  return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function apachesolr_search_block_view($delta = '') {
+  if ($delta == 'sort') {
+    $environments = apachesolr_load_all_environments();
+    foreach ($environments as $env_id => $environment) {
+      if (apachesolr_has_searched($env_id) && !apachesolr_suppress_blocks($env_id) && $delta == 'sort') {
+        $response = NULL;
+        $query = apachesolr_current_query($env_id);
+        $solrsort = NULL;
+        if ($query) {
+          // Get the query and response. Without these no blocks make sense.
+          $response = apachesolr_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('apachesolr_sort_link', $link);
+        }
+        $block = array(
+        'subject' => t('Sort by'),
+          'content' => theme('apachesolr_sort_list', array('items' => $themed_links))
+        );
+        // Cache the block
+        cache_set($cache_id, $block, 'cache_block');
+        return $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 = apachesolr_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 = apachesolr_get_solr($env_id);
+        $context['search_type'] = 'apachesolr_search_mlt';
+        $context['block_id'] = $delta;
+        $docs = apachesolr_search_mlt_suggestions($block, apachesolr_document_id($node->nid), $solr, $context);
+        if (!empty($docs)) {
+          $suggestions['subject'] = check_plain($block['name']);
+          $suggestions['content'] = array(
+            '#theme' => 'apachesolr_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 apachesolr_search_form_block_admin_display_form_alter(&$form) {
+  foreach ($form['blocks'] as $key => $block) {
+    if ((strpos($key, "apachesolr_search_mlt-") === 0) && $block['module']['#value'] == 'apachesolr_search') {
+      $form['blocks'][$key]['delete'] = array(
+        '#type' => 'link',
+        '#title' => 'delete',
+        '#href' => 'admin/config/search/apachesolr/search-pages/block/' . $block['delta']['#value'] . '/delete',
+      );
+    }
+  }
+}
+
+/**
+ * Implements hook_block_configure().
+ */
+function apachesolr_search_block_configure($delta = '') {
+  if ($delta != 'sort') {
+    require_once(drupal_get_path('module', 'apachesolr') . '/apachesolr_search.admin.inc');
+    return apachesolr_search_mlt_block_form($delta);
+  }
+}
+
+/**
+ * Implements hook_block_save().
+ */
+function apachesolr_search_block_save($delta = '', $edit = array()) {
+  if ($delta != 'sort') {
+    require_once(drupal_get_path('module', 'apachesolr') . '/apachesolr_search.admin.inc');
+    apachesolr_search_mlt_save_block($edit, $delta);
+  }
+}
+
+
+/**
+ * Return all the saved search pages
+ * @return array $search_pages
+ *   Array of all search pages
+ */
+function apachesolr_search_load_all_search_pages() {
+  $search_pages = &drupal_static(__FUNCTION__, array());
+
+  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('apachesolr_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 {apachesolr_search_page}')->fetchAllAssoc('page_id', PDO::FETCH_ASSOC);
+
+  $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_search'])) {
+    $search_pages['core_search']['env_id'] = apachesolr_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;
+    }
+  }
+  return $search_pages;
+}
+
+function apachesolr_search_load_all_mlt_blocks() {
+  $search_blocks = variable_get('apachesolr_search_mlt_blocks', array());
+  return $search_blocks;
+}
+
+function apachesolr_search_mlt_block_load($block_id) {
+  $search_blocks = variable_get('apachesolr_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 apachesolr_document_id($node->nid)
+ * @param $solr
+ *   The solr environment you want to query against
+ *
+ * @return An array of response documents, or NULL
+ */
+function apachesolr_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 = apachesolr_drupal_query('apachesolr_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('apachesolr_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_apachesolr_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 apachesolr_search_search_info() {
+  // Load our core search page
+  // This core search page is assumed to always be there. It cannot be deleted.
+  $search_page = apachesolr_search_page_load('core_search');
+
+  // This can happen during install, or if the DB was manually changed.
+  if (empty($search_page)) {
+    $search_page = array();
+    $search_page['page_title'] = 'Site';
+    $search_page['search_path'] = 'search/site';
+  }
+
+  return array(
+    'title' => $search_page['page_title'],
+    'path' => str_replace('search/', '', $search_page['search_path']),
+    'conditions_callback' => variable_get('apachesolr_search_conditions_callback', 'apachesolr_search_conditions'),
+  );
+}
+
+/**
+ * Implements hook_search_reset().
+ */
+function apachesolr_search_search_reset() {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $env_id = apachesolr_default_environment();
+  apachesolr_index_mark_for_reindex($env_id);
+}
+
+/**
+ * Implements hook_search_status().
+ */
+function apachesolr_search_search_status() {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $env_id = apachesolr_default_environment();
+  return apachesolr_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 apachesolr_search_conditions
+ */
+function apachesolr_search_search_execute($keys = NULL, $conditions = NULL) {
+  $search_page = apachesolr_search_page_load('core_search');
+  $results = apachesolr_search_search_results($keys, $conditions, $search_page);
+  return $results;
+}
+
+/**
+ * Implementation of a search_view() conditions callback.
+ */
+function apachesolr_search_conditions() {
+  //get default conditions from the core_search
+  $search_page = apachesolr_search_page_load('core_search');
+  $conditions = apachesolr_search_conditions_default($search_page);
+  return $conditions;
+}
+
+/**
+ * Implements hook_search_page().
+ * @param $results
+ *   The results that came from apache solr
+ */
+function apachesolr_search_search_page($results) {
+  $search_page = apachesolr_search_page_load('core_search');
+  $build = apachesolr_search_search_page_custom($results, $search_page);
+  return $build;
+}
+
+/**
+ * Mimics apachesolr_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 apachesolr_search_search_page_custom($results, $search_page, $build = array()) {
+  if (!empty($search_page['settings']['apachesolr_search_spellcheck'])) {
+    // Retrieve suggestion
+    $suggestions = apachesolr_search_get_search_suggestions($search_page['env_id']);
+    if ($search_page && !empty($suggestions)) {
+      $build['suggestions'] = array(
+        '#theme' => 'apachesolr_search_suggestions',
+        '#links' => array(l($suggestions[0], $search_page['search_path'] . '/' . $suggestions[0])),
+      );
+    }
+  }
+  // Retrieve expected results from searching
+  if (!empty($results['apachesolr_search_browse'])) {
+    // Show facet browsing blocks.
+    $build['search_results'] = apachesolr_search_page_browse($results['apachesolr_search_browse'], $search_page['env_id']);
+  }
+  elseif ($results) {
+    $build['search_results'] = array(
+      '#theme' => 'search_results',
+      '#results' => $results,
+      '#module' => 'apachesolr_search',
+      '#search_page' => $search_page,
+    );
+  }
+  else {
+    // Give the user some custom help text.
+    $build['search_results'] = array('#markup' => theme('apachesolr_search_noresults'));
+  }
+
+  // Allows modules to alter the render array before returning.
+  drupal_alter('apachesolr_search_page', $build, $search_page);
+
+  return $build;
+}
+
+/**
+ * Executes search depending on the conditions given.
+ * See apachesolr_search.pages.inc for another use of this function
+ */
+function apachesolr_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'] != 'apachesolr_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 = apachesolr_search_page_load('core_search');
+      // Verify if it actually loaded
+      if (empty($search_page)) {
+        // Something must have been really messed up.
+        apachesolr_failure(t('Solr search'), $keys);
+        return array();
+      }
+    }
+    if (empty($conditions)) {
+      $conditions = apachesolr_search_conditions_default($search_page);
+    }
+
+    // Sort options from the conditions array.
+    // @see apachesolr_search_conditions_default()
+    //   See This condition callback to find out how.
+    $solrsort = isset($conditions['apachesolr_search_sort']) ? $conditions['apachesolr_search_sort'] : '';
+    // What to do when we have an initial empty search
+    $empty_search_behavior = isset($search_page['settings']['apachesolr_search_browse']) ? $search_page['settings']['apachesolr_search_browse'] : '';
+
+    try {
+
+      $solr = apachesolr_get_solr($search_page['env_id']);
+      // Default parameters
+      $params['fq'] = isset($conditions['fq']) ? $conditions['fq'] : array();
+      $params['rows'] = $search_page['settings']['apachesolr_search_per_page'];
+
+      if (empty($search_page['settings']['apachesolr_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 apachesolr_search_search_page()
+        // Hardcoded apachesolr name since we rely on this for the facets
+        $context['page_id'] = $search_page['page_id'];
+        $context['search_type'] = 'apachesolr_search_browse';
+        apachesolr_search_run_empty('apachesolr', $params, $search_page['search_path'], $solr, $context);
+        $results['apachesolr_search_browse'] = $empty_search_behavior;
+
+        if ($empty_search_behavior == 'browse') {
+          // Hide sidebar blocks for content-area browsing instead.
+          apachesolr_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 apachesolr name since we rely on this for the facets
+        $context['page_id'] = $search_page['page_id'];
+        $context['search_type'] = 'apachesolr_search_results';
+        $results = apachesolr_search_run('apachesolr', $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);
+      apachesolr_failure(t('Solr search'), $keys);
+    }
+  }
+  return $results;
+}
+
+function apachesolr_search_conditions_default($search_page) {
+  $conditions = array();
+  $search_type = isset($search_page['settings']['apachesolr_search_search_type']) ? $search_page['settings']['apachesolr_search_search_type'] : '';
+  $allow_user_input = isset($search_page['settings']['apachesolr_search_allow_user_input']) ? $search_page['settings']['apachesolr_search_allow_user_input'] : FALSE;
+  $path_replacer = isset($search_page['settings']['apachesolr_search_path_replacer']) ? $search_page['settings']['apachesolr_search_path_replacer'] : '';
+  $set_custom_filter = isset($search_page['settings']['apachesolr_search_custom_enable']) ? $search_page['settings']['apachesolr_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 = apachesolr_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['apachesolr_search_sort'] = $sort;
+  return $conditions;
+}
+
+/**
+ * Handle browse results for empty searches.
+ */
+function apachesolr_search_page_browse($empty_search_behavior, $env_id) {
+  $build = array();
+  // Switch in case we come up with new flags.
+  switch ($empty_search_behavior) {
+    case 'browse':
+      if (module_exists('facetapi') && $query = apachesolr_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();
+
+        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 = 'apachesolr-' . $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'][] = 'apachesolr_search_browse_blocks';
+      }
+      break;
+  }
+  return $build;
+}
+
+/**
+ * Shows a groups of blocks for starting a search from a filter.
+ */
+function theme_apachesolr_search_browse_blocks($vars) {
+  $result = '';
+  if ($vars['content']['#children']) {
+    $result .= "<div class='apachesolr-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 apachesolr_search_run_empty($name, array $params = array(), $base_path = '', $solr = NULL, $context = array()) {
+  $query = apachesolr_drupal_query($name, $params, '', $base_path, $solr, $context);
+  $query->addParam('rows', '0');
+  $solr_id = $query->solr('getId');
+  list($final_query, $response) = apachesolr_do_query($query);
+  apachesolr_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 apachesolr_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 += apachesolr_search_basic_params();
+  // This is the object that knows about the query coming from the user.
+  $query = apachesolr_drupal_query($name, $params, $solrsort, $base_path, $solr, $context);
+
+  if ($query->getParam('q')) {
+    apachesolr_search_add_spellcheck_params($query);
+  }
+
+  // Add the paging parameters
+  $query->page = $page;
+
+  apachesolr_search_add_boost_params($query);
+  if ($query->getParam('q')) {
+    apachesolr_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) = apachesolr_do_query($query);
+  $env_id = $query->solr('getId');
+  apachesolr_has_searched($env_id, TRUE);
+  $process_response_callback = apachesolr_environment_variable_get($env_id, 'process_response_callback', 'apachesolr_search_process_response');
+  if (function_exists($process_response_callback)) {
+    return call_user_func($process_response_callback, $response, $final_query);
+  }
+  else {
+    return apachesolr_search_process_response($response, $final_query);
+  }
+}
+
+function apachesolr_search_basic_params(DrupalSolrQueryInterface $query = NULL) {
+  $params = array(
+    'fl' => array(
+        'id',
+        'entity_id',
+        'entity_type',
+        'bundle',
+        'bundle_name',
+        'label',
+        'is_comment_count',
+        'ds_created',
+        'ds_changed',
+        'score',
+        'path',
+        'url',
+        'is_uid',
+        'tos_name',
+      ),
+    'mm' => 1,
+    'rows' => 10,
+    'pf' => 'content^2.0',
+    'ps' => 15,
+    'hl' => 'true',
+    'hl.fl' => 'content',
+    '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 apachesolr_search_highlighting_params(DrupalSolrQueryInterface $query = NULL) {
+  $params['hl'] = variable_get('apachesolr_hl_active', NULL);
+  $params['hl.fragsize']= variable_get('apachesolr_hl_textsnippetlength', NULL);
+  $params['hl.simple.pre'] = variable_get('apachesolr_hl_pretag', NULL);
+  $params['hl.simple.post'] = variable_get('apachesolr_hl_posttag', NULL);
+  $params['hl.snippets'] = variable_get('apachesolr_hl_numsnippets', NULL);
+  // This should be an array of possible field names.
+  $params['hl.fl'] = variable_get('apachesolr_hl_fieldtohighlight', NULL);
+  $params = array_filter($params);
+  if ($query) {
+    $query->addParams($params);
+  }
+  return $params;
+}
+
+function apachesolr_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 apachesolr_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 = apachesolr_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 == 'ts_' || $prefix == 'tm_') {
+          // 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 = apachesolr_environment_variable_get($env_id, 'apachesolr_search_date_boost', '0:0');
+  $comment_settings = apachesolr_environment_variable_get($env_id, 'apachesolr_search_comment_boost', '0:0');
+  $changed_settings = apachesolr_environment_variable_get($env_id, 'apachesolr_search_changed_boost', '0:0');
+  $sticky_boost = apachesolr_environment_variable_get($env_id, 'apachesolr_search_sticky_boost', '0');
+  $promote_boost = apachesolr_environment_variable_get($env_id, 'apachesolr_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 = apachesolr_environment_variable_get($env_id, 'apachesolr_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 apachesolr_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) {
+    $hl_fl = array('content', 'ts_comments');
+  }
+  $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('apachesolr_search_result', $doc, $extra, $query);
+
+      // Start with an empty snippets array.
+      $snippets = array();
+
+      // 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 = 'apachesolr_search_snippets__' . $doc->entity_type;
+      $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(
+        // link is a required field, so handle it centrally.
+        'link' => $path,
+        // template_preprocess_search_result() runs check_plain() on the title
+        // again.  Decode to correct the display.
+        'title' => htmlspecialchars_decode($doc->label, 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,
+        'entity_type' => $doc->entity_type,
+        'bundle' => $bundle,
+      );
+
+      // Call entity-type-specific callbacks for extra handling.
+      $function = apachesolr_entity_get_callback($doc->entity_type, 'result callback', $bundle);
+      if (is_callable($function)) {
+        $function($doc, $result, $extra);
+      }
+
+      $result['extra'] = $extra;
+
+      $results[] = $result;
+    }
+  }
+  // Hook to allow modifications of the retrieved results
+  foreach (module_implements('apachesolr_process_results') as $module) {
+    $function = $module . '_apachesolr_process_results';
+    $function($results, $query);
+  }
+  return $results;
+}
+
+/**
+ * Retrieve all of the suggestions that were given after a certain search
+ * @return array()
+ */
+function apachesolr_search_get_search_suggestions($env_id) {
+  $suggestions_output = array();
+  if (apachesolr_has_searched($env_id)) {
+    $query = apachesolr_current_query($env_id);
+    $keyword = $query->getParam('q');
+    $searcher = $query->getSearcher();
+    $response = apachesolr_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_apachesolr_entity_info_alter().
+ */
+function apachesolr_search_apachesolr_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'] = 'apachesolr_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 apachesolr_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 apachesolr_search_page_exists($search_page_id) {
+  return db_query('SELECT 1 FROM {apachesolr_search_page} WHERE page_id = :page_id', array(':page_id' => $search_page_id))->fetchField();
+}
+
+/**
+ * Template preprocess for apachesolr search results.
+ *
+ * We need to add additional entity/bundle-based templates
+ */
+function apachesolr_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'] == 'apachesolr_search') {
+    $result = $variables['result'];
+    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 apachesolr_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'] == 'apachesolr_search') {
+    // Fetch our current query
+    if (!empty($variables['search_page']['env_id'])) {
+      $env_id = $variables['search_page']['env_id'];
+    }
+    $query = apachesolr_current_query($env_id);
+
+    if ($query) {
+      $variables['query'] = $query;
+      $variables['response'] = apachesolr_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_apachesolr_environment_delete().
+ */
+function apachesolr_search_apachesolr_environment_delete($server) {
+  db_update('apachesolr_search_page')
+    ->fields(array(
+      'env_id' => '',
+    ))
+    ->condition('env_id', $server['env_id'])
+    ->execute();
+  apachesolr_environment_variable_del($server['env_id'], 'apachesolr_search_show_facets');
+  apachesolr_environment_variable_del($server['env_id'], 'apachesolr_search_facet_pages');
+  menu_rebuild();
+}
+
+function apachesolr_search_form_search_block_form_alter(&$form, $form_state) {
+  if (variable_get('search_default_module') == 'apachesolr_search') {
+    $form['#submit'][] = 'apachesolr_search_form_search_submit';
+  }
+}
+
+/**
+ * Default theme function for spelling suggestions.
+ */
+function theme_apachesolr_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 apachesolr_search_form_search_form_alter()
+ */
+function apachesolr_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']);
+  }
+}
+
+/**
+ * Implements hook_form_[form_id]_alter().
+ *
+ * Rebuild (empty) the spellcheck dictionary when the index is deleted..
+ */
+function apachesolr_search_form_apachesolr_delete_index_confirm_alter(&$form, $form_state) {
+  $form['submit']['#submit'][] = 'apachesolr_search_build_spellcheck';
+}
+
+/**
+ * submit function for the delete_index form.
+ *
+ */
+function apachesolr_search_build_spellcheck($form, &$form_state) {
+  try {
+    $solr = apachesolr_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 apachesolr_search_form_facetapi_realm_settings_form_alter(&$form, &$form_state) {
+  if ('apachesolr' == $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 = apachesolr_environment_variable_get($env_id, 'apachesolr_search_show_facets', 0);
+    $facet_pages = apachesolr_environment_variable_get($env_id, 'apachesolr_search_facet_pages', '');
+
+    $form['#facetapi']['env_id'] = $env_id;
+
+    $form['apachesolr_search_show_facets'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show facets on non-search pages.'),
+      '#default_value' => $show_facets,
+      '#weight' => '-10',
+    );
+
+    $form['apachesolr_search_facet_pages'] = array(
+      '#title' => t('Non-search paths'),
+      '#type' => 'textarea',
+      '#default_value' => $facet_pages,
+      '#weight' => '-10',
+      '#dependency' => array(
+        'edit-apachesolr-search-show-facets' => array(1),
+      ),
+    );
+
+    $form['#submit'][] = 'apachesolr_search_facetapi_realm_settings_form_submit';
+  }
+}
+
+/**
+ * Form submission handler for facetapi_realm_settings_form().
+ */
+function apachesolr_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']['apachesolr_search_show_facets'];
+  $facet_pages = $form_state['values']['apachesolr_search_facet_pages'];
+  if ($show_facets) {
+    apachesolr_environment_variable_set($env_id, 'apachesolr_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 apachesolr_search_init().
+    apachesolr_environment_variable_del($env_id, 'apachesolr_search_show_facets');
+  }
+  apachesolr_environment_variable_set($env_id, 'apachesolr_search_facet_pages', $facet_pages);
+}
+
+/**
+ * Implements hook_theme().
+ */
+function apachesolr_search_theme() {
+  return array(
+    /**
+     * Shows the facets in blocks in the search result area
+     */
+    'apachesolr_search_browse_blocks' => array(
+      'render element' => 'content',
+    ),
+    /**
+     * Shows the search snippet
+     */
+    'apachesolr_search_snippets' => array(
+      'variables' => array('doc' => NULL, 'snippets' => array()),
+    ),
+    /**
+     * Shows a message when the search does not return any result
+     */
+    'apachesolr_search_noresults' => array(
+      'variables' => array(),
+    ),
+    /**
+     * Shows a list of suggestions
+     */
+    'apachesolr_search_suggestions' => array(
+      'variables' => array('links' => NULL),
+    ),
+    /**
+     * Shows a list of results (docs) in content recommendation block
+     */
+    'apachesolr_search_mlt_recommendation_block' => array(
+      'variables' => array('docs' => NULL, 'delta' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implements hook_theme_registry_alter().
+ */
+function apachesolr_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_apachesolr_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_apachesolr_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>');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/apachesolr_search.pages.inc	Wed Jul 31 13:49:13 2013 +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 apachesolr_search_custom_page($page_id, $keys = '', $path_replacer = NULL) {
+  $search_page = apachesolr_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']['apachesolr_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 = apachesolr_search_conditions_default($search_page);
+  // Process our keys so they are clean
+  $keys = rawurldecode($keys);
+  // Retrieve the results of the search
+  $results = apachesolr_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']['apachesolr_search_search_box'])) {
+    // Adds the search form to the page.
+    $build['search_form'] = drupal_get_form('apachesolr_search_custom_page_search_form', $search_page, $keys);
+  }
+  // Build our page and allow modification.
+  $build_results = apachesolr_search_search_page_custom($results, $search_page, $build);
+  return $build_results;
+}
+
+/**
+ * Search for placed on user defined search pages.
+ */
+function apachesolr_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 (apachesolr_has_searched($search_page['env_id'])) {
+    $query = apachesolr_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 apachesolr_search_custom_page_search_form submissions.
+ */
+function apachesolr_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']['apachesolr_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/sites/all/modules/custom/solrconnect/drush/apachesolr.drush.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,642 @@
+<?php
+
+/**
+ * @file
+ *   drush integration for apachesolr.
+ */
+
+/**
+ * Implements hook_drush_command().
+ *
+ * @return array
+ *   An associative array describing your command(s).
+ */
+function apachesolr_drush_command() {
+  $items = array();
+
+  $items['solr-delete-index'] = array(
+    'callback' => 'apachesolr_drush_solr_delete_index',
+    'description' => dt('Deletes the content from the index. Can take content types as parameters.'),
+    'arguments' => array(
+      'types' => dt('Optional. A space delimited list of content types to be deleted from the index.'),
+    ),
+    'options' => array(
+      'environment-id' => 'The environment ID',
+    ),
+    'examples' => array(
+      'drush solr-delete-index node' => 'Delete all node content from the index.',
+      'drush solr-delete-index node:article' => 'Delete all content of the article content type from the index.',
+      'drush solr-delete-index node:article node:blog' => 'Delete all content of the article and blog content types from the index.',
+    ),
+  );
+  $items['solr-mark-all'] = array(
+    'callback' => 'apachesolr_drush_solr_mark_for_reindex',
+    'description' => dt('Marks content for reindexing. Can take content types as parameters.'),
+    'arguments' => array(
+      'types' => dt('Optional. A space delimited list of content types to be marked for reindexing.'),
+    ),
+    'options' => array(
+      'environment-id' => 'The environment ID',
+    ),
+  );
+  $items['solr-index'] = array(
+    'callback' => 'apachesolr_drush_solr_index',
+    'description' => dt('Reindexes content marked for (re)indexing.'),
+    'options' => array(
+      'environment-id' => 'The environment ID',
+      'limit' => 'The total number of documents to index',
+    ),
+  );
+  $items['solr-get-last-indexed'] = array(
+    'callback' => 'apachesolr_drush_solr_get_last_indexed',
+    'description' => dt('Get the ID of the last document indexed.'),
+    'arguments' => array(
+      'environment-id' => dt('Optional. The environment ID, uses the default if not passed.'),
+      'entity-type' => dt('Optional. The machine name of the entity, defaults to "node".'),
+    ),
+  );
+  $items['solr-get-next-indexed'] = array(
+    'callback' => 'apachesolr_drush_solr_get_next_indexed',
+    'description' => dt('Get the ID of the next document to be indexed.'),
+    'arguments' => array(
+      'environment-id' => dt('Optional. The environment ID, uses the default if not passed.'),
+      'entity-type' => dt('Optional. The machine name of the entity, defaults to "node".'),
+    ),
+  );
+  $items['solr-search'] = array(
+    'callback' => 'apachesolr_drush_solr_search',
+    'description' => dt('Search the site for keywords using Apache Solr'),
+    'arguments' => array(
+      'keywords' => dt('One or more keywords, separated by spaces.'),
+    ),
+  );
+  $items['solr-get-env-id'] = array(
+    'callback' => 'apachesolr_drush_solr_get_env_id',
+    'description' => dt('Get the default Apache Solr environment ID, or all IDs and names'),
+    'arguments' => array(),
+    'options' => array(
+      'all' => array(
+         'description' => 'List all environment IDs',
+      ),
+    ),
+  );
+  $items['solr-get-env-name'] = array(
+    'callback' => 'apachesolr_drush_solr_get_env_name',
+    'description' => dt('Get the Apache Solr environment name.'),
+    'options' => array(
+      'id' => array(
+         'description' => 'Apache Solr environment ID to use (uses the default environment if not specified)',
+      ),
+    ),
+  );
+  $items['solr-get-env-url'] = array(
+    'callback' => 'apachesolr_drush_solr_get_env_url',
+    'description' => dt('Get the Apache Solr environment url.'),
+    'options' => array(
+      'id' => array(
+         'description' => 'Apache Solr environment ID to use (uses the default environment if not specified)',
+      ),
+    ),
+  );
+  $items['solr-set-env-url'] = array(
+    'callback' => 'apachesolr_drush_solr_set_env_url',
+    'description' => dt('Set the url for an Apache Solr environment.'),
+    'arguments' => array(
+      'url' => dt('Apache Solr server url string.'),
+    ),
+    'required-arguments' => TRUE,
+    'options' => array(
+      'id' => array(
+         'description' => 'Apache Solr environment ID to use (uses the default environment if not specified)',
+      ),
+    ),
+  );
+  $items['solr-variable-get'] = array(
+    'description' => 'Get a list of Apache Solr environment variable names and values.',
+    'arguments' => array(
+      'name' => 'A string to filter the variables by. Variables that have any part of their name matching the string will b listed.',
+    ),
+    'examples' => array(
+      'drush solr-vget' => 'List all variables and values.',
+      'drush solr-vget user' => 'List all variables containing the string "user".',
+    ),
+    'options' => array(
+      'id' => 'Apache Solr environment ID to use (uses the default environment if not specified)',
+      'format' => 'Format to output the object. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.',
+      'pipe' => 'A synonym for --format=export. Useful for pasting into code.',
+    ),
+    'aliases' => array('solr-vget'),
+  );
+  $items['solr-variable-set'] = array(
+    'description' => "Set an Apache Solr environment variable.",
+    'arguments' => array(
+      'name' => 'The name of a variable or the first few letters of its name.',
+      'value' => 'The value to assign to the variable. Use \'-\' to read the object from STDIN.',
+    ),
+    'required-arguments' => TRUE,
+    'options' => array(
+      'id' => 'Apache Solr environment ID to use (uses the default environment if not specified)',
+      'yes' => 'Skip confirmation if only one variable name matches.',
+      'always-set' => 'Always skip confirmation.',
+      'format' => 'Format to parse the object. Use "auto" to detect format from value (default), "string", "integer" or "boolean" for corresponding primitive type, and "json" for JSON.',
+    ),
+    'examples' => array(
+      'drush solr-vset --yes apachesolr_read_only 1' => 'Set the apachesolr_read_only variable to 1. Skip confirmation if variable already exists.',
+      'drush solr-vset pr TRUE' => 'Choose from a list of variables beginning with "pr" to set to (bool)true.',
+      'php -r "print json_encode(array(\'drupal\', \'simpletest\'));"  | drush solr-vset --format=json project_dependency_excluded_dependencies -'=> 'Set a variable to a complex value (e.g. array)',
+    ),
+    'aliases' => array('solr-vset'),
+  );
+  $items['solr-variable-delete'] = array(
+    'description' => "Delete an Apache Solr environment variable.",
+    'arguments' => array(
+      'name' => 'The name of a variable or the first few letters of its name.',
+    ),
+    'required-arguments' => TRUE,
+    'options' => array(
+      'id' => array(
+         'description' => 'Apaches Solr environment ID to use (uses the default environment if not specified)',
+      ),
+      'yes' => 'Skip confirmation if only one variable name matches.',
+      'exact' => 'Only delete the one variable that exactly matches the specified name.',
+    ),
+    'examples' => array(
+      'drush solr-vdel apachesolr_read_only --id=solr2' => 'Delete the apachesolr_read_only variable for the solr2 environment.',
+      'drush solr-vdel apa' => 'Choose from a list of variables beginning with "u" to delete.',
+      'drush solr-vdel -y --exact apachesolr_read_only' => 'Delete variable, skipping confirmation.',
+    ),
+    'aliases' => array('solr-vdel'),
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_drush_help().
+ *
+ * This function is called whenever a drush user calls
+ * 'drush help <name-of-your-command>'
+ *
+ * @param string $section
+ *   A string with the help section (prepend with 'drush:')
+ *
+ * @return string
+ *   A string with the help text for your command.
+ */
+function apachesolr_drush_help($section) {
+  switch ($section) {
+    case 'drush:solr-delete-index':
+      return dt("Used without parameters, this command deletes the entire Solr index.
+        Used with parameters for content type, it deletes just the content types that are specified.
+        After the index has been deleted, all content will be indexed again on future cron runs.");
+    case 'drush:solr-mark-all':
+      return dt("Used without parameters, this command marks all of the content in the Solr index for
+        reindexing. Used with parameters for content type, it marks just the content types that are specified.
+        Reindexing is different than deleting as the content is still searchable while it is in queue to be reindexed.
+        Reindexing is done on future cron runs.");
+    case 'drush:solr-index':
+      return dt("Reindexes content marked for (re)indexing. If you want to reindex all content or content
+         of a specific type, use solr-reindex first to mark that content.");
+    case 'drush:solr-search':
+      return dt('Executes a search against the site\'s Apache Solr search index and returns the results.');
+    case 'error:APACHESOLR_ENV_ID_ERROR':
+      return dt('Not a valid environment ID.');
+  }
+  return '';
+}
+
+/**
+ * Selectively delete content from the apachesolr index.
+ *
+ * Each argument is a filter on what to delete from the index.
+ * They are of the form entity (to delete all content of that
+ * entity) or entity:bundle (to delete all content of that
+ * bundle).
+ */
+function apachesolr_drush_solr_delete_index() {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $args = func_get_args();
+  $env_id =  drush_get_option('environment-id');
+  if (empty($env_id)) {
+    $env_id = apachesolr_default_environment();
+  }
+
+  if (count($args) > 0) {
+    foreach ($args as $type) {
+      $parts = explode(':', $type);
+      if (count($parts) === 1) {
+        apachesolr_index_delete_index($env_id, $type);
+      }
+      elseif (count($parts) == 2) {
+        apachesolr_index_delete_index($env_id, $parts[0], $parts[1]);
+      }
+      else {
+        drush_set_error('The syntax for each type is either entity or entity:bundle');
+      }
+    }
+  }
+  else {
+    apachesolr_index_delete_index($env_id);
+  }
+
+  drush_print(t('Deleted the Solr index'));
+}
+
+/**
+ * Mark all of a specific environment id for reindexing
+ */
+function apachesolr_drush_solr_mark_for_reindex() {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $args = func_get_args();
+  $env_id =  drush_get_option('environment-id');
+  if (empty($env_id)) {
+    $env_id = apachesolr_default_environment();
+  }
+  if (count($args) > 0) {
+    foreach ($args as $type) {
+      apachesolr_index_mark_for_reindex($env_id, $type);
+    }
+  }
+  else {
+    apachesolr_index_mark_for_reindex($env_id);
+  }
+  drush_print(t('Marked content for reindexing'));
+}
+
+/**
+ * Index all the items in the queue using a batch command
+ */
+function apachesolr_drush_solr_index() {
+  module_load_include('inc', 'apachesolr', 'apachesolr.admin');
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  $env_id =  drush_get_option('environment-id');
+  if (empty($env_id)) {
+    $env_id = apachesolr_default_environment();
+  }
+  $total_limit = intval(drush_get_option('limit'));
+  apachesolr_index_batch_index_remaining($env_id, $total_limit);
+  drush_backend_batch_process();
+}
+
+/**
+ * Get the last indexed document
+ *
+ * @param string $env_id
+ * @param string $entity_type
+ */
+function apachesolr_drush_solr_get_last_indexed($env_id = NULL, $entity_type = 'node') {
+  if (NULL === $env_id) {
+    $env_id = apachesolr_default_environment();
+  }
+  $return = apachesolr_get_last_index_position($env_id, $entity_type);
+  drush_print($return['last_entity_id']);
+}
+
+function apachesolr_drush_solr_get_next_indexed($env_id = NULL, $entity_type = 'node') {
+  module_load_include('inc', 'apachesolr', 'apachesolr.index');
+  if (NULL === $env_id) {
+    $env_id = apachesolr_default_environment();
+  }
+  $return = apachesolr_index_get_entities_to_index($env_id, $entity_type, 1);
+  $output = (isset($return[0]->entity_id)) ? $return[0]->entity_id : '0';
+  drush_print($output);
+}
+
+/**
+ * Search the solr index using Drush
+ */
+function apachesolr_drush_solr_search() {
+  $args = func_get_args();
+  $keys = implode(' ', $args);
+  foreach (apachesolr_search_search_execute($keys) as $result) {
+    $output = $result['fields']['path'];
+    if(isset($result['user']) && isset($result['node']->is_uid)) {
+      $output .= ' ' . dt('by @name (user/@uid)', array('@name' => strip_tags($result['user']), '@uid' => $result['node']->is_uid));
+    }
+    $output .= "\n";
+    $output .= dt('title: ') . $result['title'] . "\n";
+    $output .= trim(preg_replace('/\s+/', ' ', strip_tags($result['snippet']))) . "\n\n";
+    drush_print($output);
+  }
+}
+
+/**
+ * Get all the environments (using option all) or get the default environment id
+ */
+function apachesolr_drush_solr_get_env_id() {
+  $all = drush_get_option('all');
+
+  if ($all) {
+    foreach (apachesolr_load_all_environments() as $id => $env) {
+      drush_print(drush_format($env['name'], $id));
+    }
+  }
+  else {
+    $solr_env_id = apachesolr_default_environment();
+    drush_print($solr_env_id);
+  }
+}
+
+/**
+ * Get the environment name based on the environment ID
+ *
+ * @print The environment name
+ *
+ * @return mixed APACHESOLR_ENV_ID_ERROR
+ *   Only return error if the environment can't be found
+ */
+function apachesolr_drush_solr_get_env_name() {
+  $env_id = drush_get_option('id', apachesolr_default_environment());
+  try {
+    $environment = _apachesolr_drush_environment_load_and_validate($env_id);
+  }
+  catch (Exception $e) {
+    return drush_set_error('APACHESOLR_ENV_ID_ERROR', $e->getMessage());
+  }
+  drush_print($environment['name']);
+}
+
+/**
+ * Get the environment url based on the environment ID
+ *
+ * @print The environment url
+ *
+ * @return mixed APACHESOLR_ENV_ID_ERROR
+ *   Only return error if the environment can't be found
+ */
+function apachesolr_drush_solr_get_env_url() {
+  $env_id = drush_get_option('id', apachesolr_default_environment());
+  try {
+    $environment = _apachesolr_drush_environment_load_and_validate($env_id);
+  }
+  catch (Exception $e) {
+    return drush_set_error('APACHESOLR_ENV_ID_ERROR', $e->getMessage());
+  }
+  drush_print($environment['url']);
+}
+
+/**
+ * Set the environment url based on the environment ID
+ *
+ * @param $url
+ *
+ * @return mixed APACHESOLR_ENV_ID_ERROR
+ *   Only return error if the environment can't be found
+ */
+function apachesolr_drush_solr_set_env_url($url) {
+  $env_id = drush_get_option('id', apachesolr_default_environment());
+  try {
+    $environment = _apachesolr_drush_environment_load_and_validate($env_id);
+  }
+  catch (Exception $e) {
+    return drush_set_error('APACHESOLR_ENV_ID_ERROR', $e->getMessage());
+  }
+  $environment['url'] = $url;
+  apachesolr_environment_save($environment);
+}
+
+/**
+ * Command callback.
+ *
+ * List your site's variables.
+ * much of it copied from drush core
+ *
+ * @param string $arg_name
+ *
+ * @return array|mixed Could be the variable or a drush error
+ */
+function drush_apachesolr_solr_variable_get($arg_name = NULL) {
+  $output = NULL;
+
+  $found = array();
+
+  $env_id = drush_get_option('id', apachesolr_default_environment());
+  try {
+    $found = _apachesolr_drush_variable_like($env_id, $arg_name);
+  }
+  catch (Exception $e) {
+    return drush_set_error('APACHESOLR_ENV_ID_ERROR', $e->getMessage());
+  }
+
+  foreach ($found as $name => $value) {
+    drush_print_pipe(drush_format($value, $name, 'export'));
+    drush_print(drush_format($value, $name));
+  }
+
+  if (empty($found)) {
+    return drush_set_error('DRUSH_VARIABLE_ERROR', 'No matching variable found.');
+  }
+  else {
+    return $found;
+  }
+}
+
+/**
+ * Command callback.
+ * Set a variable.
+ *
+ * @param string $arg_name
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+function drush_apachesolr_solr_variable_set($arg_name, $value) {
+  $args = func_get_args();
+
+  if (!isset($value)) {
+    return drush_set_error('DRUSH_VARIABLE_ERROR', dt('No value specified.'));
+  }
+
+  $env_id = drush_get_option('id', apachesolr_default_environment());
+  try {
+    $found = _apachesolr_drush_variable_like($env_id, $arg_name, TRUE);
+  }
+  catch (Exception $e) {
+    return drush_set_error('APACHESOLR_ENV_ID_ERROR', $e->getMessage());
+  }
+
+  $options[] = "$arg_name ". dt('(new variable)');
+  $match = isset($found[$arg_name]);
+  if (!$match && $found) {
+    $options = array_merge($options, array_keys($found));
+  }
+
+  if ($value == '-') {
+    $value = stream_get_contents(STDIN);
+  }
+
+  // If the value is a string (usual case, unless we are called from code),
+  // then format the input
+  if (is_string($value)) {
+    $value = _apachesolr_drush_variable_format($value, drush_get_option('format', 'auto'));
+  }
+
+  // Format the output for display
+  if (is_array($value)) {
+    $display = "\n" . var_export($value, TRUE);
+  }
+  elseif (is_integer($value)) {
+    $display = $value;
+  }
+  elseif (is_bool($value)) {
+    $display = $value ? "TRUE" : "FALSE";
+  }
+  else {
+    $display = '"' . $value . '"';
+  }
+
+  $name = NULL;
+  if (drush_get_option('always-set', FALSE) || $match) {
+    $name = $arg_name;
+  }
+  else {
+    $choice = drush_choice($options, 'Enter a number to choose which variable to set.');
+    if ($choice !== FALSE) {
+      $name = ($choice == 0) ? $arg_name : $options[$choice];
+    }
+  }
+  if ($name) {
+    drush_op('apachesolr_environment_variable_set', $env_id, $name, $value);
+    drush_log(dt('!name was set to !value', array('!name' => $name, '!value' => $display)), 'success');
+  }
+}
+
+/**
+ *
+ * Format a specific variable
+ *
+ * @param $value
+ * @param $format
+ *
+ * @return bool|int|string
+ */
+function _apachesolr_drush_variable_format($value, $format) {
+  if ($format == 'auto') {
+    if (is_numeric($value)) {
+      $format = 'integer';
+    }
+    elseif (($value == 'TRUE') || ($value == 'FALSE')) {
+      $format = 'bool';
+    }
+  }
+
+  // Now, we parse the object.
+  switch ($format) {
+    case 'integer':
+      $value = (integer)$value;
+      break;
+
+    case 'bool':
+    case 'boolean':
+      if ($value == 'TRUE') {
+        $value = TRUE;
+      }
+      elseif ($value == 'FALSE') {
+        $value = FALSE;
+      }
+      else {
+        $value = (bool)$value;
+      }
+      break;
+
+    case 'json':
+      $value = drush_json_decode($value);
+      break;
+  }
+  return $value;
+}
+
+/**
+ * Command callback.
+ * Delete a variable.
+ * @param $arg_name
+ *
+ * @return string
+ */
+function drush_apachesolr_solr_variable_delete($arg_name) {
+
+  $env_id = drush_get_option('id', apachesolr_default_environment());
+  // Look for similar variable names.
+  try {
+    $found = _apachesolr_drush_variable_like($env_id, $arg_name, TRUE);
+  }
+  catch (Exception $e) {
+    return drush_set_error('APACHESOLR_ENV_ID_ERROR', $e->getMessage());
+  }
+  drush_log(dt('Using environment ID "!env_id"', array('!env_id' => $env_id)), 'success');
+  $options = array_keys($found);
+
+  if (drush_get_option('exact', FALSE)) {
+    $options = isset($found[$arg_name]) ? array($arg_name) : array();
+  }
+
+  if (empty($options)) {
+    drush_print(dt('!name not found.', array('!name' => $arg_name)));
+    return '';
+  }
+
+  $name = NULL;
+  if ((count($options) == 1) && drush_get_context('DRUSH_AFFIRMATIVE')) {
+    $name = $arg_name;
+  }
+  else {
+    $choice = drush_choice($options, 'Enter a number to choose which variable to delete.');
+    if ($choice !== FALSE) {
+      $name = $options[$choice];
+    }
+  }
+  if ($name) {
+    drush_op('apachesolr_environment_variable_del', $env_id, $name);
+    drush_log(dt('!choice was deleted.', array('!choice' => $name)), 'success');
+  }
+}
+
+/**
+ * Load an environment from an id and validate the result.
+ *
+ * @param string $env_id
+ *
+ * @return array $environment
+ * @throws Exception
+ */
+function _apachesolr_drush_environment_load_and_validate($env_id) {
+  $environment = apachesolr_environment_load($env_id);
+  if (!$environment) {
+    throw new Exception(dt('!env_id is not a valid environment ID.', array('!env_id' => $env_id)));
+  }
+  drush_log(dt('Using environment ID: "!env_id"', array('!env_id' => $env_id)), 'success');
+  return $environment;
+}
+
+/**
+ * Search for similar variable names.
+ *
+ * @param string $env_id
+ * @param string $arg
+ * @param bool|string $starts_with
+ *
+ * @throws Exception
+ *
+ * @return array $variable
+ *   Only return it if found
+ */
+function _apachesolr_drush_variable_like($env_id, $arg = NULL, $starts_with = FALSE) {
+  $found = array();
+  $environment = _apachesolr_drush_environment_load_and_validate($env_id);
+  if (!isset($arg)) {
+    return $environment['conf'];
+  }
+  if ($starts_with) {
+    $pattern = "/^{$arg}/i";
+  }
+  else {
+    $pattern = "/{$arg}/i";
+  }
+  foreach ($environment['conf'] as $name => $value) {
+    // Find all variable that start with $arg.
+    if (preg_match($pattern, $name)) {
+      $found[$name] = $value;
+    }
+  }
+  return $found;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/plugins/facetapi/adapter.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,151 @@
+<?php
+
+/**
+ * @file
+ * Classes used by the Facet API module.
+ */
+
+/**
+ * Facet API adapter for the Apache Solr Search Integration module.
+ */
+class ApacheSolrFacetapiAdapter 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/apachesolr/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 = apachesolr_has_searched($env_id);
+    // Secondary check - do we have results for this searcher?
+    $this_has_searched = $this_has_searched && apachesolr_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;
+    if ($realm_name == 'block') {
+      $env_id = $this->info['instance'];
+      $flag = apachesolr_suppress_blocks($env_id);
+    }
+    return $flag || !$this->searchExecuted();
+  }
+
+  /**
+   * Returns the search keys.
+   *
+   * @return string
+   */
+  public function getSearchKeys() {
+    if (NULL === $this->keys) {
+      $env_id = $this->info['instance'];
+      if ($query = apachesolr_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 = apachesolr_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 = apachesolr_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'][] = 'apachesolr_facet_form_validate';
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_date.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,185 @@
+<?php
+
+/**
+ * @file
+ * Date query type plugin for the Apache Solr adapter.
+ */
+
+/**
+ * Plugin for "date" query types.
+ */
+class ApacheSolrFacetapiDate 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 = apachesolr_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/sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_geo.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Plugin for "apachesolr_geo" query types.
+ */
+class ApacheSolrFacetapiGeo 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/sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_numeric_range.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Numeric range query type plugin for the Apache Solr adapter.
+ */
+
+/**
+ * Plugin for "numeric_range" query types.
+ */
+class ApacheSolrFacetapiNumericRange 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 = apachesolr_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_apachesolr');
+    $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 = apachesolr_get_solr();
+
+      // We possibly need some caching for this query
+      $query_stats = apachesolr_drupal_query('apachesolr_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_apachesolr');
+      }
+    }
+    else {
+      // Set our statistics from the cache
+      $stats_minmax = $cache->data;
+    }
+
+    if ($response = apachesolr_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/sites/all/modules/custom/solrconnect/plugins/facetapi/query_type_term.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Term query type plugin for the Apache Solr adapter.
+ */
+
+/**
+ * Plugin for "term" query types.
+ */
+class ApacheSolrFacetapiTerm 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 = apachesolr_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/sites/all/modules/custom/solrconnect/solr-conf/solr-1.4/protwords.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,8 @@
+#-----------------------------------------------------------------------
+# This file blocks words from being operated on by the stemmer and word delimiter.
+&amp;
+&lt;
+&gt;
+&#039;
+&quot;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-1.4/schema.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,497 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ 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.
+
+ For more information, on how to customize this file, please see
+ http://wiki.apache.org/solr/SchemaXml
+-->
+
+<schema name="drupal-3.0-0-solr1.4" version="1.2">
+    <!-- attribute "name" is the name of this schema and is only used for display purposes.
+         Applications should change this to reflect the nature of the search collection.
+         version="1.2" 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.
+       -->
+  <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 the
+       org.apache.solr.analysis package.
+    -->
+
+    <!-- The StrField type is not analyzed, but indexed/stored verbatim.
+       - StrField and TextField support an optional compressThreshold which
+       limits compression (if enabled in the derived fields) to values which
+       exceed a certain size (in characters).
+    -->
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- boolean type: "true" or "false" -->
+    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
+    <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
+    <fieldtype name="binary" class="solr.BinaryField"/>
+
+    <!-- The optional sortMissingLast and sortMissingFirst attributes are
+         currently supported on types that are sorted internally as strings.
+       - 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.
+    -->
+
+    <!-- numeric field types that can be sorted, but are not optimized for range queries -->
+    <fieldType name="integer" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+
+    <!--
+      Note:
+      These should only be used for compatibility with existing indexes (created with older Solr versions)
+      or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
+
+      Numeric field types that manipulate the value into
+      a string value that isn't human-readable in its internal form,
+      but with a lexicographic ordering the same as the numeric ordering,
+      so that range queries work correctly.
+    -->
+    <fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
+
+    <!--
+     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" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" 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.
+      -->
+    <fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- A Trie based date field for faster date range queries and date faceting. -->
+    <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
+
+    <!-- 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
+    <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" omitNorms="true" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A text field that uses WordDelimiterFilter to enable splitting and matching of
+        words on case-change, alpha numeric boundaries, and non-alphanumeric chars,
+        so that a query of "wifi" or "wi fi" could match a document containing "Wi-Fi".
+        Synonyms and stopwords are customized by external files, and stemming is enabled.
+        Duplicate tokens at the same position (which may result from Stemmed Synonyms or
+        WordDelim parts) are removed.
+        -->
+    <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <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="stopwords.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="1"
+                catenateNumbers="1"
+                catenateAll="0"
+                splitOnCaseChange="1"
+                preserveOriginal="1"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <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.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="0"
+                catenateNumbers="0"
+                catenateAll="0"
+                splitOnCaseChange="1"
+                preserveOriginal="1"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- An unstemmed text field - good if one does not know the language of the field -->
+    <fieldType name="text_und" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="1"
+                catenateNumbers="1"
+                catenateAll="0"
+                splitOnCaseChange="0"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </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="stopwords.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="0"
+                catenateNumbers="0"
+                catenateAll="0"
+                splitOnCaseChange="0"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Edge N gram type - for example for matching against queries with results
+        KeywordTokenizer leaves input string intact as a single term.
+        see: http://www.lucidimagination.com/blog/2009/09/08/auto-suggest-from-popular-queries-using-edgengrams/
+   -->
+    <fieldType name="edge_n2_kw_text" class="solr.TextField" omitNorms="true" positionIncrementGap="100">
+     <analyzer type="index">
+       <tokenizer class="solr.KeywordTokenizerFactory"/>
+       <filter class="solr.LowerCaseFilterFactory"/>
+       <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="25" />
+     </analyzer>
+     <analyzer type="query">
+       <tokenizer class="solr.KeywordTokenizerFactory"/>
+       <filter class="solr.LowerCaseFilterFactory"/>
+     </analyzer>
+    </fieldType>
+   <!--  Setup simple analysis for spell checking -->
+
+   <fieldType name="textSpell" class="solr.TextField" positionIncrementGap="100">
+     <analyzer>
+       <tokenizer class="solr.StandardTokenizerFactory" />
+       <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
+       <filter class="solr.LengthFilterFactory" min="4" max="20" />
+       <filter class="solr.LowerCaseFilterFactory" />
+       <filter class="solr.RemoveDuplicatesTokenFilterFactory" />
+     </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="sortString" 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 refrences to portions of the orriginal
+             string matched by the pattern.
+
+             See the Java Regular Expression documentation for more
+             infomation on pattern and replacement string syntax.
+
+             http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
+
+        <filter class="solr.PatternReplaceFilterFactory"
+                pattern="(^\p{Punct}+)" replacement="" replace="all"
+        />
+        -->
+      </analyzer>
+    </fieldType>
+
+    <!-- A random sort type -->
+    <fieldType name="rand" class="solr.RandomSortField" indexed="true" />
+
+    <!-- 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" class="solr.StrField" />
+
+ </types>
+
+
+ <fields>
+   <!-- Valid attributes for fields:
+     name: mandatory - the name for the field
+     type: mandatory - the name of a previously defined type from the <types> section
+     indexed: true if this field should be indexed (searchable or sortable)
+     stored: true if this field should be retrievable
+     compressed: [false] if this field should be stored using gzip compression
+       (this will only apply if the field type is compressable; among
+       the standard field types, only TextField and StrField are)
+     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.
+   -->
+
+<!-- The document id is usually derived from a site-spcific key (hash) and the entity type and ID like:
+     $document->id = $hash . '/node/' . $node->nid; -->
+
+   <field name="id" type="string" indexed="true" stored="true" required="true" />
+   <!-- entity_id is the numeric object ID, e.g. Node ID, File ID -->
+   <field name="entity_id"  type="long" indexed="true" stored="true" />
+   <!-- entity_type is 'node', 'file', 'user', or some other Drupal object type -->
+   <field name="entity_type" type="string" indexed="true" stored="true" required="true" />
+   <!-- bundle is a node type, or as appropriate for other entity types -->
+   <field name="bundle" type="string" indexed="true" stored="true"/>
+   <field name="bundle_name" type="string" indexed="true" stored="true"/>
+
+   <field name="site" type="string" indexed="true" stored="true"/>
+   <field name="hash" type="string" indexed="true" stored="true"/>
+   <field name="url" type="string" indexed="true" stored="true"/>
+   <!-- label is the default field for a human-readable string for this entity (e.g. the title of a node) -->
+   <field name="label" type="text" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+   <!-- The string version of the title is used for sorting -->
+   <copyField source="label" dest="sort_label"/>
+   <!-- content is the default field for full text search - dump crap here -->
+   <field name="content" type="text" indexed="true" stored="true" termVectors="true"/>
+   <field name="teaser" type="text" indexed="false" stored="true"/>
+
+   <field name="path" type="string" indexed="true" stored="true"/>
+   <field name="path_alias" type="text" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+
+ <!-- These are the fields that correspond to a Drupal node. The beauty of having
+     Lucene store title, body, type, etc., is that we retrieve them with the search
+     result set and don't need to go to the database with a node_load. -->
+
+   <field name="tid"  type="long" indexed="true" stored="true" multiValued="true"/>
+
+   <field name="taxonomy_names" type="text" indexed="true" stored="false" termVectors="true" multiValued="true" omitNorms="true"/>
+   <!-- Copy terms to a single field that contains all taxonomy term names -->
+   <copyField source="tm_vid_*" dest="taxonomy_names"/>
+
+   <!-- Here, default is used to create a "timestamp" field indicating
+        when each document was indexed.-->
+   <field name="timestamp" type="tdate" indexed="true" stored="true" default="NOW" multiValued="false"/>
+
+	<!-- This field is used to build the spellchecker index -->
+   <field name="spell" type="textSpell" indexed="true" stored="true" multiValued="true"/>
+
+  <!-- copyField commands copy one field to another at the time a document
+        is added to the index.  It's used either to index the same field differently,
+        or to add multiple fields to the same field for easier/faster searching.  -->
+   <copyField source="label" dest="spell"/>
+   <copyField source="content" dest="spell"/>
+
+   <!-- Dynamic field definitions.  If a field name is not found, dynamicFields
+        will be used if the name matches any of the patterns.
+        RESTRICTION: the glob-like pattern in the name attribute must have
+        a "*" only at the start or the end.
+        EXAMPLE:  name="*_i" will match any field ending in _i (like myid_i, z_i)
+        Longer patterns will be matched first.  if equal size patterns
+        both match, the first appearing in the schema will be used.  -->
+
+   <!-- A set of fields to contain text extracted from HTML tag contents which we
+        can boost at query time. -->
+   <dynamicField name="tags_*" type="text"   indexed="true" stored="false" omitNorms="true"/>
+
+   <!-- For 2 and 3 letter prefix dynamic fields, the 1st letter indicates the data type and
+        the last letter is 's' for single valued, 'm' for multi-valued -->
+
+   <!-- We use long for integer since 64 bit ints are now common in PHP. -->
+   <dynamicField name="is_*"  type="long"    indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="im_*"  type="long"    indexed="true"  stored="true" multiValued="true"/>
+   <!-- List of floats can be saved in a regular float field -->
+   <dynamicField name="fs_*"  type="float"   indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="fm_*"  type="float"   indexed="true"  stored="true" multiValued="true"/>
+   <!-- List of booleans can be saved in a regular boolean field -->
+   <dynamicField name="bm_*"  type="boolean" indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="bs_*"  type="boolean" indexed="true"  stored="true" multiValued="false"/>
+   <!-- Regular text (without processing) can be stored in a string field-->
+   <dynamicField name="ss_*"  type="string"  indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="sm_*"  type="string"  indexed="true"  stored="true" multiValued="true"/>
+   <!-- Normal text fields are for full text - the relevance of a match depends on the length of the text -->
+   <dynamicField name="ts_*"  type="text"    indexed="true"  stored="true" multiValued="false" termVectors="true"/>
+   <dynamicField name="tm_*"  type="text"    indexed="true"  stored="true" multiValued="true" termVectors="true"/>
+   <!-- Unstemmed text fields for full text - the relevance of a match depends on the length of the text -->
+   <dynamicField name="tus_*" type="text_und" indexed="true"  stored="true" multiValued="false" termVectors="true"/>
+   <dynamicField name="tum_*" type="text_und" indexed="true"  stored="true" multiValued="true" termVectors="true"/>
+   <!-- These text fields omit norms - useful for extracted text like taxonomy_names -->
+   <dynamicField name="tos_*" type="text"    indexed="true"  stored="true" multiValued="false" termVectors="true" omitNorms="true"/>
+   <dynamicField name="tom_*" type="text"    indexed="true"  stored="true" multiValued="true" termVectors="true" omitNorms="true"/>
+   <!-- Special-purpose text fields -->
+   <dynamicField name="tes_*" type="edge_n2_kw_text" indexed="true" stored="true" multiValued="false" omitTermFreqAndPositions="true" />
+   <dynamicField name="tem_*" type="edge_n2_kw_text" indexed="true" stored="true" multiValued="true" omitTermFreqAndPositions="true" />
+   <dynamicField name="tws_*" type="text_ws" indexed="true" stored="true" multiValued="false"/>
+   <dynamicField name="twm_*" type="text_ws" indexed="true" stored="true" multiValued="true"/>
+
+   <!-- trie dates are preferred, so give them the 2 letter prefix -->
+   <dynamicField name="ds_*"  type="tdate"   indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="dm_*"  type="tdate"   indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="its_*" type="tlong"   indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="itm_*" type="tlong"   indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="fts_*" type="tfloat"  indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="ftm_*" type="tfloat"  indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="pts_*" type="tdouble" indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="ptm_*" type="tdouble" indexed="true"  stored="true" multiValued="true"/>
+   <!-- Binary fields can be populated using base64 encoded data. Useful e.g. for embedding
+        a small image in a search result using the data URI scheme -->
+   <dynamicField name="xs_*"  type="binary"  indexed="false" stored="true" multiValued="false"/>
+   <dynamicField name="xm_*"  type="binary"  indexed="false" stored="true" multiValued="true"/>
+   <!-- In rare cases a date rather than tdate is needed for sortMissingLast -->
+   <dynamicField name="dds_*" type="date"    indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="ddm_*" type="date"    indexed="true"  stored="true" multiValued="true"/>
+   <!-- Sortable fields, good for sortMissingLast support &
+        We use long for integer since 64 bit ints are now common in PHP. -->
+   <dynamicField name="iss_*" type="slong"   indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="ism_*" type="slong"   indexed="true"  stored="true" multiValued="true"/>
+   <!-- In rare cases a sfloat rather than tfloat is needed for sortMissingLast -->
+   <dynamicField name="fss_*" type="sfloat"  indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="fsm_*" type="sfloat"  indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="pss_*" type="sdouble" indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="psm_*" type="sdouble" indexed="true"  stored="true" multiValued="true"/>
+   <!-- In case a 32 bit int is really needed, we provide these fields. 'h' is mnemonic for 'half word', i.e. 32 bit on 64 arch -->
+   <dynamicField name="hs_*" type="integer" indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="hm_*" type="integer" indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="hss_*" type="sint"   indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="hsm_*" type="sint"   indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="hts_*" type="tint"   indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="htm_*" type="tint"   indexed="true"  stored="true" multiValued="true"/>
+
+   <!-- Unindexed string fields that can be used to store values that won't be searchable -->
+   <dynamicField name="zs_*" type="string"   indexed="false"  stored="true" multiValued="false"/>
+   <dynamicField name="zm_*" type="string"   indexed="false"  stored="true" multiValued="true"/>
+
+   <!-- Begin added fields to support modules that use features in Solr 3.4
+   but are actually using solr 1.4 -->
+   <dynamicField name="points_*" type="string" indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="pointm_*" type="string" indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="locs_*" type="string" indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="locm_*" type="string" indexed="true"  stored="true" multiValued="true"/>
+   <dynamicField name="geos_*" type="string" indexed="true"  stored="true" multiValued="false"/>
+   <dynamicField name="geom_*" type="string" indexed="true"  stored="true" multiValued="true"/>
+   <!-- End added fields for the support of modules that should use solr 3.4 -->
+
+   <!-- Sortable version of the dynamic string field -->
+   <dynamicField name="sort_*" type="sortString" indexed="true" stored="false"/>
+   <copyField source="ss_*" dest="sort_*"/>
+  <!-- A random sort field -->
+   <dynamicField name="random_*" type="rand" indexed="true" stored="true"/>
+   <!-- This field is used to store access information (e.g. node access grants), as opposed to field data -->
+   <dynamicField name="access_*" type="integer" indexed="true" stored="false" multiValued="true"/>
+
+   <!-- The following causes solr 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" />
+
+ </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>id</uniqueKey>
+
+ <!-- field for the QueryParser to use when an explicit fieldname is absent -->
+ <defaultSearchField>content</defaultSearchField>
+
+ <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
+ <solrQueryParser defaultOperator="AND"/>
+
+</schema>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-1.4/solrconfig.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,736 @@
+<?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.
+-->
+
+<config name="drupal-3.0-0-solr1.4">
+  <!-- Set this to 'false' if you want solr to continue working after it has
+       encountered an severe configuration error.  In a production environment,
+       you may want solr to keep working even if one handler is mis-configured.
+
+       You may also set this to false using by setting the system property:
+         -Dsolr.abortOnConfigurationError=false
+     -->
+  <abortOnConfigurationError>${solr.abortOnConfigurationError:true}</abortOnConfigurationError>
+
+  <!-- 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:./solr/data}</dataDir>
+-->
+
+  <indexDefaults>
+   <!-- Values here affect all index writers and act as a default unless overridden. -->
+    <useCompoundFile>false</useCompoundFile>
+
+    <mergeFactor>10</mergeFactor>
+    <!--
+     If both ramBufferSizeMB and maxBufferedDocs is set, then Lucene will flush based on whichever limit is hit first.
+
+     -->
+    <!--<maxBufferedDocs>1000</maxBufferedDocs>-->
+    <!-- Tell Lucene when to flush documents to disk.
+    Giving Lucene more memory for indexing means faster indexing at the cost of more RAM
+
+    If both ramBufferSizeMB and maxBufferedDocs is set, then Lucene will flush based on whichever limit is hit first.
+
+    -->
+    <ramBufferSizeMB>32</ramBufferSizeMB>
+    <maxMergeDocs>2147483647</maxMergeDocs>
+    <maxFieldLength>20000</maxFieldLength>
+    <writeLockTimeout>1000</writeLockTimeout>
+    <commitLockTimeout>10000</commitLockTimeout>
+
+    <!--
+     Expert: Turn on Lucene's auto commit capability.
+     This causes intermediate segment flushes to write a new lucene
+     index descriptor, enabling it to be opened by an external
+     IndexReader.
+     NOTE: Despite the name, this value does not have any relation to Solr's autoCommit functionality
+     -->
+    <!--<luceneAutoCommit>false</luceneAutoCommit>-->
+    <!--
+     Expert:
+     The Merge Policy in Lucene controls how merging is handled by Lucene.  The default in 2.3 is the LogByteSizeMergePolicy, previous
+     versions used LogDocMergePolicy.
+
+     LogByteSizeMergePolicy chooses segments to merge based on their size.  The Lucene 2.2 default, LogDocMergePolicy chose when
+     to merge based on number of documents
+
+     Other implementations of MergePolicy must have a no-argument constructor
+     -->
+    <mergePolicy class="org.apache.lucene.index.LogByteSizeMergePolicy" />
+
+    <!--
+     Expert:
+     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>org.apache.lucene.index.ConcurrentMergeScheduler</mergeScheduler>-->
+
+    <!--
+      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
+      simple = SimpleFSLockFactory
+
+      (For backwards compatibility with Solr 1.2, 'simple' is the default
+       if not specified.)
+    -->
+    <lockType>single</lockType>
+  </indexDefaults>
+
+  <mainIndex>
+    <!-- options specific to the main on-disk lucene index -->
+    <useCompoundFile>false</useCompoundFile>
+    <ramBufferSizeMB>32</ramBufferSizeMB>
+    <mergeFactor>4</mergeFactor>
+    <maxMergeDocs>2147483647</maxMergeDocs>
+    <maxFieldLength>20000</maxFieldLength>
+
+    <!-- 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.
+         This is not needed if lock type is 'none' or 'single'
+     -->
+    <unlockOnStartup>false</unlockOnStartup>
+
+    <!--
+        Custom deletion policies can specified here. The class must
+        implement org.apache.lucene.index.IndexDeletionPolicy.
+
+        http://lucene.apache.org/java/2_3_2/api/org/apache/lucene/index/IndexDeletionPolicy.html
+
+        The standard 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">
+      <!-- Keep only optimized commit points -->
+      <str name="keepOptimizedOnly">false</str>
+      <!-- The maximum number of commit points to be kept -->
+      <str name="maxCommitsToKeep">1</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>
+
+  </mainIndex>
+
+  <!--	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.
+
+		If you want to connect to a particular server, specify the agentId
+		e.g. <jmx agentId="myAgent" />
+
+		If you want to start a new MBeanServer, specify the serviceUrl
+		e.g <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr" />
+
+		For more details see http://wiki.apache.org/solr/SolrJmx
+  -->
+  <jmx />
+
+  <!-- the default high-performance update handler -->
+  <updateHandler class="solr.DirectUpdateHandler2">
+
+    <!-- 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)
+     -->
+
+    <!-- Perform a <commit/> automatically under certain conditions:
+         maxDocs - number of updates since last commit is greater than this
+         maxTime - oldest uncommited update (in ms) is this long ago
+    -->
+    <autoCommit>
+      <maxDocs>2000</maxDocs>
+      <maxTime>120000</maxTime>
+    </autoCommit>
+
+
+    <!-- The RunExecutableListener executes an external command.
+         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=nothing
+         env - environment variables to set.  default=nothing
+      -->
+    <!-- A postCommit event is fired after every commit or optimize command
+    <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>
+    -->
+    <!-- A postOptimize event is fired only after every optimize command, useful
+         in conjunction with index distribution to only distribute optimized indicies
+    <listener event="postOptimize" class="solr.RunExecutableListener">
+      <str name="exe">snapshooter</str>
+      <str name="dir">solr/bin</str>
+      <bool name="wait">true</bool>
+    </listener>
+    -->
+
+  </updateHandler>
+
+
+  <query>
+    <!-- Maximum number of clauses in a boolean query... can affect
+        range or prefix queries that expand to big boolean
+        queries.  An exception is thrown if exceeded.  -->
+    <maxBooleanClauses>1024</maxBooleanClauses>
+
+
+    <!-- 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. -->
+    <!-- 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 FastLRUCache
+         size - the maximum number of entries in the cache
+         initialSize - the initial capacity (number of entries) of
+           the cache.  (seel java.util.HashMap)
+         autowarmCount - the number of entries to prepopulate from
+           and old cache.
+         -->
+    <filterCache
+      class="solr.FastLRUCache"
+      size="512"
+      initialSize="512"
+      autowarmCount="128"/>
+
+    <!-- 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"
+      />
+    -->
+
+   <!-- queryResultCache 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="32"/>
+
+  <!-- documentCache 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"/>
+
+    <!-- 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>
+
+    <!-- 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.search.CacheRegenerator if autowarming is desired.  -->
+    <!--
+    <cache name="myUserCache"
+      class="solr.LRUCache"
+      size="4096"
+      initialSize="1024"
+      autowarmCount="1024"
+      regenerator="org.mycompany.mypackage.MyRegenerator"
+      />
+    -->
+
+   <!-- An 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.
+    <useFilterForSortedQuery>true</useFilterForSortedQuery>
+   -->
+
+   <!-- 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>50</queryResultWindowSize>
+
+    <!-- Maximum number of documents to cache for any entry in the
+         queryResultCache. -->
+    <queryResultMaxDocsCached>200</queryResultMaxDocsCached>
+
+    <!-- This entry enables an int hash representation for filters (DocSets)
+         when the number of items in the set is less than maxSize.  For smaller
+         sets, this representation is more memory efficient, more efficient to
+         iterate over, and faster to take intersections.  -->
+    <HashDocSet maxSize="3000" loadFactor="0.75"/>
+
+    <!-- a newSearcher event is fired whenever a new searcher is being prepared
+         and there is a current searcher handling requests (aka registered). -->
+    <!-- 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="start">0</str> <str name="rows">10</str> </lst>
+        <lst> <str name="q">rocks</str> <str name="start">0</str> <str name="rows">10</str> </lst>
+        <lst><str name="q">static newSearcher warming query from solrconfig.xml</str></lst>
+      </arr>
+    </listener>
+
+    <!-- a firstSearcher event is fired whenever a new searcher is being
+         prepared but there is no current registered searcher to handle
+         requests or to gain autowarming data from. -->
+    <listener event="firstSearcher" class="solr.QuerySenderListener">
+      <arr name="queries">
+        <lst> <str name="q">fast_warm</str> <str name="start">0</str> <str name="rows">10</str> </lst>
+        <lst><str name="q">static firstSearcher warming query from solrconfig.xml</str></lst>
+      </arr>
+    </listener>
+
+    <!-- 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>
+
+    <!-- Maximum number of searchers that may be warming in the background
+      concurrently.  An error is returned if this limit is exceeded. Recommend
+      1-2 for read-only slaves, higher for masters w/o cache warming. -->
+    <maxWarmingSearchers>2</maxWarmingSearchers>
+
+  </query>
+
+  <!--
+    Let the dispatch filter handler /select?qt=XXX
+    handleSelect=true will use consistent error handling for /select and /update
+    handleSelect=false will use solr1.1 style error formatting
+    -->
+  <requestDispatcher handleSelect="true" >
+    <!--Make sure your system has some authentication before enabling remote streaming!  -->
+    <requestParsers enableRemoteStreaming="false" multipartUploadLimitInKB="2048" />
+
+    <!-- Set HTTP caching related parameters (for proxy caches and clients).
+
+         To get the behaviour of Solr 1.2 (ie: no caching related headers)
+         use the never304="true" option and do not specify a value for
+         <cacheControl>
+    -->
+    <!-- <httpCaching never304="true"> -->
+    <httpCaching lastModifiedFrom="openTime"
+                 etagSeed="Solr">
+       <!-- lastModFrom="openTime" is the default, 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 corrispond 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
+            differnet 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.
+       -->
+       <!-- 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"
+       -->
+       <!-- <cacheControl>max-age=30, public</cacheControl> -->
+    </httpCaching>
+  </requestDispatcher>
+
+
+  <!-- requestHandler plugins... incoming queries will be dispatched to the
+     correct handler based on the path or the qt (query type) param.
+     Names starting with a '/' are accessed with the a path equal to the
+     registered name.  Names without a leading '/' are accessed with:
+      http://host/app/select?qt=name
+     If no qt is defined, the requestHandler that declares default="true"
+     will be used.
+  -->
+  <requestHandler name="standard" class="solr.SearchHandler">
+    <!-- default values for query parameters -->
+     <lst name="defaults">
+       <str name="echoParams">explicit</str>
+       <bool name="omitHeader">true</bool>
+       <!--
+       <int name="rows">10</int>
+       <str name="fl">*</str>
+       <str name="version">2.1</str>
+        -->
+     </lst>
+  </requestHandler>
+
+<!-- Please refer to http://wiki.apache.org/solr/SolrReplication for details on configuring replication -->
+<!-- MASTER_REPLICATION_START
+<requestHandler name="/replication" class="solr.ReplicationHandler" >
+    <lst name="master">
+        <str name="replicateAfter">commit</str>
+        <str name="replicateAfter">startup</str>
+        <str name="confFiles">schema.xml,mapping-ISOLatin1Accent.txt,protwords.txt,stopwords.txt,synonyms.txt,elevate.xml</str>
+    </lst>
+</requestHandler>
+ MASTER_REPLICATION_END -->
+
+<!-- SLAVE_REPLICATION_START
+<requestHandler name="/replication" class="solr.ReplicationHandler" >
+    <lst name="slave">
+        <str name="masterUrl">$MASTER_CORE_URL/replication</str>
+        <str name="pollInterval">$POLL_TIME</str>
+     </lst>
+</requestHandler>
+ SLAVE_REPLICATION_END -->
+
+  <!-- DisMaxRequestHandler allows easy searching across multiple fields
+       for simple user-entered phrases.  It's implementation is now
+       just the standard SearchHandler with a default query type
+       of "dismax".
+       see http://wiki.apache.org/solr/DisMaxRequestHandler
+   -->
+  <requestHandler name="dismax" class="solr.SearchHandler">
+    <lst name="defaults">
+     <str name="defType">dismax</str>
+     <str name="echoParams">explicit</str>
+     <bool name="omitHeader">true</bool>
+    </lst>
+  </requestHandler>
+
+  <!-- Note how you can register the same handler multiple times with
+       different names (and different init parameters)
+    -->
+  <requestHandler name="drupal" class="solr.SearchHandler" default="true">
+    <lst name="defaults">
+     <str name="defType">dismax</str>
+     <str name="echoParams">explicit</str>
+     <bool name="omitHeader">true</bool>
+     <float name="tie">0.01</float>
+     <str name="pf">
+        content^2.0
+     </str>
+     <int name="ps">15</int>
+     <!-- Abort any searches longer than 4 seconds -->
+     <!-- <int name="timeAllowed">4000</int>  -->
+     <str name="mm">1</str>
+     <str name="q.alt">*:*</str>
+
+   <!-- example highlighter config, enable per-query with hl=true -->
+     <str name="hl">true</str>
+     <str name="hl.fl">content</str>
+     <int name="hl.snippets">3</int>
+     <str name="hl.mergeContiguous">true</str>
+   <!-- instructs Solr to return the field itself if no query terms are
+        found -->
+     <str name="f.content.hl.alternateField">teaser</str>
+     <str name="f.content.hl.maxAlternateFieldLength">256</str>
+     <!-- JS: I wasn't getting good results here... I'm turning off for now
+     because I was getting periods (.) by themselves at the beginning of
+     snippets and don't feel like debugging anymore.  Without the regex is
+     faster too -->
+     <!--<str name="f.content.hl.fragmenter">regex</str>--> <!-- defined below -->
+
+    <!-- By default, don't spell check -->
+      <str name="spellcheck">false</str>
+    <!-- Defaults for the spell checker when used -->
+      <str name="spellcheck.onlyMorePopular">true</str>
+      <str name="spellcheck.extendedResults">false</str>
+      <!--  The number of suggestions to return -->
+      <str name="spellcheck.count">1</str>
+    </lst>
+    <arr name="last-components">
+      <str>spellcheck</str>
+    </arr>
+  </requestHandler>
+
+  <!-- The more like this handler offers many advantages over the standard handler,
+    when performing moreLikeThis requests.-->
+  <requestHandler name="mlt" class="solr.MoreLikeThisHandler">
+    <lst name="defaults">
+      <str name="mlt.mintf">1</str>
+      <str name="mlt.mindf">1</str>
+      <str name="mlt.minwl">3</str>
+      <str name="mlt.maxwl">15</str>
+      <str name="mlt.maxqt">20</str>
+      <str name="mlt.match.include">false</str>
+      <!-- Abort any searches longer than 1.5 seconds -->
+      <!-- <int name="timeAllowed">1500</int> -->
+    </lst>
+  </requestHandler>
+
+
+  <!--
+   Search components are registered to SolrCore and used by Search Handlers
+
+   By default, the following components are avaliable:
+
+   <searchComponent name="query"     class="org.apache.solr.handler.component.QueryComponent" />
+   <searchComponent name="facet"     class="org.apache.solr.handler.component.FacetComponent" />
+   <searchComponent name="mlt"       class="org.apache.solr.handler.component.MoreLikeThisComponent" />
+   <searchComponent name="highlight" class="org.apache.solr.handler.component.HighlightComponent" />
+   <searchComponent name="stats"     class="org.apache.solr.handler.component.StatsComponent" />
+   <searchComponent name="debug"     class="org.apache.solr.handler.component.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.
+    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>
+  -->
+
+   <!-- The spell check component can return a list of alternative spelling
+  suggestions.  -->
+  <searchComponent name="spellcheck" class="solr.SpellCheckComponent">
+
+    <str name="queryAnalyzerFieldType">textSpell</str>
+
+    <lst name="spellchecker">
+      <str name="name">default</str>
+      <str name="field">spell</str>
+      <str name="spellcheckIndexDir">./spellchecker1</str>
+      <str name="buildOnOptimize">true</str>
+    </lst>
+    <lst name="spellchecker">
+      <str name="name">jarowinkler</str>
+      <str name="field">spell</str>
+      <!-- Use a different Distance Measure -->
+      <str name="distanceMeasure">org.apache.lucene.search.spell.JaroWinklerDistance</str>
+      <str name="spellcheckIndexDir">./spellchecker2</str>
+      <str name="buildOnOptimize">true</str>
+    </lst>
+
+  </searchComponent>
+
+  <queryConverter name="queryConverter" class="solr.SpellingQueryConverter"/>
+
+  <!-- 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 utilizing the elevator component -->
+  <requestHandler name="/elevate" class="solr.SearchHandler" startup="lazy">
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+    </lst>
+    <arr name="last-components">
+      <str>elevator</str>
+    </arr>
+  </requestHandler>
+
+
+  <!-- Update request handler.
+
+       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'
+       The response format differs from solr1.1 formatting and returns a standard error code.
+
+       To enable solr1.1 behavior, remove the /update handler or change its path
+    -->
+  <requestHandler name="/update" class="solr.XmlUpdateRequestHandler" />
+
+  <!--
+   Analysis request handler.  Since Solr 1.3.  Use to returnhow a document is analyzed.  Useful
+   for debugging and as a token server for other types of applications
+   -->
+  <requestHandler name="/analysis" class="solr.AnalysisRequestHandler" />
+
+
+  <!-- CSV update handler, loaded on demand -->
+  <requestHandler name="/update/csv" class="solr.CSVRequestHandler" startup="lazy" />
+
+
+  <!--
+   Admin Handlers - This will register all the standard admin RequestHandlers.  Adding
+   this single handler is equivalent to registering:
+
+  <requestHandler name="/admin/luke"       class="org.apache.solr.handler.admin.LukeRequestHandler" />
+  <requestHandler name="/admin/system"     class="org.apache.solr.handler.admin.SystemInfoHandler" />
+  <requestHandler name="/admin/plugins"    class="org.apache.solr.handler.admin.PluginInfoHandler" />
+  <requestHandler name="/admin/threads"    class="org.apache.solr.handler.admin.ThreadDumpHandler" />
+  <requestHandler name="/admin/properties" class="org.apache.solr.handler.admin.PropertiesRequestHandler" />
+  <requestHandler name="/admin/file"       class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
+
+  If you wish to hide files under ${solr.home}/conf, explicitly register the ShowFileRequestHandler using:
+  <requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
+    <lst name="invariants">
+     <str name="hidden">synonyms.txt</str>
+     <str name="hidden">anotherfile.txt</str>
+    </lst>
+  </requestHandler>
+  -->
+  <requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />
+
+  <!-- ping/healthcheck -->
+  <requestHandler name="/admin/ping" class="PingRequestHandler">
+    <lst name="defaults">
+      <str name="qt">standard</str>
+      <str name="q">solrpingquery</str>
+      <str name="echoParams">all</str>
+    </lst>
+  </requestHandler>
+
+  <!-- Files that are needed to start the service -->
+  <requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
+    <lst name="invariants">
+     <str name="hidden">admin-extra.html</str>
+     <str name="hidden">scripts.conf</str>
+     <str name="hidden">xslt/example.xsl</str>
+     <str name="hidden">xslt/example_atom.xsl</str>
+     <str name="hidden">xslt/example_rss.xsl</str>
+     <str name="hidden">xslt/luke.xsl</str>
+    </lst>
+  </requestHandler>
+
+  <!-- Echo the request contents back to the client -->
+  <requestHandler name="/debug/dump" class="solr.DumpRequestHandler" >
+    <lst name="defaults">
+     <str name="echoParams">explicit</str> <!-- for all params (including the default etc) use: 'all' -->
+     <str name="echoHandler">true</str>
+    </lst>
+  </requestHandler>
+
+  <highlighting>
+   <!-- Configure the standard fragmenter -->
+   <!-- This could most likely be commented out in the "default" case -->
+   <fragmenter name="gap" class="org.apache.solr.highlight.GapFragmenter" default="true">
+    <lst name="defaults">
+     <int name="hl.fragsize">100</int>
+    </lst>
+   </fragmenter>
+
+   <!-- A regular-expression-based fragmenter (f.i., for sentence extraction) -->
+   <fragmenter name="regex" class="org.apache.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\"']{20,200}</str>
+    </lst>
+   </fragmenter>
+
+   <!-- Configure the standard formatter -->
+   <formatter name="html" class="org.apache.solr.highlight.HtmlFormatter" default="true">
+    <lst name="defaults">
+     <str name="hl.simple.pre"><![CDATA[<strong>]]></str>
+     <str name="hl.simple.post"><![CDATA[</strong>]]></str>
+    </lst>
+   </formatter>
+  </highlighting>
+
+
+  <!-- queryResponseWriter plugins... query 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. XMLResponseWriter will be used if nothing is specified here.
+    The json, python, and ruby writers are also available by default.
+
+    <queryResponseWriter name="xml" class="org.apache.solr.request.XMLResponseWriter" default="true"/>
+    <queryResponseWriter name="json" class="org.apache.solr.request.JSONResponseWriter"/>
+    <queryResponseWriter name="python" class="org.apache.solr.request.PythonResponseWriter"/>
+    <queryResponseWriter name="ruby" class="org.apache.solr.request.RubyResponseWriter"/>
+    <queryResponseWriter name="php" class="org.apache.solr.request.PHPResponseWriter"/>
+    <queryResponseWriter name="phps" class="org.apache.solr.request.PHPSerializedResponseWriter"/>
+
+    <queryResponseWriter name="custom" class="com.example.MyResponseWriter"/>
+  -->
+
+  <!-- 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="org.apache.solr.request.XSLTResponseWriter">
+    <int name="xsltCacheLifetimeSeconds">5</int>
+  </queryResponseWriter>
+
+
+  <!-- example of registering a query parser
+  <queryParser name="lucene" class="org.apache.solr.search.LuceneQParserPlugin"/>
+  -->
+
+  <!-- example of registering a custom function parser
+  <valueSourceParser name="myfunc" class="com.mycompany.MyValueSourceParser" />
+  -->
+
+  <!-- config for the admin interface -->
+  <admin>
+    <defaultQuery>solr</defaultQuery>
+    <!-- configure a healthcheck file for servers behind a loadbalancer
+    <healthcheck type="file">server-enabled</healthcheck>
+    -->
+  </admin>
+
+</config>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/protwords.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,8 @@
+#-----------------------------------------------------------------------
+# This file blocks words from being operated on by the stemmer and word delimiter.
+&amp;
+&lt;
+&gt;
+&#039;
+&quot;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/schema.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ 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.
+
+ For more information, on how to customize this file, please see
+ http://wiki.apache.org/solr/SchemaXml
+-->
+
+<schema name="drupal-4.0-solr-3.x" version="1.3">
+    <!-- attribute "name" is the name of this schema and is only used for display purposes.
+         Applications should change this to reflect the nature of the search collection.
+         version="1.2" 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
+       -->
+  <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 the
+       org.apache.solr.analysis package.
+    -->
+
+    <!-- The StrField type is not analyzed, but indexed/stored verbatim.
+       - StrField and TextField support an optional compressThreshold which
+       limits compression (if enabled in the derived fields) to values which
+       exceed a certain size (in characters).
+    -->
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- boolean type: "true" or "false" -->
+    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
+    <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
+    <fieldtype name="binary" class="solr.BinaryField"/>
+
+    <!-- The optional sortMissingLast and sortMissingFirst attributes are
+         currently supported on types that are sorted internally as strings.
+       - 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.
+    -->
+
+    <!-- numeric field types that can be sorted, but are not optimized for range queries -->
+    <fieldType name="integer" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+
+    <!--
+      Note:
+      These should only be used for compatibility with existing indexes (created with older Solr versions)
+      or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
+
+      Numeric field types that manipulate the value into
+      a string value that isn't human-readable in its internal form,
+      but with a lexicographic ordering the same as the numeric ordering,
+      so that range queries work correctly.
+    -->
+    <fieldType name="sint" class="solr.TrieIntField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="slong" class="solr.TrieFloatField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="sfloat" class="solr.TrieLongField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="sdouble" class="solr.TrieDoubleField" sortMissingLast="true" omitNorms="true"/>
+
+    <!--
+     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" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+
+    <!--
+     The ExternalFileField type gets values from an external file instead of the
+     index. This is useful for data such as rankings that might change frequently
+     and require different update frequencies than the documents they are
+     associated with.
+    -->
+    <fieldType name="pfloat" class="solr.FloatField" omitNorms="true"/>
+    <fieldType name="file" keyField="id" defVal="1" stored="false" indexed="false" class="solr.ExternalFileField" valType="pfloat"/>
+
+    <!-- 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.
+      -->
+    <fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- A Trie based date field for faster date range queries and date faceting. -->
+    <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
+
+    <!-- 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
+    <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" omitNorms="true" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A text field that uses WordDelimiterFilter to enable splitting and matching of
+        words on case-change, alpha numeric boundaries, and non-alphanumeric chars,
+        so that a query of "wifi" or "wi fi" could match a document containing "Wi-Fi".
+        Synonyms and stopwords are customized by external files, and stemming is enabled.
+        Duplicate tokens at the same position (which may result from Stemmed Synonyms or
+        WordDelim parts) are removed.
+        -->
+    <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <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="stopwords.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="1"
+                catenateNumbers="1"
+                catenateAll="0"
+                splitOnCaseChange="1"
+                preserveOriginal="1"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <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.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="0"
+                catenateNumbers="0"
+                catenateAll="0"
+                splitOnCaseChange="1"
+                preserveOriginal="1"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- An unstemmed text field - good if one does not know the language of the field -->
+    <fieldType name="text_und" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="1"
+                catenateNumbers="1"
+                catenateAll="0"
+                splitOnCaseChange="0"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </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="stopwords.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="0"
+                catenateNumbers="0"
+                catenateAll="0"
+                splitOnCaseChange="0"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Edge N gram type - for example for matching against queries with results
+        KeywordTokenizer leaves input string intact as a single term.
+        see: http://www.lucidimagination.com/blog/2009/09/08/auto-suggest-from-popular-queries-using-edgengrams/ 
+      -->
+    <fieldType name="edge_n2_kw_text" class="solr.TextField" omitNorms="true" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.KeywordTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="25" />
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.KeywordTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    <!--  Setup simple analysis for spell checking -->
+
+    <fieldType name="textSpell" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory" />
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
+        <filter class="solr.LengthFilterFactory" min="4" max="20" />
+        <filter class="solr.LowerCaseFilterFactory" />
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory" />
+      </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="sortString" 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 refrences to portions of the orriginal
+            string matched by the pattern.
+
+            See the Java Regular Expression documentation for more
+            infomation on pattern and replacement string syntax.
+
+            http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
+
+        <filter class="solr.PatternReplaceFilterFactory"
+               pattern="(^\p{Punct}+)" replacement="" replace="all"
+        />
+        -->
+      </analyzer>
+    </fieldType>
+
+    <!-- A random sort type -->
+    <fieldType name="rand" class="solr.RandomSortField" indexed="true" />
+
+    <!-- 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" class="solr.StrField" />
+
+    <!-- Begin added types to use features in Solr 3.4+ -->
+    <fieldType name="point" class="solr.PointType" dimension="2" subFieldType="tdouble"/>
+
+    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
+    <fieldType name="location" class="solr.LatLonType" subFieldType="tdouble"/>
+
+    <!-- A Geohash is a compact representation of a latitude longitude pair in a single field.
+         See http://wiki.apache.org/solr/SpatialSearch
+     -->
+    <fieldtype name="geohash" class="solr.GeoHashField"/>
+    <!-- End added Solr 3.4+ types -->
+  </types>
+
+  <!-- Following is a dynamic way to include other types, added by other contrib modules -->
+  <xi:include href="schema_extra_types.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:fallback></xi:fallback>
+  </xi:include>
+
+  <fields>
+    <!-- Valid attributes for fields:
+      name: mandatory - the name for the field
+      type: mandatory - the name of a previously defined type from the <types> section
+      indexed: true if this field should be indexed (searchable or sortable)
+      stored: true if this field should be retrievable
+      compressed: [false] if this field should be stored using gzip compression
+       (this will only apply if the field type is compressable; among
+       the standard field types, only TextField and StrField are)
+      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.
+    -->
+
+    <!-- The document id is usually derived from a site-spcific key (hash) and the
+      entity type and ID like:
+      Search Api :
+        The format used is $document->id = $index_id . '-' . $item_id
+      Apache Solr Search Integration
+        The format used is $document->id = $site_hash . '/' . $entity_type . '/' . $entity->id;
+    -->
+    <field name="id" type="string" indexed="true" stored="true" required="true" />
+
+    <!-- Search Api specific fields -->
+    <!-- item_id contains the entity ID, e.g. a node's nid. -->
+    <field name="item_id"  type="string" indexed="true" stored="true" />
+    <!-- index_id is the machine name of the search index this entry belongs to. -->
+    <field name="index_id" type="string" indexed="true" stored="true" />
+    <!-- Since sorting by ID is explicitly allowed, store item_id also in a sortable way. -->
+    <copyField source="item_id" dest="sort_search_api_id" />
+
+    <!-- Apache Solr Search Integration specific fields -->
+    <!-- entity_id is the numeric object ID, e.g. Node ID, File ID -->
+    <field name="entity_id"  type="long" indexed="true" stored="true" />
+    <!-- entity_type is 'node', 'file', 'user', or some other Drupal object type -->
+    <field name="entity_type" type="string" indexed="true" stored="true" />
+    <!-- bundle is a node type, or as appropriate for other entity types -->
+    <field name="bundle" type="string" indexed="true" stored="true"/>
+    <field name="bundle_name" type="string" indexed="true" stored="true"/>
+    <field name="site" type="string" indexed="true" stored="true"/>
+    <field name="hash" type="string" indexed="true" stored="true"/>
+    <field name="url" type="string" indexed="true" stored="true"/>
+    <!-- label is the default field for a human-readable string for this entity (e.g. the title of a node) -->
+    <field name="label" type="text" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+    <!-- The string version of the title is used for sorting -->
+    <copyField source="label" dest="sort_label"/>
+
+    <!-- content is the default field for full text search - dump crap here -->
+    <field name="content" type="text" indexed="true" stored="true" termVectors="true"/>
+    <field name="teaser" type="text" indexed="false" stored="true"/>
+    <field name="path" type="string" indexed="true" stored="true"/>
+    <field name="path_alias" type="text" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+
+    <!-- These are the fields that correspond to a Drupal node. The beauty of having
+      Lucene store title, body, type, etc., is that we retrieve them with the search
+      result set and don't need to go to the database with a node_load. -->
+    <field name="tid"  type="long" indexed="true" stored="true" multiValued="true"/>
+    <field name="taxonomy_names" type="text" indexed="true" stored="false" termVectors="true" multiValued="true" omitNorms="true"/>
+    <!-- Copy terms to a single field that contains all taxonomy term names -->
+    <copyField source="tm_vid_*" dest="taxonomy_names"/>
+
+    <!-- Here, default is used to create a "timestamp" field indicating
+         when each document was indexed.-->
+    <field name="timestamp" type="tdate" indexed="true" stored="true" default="NOW" multiValued="false"/>
+
+    <!-- This field is used to build the spellchecker index -->
+    <field name="spell" type="textSpell" indexed="true" stored="true" multiValued="true"/>
+
+    <!-- copyField commands copy one field to another at the time a document
+         is added to the index.  It's used either to index the same field differently,
+         or to add multiple fields to the same field for easier/faster searching.  -->
+    <copyField source="label" dest="spell"/>
+    <copyField source="content" dest="spell"/>
+
+    <copyField source="ts_*" dest="spell"/>
+    <copyField source="tm_*" dest="spell"/>
+
+    <!-- Dynamic field definitions.  If a field name is not found, dynamicFields
+         will be used if the name matches any of the patterns.
+         RESTRICTION: the glob-like pattern in the name attribute must have
+         a "*" only at the start or the end.
+         EXAMPLE:  name="*_i" will match any field ending in _i (like myid_i, z_i)
+         Longer patterns will be matched first.  if equal size patterns
+         both match, the first appearing in the schema will be used.  -->
+
+    <!-- A set of fields to contain text extracted from HTML tag contents which we
+         can boost at query time. -->
+    <dynamicField name="tags_*" type="text"   indexed="true" stored="false" omitNorms="true"/>
+
+    <!-- For 2 and 3 letter prefix dynamic fields, the 1st letter indicates the data type and
+         the last letter is 's' for single valued, 'm' for multi-valued -->
+
+    <!-- We use long for integer since 64 bit ints are now common in PHP. -->
+    <dynamicField name="is_*"  type="long"    indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="im_*"  type="long"    indexed="true"  stored="true" multiValued="true"/>
+    <!-- List of floats can be saved in a regular float field -->
+    <dynamicField name="fs_*"  type="float"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="fm_*"  type="float"   indexed="true"  stored="true" multiValued="true"/>
+    <!-- List of doubles can be saved in a regular double field -->
+    <dynamicField name="ps_*"  type="double"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="pm_*"  type="double"   indexed="true"  stored="true" multiValued="true"/>
+    <!-- List of booleans can be saved in a regular boolean field -->
+    <dynamicField name="bm_*"  type="boolean" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="bs_*"  type="boolean" indexed="true"  stored="true" multiValued="false"/>
+    <!-- Regular text (without processing) can be stored in a string field-->
+    <dynamicField name="ss_*"  type="string"  indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="sm_*"  type="string"  indexed="true"  stored="true" multiValued="true"/>
+    <!-- Normal text fields are for full text - the relevance of a match depends on the length of the text -->
+    <dynamicField name="ts_*"  type="text"    indexed="true"  stored="true" multiValued="false" termVectors="true"/>
+    <dynamicField name="tm_*"  type="text"    indexed="true"  stored="true" multiValued="true" termVectors="true"/>
+    <!-- Unstemmed text fields for full text - the relevance of a match depends on the length of the text -->
+    <dynamicField name="tus_*" type="text_und" indexed="true"  stored="true" multiValued="false" termVectors="true"/>
+    <dynamicField name="tum_*" type="text_und" indexed="true"  stored="true" multiValued="true" termVectors="true"/>
+    <!-- These text fields omit norms - useful for extracted text like taxonomy_names -->
+    <dynamicField name="tos_*" type="text"    indexed="true"  stored="true" multiValued="false" termVectors="true" omitNorms="true"/>
+    <dynamicField name="tom_*" type="text"    indexed="true"  stored="true" multiValued="true" termVectors="true" omitNorms="true"/>
+    <!-- Special-purpose text fields -->
+    <dynamicField name="tes_*" type="edge_n2_kw_text" indexed="true" stored="true" multiValued="false" omitTermFreqAndPositions="true" />
+    <dynamicField name="tem_*" type="edge_n2_kw_text" indexed="true" stored="true" multiValued="true" omitTermFreqAndPositions="true" />
+    <dynamicField name="tws_*" type="text_ws" indexed="true" stored="true" multiValued="false"/>
+    <dynamicField name="twm_*" type="text_ws" indexed="true" stored="true" multiValued="true"/>
+
+    <!-- trie dates are preferred, so give them the 2 letter prefix -->
+    <dynamicField name="ds_*"  type="tdate"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="dm_*"  type="tdate"   indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="its_*" type="tlong"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="itm_*" type="tlong"   indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="fts_*" type="tfloat"  indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ftm_*" type="tfloat"  indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="pts_*" type="tdouble" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ptm_*" type="tdouble" indexed="true"  stored="true" multiValued="true"/>
+    <!-- Binary fields can be populated using base64 encoded data. Useful e.g. for embedding
+         a small image in a search result using the data URI scheme -->
+    <dynamicField name="xs_*"  type="binary"  indexed="false" stored="true" multiValued="false"/>
+    <dynamicField name="xm_*"  type="binary"  indexed="false" stored="true" multiValued="true"/>
+    <!-- In rare cases a date rather than tdate is needed for sortMissingLast -->
+    <dynamicField name="dds_*" type="date"    indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ddm_*" type="date"    indexed="true"  stored="true" multiValued="true"/>
+    <!-- Sortable fields, good for sortMissingLast support &
+         We use long for integer since 64 bit ints are now common in PHP. -->
+    <dynamicField name="iss_*" type="slong"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ism_*" type="slong"   indexed="true"  stored="true" multiValued="true"/>
+    <!-- In rare cases a sfloat rather than tfloat is needed for sortMissingLast -->
+    <dynamicField name="fss_*" type="sfloat"  indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="fsm_*" type="sfloat"  indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="pss_*" type="sdouble" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="psm_*" type="sdouble" indexed="true"  stored="true" multiValued="true"/>
+    <!-- In case a 32 bit int is really needed, we provide these fields. 'h' is mnemonic for 'half word', i.e. 32 bit on 64 arch -->
+    <dynamicField name="hs_*" type="integer" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="hm_*" type="integer" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="hss_*" type="sint"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="hsm_*" type="sint"   indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="hts_*" type="tint"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="htm_*" type="tint"   indexed="true"  stored="true" multiValued="true"/>
+
+    <!-- Unindexed string fields that can be used to store values that won't be searchable -->
+    <dynamicField name="zs_*" type="string"   indexed="false"  stored="true" multiValued="false"/>
+    <dynamicField name="zm_*" type="string"   indexed="false"  stored="true" multiValued="true"/>
+
+    <!-- Begin added fields to use features in Solr 3.4+
+         http://wiki.apache.org/solr/SpatialSearch#geodist_-_The_distance_function -->
+    <dynamicField name="points_*" type="point" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="pointm_*" type="point" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="locs_*" type="location" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="locm_*" type="location" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="geos_*" type="geohash" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="geom_*" type="geohash" indexed="true"  stored="true" multiValued="true"/>
+
+    <!-- External file fields -->
+    <dynamicField name="eff_*" type="file"/>
+    <!-- End added fields for Solr 3.4+ -->
+
+    <!-- Sortable version of the dynamic string field -->
+    <dynamicField name="sort_*" type="sortString" indexed="true" stored="false"/>
+    <copyField source="ss_*" dest="sort_*"/>
+    <!-- A random sort field -->
+    <dynamicField name="random_*" type="rand" indexed="true" stored="true"/>
+    <!-- This field is used to store access information (e.g. node access grants), as opposed to field data -->
+    <dynamicField name="access_*" type="integer" indexed="true" stored="false" multiValued="true"/>
+
+    <!-- The following causes solr 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" />
+
+  </fields>
+
+  <!-- Following is a dynamic way to include other fields, added by other contrib modules -->
+  <xi:include href="schema_extra_fields.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:fallback></xi:fallback>
+  </xi:include>
+
+  <!-- Field to use to determine and enforce document uniqueness.
+       Unless this field is marked with required="false", it will be a required field
+    -->
+  <uniqueKey>id</uniqueKey>
+
+  <!-- field for the QueryParser to use when an explicit fieldname is absent -->
+  <defaultSearchField>content</defaultSearchField>
+
+  <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
+  <solrQueryParser defaultOperator="AND"/>
+
+</schema>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/schema_extra_fields.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,23 @@
+<fields>
+<!--
+  Adding German dynamic field types to our Solr Schema
+  If you enable this, make sure you have a folder called lang with stopwords_de.txt
+  and synonyms_de.txt in there
+  This also requires to enable the content in schema_extra_types.xml
+-->
+<!--
+   <field name="label_de" type="text_de" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+   <field name="content_de" type="text_de" indexed="true" stored="true" termVectors="true"/>
+   <field name="teaser_de" type="text_de" indexed="false" stored="true"/>
+   <field name="path_alias_de" type="text_de" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+   <field name="taxonomy_names_de" type="text_de" indexed="true" stored="false" termVectors="true" multiValued="true" omitNorms="true"/>
+   <field name="spell_de" type="text_de" indexed="true" stored="true" multiValued="true"/>
+   <copyField source="label_de" dest="spell_de"/>
+   <copyField source="content_de" dest="spell_de"/>
+   <dynamicField name="tags_de_*" type="text_de" indexed="true" stored="false" omitNorms="true"/>
+   <dynamicField name="ts_de_*" type="text_de" indexed="true" stored="true" multiValued="false" termVectors="true"/>
+   <dynamicField name="tm_de_*" type="text_de" indexed="true" stored="true" multiValued="true" termVectors="true"/>
+   <dynamicField name="tos_de_*" type="text_de" indexed="true" stored="true" multiValued="false" termVectors="true" omitNorms="true"/>
+   <dynamicField name="tom_de_*" type="text_de" indexed="true" stored="true" multiValued="true" termVectors="true" omitNorms="true"/>
+-->
+</fields>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/schema_extra_types.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,30 @@
+<types>
+<!--
+  Adding German language to our Solr Schema German
+  If you enable this, make sure you have a folder called lang with stopwords_de.txt
+  and synonyms_de.txt in there
+-->
+<!--
+    <fieldType name="text_de" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" words="lang/stopwords_de.txt" format="snowball" ignoreCase="true" enablePositionIncrements="true"/>
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnCaseChange="1" splitOnNumerics="1" catenateWords="1" catenateNumbers="1" catenateAll="0" protected="protwords.txt" preserveOriginal="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.GermanLightStemFilterFactory"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="lang/synonyms_de.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory" words="lang/stopwords_de.txt" format="snowball" ignoreCase="true" enablePositionIncrements="true"/>
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnCaseChange="1" splitOnNumerics="1" catenateWords="0" catenateNumbers="0" catenateAll="0" protected="protwords.txt" preserveOriginal="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.GermanLightStemFilterFactory"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+-->
+</types>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/solrconfig.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1583 @@
+<?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.
+    -->
+
+  <!-- Set this to 'false' if you want solr to continue working after
+       it has encountered an severe configuration error.  In a
+       production environment, you may want solr to keep working even
+       if one handler is mis-configured.
+
+       You may also set this to false using by setting the system
+       property:
+
+         -Dsolr.abortOnConfigurationError=false
+    -->
+  <abortOnConfigurationError>${solr.abortOnConfigurationError:true}</abortOnConfigurationError>
+  
+  <!-- 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>${luceneVersion:LUCENE_35}</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.
+
+       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.
+    -->
+  <lib dir="../../contrib/extraction/lib" />
+  <lib dir="../../contrib/clustering/lib/" />
+  <!-- The velocity library has been known to crash Solr in some
+       instances when deployed as a war file to Tomcat. Therefore all
+       references have been removed from the default configuration.
+       @see http://drupal.org/node/1612556
+  -->
+  <!-- <lib dir="../../contrib/velocity/lib" /> -->
+
+  <!-- When a regex is specified in addition to a directory, only the
+       files in that directory which completely match the regex
+       (anchored on both ends) will be included.
+    -->
+  <!--<lib dir="../../dist/" regex="apache-solr-cell-\d.*\.jar" />-->
+  <!--<lib dir="../../dist/" regex="apache-solr-clustering-\d.*\.jar" />-->
+  <!--<lib dir="../../dist/" regex="apache-solr-dataimporthandler-\d.*\.jar" />-->
+  <!--<lib dir="../../dist/" regex="apache-solr-langid-\d.*\.jar" />-->
+  <!-- <lib dir="../../dist/" regex="apache-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="../../contrib/clustering/lib/" />-->
+  <!--<lib dir="/total/crap/dir/ignored" />-->
+
+  <!-- an exact path can be used to specify a specific 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, the default, is filesystem
+       based and tries to pick the best implementation for the current
+       JVM and platform.  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.StandardDirectoryFactory}"/>
+
+  <!-- Index Defaults
+
+       Values here affect all index writers and act as a default
+       unless overridden.
+
+       WARNING: See also the <mainIndex> section below for parameters
+       that overfor Solr's main Lucene index.
+    -->
+  <indexDefaults>
+
+    <useCompoundFile>false</useCompoundFile>
+
+    <mergeFactor>4</mergeFactor>
+    <!-- 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.  -->
+    <ramBufferSizeMB>32</ramBufferSizeMB>
+    <!-- If both ramBufferSizeMB and maxBufferedDocs is set, then
+         Lucene will flush based on whichever limit is hit first.  
+      -->
+    <!-- <maxBufferedDocs>1000</maxBufferedDocs> -->
+
+    <maxMergeDocs>2147483647</maxMergeDocs>
+    <maxFieldLength>100000</maxFieldLength>
+    <writeLockTimeout>1000</writeLockTimeout>
+
+    <!-- Expert: Merge Policy 
+
+         The Merge Policy in Lucene controls how merging is handled by
+         Lucene.  The default in Solr 3.3 is TieredMergePolicy.
+         
+         The default in 2.3 was the LogByteSizeMergePolicy,
+         previous versions used LogDocMergePolicy.
+         
+         LogByteSizeMergePolicy chooses segments to merge based on
+         their size.  The Lucene 2.2 default, LogDocMergePolicy chose
+         when to merge based on number of documents
+         
+         Other implementations of MergePolicy must have a no-argument
+         constructor
+      -->
+    <mergePolicy class="org.apache.lucene.index.LogByteSizeMergePolicy"/>
+
+    <!-- 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
+
+         (For backwards compatibility with Solr 1.2, 'simple' is the
+         default if not specified.)
+
+         More details on the nuances of each LockFactory...
+         http://wiki.apache.org/lucene-java/AvailableLockFactories
+    -->
+    <lockType>single</lockType>
+
+    <!-- Expert: Controls how often Lucene loads terms into memory
+         Default is 128 and is likely good for most everyone.
+      -->
+    <!-- <termIndexInterval>256</termIndexInterval> -->
+  </indexDefaults>
+
+  <!-- Main Index
+
+       Values here override the values in the <indexDefaults> section
+       for the main on disk index.
+    -->
+  <mainIndex>
+
+    <useCompoundFile>false</useCompoundFile>
+    <ramBufferSizeMB>32</ramBufferSizeMB>
+    <mergeFactor>10</mergeFactor>
+
+    <!-- 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.
+
+         This is not needed if lock type is 'none' or 'single'
+     -->
+    <unlockOnStartup>false</unlockOnStartup>
+    
+    <!-- If true, IndexReaders will be reopened (often more efficient)
+         instead of closed and then opened.
+      -->
+    <reopenReaders>true</reopenReaders>
+
+    <!-- Commit Deletion Policy
+
+         Custom deletion policies can specified here. The class must
+         implement org.apache.lucene.index.IndexDeletionPolicy.
+
+         http://lucene.apache.org/java/2_9_1/api/all/org/apache/lucene/index/IndexDeletionPolicy.html
+
+         The standard 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> 
+
+  </mainIndex>
+
+  <!-- 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 <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 that is allowed to pass
+                   since a document was added before automaticly
+                   triggering a new commit. 
+      -->
+    <autoCommit> 
+      <maxDocs>10000</maxDocs>
+      <maxTime>120000</maxTime> 
+    </autoCommit>
+
+    <!-- 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>
+      -->
+  </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>
+    <!-- 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="32"/>
+   
+    <!-- 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">solr rocks</str><str name="start">0</str><str name="rows">10</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 affects the behavior of requests such as /select?qt=XXX
+
+       handleSelect="true" will cause the SolrDispatchFilter to process
+       the request and will result in consistent error handling and
+       formatting for all types of requests.
+
+       handleSelect="false" will cause the SolrDispatchFilter to
+       ignore "/select" requests and fallback to using the legacy
+       SolrServlet and it's Solr 1.1 style error formatting
+    -->
+  <requestDispatcher handleSelect="true" >
+    <!-- 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>
+
+  <!-- Request Handlers 
+
+       http://wiki.apache.org/solr/SolrRequestHandler
+
+       incoming queries will be dispatched to the correct handler
+       based on the path or the qt (query type) param.
+
+       Names starting with a '/' are accessed with the a path equal to
+       the registered name.  Names without a leading '/' are accessed
+       with: http://host/app/[core/]select?qt=name
+
+       If a /select request is processed with out a qt param
+       specified, the requestHandler that declares default="true" will
+       be used.
+       
+       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="search" class="solr.SearchHandler" default="true">-->
+    <!-- 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>
+     </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 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">Solritas</str>
+
+       <str name="defType">edismax</str>
+       <str name="q.alt">*:*</str>
+       <str name="rows">10</str>
+       <str name="fl">*,score</str>
+       <str name="mlt.qf">
+         text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
+       </str>
+       <str name="mlt.fl">text,features,name,sku,id,manu,cat</str>
+       <int name="mlt.count">3</int>
+
+       <str name="qf">
+          text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
+       </str>
+
+       <str name="facet">on</str>
+       <str name="facet.field">cat</str>
+       <str name="facet.field">manu_exact</str>
+       <str name="facet.query">ipod</str>
+       <str name="facet.query">GB</str>
+       <str name="facet.mincount">1</str>
+       <str name="facet.pivot">cat,inStock</str>
+       <str name="facet.range.other">after</str>
+       <str name="facet.range">price</str>
+       <int name="f.price.facet.range.start">0</int>
+       <int name="f.price.facet.range.end">600</int>
+       <int name="f.price.facet.range.gap">50</int>
+       <str name="facet.range">popularity</str>
+       <int name="f.popularity.facet.range.start">0</int>
+       <int name="f.popularity.facet.range.end">10</int>
+       <int name="f.popularity.facet.range.gap">3</int>
+       <str name="facet.range">manufacturedate_dt</str>
+       <str name="f.manufacturedate_dt.facet.range.start">NOW/YEAR-10YEARS</str>
+       <str name="f.manufacturedate_dt.facet.range.end">NOW</str>
+       <str name="f.manufacturedate_dt.facet.range.gap">+1YEAR</str>
+       <str name="f.manufacturedate_dt.facet.range.other">before</str>
+       <str name="f.manufacturedate_dt.facet.range.other">after</str>-->
+
+
+       <!-- Highlighting defaults -->
+       <!--<str name="hl">on</str>
+       <str name="hl.fl">text features name</str>
+       <str name="f.name.hl.fragsize">0</str>
+       <str name="f.name.hl.alternateField">name</str>
+     </lst>
+     <arr name="last-components">
+       <str>spellcheck</str>
+     </arr>-->
+     <!--
+     <str name="url-scheme">httpx</str>
+     -->
+  <!--</requestHandler>-->
+  <!-- trivia: the name pinkPony requestHandler was an agreement between the Search API and the
+    apachesolr maintainers. The decision was taken during the Drupalcon Munich codesprint.
+    -->
+  <requestHandler name="pinkPony" class="solr.SearchHandler" default="true">
+    <lst name="defaults">
+      <str name="defType">edismax</str>
+      <str name="echoParams">explicit</str>
+      <bool name="omitHeader">true</bool>
+      <float name="tie">0.01</float>
+      <!-- Don't abort searches for the pinkPony request handler (set in solrcore.properties) -->
+      <int name="timeAllowed">${pinkPony.timeAllowed:-1}</int>
+      <str name="q.alt">*:*</str>
+
+      <!-- By default, don't spell check -->
+      <str name="spellcheck">false</str>
+      <!-- Defaults for the spell checker when used -->
+      <str name="spellcheck.onlyMorePopular">true</str>
+      <str name="spellcheck.extendedResults">false</str>
+      <!--  The number of suggestions to return -->
+      <str name="spellcheck.count">1</str>
+    </lst>
+    <arr name="last-components">
+      <str>spellcheck</str>
+    </arr>
+  </requestHandler>
+
+  <!-- The more like this handler offers many advantages over the standard handler,
+     when performing moreLikeThis requests.-->
+  <requestHandler name="mlt" class="solr.MoreLikeThisHandler">
+    <lst name="defaults">
+      <str name="mlt.mintf">1</str>
+      <str name="mlt.mindf">1</str>
+      <str name="mlt.minwl">3</str>
+      <str name="mlt.maxwl">15</str>
+      <str name="mlt.maxqt">20</str>
+      <str name="mlt.match.include">false</str>
+      <!-- Abort any searches longer than 2 seconds (set in solrcore.properties) -->
+      <int name="timeAllowed">${mlt.timeAllowed:2000}</int>
+    </lst>
+  </requestHandler>
+
+  <!-- XML Update Request Handler.  
+       
+       http://wiki.apache.org/solr/UpdateXmlMessages
+
+       The canonical Request Handler for Modifying the Index through
+       commands specified using XML.
+
+       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'
+    -->
+  <requestHandler name="/update" 
+                  class="solr.XmlUpdateRequestHandler">
+    <!-- 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>
+  <!-- Binary Update Request Handler
+       http://wiki.apache.org/solr/javabin
+    -->
+  <requestHandler name="/update/javabin" 
+                  class="solr.BinaryUpdateRequestHandler" />
+
+  <!-- CSV Update Request Handler
+       http://wiki.apache.org/solr/UpdateCSV
+    -->
+  <requestHandler name="/update/csv" 
+                  class="solr.CSVRequestHandler" 
+                  startup="lazy" />
+
+  <!-- JSON Update Request Handler
+       http://wiki.apache.org/solr/UpdateJSON
+    -->
+  <requestHandler name="/update/json" 
+                  class="solr.JsonUpdateRequestHandler" 
+                  startup="lazy" />
+
+  <!-- Solr Cell Update Request Handler
+
+       http://wiki.apache.org/solr/ExtractingRequestHandler 
+
+    -->
+  <requestHandler name="/update/extract" 
+                  startup="lazy"
+                  class="solr.extraction.ExtractingRequestHandler" >
+    <lst name="defaults">
+      <!-- All the main content goes into "text"... if you need to return
+           the extracted text or do highlighting, use a stored field. -->
+      <str name="fmap.content">text</str>
+      <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>
+
+  <!-- XSLT Update Request Handler
+       Transforms incoming XML with stylesheet identified by tr=
+  -->
+  <requestHandler name="/update/xslt"
+                   startup="lazy"
+                   class="solr.XsltUpdateRequestHandler"/>
+
+  <!-- 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 docuemnts. 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="qt">pinkPony</str>
+      <str name="q">solrpingquery</str>
+    </lst>
+    <lst name="defaults">
+      <str name="echoParams">all</str>
+    </lst>
+  </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 "salves" used for queries.
+
+       http://wiki.apache.org/solr/SolrReplication 
+
+       In the example below, remove the <lst name="master"> section if
+       this is just a slave and remove  the <lst name="slave"> section
+       if this is just a master.
+    -->
+     <requestHandler name="/replication" class="solr.ReplicationHandler" >
+       <lst name="master">
+         <str name="enable">${enable.master:false}</str>
+         <str name="replicateAfter">commit</str>
+         <str name="replicateAfter">startup</str>
+         <str name="confFiles">${confFiles}</str>
+       </lst>
+       <lst name="slave">
+         <str name="enable">${enable.slave:false}</str>
+         <str name="masterUrl">${masterCoreUrl}/replication</str>
+         <str name="pollInterval">${pollTime: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" 
+       
+     -->
+
+  <!-- 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="spellcheck.onlyMorePopular">false</str>
+      <str name="spellcheck.extendedResults">false</str>
+      <str name="spellcheck.count">1</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">
+      <bool name="tv">true</bool>
+    </lst>
+    <arr name="last-components">
+      <str>tvComponent</str>
+    </arr>
+  </requestHandler>
+
+  <!-- Clustering Component
+
+       http://wiki.apache.org/solr/ClusteringComponent
+
+       This relies on third party jars which are notincluded in the
+       release.  To use this component (and the "/clustering" handler)
+       Those jars will need to be downloaded, and you'll need to set
+       the solr.cluster.enabled system property when running solr...
+
+          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">ENGLISH</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">
+          text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
+       </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>
+    </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>
+    </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" 
+                       default="true"
+                       class="solr.highlight.SimpleFragListBuilder"/>
+
+      <!-- Configure the single fragListBuilder -->
+      <fragListBuilder name="single" 
+                       class="solr.highlight.SingleFragListBuilder"/>
+
+      <!-- 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>
+    -->
+
+    <!--
+       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>
+    -->
+ 
+  <!-- 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...
+    -->
+    <!-- The solr.velocity.enabled flag is used by Solr's test cases so that this response writer is not
+         loaded (causing an error if contrib/velocity has not been built fully) -->
+    <!-- <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" enable="${solr.velocity.enabled:true}"/> -->
+  
+
+  <!-- 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" />
+    -->
+
+  <!-- Legacy config for the admin interface -->
+  <admin>
+    <defaultQuery>*:*</defaultQuery>
+
+    <!-- configure a healthcheck file for servers behind a
+         loadbalancer 
+      -->
+    <!--
+       <healthcheck type="file">server-enabled</healthcheck>
+      -->
+  </admin>
+
+  <!-- Following is a dynamic way to include other components or any customized solrconfig.xml stuff, added by other contrib modules -->
+  <xi:include href="solrconfig_extra.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:fallback></xi:fallback>
+  </xi:include>
+
+</config>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/solrconfig_extra.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,79 @@
+<!-- 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, and
+     written to disk
+  -->
+<lst name="spellchecker">
+  <str name="name">default</str>
+  <str name="field">content</str>
+  <str name="spellcheckIndexDir">spellchecker</str>
+  <!-- uncomment this to require terms to occur in 1% of the documents in order to be included in the dictionary
+    <float name="thresholdTokenFrequency">.01</float>
+  -->
+</lst>
+
+<!--
+  Adding German spellhecker index to our Solr index
+  This also requires to enable the content in schema_extra_types.xml and schema_extra_fields.xml
+-->
+<!--
+<lst name="spellchecker">
+  <str name="name">spellchecker_de</str>
+  <str name="field">spell_de</str>
+  <str name="spellcheckIndexDir">./spellchecker_de</str>
+  <str name="buildOnOptimize">true</str>
+</lst>
+-->
+
+<!-- a spellchecker that uses a different distance measure -->
+<!--
+   <lst name="spellchecker">
+     <str name="name">jarowinkler</str>
+     <str name="field">spell</str>
+     <str name="distanceMeasure">
+       org.apache.lucene.search.spell.JaroWinklerDistance
+     </str>
+     <str name="spellcheckIndexDir">spellcheckerJaro</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="spellcheckIndexDir">spellcheckerFreq</str>
+     <str name="comparatorClass">freq</str>
+     <str name="buildOnCommit">true</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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-3.x/solrcore.properties	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,10 @@
+#solrcore.properties for this specific core
+enable.master=false
+enable.slave=false
+pollTime=00:00:60
+masterCoreUrl=http://localhost:8983/solr
+confFiles=schema.xml,mapping-ISOLatin1Accent.txt,protwords.txt,stopwords.txt,synonyms.txt,elevate.xml
+mlt.timeAllowed=2000
+# You should not set your luceneVersion to anything lower then your Solr Version
+luceneVersion=LUCENE_35
+pinkPony.timeAllowed=-1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/protwords.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,8 @@
+#-----------------------------------------------------------------------
+# This file blocks words from being operated on by the stemmer and word delimiter.
+&amp;
+&lt;
+&gt;
+&#039;
+&quot;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/schema.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,547 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ 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.
+
+ For more information, on how to customize this file, please see
+ http://wiki.apache.org/solr/SchemaXml
+-->
+
+<schema name="drupal-4.0-solr-4.x" version="1.3">
+    <!-- attribute "name" is the name of this schema and is only used for display purposes.
+         Applications should change this to reflect the nature of the search collection.
+         version="1.2" 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
+       -->
+  <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 the
+       org.apache.solr.analysis package.
+    -->
+
+    <!-- The StrField type is not analyzed, but indexed/stored verbatim.
+       - StrField and TextField support an optional compressThreshold which
+       limits compression (if enabled in the derived fields) to values which
+       exceed a certain size (in characters).
+    -->
+    <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- boolean type: "true" or "false" -->
+    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
+    <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
+    <fieldtype name="binary" class="solr.BinaryField"/>
+
+    <!-- The optional sortMissingLast and sortMissingFirst attributes are
+         currently supported on types that are sorted internally as strings.
+       - 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.
+    -->
+
+    <!-- numeric field types that can be sorted, but are not optimized for range queries -->
+    <fieldType name="integer" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+
+    <!--
+      Note:
+      These should only be used for compatibility with existing indexes (created with older Solr versions)
+      or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
+
+      Numeric field types that manipulate the value into
+      a string value that isn't human-readable in its internal form,
+      but with a lexicographic ordering the same as the numeric ordering,
+      so that range queries work correctly.
+    -->
+    <fieldType name="sint" class="solr.TrieIntField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="slong" class="solr.TrieFloatField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="sfloat" class="solr.TrieLongField" sortMissingLast="true" omitNorms="true"/>
+    <fieldType name="sdouble" class="solr.TrieDoubleField" sortMissingLast="true" omitNorms="true"/>
+
+    <!--
+     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" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+    <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+
+    <!--
+     The ExternalFileField type gets values from an external file instead of the
+     index. This is useful for data such as rankings that might change frequently
+     and require different update frequencies than the documents they are
+     associated with.
+    -->
+    <fieldType name="pfloat" class="solr.FloatField" omitNorms="true"/>
+    <fieldType name="file" keyField="id" defVal="1" stored="false" indexed="false" class="solr.ExternalFileField" valType="pfloat"/>
+
+    <!-- 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.
+      -->
+    <fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
+
+    <!-- A Trie based date field for faster date range queries and date faceting. -->
+    <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
+
+    <!-- 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
+    <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" omitNorms="true" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- A text field that uses WordDelimiterFilter to enable splitting and matching of
+        words on case-change, alpha numeric boundaries, and non-alphanumeric chars,
+        so that a query of "wifi" or "wi fi" could match a document containing "Wi-Fi".
+        Synonyms and stopwords are customized by external files, and stemming is enabled.
+        Duplicate tokens at the same position (which may result from Stemmed Synonyms or
+        WordDelim parts) are removed.
+        -->
+    <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <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="stopwords.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="1"
+                catenateNumbers="1"
+                catenateAll="0"
+                splitOnCaseChange="1"
+                preserveOriginal="1"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <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.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="0"
+                catenateNumbers="0"
+                catenateAll="0"
+                splitOnCaseChange="1"
+                preserveOriginal="1"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- An unstemmed text field - good if one does not know the language of the field -->
+    <fieldType name="text_und" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="1"
+                catenateNumbers="1"
+                catenateAll="0"
+                splitOnCaseChange="0"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </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="stopwords.txt"
+                enablePositionIncrements="true"
+                />
+        <filter class="solr.WordDelimiterFilterFactory"
+                protected="protwords.txt"
+                generateWordParts="1"
+                generateNumberParts="1"
+                catenateWords="0"
+                catenateNumbers="0"
+                catenateAll="0"
+                splitOnCaseChange="0"/>
+        <filter class="solr.LengthFilterFactory" min="2" max="100" />
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
+    <!-- Edge N gram type - for example for matching against queries with results
+        KeywordTokenizer leaves input string intact as a single term.
+        see: http://www.lucidimagination.com/blog/2009/09/08/auto-suggest-from-popular-queries-using-edgengrams/ 
+      -->
+    <fieldType name="edge_n2_kw_text" class="solr.TextField" omitNorms="true" positionIncrementGap="100">
+      <analyzer type="index">
+        <tokenizer class="solr.KeywordTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="25" />
+      </analyzer>
+      <analyzer type="query">
+        <tokenizer class="solr.KeywordTokenizerFactory"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    <!--  Setup simple analysis for spell checking -->
+
+    <fieldType name="textSpell" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory" />
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
+        <filter class="solr.LengthFilterFactory" min="4" max="20" />
+        <filter class="solr.LowerCaseFilterFactory" />
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory" />
+      </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="sortString" 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 refrences to portions of the orriginal
+            string matched by the pattern.
+
+            See the Java Regular Expression documentation for more
+            infomation on pattern and replacement string syntax.
+
+            http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
+
+        <filter class="solr.PatternReplaceFilterFactory"
+               pattern="(^\p{Punct}+)" replacement="" replace="all"
+        />
+        -->
+      </analyzer>
+    </fieldType>
+
+    <!-- A random sort type -->
+    <fieldType name="rand" class="solr.RandomSortField" indexed="true" />
+
+    <!-- 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" class="solr.StrField" />
+
+    <!-- Begin added types to use features in Solr 3.4+ -->
+    <fieldType name="point" class="solr.PointType" dimension="2" subFieldType="tdouble"/>
+
+    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
+    <fieldType name="location" class="solr.LatLonType" subFieldType="tdouble"/>
+
+    <!-- A Geohash is a compact representation of a latitude longitude pair in a single field.
+         See http://wiki.apache.org/solr/SpatialSearch
+     -->
+    <fieldtype name="geohash" class="solr.GeoHashField"/>
+    <!-- End added Solr 3.4+ types -->
+
+  </types>
+
+  <!-- Following is a dynamic way to include other types, added by other contrib modules -->
+  <xi:include href="schema_extra_types.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:fallback></xi:fallback>
+  </xi:include>
+
+  <fields>
+    <!-- Valid attributes for fields:
+      name: mandatory - the name for the field
+      type: mandatory - the name of a previously defined type from the <types> section
+      indexed: true if this field should be indexed (searchable or sortable)
+      stored: true if this field should be retrievable
+      compressed: [false] if this field should be stored using gzip compression
+       (this will only apply if the field type is compressable; among
+       the standard field types, only TextField and StrField are)
+      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.
+    -->
+
+    <!-- The document id is usually derived from a site-spcific key (hash) and the
+      entity type and ID like:
+      Search Api :
+        The format used is $document->id = $index_id . '-' . $item_id
+      Apache Solr Search Integration
+        The format used is $document->id = $site_hash . '/' . $entity_type . '/' . $entity->id;
+    -->
+    <field name="id" type="string" indexed="true" stored="true" required="true" />
+
+    <!-- Search Api specific fields -->
+    <!-- item_id contains the entity ID, e.g. a node's nid. -->
+    <field name="item_id"  type="string" indexed="true" stored="true" />
+    <!-- index_id is the machine name of the search index this entry belongs to. -->
+    <field name="index_id" type="string" indexed="true" stored="true" />
+    <!-- Since sorting by ID is explicitly allowed, store item_id also in a sortable way. -->
+    <copyField source="item_id" dest="sort_search_api_id" />
+
+    <!-- Apache Solr Search Integration specific fields -->
+    <!-- entity_id is the numeric object ID, e.g. Node ID, File ID -->
+    <field name="entity_id"  type="long" indexed="true" stored="true" />
+    <!-- entity_type is 'node', 'file', 'user', or some other Drupal object type -->
+    <field name="entity_type" type="string" indexed="true" stored="true" />
+    <!-- bundle is a node type, or as appropriate for other entity types -->
+    <field name="bundle" type="string" indexed="true" stored="true"/>
+    <field name="bundle_name" type="string" indexed="true" stored="true"/>
+    <field name="site" type="string" indexed="true" stored="true"/>
+    <field name="hash" type="string" indexed="true" stored="true"/>
+    <field name="url" type="string" indexed="true" stored="true"/>
+    <!-- label is the default field for a human-readable string for this entity (e.g. the title of a node) -->
+    <field name="label" type="text" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+    <!-- The string version of the title is used for sorting -->
+    <copyField source="label" dest="sort_label"/>
+
+    <!-- content is the default field for full text search - dump crap here -->
+    <field name="content" type="text" indexed="true" stored="true" termVectors="true"/>
+    <field name="teaser" type="text" indexed="false" stored="true"/>
+    <field name="path" type="string" indexed="true" stored="true"/>
+    <field name="path_alias" type="text" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+
+    <!-- These are the fields that correspond to a Drupal node. The beauty of having
+      Lucene store title, body, type, etc., is that we retrieve them with the search
+      result set and don't need to go to the database with a node_load. -->
+    <field name="tid"  type="long" indexed="true" stored="true" multiValued="true"/>
+    <field name="taxonomy_names" type="text" indexed="true" stored="false" termVectors="true" multiValued="true" omitNorms="true"/>
+    <!-- Copy terms to a single field that contains all taxonomy term names -->
+    <copyField source="tm_vid_*" dest="taxonomy_names"/>
+
+    <!-- Here, default is used to create a "timestamp" field indicating
+         when each document was indexed.-->
+    <field name="timestamp" type="tdate" indexed="true" stored="true" default="NOW" multiValued="false"/>
+
+    <!-- This field is used to build the spellchecker index -->
+    <field name="spell" type="textSpell" indexed="true" stored="true" multiValued="true"/>
+
+    <!-- copyField commands copy one field to another at the time a document
+         is added to the index.  It's used either to index the same field differently,
+         or to add multiple fields to the same field for easier/faster searching.  -->
+    <copyField source="label" dest="spell"/>
+    <copyField source="content" dest="spell"/>
+
+    <copyField source="ts_*" dest="spell"/>
+    <copyField source="tm_*" dest="spell"/>
+
+    <!-- Dynamic field definitions.  If a field name is not found, dynamicFields
+         will be used if the name matches any of the patterns.
+         RESTRICTION: the glob-like pattern in the name attribute must have
+         a "*" only at the start or the end.
+         EXAMPLE:  name="*_i" will match any field ending in _i (like myid_i, z_i)
+         Longer patterns will be matched first.  if equal size patterns
+         both match, the first appearing in the schema will be used.  -->
+
+    <!-- A set of fields to contain text extracted from HTML tag contents which we
+         can boost at query time. -->
+    <dynamicField name="tags_*" type="text"   indexed="true" stored="false" omitNorms="true"/>
+
+    <!-- For 2 and 3 letter prefix dynamic fields, the 1st letter indicates the data type and
+         the last letter is 's' for single valued, 'm' for multi-valued -->
+
+    <!-- We use long for integer since 64 bit ints are now common in PHP. -->
+    <dynamicField name="is_*"  type="long"    indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="im_*"  type="long"    indexed="true"  stored="true" multiValued="true"/>
+    <!-- List of floats can be saved in a regular float field -->
+    <dynamicField name="fs_*"  type="float"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="fm_*"  type="float"   indexed="true"  stored="true" multiValued="true"/>
+    <!-- List of doubles can be saved in a regular double field -->
+    <dynamicField name="ps_*"  type="double"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="pm_*"  type="double"   indexed="true"  stored="true" multiValued="true"/>
+    <!-- List of booleans can be saved in a regular boolean field -->
+    <dynamicField name="bm_*"  type="boolean" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="bs_*"  type="boolean" indexed="true"  stored="true" multiValued="false"/>
+    <!-- Regular text (without processing) can be stored in a string field-->
+    <dynamicField name="ss_*"  type="string"  indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="sm_*"  type="string"  indexed="true"  stored="true" multiValued="true"/>
+    <!-- Normal text fields are for full text - the relevance of a match depends on the length of the text -->
+    <dynamicField name="ts_*"  type="text"    indexed="true"  stored="true" multiValued="false" termVectors="true"/>
+    <dynamicField name="tm_*"  type="text"    indexed="true"  stored="true" multiValued="true" termVectors="true"/>
+    <!-- Unstemmed text fields for full text - the relevance of a match depends on the length of the text -->
+    <dynamicField name="tus_*" type="text_und" indexed="true"  stored="true" multiValued="false" termVectors="true"/>
+    <dynamicField name="tum_*" type="text_und" indexed="true"  stored="true" multiValued="true" termVectors="true"/>
+    <!-- These text fields omit norms - useful for extracted text like taxonomy_names -->
+    <dynamicField name="tos_*" type="text"    indexed="true"  stored="true" multiValued="false" termVectors="true" omitNorms="true"/>
+    <dynamicField name="tom_*" type="text"    indexed="true"  stored="true" multiValued="true" termVectors="true" omitNorms="true"/>
+    <!-- Special-purpose text fields -->
+    <dynamicField name="tes_*" type="edge_n2_kw_text" indexed="true" stored="true" multiValued="false" omitTermFreqAndPositions="true" />
+    <dynamicField name="tem_*" type="edge_n2_kw_text" indexed="true" stored="true" multiValued="true" omitTermFreqAndPositions="true" />
+    <dynamicField name="tws_*" type="text_ws" indexed="true" stored="true" multiValued="false"/>
+    <dynamicField name="twm_*" type="text_ws" indexed="true" stored="true" multiValued="true"/>
+
+    <!-- trie dates are preferred, so give them the 2 letter prefix -->
+    <dynamicField name="ds_*"  type="tdate"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="dm_*"  type="tdate"   indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="its_*" type="tlong"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="itm_*" type="tlong"   indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="fts_*" type="tfloat"  indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ftm_*" type="tfloat"  indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="pts_*" type="tdouble" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ptm_*" type="tdouble" indexed="true"  stored="true" multiValued="true"/>
+    <!-- Binary fields can be populated using base64 encoded data. Useful e.g. for embedding
+         a small image in a search result using the data URI scheme -->
+    <dynamicField name="xs_*"  type="binary"  indexed="false" stored="true" multiValued="false"/>
+    <dynamicField name="xm_*"  type="binary"  indexed="false" stored="true" multiValued="true"/>
+    <!-- In rare cases a date rather than tdate is needed for sortMissingLast -->
+    <dynamicField name="dds_*" type="date"    indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ddm_*" type="date"    indexed="true"  stored="true" multiValued="true"/>
+    <!-- Sortable fields, good for sortMissingLast support &
+         We use long for integer since 64 bit ints are now common in PHP. -->
+    <dynamicField name="iss_*" type="slong"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="ism_*" type="slong"   indexed="true"  stored="true" multiValued="true"/>
+    <!-- In rare cases a sfloat rather than tfloat is needed for sortMissingLast -->
+    <dynamicField name="fss_*" type="sfloat"  indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="fsm_*" type="sfloat"  indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="pss_*" type="sdouble" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="psm_*" type="sdouble" indexed="true"  stored="true" multiValued="true"/>
+    <!-- In case a 32 bit int is really needed, we provide these fields. 'h' is mnemonic for 'half word', i.e. 32 bit on 64 arch -->
+    <dynamicField name="hs_*" type="integer" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="hm_*" type="integer" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="hss_*" type="sint"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="hsm_*" type="sint"   indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="hts_*" type="tint"   indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="htm_*" type="tint"   indexed="true"  stored="true" multiValued="true"/>
+
+    <!-- Unindexed string fields that can be used to store values that won't be searchable -->
+    <dynamicField name="zs_*" type="string"   indexed="false"  stored="true" multiValued="false"/>
+    <dynamicField name="zm_*" type="string"   indexed="false"  stored="true" multiValued="true"/>
+
+    <!-- Begin added fields to use features in Solr 3.4+
+         http://wiki.apache.org/solr/SpatialSearch#geodist_-_The_distance_function -->
+    <dynamicField name="points_*" type="point" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="pointm_*" type="point" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="locs_*" type="location" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="locm_*" type="location" indexed="true"  stored="true" multiValued="true"/>
+    <dynamicField name="geos_*" type="geohash" indexed="true"  stored="true" multiValued="false"/>
+    <dynamicField name="geom_*" type="geohash" indexed="true"  stored="true" multiValued="true"/>
+
+    <!-- External file fields -->
+    <dynamicField name="eff_*" type="file"/>
+    <!-- End added fields for Solr 3.4+ -->
+
+    <!-- Sortable version of the dynamic string field -->
+    <dynamicField name="sort_*" type="sortString" indexed="true" stored="false"/>
+    <copyField source="ss_*" dest="sort_*"/>
+    <!-- A random sort field -->
+    <dynamicField name="random_*" type="rand" indexed="true" stored="true"/>
+    <!-- This field is used to store access information (e.g. node access grants), as opposed to field data -->
+    <dynamicField name="access_*" type="integer" indexed="true" stored="false" multiValued="true"/>
+
+    <!-- The following causes solr 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" />
+
+  </fields>
+
+  <!-- Following is a dynamic way to include other fields, added by other contrib modules -->
+  <xi:include href="schema_extra_fields.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:fallback></xi:fallback>
+  </xi:include>
+
+  <!-- Field to use to determine and enforce document uniqueness.
+       Unless this field is marked with required="false", it will be a required field
+    -->
+  <uniqueKey>id</uniqueKey>
+
+  <!-- field for the QueryParser to use when an explicit fieldname is absent -->
+  <defaultSearchField>content</defaultSearchField>
+
+  <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
+  <solrQueryParser defaultOperator="AND"/>
+
+</schema>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/schema_extra_fields.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,23 @@
+<fields>
+<!--
+  Adding German dynamic field types to our Solr Schema
+  If you enable this, make sure you have a folder called lang with stopwords_de.txt
+  and synonyms_de.txt in there
+  This also requires to enable the content in schema_extra_types.xml
+-->
+<!--
+   <field name="label_de" type="text_de" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+   <field name="content_de" type="text_de" indexed="true" stored="true" termVectors="true"/>
+   <field name="teaser_de" type="text_de" indexed="false" stored="true"/>
+   <field name="path_alias_de" type="text_de" indexed="true" stored="true" termVectors="true" omitNorms="true"/>
+   <field name="taxonomy_names_de" type="text_de" indexed="true" stored="false" termVectors="true" multiValued="true" omitNorms="true"/>
+   <field name="spell_de" type="text_de" indexed="true" stored="true" multiValued="true"/>
+   <copyField source="label_de" dest="spell_de"/>
+   <copyField source="content_de" dest="spell_de"/>
+   <dynamicField name="tags_de_*" type="text_de" indexed="true" stored="false" omitNorms="true"/>
+   <dynamicField name="ts_de_*" type="text_de" indexed="true" stored="true" multiValued="false" termVectors="true"/>
+   <dynamicField name="tm_de_*" type="text_de" indexed="true" stored="true" multiValued="true" termVectors="true"/>
+   <dynamicField name="tos_de_*" type="text_de" indexed="true" stored="true" multiValued="false" termVectors="true" omitNorms="true"/>
+   <dynamicField name="tom_de_*" type="text_de" indexed="true" stored="true" multiValued="true" termVectors="true" omitNorms="true"/>
+-->
+</fields>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/schema_extra_types.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,30 @@
+<types>
+<!--
+  Adding German language to our Solr Schema German
+  If you enable this, make sure you have a folder called lang with stopwords_de.txt
+  and synonyms_de.txt in there
+-->
+<!--
+    <fieldType name="text_de" class="solr.TextField" positionIncrementGap="100">
+      <analyzer type="index">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.StopFilterFactory" words="lang/stopwords_de.txt" format="snowball" ignoreCase="true" enablePositionIncrements="true"/>
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnCaseChange="1" splitOnNumerics="1" catenateWords="1" catenateNumbers="1" catenateAll="0" protected="protwords.txt" preserveOriginal="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.GermanLightStemFilterFactory"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+      <analyzer type="query">
+        <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
+        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
+        <filter class="solr.SynonymFilterFactory" synonyms="lang/synonyms_de.txt" ignoreCase="true" expand="true"/>
+        <filter class="solr.StopFilterFactory" words="lang/stopwords_de.txt" format="snowball" ignoreCase="true" enablePositionIncrements="true"/>
+        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" splitOnCaseChange="1" splitOnNumerics="1" catenateWords="0" catenateNumbers="0" catenateAll="0" protected="protwords.txt" preserveOriginal="1"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.GermanLightStemFilterFactory"/>
+        <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
+      </analyzer>
+    </fieldType>
+-->
+</types>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/solrconfig.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1575 @@
+<?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 name="drupal-4.0-solr-4.x" >
+  <!-- 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.
+    -->
+
+  <!-- Set this to 'false' if you want solr to continue working after
+       it has encountered an severe configuration error.  In a
+       production environment, you may want solr to keep working even
+       if one handler is mis-configured.
+
+       You may also set this to false using by setting the system
+       property:
+
+         -Dsolr.abortOnConfigurationError=false
+    -->
+  <abortOnConfigurationError>${solr.abortOnConfigurationError:true}</abortOnConfigurationError>
+  
+  <!-- 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>${luceneVersion:LUCENE_35}</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.
+
+       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.
+    -->
+  <lib dir="../../contrib/extraction/lib" />
+  <lib dir="../../contrib/clustering/lib/" />
+  <!-- The velocity library has been known to crash Solr in some
+       instances when deployed as a war file to Tomcat. Therefore all
+       references have been removed from the default configuration.
+       @see http://drupal.org/node/1612556
+  -->
+  <!-- <lib dir="../../contrib/velocity/lib" /> -->
+
+  <!-- When a regex is specified in addition to a directory, only the
+       files in that directory which completely match the regex
+       (anchored on both ends) will be included.
+    -->
+  <!--<lib dir="../../dist/" regex="apache-solr-cell-\d.*\.jar" />-->
+  <!--<lib dir="../../dist/" regex="apache-solr-clustering-\d.*\.jar" />-->
+  <!--<lib dir="../../dist/" regex="apache-solr-dataimporthandler-\d.*\.jar" />-->
+  <!--<lib dir="../../dist/" regex="apache-solr-langid-\d.*\.jar" />-->
+  <!-- <lib dir="../../dist/" regex="apache-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="../../contrib/clustering/lib/" />-->
+  <!--<lib dir="/total/crap/dir/ignored" />-->
+
+  <!-- an exact path can be used to specify a specific 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, the default, is filesystem
+       based and tries to pick the best implementation for the current
+       JVM and platform.  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.StandardDirectoryFactory}"/>
+
+  <!-- Index Defaults
+
+       Values here affect all index writers and act as a default
+       unless overridden.
+
+       WARNING: See also the <mainIndex> section below for parameters
+       that overfor Solr's main Lucene index.
+    -->
+  <indexConfig>
+
+    <useCompoundFile>false</useCompoundFile>
+
+    <mergeFactor>4</mergeFactor>
+    <!-- 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.  -->
+    <ramBufferSizeMB>32</ramBufferSizeMB>
+    <!-- If both ramBufferSizeMB and maxBufferedDocs is set, then
+         Lucene will flush based on whichever limit is hit first.  
+      -->
+    <!-- <maxBufferedDocs>1000</maxBufferedDocs> -->
+
+    <maxMergeDocs>2147483647</maxMergeDocs>
+    <maxFieldLength>100000</maxFieldLength>
+    <writeLockTimeout>1000</writeLockTimeout>
+
+    <!-- Expert: Merge Policy 
+
+         The Merge Policy in Lucene controls how merging is handled by
+         Lucene.  The default in Solr 3.3 is TieredMergePolicy.
+         
+         The default in 2.3 was the LogByteSizeMergePolicy,
+         previous versions used LogDocMergePolicy.
+         
+         LogByteSizeMergePolicy chooses segments to merge based on
+         their size.  The Lucene 2.2 default, LogDocMergePolicy chose
+         when to merge based on number of documents
+         
+         Other implementations of MergePolicy must have a no-argument
+         constructor
+      -->
+    <mergePolicy class="org.apache.lucene.index.LogByteSizeMergePolicy"/>
+
+    <!-- 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
+
+         (For backwards compatibility with Solr 1.2, 'simple' is the
+         default if not specified.)
+
+         More details on the nuances of each LockFactory...
+         http://wiki.apache.org/lucene-java/AvailableLockFactories
+    -->
+    <lockType>single</lockType>
+
+    <!-- Expert: Controls how often Lucene loads terms into memory
+         Default is 128 and is likely good for most everyone.
+      -->
+    <!-- <termIndexInterval>256</termIndexInterval> -->
+
+    <useCompoundFile>false</useCompoundFile>
+    <ramBufferSizeMB>32</ramBufferSizeMB>
+    <mergeFactor>10</mergeFactor>
+
+    <!-- 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.
+
+         This is not needed if lock type is 'none' or 'single'
+     -->
+    <unlockOnStartup>false</unlockOnStartup>
+    
+    <!-- If true, IndexReaders will be reopened (often more efficient)
+         instead of closed and then opened.
+      -->
+    <reopenReaders>true</reopenReaders>
+
+    <!-- Commit Deletion Policy
+
+         Custom deletion policies can specified here. The class must
+         implement org.apache.lucene.index.IndexDeletionPolicy.
+
+         http://lucene.apache.org/java/2_9_1/api/all/org/apache/lucene/index/IndexDeletionPolicy.html
+
+         The standard 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 <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 that is allowed to pass
+                   since a document was added before automaticly
+                   triggering a new commit. 
+      -->
+    <autoCommit> 
+      <maxDocs>10000</maxDocs>
+      <maxTime>120000</maxTime> 
+    </autoCommit>
+
+    <!-- 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>
+      -->
+  </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>
+    <!-- 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="32"/>
+   
+    <!-- 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">solr rocks</str><str name="start">0</str><str name="rows">10</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 affects the behavior of requests such as /select?qt=XXX
+
+       handleSelect="true" will cause the SolrDispatchFilter to process
+       the request and will result in consistent error handling and
+       formatting for all types of requests.
+
+       handleSelect="false" will cause the SolrDispatchFilter to
+       ignore "/select" requests and fallback to using the legacy
+       SolrServlet and it's Solr 1.1 style error formatting
+    -->
+  <requestDispatcher handleSelect="true" >
+    <!-- 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>
+
+  <!-- Request Handlers 
+
+       http://wiki.apache.org/solr/SolrRequestHandler
+
+       incoming queries will be dispatched to the correct handler
+       based on the path or the qt (query type) param.
+
+       Names starting with a '/' are accessed with the a path equal to
+       the registered name.  Names without a leading '/' are accessed
+       with: http://host/app/[core/]select?qt=name
+
+       If a /select request is processed with out a qt param
+       specified, the requestHandler that declares default="true" will
+       be used.
+       
+       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="search" class="solr.SearchHandler" default="true">-->
+    <!-- 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>
+     </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 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">Solritas</str>
+
+       <str name="defType">edismax</str>
+       <str name="q.alt">*:*</str>
+       <str name="rows">10</str>
+       <str name="fl">*,score</str>
+       <str name="mlt.qf">
+         text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
+       </str>
+       <str name="mlt.fl">text,features,name,sku,id,manu,cat</str>
+       <int name="mlt.count">3</int>
+
+       <str name="qf">
+          text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
+       </str>
+
+       <str name="facet">on</str>
+       <str name="facet.field">cat</str>
+       <str name="facet.field">manu_exact</str>
+       <str name="facet.query">ipod</str>
+       <str name="facet.query">GB</str>
+       <str name="facet.mincount">1</str>
+       <str name="facet.pivot">cat,inStock</str>
+       <str name="facet.range.other">after</str>
+       <str name="facet.range">price</str>
+       <int name="f.price.facet.range.start">0</int>
+       <int name="f.price.facet.range.end">600</int>
+       <int name="f.price.facet.range.gap">50</int>
+       <str name="facet.range">popularity</str>
+       <int name="f.popularity.facet.range.start">0</int>
+       <int name="f.popularity.facet.range.end">10</int>
+       <int name="f.popularity.facet.range.gap">3</int>
+       <str name="facet.range">manufacturedate_dt</str>
+       <str name="f.manufacturedate_dt.facet.range.start">NOW/YEAR-10YEARS</str>
+       <str name="f.manufacturedate_dt.facet.range.end">NOW</str>
+       <str name="f.manufacturedate_dt.facet.range.gap">+1YEAR</str>
+       <str name="f.manufacturedate_dt.facet.range.other">before</str>
+       <str name="f.manufacturedate_dt.facet.range.other">after</str>-->
+
+
+       <!-- Highlighting defaults -->
+       <!--<str name="hl">on</str>
+       <str name="hl.fl">text features name</str>
+       <str name="f.name.hl.fragsize">0</str>
+       <str name="f.name.hl.alternateField">name</str>
+     </lst>
+     <arr name="last-components">
+       <str>spellcheck</str>
+     </arr>-->
+     <!--
+     <str name="url-scheme">httpx</str>
+     -->
+  <!--</requestHandler>-->
+  <!-- trivia: the name pinkPony requestHandler was an agreement between the Search API and the
+    apachesolr maintainers. The decision was taken during the Drupalcon Munich codesprint.
+    -->
+  <requestHandler name="pinkPony" class="solr.SearchHandler" default="true">
+    <lst name="defaults">
+      <str name="defType">edismax</str>
+      <str name="echoParams">explicit</str>
+      <bool name="omitHeader">true</bool>
+      <float name="tie">0.01</float>
+      <!-- Don't abort searches for the pinkPony request handler (set in solrcore.properties) -->
+      <int name="timeAllowed">${pinkPony.timeAllowed:-1}</int>
+      <str name="q.alt">*:*</str>
+
+      <!-- By default, don't spell check -->
+      <str name="spellcheck">false</str>
+      <!-- Defaults for the spell checker when used -->
+      <str name="spellcheck.onlyMorePopular">true</str>
+      <str name="spellcheck.extendedResults">false</str>
+      <!--  The number of suggestions to return -->
+      <str name="spellcheck.count">1</str>
+    </lst>
+    <arr name="last-components">
+      <str>spellcheck</str>
+    </arr>
+  </requestHandler>
+
+  <!-- The more like this handler offers many advantages over the standard handler,
+     when performing moreLikeThis requests.-->
+  <requestHandler name="mlt" class="solr.MoreLikeThisHandler">
+    <lst name="defaults">
+      <str name="mlt.mintf">1</str>
+      <str name="mlt.mindf">1</str>
+      <str name="mlt.minwl">3</str>
+      <str name="mlt.maxwl">15</str>
+      <str name="mlt.maxqt">20</str>
+      <str name="mlt.match.include">false</str>
+      <!-- Abort any searches longer than 2 seconds (set in solrcore.properties) -->
+      <int name="timeAllowed">${mlt.timeAllowed:2000}</int>
+    </lst>
+  </requestHandler>
+
+  <!-- XML Update Request Handler.  
+       
+       http://wiki.apache.org/solr/UpdateXmlMessages
+
+       The canonical Request Handler for Modifying the Index through
+       commands specified using XML.
+
+       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'
+    -->
+  <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>
+  <!-- Binary Update Request Handler
+       http://wiki.apache.org/solr/javabin
+    -->
+  <requestHandler name="/update/javabin" 
+                  class="solr.UpdateRequestHandler" />
+
+  <!-- CSV Update Request Handler
+       http://wiki.apache.org/solr/UpdateCSV
+    -->
+  <requestHandler name="/update/csv" 
+                  class="solr.CSVRequestHandler" 
+                  startup="lazy" />
+
+  <!-- JSON Update Request Handler
+       http://wiki.apache.org/solr/UpdateJSON
+    -->
+  <requestHandler name="/update/json" 
+                  class="solr.JsonUpdateRequestHandler" 
+                  startup="lazy" />
+
+  <!-- Solr Cell Update Request Handler
+
+       http://wiki.apache.org/solr/ExtractingRequestHandler 
+
+    -->
+  <requestHandler name="/update/extract" 
+                  startup="lazy"
+                  class="solr.extraction.ExtractingRequestHandler" >
+    <lst name="defaults">
+      <!-- All the main content goes into "text"... if you need to return
+           the extracted text or do highlighting, use a stored field. -->
+      <str name="fmap.content">text</str>
+      <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>
+
+  <!-- XSLT Update Request Handler
+       Transforms incoming XML with stylesheet identified by tr=
+  -->
+  <requestHandler name="/update/xslt"
+                   startup="lazy"
+                   class="solr.XsltUpdateRequestHandler"/>
+
+  <!-- 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 docuemnts. 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="qt">pinkPony</str>
+      <str name="q">solrpingquery</str>
+    </lst>
+    <lst name="defaults">
+      <str name="echoParams">all</str>
+    </lst>
+  </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 "salves" used for queries.
+
+       http://wiki.apache.org/solr/SolrReplication 
+
+       In the example below, remove the <lst name="master"> section if
+       this is just a slave and remove  the <lst name="slave"> section
+       if this is just a master.
+    -->
+     <requestHandler name="/replication" class="solr.ReplicationHandler" >
+       <lst name="master">
+         <str name="enable">${enable.master:false}</str>
+         <str name="replicateAfter">commit</str>
+         <str name="replicateAfter">startup</str>
+         <str name="confFiles">${confFiles}</str>
+       </lst>
+       <lst name="slave">
+         <str name="enable">${enable.slave:false}</str>
+         <str name="masterUrl">${masterCoreUrl}/replication</str>
+         <str name="pollInterval">${pollTime: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" 
+       
+     -->
+
+  <!-- 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="spellcheck.onlyMorePopular">false</str>
+      <str name="spellcheck.extendedResults">false</str>
+      <str name="spellcheck.count">1</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">
+      <bool name="tv">true</bool>
+    </lst>
+    <arr name="last-components">
+      <str>tvComponent</str>
+    </arr>
+  </requestHandler>
+
+  <!-- Clustering Component
+
+       http://wiki.apache.org/solr/ClusteringComponent
+
+       This relies on third party jars which are notincluded in the
+       release.  To use this component (and the "/clustering" handler)
+       Those jars will need to be downloaded, and you'll need to set
+       the solr.cluster.enabled system property when running solr...
+
+          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">ENGLISH</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">
+          text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
+       </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>
+    </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>
+    </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" 
+                       default="true"
+                       class="solr.highlight.SimpleFragListBuilder"/>
+
+      <!-- Configure the single fragListBuilder -->
+      <fragListBuilder name="single" 
+                       class="solr.highlight.SingleFragListBuilder"/>
+
+      <!-- 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>
+    -->
+
+    <!--
+       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>
+    -->
+ 
+  <!-- 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...
+    -->
+    <!-- The solr.velocity.enabled flag is used by Solr's test cases so that this response writer is not
+         loaded (causing an error if contrib/velocity has not been built fully) -->
+    <!-- <queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" enable="${solr.velocity.enabled:true}"/> -->
+  
+
+  <!-- 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" />
+    -->
+
+  <!-- Legacy config for the admin interface -->
+  <admin>
+    <defaultQuery>*:*</defaultQuery>
+
+    <!-- configure a healthcheck file for servers behind a
+         loadbalancer 
+      -->
+    <!--
+       <healthcheck type="file">server-enabled</healthcheck>
+      -->
+  </admin>
+
+  <!-- Following is a dynamic way to include other components or any customized solrconfig.xml stuff, added by other contrib modules -->
+  <xi:include href="solrconfig_extra.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:fallback></xi:fallback>
+  </xi:include>
+
+</config>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/solrconfig_extra.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,79 @@
+<!-- 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, and
+     written to disk
+  -->
+<lst name="spellchecker">
+  <str name="name">default</str>
+  <str name="field">content</str>
+  <str name="spellcheckIndexDir">spellchecker</str>
+  <!-- uncomment this to require terms to occur in 1% of the documents in order to be included in the dictionary
+    <float name="thresholdTokenFrequency">.01</float>
+  -->
+</lst>
+
+<!--
+  Adding German spellhecker index to our Solr index
+  This also requires to enable the content in schema_extra_types.xml and schema_extra_fields.xml
+-->
+<!--
+<lst name="spellchecker">
+  <str name="name">spellchecker_de</str>
+  <str name="field">spell_de</str>
+  <str name="spellcheckIndexDir">./spellchecker_de</str>
+  <str name="buildOnOptimize">true</str>
+</lst>
+-->
+
+<!-- a spellchecker that uses a different distance measure -->
+<!--
+   <lst name="spellchecker">
+     <str name="name">jarowinkler</str>
+     <str name="field">spell</str>
+     <str name="distanceMeasure">
+       org.apache.lucene.search.spell.JaroWinklerDistance
+     </str>
+     <str name="spellcheckIndexDir">spellcheckerJaro</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="spellcheckIndexDir">spellcheckerFreq</str>
+     <str name="comparatorClass">freq</str>
+     <str name="buildOnCommit">true</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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/solr-conf/solr-4.x/solrcore.properties	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,10 @@
+#solrcore.properties for this specific core
+enable.master=false
+enable.slave=false
+pollTime=00:00:60
+masterCoreUrl=http://localhost:8983/solr
+confFiles=schema.xml,mapping-ISOLatin1Accent.txt,protwords.txt,stopwords.txt,synonyms.txt,elevate.xml
+mlt.timeAllowed=2000
+# You should not set your luceneVersion to anything lower then your Solr Version
+luceneVersion=LUCENE_40
+pinkPony.timeAllowed=-1
Binary file sites/all/modules/custom/solrconnect/task-check.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/Dummy_Solr.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,474 @@
+<?php
+
+/**
+ * @file
+ *   Dummy object to simulate a Solr Service
+ *
+ */
+class DummySolr implements DrupalApacheSolrServiceInterface {
+
+  function getId() {
+    return __CLASS__;
+  }
+
+  function getFields($num_terms = 0) {
+    return (object) array(
+       'is_uid' =>
+      (object) array(
+         'type' => 'long',
+         'schema' => 'I-S----OF-----',
+      ),
+       'content' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => 'ITS-V---------',
+      ),
+       'im_3_field_tags' =>
+      (object) array(
+         'type' => 'long',
+         'schema' => 'I-SM---OF-----',
+         'dynamicBase' => 'im_*',
+      ),
+       'entity_type' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'ds_last_comment_or_change' =>
+      (object) array(
+         'type' => 'tdate',
+         'schema' => 'ITS----OF-----',
+      ),
+       'nodeaccess_ari4jj_node_access_example_view' =>
+      (object) array(
+         'type' => 'integer',
+         'schema' => 'I--M---OF-----',
+         'dynamicBase' => 'nodeaccess*',
+      ),
+       'entity_id' =>
+      (object) array(
+         'type' => 'tlong',
+         'schema' => 'ITS----OF-----',
+      ),
+       'ds_changed' =>
+      (object) array(
+         'type' => 'tdate',
+         'schema' => 'ITS----OF-----',
+      ),
+       'id' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'timestamp' =>
+      (object) array(
+         'type' => 'date',
+         'schema' => 'I-S----OF----l',
+      ),
+       'label' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => 'ITS-V--O------',
+      ),
+       'nodeaccess_ari4jj_node_access_example_edit' =>
+      (object) array(
+         'type' => 'integer',
+         'schema' => 'I--M---OF-----',
+         'dynamicBase' => 'nodeaccess*',
+      ),
+       'ds_created' =>
+      (object) array(
+         'type' => 'tdate',
+         'schema' => 'ITS----OF-----',
+      ),
+       'ss_name' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => 'ITS-V---------',
+      ),
+       'path' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'taxonomy_names' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => 'IT-MV--O------',
+      ),
+       'bundle' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'tid' =>
+      (object) array(
+         'type' => 'long',
+         'schema' => 'I-SM---OF-----',
+      ),
+       'is_tnid' =>
+      (object) array(
+         'type' => 'long',
+         'schema' => 'I-S----OF-----',
+      ),
+       'nodeaccess_ari4jj_node_access_example_author' =>
+      (object) array(
+         'type' => 'integer',
+         'schema' => 'I--M---OF-----',
+         'dynamicBase' => 'nodeaccess*',
+      ),
+       'tm_vid_1_names' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => 'ITSMV---------',
+         'dynamicBase' => 'tm_*',
+      ),
+       'spell' =>
+      (object) array(
+         'type' => 'textSpell',
+         'schema' => 'ITSM----------',
+      ),
+       'site' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'is_comment_count' =>
+      (object) array(
+         'type' => 'tint',
+         'schema' => 'ITS----OF-----',
+      ),
+       'bundle_name' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'hash' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'bs_status' =>
+      (object) array(
+         'type' => 'boolean',
+         'schema' => 'I-S----OF----l',
+      ),
+       'entity_id' =>
+      (object) array(
+         'type' => 'long',
+         'schema' => 'I-S----OF-----',
+      ),
+       'url' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'nodeaccess_all' =>
+      (object) array(
+         'type' => 'integer',
+         'schema' => 'I--M---OF-----',
+         'dynamicBase' => 'nodeaccess*',
+      ),
+       'sort_name' =>
+      (object) array(
+         'type' => 'sortString',
+         'schema' => 'IT-----O-----l',
+      ),
+       'tags_a' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => 'IT-----O------',
+         'dynamicBase' => 'tags_*',
+      ),
+       'bs_sticky' =>
+      (object) array(
+         'type' => 'boolean',
+         'schema' => 'I-S----OF----l',
+      ),
+       'bs_promote' =>
+      (object) array(
+         'type' => 'boolean',
+         'schema' => 'I-S----OF----l',
+      ),
+       'teaser' =>
+      (object) array(
+         'type' => 'text',
+         'schema' => '-TS-----------',
+      ),
+       'im_vid_1' =>
+      (object) array(
+         'type' => 'long',
+         'schema' => 'I-SM---OF-----',
+         'dynamicBase' => 'im_*',
+      ),
+       'bs_translate' =>
+      (object) array(
+         'type' => 'boolean',
+         'schema' => 'I-S----OF----l',
+      ),
+       'sort_label' =>
+      (object) array(
+         'type' => 'sortString',
+         'schema' => 'IT-----O-----l',
+      ),
+       'ss_language' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-S----OF----l',
+      ),
+       'sm_vid_Tags' =>
+      (object) array(
+         'type' => 'string',
+         'schema' => 'I-SM---OF----l',
+         'dynamicBase' => 'sm_*',
+      ),
+    );
+  }
+
+  protected $last_search = array();
+
+  public function search($query = '', array $params = array(), $method = 'GET') {
+    $this->last_search = array('query' => $query, 'params' => $params, 'method' => $method);
+    $response = new stdClass();
+    $response->response = new stdClass();
+    $response->response->numFound = 0;
+    $response->response->docs = array();
+
+    return $response;
+  }
+
+  public function getLastSearch() {
+    return $this->last_search;
+  }
+
+  /**
+   * 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 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) {
+  }
+
+  /**
+   * 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
+   * @param boolean $softCommit optimize by using a softCommit
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function commit($optimize = TRUE, $waitFlush = TRUE, $waitSearcher = TRUE, $timeout = 3600, $softCommit = FALSE) {
+  }
+
+  /**
+   * 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)
+   * @param boolean $softCommit optimize by using a softCommit
+   *
+   * @return response object
+   *
+   * @throws Exception If an error occurs during the service call
+   */
+  function optimize($waitFlush = TRUE, $waitSearcher = TRUE, $timeout = 3600, $softCommit = FALSE) {
+  }
+
+  /**
+   * 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() {
+  }
+
+  /**
+   * 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) { 
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/apachesolr_base.test	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,547 @@
+<?php
+
+abstract class DrupalSolrOfflineWebTestCase extends DrupalWebTestCase {
+  function _nestedCompare($a1, $a2) {
+    if (count($a1) != count($a2)) {
+      $extra1 = array_diff_key($a1, $a2);
+      $extra2 = array_diff_key($a2, $a1);
+      $extra = '';
+      if ($extra1) {
+        $extra .= ' Extra keys in $a1: ' . implode(', ', array_keys($extra1));
+      }
+      if ($extra2) {
+        $extra .= ' Extra keys in $a2: ' . implode(', ', array_keys($extra2));
+      }
+      debug('count($a1) != count($a2) :' . $extra);
+      return FALSE;
+    }
+    foreach ($a1 as $k => $v) {
+      if (!isset($a2[$k])) {
+        debug("\$a2[$k] is not set");
+        return FALSE;
+      }
+      if (is_array($a1[$k]) && is_array($a2[$k])) {
+        if (!$this->_nestedCompare($a1[$k], $a2[$k])) {
+          debug("_nestedCompare(\$a1[$k], \$a2[$k]) is false");
+          return FALSE;
+        }
+      }
+      elseif ($a1[$k] !== $a2[$k]) {
+        debug("\$a1[$k] !== \$a2[$k] : " . var_export($a1[$k], TRUE) . " " . var_export($a2[$k], TRUE));
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+}
+
+/**
+ * @file
+ *   Unit test class that provides tests for base functionality of the Apachesolr
+ *   Module without having the need of a Solr Server
+ */
+class DrupalSolrOfflineEnvironmentWebTestCase extends DrupalSolrOfflineWebTestCase {
+  /**
+   * A global basic user who can search.
+   */
+  var $basic_user;
+
+  /**
+   * A global administrative user who can administer search.
+   */
+  var $admin_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Solr Search Environments',
+      'description' => 'Tests search environments functionality of the Solr module',
+      'group' => 'ApacheSolr',
+    );
+  }
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp('apachesolr', 'search', 'apachesolr_test');
+    // Create a basic user, which is subject to moderation.
+    $permissions = array(
+      'access content',
+      'search content',
+    );
+    $basic_user = $this->drupalCreateUser($permissions);
+
+    // Create an admin user that can bypass revision moderation.
+    $permissions = array(
+      'access content',
+      'search content',
+      'administer nodes',
+      'administer search',
+    );
+    $admin_user = $this->drupalCreateUser($permissions);
+
+    // Assign users to their test suite-wide properties.
+    $this->basic_user = $basic_user;
+    $this->admin_user = $admin_user;
+  }
+
+  /**
+   * Asserts that the module was installed and that a notice appears that the server is offline
+   */
+  function testServerOffline() {
+    // Load the default server.
+    $env_id = apachesolr_default_environment();
+    $environment = apachesolr_environment_load($env_id);
+    $environment['url'] = 'http://localhost/solr/core_that_should_not_exist';
+    apachesolr_environment_save($environment);
+    $status = apachesolr_server_status($environment['url']);
+    $this->assertFalse($status, t('A false URL could not be loaded and is offline'));
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr');
+    $text = t('The server seems to be unavailable. Please verify the server settings');
+    $this->assertText($text, t('When checking the status of the server it gives the correct message to inform the user that the server is not reachable'));
+  }
+
+  /**
+   * Asserts that the module was installed and that a notice appears that the server is offline
+   */
+  function testIndexFileIncluded() {
+    $env_id = apachesolr_default_environment();
+    $environment = apachesolr_environment_load($env_id);
+    $environment['url'] = 'http://localhost/solr/core_that_should_not_exist';
+    apachesolr_environment_save($environment);
+
+    $paths = array(
+      'user',
+      'node',
+      'admin/config/search/apachesolr',
+      'admin/config/search/apachesolr/search-pages',
+      'admin/config/search/apachesolr/search-pages/core_search/edit',
+      'admin/structure/block/manage/apachesolr_search/mlt-001/configure',
+      'admin/config/search/apachesolr/settings/solr/bias',
+      'admin/config/search/apachesolr/settings/solr/index',
+      'admin/config/search/apachesolr/settings/solr/edit',
+      'admin/reports/apachesolr',
+      'admin/reports/apachesolr/conf',
+      'search/site',
+    );
+    $this->drupalLogin($this->admin_user);
+    foreach ($paths as $path) {
+      $this->drupalGet($path);
+      $text = 'apachesolr.index.inc was included';
+      $this->assertNoText($text, t('Apachesolr.index.inc was not included'));
+    }
+  }
+
+  /**
+   * Asserts that we can edit a search environment
+   */
+  function testEditSearchEnvironment() {
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->clickLink(t('Edit'));
+    $this->assertText(t('Example: http://localhost:8983/solr'), t('Edit page was succesfully loaded'));
+    $edit = array('name' => 'new description foo bar', 'url' => 'http://localhost:8983/solr/core_does_not_exists');
+    $this->drupalPost($this->getUrl(), $edit, t('Save'));
+    $this->assertResponse(200);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->assertText(t('new description foo bar'), t('Search environment description was succesfully edited'));
+    $this->assertText('http://localhost:8983/solr/core_does_not_exists', t('Search environment url was succesfully edited'));
+  }
+
+  /**
+   * Asserts that we can use various url forms for the search environment
+   */
+  function testEditSearchEnvironmentURLs() {
+    // Set the various url schemes that will be tested
+    $urls = array(
+      'http://user@localhost:8983/solr/core_does_not_exists',
+      'http://user:pass@localhost:8983/solr/core_does_not_exists',
+      'http://user:pass@localhost/solr/core_does_not_exists',
+      'https://localhost:8983/solr/core_does_not_exists'
+    );
+    $this->drupalLogin($this->admin_user);
+    foreach ($urls as $url) {
+      $this->drupalGet('admin/config/search/apachesolr/settings');
+      $this->clickLink(t('Edit'));
+      $this->assertText(t('Example: http://localhost:8983/solr'), t('Edit page was succesfully loaded'));
+      $edit = array('url' => $url);
+      $this->drupalPost($this->getUrl(), $edit, t('Save'));
+      $this->assertResponse(200);
+      $this->drupalGet('admin/config/search/apachesolr/settings');
+      $this->assertText($url, t('Search environment url was succesfully set to !url', array('!url' => $url)));
+    }
+  }
+
+  /**
+   * Asserts that we can clone a search environment
+   */
+  function testCloneSearchEnvironment() {
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->assertText(t('Clone'), t('Clone button is available'));
+    $this->drupalGet('admin/config/search/apachesolr/settings/solr/clone');
+    $this->assertText(t('Are you sure you want to clone search environment localhost server'), t('Clone confirmation page was succesfully loaded'));
+    $this->drupalPost($this->getUrl(), array(), t('Clone'));
+    $this->assertResponse(200);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->assertText(t('localhost server [cloned]'), t('Search Environment was succesfully cloned'));
+    // Check if the bundles and configurations are exactly the same
+    // after we clear the caches.
+    apachesolr_environments_clear_cache();
+    $envs = apachesolr_load_all_environments();
+    $this->assertEqual(count($envs), 2, 'Now we have 2 environments');
+    $orig_env = $envs['solr'];
+    unset($envs['solr']);
+    $cloned_env = array_pop($envs);
+    $this->assertTrue($this->_nestedCompare($orig_env['index_bundles'], $cloned_env['index_bundles']));
+    $this->assertTrue($this->_nestedCompare($orig_env['conf'], $cloned_env['conf']));
+  }
+
+  /**
+   * Asserts that we can edit a search environment
+   */
+  function testCreateNewSearchEnvironment() {
+    // Create a new environment
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->assertText(t('Add search environment'), t('Create new environment link is available'));
+    $this->clickLink(t('Add search environment'));
+    $this->assertText(t('Make this Solr search environment the default'), t('Environment creation page succesfully added'));
+    $edit = array('url' => 'http://localhost:8983/solr', 'name' => 'my test description', 'env_id' => 'solr_test');
+    $this->drupalPost($this->getUrl(), $edit, t('Save'));
+    $this->assertResponse(200);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->assertText(t('my test description'), t('Search Environment was succesfully created'));
+
+    // Make this new search environment the default
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    // Click on the second environment edit link
+    $this->clickLink(t('Edit'), 1);
+    $this->assertText(t('Example: http://localhost:8983/solr'), t('Edit page was succesfully loaded'));
+    $edit = array('make_default' => 1, 'conf[apachesolr_read_only]' => APACHESOLR_READ_ONLY);
+    $this->drupalPost($this->getUrl(), $edit, t('Save'));
+    $this->assertResponse(200);
+    $this->drupalGet('admin/config/search/apachesolr/settings');
+    $this->assertText(t('my test description (Default)'), t('New Search environment was succesfully changed to default environment'));
+    // Clear our cache.
+    apachesolr_environments_clear_cache();
+    $mode = apachesolr_environment_variable_get('solr_test', 'apachesolr_read_only', APACHESOLR_READ_WRITE);
+    $this->assertEqual($mode, APACHESOLR_READ_ONLY, 'Environment succesfully changed to read only');
+  }
+}
+
+/**
+ * @file
+ *   Unit test class that provides tests for base functionality of the Apachesolr
+ *   Module without having the need of a Solr Server
+ */
+class DrupalSolrOfflineSearchPagesWebTestCase extends DrupalSolrOfflineWebTestCase {
+  /**
+   * A global basic user who can search.
+   */
+  var $basic_user;
+
+  /**
+   * A global administrative user who can administer search.
+   */
+  var $admin_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Solr Search Pages',
+      'description' => 'Tests search pages functionality of the Solr module',
+      'group' => 'ApacheSolr',
+    );
+  }
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp('apachesolr', 'apachesolr_search', 'search', 'apachesolr_test');
+    // Create a basic user, which is subject to moderation.
+    $permissions = array(
+      'access content',
+      'search content',
+    );
+    $basic_user = $this->drupalCreateUser($permissions);
+
+    // Create an admin user that can bypass revision moderation.
+    $permissions = array(
+      'access content',
+      'search content',
+      'administer nodes',
+      'administer search',
+    );
+    $admin_user = $this->drupalCreateUser($permissions);
+
+    // Assign users to their test suite-wide properties.
+    $this->basic_user = $basic_user;
+    $this->admin_user = $admin_user;
+
+    // Make sure our environment does not exists
+    $env_id = apachesolr_default_environment(NULL, TRUE);
+    $environment = apachesolr_environment_load($env_id, TRUE);
+    $environment['url'] = 'http://localhost/solr/core_that_should_not_exist';
+    apachesolr_environment_save($environment);
+    // Reset all caches
+    apachesolr_load_all_environments(TRUE);
+  }
+
+  /**
+   *	Asserts that we can edit a search environment
+   */
+  function testCheckCoreSearchPage() {
+    // Create a new environment
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertText(t('Core Search'), t('Core Search page is available'));
+  }
+
+  /**
+   * Asserts that we can edit a search environment
+   */
+  function testEditSearchPage() {
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->clickLink(t('Edit'));
+    $this->assertText(t('The human-readable name of the search page configuration'), t('Edit page was succesfully loaded'));
+    $edit = array(
+      'label' => 'Test Search Page',
+      'description' => 'Test Description',
+      'page_title' => 'Test Title',
+      'search_path' => 'search/searchdifferentpath',
+    );
+    $this->drupalPost($this->getUrl(), $edit, t('Save'));
+    $this->assertResponse(200);
+    // Make sure the menu is recognized
+    drupal_static_reset('apachesolr_search_load_all_search_pages');
+    menu_cache_clear_all();
+    menu_rebuild();
+
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertText(t('Test Search Page'), t('Search page was succesfully edited'));
+    $this->assertText('search/searchdifferentpath', t('Search path was updated'));
+    $this->drupalGet('search/searchdifferentpath');
+    $this->assertText(t('Search is temporarily unavailable. If the problem persists, please contact the site administrator.'), t('Search path was successfully created and is accessible'));
+  }
+
+  /**
+   * Asserts that we can clone a search page
+   */
+  function testCloneSearchPage() {
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertText(t('Clone'), t('Clone button is available'));
+    $this->drupalGet('admin/config/search/apachesolr/search-pages/core_search/clone');
+    $this->assertText(t('Are you sure you want to clone search page Core Search?'), t('Clone confirmation page was succesfully loaded'));
+    $this->drupalPost($this->getUrl(), array(), t('Clone'));
+    $this->assertResponse(200);
+    drupal_static_reset('apachesolr_search_load_all_search_pages');
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertText(t('Core Search [cloned]'), 'Search page was succesfully cloned');
+  }
+
+  /**
+   * Asserts that we can edit a search environment
+   */
+  function testNewAndRemoveSearchPage() {
+    // Create a new search page
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertText(t('Add search page'), t('Create new search page link is available'));
+    $this->clickLink(t('Add search page'));
+    $this->assertText(t('The human-readable name of the search page configuration.'), t('Search page creation page succesfully added'));
+    $edit = array(
+      'page_id' => 'solr_testingsuite',
+      'env_id' => 'solr',
+      'label' => 'Test Search Page',
+      'description' => 'Test Description',
+      'page_title' => 'Test Title',
+      'search_path' => 'search/searchdifferentpath',
+    );
+    $this->drupalPost($this->getUrl(), $edit, t('Save'));
+    $this->assertResponse(200);
+    // Make sure the menu is recognized
+    drupal_static_reset('apachesolr_search_load_all_search_pages');
+    menu_cache_clear_all();
+    menu_rebuild();
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertText(t('Test Search Page'), t('Search Page was succesfully created'));
+
+    // Remove the same environment
+    $this->clickLink(t('Delete'));
+    $this->assertText(t('search page configuration will be deleted.This action cannot be undone.'), t('Delete confirmation page was succesfully loaded'));
+    $this->drupalPost($this->getUrl(), array(), t('Delete'));
+    $this->assertResponse(200);
+    drupal_static_reset('apachesolr_search_load_all_search_pages');
+    $this->drupalGet('admin/config/search/apachesolr/search-pages');
+    $this->assertNoText(t('Test Search Page'), t('Search Environment was succesfully deleted'));
+    apachesolr_environment_save(array('env_id' => 'DummySolr', 'service_class' => 'DummySolr', 'name' => 'dummy server', 'url' => 'http://localhost:8983/solr'));
+    $solr = new DummySolr($url = NULL, $env_id = 'DummySolr');
+    $params = array(
+      'rows' => 5,
+    );
+    $results = apachesolr_search_run('apachesolr_test', $params, '', '', 0, $solr);
+    $query = apachesolr_current_query('DummySolr');
+    $this->assertEqual($query->getParam('rows'), 5, 'Passed in rows param overrode default');
+  }
+}
+
+class DrupalSolrNodeTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Solr Node add and deletion tests',
+      'description' => 'Tests if we can succesfully add and delete nodes',
+      'group' => 'ApacheSolr',
+    );
+  }
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp('apachesolr', 'apachesolr_search', 'search', 'apachesolr_test');
+  }
+
+  function testApacheSolrNodeAddDelete() {
+    // Login as basic user to perform initial content creation.
+    // Create an admin user that can bypass revision moderation.
+    $permissions = array(
+      'access content',
+      'search content',
+      'administer nodes',
+      'administer search',
+      'access content overview',
+      'bypass node access',
+    );
+    $admin_user = $this->drupalCreateUser($permissions);
+
+    $this->drupalLogin($admin_user);
+
+    // enable our bundles to be indexed, and clear caches
+    apachesolr_index_set_bundles('solr', 'node', array('page', 'article'));
+    entity_info_cache_clear();
+    apachesolr_environments_clear_cache();
+
+    // Define types of node bundles that we want to index
+    $types = array('page', 'article');
+
+    foreach ($types as $type) {
+      $edit = array();
+      // Create a node of the type $type.
+      $edit['uid'] = $admin_user->uid;
+      $edit['type'] = $type;
+      $edit['title'] = $this->randomName(16);
+      $node = $this->drupalCreateNode($edit);
+      $this->assertTrue(is_object($node) && isset($node->nid), t('Article type @type has been created.', array('@type' => $type)));
+
+      // Check that the node has been created.
+      $node = $this->drupalGetNodeByTitle($edit['title']);
+      $this->assertTrue($node, t('Created article @type found in database.', array('@type' => $type)));
+
+      // Check that the node has status 1
+      $indexer_table = apachesolr_get_indexer_table('node');
+      $query = db_select($indexer_table, 'aien')
+        ->condition('entity_id', $node->nid)
+        ->fields('aien', array('entity_id', 'status'));
+      $db_node = $query->execute()->fetchObject();
+      $this->assertEqual($db_node->status, 1, t('Node @entity_id has status 1', array('@entity_id' => $db_node->entity_id)));
+
+      // Delete the node
+      $this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
+
+      // check if the entity delete does its work. It should have set the
+      // status to 0 so it will be deleted when solr comes online
+      $indexer_table = apachesolr_get_indexer_table('node');
+      $query = db_select($indexer_table, 'aien')
+        ->condition('entity_id', $node->nid)
+        ->fields('aien', array('entity_id', 'status'));
+      $db_node = $query->execute()->fetchObject();
+
+      // Check that all of the nodes (should only have 1) have status 0, it
+      // is set as 0 because it is pending to be deleted
+      $this->assertEqual($db_node->status, 0, t('Node @entity_id has status 0', array('@entity_id' => $db_node->entity_id)));
+
+      // Check that all the nodes have been deleted.
+      $count = db_select('node', 'n')
+        ->condition('n.nid', $node->nid)
+        ->countQuery()
+        ->execute()
+        ->fetchField();
+      $this->assertEqual($count, 0, t('No more nodes left in the node table.'));
+    }
+  }
+}
+
+class DrupalSolrOfflineUnitTestCase extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Solr Base Framework Tests Unit Test',
+      'description' => 'Unit test functionality of the Solr module',
+      'group' => 'ApacheSolr',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    require_once dirname(dirname(realpath(__FILE__))) . '/apachesolr.module';
+
+    $this->script_content = <<<EOF
+<p>GOOD_CONTENT</p>
+<script type="text/javascript" >
+$(document).ready(function(){
+  $('.accordion_teachers').accordion({ collapsible:true, autoHeight:false });
+});
+</script>
+
+EOF;
+
+    $this->embed_content = <<<EOF
+<p>GOOD_CONTENT</p>
+<object width="425" height="349"><param name="movie" value="http://www.youtube.com/v/8Vmnq5dBF7Y?version=3&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/8Vmnq5dBF7Y?version=3&amp;hl=en_US" type="application/x-shockwave-flash" width="425" height="349" allowscriptaccess="always" allowfullscreen="true"></embed></object>
+OTHER_CONTENT
+
+EOF;
+
+    $this->iframe_content = <<<EOF
+<iframe width="425" height="349" src="http://www.youtube.com/embed/8Vmnq5dBF7Y" frameborder="0" allowfullscreen></iframe>
+<p><a href="#">GOOD_CONTENT</a></p><iframe></iframe>
+
+EOF;
+
+    $this->comment_content = <<<EOF
+<p><em>GOOD_CONTENT</em></p><!-- COMMENT -->
+OTHER_CONTENT
+
+EOF;
+  }
+
+  /**
+   * Test ordering of parsed filter positions.
+   *
+   * Regression test for http://drupal.org/node/891962
+   */
+  function testContentFilters() {
+    $cleaned = apachesolr_clean_text($this->script_content);
+    $this->assertFalse(strpos($cleaned, 'script'), 'Script tags removed');
+    $this->assertFalse(strpos($cleaned, 'accordion_teachers'), 'Script tags conent removed');
+    $this->assertTrue(strpos(trim($cleaned), 'GOOD_CONTENT') === 0, 'Real content retained');
+
+    $cleaned = apachesolr_clean_text($this->embed_content);
+    $this->assertFalse(strpos($cleaned, 'object'), 'object tags removed');
+    $this->assertFalse(strpos($cleaned, 'embed'), 'embed tags removed');
+    $this->assertFalse(strpos($cleaned, '8Vmnq5dBF7Y'), 'object tags conent removed');
+    $this->assertFalse(strpos($cleaned, 'shockwave-flash'), 'embed tags conent removed');
+    $this->assertTrue(strpos(trim($cleaned), 'GOOD_CONTENT') === 0, 'Real content retained');
+    $this->assertTrue(strpos($cleaned, 'OTHER_CONTENT') > 0, 'Other content retained');
+
+    $cleaned = apachesolr_clean_text($this->iframe_content);
+    $this->assertFalse(strpos($cleaned, 'iframe'), 'iframe tags removed');
+    $this->assertFalse(strpos($cleaned, '8Vmnq5dBF7Y'), 'iframe tags conent removed');
+    $this->assertTrue(strpos(trim($cleaned), 'GOOD_CONTENT') === 0, 'Real content retained');
+
+    $cleaned = apachesolr_clean_text($this->comment_content);
+    $this->assertFalse(strpos($cleaned, 'COMMENT'), 'html comment content removed ');
+    $this->assertTrue(strpos(trim($cleaned), 'GOOD_CONTENT') === 0, 'Real content retained');
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/apachesolr_test/apachesolr_test.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,13 @@
+name = Apache Solr helper module for tests
+description = Support module for apachesolr module testing.
+package = Search Toolkit
+core = 7.x
+hidden = TRUE
+dependencies[] = apachesolr
+
+; Information added by drupal.org packaging script on 2013-03-15
+version = "7.x-1.1+34-dev"
+core = "7.x"
+project = "apachesolr"
+datestamp = "1363307665"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/apachesolr_test/apachesolr_test.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * Implements hook_page_alter().
+ *
+ */
+function apachesolr_test_page_alter(&$page) {
+  $included_files = get_included_files();
+  $filename = dirname(dirname(dirname(realpath(__FILE__)))) . '/apachesolr.index.inc';
+  if (in_array($filename, $included_files)) {
+    $page['page_bottom']['solr']= array(
+      '#type' => 'markup',
+      '#markup' => 'apachesolr.index.inc was included',
+    );
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/conf/README.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,7 @@
+
+The conf files in this directory are complete in addition to schema.xml and solrconfig.xml
+in order to provide what a core needs to run.  However, these are only for testing.  For
+production start from the ones from the example/solr directory of a Solr 1.4.x
+release tarball.
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/conf/elevate.xml	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<elevate>
+</elevate>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/conf/mapping-ISOLatin1Accent.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,20 @@
+# Incomplete example for testing
+
+# à => a
+"\u00E0" => "a"
+
+# á => a
+"\u00E1" => "a"
+
+# â => a
+"\u00E2" => "a"
+
+# ã => a
+"\u00E3" => "a"
+
+# ä => a
+"\u00E4" => "a"
+
+# å => a
+"\u00E5" => "a"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/conf/protwords.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,2 @@
+# Incomplete example for testing
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/conf/stopwords.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,6 @@
+# Incomplete example set for testing
+a
+an
+of
+the
+to
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/conf/synonyms.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,3 @@
+# Example synonmym mappings for testing
+druplicon => drupal icon
+love,hate,spam
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/solr_base_query.test	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * Unit tests for query object methods.
+ */
+class SolrBaseQueryTests extends DrupalUnitTestCase {
+  public static function getInfo() {
+        return array(
+      'name' => 'SolrBaseQuery Unit tests',
+      'description' => 'Unit Tests for queries.',
+      'group' => 'ApacheSolr',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    require_once dirname(dirname(realpath(__FILE__))) . '/apachesolr.module';
+    require_once dirname(dirname(realpath(__FILE__))) . '/apachesolr.interface.inc';
+    require_once dirname(dirname(realpath(__FILE__))) . '/Solr_Base_Query.php';
+    require_once dirname(dirname(realpath(__FILE__))) . '/Drupal_Apache_Solr_Service.php';
+    require_once dirname(dirname(realpath(__FILE__))) . '/tests/Dummy_Solr.php';
+  }
+
+  /**
+   * Helper function to simulate the auto loading and other non-needed functions
+   * that otherwise require a database
+   * @see apachesolr_drupal_query().
+   * @return SolrBaseQuery
+   */
+  private function _apachesolr_drupal_query($name, $params = array(), $solrsort = '', $base_path = '', $solr = NULL) {
+    if (empty($solr)) {
+      $solr = new DummySolr(NULL);
+    }
+    return new SolrBaseQuery($name, $solr, $params, $solrsort, $base_path);
+  }
+
+  private function _apachesolr_drupal_subquery($operator = 'OR') {
+    return new SolrFilterSubQuery($operator);
+  }
+
+  /**
+   * Test ordering of parsed filter positions.
+   *
+   * Regression test for http://drupal.org/node/891962
+   */
+  function testParseFilters() {
+    $fq = array('tid:3', 'sort_label:hello', 'tid:11', 'tid:1', 'tid:12', 'label:hello');
+    // Setup dummy Solr object.
+    $query = $this->_apachesolr_drupal_query("apachesolr_tests", array('q' => 'mykeys', 'fq' => $fq), 'sort_label asc', 'search/test');
+    // Check sortsort
+    $this->assertEqual(array('solrsort' => 'sort_label asc'), $query->getSolrsortUrlQuery());
+    $query->setSolrsort('sort_name', 'desc');
+    $this->assertEqual(array('solrsort' => 'sort_name desc'), $query->getSolrsortUrlQuery());
+    $query->setSolrsort('score', 'desc');
+    $this->assertEqual(array(), $query->getSolrsortUrlQuery());
+    // Check getPath() functionality
+    $this->assertEqual('search/test/mykeys', $query->getPath());
+    $this->assertEqual('search/test/newkeys', $query->getPath('newkeys'));
+    // Check hasFilter functionality
+    $this->assertFalse($query->hasFilter('label', 'Jello'), "hasFilter('label', 'Jello') is FALSE");
+    $this->assertTrue($query->hasFilter('label', 'hello'), "hasFilter('label', 'hello') is TRUE");
+    $this->assertTrue($query->hasFilter('label', 'hello', FALSE), "hasFilter('label', 'hello', FALSE) is TRUE");
+    $this->assertFalse($query->hasFilter('label', 'hello', TRUE), "hasFilter('label', 'hello', TRUE) is FALSE");
+    $filters = $query->getFilters();
+    $this->assertEqual(count($filters), 6, count($filters) . ' filters found, expected 6 filters');
+    // Check positions of filters
+    foreach ($fq as $idx => $filter) {
+      $this->assertEqual($filter, $query->makeFilterQuery($filters[$idx]));
+    }
+    // Check that the query string is re-assembled correctly
+    $this->assertEqual($fq, $query->getParam('fq'));
+    $this->assertEqual('mykeys', $query->getParam('q'));
+    $query->removeFilter('tid', '11');
+    $filters = $query->getFilters();
+    $this->assertEqual(count($filters), 5, count($filters) . ' filters found, expected 5 filters');
+    $this->assertEqual(array('tid:3', 'sort_label:hello', 'tid:1', 'tid:12', 'label:hello'), $query->getParam('fq'));
+    $query->removeFilter('tid');
+    $filters = $query->getFilters();
+    $this->assertEqual(count($filters), 2, count($filters) . ' filters found, expected 2 filters');
+    $this->assertEqual(array('sort_label:hello', 'label:hello'), $query->getParam('fq'));
+
+    $subquery = $this->_apachesolr_drupal_subquery();
+    $subquery->addFilter('access__all', 0);
+    $subquery->addFilter('hash', 'randomhash');
+    $query->addFilterSubQuery($subquery);
+    $this->assertEqual(count($query->getParam('fq')), 3, count($query->getParam('fq')) . ' fq params found, expected 3 after adding subquery');
+    $this->assertEqual(array('sort_label:hello', 'label:hello', '(access__all:0 OR hash:randomhash' . ')'), $query->getParam('fq'));
+  }
+
+  function testAddParams() {
+    $examples = array();
+    $examples['{!cache=false}inStock:true'] = array(
+      '#local' => 'cache=false',
+      '#exclude' => FALSE,
+      '#name' => 'inStock',
+      '#value' => 'true',
+    );
+    $examples['{!frange l=1 u=4 cache=false}sqrt(popularity)'] = array(
+      '#local' => 'frange l=1 u=4 cache=false',
+      '#exclude' => FALSE,
+      '#name' => NULL,
+      '#value' => 'sqrt(popularity)',
+    );
+    $examples['{!cache=false cost=5}inStock:true'] = array(
+      '#local' => 'cache=false cost=5',
+      '#exclude' => FALSE,
+      '#name' => 'inStock',
+      '#value' => 'true',
+    );
+    $examples['{!tag=impala}model:Impala'] = array(
+      '#local' => 'tag=impala',
+      '#exclude' => FALSE,
+      '#name' => 'model',
+      '#value' => 'Impala',
+    );
+    $examples['{!anything that appears to be local}'] = array(
+      '#local' => 'anything that appears to be local',
+      '#exclude' => FALSE,
+      '#name' => NULL,
+      '#value' => NULL,
+    );
+    $examples['bundle:(article OR page)'] = array(
+      '#local' => NULL,
+      '#exclude' => FALSE,
+      '#name' => 'bundle',
+      '#value' => '(article OR page)',
+    );
+    $examples['-bundle:(article OR page)'] = array(
+      '#local' => NULL,
+      '#exclude' => TRUE,
+      '#name' => 'bundle',
+      '#value' => '(article OR page)',
+    );
+    $examples['-{!anything that appears to be local}'] = array(
+      '#local' => 'anything that appears to be local',
+      '#exclude' => TRUE,
+      '#name' => NULL,
+      '#value' => NULL,
+    );
+    $examples['title:"double words"'] = array(
+      '#local' => NULL,
+      '#exclude' => FALSE,
+      '#name' => 'title',
+      '#value' => '"double words"',
+    );
+    $examples['field_date:[1970-12-31T23:59:59Z TO NOW]'] = array(
+      '#local' => NULL,
+      '#exclude' => FALSE,
+      '#name' => 'field_date',
+      '#value' => '[1970-12-31T23:59:59Z TO NOW]',
+    );
+
+    $query = $this->_apachesolr_drupal_query("apachesolr_tests");
+    foreach ($examples as $fq => $example) {
+      $name = (!empty($example['#name'])) ? $example['#name'] : '_QUERY_';
+      $value = (!empty($example['#value'])) ? $example['#value'] : '_VALUE_';
+      $filter = $name . ':' . $value;
+      // Check if filter is seen as a valid one
+      $message = t('Filter (@fq) is Valid', array('@fq' => $filter));
+      $this->assertTrue($query->validFilterValue($filter), $message);
+
+      $query->addParam('fq', $fq);
+      // Check if filter was added
+      $message = t('hasFilter(@name, @value) is True', array('@name' => $example['#name'], '@value' => $example['#value']));
+      $this->assertTrue($query->hasFilter($example['#name'], $example['#value'], $example['#exclude']), $message);
+      $filters = $query->getFilters();
+      $filter = reset($filters);
+      $message = t('The filter "@fq" was added with all the given properties', array('@fq' => $fq));
+      $this->assertTrue(!array_diff($example, $filter), $message);
+      $query->removeFilter($example['#name']);
+      $message = t('The filter "@fq" was correctly removed', array('@fq' => $fq));
+      $this->assertFalse($query->hasFilter($example['#name'], $example['#value'], $example['#exclude']), $message);
+      // Since we cannot remove filters without names yet we have to clear the whole fq array
+      $query->removeParam('fq');
+      // Check the ones without the name also
+    }
+
+    // Check for invalid combinations
+    $bad_examples['wrong name:"double words"'] = array(
+      '#local' => NULL,
+      '#exclude' => FALSE,
+      '#name' => 'wrong name',
+      '#value' => '"double words"',
+    );
+    $bad_examples['field_date:[1970-12-31 TO NOW]'] = array(
+      '#local' => NULL,
+      '#exclude' => FALSE,
+      '#name' => 'field_date',
+      '#value' => '[1970-12-31 TO NOW]',
+    );
+    $bad_examples['bundle:((article OR page)]'] = array(
+      '#local' => NULL,
+      '#exclude' => FALSE,
+      '#name' => 'bundle',
+      '#value' => '((article OR page)]',
+    );
+
+    foreach ($bad_examples as $fq => $example) {
+      $name = (!empty($example['#name'])) ? $example['#name'] : '_QUERY_';
+      $value = (!empty($example['#value'])) ? $example['#value'] : '_VALUE_';
+      $filter = $name . ':' . $value;
+      // Check if filter is seen as a valid one
+      $message = t('Filter (@fq) is not Valid', array('@fq' => $filter));
+      $this->assertFalse($query->validFilterValue($filter), $message);
+    }
+    // Check parameter normalization.
+    $query->addParam('spellcheck', TRUE);
+    $this->assertTrue($query->getParam('spellcheck') === 'true', "TRUE normalized to string 'true'");
+    $query->replaceParam('spellcheck', FALSE);
+    $this->assertTrue($query->getParam('spellcheck') === 'false', "FALSE normalized to string 'false'");
+    $query->addParam('fl', array(' x ', TRUE));
+    $this->assertTrue($query->getParam('fl') === array('x', 'true'), "Array of params all normalized");
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/solr_base_subquery.test	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ *   Unit tests for subquery object methods.
+ */
+class DrupalSolrFilterSubQueryTests extends DrupalUnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'SolrFilterSubQuery Unit tests',
+      'description' => 'Unit Tests for subqueries.',
+      'group' => 'ApacheSolr',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    require_once dirname(dirname(realpath(__FILE__))) . '/apachesolr.module';
+    require_once dirname(dirname(realpath(__FILE__))) . '/apachesolr.interface.inc';
+    require_once dirname(dirname(realpath(__FILE__))) . '/Solr_Base_Query.php';
+    require_once dirname(dirname(realpath(__FILE__))) . '/Drupal_Apache_Solr_Service.php';
+    require_once dirname(dirname(realpath(__FILE__))) . '/tests/Dummy_Solr.php';
+  }
+
+  /**
+   * Helper function to simulate the auto loading and other non-needed functions
+   * that otherwise require a database
+   * @see apachesolr_drupal_query().
+   * @return SolrBaseQuery
+   */
+  private function _apachesolr_drupal_query($name, $params = array(), $solrsort = '', $base_path = '', $solr = NULL) {
+    if (empty($solr)) {
+      $solr = new DummySolr(NULL);
+    }
+    return new SolrBaseQuery($name, $solr, $params, $solrsort, $base_path);
+  }
+
+  private function _apachesolr_drupal_subquery($operator = 'OR') {
+    return new SolrFilterSubQuery($operator);
+  }
+
+  function testSubQueriesQuery() {
+    $query1 = $this->_apachesolr_drupal_query('DrupalTest');
+    $query1->addFilter('label', 'foo');
+
+    $query2 = $this->_apachesolr_drupal_subquery();
+    $query2->addFilter('label', 'bar');
+
+    $query3 = $this->_apachesolr_drupal_subquery();
+    $query3->addFilter('label', 'baz');
+
+    $query1->addFilterSubQuery($query2);
+    $params = $query1->getParam('fq');
+    $this->assertEqual($params[0], 'label:foo', t('First field should be label:foo'));
+    $this->assertEqual($params[1], '(label:bar)', t('Second field should be label:bar'));
+
+    $query1->removeFilterSubQuery($query2);
+    $params = $query1->getParam('fq');
+    $this->assertFalse(isset($params[1]), t('Second field should be empty'));
+
+    $query1->addFilterSubQuery($query2);
+    $query1->addFilterSubQuery($query2);
+    $query1->addFilterSubQuery($query2);
+    $params = $query1->getParam('fq');
+    $this->assertEqual($params[0], 'label:foo', t('First field should be label:foo'));
+    $this->assertEqual($params[1], '(label:bar)', t('Second field should be label:bar'));
+    $this->assertEqual(count($params), 2, t('Add bar several times; should only appear once.'));
+
+    // Empty out query1
+    $query1 = $this->_apachesolr_drupal_query('DrupalTest');
+    $query2 = $this->_apachesolr_drupal_subquery('DrupalTest');
+    $query2->operator = 'OR';
+    $query2->addFilter('label', 'bar');
+    $query2->addFilter('label', 'baz');
+    $query1->addFilterSubQuery($query2);
+    $params = $query1->getParam('fq');
+    $this->assertEqual($params[0], '(label:bar OR label:baz)', '(label:bar OR label:baz)');
+
+    // Empty out query1
+    $query1 = $this->_apachesolr_drupal_query('DrupalTest');
+    $query2 = $this->_apachesolr_drupal_subquery('DrupalTest');
+    $query2->operator = 'AND';
+    $query2->addFilter('label', 'bar');
+    $query2->addFilter('label', 'baz');
+    $query1->addFilterSubQuery($query2);
+    $params = $query1->getParam('fq');
+    $this->assertEqual($params[0], '(label:bar AND label:baz)', '(label:bar AND label:baz)');
+
+    // Test with multiple filters in first query
+    $query1 = $this->_apachesolr_drupal_query('DrupalTest');
+    $query1->addFilter('is_uid', '10');
+
+    $query2 = $this->_apachesolr_drupal_subquery();
+    $query2->addFilter('is_uid', '1');
+    $query2->addFilter('tid', '5');
+    $query1->addFilterSubQuery($query2);
+
+    $params = $query1->getParam('fq');
+    $this->assertEqual($params[0], 'is_uid:10', 'First field value is is_uid:10');
+    $this->assertEqual($params[1], '(is_uid:1 OR tid:5)', 'Second field value is (is_uid:1 OR tid:5)');
+
+    $query2->operator = 'AND';
+    $query1->addFilterSubQuery($query2);
+    $params = $query1->getParam('fq');
+    $this->assertEqual($params[0], 'is_uid:10', 'First field value is is_uid:10');
+    $this->assertEqual($params[1], '(is_uid:1 AND tid:5)', 'Second field value is (is_uid:1 AND tid:5)');
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/solr_document.test	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ *   Unit tests for query object methods.
+ *
+ *
+ */
+class DrupalSolrDocumentTest extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'ApacheSolrDocument Unit tests',
+      'description' => 'Unit test of ApacheSolrDocument',
+      'group' => 'ApacheSolr',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    require_once dirname(dirname(realpath(__FILE__))) . '/apachesolr.module';
+    require_once dirname(dirname(realpath(__FILE__))) . '/Apache_Solr_Document.php';
+  }
+
+  function testSolrDocument() {
+    $document = new ApacheSolrDocument();
+
+    $document->addField('ss_testing', 'testingvalue');
+    $field_value = $document->getField('ss_testing');
+    $this->assertEqual($field_value['value'][0], 'testingvalue', t('The field was correctly added and verified'));
+    $document->clear();
+
+    $document->addField('ss_testing', 'testingvalue', 10);
+    $field_value = $document->getField('ss_testing');
+    $this->assertEqual($field_value['value'][0], 'testingvalue', t('The field and boost were correctly added and verified'));
+    $field_boost = $document->getFieldBoost('ss_testing');
+    $this->assertEqual($field_boost, 10, t('The field boost was correctly added and verified'));
+    $document->clear();
+
+    $document->setMultiValue('sm_testing', 'testingvalue1');
+    $document->setMultiValue('sm_testing', 'testingvalue2');
+    $field_value = $document->getField('sm_testing');
+    $this->assertTrue(in_array('testingvalue1', $field_value['value']), t('The multivalued field value was correctly added and verified'));
+    $this->assertTrue(in_array('testingvalue2', $field_value['value']), t('The second multivalued field value was correctly added and verified'));
+    $document->clear();
+
+    $document->setMultiValue('sm_testing', 'testingvalue1', 10);
+    $document->setMultiValue('sm_testing', 'testingvalue2', 20);
+    $field_value = $document->getField('sm_testing');
+    $this->assertTrue(in_array('testingvalue1', $field_value['value']), t('The multivalued field value and boost were correctly added and verified'));
+    $this->assertTrue(in_array('testingvalue2', $field_value['value']), t('The second multivalued field value and boost were correctly added and verified'));
+    $field_boost = $document->getFieldBoost('sm_testing');
+    $this->assertEqual($field_boost, 200, t('The field boost was correctly multiplied and retrieved'));
+
+    $document_field_names = $document->getFieldNames();
+    $this->assertTrue(in_array('sm_testing', $document_field_names), t('The field name was found in the document'));
+
+    $document_field_names = $document->getFieldValues();
+    $this->assertTrue(in_array('testingvalue1', $document_field_names[0]), t('The field value was found in the document'));
+
+    // Clear the complete document
+    $document->clear();
+
+    // Set and Get the document boost
+    $document->setBoost('10');
+    $document_boost = $document->getBoost();
+    $this->assertEqual($document_boost, 10, t('The document boost was correctly added and verified'));
+
+    $document->clear();
+    $document_boost = $document->getBoost();
+    $document_fields = $document->getFieldNames();
+    $document_field_boosts = $document->getFieldBoosts();
+    $this->assertFalse($document_boost, t('Document boost was succesfully emptied'));
+    $this->assertFalse($document_fields, t('Document fields were succesfully emptied'));
+    $this->assertFalse($document_field_boosts, t('Document field boosts were succesfully emptied'));
+  }
+
+  function tearDown() {
+    parent::tearDown();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrconnect/tests/solr_index_and_search.test	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,354 @@
+<?php
+
+abstract class AbstractDrupalSolrOnlineWebTestCase extends DrupalWebTestCase {
+
+  protected $solr;
+
+  protected $solr_available = FALSE; // workaround for drupal.org test bot
+
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    // Install modules needed for this test. This could have been passed in as
+    // either a single array argument or a variable number of string arguments.
+    $modules = func_get_args();
+    if (isset($modules[0]) && is_array($modules[0])) {
+      $modules = $modules[0];
+    }
+    $modules[] = 'apachesolr';
+    $modules[] = 'apachesolr_search';
+    $modules[] = 'search';
+
+    parent::setUp($modules);
+  }
+
+  function setUpSolr() {
+    // Load the default server.
+    $env_id = apachesolr_default_environment();
+    $environment = apachesolr_environment_load($env_id);
+    $this->base_solr_url = $environment['url'];
+
+    // Because we are in a clean environment, this will always be the default
+    // http://localhost:8983/solr
+    $this->core_admin_url = "{$this->base_solr_url}/admin/cores";
+
+    // The core admin url will give a valid response if the
+    // Solr server is running locally.
+    if ($this->coreAdminAvailable()) {
+
+      // We will use a core named after the simpletest prefix.
+      $environment['url'] .= '/' . $this->databasePrefix;
+
+      $filesdir = file_directory_temp();
+      // Create our Solr core directory.
+      drupal_mkdir("{$filesdir}/solr", 0777, TRUE);
+      // Our temporary core is located here.
+      $instancedir = realpath($filesdir . "/solr");
+
+      // use the Solr version confs where appropriate.
+      $version = $this->getSolrVersion();
+      if (isset($version) && $version == 3) {
+        $conf_path = dirname(__FILE__) . '/../solr-conf/solr-3.x/*';
+      }
+      elseif (isset($version) && $version == 4) {
+        $conf_path = dirname(__FILE__) . '/../solr-conf/solr-4.x/*';
+      }
+      else {
+        $conf_path = dirname(__FILE__) . '/../solr-conf/solr-1.4/*';
+      }
+
+      $patterns = array(
+        $conf_path,
+        dirname(__FILE__) . '/conf/*',
+      );
+
+      // Copy all files in solr-conf dir to our temporary solr core.
+      drupal_mkdir("{$instancedir}/conf", 0777, TRUE);
+      foreach ($patterns as $pattern) {
+        foreach (glob($pattern) as $conf_file) {
+          copy($conf_file, "$instancedir/conf/" . basename($conf_file));
+        }
+      }
+
+      $contents = file_get_contents("$instancedir/conf/solrconfig.xml");
+
+      // Change the autoCommit time down to 1 second.
+      // @todo - use solrcore.properties file for 3.x.
+      file_put_contents("$instancedir/conf/solrconfig.xml", preg_replace('@<maxTime>[0-9]+</maxTime>@', '<maxTime>1000</maxTime>', $contents));
+
+      // hard chmod -R because it seems drupal dirs are too restricted in a
+      // testing environment
+      system("chmod -R 777 {$instancedir}");
+
+      $query['name'] = $this->databasePrefix;
+      $query['instanceDir'] = $instancedir;
+      $created = $this->coreAdmin('CREATE', $query);
+
+      if ($created && apachesolr_server_status($environment['url'])) {
+        $this->instancedir = $instancedir;
+        $this->solr_url = $environment['url'];
+        apachesolr_environment_save($environment);
+        $this->solr = apachesolr_get_solr($env_id);
+        $this->solr_available = TRUE;
+        $this->checkCoreStatus($this->databasePrefix);
+      }
+    }
+    // Workaround for drupal.org test bot.
+    // The tests succeed but further tests will not run because $this->solr_available is FALSE.
+    if (!$this->solr_available) {
+      $this->pass(t('Warning : The solr instance could not be found. Please enable a multicore one on http://localhost:8983/solr'));
+    }
+  }
+
+  protected function coreAdminAvailable() {
+    $url = url($this->core_admin_url, array('query' => array('action' => 'STATUS')));
+    $options['timeout'] = 2;
+    $result = drupal_http_request($url, $options);
+    return ($result->code == 200 && empty($result->error));
+  }
+
+  protected function getSolrVersion() {
+    $status = $this->coreAdmin('STATUS');
+    foreach($status['status'] as $core_id => $core) {
+      $solr = new DrupalApacheSolrService($this->base_solr_url . '/' . $core_id);
+      $version = $solr->getSolrVersion();
+      if (!empty($version)) {
+        return $version;
+      }
+      else {
+        return "1";
+      }
+    }
+  }
+
+  /**
+   * Helper function to invoke core admin actions.
+   */
+  protected function coreAdmin($action, $query = array()) {
+    $query['action'] = $action;
+    $query['wt'] = 'json';
+    $url = url($this->core_admin_url, array('query' => $query));
+    $options['timeout'] = 2;
+    $result = drupal_http_request($url, $options);
+
+    if ($result->code == 200) {
+      return json_decode($result->data, TRUE);
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Helper function to verify that the expected core exists.
+   */
+  protected function checkCoreStatus($core_name) {
+    $response = $this->coreAdmin('STATUS', array('core' => $core_name));
+    $this->assertTrue(isset($response['status'][$core_name]['index']), 'Found Solr test core index status');
+  }
+
+  function tearDown() {
+    // Workaround for drupal.org test bot
+    if ($this->solr_available) {
+      // Unload the Solr core & delete all files
+      $query = array(
+        'core' => $this->databasePrefix,
+        'deleteIndex' => 'true',
+        'deleteDataDir' => 'true',
+        'deleteInstanceDir' => 'true'
+      );
+      // This is currently broken due to
+      // https://issues.apache.org/jira/browse/SOLR-3586
+      $this->coreAdmin('UNLOAD', $query);
+
+    }
+    parent::tearDown();
+  }
+}
+
+
+class DrupalSolrOnlineWebTestCase extends AbstractDrupalSolrOnlineWebTestCase {
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp();
+    parent::setUpSolr();
+  }
+}
+
+
+class DrupalSolrMatchTestCase extends DrupalSolrOnlineWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Solr Index Data and test live queries',
+      'description' => 'Indexes content and queries it.',
+      'group' => 'ApacheSolr',
+    );
+  }
+
+  /**
+   * Test search indexing.
+   */
+  function testMatching() {
+    if ($this->solr_available) { // workaround for drupal.org test bot
+      $this->assertTrue($this->solr->ping(), "The Server could be Pinged");
+      $response = $this->solr->search("*:*", array());
+      $response = $response->response;
+      $this->assertEqual($response->numFound, 0, "There should not be any documents in the index");
+      $this->populateIndex(7);
+      $response = $this->solr->search("*:*", array());
+      $response = $response->response;
+      $this->assertEqual($response->numFound, 7, "There should be 7 documents in the index");
+      $this->_testQueries();
+    }
+  }
+
+  /**
+   * Set up a small index of items to test against.
+   */
+  protected function populateIndex($count) {
+
+    variable_set('minimum_word_size', 3);
+    for ($i = 1; $i <= $count; ++$i) {
+      $documents[] = $this->buildDocument(array('entity_id' => $i, 'content' => $this->getText($i)));
+    }
+    $this->solr->addDocuments($documents);
+    $this->solr->commit();
+  }
+
+  protected function buildDocument($values = array()) {
+    $document = new ApacheSolrDocument();
+    if (!isset($values['entity_type'])) {
+       $values['entity_type'] = 'fake.';
+    }
+    $document->id = apachesolr_document_id($values['entity_id'], $values['entity_type']);
+    foreach ($values as $key => $value) {
+      $document->$key = $value;
+    }
+    return $document;
+  }
+
+  /**
+   * Helper method for generating snippets of content.
+   *
+   * Generated items to test against:
+   *   1  ipsum
+   *   2  dolore sit
+   *   3  sit am ut
+   *   4  am ut enim am
+   *   5  ut enim am minim veniam
+   *   6  enim am minim veniam es cillum
+   *   7  am minim veniam es cillum dolore eu
+   */
+  function getText($n) {
+    // Start over after 7.
+    $n = $n % 7;
+    $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu.");
+    return implode(' ', array_slice($words, $n - 1, $n));
+  }
+
+  /**
+   * Run predefine queries looking for indexed terms.
+   */
+  function _testQueries() {
+    /*
+      Note: OR queries that include short words in OR groups are only accepted
+      if the ORed terms are ANDed with at least one long word in the rest of the query.
+
+      e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good
+      e.g. dolore OR ut = (dolore) OR (ut) -> bad
+
+      This is a design limitation to avoid full table scans.
+
+      APACHESOLR NOTE: These are not all in lucene syntax... @TODO.  Still works for text searching
+    */
+    $queries = array(
+      // Simple AND queries.
+      'ipsum' => array(1),
+      'enim' => array(4, 5, 6),
+      'xxxxx' => array(),
+      // Mixed queries.
+      '"minim am veniam es" OR "dolore sit"' => array(2),
+      '"minim am veniam es" OR "sit dolore"' => array(),
+      '"am minim veniam es" -eu' => array(6),
+      '"am minim veniam" -"cillum dolore"' => array(5, 6),
+    );
+    $broken = array(
+      'enim minim' => array(5, 6),
+      'enim xxxxx' => array(),
+      'dolore eu' => array(7),
+      'dolore xx' => array(),
+      'ut minim' => array(5),
+      'xx minim' => array(),
+      'enim veniam am minim ut' => array(5),
+      // Simple OR queries.
+      'dolore OR ipsum' => array(1, 2, 7),
+      'dolore OR xxxxx' => array(2, 7),
+      'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
+      'minim dolore OR ipsum OR enim' => array(5, 6, 7),
+      'xxxxx dolore OR ipsum' => array(),
+      // Negative queries.
+      'dolore -sit' => array(7),
+      'dolore -eu' => array(2),
+      'dolore -xxxxx' => array(2, 7),
+      'dolore -xx' => array(2, 7),
+      // Phrase queries.
+      '"dolore sit"' => array(2),
+      '"sit dolore"' => array(),
+      '"am minim veniam es"' => array(6, 7),
+      '"minim am veniam es"' => array(),
+      'xxxxx "minim am veniam es" OR dolore' => array(),
+      'xx "minim am veniam es" OR dolore' => array(),
+      // Mixed queries.
+      '"am minim veniam es" OR dolore' => array(2, 6, 7),
+      '"am minim veniam" -"dolore cillum"' => array(5, 6, 7),
+    );
+    foreach ($queries as $query => $results) {
+      $response = $this->solr->search($query, array());
+      $this->_testQueryMatching($query, $response->response->docs, $results);
+      //@TODO: We might get to this later
+      #$this->_testQueryScores($query, $response->responses->docs, $results);
+    }
+  }
+
+  /**
+   * Test the matching abilities of the engine.
+   *
+   * Verify if a query produces the correct results.
+   */
+  function _testQueryMatching($query, $set, $results) {
+    // Get result IDs.
+    $found = array();
+    foreach ($set as $item) {
+      $found[] = $item->entity_id;
+    }
+    // Compare $results and $found.
+    sort($found);
+    sort($results);
+    $this->assertEqual($found, $results, strtr("Query matching '$query' found: @found expected: @expected", array('@found' => implode(',', $found), '@expected' => implode(',', $results))));
+  }
+
+  /**
+   * Test the scoring abilities of the engine.
+   *
+   * Verify if a query produces normalized, monotonous scores.
+   */
+  function _testQueryScores($query, $set, $results) {
+    // Get result scores.
+    $scores = array();
+    foreach ($set as $item) {
+      $scores[] = $item->score;
+    }
+
+    // Check order.
+    $sorted = $scores;
+    sort($sorted);
+    $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'");
+
+    // Check range.
+    $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'");
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrext/solrext.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,11 @@
+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;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrext/solrext.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,4 @@
+name = solrext
+description = search an solr index
+core = 7.x
+stylesheets[all][] = solrext.css
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrext/solrext.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,261 @@
+<?php
+/**
+ * @file
+ * Helps site builders and module developers investigate a site.
+ */
+/**
+ * Implements hook_form_alter() to show each form's identifier.
+ */
+function solrext_form_alter(&$form, &$form_state, $form_id) {
+  $form['solrext_display_form_id'] = array(
+					   '#type' => 'item',
+					   '#title' => t('Form ID'),
+					   '#markup' => $form_id,
+					   '#theme_wrappers' => array('container__solrext__form'),
+					   '#attributes' => array('class' => array('solrext')),
+					   '#weight' => -100,
+					   );
+  //debug($form, $form_id, TRUE);
+}
+
+
+/**
+ * Implements hook_help().
+ */
+function solrext_help($path, $arg) {
+  switch ($path) {
+  case 'admin/structure/block':
+    return t('This site has stuff!');
+  case 'admin/appearance':{
+    return _solrext_help_admin_appearance();
+  }}
+}
+
+
+
+
+
+/**
+ * Help text for the admin/appearance page.
+ */
+function _solrext_help_admin_appearance() {
+  $output = '';
+  $data = solrext_stats_enabled_themes();
+  $output .= format_plural(
+			   $data['num_hidden'],
+			   'There is one hidden theme.',
+			   'There are @count hidden themes.'
+			   );
+  return theme('solrext_help', array('text' => $output));
+}
+
+/**
+ * Implements hook_menu().
+ */
+function solrext_menu() {
+  $items['admin/reports/solrtext'] = array(
+					   'title' => 'X-ray technical site overview',
+					   'description' => 'See the internal structure of this site.',
+					   'page callback' => 'solrtext_overview_page',
+					   'access callback' => TRUE,
+					   'access arguments' => array('access site reports'),
+					   );
+
+  $items['admin/reports/solrtext/overview'] = array(
+						    'title' => 'Overview',
+						    'description' => "Technical overview of the site's internals.",
+						    'type' => MENU_DEFAULT_LOCAL_TASK,
+						    'weight' => -10,
+						    );
+
+  $items['admin/reports/solrtext/overview2'] = array(
+						     'title' => 'Overview2',
+						     'description' => "Technical overview of the site's internals.",
+						     'type' => MENU_LOCAL_TASK,
+						     'weight' => -1,
+						     'page callback' => 'solrext_permission_names_page',
+						     'access arguments' => array('access site reports'),
+						     );
+ 
+
+  return $items;
+}
+
+
+function solrtext_overview_page(){
+  return "XX";
+}
+
+
+
+/**
+ * Display the X-ray permission names page.
+ */
+function solrext_permission_names_page_OLD() {
+
+  $names = solrext_permission_names();
+  //debug($names);
+  return theme('solrext_permission_names', array('names' => $names));
+}
+/**
+ * Collect permission names.
+ */
+function solrext_permission_names() {
+
+  $names = array();
+  $permissions = module_invoke_all('permission');
+  // Extract just the permission title from each permission array.
+
+  foreach ($permissions as $machine_name => $permission) {
+    $names[$machine_name] = $permission['title'];
+  }
+  // Put permission names in alphabetical order by title.
+  asort($names);
+
+  return $names;
+}
+/**
+ * Returns HTML of permission machine and display names in a table.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - names: Array of human-readable names keyed by machine names.
+ *
+ * @ingroup themeable
+ */
+function theme_solrext_permission_names($variables) {
+  $names = $variables['names'];
+  $output = '';
+  $header = array(t('Permission title'), t('Permission machine name'));
+  $rows = array();
+  foreach ($names as $machine_name => $title) {
+    $rows[] = array($title, $machine_name);
+  }
+  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' =>
+				  array('id' => 'solrext-permission-names')));
+  return $output;
+
+
+
+  /**
+   * Fetch information about themes.
+   */
+  function solrext_stats_enabled_themes() {
+    $themes = list_themes();
+    $num_themes = count($themes);
+    // Initialize variables for the data you will collect.
+    $num_hidden = 0; // Number of hidden themes.
+    $num_enabled = 0;
+    $summaries = array();
+    // Iterate through each theme, gathering data that you care about.
+    foreach ($themes as $themename => $theme) {
+      // Do not gather statistics for hidden themes, but keep a count of them.
+      if (isset($theme->info['hidden']) && $theme->info['hidden']) {
+	$num_hidden++;
+      }
+      else { // This is a visible theme.
+	if ($theme->status) {
+	  $num_enabled++;
+	  // This is an enabled theme, provide more stats.
+	  $summaries[$theme->info['name']] = array(
+						   'regions' => count($theme->info['regions']),
+						   'overlay_regions' => count($theme->info['overlay_regions']),
+						   'regions_hidden' => count($theme->info['regions_hidden']),
+						   'features' => count($theme->info['features']),
+						   'kindsofstylesheets' => count($theme->info['stylesheets']),
+						   'allstylesheets' => isset($theme->info['stylesheets']['all']) ? count($theme->info['stylesheets']['all']) : 0,
+						   );
+	}
+      }
+    }
+    return compact('num_themes', 'num_hidden', 'num_enabled', 'summaries');
+  }
+}
+
+
+/**
+ * Implements hook_theme().
+ */
+function solrext_theme() {
+
+  return array(
+	       'solrext_permission_names' => array(
+						   'render element' => 'names',
+						   ),
+	       'solrext_show_page_callback' => array(
+						  'variables' => array(
+								       'page_callback' => NULL,
+								       'include_file' => NULL,
+								       'page_arguments' => NULL,
+								       ),
+						  ),
+	       );
+}
+
+
+/**
+ * Display permission machine and display names in a table.
+ *
+ * @return
+ * An array as expected by drupal_render().
+ */
+function solrext_permission_names_page() {
+  $build = array();
+  // Gather data, an array of human-readable names keyed by machine names.
+  $names = solrext_permission_names();
+  // Format the data as a table.
+  $header = array(t('Permission title'), t('Permission machine name'));
+  $rows = array();
+  foreach ($names as $machine_name => $title) {
+    $rows[] = array($title, $machine_name);
+  }
+  $build['names_table'] = array(
+				'#theme' => 'table__solrext__permission_names',
+				'#header' => $header,
+				'#rows' => $rows,
+				'#attributes' => array('id' => 'solrext-permission-names')
+				);
+  return $build;
+}
+
+
+/**
+ * Provide the page callback function (and other router item information).
+ */
+function solrext_show_page_callback() {
+  debug("XX");
+  // Do not hand in the path; menu_get_item() finds dynamic paths on its own
+  // but fails if handed help's $path variable which is node/% for node/1.
+  $router_item = menu_get_item();
+  // menu_get_item() can return null when called via drush command line.
+  if ($router_item) {
+    return theme('solrext_show_page_callback', $router_item);
+  }
+}
+/**
+ * Theme the page callback and optionally other elements of a router item.
+ */
+function theme_solrext_show_page_callback($variables) {
+  extract($variables, EXTR_SKIP);
+  $output = '';
+  $output .= '<p class="solrext-help solrext-page-callback">';
+  $output .= t('This page is brought to you by ');
+  if ($page_arguments) {
+    foreach ($page_arguments as $key => $value) {
+      $page_arguments[$key] = drupal_placeholder($value);
+    }
+    $output .= format_plural(count($page_arguments),
+			     'the argument !arg handed to ',
+			     'the arguments !arg handed to ',
+			     array('!arg' => solrext_oxford_comma_list($page_arguments))
+			     );
+  }
+  $output .= t('the function %func',
+	       array('%func' => $page_callback . '()'));
+  if ($include_file) {
+    $output .= t(' and the included file %file',
+		 array('%file' => $include_file));
+  }
+  $output .= '.</p>';
+  return $output;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch/Apache_Solr_Document.php	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/Drupal_Apache_Solr_Service.php	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/Solr_Base_Query.php	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/apachesolr.admin.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/facetapi.callbacks.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/plugins/facetapi/adapter.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_date.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_geo.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_integer.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_numeric_range.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/plugins/facetapi/query_type_term.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solr-search-result.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation for displaying a single search result.
+
+ * @see template_preprocess()
+ * @see template_preprocess_search_result()
+ * @see template_process()
+ *
+ * @ingroup themeable
+ */
+?>
+
+
+
+
+
+
+
+<tr class="line <?php print $classes; ?>"<?php print $attributes; ?>>
+
+
+
+<td>
+ <?php if ($access_type !="free"):?>
+
+<a href="<?php print $url; ?>"><span class="solrsearch_internal" c>
+intern
+</span>
+<?php endif ;?><img src="<?php print $thumburl; ?>"/></a></td>
+<td>  <a href="<?php print $url; ?>"><?php print $title; ?>
+<?php if (isset ($signature)):?><?php print $signature; ?><?php endif;?>
+</a></td>
+<td><?php print $author; ?></td>
+
+<td><?php if (isset ($date)):?>
+<?php print $date; ?>
+<?php else: ?>
+<?php print $year; ?></td>
+<?php endif;?>
+ <td></td>
+</tr>
+<!--  <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/sites/all/modules/custom/solrsearch/solr-search-results.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,52 @@
+<?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="box">
+
+<?php if ($search_results): ?>
+<?php print $description; ?>
+ <?php print $pager; ?>
+  <h2><?php print t('Search results');?></h2>
+
+  <?php if (isset($digitalobjects_items_select)):?>
+
+  <form method="GET" action="<?php print $digitalobjects_items_select ?>">
+  <?php endif;?>
+  <table class="items search-results <?php print $module; ?>-results">
+
+    <?php print $search_results; ?>
+
+
+  </table>
+  <?php print $pager; ?>
+
+   <?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>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch/solrsearch-author-block-form.tpl.php	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch-block-form.tpl.php	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch-mpiwg.js	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,35 @@
+/*
+ * 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();
+        }
+    });
+}); 
+})(jQuery);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch/solrsearch-term-list-author.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,7 @@
+
+
+<h2>Authors (found <?php print $cnt ?> starting with letter "<?php print $letter?>")</h2>
+
+<?php foreach ($authors as $author => $hits):?>
+<div><?php print l($author . ' ('. $hits .')','/solrsearch/author_c:"' . $author .'"') ?></div>
+<?php endforeach;?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch/solrsearch-term-list-title.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,7 @@
+
+
+<h2>Titles (found <?php print $cnt ?> starting with letter "<?php print $letter?>")</h2>
+
+<?php foreach ($titles as $title => $hits):?>
+<div><?php print l($title . ' ('. $hits .')','/solrsearch/title_s:"' . $title .'")') ?></div>
+<?php endforeach;?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch/solrsearch-term-selection-form.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,40 @@
+<?php
+drupal_add_js(drupal_get_path('module', 'solrsearch') .'/solrsearch-mpiwg.js');
+?>
+<h1>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 == "title_s"):?> checked="checked"<?php endif;?>  name="browseField" value="title_s"/>
+                Title A-Z
+              </li>
+              <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="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">(other)</a>
+	      </li>
+	    </ul>
+	  </div>
+	</form>
+	</div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch/solrsearch-title-block-form.tpl.php	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch.admin.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch.css	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch.index.inc_unused	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,23 @@
+name = solrsearch
+description = Frontend for searching Solr
+core = 7.x
+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/sites/all/modules/custom/solrsearch/solrsearch.install	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch.interface.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,2715 @@
+<?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['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();
+  //dpm("solrsearch_entity_field_facets");
+  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['docType'] = array(
+    'label' => t('Document type'),
+    'description' => t('Filter by document type.'),
+    'field' => 'doc-type',
+    'field api bundles' => array('node'),
+    'map callback' => 'facetapi_map_bundle',
+    'values callback' => 'facetapi_callback_type_values',
+    'facet mincount allowed' => TRUE,
+    'dependency plugins' => array('role'),
+  );
+  */
+  $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('Collection type (online or not)'),
+      'description' => t('Filter by Collection Type.'),
+      'field' => 'doc-type',
+      'facet mincount allowed' => TRUE,
+  );
+
+  $facets['access-type'] = array(
+      'label' => t('Access restrictions'),
+      'description' => t('Filter by Access Type.'),
+      'field' => 'access-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;
+}
+
+
+/**
+ * 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');
+
+  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/sites/all/modules/custom/solrsearch/solrsearch.pages.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,269 @@
+<?php
+
+/**
+ * @file
+ * User page callbacks for the search module.
+ */
+
+/**
+ * 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>';
+  }
+
+  foreach ($variables['results'] as $result) {
+
+
+    /* add checkboxes for the item */
+
+    $variables['search_results'] .= '<tr><td colspan="5">';
+
+    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'])) . "</td></tr>";
+
+
+    $variables['search_results'] .= theme('solrsearch_result', array('result' => $result, 'module' => $variables['module']));
+
+  }
+  $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) {
+  global $language;
+
+  $result = $variables['result'];
+
+  $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,'signature');
+  $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'];
+}
+
+/**
+ * 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/sites/all/modules/custom/solrsearch/solrsearch_search.admin.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1192 @@
+<?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/sites/all/modules/custom/solrsearch/solrsearch_search.info	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch_search.install	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch_search.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1824 @@
+<?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'] = $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";
+  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':
+      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',
+        'archive-path',
+        'doc-type',
+        'mpiwg-dri',
+        'access-type',
+      ),
+    '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'};
+      /*$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'},
+      );
+
+      if (isset($doc->{'IM_date'})){
+        $result['date']=$doc->{'IM_date'};
+      }
+      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/sites/all/modules/custom/solrsearch/solrsearch_search.pages.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,130 @@
+<?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/sites/all/modules/custom/solrsearch/solrsearch_search_author_block.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch_search_blocks.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch_search_title_block.inc	Wed Jul 31 13:49:13 2013 +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/sites/all/modules/custom/solrsearch/solrsearch_terms.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,106 @@
+<?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;
+  $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";
+
+  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). "&nbsp;|</li>";
+    }
+
+
+
+    return $output;
+  }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/LICENSE.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/README.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,102 @@
+-- veraenderunge bisher 
+DW
+
+An das Textinputfeld für die solr suche wird jeweils zusätzlich eine klasse:
+    "solrsearch-autocomplete.field.FIELDNAME" angeängt, FIELDName ist hierbei das Feld das für die autovervollständigung durchsucht werden soll.
+    
+    
+    daher hat die function:
+    solrsearch_autocomplete_do_alter
+    
+    jetzt einen zusätzlichen parameter für den feldnamen
+    
+    function solrsearch_autocomplete_do_alter(&$element,$field_name) {
+    
+
+    solrsearch_autocomplete.js
+    
+    is dementsprechend erweitert, dass die zusatzliche klasse ausgewertet wird
+    jquery fragt dann die autocompletion function von solrsearch (angehaengt bei /solrsearch_autocomplete") mit den paramtern "query" und "fieldName".
+    
+    entsprechend wird dann solr abgefragt.
+    
+TODO:
+--bisher ist fuer jedes such feld in der solrsuche
+die form-Veränderung hard codiert (korrespondierte dazu ist bisher ist auch noch fuer jedes suchfeld eine eigene such box in solrsearch angelegt.)
+
+das sollte so geändert werden, dass fuer jedes suchfeld ein parameter existiert. 
+
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function solrsearch_autocomplete_form_solrsearch_search_block_form_alter(&$form, $form_state) {
+
+  $element = &$form['solrsearch_search_block_form'];
+  solrsearch_autocomplete_do_alter($element,"title");
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function solrsearch_autocomplete_form_solrsearch_search_author_block_form_alter(&$form, $form_state) {
+
+  $element = &$form['solrsearch_search_author_block_form'];
+  solrsearch_autocomplete_do_alter($element,"author");
+}
+
+
+--die widgets funktioneren nicht
+
+---- original apachesolr_autocomplete README -- begin ----
+
+
+Apache Solr Autocomplete module for Drupal.
+
+-- SUMMARY --
+
+Add-on module to Apache Solr Search Integration that adds simple autocomplete
+functionality. It enforces node access, meaning that all suggestions are only
+from nodes that the user actually has access to.
+
+For a full description of the module, visit the project page:
+  http://drupal.org/project/apachesolr_autocomplete
+
+To submit bug reports and feature suggestions, or to track changes:
+  http://drupal.org/project/issues/apachesolr_autocomplete
+
+
+-- REQUIREMENTS --
+
+Apache Solr Search Integration module.
+See http://drupal.org/project/apachesolr
+
+
+-- INSTALLATION --
+
+* Install as usual, see http://drupal.org/node/70151 for further information.
+
+
+-- CONFIGURATION  --
+
+For configuration, go to:
+
+  Administration >> Settings >> Apache Solr
+
+and look for the "Advanced Options" fieldset. The setting is:
+
+  "Autocomplete widget to use:"
+
+where you can choose between a custom Javascript widget (included with the
+module) or fall back to the core Drupal autocomplete widget. The default is to
+use the custom widget.
+
+-- TROUBLESHOOTING --
+
+If you are having trouble with the autocomplete suggestions not working correctly,
+try changing the configuration to use the core Drupal autocomplete widget.
+
+If you encounter other problems, please post to the project issue queue:
+  http://drupal.org/project/issues/apachesolr_autocomplete
+
+--
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/changelog.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,27 @@
+1.1
+---
+* Added matchContains: "word" option, match only the start of words instead of everywhere
+* Fixed mustMatch to trigger result event when no match was found
+* Fixed the issue where an autocomplete was applied after the field had focus
+* Extended multiple complete to enable editing words not at the end of the field (doesn't work in Opera)
+
+1.0.2
+-----
+* Fixed missing semicolon
+
+1.0.1
+-----
+* Fixed element creation (<ul> to <ul/> and <li> to </li>)
+* Fixed ac_even class (was ac_event)
+* Fixed bgiframe usage: now its really optional
+* Removed the blur-on-return workaround, added a less obtrusive one only for Opera
+* Fixed hold cursor keys: Opera needs keypress, everyone else keydown to scroll through result list when holding cursor key
+* Updated package to jQuery 1.2.5, removing dimensions
+* Fixed multiple-mustMatch: Remove only the last term when no match is found
+* Fixed multiple without mustMatch: Don't select the last active when no match is found (on tab/return)
+* Fixed multiple cursor position: Put cursor at end of input after selecting a value
+
+1.0
+---
+
+* First release.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/jquery.autocomplete.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,48 @@
+.ac_results {
+	padding: 0px;
+	border: 1px solid black;
+	background-color: white;
+	overflow: hidden;
+	z-index: 99999;
+}
+
+.ac_results ul {
+	width: 100%;
+	list-style-position: outside;
+	list-style: none;
+	padding: 0;
+	margin: 0;
+}
+
+.ac_results li {
+	margin: 0px;
+	padding: 2px 5px;
+	cursor: default;
+	display: block;
+	/* 
+	if width will be 100% horizontal scrollbar will apear 
+	when scroll mode will be used
+	*/
+	/*width: 100%;*/
+	font: menu;
+	font-size: 12px;
+	/* 
+	it is very important, if line-height not setted or setted 
+	in relative units scroll will be broken in firefox
+	*/
+	line-height: 16px;
+	overflow: hidden;
+}
+
+.ac_loading {
+	background: white url('indicator.gif') right center no-repeat;
+}
+
+.ac_odd {
+	background-color: #eee;
+}
+
+.ac_over {
+	background-color: #0A246A;
+	color: white;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/jquery.autocomplete.js	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,808 @@
+/*
+ * jQuery Autocomplete plugin 1.1
+ *
+ * Copyright (c) 2009 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ */
+
+;(function($) {
+	
+$.fn.extend({
+	autocomplete: function(urlOrData, options) {
+		
+		var isUrl = typeof urlOrData == "string";
+		options = $.extend({}, $.Autocompleter.defaults, {
+			url: isUrl ? urlOrData : null,
+			data: isUrl ? null : urlOrData,
+			delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+			max: options && !options.scroll ? 10 : 150
+		}, options);
+		
+		// if highlight is set to false, replace it with a do-nothing function
+		options.highlight = options.highlight || function(value) { return value; };
+		
+		// if the formatMatch option is not specified, then use formatItem for backwards compatibility
+		options.formatMatch = options.formatMatch || options.formatItem;
+		
+		return this.each(function() {
+			new $.Autocompleter(this, options);
+		});
+	},
+	result: function(handler) {
+		return this.bind("result", handler);
+	},
+	search: function(handler) {
+		return this.trigger("search", [handler]);
+	},
+	flushCache: function() {
+		return this.trigger("flushCache");
+	},
+	setOptions: function(options){
+		return this.trigger("setOptions", [options]);
+	},
+	unautocomplete: function() {
+		return this.trigger("unautocomplete");
+	}
+});
+
+$.Autocompleter = function(input, options) {
+
+	var KEY = {
+		UP: 38,
+		DOWN: 40,
+		DEL: 46,
+		TAB: 9,
+		RETURN: 13,
+		ESC: 27,
+		COMMA: 188,
+		PAGEUP: 33,
+		PAGEDOWN: 34,
+		BACKSPACE: 8
+	};
+
+	// Create $ object for input element
+	var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+	var timeout;
+	var previousValue = "";
+	var cache = $.Autocompleter.Cache(options);
+	var hasFocus = 0;
+	var lastKeyPressCode;
+	var config = {
+		mouseDownOnSelect: false
+	};
+	var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+	
+	var blockSubmit;
+	
+	// prevent form submit in opera when selecting with return key
+	$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+		if (blockSubmit) {
+			blockSubmit = false;
+			return false;
+		}
+	});
+	
+	// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
+	$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+		// a keypress means the input has focus
+		// avoids issue where input had focus before the autocomplete was applied
+		hasFocus = 1;
+		// track last key pressed
+		lastKeyPressCode = event.keyCode;
+		switch(event.keyCode) {
+		
+			case KEY.UP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.prev();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.DOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.next();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.PAGEUP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageUp();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.PAGEDOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageDown();
+				} else {
+					onChange(0, true);
+				}
+				break;
+			
+			// matches also semicolon
+			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+			case KEY.TAB:
+			case KEY.RETURN:
+				if( selectCurrent() ) {
+					// stop default to prevent a form submit, Opera needs special handling
+					event.preventDefault();
+					blockSubmit = true;
+					return false;
+				}
+				break;
+				
+			case KEY.ESC:
+				select.hide();
+				break;
+				
+			default:
+				clearTimeout(timeout);
+				timeout = setTimeout(onChange, options.delay);
+				break;
+		}
+	}).focus(function(){
+		// track whether the field has focus, we shouldn't process any
+		// results if the field no longer has focus
+		hasFocus++;
+	}).blur(function() {
+		hasFocus = 0;
+		if (!config.mouseDownOnSelect) {
+			hideResults();
+		}
+	}).click(function() {
+		// show select when clicking in a focused field
+		if ( hasFocus++ > 1 && !select.visible() ) {
+			onChange(0, true);
+		}
+	}).bind("search", function() {
+		// TODO why not just specifying both arguments?
+		var fn = (arguments.length > 1) ? arguments[1] : null;
+		function findValueCallback(q, data) {
+			var result;
+			if( data && data.length ) {
+				for (var i=0; i < data.length; i++) {
+					if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+						result = data[i];
+						break;
+					}
+				}
+			}
+			if( typeof fn == "function" ) fn(result);
+			else $input.trigger("result", result && [result.data, result.value]);
+		}
+		$.each(trimWords($input.val()), function(i, value) {
+			request(value, findValueCallback, findValueCallback);
+		});
+	}).bind("flushCache", function() {
+		cache.flush();
+	}).bind("setOptions", function() {
+		$.extend(options, arguments[1]);
+		// if we've updated the data, repopulate
+		if ( "data" in arguments[1] )
+			cache.populate();
+	}).bind("unautocomplete", function() {
+		select.unbind();
+		$input.unbind();
+		$(input.form).unbind(".autocomplete");
+	});
+	
+	
+	function selectCurrent() {
+		var selected = select.selected();
+		if( !selected )
+			return false;
+		
+		var v = selected.result;
+		previousValue = v;
+		
+		if ( options.multiple ) {
+			var words = trimWords($input.val());
+			if ( words.length > 1 ) {
+				var seperator = options.multipleSeparator.length;
+				var cursorAt = $(input).selection().start;
+				var wordAt, progress = 0;
+				$.each(words, function(i, word) {
+					progress += word.length;
+					if (cursorAt <= progress) {
+						wordAt = i;
+						return false;
+					}
+					progress += seperator;
+				});
+				words[wordAt] = v;
+				// TODO this should set the cursor to the right position, but it gets overriden somewhere
+				//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
+				v = words.join( options.multipleSeparator );
+			}
+			v += options.multipleSeparator;
+		}
+		
+		$input.val(v);
+		hideResultsNow();
+		$input.trigger("result", [selected.data, selected.value]);
+		return true;
+	}
+	
+	function onChange(crap, skipPrevCheck) {
+		if( lastKeyPressCode == KEY.DEL ) {
+			select.hide();
+			return;
+		}
+		
+		var currentValue = $input.val();
+		
+		if ( !skipPrevCheck && currentValue == previousValue )
+			return;
+		
+		previousValue = currentValue;
+		
+		currentValue = lastWord(currentValue);
+		if ( currentValue.length >= options.minChars) {
+			$input.addClass(options.loadingClass);
+			if (!options.matchCase)
+				currentValue = currentValue.toLowerCase();
+			request(currentValue, receiveData, hideResultsNow);
+		} else {
+			stopLoading();
+			select.hide();
+		}
+	};
+	
+	function trimWords(value) {
+		if (!value)
+			return [""];
+		if (!options.multiple)
+			return [$.trim(value)];
+		return $.map(value.split(options.multipleSeparator), function(word) {
+			return $.trim(value).length ? $.trim(word) : null;
+		});
+	}
+	
+	function lastWord(value) {
+		if ( !options.multiple )
+			return value;
+		var words = trimWords(value);
+		if (words.length == 1) 
+			return words[0];
+		var cursorAt = $(input).selection().start;
+		if (cursorAt == value.length) {
+			words = trimWords(value)
+		} else {
+			words = trimWords(value.replace(value.substring(cursorAt), ""));
+		}
+		return words[words.length - 1];
+	}
+	
+	// fills in the input box w/the first match (assumed to be the best match)
+	// q: the term entered
+	// sValue: the first matching result
+	function autoFill(q, sValue){
+		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
+		// if the last user key pressed was backspace, don't autofill
+		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+			// fill in the value (keep the case the user has typed)
+			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+			// select the portion of the value not typed by the user (so the next character will erase)
+			$(input).selection(previousValue.length, previousValue.length + sValue.length);
+		}
+	};
+
+	function hideResults() {
+		clearTimeout(timeout);
+		timeout = setTimeout(hideResultsNow, 200);
+	};
+
+	function hideResultsNow() {
+		var wasVisible = select.visible();
+		select.hide();
+		clearTimeout(timeout);
+		stopLoading();
+		if (options.mustMatch) {
+			// call search and run callback
+			$input.search(
+				function (result){
+					// if no value found, clear the input box
+					if( !result ) {
+						if (options.multiple) {
+							var words = trimWords($input.val()).slice(0, -1);
+							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+						}
+						else {
+							$input.val( "" );
+							$input.trigger("result", null);
+						}
+					}
+				}
+			);
+		}
+	};
+
+	function receiveData(q, data) {
+		if ( data && data.length && hasFocus ) {
+			stopLoading();
+			select.display(data, q);
+			autoFill(q, data[0].value);
+			select.show();
+		} else {
+			hideResultsNow();
+		}
+	};
+
+	function request(term, success, failure) {
+		if (!options.matchCase)
+			term = term.toLowerCase();
+		var data = cache.load(term);
+		// recieve the cached data
+		if (data && data.length) {
+			success(term, data);
+		// if an AJAX url has been supplied, try loading the data now
+		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
+			
+			var extraParams = {
+				timestamp: +new Date()
+			};
+			$.each(options.extraParams, function(key, param) {
+				extraParams[key] = typeof param == "function" ? param() : param;
+			});
+			
+			$.ajax({
+				// try to leverage ajaxQueue plugin to abort previous requests
+				mode: "abort",
+				// limit abortion to this input
+				port: "autocomplete" + input.name,
+				dataType: options.dataType,
+				url: options.url,
+				data: $.extend({
+					query: lastWord(term),
+					limit: options.max
+				}, extraParams),
+				success: function(data) {
+					var parsed = options.parse && options.parse(data) || parse(data);
+					cache.add(term, parsed);
+					success(term, parsed);
+				}
+			});
+		} else {
+			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+			select.emptyList();
+			failure(term);
+		}
+	};
+	
+	function parse(data) {
+		var parsed = [];
+		var rows = data.split("\n");
+		for (var i=0; i < rows.length; i++) {
+			var row = $.trim(rows[i]);
+			if (row) {
+				row = row.split("|");
+				parsed[parsed.length] = {
+					data: row,
+					value: row[0],
+					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+				};
+			}
+		}
+		return parsed;
+	};
+
+	function stopLoading() {
+		$input.removeClass(options.loadingClass);
+	};
+
+};
+
+$.Autocompleter.defaults = {
+	inputClass: "ac_input",
+	resultsClass: "ac_results",
+	loadingClass: "ac_loading",
+	minChars: 1,
+	delay: 400,
+	matchCase: false,
+	matchSubset: true,
+	matchContains: false,
+	cacheLength: 10,
+	max: 100,
+	mustMatch: false,
+	extraParams: {},
+	selectFirst: true,
+	formatItem: function(row) { return row[0]; },
+	formatMatch: null,
+	autoFill: false,
+	width: 0,
+	multiple: false,
+	multipleSeparator: ", ",
+	highlight: function(value, term) {
+		return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+	},
+    scroll: true,
+    scrollHeight: 180
+};
+
+$.Autocompleter.Cache = function(options) {
+
+	var data = {};
+	var length = 0;
+	
+	function matchSubset(s, sub) {
+		if (!options.matchCase) 
+			s = s.toLowerCase();
+		var i = s.indexOf(sub);
+		if (options.matchContains == "word"){
+			i = s.toLowerCase().search("\\b" + sub.toLowerCase());
+		}
+		if (i == -1) return false;
+		return i == 0 || options.matchContains;
+	};
+	
+	function add(q, value) {
+		if (length > options.cacheLength){
+			flush();
+		}
+		if (!data[q]){ 
+			length++;
+		}
+		data[q] = value;
+	}
+	
+	function populate(){
+		if( !options.data ) return false;
+		// track the matches
+		var stMatchSets = {},
+			nullData = 0;
+
+		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
+		if( !options.url ) options.cacheLength = 1;
+		
+		// track all options for minChars = 0
+		stMatchSets[""] = [];
+		
+		// loop through the array and create a lookup structure
+		for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+			var rawValue = options.data[i];
+			// if rawValue is a string, make an array otherwise just reference the array
+			rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+			
+			var value = options.formatMatch(rawValue, i+1, options.data.length);
+			if ( value === false )
+				continue;
+				
+			var firstChar = value.charAt(0).toLowerCase();
+			// if no lookup array for this character exists, look it up now
+			if( !stMatchSets[firstChar] ) 
+				stMatchSets[firstChar] = [];
+
+			// if the match is a string
+			var row = {
+				value: value,
+				data: rawValue,
+				result: options.formatResult && options.formatResult(rawValue) || value
+			};
+			
+			// push the current match into the set list
+			stMatchSets[firstChar].push(row);
+
+			// keep track of minChars zero items
+			if ( nullData++ < options.max ) {
+				stMatchSets[""].push(row);
+			}
+		};
+
+		// add the data items to the cache
+		$.each(stMatchSets, function(i, value) {
+			// increase the cache size
+			options.cacheLength++;
+			// add to the cache
+			add(i, value);
+		});
+	}
+	
+	// populate any existing data
+	setTimeout(populate, 25);
+	
+	function flush(){
+		data = {};
+		length = 0;
+	}
+	
+	return {
+		flush: flush,
+		add: add,
+		populate: populate,
+		load: function(q) {
+			if (!options.cacheLength || !length)
+				return null;
+			/* 
+			 * if dealing w/local data and matchContains than we must make sure
+			 * to loop through all the data collections looking for matches
+			 */
+			if( !options.url && options.matchContains ){
+				// track all matches
+				var csub = [];
+				// loop through all the data grids for matches
+				for( var k in data ){
+					// don't search through the stMatchSets[""] (minChars: 0) cache
+					// this prevents duplicates
+					if( k.length > 0 ){
+						var c = data[k];
+						$.each(c, function(i, x) {
+							// if we've got a match, add it to the array
+							if (matchSubset(x.value, q)) {
+								csub.push(x);
+							}
+						});
+					}
+				}				
+				return csub;
+			} else 
+			// if the exact item exists, use it
+			if (data[q]){
+				return data[q];
+			} else
+			if (options.matchSubset) {
+				for (var i = q.length - 1; i >= options.minChars; i--) {
+					var c = data[q.substr(0, i)];
+					if (c) {
+						var csub = [];
+						$.each(c, function(i, x) {
+							if (matchSubset(x.value, q)) {
+								csub[csub.length] = x;
+							}
+						});
+						return csub;
+					}
+				}
+			}
+			return null;
+		}
+	};
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+	var CLASSES = {
+		ACTIVE: "ac_over"
+	};
+	
+	var listItems,
+		active = -1,
+		data,
+		term = "",
+		needsInit = true,
+		element,
+		list;
+	
+	// Create results
+	function init() {
+		if (!needsInit)
+			return;
+		element = $("<div/>")
+		.hide()
+		.addClass(options.resultsClass)
+		.css("position", "absolute")
+		.appendTo(document.body);
+	
+		list = $("<ul/>").appendTo(element).mouseover( function(event) {
+			if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+	            active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+			    $(target(event)).addClass(CLASSES.ACTIVE);            
+	        }
+		}).click(function(event) {
+			$(target(event)).addClass(CLASSES.ACTIVE);
+			select();
+			// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+			input.focus();
+			return false;
+		}).mousedown(function() {
+			config.mouseDownOnSelect = true;
+		}).mouseup(function() {
+			config.mouseDownOnSelect = false;
+		});
+		
+		if( options.width > 0 )
+			element.css("width", options.width);
+			
+		needsInit = false;
+	} 
+	
+	function target(event) {
+		var element = event.target;
+		while(element && element.tagName != "LI")
+			element = element.parentNode;
+		// more fun with IE, sometimes event.target is empty, just ignore it then
+		if(!element)
+			return [];
+		return element;
+	}
+
+	function moveSelect(step) {
+		listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+		movePosition(step);
+        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+        if(options.scroll) {
+            var offset = 0;
+            listItems.slice(0, active).each(function() {
+				offset += this.offsetHeight;
+			});
+            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+            } else if(offset < list.scrollTop()) {
+                list.scrollTop(offset);
+            }
+        }
+	};
+	
+	function movePosition(step) {
+		active += step;
+		if (active < 0) {
+			active = listItems.size() - 1;
+		} else if (active >= listItems.size()) {
+			active = 0;
+		}
+	}
+	
+	function limitNumberOfItems(available) {
+		return options.max && options.max < available
+			? options.max
+			: available;
+	}
+	
+	function fillList() {
+		list.empty();
+		var max = limitNumberOfItems(data.length);
+		for (var i=0; i < max; i++) {
+			if (!data[i])
+				continue;
+			var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+			if ( formatted === false )
+				continue;
+			var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
+			$.data(li, "ac_data", data[i]);
+		}
+		listItems = list.find("li");
+		if ( options.selectFirst ) {
+			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+			active = 0;
+		}
+		// apply bgiframe if available
+		if ( $.fn.bgiframe )
+			list.bgiframe();
+	}
+	
+	return {
+		display: function(d, q) {
+			init();
+			data = d;
+			term = q;
+			fillList();
+		},
+		next: function() {
+			moveSelect(1);
+		},
+		prev: function() {
+			moveSelect(-1);
+		},
+		pageUp: function() {
+			if (active != 0 && active - 8 < 0) {
+				moveSelect( -active );
+			} else {
+				moveSelect(-8);
+			}
+		},
+		pageDown: function() {
+			if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+				moveSelect( listItems.size() - 1 - active );
+			} else {
+				moveSelect(8);
+			}
+		},
+		hide: function() {
+			element && element.hide();
+			listItems && listItems.removeClass(CLASSES.ACTIVE);
+			active = -1;
+		},
+		visible : function() {
+			return element && element.is(":visible");
+		},
+		current: function() {
+			return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+		},
+		show: function() {
+			var offset = $(input).offset();
+			element.css({
+				width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+				top: offset.top + input.offsetHeight,
+				left: offset.left
+			}).show();
+            if(options.scroll) {
+                list.scrollTop(0);
+                list.css({
+					maxHeight: options.scrollHeight,
+					overflow: 'auto'
+				});
+				
+                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
+					var listHeight = 0;
+					listItems.each(function() {
+						listHeight += this.offsetHeight;
+					});
+					var scrollbarsVisible = listHeight > options.scrollHeight;
+                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
+					if (!scrollbarsVisible) {
+						// IE doesn't recalculate width when scrollbar disappears
+						listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+					}
+                }
+                
+            }
+		},
+		selected: function() {
+			var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+			return selected && selected.length && $.data(selected[0], "ac_data");
+		},
+		emptyList: function (){
+			list && list.empty();
+		},
+		unbind: function() {
+			element && element.remove();
+		}
+	};
+};
+
+$.fn.selection = function(start, end) {
+	if (start !== undefined) {
+		return this.each(function() {
+			if( this.createTextRange ){
+				var selRange = this.createTextRange();
+				if (end === undefined || start == end) {
+					selRange.move("character", start);
+					selRange.select();
+				} else {
+					selRange.collapse(true);
+					selRange.moveStart("character", start);
+					selRange.moveEnd("character", end);
+					selRange.select();
+				}
+			} else if( this.setSelectionRange ){
+				this.setSelectionRange(start, end);
+			} else if( this.selectionStart ){
+				this.selectionStart = start;
+				this.selectionEnd = end;
+			}
+		});
+	}
+	var field = this[0];
+	if ( field.createTextRange ) {
+		var range = document.selection.createRange(),
+			orig = field.value,
+			teststring = "<->",
+			textLength = range.text.length;
+		range.text = teststring;
+		var caretAt = field.value.indexOf(teststring);
+		field.value = orig;
+		this.selection(caretAt, caretAt + textLength);
+		return {
+			start: caretAt,
+			end: caretAt + textLength
+		}
+	} else if( field.selectionStart !== undefined ){
+		return {
+			start: field.selectionStart,
+			end: field.selectionEnd
+		}
+	}
+};
+
+})(jQuery);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/jquery-autocomplete/todo	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,166 @@
+TODO
+
+- test formatItem implementation that returns (clickable) anchors
+- bug: handle del key; eg. type a letter, remove it using del, type same letter again: nothing happens
+- handle up/down keys in textarea (prevent default while select is open?)
+- docs: max:0 works, too, "removing" it(??)
+- fix ac_loading/options.loadingClass
+- support/enable request urls like foo/bar/10 instead of foo/q=10
+- urlencode request term before passing to $.ajax/data; evaluate why $.ajax doesn't handle that itself, if at all; try with umlauts, russian/danish/chinese characeters (see validate)
+- test what happens when an element gets focused programmatically (maybe even before then autcomplete is applied)
+- check if blur on selecting can be removed
+- fix keyhandling to ignore metakeys, eg. shift; especially important for chinese characters that need more then one key
+- enhance mustMatch: provide event/callback when a value gets deleted
+- handle tab key different then enter, eg. don't blur field or prevent default, just let it move on; in any case, no need to blur the field when selecting a value via tab, unlike return
+- prevent redundant requests on
+  - superstring returned no result, no need to query again for substring, eg. pete returned nothing, peter won't either
+  - previous query mustn't be requested again, eg. pete returns 10 lines, peter nothing, backspace to pete should get the 10 lines from cache (may need TimeToLive setting for cache to invalidate it)
+- incorporate improvements and suggestions by Hector: http://beta.winserver.com/public/test/MultiSuggestTest.wct
+- json support: An optional JSON format, that assumes a certain JSON format as default and just looks for a dataType "json" to be activated; [records], where each record is { id:String, label:String, moreOptionalValues... } 
+- accept callback as first argument to let users implement their own dynamic data (no caching) - consider async API
+- allow users to keep their incomplete value when pressing tab, just mimic the default-browser-autocomplete: tab doesn't select any proposed value -> tab closes the select and works normal otherwise
+- small bug in your autocomplete,  When setting autoFill:true I would expect formatResult to be called on autofill, it seems not to be the case.
+- add a callback to allow decoding the response
+- allow modification of not-last value in multiple-fields
+@option Number size Limit the number of items to show at once. Default: 
+@option Function parse - TEST AND DOCUMENT ME
+- add option to display selectbox on focus
+
+$input.bind("show", function() {
+	if ( !select.visible() ) {
+		onChange(0, true);
+	}
+});
+
+- reference: http://capxous.com/
+  - add "try ..." hints to demo
+  - check out demos
+- reference: http://createwebapp.com/demo/
+  
+- add option to hide selectbox when no match is found - see comment by Ian on plugin page (14. Juli 2007 04:31)
+- add example for reinitializing an autocomplete using unbind()
+
+- Add option to pass through additional arguments to $.ajax, like type to use POST instead of GET
+
+ - I found out that the problem with UTF-8 not being correctly sent can be solved on the server side by applying (PHP) rawurldecode() function, which decodes the Unicode characters sent by GET method and therefore URL-encoded.
+-> add that hint to docs and examples
+
+But I am trying this with these three values: “foo bar”, “foo foo”, and “foo far”, and if I enter “b” (or “ba”) nothing matches, if I enter “f” all three do match, and if I enter “fa” the last one matches.
+The problem seems to be that the cache is implemented with a first-character hashtable, so only after matching the first character, the latter ones are searched for.
+
+xml example:
+<script type="text/javascript">
+      function parseXML(data) {
+        var results = [];
+        var branches = $(data).find('item');
+        $(branches).each(function() {
+          var text = $.trim($(this).find('text').text());
+          var value = $.trim($(this).find('value').text());
+          //console.log(text);
+          //console.log(value);
+          results[results.length] = {'data': this, 'result': value, 'value': text};
+        });
+        $(results).each(function() {
+          //console.log('value', this.value);
+          //console.log('text', this.text);
+        });
+        //console.log(results);
+        return results;
+      };
+    $(YourOojHere).autocomplete(SERVER_AJAX_URL, {parse: parseXML});
+  </script>
+<?xml version="1.0"?>
+<ajaxresponse>
+  <item>
+    <text>
+      <![CDATA[<b>FreeNode:</b> irc.freenode.net:6667]]>
+    </text>
+    <value><![CDATA[irc.freenode.net:6667]]></value>
+  </item><item>
+    <text>
+      <![CDATA[<b>irc.oftc.net</b>:6667]]>
+    </text>
+    <value><![CDATA[irc.oftc.net:6667]]></value>
+  </item><item>
+    <text>
+      <![CDATA[<b>irc.undernet.org</b>:6667]]>
+    </text>
+    <value><![CDATA[irc.undernet.org:6667]]></value>
+  </item>
+</ajaxresponse>
+  
+  
+
+Hi all,
+
+I use Autocomplete 1.0 Alpha mostly for form inputs bound to foreign
+key columns. For instance I have a user_position table with two
+columns: user_id and position_id. On new appointment form I have two
+autocomplete text inputs with the following code:
+
+   <input type="text" id="user_id" class="ac_input"  tabindex="1" />
+   <input type="text" id="position_id" class="ac_input" tabindex="2" />
+
+As you can see the inputs do not have a name attribute, and when the
+form is submitted their values are not sent, which is all right since
+they will contain strings like:
+
+   'John Doe'
+   'Sales Manager'
+
+whereas our backend expects something like:
+
+   23
+   14
+
+which are the user_id for John Doe and position_id for Sales Manager.
+To send these values I have two hidden inputs in the form like this:
+
+   <input type="hidden" name="user_id" value="">
+   <input type="hidden" name="position_id" value="">
+
+Also I have the following code in the $().ready function:
+
+   $("#user_id").result(function(event, data, formatted) {
+     $("input[@name=user_id]").val(data[1]);
+   });
+   $("#position_id").result(function(event, data, formatted) {
+     $("input[@name=position_id]").val(data[1]);
+   });
+
+As could be seen these functions stuff user_id and position_id values
+(in our example 23 and 14) into the hidden inputs, and when the form
+is submitted these values are sent:
+
+   user_id = 23
+   position_id = 14
+
+The backend script then takes care of adding a record to our
+user_position table containing those values.
+
+I wonder how could the plugin code be modified to simplify the setup
+by taking care of adding hidden inputs and updating the value of
+hidden inputs as default behavior. I have successfully attempted a
+simpler solution - writing a wrapper to perform these additional tasks
+and invoke autocomplete as well. I hope my intention is clear enough,
+if not, this is exactly the expected outcome:
+
+Before:
+
+   <script type="text/javascript"
+   src="jquery.autocomplete-modified.js"></script>
+   <input type="text" name="user_id" class="ac_input" tabindex="1" />
+
+After:
+
+   <input type="text" id="user_id" class="ac_input" tabindex="1" />
+   <input type="hidden" name="user_id" value="23">
+
+
+Last word, I know this looks like a tall order, and I do not hope
+someone will make a complete working mod for me, but rather would very
+much appreciate helpful  advise and directions.
+
+Many thanks in advance
+Majid
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,21 @@
+/**
+ * @file
+ *   Adds some styles to the standard Drupal autocomplete widget.
+ */
+.apachesolr_autocomplete {
+}
+.apachesolr_autocomplete.message {
+  font-size:80%;
+  color:#888;
+}
+.apachesolr_autocomplete.count {
+  float:right;
+}
+div.apachesolr_autocomplete.suggestion {
+  display:inline;
+  float:left;
+}
+div.ac_results li {
+  list-style:none;
+  background-image: none !important;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,13 @@
+name = Search Solr autocomplete
+description = Enables autocomplete on Search Solr search boxes
+dependencies[] = solrsearch
+dependencies[] = solrsearch_search
+package = SolrSearch 
+core = "7.x"
+
+; Information added by drupal.org packaging script on 2012-08-29
+version = "7.x-1.3"
+core = "7.x"
+project = "solrsearch_autocomplete"
+datestamp = "1346272315"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.install	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * solrsearch Autocomplete module install, update and uninstall functions.
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function solrsearch_autocomplete_uninstall() {
+  // Delete variables.
+  variable_del('solrsearch_autocomplete_widget');
+  variable_del('solrsearch_autocomplete_suggest_keywords');
+  variable_del('solrsearch_autocomplete_suggest_spellcheck');
+  variable_del('solrsearch_autocomplete_counts');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.js	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,54 @@
+
+/**
+ * Adds the custom autocomplete widget behavior.
+ */
+Drupal.behaviors.solrsearch_autocomplete = {
+  attach: function(context) {
+	
+    jQuery(".solrsearch-autocomplete.unprocessed", context).add(".solrsearch-autocomplete.unprocessed input", context).autocomplete(Drupal.settings.solrsearch_autocomplete.path,
+    {
+      // Classnames for the widget.
+      extraParams : {fieldName:function(){
+    	  classes = document.activeElement.getAttribute("class");
+    	  splitted =classes.split(" ");
+    	  for (var i=0; i <splitted.length; i++){
+    		    if (splitted[i].match("^solrsearch-autocomplete.field.")){
+    		    	return splitted[i].replace("solrsearch-autocomplete.field.","");
+    		    }
+      			};
+    	  return "IM_author";
+      }},
+      inputClass: "",
+      loadingClass: "throbbing",
+      // Do not select first suggestion by default.
+      selectFirst: false,
+      // Specify no matching as it wil be done on server-side.
+      matchContains: false,
+      matchSubset: false,
+      // Maximum number of items to show in widget.
+      max: 50,
+      scroll: true,
+      scrollHeight: 360,
+      // Data returned from server is JSON-encoded.
+      dataType: "json",
+      // Function to parse returned json into elements.
+      parse: function(data) {
+        return jQuery.map(data, function(item) {
+          return {
+            data: item,          // Echo the input data.
+            value: item.display, // This will be shown in the options widget.
+            result: item.key     // The actual value to put into the form element.
+          }
+        });
+      },
+      // Return the HTML to display in the options widget.
+      formatItem: function(item) {
+        return item.display;
+      }
+    }).result(function(item, element) {
+      // Handle selection of an element in the autocomplete widget.
+      // We should submit the widget's parent form.
+      jQuery(this).get(0).form.submit();
+    }).addClass('form-autocomplete'); // Add Drupal autocomplete widget's style.
+  }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/modules/custom/solrsearch_autocomplete/solrsearch_autocomplete.module	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,519 @@
+<?php
+
+/**
+ * @file
+ *   Alters search forms to suggest terms using Apache Solr using AJAX.
+ *   Thanks to:
+ *     robertDouglass who contributed some of the code.
+ *     sch4lly for contributing to D7 version
+ */
+
+/**
+ * Implementation of hook_init().
+ */
+function solrsearch_autocomplete_init() {
+
+  drupal_add_css( drupal_get_path('module', 'solrsearch_autocomplete') . '/solrsearch_autocomplete.css');
+
+  // If using custom JS widget, include files and settings.
+  if (solrsearch_autocomplete_variable_get_widget() == 'custom') {
+    // Add custom autocomplete files
+    drupal_add_js(drupal_get_path('module', 'solrsearch_autocomplete') .'/solrsearch_autocomplete.js');
+    drupal_add_js(drupal_get_path('module', 'solrsearch_autocomplete') .'/jquery-autocomplete/jquery.autocomplete.js');
+    drupal_add_css( drupal_get_path('module', 'solrsearch_autocomplete') .'/jquery-autocomplete/jquery.autocomplete.css');
+    // Specify path to autocomplete handler.
+    drupal_add_js(array('solrsearch_autocomplete' => array('path' => url('solrsearch_autocomplete'))), 'setting');
+  }
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function solrsearch_autocomplete_form_search_form_alter(&$form, $form_state) {
+
+  if ($form['module']['#value'] == 'solrsearch_search' || $form['module']['#value'] == 'solrsearch_multisitesearch') {
+    $element = &$form['basic']['keys'];
+    solrsearch_autocomplete_do_alter($element);
+  }
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function solrsearch_autocomplete_form_solrsearch_search_block_form_alter(&$form, $form_state) {
+
+  $element = &$form['solrsearch_search_block_form'];
+  solrsearch_autocomplete_do_alter($element,"title_s,title,author");
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function solrsearch_autocomplete_form_solrsearch_search_author_block_form_alter(&$form, $form_state) {
+
+  $element = &$form['solrsearch_search_author_block_form'];
+
+  solrsearch_autocomplete_do_alter($element,"author");
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function solrsearch_autocomplete_form_solrsearch_search_title_block_form_alter(&$form, $form_state) {
+
+  $element = &$form['solrsearch_search_title_block_form'];
+
+  solrsearch_autocomplete_do_alter($element,"title_s,title");
+}
+/**
+ * Helper function to do the actual altering of search forms.
+ *
+ * @param $element
+ *   The element to alter. Should be passed by reference so that original form
+ *   element will be altered.
+ *   E.g.: solrsearch_autocomplete_do_alter(&$form['xyz'])
+ */
+function solrsearch_autocomplete_do_alter(&$element,$field_name) {
+
+  if (solrsearch_autocomplete_variable_get_widget() == 'custom') {
+    // Create elements if they do not exist.
+    if (!isset($element['#attributes'])) {
+      $element['#attributes'] = array();
+    }
+    if (!isset($element['#attributes']['class'])) {
+      $element['#attributes']['class'] = array();
+    }
+    array_push($element['#attributes']['class'], 'solrsearch-autocomplete', 'unprocessed','solrsearch-autocomplete.field.'.$field_name);
+
+  }
+  else {
+    $element['#autocomplete_path'] = 'solrsearch_autocomplete';
+  }
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function solrsearch_autocomplete_menu() {
+  $items = array();
+
+  $items['solrsearch_autocomplete'] = array(
+    'page callback' => 'solrsearch_autocomplete_callback',
+    'access callback' => 'user_access',
+    'access arguments' => array('search content'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Callback for url solrsearch_autocomplete/autocomplete.
+ * @param $keys
+ *   The user-entered query.
+ */
+function solrsearch_autocomplete_callback($keys = '', $field_name='') {
+
+
+  if (solrsearch_autocomplete_variable_get_widget() == 'custom') {
+    // Keys for custom widget come from $_GET.
+    $keys = $_GET['query'];
+    $field_names =$_GET['fieldName'];
+  }
+
+  /*
+  $res = split("\:",$keys);
+
+  if (count($res) == 2){
+    $keys=$res[1];
+    $field_name=$res[0];
+  } else {
+    $keys=$res[0];
+    $field_name="";
+  }
+  */
+
+  $suggestions = array();
+
+  foreach (explode(",",$field_names) as $field_name) {
+
+  $suggestions = array_merge($suggestions, solrsearch_autocomplete_suggest_word_completion($keys, 5,$field_name));
+  if (solrsearch_autocomplete_variable_get_suggest_keywords() || solrsearch_autocomplete_variable_get_suggest_spellcheck()) {
+    $suggestions = array_merge($suggestions, solrsearch_autocomplete_suggest_additional_term($keys, 5,$field_name));
+
+  }
+  }
+  $result = array();
+  if (solrsearch_autocomplete_variable_get_widget() == 'custom') {
+    // Place suggestions into new array for returning as JSON.
+    foreach ($suggestions as $key => $display) {
+      $result[] = array(
+        "key" => substr($key,1),
+        "display" => $display
+      );
+    }
+  }
+  else {
+    foreach ($suggestions as $key => $display) {
+      $result[substr($key,1)] =$display;
+    }
+  }
+  drupal_json_output($result);
+  exit();
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function solrsearch_autocomplete_theme() {
+  return array(
+    'solrsearch_autocomplete_highlight' => array(
+      'file' => 'solrsearch_autocomplete.module',
+      'arguments' => array(
+        'keys' => NULL,
+        'suggestion' => NULL,
+        'count' => NULL,
+      ),
+    ),
+    'solrsearch_autocomplete_spellcheck' => array(
+      'file' => 'solrsearch_autocomplete.module',
+      'arguments' => array(
+        'suggestion' => NULL,
+      ),
+    ),
+  );
+}
+
+/**
+ * Themes each returned suggestion.
+ */
+function theme_solrsearch_autocomplete_highlight($variables) {
+  static $first = true;
+  $html = '';
+  $html .= '<div class="solrsearch_autocomplete suggestion">';
+  $html .= '<strong>' . drupal_substr($variables['suggestion'], 0, strlen($variables['keys'])) . '</strong>' . drupal_substr($variables['suggestion'], strlen($variables['keys']));
+  $html .= '</div>';
+  if ($variables['count'] && $variables['show_counts']) {
+    if ($first) {
+      $html .= "<div class='solrsearch_autocomplete message' style='float:right'>";
+      $html .= t('!count results', array('!count' => $variables['count']));
+      $html .= "</div><br style='clear:both'>";
+      $first = false;
+    } else {
+      $html .= "<div class='solrsearch_autocomplete message count'>" . $variables['count'] . "</div><br style='clear:both'>";
+    }
+  }
+  return $html;
+}
+
+/**
+ * Themes the spellchecker's suggestion.
+ */
+function theme_solrsearch_autocomplete_spellcheck($variables) {
+  return '<span class="solrsearch_autocomplete message">' . t('Did you mean') .':</span> ' . $variables['suggestion'];
+}
+
+/**
+ * Return the basic set of parameters for the Solr query.
+ *
+ * @param $suggestions_to_return
+ *   Number of facets to return.
+ * @return array
+ */
+function solrsearch_autocomplete_basic_params($suggestions_to_return,$facet_name) {
+  return array(
+    'facet' => 'true',
+    'facet.field' => array($facet_name),
+    // We ask for $suggestions_to_return * 5 facets, because we want
+    // not-too-frequent terms (will be filtered below). 5 is just my best guess.
+    'facet.limit' => $suggestions_to_return * 5,
+    'facet.mincount' => 1,
+    'start' => 0,
+    'rows' => 0,
+  );
+}
+
+/**
+ * Helper function that suggests ways to complete partial words.
+ *
+ * For example, if $keys = "learn", this might return suggestions like:
+ *    learn, learning, learner, learnability.
+ * The suggested terms are returned in order of frequency (most frequent first).
+ *
+ */
+function solrsearch_autocomplete_suggest_word_completion($keys, $suggestions_to_return = 5, $facet_name="author") {
+  /**
+   * Split $keys into two:
+   *  $first_part will contain all complete words (delimited by spaces). Can be empty.
+   *  $last_part is the (assumed incomplete) last word. If this is empty, don't suggest.
+   * Example:
+   *  $keys = "learning dis" : $first_part = "learning", $last_part = "dis"
+   */
+  preg_match('/^(:?(.* |))([^ ]+)$/', $keys, $matches);
+  $first_part = @$matches[2];
+  // Make sure $last_part contains meaningful characters
+  $last_part = preg_replace('/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', '', @$matches[3]);
+  if ($last_part == '') {
+    return array();
+  }
+  // Ask Solr to return facets that begin with $last_part; these will be the suggestions.
+  $params = solrsearch_autocomplete_basic_params($suggestions_to_return,$facet_name);
+  $params['facet.prefix'] = $last_part;
+  // Get array of themed suggestions.
+  $result = solrsearch_autocomplete_suggest($first_part, $params, 'solrsearch_autocomplete_highlight', $keys, $suggestions_to_return);
+  if ($result && $result['suggestions']) {
+    return $result['suggestions'];
+  } else {
+    return array();
+  }
+}
+
+/**
+ * Helper function that suggests additional terms to search for.
+ *
+ * For example, if $keys = "learn", this might return suggestions like:
+ *    learn student, learn school, learn mathematics.
+ * The suggested terms are returned in order of frequency (most frequent first).
+ */
+function solrsearch_autocomplete_suggest_additional_term($keys, $suggestions_to_return = 5, $facet_name="author") {
+  $keys = trim($keys);
+  $keys = check_plain($keys);
+  if ($keys == '') {
+    return array();
+  }
+  // Return no suggestions when $keys consists of only word delimiters
+  if (drupal_strlen(preg_replace('/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', '', $keys)) < 1) {
+    return array();
+  }
+
+  // Ask Solr to return facets from the 'spell' field to use as suggestions.
+  $params = solrsearch_autocomplete_basic_params($suggestions_to_return,$facet_name);
+
+  // Initialize arrays
+  $suggestions = array();
+  $replacements = array();
+
+  // Get array of themed suggestions.
+  $result = solrsearch_autocomplete_suggest($keys, $params, 'solrsearch_autocomplete_highlight', $keys, $suggestions_to_return);
+  if ($result && solrsearch_autocomplete_variable_get_suggest_keywords()) {
+    if (isset($result['suggestions']) && sizeof($result['suggestions'])) {
+      $suggestions = array_merge($suggestions, $result['suggestions']);
+    }
+  }
+
+  // Suggest using the spellchecker
+  if (solrsearch_autocomplete_variable_get_suggest_spellcheck()) {
+    if (isset($result['response']->spellcheck) && isset($result['response']->spellcheck->suggestions)) {
+      $spellcheck_suggestions = get_object_vars($result['response']->spellcheck->suggestions);
+      foreach($spellcheck_suggestions as $word => $value) {
+        $replacements[$word] = $value->suggestion[0];
+      }
+      if (count($replacements)) {
+        $new_keywords = strtr($keys, $replacements);
+        if ($new_keywords != $keys) {
+          // Place spellchecker suggestion before others
+          $suggestions = array_merge(array('*' . $new_keywords => theme('solrsearch_autocomplete_spellcheck', array('suggestion' => $new_keywords))), $suggestions);
+        }
+      }
+    }
+  }
+
+  return $suggestions;
+}
+
+
+function solrsearch_autocomplete_suggest($keys, $params, $theme_callback, $orig_keys, $suggestions_to_return = 5) {
+  $matches = array();
+  $suggestions = array();
+  $keys = trim($keys);
+  $show_counts = solrsearch_autocomplete_variable_get_counts();
+
+  // We need the keys array to make sure we don't suggest words that are already
+  // in the search terms.
+  $keys_array = explode(' ', $keys);
+  $keys_array = array_filter($keys_array);
+
+  // Query Solr for $keys so that suggestions will always return results.
+  $query = solrsearch_drupal_query($keys);
+
+  // This hook allows modules to modify the query and params objects.
+  drupal_alter('solrsearch_query', $query);
+  if (!$query) {
+    return array();
+  }
+  solrsearch_search_add_spellcheck_params($query);
+  foreach ($params as $param => $paramValue) {
+    $query->addParam($param, $paramValue);
+  }
+  solrsearch_search_add_boost_params($query);
+
+  // Query Solr
+  $response = $query->search($keys);
+  // Loop through requested fields and get suggestions.
+  foreach ($params['facet.field'] as $field) {
+    foreach ($response->facet_counts->facet_fields->{$field} as $terms => $count) {
+
+
+
+      $terms = preg_replace('/[_-]+/', ' ', $terms);
+      $term=$terms;
+        if ($term) {
+
+          if (isset($matches[$term])) {
+            $matches[$term] += $count;
+          }
+          else {
+            $matches[$term] = $count;
+          }
+        }
+
+    }
+  }
+
+  if (sizeof($matches) > 0) {
+    // Eliminate suggestions that are stopwords or are already in the query.
+    $matches_clone = $matches;
+    $stopwords = solrsearch_autocomplete_get_stopwords();
+    foreach ($matches_clone as $term => $count) {
+      if ((strlen($term) > 3) && !in_array($term, $stopwords) && !array_search($term, $keys_array)) {
+        // Longer strings get higher ratings.
+        #$matches_clone[$term] += strlen($term);
+      }
+      else {
+        unset($matches_clone[$term]);
+        unset($matches[$term]);
+      }
+    }
+
+    // Don't suggest terms that are too frequent (in >90% of results).
+    $max_occurence =  $response->response->numFound * 0.90;
+    foreach ($matches_clone as $match => $count) {
+      if ($count > $max_occurence) {
+        unset($matches_clone[$match]);
+      }
+    }
+
+    // The $count in this array is actually a score. We want the highest ones first.
+    arsort($matches_clone);
+
+    // Shorten the array to the right ones.
+    $matches_clone = array_slice($matches_clone, 0, $suggestions_to_return, TRUE);
+
+    // Add current search as suggestion if results > 0
+    if ($response->response->numFound > 0 && $keys != '') {
+      // Add * to array element key to force into a string, else PHP will
+      // renumber keys that look like numbers on the returned array.
+      $suggestions['*' . $keys] = theme('solrsearch_autocomplete_highlight', array('keys' => $keys, 'suggestion' => $keys, 'count' => $response->response->numFound, 'show_counts' => $show_counts));
+    }
+
+    // Build suggestions using returned facets
+    foreach ($matches_clone as $match => $count) {
+      if ($keys != $match) {
+        $suggestion = trim($keys . ' ' . $match);
+        // On cases where there are more than 3 keywords, omit displaying
+        //  the count because of the mm settings in solrconfig.xml
+        if (substr_count($suggestion, ' ') >= 2) {
+          $count = 0;
+        }
+        if ($suggestion != '') {
+          // Add * to array element key to force into a string, else PHP will
+          // renumber keys that look like numbers on the returned array.
+          $suggestions['*' . $suggestion] = theme('solrsearch_autocomplete_highlight', array('keys' => $orig_keys, 'suggestion' => $suggestion, 'count' => $count, 'show_counts' => $show_counts));
+        }
+      }
+    }
+  }
+
+  return array(
+    'suggestions' => $suggestions,
+    'response' => &$response
+  );
+}
+
+/**
+ * Gets the current stopwords list configured in Solr.
+ */
+function solrsearch_autocomplete_get_stopwords() {
+  static $words = array(), $flag = false;
+  if ($flag) {
+    return $words;
+  }
+  $stopwords_url = "/admin/file/?file=stopwords.txt";
+  $host = variable_get('solrsearch_host', 'localhost');
+  $port = variable_get('solrsearch_port', 8983);
+  $path = variable_get('solrsearch_path', '/solr');
+  $url = "http://{$host}:{$port}{$path}{$stopwords_url}";
+  $result = drupal_http_request($url);
+  if ($result->code != 200) {
+    return array();
+  }
+  $words = array();
+  foreach (explode("\n", $result->data) as $line) {
+    if (drupal_substr($line, 0, 1) == "#") {
+      continue;
+    }
+    if ($word = trim($line)) {
+      $words[] = $word;
+    }
+  }
+  $flag = true;
+  return $words;
+}
+
+/**
+ * Wrapper around variable_get() for variable solrsearch_autocomplete_widget.
+ */
+function solrsearch_autocomplete_variable_get_widget() {
+  return variable_get('solrsearch_autocomplete_widget', 'custom');
+}
+
+/**
+ * Wrapper around variable_get() for variable solrsearch_autocomplete_suggest_keywords.
+ */
+function solrsearch_autocomplete_variable_get_suggest_keywords() {
+  return variable_get('solrsearch_autocomplete_suggest_keywords', 1);
+}
+
+/**
+ * Wrapper around variable_get() for variable solrsearch_autocomplete_suggest_spellcheck.
+ */
+function solrsearch_autocomplete_variable_get_suggest_spellcheck() {
+  return variable_get('solrsearch_autocomplete_suggest_spellcheck', 1);
+}
+
+/**
+ * Wrapper around variable_get() for variable solrsearch_autocomplete_counts.
+ */
+function solrsearch_autocomplete_variable_get_counts() {
+  return variable_get('solrsearch_autocomplete_counts', TRUE);
+}
+
+/**
+ * Alter the solrsearch.module "advanced settings" form.
+ */
+function solrsearch_autocomplete_form_solrsearch_settings_alter(&$form, $form_state) {
+  $form['advanced']['solrsearch_autocomplete_widget'] = array(
+    '#type' => 'radios',
+    '#title' => t('Autocomplete widget to use'),
+    '#description' => t('The custom widget provides instant search upon selection, whereas the Drupal widget needs the user to hit Enter or click on the Search button. If you are having problems, try switching to the default Drupal autocomplete widget.'),
+    '#options' => array('custom' => t('Custom autocomplete widget'), 'drupal' => t('Drupal core autocomplete widget')),
+    '#default_value' => solrsearch_autocomplete_variable_get_widget(),
+  );
+  $form['advanced']['solrsearch_autocomplete_suggest_keywords'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable additional keyword suggestions on the autocomplete widget'),
+    '#description' => t('Suggest words to add to the currently typed-in words. E.g.: typing "blue" might suggest "blue bike" or "blue shirt".'),
+    '#default_value' => solrsearch_autocomplete_variable_get_suggest_keywords(),
+  );
+  $form['advanced']['solrsearch_autocomplete_suggest_spellcheck'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable spellchecker suggestions on the autocomplete widget'),
+    '#description' => t('Suggest corrections to the currently typed-in words. E.g.: typing "rec" or "redd" might suggest "red".'),
+    '#default_value' => solrsearch_autocomplete_variable_get_suggest_spellcheck(),
+  );
+  $form['advanced']['solrsearch_autocomplete_counts'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable counts in autocomplete widget suggestions'),
+    '#description' => t('WARNING: Counts shown alongside suggestions might be lower than the actual result count due to stemming and minimum match (mm) settings in solrconfig.xml.'),
+    '#default_value' => solrsearch_autocomplete_variable_get_counts(),
+  );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/README.txt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,25 @@
+
+ABOUT STARK
+-----------
+
+The Stark theme is provided for demonstration purposes; it uses Drupal's default
+HTML markup and CSS styles. It can be used as a troubleshooting tool to
+determine whether module-related CSS and JavaScript are interfering with a more
+complex theme, and can be used by designers interested in studying Drupal's
+default markup without the interference of changes commonly made by more complex
+themes.
+
+To avoid obscuring CSS added to the page by Drupal or a contrib module, the
+Stark theme itself has no styling, except just enough CSS to arrange the page in
+a traditional "Header, sidebars, content, and footer" layout. See the layout.css
+file for more information.
+
+
+ABOUT DRUPAL THEMING
+--------------------
+
+To learn how to build your own custom theme and override Drupal's default code,
+see the Theming Guide: http://drupal.org/theme-guide
+
+See the sites/all/themes/README.txt for more information on where to place your
+custom themes to ensure easy maintenance and upgrades.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/block.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display a block.
+ *
+ * Available variables:
+ * - $block->subject: Block title.
+ * - $content: Block content.
+ * - $block->module: Module that generated the block.
+ * - $block->delta: An ID for the block, unique within each module.
+ * - $block->region: The block region embedding the current block.
+ * - $classes: String of classes that can be used to style contextually through
+ *   CSS. It can be manipulated through the variable $classes_array from
+ *   preprocess functions. The default values can be one or more of the
+ *   following:
+ *   - block: The current template type, i.e., "theming hook".
+ *   - block-[module]: The module generating the block. For example, the user
+ *     module is responsible for handling the default user navigation block. In
+ *     that case the class would be 'block-user'.
+ * - $title_prefix (array): An array containing additional output populated by
+ *   modules, intended to be displayed in front of the main title tag that
+ *   appears in the template.
+ * - $title_suffix (array): An array containing additional output populated by
+ *   modules, intended to be displayed after the main title tag that appears in
+ *   the template.
+ *
+ * Helper variables:
+ * - $classes_array: Array of html class attribute values. It is flattened
+ *   into a string within the variable $classes.
+ * - $block_zebra: Outputs 'odd' and 'even' dependent on each block region.
+ * - $zebra: Same output as $block_zebra but independent of any block region.
+ * - $block_id: Counter dependent on each block region.
+ * - $id: Same output as $block_id but independent of any block region.
+ * - $is_front: Flags true when presented in the front page.
+ * - $logged_in: Flags true when the current user is a logged-in member.
+ * - $is_admin: Flags true when the current user is an administrator.
+ * - $block_html_id: A valid HTML ID and guaranteed unique.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_block()
+ * @see template_process()
+ *
+ * @ingroup themeable
+ */
+?>
+<div id="<?php print $block_html_id; ?>" class="side-block <?php print $classes; ?>"<?php print $attributes; ?>>
+  <?php print render($title_prefix); ?>
+<?php if ($block->subject): ?>
+  <h2<?php print $title_attributes; ?>><?php print $block->subject ?></h2>
+<?php endif;?>
+  <?php print render($title_suffix); ?>
+  <div class="item"<?php print $content_attributes; ?>>
+    <?php print $content ?>
+  </div>
+</div>
Binary file sites/all/themes/mpiwgDev/color/base.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/color/color.inc	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,103 @@
+<?php
+
+//'#fdf8ef','#d45a00','#fdf8ef','#fdf8ef','#fffff' => t('Web Main (Default)'),
+
+// Put the logo path into JavaScript for the live preview.
+drupal_add_js(array('color' => array('logo' => theme_get_setting('logo', 'mpiwgDev'))), 'setting');
+
+$info = array(
+  // Available colors and color labels used in theme.
+  'fields' => array(
+      'headings' => t('Ueberschriften'),
+      'base' => t('Hintergrund'),
+    'top' => t('Hintergrud Content'),
+    'sidebarborders' => t('Trennstriche'),
+    'titleslogan' => t('Title and slogan'),
+    'text' => t('Text color'),
+      'link' => t('Link color'),
+      'searchbox' => t('Search box'),
+
+  ),
+  // Pre-defined color schemes.
+  'schemes' => array(
+    'default' => array(
+      'title' => t('MPIWG  (default)'),
+      'colors' => array(
+        'base' => '#ebecf3',
+        'top' => '#f8f8f9',
+        'sidebarborders' => '#9da0c2',
+        'titleslogan' => '#3c4286',
+        'text' => '#000000',
+        'link' => '#000b86',
+        'headings' => '#c10000',
+        'searchbox'=> '#ebecf3'
+      ),
+    ),
+    'institute_add' => array(
+      'title' => t('Additional institute sites'),
+        'colors' => array(
+        'base' => '#dce8e8',
+        'top' => '#f8f8f9',
+        'sidebarborders' => '#9da0c2',
+        'titleslogan' => '#3c4286',
+        'text' => '#000000',
+        'link' => '#3c4286',
+        'headings' => '#00b2a3',
+        'searchbox'=> '#ebecf3'
+      ),
+        ),
+      'institute_add2' => array(
+          'title' => t('Additional institute sites II'),
+          'colors' => array(
+              'base' => '#f4f9e5',
+              'top' => '#f8f8f9',
+              'sidebarborders' => '#9da0c2',
+              'titleslogan' => '#3c4286',
+              'text' => '#000000',
+              'link' => '#3c4286',
+              'headings' => '#79a000',
+              'searchbox'=> '#ebecf3'
+          ),
+      ),
+      ),
+
+  // CSS files (excluding @import) to rewrite with new color scheme.
+  'css' => array(
+    'layout.css',
+  ),
+
+  // Files to copy.
+  'copy' => array(
+    'logo.png',
+  ),
+
+  // Gradient definitions.
+  'gradients' => array(
+    array(
+      // (x, y, width, height).
+      'dimension' => array(0, 0, 0, 0),
+      // Direction of gradient ('vertical' or 'horizontal').
+      'direction' => 'vertical',
+      // Keys of colors to use for the gradient.
+      'colors' => array('top', 'bottom'),
+    ),
+  ),
+
+  // Color areas to fill (x, y, width, height).
+  'fill' => array(),
+
+  // Coordinates of all the theme slices (x, y, width, height)
+  // with their filename as used in the stylesheet.
+  'slices' => array(),
+
+  // Reference color used for blending. Matches the base.png's colors.
+  'blend_target' => '#ffffff',
+
+  // Preview files.
+  'preview_css' => 'color/preview.css',
+  'preview_js' => 'color/preview.js',
+  'preview_html' => 'color/preview.html',
+
+  // Base file for image generation.
+  'base_image' => 'color/base.png',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/color/preview.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,200 @@
+
+/* ---------- Color form ----------- */
+#color_scheme_form #palette .form-item {
+  width: 25em;
+}
+#color_scheme_form #palette .form-item label {
+  width: 15em;
+}
+
+/* ---------- Preview Styles ----------- */
+
+html.js #preview {
+  clear: both;
+  float: none !important;
+}
+#preview {
+  background-color: #fff;
+  font-family: Georgia, "Times New Roman", Times, serif;
+  font-size: 14px;
+  line-height: 1.5;
+  overflow: hidden;
+  word-wrap: break-word;
+  margin-bottom: 10px;
+}
+#preview-header {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  position: relative;
+}
+#preview-logo {
+  float: left;
+  padding: 15px 15px 15px 10px;
+}
+#preview-site-name {
+  color: #686868;
+  font-weight: normal;
+  font-size: 1.821em;
+  line-height: 1;
+  margin-bottom: 30px;
+  margin-left: 15px;
+  padding-top: 34px;
+}
+#preview-main-menu {
+  clear: both;
+  padding: 0 15px 3px;
+}
+#preview-main-menu-links a {
+  color: #d9d9d9;
+  padding: 0.6em 1em 0.4em;
+}
+#preview-main-menu-links {
+  font-size: 0.929em;
+  margin: 0;
+  padding: 0;
+}
+#preview-main-menu-links a {
+  color: #333;
+  background: #ccc;
+  background: rgba(255, 255, 255, 0.7);
+  text-shadow: 0 1px #eee;
+  -khtml-border-radius-topleft: 8px;
+  -moz-border-radius-topleft: 8px;
+  -webkit-border-top-left-radius: 8px;
+  border-top-left-radius: 8px;
+  -khtml-border-radius-topright: 8px;
+  -moz-border-radius-topright: 8px;
+  -webkit-border-top-right-radius: 8px;
+  border-top-right-radius: 8px;
+}
+#preview-main-menu-links a:hover,
+#preview-main-menu-links a:focus {
+  background: #fff;
+  background: rgba(255, 255, 255, 0.95);
+}
+#preview-main-menu-links a:active {
+  background: #b3b3b3;
+  background: rgba(255, 255, 255, 1);
+}
+#preview-main-menu-links li a.active {
+  border-bottom: none;
+}
+#preview-main-menu-links li {
+  display: inline;
+  list-style-type: none;
+  padding: 0.6em 0 0.4em;
+}
+#preview-sidebar,
+#preview-content {
+  display: inline;
+  float: left;
+  position: relative;
+}
+#preview-sidebar {
+  margin-left: 15px;
+  width: 210px;
+}
+#preview-content {
+  margin-left: 30px;
+  width: 26.5em;
+}
+#preview-sidebar .preview-block {
+  border: 1px solid;
+  margin: 20px 0;
+  padding: 15px 20px;
+}
+#preview-sidebar h2 {
+  border-bottom: 1px solid #d6d6d6;
+  font-size: 1.071em;
+  font-weight: normal;
+  line-height: 1.2;
+  margin: 0 0 0.5em;
+  padding-bottom: 5px;
+  text-shadow: 0 1px 0 #fff;
+}
+#preview .preview-block .preview-content {
+  margin-top: 1em;
+}
+#preview .preview-block-menu .preview-content,
+#preview .preview-block-menu .preview-content ul {
+  margin-top: 0;
+}
+#preview-main {
+  margin-bottom: 40px;
+  margin-top: 20px;
+}
+#preview-page-title {
+  font-size: 2em;
+  font-weight: normal;
+  line-height: 1;
+  margin: 1em 0 0.5em;
+}
+#preview-footer-wrapper {
+  color: #c0c0c0;
+  color: rgba(255, 255, 255, 0.65);
+  display: block !important;
+  font-size: 0.857em;
+  padding: 20px 20px 25px;
+}
+#preview-footer-wrapper a {
+  color: #fcfcfc;
+  color: rgba(255, 255, 255, 0.8);
+}
+#preview-footer-wrapper a:hover,
+#preview-footer-wrapper a:focus {
+  color: #fefefe;
+  color: rgba(255, 255, 255, 0.95);
+  text-decoration: underline;
+}
+#preview-footer-wrapper .preview-footer-column {
+  display: inline;
+  float: left;
+  padding: 0 10px;
+  position: relative;
+  width: 220px;
+}
+#preview-footer-wrapper .preview-block {
+  border: 1px solid #444;
+  border-color: rgba(255, 255, 255, 0.1);
+  margin: 20px 0;
+  padding: 10px;
+}
+#preview-footer-columns .preview-block-menu {
+  border: none;
+  margin: 0;
+  padding: 0;
+}
+#preview-footer-columns h2 {
+  border-bottom: 1px solid #555;
+  border-color: rgba(255, 255, 255, 0.15);
+  font-size: 1em;
+  margin-bottom: 0;
+  padding-bottom: 3px;
+  text-transform: uppercase;
+}
+#preview-footer-columns .preview-content {
+  margin-top: 0;
+}
+#preview-footer-columns .preview-content ul {
+  margin-left: 0;
+  padding-left: 0;
+}
+#preview-footer-columns .preview-content li {
+  list-style: none;
+  list-style-image: none;
+  margin: 0;
+  padding: 0;
+}
+#preview-footer-columns .preview-content li a {
+  border-bottom: 1px solid #555;
+  border-color: rgba(255, 255, 255, 0.15);
+  display: block;
+  line-height: 1.2;
+  padding: 0.8em 2px 0.8em 20px;
+  text-indent: -15px;
+}
+#preview-footer-columns .preview-content li a:hover,
+#preview-footer-columns .preview-content li a:focus {
+  background-color: #1f1f21;
+  background-color: rgba(255, 255, 255, 0.05);
+  text-decoration: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/color/preview.html	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,65 @@
+<div id="preview">
+
+  <div id="preview-header">
+    <div id="preview-logo"><img src="../../../themes/bartik/logo.png" alt="Site Logo" /></div>
+    <div id="preview-site-name">Bartik</div>
+    <div id="preview-main-menu">
+      <ul id="preview-main-menu-links">
+        <li><a>Home</a></li>
+        <li><a>Te Quidne</a></li>
+        <li><a>Vel Torqueo Quae Erat</a></li>
+      </ul>
+    </div>
+  </div>
+
+  <div id="preview-main" class="clearfix">
+    <div id="preview-sidebar">
+      <div id="preview-block" class="preview-block">
+        <h2>Etiam est risus</h2>
+        <div class="preview-content">
+          Maecenas id porttitor Ut enim ad minim veniam, quis nostrudfelis.
+          Laboris nisi ut aliquip ex ea.
+        </div>
+      </div>
+    </div>
+    <div id="preview-content">
+      <h1 id="preview-page-title">Lorem ipsum dolor</h1>
+      <div id="preview-node">
+        <div class="preview-content">
+          Sit amet, <a>consectetur adipisicing elit</a>, sed do eiusmod tempor
+          incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
+          nostrud <a>exercitation ullamco</a> laboris nisi ut aliquip ex ea
+          commodo consequat. Maecenas id porttitor Ut enim ad minim veniam, quis nostr udfelis.
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div id="preview-footer-wrapper">
+    <div id="preview-footer-columns" class="clearfix">
+      <div class="preview-footer-column">
+        <div class="preview-block">
+          <h2>Etiam est risus</h2>
+          <div class="content">
+            Maecenas id porttitor Ut enim ad minim veniam, quis nostrudfelis.
+            Laboris nisi ut aliquip ex ea.
+          </div>
+        </div>
+      </div>
+      <div class="preview-footer-column">
+        <div class="preview-block preview-block-menu">
+          <h2>Erisus dolor</h2>
+          <div class="preview-content">
+            <ul>
+              <li><a>Donec placerat</a></li>
+              <li><a>Nullam nibh dolor</a></li>
+              <li><a>Blandit sed</a></li>
+              <li><a>Fermentum id</a></li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/color/preview.js	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,39 @@
+
+(function ($) {
+  Drupal.color = {
+    logoChanged: false,
+    callback: function(context, settings, form, farb, height, width) {
+      // Change the logo to be the real one.
+      if (!this.logoChanged) {
+        $('#preview #preview-logo img').attr('src', Drupal.settings.color.logo);
+        this.logoChanged = true;
+      }
+      // Remove the logo if the setting is toggled off. 
+      if (Drupal.settings.color.logo == null) {
+        $('div').remove('#preview-logo');
+      }
+
+      // Solid background.
+      $('#preview', form).css('backgroundColor', $('#palette input[name="palette[bg]"]', form).val());
+
+      // Text preview.
+      $('#preview #preview-main h2, #preview .preview-content', form).css('color', $('#palette input[name="palette[text]"]', form).val());
+      $('#preview #preview-content a', form).css('color', $('#palette input[name="palette[link]"]', form).val());
+
+      // Sidebar block.
+      $('#preview #preview-sidebar #preview-block', form).css('background-color', $('#palette input[name="palette[sidebar]"]', form).val());
+      $('#preview #preview-sidebar #preview-block', form).css('border-color', $('#palette input[name="palette[sidebarborders]"]', form).val());
+
+      // Footer wrapper background.
+      $('#preview #preview-footer-wrapper', form).css('background-color', $('#palette input[name="palette[footer]"]', form).val());
+
+      // CSS3 Gradients.
+      var gradient_start = $('#palette input[name="palette[top]"]', form).val();
+      var gradient_end = $('#palette input[name="palette[bottom]"]', form).val();
+
+      $('#preview #preview-header', form).attr('style', "background-color: " + gradient_start + "; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(" + gradient_start + "), to(" + gradient_end + ")); background-image: -moz-linear-gradient(-90deg, " + gradient_start + ", " + gradient_end + ");");
+
+      $('#preview #preview-site-name', form).css('color', $('#palette input[name="palette[titleslogan]"]', form).val());
+    }
+  };
+})(jQuery);
Binary file sites/all/themes/mpiwgDev/color/preview.png has changed
Binary file sites/all/themes/mpiwgDev/icons/blue/Download.png has changed
Binary file sites/all/themes/mpiwgDev/icons/blue/Email.png has changed
Binary file sites/all/themes/mpiwgDev/icons/blue/IconsSatellitenseiten.psd has changed
Binary file sites/all/themes/mpiwgDev/icons/blue/LinkExtern.png has changed
Binary file sites/all/themes/mpiwgDev/icons/blue/LinkIntern.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/layout.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1343 @@
+
+/**
+ * @file
+ * Stark layout method
+ *
+ * To avoid obscuring CSS added to the page by Drupal or a contrib module, the
+ * Stark theme itself has no styling, except just enough CSS to arrange the page
+ * in a traditional "Header, sidebars, content, and footer" layout.
+ *
+ * This layout method works reasonably well, but shouldn't be used on a
+ * production site because it can break. For example, if an over-large image
+ * (one that is wider than 20% of the viewport) is in the left sidebar, the
+ * image will overlap with the #content to the right. The exception to this
+ * is IE6 which will just hide the navigation block completely in these
+ * instances due to a positioning bug.
+ */
+
+
+/* MPIWG addtional syles for drupal */
+
+/* 
+ * return to mpiwg
+ */
+ 
+ 
+/* suchseite */
+div.form-item-exact{
+	display: block;
+}
+
+ 
+ /* entspricht .line*/
+ 
+li.node-readmore a {
+	background: url("./icons/blue/LinkIntern.png") center left no-repeat;
+    padding-left: 10px;
+}
+.pane-title{
+	border-bottom: 3px solid #9DA0C2;
+	margin-bottom: 5px;
+	padding-bottom: 5px;
+}
+
+
+
+
+td.key{
+	color :rgb(59, 65, 134);
+
+	}
+	
+ div.main{
+ width: 75%;
+ }
+ 
+ div.title h1,
+ div.title h2
+ {
+ color: #3c4286;
+ }
+
+.mpiwg_internal_view{
+ background-color: red;
+ }
+ .webSearch_hl {
+   font-weight: bold;
+ }
+ 
+
+
+#mpiwg_link a {
+	background-color: #ebecf3;
+	color: #3b4186;
+	padding: 3px 10px 3px 10px;
+	font-style: italic;
+
+}
+
+#header div.title img.logo_left {
+    width: 180px;
+    height: 100%;
+    vertical-align: top;
+    padding: 0px 5px 20px 0px;
+}
+
+div.logo_left {
+float: left;
+}
+
+#header div.title {
+ width: 750px;
+ 
+   
+}
+
+div.subnav .sn_off {
+    padding-left: 0px;
+    background-color: #f8f8f9;
+    font-weight: normal;
+}
+
+div.subnav li.sn_off a{
+    padding-left: 0px;
+    color: #c10000;
+    font-weight: normal;
+}
+
+ul li.leaf{
+	list-style-image: none;
+}
+
+ul li.expanded{
+    list-style-image: none;
+}
+
+ul li.collapsed{
+    list-style-image: none;
+}
+
+ul.menu li{
+	margin-left : 0px;
+}
+
+ul.inline li {
+    display: inline;
+    padding: 0;
+}
+
+/* MPIWG website style sheet 
+ * 
+ * (c) 2013 MPIWG Berlin
+ * Author: Robert Casties
+ */
+body {
+    margin: 0;
+    font-family: Verdana, Arial, sans-serif;
+    background-color: #ebecf3;
+    font-size: 12px;
+}
+
+/* 
+ * global styles
+ */
+img {
+    border: none;
+}
+
+a {
+    color: #000b86;
+    text-decoration: none;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+/*  link-classes (with icons)  */
+a.internal,
+a.more {
+    background: url(../images/internal.png) center left no-repeat;
+    padding-left: 10px;
+}
+
+a.download {
+    background: url(../images/download.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.external {
+    background: url(../images/external.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.down {
+    background: url(../images/down.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.jumptop {
+    background: url(../images/jump_top.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.email {
+    background: url(../images/email.png) center left no-repeat;
+    padding-left: 23px;
+}
+
+/* old blue mail link */
+a.maillink {
+    color: #000b86 !important;
+}
+
+h1 {
+    color: #c10000;
+    font-size: 20px;
+    font-weight: normal;
+    margin: 0.5em 0;
+}
+
+h1:first-child {
+    /* does this work correctly? */
+    margin-top: 0;
+}
+
+h2 {
+    color: #c10000;
+    font-size: 16px;
+    font-weight: normal;
+    margin: 0.5em 0;
+}
+
+h3 {
+    color: #c10000;
+    font-size: 14px;
+    font-weight: normal;
+    margin: 0;
+}
+
+h3 + h3 {
+    /* h3 directly after h3 has a margin */
+    margin-top: 0.5em; 
+}
+
+h1 a {
+    /* a header with a link looks like a header */
+    color: inherit;
+    font-weight: inherit;
+    text-decoration: none;
+}
+
+p {
+    margin: 0.5em 0;
+    line-height: 1.5;
+}
+
+ul.plain {
+    list-style-type: none;
+    padding-left: 0.5em;
+}
+ul.plain > li {
+    margin-top: 0.5em;
+}
+
+ul.inline {
+    display: inline;
+    padding: 0;
+    margin: 0;
+}
+
+ul.inline li {
+    display: inline;
+}
+
+/*
+ * table-like rows and columns
+ */
+
+div.rows {
+    display: table;
+}
+
+div.row {
+    display: table-row;
+}
+
+div.row>div.col {
+    display: table-cell;
+    vertical-align: top;
+}
+
+div.row>div.col:first-child {
+    /* first block has no left margin */
+    padding-left: 0 !important;
+}
+
+/* 
+ * page wrapper and header
+ */
+#wrapper {
+    width: 900px;
+    margin: 0 auto;
+    padding: 10px 40px 10px 20px;
+    background-color: #f8f8f9;
+    box-shadow: 0 0 5px 3px #f8f8f9;
+}
+
+#header {
+    display: block;
+    position: relative;
+    height: 95px;
+    margin: 0 0 10px 0;
+}
+
+#header div.title {
+    position: absolute;
+    left: 0;
+    top: 25px;
+}
+
+#header div.title img {
+    width: 661px;
+    height: 40px;
+}
+
+#header div.logo {
+    position: absolute;
+    right: 0;
+}
+
+#header div.logo img {
+  
+    height: 88px;
+}
+
+/* 
+ * Main Navigation
+ */
+#mainnav {
+    display: block;
+    position: relative;
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 12px;
+    border-top: 8px solid #9da0c2;
+    padding-top: 5px;
+    margin-bottom: 15px;
+    /* make iPhone-Safari not botch text size */
+    -webkit-text-size-adjust: 100%;
+}
+
+#mainnav ul {
+    padding: 0;
+    margin: 0;
+}
+
+#mainnav li.sec {
+    display: inline;
+    border-top: 8px solid #9da0c2;
+    margin: 0 35px 0 0;
+    padding-top: 5px;
+}
+
+#mainnav li.sec:hover {
+    border-top: 8px solid #c10000;
+}
+
+#mainnav a {
+    color: #696968;
+    outline: none;
+}
+
+#mainnav a:hover {
+    color: #000000;
+    text-decoration: none;
+}
+
+#mainnav li.sec.on {
+    border-top: 8px solid #c10000;
+}
+
+#mainnav li.sec.on a {
+    color: #3c4286;
+    outline: none;
+    font-weight: bold;
+}
+
+#mainnav li.sec.lang {
+    position: absolute;
+    right: 0;
+    top: -8px;
+    margin-right: 0;
+}
+
+/* 
+ * breadcrumbs 
+ */
+#breadcrumbs {
+    font-size: 11px;
+    color: #6a4d3c;
+    margin-bottom: 5px;
+}
+
+#breadcrumbs a {
+    color: #6a4d3c;
+}
+
+#breadcrumbs .selected {
+    font-weight: bold;
+}
+
+/* 
+ * main section wrapper
+ */
+#mainrow {
+    display: table-row;
+}
+
+/*
+ * subnavigation
+ */
+div.leftbox {
+    display: table-cell;
+    vertical-align: top;
+    width: 170px;
+    border-top: 3px solid #9da0c2;
+}
+
+div.subnav {
+    font-size: 11px;
+}
+
+div.subnav ul {
+    margin: 0;
+    padding: 0;
+}
+
+div.subnav li {
+    list-style-type: none;
+    border-bottom: 1px solid #9da0c2;
+    padding: 3px 6px 6px 3px;
+}
+
+div.subnav .sn_on {
+    padding-left: 6px;
+    background-color: #c10000;
+}
+
+div.subnav .sn_on a {
+    color: #f8f8f9;
+    font-weight: bold;
+}
+
+div.subnav h2.subnavhead {
+    background-color: white;
+    color: #c10000;
+    border-bottom: 1px solid #9da0c2;
+    font-size: 11px;
+    font-weight: bold;
+    margin: 0;
+    padding: 5px 3px;
+}
+
+div.subnav li.dept {
+    padding-left: 18px;
+    list-style-position: inside;
+    list-style-image: url(../images/internal.png);
+    text-indent: -14px;
+}
+
+div.subnav li.dept .type {
+    font-style: italic;
+    color: #6a4d3c;
+}
+
+div.subnav li.dept.sn_on .type {
+    color: #f9f4e1;
+}
+
+div.subnavbox {
+    /* navigation below e.g. research projects */
+    font-size: 11px;
+    margin-top: 30px;
+}
+
+div.subnavbox h2 {
+    font-size: 11px;
+    color: #b69f7b;
+    padding-bottom: 3px;
+    border-bottom: 1px solid #9da0c2;
+    margin-top: 20px;
+}
+
+/*
+ *  main section 
+ */
+div.main {
+    display: table-cell;
+    vertical-align: top;
+    background-color: white;
+    border-top: 3px solid #9da0c2;
+    padding: 20px;
+}
+
+div.main div.row>div.col {
+    /* col default padding */
+    padding-left: 20px;
+}
+
+div.main div.center {
+    /* make sure center does not collapse */
+    min-width: 460px;
+}
+
+
+/* 
+ * frontpage
+ */
+div.main.home {
+    background-color: transparent;
+    border-top: 0;
+    padding: 0;
+}
+
+div.main.home b {
+    /* bold is blue */
+    color: #3b4186;
+}
+
+div.main.home div.row.triple>div.col {
+    /* frontpage col width */
+    width: 286px;
+    padding-left: 21px;
+}
+
+div.box h2,
+h2.line {
+    /* more-links in title are right */
+    position: relative;
+}
+
+div.box h2 a:link,
+h2.line a.more:link {
+    /* more-links in title are right */
+    position: absolute;
+    right: 0;
+    top: 3px;
+    /* more-links in title are small */
+    font-size: 12px;
+}
+
+/*
+ * front page feature teaser
+ */
+div.main.home div.teaser {
+    background-color: #f4e0c7;
+}
+
+div.main.home div.teaser div.box {
+    /* teaser box needs no margins */
+    margin: 0;
+}
+
+div.teaser_image {
+    position: relative;
+    width: 593px;
+    height: 351px;
+}
+
+div.teaser_image div.caption {
+    /* caption inside image */
+    position: absolute;
+    bottom: 0;
+    padding: 3px;
+    text-align: right;
+    color: white;
+    font-size: 10px;
+    opacity: 0.8;
+    background-color: gray;
+    /* background-color: rgba(80,80,80,0.6); */
+}
+
+div.teaser_text {
+    padding-right: 21px;
+}
+/* feature number */
+div.teaser_text div.feature_number {
+    position: absolute;
+    right: 14px;
+    top: 5px;
+    font-family: Georgia, Times, serif;
+    font-size: 26px;
+    color: #887163;
+}
+/* feature title */
+div.teaser_text h1 {
+    font-size: 16px;
+    font-weight: normal;
+    margin-top: 0;
+}
+/* current research topic */
+div.teaser_text h3 {
+    color: #887163;
+    margin-bottom: 3px;
+    margin-top: 15px;
+}
+/* feature date */
+div.teaser_text div.date {
+    color: #3b4186;
+    padding-top: 20px;
+    margin-bottom: 20px;
+}
+
+/* teaser slider */
+div.teaser div.box div.slider {
+    top: 150px;
+    width: 18px;
+    height: 24px;
+    padding-top: 11px;
+}
+
+div.teaser div.box div.slider.prev {
+    background-image: url(../images/slider_prev_bg.png);
+}
+
+div.teaser div.box div.slider.next {
+    background-image: url(../images/slider_next_bg.png);
+}
+
+/* 
+ * toolboxes 
+ */
+div.tool.box {
+    font-family: Georgia, Times, serif;
+    font-style: italic;
+    font-size: 16px;
+    color: #887163;
+    background-color: #efeeec;
+    padding: 20px;
+    box-shadow: 0 0 1px 1px #e0e0e0;
+}
+
+div.tool.box a {
+    color: #3b4186;
+}
+
+div.tool.box div.searchbox {
+    /* only to make it shrink-wrap */
+    display: table-cell;
+}
+
+div.tool.box div.searchbox input.text {
+    height: 20px;
+    width: 370px;
+    background-color: #9fa0a7;
+    color: white;
+    font-size: 14px;
+    font-style: italic;
+    border: 0;
+    padding: 3px;
+}
+
+div.tool.box div.searchbox input.submit {
+    /* submit uses border-box. don't ask */
+    height: 26px;
+    width: 26px;
+    border: 0;
+    padding: 2px 3px 4px 3px;
+    /* TODO: this should be an image */
+    background-color: #ee7f2d;
+    color: white;
+    font-size: 14px;
+}
+
+div.tool.box div.searchbox div.extended {
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 12px;
+    text-align: right;
+    padding: 5px 30px 5px 3px;
+}
+
+div.tool.box h3 {
+    font-family: Georgia, Times, serif;
+    font-style: italic;
+    font-size: 16px;
+    color: #887163;
+    margin: 0.5em 0;
+}
+
+div.tool.box h3:first-child {
+    /* top header has no margin */
+    margin-top: 0;
+}
+
+div.tool.box ul {
+    margin: 0;
+    padding: 0;
+}
+
+div.tool.box div.options {
+    margin-top: 10px;
+}
+
+div.tool.box div.options li {
+    list-style-type: none;
+    margin: 0.5em 0;
+}
+
+div.tool.box div.atoz {
+    color: #887163;
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 14px;
+    font-style: normal;
+    margin-top: 10px;
+}
+
+div.tool.box input.checkbox,
+div.tool.box input.radio {
+    /* checkbox uses border-box. don't ask */
+    width: 20px;
+    height: 20px;
+    margin: 0;
+    /* TODO: this should be an image */
+    background-color: white;
+}
+
+/* project page toolbox with thumbs */
+div.tool.box div.box.thumbs {
+    width: 420px;
+}
+
+div.box.thumbs div.thumb {
+    display: inline;
+}
+div.box.thumbs div.thumb img {
+    width: 55px;
+    height: 34px;
+}
+
+
+/* 
+ * service box on frontpage 
+ */
+div.box h2.service {
+    /* title has no line */
+    border: 0;
+}
+
+div.tool.box.service {
+    margin-top: 0;
+    padding: 0;
+}
+
+div.tool.box.service div.row > div.col {
+    padding: 0;
+}
+div.tool.box.service div.search {
+    width: 200px;
+    height: 20px;
+    padding: 26px 0 26px 16px;
+    border-bottom: 1px solid #9fa0a7;
+}
+div.tool.box.service div.search.both {
+    height: 44px;
+    padding: 14px 0 14px 16px;
+}
+div.tool.box.service div.searchicon {
+    width: 38px;
+    height: 40px;
+    text-align: center;
+    padding: 16px;
+    border-bottom: 1px solid #9fa0a7;
+}
+div.tool.box.service div.icon {
+    width: 71px;
+    height: 70px;
+    text-align: center;
+    line-height: 2.5;
+    padding: 1px 0;
+    border-left: 1px solid #9fa0a7;
+}
+div.tool.box.service div.icon a {
+    color: black;
+    font-size: 12px;
+    font-family: Verdana, Arial, sans-serif;
+    font-style: normal;
+}
+div.tool.box.service div.icon.first {
+    border-left: 0;
+    width: 70px;
+}
+div.tool.box.service div.searchbox {
+    display: block;
+}
+div.tool.box.service div.searchbox input.text {
+    width: 170px;
+    height: 17px;
+    font-size: 12px;
+    padding: 1px 3px;
+}
+div.tool.box.service div.searchbox input.submit {
+    height: 19px;
+    width: 19px;
+    padding: 0;
+    margin: 0;
+    vertical-align: bottom;
+}
+div.tool.box.service select.quickfinder {
+    width: 200px;
+    height: 19px;
+    font-size: 12px;
+    background-color: white;
+    margin-bottom: 5px;
+    border: 0;
+}
+
+/*
+ * other boxes
+ */
+div.box {
+    margin: 20px 0;
+    position: relative;
+}
+
+div.box h2,
+h2.line {
+    /* h2 is title with line */
+    padding-bottom: 5px;
+    border-bottom: 3px solid #9da0c2;
+    margin-bottom: 5px;
+}
+
+div.box.line,
+div.line {
+    border-bottom: 1px solid #9da0c2;
+}
+
+div.box h3 {
+    /* h3 is subtitle/type */
+    font-size: 12px;
+    color: #6a4d3c;
+}
+
+div.box h3 + h2 {
+    /* directly over h2 */
+    margin-top: 0;
+}
+
+/*
+ * small box with thumbnail
+ */
+div.mini {
+    padding: 10px 0;
+    /* line-height: 1; */
+}
+
+div.row.quintuple div.mini {
+    max-width: 122px;
+    font-size: 11px;
+}
+
+div.mini div.thumb {
+    background-color: #f8f8f8;
+    text-align: center;
+    margin-top: 0.5em;
+}
+div.mini div.thumb:first-child {
+    margin-top: 0;
+}
+div.row.quintuple div.mini div.thumb img {
+    width: 120px;
+    height: 75px;
+}
+
+div.row.triple div.mini {
+    width: 140px;
+}
+
+div.row.triple div.mini div.thumb img {
+    width: 140px;
+    height: 87px;
+}
+
+div.row.triple div.mini.source div.thumb img {
+    /* thumb size for sources given by digilib */
+    width: auto;
+    height: auto;
+}
+
+div.row.double div.mini div.thumb img {
+    height: 180px;
+}
+
+div.mini div.type {
+    color: #9f917a;
+    margin-top: 0.5em;
+}
+
+div.mini div.title {
+    margin-top: 0.5em;
+}
+
+div.mini div.author {
+    color: #3b4186;
+    margin-top: 0.5em;
+}
+
+div.mini div.link {
+    margin-top: 0.5em;
+}
+
+div.mini h2 {
+    /* e.g. title for mini-books */
+    font-size: 14px;
+}
+
+div.mini.website div.description {
+    /* initially hidden */
+    display: none;
+}
+
+div.box.foldable div.fold {
+    /* initially hidden */
+    display: none;
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    /* TODO: this should be an image */
+    background-color: #ee7f2d;
+    color: white;
+    font-size: 14px;
+}
+
+/* 
+ * slider buttons 
+ */
+div.box div.slider {
+    position: absolute;
+    top: 110px;
+    width: 16px;
+    height: 18px;
+}
+
+div.box div.slider.prev {
+    left: 0;
+}
+
+div.box div.slider.next {
+    right: 0;
+    text-align: right;
+}
+
+/* 
+ * paragraph with line
+ */
+div.item {
+    padding-top: 5px;
+    padding-bottom: 5px;
+    border-bottom: 1px solid #9da0c2;
+    line-height: 1.5;
+}
+
+/*
+ * pubman references
+ */
+.reference .Italic {
+    font-style: italic;
+}
+
+
+/*
+ * table with items (articles)
+ */
+table.items {
+    /* no space between cells */
+    border-collapse: collapse;
+}
+
+table.items td {
+    vertical-align: top;
+    text-align: left;
+    line-height: 1.5;
+    padding: 1em 1em 0.5em 0;
+}
+table.items td:last-child {
+    padding-right: 0;
+}
+table.items.shorter td {
+    padding: 0.5em 0.5em 0.5em 0;
+}
+
+table.items th {
+    font-family: Georgia, Times, serif;
+    font-size: 16px;
+    font-weight: normal;
+    font-style: italic;
+    color: #887163;
+    text-align: left;
+    border-bottom: 3px solid #9da0c2;
+}
+
+table.items h2 {
+    border-bottom: 3px solid #9da0c2;
+    margin-bottom: 0;
+}
+table.items h2 a {
+    color: inherit;
+    font-weight: inherit;
+    text-decoration: none;    
+}
+
+table.items img.thumb {
+    width: 55px;
+    height: 34px;
+}
+
+table.items td.line,
+table.items tr.line td {
+    border-bottom: 1px solid #9da0c2;
+}
+
+table.items td.topline,
+table.items tr.topline td {
+    border-top: 1px solid #9da0c2;
+}
+
+table.items tr.last_item td {
+    /* last_item has no padding-top */
+    padding: 0 0 0.5em 0;
+}
+
+table.items h3.fold_head {
+    font-size: 12px;
+    background-color: #f6f2eb;
+    padding: 3px;
+}
+
+table.items h3.fold_head img {
+    margin: 0 5px 0 3px;
+}
+
+table.items div.fold_body {
+    /* e.g. preprint abstracts */
+    padding: 0.5em 0 0 20px;
+}
+
+ul.items {
+    list-style-type: none;
+    padding: 0;
+}
+
+ul.items li {
+    padding: 0 0 0.5em 20px;    
+    border-bottom: 3px solid #9da0c2;
+    margin-bottom: 0.5em;
+}
+
+ul.items .fold_head {
+    text-indent: -20px;
+}
+ul.items .fold_head img.fold_open,
+ul.items .fold_head img.fold_closed {
+    padding-right: 5px;
+}
+
+ul.items .type {
+    /* font-size: 11px; */
+    color: #696968;
+}
+
+ul.items h2 {
+    font-size: 12px;
+}
+
+ul.items td.key {
+    color: #3b4186;
+}
+
+.foldable img.fold_closed {
+    /* fold is initially open */
+    display: none;
+}
+
+
+/*
+ * hierarchical list (of projects)
+ */
+div.hierlist h2 {
+    text-indent: -20px;
+    padding-left: 20px;
+    padding-bottom: 5px;
+    border-bottom: 3px solid #9da0c2;
+    margin-bottom: 0;
+}
+
+div.hierlist h2 img {
+    padding: 0 3px 3px 3px; 
+}
+
+div.hierlist h2 a {
+    color: inherit;
+    font-weight: inherit;
+    text-decoration: none;
+}
+
+div.hierlist ul {
+    list-style-type: none;
+    padding: 0 0 10px 0;
+    margin: 0;
+    border-bottom: 1px solid #9da0c2;
+}
+
+div.hierlist li {
+    padding: 5px 0 5px 16px;
+    text-indent: -13px;
+}
+
+div.hierlist li a {
+    /* icon is more distant from text */
+    padding-left: 13px;
+}
+
+div.hierlist li.indent {
+    padding-bottom: 10px;
+    border-bottom: 1px solid #9da0c2;
+    margin-bottom: 5px;
+}
+
+div.hierlist li.level_1 {
+    font-size: 14px;
+}
+
+div.hierlist li.level_2 {
+    font-weight: bold;
+    margin-left: 20px;
+}
+
+div.hierlist li.level_3 {
+    margin-left: 40px;
+}
+
+div.hierlist li.level_4 {
+    margin-left: 60px;
+}
+
+div.hierlist li.level_5 {
+    margin-left: 80px;
+}
+
+
+/*
+ * figures (in project descriptions and features)
+ */
+div.figure,
+div.image_small {
+    float: left;
+    width: 230px;
+    padding: 0.5em 1em 0 0;
+}
+div.image_small.right {
+    float: right;
+    padding: 0.5em 0 0 1em;
+}
+div.figure div.image img,
+div.image_small img {
+    width: 230px;
+}
+div.figure div.figcaption,
+div.banner_large div.caption,
+div.image_small div.caption {
+    font-size: 10px;
+    line-height: 1.5;
+    color: #9f917a;
+    margin-top: 0.5em;
+}
+div.banner_large img {
+    width: 460px;
+}
+
+/*
+ * project description and feature
+ */
+h3.authors {
+    margin-top: 0.5em;
+}
+
+p.maintext_authors {
+    font-size: 14px;
+    color: #3b4186;
+}
+
+div.description {
+    margin-top: 0.5em;
+    line-height: 1.5;
+}
+
+h2.type {
+    color: #9f917a;
+}
+
+/*
+ * feature story archive
+ */
+ul.items.features li {
+    padding: 0 0 0.5em 0;
+    border-bottom: 1px solid #9da0c2;
+}
+ul.items.features .thumb {
+    float:left;
+    padding: 8px 10px 0 0;
+}
+ul.items.features .thumb img {
+    width: 55px;
+    height: 34px;
+}
+ul.items.features .feature_number {
+    font-family: Georgia, Times, serif;
+    font-size: 24px;
+    color: #696968;
+}
+ul.items.features .date {
+    font-size: 11px;
+    color: #696968;
+}
+ul.items.features h1 {
+    font-size: 12px;
+    font-weight: bold;
+    margin: 0;
+}
+
+/* 
+ * sidebar 
+ */
+div.sidebar {
+    display: table-cell;
+    width: 220px;
+    border-top: 3px solid #9da0c2;
+    background-color: white;
+    padding: 20px 10px 20px 0;
+}
+
+div.sideblock h2 {
+    position: relative;
+    font-size: 12px;
+    margin-top: 20px;
+    padding-bottom: 5px;
+    border-bottom: 3px solid #9da0c2;
+    margin-bottom: 0;
+}
+
+div.sideblock:first-child h2 {
+    margin-top: 0;
+}
+
+div.sideblock h3 {
+    font-size: 12px;
+    margin-top: 5px;
+    padding-bottom: 5px;
+    border-bottom: 1px solid #9da0c2;
+}
+
+/* like sidebar without line */
+div.sidebox {
+    width: 220px;
+    padding: 0 0 20px 0;
+}
+
+/*
+ * project sidebars
+ */
+div.sideblock h2 .proj_state {
+    position: absolute;
+    right: 0;
+}
+
+div.sideblock .project {
+    background: url(../images/internal.png) no-repeat scroll 6px 11px #F5DAAF;
+    border-bottom: 1px solid #F3BE7C;
+    padding: 6px 6px 6px 20px;
+}
+div.sideblock .project.inactive {
+    background: url(../images/internal.png) no-repeat scroll 6px 11px #f6e6cc;
+}
+div.sideblock .project.parent {
+    background: url(../images/up.png) no-repeat scroll 6px 11px #F9F4E1;
+    border-bottom: 1px solid #9da0c2;
+    color: #666666;
+}
+div.sideblock .project a {
+    color: #333;
+}
+
+/*
+ * special sideblocks
+ */
+div.sideblock .item.link, 
+div.sideblock .item.internal {
+    background: url(../images/internal.png) 0 11px no-repeat;
+    padding-left: 10px;
+}
+
+div.sideblock .item.external {
+    background: url(../images/external.png) 0 11px no-repeat;
+    padding-left: 13px;
+}
+
+div.sideblock .item.download {
+    background: url(../images/download.png) 0 8px no-repeat;
+    padding-left: 15px;
+}
+
+div.sideblock .item.thumb img {
+    width: 55px;
+    height: 34px;
+}
+div.sideblock .item.thumb .text {
+    padding-left: 0.5em;
+}
+
+/*
+ * footer
+ */
+#footer {
+    position: relative;
+    height: 50px;
+    border-top: 1px solid #9da0c2;
+    padding-top: 8px;
+    margin-top: 29px;
+}
+
+#footer div.text {
+    display: inline-block;
+    color: #c10000;
+    font-size: 11px;
+}
+
+#footer div.logo {
+    display: inline-block;
+    position: absolute;
+    right: 0;
+}
+
+#footer div.logo img {
+    width: 204px;
+    height: 41px;
+}
+/* extension */
+
+#header div.title {
+ width: 750px;
+ top: 15px;
+   
+}
+
+/*#header div.logo img {
+    width: 100%;
+    height: 100px;
+    
+}*/
+
+a.email {
+    background: url("./icons/blue/Email.png") center left no-repeat;
+    padding-left: 23px;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/layout_orig.css	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,1312 @@
+
+/**
+ * @file
+ * Stark layout method
+ *
+ * To avoid obscuring CSS added to the page by Drupal or a contrib module, the
+ * Stark theme itself has no styling, except just enough CSS to arrange the page
+ * in a traditional "Header, sidebars, content, and footer" layout.
+ *
+ * This layout method works reasonably well, but shouldn't be used on a
+ * production site because it can break. For example, if an over-large image
+ * (one that is wider than 20% of the viewport) is in the left sidebar, the
+ * image will overlap with the #content to the right. The exception to this
+ * is IE6 which will just hide the navigation block completely in these
+ * instances due to a positioning bug.
+ */
+
+
+/* MPIWG addtional syles for drupal */
+
+/* 
+ * return to mpiwg
+ */
+ 
+ 
+
+td.key{
+	color :rgb(59, 65, 134);
+
+	}
+	
+ div.main{
+ width: 75%;
+ }
+
+.mpiwg_internal_view{
+ background-color: red;
+ }
+ .webSearch_hl {
+   font-weight: bold;
+ }
+ 
+
+
+#mpiwg_link a {
+	background-color: #ebecf3;
+	color: #3b4186;
+	padding: 3px 10px 3px 10px;
+	font-style: italic;
+
+}
+
+#header div.title img.logo_left {
+    width: 180px;
+    height: 100%;
+    vertical-align: top;
+    padding: 0px 5px 20px 0px;
+}
+
+div.logo_left {
+float: left;
+}
+
+#header div.title {
+ width: 750px;
+ 
+   
+}
+
+div.subnav .sn_off {
+    padding-left: 0px;
+    background-color: #fcf2df;
+    font-weight: normal;
+}
+
+div.subnav li.sn_off a{
+    padding-left: 0px;
+    color: #d45a00;
+    font-weight: normal;
+}
+
+ul li.leaf{
+	list-style-image: none;
+}
+
+ul li.expanded{
+    list-style-image: none;
+}
+
+ul li.collapsed{
+    list-style-image: none;
+}
+
+ul.menu li{
+	margin-left : 0px;
+}
+
+ul.inline li {
+    display: inline;
+    padding: 0;
+}
+
+/* MPIWG website style sheet 
+ * 
+ * (c) 2013 MPIWG Berlin
+ * Author: Robert Casties
+ */
+body {
+    margin: 0;
+    font-family: Verdana, Arial, sans-serif;
+    background-color: #fdf8ef;
+    font-size: 12px;
+}
+
+/* 
+ * global styles
+ */
+img {
+    border: none;
+}
+
+a {
+    color: #d45a00;
+    text-decoration: none;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+/*  link-classes (with icons)  */
+a.internal,
+a.more {
+    background: url(../images/internal.png) center left no-repeat;
+    padding-left: 10px;
+}
+
+a.download {
+    background: url(../images/download.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.external {
+    background: url(../images/external.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.down {
+    background: url(../images/down.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.jumptop {
+    background: url(../images/jump_top.png) center left no-repeat;
+    padding-left: 15px;
+}
+
+a.email {
+    background: url(../images/email.png) center left no-repeat;
+    padding-left: 23px;
+}
+
+/* old blue mail link */
+a.maillink {
+    color: #3b4186 !important;
+}
+
+h1 {
+    color: #3b4186;
+    font-size: 20px;
+    font-weight: normal;
+    margin: 0.5em 0;
+}
+
+h1:first-child {
+    /* does this work correctly? */
+    margin-top: 0;
+}
+
+h2 {
+    color: #3b4186;
+    font-size: 16px;
+    font-weight: normal;
+    margin: 0.5em 0;
+}
+
+h3 {
+    color: #3b4186;
+    font-size: 14px;
+    font-weight: normal;
+    margin: 0;
+}
+
+h3 + h3 {
+    /* h3 directly after h3 has a margin */
+    margin-top: 0.5em; 
+}
+
+h1 a {
+    /* a header with a link looks like a header */
+    color: inherit;
+    font-weight: inherit;
+    text-decoration: none;
+}
+
+p {
+    margin: 0.5em 0;
+    line-height: 1.5;
+}
+
+ul.plain {
+    list-style-type: none;
+    padding-left: 0.5em;
+}
+ul.plain > li {
+    margin-top: 0.5em;
+}
+
+ul.inline {
+    display: inline;
+    padding: 0;
+    margin: 0;
+}
+
+ul.inline li {
+    display: inline;
+}
+
+/*
+ * table-like rows and columns
+ */
+
+div.rows {
+    display: table;
+}
+
+div.row {
+    display: table-row;
+}
+
+div.row>div.col {
+    display: table-cell;
+    vertical-align: top;
+}
+
+div.row>div.col:first-child {
+    /* first block has no left margin */
+    padding-left: 0 !important;
+}
+
+/* 
+ * page wrapper and header
+ */
+#wrapper {
+    width: 900px;
+    margin: 0 auto;
+    padding: 10px 40px 10px 20px;
+    background-color: #fcf2df;
+    box-shadow: 0 0 5px 3px #d0d0d0;
+}
+
+#header {
+    display: block;
+    position: relative;
+    height: 95px;
+    margin: 0 0 10px 0;
+}
+
+#header div.title {
+    position: absolute;
+    left: 0;
+    top: 25px;
+}
+
+#header div.title img {
+    width: 661px;
+    height: 40px;
+}
+
+#header div.logo {
+    position: absolute;
+    right: 0;
+}
+
+#header div.logo img {
+    width: 87px;
+    height: 88px;
+}
+
+/* 
+ * Main Navigation
+ */
+#mainnav {
+    display: block;
+    position: relative;
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 12px;
+    border-top: 8px solid #fab775;
+    padding-top: 5px;
+    margin-bottom: 15px;
+    /* make iPhone-Safari not botch text size */
+    -webkit-text-size-adjust: 100%;
+}
+
+#mainnav ul {
+    padding: 0;
+    margin: 0;
+}
+
+#mainnav li.sec {
+    display: inline;
+    border-top: 8px solid #fab775;
+    margin: 0 35px 0 0;
+    padding-top: 5px;
+}
+
+#mainnav li.sec:hover {
+    border-top: 8px solid #d45a00;
+}
+
+#mainnav a {
+    color: #696968;
+    outline: none;
+}
+
+#mainnav a:hover {
+    color: #000000;
+    text-decoration: none;
+}
+
+#mainnav li.sec.on {
+    border-top: 8px solid #d45a00;
+}
+
+#mainnav li.sec.on a {
+    color: #d45a00;
+    outline: none;
+    font-weight: bold;
+}
+
+#mainnav li.sec.lang {
+    position: absolute;
+    right: 0;
+    top: -8px;
+    margin-right: 0;
+}
+
+/* 
+ * breadcrumbs 
+ */
+#breadcrumbs {
+    font-size: 11px;
+    color: #6a4d3c;
+    margin-bottom: 5px;
+}
+
+#breadcrumbs a {
+    color: #6a4d3c;
+}
+
+#breadcrumbs .selected {
+    font-weight: bold;
+}
+
+/* 
+ * main section wrapper
+ */
+#mainrow {
+    display: table-row;
+}
+
+/*
+ * subnavigation
+ */
+div.leftbox {
+    display: table-cell;
+    vertical-align: top;
+    width: 170px;
+    border-top: 3px solid #dccbae;
+}
+
+div.subnav {
+    font-size: 11px;
+}
+
+div.subnav ul {
+    margin: 0;
+    padding: 0;
+}
+
+div.subnav li {
+    list-style-type: none;
+    border-bottom: 1px solid #dccbae;
+    padding: 3px 6px 6px 3px;
+}
+
+div.subnav .sn_on {
+    padding-left: 6px;
+    background-color: #d45a00;
+}
+
+div.subnav .sn_on a {
+    color: #fcf2df;
+    font-weight: bold;
+}
+
+div.subnav h2.subnavhead {
+    background-color: white;
+    color: #d45a00;
+    border-bottom: 1px solid #dccbae;
+    font-size: 11px;
+    font-weight: bold;
+    margin: 0;
+    padding: 5px 3px;
+}
+
+div.subnav li.dept {
+    padding-left: 18px;
+    list-style-position: inside;
+    list-style-image: url(../images/internal.png);
+    text-indent: -14px;
+}
+
+div.subnav li.dept .type {
+    font-style: italic;
+    color: #6a4d3c;
+}
+
+div.subnav li.dept.sn_on .type {
+    color: #f9f4e1;
+}
+
+div.subnavbox {
+    /* navigation below e.g. research projects */
+    font-size: 11px;
+    margin-top: 30px;
+}
+
+div.subnavbox h2 {
+    font-size: 11px;
+    color: #b69f7b;
+    padding-bottom: 3px;
+    border-bottom: 1px solid #dccbae;
+    margin-top: 20px;
+}
+
+/*
+ *  main section 
+ */
+div.main {
+    display: table-cell;
+    vertical-align: top;
+    background-color: white;
+    border-top: 3px solid #dccbae;
+    padding: 20px;
+}
+
+div.main div.row>div.col {
+    /* col default padding */
+    padding-left: 20px;
+}
+
+div.main div.center {
+    /* make sure center does not collapse */
+    min-width: 460px;
+}
+
+
+/* 
+ * frontpage
+ */
+div.main.home {
+    background-color: transparent;
+    border-top: 0;
+    padding: 0;
+}
+
+div.main.home b {
+    /* bold is blue */
+    color: #3b4186;
+}
+
+div.main.home div.row.triple>div.col {
+    /* frontpage col width */
+    width: 286px;
+    padding-left: 21px;
+}
+
+div.box h2,
+h2.line {
+    /* more-links in title are right */
+    position: relative;
+}
+
+div.box h2 a:link,
+h2.line a.more:link {
+    /* more-links in title are right */
+    position: absolute;
+    right: 0;
+    top: 3px;
+    /* more-links in title are small */
+    font-size: 12px;
+}
+
+/*
+ * front page feature teaser
+ */
+div.main.home div.teaser {
+    background-color: #f4e0c7;
+}
+
+div.main.home div.teaser div.box {
+    /* teaser box needs no margins */
+    margin: 0;
+}
+
+div.teaser_image {
+    position: relative;
+    width: 593px;
+    height: 351px;
+}
+
+div.teaser_image div.caption {
+    /* caption inside image */
+    position: absolute;
+    bottom: 0;
+    padding: 3px;
+    text-align: right;
+    color: white;
+    font-size: 10px;
+    opacity: 0.8;
+    background-color: gray;
+    /* background-color: rgba(80,80,80,0.6); */
+}
+
+div.teaser_text {
+    padding-right: 21px;
+}
+/* feature number */
+div.teaser_text div.feature_number {
+    position: absolute;
+    right: 14px;
+    top: 5px;
+    font-family: Georgia, Times, serif;
+    font-size: 26px;
+    color: #887163;
+}
+/* feature title */
+div.teaser_text h1 {
+    font-size: 16px;
+    font-weight: normal;
+    margin-top: 0;
+}
+/* current research topic */
+div.teaser_text h3 {
+    color: #887163;
+    margin-bottom: 3px;
+    margin-top: 15px;
+}
+/* feature date */
+div.teaser_text div.date {
+    color: #3b4186;
+    padding-top: 20px;
+    margin-bottom: 20px;
+}
+
+/* teaser slider */
+div.teaser div.box div.slider {
+    top: 150px;
+    width: 18px;
+    height: 24px;
+    padding-top: 11px;
+}
+
+div.teaser div.box div.slider.prev {
+    background-image: url(../images/slider_prev_bg.png);
+}
+
+div.teaser div.box div.slider.next {
+    background-image: url(../images/slider_next_bg.png);
+}
+
+/* 
+ * toolboxes 
+ */
+div.tool.box {
+    font-family: Georgia, Times, serif;
+    font-style: italic;
+    font-size: 16px;
+    color: #887163;
+    background-color: #efeeec;
+    padding: 20px;
+    box-shadow: 0 0 1px 1px #e0e0e0;
+}
+
+div.tool.box a {
+    color: #3b4186;
+}
+
+div.tool.box div.searchbox {
+    /* only to make it shrink-wrap */
+    display: table-cell;
+}
+
+div.tool.box div.searchbox input.text {
+    height: 20px;
+    width: 370px;
+    background-color: #9fa0a7;
+    color: white;
+    font-size: 14px;
+    font-style: italic;
+    border: 0;
+    padding: 3px;
+}
+
+div.tool.box div.searchbox input.submit {
+    /* submit uses border-box. don't ask */
+    height: 26px;
+    width: 26px;
+    border: 0;
+    padding: 2px 3px 4px 3px;
+    /* TODO: this should be an image */
+    background-color: #ee7f2d;
+    color: white;
+    font-size: 14px;
+}
+
+div.tool.box div.searchbox div.extended {
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 12px;
+    text-align: right;
+    padding: 5px 30px 5px 3px;
+}
+
+div.tool.box h3 {
+    font-family: Georgia, Times, serif;
+    font-style: italic;
+    font-size: 16px;
+    color: #887163;
+    margin: 0.5em 0;
+}
+
+div.tool.box h3:first-child {
+    /* top header has no margin */
+    margin-top: 0;
+}
+
+div.tool.box ul {
+    margin: 0;
+    padding: 0;
+}
+
+div.tool.box div.options {
+    margin-top: 10px;
+}
+
+div.tool.box div.options li {
+    list-style-type: none;
+    margin: 0.5em 0;
+}
+
+div.tool.box div.atoz {
+    color: #887163;
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 14px;
+    font-style: normal;
+    margin-top: 10px;
+}
+
+div.tool.box input.checkbox,
+div.tool.box input.radio {
+    /* checkbox uses border-box. don't ask */
+    width: 20px;
+    height: 20px;
+    margin: 0;
+    /* TODO: this should be an image */
+    background-color: white;
+}
+
+/* project page toolbox with thumbs */
+div.tool.box div.box.thumbs {
+    width: 420px;
+}
+
+div.box.thumbs div.thumb {
+    display: inline;
+}
+div.box.thumbs div.thumb img {
+    width: 55px;
+    height: 34px;
+}
+
+
+/* 
+ * service box on frontpage 
+ */
+div.box h2.service {
+    /* title has no line */
+    border: 0;
+}
+
+div.tool.box.service {
+    margin-top: 0;
+    padding: 0;
+}
+
+div.tool.box.service div.row > div.col {
+    padding: 0;
+}
+div.tool.box.service div.search {
+    width: 200px;
+    height: 20px;
+    padding: 26px 0 26px 16px;
+    border-bottom: 1px solid #9fa0a7;
+}
+div.tool.box.service div.search.both {
+    height: 44px;
+    padding: 14px 0 14px 16px;
+}
+div.tool.box.service div.searchicon {
+    width: 38px;
+    height: 40px;
+    text-align: center;
+    padding: 16px;
+    border-bottom: 1px solid #9fa0a7;
+}
+div.tool.box.service div.icon {
+    width: 71px;
+    height: 70px;
+    text-align: center;
+    line-height: 2.5;
+    padding: 1px 0;
+    border-left: 1px solid #9fa0a7;
+}
+div.tool.box.service div.icon a {
+    color: black;
+    font-size: 12px;
+    font-family: Verdana, Arial, sans-serif;
+    font-style: normal;
+}
+div.tool.box.service div.icon.first {
+    border-left: 0;
+    width: 70px;
+}
+div.tool.box.service div.searchbox {
+    display: block;
+}
+div.tool.box.service div.searchbox input.text {
+    width: 170px;
+    height: 17px;
+    font-size: 12px;
+    padding: 1px 3px;
+}
+div.tool.box.service div.searchbox input.submit {
+    height: 19px;
+    width: 19px;
+    padding: 0;
+    margin: 0;
+    vertical-align: bottom;
+}
+div.tool.box.service select.quickfinder {
+    width: 200px;
+    height: 19px;
+    font-size: 12px;
+    background-color: white;
+    margin-bottom: 5px;
+    border: 0;
+}
+
+/*
+ * other boxes
+ */
+div.box {
+    margin: 20px 0;
+    position: relative;
+}
+
+div.box h2,
+h2.line {
+    /* h2 is title with line */
+    padding-bottom: 5px;
+    border-bottom: 3px solid #dccbae;
+    margin-bottom: 5px;
+}
+
+div.box.line,
+div.line {
+    border-bottom: 1px solid #dccbae;
+}
+
+div.box h3 {
+    /* h3 is subtitle/type */
+    font-size: 12px;
+    color: #6a4d3c;
+}
+
+div.box h3 + h2 {
+    /* directly over h2 */
+    margin-top: 0;
+}
+
+/*
+ * small box with thumbnail
+ */
+div.mini {
+    padding: 10px 0;
+    /* line-height: 1; */
+}
+
+div.row.quintuple div.mini {
+    max-width: 122px;
+    font-size: 11px;
+}
+
+div.mini div.thumb {
+    background-color: #f8f8f8;
+    text-align: center;
+    margin-top: 0.5em;
+}
+div.mini div.thumb:first-child {
+    margin-top: 0;
+}
+div.row.quintuple div.mini div.thumb img {
+    width: 120px;
+    height: 75px;
+}
+
+div.row.triple div.mini {
+    width: 140px;
+}
+
+div.row.triple div.mini div.thumb img {
+    width: 140px;
+    height: 87px;
+}
+
+div.row.triple div.mini.source div.thumb img {
+    /* thumb size for sources given by digilib */
+    width: auto;
+    height: auto;
+}
+
+div.row.double div.mini div.thumb img {
+    height: 180px;
+}
+
+div.mini div.type {
+    color: #9f917a;
+    margin-top: 0.5em;
+}
+
+div.mini div.title {
+    margin-top: 0.5em;
+}
+
+div.mini div.author {
+    color: #3b4186;
+    margin-top: 0.5em;
+}
+
+div.mini div.link {
+    margin-top: 0.5em;
+}
+
+div.mini h2 {
+    /* e.g. title for mini-books */
+    font-size: 14px;
+}
+
+div.mini.website div.description {
+    /* initially hidden */
+    display: none;
+}
+
+div.box.foldable div.fold {
+    /* initially hidden */
+    display: none;
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    /* TODO: this should be an image */
+    background-color: #ee7f2d;
+    color: white;
+    font-size: 14px;
+}
+
+/* 
+ * slider buttons 
+ */
+div.box div.slider {
+    position: absolute;
+    top: 110px;
+    width: 16px;
+    height: 18px;
+}
+
+div.box div.slider.prev {
+    left: 0;
+}
+
+div.box div.slider.next {
+    right: 0;
+    text-align: right;
+}
+
+/* 
+ * paragraph with line
+ */
+div.item {
+    padding-top: 5px;
+    padding-bottom: 5px;
+    border-bottom: 1px solid #dccbae;
+    line-height: 1.5;
+}
+
+/*
+ * pubman references
+ */
+.reference .Italic {
+    font-style: italic;
+}
+
+
+/*
+ * table with items (articles)
+ */
+table.items {
+    /* no space between cells */
+    border-collapse: collapse;
+}
+
+table.items td {
+    vertical-align: top;
+    text-align: left;
+    line-height: 1.5;
+    padding: 1em 1em 0.5em 0;
+}
+table.items td:last-child {
+    padding-right: 0;
+}
+table.items.shorter td {
+    padding: 0.5em 0.5em 0.5em 0;
+}
+
+table.items th {
+    font-family: Georgia, Times, serif;
+    font-size: 16px;
+    font-weight: normal;
+    font-style: italic;
+    color: #887163;
+    text-align: left;
+    border-bottom: 3px solid #dccbae;
+}
+
+table.items h2 {
+    border-bottom: 3px solid #dccbae;
+    margin-bottom: 0;
+}
+table.items h2 a {
+    color: inherit;
+    font-weight: inherit;
+    text-decoration: none;    
+}
+
+table.items img.thumb {
+    width: 55px;
+    height: 34px;
+}
+
+table.items td.line,
+table.items tr.line td {
+    border-bottom: 1px solid #dccbae;
+}
+
+table.items td.topline,
+table.items tr.topline td {
+    border-top: 1px solid #dccbae;
+}
+
+table.items tr.last_item td {
+    /* last_item has no padding-top */
+    padding: 0 0 0.5em 0;
+}
+
+table.items h3.fold_head {
+    font-size: 12px;
+    background-color: #f6f2eb;
+    padding: 3px;
+}
+
+table.items h3.fold_head img {
+    margin: 0 5px 0 3px;
+}
+
+table.items div.fold_body {
+    /* e.g. preprint abstracts */
+    padding: 0.5em 0 0 20px;
+}
+
+ul.items {
+    list-style-type: none;
+    padding: 0;
+}
+
+ul.items li {
+    padding: 0 0 0.5em 20px;    
+    border-bottom: 3px solid #dccbae;
+    margin-bottom: 0.5em;
+}
+
+ul.items .fold_head {
+    text-indent: -20px;
+}
+ul.items .fold_head img.fold_open,
+ul.items .fold_head img.fold_closed {
+    padding-right: 5px;
+}
+
+ul.items .type {
+    /* font-size: 11px; */
+    color: #696968;
+}
+
+ul.items h2 {
+    font-size: 12px;
+}
+
+ul.items td.key {
+    color: #3b4186;
+}
+
+.foldable img.fold_closed {
+    /* fold is initially open */
+    display: none;
+}
+
+
+/*
+ * hierarchical list (of projects)
+ */
+div.hierlist h2 {
+    text-indent: -20px;
+    padding-left: 20px;
+    padding-bottom: 5px;
+    border-bottom: 3px solid #dccbae;
+    margin-bottom: 0;
+}
+
+div.hierlist h2 img {
+    padding: 0 3px 3px 3px; 
+}
+
+div.hierlist h2 a {
+    color: inherit;
+    font-weight: inherit;
+    text-decoration: none;
+}
+
+div.hierlist ul {
+    list-style-type: none;
+    padding: 0 0 10px 0;
+    margin: 0;
+    border-bottom: 1px solid #dccbae;
+}
+
+div.hierlist li {
+    padding: 5px 0 5px 16px;
+    text-indent: -13px;
+}
+
+div.hierlist li a {
+    /* icon is more distant from text */
+    padding-left: 13px;
+}
+
+div.hierlist li.indent {
+    padding-bottom: 10px;
+    border-bottom: 1px solid #dccbae;
+    margin-bottom: 5px;
+}
+
+div.hierlist li.level_1 {
+    font-size: 14px;
+}
+
+div.hierlist li.level_2 {
+    font-weight: bold;
+    margin-left: 20px;
+}
+
+div.hierlist li.level_3 {
+    margin-left: 40px;
+}
+
+div.hierlist li.level_4 {
+    margin-left: 60px;
+}
+
+div.hierlist li.level_5 {
+    margin-left: 80px;
+}
+
+
+/*
+ * figures (in project descriptions and features)
+ */
+div.figure,
+div.image_small {
+    float: left;
+    width: 230px;
+    padding: 0.5em 1em 0 0;
+}
+div.image_small.right {
+    float: right;
+    padding: 0.5em 0 0 1em;
+}
+div.figure div.image img,
+div.image_small img {
+    width: 230px;
+}
+div.figure div.figcaption,
+div.banner_large div.caption,
+div.image_small div.caption {
+    font-size: 10px;
+    line-height: 1.5;
+    color: #9f917a;
+    margin-top: 0.5em;
+}
+div.banner_large img {
+    width: 460px;
+}
+
+/*
+ * project description and feature
+ */
+h3.authors {
+    margin-top: 0.5em;
+}
+
+p.maintext_authors {
+    font-size: 14px;
+    color: #3b4186;
+}
+
+div.description {
+    margin-top: 0.5em;
+    line-height: 1.5;
+}
+
+h2.type {
+    color: #9f917a;
+}
+
+/*
+ * feature story archive
+ */
+ul.items.features li {
+    padding: 0 0 0.5em 0;
+    border-bottom: 1px solid #dccbae;
+}
+ul.items.features .thumb {
+    float:left;
+    padding: 8px 10px 0 0;
+}
+ul.items.features .thumb img {
+    width: 55px;
+    height: 34px;
+}
+ul.items.features .feature_number {
+    font-family: Georgia, Times, serif;
+    font-size: 24px;
+    color: #696968;
+}
+ul.items.features .date {
+    font-size: 11px;
+    color: #696968;
+}
+ul.items.features h1 {
+    font-size: 12px;
+    font-weight: bold;
+    margin: 0;
+}
+
+/* 
+ * sidebar 
+ */
+div.sidebar {
+    display: table-cell;
+    width: 220px;
+    border-top: 3px solid #dccbae;
+    background-color: white;
+    padding: 20px 10px 20px 0;
+}
+
+div.sideblock h2 {
+    position: relative;
+    font-size: 12px;
+    margin-top: 20px;
+    padding-bottom: 5px;
+    border-bottom: 3px solid #dccbae;
+    margin-bottom: 0;
+}
+
+div.sideblock:first-child h2 {
+    margin-top: 0;
+}
+
+div.sideblock h3 {
+    font-size: 12px;
+    margin-top: 5px;
+    padding-bottom: 5px;
+    border-bottom: 1px solid #dccbae;
+}
+
+/* like sidebar without line */
+div.sidebox {
+    width: 220px;
+    padding: 0 0 20px 0;
+}
+
+/*
+ * project sidebars
+ */
+div.sideblock h2 .proj_state {
+    position: absolute;
+    right: 0;
+}
+
+div.sideblock .project {
+    background: url(../images/internal.png) no-repeat scroll 6px 11px #F5DAAF;
+    border-bottom: 1px solid #F3BE7C;
+    padding: 6px 6px 6px 20px;
+}
+div.sideblock .project.inactive {
+    background: url(../images/internal.png) no-repeat scroll 6px 11px #f6e6cc;
+}
+div.sideblock .project.parent {
+    background: url(../images/up.png) no-repeat scroll 6px 11px #F9F4E1;
+    border-bottom: 1px solid #DCCBAE;
+    color: #666666;
+}
+div.sideblock .project a {
+    color: #333;
+}
+
+/*
+ * special sideblocks
+ */
+div.sideblock .item.link, 
+div.sideblock .item.internal {
+    background: url(../images/internal.png) 0 11px no-repeat;
+    padding-left: 10px;
+}
+
+div.sideblock .item.external {
+    background: url(../images/external.png) 0 11px no-repeat;
+    padding-left: 13px;
+}
+
+div.sideblock .item.download {
+    background: url(../images/download.png) 0 8px no-repeat;
+    padding-left: 15px;
+}
+
+div.sideblock .item.thumb img {
+    width: 55px;
+    height: 34px;
+}
+div.sideblock .item.thumb .text {
+    padding-left: 0.5em;
+}
+
+/*
+ * footer
+ */
+#footer {
+    position: relative;
+    height: 50px;
+    border-top: 1px solid #dccbae;
+    padding-top: 8px;
+    margin-top: 29px;
+}
+
+#footer div.text {
+    display: inline-block;
+    color: #d45a00;
+    font-size: 11px;
+}
+
+#footer div.logo {
+    display: inline-block;
+    position: absolute;
+    right: 0;
+}
+
+#footer div.logo img {
+    width: 204px;
+    height: 41px;
+}
+/* extension */
+
+#header div.title {
+ width: 750px;
+ top: 15px;
+   
+}
+
+#header div.logo img {
+    width: 100%;
+    height: 100px;
+    
+}
Binary file sites/all/themes/mpiwgDev/logo.png has changed
Binary file sites/all/themes/mpiwgDev/logo_BW.png has changed
Binary file sites/all/themes/mpiwgDev/logo_old.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/main_template.zpt	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" metal:define-macro="page"
+  tal:define="root here/MPIWGrootURL|here/en/MPIWGrootURL; 
+                  crumbs template/getBreadcrumbs | nothing;
+                  section here/getSection | nothing;
+                  sections here/getSections | nothing;
+                  lang here/getLang | nothing;
+                  allsecs python:{'en':{
+                    'institute':'institute',
+                    'staff':'staff',
+                    'research':'research',
+                    'resources':'resources',
+                    'news':'news'},
+                  'de':{
+                    'institute':'institut',
+                    'staff':'mitarbeiter',
+                    'research':'forschung',
+                    'resources':'ressourcen',
+                    'news':'aktuelles'}};
+                  secmap python:allsecs[lang];
+                  subsection python:template.getSubSection(crumbs=crumbs);
+                  ">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title metal:define-slot="title">Max Planck Institute for the History of Science</title>
+<link rel="stylesheet" type="text/css" tal:attributes="href string:$root/mpiwg_css" />
+<link rel="shortcut icon" href="http://www.mpiwg-berlin.mpg.de/favicon.ico" />
+<tal:block metal:define-slot="head" />
+</head>
+<body>
+  <div tal:condition="exists:here/mpiwg_test" tal:replace="structure here/mpiwg_test" />
+  <div id="wrapper">
+    <div id="header">
+      <div class="title">
+        <a tal:attributes="href string:$root/index.html"><img tal:attributes="src string:$root/images/title.png"
+          alt="Max Planck Institute for the History of Science" /></a>
+      </div>
+      <div class="logo">
+        <a tal:attributes="href string:$root/index.html"><img tal:attributes="src string:$root/images/logo.png" alt="MPIWG Logo" /></a>
+      </div>
+    </div>
+
+    <div id="mainnav">
+      <ul>
+        <li tal:repeat="sec sections" tal:attributes="class python:test(sec.getId()==section,'sec on','sec')"><a
+          tal:attributes="href sec/absolute_url" tal:content="sec/title">Institute</a></li>
+        <li class="sec lang"><a class="internal" tal:attributes="href string:$root/../de">Deutsch</a></li>
+      </ul>
+    </div>
+
+    <div id="breadcrumbs" metal:define-slot="breadcrumbs">
+      <a tal:attributes="href root">Home</a> &gt;
+      <tal:block tal:repeat="crumb crumbs | nothing">
+        <tal:block tal:condition="not:repeat/crumb/end">
+          <a tal:attributes="href python:crumb[1]" tal:content="python:crumb[0]">News</a> &gt;
+            </tal:block>
+        <tal:block tal:condition="repeat/crumb/end">
+          <span class="selected"> <a tal:attributes="href python:crumb[1]" tal:content="python:crumb[0]">News</a>
+          </span>
+        </tal:block>
+      </tal:block>
+    </div>
+
+    <div id="mainrow" metal:define-slot="mainrow">
+      <div class="leftbox" metal:define-slot="leftbox">
+        <div class="subnav" metal:define-slot="subnav">
+          <ul tal:condition="crumbs">
+            <li tal:repeat="subnav python:here.subNav(here)"
+              tal:attributes="class python:test(subnav[0]==subsection,'sn_on','sn_off')"><a
+              tal:attributes="href python:subnav[1].absolute_url()" tal:content="python:subnav[1].title">Subnav</a></li>
+          </ul>
+        </div>
+        <div class="subnavbox" metal:define-slot="subnavbox"></div>
+      </div>
+      <!-- /leftbox -->
+      <div class="main content" metal:define-slot="main">
+
+        <div class="center" metal:define-slot="center"></div>
+        <!-- /center -->
+
+      </div>
+      <!-- /main -->
+
+      <metal:block metal:define-slot="sidebar" />
+      <!-- /sidebar -->
+
+    </div>
+    <!-- /maincontent -->
+
+    <div id="footer">
+      <div class="text">
+        <a tal:attributes="href string:$root/resources/intranet.html">Intranet</a> | <a href="https://webmail.mpiwg-berlin.mpg.de"
+          target="_blank">Webmail</a> | <a tal:attributes="href string:$root/institute/address.html">Contact</a> | <a
+          tal:attributes="href string:$root/impressum.html">Imprint</a> | <a
+          tal:attributes="href python:'http://www2.mpiwg-berlin.mpg.de/Library/libindex.html'" target="_blank">Library
+          (internal)</a> | <a tal:attributes="href string:$root/institute/sitemap.html">Sitemap</a>
+      </div>
+      <div class="logo">
+        <a href="http://www.mpg.de/"> <img tal:attributes="src string:$root/images/signet.png" alt="MPG" width="204" height="41" />
+        </a>
+      </div>
+    </div>
+    <!-- /footer -->
+  </div>
+  <!-- /wrapper -->
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/mpiwgDev.info	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,12 @@
+name = MPIWGDev
+description = MPIWGDev  theme
+package = Core
+version = VERSION
+core = 7.x
+stylesheets[all][] = layout.css
+
+; Information added by drupal.org packaging script on 2013-02-20
+version = "7.20"
+project = "drupal"
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/node.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,30 @@
+<?php
+?>
+<div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>>
+  <?php print $user_picture; ?>
+  <?php print render($title_prefix); ?>
+  <?php if (!$page): ?>
+    <h2<?php print $title_attributes; ?>><a href="<?php print $node_url; ?>"><?php print $title; ?></a></h2>
+  <?php endif; ?>
+  <?php print render($title_suffix); ?>
+  <?php if ($display_submitted): ?>
+    <span class="submitted"><?php print $submitted ?></span>
+  <?php endif; ?>
+  <div class="content clearfix"<?php print $content_attributes; ?>>
+    <?php
+      // We hide the comments and links now so that we can render them later.
+      hide($content['comments']);
+      hide($content['links']);
+      print render($content);
+    ?>
+  </div>
+
+  <div class="clearfix">
+    <?php if (!empty($content['links'])): ?>
+      <div class="links"><?php print render($content['links']); ?></div>
+    <?php endif; ?>
+
+    <?php print render($content['comments']); ?>
+  </div>
+
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/page.tpl.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,126 @@
+<?php print render($page['header']); ?>
+<div id="wrapper">
+	<div id="container" class="clearfix">
+		<div id="mpiwg_link">
+			<a href="http://www.mpiwg-berlin.mpg.de/en"><b>&#171;</b> Max Planck
+				Institute for the History for Science</a>
+		</div>
+
+		<div id="header">
+
+			<div class="title">
+			  <?php if ($logo!=''):?>
+              <div class="logo_left">
+              <?php if (user_access("view restricted content")):?>
+	            <div class="mpiwg_internal_view">INTERNAL VIEW</div>
+	            <?php endif;?>
+				<a href="<?php print theme_get_setting('mpiwg_logo_link') ?>"> <img  class="logo_left"
+					src="<?php print $logo ?>"
+					alt="<?php print $site_name_and_slogan ?>"
+					title="<?php print $site_name_and_slogan ?>" id="logo" />
+				</a>
+				</div>
+				<?php endif;?>
+                <h1>
+					<a href="<?php print $front_page ?>"> <?php print $site_name ?>
+					</a>
+				</h1>
+				<h2>
+					<?php print $site_slogan ?>
+				</h2>
+
+			</div>
+			<div class="logo">
+				<a href="<?php print theme_get_setting('mpiwg_logo_link') ?>"> <img
+					src="<?php print $MPIWGlogo ?>"
+					alt="<?php print $site_name_and_slogan ?>"
+					title="<?php print $site_name_and_slogan ?>" id="logo" />
+				</a>
+
+
+			</div>
+		</div>
+
+	</div>
+	<!-- /#header -->
+	<div id="mainnav">
+
+			<?php if ($primary_nav): print $primary_nav; endif; ?>
+
+			<!--<li tal:repeat="sec sections" tal:attributes="class python:test(sec.getId()==section,'sec on','sec')"><a
+          tal:attributes="href sec/absolute_url" tal:content="sec/title">Institute</a></li>-->
+			<!--<li class="sec lang"><a class="internal" tal:attributes="href string:$root/../de">Deutsch</a></li>-->
+
+	</div>
+	<!-- /#mainnav-->
+
+
+
+	<div id="mainrow">
+		<?php if ($page['sidebar_first']): ?>
+		<div id="leftbox" class="leftbox">
+			<?php print render($page['sidebar_first']); ?>
+		</div>
+		<?php endif; ?>
+
+		<div class="main">
+			<div id="center">
+
+
+				<!--<?php print $breadcrumb; ?>-->
+				<?php if ($page['highlighted']): ?>
+				<div id="highlighted">
+					<?php print render($page['highlighted']); ?>
+				</div>
+				<?php endif; ?>
+				<a id="main-content"></a>
+				<?php if ($tabs): ?>
+				<div id="tabs-wrapper" class="clearfix">
+					<?php endif; ?>
+					<?php print render($title_prefix); ?>
+					<?php if(substr($title,0,1) != "_"): ?>
+					<h1 <?php print $tabs ? ' class="with-tabs"' : '' ?>>
+
+						<?php print $title ?>
+
+					</h1>
+					<?php endif; ?>
+					<?php print render($title_suffix); ?>
+					<?php if ($tabs): ?>
+					<?php print render($tabs); ?>
+				</div>
+				<?php endif; ?>
+				<?php print render($tabs2); ?>
+				<?php print $messages; ?>
+				<?php print render($page['help']); ?>
+				<?php if ($action_links): ?>
+				<ul class="action-links">
+					<?php print render($action_links); ?>
+				</ul>
+				<?php endif; ?>
+				<div class="clearfix">
+					<?php print render($page['content']); ?>
+				</div>
+				<?php print $feed_icons ?>
+				<?php print render($page['footer']); ?>
+			</div>
+			<!--/#center -->
+
+		</div>
+		<!-- .main.content-->
+		<?php if ($page['sidebar_second']): ?>
+		<div id="sidebar" class="sidebar">
+			<div id="sideblock" class="sideblock">
+				<?php print render($page['sidebar_second']); ?>
+			</div>
+		</div>
+		<?php endif; ?>
+
+
+
+	</div>
+	<!-- #mainrow-->
+</div>
+<!-- /#container -->
+</div>
+<!-- /#wrapper -->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/page.tpl_orig.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,68 @@
+<?php
+?>
+  <?php print render($page['header']); ?>
+
+  <div id="wrapper">
+    <div id="container" class="clearfix">
+
+      <div id="header">
+        <div id="logo-floater">
+        <?php if ($logo || $site_title): ?>
+          <?php if ($title): ?>
+            <div id="branding"><strong><a href="<?php print $front_page ?>">
+            <?php if ($logo): ?>
+              <img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" />
+            <?php endif; ?>
+            <?php print $site_html ?>
+            </a></strong></div>
+          <?php else: /* Use h1 when the content title is empty */ ?>
+            <h1 id="branding"><a href="<?php print $front_page ?>">
+            <?php if ($logo): ?>
+              <img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" />
+            <?php endif; ?>
+            <?php print $site_html ?>
+            </a></h1>
+        <?php endif; ?>
+        <?php endif; ?>
+        </div>
+
+        <?php if ($primary_nav): print $primary_nav; endif; ?>
+        <?php if ($secondary_nav): print $secondary_nav; endif; ?>
+      </div> <!-- /#header -->
+
+      <?php if ($page['sidebar_first']): ?>
+        <div id="sidebar-first" class="sidebar">
+          <?php print render($page['sidebar_first']); ?>
+        </div>
+      <?php endif; ?>
+
+      <div id="center"><div id="squeeze"><div class="right-corner"><div class="left-corner">
+          <?php print $breadcrumb; ?>
+          <?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?>
+          <a id="main-content"></a>
+          <?php if ($tabs): ?><div id="tabs-wrapper" class="clearfix"><?php endif; ?>
+          <?php print render($title_prefix); ?>
+          <?php if ($title): ?>
+            <h1<?php print $tabs ? ' class="with-tabs"' : '' ?>><?php print $title ?></h1>
+          <?php endif; ?>
+          <?php print render($title_suffix); ?>
+          <?php if ($tabs): ?><?php print render($tabs); ?></div><?php endif; ?>
+          <?php print render($tabs2); ?>
+          <?php print $messages; ?>
+          <?php print render($page['help']); ?>
+          <?php if ($action_links): ?><ul class="action-links"><?php print render($action_links); ?></ul><?php endif; ?>
+          <div class="clearfix">
+            <?php print render($page['content']); ?>
+          </div>
+          <?php print $feed_icons ?>
+          <?php print render($page['footer']); ?>
+      </div></div></div></div> <!-- /.left-corner, /.right-corner, /#squeeze, /#center -->
+
+      <?php if ($page['sidebar_second']): ?>
+        <div id="sidebar-second" class="sidebar">
+          <?php print render($page['sidebar_second']); ?>
+        </div>
+      <?php endif; ?>
+
+    </div> <!-- /#container -->
+  </div> <!-- /#wrapper -->
Binary file sites/all/themes/mpiwgDev/screenshot.png has changed
Binary file sites/all/themes/mpiwgDev/screenshot_old.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/template.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,241 @@
+<?php
+
+/**
+ * Override of theme_breadcrumb().
+ */
+
+function mpiwgDev_breadcrumb($variables) {
+  $breadcrumb = $variables['breadcrumb'];
+
+  if (!empty($breadcrumb)) {
+    // Provide a navigational heading to give context for breadcrumb links to
+    // screen-reader users. Make the heading invisible with .element-invisible.
+    $output = '<h2 class="element-invisible">' . t('You are here') . '</h2>';
+
+    $output .= '<div class="breadcrumb">' . implode(' › ', $breadcrumb) . '</div>';
+    return $output;
+  }
+}
+
+/**
+ * Override or insert variables into the maintenance page template.
+ */
+function mpiwgDev_preprocess_maintenance_page(&$vars) {
+  // While markup for normal pages is split into page.tpl.php and html.tpl.php,
+  // the markup for the maintenance page is all in the single
+  // maintenance-page.tpl.php template. So, to have what's done in
+  // mpiwgDev_preprocess_html() also happen on the maintenance page, it has to be
+  // called here.
+  mpiwgDev_preprocess_html($vars);
+}
+
+/**
+ * Override or insert variables into the html template.
+ */
+function mpiwgDev_preprocess_html(&$vars) {
+  // Toggle fixed or fluid width.
+  if (theme_get_setting('mpiwgDev_width') == 'fluid') {
+    $vars['classes_array'][] = 'fluid-width';
+  }
+  // Add conditional CSS for IE6.
+  drupal_add_css(path_to_theme() . '/fix-ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE));
+}
+
+/**
+ * Override or insert variables into the html template.
+ */
+function mpiwgDev_process_html(&$vars) {
+  // Hook into color.module
+  if (module_exists('color')) {
+    _color_html_alter($vars);
+  }
+}
+
+/**
+ * Override or insert variables into the page template.
+ */
+function mpiwgDev_preprocess_page(&$vars) {
+  // Move secondary tabs into a separate variable.
+
+
+  $vars['MPIWGlogo'] = base_path() . path_to_theme() . '/logo.png';
+  $vars['tabs2'] = array(
+    '#theme' => 'menu_local_tasks',
+    '#secondary' => $vars['tabs']['#secondary'],
+  );
+  unset($vars['tabs']['#secondary']);
+
+  if (isset($vars['main_menu'])) {
+    /* $vars['primary_nav'] = theme('mpiwgDev_links__system__main_menu', array(
+      'links' => $vars['main_menu'],
+      'attributes' => array(
+        'class' => array('links', 'inline', 'main-menu'),
+      ),
+      'heading' => array(
+        'text' => t('Main menu'),
+        'level' => 'h2',
+        'class' => array('element-invisible'),
+      )
+    ));*/
+    $vars['primary_nav'] = mpiwgDev_links__system__main_menu($vars['main_menu']);
+
+  }
+  else {
+    $vars['primary_nav'] = FALSE;
+  }
+  if (isset($vars['secondary_menu'])) {
+    $vars['secondary_nav'] = theme('links__system_secondary_menu', array(
+      'links' => $vars['secondary_menu'],
+      'attributes' => array(
+        'class' => array('links', 'inline', 'secondary-menu'),
+      ),
+      'heading' => array(
+        'text' => t('Secondary menu'),
+        'level' => 'h2',
+        'class' => array('element-invisible'),
+      )
+    ));
+  }
+  else {
+    $vars['secondary_nav'] = FALSE;
+  }
+
+  // Prepare header.
+  $site_fields = array();
+  if (!empty($vars['site_name'])) {
+    $site_fields[] = $vars['site_name'];
+  }
+  if (!empty($vars['site_slogan'])) {
+    $site_fields[] = $vars['site_slogan'];
+  }
+  $vars['site_title'] = implode(' ', $site_fields);
+  if (!empty($site_fields)) {
+    $site_fields[0] = '<span>' . $site_fields[0] . '</span>';
+  }
+  $vars['site_html'] = implode(' ', $site_fields);
+
+  // Set a variable for the site name title and logo alt attributes text.
+  $slogan_text = $vars['site_slogan'];
+  $site_name_text = $vars['site_name'];
+  $vars['site_name_and_slogan'] = $site_name_text . ' ' . $slogan_text;
+}
+
+/**
+ * Override or insert variables into the node template.
+ */
+function mpiwgDev_preprocess_node(&$vars) {
+  $vars['submitted'] = $vars['date'] . ' — ' . $vars['name'];
+}
+
+/**
+ * Override or insert variables into the comment template.
+ */
+function mpiwgDev_preprocess_comment(&$vars) {
+  $vars['submitted'] = $vars['created'] . ' — ' . $vars['author'];
+}
+
+/**
+ * Override or insert variables into the block template.
+ */
+function mpiwgDev_preprocess_block(&$vars) {
+  $vars['title_attributes_array']['class'][] = 'title';
+  //$vars['classes_array'][] = 'clearfix';
+  $vars['classes_array']= array('MPIWGDEV_block');
+}
+
+/**
+ * Override or insert variables into the page template.
+ */
+function mpiwgDev_process_page(&$vars) {
+  // Hook into color.module
+  if (module_exists('color')) {
+    _color_page_alter($vars);
+  }
+}
+
+/**
+ * Override or insert variables into the region template.
+ */
+function mpiwgDev_preprocess_region(&$vars) {
+  if ($vars['region'] == 'header') {
+    $vars['classes_array'][] = 'clearfix';
+  }
+}
+
+function mpiwgDev_links__system__main_menu($variables) {
+  $html = "  <ul>\n";
+
+  #$tree = menu_tree_all_data('main_menu',$variables['links']);
+
+  foreach ($variables as $link) {
+    $path=$link['href'];
+ if (($path == $_GET['q'] ||
+      ($path == '<front>' && drupal_is_front_page())) && (empty($options['language']) )) {
+ $html .= '<li class="sec on">'.l($link['title'], $link['href'], $link)."</li>";
+  }else{
+
+    $html .= '<li  class="sec" >'.l($link['title'], $link['href'], $link)."</li>";
+  }
+  }
+
+  global $user;
+
+  // soll login im hauptmenu angezeigt werden?
+  if (theme_get_setting('mpiwg_login_in_main_menu')){
+  if (!$user->uid) {
+
+    $html .='<li  class="sec" >'.l(t("Login"), 'user')."</li>";
+  } elseif ($user->name=='ip_login'){
+    $html .='<li  class="sec" >'.l(t("Login"), 'user/logout')."</li>";
+  } else
+   {
+    $html .='<li  class="sec" >'.l(t("Logout").'('.$user->name . ')', 'user/logout')."</li>";
+  }
+  }
+  $html .= "  </ul>\n";
+
+
+  return $html;
+}
+
+
+
+function mpiwgDev_menu_tree($variables) {
+  return '<div class="subnav"> <ul class="menu">' . $variables['tree'] . '</ul></div>';
+}
+function mpiwgDev_menu_link  ($variables){
+
+
+  $element = $variables['element'];
+  $sub_menu = '';
+
+  if ($element['#below']) {
+    $sub_menu = drupal_render($element['#below']);
+
+  }
+  $output = l($element['#title'], $element['#href'], $element['#localized_options']);
+
+  $active=false;
+  foreach ($element['#attributes']['class'] as $attr){
+    if ($attr == "active"){
+      $active=true;
+  }
+  }
+
+  if ($active) {
+    array_push($element['#attributes']['class'],'sn_on');
+  } else {
+    array_push($element['#attributes']['class'],'sn_off');
+  }
+
+  return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
+}
+
+function mpiwgDev_block_view_locale_language_alter(&$vars,$block){
+
+  $vars['content']='<div class="subnav">'.$vars['content'].'</div>';
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sites/all/themes/mpiwgDev/theme-settings.php	Wed Jul 31 13:49:13 2013 +0200
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Theme setting callbacks for the garland theme.
+ */
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * @param $form
+ *   The form.
+ * @param $form_state
+ *   The form state.
+ */
+function mpiwgDev_form_system_theme_settings_alter(&$form, &$form_state) {
+
+  $form['mpiwg_logo_link'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Link behind Logo'),
+
+    '#default_value' => theme_get_setting('mpiwg_logo_link'),
+    '#description' => t('Specify the link behing the logo.'),
+    // Place this above the color scheme options.
+    '#weight' => -2,
+  );
+
+  $form['mpiwg_login_in_main_menu'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Login link in main menu?'),
+      '#default_value' => theme_get_setting('mpiwg_login_in_main_menu'),
+      '#description' => t('Puts a login link in the main menu.'),
+      // Place this above the color scheme options.
+      '#weight' => -3,
+  );
+}