Updated Rico 2 and Rico 3 with all patches submitted on Sourceforge.
[infodrom/rico3] / plugins / php / ricoResponse.php
1 <?php\r
2 \r
3 // for PHP4\r
4 // copied from http://www.php.net/json_encode\r
5 if (!function_exists('json_encode'))\r
6 {\r
7   function json_encode($a=false)\r
8   {\r
9     if (is_null($a)) return 'null';\r
10     if ($a === false) return 'false';\r
11     if ($a === true) return 'true';\r
12     if (is_scalar($a))\r
13     {\r
14       if (is_float($a))\r
15       {\r
16         // Always use "." for floats.\r
17         return floatval(str_replace(",", ".", strval($a)));\r
18       }\r
19 \r
20       if (is_string($a))\r
21       {\r
22         static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));\r
23         return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"';\r
24       }\r
25       else\r
26         return $a;\r
27     }\r
28     $isList = true;\r
29     for ($i = 0, reset($a); $i < count($a); $i++, next($a))\r
30     {\r
31       if (key($a) !== $i)\r
32       {\r
33         $isList = false;\r
34         break;\r
35       }\r
36     }\r
37     $result = array();\r
38     if ($isList)\r
39     {\r
40       foreach ($a as $v) $result[] = json_encode($v);\r
41       return '[' . join(',', $result) . ']';\r
42     }\r
43     else\r
44     {\r
45       foreach ($a as $k => $v) $result[] = json_encode($k).':'.json_encode($v);\r
46       return '{' . join(',', $result) . '}';\r
47     }\r
48   }\r
49 }\r
50 \r
51 class ricoXmlResponse {\r
52 \r
53   // public properties\r
54   var $orderByRef;\r
55   var $sendDebugMsgs;\r
56   var $readAllRows;    // always return the total number of rows? (if true, the user will always see the total number of rows, but there is a small performance hit)\r
57   var $convertCharSet; // set to true if database is ISO-8859-1 encoded, false if UTF-8\r
58   var $AllRowsMax;     // max # of rows to send if numrows=-1\r
59   var $fmt;            // xml, json, html, xl\r
60  \r
61   // private properties\r
62   var $objDB;\r
63   var $eof;\r
64   var $oParse;\r
65   var $sqltext;\r
66   var $arParams;\r
67   var $allParams;\r
68   var $condType;\r
69   var $RowsStart;\r
70   var $RowsEnd;\r
71   var $SendHdg;\r
72   var $Headings;\r
73   var $HiddenCols;\r
74 \r
75   function ricoXmlResponse() {\r
76     if (isset($GLOBALS['oDB']) && is_object($GLOBALS['oDB'])) {\r
77       $this->objDB=$GLOBALS['oDB'];   // use oDB global as database connection, if it exists\r
78     }\r
79     $this->orderByRef=false;\r
80     $this->sendDebugMsgs=false;\r
81     $this->readAllRows=true;    // has no effect on SQL Server 2005, Oracle, and MySQL because they use Query2xmlRaw_Limit()\r
82     $this->convertCharSet=false;\r
83     $this->SendHdg=false;\r
84     $this->AllRowsMax=1999;\r
85     $this->Headings=array();\r
86     $this->HiddenCols=array();\r
87   }\r
88 \r
89   function ProcessQuery($id, $sqlselect, $filters=array(), $errmsg='') {\r
90     $this->fmt=isset($_GET["_fmt"]) ? $_GET["_fmt"] : "xml";\r
91     $offset=isset($_GET["offset"]) ? $_GET["offset"] : "0";\r
92     $size=isset($_GET["page_size"]) ? $_GET["page_size"] : "";\r
93     $total=isset($_GET["get_total"]) ? strtolower($_GET["get_total"]) : "false";\r
94     $distinct=isset($_GET["distinct"]) ? $_GET["distinct"] : "";\r
95     $edit=isset($_GET["edit"]) ? $_GET["edit"] : "";\r
96     if (isset($_GET["hidden"]) && $_GET["hidden"]!="") $this->HiddenCols=explode(",", $_GET["hidden"]);\r
97 \r
98     ob_clean();\r
99     if ($this->fmt != "xl") {\r
100       header("Cache-Control: no-cache");\r
101       header("Pragma: no-cache");\r
102       header("Expires: ".gmdate("D, d M Y H:i:s",time()+(-1*60))." GMT");\r
103     }\r
104 \r
105     switch ($this->fmt) {\r
106       case "html":\r
107         header("Content-type: text/html");\r
108         echo "<html><head></head><body>\n";\r
109         $closetags="</body></html>";\r
110         $this->RowsStart="\n<table border='1'>";\r
111         $this->RowsEnd="\n</table>";\r
112         $total="false";\r
113         $this->sendDebugMsgs=false;\r
114         $this->SendHdg=true;\r
115         break;\r
116       case "xl":\r
117         $this->convertCharSet=false;\r
118         header("Content-type: application/vnd.ms-excel");\r
119         echo "<html><head></head><body>\n";\r
120         $closetags="</body></html>";\r
121         $this->RowsStart="\n<table>";\r
122         $this->RowsEnd="\n</table>";\r
123         $total="false";\r
124         $this->sendDebugMsgs=false;\r
125         $this->SendHdg=true;\r
126         break;\r
127       case "json":\r
128         header("Content-type: application/json");\r
129         echo "{\n\"id\":\"" . $id . "\"";\r
130         $this->RowsStart=",\n\"update_ui\":true,\n\"offset\":" . $offset . ",\n\"rows\":[";\r
131         $this->RowsEnd="\n]";\r
132         $closetags="}";\r
133         $this->sendDebugMsgs=false;\r
134         break;\r
135       default:\r
136         // default to xml\r
137         $this->fmt="xml";\r
138         header("Content-type: text/xml");\r
139         echo "<?xml version='1.0' encoding='UTF-8'?".">\n";\r
140         echo "\n<ajax-response><response type='object' id='" . $id . "'>";\r
141         $closetags="</response></ajax-response>";\r
142         $this->RowsStart="\n<rows update_ui='true' offset='" . $offset . "'>";\r
143         $this->RowsEnd="\n</rows>";\r
144         break;\r
145     }\r
146     if (!empty($errmsg)) {\r
147       $this->ErrorResponse($errmsg);\r
148     } elseif (empty($id)) {\r
149       $this->ErrorResponse("No ID provided!");\r
150     } elseif ($distinct=="" && !is_numeric($offset)) {\r
151       $this->ErrorResponse("Invalid offset!");\r
152     } elseif ($distinct=="" && !is_numeric($size)) {\r
153       $this->ErrorResponse("Invalid size!");\r
154     } elseif ($distinct!="" && !is_numeric($distinct)) {\r
155       $this->ErrorResponse("Invalid distinct parameter!");\r
156     } else {\r
157       if ($this->SendHdg && is_array($sqlselect)) {\r
158         // populate $Headings from $sqlselect[9] taking into account hidden columns\r
159         for ($i=0,$j=0,$SkipIdx=0; $i<count($sqlselect[9]); $i++) {\r
160           $skip=false;\r
161           if ($SkipIdx < count($this->HiddenCols)) {\r
162             $skip=($this->HiddenCols[$SkipIdx] == $i);\r
163             if ($skip) $SkipIdx++;\r
164           }\r
165           if (!$skip) {\r
166             $this->Headings[$j++]=$sqlselect[9][$i];\r
167           }\r
168         }\r
169       }\r
170       $this->objDB->DisplayErrors=false;\r
171       $this->objDB->ErrMsgFmt="MULTILINE";\r
172       if ($distinct!="" && is_numeric($distinct)) {\r
173         $this->Query2xmlDistinct($sqlselect, intval($distinct), -1, $filters);\r
174       } elseif ($edit!="" && is_numeric($edit) && is_array($sqlselect)) {\r
175         $this->Query2xml($sqlselect[8][intval($edit)], intval($offset), intval($size), $total!="false", $filters);\r
176       } else {\r
177         $this->Query2xml($sqlselect, intval($offset), intval($size), $total!="false", $filters);\r
178       }\r
179       if (!empty($this->objDB->LastErrorMsg)) {\r
180         $this->ErrorResponse($this->objDB->LastErrorMsg);\r
181       }\r
182     }\r
183     echo "\n".$closetags;\r
184   }\r
185 \r
186   function ErrorResponse($msg) {\r
187     $this->AppendResponse("error",$msg);\r
188   }\r
189 \r
190   function AppendResponse($tag, $content) {\r
191     switch ($this->fmt) {\r
192       case "html":\r
193         echo "\n<p>".$tag."<br>".htmlspecialchars($content)."</p>";\r
194         break;\r
195       case "xl":\r
196         echo "\n<p>".$tag."<br>".htmlspecialchars($content)."</p>";\r
197         break;\r
198       case "json":\r
199         echo ",\n\"".$tag."\":".json_encode($content);\r
200         break;\r
201       case "xml":\r
202         echo "\n<".$tag.">".htmlspecialchars($content)."</".$tag.">";\r
203         break;\r
204     }\r
205   }\r
206 \r
207   // All Oracle and SQL Server 2005 queries *must* have an ORDER BY clause\r
208   // "as" clauses are now ok\r
209   // If numrows < 0, then retrieve all rows\r
210   function Query2xml($sqlselect, $offset, $numrows, $gettotal, $filters=array()) {\r
211     if ($numrows >= 0) {\r
212       $Dialect=$this->objDB->Dialect;\r
213     } else {\r
214       $numrows=$this->AllRowsMax;\r
215       $Dialect="";  // don't use limit query\r
216     }\r
217     switch ($this->objDB->Dialect) {\r
218       case "MySQL": $this->orderByRef=true; break;\r
219     }\r
220     $this->arParams=array('H'=>array(), 'W'=>array());\r
221     $this->oParse= new sqlParse();\r
222     if (is_array($sqlselect)) {\r
223       $this->oParse->LoadArray($sqlselect);\r
224     } else {\r
225       $this->oParse->ParseSelect($sqlselect);\r
226     }\r
227     $this->ApplyQStringParms($filters);\r
228     $this->allParams=array_merge($this->arParams['W'],$this->arParams['H']);\r
229     echo $this->RowsStart;\r
230     switch ($Dialect) {\r
231 \r
232       case "TSQL":\r
233         $this->objDB->SingleRecordQuery("select @@VERSION", $version);\r
234         if (is_string($sqlselect) && strtoupper(substr($sqlselect,0,7))!="SELECT ") {\r
235           $this->allParams=array();\r
236           $totcnt=$this->Query2xmlRaw($sqlselect, $offset, $numrows);\r
237         }\r
238         else if (preg_match("/SQL Server 200(5|8)/i",$version[0])) {\r
239           $this->sqltext=$this->UnparseWithRowNumber($offset, $numrows + 1, true);\r
240           $totcnt=$this->Query2xmlRaw_Limit($this->sqltext, $offset, $numrows, 1);\r
241         }\r
242         else {\r
243           $this->sqltext=$this->oParse->UnparseSelectSkip($this->HiddenCols);\r
244           $totcnt=$this->Query2xmlRaw($this->sqltext, $offset, $numrows);\r
245         }\r
246         break;\r
247 \r
248       case "Oracle":\r
249         $this->sqltext=$this->UnparseWithRowNumber($offset, $numrows + 1, false);\r
250         $totcnt=$this->Query2xmlRaw_Limit($this->sqltext, $offset, $numrows, 1);\r
251         break;\r
252 \r
253       case "MySQL":\r
254         $this->sqltext=$this->oParse->UnparseSelectSkip($this->HiddenCols)." LIMIT ".$offset.",".($numrows + 1);\r
255         $totcnt=$this->Query2xmlRaw_Limit($this->sqltext, $offset, $numrows, 0);\r
256         break;\r
257 \r
258       case "PostgreSQL":\r
259         $this->sqltext=$this->oParse->UnparseSelect()." LIMIT ".($numrows + 1) . " OFFSET ".$offset;\r
260         $totcnt=$this->Query2xmlRaw_Limit($this->sqltext, $offset, $numrows, 0);\r
261         break;\r
262 \r
263       default:\r
264         $this->sqltext=$this->oParse->UnparseSelectSkip($this->HiddenCols);\r
265         $totcnt=$this->Query2xmlRaw($this->sqltext, $offset, $numrows);\r
266         break;\r
267     }\r
268     echo $this->RowsEnd;\r
269     if ($this->sendDebugMsgs) {\r
270       $this->AppendResponse("debug",$this->objDB->db->lastQuery);\r
271     }\r
272     if (!$this->eof && $gettotal) {\r
273       $totcnt=$this->getTotalRowCount();\r
274     }\r
275     if ($this->fmt=="xml" || $this->fmt=="json") {\r
276       if ($this->eof) $this->AppendResponse("rowcount",$totcnt);\r
277     }\r
278     $this->oParse=NULL;\r
279     return $totcnt;\r
280   }\r
281 \r
282 \r
283   function Query2xmlDistinct($sqlselect, $colnum, $numrows, $filters=array()) {\r
284     if ($numrows < 0) $numrows=$this->AllRowsMax;\r
285     $this->arParams=array('H'=>array(), 'W'=>array());\r
286     $this->oParse= new sqlParse();\r
287     if (is_array($sqlselect)) {\r
288       $this->oParse->LoadArray($sqlselect);\r
289     } else {\r
290       $this->oParse->ParseSelect($sqlselect);\r
291     }\r
292     $this->ApplyQStringParms($filters);\r
293     $this->allParams=array_merge($this->arParams['W'],$this->arParams['H']);\r
294     echo $this->RowsStart;\r
295     $this->sqltext=$this->oParse->UnparseDistinctColumn($colnum);\r
296     $totcnt=$this->Query2xmlRaw($this->sqltext, 0, $numrows);\r
297     echo $this->RowsEnd;\r
298     if ($this->sendDebugMsgs) {\r
299       $this->AppendResponse("debug",$this->objDB->db->lastQuery);\r
300     }\r
301     $this->oParse=NULL;\r
302   }\r
303 \r
304 \r
305   // Tested ok with SQL Server 2005, MySQL, and Oracle\r
306   function getTotalRowCount() {\r
307     $countSql="SELECT ".$this->oParse->UnparseColumnList()." FROM ".$this->oParse->FromClause;\r
308     if (!empty($this->oParse->WhereClause)) {\r
309       $countSql.=" WHERE ".$this->oParse->WhereClause;\r
310     }\r
311     if (is_array($this->oParse->arGroupBy)) {\r
312       if (count($this->oParse->arGroupBy) >  0) {\r
313         $countSql.=" GROUP BY ".implode(",",$this->oParse->arGroupBy);\r
314       }\r
315     }\r
316     if (!empty($this->oParse->HavingClause)) {\r
317       $countSql.=" HAVING ".$this->oParse->HavingClause;\r
318     }\r
319     $countSql="SELECT COUNT(*) FROM (".$countSql.")";\r
320     if ($this->objDB->Dialect != "Oracle") {\r
321       $countSql.=" AS rico_Main";\r
322     }\r
323     if (count($this->allParams)>0) {\r
324       $rsMain=$this->objDB->RunParamQuery($countSql,$this->allParams);\r
325     } else {\r
326       $rsMain=$this->objDB->RunQuery($countSql);\r
327     }\r
328     if (!$rsMain) {\r
329       echo "\n<debug>getTotalRowCount: rsMain is null</debug>";\r
330       return;\r
331     }\r
332     if (!$this->objDB->db->FetchArray($rsMain,$a)) return;\r
333     $this->objDB->rsClose($rsMain);\r
334     $this->eof=true;\r
335     return $a[0];\r
336   }\r
337 \r
338 \r
339   function UnparseWithRowNumber($offset, $numrows, $includeAS) {\r
340     if (is_array($this->oParse->arOrderBy)) {\r
341       if (count($this->oParse->arOrderBy) >  0) {\r
342         $strOrderBy=implode(",",$this->oParse->arOrderBy);\r
343       }\r
344     }\r
345     if (empty($strOrderBy) && !preg_match("/\bjoin\b/",$this->oParse->FromClause)) {\r
346       // order by clause should be included in main sql select statement\r
347       // However, if it isn't, then use primary key as sort - assuming FromClause is a simple table name\r
348       $strOrderBy=$this->objDB->PrimaryKey($this->oParse->FromClause);\r
349     }\r
350     $unparseText="SELECT ROW_NUMBER() OVER (ORDER BY ".$strOrderBy.") AS rico_rownum,";\r
351     $unparseText.=$this->oParse->UnparseColumnListSkip($this->HiddenCols)." FROM ".$this->oParse->FromClause;\r
352     if (!empty($this->oParse->WhereClause)) {\r
353       $unparseText.=" WHERE ".$this->oParse->WhereClause;\r
354     }\r
355     if (is_array($this->oParse->arGroupBy)) {\r
356       if (count($this->oParse->arGroupBy) >  0) {\r
357         $unparseText.=" GROUP BY ".implode(",",$this->oParse->arGroupBy);\r
358       }\r
359     }\r
360     if (!empty($this->oParse->HavingClause)) {\r
361       $unparseText.=" HAVING ".$this->oParse->HavingClause;\r
362     }\r
363     $unparseText="SELECT * FROM (".$unparseText.")";\r
364     if ($includeAS) {\r
365       $unparseText.=" AS rico_Main";\r
366     }\r
367     $unparseText.=" WHERE rico_rownum > ".$offset." AND rico_rownum <= ".($offset + $numrows);\r
368     return $unparseText;\r
369   }\r
370 \r
371   function Query2xmlRaw($rawsqltext, $offset, $numrows) {\r
372     if (count($this->allParams)>0) {\r
373       $rsMain=$this->objDB->RunParamQuery($rawsqltext,$this->allParams);\r
374     } else {\r
375       $rsMain=$this->objDB->RunQuery($rawsqltext);\r
376     }\r
377     if (!$rsMain) return;\r
378   \r
379     $colcnt = $this->objDB->db->NumFields($rsMain);\r
380     $totcnt = $this->objDB->db->NumRows($rsMain);\r
381     //echo "<debug>Query2xmlRaw: NumRows=$totcnt</debug>";\r
382     if ($offset < $totcnt || $totcnt==-1) {\r
383       $this->objDB->db->Seek($rsMain,$offset);\r
384       switch ($this->fmt) {\r
385         case "json": $rowcnt=$this->WriteRowsJSON($rsMain, $numrows, 0); break;\r
386         default:     $rowcnt=$this->WriteRowsXHTML($rsMain, $numrows, 0); break;\r
387       }\r
388       if ($totcnt < 0) {\r
389         $totcnt=$offset+$rowcnt;\r
390         while($this->objDB->db->FetchRow($rsMain,$row))\r
391           $totcnt++;\r
392       }\r
393     } else {\r
394       $totcnt=$offset;\r
395     }\r
396     $this->objDB->rsClose($rsMain);\r
397     $this->eof=true;\r
398     return $totcnt;\r
399   }\r
400 \r
401   function Query2xmlRaw_Limit($rawsqltext, $offset, $numrows, $firstcol) {\r
402     if (count($this->allParams)>0) {\r
403       $rsMain=$this->objDB->RunParamQuery($rawsqltext,$this->allParams);\r
404     } else {\r
405       $rsMain=$this->objDB->RunQuery($rawsqltext);\r
406     }\r
407     //if ($this->objDB->db->HasError()) echo "<error>" . $this->objDB->db->ErrorMsg() . "</error>";\r
408     $totcnt=$offset;\r
409     $this->eof=true;\r
410     if (!$rsMain) return;\r
411     switch ($this->fmt) {\r
412       case "json": $totcnt+=$this->WriteRowsJSON($rsMain, $numrows, $firstcol); break;\r
413       default:     $totcnt+=$this->WriteRowsXHTML($rsMain, $numrows, $firstcol); break;\r
414     }\r
415     $this->objDB->rsClose($rsMain);\r
416     return $totcnt;\r
417   }\r
418 \r
419   function WriteRowsXHTML($rsMain, $numrows, $firstcol) {\r
420     $colcnt = $this->objDB->db->NumFields($rsMain);\r
421     $rowcnt=0;\r
422     if ($this->SendHdg) {\r
423       echo "\n<tr>";\r
424       for ($i=$firstcol; $i < $colcnt; $i++) {\r
425         $n=empty($this->Headings[$i-$firstcol]) ? $this->objDB->db->FieldName($rsMain,$i) : $this->Headings[$i-$firstcol];\r
426         print $this->XmlStringCell($n);\r
427       }\r
428       echo "</tr>";\r
429     }\r
430     while(($this->objDB->db->FetchRow($rsMain,$row)) && $rowcnt < $numrows) {\r
431       $rowcnt++;\r
432       print "\n<tr>";\r
433       for ($i=$firstcol; $i < $colcnt; $i++)\r
434         print $this->XmlStringCell($row[$i]);\r
435       print "</tr>";\r
436     }\r
437     $this->eof=($rowcnt < $numrows);\r
438     return $rowcnt;\r
439   }\r
440   \r
441   function WriteRowsJSON($rsMain, $numrows, $firstcol) {\r
442     $colcnt = $this->objDB->db->NumFields($rsMain);\r
443     $rowcnt=0;\r
444     if ($this->SendHdg) {\r
445       echo "\n[";\r
446       for ($i=$firstcol; $i < $colcnt; $i++) {\r
447         //$n=empty($this->Headings($i-$firstcol)) ? $this->objDB->db->FieldName($rsMain,$i) : $this->Headings($i-$firstcol);\r
448         print json_encode($n);\r
449       }\r
450       echo "]";\r
451     }\r
452     while(($this->objDB->db->FetchRow($rsMain,$row)) && $rowcnt < $numrows) {\r
453       if ($rowcnt>0 || $this->SendHdg) echo ",";\r
454       $rowcnt++;\r
455       print "\n[";\r
456       for ($i=$firstcol; $i < $colcnt; $i++) {\r
457         if ($i>$firstcol) echo ",";\r
458         print json_encode($this->convertCharSet ? utf8_encode($row[$i]) : $row[$i]);\r
459       }\r
460       print "]";\r
461     }\r
462     $this->eof=($rowcnt < $numrows);\r
463     return $rowcnt;\r
464   }\r
465   \r
466   function SetDbConn(&$dbcls) {\r
467     $this->objDB=&$dbcls;\r
468   }\r
469 \r
470   function PushParam($newvalue) {\r
471     $parm=$this->convertCharSet ? utf8_decode($newvalue) : $newvalue;\r
472     if (get_magic_quotes_gpc()) $parm=stripslashes($parm);\r
473     array_push($this->arParams[$this->condType], $parm);\r
474     if ($this->sendDebugMsgs) {\r
475       echo "\n<debug>".$this->condType." param=".htmlspecialchars($parm)."</debug>";\r
476     }\r
477   }\r
478   \r
479   function setCondType($selectItem) {\r
480     $this->condType=(preg_match("/\bmin\(|\bmax\(|\bsum\(|\bcount\(/i",$selectItem) && !preg_match("/\bselect\b/i",$selectItem)) ? 'H' : 'W';\r
481   }\r
482   \r
483   function addCondition($newfilter) {\r
484     switch ($this->condType) {\r
485       case 'H': $this->oParse->AddHavingCondition($newfilter); break;\r
486       case 'W': $this->oParse->AddWhereCondition($newfilter); break;\r
487     }\r
488   }\r
489 \r
490   function ApplyQStringParms($filters) {\r
491     foreach($_GET as $qs => $value) {\r
492       $prefix=substr($qs,0,1);\r
493       switch ($prefix) {\r
494 \r
495         // user-invoked condition\r
496         case "w":\r
497         case "h":\r
498           $i=substr($qs,1);\r
499           if (!is_numeric($i)) break;\r
500           $i=intval($i);\r
501           if ($i<0 || $i>=count($filters)) break;\r
502           $newfilter=$filters[$i];\r
503           $this->condType=strtoupper($prefix);\r
504 \r
505           $j=strpos($newfilter," in (?)");\r
506           if ($j !== false) {\r
507             $a=explode(",", $value);\r
508             for ($i=0; $i < count($a); $i++) {\r
509               $this->PushParam($a[$i]);\r
510               $a[$i]="?";\r
511             }\r
512             $newfilter=substr($newfilter,0,$j+4) . implode(",",$a) . substr($newfilter,$j+5);\r
513           } elseif (strpos($newfilter,"?") !== false) {\r
514             $this->PushParam($value);\r
515           }\r
516 \r
517           $this->addCondition($newfilter);\r
518           break;\r
519 \r
520         // sort\r
521         case "s":\r
522           $i=substr($qs,1);\r
523           if (!is_numeric($i)) break;\r
524           $i=intval($i);\r
525           if ($i<0 || $i>=count($this->oParse->arSelList)) break;\r
526           $value=strtoupper(substr($value,0,4));\r
527           if (!in_array($value,array('ASC','DESC'))) $value="ASC";\r
528           if ($this->orderByRef)\r
529             $this->oParse->AddSort(($i + 1)." ".$value);\r
530           else\r
531             $this->oParse->AddSort($this->oParse->arSelList[$i]." ".$value);\r
532           break;\r
533 \r
534         // user-supplied filter\r
535         case "f":\r
536           //print_r($value);\r
537           foreach($value as $i => $filter) {\r
538             if ($i<0 || $i>=count($this->oParse->arSelList)) break;\r
539             $newfilter=$this->oParse->arSelList[$i];\r
540             $this->setCondType($newfilter);\r
541             switch ($filter['op']) {\r
542               case "EQ":\r
543                 $newfilter='('.$this->AddCoalesce($newfilter).' IN '.$this->GetMultiParmFilter($filter).')';\r
544                 break;\r
545               case "LE":\r
546                 $newfilter.="<=?";\r
547                 $this->PushParam($filter[0]);\r
548                 break;\r
549               case "GE":\r
550                 $newfilter.=">=?";\r
551                 $this->PushParam($filter[0]);\r
552                 break;\r
553               case "NULL": $newfilter.=" is null"; break;\r
554               case "NOTNULL": $newfilter.=" is not null"; break;\r
555               case "LIKE":\r
556                 $newfilter.=" LIKE ?";\r
557                 $this->PushParam(str_replace("*",$this->objDB->Wildcard,$filter[0]));\r
558                 break;\r
559               case "NE":\r
560                 $newfilter='('.$this->AddCoalesce($newfilter).' NOT IN '.$this->GetMultiParmFilter($filter).')';\r
561                 break;\r
562             }\r
563             $this->addCondition($newfilter);\r
564           }\r
565           break;\r
566       }\r
567     }\r
568   }\r
569 \r
570   function AddCoalesce($newfilter) {\r
571     if ($this->objDB->Dialect=="Access") {\r
572       return "iif(IsNull(" . $newfilter . "),''," . $newfilter . ")";\r
573     } else {\r
574       return "coalesce(" . $newfilter . ",'')";\r
575     }\r
576   }\r
577 \r
578 \r
579   function GetMultiParmFilter($filter) {\r
580     $flen=$filter['len'];\r
581     if (!is_numeric($flen)) return "";\r
582     $flen=intval($flen);\r
583     $newfilter='(';\r
584     for ($j=0; $j<$flen; $j++) {\r
585       if ($j > 0) $newfilter.=",";\r
586       $newfilter.='?';\r
587       $this->PushParam($filter[$j]);\r
588     }\r
589     $newfilter.=')';\r
590     return $newfilter;\r
591   }\r
592 \r
593   function XmlStringCell($value) {\r
594     if (!isset($value)) {\r
595       $result="";\r
596     }\r
597     else {\r
598       if ($this->convertCharSet) {\r
599         $value=utf8_encode($value);\r
600         $result=htmlspecialchars($value, ENT_COMPAT, 'UTF-8');\r
601       } else {\r
602         $result=htmlspecialchars($value);\r
603       }\r
604     }\r
605     if ($this->fmt=="html" && $result=="") $result="&nbsp;";\r
606     return "<td>".$result."</td>";\r
607   }\r
608 \r
609   // for the root node, parentID should "" (empty string)\r
610   // containerORleaf: L/zero (leaf), C/non-zero (container)\r
611   // selectable:      0->not selectable, 1->selectable\r
612   function WriteTreeRow($parentID, $ID, $description, $containerORleaf, $selectable) {\r
613     echo "\n<tr>";\r
614     echo $this->XmlStringCell($parentID);\r
615     echo $this->XmlStringCell($ID);\r
616     echo $this->XmlStringCell($description);\r
617     echo $this->XmlStringCell($containerORleaf);\r
618     echo $this->XmlStringCell($selectable);\r
619     echo "</tr>";\r
620   }\r
621 \r
622 }\r
623 \r
624 ?>\r
625 \r