xref: /plugin/sphinxsearch-was/action.php (revision 82:9160a12cd369)
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 left 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(!empty($_REQUEST['ssplugininfo'])){
75            $info = array();
76            echo p_render('xhtml',p_get_instructions($this->_helpMessage), $info);
77            return;
78        }
79
80        $this->_search($QUERY,$_REQUEST['start'],$_REQUEST['prev']);
81    }
82
83    /**
84     * do the search and displays the result
85     */
86    function _search($query, $start, $prev) {
87        global $conf;
88
89        $start = (int) $start;
90        if($start < 0){
91            $start = 0;
92        }
93        if(empty($prev)){
94            $prev = 0;
95        }
96
97        $categories = $this->_getCategories($query);
98        $keywords = $this->_getKeywords($query);
99
100        $search = new SphinxSearch($this->getConf('host'), $this->getConf('port'), $this->getConf('index'));
101        $search->setSnippetSize($this->getConf('snippetsize'));
102        $search->setArroundWordsCount($this->getConf('aroundwords'));
103        $search->setTitlePriority($this->getConf('title_priority'));
104        $search->setBodyPriority($this->getConf('body_priority'));
105        $search->setNamespacePriority($this->getConf('namespace_priority'));
106        $search->setPagenamePriority($this->getConf('pagename_priority'));
107
108        if (!empty($keywords) && empty($categories)){
109            $search->setSearchAllQuery($keywords, $categories);
110        } elseif (!empty($keywords)) {
111            $search->setSearchAllQueryWithCategoryFilter($keywords, $categories);
112        } else {
113            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br>
114                <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
115            return;
116        }
117        $result = $search->search($start, $this->getConf('maxresults'));
118        $this->_search = $search;
119
120        if (!$result || $search->getError()){
121            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
122                <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
123            return;
124        }
125
126        $pagesList = $search->getPages($keywords);
127
128        $totalFound = $search->getTotalFound();
129        if(empty($pagesList) || 0 == $totalFound){
130            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
131                <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
132            return;
133        } else {
134            echo '<style type="text/css">
135                div.dokuwiki .search{
136                    width:1024px;
137                }
138                div.dokuwiki .search_snippet{
139                    color:#000000;
140                    margin-left:0px;
141                    font-size: 13px;
142                }
143                div.dokuwiki .search_result{
144                    width:600px;
145                    float:left;
146                }
147                div.dokuwiki .search_result a.title{
148                    font:16px Arial,Helvetica,sans-serif;
149                }
150                div.dokuwiki .search_result span{
151                    font:12px Arial,Helvetica,sans-serif;
152                }
153                div.dokuwiki .search_sidebar{
154                    width:300px;
155                    float:right;
156                    margin-right: 30px;
157                }
158                div.dokuwiki .search_result_row{
159                    color:#000000;
160                    margin-left:0px;
161                    width:600px;
162                    text-align:left;
163                }
164                div.dokuwiki .search_result_row_child{
165                    color:#000000;
166                    margin-left:30px;
167                    width:600px;
168                    text-align:left;
169                }
170                div.dokuwiki .hide{
171                    display:none;
172                }
173                div.dokuwiki .search_cnt{
174                    color:#909090;
175                    font:12px Arial,Helvetica,sans-serif;
176                }
177                div.dokuwiki .search_nmsp{
178                    font-size: 10px;
179                }
180                div.dokuwiki .sphinxsearch_nav{
181                    clear:both;
182                }
183                </style>
184                <script type="text/javascript">
185function sh(id)
186{
187    var e = document.getElementById(id);
188    if(e.style.display == "block")
189        e.style.display = "none";
190    else
191        e.style.display = "block";
192}
193</script>
194';
195
196            echo '<h2>Found '.$totalFound . ($totalFound == 1  ? ' match ' : ' matches ') . ' for query "' . hsc($query).'"</h2>';
197            echo '<div class="search">';
198            echo '<div class="search_result">';
199            // printout the results
200            $pageListGroupByPage = array();
201            foreach ($pagesList as $row) {
202                $page = $row['page'];
203                if(!isset ($pageListGroupByPage[$page])){
204                    $pageListGroupByPage[$page] = $row;
205                } else {
206                    $pageListGroupByPage[$page]['subpages'][] = $row;
207                }
208            }
209            foreach ($pageListGroupByPage as $row) {
210                $this->_showResult($row, $keywords, false);
211                if(!empty($row['subpages'])){
212                    echo '<div id="more'.$row['page'].'" class="hide">';
213                    foreach($row['subpages'] as $sub){
214                        $this->_showResult($sub, $keywords, true);
215                    }
216                    echo '</div>';
217                }
218
219            }
220            echo '</div>';
221            echo '<div class="search_sidebar">';
222            printNamespacesNew($this->_getMatchingPagenames($keywords, $categories));
223            echo '</div>';
224            echo '<div class="sphinxsearch_nav">';
225            if ($start > 1){
226                if(false !== strpos($prev, ',')){
227                    $prevArry = explode(",", $prev);
228                    $prevNum = $prevArry[count($prevArry)-1];
229                    unset($prevArry[count($prevArry)-1]);
230                    $prevString = implode(",", $prevArry);
231                } else {
232                    $prevNum = 0;
233                }
234
235                echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$prevNum, 'prev'=>$prevString),'false','&'),
236                                          'prev','wikilink1 gs_prev',$conf['target']['interwiki']);
237            }
238            echo ' ';
239
240            //if($start + $this->getConf('maxresults') < $totalFound){
241                //$next = $start + $this->getConf('maxresults');
242            if($start + $search->getOffset()< $totalFound){
243                $next = $start + $search->getOffset();
244                if($start > 1){
245                    $prevString = $prev.','.$start;
246                }
247                echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$next,'prev'=>$prevString),'false','&'),
248                                          'next','wikilink1 gs_next',$conf['target']['interwiki']);
249            }
250            echo '</div>';
251            echo '<a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
252            echo '</div>';
253        }
254
255
256    }
257
258    function _showResult($row, $keywords, $subpages = false)
259    {
260        $page = $row['page'];
261        $bodyExcerpt = $row['bodyExcerpt'];
262        $titleTextExcerpt = $row['titleTextExcerpt'];
263        $hid = $row['hid'];
264
265        $metaData = p_get_metadata($page);
266
267        if (!empty($titleTextExcerpt)){
268            $titleText = $titleTextExcerpt;
269        } elseif(!empty($row['title_text'])){
270            $titleText = $row['title_text'];
271        } elseif(!empty($metaData['title'])){
272            $titleText = hsc($metaData['title']);
273        } else {
274            $titleText = hsc($page);
275        }
276
277        $namespaces = getNsLinks($page, $keywords, $this->_search);
278        $href = !empty($hid) ? (wl($page).'#'.$hid) : wl($page);
279
280        if($subpages){
281            echo '<div class="search_result_row_child">';
282        } else {
283            echo '<div class="search_result_row">';
284        }
285
286        echo '<a class="wikilink1 title" href="'.$href.'" title="" >'.$titleText.'</a><br/>';
287        echo '<div class="search_snippet">';
288        echo strip_tags($bodyExcerpt, '<b>,<strong>');
289        echo '</div>';
290        $sep=':';
291        $i = 0;
292        echo '<span class="search_nmsp">';
293        foreach ($namespaces as $name){
294            $link = $name['link'];
295            $pageTitle = $name['title'];
296            tpl_link($link, $pageTitle);
297            if ($i++ < count($namespaces)-1){
298                echo $sep;
299            }
300        }
301        if (!empty($hid)){
302            echo '#'.$hid;
303        }
304        echo '</span>';
305
306        if (!empty($metaData['last_change']['date'])){
307            echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['last_change']['date']).'</span> ';
308        } else if (!empty($metaData['date']['created'])){
309            echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['date']['created']).'</span> ';
310        }
311
312        if(!empty($metaData['last_change']['user'])){
313            echo '<span class="search_cnt">by '.$metaData['last_change']['user'].'</span> ';
314        } else if(!empty($metaData['creator'])){
315            echo '<span class="search_cnt">by '.$metaData['creator'].'</span> ';
316        }
317
318        if (!empty($row['subpages'])){
319            echo '<br />';
320            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>';
321        }else {
322            echo '<br />';
323        }
324        echo '<br />';
325        echo '</div>';
326    }
327
328     function searchform()
329    {
330          global $lang;
331          global $ACT;
332          global $QUERY;
333
334          // don't print the search form if search action has been disabled
335          if (!actionOk('search')) return false;
336
337          print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search"><div class="no">';
338          print '<input type="hidden" name="do" value="search" />';
339          print '<input type="text" ';
340          if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
341          print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[ALT+F]" />';
342          print '<input type="submit" value="'.$lang['btn_search'].'" class="button" title="'.$lang['btn_search'].'" />';
343          print '</div></form>';
344          return true;
345    }
346
347    function _getCategories($query)
348    {
349        $categories = '';
350        $query = urldecode($query);
351        if (false !== ($pos = strpos($query, "@ns"))){;
352            $categories = substr($query, $pos + strlen("@ns"));
353        }
354        return trim($categories);
355    }
356
357    function _getKeywords($query)
358    {
359        $keywords = $query;
360        $query = urldecode($query);
361        if (false !== ($pos = strpos($query, "-@ns"))){;
362            $keywords = substr($keywords, 0, $pos);
363        }else if (false !== ($pos = strpos($query, "@ns"))){;
364            $keywords = substr($keywords, 0, $pos);
365        }
366        return trim($keywords);
367    }
368
369    function _getMatchingPagenames($keywords, $categories)
370    {
371        $this->_search->setSearchCategoryQuery($keywords, $categories);
372        $this->_search->setNamespacePriority($this->getConf('mp_namespace_priority'));
373        $this->_search->setPagenamePriority($this->getConf('mp_pagename_priority'));
374
375        $res = $this->_search->search(0, 10);
376        if (!$res){
377            return false;
378        }
379        $pageIds = $this->_search->getPagesIds();
380
381        $matchPages = array();
382        foreach($pageIds as $page){
383            $matchPages[$page['page']] = $page['hid'];
384        }
385        return array_unique($matchPages);
386    }
387}
388
389?>
390