root/branches/multiuser/cls/search.php

Revision 1628, 11.4 kB (checked in by mdodoo, 4 years ago)

Trunk sync up until [1613]. To tell the truth, I do not like doing these syncs.

  • Property svn:eolstyle set to native
Line 
1<?php
2###############################################################################
3# Gregarius - A PHP based RSS aggregator.
4# Copyright (C) 2003 - 2006 Marco Bonetti
5#
6###############################################################################
7# This program is free software and open source software; you can redistribute
8# it and/or modify it under the terms of the GNU General Public License as
9# published by the Free Software Foundation; either version 2 of the License,
10# or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful, but WITHOUT
13# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15# more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  or visit
20# http://www.gnu.org/licenses/gpl.html
21#
22###############################################################################
23# E-mail:      mbonetti at gmail dot com
24# Web page:    http://gregarius.net/
25#
26###############################################################################
27
28define ('INFINE_RESULTS',-1);
29
30define ('QUERY_PRM','rss_query');
31define ('QUERY_MATCH_MODE', 'rss_query_match');
32define ('QUERY_CHANNEL', 'rss_query_channel');
33define ('QUERY_RESULTS','rss_query_res_per_page');
34define ('QUERY_CURRENT_PAGE','rss_query_current_page');
35define ('HIT_BEFORE',"<span class=\"searchhit\">");
36define ('HIT_AFTER',"</span>");
37
38define ('QUERY_ORDER_BY','rss_order');
39define ('QUERY_ORDER_BY_DATE','date');
40define ('QUERY_ORDER_BY_CHANNEL','channel');
41define ('QUERY_MATCH_OR','or');
42define ('QUERY_MATCH_AND','and');
43define ('QUERY_MATCH_EXACT','exact');
44
45// This is needed for some constants
46rss_require('cls/wrappers/toolkit.php');
47
48class SearchItemList extends ItemList {
49
50    var $searchTerms = array();
51    var $matchMode;
52    var $regMatch = "";
53
54    var $currentPage;
55    var $resultsPerPage = 0;
56    var $startItem;
57    var $endItem;
58    var $orderBy;
59    var $query = "";
60    var $logicSep;
61
62    function SearchItemList($query=null,$results=0) {
63        parent::ItemList();
64                if ($query) {
65                        $this -> query=$query;
66                } elseif(isset($_REQUEST[QUERY_PRM])) {
67                        $this->query = $_REQUEST[QUERY_PRM];
68                }else{
69                        $this -> query = null;
70                }
71               
72               
73                // Sanitize the query parameters:
74                // fixme: this probably breaks on queries with weird characters, depending
75                // on the locale.
76                // see: http://php.benscom.com/manual/en/reference.pcre.pattern.syntax.php
77                if ($this -> query) {
78                $this -> query = trim(preg_replace('#[^\w\s\x80-\xff]#','',$this -> query));
79                }
80               
81                $this->resultsPerPage = (int) $results;
82               
83        $this -> populate();
84
85        $this->humanReadableQuery = implode(" ".strtoupper($this->logicSep)." ", $this->searchTerms);
86    }
87
88    function filterItems() {
89        $cntr = 0;
90        foreach($this->feeds as $fkey => $feed) {
91            foreach ($this -> feeds[$fkey]->items as $ikey => $item) {
92                $descr_noTags = strip_tags($item -> description);
93                $title_noTags = strip_tags($item -> title);
94                $match = false;
95                reset($this->searchTerms);
96                $match = ($this->matchMode == QUERY_MATCH_AND || $this->matchMode == QUERY_MATCH_EXACT);
97                foreach ($this->searchTerms as $term) {
98                    if ($this->matchMode == QUERY_MATCH_AND || $this->matchMode == QUERY_MATCH_EXACT) {
99                        $match = ((stristr($descr_noTags, $term) || stristr($title_noTags, $term)) && $match);
100                    } else {
101                        $match = ($match || (stristr($descr_noTags, $term) || stristr($title_noTags, $term)));
102                    }
103                }
104
105                if (!$match) {
106                    $this->removeItem($fkey,$ikey,true);
107                } else {
108
109                    if ($cntr >= $this->startItem && $cntr <= $this->endItem) {
110                        $this->feeds[$fkey] -> items[$ikey] -> description =
111                            preg_replace("'(?!<.*?)(".$this->regMatch.")(?![^<>]*?>)'si",
112                                         HIT_BEFORE."\\1".HIT_AFTER, $item -> description);
113                    } else {
114                        $this->removeItem($fkey,$ikey,false);
115                    }
116
117                    $cntr++;
118                }
119            }
120        }
121    }
122
123    function populate() {
124       
125        if (!$this->query) {
126            return;
127        }
128
129        $this->matchMode = sanitize(
130                        (!array_key_exists(QUERY_MATCH_MODE, $_REQUEST) ? QUERY_MATCH_AND : $_REQUEST[QUERY_MATCH_MODE]), 
131                RSS_SANITIZER_CHARACTERS_EXT);
132               
133        $this->channelId = sanitize(
134                ((array_key_exists(QUERY_CHANNEL, $_REQUEST)) ? $_REQUEST[QUERY_CHANNEL] : ALL_CHANNELS_ID),
135                RSS_SANITIZER_NUMERIC);
136
137                if (!$this->resultsPerPage) {
138                $this->resultsPerPage = sanitize(
139                        ((array_key_exists(QUERY_RESULTS, $_REQUEST)) ? $_REQUEST[QUERY_RESULTS] : INFINE_RESULTS),
140                        RSS_SANITIZER_NUMERIC);
141        }
142
143        $this->currentPage = sanitize(
144                (array_key_exists(QUERY_CURRENT_PAGE, $_REQUEST) ? $_REQUEST[QUERY_CURRENT_PAGE] : 0),
145                RSS_SANITIZER_NUMERIC);
146
147        $this->startItem = $this->resultsPerPage * $this->currentPage;
148
149        $this->endItem = $this->startItem + $this->resultsPerPage -1;
150
151        if ($this->resultsPerPage == INFINE_RESULTS) {
152            $this->endItem = 99999999;
153        }
154
155        $this->orderBy = sanitize(
156                (array_key_exists(QUERY_ORDER_BY, $_REQUEST) ? $_REQUEST[QUERY_ORDER_BY] : QUERY_ORDER_BY_DATE),
157                RSS_SANITIZER_CHARACTERS_EXT);
158               
159        $qWhere = "";
160        $this->regMatch = "";
161        $term = "";
162
163        if ($this->matchMode == QUERY_MATCH_OR || $this->matchMode == QUERY_MATCH_AND) {
164
165            $this->logicSep = ($this->matchMode == QUERY_MATCH_OR ? "or" : "and");
166            $this->searchTerms = explode(" ", $this->query);
167            foreach ($this->searchTerms as $term) {
168                $term = trim($term);
169                if ($term != "") {
170                    $qWhere .= "(i.description like '%$term%' or "." i.title like '%$term%') ".$this->logicSep;
171                }
172                // this will be used later for the highliting regexp
173                if ($this->regMatch != "") {
174                    $this->regMatch .= "|";
175                }
176                $this->regMatch .= $term;
177            }
178
179            $qWhere .= ($this->matchMode == QUERY_MATCH_OR ? " 1=0 " : " 1=1 ");
180        } else {
181            $this->logicSep = "";
182            $this->searchTerms[0] = $this->query;
183            $term = $this->query;
184            $qWhere .= "(i.description like '%$term%' or "." i.title like '%$term%') ";
185            $this->regMatch = $this->query;
186        }
187
188        $qWhere= "(" . $qWhere. ") ";
189
190
191
192        if ($this->channelId != ALL_CHANNELS_ID) {
193            $qWhere .= " and c.id = " . $this->channelId  . " ";
194        }
195
196        if (hidePrivate()) {
197            $qWhere .= " and not(i.unread & ".RSS_MODE_PRIVATE_STATE.") ";
198        }
199        $qWhere .= " and not(i.unread & ".RSS_MODE_DELETED_STATE.") ";
200
201        if ($this->orderBy == QUERY_ORDER_BY_DATE) {
202            $qOrder = "  ts desc";
203        } else {
204            if (getConfig('rss.config.absoluteordering')) {
205                $qOrder = "  f.position asc, c.position asc";
206            } else {
207                $qOrder = "  f.name asc, c.title asc";
208            }
209        }
210
211
212
213        $qOrder .= ", i.added desc";
214
215
216
217        parent::populate($qWhere,$qOrder,0,getConfig("rss.search.maxitems"),ITEM_SORT_HINT_MIXED,true);
218
219        $this -> filterItems();
220        $this -> nav();
221    }
222
223    function nav() {
224        $nav = "";
225
226        if ($this->resultsPerPage != INFINE_RESULTS && $this->itemCount >  $this->resultsPerPage) {
227            $nav .= "<div class=\"readmore\">";
228            $nav .= __('Results: ');
229
230            // first page
231            $fp = 0;
232            //last page
233            $lp = floor(($this->itemCount -1) / $this -> resultsPerPage);
234            // current page
235            $cp = $this -> currentPage;
236            //shown pages
237            $pages = array ();
238
239            for ($i = 0; $i < 4; $i ++) {
240                if ($cp - $i >= 0) {
241                    $pages[$cp - $i] = true;
242                } else {
243                    if ($cp + $i < $lp) {
244                        $pages[$cp + $i] = true;
245                    }
246                }
247                if ($cp + $i < $lp) {
248                    $pages[$cp + $i] = true;
249                } else {
250                    if ($cp - $i >= 0) {
251                        $pages[$cp - $i] = true;
252                    }
253                }
254            }
255
256            $pages[0] = true;
257            $pages[$lp] = true;
258
259            for ($p = $fp; $p < $lp; $p ++) {
260                if (!array_key_exists($p, $pages)) {
261                    if (array_key_exists($p -1, $pages)) {
262                        $nav .= " ... ";
263                    }
264                    continue;
265                }
266
267                $cpp = ($p * $this -> resultsPerPage == $this->startItem);
268                if (!$cpp) {
269                    $nav .= " <a href=\"".$_SERVER['PHP_SELF']."?".QUERY_PRM."=".$this->query."&amp;"
270                            .QUERY_MATCH_MODE."=".$this->matchMode."&amp;"
271                            .QUERY_CHANNEL."=".$this->channelId ."&amp;"
272                            .QUERY_RESULTS."=".$this->resultsPerPage."&amp;"
273                            .QUERY_ORDER_BY."=".$this->orderBy."&amp;"
274                            .QUERY_CURRENT_PAGE."=$p"."\">";
275                } else {
276                    $nav .= HIT_BEFORE;
277                }
278
279                $nav .= "". (1 + $p * $this -> resultsPerPage)."-". ((1 + $p) * $this -> resultsPerPage)."";
280
281                if (!$cpp) {
282                    $nav .= "</a>";
283                } else {
284                    $nav .= HIT_AFTER;
285                }
286                if ((1 + $p) * $this -> resultsPerPage < $this->itemCount) {
287                    $nav .= ", \n";
288                }
289            }
290
291            if ($p * $this -> resultsPerPage >= $this->endItem) {
292                $nav .= " <a href=\"".$_SERVER['PHP_SELF']."?".QUERY_PRM."=".$this->query."&amp;"
293                        .QUERY_MATCH_MODE."=".$this->matchMode."&amp;"
294                        .QUERY_CHANNEL."=".$this->channelId ."&amp;"
295                        .QUERY_RESULTS."=".$this->resultsPerPage."&amp;"
296                        .QUERY_ORDER_BY."=".$this->orderBy."&amp;"
297                        .QUERY_CURRENT_PAGE."=$p"."\">";
298                $nav .= (1 + $p * $this -> resultsPerPage)."-" . $this->itemCount;
299                $nav .= "</a> \n";
300            } else {
301                $nav .= HIT_BEFORE. (1 + $p * $this -> resultsPerPage)."-" . $this->itemCount.HIT_AFTER;
302            }
303            $nav .= "<hr class=\"clearer hidden\"/>\n</div>\n";
304        }
305
306        if ($nav) {
307            $this -> beforeList .= $nav;
308            $this -> afterList .= $nav ;
309        }
310    }
311
312    function render() {
313        $GLOBALS['rss'] -> searchFormTitle = $this->title;
314        $this->title="";
315        require($GLOBALS['rss'] ->getTemplateFile("searchform.php"));
316
317        $GLOBALS['rss'] -> currentItemList = $this;
318        rss_plugin_hook('rss.plugins.items.beforeitems', null);
319        require($GLOBALS['rss'] ->getTemplateFile("itemlist.php"));
320        rss_plugin_hook('rss.plugins.items.afteritems', null);
321    }
322
323}
324?>
Note: See TracBrowser for help on using the browser.