Search functions moved from mod/name/lib.php to search/documents/mod_document.php.
authormchampan <mchampan>
Tue, 11 Jul 2006 17:15:22 +0000 (17:15 +0000)
committermchampan <mchampan>
Tue, 11 Jul 2006 17:15:22 +0000 (17:15 +0000)
Pagination added to query page. Various small changes and amendments.

14 files changed:
blocks/search/block_search.php
mod/wiki/lib.php
search/README.txt
search/db/mysql.sql
search/db/postgres7.sql
search/documents/document.php
search/documents/forum_document.php [new file with mode: 0644]
search/documents/wiki_document.php
search/indexer.php
search/indexersplash.php
search/lib.php
search/query.php
search/stats.php
search/tests/index.php [new file with mode: 0644]

index 3cafd10..c370ca2 100644 (file)
@@ -6,7 +6,7 @@
      this block, the broadest possible selection of documents is searched.
      
      Author:  Michael Champanis (mchampan)
-     Date:    2006 06 23
+     Date:    2006 06 25
   
      Todo: make strings -> get_string()  
   */
@@ -15,7 +15,7 @@
     
     function init() {
       $this->title = "Global Search"; //get_string()
-      $this->version = 20060625;
+      $this->version = 2006062500;
     } //init  
     
     // only one instance of this block is required
@@ -46,7 +46,7 @@
         
         //basic search form
         $this->content->text =
-            '<form name="query" method="post" action="search/query.php">'
+            '<form name="query" method="get" action="search/query.php">'
           . "<label for=''>$label</label>"
           . '<input type="text" name="query_string" length="50" value=""/>'
           . '<input type="submit" value="'.$button.'"/>'
index 6f99d25..54fe22d 100644 (file)
@@ -352,112 +352,6 @@ function wiki_get_entries(&$wiki, $byindex=NULL) {
     }
 }
 
