Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/README.txt,v
retrieving revision 1.1.2.1.2.13
diff -u -p -r1.1.2.1.2.13 README.txt
--- README.txt	20 Mar 2009 04:11:16 -0000	1.1.2.1.2.13
+++ README.txt	30 Mar 2009 18:53:03 -0000
@@ -88,7 +88,19 @@ it may take a few minutes between runnin
 is visible in search.
 
 Enable blocks for facets first at Administer > Site configuration > Apache Solr > Enabled filters,
-then position them as you like at Administer > Site building > Blocks.   
+then position them as you like at Administer > Site building > Blocks.
+
+Creating an Apache Solr view
+----------------------------
+
+Once this module is enabled, a new base table becomes available when creating a
+new view. In Views > Add just select "Apache Solr" as the view type to create a
+view which gets its data from the Solr search server.
+
+You can then go on to add fields, a style, etc., to the view, just like normal.
+To create a view for normally searching with Apache Solr (just like in
+search/apachesolr_search), add the "Apachesolr: Search" argument to the view.
+
 
 Troubleshooting
 --------------
Index: apachesolr.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.module,v
retrieving revision 1.1.2.12.2.119
diff -u -p -r1.1.2.12.2.119 apachesolr.module
--- apachesolr.module	20 Mar 2009 17:34:23 -0000	1.1.2.12.2.119
+++ apachesolr.module	30 Mar 2009 18:53:04 -0000
@@ -194,6 +194,13 @@ function apachesolr_node_type($op, $info
     db_query("UPDATE {apachesolr_search_node} SET changed = %d WHERE nid IN (SELECT nid FROM {node} WHERE type = '%s' OR type = '%s')", time(), $info->old_type, $info->type);
   }
 }
