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

Last change on this file since 29087 was 29087, checked in by kjdon, 10 years ago

sortfield option. will now display even when there is only one option. this allows user to choose ascending/descending for that one sortfield. add code to use _textsortbynone_ macro for 'none' option.

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