source: main/trunk/greenstone2/runtime-src/src/recpt/queryaction.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.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 49.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 *********************************************************************/
25
26#include "queryaction.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
40
41queryaction::queryaction ()
42 : basequeryaction()
43{
44 num_phrases = 0;
45
46 cgiarginfo arg_ainfo;
47
48 // this action uses cgi variable "a"
49 arg_ainfo.shortname = "a";
50 arg_ainfo.longname = "action";
51 arg_ainfo.multiplechar = true;
52 arg_ainfo.multiplevalue = false;
53 arg_ainfo.defaultstatus = cgiarginfo::weak;
54 arg_ainfo.argdefault = "q";
55 arg_ainfo.savedarginfo = cgiarginfo::must;
56 argsinfo.addarginfo (NULL, arg_ainfo);
57
58 // "ct" - 0 = mg, 1 = mgpp, 2=lucene
59 arg_ainfo.shortname = "ct";
60 arg_ainfo.longname = "collection type";
61 arg_ainfo.multiplechar = true; // can be empty or single char
62 arg_ainfo.multiplevalue = false;
63 arg_ainfo.defaultstatus = cgiarginfo::weak;
64 arg_ainfo.argdefault = g_EmptyText;
65 arg_ainfo.savedarginfo = cgiarginfo::must;
66 argsinfo.addarginfo (NULL, arg_ainfo);
67
68 // "b" - 0 = simple, 1 = advanced
69 arg_ainfo.shortname = "b";
70 arg_ainfo.longname = "query mode";
71 arg_ainfo.multiplechar = false;
72 arg_ainfo.multiplevalue = false;
73 arg_ainfo.defaultstatus = cgiarginfo::weak;
74 arg_ainfo.argdefault = "0";
75 arg_ainfo.savedarginfo = cgiarginfo::must;
76 argsinfo.addarginfo (NULL, arg_ainfo);
77
78 // "h"
79 arg_ainfo.shortname = "h";
80 arg_ainfo.longname = "main index";
81 arg_ainfo.multiplechar = true;
82 arg_ainfo.multiplevalue = false;
83 arg_ainfo.defaultstatus = cgiarginfo::weak;
84 arg_ainfo.argdefault = g_EmptyText;
85 arg_ainfo.savedarginfo = cgiarginfo::must;
86 argsinfo.addarginfo (NULL, arg_ainfo);
87
88 // "h2"
89 arg_ainfo.shortname = "h2";
90 arg_ainfo.longname = "main index for second query";
91 arg_ainfo.multiplechar = true;
92 arg_ainfo.multiplevalue = false;
93 arg_ainfo.defaultstatus = cgiarginfo::weak;
94 arg_ainfo.argdefault = g_EmptyText;
95 arg_ainfo.savedarginfo = cgiarginfo::must;
96 argsinfo.addarginfo (NULL, arg_ainfo);
97
98 // "j"
99 arg_ainfo.shortname = "j";
100 arg_ainfo.longname = "sub collection index";
101 arg_ainfo.multiplechar = true;
102 arg_ainfo.multiplevalue = false;
103 arg_ainfo.defaultstatus = cgiarginfo::weak;
104 arg_ainfo.argdefault = g_EmptyText;
105 arg_ainfo.savedarginfo = cgiarginfo::must;
106 argsinfo.addarginfo (NULL, arg_ainfo);
107
108 // "j2"
109 arg_ainfo.shortname = "j2";
110 arg_ainfo.longname = "sub collection index for second query";
111 arg_ainfo.multiplechar = true;
112 arg_ainfo.multiplevalue = false;
113 arg_ainfo.defaultstatus = cgiarginfo::weak;
114 arg_ainfo.argdefault = g_EmptyText;
115 arg_ainfo.savedarginfo = cgiarginfo::must;
116 argsinfo.addarginfo (NULL, arg_ainfo);
117
118 // "n"
119 arg_ainfo.shortname = "n";
120 arg_ainfo.longname = "language index";
121 arg_ainfo.multiplechar = true;
122 arg_ainfo.multiplevalue = false;
123 arg_ainfo.defaultstatus = cgiarginfo::weak;
124 arg_ainfo.argdefault = g_EmptyText;
125 arg_ainfo.savedarginfo = cgiarginfo::must;
126 argsinfo.addarginfo (NULL, arg_ainfo);
127
128 // "n2"
129 arg_ainfo.shortname = "n2";
130 arg_ainfo.longname = "language index for second query";
131 arg_ainfo.multiplechar = true;
132 arg_ainfo.multiplevalue = false;
133 arg_ainfo.defaultstatus = cgiarginfo::weak;
134 arg_ainfo.argdefault = g_EmptyText;
135 arg_ainfo.savedarginfo = cgiarginfo::must;
136 argsinfo.addarginfo (NULL, arg_ainfo);
137
138
139 // "t" - 1 = ranked 0 = boolean
140 arg_ainfo.shortname = "t";
141 arg_ainfo.longname = "search type";
142 arg_ainfo.multiplechar = false;
143 arg_ainfo.multiplevalue = false;
144 arg_ainfo.defaultstatus = cgiarginfo::weak;
145 arg_ainfo.argdefault = "1";
146 arg_ainfo.savedarginfo = cgiarginfo::must;
147 argsinfo.addarginfo (NULL, arg_ainfo);
148
149 // "k"
150 arg_ainfo.shortname = "k";
151 arg_ainfo.longname = "casefolding";
152 arg_ainfo.multiplechar = false;
153 arg_ainfo.multiplevalue = false;
154 arg_ainfo.defaultstatus = cgiarginfo::weak;
155 arg_ainfo.argdefault = "1";
156 arg_ainfo.savedarginfo = cgiarginfo::must;
157 argsinfo.addarginfo (NULL, arg_ainfo);
158
159 // "ks"
160 arg_ainfo.shortname = "ks";
161 arg_ainfo.longname = "casefolding support";
162 arg_ainfo.multiplechar = false;
163 arg_ainfo.multiplevalue = false;
164 arg_ainfo.defaultstatus = cgiarginfo::weak;
165 arg_ainfo.argdefault = "0";
166 arg_ainfo.savedarginfo = cgiarginfo::must;
167 argsinfo.addarginfo (NULL, arg_ainfo);
168
169 // "s"
170 arg_ainfo.shortname = "s";
171 arg_ainfo.longname = "stemming";
172 arg_ainfo.multiplechar = false;
173 arg_ainfo.multiplevalue = false;
174 arg_ainfo.defaultstatus = cgiarginfo::weak;
175 arg_ainfo.argdefault = "0";
176 arg_ainfo.savedarginfo = cgiarginfo::must;
177 argsinfo.addarginfo (NULL, arg_ainfo);
178
179 // "ss"
180 arg_ainfo.shortname = "ss";
181 arg_ainfo.longname = "stemming support";
182 arg_ainfo.multiplechar = false;
183 arg_ainfo.multiplevalue = false;
184 arg_ainfo.defaultstatus = cgiarginfo::weak;
185 arg_ainfo.argdefault = "0";
186 arg_ainfo.savedarginfo = cgiarginfo::must;
187 argsinfo.addarginfo (NULL, arg_ainfo);
188
189 // "af"
190 arg_ainfo.shortname = "af";
191 arg_ainfo.longname = "accentfolding";
192 arg_ainfo.multiplechar = false;
193 arg_ainfo.multiplevalue = false;
194 arg_ainfo.defaultstatus = cgiarginfo::weak;
195 arg_ainfo.argdefault = "0";
196 arg_ainfo.savedarginfo = cgiarginfo::must;
197 argsinfo.addarginfo (NULL, arg_ainfo);
198
199 // "afs"
200 arg_ainfo.shortname = "afs";
201 arg_ainfo.longname = "accentfolding support";
202 arg_ainfo.multiplechar = false;
203 arg_ainfo.multiplevalue = false;
204 arg_ainfo.defaultstatus = cgiarginfo::weak;
205 arg_ainfo.argdefault = "0";
206 arg_ainfo.savedarginfo = cgiarginfo::must;
207 argsinfo.addarginfo (NULL, arg_ainfo);
208
209
210 // "ccs"
211 arg_ainfo.shortname = "ccs";
212 arg_ainfo.longname = "cross collection searching";
213 arg_ainfo.multiplechar = false;
214 arg_ainfo.multiplevalue = false;
215 arg_ainfo.defaultstatus = cgiarginfo::weak;
216 arg_ainfo.argdefault = "0";
217 arg_ainfo.savedarginfo = cgiarginfo::must;
218 argsinfo.addarginfo (NULL, arg_ainfo);
219
220 // "ccp"
221 arg_ainfo.shortname = "ccp";
222 arg_ainfo.longname = "cross collection page";
223 arg_ainfo.multiplechar = false;
224 arg_ainfo.multiplevalue = false;
225 arg_ainfo.defaultstatus = cgiarginfo::weak;
226 arg_ainfo.argdefault = "0";
227 arg_ainfo.savedarginfo = cgiarginfo::must;
228 argsinfo.addarginfo (NULL, arg_ainfo);
229
230 // "g" - new arg for granularity, for mgpp collections
231 arg_ainfo.shortname = "g";
232 arg_ainfo.longname = "granularity";
233 arg_ainfo.multiplechar = true;
234 arg_ainfo.multiplevalue = false;
235 arg_ainfo.defaultstatus = cgiarginfo::weak;
236 arg_ainfo.argdefault = g_EmptyText;
237 arg_ainfo.savedarginfo = cgiarginfo::must;
238 argsinfo.addarginfo (NULL, arg_ainfo);
239
240 // "ds" - start date
241 arg_ainfo.shortname = "ds";
242 arg_ainfo.longname = "start date";
243 arg_ainfo.multiplechar = true;
244 arg_ainfo.multiplevalue = false;
245 arg_ainfo.defaultstatus = cgiarginfo::weak;
246 arg_ainfo.argdefault = g_EmptyText;
247 arg_ainfo.savedarginfo = cgiarginfo::must;
248 argsinfo.addarginfo (NULL, arg_ainfo);
249
250 // "de" - end date
251 arg_ainfo.shortname = "de";
252 arg_ainfo.longname = "end date";
253 arg_ainfo.multiplechar = true;
254 arg_ainfo.multiplevalue = false;
255 arg_ainfo.defaultstatus = cgiarginfo::weak;
256 arg_ainfo.argdefault = g_EmptyText;
257 arg_ainfo.savedarginfo = cgiarginfo::must;
258 argsinfo.addarginfo (NULL, arg_ainfo);
259
260 // "dsbc" - whether or not start date is prechristian
261 arg_ainfo.shortname = "dsbc";
262 arg_ainfo.longname = "start date bc";
263 arg_ainfo.multiplechar = false;
264 arg_ainfo.multiplevalue = false;
265 arg_ainfo.defaultstatus = cgiarginfo::weak;
266 arg_ainfo.argdefault = "0";
267 arg_ainfo.savedarginfo = cgiarginfo::must;
268 argsinfo.addarginfo (NULL, arg_ainfo);
269
270 // "debc" - whether or not end date is prechristian
271 arg_ainfo.shortname = "debc";
272 arg_ainfo.longname = "end date bc";
273 arg_ainfo.multiplechar = false;
274 arg_ainfo.multiplevalue = false;
275 arg_ainfo.defaultstatus = cgiarginfo::weak;
276 arg_ainfo.argdefault = "0";
277 arg_ainfo.savedarginfo = cgiarginfo::must;
278 argsinfo.addarginfo (NULL, arg_ainfo);
279
280 // "qt" - 0 = text, 1 = form
281 arg_ainfo.shortname = "qt";
282 arg_ainfo.longname = "query type";
283 arg_ainfo.multiplechar = true; // can be empty or single char
284 arg_ainfo.multiplevalue = false;
285 arg_ainfo.defaultstatus = cgiarginfo::weak;
286 arg_ainfo.argdefault = g_EmptyText;
287 arg_ainfo.savedarginfo = cgiarginfo::must;
288 argsinfo.addarginfo (NULL, arg_ainfo);
289
290 // "qto" - 1 = text only, 2 = form only, 3 = text and form
291 arg_ainfo.shortname = "qto";
292 arg_ainfo.longname = "query type options";
293 arg_ainfo.multiplechar = true; // can be empty or single char
294 arg_ainfo.multiplevalue = false;
295 arg_ainfo.defaultstatus = cgiarginfo::weak;
296 arg_ainfo.argdefault = g_EmptyText;
297 arg_ainfo.savedarginfo = cgiarginfo::must;
298 argsinfo.addarginfo (NULL, arg_ainfo);
299
300 // "qb" - 0 = regular, 1 = large
301 arg_ainfo.shortname = "qb";
302 arg_ainfo.longname = "query box type";
303 arg_ainfo.multiplechar = false;
304 arg_ainfo.multiplevalue = false;
305 arg_ainfo.defaultstatus = cgiarginfo::weak;
306 arg_ainfo.argdefault = "0";
307 arg_ainfo.savedarginfo = cgiarginfo::must;
308 argsinfo.addarginfo (NULL, arg_ainfo);
309
310
311 // "fqs" - the list of stemming options in the form query
312 // - a comma separated list
313 arg_ainfo.shortname = "fqs";
314 arg_ainfo.longname = "form query stems";
315 arg_ainfo.multiplechar = true;
316 arg_ainfo.multiplevalue = false;
317 arg_ainfo.defaultstatus = cgiarginfo::weak;
318 arg_ainfo.argdefault = g_EmptyText;
319 arg_ainfo.savedarginfo = cgiarginfo::must;
320 argsinfo.addarginfo (NULL, arg_ainfo);
321
322
323 // "fqk" - the list of casefolding options in the form query
324 // - a comma separated list
325 arg_ainfo.shortname = "fqk";
326 arg_ainfo.longname = "form query casefolds";
327 arg_ainfo.multiplechar = true;
328 arg_ainfo.multiplevalue = false;
329 arg_ainfo.defaultstatus = cgiarginfo::weak;
330 arg_ainfo.argdefault = g_EmptyText;
331 arg_ainfo.savedarginfo = cgiarginfo::must;
332 argsinfo.addarginfo (NULL, arg_ainfo);
333
334 // "fqaf" - the list of accentfolding options in the form query
335 // - a comma separated list
336 arg_ainfo.shortname = "fqaf";
337 arg_ainfo.longname = "form query accentfold";
338 arg_ainfo.multiplechar = true;
339 arg_ainfo.multiplevalue = false;
340 arg_ainfo.defaultstatus = cgiarginfo::weak;
341 arg_ainfo.argdefault = g_EmptyText;
342 arg_ainfo.savedarginfo = cgiarginfo::must;
343 argsinfo.addarginfo (NULL, arg_ainfo);
344
345 // "cc"
346 arg_ainfo.shortname = "cc";
347 arg_ainfo.longname = "collections to search";
348 arg_ainfo.multiplechar = true;
349 arg_ainfo.multiplevalue = true;
350 arg_ainfo.defaultstatus = cgiarginfo::weak;
351 arg_ainfo.argdefault = g_EmptyText;
352 arg_ainfo.savedarginfo = cgiarginfo::must;
353 argsinfo.addarginfo (NULL, arg_ainfo);
354
355
356 // ****
357 // should this even be here???
358 // seems to be mixed up between "sf" and "sqlsf"
359
360 // "sf" - Sort field. Set to field to be used for sorting search reult
361 // set (only implemented for lucene collections at present).
362 arg_ainfo.shortname = "sqlsf";
363 arg_ainfo.longname = "sql sort field";
364 arg_ainfo.multiplechar = true;
365 arg_ainfo.multiplevalue = false;
366 arg_ainfo.defaultstatus = cgiarginfo::weak;
367 arg_ainfo.argdefault = g_EmptyText;
368 arg_ainfo.savedarginfo = cgiarginfo::must;
369 argsinfo.addarginfo (NULL, arg_ainfo);
370
371
372}
373
374queryaction::~queryaction ()
375{
376}
377
378void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
379 basequeryaction::configure (key, cfgline);
380}
381
382bool queryaction::init (ostream &logout) {
383 return basequeryaction::init (logout);
384}
385
386bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
387 recptprotolistclass* protos, ostream &logout) {
388
389 // check t argument
390 int arg_t = args.getintarg("t");
391 if (arg_t != 0 && arg_t != 1) {
392 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
393 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
394 if (tinfo != NULL) args["t"] = tinfo->argdefault;
395 }
396
397 // check k argument
398 int arg_k = args.getintarg("k");
399 if (arg_k != 0 && arg_k != 1) {
400 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
401 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
402 if (kinfo != NULL) args["k"] = kinfo->argdefault;
403 }
404
405 // check s argument
406 int arg_s = args.getintarg("s");
407 if (arg_s != 0 && arg_s != 1) {
408 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
409 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
410 if (sinfo != NULL) args["s"] = sinfo->argdefault;
411 }
412
413
414 // check ct argument
415 int arg_ct = args.getintarg("ct");
416 if (arg_ct < 0 || arg_ct > 2) {
417 logout << "Warning: \"ct\" argument out of range (" << arg_ct << ")\n";
418 cgiarginfo *ctinfo = argsinfo.getarginfo ("ct");
419 if (ctinfo != NULL) args["ct"] = ctinfo->argdefault;
420 }
421
422 // check qt argument
423 int arg_qt = args.getintarg("qt");
424 if (arg_qt<0 || arg_qt>2) {
425 logout << "Warning: \"qt\" argument out of range (" << arg_qt << ")\n";
426 cgiarginfo *qtinfo = argsinfo.getarginfo ("qt");
427 if (qtinfo != NULL) args["qt"] = qtinfo->argdefault;
428 }
429
430 // check qb argument
431 int arg_qb = args.getintarg("qb");
432 if (arg_qb !=0 && arg_qb !=1) {
433 logout << "Warning: \"qb\" argument out of range (" << arg_qb << ")\n";
434 cgiarginfo *qbinfo = argsinfo.getarginfo ("qb");
435 if (qbinfo != NULL) args["qb"] = qbinfo->argdefault;
436 }
437
438 // check fqa argument
439 int arg_fqa = args.getintarg("fqa");
440 if (arg_fqa !=0 && arg_fqa !=1) {
441 logout << "Warning: \"fqa\" argument out of range (" << arg_fqa << ")\n";
442 cgiarginfo *fqainfo = argsinfo.getarginfo ("fqa");
443 if (fqainfo != NULL) args["fqa"] = fqainfo->argdefault;
444 }
445
446 // check fqn argument
447 int arg_fqn = args.getintarg("fqn");
448 if (arg_fqn < -1) {
449 logout << "Warning: \"fqn\" argument less than -1 (" << arg_fqn << ")\n";
450 cgiarginfo *fqninfo = argsinfo.getarginfo ("fqn");
451 if (fqninfo != NULL) args["fqn"] = fqninfo->argdefault;
452 }
453
454 return basequeryaction::check_cgiargs(argsinfo,args,protos,logout);
455
456}
457
458void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
459 recptprotolistclass * protos,
460 ostream &logout)
461{
462 basequeryaction::define_internal_macros(disp,args,protos,logout);
463
464 define_query_interface(disp, args, protos, logout);
465}
466
467void queryaction::define_query_interface(displayclass &disp,
468 cgiargsclass &args,
469 recptprotolistclass * protos,
470 ostream &logout){
471 text_t collection = args["c"];
472
473 //check that the protocol is alive
474 recptproto* colproto = protos->getrecptproto (collection, logout);
475 if(colproto == NULL) {
476 logout << "ERROR: Null collection protocol trying to query"
477 << collection.getcstr() << "\n";
478 return;
479 }
480
481 //check the collection is responding/in place
482 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(colproto, collection,
483 logout);
484 if(colinfo == NULL){
485 logout << "ERROR: Null returned for get_collectinfo_ptr on "
486 << collection.getcstr() << "in queryaction::define_query_interface\n";
487 return;
488 }
489
490 text_tmap::iterator check = colinfo->format.find("QueryInterface");
491 if(check != colinfo->format.end()){
492 if((*check).second=="DateSearch"){
493 text_t current = "_datesearch_";
494 disp.setmacro("optdatesearch","query",current);
495 }
496 }
497}
498
499
500
501void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args,
502 recptprotolistclass *protos, ostream &logout) {
503
504 // define_external_macros sets the following macros:
505
506 // some or all of these may not be required to be set
507 // _hselection_, _h2selection_ the selection box for the main part of the index
508 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
509 // _nselection_, _n2selection_ the selection box for the language part of the index
510 // _cq2selection the selection box for combining two queries
511
512 // _gselection_, the selection box forlevels (mgpp)
513 // _fqfselection_, the selection box for index/fields (mgpp)
514 // can't do anything if collectproto is null (i.e. no collection was specified)
515 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
516 if (collectproto == NULL) return;
517
518 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(collectproto,
519 args["c"],
520 logout);
521 set_query_type_args(colinfo, args);
522 set_stem_index_args(colinfo, args);
523
524 comerror_t err;
525 InfoFilterOptionsResponse_t response;
526 InfoFilterOptionsRequest_t request;
527 request.filterName = "QueryFilter";
528
529 collectproto->get_filteroptions (args["c"], request, response, err, logout);
530 if (err == noError) {
531
532 FilterOption_tmap::const_iterator it;
533 FilterOption_tmap::const_iterator end = response.filterOptions.end();
534
535 // _hselection_ and _h2selection_ (Index)
536 it = response.filterOptions.find ("Index");
537 if (it != end) set_option_macro ("h", args["h"], true, false, (*it).second, disp);
538 if (it != end) set_option_macro ("h2", args["h2"], true,false, (*it).second, disp);
539
540 // _jselection_ and _j2selection_ (Subcollection)
541 it = response.filterOptions.find ("Subcollection");
542 if (it != end) set_option_macro ("j", args["j"], true,false, (*it).second, disp);
543 if (it != end) set_option_macro ("j2", args["j2"], true,false, (*it).second, disp);
544
545 // _nselection_ and _n2selection_ (Language)
546 it = response.filterOptions.find ("Language");
547 if (it != end) set_option_macro ("n", args["n"], true,false, (*it).second, disp);
548 if (it != end) set_option_macro ("n2", args["n2"], true,false, (*it).second, disp);
549
550 // _cq2selection_ (CombineQuery)
551 it = response.filterOptions.find ("CombineQuery");
552 if (it != end) set_option_macro ("cq2", args["cq2"], true,false, (*it).second, disp);
553
554 if ((args["ct"] == "1") || (args["ct"] == "2")) { // mgpp/lucene collections
555 // _gselection_ (Level)
556 it = response.filterOptions.find("Level");
557 if (it!=end) {
558 set_option_macro("g", args["g"], false, false, (*it).second, disp);
559 if (args["qt"]=="1") { // form search
560 set_gformselection_macro(args["g"], (*it).second, disp);
561 }
562 }
563 // _fqfselection_ field list
564 it = response.filterOptions.find("IndexField");
565 if (it!=end) {
566 bool form_search = false;
567 if (args["qto"]=="2" || args["qt"]=="1") {
568 form_search = true;
569 }
570 set_option_macro ("fqf", args["fqf"], true, form_search, (*it).second, disp);
571 }
572 if (args["ct"] == "2") {// lucene
573 it = response.filterOptions.find("SortField");
574 // set the sort field macro
575 set_sfselection_macro(args["sf"], (*it).second, disp);
576 }
577 }
578
579
580 // add a queryterms macro for plain version of search terms
581 if (!args["q"].empty()|| !args["fqv"].empty()) {
582 text_t query_arg = "";
583 if (args["qt"]=="0" && args["qto"] != "2") { // normal text search
584 query_arg = args["q"];
585 }
586 else if (args["qt"]=="1" || args["qto"]=="2"){ // form search
587
588 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
589 query_arg = args["q"];
590 }
591 else { // form search
592 query_arg = args["fqv"];
593 }
594 }
595 disp.setmacro ("queryterms", displayclass::defaultpackage, get_plain_query_terms(query_arg, args["ct"]));
596 }
597
598 }
599} // define external macros
600
601
602void queryaction::set_sfselection_macro(text_t current_value,
603 const FilterOption_t &option,
604 displayclass &disp) {
605
606 // we need at least two options here to continue
607 if (option.validValues.size() < 2) {
608 return;
609 }
610
611 text_t macrovalue = "<select name=\"sf\">\n";
612
613 if (current_value.empty()) current_value = "";
614
615 text_tarray::const_iterator thisvalue = option.validValues.begin();
616 text_tarray::const_iterator endvalue = option.validValues.end();
617 // int valid_count = 0;
618 while (thisvalue != endvalue) {
619 if (*thisvalue == "rank") {
620 macrovalue += "<option value=\"rank\"";
621 if (current_value == "rank") {
622 macrovalue += " selected";
623 }
624 macrovalue += ">_query:textsortbyrank_\n";
625 } else {
626 macrovalue += "<option value=\"" + *thisvalue + "\"";
627 if (current_value == *thisvalue)
628 macrovalue += " selected";
629 macrovalue += ">_" + *thisvalue + "_\n";
630 }
631 ++thisvalue;
632 }
633 macrovalue += "</select>";
634 disp.setmacro ("sfselection", displayclass::defaultpackage, macrovalue);
635
636}
637
638// sets the selection box macro _gformselection_.
639// the default for _gformselection_ is _gselection_
640void queryaction::set_gformselection_macro (text_t current_value,
641 const FilterOption_t &option,
642 displayclass &disp) {
643
644 if (option.validValues.size() <= 1) {
645 return;
646 }
647 // we need to check to see if there is paragraph present
648 text_tarray::const_iterator thisvalue = option.validValues.begin();
649 text_tarray::const_iterator endvalue = option.validValues.end();
650
651 bool has_paras = false;
652 while (thisvalue != endvalue) {
653 if (*thisvalue == "Para") {
654 has_paras = true;
655 break;
656 }
657 ++thisvalue;
658 }
659 if (!has_paras) return; // there is no difference between the form selection and the normal one
660
661 if (option.validValues.size() == 2) {
662 // we will only have one value, but we will still put it in as a text string
663 int opt = 0;
664 if (option.validValues[0] == "Para") {
665 opt = 1;
666 }
667 disp.setmacro ("gformselection", displayclass::defaultpackage, "_"+option.validValues[opt]+"_");
668 return;
669 }
670
671 // there will be a select box
672 text_t macrovalue = "<select name=\"g\">\n";
673
674 if (current_value.empty()) current_value = option.defaultValue;
675
676 thisvalue = option.validValues.begin();
677
678 while (thisvalue != endvalue) {
679 if (*thisvalue != "Para") {
680 macrovalue += "<option value=\"" + *thisvalue + "\"";
681 if (*thisvalue == current_value)
682 macrovalue += " selected";
683 macrovalue += ">_" + *thisvalue + "_\n";
684 }
685 ++thisvalue;
686 }
687 macrovalue += "</select>\n";
688 disp.setmacro ("gformselection", displayclass::defaultpackage, macrovalue);
689}
690
691void queryaction::define_form_macros (displayclass &disp, cgiargsclass &args,
692 recptprotolistclass *protos,
693 ostream &logout)
694{
695
696 // defines the following macros
697 // _regformlist_
698 // _advformlist_
699
700 if (args["ct"]=="0" || args["qto"]=="1" || (args["qto"]=="3" && args["qt"] == "0") ) // mg, or mgpp/lucene with plain only, or mgpp with both, but set to plain
701 return; // dont need these macros
702
703 text_t form = "";
704 int argfqn = args.getintarg("fqn");
705
706 if (args["b"] == "1") { // advanced form
707 form += "_firstadvformelement_\n";
708 for (int i=1; i<argfqn; ++i) {
709 form += "_advformelement_\n";
710 }
711 disp.setmacro("advformlist", "query", form);
712 }
713 else { // simple form
714 for (int i=0; i<argfqn; ++i) {
715 form += "_regformelement_\n";
716 }
717 disp.setmacro("regformlist", "query", form);
718 }
719
720}
721
722void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
723 displayclass &disp, outconvertclass &outconvert,
724 ostream &textout, ostream &logout) {
725
726 ColInfoResponse_t *cinfo = NULL;
727 comerror_t err;
728 InfoFilterOptionsResponse_t fresponse;
729 InfoFilterOptionsRequest_t frequest;
730 frequest.filterName = "QueryFilter";
731
732 text_t &index = args["h"];
733 text_t &subcollection = args["j"];
734 text_t &language = args["n"];
735
736 text_tset collections;
737 text_t arg_cc = args["cc"];
738 decode_cgi_arg (arg_cc);
739 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
740
741 textout << outconvert << disp << "_query:header_\n"
742 << "<center>_navigationbar_</center><br>\n"
743 << "<form name=\"QueryForm\" method=\"get\" action=\"_gwcgi_\">\n"
744 << "<input type=\"hidden\" name=\"a\" value=\"q\">\n"
745 << "<input type=\"hidden\" name=\"site\" value=\"_cgiargsite_\"\n"
746 << "<input type=\"hidden\" name=\"e\" value=\"_compressedoptions_\">\n"
747 << "<input type=\"hidden\" name=\"ccp\" value=\"1\">\n"
748 << "<center><table width=\"_pagewidth_\"><tr valign=\"top\">\n"
749 << "<td>Select collections to search for \"" << encodeForHTML(args["q"])
750 << "\" <i>(index=" << encodeForHTML(index) << " subcollection=" << encodeForHTML(subcollection)
751 << " language=" << encodeForHTML(language) << ")</i></td>\n"
752 << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
753 << "</tr></table></center>\n"
754 << "<center><table width=\"_pagewidth_\">\n"
755 << "<tr><td>\n";
756
757 recptprotolistclass::iterator rprotolist_here = protos->begin();
758 recptprotolistclass::iterator rprotolist_end = protos->end();
759 while (rprotolist_here != rprotolist_end) {
760 if ((*rprotolist_here).p != NULL) {
761
762 text_tarray collist;
763 (*rprotolist_here).p->get_collection_list (collist, err, logout);
764 if (err == noError) {
765 text_tarray::iterator collist_here = collist.begin();
766 text_tarray::iterator collist_end = collist.end();
767 while (collist_here != collist_end) {
768
769 cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
770 // if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
771 if (cinfo != NULL && (cinfo->buildDate > 0)) {
772
773 (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
774 if (err == noError) {
775
776 FilterOption_tmap::const_iterator it;
777 FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
778 if (!index.empty()) {
779 it = fresponse.filterOptions.find ("Index");
780 if (it == end) {++collist_here; continue;}
781 text_tarray::const_iterator there = (*it).second.validValues.begin();
782 text_tarray::const_iterator tend = (*it).second.validValues.end();
783 while (there != tend) {
784 if (*there == index) break;
785 ++there;
786 }
787 if (there == tend) {++collist_here; continue;}
788 }
789 if (!subcollection.empty()) {
790 it = fresponse.filterOptions.find ("Subcollection");
791 if (it == end) {++collist_here; continue;}
792 text_tarray::const_iterator there = (*it).second.validValues.begin();
793 text_tarray::const_iterator tend = (*it).second.validValues.end();
794 while (there != tend) {
795 if (*there == subcollection) break;
796 ++there;
797 }
798 if (there == tend) {++collist_here; continue;}
799 }
800 if (!language.empty()) {
801 it = fresponse.filterOptions.find ("Language");
802 if (it == end) {++collist_here; continue;}
803 text_tarray::const_iterator there = (*it).second.validValues.begin();
804 text_tarray::const_iterator tend = (*it).second.validValues.end();
805 while (there != tend) {
806 if (*there == language) break;
807 ++there;
808 }
809 if (there == tend) {++collist_here; continue;}
810 }
811
812 // we've got a matching collection
813 textout << outconvert << "<input type=\"checkbox\"";
814
815 text_tset::const_iterator t = collections.find (*collist_here);
816 if (t != collections.end()) textout << outconvert << " checked";
817
818 text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
819 if (collectionname.empty()) {
820 collectionname = *collist_here;
821 }
822 textout << outconvert << disp
823 << " name=\"cc\" value=\"" << *collist_here << "\">"
824 << collectionname << "<br>\n";
825
826
827 }
828 }
829 ++collist_here;
830 }
831 }
832 }
833 ++rprotolist_here;
834 }
835 textout << outconvert << disp
836 << "</td></tr></table></center>\n"
837 << "</form>\n"
838 << "_query:footer_\n";
839
840}
841
842bool queryaction::user_groups_match(const text_t &collection_groups, const text_t &user_groups) {
843
844 text_tset splitgrps;
845 text_t::const_iterator split_here = collection_groups.begin();
846 text_t::const_iterator split_end = collection_groups.end();
847
848 splitchar(split_here,split_end,',',splitgrps);
849
850 text_t::const_iterator ugroup_here = user_groups.begin();
851 text_t::const_iterator ugroup_end = user_groups.end();
852 text_t thisugroup;
853 while (ugroup_here != ugroup_end) {
854 ugroup_here = getdelimitstr (ugroup_here, ugroup_end, ',', thisugroup);
855 if (splitgrps.find(thisugroup) != splitgrps.end() )
856 { // we have permission!
857 return true;
858 }
859 }
860 return false;
861}
862
863// If we are currently authenticated to be in this collection, then check all
864// collections in the list against the groups of the current user - if there is an overlap of groups, then add the collection into ccs list
865// If there had been no authentication needed to get to this collection, then
866// we'll ignore any collections that have collection level authentication
867void queryaction::validate_ccs_collection_list(cgiargsclass &args, recptprotolistclass *protos, ostream &logout) {
868
869 text_tarray collections;
870 text_t arg_cc = args["cc"];
871 text_t arg_c = args["c"];
872 decode_cgi_arg (arg_cc);
873 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
874 bool currently_authenticated = false;
875 if (!args["uan"].empty()) {
876 // uan=1 means needs authentication. We'll only get here if we have passed authentication, otherwise the page would have been redirected to login page
877 currently_authenticated = true;
878 }
879 args["cc"] = ""; // we will add colls in one by one if they are valid
880 text_tarray::iterator col_here = collections.begin();
881 text_tarray::iterator col_end = collections.end();
882 bool first = true;
883 text_t current_user_name = args["un"];
884 userinfo_t thisuser;
885 if (currently_authenticated) {
886 int status = user_database->get_user_info (current_user_name, thisuser);
887 if (status != ERRNO_SUCCEED) { // something has gone wrong, so assume not
888 // authenticated
889 currently_authenticated = false;
890 }
891 }
892
893 while (col_here != col_end) {
894 bool include_coll = false;
895 if (*col_here == arg_c) {
896 // current collection must be accessible otherwise we wouldn't be here.
897 include_coll = true;
898 } else {
899 recptproto *collectproto = protos->getrecptproto (*col_here, logout);
900 if (collectproto != NULL) {
901 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
902 text_t authenticate = cinfo->authenticate;
903 if (authenticate == "collection") {
904 if (currently_authenticated) {
905 text_t collection_groups = cinfo->auth_group;
906 if (user_groups_match(collection_groups, thisuser.groups)) {
907 include_coll = true;
908 }
909 } // else we'll not include it
910 } else { // not authenticated, or document level authentication - can include in the list
911 include_coll = true;
912 }
913 }
914 }
915 if (include_coll) {
916 if (!first) args["cc"].push_back (',');
917 args["cc"] += *col_here;
918 first = false;
919 }
920
921 ++col_here;
922 }
923
924}
925
926bool queryaction::do_action (cgiargsclass &args, recptprotolistclass *protos,
927 browsermapclass *browsers, displayclass &disp,
928 outconvertclass &outconvert, ostream &textout,
929 ostream &logout) {
930
931 if (recpt == NULL) {
932 logout << "ERROR (queryaction::do_action): This action does not contain information\n"
933 << " about any receptionists. The method set_receptionist was probably\n"
934 << " not called from the module which instantiated this action.\n";
935 return true;
936 }
937
938 if (args["ccs"] == "1") {
939 if (!args["cc"].empty()) {
940 validate_ccs_collection_list(args, protos, logout); // include only those which current user has access to
941 // query the selected collections
942 text_t::const_iterator b = args["cc"].begin();
943 text_t::const_iterator e = args["cc"].end();
944 if (findchar (b, e, ',') != e) {
945 if (!search_multiple_collections (args, protos, browsers, disp, outconvert,
946 textout, logout)) return false;
947 return true;
948 } else {
949 if (!search_single_collection (args, args["cc"], protos, browsers, disp,
950 outconvert, textout, logout)) return false;
951 return true;
952 }
953 }
954 }
955
956 // simply query the current collection
957 if (!search_single_collection (args, args["c"], protos, browsers, disp,
958 outconvert, textout, logout)) return false;
959 return true;
960}
961
962
963
964// request.filterResultOptions and request.fields (if required) should
965// be set from the calling code
966void queryaction::set_queryfilter_options (FilterRequest_t &request,
967 const text_t &querystring,
968 cgiargsclass &args)
969{
970 set_fulltext_queryfilter_options(request,querystring,args);
971}
972
973
974
975void queryaction::set_queryfilter_options (FilterRequest_t &request,
976 const text_t &querystring1,
977 const text_t &querystring2,
978 cgiargsclass &args)
979{
980 set_fulltext_queryfilter_options(request,querystring1,querystring2,args);
981}
982
983
984
985bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
986 browsermapclass *browsers, displayclass &disp,
987 outconvertclass &outconvert, ostream &textout,
988 ostream &logout) {
989
990 text_tarray collections;
991
992 text_t arg_cc = args["cc"];
993 decode_cgi_arg (arg_cc);
994 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
995
996 if (collections.empty()) {
997 logout << "queryaction::search_multiple_collections: No collections "
998 << "set for doing multiple query - will search current collection\n";
999 textout << outconvert << disp << "_query:textwarningnocollections_\n";
1000 return search_single_collection (args, args["c"], protos, browsers, disp,
1001 outconvert, textout, logout);
1002 }
1003
1004 // check the main coll
1005 text_t main_collection = args["c"];
1006 recptproto *collectproto = protos->getrecptproto (main_collection, logout);
1007 if (collectproto == NULL) {
1008 logout << outconvert << "queryaction::search_multiple_collection: " << main_collection
1009 << " collection has a NULL collectproto\n";
1010
1011 // Display the "this collection is not installed on this system" page
1012 disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(main_collection));
1013 disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
1014
1015 textout << outconvert << disp << "_query:header_\n"
1016 << "_query:content_\n" << "_query:footer_\n";
1017 return true;
1018 }
1019
1020 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, main_collection, logout);
1021 if (cinfo == NULL) {
1022 logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL for '"<<main_collection<<"'\n";
1023 return false;
1024 }
1025
1026 // get the formatted query string
1027 // what to do about segmentation for multiple colls??
1028 // TODO
1029 bool segment = false;
1030 text_t formattedstring = "";
1031 get_formatted_query_string(formattedstring, segment, args, disp, logout);
1032
1033 if (formattedstring.empty()) {
1034 // dont bother doing a query if no query string
1035 define_history_macros (disp, args, protos, logout);
1036 textout << outconvert << disp << "_query:header_\n"
1037 << "_query:content_";
1038 textout << outconvert << disp << "_query:footer_";
1039
1040 return true;
1041 }
1042
1043
1044 // queryaction uses "VList" browser to display results,
1045 // a queries clasification is "Search"
1046 text_t browsertype = "VList";
1047 text_t classification = "Search";
1048
1049 QueryResult_tset results;
1050 map<text_t, colinfo_t, lttext_t> colinfomap;
1051
1052 comerror_t err;
1053 FilterRequest_t request;
1054 FilterResponse_t response;
1055 request.filterResultOptions = FROID | FRmetadata | FRtermFreq | FRranking;
1056 text_t freqmsg = "_textfreqmsg1_";
1057 int numdocs = 0;
1058 isapprox isApprox = Exact;
1059
1060
1061 bool syntax_error = false;
1062
1063 set_queryfilter_options (request, formattedstring, args);
1064
1065 // need to retrieve maxdocs matches for each collection
1066 // (will eventually want to tidy this up, do so caching etc.)
1067 OptionValue_t option;
1068 option.name = "StartResults";
1069 option.value = "1";
1070 request.filterOptions.push_back (option);
1071
1072 option.name = "EndResults";
1073 option.value = args["m"];
1074 request.filterOptions.push_back (option);
1075
1076 // check the main collection for uniform formatting info - do we use
1077 // individual format statements, or just the main one?
1078 bool use_main_col_format = false;
1079 if (cinfo->ccsOptions & CCSUniformSearchResultsFormatting) {
1080 use_main_col_format = true;
1081 }
1082
1083 browserclass *bptr = browsers->getbrowser (browsertype);
1084
1085 request.fields.erase (request.fields.begin(), request.fields.end());
1086 request.getParents = false;
1087 bptr->load_metadata_defaults (request.fields);
1088
1089 text_t formatstring;
1090 format_t *formatlistptr = new format_t();
1091 if (use_main_col_format) {
1092 // just get one format for main coll and use it for each subcol
1093 if (!get_formatstring (classification, browsertype,
1094 cinfo->format, formatstring)) {
1095 formatstring = bptr->get_default_formatstring();
1096 }
1097
1098 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1099 }
1100
1101 text_tarray::iterator col_here = collections.begin();
1102 text_tarray::iterator col_end = collections.end();
1103
1104 map<text_t, int, lttext_t> termfreqs;
1105
1106 ColInfoResponse_t *tmp_cinfo;
1107 while (col_here != col_end) {
1108
1109 collectproto = protos->getrecptproto (*col_here, logout);
1110 if (collectproto == NULL) {
1111 // skip this collection
1112 logout << outconvert << "queryaction::search_multiple_collections: " << *col_here
1113 << " collection has a NULL collectproto, ignoring\n";
1114 ++col_here;
1115 continue;
1116 }
1117 tmp_cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
1118 if (tmp_cinfo == NULL) {
1119 // skip this collection
1120 logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL\n";
1121 ++col_here;
1122 continue;
1123 }
1124
1125 if (!use_main_col_format) {
1126 request.fields.erase (request.fields.begin(), request.fields.end());
1127 request.getParents = false;
1128 bptr->load_metadata_defaults (request.fields);
1129
1130 // get the formatstring if there is one
1131 if (!get_formatstring (classification, browsertype,
1132 tmp_cinfo->format, formatstring)) {
1133 formatstring = bptr->get_default_formatstring();
1134 }
1135
1136 formatlistptr = new format_t();
1137 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1138 }
1139
1140 colinfo_t thiscolinfo;
1141 thiscolinfo.formatlistptr = formatlistptr;
1142 thiscolinfo.browserptr = bptr;
1143 colinfomap[*col_here] = thiscolinfo;
1144
1145 // do the query
1146 collectproto->filter (*col_here, request, response, err, logout);
1147 if (err != noError && err != syntaxError) {
1148 outconvertclass text_t2ascii;
1149 logout << text_t2ascii
1150 << "queryaction::search_multiple_collections: call to QueryFilter failed "
1151 << "for " << *col_here << " collection (" << get_comerror_string (err) << ")\n";
1152 return false;
1153 }
1154
1155 if (err == syntaxError) {
1156 syntax_error = true;
1157 freqmsg = "_textinvalidquery_";
1158 // assume the syntax will be invalid for all colls
1159 break;
1160 }
1161 if (response.error_message == "TOO_MANY_CLAUSES") {
1162 freqmsg = "_textlucenetoomanyclauses_";
1163 break;
1164 }
1165 if (isApprox == Exact)
1166 isApprox = response.isApprox;
1167 else if (isApprox == MoreThan)
1168 if (response.isApprox == Approximate)
1169 isApprox = response.isApprox;
1170
1171 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
1172 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
1173 while (this_term != end_term) {
1174 termfreqs[(*this_term).term] += (*this_term).freq;
1175 if ((col_here+1) == col_end) {
1176 freqmsg += (*this_term).term + ": " + termfreqs[(*this_term).term];
1177 if ((this_term+1) != end_term) freqmsg += ", ";
1178 }
1179 ++this_term;
1180 }
1181
1182 if (response.numDocs > 0) {
1183 numdocs += response.numDocs;
1184
1185 QueryResult_t thisresult;
1186 thisresult.collection = *col_here;
1187 ResultDocInfo_tarray::iterator doc_here = response.docInfo.begin();
1188 ResultDocInfo_tarray::iterator doc_end = response.docInfo.end();
1189 while (doc_here != doc_end) {
1190 thisresult.doc = *doc_here;
1191 results.insert (thisresult); // this is ordering based on doc rank
1192 ++doc_here;
1193 }
1194 }
1195 ++col_here;
1196 } // for each coll
1197
1198 // now we have an ordered list of results. If ifl (I feel lucky) is set, then pick out the one we want
1199 if (args["ifl"] == 1 || (args["ifl"] == 2 && numdocs == 1)) {
1200
1201 //Find whether DocumentSearchResultLinks is enabled
1202 bool show_links = false;
1203 text_tmap::const_iterator format_here = cinfo->format.begin();
1204 text_tmap::const_iterator format_end = cinfo->format.end();
1205
1206 while (format_here != format_end) {
1207 if (((*format_here).first == "DocumentSearchResultLinks") &&
1208 ((*format_here).second == "true")){
1209 show_links = true;
1210 break;
1211 }
1212 ++format_here;
1213 }
1214
1215 // which doc do we want?
1216 int docnum;
1217 int ifl;
1218 int srn = 0;
1219 int srp = 0;
1220 if (args["ifl"] == 1) {
1221 ifl = args["ifln"].getint();
1222 docnum = ifl - 1;
1223 if (show_links) {
1224 // set the values for next and prev search result number
1225 srn = ifl + 1;
1226 if (srn > numdocs) {
1227 srn = 0;
1228 }
1229 srp = ifl - 1;
1230 if (srp < 0) {
1231 srp = 0;
1232 }
1233 }
1234 } else {
1235 // we just want the first (and only) result
1236 docnum = 0;
1237 }
1238
1239 if (docnum >= 0 && docnum < numdocs) {
1240 // get the docnum'th item from the results
1241 QueryResult_tset::iterator res_here = results.begin();
1242 for (int i=0; i< docnum; i++) {
1243 ++res_here;
1244 }
1245
1246 textout << outconvert << disp
1247 << "Location: _gwcgi_?e=_compressedoptions_&a=d&c="
1248 << (*res_here).collection << "&cl=search&d=" << (*res_here).doc.OID
1249 << "&srn=" << srn << "&srp=" << srp << "\n\n";
1250 textout << flush;
1251
1252 return true;
1253
1254 }
1255 }
1256
1257 if (!args["ifl"].empty()) {
1258 // if we get here, and ifl was set but we haven't output a document, then we'll carry on as if ifl wasn't set. The only catch is that get_cgihead_info won't have
1259 // done the right thing (because ifl was set), so we need to make sure the output is html
1260 textout << "Content-type: text/html\n\n";
1261 }
1262
1263
1264 text_t numdocs_t = numdocs;
1265 args["nmd"] = numdocs_t;
1266
1267 disp.setmacro ("freqmsg", "query", freqmsg);
1268
1269 define_query_macros( args, disp, numdocs, isApprox);
1270 // save the query if appropriate
1271 save_search_history(args, numdocs, isApprox);
1272 define_history_macros (disp, args, protos, logout);
1273
1274 textout << outconvert << disp << "_query:header_\n"
1275 << "_query:content_";
1276
1277 if (!syntax_error) {
1278
1279 // now go through each result and output it
1280 QueryResult_tset::iterator res_here = results.begin();
1281 QueryResult_tset::iterator res_end = results.end();
1282 text_tset metadata; // empty !!
1283 bool getParents = false; // don't care !!
1284 bool use_table;
1285 ResultDocInfo_t thisdoc;
1286 format_t *formatlistptr = NULL;
1287 browserclass *browserptr = NULL;
1288
1289 int count = 1;
1290 int firstdoc = args.getintarg("r");
1291 int hitsperpage = args.getintarg("o");
1292 int thislast = firstdoc + (hitsperpage - 1);
1293
1294 // output results
1295 while (res_here != res_end) {
1296 if (count < firstdoc) {++count; ++res_here; continue;}
1297 if (count > thislast) break;
1298
1299 formatlistptr = colinfomap[(*res_here).collection].formatlistptr;
1300 browserptr = colinfomap[(*res_here).collection].browserptr;
1301 thisdoc = (*res_here).doc;
1302 use_table = is_table_content (formatlistptr);
1303
1304 collectproto = protos->getrecptproto ((*res_here).collection, logout);
1305 if (collectproto == NULL) {
1306 logout << outconvert << "queryaction::search_multiple_collections: " << (*res_here).collection
1307 << " collection has a NULL collectproto, ignoring results\n";
1308 ++res_here;
1309 continue;
1310 }
1311
1312 browserptr->output_section_group (thisdoc, args, (*res_here).collection, 0,
1313 formatlistptr, use_table, metadata, getParents,
1314 collectproto, disp, outconvert, textout, logout);
1315 // textout << outconvert << "(ranking: " << (*res_here).doc.ranking << ")\n";
1316 ++res_here;
1317 ++count;
1318 }
1319 }
1320 textout << outconvert << disp << "_query:footer_";
1321
1322 // clean up the format_t pointers
1323 map<text_t, colinfo_t, lttext_t>::iterator here = colinfomap.begin();
1324 map<text_t, colinfo_t, lttext_t>::iterator end = colinfomap.end();
1325 while (here != end) {
1326 delete ((*here).second.formatlistptr);
1327 ++here;
1328 }
1329 return true;
1330}
1331
1332
1333// does the formatting of the query string - either uses q for a text search
1334// or the form values for an form search
1335// also adds dates if appropriate in text search
1336void queryaction::get_formatted_query_string (text_t &formattedstring,
1337 bool segment,
1338 cgiargsclass &args,
1339 displayclass &disp,
1340 ostream &logout) {
1341 if (args["qt"]=="0" && args["qto"] != "2") { // normal text search
1342 formattedstring = args["q"];
1343 // remove & | ! for simple search,do segmentation if necessary
1344 // To url-decode the '&', format_querystring() will call unsafe_cgi_arg() first
1345 format_querystring (formattedstring, args.getintarg("b"), segment);
1346 if (args["ct"]!=0) { // mgpp and lucene - need to add in tag info if appropriate
1347 format_field_info(formattedstring, args["fqf"], args.getintarg("ct"),
1348 args.getintarg("t"), args.getintarg("b"));
1349 }
1350
1351 add_dates(formattedstring, args.getintarg("ds"), args.getintarg("de"),
1352 args.getintarg("dsbc"), args.getintarg("debc"),
1353 args.getintarg("ct"));
1354 args["q"] = formattedstring;
1355
1356 }
1357 else if (args["qt"]=="1" || args["qto"]=="2"){ // form search
1358
1359 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
1360 formattedstring = args["q"];
1361
1362 // Replace %22 and %26 with " and & respectively, since these characters have meaning
1363 // in queries: " are used in phrases and & is used in boolean advanced searches.
1364 // For form searches below, unsafe_cgi_arg is called in the parse_..._form() functions
1365
1366 unsafe_cgi_arg("ALL", formattedstring);
1367 }
1368 else { // form search
1369 if (args["b"]=="0") { // regular form
1370 parse_reg_query_form(formattedstring, args, segment); // will call unsafe_cgi_arg to decode url encoding
1371 }
1372 else { // advanced form
1373 parse_adv_query_form(formattedstring, args, segment); // will call unsafe_cgi_arg to decode url encoding
1374 }
1375 args["q"] = formattedstring;
1376
1377 // reset the cgiargfqv macro - need to escape any quotes in it
1378 disp.setmacro("cgiargfqv", "query", escape_quotes(args["fqv"]));
1379
1380 // also reset the _cgiargq_ macro as it has changed now
1381 disp.setmacro("cgiargq", displayclass::defaultpackage, html_safe(args["q"]));
1382
1383 // reset the compressed options to include the q arg
1384 text_t compressedoptions = recpt->get_compressed_arg(args, logout);
1385 if (!compressedoptions.empty()) {
1386 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1387 // need a decoded version of compressedoptions for use within forms
1388 // as browsers encode values from forms before sending to server
1389 // (e.g. %25 becomes %2525)
1390 decode_cgi_arg (compressedoptions);
1391 if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
1392 // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
1393 compressedoptions = to_uni(compressedoptions);
1394 }
1395
1396 text_t macrovalue = dm_safe(compressedoptions);
1397 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, macrovalue);
1398 disp.setmacro ("decodedcompressedoptionsAttrsafe", displayclass::defaultpackage, encodeForHTMLAttr(macrovalue));
1399
1400 }
1401 } // form search
1402 } // args["qt"]=1
1403 else {
1404 logout << "ERROR (queryaction::get_formatted_query_string): querytype not defined\n";
1405 }
1406}
1407
1408
1409// define_query_macros sets the macros that couldn't be set until the
1410// query had been done. Those macros are
1411// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
1412// _thisfirst_, and _thislast_ and _quotedquery_
1413// this has been simplified so it can be used with both search_single_coll
1414// and search_multiple_coll
1415void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
1416 int numdocs, isapprox isApprox)
1417{
1418 // The following 'if' statatment is placed here to be keep the semantics
1419 // the same as the version before basequeryaction was introduced
1420
1421 if (num_phrases > 0) isApprox = Exact;
1422
1423 basequeryaction::define_query_macros(args,disp,numdocs,isApprox);
1424
1425 if (args["ct"]==0) { // mg queries only, not mgpp
1426 // get the quoted bits of the query string and set _quotedquery_
1427 text_tarray phrases;
1428 get_phrases (args["q"], phrases);
1429 num_phrases = phrases.size();
1430 text_tarray::const_iterator phere = phrases.begin();
1431 text_tarray::const_iterator pend = phrases.end();
1432 bool first = true;
1433 text_t quotedquery;
1434 while (phere != pend) {
1435 if (!first)
1436 if ((phere +1) == pend) quotedquery += " and ";
1437 else quotedquery += ", ";
1438
1439 quotedquery += "\"" + *phere + "\"";
1440 first = false;
1441 ++phere;
1442 }
1443 if (args.getintarg("s") && !quotedquery.empty()) quotedquery += "_textstemon_";
1444 disp.setmacro ("quotedquery", "query", quotedquery);
1445 }
1446
1447}
1448
1449// should this change for cross coll search??
1450bool queryaction::save_search_history (cgiargsclass &args, int numdocs,
1451 isapprox isApprox) {
1452 if (args["q"]=="") return true; // null query, dont save
1453 if (args["hs"]=="0") return true; // only save when submit query pressed
1454
1455 // get userid
1456 text_t userid = args["z"];
1457
1458 // the number of docs goes on the front of the query string
1459 text_t query = text_t(numdocs);
1460 if (isApprox==MoreThan) { // there were more docs found
1461 query.push_back('+');
1462 }
1463 query += "c="+args["c"];
1464 query += ";h="+args["h"];
1465 query += ";t="+args["t"];
1466 query += ";b="+args["b"];
1467 query += ";j="+args["j"];
1468 query += ";n="+args["n"];
1469 query += ";s="+args["s"];
1470 query += ";k="+args["k"];
1471 query += ";g="+args["g"];
1472
1473 text_t qstring = args["q"];
1474 //text_t formattedquery =cgi_safe(qstring);
1475 //query += "&amp;q="+formattedquery;
1476 query += ";q="+qstring;
1477 bool display=false;
1478 int hd = args.getintarg("hd");
1479 if (hd > 0) display=true;
1480 if (set_history_info(userid, query, dbhome, display)) return true;
1481 else return false;
1482}
1483
Note: See TracBrowser for help on using the repository browser.