Index: trunk/gsdl/src/recpt/browsetools.cpp
===================================================================
--- trunk/gsdl/src/recpt/browsetools.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/browsetools.cpp	(revision 928)
@@ -29,4 +29,7 @@
 /*
    $Log$
+   Revision 1.37  2000/02/15 22:53:47  kjm18
+   search history stuff added.
+
    Revision 1.36  2000/02/06 21:31:36  sjboddie
    made some formerly static functions accessible from elsewhere (for
@@ -171,4 +174,5 @@
 // output_controls displays the detach, expand/contract contents,
 // expand/contract text and highlighting/no highlighting buttons
+
 void output_controls (cgiargsclass &args, const text_tarray &ibuttons,
 		      recptproto * /*collectproto*/, displayclass &disp, 
@@ -226,5 +230,5 @@
 // server which would send a link to the cover image if there
 // was one otherwise send title, author and stuff
-static void output_cover_image (cgiargsclass &args, recptproto * /*collectproto*/, 
+ void output_cover_image (cgiargsclass &args, recptproto * /*collectproto*/, 
 				displayclass &disp, outconvertclass &outconvert, 
 				ostream &textout, ostream &/*logout*/) {
@@ -234,7 +238,7 @@
   textout << outconvert << disp << 
     "<img src=\"_httpcollection_/archives/_thisOID_/cover.jpg\"><br>\n";
-}
-
-static void output_titles (cgiargsclass &args, recptproto *collectproto, 
+ }
+
+ void output_titles (cgiargsclass &args, recptproto *collectproto, 
 			   formatinfo_t &formatinfo, displayclass &disp, 
 			   outconvertclass &outconvert, ostream &textout, 
@@ -533,5 +537,4 @@
   }
 }
-
 
 void contracted_contents (cgiargsclass &args, int tabcount, bool fulltoc, 
@@ -682,2 +685,5 @@
   if (havecontrols) textout << "</td></tr></table>\n";
 }
+
+
+
Index: trunk/gsdl/src/recpt/browsetools.h
===================================================================
--- trunk/gsdl/src/recpt/browsetools.h	(revision 927)
+++ trunk/gsdl/src/recpt/browsetools.h	(revision 928)
@@ -56,8 +56,32 @@
                  ostream &textout, ostream &logout);
 
-void output_controls (cgiargsclass &args, const text_tarray &ibuttons,
-		      recptproto * /*collectproto*/, displayclass &disp, 
-		      outconvertclass &outconvert, ostream &textout, 
-		      ostream &/*logout*/);
+
+// output_controls displays the detach, expand/contract contents,
+// expand/contract text and highlighting/no highlighting buttons
+ void output_controls (cgiargsclass &args, const text_tarray &ibuttons,
+			     recptproto * /*collectproto*/, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout, 
+			     ostream &/*logout*/);
+    
+ void output_cover_image (cgiargsclass &args, recptproto * /*collectproto*/, 
+				displayclass &disp, outconvertclass &outconvert, 
+				ostream &textout, ostream &/*logout*/);
+
+ void output_titles (cgiargsclass &args, recptproto *collectproto, 
+			   formatinfo_t &formatinfo, displayclass &disp, 
+			   outconvertclass &outconvert, ostream &textout, 
+			   ostream &logout);
+void contracted_contents (cgiargsclass &args, int tabcount, bool fulltoc, 
+				 browsermapclass *browsermap, formatinfo_t &formatinfo, 
+				 recptproto *collectproto, displayclass &disp, 
+				 outconvertclass &outconvert, ostream &textout, 
+				 ostream &logout);
+
+ void expanded_contents (cgiargsclass &args, int tabcount, bool fulltoc,
+			       browsermapclass *browsermap, formatinfo_t &formatinfo, 
+			       recptproto *collectproto, displayclass &disp, 
+			       outconvertclass &outconvert, ostream &textout, 
+			       ostream &logout);
+
 
 #endif
Index: trunk/gsdl/src/recpt/cgiargs.h
===================================================================
--- trunk/gsdl/src/recpt/cgiargs.h	(revision 927)
+++ trunk/gsdl/src/recpt/cgiargs.h	(revision 928)
@@ -234,2 +234,5 @@
 
 #endif
