xref: /plugin/sphinxsearch-was/action.php (revision 89:9122e80f4da6)
1<?php
2/**
3 * Script to search in dokuwiki documents
4 *
5 * @author Yaroslav Vorozhko <yaroslav@ivinco.com>
6 */
7
8if(!defined('DOKU_INC')) die();
9if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10
11require_once(DOKU_INC.'inc/parser/parser.php');
12
13require_once(DOKU_PLUGIN . 'action.php');
14require_once(DOKU_PLUGIN . 'sphinxsearch/sphinxapi.php');
15require_once(DOKU_PLUGIN . 'sphinxsearch/PageMapper.php');
16require_once(DOKU_PLUGIN . 'sphinxsearch/SphinxSearch.php');
17require_once(DOKU_PLUGIN . 'sphinxsearch/functions.php');
18
19
20class action_plugin_sphinxsearch extends DokuWiki_Action_Plugin {
21    var $_search = null;
22
23    var $_helpMessage = "
24===== DokuWiki Sphinx Search plugin features=====
25
26To use the search you need to just enter your search keywords into the searchbox at the top right corner of the DokuWiki. When basic simple search is not enough you can try using the methods listed below:
27
28=== Phrase search (\"\") ===
29Put double quotes around a set of words to enable phrase search mode. For example:
30<code>\"James Bond\"</code>
31
32=== Search within a namespace ===
33You can add \"@ns\" parameter to limit the search to some namespace. For exapmle:
34<code>hotel @ns personal:mike:travel</code>
35Such query will return only results from \"personal:mike:travel\" namespace for keyword \"hotel\".
36
37=== Excluding keywords or namespaces from search ===
38You can add a minus sign to a keyword or a category name exclude it from search. For example:
39<code>hotel @ns -personal:mike</code>
40Such query will look for \"hotel\" everywhere except the \"personal:mike\" namespace.
41<code>blog -post</code>
42Such query will look for documents that have keyword \"blog\" but don't have keyword \"post\".
43";
44
45    /**
46	* return some info
47	*/
48    function getInfo() {
49        return confToHash(dirname(__FILE__).'/plugin.info.txt');
50	}
51
52    /**
53	* Register to the content display event to place the results under it.
54	*/
55    /**
56     * register the eventhandlers
57     */
58    function register(&$controller){
59        $controller->register_hook('TPL_CONTENT_DISPLAY',   'BEFORE', $this, 'handle_act_unknown', array());
60    }
61
62    /**
63     * If our own 'googlesearch' action was given we produce our content here
64     */
65    function handle_act_unknown(&$event, $param){
66        global $ACT;
67        global $QUERY;
68        if($ACT != 'search') return; // nothing to do for us
69
70        // we can handle it -> prevent others
71        $event->stopPropagation();
72        $event->preventDefault();
73
74        if (!extension_loaded('sqlite')) {
75            echo "SQLite extension is not loaded!";
76            return;
77        }
78
79        if(!empty($_REQUEST['ssplugininfo'])){
80            $info = array();
81            echo p_render('xhtml',p_get_instructions($this->_helpMessage), $info);
82            return;
83        }
84
85        $this->_search($QUERY,$_REQUEST['start'],$_REQUEST['prev']);
86    }
87
88    /**
89     * do the search and displays the result
90     */
91    function _search($query, $start, $prev) {
92        global $conf;
93
94        $start = (int) $start;
95        if($start < 0){
96            $start = 0;
97        }
98        if(empty($prev)){
99            $prev = 0;
100        }
101
102        $categories = $this->_getCategories($query);
103        $keywords = $this->_getKeywords($query);
104
105        $search = new SphinxSearch($this->getConf('host'), $this->getConf('port'), $this->getConf('index'));
106        $search->setSnippetSize($this->getConf('snippetsize'));
107        $search->setArroundWordsCount($this->getConf('aroundwords'));
108        $search->setTitlePriority($this->getConf('title_priority'));
109        $search->setBodyPriority($this->getConf('body_priority'));
110        $search->setNamespacePriority($this->getConf('namespace_priority'));
111        $search->setPagenamePriority($this->getConf('pagename_priority'));
112
113        if (!empty($keywords) && empty($categories)){
114            $search->setSearchAllQuery($keywords, $categories);
115        } elseif (!empty($keywords)) {
116            $search->setSearchAllQueryWithCategoryFilter($keywords, $categories);
117        } else {
118            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br>
119                <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
120            return;
121        }
122        $result = $search->search($start, $this->getConf('maxresults'));
123        $this->_search = $search;
124
125        if ($search->getError()){
126            echo "Could not connect to Sphinx search engine.";
127            return;
128        }
129
130        $pagesList = $search->getPages($keywords);
131
132        $totalFound = $search->getTotalFound();
133        if(!$result || empty($pagesList) || 0 == $totalFound){
134            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
135                <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
136            return;
137        } else {
138            echo '<style type="text/css">
139                div.dokuwiki .search{
140                    width:1024px;
141                }
142                div.dokuwiki .search_snippet{
143                    color:#000000;
144                    margin-left:0px;
145                    font-size: 13px;
146                }
147                div.dokuwiki .search_result{
148                    width:600px;
149                    float:left;
150                }
151                div.dokuwiki .search_result a.title{
152                    font:16px Arial,Helvetica,sans-serif;
153                }
154                div.dokuwiki .search_result span{
155                    font:12px Arial,Helvetica,sans-serif;
156                }
157                div.dokuwiki .search_sidebar{
158                    width:300px;
159                    float:right;
160                    margin-right: 30px;
161                }
162                div.dokuwiki .search_result_row{
163                    color:#000000;
164                    margin-left:0px;
165                    width:600px;
166                    text-align:left;
167                }
168                div.dokuwiki .search_result_row_child{
169                    color:#000000;
170                    margin-left:30px;
171                    width:600px;
172                    text-align:left;
173                }
174                div.dokuwiki .hide{
175                    display:none;
176                }
177                div.dokuwiki .search_cnt{
178                    color:#909090;
179                    font:12px Arial,Helvetica,sans-serif;
180                }
181                div.dokuwiki .search_nmsp{
182                    font-size: 10px;
183                }
184                div.dokuwiki .sphinxsearch_nav{
185                    clear:both;
186                }
187                </style>
188                <script type="text/javascript">
189function sh(id)
190{
191    var e = document.getElementById(id);
192    if(e.style.display == "block")
193        e.style.display = "none";
194    else
195        e.style.display = "block";
196}
197</script>
198';
199
200            echo '<h2>Found '.$totalFound . ($totalFound == 1  ? ' match ' : ' matches ') . ' for query "' . hsc($query).'"</h2>';
201            echo '<div class="search">';
202            echo '<div class="search_result">';
203            // printout the results
204            $pageListGroupByPage = array();
205            foreach ($pagesList as $row) {
206                $page = $row['page'];
207                if(!isset ($pageListGroupByPage[$page])){
208                    $pageListGroupByPage[$page] = $row;
209                } else {
210                    $pageListGroupByPage[$page]['subpages'][] = $row;
211                }
212            }
213            foreach ($pageListGroupByPage as $row) {
214                $this->_showResult($row, $keywords, false);
215                if(!empty($row['subpages'])){
216                    echo '<div id="more'.$row['page'].'" class="hide">';
217                    foreach($row['subpages'] as $sub){
218                        $this->_showResult($sub, $keywords, true);
219                    }
220                    echo '</div>';
221                }
222
223            }
224            echo '</div>';
225            echo '<div class="search_sidebar">';
226            printNamespacesNew($this->_getMatchingPagenames($keywords, $categories));
227            echo '</div>';
228            echo '<div class="sphinxsearch_nav">';
229            if ($start > 1){
230                if(false !== strpos($prev, ',')){
231                    $prevArry = explode(",", $prev);
232                    $prevNum = $prevArry[count($prevArry)-1];
233                    unset($prevArry[count($prevArry)-1]);
234                    $prevString = implode(",", $prevArry);
235                } else {
236                    $prevNum = 0;
237                }
238
239                echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$prevNum, 'prev'=>$prevString),'false','&'),
240                                          'prev','wikilink1 gs_prev',$conf['target']['interwiki']);
241            }
242            echo ' ';
243
244            //if($start + $this->getConf('maxresults') < $totalFound){
245                //$next = $start + $this->getConf('maxresults');
246            if($start + $search->getOffset()< $totalFound){
247                $next = $start + $search->getOffset();
248                if($start > 1){
249                    $prevString = $prev.','.$start;
250                }
251                echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$next,'prev'=>$prevString),'false','&'),
252                                          'next','wikilink1 gs_next',$conf['target']['interwiki']);
253            }
254            echo '</div>';
255            echo '<a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
256            echo '</div>';
257        }
258
259
260    }
261
262    function _showResult($row, $keywords, $subpages = false)
263    {
264        $page = $row['page'];
265        $bodyExcerpt = $row['bodyExcerpt'];
266        $titleTextExcerpt = $row['titleTextExcerpt'];
267        $hid = $row['hid'];
268
269        $metaData = p_get_metadata($page);
270
271        if (!empty($titleTextExcerpt)){
272            $titleText = $titleTextExcerpt;
273        } elseif(!empty($row['title_text'])){
274            $titleText = $row['title_text'];
275        } elseif(!empty($metaData['title'])){
276            $titleText = hsc($metaData['title']);
277        } else {
278            $titleText = hsc($page);
279        }
280
281        $namespaces = getNsLinks($page, $keywords, $this->_search);
282        $href = !empty($hid) ? (wl($page).'#'.$hid) : wl($page);
283
284        if($subpages){
285            echo '<div class="search_result_row_child">';
286        } else {
287            echo '<div class="search_result_row">';
288        }
289
290        echo '<a class="wikilink1 title" href="'.$href.'" title="" >'.$titleText.'</a><br/>';
291        echo '<div class="search_snippet">';
292        echo strip_tags($bodyExcerpt, '<b>,<strong>');
293        echo '</div>';
294        $sep=':';
295        $i = 0;
296        echo '<span class="search_nmsp">';
297        foreach ($namespaces as $name){
298            $link = $name['link'];
299            $pageTitle = $name['title'];
300            tpl_link($link, $pageTitle);
301            if ($i++ < count($namespaces)-1){
302                echo $sep;
303            }
304        }
305        if (!empty($hid)){
306            echo '#'.$hid;
307        }
308        echo '</span>';
309
310        if (!empty($metaData['last_change']['date'])){
311            echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['last_change']['date']).'</span> ';
312        } else if (!empty($metaData['date']['created'])){
313            echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['date']['created']).'</span> ';
314        }
315
316        if(!empty($metaData['last_change']['user'])){
317            echo '<span class="search_cnt">by '.$metaData['last_change']['user'].'</span> ';
318        } else if(!empty($metaData['creator'])){
319            echo '<span class="search_cnt">by '.$metaData['creator'].'</span> ';
320        }
321
322        if (!empty($row['subpages'])){
323            echo '<br />';
324            echo '<div style="text-align:right;font:12px Arial,Helvetica,sans-serif;text-decoration:underline;"><a href="javascript:void(0)" onClick="sh('."'more".$page."'".');" >More matches in this document</a></div>';
325        }else {
326            echo '<br />';
327        }
328        echo '<br />';
329        echo '</div>';
330    }
331
332     function searchform()
333    {
334          global $lang;
335          global $ACT;
336          global $QUERY;
337
338          // don't print the search form if search action has been disabled
339          if (!actionOk('search')) return false;
340
341          print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search"><div class="no">';
342          print '<input type="hidden" name="do" value="search" />';
343          print '<input type="text" ';
344          if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
345          print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[ALT+F]" />';
346          print '<input type="submit" value="'.$lang['btn_search'].'" class="button" title="'.$lang['btn_search'].'" />';
347          print '</div></form>';
348          return true;
349    }
350
351    function _getCategories($query)
352    {
353        $categories = '';
354        $query = urldecode($query);
355        if (false !== ($pos = strpos($query, "@ns"))){;
356            $categories = substr($query, $pos + strlen("@ns"));
357        }
358        return trim($categories);
359    }
360
361    function _getKeywords($query)
362    {
363        $keywords = $query;
364        $query = urldecode($query);
365        if (false !== ($pos = strpos($query, "-@ns"))){;
366            $keywords = substr($keywords, 0, $pos);
367        }else if (false !== ($pos = strpos($query, "@ns"))){;
368            $keywords = substr($keywords, 0, $pos);
369        }
370        return trim($keywords);
371    }
372
373    function _getMatchingPagenames($keywords, $categories)
374    {
375        $this->_search->setSearchCategoryQuery($keywords, $categories);
376        $this->_search->setNamespacePriority($this->getConf('mp_namespace_priority'));
377        $this->_search->setPagenamePriority($this->getConf('mp_pagename_priority'));
378
379        $res = $this->_search->search(0, 10);
380        if (!$res){
381            return false;
382        }
383        $pageIds = $this->_search->getPagesIds();
384
385        $matchPages = array();
386        foreach($pageIds as $page){
387            $matchPages[$page['page']] = $page['hid'];
388        }
389        return array_unique($matchPages);
390    }
391}
392
393?>
394