source: main/trunk/greenstone2/runtime-src/src/recpt/sqlqueryaction.cpp@ 22044

Last change on this file since 22044 was 22044, checked in by davidb, 14 years ago

Introduction of sql-query action. This has been achived be creating a base call for query-action (sharing common functionality for fulltext query action and sql query action).

File size: 11.8 KB
RevLine 
[22044]1/**********************************************************************
2 *
3 * sqlqueryaction.cpp --
4 * Copyright (C) 2010 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 "sqlqueryaction.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 <stdlib.h> // for strtol
37#include <assert.h>
38
39
40sqlqueryaction::sqlqueryaction ()
41 : basequeryaction()
42{
43 cgiarginfo arg_ainfo;
44
45 // this action uses cgi variable "a"
46 arg_ainfo.shortname = "a";
47 arg_ainfo.longname = "action";
48 arg_ainfo.multiplechar = true;
49 arg_ainfo.defaultstatus = cgiarginfo::weak;
50 arg_ainfo.argdefault = "sqlq";
51 arg_ainfo.savedarginfo = cgiarginfo::must;
52 argsinfo.addarginfo (NULL, arg_ainfo);
53
54 // "sqlqto" - 0 = not available, 1 = available
55 arg_ainfo.shortname = "sqlqto";
56 arg_ainfo.longname = "sqlquery type options";
57 arg_ainfo.multiplechar = true; // can be empty or single char
58 arg_ainfo.defaultstatus = cgiarginfo::weak;
59 arg_ainfo.argdefault = g_EmptyText;
60 arg_ainfo.savedarginfo = cgiarginfo::must;
61 argsinfo.addarginfo (NULL, arg_ainfo);
62
63 // "sqlfqn" - number of fields in the query form
64 arg_ainfo.shortname = "sqlfqn";
65 arg_ainfo.longname = "sql form query num fields";
66 arg_ainfo.multiplechar = true;
67 arg_ainfo.defaultstatus = cgiarginfo::weak;
68 arg_ainfo.argdefault = "4";
69 arg_ainfo.savedarginfo = cgiarginfo::must;
70 argsinfo.addarginfo (NULL, arg_ainfo);
71
72 // "sqlfqf" - the list of field names in the form query
73 // - a comma separated list
74 arg_ainfo.shortname = "sqlfqf";
75 arg_ainfo.longname = "sql form query fields";
76 arg_ainfo.multiplechar = true;
77 arg_ainfo.defaultstatus = cgiarginfo::weak;
78 arg_ainfo.argdefault = g_EmptyText;
79 arg_ainfo.savedarginfo = cgiarginfo::must;
80 argsinfo.addarginfo (NULL, arg_ainfo);
81
82 // "sqlfqv" - the list of values in the form query
83 // - a comma separated list
84 arg_ainfo.shortname = "sqlfqv";
85 arg_ainfo.longname = "sql form query values";
86 arg_ainfo.multiplechar = true;
87 arg_ainfo.defaultstatus = cgiarginfo::weak;
88 arg_ainfo.argdefault = g_EmptyText;
89 arg_ainfo.savedarginfo = cgiarginfo::must;
90 argsinfo.addarginfo (NULL, arg_ainfo);
91
92 // "sqlfqc" - the list of boolean operators in the form query
93 // - a comma separated list
94 arg_ainfo.shortname = "sqlfqc";
95 arg_ainfo.longname = "sql form query combines";
96 arg_ainfo.multiplechar = true;
97 arg_ainfo.defaultstatus = cgiarginfo::weak;
98 arg_ainfo.argdefault = g_EmptyText;
99 arg_ainfo.savedarginfo = cgiarginfo::must;
100 argsinfo.addarginfo (NULL, arg_ainfo);
101
102 // "sqlsf" - Sort field. Set to field to be used for sorting search reult
103 // set
104 arg_ainfo.shortname = "sqlsf";
105 arg_ainfo.longname = "sql sort field";
106 arg_ainfo.multiplechar = true;
107 arg_ainfo.defaultstatus = cgiarginfo::weak;
108 arg_ainfo.argdefault = g_EmptyText;
109 arg_ainfo.savedarginfo = cgiarginfo::must;
110 argsinfo.addarginfo (NULL, arg_ainfo);
111
112}
113
114sqlqueryaction::~sqlqueryaction ()
115{
116}
117
118void sqlqueryaction::configure (const text_t &key, const text_tarray &cfgline) {
119 action::configure (key, cfgline);
120}
121
122bool sqlqueryaction::init (ostream &logout)
123{
124 return basequeryaction::init (logout);
125}
126
127
128bool sqlqueryaction::check_cgiargs (cgiargsinfoclass &argsinfo,
129 cgiargsclass &args,
130 recptprotolistclass* protos,
131 ostream &logout)
132{
133
134 // check sqlfqn argument
135 int arg_sqlfqn = args.getintarg("sqlfqn");
136 if (arg_sqlfqn < -1) {
137 logout << "Warning: \"sqlfqn\" argument less than -1 (" << arg_sqlfqn << ")\n";
138 cgiarginfo *sqlfqninfo = argsinfo.getarginfo ("sqlfqn");
139 if (sqlfqninfo != NULL) args["sqlfqn"] = sqlfqninfo->argdefault;
140 }
141
142 return basequeryaction::check_cgiargs(argsinfo,args,protos,logout);
143}
144
145
146
147void sqlqueryaction::define_external_macros (displayclass &disp,
148 cgiargsclass &args,
149 recptprotolistclass *protos,
150 ostream &logout)
151{
152 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
153 if (collectproto == NULL) return;
154
155 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(collectproto,
156 args["c"],
157 logout);
158 comerror_t err;
159 InfoFilterOptionsResponse_t response;
160 InfoFilterOptionsRequest_t request;
161 request.filterName = "SQLQueryFilter";
162
163 collectproto->get_filteroptions (args["c"], request, response, err, logout);
164 if (err == noError) {
165 FilterOption_tmap::const_iterator it_dom;
166 FilterOption_tmap::const_iterator it_ran;
167 FilterOption_tmap::const_iterator end = response.filterOptions.end();
168
169 // _sqlfqfselection_ field list
170 it_dom = response.filterOptions.find("IndexFieldDomain");
171 it_ran = response.filterOptions.find("IndexFieldRange");
172 if ((it_dom!=end) && (it_ran!=end)) {
173
174 set_option_macro ("sqlfqf",args["sqlfqf"], true,true,
175 (*it_dom).second, (*it_ran).second, disp);
176
177 if (args["b"] == "1") {
178 // set the sort field macro
179 set_sfselection_macro(args["sqlsf"], (*it_dom).second,(*it_ran).second,
180 disp);
181 }
182 }
183 }
184
185
186}
187
188
189void sqlqueryaction::set_sfselection_macro(text_t current_value,
190 const FilterOption_t &option_domain,
191 const FilterOption_t &option_range,
192 displayclass &disp)
193{
194
195 // we need at least one option here to continue
196 if (option_domain.validValues.size() < 1) { return; }
197 if (option_range.validValues.size() < 1) { return; }
198
199 text_t macrovalue = "<select name=\"sqlsf\">\n";
200
201 if (current_value.empty()) current_value = "";
202
203 text_tarray::const_iterator dom_thisvalue = option_domain.validValues.begin();
204 text_tarray::const_iterator dom_endvalue = option_domain.validValues.end();
205
206 text_tarray::const_iterator ran_thisvalue = option_range.validValues.begin();
207 text_tarray::const_iterator ran_endvalue = option_range.validValues.end();
208
209 int valid_count = 0;
210 while ((dom_thisvalue != dom_endvalue) && (ran_thisvalue != ran_endvalue)) {
211
212 if (*ran_thisvalue != "ZZ" && *ran_thisvalue != "TX") {
213 ++valid_count;
214
215 text_t option_val = *dom_thisvalue;
216 option_val.replace(",","/");
217
218 macrovalue += "<option value=\"" + option_val + "\"";
219 if (current_value == *dom_thisvalue)
220 macrovalue += " selected";
221 macrovalue += ">_" + *ran_thisvalue + "_\n";
222 }
223 ++dom_thisvalue;
224 ++ran_thisvalue;
225 }
226 macrovalue += "</select>";
227 if (valid_count > 0) {
228 disp.setmacro ("sqlsfselection", displayclass::defaultpackage, macrovalue);
229 }
230}
231
232
233void sqlqueryaction::get_formatted_query_string (text_t& formattedstring,
234 bool segment,
235 cgiargsclass& args,
236 displayclass& disp,
237 ostream& logout)
238{
239 if (args["qt"]=="0" && args["sqlqto"] != "1") { // normal text search
240 formattedstring = "SELECT DISTINCT docOID FROM document_metadata WHERE " + args["q"];
241 }
242 else if (args["qt"]=="1" || args["sqlqto"]=="1"){ // form search
243
244 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
245 formattedstring = args["q"];
246 }
247 else { // form search
248 // *****
249 // Consider making this its own method in basequeryaction
250 // then make parse_.*reg_query_form virtual method in class
251
252 if (args["b"]=="0") { // regular form
253 parse_sqlreg_query_form(formattedstring, args, segment);
254 }
255 else { // advanced form
256 parse_sqladv_query_form(formattedstring, args, segment);
257 }
258 args["q"] = formattedstring;
259
260 // reset the cgiargfqv macro - need to escape any quotes in it
261 disp.setmacro("cgiargfqv", "query", escape_quotes(args["fqv"]));
262
263 // also reset the _cgiargq_ macro as it has changed now
264 disp.setmacro("cgiargq", displayclass::defaultpackage, html_safe(args["q"]));
265
266 // reset the compressed options to include the q arg
267 text_t compressedoptions = recpt->get_compressed_arg(args, logout);
268 if (!compressedoptions.empty()) {
269 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
270 // need a decoded version of compressedoptions for use within forms
271 // as browsers encode values from forms before sending to server
272 // (e.g. %25 becomes %2525)
273 decode_cgi_arg (compressedoptions);
274 if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
275 // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
276 compressedoptions = to_uni(compressedoptions);
277 }
278
279 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
280 }
281 } // form search
282 } // args["qt"]=1
283 else {
284 logout << "ERROR (sqlqueryaction::get_formatted_query_string): querytype not defined\n";
285 }
286
287
288
289
290
291}
292
293
294// request.filterResultOptions and request.fields (if required) should
295// be set from the calling code
296void sqlqueryaction::set_queryfilter_options (FilterRequest_t &request,
297 const text_t &querystring,
298 cgiargsclass &args)
299{
300 request.filterName = "SQLQueryFilter";
301
302 OptionValue_t option;
303
304 option.name = "SQLWhere";
305 option.value = querystring;
306 request.filterOptions.push_back (option);
307
308 set_sql_queryfilter_options(request, args);
309}
310
311
312
313// should this change for cross coll search??
314bool sqlqueryaction::save_search_history (cgiargsclass &args, int numdocs,
315 isapprox isApprox)
316{
317
318
319 if (args["q"]=="") return true; // null query, dont save
320 if (args["hs"]=="0") return true; // only save when submit query pressed
321
322 // get userid
323 text_t userid = args["z"];
324
325 // the number of docs goes on the front of the query string
326 text_t query = text_t(numdocs);
327 if (isApprox==MoreThan) { // there were more docs found
328 query.push_back('+');
329 }
330 query += "c="+args["c"];
331
332 text_t qstring = args["q"];
333 //text_t formattedquery =cgi_safe(qstring);
334 //query += "&amp;q="+formattedquery;
335 query += ";q="+qstring;
336 bool display=false;
337 int hd = args.getintarg("hd");
338 if (hd > 0) display=true;
339 if (set_history_info(userid, query, dbhome, display)) return true;
340 else return false;
341
342}
343
344void sqlqueryaction::define_form_macros (displayclass &disp,
345 cgiargsclass &args,
346 recptprotolistclass *protos,
347 ostream &logout)
348{
349 // defines the following macros
350 // _sqlregformlist_
351 // _sqladvformlist_
352
353 text_t form = "";
354 int argsqlfqn = args.getintarg("sqlfqn");
355
356 if (args["b"] == "1") { // advanced form
357 form += "_firstsqladvformelement_\n";
358 for (int i=1; i<argsqlfqn; ++i) {
359 form += "_sqladvformelement_\n";
360 }
361 disp.setmacro("sqladvformlist", "query", form);
362 }
363 else { // simple form
364 for (int i=0; i<argsqlfqn; ++i) {
365 form += "_sqlregformelement_\n";
366 }
367 disp.setmacro("sqlregformlist", "query", form);
368 }
369
370}
371
372
373bool sqlqueryaction::do_action (cgiargsclass &args,
374 recptprotolistclass *protos,
375 browsermapclass *browsers, displayclass &disp,
376 outconvertclass &outconvert, ostream &textout,
377 ostream &logout)
378{
379 return search_single_collection (args, args["c"], protos, browsers, disp,
380 outconvert, textout, logout);
381}
382
383
384
Note: See TracBrowser for help on using the repository browser.