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 /** 24 * return some info 25 */ 26 function getInfo() { 27 return confToHash(dirname(__FILE__).'/plugin.info.txt'); 28 } 29 30 /** 31 * Register to the content display event to place the results under it. 32 */ 33 /** 34 * register the eventhandlers 35 */ 36 function register(&$controller){ 37 $controller->register_hook('TPL_CONTENT_DISPLAY', 'BEFORE', $this, 'handle_act_unknown', array()); 38 } 39 40 /** 41 * If our own 'googlesearch' action was given we produce our content here 42 */ 43 function handle_act_unknown(&$event, $param){ 44 global $ACT; 45 global $QUERY; 46 if($ACT != 'search') return; // nothing to do for us 47 48 // we can handle it -> prevent others 49 $event->stopPropagation(); 50 $event->preventDefault(); 51 52 53 $this->_search($QUERY,$_REQUEST['start'],$_REQUEST['prev']); 54 } 55 56 /** 57 * do the search and displays the result 58 */ 59 function _search($query, $start, $prev) { 60 global $conf; 61 62 $start = (int) $start; 63 if($start < 0){ 64 $start = 0; 65 } 66 if(empty($prev)){ 67 $prev = 0; 68 } 69 70 $categories = $this->_getCategories($query); 71 $keywords = $this->_getKeywords($query); 72 73 $search = new SphinxSearch($this->getConf('host'), $this->getConf('port'), $this->getConf('index')); 74 $search->setSnippetSize($this->getConf('snippetsize')); 75 $search->setArroundWordsCount($this->getConf('aroundwords')); 76 $search->setTitlePriority($this->getConf('title_priority')); 77 $search->setBodyPriority($this->getConf('body_priority')); 78 $search->setCategoriesPriority($this->getConf('categories_priority')); 79 80 if (!empty($keywords) && empty($categories)){ 81 $search->setSearchAllQuery($keywords, $categories); 82 } else { 83 $search->setSearchAllQueryWithCategoryFilter($keywords, $categories); 84 } 85 $result = $search->search($start, $this->getConf('maxresults')); 86 $this->_search = $search; 87 88 if (!$result || $search->getError()){ 89 echo '<b>' . $search->getError() . '</b>!'; 90 return; 91 } 92 93 $pagesList = $search->getPages(); 94 95 $totalFound = $search->getTotalFound(); 96 if(empty($pagesList)){ 97 echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.'; 98 return; 99 } else { 100 echo '<style type="text/css"> 101 div.dokuwiki .search{ 102 width:1024px; 103 } 104 div.dokuwiki .search_snippet{ 105 color:#000000; 106 margin-left:0px; 107 font-size: 13px; 108 } 109 div.dokuwiki .search_result{ 110 width:600px; 111 float:left; 112 } 113 div.dokuwiki .search_result a.title{ 114 font:16px Arial,Helvetica,sans-serif; 115 } 116 div.dokuwiki .search_result span{ 117 font:12px Arial,Helvetica,sans-serif; 118 } 119 div.dokuwiki .search_sidebar{ 120 width:300px; 121 float:right; 122 margin-right: 30px; 123 } 124 div.dokuwiki .search_result_row{ 125 color:#000000; 126 margin-left:0px; 127 width:600px; 128 text-align:left; 129 } 130 div.dokuwiki .search_result_row_child{ 131 color:#000000; 132 margin-left:30px; 133 width:600px; 134 text-align:left; 135 } 136 div.dokuwiki .hide{ 137 display:none; 138 } 139 div.dokuwiki .search_cnt{ 140 color:#909090; 141 font:12px Arial,Helvetica,sans-serif; 142 } 143 div.dokuwiki .search_nmsp{ 144 font-size: 10px; 145 } 146 div.dokuwiki .sphinxsearch_nav{ 147 clear:both; 148 } 149 </style> 150 <script type="text/javascript"> 151function sh(id) 152{ 153 var e = document.getElementById(id); 154 if(e.style.display == "block") 155 e.style.display = "none"; 156 else 157 e.style.display = "block"; 158} 159</script> 160'; 161 162 echo '<h2>Found '.$totalFound . ($totalFound == 1 ? ' match ' : ' matches ') . ' for query "' . hsc($query).'"</h2>'; 163 echo '<div class="search">'; 164 echo '<div class="search_result">'; 165 // printout the results 166 $pageListGroupByPage = array(); 167 foreach ($pagesList as $row) { 168 $page = $row['page']; 169 if(!isset ($pageListGroupByPage[$page])){ 170 $pageListGroupByPage[$page] = $row; 171 } else { 172 $pageListGroupByPage[$page]['subpages'][] = $row; 173 } 174 } 175 foreach ($pageListGroupByPage as $row) { 176 $this->_showResult($row, $keywords, false); 177 if(!empty($row['subpages'])){ 178 echo '<div id="more'.$row['page'].'" class="hide">'; 179 foreach($row['subpages'] as $sub){ 180 $this->_showResult($sub, $keywords, true); 181 } 182 echo '</div>'; 183 } 184 185 } 186 echo '</div>'; 187 echo '<div class="search_sidebar">'; 188 printNamespacesNew($this->_getMatchingPagenames($keywords)); 189 echo '</div>'; 190 echo '<div class="sphinxsearch_nav">'; 191 if ($start > 1){ 192 if(false !== strpos($prev, ',')){ 193 $prevArry = explode(",", $prev); 194 $prevNum = $prevArry[count($prevArry)-1]; 195 unset($prevArry[count($prevArry)-1]); 196 $prevString = implode(",", $prevArry); 197 } else { 198 $prevNum = 0; 199 } 200 201 echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$prevNum, 'prev'=>$prevString),'false','&'), 202 'prev','wikilink1 gs_prev',$conf['target']['interwiki']); 203 } 204 echo ' '; 205 206 //if($start + $this->getConf('maxresults') < $totalFound){ 207 //$next = $start + $this->getConf('maxresults'); 208 if($start + $search->getOffset()< $totalFound){ 209 $next = $start + $search->getOffset(); 210 if($start > 1){ 211 $prevString = $prev.','.$start; 212 } 213 echo $this->external_link(wl('',array('do'=>'search','id'=>$query,'start'=>$next,'prev'=>$prevString),'false','&'), 214 'next','wikilink1 gs_next',$conf['target']['interwiki']); 215 } 216 echo '</div>'; 217 218 echo '</div>'; 219 } 220 221 222 } 223 224 function _showResult($row, $keywords, $subpages = false) 225 { 226 $page = $row['page']; 227 $bodyExcerpt = $row['bodyExcerpt']; 228 $titleTextExcerpt = $row['titleTextExcerpt']; 229 $hid = $row['hid']; 230 231 $metaData = p_get_metadata($page); 232 233 if (!empty($titleTextExcerpt)){ 234 $titleText = $titleTextExcerpt; 235 } elseif(!empty($row['title_text'])){ 236 $titleText = $row['title_text']; 237 } elseif(!empty($metaData['title'])){ 238 $titleText = hsc($metaData['title']); 239 } else { 240 $titleText = hsc($page); 241 } 242 243 $namespaces = getNsLinks($page, $keywords, $this->_search); 244 $href = !empty($hid) ? (wl($page).'#'.$hid) : wl($page); 245 246 if($subpages){ 247 echo '<div class="search_result_row_child">'; 248 } else { 249 echo '<div class="search_result_row">'; 250 } 251 252 echo '<a class="wikilink1 title" href="'.$href.'" title="" >'.$titleText.'</a><br/>'; 253 echo '<div class="search_snippet">'; 254 echo strip_tags($bodyExcerpt, '<b>,<strong>'); 255 echo '</div>'; 256 $sep=':'; 257 $i = 0; 258 echo '<span class="search_nmsp">'; 259 foreach ($namespaces as $name){ 260 $link = $name['link']; 261 $pageTitle = $name['title']; 262 tpl_link($link, $pageTitle); 263 if ($i++ < count($namespaces)-1){ 264 echo $sep; 265 } 266 } 267 if (!empty($hid)){ 268 echo '#'.$hid; 269 } 270 echo '</span>'; 271 272 if (!empty($metaData['last_change']['date'])){ 273 echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['last_change']['date']).'</span> '; 274 } else if (!empty($metaData['date']['created'])){ 275 echo '<span class="search_cnt"> - Last modified '.date("Y-m-d H:i",$metaData['date']['created']).'</span> '; 276 } 277 278 if(!empty($metaData['last_change']['user'])){ 279 echo '<span class="search_cnt">by '.$metaData['last_change']['user'].'</span> '; 280 } else if(!empty($metaData['creator'])){ 281 echo '<span class="search_cnt">by '.$metaData['creator'].'</span> '; 282 } 283 284 if (!empty($row['subpages'])){ 285 echo '<br />'; 286 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>'; 287 }else { 288 echo '<br />'; 289 } 290 echo '<br />'; 291 echo '</div>'; 292 } 293 294 function searchform(){ 295 global $lang; 296 global $ACT; 297 global $QUERY; 298 299 // don't print the search form if search action has been disabled 300 if (!actionOk('search')) return false; 301 302 print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search"><div class="no">'; 303 print '<input type="hidden" name="do" value="search" />'; 304 print '<input type="text" '; 305 if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" '; 306 print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[ALT+F]" />'; 307 print '<input type="submit" value="'.$lang['btn_search'].'" class="button" title="'.$lang['btn_search'].'" />'; 308 print '</div></form>'; 309 return true; 310 } 311 312 function _getCategories($query) 313 { 314 $categories = ''; 315 $query = urldecode($query); 316 if (false !== ($pos = strpos($query, "@categories"))){; 317 $categories = substr($query, $pos + strlen("@categories")); 318 } 319 return trim($categories); 320 } 321 322 function _getKeywords($query) 323 { 324 $keywords = $query; 325 $query = urldecode($query); 326 if (false !== ($pos = strpos($query, "-@categories"))){; 327 $keywords = substr($keywords, 0, $pos); 328 }else if (false !== ($pos = strpos($query, "@categories"))){; 329 $keywords = substr($keywords, 0, $pos); 330 } 331 return trim($keywords); 332 } 333 334 function _getMatchingPagenames($keywords, $categories) 335 { 336 $this->_search->setSearchCategoryQuery($keywords, $categories); 337 $res = $this->_search->search(0, 20); 338 if (!$res){ 339 return false; 340 } 341 $pageIds = $this->_search->getPagesIds(); 342 343 $matchPages = array(); 344 foreach($pageIds as $page){ 345 $matchPages[] = $page['page']; 346 } 347 return array_unique($matchPages); 348 } 349} 350 351?> 352