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

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

For diego: when doing cross collection searching, now it takes into account authentication directives for the collections in the list. If a user has authenticated to get into the top collection, then his user groups are checked against the groups for all the collections. If he matches any, then they will be searched. But if he is not a member of the right group they will not be searched. If there was no authentication needed to get into top colleciton, then any collections with collection-level authentication will not be searched.

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