+
+
+
Index: trunk/gsdl/src/recpt/delhistoryaction.cpp
===================================================================
--- trunk/gsdl/src/recpt/delhistoryaction.cpp	(revision 928)
+++ trunk/gsdl/src/recpt/delhistoryaction.cpp	(revision 928)
@@ -0,0 +1,231 @@
+/**********************************************************************
+ *
+ * delhistoryaction.cpp -- allows user to select history items to delete
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+
+
+#include "delhistoryaction.h"
+//#include "querytools.h"
+//#include "formattools.h"
+#include "cgiutils.h"
+//#include "OIDtools.h"
+//#include "infodbclass.h"
+//#include "fileutil.h"
+#include "text_t.h"
+#include "historydb.h"
+
+
+delhistoryaction::delhistoryaction () {
+
+  //  num_phrases = 0;
+
+  // this action uses cgi variable "a"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "dh";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "hmode" -- indicate whether selected records are to be
+  // saved or deleted
+  arg_ainfo.shortname = "hmode";
+  arg_ainfo.longname = "history deletion mode";
+  arg_ainfo.multiplechar = true;
+  //arg_ainfo.multiplevalue = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "delete";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+ // "hsr" -- the selected records
+ 
+  arg_ainfo.shortname = "hsr";
+  arg_ainfo.longname = "history selected records";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.multiplevalue = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "hdh" delete history for current user
+  // clear history = delete some, delete all
+
+  arg_ainfo.shortname = "hdh";
+  arg_ainfo.longname = "history delete history";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+void delhistoryaction::configure (const text_t &key, const text_tarray &cfgline) {
+  action::configure (key, cfgline);
+}
+
+bool delhistoryaction::init (ostream &logout) {
+  return action::init (logout);
+}
+
+bool delhistoryaction::check_cgiargs (cgiargsinfoclass & /*argsinfo*/, cgiargsclass & /*args*/, 
+				 ostream &/*logout*/) {
+  //dont check anything yet
+  return true;
+}
+
+void delhistoryaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+				    response_t &response, text_t &response_data, 
+				    ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+void delhistoryaction::define_internal_macros (displayclass &/*disp*/, cgiargsclass &/*args*/, 
+					  recptprotolistclass */*protos*/, 
+					  ostream &/*logout*/) {
+
+  // define_internal_macros sets the following macros:
+  //_searchhistorylist_
+
+  //  define_history_macros(disp, args, protos, logout);
+
+}
+
+
+void delhistoryaction::define_external_macros (displayclass &/*disp*/, cgiargsclass &/*args*/, 
+					  recptprotolistclass */*protos*/, ostream &/*logout*/) {
+
+  // define_external_macros sets the following macros:
+
+  // can't do anything if collectproto is null (i.e. no collection was specified)
+  //recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  //if (collectproto == NULL) return;
+
+    // define_history_macros(disp, args, protos, logout);
+  
+} // define external macros
+
+void delhistoryaction::define_history_macros(displayclass &disp, cgiargsclass &args, 
+					  recptprotolistclass *protos, ostream &logout) {
+
+  // defines the following macros
+
+  // _searchhistorylist_
+
+    text_t historylist = "<!-- Search History List -->\n";
+   
+    text_t userid = args["z"];
+    text_tarray entries;
+
+    historylist += " <table align=center width=500 border=1> \n <tr><th colspan=4 align=center>";
+    historylist += "Search History </th></tr>\n";
+    historylist += "<tr><th width=60>select</th><th width=40>#</th>\n<th width=340>Query</th>\n";
+    historylist += "<th width=60>Results</th></tr>\n";
+    if (get_history_info(userid, entries)) {
+      int count = 1;
+      text_tarray::iterator here = entries.begin();
+      text_tarray::iterator end = entries.end();
+      //      int size=(int)entries.size();
+      while (here !=end ) {
+	text_t c=count;
+	text_t querynum;
+	text_t query;
+	text_t q = "q";
+	text_t numdocs;
+	text_t cgiargs;
+	text_t  userinfo;
+	split_saved_query(*here, querynum, numdocs, cgiargs);
+	parse_saved_args(cgiargs, q, query); // get query string out
+	decode_cgi_arg(query); // un cgisafe it
+	
+	format_user_info(cgiargs, userinfo, protos, logout);
+	
+	historylist += "<tr> <td width=60 align=center><input type=checkbox name=hsr value=\""+querynum+"\"></td>";
+	historylist += "<td width=40 align=center>"+querynum+"</td>\n";
+	historylist += "<td width=340 align=left>"+query+"</td><td width=60 align=center>"+numdocs+"</td>\n";
+	here++;
+	count++;
+      }
+     
+     
+    } // if
+    else { // have an empty row
+      historylist += "";
+    }
+    historylist+="</table>";
+    historylist += "<p><! ---- end of history list ----->\n";
+    disp.setmacro("searchhistorylist", "delhistory", historylist);
+
+} // define history macros
+
+
+bool delhistoryaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			     browsermapclass */*browsers*/, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout, 
+			     ostream &logout) {
+
+  if (args["hdh"] !="") {
+    delete_search_history(args);
+  }
+  define_history_macros(disp, args, protos, logout);
+  textout << outconvert << disp << ("_delhistory:header_\n")
+	  << ("_delhistory:content_\n")
+	  << ("_delhistory:footer_\n");
+
+  return true;
+}
+
+
+bool delhistoryaction::delete_search_history(cgiargsclass &args)
+{
+
+  bool result;
+  text_t userid = args["z"];
+  text_t type = args["hdh"];
+  decode_cgi_arg(type);
+  if (type=="Delete All") {
+    result=delete_all_history_info(userid);
+  }else if (type=="Clear History"){
+    text_t records = args["hsr"];
+    text_t deletemode = args["hmode"];
+    result=delete_history_info(userid, deletemode, records);
+  } else {
+    result=false;
+  }
+
+  return result;
+
+}
+
+
+
+
+
Index: trunk/gsdl/src/recpt/delhistoryaction.h
===================================================================
--- trunk/gsdl/src/recpt/delhistoryaction.h	(revision 928)
+++ trunk/gsdl/src/recpt/delhistoryaction.h	(revision 928)
@@ -0,0 +1,78 @@
+/**********************************************************************
+ *
+ * delhistoryaction.h --  allows user to select history items to delete
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+
+#ifndef DELHISTORYACTION_H
+#define DELHISTORYACTION_H
+
+#include "gsdlconf.h"
+#include "action.h"
+
+
+class delhistoryaction : public action {
+
+protected:
+
+  void define_history_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  void set_option_macro (const text_t &macroname, text_t current_value,
+			 const FilterOption_t &option, displayclass &disp);
+
+
+  bool delete_search_history(cgiargsclass &args);
+
+public:
+  delhistoryaction ();
+  virtual ~delhistoryaction () {}
+  
+  void configure (const text_t &key, const text_tarray &cfgline);
+  bool init (ostream &logout);
+  
+  text_t get_action_name () {return "dh";}
+  
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+  
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  void define_external_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+  
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+};
+
+#endif
+
Index: trunk/gsdl/src/recpt/documentaction.cpp
===================================================================
--- trunk/gsdl/src/recpt/documentaction.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/documentaction.cpp	(revision 928)
@@ -28,4 +28,7 @@
 /*
    $Log$
+   Revision 1.34  2000/02/15 22:53:49  kjm18
+   search history stuff added.
+
    Revision 1.33  2000/02/13 20:40:05  sjboddie
    now dm_safe _thisOID_ macro
@@ -372,4 +375,6 @@
   iwidth += width.getint();
   
+  
+  
   while (dochere != docend) {
     const text_t &title = (*dochere).metadata["Title"].values[0];
@@ -410,4 +415,6 @@
     javaimagesnavbar = "_javasearch_";
   }
+  
+  
   if (numc == 0) navigationbar += "_imagespacer_";
 	
@@ -534,4 +541,5 @@
 	// set _navigationbar_ and _javaimagesnavbar_ macros
 	set_navbarmacros (disp, response, args);
+   
       }
     } else {
@@ -1083,2 +1091,13 @@
   textout << outconvert << disp << buffer;
 }
+
+
+
+
+
+
+
+
+
+
+
Index: trunk/gsdl/src/recpt/documentaction.h
===================================================================
--- trunk/gsdl/src/recpt/documentaction.h	(revision 927)
+++ trunk/gsdl/src/recpt/documentaction.h	(revision 928)
@@ -53,4 +53,8 @@
   virtual void set_java_macros (cgiargsclass &args, displayclass &disp);
 
+  void get_classificationlinks (const text_t &arg_cl, const text_t &collection,
+				recptproto *collectproto, displayclass &disp, 
+				ostream &logout);
+
   virtual void output_text (ResultDocInfo_t &docinfo, format_t *formatlistptr,
 			    const TermInfo_tarray &terminfo, const text_t &OID,
Index: trunk/gsdl/src/recpt/historydb.cpp
===================================================================
--- trunk/gsdl/src/recpt/historydb.cpp	(revision 928)
+++ trunk/gsdl/src/recpt/historydb.cpp	(revision 928)
@@ -0,0 +1,601 @@
+
+#include "historydb.h"
+#include "gsdlhome.h"
+#include "fileutil.h"
+#include "cgiutils.h"
+#include "recptproto.h"
+#include "OIDtools.h"
+
+
+// returns true on success (in which case historyinfo will contain
+// the information for this history)
+bool get_history_info (const text_t &userid, text_tarray &historyinfo)
+{
+  text_t historyfile = filename_cat(GSDL_GSDLHOME, "etc", "history.db");
+  
+  bool result = false;
+  // open the history database  
+  gdbmclass historydb;
+  
+  if (historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) {
+    // get history list
+    text_t historyresult;
+
+    historydb.getkeydata(userid, historyresult);
+    
+    if (historyresult != "") { //  there are entries, process them
+	
+      splitchar(historyresult.begin(), historyresult.end(), '\n', historyinfo);
+      result = true;
+    }
+    historydb.closedatabase();
+    
+    } // opendatabase
+    else {
+      cerr << "couldn't open db\n";
+      
+    }
+  return result;
+} // get history info
+
+
+// returns true on success
+bool set_history_info (const text_t &userid, const text_t &history)
+{
+
+  text_t historyfile = filename_cat(GSDL_GSDLHOME, "etc", "history.db");
+  
+  bool result = false;
+  // open the history database  
+  gdbmclass historydb;
+  
+  text_t querynum;
+  text_t historyresult;
+
+  
+  if ( !historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) {
+    // not created yet
+    historyresult="";
+    querynum="1";
+  }
+  else {
+
+    // get history list
+   if (! historydb.getkeydata(userid, historyresult)) {
+     historyresult="";
+     querynum="1";
+   }
+   historydb.closedatabase();
+  }
+
+  // get next query num
+  if (historyresult!="") {
+    text_tarray entries;
+    splitchar(historyresult.begin(), historyresult.end(), '\n', entries);
+    get_query_num(entries.back(), querynum);
+    querynum.setint(querynum.getint()+1);
+  }
+  
+  // open for writing
+  if (!historydb.opendatabase(historyfile, GDBM_WRCREAT, 1000, true)) return false;
+    
+  // add on new line
+  historyresult += querynum;
+  historyresult += ":";
+  historyresult += history;
+  historyresult += "\n";
+  
+    if (historydb.setinfo(userid, historyresult)) 
+      result=true; 
+    
+    historydb.closedatabase();
+    return result;
+}
+
+// deletes all a users history
+bool delete_all_history_info (const text_t &userid)
+{
+  text_t historyfile = filename_cat(GSDL_GSDLHOME, "etc", "history.db");
+  
+  // open the history database  
+  gdbmclass historydb;
+  
+  if ( !historydb.opendatabase(historyfile, GDBM_WRITER, 1000, true)) return false;
+
+  historydb.deletekey(userid);
+  return true;
+  
+}
+
+//deletes only the selected records
+bool delete_history_info(const text_t &userid, const text_t &deletemode, const text_t &selection)
+{
+  text_t historyfile = filename_cat(GSDL_GSDLHOME, "etc", "history.db");
+  bool result;
+  // open the history database  
+  gdbmclass historydb;
+  
+  if ( !historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) return false;
+
+  // get history list
+  text_t historyresult;
+
+  if (! historydb.getkeydata(userid, historyresult)) return false;
+   
+  historydb.closedatabase();
+
+  text_t newhistory; // for the new list of search history
+  // get list of queries
+  text_tarray queries;
+  splitchar(historyresult.begin(), historyresult.end(), '\n', queries);
+  text_tarray::iterator begin=queries.begin();
+  text_tarray::iterator end=queries.end();
+  
+  // get list of numbers to delete
+  text_tarray numbers;
+  splitchar(selection.begin(), selection.end(), ',', numbers);
+  text_tarray::iterator numbegin=numbers.begin();
+  text_tarray::iterator numend=numbers.end();
+
+  bool tdefault, tset;
+  if (deletemode=="delete") {
+    tdefault=true;
+    tset=false;
+  }
+  else {// deletemode == save
+    tdefault=false;
+    tset=true;
+  }
+
+  // create a HistRecord map of all the entries
+  HistRecordmap records;
+  while (begin != end) {
+    HistRecord rec;
+    rec.entry=*begin;
+    rec.save=tdefault;
+    text_t num;
+    get_query_num(*begin, num);
+    records[num] = rec;
+    begin++;
+  }
+
+ 
+  // now set the save field for all records that are selected
+  while(numbegin != numend) {
+    
+    if (is_number(*numbegin)) {
+      if (records.count(*numbegin)==1) {
+	records[*numbegin].save=tset;
+      } // else invalid number
+    }
+    else{
+      int start, stop;
+      if (get_selection_params(*numbegin, start, stop)) {
+	for (int i=start; i<=stop; i++) {
+	  text_t num = i;
+	  if (records.count(num)==1) {
+	    records[num].save=tset;
+	  }
+	}}
+	else { // dodgy format 
+	  // error message
+	}
+      
+    }
+    numbegin++;
+  } // while
+	
+  HistRecordmap::iterator recbegin = records.begin();
+  HistRecordmap::iterator recend = records.end();
+ 
+  //create new history record - go through all Records
+  // and add to new history those with save set to true
+  while (recbegin != recend) {
+    if ((*recbegin).second.save) {
+      expand_query((*recbegin).second, records);
+      newhistory += (*recbegin).second.entry + "\n";
+    }
+    recbegin++;
+  }
+  
+  // open for writing
+  if (!historydb.opendatabase(historyfile, GDBM_WRITER, 1000, true)) return false;
+
+  if (historydb.setinfo(userid, newhistory)) 
+    result=true; 
+    
+  historydb.closedatabase();
+  return result;
+  
+
+}
+
+bool get_selection_params (text_t &data, int &start, int &stop) 
+{
+   text_tarray results;
+
+  splitchar(data.begin(), data.end(), '-', results);
+  if (results.size()==2 && is_number(results[0]) && is_number(results[1])) {
+      start=results[0].getint();
+      stop=results[1].getint();
+      
+      return true;
+  }
+  else {
+    // error message
+    return false;
+  }
+  
+}
+
+// expand query takes a HistRecord, and expands out any hashes in
+// the query string for queries that aren't to be saved
+bool expand_query(HistRecord &record,  HistRecordmap &records)
+{
+  text_tarray args;
+   splitchar(record.entry.begin(), record.entry.end(), '&', args);
+
+  text_tarray::iterator begin=args.begin();
+  text_tarray::iterator end=args.end();
+
+  text_t query;
+  // find the q argument
+  while(begin != end) {
+    if ((*begin)[0] == 'q'&& (*begin)[1] == '=') {
+      query=*begin;
+      args.erase(begin);
+      break;
+    }
+    else {
+      begin++;
+    }
+  }
+  if (query !="") {
+
+    //remove the q=
+    text_t::iterator front = query.begin();
+    query.erase(front);
+    front=query.begin();
+    
+    query.erase(front);
+    decode_cgi_arg(query);
+    // now have just the query string
+    // check any refs in it to see if they are still valid
+    combine_query(query, records);
+    text_t formattedquery = cgi_safe(query);
+    text_t newquery = "q=";
+    newquery += formattedquery;
+
+    args.push_back(newquery);
+    joinchar(args, '&', record.entry); 
+  
+    return true;
+  }else return false;
+}// expand query
+
+// replaces a reference to a previous query with the search
+// string from that query
+bool combine_query(text_t &userid, text_t &query) 
+{
+  text_t::iterator begin = query.begin();
+  text_t::iterator end = query.end();
+  text_t::iterator it = findchar(begin, end, '#');
+  if (it==end) return true;  // no need to swap anything
+   
+ 
+  text_t queryresult = "";
+  text_t historyfile = filename_cat(GSDL_GSDLHOME, "etc", "history.db");
+  text_t historyresult;
+  // open the history database  
+  gdbmclass historydb;
+  bool exists = false;
+  if (historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) {
+    // get history list
+    if (historydb.getkeydata(userid, historyresult)) {
+	exists=true;
+      
+	historydb.closedatabase();
+    }
+  }
+  if (!exists) {// cant open history file, or user entry doesn't exist. replace #x with blanks 
+    while(it !=end) {
+      queryresult += substr(begin, it);
+      it++;
+      while(*it>='0'&&*it<='9') it++;
+      begin=it;
+      it=findchar(begin, end, '#');
+    }
+    queryresult += substr(begin, end);
+    query = queryresult;
+    return false;
+  }
+  bool result=true;
+
+  // if have got to here, have a previous history in historyresult
+   text_tarray historyinfo;
+   splitchar(historyresult.begin(), historyresult.end(), '\n', historyinfo);
+   text_tarray::iterator histbegin = historyinfo.begin();
+   text_tarray::iterator histend = historyinfo.end();
+   
+   text_tmap historymap;
+   // put all entries into a map, so can pull out the appropriate one easily
+   while (histbegin != histend) {
+     text_t num;
+     get_query_num(*histbegin, num);
+     historymap[num]=*histbegin;
+     histbegin++;
+   }
+   while(it!=end) { // while there still is a hash present
+   text_t querynum;
+   text_t newquery;
+   
+   while (begin!=end) {
+     if (*begin==text_t('#')) {
+       begin++;
+       while(begin !=end && *begin>='0'&&*begin<='9') {
+	 querynum.push_back(*begin);
+	 begin++;
+       }
+        text_t oldquery = historymap[querynum];
+       if (oldquery !="") {   // valid entry
+	 parse_saved_args(oldquery, text_t("q"), newquery);
+	 decode_cgi_arg(newquery);
+	 queryresult += newquery;
+       }
+       else {
+	 result=false;
+       }
+       querynum.clear();
+        // else do nothing (replace #x with nothing)
+    } // if
+    else {
+      queryresult.push_back(*begin);
+      begin++;
+    }
+   } // while begin!=end
+
+   // have got to end of query string, 
+   // now go back and check for internal #
+   query = queryresult;
+   begin = query.begin();
+   end = query.end();
+   it = findchar(begin, end, '#');
+   queryresult.clear();
+
+   } // while it !=end
+
+
+    return result;
+   
+} // combine query
+
+
+// replaces a reference to a previous query with the search
+// string from that query, uses the Histrecordmap rather than the GDBM file
+bool combine_query( text_t &query, HistRecordmap &records) 
+{
+  text_t::iterator begin = query.begin();
+  text_t::iterator end = query.end();
+  text_t::iterator it = findchar(begin, end, '#');
+  if (it==end) return true;  // no need to swap anything
+    
+  text_t queryresult = "";
+  bool changed=true;
+  while(it!=end && changed) { // while there still is a hash present
+                             // and query has changed since last time round
+                              // we are leaving some #X in 
+   text_t querynum;
+   text_t newquery;
+   changed=false;
+   while (begin!=end) { // go through the query looking for hash
+     if (*begin==text_t('#')) {
+       begin++;
+       while(begin !=end && *begin>='0'&&*begin<='9') {
+	 querynum.push_back(*begin);
+	 begin++;
+       }
+       if(records.count(querynum)>0) { // valid entry
+	 if (!records[querynum].save){ // referenced record to be deleted
+	   // get the q arg out of referenced query
+	   parse_saved_args(records[querynum].entry, text_t("q"), newquery);
+	   decode_cgi_arg(newquery);
+	   queryresult += newquery;
+	   changed=true;
+	   }
+	 else { // leave the #x in
+	   queryresult.push_back('#');
+	   queryresult +=  querynum;
+	 }
+       }
+       querynum.clear();
+       newquery.clear();
+       // else do nothing (replace #x with nothing)
+    } // if its a hash
+    else {
+      queryresult.push_back(*begin);
+      begin++;
+    }
+   } // while begin!=end
+
+   // have got to end of query string, 
+   // now go back and check for internal #
+   query = queryresult;
+   begin = query.begin();
+   end = query.end();
+   it = findchar(begin, end, '#');
+   queryresult.clear();
+
+   } // while it !=end
+
+
+    return true;
+   
+} // combine query
+
+// retrieves the value of one of the arguments
+void parse_saved_args(text_t &args, text_t &key, text_t &value)
+{
+  text_t::iterator here = args.begin();
+  text_t::iterator end = args.end();
+  text_t::iterator it;
+  while (here != end) {
+    if(*here==key[0]) {
+      it=findchar(here, end, '=');
+      if (it==end) {
+	value=""; return;
+      }
+      text_t entry = substr(here, it);
+      if (entry==key) {
+	here=it+1;
+      	it=findchar(here, end, '&');
+	value = substr(here, it);
+	return;
+      }
+    }
+    here++;
+  }// while
+}
+
+// retrieves the value of all of the arguments
+void parse_saved_args(text_t &args, infodbclass &info)
+{
+  text_t::iterator here = args.begin();
+  text_t::iterator end = args.end();
+  text_t::iterator it;
+  text_tarray values;
+
+  splitchar(here, end, '&', values);
+  
+  text_tarray::iterator start = values.begin();
+  text_tarray::iterator stop = values.end();
+
+  text_t key;
+  text_t value;
+  while(start!=stop) {
+
+    here=(*start).begin();
+    end=(*start).end();
+    it=findchar(here, end, '=');
+    if (it!=end) {
+      key = substr(here, it);
+      value = substr(it+1, end);
+      
+      info[key]=value;
+    }
+    start++;
+  }
+}
+
+void get_query_num(text_t &query, text_t &querynum)
+{
+  text_t::iterator begin = query.begin();
+
+  while (*begin >='0'&& *begin <='9') { // get the digits
+        querynum.push_back(*begin);
+	    begin++;
+  }
+  if (*begin != ':') {
+    // something has gone wrong
+  }   
+}
+void split_saved_query(text_t &query, text_t &querynum, text_t &numdocs, text_t &cgiargs)
+{
+  text_t::iterator begin = query.begin();
+  text_t::iterator end = query.end();
+
+  while (*begin >='0'&& *begin <='9') { // get the digits
+    querynum.push_back(*begin);
+    begin++;
+  }
+  if (*begin != ':') {
+    // something has gone wrong
+  }
+  else begin++;
+  while (*begin >='0'&& *begin <='9') { // get the digits
+        numdocs.push_back(*begin);
+	    begin++;
+  }                                                                                                                         
+  if (*begin == '+') {  // get the + if there
+    numdocs.push_back(*begin);
+    begin++;
+  }
+
+  cgiargs += (substr(begin, end));  // put rest of query into cgiargs
+
+}
+
+void format_user_info (text_t &cgiargs, text_t &userinfo,  recptprotolistclass *protos, ostream &logout) 
+{
+  logout << "in format user info";
+  text_tset metadata;
+
+  infodbclass argsinfo;
+  parse_saved_args(cgiargs, argsinfo);
+
+  text_t collect = argsinfo["c"];
+  if (collect=="") {
+    userinfo="";
+    return;
+  }
+  recptproto *collectproto = protos->getrecptproto(collect,logout);
+  if (collectproto == NULL) {
+    userinfo="";
+    return;
+  }
+  metadata.insert(argsinfo["h"]);
+  FilterResponse_t response;
+
+  get_info("collection", collect, metadata, false, collectproto, response, logout);
+
+  text_t index = response.docInfo[0].metadata[argsinfo["h"]].values[0];
+  
+  text_t mode;
+  if (argsinfo["b"]=="0") { // simple mode
+    if (argsinfo["t"]=="0") {
+      mode = "all words";
+    }
+    else { // t=1
+      mode = "some words";
+    }
+
+  } 
+  else { // advanced mode
+    if (argsinfo["t"]=="0") {
+      mode = "boolean";
+    }
+    else {
+      mode = "ranked";
+    }
+  }
+
+  text_t options;
+  if (argsinfo["k"]=="0") {
+    options = "case must match";
+  }
+  else {
+    options = "ignore case";
+  }
+  if (argsinfo["s"]=="0") {
+    options += ", whole word must match";
+  }
+  else {
+    options += ", ignore word endings";
+  }
+    
+  userinfo.clear();
+  userinfo = "collection ("+argsinfo["c"] + ") ";
+  userinfo += "search (" + index + ") ";
+  userinfo += "mode (" + mode +") ";
+  userinfo += "options (" + options + ")";
+
+  logout << "in format user info - end";
+}
+
+
+
+
+
+
+
+
+
Index: trunk/gsdl/src/recpt/historydb.h
===================================================================
--- trunk/gsdl/src/recpt/historydb.h	(revision 928)
+++ trunk/gsdl/src/recpt/historydb.h	(revision 928)
@@ -0,0 +1,71 @@
+
+
+#ifndef HISTORY_H
+#define HISTORY_H
+
+#include "infodbclass.h"
+#include "text_t.h"
+#include "recptproto.h"
+//#include <vector.h>
+#include <map.h>
+
+struct HistRecord {
+  text_t entry;
+  bool save;
+};
+
+struct ltinttext_t
+{
+  bool operator()(const text_t &t1, const text_t &t2) const
+  { return t1.getint() < t2.getint(); }
+};
+
+
+typedef map<text_t, HistRecord, ltinttext_t> HistRecordmap;
+
+// returns true on success (in which case historyinfo will contain
+// the information for this history)
+bool get_history_info ( const text_t &userid, text_tarray &historyinfo);
+
+// returns true on success
+bool set_history_info (const text_t &userid, const text_t &historyinfo);
+
+bool delete_all_history_info (const text_t &userid);
+
+// deletes only the entries specified
+bool delete_history_info(const text_t &userid, const text_t &deletemode,
+			 const text_t &selection);
+
+// takes a text_t with x-y, and retrieves the x and y params
+bool get_selection_params (text_t &data, int &start, int &stop);
+
+bool expand_query(HistRecord &query, HistRecordmap &records);
+
+// combine query looks for #x and replaces it with the query
+// string from the xth entry. THe first one looks up the xth
+// query in the database (used before running a query), the 
+// second version looks it up inthe records map (used in
+// delete history -are doing a lotof #x replacing at once.
+bool combine_query(text_t &userid, text_t &query);
+bool combine_query(text_t &userid, HistRecordmap  &records);
+
+// retrieves the value of an arg (key) from a list of cgi args
+// in the form a=q&b=1&...
+void parse_saved_args(text_t &args, text_t &key, text_t &value);
+
+// extracts out the components of an entry in the database
+// currently:
+// querynum:numdocs cgiargs
+void split_saved_query(text_t &query, text_t &querynum, text_t &numdocs, text_t &cgiargs);
+
+// just extracts the query number out of the entry
+void get_query_num(text_t &query, text_t &querynum);
+
+// takes the cgiarg string, and outputs it in some form useful
+// to the user
+void format_user_info (text_t &cgiargs, text_t &info, 
+		       recptprotolistclass *protos, ostream &logout);
+
+#endif
+
+
Index: trunk/gsdl/src/recpt/infodbclass.cpp
===================================================================
--- trunk/gsdl/src/recpt/infodbclass.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/infodbclass.cpp	(revision 928)
@@ -28,4 +28,7 @@
 /*
    $Log$
+   Revision 1.6  2000/02/15 22:53:50  kjm18
+   search history stuff added.
+
    Revision 1.5  1999/10/19 03:23:42  davidb
    Collection building support through web pages
@@ -269,4 +272,36 @@
 
   return (ret == 0);
+
+}
+
+//returns true on success
+bool gdbmclass::setinfo (const text_t &key, const text_t &data) {
+  if (gdbmfile == NULL) return false;
+  
+  // store the value
+  datum key_data;
+  datum data_data;
+
+  // get a utf-8 encoded c string of the unicode key
+  key_data.dptr = (to_utf8(key)).getcstr();
+  if (key_data.dptr == NULL) {
+    if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
+    return false;
+  }
+  key_data.dsize = strlen (key_data.dptr);
+
+  data_data.dptr = (to_utf8(data)).getcstr();
+  if (data_data.dptr == NULL) {
+    if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
+    delete key_data.dptr;
+  }
+  data_data.dsize = strlen (data_data.dptr);
+
+  int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
+  delete key_data.dptr;
+  delete data_data.dptr;
+
+  return (ret == 0);
+
 }
 
@@ -549,2 +584,3 @@
 }
 
+
Index: trunk/gsdl/src/recpt/infodbclass.h
===================================================================
--- trunk/gsdl/src/recpt/infodbclass.h	(revision 927)
+++ trunk/gsdl/src/recpt/infodbclass.h	(revision 928)
@@ -142,4 +142,6 @@
   // returns true on success
   bool setinfo (const text_t &key, const infodbclass &info);
+  // returns true on success
+  bool setinfo (const text_t &key, const text_t &data);
 
   void deletekey (const text_t &key);
@@ -151,5 +153,8 @@
   text_t getfirstkey ();
   text_t getnextkey (const text_t &key);
-  
+
+    // returns true on success
+  bool getkeydata (text_t key, text_t &data);
+
 protected:
   text_t openfile;
@@ -162,6 +167,4 @@
   void get_previous_sibling (text_t &OID, infodbclass &info);
 
-  // returns true on success
-  bool getkeydata (text_t key, text_t &data);
 
   // returns true on success
@@ -172,2 +175,7 @@
 
 #endif
+
+
+
+
+
Index: trunk/gsdl/src/recpt/librarymain.cpp
===================================================================
--- trunk/gsdl/src/recpt/librarymain.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/librarymain.cpp	(revision 928)
@@ -28,4 +28,7 @@
 /*
    $Log$
+   Revision 1.21  2000/02/15 22:53:51  kjm18
+   search history stuff added.
+
    Revision 1.20  1999/12/13 02:46:56  davidb
    Teach buildaction about recptionist
@@ -122,4 +125,8 @@
 #include "extlinkaction.h"
 #include "buildaction.h"
+#include "delhistoryaction.h"
+//#include "bibqueryaction.h"
+//#include "bibindexaction.h"
+//#include "bibdatabaseaction.h"
 
 #include "browserclass.h"
@@ -233,4 +240,15 @@
   recpt.add_action (&aauthenaction);
 
+  delhistoryaction adelhistoryaction;
+  recpt.add_action(&adelhistoryaction);
+
+  //  bibqueryaction abibqueryaction;
+  //recpt.add_action(&abibqueryaction);
+
+  //  bibindexaction abibindexaction;
+  //recpt.add_action (&abibindexaction);
+
+  //  bibdatabaseaction abibdatabaseaction;
+  //recpt.add_action(&abibdatabaseaction):
   // list of browsers
   vlistbrowserclass avlistbrowserclass;
@@ -257,2 +275,6 @@
 }
 
+
+
+
+
Index: trunk/gsdl/src/recpt/pageaction.cpp
===================================================================
--- trunk/gsdl/src/recpt/pageaction.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/pageaction.cpp	(revision 928)
@@ -28,4 +28,7 @@
 /*
    $Log$
+   Revision 1.27  2000/02/15 22:53:51  kjm18
+   search history stuff added.
+
    Revision 1.26  2000/02/03 01:49:35  sjboddie
    fixed up some of the preferences stuff
@@ -516,2 +519,7 @@
   return true;
 }
+
+
+
+
+
Index: trunk/gsdl/src/recpt/pagedbrowserclass.cpp
===================================================================
--- trunk/gsdl/src/recpt/pagedbrowserclass.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/pagedbrowserclass.cpp	(revision 928)
@@ -28,4 +28,7 @@
 /*
    $Log$
+   Revision 1.6  2000/02/15 22:53:51  kjm18
+   search history stuff added.
+
    Revision 1.5  2000/02/06 21:29:11  sjboddie
    fixed a bug - made some functions virtual for use with cstr collection
@@ -105,4 +108,7 @@
     }
   }
+       
+
+    
 }
 
@@ -176,4 +182,8 @@
   }
 
+  if (!found) {
+    textout << outconvert << disp 
+	    << "<table><tr valign=top> <td colspan=3><center>\n";
+  }
   int numpages = sections.docInfo.size();
   textout << outconvert << disp 
@@ -182,5 +192,5 @@
   if (!found)
     nextarrow = "<a href=\"_httpdocument_&cl=" + args["cl"] + "&d=" + sections.docInfo[0].OID + 
-      "\">" + sections.docInfo[0].metadata["Title"].values[0] + "_iconnext_";
+      "\">" + sections.docInfo[0].metadata["Title"].values[0] + "_iconnext_</a>\n";
   else {
     if (!previousOID.empty())
@@ -200,2 +210,8 @@
   return 0;
 }
+
+
+
+
+
+
Index: trunk/gsdl/src/recpt/pagedbrowserclass.h
===================================================================
--- trunk/gsdl/src/recpt/pagedbrowserclass.h	(revision 927)
+++ trunk/gsdl/src/recpt/pagedbrowserclass.h	(revision 928)
@@ -41,5 +41,5 @@
 
   // returns the name that specifies the browserclass type
-  text_t get_browser_name ();
+  virtual text_t get_browser_name ();
 
   void load_metadata_defaults (text_tset &metadata);
@@ -49,4 +49,5 @@
   virtual void processOID (cgiargsclass &args, recptproto *collectproto,
 			   ostream &logout);
+
 
   virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
@@ -65,2 +66,3 @@
 
 #endif
+
Index: trunk/gsdl/src/recpt/queryaction.cpp
===================================================================
--- trunk/gsdl/src/recpt/queryaction.cpp	(revision 927)
+++ trunk/gsdl/src/recpt/queryaction.cpp	(revision 928)
@@ -28,4 +28,7 @@
 /*
    $Log$
+   Revision 1.34  2000/02/15 22:53:52  kjm18
+   search history stuff added.
+
    Revision 1.33  2000/01/24 22:57:59  sjboddie
    fixed bug in cross-collection searching, tidied up a bit
@@ -151,4 +154,8 @@
 #include "cgiutils.h"
 #include "OIDtools.h"
+//#include "infodbclass.h"
+#include "fileutil.h"
+#include "text_t.h"
+#include "historydb.h"
 
 void colinfo_t::clear () {
@@ -347,4 +354,40 @@
   arg_ainfo.savedarginfo = cgiarginfo::must;
   argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "hd" history display  - search history only displayed when
+  // this var set.
+  arg_ainfo.shortname = "hd";
+  arg_ainfo.longname = "history display";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+	    
+
+ // "hs"  save - set to 1 in query form, so only save when submit
+  // query
+  // 0 = no save 1 = save
+  arg_ainfo.shortname = "hs";
+  arg_ainfo.longname = "history save";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+
+  // "hcl" compress the list (show only the last 5)
+  // 0 = expand, 1 = compress
+
+  arg_ainfo.shortname = "hcl";
+  arg_ainfo.longname = "history compress list";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+
 
 }
@@ -407,4 +450,26 @@
     cgiarginfo *rinfo = argsinfo.getarginfo ("r");
     if (rinfo != NULL) args["r"] = rinfo->argdefault;
+  }
+  //check hd argument
+    int arg_hd = args.getintarg("hd");
+    if (arg_hd !=0 && arg_hd !=1) {
+      logout << "Warning: \"hd\" argument out of range (" << arg_hd << ")\n";
+          cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
+	      if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
+    }  
+  //check hs argument
+  int arg_hs = args.getintarg("hs");
+  if (arg_hs !=0 && arg_hs !=1) {
+    logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
+    cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
+    if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
+  }
+
+  // chech hcl argument
+  int arg_hcl = args.getintarg("hcl");
+  if (arg_hcl !=0 && arg_hcl !=1) {
+    logout << "Warning: \"hcl\" argument out of range (" << arg_hcl << ")\n";
+    cgiarginfo *hclinfo = argsinfo.getarginfo ("hcl");
+    if (hclinfo != NULL) args["hcl"] = hclinfo->argdefault;
   }
 
@@ -505,5 +570,5 @@
   // _cq2selection                the selection box for combining two queries
   
-  
+
   // can't do anything if collectproto is null (i.e. no collection was specified)
   recptproto *collectproto = protos->getrecptproto (args["c"], logout);
@@ -539,5 +604,203 @@
     it = response.filterOptions.find ("CombineQuery");
     if (it != end) set_option_macro ("cq2", args["cq2"], (*it).second, disp);
-  }
+
+    // define_history_macros(disp, args, protos, logout);
+  }
+} // define external macros
+
+void queryaction::define_history_macros(displayclass &disp, cgiargsclass &args, 
+					  recptprotolistclass *protos, ostream &logout) {
+
+  // defines the following macros
+
+  // _searchhistorylist_
+
+  text_t historylist;
+  int arghd = args.getintarg("hd");
+  if (arghd != 1) {
+    historylist="";
+  }
+  else {
+    historylist = "<!-- Search History List -->\n";
+   
+    text_t userid = args["z"];
+    text_tarray entries;
+    if (get_history_info(userid, entries)) {
+      int count = 1;
+      text_tarray::iterator here = entries.begin();
+      text_tarray::iterator end = entries.end();
+      int size=(int)entries.size();
+      if (args["hcl"]==1&&size>5) { // compress the list 
+	here = end-5;
+	count=size-4;
+      }
+      historylist += "<table align=center width=500 border=0>\n";
+      historylist += "<tr><td width=300 align=center>";
+      historylist += "<a href=\"_httpclearhistory_\">Clear History</a></td>\n";
+
+      if (size>5&&args["hcl"]==1) { //compress the list, show the expand button
+       
+      historylist += "<td><a href=\"_gwcgi_?e=_compressedoptions_&a=q&hcl=0\">Expand</a></td>\n";
+      }
+      else if (size >5 && args["hcl"]==0) { // expand the list, show contract button
+	historylist += "<td><a href=\"_gwcgi_?e=_compressedoptions_&a=q&hcl=1\">Contract</a></td>\n";
+      }
+      historylist += "</table>\n";
+      historylist += " <table align=center width=500 border=1> \n <tr><th colspan=4 align=center>";
+      historylist += "Search History </th></tr>\n";
+      historylist += "<tr><th width=40>#</th>\n<th width=340>Query</th>\n";
+      historylist += "<th width=60>Results</th><th width=60>View</th></tr>\n";
+
+      while (here !=end ) {
+	text_t c;
+	text_t query;
+	text_t q = "q";
+	text_t numdocs;
+	text_t cgiargs;
+	text_t  userinfo;
+	split_saved_query(*here,c, numdocs, cgiargs);
+	parse_saved_args(cgiargs, q, query); // get query string out
+	decode_cgi_arg(query); // un cgisafe it
+	
+	format_user_info(cgiargs, userinfo, protos, logout);
+	
+	historylist += "<tr> <td width=40 align=center>"+c+"</td>\n";
+	historylist += "<td width=340 align=left>"+query+"</td><td width=60 align=center>"+numdocs+"</td>\n";
+	historylist += "<td width=60 align=center><a href=\"_gwcgi_?e=_compressedoptions_&";
+	historylist += *here+"\"><img name=\"display\" src=\"/gsdl/images/display.gif\" width=\"60\" ";
+	historylist += "height=\"20\" border=\"0\" alt=\"" + userinfo +"\"></a></td></tr>\n";
+	  here++;
+	  count++;
+      }
+      historylist+="</table>\n\n";
+     
+    } // if
+    else {
+      historylist += "Search history not available\n";
+    }
+    historylist += "<p><! ---- end of history list ----->\n";
+  } // else display list
+  disp.setmacro("searchhistorylist", "query", historylist);
+  
+} // define history macros
+
+void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
+			      displayclass &disp, outconvertclass &outconvert,
+			      ostream &textout, ostream &logout) {
+
+  ColInfoResponse_t cinfo;
+  comerror_t err;
+  InfoFilterOptionsResponse_t fresponse;
+  InfoFilterOptionsRequest_t frequest;
+  frequest.filterName = "QueryFilter";
+
+  text_t &index = args["h"];
+  text_t &subcollection = args["j"];
+  text_t &language = args["n"];
+
+  text_tset collections;
+  text_t arg_cc = args["cc"];
+  decode_cgi_arg (arg_cc);
+  splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
+
+  textout << outconvert << disp << "_query:header_\n"
+	  << "<center>_navigationbar_</center><br>\n"
+	  << "<form name=QueryForm method=get action=\"_gwcgi_\">\n"
+	  << "<input type=hidden name=a value=\"q\">\n"
+	  << "<input type=hidden name=e value=\"_compressedoptions_\">\n"
+	  << "<input type=hidden name=ccp value=\"1\">\n"
+	  << "<center><table width=_pagewidth_><tr valign=top>\n"
+	  << "<td>Select collections to search for \"" << args["q"] 
+	  << "\" <i>(index=" << index << " subcollection=" << subcollection
+	  << " language=" << language << ")</i></td>\n"
+	  << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
+	  << "</tr></table></center>\n"
+	  << "<center><table width=_pagewidth_>\n"
+	  << "<tr><td>\n";
+  
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+      
+      text_tarray collist;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+	while (collist_here != collist_end) {
+	  
+	  (*rprotolist_here).p->get_collectinfo (*collist_here, cinfo, err, logout);
+	  //	  if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
+	  if (err == noError && (cinfo.buildDate > 0)) {
+	    
+	    (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
+	    if (err == noError) {
+	      
+	      FilterOption_tmap::const_iterator it;
+	      FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
+	      if (!index.empty()) {
+		it = fresponse.filterOptions.find ("Index");
+		if (it == end) {collist_here ++; continue;}
+		text_tarray::const_iterator there = (*it).second.validValues.begin();
+		text_tarray::const_iterator tend = (*it).second.validValues.end();
+		while (there != tend) {
+		  if (*there == index) break;
+		  there ++;
+		}
+		if (there == tend) {collist_here++; continue;}
+	      }
+	      if (!subcollection.empty()) {
+		it = fresponse.filterOptions.find ("Subcollection");
+		if (it == end) {collist_here++; continue;}
+		text_tarray::const_iterator there = (*it).second.validValues.begin();
+		text_tarray::const_iterator tend = (*it).second.validValues.end();
+		while (there != tend) {
+		  if (*there == subcollection) break;
+		  there ++;
+		}
+		if (there == tend) {collist_here++; continue;}
+	      }
+	      if (!language.empty()) {
+		it = fresponse.filterOptions.find ("Language");
+		if (it == end) {collist_here++; continue;}
+		text_tarray::const_iterator there = (*it).second.validValues.begin();
+		text_tarray::const_iterator tend = (*it).second.validValues.end();
+		while (there != tend) {
+		  if (*there == language) break;
+		  there ++;
+		}
+		if (there == tend) {collist_here++; continue;}
+	      }
+	    
+	      // we've got a matching collection
+	      textout << "<input type=checkbox";
+
+	      text_tset::const_iterator t = collections.find (*collist_here);
+	      if (t != collections.end()) textout << " checked";
+
+	      textout << outconvert 
+		      << " name=cc value=\"" << *collist_here << "\">";
+	      
+	      if (!cinfo.collectionmeta["collectionname"].empty())
+		textout << outconvert << disp << cinfo.collectionmeta["collectionname"];
+	      else 
+		textout << outconvert << *collist_here;
+	      
+	      textout << "<br>\n";
+	      
+	    }
+	  }
+	  collist_here ++;
+	}
+      }
+    }
+    rprotolist_here ++;
+  }
+  textout << outconvert << disp 
+	  << "</td></tr></table></center>\n"
+	  << "</form>\n"
+	  << "_query:footer_\n";
+  
 }
 
@@ -823,4 +1086,7 @@
   request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
   text_t formattedstring = args["q"];
+  if (!combine_query(args["z"], formattedstring)) {
+    args["q"]=formattedstring;
+  }
   format_querystring (formattedstring, args.getintarg("b"));
   set_queryfilter_options (request, formattedstring, args);
@@ -833,6 +1099,13 @@
     return false;
   }
+
   
   define_query_macros (args, disp, response);
+
+  // save the query if appropriate
+  if (!save_search_history(args, response))
+    logout << "save failed";
+
+  define_history_macros(disp, args, protos, logout);
 
   textout << outconvert << disp << "_query:header_\n"
@@ -859,4 +1132,5 @@
 void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp, 
 				       const FilterResponse_t &response) { 
+
   // set up _freqmsg_ and _quotedquery_ macros
   text_t freqmsg = "_textfreqmsg1_";
@@ -920,4 +1194,40 @@
 }
 
-
-
+bool queryaction::save_search_history (cgiargsclass &args, const FilterResponse_t &response) 
+{
+  if (args["q"]=="") return true; // null query, dont save
+  if (args["hs"]=="0") return true; // only save when submit query
+
+  // get userid
+  text_t userid = args["z"];
+
+  // the number of docs goes on the front of the query string
+  int numdocs= response.numDocs;
+  text_t query = text_t(numdocs);
+  if (response.isApprox==MoreThan) { // there were more docs found
+    query.push_back('+');
+  }
+  query += "a=q";
+  query += "&c="+args["c"];
+  query += "&h="+args["h"];
+  query += "&t="+args["t"];
+  query += "&b="+args["b"];
+  query += "&j="+args["j"];
+  query += "&n="+args["n"];
+  query += "&s="+args["s"];
+  query += "&k="+args["k"];
+   
+  text_t qstring = args["q"];
+  text_t formattedquery =cgi_safe(qstring);
+  query += "&q="+formattedquery;
+
+  if (set_history_info(userid, query)) return true;
+  else return false;
+
+
+}
+
+
+
+
+
Index: trunk/gsdl/src/recpt/queryaction.h
===================================================================
--- trunk/gsdl/src/recpt/queryaction.h	(revision 927)
+++ trunk/gsdl/src/recpt/queryaction.h	(revision 928)
@@ -64,9 +64,18 @@
   int num_phrases;
 
-  void define_query_macros (cgiargsclass &args, displayclass &disp, 
+  virtual void define_query_macros (cgiargsclass &args, displayclass &disp, 
 			    const FilterResponse_t &response); 
 
-  void set_option_macro (const text_t &macroname, text_t current_value,
+  void define_history_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  virtual void set_option_macro (const text_t &macroname, text_t current_value,
 			 const FilterOption_t &option, displayclass &disp);
+
+  void output_ccp (cgiargsclass &args, recptprotolistclass *protos,
+		   displayclass &disp, outconvertclass &outconvert,
+		   ostream &textout, ostream &logout);
+  
+
 
   bool search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
@@ -75,4 +84,5 @@
 				    ostream &logout);
 
+
   bool search_single_collection (cgiargsclass &args, const text_t &collection,
 				 recptprotolistclass *protos, browsermapclass *browsers, 
@@ -80,4 +90,7 @@
 				 ostream &textout, ostream &logout);
 
+
+  bool save_search_history(cgiargsclass &args, const FilterResponse_t &response); 
+  bool delete_search_history(cgiargsclass &args);
 
 public:
@@ -88,7 +101,7 @@
   bool init (ostream &logout);
   
-  text_t get_action_name () {return "q";}
+  virtual text_t get_action_name () {return "q";}
   
-  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+  virtual bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
 		      ostream &logout);
 
@@ -97,11 +110,11 @@
 			 ostream &logout);
   
-  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+  virtual void define_internal_macros (displayclass &disp, cgiargsclass &args, 
 			       recptprotolistclass *protos, ostream &logout);
 
-  void define_external_macros (displayclass &disp, cgiargsclass &args, 
+  virtual void define_external_macros (displayclass &disp, cgiargsclass &args, 
 			       recptprotolistclass *protos, ostream &logout);
   
-  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+  virtual bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
 		  browsermapclass *browsers, displayclass &disp, 
 		  outconvertclass &outconvert, ostream &textout, 
@@ -110,2 +123,4 @@
 
 #endif
+
+
