xref: /plugin/sphinxsearch-was/action.php (revision 87:ec4f02ae6183)
1 <?php
2 /**
3  * Script to search in dokuwiki documents
4  *
5  * @author Yaroslav Vorozhko <yaroslav@ivinco.com>
6  */
7 
8 if(!defined('DOKU_INC')) die();
9 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10 
11 require_once(DOKU_INC.'inc/parser/parser.php');
12 
13 require_once(DOKU_PLUGIN . 'action.php');
14 require_once(DOKU_PLUGIN . 'sphinxsearch/sphinxapi.php');
15 require_once(DOKU_PLUGIN . 'sphinxsearch/PageMapper.php');
16 require_once(DOKU_PLUGIN . 'sphinxsearch/SphinxSearch.php');
17 require_once(DOKU_PLUGIN . 'sphinxsearch/functions.php');
18 
19 
20 class action_plugin_sphinxsearch extends DokuWiki_Action_Plugin {
21     var $_search = null;
22 
23     var $_helpMessage = "
24 ===== DokuWiki Sphinx Search plugin features=====
25 
26 To 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 (\"\") ===
29 Put 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 ===
33 You can add \"@ns\" parameter to limit the search to some namespace. For exapmle:
34 <code>hotel @ns personal:mike:travel</code>
35 Such query will return only results from \"personal:mike:travel\" namespace for keyword \"hotel\".
36 
37 === Excluding keywords or namespaces from search ===
38 You 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>
40 Such query will look for \"hotel\" everywhere except the \"personal:mike\" namespace.
41 <code>blog -post</code>
42 Such 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 (!$result || $search->getError()){
126             echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
127                 <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
128             return;
129         }
130 
131         $pagesList = $search->getPages($keywords);
132 
133         $totalFound = $search->getTotalFound();
134         if(empty($pagesList) || 0 == $totalFound){
135             echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
136                 <a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
137             return;
138         } else {
139             echo '<style type="text/css">
140                 div.dokuwiki .search{
141                     width:1024px;
142                 }
143                 div.dokuwiki .search_snippet{
144                     color:#000000;
145                     margin-left:0px;
146                     font-size: 13px;
147                 }
148                 div.dokuwiki .search_result{
149                     width:600px;
150                     float:left;
151                 }
152                 div.dokuwiki .search_result a.title{
153                     font:16px Arial,Helvetica,sans-serif;
154                 }
155                 div.dokuwiki .search_result span{
156                     font:12px Arial,Helvetica,sans-serif;
157                 }
158                 div.dokuwiki .search_sidebar{
159                     width:300px;
160                     float:right;
161                     margin-right: 30px;
162                 }
163                 div.dokuwiki .search_result_row{
164                     color:#000000;
165                     margin-left:0px;
166                     width:600px;
167                     text-align:left;
168                 }
169                 div.dokuwiki .search_result_row_child{
170                     color:#000000;
171                     margin-left:30px;
172                     width:600px;
173                     text-align:left;
174                 }
175                 div.dokuwiki .hide{
176                     display:none;
177                 }
178                 div.dokuwiki .search_cnt{
179                     color:#909090;
180                     font:12px Arial,Helvetica,sans-serif;
181                 }
182                 div.dokuwiki .search_nmsp{
183                     font-size: 10px;
184                 }
185                 div.dokuwiki .sphinxsearch_nav{
186                     clear:both;
187                 }
188                 </style>
189                 <script type="text/javascript">
190 function sh(id)
191 {
192     var e = document.getElementById(id);
193     if(e.style.display == "block")
194         e.style.display = "none";
195     else
196         e.style.display = "block";
197 }
198 </script>
199 ';
200 
201             echo '<h2>Found '.$totalFound . ($totalFound == 1  ? ' match ' : ' matches ') . ' for query "' . hsc($query).'"</h2>';
202             echo '<div class="search">';
203             echo '<div class="search_result">';
204             // printout the results
205             $pageListGroupByPage = array();
206             foreach ($pagesList as $row) {
207                 $page = $row['page'];
208                 if(!isset ($pageListGroupByPage[$page])){
209                     $pageListGroupByPage[$page] = $row;
210                 } else {
211                     $pageListGroupByPage[$page]['subpages'][] = $row;
212                 }
213             }
214             foreach ($pageListGroupByPage as $row) {
215                 $this->_showResult($row, $keywords, false);
216                 if(!empty($row['subpages'])){
217                     echo '<div id="more'.$row['page'].'" class="hide">';
218                     foreach($row['subpages'] as $sub){
219                         $this->_showResult($sub, $keywords, true);
220                     }
221                     echo '</div>';
222                 }
223 
224             }
225             echo '</div>';
226             echo '<div class="search_sidebar">';
227             printNamespacesNew($this->_getMatchingPagenames($keywords, $categories));
228             echo '</div>';
229             echo '<div class="sphinxsearch_nav">';
230             if ($start > 1){
231                 if(false !== strpos($prev, ',')){
232                     $prevArry = explode(",", $prev);
233                     $prevNum = $prevArry[count($prevArry)-1];
234                     unset($prevArry[count($prevArry)-1]);
235                     $prevString = implode(",", $prevArry);
236                 } else {
237                     $prevNum = 0;
238                 }
239 
240                 echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$prevNum, 'prev'=>$prevString),'false','&'),
241                                           'prev','wikilink1 gs_prev',$conf['target']['interwiki']);
242             }
243             echo ' ';
244 
245             //if($start + $this->getConf('maxresults') < $totalFound){
246                 //$next = $start + $this->getConf('maxresults');
247             if($start + $search->getOffset()< $totalFound){
248                 $next = $start + $search->getOffset();
249                 if($start > 1){
250                     $prevString = $prev.','.$start;
251                 }
252                 echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$next,'prev'=>$prevString),'false','&'),
253                                           'next','wikilink1 gs_next',$conf['target']['interwiki']);
254             }
255             echo '</div>';
256             echo '<a href="?do=search&ssplugininfo=1&id='.$query.'">Search help</a>';
257             echo '</div>';
258         }
259 
260 
261     }
262 
263     function _showResult($row, $keywords, $subpages = false)
264     {
265         $page = $row['page'];
266         $bodyExcerpt = $row['bodyExcerpt'];
267         $titleTextExcerpt = $row['titleTextExcerpt'];
268         $hid = $row['hid'];
269 
270         $metaData = p_get_metadata($page);
271 
272         if (!empty($titleTextExcerpt)){
273             $titleText = $titleTextExcerpt;
274         } elseif(!empty($row['title_text'])){
275             $titleText = $row['title_text'];
276         } elseif(!empty($metaData['title'])){
277             $titleText = hsc($metaData['title']);
278         } else {
279             $titleText = hsc($page);
280         }
281 
282         $namespaces = getNsLinks($page, $keywords, $this->_search);
283         $href = !empty($hid) ? (wl($page).'#'.$hid) : wl($page);
284 
285         if($subpages){
286             echo '<div class="search_result_row_child">';
287         } else {
288             echo '<div class="search_result_row">';
289         }
290 
291         echo '<a class="wikilink1 title" href="'.$href.'" title="" >'.$titleText.'</a><br/>';
292         echo '<div class="search_snippet">';
293         echo strip_tags($bodyExcerpt, '<b>,<strong>');
294         echo '</div>';
295         $sep=':';
296         $i = 0;
297         echo '<span class="search_nmsp">';
298         foreach ($namespaces as $name){
299             $link = $name['link'];
300             $pageTitle = $name['title'];
301             tpl_link($link, $pageTitle);
302             if ($i++ < count($namespaces)-1){
303                 echo $sep;
304             }
305         }
306         if (!empty($hid)){
307             echo '#'.$hid;
308         }
309         echo '</span>';
310 
311         if (!empty($metaData['last_change']['date'])){
312             echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['last_change']['date']).'</span> ';
313         } else if (!empty($metaData['date']['created'])){
314             echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['date']['created']).'</span> ';
315         }
316 
317         if(!empty($metaData['last_change']['user'])){
318             echo '<span class="search_cnt">by '.$metaData['last_change']['user'].'</span> ';
319         } else if(!empty($metaData['creator'])){
320             echo '<span class="search_cnt">by '.$metaData['creator'].'</span> ';
321         }
322 
323         if (!empty($row['subpages'])){
324             echo '<br />';
325             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>';
326         }else {
327             echo '<br />';
328         }
329         echo '<br />';
330         echo '</div>';
331     }
332 
333      function searchform()
334     {
335           global $lang;
336           global $ACT;
337           global $QUERY;
338 
339           // don't print the search form if search action has been disabled
340           if (!actionOk('search')) return false;
341 
342           print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search"><div class="no">';
343           print '<input type="hidden" name="do" value="search" />';
344           print '<input type="text" ';
345           if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
346           print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[ALT+F]" />';
347           print '<input type="submit" value="'.$lang['btn_search'].'" class="button" title="'.$lang['btn_search'].'" />';
348           print '</div></form>';
349           return true;
350     }
351 
352     function _getCategories($query)
353     {
354         $categories = '';
355         $query = urldecode($query);
356         if (false !== ($pos = strpos($query, "@ns"))){;
357             $categories = substr($query, $pos + strlen("@ns"));
358         }
359         return trim($categories);
360     }
361 
362     function _getKeywords($query)
363     {
364         $keywords = $query;
365         $query = urldecode($query);
366         if (false !== ($pos = strpos($query, "-@ns"))){;
367             $keywords = substr($keywords, 0, $pos);
368         }else if (false !== ($pos = strpos($query, "@ns"))){;
369             $keywords = substr($keywords, 0, $pos);
370         }
371         return trim($keywords);
372     }
373 
374     function _getMatchingPagenames($keywords, $categories)
375     {
376         $this->_search->setSearchCategoryQuery($keywords, $categories);
377         $this->_search->setNamespacePriority($this->getConf('mp_namespace_priority'));
378         $this->_search->setPagenamePriority($this->getConf('mp_pagename_priority'));
379 
380         $res = $this->_search->search(0, 10);
381         if (!$res){
382             return false;
383         }
384         $pageIds = $this->_search->getPagesIds();
385 
386         $matchPages = array();
387         foreach($pageIds as $page){
388             $matchPages[$page['page']] = $page['hid'];
389         }
390         return array_unique($matchPages);
391     }
392 }
393 
394 ?>
395