Index: apachesolr.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/apachesolr.module,v
retrieving revision 1.1.2.12.2.107
diff -u -r1.1.2.12.2.107 apachesolr.module
--- apachesolr.module	10 Feb 2009 20:47:03 -0000	1.1.2.12.2.107
+++ apachesolr.module	23 Feb 2009 16:35:05 -0000
@@ -189,6 +189,13 @@
 }
 
 /**
+ * Implementation of hook_views_api().
+ */
+function apachesolr_views_api() {
+  return array('api' => '2.0');
+}
+
+/**
  * Helper function for modules implmenting hook_search's 'status' op.
  */
 function apachesolr_index_status($namespace) {
Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/apachesolr/README.txt,v
retrieving revision 1.1.2.1.2.10
diff -u -r1.1.2.1.2.10 README.txt
--- README.txt	15 Feb 2009 11:16:01 -0000	1.1.2.1.2.10
+++ README.txt	23 Feb 2009 16:35:04 -0000
@@ -55,7 +55,19 @@
 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: handlers/apachesolr_views_handler_field_taxonomy.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field_taxonomy.inc
diff -N handlers/apachesolr_views_handler_field_taxonomy.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field_taxonomy.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,76 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for rendering taxonomy terms.
+ */
+class apachesolr_views_handler_field_taxonomy
+    extends apachesolr_views_handler_field {
+  
+  /**
+   * Unset the link_to_node option and provide link_to_terms.
+   */
+  public function option_definition() {
+    $options = parent::option_definition();
+    if (isset($options['link_to_node'])) {
+      unset($options['link_to_node']);
+    }
+    $options['link_to_terms'] = array('default' => TRUE);
+    return $options;
+  }
+
+  /**
+   * Unset the link_to_node option and provide link_to_terms.
+   */
+  public function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    if (isset($form['link_to_node'])) {
+      unset($form['link_to_node']);
+    }
+    $form['link_to_terms'] = array(
+      '#title' => t("Link each term to the term's overview page."),
+      '#type' => 'checkbox',
+      '#default_value' => !empty($this->options['link_to_terms']),
+    );
+  }
+  
+  /**
+   * Tell the query object to retrieve this field.
+   */
+  public function query() {
+    parent::query();
+    $this->query->add_field('vid');
+  }
+  
+  /**
+   * Render the taxonomy terms as links.
+   */
+  public function render($values) {
+    $taxonomies = '';
+    
+    if (is_array($values->tid)) {
+      $taxonomies = array();
+      
+      if (!empty($this->options['link_to_terms'])) {
+        while (!empty($values->tid) && !empty($values->taxonomy_names)) {
+          $term = new stdClass();
+          $term->tid = array_shift($values->tid);
+          $term->vid = array_shift($values->vid);
+          $url = taxonomy_term_path($term);
+          $name = array_shift($values->taxonomy_names);
+          
+          $taxonomies[] = l($name, $url, array('absolute' => TRUE));
+        }
+      }
+      else {
+        $taxonomies = $values->taxonomy_name;
+      }
+      
+      $taxonomies = implode(', ', $taxonomies);
+    }
+    
+    return $taxonomies;
+  }
+  
+}
Index: handlers/apachesolr_views_handler_field_type.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field_type.inc
diff -N handlers/apachesolr_views_handler_field_type.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field_type.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,19 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for rendering the node type.
+ */
+class apachesolr_views_handler_field_type
+    extends apachesolr_views_handler_field {
+  
+  /**
+   * Render the field value.
+   */
+  public function render($values) {
+    return $this->render_link(
+        node_get_types('name', $values->{$this->real_field}), $values);
+  }
+  
+}
Index: handlers/apachesolr_views_handler_field.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field.inc
diff -N handlers/apachesolr_views_handler_field.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,59 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Default class for a field of the apachesolr base table. Inherits from the
+ * node default class and overrides methods where necessary.
+ */
+class apachesolr_views_handler_field extends views_handler_field_node {
+  
+  /**
+   * 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->field_alias = $this->real_field;
+    $this->query->add_field($this->real_field);
+  }
+  
+  /**
+   * Render whatever the data is as a link to the node.
+   * 
+   * This is copied from views_handler_field_node, but without the indirection
+   * via the aliases table, since in Apache Solr there are no field aliases.
+   */
+  public function render_link($data, $values) {
+    if (!empty($this->options['link_to_node'])
+        && $data !== NULL && $data !== '') {
+      $this->options['alter']['make_link'] = TRUE;
+      $this->options['alter']['path'] = $values->url;
+    }
+    return $data;
+  }
+  
+  /**
+   * 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->query->add_sort($this->real_field, strtolower($order), TRUE);
+    }
+    else {
+      $this->query->add_sort(
+          $special_sort_fields[$this->real_field], strtolower($order), TRUE);
+    }
+  }
+  
+}
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	1 Jan 1970 00:00:00 -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_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	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,57 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class that allows searching the site with Apache Solr through a view.
+ */
+class apachesolr_views_handler_filter_search extends views_handler_filter_string {
+  
+  /**
+   * Override options to remove case sensitivity option.
+   */
+  public function option_definition() {
+    $options = parent::option_definition();
+    unset($options['case']);
+    return $options;
+  }
+  
+  /**
+   * Override options to remove case sensitivity option.
+   */
+  public function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    unset($form['case']);
+  }
+  
+  /**
+   * Override list of available operators.
+   */
+  public function operators() {
+    $operators = array(
+      'append' => array(
+        'title' => t('Append to query'),
+        'short' => '',
+        'method' => 'query',
+        'values' => 1,
+      ),
+    );
+
+    return $operators;
+  }
+  
+  public function query() {
+    if (!empty($this->value)) {
+      $this->query->add_term($this->value);
+    }
+  }
+  
+  public function admin_summary() {
+    if (!empty($this->options['exposed'])) {
+      return t('exposed');
+    }
+
+    return $this->value;
+  }
+  
+}
Index: handlers/apachesolr_views_handler_field_date.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field_date.inc
diff -N handlers/apachesolr_views_handler_field_date.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field_date.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,89 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for rendering dates.
+ * Largely copied from views_handler_field_date.
+ */
+class apachesolr_views_handler_field_date
+    extends apachesolr_views_handler_field {
+  
+  public function option_definition() {
+    $options = parent::option_definition();
+
+    $options['date_format'] = array('default' => 'small');
+    $options['custom_date_format'] = array('default' => '');
+
+    return $options;
+  }
+  
+  public function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $time = time();
+
+    $form['date_format'] = array(
+      '#type' => 'select',
+      '#title' => t('Date format'),
+      '#options' => array(
+        'small' => format_date($time, 'small'),
+        'medium' => format_date($time, 'medium'),
+        'large' => format_date($time, 'large'),
+        'custom' => t('Custom'),
+        'raw time ago' => t('Time ago'),
+        'time ago' => t('Time ago (with "ago" appended)'),
+      ),
+      '#default_value' =>
+          isset($this->options['date_format'])
+          ? $this->options['date_format']
+          : 'small',
+    );
+    $form['custom_date_format'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Custom date format'),
+      '#description' => t('If "Custom", see <a href="!url" target="_blank">' .
+          'the PHP docs</a> for date formats. If "Time ago" this is the the ' .
+          'number of different units to display, which defaults to two.',
+          array('!url' => 'http://us.php.net/manual/en/function.date.php')),
+      '#default_value' =>
+          isset($this->options['custom_date_format'])
+          ? $this->options['custom_date_format']
+          : '',
+      '#process' => array('views_process_dependency'),
+      '#dependency' =>
+          array('edit-options-date-format' => array('custom', 'time ago')),
+    );
+  }
+
+  public function render($values) {
+    // Apache Solr sends the time as a time string, so first convert it back
+    $value = strtotime($values->{$this->real_field});
+    
+    $format = $this->options['date_format'];
+    if ($format == 'custom' || $format == 'time ago' || $format == 'raw time ago') {
+      $custom_format = $this->options['custom_date_format'];
+    }
+
+    switch ($format) {
+      case 'raw time ago':
+        return $value
+            ? format_interval(time() - $value,
+                is_numeric($custom_format) ? $custom_format : 2)
+            : theme('views_nodate');
+      case 'time ago':
+        return $value
+            ? t('%time ago', array('%time' => format_interval(time() - $value,
+                is_numeric($custom_format) ? $custom_format : 2)))
+            : theme('views_nodate');
+      case 'custom':
+        return $value
+            ? format_date($value, $format, $custom_format)
+            : theme('views_nodate');
+      default:
+        return $value
+            ? format_date($value, $format)
+            : theme('views_nodate');
+    }
+  }
+  
+}
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	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,30 @@
+<?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: 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	1 Jan 1970 00:00:00 -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: 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	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,233 @@
+<?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_taxonomy' => array(
+        'parent' => 'apachesolr_views_handler_field',
+      ),
+      'apachesolr_views_handler_field_type' => array(
+        'parent' => 'apachesolr_views_handler_field',
+      ),
+      'apachesolr_views_handler_filter_author' => array(
+        'parent' => 'views_handler_filter',
+      ),
+      'apachesolr_views_handler_filter_search' => array(
+        'parent' => 'views_handler_filter_string',
+      ),
+      'apachesolr_views_handler_filter_type' => array(
+        'parent' => 'views_handler_filter',
+      ),
+      '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(
+      'handler' => 'apachesolr_views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    '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',
+      'click sortable' => TRUE,
+     ),
+    '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_date',
+      'click sortable' => TRUE,
+    ),
+    '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_date',
+      'click sortable' => TRUE,
+    ),
+    '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_type',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_apachesolr_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_author',
+      'click sortable' => TRUE,
+    ),
+    '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(
+      'handler' => 'apachesolr_views_handler_field_author',
+      'click sortable' => TRUE,
+    ),
+  );
+  $data['apachesolr']['body'] = array(
+    'title' => t('Body'),
+    'help' => t("The node's content."),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field',
+      'click sortable' => FALSE,
+      'element type' => 'div',
+    ),
+  );
+  $data['apachesolr']['comment_count'] = array(
+    'title' => t('Comment count'),
+    'help' => t('The number of comments that were posted to the node.'),
+    'field' => array(
+      'handler' => 'apachesolr_views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'apachesolr_views_handler_sort',
+    ),
+  );
+  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,
+      ),
+    );
+  }
+  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',
+        'click sortable' => TRUE,
+      ),
+      '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',
+    ),
+  );
+  
+  return $data;
+}
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	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,72 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for filtering by type.
+ */
+class apachesolr_views_handler_filter_type 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'),
+      ),
+      '#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 types.'),
+      '#default_value' =>
+          isset($this->options['value'])
+          ? $this->options['value']
+          : '',
+    );
+  }
+
+  public function query() {
+    $not = $this->options['operator'] == 'not one of';
+    $types = array_map('trim', explode(',', $this->options['value']));
+    foreach ($types as $i => $type) {
+      if (empty($type)) {
+        unset($types[$i]);
+      }
+    }
+    if (empty($types)) {
+      if (!$not) {
+        $this->query->add_term('nid:-1');//Add term that will yield no results
+      }
+    }
+    else if (count($types) == 1) {
+      $this->query->add_term(($not ? 'NOT ' : '') . 'type:' . 
+          array_shift($types));
+    }
+    else {
+      $key = 'type:' . array_shift($types);
+      foreach ($types as $type) {
+        $key .= ' OR type:' . $type;
+      }
+      $this->query->add_term($not ? 'NOT (' . $key . ')' : $key);
+    }
+  }
+  
+}
Index: handlers/apachesolr_views_handler_field_author.inc
===================================================================
RCS file: handlers/apachesolr_views_handler_field_author.inc
diff -N handlers/apachesolr_views_handler_field_author.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/apachesolr_views_handler_field_author.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,47 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * Class for rendering the node author.
+ */
+class apachesolr_views_handler_field_author
+    extends apachesolr_views_handler_field {
+  
+  public function option_definition() {
+    $options = parent::option_definition();
+    if (isset($options['link_to_node'])) {
+      unset($options['link_to_node']);
+    }
+    $options['link_to_profile'] = array('default' => TRUE);
+    return $options;
+  }
+
+  /**
+   * Provide link_to_profile option
+   */
+  public function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    if (isset($form['link_to_node'])) {
+      unset($form['link_to_node']);
+    }
+    $form['link_to_profile'] = array(
+      '#title' => t("Link the name to the user's profile."),
+      '#type' => 'checkbox',
+      '#default_value' => !empty($this->options['link_to_profile']),
+    );
+  }
+
+  /**
+   * If the link_to_profile option is set, render the field as a link to the
+   * user's profile.
+   */
+  function render_link($data, $values) {
+    if (!empty($this->options['link_to_profile']) && $data !== NULL && $data !== '') {
+      $this->options['alter']['make_link'] = TRUE;
+      $this->options['alter']['path'] = "user/" . $values->uid;
+    }
+    return $data;
+  }
+  
+}
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	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,354 @@
+<?php
+
+/* $Id$ */
+
+/**
+ * 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 {
+  
+  /** Array storing the keys that will be used for the search. */
+  private $_keys;
+  
+  /** String containing the query that will be handed to Solr. */
+  private $_query;
+  
+  /** Object encapsulating the actual query to Solr. */
+  private $_query_object;
+  
+  /** Array containing the parameters that will be handed to Solr. */
+  private $_params;
+  
+  /** An array of all required fields. */
+  private $_used_fields;
+  
+  /** The object used for communitcating with the solr server. */
+  private $_solr;
+  
+  /** The Solr service class. */
+  private $_service_class;
+  
+  
+  /*
+   * Functions required by the views query class interface
+   */
+  
+  /**
+   * Constructor; Create the basic query object and fill with default values.
+   */
+  public function init($base_table, $base_field) {
+    $this->_keys = array();
+    // Always retrieve these fields:
+    $this->_used_fields = array(
+      'id' => TRUE,
+      'nid' => TRUE,
+      'url' => TRUE,
+    );
+    $this->_params = $this->_get_params();
+    
+    // Copied from apachesolr_get_solr().
+    // TODO Refactor
+    list($module, $filepath, $class) = variable_get('apachesolr_service_class',
+        array('apachesolr', 'Drupal_Apache_Solr_Service.php',
+            'Drupal_Apache_Solr_Service'));
+    include_once(drupal_get_path('module', $module) .'/'. $filepath);
+    $this->_service_class = $class;
+  }
+  
+  /**
+   * Build the query object.
+   */
+  public function build() {
+    if (empty($this->_keys)) {
+      $this->_query = FALSE;
+      return;
+    }
+    else if (count($this->_keys)) {
+      $this->_query = $this->_keys[0];
+    }
+    else {
+      $this->_query = '(' . implode(') (', $this->_keys) . ')';
+    }
+    
+    $this->_query_object = apachesolr_current_query($this->_query, '',
+        $this->get_param('sort'));
+  }
+  
+  /**
+   * Let modules modify the query just prior to finalizing it.
+   */
+  public function alter(&$view) {
+    // TODO Do something here? (apachesolr_modify_query() is called later.)
+  }
+  
+  /**
+   * 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) {
+    $view->result = array();
+    $start_time = views_microtime();
+    $query = $this->_query_object;
+    $params = $this->_params;
+    $solr = $this->_solr;
+    
+    if (empty($query)) {
+      return;
+    }
+    
+    try {
+      // Get only the necessary fields.
+      $params['fl'] = implode(',', array_keys($this->_used_fields));
+      
+      // Get the number of results to retrieve.
+      $params['rows'] = $view->pager['items_per_page'];
+      
+      // Since only now the query is known, we couldn't do this earlier
+      // (i.e., in _get_params())
+      if (variable_get('apachesolr_search_spellcheck', FALSE)) {
+        //Add new parameter to the search request
+        $params['spellcheck.q'] = $this->_query_object->get_query_basic();
+        $params['spellcheck'] = 'true';
+      }
+      
+      // We default to getting snippets from the body.
+      $hl_fl = is_null($params['hl.fl']) ? 'body' : $params['hl.fl'];
+      
+      // This hook allows modules to modify the query and params objects.
+      apachesolr_modify_query($query, $params, 'apachesolr_search');
+      if (!$query) {
+        return array();
+      }
+
+      $response = $solr->search($query->get_query_basic(), $params['start'],
+          $params['rows'], $params);
+      
+      $view->total_rows = $total = $response->response->numFound;
+      $view->pager['current_page'] = $params['start'] / $params['rows'];
+      
+      // 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);
+      pager_query("SELECT %d", $params['rows'], 0, NULL, $total);
+      
+      if ($total > 0) {
+        $results = $response->response->docs;
+
+        // Hook to allow modifications of the retrieved results
+        foreach (module_implements('apachesolr_process_results') as $module) {
+          $function = $module .'_apachesolr_process_results';
+          call_user_func_array($function, array(&$results));
+        }
+        
+        $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;
+  }
+  
+  
+  /*
+   * Functions offered for apachesolr-specific handlers, etc.
+   */
+  
+  /** Adds the specified term to the query. */
+  public function add_term($term, $field = 'text') {
+    if ($field != 'text') {
+      $term = $field . ':' . $term; 
+    }
+    $this->_keys[] = $term;
+  }
+  
+  /**
+   * Add a group of search terms to the query, which will be connected by OR
+   * operators.
+   * 
+   * @param $terms an array where each entry must be either of the form
+   *     array('term' => $term, 'field' => $field);
+   * (the 'field' entry can be omitted, in which case 'text' is assumed), or
+   *     $term
+   */
+  public function add_or_group($terms) {
+    if (count($terms) == 0 || !is_array($terms)) {
+      return;
+    }
+    $keys = array();
+    foreach ($terms as $term) {
+      if (is_array($term)) {
+        if (empty($term['field']) || $term['field'] == 'text') {
+          $keys[] = $term['term'];
+        }
+        else {
+          $keys[] = $term['field'] . ':' . $term['term'];
+        }
+      }
+      else {
+        $keys[] = $term;
+      }
+    }
+    $this->_keys[] = '(' . implode(' OR ', $keys) . ')';
+  }
+  
+  /** Sets the specified Solr search parameter to the specified value. */
+  public function set_param($param, $value) {
+    $this->_params[$param] = $value;
+  }
+  
+  /** Get the specified Solr search parameter. */
+  public function get_param($param) {
+    return isset($this->_params[$param]) ? $this->_params[$param] : '';
+  }
+  
+  /**
+   * Adds the specified parameters to the Solr search parameters, overwriting
+   * old values where necessary. Parameters must be specified as
+   * $param => $value in the array.
+   */
+  public function set_params($params) {
+    $this->_params = $params + $this->_params;
+  }
+  
+  /** Add a field to retrieve. */
+  public function add_field($field) {
+    $this->_used_fields[$field] = TRUE;
+  }
+  
+  /**
+   * 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 . ' ' . $order;
+    if (empty($this->_params['sort']) || $single) {
+      $this->_params['sort'] = $sort;
+    }
+    else {
+      $this->_params['sort'] .= ',' . $sort;
+    }
+  }
+  
+  
+  /*
+   * (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 Apache_Solr_Service::phrase($term);
+    }
+    return Apache_Solr_Service::escape($term);
+  }
+  
+  /**
+   * Returns the parameters that will be handed to Solr along with the query.
+   */
+  private function _get_params() {
+    // Whole method copied from
+    //   apachesolr_search.module:101-209: apachesolr_search_search()
+    // TODO Refactor, so both this class and apachesolr_search_search() call new
+    //      function, ~apachesolr_get_params().
+    //      (If apachesolr_search_get_params() is used, add apachesolr_search to
+    //      dependencies in .info.)
+    $params = array(
+      'fl' => 'id,nid,title,comment_count,type,created,changed,url,uid,name',
+      'facet' => 'true',
+      'facet.mincount' => 1,
+      'facet.sort' => 'true',
+      'hl' => 'false',
+    );
+
+    $facet_query_limits = variable_get('apachesolr_facet_query_limits', array());
+    // Request all enabled facets.
+    foreach (apachesolr_get_enabled_facets() as $module => $module_facets) {
+      foreach($module_facets as $delta => $facet_field) {
+        $params['facet.field'][] = $facet_field;
+        // Facet limits
+        if (isset($facet_query_limits[$module][$delta])) {
+          $params['f.' . $facet_field . '.facet.limit'] = $facet_query_limits[$module][$delta];
+        }
+      }
+    }
+    if (!empty($params['facet.field'])) {
+      // Add a default limit for fields where no limit was set.
+      $params['facet.limit'] = variable_get('apachesolr_facet_query_limit_default', 20);
+    }
+
+    $page = isset($_GET['page']) ? $_GET['page'] : 0;
+    $params['start'] = $page * $params['rows'];
+    // This is the object that does the communication with the solr server.
+    $solr = $this->_solr = apachesolr_get_solr();
+
+    // Note - we have query fields set in solrconfig.xml, which will operate when
+    // none are set.
+    $qf = variable_get('apachesolr_search_query_fields', array());
+    $fields = $solr->getFields();
+    if ($qf && $fields) {
+      foreach ($fields as $field_name => $field) {
+        if (!empty($qf[$field_name])) {
+          if ($field_name == 'body') {
+            // Body is the only normed field.
+            $qf[$field_name] *= 40.0;
+          }
+          $params['qf'][] = $field_name . '^'. $qf[$field_name];
+        }
+      }
+    }
+    // Note: we use 2 since 1 fails on Ubuntu Hardy.
+    $data = $solr->getLuke(2);
+    if (isset($data->index->numDocs)) {
+      $total = $data->index->numDocs;
+    }
+    else {
+      $total = db_result(db_query("SELECT COUNT(nid) FROM {node}"));
+    }
+    $date_settings = variable_get('apachesolr_search_date_boost', '4:200.0');
+    list($date_steepness, $date_boost) = explode(':', $date_settings);
+    // Default 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).
+    if ($date_boost) {
+      $params['bf'][] = "recip(rord(created),$date_steepness,$total,$total)^$date_boost";
+    }
+    $comment_settings = variable_get('apachesolr_search_comment_boost', '4:200.0');
+    list($comment_steepness, $comment_boost) = explode(':', $comment_settings);
+    // Default 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).
+    if ($comment_boost) {
+      $params['bf'][] = "recip(rord(comment_count),$comment_steepness,$total,$total)^$comment_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) {
+        $params['bq'][] = "type:$type^$boost";
+      }
+    }
+    
+    return $params;
+  }
+    
+}
