register_hook('TPL_CONTENT_DISPLAY', 'BEFORE', $this, 'handle_act_unknown'); } public function handle_act_unknown(\dokuwiki\Extension\Event $event, $param) { global $ACT, $QUERY; if ($ACT !== 'search') return; $event->stopPropagation(); $event->preventDefault(); $this->_search($QUERY, (int)($_REQUEST['start'] ?? 0)); } private function _search($query, $start) { $search = new SphinxSearch($this->getConf('host'), $this->getConf('port'), $this->getConf('index')); $maxResults = (int)$this->getConf('maxresults') ?: 10; $search->setSnippetSize((int)$this->getConf('snippetsize') ?: 512); $keywords = $this->_getKeywords($query); $cats = $this->_getCategories($query); if (empty($keywords)) { echo "No keywords."; return; } if (empty($cats)) $search->setSearchAllQuery($keywords, ''); else $search->setSearchAllQueryWithCategoryFilter($keywords, $cats); $res = $search->search($start, $maxResults); $this->_sObj = $search; if ($search->getError()) { echo "Sphinx Error: " . hsc($search->getError()); return; } $pages = $search->getPages($keywords); $total = $search->getTotalFound(); if (!$res || empty($pages) || $total == 0) { echo "No results."; return; } $this->_dom = new DOMDocument('1.0', 'UTF-8'); $root = $this->_dom->createElement('div'); $this->_dom->appendChild($root); $h1 = $this->_dom->createElement('h1', "Found $total matches for \"" . hsc($query) . "\""); $root->appendChild($h1); $container = $this->_dom->createElement('div'); $container->setAttribute('class', 'sphinx_search_container'); $root->appendChild($container); $sidebar = $this->_dom->createElement('div'); $sidebar->setAttribute('class', 'search_sidebar'); ob_start(); if (function_exists('printNamespacesNew')) printNamespacesNew($this->_getMatchingPagenames($keywords, $cats)); $this->_appendRawHTML($sidebar, ob_get_clean()); $container->appendChild($sidebar); $h2 = $this->_dom->createElement('h2', "Matching keywords"); $container->appendChild($h2); $list = $this->_dom->createElement('div'); $list->setAttribute('class', 'search_result_list'); $container->appendChild($list); foreach ($pages as $row) { $list->appendChild($this->_createResultNode($row, $keywords)); } $this->_addNumberedPagination($container, $query, $start, $total, $maxResults); echo $this->_dom->saveHTML($root); } private function _createResultNode($row, $keywords) { $div = $this->_dom->createElement('div'); $div->setAttribute('class', 'search_result_row'); $a = $this->_dom->createElement('a', hsc($row['titleTextExcerpt'])); $a->setAttribute('class', 'title'); $a->setAttribute('href', wl($row['page'])); $div->appendChild($a); $snippet = $this->_dom->createElement('div'); $snippet->setAttribute('class', 'search_snippet'); $this->_appendRawHTML($snippet, $row['bodyExcerpt']); $div->appendChild($snippet); $nmsp = $this->_dom->createElement('span'); $nmsp->setAttribute('class', 'search_nmsp'); foreach (getNsLinks($row['page'], $keywords, $this->_sObj) as $i => $n) { if ($i > 0) $nmsp->appendChild($this->_dom->createTextNode(' : ')); $na = $this->_dom->createElement('a', hsc($n['title'])); $na->setAttribute('href', wl($n['link'])); $nmsp->appendChild($na); } $div->appendChild($nmsp); return $div; } private function _addNumberedPagination($parent, $query, $start, $total, $perPage) { $nav = $this->_dom->createElement('div'); $nav->setAttribute('class', 'sphinxsearch_pagination'); $totalPages = (int)ceil($total / $perPage); $currentPage = (int)floor($start / $perPage) + 1; $range = 4; $pageStart = max(1, $currentPage - $range); $pageEnd = min($totalPages, $currentPage + $range); if ($currentPage > 1) $this->_addPageLink($nav, $query, ($currentPage - 2) * $perPage, '« Prev', 'page_box prev'); for ($i = $pageStart; $i <= $pageEnd; $i++) { $class = ($i == $currentPage) ? 'page_box active' : 'page_box'; $this->_addPageLink($nav, $query, ($i - 1) * $perPage, $i, $class); } if ($currentPage < $totalPages) $this->_addPageLink($nav, $query, $currentPage * $perPage, 'Next »', 'page_box next'); $parent->appendChild($nav); } private function _addPageLink($parent, $query, $startValue, $label, $class) { $a = $this->_dom->createElement('a', $label); $a->setAttribute('href', wl('', ['do'=>'search', 'id'=>$query, 'start'=>$startValue])); $a->setAttribute('class', $class); $parent->appendChild($a); } private function _appendRawHTML($node, $html) { if (empty($html)) return; $tmp = new DOMDocument(); @$tmp->loadHTML('' . $html); $body = $tmp->getElementsByTagName('body')->item(0); if ($body) { foreach ($body->childNodes as $child) { if ($child->nodeName === 'meta') continue; $node->appendChild($this->_dom->importNode($child, true)); } } } private function _getCategories($q) { preg_match('/@ns\s+([^\s]+)/i', urldecode($q), $m); return $m[1] ?? ''; } private function _getKeywords($q) { return trim(preg_replace('/@ns\s+[^\s]+/i', '', urldecode($q))); } private function _getMatchingPagenames($kw, $cat) { $this->_sObj->setSearchOnlyPagename(); if (!$this->_sObj->search(0, 50)) return false; $m = []; foreach ($this->_sObj->getPagesIds() as $p) $m[$p['page']] = $p['hid']; return $m; } }