+ 
+/**
+ * Implementation of hook_views_api().
+ */
+function apachesolr_views_api() {
+  return array('api' => '2.0');
+}
 
 /**
  * Helper function for modules implmenting hook_search's 'status' op.
Index: apachesolr.views.inc
===================================================================
RCS file: apachesolr.views.inc
diff -N apachesolr.views.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apachesolr.views.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,315 @@
+<?php
+
+// $Id: $
+
+/*
+ * Load files with base classes of the contained classes.
+ */
+
+/**
+ * Implementation of hook_views_handlers().
+ */
+function apachesolr_views_handlers() {
+  return array(
+    'info' => array(
+      'path' => drupal_get_path('module', 'apachesolr') . '/handlers',
+    ),
+    'handlers' => array(
+      'apachesolr_views_handler_argument' => array(
+        'parent' => 'views_handler_argument',
+      ),
+      'apachesolr_views_handler_field' => array(
+        'parent' => 'views_handler_field_node',
+      ),
+      'apachesolr_views_handler_field_author' => array(
+        'parent' => 'apachesolr_views_handler_field',
+      ),
+      'apachesolr_views_handler_field_date' => array(
+        'parent' => 'apachesolr_views_handler_field',
+      ),
+      'apachesolr_views_handler_field_generic' => array(
+        'parent' => 'views_object',
+      ),
+      'apachesolr_views_handler_field_taxonomy' => array(
+        'parent' => 'apachesolr_views_handler_field',
+      ),
+      'apachesolr_views_handler_field_type' => array(
+        'parent' => 'apachesolr_views_handler_field',
+      ),
+      'apachesolr_views_handler_field_score' => array(
+        'parent' => 'views_handler_field_numeric',
+      ),
+      'apachesolr_views_handler_field_snippet' => array(
+        'parent' => 'views_handler_field',
+      ),
+      'apachesolr_views_handler_filter_author' => array(
+        'parent' => 'views_handler_filter',
+      ),
+      'apachesolr_views_handler_filter_search' => array(
+        'parent' => 'views_handler_filter_search',
+      ),
+      'apachesolr_views_handler_filter_type' => array(
+        'parent' => 'views_handler_filter_in_operator',
+      ),
+      'apachesolr_views_handler_sort' => array(
+        'parent' => 'views_handler_sort',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_views_plugins().
+ */
+function apachesolr_views_plugins() {
+  return array(
+    'module' => 'apachesolr',
+    'query' => array(
+      'apachesolr_views_query' => array(
+        'title' => t('Apache Solr Query'),
+        'help' => t('Query that allows you to search with Apache Solr.'),
+        'handler' => 'apachesolr_views_query',
+        'parent' => 'views_query',
+      ),
+    ),
+  );
+
+}
+
+/**
+ * Implementation of hook_views_data().
+ */
+function apachesolr_views_data() {
+  $data['apachesolr']['table']['group']  = t('Apache Solr');
+
+  $data['apachesolr']['table']['base'] = array(
+    'query class' => 'apachesolr_views_query',
+    'title' => t('Apache Solr'),
+    'help' => t('Searches the site with the Apache Solr search engine.'),
+  );
+
+  $data['apachesolr']['nid'] = array(
+    'title' => t('Nid'),
+    'help' => t('The node ID of the node.'),
+    'field' => array(
+      'name field' => 'title',
+      'numeric' => TRUE,
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'node',
+        'field' => 'nid',
+      ),
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  $data['apachesolr']['title'] = array(
+    'title' => t('Title'),
+    'help' => t('The title of the node.'),
+    'argument' => array(
+      'handler' => 'apachesolr_views_handler_argument',
+    ),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'node',
+        'field' => 'title',
+      ),
+     ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  $data['apachesolr']['created'] = array(
+    'title' => t('Creation date'),
+    'help' => t('The date the node was created.'),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'node',
+        'field' => 'created',
+      ),
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  $data['apachesolr']['changed'] = array(
+    'title' => t('Updated date'),
+    'help' => t('The date the node was last updated.'),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'node',
+        'field' => 'changed',
+      ),
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  $data['apachesolr']['type'] = array(
+    'title' => t('Type'),
+    'help' => t('The type of a node (for example, "blog entry", "forum post", "story", etc).'),
+    'argument' => array(
+      'handler' => 'apachesolr_views_handler_argument',
+    ),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'node',
+        'field' => 'type',
+      ),
+    ),
+    'filter' => array(
+      'handler' => 'apachesolr_views_handler_filter_type',
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  $data['apachesolr']['name'] = array(
+    'title' => t('Author'),
+    'help' => t("The node's author."),
+    'argument' => array(
+      'handler' => 'apachesolr_views_handler_argument',
+    ),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'additional fields' => array('uid'),
+      'apachesolr base handler' => array(
+        'table' => 'users',
+        'field' => 'name',
+      ),
+    ),
+    'filter' => array(
+      'handler' => 'apachesolr_views_handler_filter_author',
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  $data['apachesolr']['uid'] = array(
+    'title' => t('Author Uid'),
+    'help' => t("The node's author's user ID."),
+    'argument' => array(
+      'handler' => 'apachesolr_views_handler_argument',
+    ),
+    'field' => array(
+      'name field' => 'name',
+      'numeric' => TRUE,
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'users',
+        'field' => 'uid',
+      ),
+    ),
+  );
+  $data['apachesolr']['body'] = array(
+    'title' => t('Body'),
+    'help' => t("The node's content."),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => FALSE,
+      'element type' => 'div',
+      'apachesolr base handler' => array(
+        'table' => 'node_revisions',
+        'field' => 'body',
+      ),
+    ),
+  );
+  $data['apachesolr']['comment_count'] = array(
+    'title' => t('Comment count'),
+    'help' => t('The number of comments that were posted to the node.'),
+    'field' => array(
+      'numeric' => TRUE,
+      'handler' => 'apachesolr_views_handler_field_generic',
+      'click sortable' => TRUE,
+      'apachesolr base handler' => array(
+        'table' => 'node_comment_statistics',
+        'field' => 'comment_count',
+      ),
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  // TODO Get taxonomy fields to work
+  /*if (module_exists('taxonomy')) {
+    $data['apachesolr']['tid'] = array(
+      'title' => t('Taxonomy terms'),
+      'help' => t('Taxonomy terms associated with the node.'),
+      'field' => array(
+        'handler' => 'apachesolr_views_handler_field_taxonomy',
+        'click sortable' => FALSE,
+        'additional fields' => array('vid'),
+        'apachesolr base handler' => array(
+          'table' => 'vocabulary',
+          'field' => 'name',
+        ),
+      ),
+    );
+  }*/
+  if (module_exists('translation')) {
+    $data['apachesolr']['language'] = array(
+      'title' => t('Language'),
+      'help' => t('The language the node is in.'),
+      'argument' => array(
+        'handler' => 'apachesolr_views_handler_argument',
+      ),
+      'field' => array(
+        'handler' => 'apachesolr_views_handler_field_generic',
+        'click sortable' => TRUE,
+        'apachesolr base handler' => array(
+          'table' => 'node',
+          'field' => 'language',
+        ),
+      ),
+      'sort' => array(
+        'handler' => 'apachesolr_views_handler_sort',
+      ),
+    );
+  }
+  
+  $data['apachesolr']['text'] = array(
+    'title' => t('Search'),
+    'help' => t('Searches the content with Solr'),
+    'argument' => array(
+      'handler' => 'apachesolr_views_handler_argument',
+    ),
+    'filter' => array(
+      'handler' => 'apachesolr_views_handler_filter_search',
+    ),
+  );
+  
+  // score field. Useful for when combining sorts. So you sort by score, creation etc.
+  $data['apachesolr']['score'] = array(
+    'title' => t('Search Score'),
+    'help' => t('The score of the search for this item. When no sorts are present this is the default sort'),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_score'
+    ),
+  );
+  
+  // snippet field
+  $data['apachesolr']['snippet'] = array(
+    'title' => t('Search Snippet'),
+    'help' => t('The return snippet that matches the query sent to Solr.'),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field_snippet',
+    ),
+  );
+  
+  return $data;
+}
Index: handlers/apachesolr_views_handler_argument.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_argument.inc
diff -N handlers/apachesolr_views_handler_argument.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_argument.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,18 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class that allows searching the site with Apache Solr through a view.
+ */
+class apachesolr_views_handler_argument extends views_handler_argument {
+  
+  /**
+   * Add argument to query.
+   */
+  public function query() {
+    $this->query->add_term(apachesolr_views_query::escape_term($this->argument),
+        $this->real_field);
+  }
+  
+}
Index: handlers/apachesolr_views_handler_field_generic.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field_generic.inc
diff -N handlers/apachesolr_views_handler_field_generic.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field_generic.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,170 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for retrieving and rendering an arbitrary field from an Apache Solr
+ * query.
+ * The class uses a predefined handler to which it acts as a proxy.
+ */
+class apachesolr_views_handler_field_generic {
+  
+  /**
+   * The handler from which to copy methods.
+   */
+  private $_original;
+  
+  /*
+   * Overloaded methods (this is where the magic lies)
+   */
+  
+  /**
+   * Gets called when trying to retrieve an undefined property.
+   */
+  public function &__get($name) {
+    if (isset($this->_original->$name)) {
+      return $this->_original->$name;
+    }
+    return NULL;
+  }
+  
+  /**
+   * Gets called when trying to set an undefined property.
+   */
+  public function __set($name, $value) {
+    $this->_original->$name = $value;
+  }
+  
+  /**
+   * Gets called when calling isset() or empty() on an undefined property.
+   */
+  public function __isset($name) {
+    return isset($this->_original->$name);
+  }
+  
+  /**
+   * Gets called when calling unser() on an undefined property.
+   */
+  public function __unset($name) {
+    unset($this->_original->$name);
+  }
+  
+  /**
+   * Gets called when calling an undefined instance method.
+   */
+  public function __call($name, $args) {
+    if (!method_exists($this->_original, $name)) {
+      trigger_error("tried to call undefined instance method $name", E_USER_ERROR);
+    }
+    return call_user_func_array(array($this->_original, $name), $args);
+  }
+  
+  /**
+   * Gets called when calling an undefined static method.
+   */
+  public static function __callStatic($name, $args) {
+    $method = array(get_class($this->_original), $name);
+    if (!method_exists($method)) {
+      trigger_error("tried to call undefined static method $name", E_USER_ERROR);
+    }
+    call_user_func_array($method, $args);
+  }
+  
+  /*
+   * "Normal" methods
+   */
+  
+  /**
+   * Loads the base handler and loads it with the specified definition.
+   * Throws an exception if unsuccessful.
+   */
+  public function set_definition($definition) {
+    if (empty($definition['apachesolr base handler'])) {
+      watchdog('views',
+          'no base handler specified for apachesolr field handler', array(),
+          WATCHDOG_ERROR);
+    }
+    $o = $definition['apachesolr base handler'];
+    $this->_original = views_get_handler($o['table'], $o['field'], 'field');
+    if (empty($this->_original)) {
+      watchdog('views',
+          'invalid base handler specified for apachesolr field handler: ' .
+          $o['table'] . '/' . $o['field'], array(), WATCHDOG_ERROR);
+    }
+    // Give the original handler the correct definition.
+    $this->_original->set_definition($definition);
+  }
+  
+  /**
+   * Get option form definition.
+   * 
+   * <strong>NOTE:</strong> This method must be defined explicitly since
+   * pass-by-reference doesn't work with <tt>call_user_func_array()</tt>.
+   */
+  public function options_form(&$form, &$form_state) {
+    return $this->_original->options_form($form, $form_state);
+  }
+  
+  /**
+   * Construct a new apachesolr field handler.
+   */
+  public function construct() {
+    $this->_original->construct();
+
+    $this->_original->aliases = drupal_map_assoc(
+        array('id', 'site', 'hash', 'url', 'title', 'body', 'type', 'type_name',
+            'path', 'path_alias', 'uid', 'name', 'created', 'changed',
+            'last_comment_or_change', 'nid', 'status', 'promote', 'moderate',
+            'sticky', 'tnid', 'translate', 'language', 'comment_count', 'tid',
+            'vid', 'timestamp'
+        ));
+  }
+  
+  /**
+   * We don't need to ensure any tables.
+   * So overwrite this method because it might be called by inherited methods.
+   */
+  public function ensure_my_table() {}
+  
+  /**
+   * Tell the query object to retrieve this field.
+   */
+  public function query() {
+    $this->_original->field_alias = $this->_original->real_field;
+    $this->_original->query->add_field($this->_original->real_field);
+    $this->add_additional_fields();
+  }
+  
+  /**
+   * Add additionally required fields.
+   */
+  public function add_additional_fields($fields = NULL) {
+    if (!empty($fields)) {
+      return $this->_original->add_additional_fields($fields);
+    }
+    foreach ($this->_original->additional_fields as $f) {
+      $this->_original->query->add_field($f);
+    }
+  }
+  
+  /**
+   * Called when click-sorting.
+   */
+  public function click_sort($order) {
+    /* These fields have a special "*_sort" field for sorting: */
+    $special_sort_fields = array(
+      'name' => 'name_sort',
+      'title' => 'title_sort',
+    );
+    
+    if (empty($special_sort_fields[$this->real_field])) {
+      $this->_original->query->add_sort(
+          $this->_original->real_field, $order, TRUE);
+    }
+    else {
+      $this->_original->query->add_sort(
+          $special_sort_fields[$this->_original->real_field], $order, TRUE);
+    }
+  }
+  
+}
Index: handlers/apachesolr_views_handler_field_score.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field_score.inc
diff -N handlers/apachesolr_views_handler_field_score.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field_score.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,16 @@
+<?php
+// $Id: $
+
+/**
+ * @file
+ * Views handler to handle the score field. Will not add anything to the query
+ */
+class apachesolr_views_handler_field_score extends views_handler_field_numeric {
+  function construct() {
+    parent::construct();
+    $this->definition['float'] = TRUE;
+  }
+  function query() {
+    $this->field_alias = $this->query->add_field('score');
+  }
+}
\ No newline at end of file
Index: handlers/apachesolr_views_handler_filter_author.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_filter_author.inc
diff -N handlers/apachesolr_views_handler_filter_author.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_filter_author.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,87 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for filtering by users.
+ */
+class apachesolr_views_handler_filter_author extends views_handler_filter {
+  
+  public function option_definition() {
+    $options = parent::option_definition();
+
+    $options['operator'] = array('default' => 'one of');
+    $options['value'] = array('default' => '');
+
+    return $options;
+  }
+  
+  public function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $time = time();
+
+    $form['operator'] = array(
+      '#type' => 'select',
+      '#title' => t('Filter for'),
+      '#options' => array(
+        'one of' => t('One of these'),
+        'not one of' => t('Not one of these'),
+        'current' => t('Current user'),
+        'not current' => t('Not current user'),
+      ),
+      '#default_value' =>
+          isset($this->options['operator'])
+          ? $this->options['operator']
+          : 'one of',
+    );
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Usernames'),
+      '#description' => t('Enter a comma seperated list of user names.'),
+      '#default_value' =>
+          isset($this->options['value'])
+          ? $this->options['value']
+          : '',
+      '#process' => array('views_process_dependency'),
+      '#dependency' =>
+          array('edit-options-operator' => array('one of', 'not one of')),
+    );
+  }
+
+  public function query() {
+    if ($this->options['operator'] == 'current') {
+      global $user;
+      $this->query->add_term($user->uid, 'uid');
+    }
+    else if ($this->options['operator'] == 'not current') {
+      global $user;
+      $this->query->add_term('NOT uid:' . $user->uid);
+    }
+    else {
+      $not = $this->options['operator'] == 'not one of';
+      $names = array_map('trim', explode(',', $this->options['value']));
+      foreach ($names as $i => $name) {
+        if (empty($name)) {
+          unset($names[$i]);
+        }
+      }
+      if (empty($names)) {
+        if (!$not) {
+          $this->query->add_term('nid:-1');//Add term that will yield no results
+        }
+      }
+      else if (count($names) == 1) {
+        $this->query->add_term(($not ? 'NOT ' : '') . 'name:' . 
+            array_shift($names));
+      }
+      else {
+        $key = 'name:' . array_shift($names);
+        foreach ($names as $name) {
+          $key .= ' OR name:' . $name;
+        }
+        $this->query->add_term($not ? 'NOT (' . $key . ')' : $key);
+      }
+    }
+  }
+  
+}
Index: handlers/apachesolr_views_handler_filter_search.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_filter_search.inc
diff -N handlers/apachesolr_views_handler_filter_search.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_filter_search.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,16 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class that allows searching the site with Apache Solr through a view.
+ */
+class apachesolr_views_handler_filter_search extends views_handler_filter_search {
+  
+  public function query() {
+    if (!empty($this->value)) {
+      $this->query->set_query($this->value);
+    }
+  }
+  
+}
Index: handlers/apachesolr_views_handler_filter_type.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_filter_type.inc
diff -N handlers/apachesolr_views_handler_filter_type.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_filter_type.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,34 @@
+<?php
+
+// $Id: $
+
+/**
+* Class for filtering by type.
+*/
+class apachesolr_views_handler_filter_type extends views_handler_filter_in_operator {
+ 
+  public function get_value_options() {
+    if (!isset($this->value_options)) {
+      $this->value_title = t('Node type');
+      $types = node_get_types();
+      foreach ($types as $type => $info) {
+        $options[$type] = $info->name;
+      }
+      $this->value_options = $options;
+    }
+  }
+
+  public function query() {
+    if (empty($this->value)) {
+      // Add term that will yield no results
+      $this->query->add_facet('nid', '-1');
+    }
+    else {
+      $not = $this->operator != 'in';
+     
+      foreach ($this->value as $type) {
+        $this->query->add_facet('type', $type, $not);
+      }
+    }
+  }
+}
\ No newline at end of file
Index: handlers/apachesolr_views_handler_sort.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_sort.inc
diff -N handlers/apachesolr_views_handler_sort.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_sort.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,29 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for sorting for a field.
+ */
+class apachesolr_views_handler_sort extends views_handler_sort {
+  
+  /**
+   * Places the sort into the search parameters.
+   */
+  public function query() {
+    /* These fields have a special "*_sort" field for sorting: */
+    $special_sort_fields = array(
+      'name' => 'name_sort',
+      'title' => 'title_sort',
+    );
+    $order = strtolower($this->options['order']);
+    
+    if (empty($special_sort_fields[$this->real_field])) {
+      $this->query->add_sort($this->real_field, $order);
+    }
+    else {
+      $this->query->add_sort($special_sort_fields[$this->real_field], $order);
+    }
+  }
+  
+}
Index: apachesolr_views_query.inc
===================================================================
RCS file: apachesolr_views_query.inc
diff -N apachesolr_views_query.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apachesolr_views_query.inc	30 Mar 2009 18:53:04 -0000
@@ -0,0 +1,338 @@
+<?php
+
+// $Id$ 
+
+// TODO: Spellchecker field? What todo about spellchecker...
+
+/**
+ * Class for handling a view that gets its data not from the database, but from
+ * a Solr server.
+ */
+class apachesolr_views_query extends views_plugin_query {
+  
+  // facets for the solr query
+  private $_facets = array();
+  
+  // String containing the query that will be handed to Solr.
+  private $_query;
+  
+  // Object encapsulating the actual query to Solr.
+  private $_solr_service;
+  
+  // Array containing the parameters that will be handed to Solr.
+  private $_params = array(
+    'fl' => 'id,nid,title,comment_count,type,created,changed,url,uid,name',
+    'facet' => 'true',
+    'facet.mincount' => 1,
+    'facet.sort' => 'true',
+    'hl' => 'false',
+  );
+  
+  // array all of the fields for the query
+  private $_used_fields = array(
+    'id',
+    'nid',
+    'url',
+    'uid', // TODO: skip this probably, but not for user searching...
+  );
+  
+  
+  /*
+   * Functions required by the views query class interface
+   */
+  
+  /**
+   * Constructor. Grab the PHP solr client wrapper
+   */
+  public function init($base_table, $base_field) {
+    $this->_solr_service = apachesolr_get_solr();
+  }
+  
+  /**
+   * Build the query object. Load up all avaivable facets so the blocks work.
+   */
+  public function build() {
+    /**
+     * Highlighting settings
+     * These settings are set in solrconfig.xml.
+     * See the defaults there.
+     * If you wish to override them, you can via settings.php
+     */
+    $this->_params['hl'] = variable_get('apachesolr_hl_active', NULL);
+    $this->_params['hl.fragsize']= variable_get('apachesolr_hl_textsnippetlength', NULL);
+    $this->_params['hl.simple.pre'] = variable_get('apachesolr_hl_pretag', NULL);
+    $this->_params['hl.simple.post'] = variable_get('apachesolr_hl_posttag', NULL);
+    $this->_params['hl.snippets'] = variable_get('apachesolr_hl_numsnippets', NULL);
+    $this->_params['hl.fl'] = variable_get('apachesolr_hl_fieldtohightlight', NULL);
+    // We default to getting snippets from the body.
+    $hl_fl = is_null($this->_params['hl.fl']) ? 'body' : $this->_params['hl.fl'];
+    
+    $facet_query_limits = variable_get('apachesolr_facet_query_limits', array());
+    // Request all enabled facets. And go fetch them
+    // this is used so the blocks work..
+    // TODO: with this views patch, the blocks arn't working currently
+    foreach (apachesolr_get_enabled_facets() as $module => $module_facets) {
+      foreach($module_facets as $delta => $facet_field) {
+        $this->_params['facet.field'][] = $facet_field;
+        // Facet limits
+        if (isset($facet_query_limits[$module][$delta])) {
+          $this->_params['f.' . $facet_field . '.facet.limit'] = $facet_query_limits[$module][$delta];
+        }
+      }
+    }
+    if (!empty($this->_params['facet.field'])) {
+      // Add a default limit for fields where no limit was set.
+      $this->_params['facet.limit'] = variable_get('apachesolr_facet_query_limit_default', 20);
+    }
+    
+    // Add in facets now
+    $this->_params['fq'] = array();
+    foreach ($this->_facets as $type => $filter_string) {
+      $this->_params['fq'][] = "$type:$filter_string";
+    }
+    
+    // add in the fields. These fields include the necessary fields.
+    $this->_params['fl'] = implode(',', $this->_used_fields);
+    
+    /**
+     * TODO: use the query field settings from the interface
+     */
+    
+    // get the total docs
+    $data = $this->_solr_service->getLuke();
+    if (isset($data->index->numDocs)) {
+      $total = $data->index->numDocs;
+    }
+    else {
+      $total = db_result(db_query("SELECT COUNT(nid) FROM {node}"));
+    }
+    
+    // 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/DisMaxRequestHandler
+    // rord() returns 1 for the newset doc, and the number in the index for
+    // the oldest doc.  The function is thus: $total/(rord()*$steepness + $total).
+    $date_settings = variable_get('apachesolr_search_date_boost', '4:200.0');
+    list($date_steepness, $date_boost) = explode(':', $date_settings);
+    if ($date_boost) {
+      $this->_params['bf'][] = "recip(rord(created),$date_steepness,$total,$total)^$date_boost";
+    }
+    // Boost on comment count.
+    $comment_settings = variable_get('apachesolr_search_comment_boost', '0:0');
+    list($comment_steepness, $comment_boost) = explode(':', $comment_settings);
+    if ($comment_boost) {
+      $this->_params['bf'][] = "recip(rord(comment_count),$comment_steepness,$total,$total)^$comment_boost";
+    }
+    // Boost for a more recent comment or node edit.
+    $changed_settings = variable_get('apachesolr_search_changed_boost', '0:0');
+    list($changed_steepness, $changed_boost) = explode(':', $changed_settings);
+    if ($changed_boost) {
+      $this->_params['bf'][] = "recip(rord(last_comment_or_change),$changed_steepness,$total,$total)^$changed_boost";
+    }
+    // Boost for nodes with sticky bit set.
+    $sticky_boost = variable_get('apachesolr_search_sticky_boost', 0);
+    if ($sticky_boost) {
+      $this->_params['bq'][] = "sticky:true^$sticky_boost";
+    }
+    // Boost for nodes with promoted bit set.
+    $promote_boost = variable_get('apachesolr_search_promote_boost', 0);
+    if ($promote_boost) {
+      $this->_params['bq'][] = "promote:true^$promote_boost";
+    }
+    // Modify the weight of results according to the node types.
+    $type_boosts = variable_get('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) {
+          $this->_params['bq'][] = "type:$type^$boost";
+        }
+      }
+    }
+  }
+  
+  /**
+   * Let modules modify the query just prior to finalizing it.
+   */
+  public function alter(&$view) {
+    // TODO drupal_alter our params here dont ya think?
+  }
+  
+  /**
+   * Executes the query and fills the associated view object with according
+   * values.
+   * 
+   * Values to set: $view->result, $view->total_rows, $view->execute_time,
+   * $view->pager['current_page'].
+   */
+  public function execute(&$view) {
+    $start_time = views_microtime();
+    $view->result = array();
+    
+    try {
+      // TODO: highlighting
+      // TODO: handle multiple pages seperated by commas. @see views_plugin_query_default.inc
+      $page = isset($_GET['page']) ? $_GET['page'] : 0;
+      $offset = $page * $view->pager['items_per_page'];
+      
+      $response = $this->_solr_service->search($this->_query, $offset, $view->pager['items_per_page'], $this->_params);
+      
+      $view->total_rows = $total = $response->response->numFound;
+      
+      // we do a fake pager here. Set the globals so the pager works (tm)
+      if (!empty($view->pager['use_pager'])) {
+        $view->pager['current_page'] = $offset / $view->pager['items_per_page'];
+        
+        // OOO! globals. This is to make paging work..
+        global $pager_page_array, $pager_total, $pager_total_items;
+        
+        // Set the item count for the pager.
+        $pager_total_items[$view->pager['element']] = $view->total_rows;
+        
+        // Calculate and set the count of available pages.
+        $pager_total[$view->pager['element']] = ceil($pager_total_items[$view->pager['element']] / $view->pager['items_per_page']);
+
+        // What page was requested:
+        // TODO: handle multiple page requests seperated by commas.
+        $pager_page_array = array($page);
+        $pager_page_array[$view->pager['element']] = $view->pager['current_page'];
+      }
+      
+      // 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($response);
+      apachesolr_has_searched(TRUE);
+      
+      if ($total > 0) {
+        $results = $response->response->docs;
+        
+        // Process dates
+        $date_fields = array('created', 'changed');
+        foreach (array_values($date_fields) as $field) {
+          if (empty($this->_used_fields[$field])) {
+            unset($date_fields[$field]);
+          }
+        }
+        if (!empty($date_fields)) {
+          foreach ($results as $doc) {
+            foreach ($date_fields as $field) {
+              $doc->$field = strtotime($doc->$field);
+            }
+          }
+        }
+        
+        $view->result = $results;
+      }
+    }
+    catch (Exception $e) {
+      watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR);
+      apachesolr_failure(t('Solr search'), is_null($query) ? $this->_keys : $query->get_query_basic());
+    }
+    
+    $view->execute_time = views_microtime() - $start_time;
+  }
+  
+  
+  /**
+   * Add a field to retrieve.
+   * 
+   * $compat_field is used for compatibility with the views_plugin_query_default
+   * definition of this method - when $compat_field is set, $field is ignored
+   * and $compat_field used instead.
+   */
+  public function add_field($field, $compat_field = NULL) {
+    if (!empty($compat_field)) {
+      $field = $compat_field;
+    }
+    if (is_array($field) && isset($field['field'])) {
+      $field = $field['field'];
+    }
+    if (empty($field) || !is_string($field)) {
+      return FALSE;
+    }
+    $this->_used_fields[] = $field;
+    return $field;
+  }
+  
+  /**
+   * Add a sorting directive.
+   * 
+   * @param $single If TRUE, the results will only be sorted by this order.
+   */
+  public function add_sort($field, $order, $single = FALSE) {
+    $sort = $field . ' ' . strtolower($order);
+    if (empty($this->_params['sort']) || $single) {
+      $this->_params['sort'] = $sort;
+    }
+    else {
+      $this->_params['sort'] .= ',' . $sort;
+    }
+  }
+  
+  /**
+   * get a search param. Used primarly by the snippet field
+   *
+   * @param string $param_name
+   *
+   * @return string parameter setting
+   */
+  function get_param($param_name) {
+    return $this->_params[$param_name];
+  }
+  
+  /**
+   * Add in a facet string
+   *
+   * @param string $type
+   * The type of facet. use apachesolr_index_key() for dynamic fields
+   *
+   * @param string $value
+   * The value of the facet. Can be "story OR page" to filter multiple
+   *
+   * @param boolean $exclude
+   * Whether or not to exclude the value from the results
+   *
+   * @return none
+   */
+  public function add_facet($type, $value, $exclude = FALSE) {
+    if (!$exclude) {
+      $this->_facets[$type] = $value;
+    }
+    else {
+      $this->_facets["NOT $type"] = $value;
+    }
+  }
+  
+  /**
+   * This function sets the query string
+   *
+   * @param string $query
+   * plain text typed in search query
+   *
+   * @return none
+   */
+  public function set_query($query) {
+    $this->_query = $query;
+  }
+  
+  /*
+   * (Public and private) helper functions
+   */
+  
+  /** Escapes a term for passing it to the query. */
+  public static function escape_term($term) {
+    $term = trim($term);
+    if (empty($term)) {
+      return '';
+    }
+    if (($term{0} == '"' && $term{strlen($term)-1} == '"')
+        || $term{0} == '(' && $term{strlen($term)-1} == ')') {
+      return $term;
+    }
+    
+    if (strpos($term, ' ') !== FALSE) {
+      return Drupal_Apache_Solr_Service::phrase($term);
+    }
+    return Drupal_Apache_Solr_Service::escape($term);
+  }
+}
