10Syaroslav@ivinco.com<?php 20Syaroslav@ivinco.com/** 312Syaroslav@ivinco.com * Script to search in dokuwiki documents 40Syaroslav@ivinco.com * 5118Syaroslav * @author Ivinco <opensource@ivinco.com> 60Syaroslav@ivinco.com */ 70Syaroslav@ivinco.com 80Syaroslav@ivinco.comif (!defined('DOKU_INC')) die(); 90Syaroslav@ivinco.comif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 100Syaroslav@ivinco.com 116Syaroslav@ivinco.comrequire_once(DOKU_INC . 'inc/parser/parser.php'); 126Syaroslav@ivinco.com 130Syaroslav@ivinco.comrequire_once(DOKU_PLUGIN . 'action.php'); 140Syaroslav@ivinco.comrequire_once(DOKU_PLUGIN . 'sphinxsearch/sphinxapi.php'); 150Syaroslav@ivinco.comrequire_once(DOKU_PLUGIN . 'sphinxsearch/PageMapper.php'); 160Syaroslav@ivinco.comrequire_once(DOKU_PLUGIN . 'sphinxsearch/SphinxSearch.php'); 176Syaroslav@ivinco.comrequire_once(DOKU_PLUGIN . 'sphinxsearch/functions.php'); 180Syaroslav@ivinco.com 190Syaroslav@ivinco.com 200Syaroslav@ivinco.comclass action_plugin_sphinxsearch extends DokuWiki_Action_Plugin 2143Syaroslav@ivinco.com{ 220Syaroslav@ivinco.com var $_search = null; 23110Syaroslav 24110Syaroslav var $_helpMessage = ''; 25110Syaroslav var $_versionNumber = '0.3.11-was1'; 26110Syaroslav 27110Syaroslav /** 28110Syaroslav * return some info 29110Syaroslav */ 30110Syaroslav function getInfo() 31110Syaroslav { 32110Syaroslav return confToHash(dirname(__FILE__) . '/plugin.info.txt'); 33110Syaroslav } 34110Syaroslav 3582Syaroslav@ivinco.com function getHelpInfo() 3682Syaroslav@ivinco.com { 3785Syaroslav@ivinco.com $this->_helpMessage = " 3882Syaroslav@ivinco.com====== DokuWiki Sphinx Search plugin features ====== 3982Syaroslav@ivinco.com 4082Syaroslav@ivinco.comTo 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: 4182Syaroslav@ivinco.com 4282Syaroslav@ivinco.com===== Phrase search (\"\") ===== 4382Syaroslav@ivinco.comPut double quotes around a set of words to enable phrase search mode. For example: 4482Syaroslav@ivinco.com<code>\"James Bond\"</code> 4582Syaroslav@ivinco.com 4682Syaroslav@ivinco.com===== Search within a namespace ===== 4782Syaroslav@ivinco.comYou can add \"@ns\" parameter to limit the search to some namespace. For exapmle: 4882Syaroslav@ivinco.com<code>hotel @ns personal:mike:travel</code> 4982Syaroslav@ivinco.comSuch query will return only results from \"personal:mike:travel\" namespace for keyword \"hotel\". 5082Syaroslav@ivinco.com 5182Syaroslav@ivinco.com===== Excluding keywords or namespaces from search ===== 5282Syaroslav@ivinco.comYou can add a minus sign to a keyword or a category name exclude it from search. For example: 5382Syaroslav@ivinco.com<code>hotel @ns -personal:mike</code> 54115SyaroslavSuch query will look for \"hotel\" everywhere except the \"personal:mike\" namespace. 55116Syaroslav<code>blog -post</code> 56117SyaroslavSuch query will look for documents that have keyword \"blog\" but don't have keyword \"post\". 57115Syaroslav 5882Syaroslav@ivinco.com"; 59111Syaroslav@ivinco.com if ($this->getConf('showversion')) { 60110Syaroslav $this->_helpMessage .= "DokuWiki Sphinx Search plugin (version $this->_versionNumber) by [[http://www.ivinco.com/software/dokuwiki-sphinx-search-plugin/|Ivinco]]."; 610Syaroslav@ivinco.com } 620Syaroslav@ivinco.com return $this->_helpMessage; 630Syaroslav@ivinco.com } 640Syaroslav@ivinco.com 650Syaroslav@ivinco.com /** 660Syaroslav@ivinco.com * Register to the content display event to place the results under it. 670Syaroslav@ivinco.com */ 680Syaroslav@ivinco.com /** 69*134Sandrey * register the eventhandlers 700Syaroslav@ivinco.com */ 710Syaroslav@ivinco.com function register(Doku_Event_Handler $controller) 720Syaroslav@ivinco.com { 7397Syaroslav@ivinco.com $controller->register_hook('TPL_CONTENT_DISPLAY', 'BEFORE', $this, 'handle_act_unknown', array()); 740Syaroslav@ivinco.com } 750Syaroslav@ivinco.com 7627Syaroslav@ivinco.com /** 7727Syaroslav@ivinco.com * If our own action was given we produce our content here 7827Syaroslav@ivinco.com */ 790Syaroslav@ivinco.com function handle_act_unknown(&$event, $param) 800Syaroslav@ivinco.com { 810Syaroslav@ivinco.com global $ACT; 820Syaroslav@ivinco.com global $QUERY; 830Syaroslav@ivinco.com if ($ACT != 'search') return; // nothing to do for us 8491Syaroslav@ivinco.com 8587Syaroslav@ivinco.com // we can handle it -> prevent others 8687Syaroslav@ivinco.com $event->stopPropagation(); 8791Syaroslav@ivinco.com $event->preventDefault(); 8887Syaroslav@ivinco.com 8982Syaroslav@ivinco.com /* if (!extension_loaded('sqlite')) { 9082Syaroslav@ivinco.com echo "SQLite extension is not loaded!"; 91111Syaroslav@ivinco.com return; 9282Syaroslav@ivinco.com }*/ 9382Syaroslav@ivinco.com 94*134Sandrey if (!empty($_REQUEST['ssplugininfo'])) { 9543Syaroslav@ivinco.com $info = array(); 96*134Sandrey echo p_render('xhtml', p_get_instructions($this->getHelpInfo()), $info); 970Syaroslav@ivinco.com return; 980Syaroslav@ivinco.com } 990Syaroslav@ivinco.com 1000Syaroslav@ivinco.com $this->_search($QUERY, $_REQUEST['start'], $_REQUEST['prev']); 10143Syaroslav@ivinco.com } 102*134Sandrey 1030Syaroslav@ivinco.com /** 1040Syaroslav@ivinco.com * do the search and displays the result 10544Syaroslav@ivinco.com */ 10644Syaroslav@ivinco.com function _search($query, $start, $prev) 10744Syaroslav@ivinco.com { 10844Syaroslav@ivinco.com global $conf; 10944Syaroslav@ivinco.com 11044Syaroslav@ivinco.com $start = (int)$start; 111*134Sandrey if ($start < 0) { 11263Syaroslav@ivinco.com $start = 0; 11367Syaroslav@ivinco.com } 1140Syaroslav@ivinco.com if (empty($prev)) { 1150Syaroslav@ivinco.com $prev = 0; 11616Syaroslav@ivinco.com } 11731Syaroslav@ivinco.com 11819Syaroslav@ivinco.com $categories = $this->_getCategories($query); 11919Syaroslav@ivinco.com $keywords = $this->_getKeywords($query); 12080Syaroslav@ivinco.com 12180Syaroslav@ivinco.com $search = new SphinxSearch($this->getConf('host'), $this->getConf('port'), $this->getConf('index')); 12269Syaroslav@ivinco.com $search->setSnippetSize($this->getConf('snippetsize')); 12369Syaroslav@ivinco.com $search->setArroundWordsCount($this->getConf('aroundwords')); 12469Syaroslav@ivinco.com $search->setTitlePriority($this->getConf('title_priority')); 12579Syaroslav@ivinco.com $search->setBodyPriority($this->getConf('body_priority')); 12679Syaroslav@ivinco.com $search->setNamespacePriority($this->getConf('namespace_priority')); 12769Syaroslav@ivinco.com $search->setPagenamePriority($this->getConf('pagename_priority')); 12882Syaroslav@ivinco.com 12982Syaroslav@ivinco.com if (!empty($keywords) && empty($categories)) { 13079Syaroslav@ivinco.com $search->setSearchAllQuery($keywords, $categories); 13169Syaroslav@ivinco.com } elseif (!empty($keywords)) { 13269Syaroslav@ivinco.com $search->setSearchAllQueryWithCategoryFilter($keywords, $categories); 13343Syaroslav@ivinco.com } else { 13428Syaroslav@ivinco.com echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br> 13588Syaroslav@ivinco.com <a href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>'; 13689Syaroslav@ivinco.com return; 13728Syaroslav@ivinco.com } 138*134Sandrey $result = $search->search($start, $this->getConf('maxresults')); 13969Syaroslav@ivinco.com $this->_search = $search; 14093Syaroslav@ivinco.com 14193Syaroslav@ivinco.com if ($search->getError()) { 14293Syaroslav@ivinco.com echo "Could not connect to Sphinx search engine."; 14393Syaroslav@ivinco.com return; 14493Syaroslav@ivinco.com } 14593Syaroslav@ivinco.com 14677Syaroslav@ivinco.com if (!$result) { 147133Sandrey echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/> 148*134Sandrey <a href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>'; 14993Syaroslav@ivinco.com return; 15082Syaroslav@ivinco.com } 15182Syaroslav@ivinco.com 15227Syaroslav@ivinco.com $pagesList = $search->getPages($keywords); 15327Syaroslav@ivinco.com $totalFound = $search->getTotalFound(); 15427Syaroslav@ivinco.com 155133Sandrey if (empty($pagesList) || 0 == $totalFound) { 15651Syaroslav@ivinco.com echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/> 15751Syaroslav@ivinco.com <a href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>'; 15827Syaroslav@ivinco.com return; 15927Syaroslav@ivinco.com } else { 16027Syaroslav@ivinco.com echo '<link rel="stylesheet" href="' . DOKU_URL . 'lib/plugins/sphinxsearch/css/style.css">'; 16139Syaroslav@ivinco.com echo '<script type="text/javascript" src="' . DOKU_URL . 'lib/plugins/sphinxsearch/js/search.js"></script>'; 16239Syaroslav@ivinco.com echo '<div class="search">'; 16339Syaroslav@ivinco.com echo '<div class="search_sidebar">'; 16451Syaroslav@ivinco.com echo '<h1>Pages Containing Search Terms</h1>'; 16539Syaroslav@ivinco.com printNamespacesNew($this->_getMatchingPagenames($keywords, $categories)); 16639Syaroslav@ivinco.com echo '</div>'; 16750Syaroslav@ivinco.com echo '<h1>Found ' . $totalFound . ($totalFound == 1 ? ' match ' : ' matches ') . ' for query "' . hsc($query) . '"</h1>'; 16850Syaroslav@ivinco.com echo '<div class="search_result">'; 16950Syaroslav@ivinco.com // printout the results 17050Syaroslav@ivinco.com $pageListGroupByPage = array(); 17151Syaroslav@ivinco.com foreach ($pagesList as $row) { 17250Syaroslav@ivinco.com $page = $row['page']; 17339Syaroslav@ivinco.com if (!isset($pageListGroupByPage[$page])) { 17450Syaroslav@ivinco.com $pageListGroupByPage[$page] = $row; 17539Syaroslav@ivinco.com } else { 17639Syaroslav@ivinco.com $pageListGroupByPage[$page]['subpages'][] = $row; 17739Syaroslav@ivinco.com } 17839Syaroslav@ivinco.com } 17939Syaroslav@ivinco.com foreach ($pageListGroupByPage as $row) { 18039Syaroslav@ivinco.com $this->_showResult($row, $keywords, false); 18150Syaroslav@ivinco.com if (!empty($row['subpages'])) { 18250Syaroslav@ivinco.com echo '<div id="more' . $row['page'] . '" class="hide">'; 18339Syaroslav@ivinco.com foreach ($row['subpages'] as $sub) { 18439Syaroslav@ivinco.com $this->_showResult($sub, $keywords, true); 18539Syaroslav@ivinco.com } 18639Syaroslav@ivinco.com echo '</div>'; 18750Syaroslav@ivinco.com } 18850Syaroslav@ivinco.com } 18950Syaroslav@ivinco.com echo '</div>'; 19050Syaroslav@ivinco.com echo '<div class="sphinxsearch_nav">'; 19150Syaroslav@ivinco.com echo '<a class="searchhelp" href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>'; 19227Syaroslav@ivinco.com if ($start > 1) { 19327Syaroslav@ivinco.com if (false !== strpos($prev, ',')) { 19450Syaroslav@ivinco.com $prevArry = explode(",", $prev); 19551Syaroslav@ivinco.com $prevNum = $prevArry[count($prevArry) - 1]; 19627Syaroslav@ivinco.com unset($prevArry[count($prevArry) - 1]); 19727Syaroslav@ivinco.com $prevString = implode(",", $prevArry); 19827Syaroslav@ivinco.com } else { 19927Syaroslav@ivinco.com $prevNum = 0; 20039Syaroslav@ivinco.com } 20139Syaroslav@ivinco.com 20239Syaroslav@ivinco.com echo $this->external_link( 20327Syaroslav@ivinco.com wl('', array('do' => 'search', 'id' => $query, 'start' => $prevNum, 'prev' => $prevString), 'false', '&'), 20450Syaroslav@ivinco.com 'prev', 20550Syaroslav@ivinco.com 'wikilink1 gs_prev', 20650Syaroslav@ivinco.com $conf['target']['interwiki'] 20750Syaroslav@ivinco.com ); 20850Syaroslav@ivinco.com } 20950Syaroslav@ivinco.com 21050Syaroslav@ivinco.com echo ' '; 21150Syaroslav@ivinco.com 21250Syaroslav@ivinco.com //if($start + $this->getConf('maxresults') < $totalFound){ 213*134Sandrey //$next = $start + $this->getConf('maxresults'); 21450Syaroslav@ivinco.com if ($start + $search->getOffset() < $totalFound) { 21527Syaroslav@ivinco.com $next = $start + $search->getOffset(); 21652Syaroslav@ivinco.com if ($start > 1) { 21751Syaroslav@ivinco.com $prevString = $prev . ',' . $start; 21827Syaroslav@ivinco.com } 21927Syaroslav@ivinco.com echo $this->external_link( 22043Syaroslav@ivinco.com wl('', array('do' => 'search', 'id' => $query, 'start' => $next, 'prev' => $prevString), 'false', '&'), 22143Syaroslav@ivinco.com 'next', 22227Syaroslav@ivinco.com 'wikilink1 gs_next', 22343Syaroslav@ivinco.com $conf['target']['interwiki'] 22443Syaroslav@ivinco.com ); 22527Syaroslav@ivinco.com } 22643Syaroslav@ivinco.com echo '</div>'; 22727Syaroslav@ivinco.com if ($this->getConf('showversion')) { 22843Syaroslav@ivinco.com echo 'DokuWiki Sphinx Search plugin (version ' . $this->_versionNumber . ') by <a href="http://www.ivinco.com/software/dokuwiki-sphinx-search-plugin/">Ivinco</a>.'; 22943Syaroslav@ivinco.com } 23050Syaroslav@ivinco.com echo '</div>'; 23143Syaroslav@ivinco.com } 23250Syaroslav@ivinco.com } 23343Syaroslav@ivinco.com 23449Syaroslav@ivinco.com function _showResult($row, $keywords, $subpages = false) 23527Syaroslav@ivinco.com { 23650Syaroslav@ivinco.com $page = $row['page']; 23727Syaroslav@ivinco.com $bodyExcerpt = $row['bodyExcerpt']; 238*134Sandrey $titleTextExcerpt = $row['titleTextExcerpt']; 2394Syaroslav@ivinco.com $hid = $row['hid']; 24027Syaroslav@ivinco.com 24153Syaroslav@ivinco.com $metaData = p_get_metadata($page); 24275Syaroslav@ivinco.com 24339Syaroslav@ivinco.com if (!empty($titleTextExcerpt)) { 24427Syaroslav@ivinco.com $titleText = $titleTextExcerpt; 24543Syaroslav@ivinco.com } elseif (!empty($row['title_text'])) { 24643Syaroslav@ivinco.com $titleText = $row['title_text']; 24746Syaroslav@ivinco.com } elseif (!empty($metaData['title'])) { 24846Syaroslav@ivinco.com $titleText = hsc($metaData['title']); 24946Syaroslav@ivinco.com } else { 25046Syaroslav@ivinco.com $titleText = hsc($page); 25143Syaroslav@ivinco.com } 25243Syaroslav@ivinco.com 25343Syaroslav@ivinco.com $namespaces = getNsLinks($page, $keywords, $this->_search); 2544Syaroslav@ivinco.com $href = !empty($hid) ? (wl($page) . '#' . $hid) : wl($page); 25546Syaroslav@ivinco.com 256*134Sandrey if ($subpages) { 25718Syaroslav@ivinco.com echo '<div class="search_result_row_child">'; 25827Syaroslav@ivinco.com } else { 259*134Sandrey echo '<div class="search_result_row">'; 26039Syaroslav@ivinco.com } 26139Syaroslav@ivinco.com 26243Syaroslav@ivinco.com echo '<a class="wikilink1 title" href="' . $href . '" title="" >' . $titleText . '</a><br/>'; 26339Syaroslav@ivinco.com echo '<div class="search_snippet">'; 26445Syaroslav@ivinco.com echo strip_tags($bodyExcerpt, '<b>,<strong>'); 26546Syaroslav@ivinco.com echo '</div>'; 26645Syaroslav@ivinco.com $sep = ':'; 26746Syaroslav@ivinco.com $i = 0; 26827Syaroslav@ivinco.com echo '<span class="search_nmsp">'; 26927Syaroslav@ivinco.com foreach ($namespaces as $name) { 27027Syaroslav@ivinco.com $link = $name['link']; 271111Syaroslav@ivinco.com $pageTitle = $name['title']; 272111Syaroslav@ivinco.com tpl_link($link, $pageTitle); 27351Syaroslav@ivinco.com if ($i++ < count($namespaces) - 1) { 2740Syaroslav@ivinco.com echo $sep; 27551Syaroslav@ivinco.com } 276*134Sandrey } 2770Syaroslav@ivinco.com if (!empty($hid)) { 2780Syaroslav@ivinco.com echo '#' . $hid; 27949Syaroslav@ivinco.com } 28043Syaroslav@ivinco.com echo '</span>'; 28143Syaroslav@ivinco.com 28243Syaroslav@ivinco.com if (!empty($metaData['last_change']['date'])) { 28343Syaroslav@ivinco.com echo '<span class="search_cnt"> - Last modified ' . date("Y-m-d H:i", $metaData['last_change']['date']) . '</span> '; 28443Syaroslav@ivinco.com } else if (!empty($metaData['date']['created'])) { 28543Syaroslav@ivinco.com echo '<span class="search_cnt"> - Last modified ' . date("Y-m-d H:i", $metaData['date']['created']) . '</span> '; 28643Syaroslav@ivinco.com } 28743Syaroslav@ivinco.com 28843Syaroslav@ivinco.com if (!empty($metaData['last_change']['user'])) { 28943Syaroslav@ivinco.com echo '<span class="search_cnt">by ' . $metaData['last_change']['user'] . '</span> '; 29043Syaroslav@ivinco.com } else if (!empty($metaData['creator'])) { 29143Syaroslav@ivinco.com echo '<span class="search_cnt">by ' . $metaData['creator'] . '</span> '; 29243Syaroslav@ivinco.com } 29343Syaroslav@ivinco.com 29443Syaroslav@ivinco.com if (!empty($row['subpages'])) { 29543Syaroslav@ivinco.com echo '<br />'; 29643Syaroslav@ivinco.com echo '<div class="morematches"><a href="javascript:void(0)" onClick="sh(' . "'more" . $page . "'" . ');" >Show more matches from this document</a></div>'; 29743Syaroslav@ivinco.com } else { 29843Syaroslav@ivinco.com echo '<br />'; 29943Syaroslav@ivinco.com } 30043Syaroslav@ivinco.com echo '<br />'; 30146Syaroslav@ivinco.com echo '</div>'; 30246Syaroslav@ivinco.com } 30343Syaroslav@ivinco.com 30446Syaroslav@ivinco.com function searchform() 30543Syaroslav@ivinco.com { 30643Syaroslav@ivinco.com global $lang; 30750Syaroslav@ivinco.com global $ACT; 30843Syaroslav@ivinco.com global $QUERY; 30943Syaroslav@ivinco.com 31043Syaroslav@ivinco.com // don't print the search form if search action has been disabled 31143Syaroslav@ivinco.com if (!actionOk('search')) return false; 31243Syaroslav@ivinco.com 31343Syaroslav@ivinco.com print '<form action="' . wl() . '" accept-charset="utf-8" class="search" id="dw__search"><div class="no">'; 31443Syaroslav@ivinco.com print '<input type="hidden" name="do" value="search" />'; 31543Syaroslav@ivinco.com print '<input type="text" '; 31643Syaroslav@ivinco.com if ($ACT == 'search') print 'value="' . htmlspecialchars($QUERY) . '" '; 31751Syaroslav@ivinco.com print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[ALT+F]" />'; 31843Syaroslav@ivinco.com print '<input type="submit" value="' . $lang['btn_search'] . '" class="button" title="' . $lang['btn_search'] . '" />'; 31943Syaroslav@ivinco.com print '</div></form>'; 32043Syaroslav@ivinco.com return true; 32143Syaroslav@ivinco.com } 32243Syaroslav@ivinco.com 32343Syaroslav@ivinco.com function _getCategories($query) 32443Syaroslav@ivinco.com { 32543Syaroslav@ivinco.com $categories = ''; 32657Syaroslav@ivinco.com $query = urldecode($query); 32757Syaroslav@ivinco.com if (false !== ($pos = strpos($query, "@ns"))) {; 32857Syaroslav@ivinco.com $categories = substr($query, $pos + strlen("@ns")); 32957Syaroslav@ivinco.com } 33057Syaroslav@ivinco.com return trim($categories); 33157Syaroslav@ivinco.com } 33257Syaroslav@ivinco.com 33357Syaroslav@ivinco.com function _getKeywords($query) 33457Syaroslav@ivinco.com { 33557Syaroslav@ivinco.com $keywords = $query; 33657Syaroslav@ivinco.com $query = urldecode($query); 33757Syaroslav@ivinco.com if (false !== ($pos = strpos($query, "-@ns"))) {; 338*134Sandrey $keywords = substr($keywords, 0, $pos); 33950Syaroslav@ivinco.com } else if (false !== ($pos = strpos($query, "@ns"))) {; 34050Syaroslav@ivinco.com $keywords = substr($keywords, 0, $pos); 34151Syaroslav@ivinco.com } 34250Syaroslav@ivinco.com return trim($keywords); 34350Syaroslav@ivinco.com } 34450Syaroslav@ivinco.com 34543Syaroslav@ivinco.com function _getMatchingPagenames($keywords, $categories) 34643Syaroslav@ivinco.com { 34743Syaroslav@ivinco.com //$this->_search->setSearchCategoryQuery($keywords, $categories); 34843Syaroslav@ivinco.com $this->_search->setSearchOnlyPagename(); 34982Syaroslav@ivinco.com $this->_search->setNamespacePriority($this->getConf('mp_namespace_priority')); 35082Syaroslav@ivinco.com $this->_search->setPagenamePriority($this->getConf('mp_pagename_priority')); 3510Syaroslav@ivinco.com 3520Syaroslav@ivinco.com $res = $this->_search->search(0, 10); 3530Syaroslav@ivinco.com if (!$res) { 3540Syaroslav@ivinco.com return false; 3550Syaroslav@ivinco.com } 35627Syaroslav@ivinco.com 3570Syaroslav@ivinco.com return $this->_search->getPageNames(); 3580Syaroslav@ivinco.com } 35927Syaroslav@ivinco.com} 3600Syaroslav@ivinco.com