source: trunk/gsdl/src/recpt/queryaction.cpp@ 962

Last change on this file since 962 was 962, checked in by sjboddie, 24 years ago

actions are now configured with gsdlhome

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 40.9 KB
Line 
1/**********************************************************************
2 *
3 * queryaction.cpp --
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 * $Id: queryaction.cpp 962 2000-02-21 21:57:50Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.36 2000/02/21 21:57:48 sjboddie
31 actions are now configured with gsdlhome
32
33 Revision 1.35 2000/02/17 02:35:04 sjboddie
34 tidied up search history stuff a bit - replaced strings with macros
35
36 Revision 1.34 2000/02/15 22:53:52 kjm18
37 search history stuff added.
38
39 Revision 1.33 2000/01/24 22:57:59 sjboddie
40 fixed bug in cross-collection searching, tidied up a bit
41
42 Revision 1.32 1999/12/13 02:54:11 davidb
43 Support for cross collection searching (CCS)
44
45 Revision 1.31 1999/12/05 21:22:33 sjboddie
46 tidied up cross-collection searching a bit
47
48 Revision 1.30 1999/11/08 20:26:37 sjboddie
49 added multiplevalue option to cgiarginfo
50
51 Revision 1.29 1999/11/04 20:29:53 sjboddie
52 small change to cross-collection searching
53
54 Revision 1.28 1999/11/03 22:49:50 sjboddie
55 A few changes to cross-collection searching for fao
56
57 Revision 1.27 1999/11/01 21:53:27 sjboddie
58 added cross-collection searching capability - still needs lots of
59 work but the basic functionality is there
60
61 Revision 1.26 1999/10/10 08:14:10 sjboddie
62 - metadata now returns mp rather than array
63 - redesigned browsing support (although it's not finished so
64 won't currently work ;-)
65
66 Revision 1.25 1999/09/24 04:49:39 sjboddie
67 fixed up the query selection boxes to display properly if there's only
68 a single index/sub-collection
69
70 Revision 1.24 1999/09/22 03:44:31 sjboddie
71 EndResults query filter option may now take '-1' for 'all'
72
73 Revision 1.23 1999/09/21 11:34:42 sjboddie
74 added Maxdocs queryfilter option which may be -1 for 'all'
75
76 Revision 1.22 1999/09/07 23:08:51 rjmcnab
77 removed some compiler warnings
78
79 Revision 1.21 1999/09/07 04:56:57 sjboddie
80 added GPL notice
81
82 Revision 1.20 1999/08/25 04:47:55 sjboddie
83 added advanced search option - other minor changes
84
85 Revision 1.19 1999/08/13 04:17:24 sjboddie
86 small change to do with new collection-level metadata
87
88 Revision 1.18 1999/08/10 22:46:33 sjboddie
89 changed format option result to QueryResults and added QueryLinks option
90
91 Revision 1.17 1999/07/30 02:24:42 sjboddie
92 added collectinfo argument to some functions
93
94 Revision 1.16 1999/07/19 00:16:58 sjboddie
95 no longer display documents that don't match all phrases in query string
96
97 Revision 1.15 1999/07/16 08:33:36 rjmcnab
98 Changed the logic for getting the results string slightly
99
100 Revision 1.14 1999/07/16 03:41:29 sjboddie
101 changed isApprox
102
103 Revision 1.13 1999/07/16 00:19:01 sjboddie
104 some changes to the way quoted queries are handled
105
106 Revision 1.12 1999/07/09 02:17:55 rjmcnab
107 Setting macros needed for a second query.
108
109 Revision 1.11 1999/07/07 06:13:10 rjmcnab
110 Added ability to combine two independant queries.
111
112 Revision 1.10 1999/07/07 05:49:35 sjboddie
113 had another crack at the format string code - created a new formattools
114 module. It can now handle {If} and {Or} statements although there's a
115 bug preventing nested if's and or's.
116
117 Revision 1.9 1999/07/01 22:48:46 sjboddie
118 had a go at getting a query result format string working
119
120 Revision 1.8 1999/06/27 22:02:11 sjboddie
121 author is added to queryresults if there is one
122
123 Revision 1.7 1999/06/26 01:10:18 rjmcnab
124 Made h, i, and n arguments saved in the compressed arguments.
125
126 Revision 1.6 1999/06/24 05:12:25 sjboddie
127 lots of small changes
128
129 Revision 1.5 1999/06/16 04:03:48 sjboddie
130 Now sets "cl" arg to "search" when going to a document from a search
131 results page. This allows the close book icon (in hierarchy toc) to
132 take you back to the results page if that's where you came from.
133 If you got to the document page somehow other than from a
134 classification or a search (i.e. if "cl" isn't set) then the close
135 book icon is disabled
136
137 Revision 1.4 1999/06/16 02:08:38 sjboddie
138 got queryaction working
139
140 Revision 1.3 1999/03/25 03:06:45 sjboddie
141
142 altered receptionist slightly so it now passes *collectproto to
143 define_internal_macros and define_external_macros - need it
144 for browseaction
145
146 Revision 1.2 1999/03/03 20:26:50 rjmcnab
147
148 Modified stuff.
149
150 Revision 1.1 1999/02/28 22:45:21 rjmcnab
151
152 Initial revision.
153
154 */
155
156
157#include "queryaction.h"
158#include "querytools.h"
159#include "formattools.h"
160#include "cgiutils.h"
161#include "OIDtools.h"
162//#include "infodbclass.h"
163#include "fileutil.h"
164#include "text_t.h"
165#include "historydb.h"
166
167void colinfo_t::clear () {
168 formatlistptr = NULL;
169 browserptr = NULL;
170}
171
172void QueryResult_t::clear() {
173 doc.clear();
174 collection.clear();
175}
176
177queryaction::queryaction () {
178
179 num_phrases = 0;
180
181 // this action uses cgi variable "a"
182 cgiarginfo arg_ainfo;
183 arg_ainfo.shortname = "a";
184 arg_ainfo.longname = "action";
185 arg_ainfo.multiplechar = true;
186 arg_ainfo.defaultstatus = cgiarginfo::weak;
187 arg_ainfo.argdefault = "q";
188 arg_ainfo.savedarginfo = cgiarginfo::must;
189 argsinfo.addarginfo (NULL, arg_ainfo);
190
191 // "b" - 0 = simple, 1 = advanced
192 arg_ainfo.shortname = "b";
193 arg_ainfo.longname = "query mode";
194 arg_ainfo.multiplechar = false;
195 arg_ainfo.defaultstatus = cgiarginfo::weak;
196 arg_ainfo.argdefault = "0";
197 arg_ainfo.savedarginfo = cgiarginfo::must;
198 argsinfo.addarginfo (NULL, arg_ainfo);
199
200 // "h"
201 arg_ainfo.shortname = "h";
202 arg_ainfo.longname = "main index";
203 arg_ainfo.multiplechar = true;
204 arg_ainfo.defaultstatus = cgiarginfo::weak;
205 arg_ainfo.argdefault = "";
206 arg_ainfo.savedarginfo = cgiarginfo::must;
207 argsinfo.addarginfo (NULL, arg_ainfo);
208
209 // "h2"
210 arg_ainfo.shortname = "h2";
211 arg_ainfo.longname = "main index for second query";
212 arg_ainfo.multiplechar = true;
213 arg_ainfo.defaultstatus = cgiarginfo::weak;
214 arg_ainfo.argdefault = "";
215 arg_ainfo.savedarginfo = cgiarginfo::must;
216 argsinfo.addarginfo (NULL, arg_ainfo);
217
218 // "j"
219 arg_ainfo.shortname = "j";
220 arg_ainfo.longname = "sub collection index";
221 arg_ainfo.multiplechar = true;
222 arg_ainfo.defaultstatus = cgiarginfo::weak;
223 arg_ainfo.argdefault = "";
224 arg_ainfo.savedarginfo = cgiarginfo::must;
225 argsinfo.addarginfo (NULL, arg_ainfo);
226
227 // "j2"
228 arg_ainfo.shortname = "j2";
229 arg_ainfo.longname = "sub collection index for second query";
230 arg_ainfo.multiplechar = true;
231 arg_ainfo.defaultstatus = cgiarginfo::weak;
232 arg_ainfo.argdefault = "";
233 arg_ainfo.savedarginfo = cgiarginfo::must;
234 argsinfo.addarginfo (NULL, arg_ainfo);
235
236 // "n"
237 arg_ainfo.shortname = "n";
238 arg_ainfo.longname = "language index";
239 arg_ainfo.multiplechar = true;
240 arg_ainfo.defaultstatus = cgiarginfo::weak;
241 arg_ainfo.argdefault = "";
242 arg_ainfo.savedarginfo = cgiarginfo::must;
243 argsinfo.addarginfo (NULL, arg_ainfo);
244
245 // "n2"
246 arg_ainfo.shortname = "n2";
247 arg_ainfo.longname = "language index for second query";
248 arg_ainfo.multiplechar = true;
249 arg_ainfo.defaultstatus = cgiarginfo::weak;
250 arg_ainfo.argdefault = "";
251 arg_ainfo.savedarginfo = cgiarginfo::must;
252 argsinfo.addarginfo (NULL, arg_ainfo);
253
254 // "q"
255 arg_ainfo.shortname = "q";
256 arg_ainfo.longname = "query string";
257 arg_ainfo.multiplechar = true;
258 arg_ainfo.defaultstatus = cgiarginfo::weak;
259 arg_ainfo.argdefault = "";
260 arg_ainfo.savedarginfo = cgiarginfo::must;
261 argsinfo.addarginfo (NULL, arg_ainfo);
262
263 // "q2"
264 arg_ainfo.shortname = "q2";
265 arg_ainfo.longname = "query string for second query";
266 arg_ainfo.multiplechar = true;
267 arg_ainfo.defaultstatus = cgiarginfo::weak;
268 arg_ainfo.argdefault = "";
269 arg_ainfo.savedarginfo = cgiarginfo::must;
270 argsinfo.addarginfo (NULL, arg_ainfo);
271
272 // "cq2" ""=don't combine, "and", "or", "not"
273 arg_ainfo.shortname = "cq2";
274 arg_ainfo.longname = "combine queries";
275 arg_ainfo.multiplechar = true;
276 arg_ainfo.defaultstatus = cgiarginfo::weak;
277 arg_ainfo.argdefault = "";
278 arg_ainfo.savedarginfo = cgiarginfo::must;
279 argsinfo.addarginfo (NULL, arg_ainfo);
280
281 // "t" - 1 = ranked 0 = boolean
282 arg_ainfo.shortname = "t";
283 arg_ainfo.longname = "search type";
284 arg_ainfo.multiplechar = false;
285 arg_ainfo.defaultstatus = cgiarginfo::weak;
286 arg_ainfo.argdefault = "1";
287 arg_ainfo.savedarginfo = cgiarginfo::must;
288 argsinfo.addarginfo (NULL, arg_ainfo);
289
290 // "k"
291 arg_ainfo.shortname = "k";
292 arg_ainfo.longname = "casefolding";
293 arg_ainfo.multiplechar = false;
294 arg_ainfo.defaultstatus = cgiarginfo::weak;
295 arg_ainfo.argdefault = "1";
296 arg_ainfo.savedarginfo = cgiarginfo::must;
297 argsinfo.addarginfo (NULL, arg_ainfo);
298
299 // "s"
300 arg_ainfo.shortname = "s";
301 arg_ainfo.longname = "stemming";
302 arg_ainfo.multiplechar = false;
303 arg_ainfo.defaultstatus = cgiarginfo::weak;
304 arg_ainfo.argdefault ="0";
305 arg_ainfo.savedarginfo = cgiarginfo::must;
306 argsinfo.addarginfo (NULL, arg_ainfo);
307
308 // "m"
309 arg_ainfo.shortname = "m";
310 arg_ainfo.longname = "maximum number of documents";
311 arg_ainfo.multiplechar = true;
312 arg_ainfo.defaultstatus = cgiarginfo::weak;
313 arg_ainfo.argdefault = "50";
314 arg_ainfo.savedarginfo = cgiarginfo::must;
315 argsinfo.addarginfo (NULL, arg_ainfo);
316
317 // "o"
318 arg_ainfo.shortname = "o";
319 arg_ainfo.longname = "hits per page";
320 arg_ainfo.multiplechar = true;
321 arg_ainfo.defaultstatus = cgiarginfo::weak;
322 arg_ainfo.argdefault = "20";
323 arg_ainfo.savedarginfo = cgiarginfo::must;
324 argsinfo.addarginfo (NULL, arg_ainfo);
325
326 // "r"
327 arg_ainfo.shortname = "r";
328 arg_ainfo.longname = "start results from";
329 arg_ainfo.multiplechar = true;
330 arg_ainfo.defaultstatus = cgiarginfo::weak;
331 arg_ainfo.argdefault = "1";
332 arg_ainfo.savedarginfo = cgiarginfo::must;
333 argsinfo.addarginfo (NULL, arg_ainfo);
334
335 // "ccs"
336 arg_ainfo.shortname = "ccs";
337 arg_ainfo.longname = "cross collection searching";
338 arg_ainfo.multiplechar = false;
339 arg_ainfo.defaultstatus = cgiarginfo::weak;
340 arg_ainfo.argdefault = "0";
341 arg_ainfo.savedarginfo = cgiarginfo::must;
342 argsinfo.addarginfo (NULL, arg_ainfo);
343
344 // "ccp"
345 arg_ainfo.shortname = "ccp";
346 arg_ainfo.longname = "cross collection page";
347 arg_ainfo.multiplechar = false;
348 arg_ainfo.defaultstatus = cgiarginfo::weak;
349 arg_ainfo.argdefault = "0";
350 arg_ainfo.savedarginfo = cgiarginfo::must;
351 argsinfo.addarginfo (NULL, arg_ainfo);
352
353 // "cc"
354 arg_ainfo.shortname = "cc";
355 arg_ainfo.longname = "collections to search";
356 arg_ainfo.multiplechar = true;
357 arg_ainfo.multiplevalue = true;
358 arg_ainfo.defaultstatus = cgiarginfo::weak;
359 arg_ainfo.argdefault = "";
360 arg_ainfo.savedarginfo = cgiarginfo::must;
361 argsinfo.addarginfo (NULL, arg_ainfo);
362
363 // "hd" history display - search history only displayed when
364 // this var set.
365 arg_ainfo.shortname = "hd";
366 arg_ainfo.longname = "history display";
367 arg_ainfo.multiplechar = false;
368 arg_ainfo.multiplevalue = false;
369 arg_ainfo.defaultstatus = cgiarginfo::weak;
370 arg_ainfo.argdefault = "0";
371 arg_ainfo.savedarginfo = cgiarginfo::must;
372 argsinfo.addarginfo (NULL, arg_ainfo);
373
374
375 // "hs" save - set to 1 in query form, so only save when submit
376 // query
377 // 0 = no save 1 = save
378 arg_ainfo.shortname = "hs";
379 arg_ainfo.longname = "history save";
380 arg_ainfo.multiplechar = false;
381 arg_ainfo.defaultstatus = cgiarginfo::weak;
382 arg_ainfo.argdefault = "0";
383 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
384 argsinfo.addarginfo (NULL, arg_ainfo);
385
386
387 // "hcl" compress the list (show only the last 5)
388 // 0 = expand, 1 = compress
389
390 arg_ainfo.shortname = "hcl";
391 arg_ainfo.longname = "history compress list";
392 arg_ainfo.multiplechar = false;
393 arg_ainfo.defaultstatus = cgiarginfo::weak;
394 arg_ainfo.argdefault = "1";
395 arg_ainfo.savedarginfo = cgiarginfo::must;
396 argsinfo.addarginfo (NULL, arg_ainfo);
397
398
399
400}
401
402void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
403 action::configure (key, cfgline);
404}
405
406bool queryaction::init (ostream &logout) {
407 return action::init (logout);
408}
409
410bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
411 ostream &logout) {
412
413 // check t argument
414 int arg_t = args.getintarg("t");
415 if (arg_t != 0 && arg_t != 1) {
416 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
417 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
418 if (tinfo != NULL) args["t"] = tinfo->argdefault;
419 }
420
421 // check k argument
422 int arg_k = args.getintarg("k");
423 if (arg_k != 0 && arg_k != 1) {
424 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
425 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
426 if (kinfo != NULL) args["k"] = kinfo->argdefault;
427 }
428
429 // check s argument
430 int arg_s = args.getintarg("s");
431 if (arg_s != 0 && arg_s != 1) {
432 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
433 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
434 if (sinfo != NULL) args["s"] = sinfo->argdefault;
435 }
436
437 // check m argument
438 int arg_m = args.getintarg("m");
439 if (arg_m < -1) {
440 logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
441 cgiarginfo *minfo = argsinfo.getarginfo ("m");
442 if (minfo != NULL) args["m"] = minfo->argdefault;
443 }
444
445 // check o argument
446 int arg_o = args.getintarg("o");
447 if (arg_o < -1) {
448 logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
449 cgiarginfo *oinfo = argsinfo.getarginfo ("o");
450 if (oinfo != NULL) args["o"] = oinfo->argdefault;
451 }
452
453 // check r argument
454 int arg_r = args.getintarg("r");
455 if (arg_r < 1) {
456 logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
457 cgiarginfo *rinfo = argsinfo.getarginfo ("r");
458 if (rinfo != NULL) args["r"] = rinfo->argdefault;
459 }
460 //check hd argument
461 int arg_hd = args.getintarg("hd");
462 if (arg_hd !=0 && arg_hd !=1) {
463 logout << "Warning: \"hd\" argument out of range (" << arg_hd << ")\n";
464 cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
465 if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
466 }
467 //check hs argument
468 int arg_hs = args.getintarg("hs");
469 if (arg_hs !=0 && arg_hs !=1) {
470 logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
471 cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
472 if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
473 }
474
475 // chech hcl argument
476 int arg_hcl = args.getintarg("hcl");
477 if (arg_hcl !=0 && arg_hcl !=1) {
478 logout << "Warning: \"hcl\" argument out of range (" << arg_hcl << ")\n";
479 cgiarginfo *hclinfo = argsinfo.getarginfo ("hcl");
480 if (hclinfo != NULL) args["hcl"] = hclinfo->argdefault;
481 }
482
483 return true;
484}
485
486void queryaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
487 response_t &response, text_t &response_data,
488 ostream &/*logout*/) {
489 response = content;
490 response_data = "text/html";
491}
492
493void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
494 recptprotolistclass * /*protos*/,
495 ostream &/*logout*/) {
496
497 // define_internal_macros sets the following macros:
498
499 // _quotedquery_ the part of the query string that was quoted for post-processing
500
501
502
503 // The following macros are set later (in define_query_macros) as they can't be set until
504 // the query has been done.
505
506 // _freqmsg_ the term frequency string
507
508 // _resultline_ the "x documents matched the query" string
509
510 // _prevfirst_ these are used when setting up the links to previous/next
511 // _prevlast_ pages of results (_thisfirst_ and _thislast_ are used to set
512 // _nextfirst_ the 'results x-x for query: xxxx' string in the title bar)
513 // _nextlast_
514 // _thisfirst_
515 // _thislast_
516
517
518 // get the quoted bits of the query string and set _quotedquery_
519 text_tarray phrases;
520 get_phrases (args["q"], phrases);
521 num_phrases = phrases.size();
522 text_tarray::const_iterator phere = phrases.begin();
523 text_tarray::const_iterator pend = phrases.end();
524 bool first = true;
525 text_t quotedquery;
526 while (phere != pend) {
527 if (!first)
528 if ((phere +1) == pend) quotedquery += " and ";
529 else quotedquery += ", ";
530
531 quotedquery += "\"" + *phere + "\"";
532 first = false;
533 phere ++;
534 }
535 if (args.getintarg("s")) quotedquery += "_textstemon_";
536 disp.setmacro ("quotedquery", "query", quotedquery);
537}
538
539// sets the selection box macros _hselection_, _jselection_, and _nselection_.
540void queryaction::set_option_macro (const text_t &macroname, text_t current_value,
541 const FilterOption_t &option, displayclass &disp) {
542
543 if (option.validValues.empty()) return;
544 else if (option.validValues.size() == 1) {
545 disp.setmacro (macroname + "selection", "Global", "_" + option.defaultValue + "_");
546 return;
547 }
548 if (option.validValues.size() < 2) return;
549
550 text_t macrovalue = "<select name=\"" + macroname + "\">\n";
551
552 if (current_value.empty()) current_value = option.defaultValue;
553
554 text_tarray::const_iterator thisvalue = option.validValues.begin();
555 text_tarray::const_iterator endvalue = option.validValues.end();
556
557 while (thisvalue != endvalue) {
558 macrovalue += "<option value=\"" + *thisvalue + "\"";
559 if (*thisvalue == current_value)
560 macrovalue += " selected";
561 macrovalue += ">_" + *thisvalue + "_\n";
562 thisvalue ++;
563 }
564 macrovalue += "</select>\n";
565 disp.setmacro (macroname + "selection", "Global", macrovalue);
566}
567
568void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args,
569 recptprotolistclass *protos, ostream &logout) {
570
571 // define_external_macros sets the following macros:
572
573 // some or all of these may not be required to be set
574 // _hselection_, _h2selection_ the selection box for the main part of the index
575 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
576 // _nselection_, _n2selection_ the selection box for the language part of the index
577 // _cq2selection the selection box for combining two queries
578
579
580 // can't do anything if collectproto is null (i.e. no collection was specified)
581 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
582 if (collectproto == NULL) return;
583
584 comerror_t err;
585 InfoFilterOptionsResponse_t response;
586 InfoFilterOptionsRequest_t request;
587 request.filterName = "QueryFilter";
588
589 collectproto->get_filteroptions (args["c"], request, response, err, logout);
590 if (err == noError) {
591
592 FilterOption_tmap::const_iterator it;
593 FilterOption_tmap::const_iterator end = response.filterOptions.end();
594
595 // _hselection_ and _h2selection_ (Index)
596 it = response.filterOptions.find ("Index");
597 if (it != end) set_option_macro ("h", args["h"], (*it).second, disp);
598 if (it != end) set_option_macro ("h2", args["h2"], (*it).second, disp);
599
600 // _jselection_ and _j2selection_ (Subcollection)
601 it = response.filterOptions.find ("Subcollection");
602 if (it != end) set_option_macro ("j", args["j"], (*it).second, disp);
603 if (it != end) set_option_macro ("j2", args["j2"], (*it).second, disp);
604
605 // _nselection_ and _n2selection_ (Language)
606 it = response.filterOptions.find ("Language");
607 if (it != end) set_option_macro ("n", args["n"], (*it).second, disp);
608 if (it != end) set_option_macro ("n2", args["n2"], (*it).second, disp);
609
610 // _cq2selection_ (CombineQuery)
611 it = response.filterOptions.find ("CombineQuery");
612 if (it != end) set_option_macro ("cq2", args["cq2"], (*it).second, disp);
613
614 // define_history_macros(disp, args, protos, logout);
615 }
616} // define external macros
617
618void queryaction::define_history_macros (displayclass &disp, cgiargsclass &args,
619 recptprotolistclass *protos, ostream &logout) {
620
621 // defines the following macros
622
623 // _searchhistorylist_
624
625 text_t historylist;
626 int arghd = args.getintarg("hd");
627 if (arghd != 1) {
628 historylist="";
629 }
630 else {
631 historylist = "<!-- Search History List -->\n";
632
633 text_t userid = args["z"];
634 text_tarray entries;
635 if (get_history_info (userid, entries, gsdlhome, logout)) {
636 int count = 1;
637 text_tarray::iterator here = entries.begin();
638 text_tarray::iterator end = entries.end();
639 int size=(int)entries.size();
640 if (args["hcl"]==1&&size>5) { // compress the list
641 here = end-5;
642 count=size-4;
643 }
644 historylist += "<table align=center width=500 border=0>\n";
645 historylist += "<tr><td width=300 align=center>";
646 historylist += "<a href=\"_httpclearhistory_\">_textclearhistory_</a></td>\n";
647
648 if (size>5&&args["hcl"]==1) { //compress the list, show the expand button
649
650 historylist += "<td><a href=\"_gwcgi_?e=_compressedoptions_&a=q&hcl=0\">_textexpand_</a></td>\n";
651 }
652 else if (size >5 && args["hcl"]==0) { // expand the list, show contract button
653 historylist += "<td><a href=\"_gwcgi_?e=_compressedoptions_&a=q&hcl=1\">_textcontract_</a></td>\n";
654 }
655 historylist += "</table>\n";
656 historylist += "<table align=center width=500 border=1>\n<tr><th colspan=4 align=center>";
657 historylist += "_textsearchhistory_</th></tr>\n";
658 historylist += "<tr><th width=40>#</th>\n<th width=340>_textquery_</th>\n";
659 historylist += "<th width=60>_textresults_</th><th width=60>_textview_</th></tr>\n";
660
661 while (here !=end ) {
662 text_t c;
663 text_t query;
664 text_t numdocs;
665 text_t cgiargs;
666 text_t userinfo;
667 split_saved_query(*here,c, numdocs, cgiargs);
668 parse_saved_args(cgiargs, "q", query); // get query string out
669 decode_cgi_arg(query); // un cgisafe it
670
671 format_user_info(cgiargs, userinfo, protos, logout);
672
673 historylist += "<tr> <td width=40 align=center>"+c+"</td>\n";
674 historylist += "<td width=340 align=left>"+query+"</td><td width=60 align=center>"+numdocs+"</td>\n";
675 historylist += "<td width=60 align=center><a href=\"_gwcgi_?e=_compressedoptions_&";
676 historylist += *here+"\"><img name=\"display\" src=\"_httpicondisplay_\" width=_widthdisplay_ ";
677 historylist += "height=_heightdisplay_ border=\"0\" alt=\"" + userinfo +"\"></a></td></tr>\n";
678 here++;
679 count++;
680 }
681 historylist+="</table>\n\n";
682
683 } // if
684 else {
685 historylist += "_textnohistory_";
686 }
687 historylist += "<p><! ---- end of history list ----->\n";
688 } // else display list
689 disp.setmacro("searchhistorylist", "query", historylist);
690
691} // define history macros
692
693void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
694 displayclass &disp, outconvertclass &outconvert,
695 ostream &textout, ostream &logout) {
696
697 ColInfoResponse_t cinfo;
698 comerror_t err;
699 InfoFilterOptionsResponse_t fresponse;
700 InfoFilterOptionsRequest_t frequest;
701 frequest.filterName = "QueryFilter";
702
703 text_t &index = args["h"];
704 text_t &subcollection = args["j"];
705 text_t &language = args["n"];
706
707 text_tset collections;
708 text_t arg_cc = args["cc"];
709 decode_cgi_arg (arg_cc);
710 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
711
712 textout << outconvert << disp << "_query:header_\n"
713 << "<center>_navigationbar_</center><br>\n"
714 << "<form name=QueryForm method=get action=\"_gwcgi_\">\n"
715 << "<input type=hidden name=a value=\"q\">\n"
716 << "<input type=hidden name=e value=\"_compressedoptions_\">\n"
717 << "<input type=hidden name=ccp value=\"1\">\n"
718 << "<center><table width=_pagewidth_><tr valign=top>\n"
719 << "<td>Select collections to search for \"" << args["q"]
720 << "\" <i>(index=" << index << " subcollection=" << subcollection
721 << " language=" << language << ")</i></td>\n"
722 << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
723 << "</tr></table></center>\n"
724 << "<center><table width=_pagewidth_>\n"
725 << "<tr><td>\n";
726
727 recptprotolistclass::iterator rprotolist_here = protos->begin();
728 recptprotolistclass::iterator rprotolist_end = protos->end();
729 while (rprotolist_here != rprotolist_end) {
730 if ((*rprotolist_here).p != NULL) {
731
732 text_tarray collist;
733 (*rprotolist_here).p->get_collection_list (collist, err, logout);
734 if (err == noError) {
735 text_tarray::iterator collist_here = collist.begin();
736 text_tarray::iterator collist_end = collist.end();
737 while (collist_here != collist_end) {
738
739 (*rprotolist_here).p->get_collectinfo (*collist_here, cinfo, err, logout);
740 // if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
741 if (err == noError && (cinfo.buildDate > 0)) {
742
743 (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
744 if (err == noError) {
745
746 FilterOption_tmap::const_iterator it;
747 FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
748 if (!index.empty()) {
749 it = fresponse.filterOptions.find ("Index");
750 if (it == end) {collist_here ++; continue;}
751 text_tarray::const_iterator there = (*it).second.validValues.begin();
752 text_tarray::const_iterator tend = (*it).second.validValues.end();
753 while (there != tend) {
754 if (*there == index) break;
755 there ++;
756 }
757 if (there == tend) {collist_here++; continue;}
758 }
759 if (!subcollection.empty()) {
760 it = fresponse.filterOptions.find ("Subcollection");
761 if (it == end) {collist_here++; continue;}
762 text_tarray::const_iterator there = (*it).second.validValues.begin();
763 text_tarray::const_iterator tend = (*it).second.validValues.end();
764 while (there != tend) {
765 if (*there == subcollection) break;
766 there ++;
767 }
768 if (there == tend) {collist_here++; continue;}
769 }
770 if (!language.empty()) {
771 it = fresponse.filterOptions.find ("Language");
772 if (it == end) {collist_here++; continue;}
773 text_tarray::const_iterator there = (*it).second.validValues.begin();
774 text_tarray::const_iterator tend = (*it).second.validValues.end();
775 while (there != tend) {
776 if (*there == language) break;
777 there ++;
778 }
779 if (there == tend) {collist_here++; continue;}
780 }
781
782 // we've got a matching collection
783 textout << "<input type=checkbox";
784
785 text_tset::const_iterator t = collections.find (*collist_here);
786 if (t != collections.end()) textout << " checked";
787
788 textout << outconvert
789 << " name=cc value=\"" << *collist_here << "\">";
790
791 if (!cinfo.collectionmeta["collectionname"].empty())
792 textout << outconvert << disp << cinfo.collectionmeta["collectionname"];
793 else
794 textout << outconvert << *collist_here;
795
796 textout << "<br>\n";
797
798 }
799 }
800 collist_here ++;
801 }
802 }
803 }
804 rprotolist_here ++;
805 }
806 textout << outconvert << disp
807 << "</td></tr></table></center>\n"
808 << "</form>\n"
809 << "_query:footer_\n";
810
811}
812
813bool queryaction::do_action (cgiargsclass &args, recptprotolistclass *protos,
814 browsermapclass *browsers, displayclass &disp,
815 outconvertclass &outconvert, ostream &textout,
816 ostream &logout) {
817
818 if (args["ccs"] == "1") {
819 if (!args["cc"].empty()) {
820 // query the selected collections
821 text_t::const_iterator b = args["cc"].begin();
822 text_t::const_iterator e = args["cc"].end();
823 if (findchar (b, e, ',') != e) {
824 if (!search_multiple_collections (args, protos, browsers, disp, outconvert,
825 textout, logout)) return false;
826 return true;
827 } else {
828 if (!search_single_collection (args, args["cc"], protos, browsers, disp,
829 outconvert, textout, logout)) return false;
830 return true;
831 }
832 }
833 }
834
835 // simply query the current collection
836 if (!search_single_collection (args, args["c"], protos, browsers, disp,
837 outconvert, textout, logout)) return false;
838 return true;
839}
840
841bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
842 browsermapclass *browsers, displayclass &disp,
843 outconvertclass &outconvert, ostream &textout,
844 ostream &logout) {
845
846 text_tarray collections;
847
848 text_t arg_cc = args["cc"];
849 decode_cgi_arg (arg_cc);
850 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
851
852 if (collections.empty()) {
853 logout << "queryaction::search_multiple_collections: No collections "
854 << "set for doing multiple query - will search current collection\n";
855 textout << outconvert << disp << "_query:textwarningnocollections_\n";
856 return search_single_collection (args, args["c"], protos, browsers, disp,
857 outconvert, textout, logout);
858 }
859
860 // queryaction uses "VList" browser to display results,
861 // a queries clasification is "Search"
862 text_t browsertype = "VList";
863 text_t classification = "Search";
864
865 QueryResult_tset results;
866 map<text_t, colinfo_t, lttext_t> colinfomap;
867
868 ColInfoResponse_t cinfo;
869 comerror_t err;
870 FilterRequest_t request;
871 FilterResponse_t response;
872 request.filterResultOptions = FROID | FRmetadata | FRtermFreq | FRranking;
873 text_t formattedstring = args["q"];
874 text_t freqmsg = "_textfreqmsg1_";
875 int numdocs = 0;
876 isapprox isApprox = Exact;
877
878 format_querystring (formattedstring, args.getintarg("b"));
879 set_queryfilter_options (request, formattedstring, args);
880
881 // need to retrieve maxdocs matches for each collection
882 // (will eventually want to tidy this up, do so caching etc.)
883 OptionValue_t option;
884 option.name = "StartResults";
885 option.value = "1";
886 request.filterOptions.push_back (option);
887
888 option.name = "EndResults";
889 option.value = args["m"];
890 request.filterOptions.push_back (option);
891
892 text_tarray::iterator col_here = collections.begin();
893 text_tarray::iterator col_end = collections.end();
894
895 map<text_t, int, lttext_t> termfreqs;
896 while (col_here != col_end) {
897
898 request.fields.erase (request.fields.begin(), request.fields.end());
899 request.getParents = false;
900
901 recptproto *collectproto = protos->getrecptproto (*col_here, logout);
902 if (collectproto == NULL) {
903 logout << outconvert << "queryaction::search_multiple_collections: " << *col_here
904 << " collection has a NULL collectproto, ignoring\n";
905 col_here ++;
906 continue;
907 }
908 collectproto->get_collectinfo (*col_here, cinfo, err, logout);
909
910 browserclass *bptr = browsers->getbrowser (browsertype);
911
912 // get the formatstring if there is one
913 text_t formatstring;
914 if (!get_formatstring (classification, browsertype,
915 cinfo.format, formatstring))
916 formatstring = bptr->get_default_formatstring();
917
918 bptr->load_metadata_defaults (request.fields);
919
920 format_t *formatlistptr = new format_t();
921 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
922
923 colinfo_t thiscolinfo;
924 thiscolinfo.formatlistptr = formatlistptr;
925 thiscolinfo.browserptr = bptr;
926 colinfomap[*col_here] = thiscolinfo;
927
928 // do the query
929 collectproto->filter (*col_here, request, response, err, logout);
930 if (err != noError) {
931 outconvertclass text_t2ascii;
932 logout << text_t2ascii
933 << "queryaction::search_multiple_collections: call to QueryFilter failed "
934 << "for " << *col_here << " collection (" << get_comerror_string (err) << ")\n";
935 return false;
936 }
937
938 if (isApprox == Exact)
939 isApprox = response.isApprox;
940 else if (isApprox == MoreThan)
941 if (response.isApprox == Approximate)
942 isApprox = response.isApprox;
943
944 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
945 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
946 while (this_term != end_term) {
947 termfreqs[(*this_term).term] += (*this_term).freq;
948 if ((col_here+1) == col_end) {
949 freqmsg += (*this_term).term + ": " + termfreqs[(*this_term).term];
950 if ((this_term+1) != end_term) freqmsg += ", ";
951 }
952 this_term ++;
953 }
954
955 if (response.numDocs > 0) {
956 numdocs += response.numDocs;
957
958 QueryResult_t thisresult;
959 thisresult.collection = *col_here;
960 ResultDocInfo_tarray::iterator doc_here = response.docInfo.begin();
961 ResultDocInfo_tarray::iterator doc_end = response.docInfo.end();
962 while (doc_here != doc_end) {
963 thisresult.doc = *doc_here;
964 results.insert (thisresult);
965 doc_here ++;
966 }
967 }
968 col_here ++;
969 }
970
971 disp.setmacro ("freqmsg", "query", freqmsg);
972
973 text_t resline;
974 if (num_phrases > 0) isApprox = Exact;
975 if (isApprox == Approximate) resline = "_textapprox_";
976 else if (isApprox == MoreThan) resline = "_textmorethan_";
977
978 if (numdocs == 0) resline = "_textnodocs_";
979 else if (numdocs == 1) resline += "_text1doc_";
980 else resline += text_t(numdocs) + " _textlotsdocs_";
981 disp.setmacro("resultline", "query", resline);
982
983 QueryResult_tset::iterator res_here = results.begin();
984 QueryResult_tset::iterator res_end = results.end();
985 text_tset metadata; // empty !!
986 bool getParents = false; // don't care !!
987 recptproto *collectproto = NULL;
988 bool use_table;
989 ResultDocInfo_t thisdoc;
990 format_t *formatlistptr = NULL;
991 browserclass *browserptr = NULL;
992
993 int maxdocs = args.getintarg("m");
994 int firstdoc = args.getintarg("r");
995 int hitsperpage = args.getintarg("o");
996 if (numdocs > maxdocs) numdocs = maxdocs;
997 if (hitsperpage == -1) hitsperpage = numdocs;
998
999 // set up _thisfirst_ and _thislast_ macros
1000 disp.setmacro ("thisfirst", "query", firstdoc);
1001 int thislast = firstdoc + (hitsperpage - 1);
1002 if (thislast > numdocs) thislast = numdocs;
1003 disp.setmacro ("thislast", "query", thislast);
1004
1005 // set up _prevfirst_ and _prevlast_ macros
1006 if (firstdoc > 1) {
1007 disp.setmacro ("prevlast", "query", firstdoc - 1);
1008 int prevfirst = firstdoc - hitsperpage;
1009 if (prevfirst < 1) prevfirst = 1;
1010 disp.setmacro ("prevfirst", "query", prevfirst);
1011 }
1012
1013 // set up _nextfirst_ and _nextlast_ macros
1014 if (thislast < numdocs) {
1015 disp.setmacro ("nextfirst", "query", thislast + 1);
1016 int nextlast = thislast + hitsperpage;
1017 if (nextlast > numdocs) nextlast = numdocs;
1018 disp.setmacro ("nextlast", "query", nextlast);
1019 }
1020
1021 textout << outconvert << disp << "_query:header_\n"
1022 << "_query:content_";
1023
1024 int count = 1;
1025
1026 // output results
1027 while (res_here != res_end) {
1028 if (count < firstdoc) {count ++; res_here ++; continue;}
1029 if (count > thislast) break;
1030 formatlistptr = colinfomap[(*res_here).collection].formatlistptr;
1031 browserptr = colinfomap[(*res_here).collection].browserptr;
1032 thisdoc = (*res_here).doc;
1033 use_table = is_table_content (formatlistptr);
1034 browserptr->output_section_group (thisdoc, args, (*res_here).collection, 0,
1035 formatlistptr, use_table, metadata, getParents,
1036 collectproto, disp, outconvert, textout, logout);
1037 // textout << outconvert << "(ranking: " << (*res_here).doc.ranking << ")\n";
1038 res_here ++;
1039 count ++;
1040 }
1041
1042 textout << outconvert << disp << "_query:footer_";
1043
1044 // clean up the format_t pointers
1045 map<text_t, colinfo_t, lttext_t>::iterator here = colinfomap.begin();
1046 map<text_t, colinfo_t, lttext_t>::iterator end = colinfomap.end();
1047 while (here != end) {
1048 delete ((*here).second.formatlistptr);
1049 here ++;
1050 }
1051 return true;
1052}
1053
1054bool queryaction::search_single_collection (cgiargsclass &args, const text_t &collection,
1055 recptprotolistclass *protos, browsermapclass *browsers,
1056 displayclass &disp, outconvertclass &outconvert,
1057 ostream &textout, ostream &logout) {
1058
1059 recptproto *collectproto = protos->getrecptproto (collection, logout);
1060 if (collectproto == NULL) {
1061 logout << outconvert << "queryaction::search_single_collection: " << collection
1062 << " collection has a NULL collectproto\n";
1063 return false;
1064 }
1065
1066 // queryaction uses "VList" browser to display results,
1067 // a queries clasification is "Search"
1068 text_t browsertype = "VList";
1069 text_t classification = "Search";
1070
1071 ColInfoResponse_t cinfo;
1072 comerror_t err;
1073 collectproto->get_collectinfo (collection, cinfo, err, logout);
1074
1075 browserclass *bptr = browsers->getbrowser (browsertype);
1076
1077 // get the formatstring if there is one
1078 text_t formatstring;
1079 if (!get_formatstring (classification, browsertype,
1080 cinfo.format, formatstring))
1081 formatstring = bptr->get_default_formatstring();
1082
1083 FilterRequest_t request;
1084 FilterResponse_t response;
1085 bptr->set_filter_options (request, args);
1086 bptr->load_metadata_defaults (request.fields);
1087
1088 format_t *formatlistptr = new format_t();
1089 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1090
1091 // do the query
1092 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
1093 text_t formattedstring = args["q"];
1094 if (!combine_query (args["z"], formattedstring, gsdlhome)) {
1095 args["q"]=formattedstring;
1096 }
1097 format_querystring (formattedstring, args.getintarg("b"));
1098 set_queryfilter_options (request, formattedstring, args);
1099 collectproto->filter (collection, request, response, err, logout);
1100 if (err != noError) {
1101 outconvertclass text_t2ascii;
1102 logout << text_t2ascii
1103 << "queryaction::search_single_collections: call to QueryFilter failed "
1104 << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
1105 return false;
1106 }
1107
1108
1109 define_query_macros (args, disp, response);
1110
1111 // save the query if appropriate
1112 if (!save_search_history(args, response))
1113 logout << "save failed";
1114
1115 define_history_macros (disp, args, protos, logout);
1116
1117 textout << outconvert << disp << "_query:header_\n"
1118 << "_query:content_";
1119
1120 // output the results
1121 bool use_table = is_table_content (formatlistptr);
1122 bptr->output_section_group (response, args, collection, 0, formatlistptr,
1123 use_table, request.fields, request.getParents,
1124 collectproto, disp, outconvert, textout, logout);
1125
1126
1127 textout << outconvert << disp << "_query:footer_";
1128
1129 delete (formatlistptr);
1130
1131 return true;
1132}
1133
1134// define_query_macros sets the macros that couldn't be set until the
1135// query had been done. Those macros are _freqmsg_, _quotedquery_,
1136// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
1137// _thisfirst_, and _thislast_
1138void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
1139 const FilterResponse_t &response) {
1140
1141 // set up _freqmsg_ and _quotedquery_ macros
1142 text_t freqmsg = "_textfreqmsg1_";
1143 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
1144 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
1145 while (this_term != end_term) {
1146 freqmsg += (*this_term).term + ": " + (*this_term).freq;
1147 if ((this_term + 1) != end_term)
1148 freqmsg += ", ";
1149 this_term ++;
1150 }
1151
1152 disp.setmacro ("freqmsg", "query", freqmsg);
1153
1154
1155 // set up _resultline_ macro
1156 text_t resline;
1157 int maxdocs = args.getintarg("m");
1158 int numdocs = response.numDocs;
1159 if (maxdocs == -1) maxdocs = response.numDocs;
1160 isapprox isApprox = response.isApprox;
1161
1162 // if there were phrases (post-processing) we're not going to include
1163 // those documents that didn't match
1164 if (num_phrases > 0) isApprox = Exact;
1165
1166 if (isApprox == Approximate) resline = "_textapprox_";
1167 else if (isApprox == MoreThan) resline = "_textmorethan_";
1168
1169 if (numdocs == 0) resline = "_textnodocs_";
1170 else if (numdocs == 1) resline += "_text1doc_";
1171 else resline += text_t(numdocs) + " _textlotsdocs_";
1172
1173 disp.setmacro("resultline", "query", resline);
1174
1175 int firstdoc = args.getintarg("r");
1176 int hitsperpage = args.getintarg("o");
1177 if (hitsperpage == -1) hitsperpage = numdocs;
1178
1179 // set up _thisfirst_ and _thislast_ macros
1180 disp.setmacro ("thisfirst", "query", firstdoc);
1181 int thislast = firstdoc + (hitsperpage - 1);
1182 if (thislast > numdocs) thislast = numdocs;
1183 disp.setmacro ("thislast", "query", thislast);
1184
1185 // set up _prevfirst_ and _prevlast_ macros
1186 if (firstdoc > 1) {
1187 disp.setmacro ("prevlast", "query", firstdoc - 1);
1188 int prevfirst = firstdoc - hitsperpage;
1189 if (prevfirst < 1) prevfirst = 1;
1190 disp.setmacro ("prevfirst", "query", prevfirst);
1191 }
1192
1193 // set up _nextfirst_ and _nextlast_ macros
1194 if (thislast < numdocs) {
1195 disp.setmacro ("nextfirst", "query", thislast + 1);
1196 int nextlast = thislast + hitsperpage;
1197 if (nextlast > numdocs) nextlast = numdocs;
1198 disp.setmacro ("nextlast", "query", nextlast);
1199 }
1200}
1201
1202bool queryaction::save_search_history (cgiargsclass &args, const FilterResponse_t &response)
1203{
1204 if (args["q"]=="") return true; // null query, dont save
1205 if (args["hs"]=="0") return true; // only save when submit query
1206
1207 // get userid
1208 text_t userid = args["z"];
1209
1210 // the number of docs goes on the front of the query string
1211 int numdocs= response.numDocs;
1212 text_t query = text_t(numdocs);
1213 if (response.isApprox==MoreThan) { // there were more docs found
1214 query.push_back('+');
1215 }
1216 query += "a=q";
1217 query += "&c="+args["c"];
1218 query += "&h="+args["h"];
1219 query += "&t="+args["t"];
1220 query += "&b="+args["b"];
1221 query += "&j="+args["j"];
1222 query += "&n="+args["n"];
1223 query += "&s="+args["s"];
1224 query += "&k="+args["k"];
1225
1226 text_t qstring = args["q"];
1227 text_t formattedquery =cgi_safe(qstring);
1228 query += "&q="+formattedquery;
1229
1230 if (set_history_info(userid, query, gsdlhome)) return true;
1231 else return false;
1232
1233
1234}
1235
1236
1237
1238
1239
Note: See TracBrowser for help on using the repository browser.