source: main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp@ 27362

Last change on this file since 27362 was 27362, checked in by kjdon, 11 years ago

slightly changed the handling of sortfield query option

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