=== added file 'modules/search.views.inc'
--- modules/search.views.inc	1970-01-01 00:00:00 +0000
+++ modules/search.views.inc	2008-04-03 15:24:23 +0000
@@ -0,0 +1,326 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Provide views data and handlers for search.module
+ */
+
+/**
+ * @defgroup views_node_module node.module handlers
+ *
+ * Includes the tables 'search_index'
+ * @{
+ */
+
+/**
+ * Implementation of hook_views_data()
+ */
+function search_views_data() {
+  // Basic table information.
+
+  // Define the base group of this table. Fields that don't
+  // have a group defined will go into this field by default.
+  $data['search_index']['table']['group']  = t('Search');
+
+  // For other base tables, explain how we join
+  $data['search_index']['table']['join'] = array(
+    'node' => array(
+      'left_field' => 'nid',
+      'field' => 'sid',
+    ),
+    'users' => array(
+      'left_field' => 'uid',
+      'field' => 'sid',
+    ),
+  );
+
+  $data['search_total']['table']['join'] = array(
+    'node' => array(
+      'left_table' => 'search_index',
+      'left_field' => 'word',
+      'field' => 'word',
+    ),
+    'users' => array(
+      'left_table' => 'search_index',
+      'left_field' => 'word',
+      'field' => 'word',
+    )
+  );
+
+  $data['search_dataset']['table']['join'] = array(
+    'node' => array(
+      'left_table' => 'search_index',
+      'left_field' => 'sid',
+      'field' => 'sid',
+    ),
+    'users' => array(
+      'left_table' => 'search_index',
+      'left_field' => 'sid',
+      'field' => 'sid',
+    ),
+  );
+
+  // ----------------------------------------------------------------
+  // Fields
+
+  // score
+  $data['search_index']['score'] = array(
+    'title' => t('Score'), // The item it appears as on the UI,
+    'help' => t('The score of the search item.'), // The help that appears on the UI,
+     // Information for displaying a title as a field
+    'field' => array(
+      'field' => 'score', // the real field
+      'group' => t('Search'), // The group it appears in on the UI,
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    // Information for sorting on a search score.
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // search node links
+  $data['search_node_links']['from'] = array(
+    'title' => t('Links From'),
+    'help' => t('Nodes that link from the node'),
+    'argument' => array(
+      'handler' => 'views_handler_argument_search_node_links_from',
+    ),
+  );
+  $data['search_node_links']['to'] = array(
+    'title' => t('Links To'),
+    'help' => t('Nodes that link to the node'),
+    'argument' => array(
+      'handler' => 'views_handler_argument_search_node_links_to',
+    ),
+  );
+
+  // search filter
+  $data['search_index']['keys'] = array(
+    'title' => t('Search Terms'), // The item it appears as on the UI,
+    'help' => t('The terms to search for.'), // The help that appears on the UI,
+    // Information for searching terms using the full search syntax
+    'filter' => array(
+      'handler' => 'views_handler_filter_search',
+    ),
+  );
+
+  return $data;
+}
+
+/**
+ * Field handler to provide simple renderer that allows linking to a node.
+ *
+ * @ingroup views_field_handlers
+ */
+class views_handler_filter_search extends views_handler_filter {
+  /**
+   * Provide basic defaults for the equality operator
+   */
+  function options(&$options) {
+    parent::options($options);
+    $options['operator'] = 'optional';
+    $options['value'] = '';
+  }
+
+  /**
+   * Provide simple equality operator
+   */
+  function operator_form(&$form, &$form_state) {
+    $form['operator'] = array(
+      '#type' => 'radios',
+      '#title' => t('On empty input'),
+      '#default_value' => $this->operator,
+      '#options' => array(
+        'optional' => t('Show All'),
+        'required' => t('Show None'),
+      ),
+    );
+  }
+
+  /**
+   * Provide a simple textfield for equality
+   */
+  function exposed_form(&$form, &$form_state) {
+    $key = $this->options['expose']['identifier'];
+    $form[$key] = array(
+      '#type' => 'textfield',
+      '#title' => t('Search'),
+      '#size' => 15,
+      '#default_value' => $this->value,
+      '#attributes' => array('title' => t('Enter the terms you wish to search for.')),
+    );
+  }
+
+  /**
+   * Validate the options form.
+   */
+  function exposed_validate($form, &$form_state) {
+    $key = $this->options['expose']['identifier'];
+    if (!empty($form_state['values'][$key])) {
+      $this->search_query = search_parse_query($form_state['values'][$key]);
+      $this->search_query[0] = str_replace('d.', 'search_data.', $this->search_query[0]);
+      $this->search_query[2] = str_replace('i.', 'search_index.', $this->search_query[2]);
+
+      if ($this->search_query[2] == '') {
+        form_set_error($key, t('You must include at least one positive keyword with @count characters or more.', array('@count' => variable_get('minimum_word_size', 3))));
+      }
+      if ($this->search_query[6]) {
+        if ($this->search_query[6] == 'or') {
+          drupal_set_message(t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'));
+        }
+      }
+    }
+  }
+
+  /**
+   * Add this filter to the query.
+   *
+   * Due to the nature of fapi, the value and the operator have an unintended
+   * level of indirection. You will find them in $this->operator
+   * and $this->value respectively.
+   */
+  function query() {
+    $this->ensure_my_table();
+    if (!isset($this->search_query)) {
+      if ($this->operator == 'required') {
+        $this->query->add_where($this->options['group'], '0');
+      }
+    }
+    else {
+      $join = new views_join;
+      $join->construct('search_total', 'search_index', 'word', 'word');
+      $this->query->add_relationship(NULL, $join, 'search_index');
+#      $this->query->add_table('search_total', 'search_index', $join);
+      $this->query->add_field('', "SUM(search_index.score * search_total.count)", 'score');
+      $this->query->add_where($this->options['group'], $this->search_query[2], $this->search_query[3]);
+      $this->query->add_where($this->options['group'], "search_index.type = '%s'", $this->view->base_table);
+      if (!$this->search_query[5]) {
+        $join = new views_join_search_dataset;
+        $join->construct();
+        $this->query->add_relationship(NULL, $join, 'search_index');
+        $this->query->add_table('search_dataset', 'search_index', $join);
+        $this->query->add_where($this->options['group'], $this->search_query[0], $this->search_query[1]);
+      }
+      // @note: i don't think that this needed since we restrict based on type
+//    $this->query->add_groupby('node.type'); // isn't this the same as search_index.type?
+      $this->query->add_groupby("search_index.sid");
+      $this->query->add_having($this->options['group'], 'COUNT(*) >= %d', $this->search_query[4]);
+      // @todo: normalize the score
+    }
+  }
+}
+
+class views_join_search_dataset extends views_join {
+  function construct() {
+    parent::construct('search_dataset', 'sid', array(), 'LEFT');
+  }
+
+  function join($table, &$query) {
+    $output = parent::join($table, $query);
+    $output .= " AND table[alias].type = search_dataset.type";
+    return $output;
+  }
+}
+
+/**
+ * @}
+ */
+
+/**
+ * Implementation of hook_views_plugins
+ */
+function search_views_plugins() {
+  return array(
+    'module' => 'views', // This just tells our themes are elsewhere.
+    'row' => array(
+      'search' => array(
+        'title' => t('Search'),
+        'help' => t('Display the results with standard search view.'),
+        'handler' => 'views_plugin_row_search_view',
+        'theme' => 'views_view_row_search',
+        'base' => array('node'), // only works with 'node' as base.
+      ),
+    ),
+  );
+}
+
+/**
+ * Plugin which performs a node_view on the resulting object.
+ *
+ * @ingroup views_row_plugins
+ */
+class views_plugin_row_search_view extends views_plugin_row {
+  function options($display) {
+    return array(
+      'score' => TRUE,
+    );
+  }
+
+  function options_form(&$form, &$form_state) {
+    $form['score'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Display score'),
+      '#default_value' => $this->options['score'],
+    );
+  }
+
+  /**
+   * Override the behavior of the render() function.
+   */
+  function render($row) {
+    return theme(array('views_view_row_search__' . $this->view->name, 'views_view_row_search'), $this->view, $this->options, $row);
+  }
+}
+
+/**
+ * Template helper for theme_views_view_row_search
+ */
+function template_preprocess_views_view_row_search(&$vars) {
+  $vars['node'] = ''; // make sure var is defined.
+  $nid = $vars['row']->nid;
+  if (!is_numeric($nid)) {
+    return;
+  }
+
+  $node = node_load($nid);
+
+  if (empty($node)) {
+    return;
+  }
+
+  // Build the node body.
+  $node = node_build_content($node, FALSE, FALSE);
+  $node->body = drupal_render($node->content);
+
+  // Fetch comments for snippet
+  $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index');
+
+  // Fetch terms for snippet
+  $node->body .= module_invoke('taxonomy', 'nodeapi', $node, 'update index');
+
+  $vars['url'] = url('node/'. $nid);
+  $vars['title'] = check_plain($node->title);
+
+  $info = array();
+  $info['type'] = node_get_types('name', $node);
+  $info['user'] = theme('username', $node);
+  $info['date'] = format_date($node->changed, 'small');
+  $extra = node_invoke_nodeapi($node, 'search result');
+  if (isset($extra) && is_array($extra)) {
+    $info = array_merge($info, $extra);
+  }
+  $vars['info_split'] = $info;
+  $vars['info'] = implode(' - ', $info);
+
+  $vars['node'] = $node;
+  // @todo: get score from ???
+//$vars['score'] = $item->score;
+  $vars['snippet'] = search_excerpt($vars['view']->value, $node->body);
+}
+
+/**
+ * @}
+ */
+

