source: main/trunk/greenstone2/runtime-src/src/recpt/basequeryaction.cpp@ 28899

Last change on this file since 28899 was 28899, checked in by ak19, 10 years ago

Third commit for security, for ensuring cgiargs macros are websafe. This time all the changes to the runtime action classes.

File size: 27.4 KB
Line 
1/**********************************************************************
2 *
3 * basequeryaction.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 *********************************************************************/
25
26#include "basequeryaction.h"
27#include "querytools.h"
28#include "formattools.h"
29#include "cgiutils.h"
30#include "OIDtools.h"
31#include "fileutil.h"
32#include "text_t.h"
33#include "historydb.h"
34#include "htmlutils.h" // for html_safe in do_action
35#include "gsdltools.h"
36#include "phrases.h" // for get_phrases
37#include <stdlib.h> // for strtol
38#include <assert.h>
39
40void colinfo_t::clear () {
41 formatlistptr = NULL;
42 browserptr = NULL;
43}
44
45void QueryResult_t::clear() {
46 doc.clear();
47 collection.clear();
48}
49
50basequeryaction::basequeryaction () {
51
52 recpt = NULL;
53
54 cgiarginfo arg_ainfo;
55
56 // "q"
57 arg_ainfo.shortname = "q";
58 arg_ainfo.longname = "query string";
59 arg_ainfo.multiplechar = true;
60 arg_ainfo.multiplevalue = false;
61 arg_ainfo.defaultstatus = cgiarginfo::weak;
62 arg_ainfo.argdefault = g_EmptyText;
63 arg_ainfo.savedarginfo = cgiarginfo::must;
64 argsinfo.addarginfo (NULL, arg_ainfo);
65
66 // "q2"
67 arg_ainfo.shortname = "q2";
68 arg_ainfo.longname = "query string for second query";
69 arg_ainfo.multiplechar = true;
70 arg_ainfo.multiplevalue = false;
71 arg_ainfo.defaultstatus = cgiarginfo::weak;
72 arg_ainfo.argdefault = g_EmptyText;
73 arg_ainfo.savedarginfo = cgiarginfo::must;
74 argsinfo.addarginfo (NULL, arg_ainfo);
75
76 // "cq2" ""=don't combine, "and", "or", "not"
77 arg_ainfo.shortname = "cq2";
78 arg_ainfo.longname = "combine queries";
79 arg_ainfo.multiplechar = true;
80 arg_ainfo.multiplevalue = false;
81 arg_ainfo.defaultstatus = cgiarginfo::weak;
82 arg_ainfo.argdefault = g_EmptyText;
83 arg_ainfo.savedarginfo = cgiarginfo::must;
84 argsinfo.addarginfo (NULL, arg_ainfo);
85
86
87 // "m"
88 arg_ainfo.shortname = "m";
89 arg_ainfo.longname = "maximum number of documents";
90 arg_ainfo.multiplechar = true;
91 arg_ainfo.multiplevalue = false;
92 arg_ainfo.defaultstatus = cgiarginfo::weak;
93 arg_ainfo.argdefault = "50";
94 arg_ainfo.savedarginfo = cgiarginfo::must;
95 argsinfo.addarginfo (NULL, arg_ainfo);
96
97 // "o"
98 arg_ainfo.shortname = "o";
99 arg_ainfo.longname = "hits per page";
100 arg_ainfo.multiplechar = true;
101 arg_ainfo.multiplevalue = false;
102 arg_ainfo.defaultstatus = cgiarginfo::weak;
103 arg_ainfo.argdefault = "20";
104 arg_ainfo.savedarginfo = cgiarginfo::must;
105 argsinfo.addarginfo (NULL, arg_ainfo);
106
107 // "r"
108 arg_ainfo.shortname = "r";
109 arg_ainfo.longname = "start results from";
110 arg_ainfo.multiplechar = true;
111 arg_ainfo.multiplevalue = false;
112 arg_ainfo.defaultstatus = cgiarginfo::weak;
113 arg_ainfo.argdefault = "1";
114 arg_ainfo.savedarginfo = cgiarginfo::must;
115 argsinfo.addarginfo (NULL, arg_ainfo);
116
117
118 // "ifl" - I'm feeling lucky! (Go directly to a matching document)
119 arg_ainfo.shortname = "ifl";
120 arg_ainfo.longname = "i'm feeling lucky";
121 arg_ainfo.multiplechar = false;
122 arg_ainfo.multiplevalue = false;
123 arg_ainfo.defaultstatus = cgiarginfo::weak;
124 arg_ainfo.argdefault = g_EmptyText;
125 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
126 argsinfo.addarginfo (NULL, arg_ainfo);
127
128 // "ifln" - I'm feeling lucky number (Go directly to the nth matching document)
129 arg_ainfo.shortname = "ifln";
130 arg_ainfo.longname = "i'm feeling lucky number";
131 arg_ainfo.multiplechar = true;
132 arg_ainfo.multiplevalue = false;
133 arg_ainfo.defaultstatus = cgiarginfo::weak;
134 arg_ainfo.argdefault = "1";
135 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
136 argsinfo.addarginfo (NULL, arg_ainfo);
137
138 // "srn" - the next search result
139 arg_ainfo.shortname = "srn";
140 arg_ainfo.longname = "the next search result number";
141 arg_ainfo.multiplechar = true;
142 arg_ainfo.multiplevalue = false;
143 arg_ainfo.defaultstatus = cgiarginfo::weak;
144 arg_ainfo.argdefault = "0";
145 arg_ainfo.savedarginfo = cgiarginfo::must;
146 argsinfo.addarginfo (NULL, arg_ainfo);
147
148 // "srp" - the previous search result
149 arg_ainfo.shortname = "srp";
150 arg_ainfo.longname = "the previous search result number";
151 arg_ainfo.multiplechar = true;
152 arg_ainfo.multiplevalue = false;
153 arg_ainfo.defaultstatus = cgiarginfo::weak;
154 arg_ainfo.argdefault = "0";
155 arg_ainfo.savedarginfo = cgiarginfo::must;
156 argsinfo.addarginfo (NULL, arg_ainfo);
157
158 // "sf" - Sort field. Set to field to be used for sorting search reult
159 // set (only implemented for lucene collections at present).
160 arg_ainfo.shortname = "sf";
161 arg_ainfo.longname = "sort field";
162 arg_ainfo.multiplechar = true;
163 arg_ainfo.multiplevalue = false;
164 arg_ainfo.defaultstatus = cgiarginfo::weak;
165 arg_ainfo.argdefault = g_EmptyText;
166 arg_ainfo.savedarginfo = cgiarginfo::must;
167 argsinfo.addarginfo (NULL, arg_ainfo);
168
169 // "so" - sort order. 0 = ascending (natural order),
170 // 1 = descending (reverse order)
171 arg_ainfo.shortname = "so";
172 arg_ainfo.longname = "sort order";
173 arg_ainfo.multiplechar = false;
174 arg_ainfo.multiplevalue = false;
175 arg_ainfo.defaultstatus = cgiarginfo::weak;
176 arg_ainfo.argdefault = "0";
177 arg_ainfo.savedarginfo = cgiarginfo::must;
178 argsinfo.addarginfo (NULL, arg_ainfo);
179
180 // "fqn" - number of fields in the query form
181 arg_ainfo.shortname = "fqn";
182 arg_ainfo.longname = "form query num fields";
183 arg_ainfo.multiplechar = true;
184 arg_ainfo.multiplevalue = false;
185 arg_ainfo.defaultstatus = cgiarginfo::weak;
186 arg_ainfo.argdefault = "4";
187 arg_ainfo.savedarginfo = cgiarginfo::must;
188 argsinfo.addarginfo (NULL, arg_ainfo);
189
190 // "fqf" - the list of field names in the form query
191 // - a comma separated list
192 arg_ainfo.shortname = "fqf";
193 arg_ainfo.longname = "form query fields";
194 arg_ainfo.multiplechar = true;
195 arg_ainfo.multiplevalue = false;
196 arg_ainfo.defaultstatus = cgiarginfo::weak;
197 arg_ainfo.argdefault = g_EmptyText;
198 arg_ainfo.savedarginfo = cgiarginfo::must;
199 argsinfo.addarginfo (NULL, arg_ainfo);
200
201 // "fqv" - the list of values in the form query
202 // - a comma separated list
203 arg_ainfo.shortname = "fqv";
204 arg_ainfo.longname = "form query values";
205 arg_ainfo.multiplechar = true;
206 arg_ainfo.multiplevalue = false;
207 arg_ainfo.defaultstatus = cgiarginfo::weak;
208 arg_ainfo.argdefault = g_EmptyText;
209 arg_ainfo.savedarginfo = cgiarginfo::must;
210 argsinfo.addarginfo (NULL, arg_ainfo);
211
212
213 // "fqc" - the list of boolean operators in the form query
214 // - a comma separated list
215 arg_ainfo.shortname = "fqc";
216 arg_ainfo.longname = "form query combines";
217 arg_ainfo.multiplechar = true;
218 arg_ainfo.multiplevalue = false;
219 arg_ainfo.defaultstatus = cgiarginfo::weak;
220 arg_ainfo.argdefault = g_EmptyText;
221 arg_ainfo.savedarginfo = cgiarginfo::must;
222 argsinfo.addarginfo (NULL, arg_ainfo);
223
224 // "fqa" - form query advanced - for "run query"
225 arg_ainfo.shortname = "fqa";
226 arg_ainfo.longname = "form query advanced query";
227 arg_ainfo.multiplechar = false;
228 arg_ainfo.multiplevalue = false;
229 arg_ainfo.defaultstatus = cgiarginfo::weak;
230 arg_ainfo.argdefault = "0";
231 arg_ainfo.savedarginfo = cgiarginfo::must;
232 argsinfo.addarginfo (NULL, arg_ainfo);
233
234 // "fuzziness" controls how closely the search terms must match
235 // 100 = exact match, 0 = very inexact match (only implemented for Lucene)
236 arg_ainfo.shortname = "fuzziness";
237 arg_ainfo.longname = "Lucene fuzziness value";
238 arg_ainfo.multiplechar = true;
239 arg_ainfo.multiplevalue = false;
240 arg_ainfo.defaultstatus = cgiarginfo::weak;
241 arg_ainfo.argdefault = g_EmptyText;
242 arg_ainfo.savedarginfo = cgiarginfo::must;
243 argsinfo.addarginfo (NULL, arg_ainfo);
244
245 // "hd" history display - search history only displayed when
246 // this var set to something other than 0
247 // this number of records is displayed
248 arg_ainfo.shortname = "hd";
249 arg_ainfo.longname = "history display";
250 arg_ainfo.multiplechar = true;
251 arg_ainfo.multiplevalue = false;
252 arg_ainfo.defaultstatus = cgiarginfo::weak;
253 arg_ainfo.argdefault = "0";
254 arg_ainfo.savedarginfo = cgiarginfo::must;
255 argsinfo.addarginfo (NULL, arg_ainfo);
256
257 // "hs" save - set to 1 in query form, so only save when submit
258 // query
259 // 0 = no save 1 = save
260 arg_ainfo.shortname = "hs";
261 arg_ainfo.longname = "history save";
262 arg_ainfo.multiplechar = false;
263 arg_ainfo.multiplevalue = false;
264 arg_ainfo.defaultstatus = cgiarginfo::weak;
265 arg_ainfo.argdefault = "0";
266 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
267 argsinfo.addarginfo (NULL, arg_ainfo);
268}
269
270void basequeryaction::configure (const text_t &key, const text_tarray &cfgline) {
271 action::configure (key, cfgline);
272}
273
274bool basequeryaction::init (ostream &logout) {
275 return action::init (logout);
276}
277
278bool basequeryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
279 recptprotolistclass * /*protos*/, ostream &logout) {
280
281
282 // check m argument
283 int arg_m = args.getintarg("m");
284 if (arg_m < -1) {
285 logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
286 cgiarginfo *minfo = argsinfo.getarginfo ("m");
287 if (minfo != NULL) args["m"] = minfo->argdefault;
288 }
289
290 // check o argument
291 int arg_o = args.getintarg("o");
292 if (arg_o < -1) {
293 logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
294 cgiarginfo *oinfo = argsinfo.getarginfo ("o");
295 if (oinfo != NULL) args["o"] = oinfo->argdefault;
296 }
297
298 // check r argument
299 int arg_r = args.getintarg("r");
300 if (arg_r < 1) {
301 logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
302 cgiarginfo *rinfo = argsinfo.getarginfo ("r");
303 if (rinfo != NULL) args["r"] = rinfo->argdefault;
304 }
305
306 //check hd argument
307 int arg_hd = args.getintarg("hd");
308 if (arg_hd <0 ) {
309 logout << "Warning: \"hd\" argument less than 0 (" << arg_hd << ")\n";
310 cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
311 if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
312 }
313
314 //check hs argument
315 int arg_hs = args.getintarg("hs");
316 if (arg_hs !=0 && arg_hs !=1) {
317 logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
318 cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
319 if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
320 }
321
322 return true;
323}
324
325void basequeryaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
326 response_t &response, text_t &response_data,
327 ostream &/*logout*/) {
328 // If this is an "I'm feeling lucky" request, we don't know the target location until later
329 if (!args["ifl"].empty()) {
330 response = undecided_location;
331 return;
332 }
333
334 response = content;
335 response_data = "text/html";
336}
337
338
339
340
341void basequeryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
342 recptprotolistclass * protos,
343 ostream &logout)
344{
345 // The following macros are set later (in define_query_macros) as they can't be set until
346 // the query has been done.
347 // _quotedquery_ the part of the query string that was quoted for post-processing
348 // _freqmsg_ the term frequency string
349
350 // _resultline_ the "x documents matched the query" string
351
352 // _prevfirst_ these are used when setting up the links to previous/next
353 // _prevlast_ pages of results (_thisfirst_ and _thislast_ are used to set
354 // _nextfirst_ the 'results x-x for query: xxxx' string in the title bar)
355 // _nextlast_
356 // _thisfirst_
357 // _thislast_
358
359
360 define_form_macros(disp, args, protos, logout);
361}
362
363
364
365
366
367// define_query_macros sets the macros that couldn't be set until the
368// query had been done. Those macros are
369// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
370// _thisfirst_, and _thislast_ and _quotedquery_
371// this has been simplified so it can be used with both search_single_coll
372// and search_multiple_coll
373void basequeryaction::define_query_macros (cgiargsclass &args,
374 displayclass &disp,
375 int numdocs, isapprox isApprox) {
376
377 // set up _resultline_ macro
378 text_t resline;
379 int maxdocs = args.getintarg("m");
380 if (maxdocs == -1) maxdocs = numdocs;
381 else if (numdocs > maxdocs) {
382 numdocs = maxdocs;
383 isApprox = MoreThan;
384 }
385
386 if (isApprox == Approximate) resline = "_textapprox_";
387 else if (isApprox == MoreThan) resline = "_textmorethan_";
388
389 if (numdocs == 0) resline = "_textnodocs_";
390 else if (numdocs == 1) resline += "_text1doc_";
391 else resline += text_t(numdocs) + " _textlotsdocs_";
392
393 disp.setmacro("resultline", "query", resline);
394
395 int firstdoc = args.getintarg("r");
396 int hitsperpage = args.getintarg("o");
397 if (hitsperpage == -1) hitsperpage = numdocs;
398
399 // set up _thisfirst_ and _thislast_ macros
400 disp.setmacro ("thisfirst", "query", firstdoc);
401 int thislast = firstdoc + (hitsperpage - 1);
402 if (thislast > numdocs) thislast = numdocs;
403 disp.setmacro ("thislast", "query", thislast);
404
405 // set up _prevfirst_ and _prevlast_ macros
406 if (firstdoc > 1) {
407 disp.setmacro ("prevlast", "query", firstdoc - 1);
408 int prevfirst = firstdoc - hitsperpage;
409 if (prevfirst < 1) prevfirst = 1;
410 disp.setmacro ("prevfirst", "query", prevfirst);
411 }
412
413 // set up _nextfirst_ and _nextlast_ macros
414 if (thislast < numdocs) {
415 disp.setmacro ("nextfirst", "query", thislast + 1);
416 int nextlast = thislast + hitsperpage;
417 if (nextlast > numdocs) nextlast = numdocs;
418 disp.setmacro ("nextlast", "query", nextlast);
419 }
420}
421
422
423// sets the selection box macros such as:
424// _hselection_, _jselection_, _nselection_ _gselection_, _fqfselection_
425
426void basequeryaction::set_option_macro (const text_t &macroname,
427 text_t current_value,
428 bool display_single,
429 bool add_js_update,
430 const FilterOption_t &option,
431 displayclass &disp)
432{
433 if (option.validValues.empty()) return;
434 if (option.validValues.size() == 1) {
435 if (display_single) {
436 disp.setmacro (macroname + "selection", displayclass::defaultpackage, "_" + option.defaultValue + "_");
437 }
438 return;
439 }
440 if (option.validValues.size() < 2) return;
441
442 text_t macrovalue = "<select name=\"" + macroname + "\"";
443 if (add_js_update) {
444 macrovalue += " onChange=\"update"+macroname+"();\"";
445 }
446 macrovalue += ">\n";
447
448 if (current_value.empty()) current_value = option.defaultValue;
449
450 text_tarray::const_iterator thisvalue = option.validValues.begin();
451 text_tarray::const_iterator endvalue = option.validValues.end();
452
453 while (thisvalue != endvalue) {
454 macrovalue += "<option value=\"" + *thisvalue + "\"";
455 if (*thisvalue == current_value)
456 macrovalue += " selected";
457 macrovalue += ">_" + *thisvalue + "_\n";
458 ++thisvalue;
459 }
460 macrovalue += "</select>\n";
461 disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
462}
463
464
465
466// sets the selection box macros such as:
467// _sqlfqfselection_
468
469void basequeryaction::set_option_macro (const text_t &macroname,
470 text_t current_value,
471 bool display_single,
472 bool add_js_update,
473 const FilterOption_t &option_domain,
474 const FilterOption_t &option_range,
475 displayclass &disp)
476{
477 // this should probably be moved to sqlqueryaction.cpp // *****
478
479 if (option_domain.validValues.empty()) return;
480 if (option_range.validValues.empty()) return;
481
482 if (option_range.validValues.size() == 1) {
483 if (display_single) {
484 disp.setmacro (macroname + "selection",
485 displayclass::defaultpackage, "_" + option_range.defaultValue + "_");
486 }
487 return;
488 }
489 if (option_domain.validValues.size() < 2) return;
490 if (option_range.validValues.size() < 2) return;
491
492 text_t macrovalue = "<select name=\"" + macroname + "\"";
493 if (add_js_update) {
494 macrovalue += " onChange=\"update"+macroname+"();\"";
495 }
496 macrovalue += ">\n";
497
498 if (current_value.empty()) current_value = option_domain.defaultValue;
499
500 text_tarray::const_iterator dom_thisvalue = option_domain.validValues.begin();
501 text_tarray::const_iterator dom_endvalue = option_domain.validValues.end();
502
503 text_tarray::const_iterator ran_thisvalue = option_range.validValues.begin();
504 text_tarray::const_iterator ran_endvalue = option_range.validValues.end();
505
506
507 while ((dom_thisvalue != dom_endvalue) && (ran_thisvalue != ran_endvalue)) {
508 if (*ran_thisvalue != "ZZ" && *ran_thisvalue != "TX") {
509
510 text_t option_val = *dom_thisvalue;
511 option_val.replace(",","/");
512 macrovalue += "<option value=\"" + option_val + "\"";
513
514 if (*dom_thisvalue == current_value)
515 macrovalue += " selected";
516 macrovalue += ">_" + *ran_thisvalue + "_\n";
517 }
518
519 ++dom_thisvalue;
520 ++ran_thisvalue;
521 }
522 macrovalue += "</select>\n";
523 disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
524}
525
526
527
528
529// define_single_query_macros sets the extra macros for search_single_coll
530// that couldn't be set until the query had been done. Those macros are
531// _freqmsg_ and _stopwordsmsg_
532void basequeryaction::define_single_query_macros (cgiargsclass &args,
533 displayclass &disp,
534 const FilterResponse_t &response) {
535 // set up _freqmsg_ and _stopwordsmsg_ macros
536
537 text_t freqmsg = "";
538 freqmsg = "_textfreqmsg1_";
539 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
540 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
541 while (this_term != end_term) {
542 freqmsg += (*this_term).term + ": " + (*this_term).freq;
543 if ((this_term + 1) != end_term)
544 freqmsg += ", ";
545 ++this_term;
546 }
547 disp.setmacro ("freqmsg", "query", freqmsg);
548
549 text_tset::const_iterator this_stopword = response.stopwords.begin();
550 text_tset::const_iterator end_stopword = response.stopwords.end();
551 if (this_stopword != end_stopword) {
552 text_t stopwordsmsg = "_textstopwordsmsg_ ";
553 while (this_stopword != end_stopword) {
554 if (stopwordsmsg != "_textstopwordsmsg_ ") {
555 stopwordsmsg += ", ";
556 }
557 stopwordsmsg += (*this_stopword);
558 ++this_stopword;
559 }
560 disp.setmacro("stopwordsmsg", "query", stopwordsmsg);
561 }
562}
563
564
565
566void basequeryaction::define_history_macros (displayclass &disp,
567 cgiargsclass &args,
568 recptprotolistclass *protos,
569 ostream &logout)
570{
571
572 // defines the following macros
573 // _searchhistorylist_
574
575 text_t historylist;
576 int arghd = args.getintarg("hd");
577 if (arghd == 0) {
578 historylist="";
579 }
580 else {
581 historylist = "<!-- Search History List -->\n";
582
583 text_t userid = args["z"];
584 text_tarray entries;
585 if (get_history_info (userid, entries, dbhome, logout)) {
586 int count = 1;
587 text_tarray::iterator here = entries.begin();
588 text_tarray::iterator end = entries.end();
589 int numrecords=(int)entries.size();
590 if (numrecords>arghd) { // only display some of them
591 numrecords = arghd;
592 }
593 historylist += "<form action=\"_gwcgi_\" name=\"HistoryForm\"><table width=\"537\">\n";
594
595 for (int i=0; i<numrecords;++i) {
596 text_t query;
597 text_t numdocs;
598 text_t cgiargs;
599 text_t userinfo;
600 text_t escquery;
601 split_saved_query(entries[i],numdocs,cgiargs);
602 parse_saved_args(cgiargs, "q", query); // get query string out
603 decode_cgi_arg(query); // un cgisafe it
604 escquery = escape_quotes(query); // escape the quotes and newlines
605 text_t histvalue = "histvalue";
606 histvalue += i;
607 disp.setmacro(histvalue, "query", escquery);
608 format_user_info(cgiargs, userinfo, args, protos, logout);
609
610 historylist += "<tr><td align=\"right\">_imagehistbutton_(";
611 historylist += i;
612 historylist += ")</td>\n";
613 historylist += "<td><table border=\"1\" cellspacing=\"0\" ";
614 historylist += "cellpadding=\"0\"><tr><td width=\"365\" align=\"left\">"
615 + query
616 + "</td></tr></table></td><td width=\"110\" align=\"center\"><small>"
617 + numdocs;
618 if (numdocs == 1) historylist += " _texthresult_";
619 else historylist += " _texthresults_";
620 if (!userinfo.empty()) {
621 historylist += "<br>( "+userinfo+" )";
622 }
623 historylist += "</small></td>\n";
624 }
625 historylist+="</table></form>\n\n";
626
627 } // if get history info
628 else {
629 historylist += "_textnohistory_";
630 }
631 historylist += "<! ---- end of history list ----->\n";
632 } // else display list
633 disp.setmacro("searchhistorylist", "query", historylist);
634
635} // define history macros
636
637
638bool basequeryaction::search_single_collection (cgiargsclass& args,
639 const text_t& collection,
640 recptprotolistclass *protos,
641 browsermapclass* browsers,
642 displayclass& disp,
643 outconvertclass& outconvert,
644 ostream& textout,
645 ostream& logout)
646{
647 recptproto *collectproto = protos->getrecptproto (collection, logout);
648 if (collectproto == NULL) {
649 logout << outconvert << "basequeryaction::search_single_collection: " << collection
650 << " collection has a NULL collectproto\n";
651
652 // Display the "this collection is not installed on this system" page
653 disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(collection));
654 disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
655
656 textout << outconvert << disp << "_query:header_\n"
657 << "_query:content_\n" << "_query:footer_\n";
658 return true;
659 }
660
661 comerror_t err;
662 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
663
664 if (cinfo == NULL) {
665 logout << "ERROR (query_action::search_single_collection): get_collectinfo_ptr returned NULL\n";
666 return false;
667 }
668
669 bool segment = cinfo->isSegmented;
670
671 // get the formatted query string
672 text_t formattedstring = "";
673 get_formatted_query_string(formattedstring, segment, args, disp, logout);
674
675 if (formattedstring.empty()) {
676 // no point carrying on if we have no query
677 define_history_macros (disp, args, protos, logout);
678 textout << outconvert << disp << "_query:header_\n"
679 << "_query:content_";
680 textout << outconvert << disp << "_query:footer_";
681
682 return true;
683 }
684
685 // (.*)queryaction uses "VList" browser to display results,
686 // a queries clasification is "Search"
687 text_t browsertype = "VList";
688 text_t classification = "Search";
689
690 browserclass *bptr = browsers->getbrowser (browsertype);
691
692 // get the formatstring if there is one
693 text_t formatstring;
694 if (!get_formatstring (classification, browsertype,
695 cinfo->format, formatstring)) {
696 formatstring = bptr->get_default_formatstring();
697 }
698 FilterRequest_t request;
699 FilterResponse_t response;
700
701 text_t hits_per_page_old = args["o"];
702 text_t start_results_from_old = args["r"];
703
704 // if the "ifl" argument is set to 1, we only want to get one document
705 // this may be the first search result (from "I feel lucky") or maybe a
706 // specified search result (from next/prev search result link)
707 if (args["ifl"] == "1") {
708 args["r"] = args["ifln"]; // the document number we want
709 args["o"] = "1";
710 }
711
712 bptr->set_filter_options (request, args);
713 bptr->load_metadata_defaults (request.fields);
714
715 format_t *formatlistptr = new format_t();
716 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
717
718 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
719 // note! formattedstring is in unicode! mg and mgpp must convert!
720 set_queryfilter_options (request, formattedstring, args);
721
722 // do the query
723 collectproto->filter (collection, request, response, err, logout);
724
725 // we had an error
726 if (err != noError) {
727 outconvertclass text_t2ascii;
728 logout << text_t2ascii
729 << "basequeryaction::search_single_collections: call to "
730 << query_filter_name() << " failed "
731 << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
732
733 disp.setmacro("resultline", "query", "_textnodocs_");
734 if (err == syntaxError) {
735 disp.setmacro ("freqmsg", "query", "_textinvalidquery_");
736 } else {
737 disp.setmacro ("freqmsg", "query", "");
738 }
739 // restore r and o args in case they have changed
740 args["r"] = start_results_from_old;
741 args["o"] = hits_per_page_old;
742
743 define_history_macros (disp, args, protos, logout);
744
745 textout << outconvert << disp << "_query:header_\n"
746 << "_query:content_";
747 textout << outconvert << disp << "_query:footer_";
748 delete (formatlistptr);
749 return true;
750 }
751
752 // ok, so no error, lets output the results
753
754 // Perform the "I'm feeling lucky" trick if the "ifl" argument is set
755 if (!args["ifl"].empty()) {
756 //Restore the "r" and "o" arg
757 args["r"] = start_results_from_old;
758 args["o"] = hits_per_page_old;
759
760 //Find whether DocumentSearchResultLinks is enabled
761 bool show_links = false;
762 text_tmap::const_iterator format_here = cinfo->format.begin();
763 text_tmap::const_iterator format_end = cinfo->format.end();
764
765 while (format_here != format_end) {
766 if (((*format_here).first == "DocumentSearchResultLinks") &&
767 ((*format_here).second == "true")){
768 show_links = true;
769 break;
770 }
771 ++format_here;
772 }
773
774 if (args["ifl"] == 1 || (args["ifl"] == 2 && response.numDocs == 1)) {
775
776 // The first search result is the one we want
777 if (response.docInfo.begin() != response.docInfo.end()) {
778
779 ResultDocInfo_tarray::iterator section = response.docInfo.begin();
780
781 // We still need to set "srn" and "srp" values (next and prev search result numbers) if we are showing them
782 int srn = 0;
783 int srp = 0;
784 if (show_links) {
785 int ifln = args["ifln"].getint();
786 srn = ifln + 1;
787 if (srn > response.numDocs) {
788 srn = 0;
789 }
790 srp = ifln - 1;
791 if (srp < 0) {
792 srp = 0;
793 }
794 }
795
796 textout << outconvert << disp
797 << "Location: _gwcgi_?e=_compressedoptions_&a=d&c="
798 << encodeForURL(collection) << "&cl=search&d=" << (*section).OID
799 << "&srn=" << srn << "&srp=" << srp << "\n\n";
800 textout << flush;
801
802 return true;
803 }
804 }
805
806 // There weren't enough (or any) matching documents
807 // We'll just carry on as if ifl wasn't set. The only catch is that get_cgihead_info won't have
808 // done the right thing (because ifl was set), so we need to make sure the output is html
809 textout << "Content-type: text/html\n\n";
810
811 }
812
813 define_query_macros (args, disp, response.numDocs, response.isApprox);
814 define_single_query_macros(args, disp, response);
815 // save the query if appropriate
816 save_search_history(args, response.numDocs, response.isApprox);
817
818
819 // would this result in an error in err???
820 // If Lucene threw a TooManyClauses exception, tell the user about it
821 if (args["ct"] == 2 && response.error_message == "TOO_MANY_CLAUSES") {
822 cerr << "too many clauses\n";
823 disp.setmacro ("freqmsg", "query", "_textlucenetoomanyclauses_");
824 }
825
826
827 //Restore the "r" and "o" arg in case they have been changed and we still get here
828 args["r"] = start_results_from_old;
829 args["o"] = hits_per_page_old;
830
831 define_history_macros (disp, args, protos, logout);
832
833 textout << outconvert << disp << "_query:header_\n"
834 << "_query:content_";
835
836 // output the results
837 text_t numdocs_t = response.numDocs;
838 args["nmd"] = numdocs_t;
839 bool use_table = is_table_content (formatlistptr);
840 bptr->output_section_group (response, args, collection, 0, formatlistptr,
841 use_table, request.fields, request.getParents,
842 collectproto, disp, outconvert, textout, logout);
843
844
845 textout << outconvert << disp << "_query:footer_";
846
847 delete (formatlistptr);
848
849 return true;
850}
851
Note: See TracBrowser for help on using the repository browser.