-
-/*==== Global search modifications
- * Author: Michael Champanis (mchampan)
- * Last date: 2006 06 25
- * These modifications allow wiki documents to be indexed in the new
- * search engine module - they are probably not final, and as such
- * shouldn't be used by other stuff for the time being
- **/
-
-//rescued and converted from ewikimoodlelib.php
-//retrieves latest version of a page
-function wiki_get_latest_page(&$entry, $pagename, $version=0) { 
-  $pagename = "'".addslashes($pagename)."'";
-  
-  if ($version > 0 and is_int($version)) {
-    $version = "AND (version=$version)";
-  } else {
-    $version = '';
-  } //else
-
-  $select = "(pagename=$pagename) AND wiki=".$entry->id." $version ";
-  $sort   = 'version DESC';
-  
-  //change this to recordset_select, as per http://docs.moodle.org/en/Datalib_Notes
-  if ($result_arr = get_records_select('wiki_pages', $select, $sort, '*', 0, 1)) {    
-    foreach ($result_arr as $obj) {
-      $result_obj = $obj;                 
-    } //foreach
-  } //if
-    
-  if (isset($result_obj))  {
-    $result_obj->meta = @unserialize($result_obj->meta);
-    return $result_obj;
-  } else {
-    return false;
-  } //else
-} //wiki_get_latest_page
-
-//fetches all pages, including old versions
-function wiki_get_pages(&$entry) {   
-  return get_records('wiki_pages', 'wiki', $entry->id);
-} //wiki_get_pages
-
-//fetches all the latest versions of all the pages
-function wiki_get_latest_pages(&$entry) {
-  //== (My)SQL for this
-  /* select * from wiki_pages
-     inner join
-    (select wiki_pages.pagename, max(wiki_pages.version) as ver
-    from wiki_pages group by pagename) as a
-    on ((wiki_pages.version = a.ver) and
-    (wiki_pages.pagename like a.pagename)) */
-  
-  $pages = array();    
-  
-  //http://moodle.org/bugs/bug.php?op=show&bugid=5877&pos=0
-  //if ($ids = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) { 
-  if ($rs = get_recordset('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
-    $ids = $rs->GetRows();
-  //--    
-    foreach ($ids as $id) {      
-      $pages[] = wiki_get_latest_page($entry, $id[0]);
-    } //foreach
-  } else {
-    return false;
-  } //else  
-    
-  return $pages;   
-} //wiki_get_latest_pages
-
-function wiki_iterator() {
-  return get_all_instances_in_courses("wiki", get_courses());  
-} //wiki_search_index
-
-function wiki_get_content_for_index(&$wiki) {
-  $documents = array();
-  
-  $entries = wiki_get_entries($wiki);    
-  foreach($entries as $entry) {
-    //all pages
-    //$pages = wiki_get_pages($entry);
-    
-    //latest pages
-    $pages = wiki_get_latest_pages($entry);
-    $i = 0;
-    
-    if (is_array($pages)) {
-      foreach($pages as $page) {
-        if (strlen($page->content) > 0) {
-          $i++;
-          $documents[] = new WikiSearchDocument($page, $entry->wikiid, $entry->course, $entry->groupid);
-        } //if
-      } //foreach
-      
-      //print "$entry->id : $i"; print "<br>";
-    } else {
-      print $pages;
-    } //else
-  } //foreach
-  
-  return $documents;
-} //wiki_get_content_for_index
-
-/*==== Global search modifications end */
-
-
 function wiki_get_default_entry(&$wiki, &$course, $userid=0, $groupid=0) {
 /// Returns the wiki entry according to the wiki type.
 /// Optionally, will return wiki entry for $userid student wiki, or
index f473073..e039bc9 100644 (file)
@@ -1,4 +1,38 @@
-latest
+2006/07/11
+----------
+(Warning: It took me 1900 seconds to index the forum, go make coffee
+whilst you wait.)
+
+Forum search functions changed to use 'get_recordset' instead of
+'get_records', for speed reasons. This provides a significant improvement,
+but indexing is still slow - getting data from the database and Zend's
+tokenising _seem_ to be the prime suspects at the moment.
+
+/search/tests/ added - index.php can be used to see which modules are
+ready to be included in the search index, and it informs you of any
+errors - should be a prerequisite for indexing.
+
+Search result pagination added to query.php, will default to 20 until
+an admin page for the search module is written.
+
+2006/07/07
+----------
+Search-enabling functions moved out've the mod's lib.php files and into
+/search/documents/mod_document.php - this requires the search module to
+operate without requiring modification of lib files.
+
+SearchDocument base class improved, and the way module documents extend
+it. A custom-data field has been added to allow modules to add any custom
+data they wish to be stored in the index - this field is serialised into
+the index as a binary field.
+
+Database field 'type' renamed to 'doctype' to match the renaming in the
+index, 'type' seems to be a reserved word in Lucene. Several index field
+names change to be more descriptive (cid -> course_id). URLs are now
+stored in the index, and don't have to be generated on the fly during
+display of query results.
+
+2006/07/05
 ------
 Started cleaning and standardising things.
 
index 1efb7e4..2507a49 100644 (file)
@@ -1,6 +1,6 @@
 CREATE TABLE IF NOT EXISTS `prefix_search_documents` (
   `id` int(11) NOT NULL auto_increment,
-  `type` varchar(12) NOT NULL default 'none',
+  `doctype` varchar(12) NOT NULL default 'none',
   `title` varchar(100) NOT NULL default '',
   `url` varchar(100) NOT NULL default '',
   `updated` timestamp NOT NULL default CURRENT_TIMESTAMP,
index 52d1be4..3903a65 100644 (file)
@@ -1,6 +1,6 @@
 CREATE TABLE prefix_search_documents (
    id SERIAL8 PRIMARY KEY,
-   "type" varchar(12) NOT NULL DEFAULT 'none', 
+   doctype varchar(12) NOT NULL DEFAULT 'none', 
    title varchar(100) NOT NULL default '', 
    url varchar(100) NOT NULL default '', 
    updated timestamp NOT NULL DEFAULT NOW(), 
index c09ec37..83762aa 100644 (file)
@@ -4,10 +4,18 @@
    * */
   
   class SearchDocument extends Zend_Search_Lucene_Document {  
-    public function __construct($document_type, $cid, $gid) {
-      $this->addField(Zend_Search_Lucene_Field::Keyword('type', $document_type));
-      $this->addField(Zend_Search_Lucene_Field::Keyword('courseid', $cid));    
-      $this->addField(Zend_Search_Lucene_Field::Keyword('groupid', $gid));      
+    public function __construct(&$doc, &$data, $document_type, $course_id, $group_id) {      
+      $this->addField(Zend_Search_Lucene_Field::Keyword('id', $doc->id));
+      $this->addField(Zend_Search_Lucene_Field::Text('title', $doc->title));
+      $this->addField(Zend_Search_Lucene_Field::Text('author', $doc->author));
+      $this->addField(Zend_Search_Lucene_Field::UnStored('contents', $doc->contents));
+      $this->addField(Zend_Search_Lucene_Field::UnIndexed('url', $doc->url));
+      
+      $this->addField(Zend_Search_Lucene_Field::Binary('data', serialize($data)));
+      
+      $this->addField(Zend_Search_Lucene_Field::Keyword('doctype', $document_type));
+      $this->addField(Zend_Search_Lucene_Field::Keyword('course_id', $course_id));    
+      $this->addField(Zend_Search_Lucene_Field::Keyword('group_id', $group_id));      
     } //constructor    
   } //SearchDocument
     
diff --git a/search/documents/forum_document.php b/search/documents/forum_document.php
new file mode 100644 (file)
index 0000000..c1b81d7
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+
+  /* see wiki_document.php for descriptions */
+
+  require_once("$CFG->dirroot/search/documents/document.php");
+  require_once("$CFG->dirroot/mod/forum/lib.php");
+  
+  class ForumSearchDocument extends SearchDocument {  
+    public function __construct(&$post, $forum_id, $course_id, $group_id) {
+      // generic information
+      /*$doc->id        = $post->id;
+      $doc->title     = $post->subject;
+      $doc->author    = $post->firstname." ".$post->lastname;
+      $doc->contents  = $post->message;*/
+      
+      $doc->id        = $post['id'];
+      $doc->title     = $post['subject'];
+      $doc->author    = $post['firstname']." ".$post['lastname'];
+      $doc->contents  = $post['message'];
+      
+      $doc->url       = forum_make_link($post['discussion'], $post['id']);      
+      
+      // module specific information
+      $data->forum      = $forum_id;
+      $data->discussion = $post['discussion'];
+      
+      parent::__construct($doc, $data, SEARCH_FORUM_TYPE, $course_id, $group_id);
+    } //constructor    
+  } //ForumSearchDocument
+  
+  function forum_make_link($discussion_id, $post_id) {
+    global $CFG;
+    return $CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion_id.'#'.$post_id;
+  } //forum_make_link
+  
+  function forum_iterator() {
+      //no @ = Undefined index:  82 in /home/michael/public_html/moodle/lib/datalib.php on line 2671    
+      return @get_all_instances_in_courses("forum", get_courses());
+  } //forum_iterator
+  
+  function forum_get_content_for_index(&$forum) {
+      $documents = array();  
+      if (!$forum) return $documents;
+    
+      $posts = forum_get_discussions_fast($forum->id);     
+      if (!$posts) return $documents;
+      
+      while (!$posts->EOF) {
+        $post = $posts->fields;
+        
+        if (is_array($post)) {
+          if (strlen($post['message']) > 0 && ($post['deleted'] != 1)) {
+            $documents[] = new ForumSearchDocument($post, $forum->id, $forum->course, $post['groupid']);
+          } //if
+            
+          if ($children = forum_get_child_posts_fast($post['id'], $forum->id)) {
+            while (!$children->EOF) {
+              $child = $children->fields;
+
+              if (strlen($child['message']) > 0 && ($child['deleted'] != 1)) {
+                $documents[] = new ForumSearchDocument($child, $forum->id, $forum->course, $post['groupid']);
+              } //if
+
+              $children->MoveNext();
+            } //foreach
+          } //if
+        } //if
+        
+        $posts->MoveNext();
+      } //foreach
+            
+      return $documents;
+  } //forum_get_content_for_index
+  
+  //old slower version
+  function forum_get_content_for_index_old(&$forum) {
+    $documents = array();  
+    if (!$forum) return $documents;
+  
+    $posts = forum_get_discussions($forum->id);      
+    if (!$posts) return $documents;
+                
+    foreach($posts as $post) {
+      if (is_object($post)) {
+        if (strlen($post->message) > 0 && ($post->deleted != 1)) {
+          $documents[] = new ForumSearchDocument($post, $forum->id, $forum->course, $post->groupid);
+        } //if
+          
+        if ($children = forum_get_child_posts($post->id, $forum->id)) {
+          foreach ($children as $child) {
+            if (strlen($child->message) > 0 && ($child->deleted != 1)) {
+              $documents[] = new ForumSearchDocument($child, $forum->id, $forum->course, $post->groupid);
+            } //if
+          } //foreach
+        } //if
+      } //if                
+    } //foreach      
+    
+    return $documents;
+  } //forum_get_content_for_index_old
+  
+  //reworked faster version from /mod/forum/lib.php
+  function forum_get_discussions_fast($forum) {
+    global $CFG, $USER;
+    
+    $timelimit='';
+  
+    if (!empty($CFG->forum_enabletimedposts)) {
+      if (!((isadmin() and !empty($CFG->admineditalways)) || isteacher(get_field('forum', 'course', 'id', $forum)))) {
+        $now = time();
+        $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
+        if (!empty($USER->id)) {
+          $timelimit .= " OR d.userid = '$USER->id'";
+        }
+        $timelimit .= ')';
+      }
+    }
+  
+    if ($CFG->dbtype == 'postgres7') {
+        return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message,
+                                  p.deleted, d.groupid, u.firstname, u.lastname 
+                              FROM {$CFG->prefix}forum_discussions d
+                              JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
+                              JOIN {$CFG->prefix}user u ON p.userid = u.id
+                             WHERE d.forum = '$forum'
+                               AND p.parent = 0
+                                   $timelimit
+                          ORDER BY d.timemodified DESC");
+    } else {
+        return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message, p.deleted,
+                                  d.groupid, u.firstname, u.lastname
+                              FROM ({$CFG->prefix}forum_posts p,
+                                   {$CFG->prefix}user u,
+                                   {$CFG->prefix}forum_discussions d)
+                             WHERE d.forum = '$forum'
+                               AND p.discussion = d.id
+                               AND p.parent = 0
+                               AND p.userid = u.id $timelimit
+                          ORDER BY d.timemodified DESC");
+    } //else
+  } //forum_get_discussions_fast
+  
+  //reworked faster version from /mod/forum/lib.php
+  function forum_get_child_posts_fast($parent, $forumid) {
+    global $CFG;
+  
+    return get_recordset_sql("SELECT p.id, p.subject, p.discussion, p.message, p.deleted,
+                              $forumid AS forum, u.firstname, u.lastname
+                              FROM {$CFG->prefix}forum_posts p
+                         LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
+                             WHERE p.parent = '$parent'
+                          ORDER BY p.created ASC");
+  } //forum_get_child_posts_fast
+  
+?>
\ No newline at end of file
index c70fa5c..24ed20d 100644 (file)
 <?php
+  /* Wiki Search Document class and functions
+   * This file contains the mapping between a wiki page and it's indexable counterpart,
+   * e.g. searchdocument->title = wikipage->pagename
+   *
+   * Functions for iterating and retrieving the necessary records are now also included
+   * in this file, rather than mod/wiki/lib.php
+   * */
 
   require_once("$CFG->dirroot/search/documents/document.php");
+  require_once("$CFG->dirroot/mod/wiki/lib.php");
   
+  /* All the $doc->___ fields are required by the base document class!
+   * Each and every module that requires search functionality must correctly
+   * map their internal fields to the five $doc fields (id, title, author, contents
+   * and url). Any module specific data can be added to the $data object, which is
+   * serialised into a binary field in the index.
+   * */  
   class WikiSearchDocument extends SearchDocument {  
-    public function __construct(&$page, $wiki_id, $cid, $gid) {
-      $this->addField(Zend_Search_Lucene_Field::Text('title', $page->pagename));
-      $this->addField(Zend_Search_Lucene_Field::Text('author', $page->author));
-      $this->addField(Zend_Search_Lucene_Field::UnStored('contents', $page->content));
+    public function __construct(&$page, $wiki_id, $course_id, $group_id) {
+      // generic information; required
+      $doc->id        = $page->id;
+      $doc->title     = $page->pagename;
+      $doc->author    = $page->author;
+      $doc->contents  = $page->content;
+      $doc->url       = wiki_make_link($wiki_id, $page->pagename, $page->version);      
       
-      $this->addField(Zend_Search_Lucene_Field::Keyword('id', $page->id));
-      $this->addField(Zend_Search_Lucene_Field::Keyword('version', $page->version));
-      $this->addField(Zend_Search_Lucene_Field::Keyword('wiki', $wiki_id));
+      // module specific information; optional
+      $data->version  = $page->version;
+      $data->wiki     = $wiki_id;
       
-      parent::__construct(SEARCH_WIKI_TYPE, $cid, $gid);
+      // construct the parent class
+      parent::__construct($doc, $data, SEARCH_WIKI_TYPE, $course_id, $group_id);
     } //constructor    
   } //WikiSearchDocument
   
   function wiki_name_convert($str) {
     return str_replace(' ', '+', $str);
   } //wiki_name_convert
-  
-  function wiki_make_link(&$doc) {
+
+  function wiki_make_link($wiki_id, $title, $version) {
     global $CFG;    
-    return $CFG->wwwroot.'/mod/wiki/view.php?wid='.$doc->wiki.'&page='.wiki_name_convert($doc->title).'&version='.$doc->version;
+    return $CFG->wwwroot.'/mod/wiki/view.php?wid='.$wiki_id.'&page='.wiki_name_convert($title).'&version='.$version;
   } //wiki_make_link
   
+  //rescued and converted from ewikimoodlelib.php
+  //retrieves latest version of a page
+  function wiki_get_latest_page(&$entry, $pagename, $version=0) { 
+    $pagename = "'".addslashes($pagename)."'";
+    
+    if ($version > 0 and is_int($version)) {
+      $version = "AND (version=$version)";
+    } else {
+      $version = '';
+    } //else
+  
+    $select = "(pagename=$pagename) AND wiki=".$entry->id." $version ";
+    $sort   = 'version DESC';
+    
+    //change this to recordset_select, as per http://docs.moodle.org/en/Datalib_Notes
+    if ($result_arr = get_records_select('wiki_pages', $select, $sort, '*', 0, 1)) {    
+      foreach ($result_arr as $obj) {
+        $result_obj = $obj;                 
+      } //foreach
+    } //if
+      
+    if (isset($result_obj))  {
+      $result_obj->meta = @unserialize($result_obj->meta);
+      return $result_obj;
+    } else {
+      return false;
+    } //else
+  } //wiki_get_latest_page
+  
+  //fetches all pages, including old versions
+  function wiki_get_pages(&$entry) {   
+    return get_records('wiki_pages', 'wiki', $entry->id);
+  } //wiki_get_pages
+  
+  //fetches all the latest versions of all the pages
+  function wiki_get_latest_pages(&$entry) {
+    //== (My)SQL for this
+    /* select * from wiki_pages
+       inner join
+      (select wiki_pages.pagename, max(wiki_pages.version) as ver
+      from wiki_pages group by pagename) as a
+      on ((wiki_pages.version = a.ver) and
+      (wiki_pages.pagename like a.pagename)) */
+    
+    $pages = array();    
+    
+    //http://moodle.org/bugs/bug.php?op=show&bugid=5877&pos=0
+    //if ($ids = get_records('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) { 
+    if ($rs = get_recordset('wiki_pages', 'wiki', $entry->id, '', 'distinct pagename')) {
+      $ids = $rs->GetRows();
+    //--    
+      foreach ($ids as $id) {      
+        $pages[] = wiki_get_latest_page($entry, $id[0]);
+      } //foreach
+    } else {
+      return false;
+    } //else  
+      
+    return $pages;   
+  } //wiki_get_latest_pages
+  
+  function wiki_iterator() {
+    return get_all_instances_in_courses("wiki", get_courses());  
+  } //wiki_iterator
+  
+  function wiki_get_content_for_index(&$wiki) {
+    $documents = array();
+    
+    $entries = wiki_get_entries($wiki);    
+    foreach($entries as $entry) {
+      //all pages
+      //$pages = wiki_get_pages($entry);
+      
+      //latest pages
+      $pages = wiki_get_latest_pages($entry);
+      
+      if (is_array($pages)) {
+        foreach($pages as $page) {
+          if (strlen($page->content) > 0) {
+            $documents[] = new WikiSearchDocument($page, $entry->wikiid, $entry->course, $entry->groupid);
+          } //if
+        } //foreach
+      } //if
+    } //foreach
+    
+    return $documents;
+  } //wiki_get_content_for_index 
+  
 ?>
\ No newline at end of file
index 5fda789..a56302e 100644 (file)
@@ -1,12 +1,17 @@
 <?php
   /* The indexer logic -
-   * Look through each installed module's lib file for necessary search functions,
-   * and if they're present (and the module search document class file), add the
-   * content to the index. Repeat this for blocks.
+   * Look through each installed module's search document class file (/search/documents)
+   * for necessary search functions, and if they're present add the content to the index.
+   * Repeat this for blocks.
+   *
+   * Because the iterator/retrieval functions are now stored in /search/documents/mod_document.php,
+   * /mod/mod/lib.php doesn't have to be modified - and thus the search module becomes quite
+   * self-sufficient. URL's are now stored in the index, stopping us from needing to require
+   * the class files to generate a results page.
    *
    * Along with the index data, each document's summary gets stored in the database
    * and synchronised to the index (flat file) via the primary key ('id') which is mapped
-   * to the 'dbid' field in the index
+   * to the 'db_id' field in the index
    * */
 
   //this'll take some time, set up the environment
   //php5 found, continue including php5-only files
   require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
   
-  //begin timer
-  search_stopwatch();    
+  if (get_config("search_indexer_busy") == 1) {
+  } //if  
+  
+  //turn on busy flag
+  set_config("search_indexer_busy", 1);
   mtrace('<pre>Server Time: '.date('r',time())."\n");
   
   //paths
   } else {
     mtrace("Using $index_path as data directory.");
   } //else
-
+  
   $index = new Zend_Search_Lucene($index_path, true);
   
   //create the database tables
   $tables = $db->MetaTables();
     
   if (in_array($CFG->prefix.'search_documents', $tables)) {
-    delete_records('search_documents');
+    //delete_records('search_documents');    
+    //temporary measure - db doesn't have update scripts and I realised that cvs 1.1 db
+    //is incompatible with cvs 1.2! Must fix ASAP.    
+    execute_sql('drop table '.$CFG->prefix.'search_documents', false);
+    
+    ob_start(); //turn output buffering on - to hide modify_database() output
+    modify_database($index_db_file, '', false);
+    ob_end_clean(); //chuck the buffer and resume normal operation
   } else {        
     ob_start(); //turn output buffering on - to hide modify_database() output
     modify_database($index_db_file, '', false);
     ob_end_clean(); //chuck the buffer and resume normal operation
   } //else
-    
-  mtrace('Starting activity modules');
+
+  //begin timer
+  search_stopwatch();
+  mtrace("Starting activity modules\n");
   
   //the presence of the required search functions -
   // * mod_iterator
   
   if ($mods = get_records_select('modules' /*'index this module?' where statement*/)) {
     foreach ($mods as $mod) {
-      $libfile = "$CFG->dirroot/mod/$mod->name/lib.php";
+      $class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';              
       
-      if (file_exists($libfile)) {
-        include_once($libfile);
+      if (file_exists($class_file)) {
+        include_once($class_file);
         
         $iter_function = $mod->name.'_iterator';
         $index_function = $mod->name.'_get_content_for_index';
-        
-        //specific module search document class
-        $class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';
-        
+                
         $counter = 0;
         $doc = new stdClass;
                 
-        if (file_exists($class_file) && function_exists($index_function) && function_exists($iter_function)) {
-          include_once($class_file);
-          
+        if (function_exists($index_function) && function_exists($iter_function)) {
           mtrace("Processing module function $index_function ...");
                      
           foreach ($iter_function() as $i) {
             foreach($documents as $document) {
               $counter++;
                             
-              //data object for db
-              $doc->type = $document->type;
-              $doc->title = search_escape_string($document->title);
-              $doc->update = time();              
-              $doc->url = 'none';
-              $doc->courseid = $document->courseid;              
-              $doc->groupid = $document->groupid;
+              //object to insert into db                            
+              $doc->doctype   = $document->doctype;
+              $doc->title     = search_escape_string($document->title);
+              $doc->url       = search_escape_string($document->url);              
+              $doc->update    = time();                            
+              $doc->courseid  = $document->course_id;              
+              $doc->groupid   = $document->group_id;              
               
               //insert summary into db
               $id = insert_record('search_documents', $doc);
               
               //synchronise db with index
-              $document->addField(Zend_Search_Lucene_Field::Keyword('dbid', $id));
+              $document->addField(Zend_Search_Lucene_Field::Keyword('db_id', $id));
               
               //add document to index
               $index->addDocument($document);                  
                             
               //commit every x new documents, and print a status message                            
-              if (($counter%200) == 0) {
+              if (($counter%2000) == 0) {
                 $index->commit();
-                mtrace(".. $counter");                
+                mtrace(".. $counter");
               } //if
             } //foreach
-            
+
             //end transaction
             
           } //foreach
           $index->commit();
           
           mtrace("-- $counter documents indexed");
-          mtrace('done.');          
+          mtrace("done.\n");          
         } //if
       } //if
     } //foreach
   
   mtrace(".<br><a href='index.php'>Back to query page</a>.");
   mtrace('</pre>');
+  
+  //finished, turn busy flag off
+  set_config("search_indexer_busy", 0);
 
 ?>
\ No newline at end of file
index 35e798f..aa3b91b 100644 (file)
           ."started and cancelled an indexing session. Follow the link if you are sure that\n"
           ."you want to continue indexing - this will replace any existing index data (no\n"
           ."Moodle data is affected).\n"
-          ."\n"          
+          ."\n"
+          ."You are encouraged to use the 'Test indexing' script before continuing onto\n"
+          ."indexing - this will check if the modules are set up correctly. Please correct\n"
+          ."any errors before proceeding.\n"
+          ."\n"
+          ."<a href='tests/index.php'>Test indexing</a> or "
           ."<a href='indexer.php?areyousure=yes'>Continue indexing</a> or <a href='index.php'>Back to query page</a>."
           ."</pre>");
   } else {
index f6c17a8..17d1678 100644 (file)
@@ -6,10 +6,11 @@
   //document types that can be searched
   define('SEARCH_NO_TYPE', 'none');
   define('SEARCH_WIKI_TYPE', 'wiki');
+  define('SEARCH_FORUM_TYPE', 'forum');
   
   //returns all the document type constants
   function search_get_document_types() {
-    $r = Array(SEARCH_WIKI_TYPE, SEARCH_NO_TYPE);
+    $r = Array(SEARCH_WIKI_TYPE, SEARCH_NO_TYPE, SEARCH_FORUM_TYPE);
     return $r;
   } //search_get_document_types
   
index 5a65273..9590695 100644 (file)
@@ -1,16 +1,44 @@
 <?php
 
+  /* The query page - accepts a user-entered query string and returns results.
+   *
+   * Queries are boolean-aware, e.g.:
+   * 
+   * '+'      term required
+   * '-'      term must not be present
+   * ''       (no modifier) term's presence increases rank, but isn't required
+   * 'field:' search this field
+   *
+   * Examples:
+   *
+   * 'earthquake +author:michael'
+   *   Searches for documents written by 'michael' that contain 'earthquake'
+   *
+   * 'earthquake +doctype:wiki'
+   *   Search all wiki pages for 'earthquake'
+   *
+   * '+author:helen +author:foster'
+   *   All articles written by Helen Foster
+   *   
+   * */
+
   require_once('../config.php');  
   require_once("$CFG->dirroot/search/lib.php");    
     
   //check for php5, but don't die yet (see line 27)
   if ($check = search_check_php5()) {  
     require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
-    require_once("$CFG->dirroot/search/documents/wiki_document.php");
     
-    $query_string = optional_param('query_string', '', PARAM_CLEAN);  
+    $query_string = optional_param('query_string', '', PARAM_CLEAN);
+    $page_number  = optional_param('page', 1, PARAM_INT);
+    
+    if ($page_number < 1) {
+      $page_number = 1;
+    } //if
+        
     $index_path = "$CFG->dataroot/search";
     $no_index = false; //optimism!
+    $results_per_page = 10;
     
     try {
       $index = new Zend_Search_Lucene($index_path, false);
@@ -23,7 +51,7 @@
   if (!$site = get_site()) {
     redirect("index.php");
   } //if
-  
+    
   $strsearch = "Search"; //get_string();
   $strquery  = "Enter your search query"; //get_string();
 
@@ -45,7 +73,7 @@
 ?>
 
 <form name="query" method="get" action="query.php">
-  <input type="text" name="query_string" length="50" value="<?php print $query_string ?>"/>
+  <input type="text" name="query_string" length="50" value="<?php print stripslashes(htmlentities($query_string)) ?>"/>
   &nbsp;<input type="submit" value="Search"/>&nbsp;&nbsp;<a href="query.php?advanced=yes">Advanced search</a>
   <a href="stats.php">Statistics</a>
 </form>
@@ -77,27 +105,65 @@ if ($no_index and isadmin()) {
     print_simple_box_start('center', '50%', 'white', 10);
     
     search_stopwatch();
-    $hits = $index->find(strtolower($query_string));      
-    
-    if (count($hits) > 0) {
-      $link_function = $hits[0]->type.'_make_link';
-    } //if    
+    $hits = $index->find(strtolower($query_string));
+    $hit_count = count($hits);
     
     print "<br>";
 
-    print count($hits)." results returned for '".$query_string."'.";
-    print "<br><br>";
+    print $hit_count." results returned for '".stripslashes($query_string)."'.";
+    print "<br>";
+      
+    if ($hit_count > 0) {                        
+      if ($hit_count < $results_per_page) {
+        $page_number = 1;
+      } else if ($page_number > ceil($hit_count/$results_per_page)) {
+        $page_number = $hit_count/$results_per_page;
+      } //if
     
-    print "<ol>";    
+      $start = ($page_number - 1)*$results_per_page;
+      $end = $start + $results_per_page;
+        
+      print "<ol>";
         
-    foreach ($hits as $listing) {
-      print "<li><a href='".$link_function($listing)."'>$listing->title</a><br>\n"
-           ."<em>".search_shorten_url($link_function($listing), 70)."</em><br>\n"        
-           ."Type: ".$listing->type.", score: ".round($listing->score, 3)."<br>\n"            
-           ."<br></li>\n";
-    } //foreach
+      for ($i = $start; $i < $end; $i++) {
+        if ($i >= $hit_count) {
+          break;
+        } //if
+      
+        $listing = $hits[$i];
+      
+        print "<li value='".($i+1)."'><a href='".$listing->url."'>$listing->title</a><br>\n"
+             ."<em>".search_shorten_url($listing->url, 70)."</em><br>\n"        
+             ."Type: ".$listing->doctype.", score: ".round($listing->score, 3).", author: ".$listing->author."<br>\n"            
+             ."<br></li>\n";
+      } //for
+      
+      print "</ol>";
+    } //if
+    
+    print "<div align='center'>";
+    
+    if ($page_number > 1) {
+      print "<a href='query.php?query_string=$query_string&page=".($page_number-1)."'>< Back</a>&nbsp;";
+    } else {
+      print "< Back&nbsp;";
+    } //else
+    
+    for ($i = 1; $i <= ceil($hit_count/$results_per_page); $i++) {
+      if ($page_number == $i) {
+        print "[$i]&nbsp;";
+      } else {
+        print "<a href='query.php?query_string=$query_string&page=$i'>$i</a>&nbsp;";
+      } //else
+    } //for
+    
+    if ($page_number < ceil($hit_count/$results_per_page)) {      
+      print "<a href='query.php?query_string=$query_string&page=".($page_number+1)."'>Next ></a>&nbsp;";
+    } else {
+      print "Next >&nbsp;";
+    } //else
     
-    print "</ol>";
+    print "</div>";
     
     print_simple_box_end();
   } //if
index 37ac6f5..8736bda 100644 (file)
@@ -27,7 +27,7 @@
       $type_counts['Total'] = count_records('search_documents');
 
       foreach($types as $type) {
-        $c = count_records('search_documents', 'type', $type);
+        $c = count_records('search_documents', 'doctype', $type);
         $type_counts[$type] = (int)$c;
       } //foreach
     } else {
diff --git a/search/tests/index.php b/search/tests/index.php
new file mode 100644 (file)
index 0000000..91f72bf
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+
+  /* Used to test if modules/blocks are ready to included in the search index.
+   * Carries out some basic function/file existence tests - the search module
+   * is expected to exist, along with the db schema files and the search data
+   * directory.
+   * */
+  
+  @set_time_limit(0);
+  @ob_implicit_flush(true);
+  @ob_end_flush();  
+
+  require_once('../../config.php');
+  require_once("$CFG->dirroot/search/lib.php");  
+
+  require_login();
+
+  if (!isadmin()) {
+    error("You need to be an admin user to use this page.", "$CFG->wwwroot/login/index.php");
+  } //if
+    
+  mtrace('<pre>Server Time: '.date('r',time()));
+  mtrace("Testing global search capabilities:\n");
+  
+  $phpversion = phpversion();
+  
+  if (!search_check_php5()) {    
+    mtrace("ERROR: PHP 5.0.0 or later required (currently using version $phpversion).");
+    exit(0);
+  } else {
+    mtrace("Success: PHP 5.0.0 or later is installed ($phpversion).\n");
+  } //else
+
+  //fix paths for testing
+  set_include_path(get_include_path().":../");
+  require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
+  
+  mtrace("Checking activity modules:\n");
+  
+  //the presence of the required search functions -
+  // * mod_iterator
+  // * mod_get_content_for_index
+  //are the sole basis for including a module in the index at the moment.
+  
+  if ($mods = get_records_select('modules')) {
+    foreach ($mods as $mod) {
+      $class_file = $CFG->dirroot.'/search/documents/'.$mod->name.'_document.php';              
+      
+      if (file_exists($class_file)) {
+        include_once($class_file);
+        
+        if (!defined('SEARCH_'.strtoupper($mod->name).'_TYPE')) {
+          mtrace("ERROR: Constant 'SEARCH_".strtoupper($mod->name)."_TYPE' is not defined in /search/lib.php");
+          continue;
+        } //if
+        
+        $iter_function = $mod->name.'_iterator';
+        $index_function = $mod->name.'_get_content_for_index';
+                                
+        if (function_exists($index_function) && function_exists($iter_function)) {
+          if (is_array($iter_function())) {
+            $documents = $index_function(array_pop($iter_function()));
+            
+            if (is_array($documents)) {
+              mtrace("Success: '$mod->name' module seems to be ready for indexing.");
+            } else {
+              mtrace("ERROR: $index_function() doesn't seem to be returning an array.");
+            } //else
+          } else {
+            mtrace("ERROR: $iter_function() doesn't seem to be returning an object array.");
+          } //else
+        } else {
+          mtrace("ERROR: $iter_function() and/or $index_function() does not exist in $class_file");
+        } //else
+      } else {
+        mtrace("Notice: $class_file does not exist, this module will not be indexed.");
+      } //else
+    } //foreach
+  } //if
+  
+  //finished modules
+  mtrace("\nFinished checking activity modules.");
+  
+  //now blocks...
+  //
+  
+  mtrace("<br><a href='../index.php'>Back to query page</a> or <a href='../indexersplash.php'>Start indexing</a>.");
+  mtrace('</pre>');
+
+?>
\ No newline at end of file