xref: /plugin/sphinxsearch-was/action.php (revision 134:2b68d17b7916)
1<?php
2/**
3 * Script to search in dokuwiki documents
4 *
5 * @author Ivinco <opensource@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{
22    var $_search = null;
23
24    var $_helpMessage = '';
25    var $_versionNumber = '0.3.11-was1';
26
27    /**
28     * return some info
29     */
30    function getInfo()
31    {
32        return confToHash(dirname(__FILE__) . '/plugin.info.txt');
33    }
34
35    function getHelpInfo()
36    {
37        $this->_helpMessage = "
38====== DokuWiki Sphinx Search plugin features ======
39
40To 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:
41
42===== Phrase search (\"\") =====
43Put double quotes around a set of words to enable phrase search mode. For example:
44<code>\"James Bond\"</code>
45
46===== Search within a namespace =====
47You can add \"@ns\" parameter to limit the search to some namespace. For exapmle:
48<code>hotel @ns personal:mike:travel</code>
49Such query will return only results from \"personal:mike:travel\" namespace for keyword \"hotel\".
50
51===== Excluding keywords or namespaces from search =====
52You can add a minus sign to a keyword or a category name exclude it from search. For example:
53<code>hotel @ns -personal:mike</code>
54Such query will look for \"hotel\" everywhere except the \"personal:mike\" namespace.
55<code>blog -post</code>
56Such query will look for documents that have keyword \"blog\" but don't have keyword \"post\".
57
58";
59        if ($this->getConf('showversion')) {
60            $this->_helpMessage .= "DokuWiki Sphinx Search plugin (version $this->_versionNumber) by [[http://www.ivinco.com/software/dokuwiki-sphinx-search-plugin/|Ivinco]].";
61        }
62        return $this->_helpMessage;
63    }
64
65    /**
66     * Register to the content display event to place the results under it.
67     */
68    /**
69     * register the eventhandlers
70     */
71    function register(Doku_Event_Handler $controller)
72    {
73        $controller->register_hook('TPL_CONTENT_DISPLAY',   'BEFORE', $this, 'handle_act_unknown', array());
74    }
75
76    /**
77     * If our own action was given we produce our content here
78     */
79    function handle_act_unknown(&$event, $param)
80    {
81        global $ACT;
82        global $QUERY;
83        if ($ACT != 'search') return; // nothing to do for us
84
85        // we can handle it -> prevent others
86        $event->stopPropagation();
87        $event->preventDefault();
88
89        /*        if (!extension_loaded('sqlite')) {
90            echo "SQLite extension is not loaded!";
91            return;
92        }*/
93
94        if (!empty($_REQUEST['ssplugininfo'])) {
95            $info = array();
96            echo p_render('xhtml', p_get_instructions($this->getHelpInfo()), $info);
97            return;
98        }
99
100        $this->_search($QUERY, $_REQUEST['start'], $_REQUEST['prev']);
101    }
102
103    /**
104     * do the search and displays the result
105     */
106    function _search($query, $start, $prev)
107    {
108        global $conf;
109
110        $start = (int)$start;
111        if ($start < 0) {
112            $start = 0;
113        }
114        if (empty($prev)) {
115            $prev = 0;
116        }
117
118        $categories = $this->_getCategories($query);
119        $keywords = $this->_getKeywords($query);
120
121        $search = new SphinxSearch($this->getConf('host'), $this->getConf('port'), $this->getConf('index'));
122        $search->setSnippetSize($this->getConf('snippetsize'));
123        $search->setArroundWordsCount($this->getConf('aroundwords'));
124        $search->setTitlePriority($this->getConf('title_priority'));
125        $search->setBodyPriority($this->getConf('body_priority'));
126        $search->setNamespacePriority($this->getConf('namespace_priority'));
127        $search->setPagenamePriority($this->getConf('pagename_priority'));
128
129        if (!empty($keywords) && empty($categories)) {
130            $search->setSearchAllQuery($keywords, $categories);
131        } elseif (!empty($keywords)) {
132            $search->setSearchAllQueryWithCategoryFilter($keywords, $categories);
133        } else {
134            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br>
135                <a href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>';
136            return;
137        }
138        $result = $search->search($start, $this->getConf('maxresults'));
139        $this->_search = $search;
140
141        if ($search->getError()) {
142            echo "Could not connect to Sphinx search engine.";
143            return;
144        }
145
146        if (!$result) {
147            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
148                <a href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>';
149            return;
150        }
151
152        $pagesList = $search->getPages($keywords);
153        $totalFound = $search->getTotalFound();
154
155        if (empty($pagesList) || 0 == $totalFound) {
156            echo 'Your search - <strong>' . $query . '</strong> - did not match any documents.<br/>
157                <a href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>';
158            return;
159        } else {
160            echo '<link rel="stylesheet" href="' . DOKU_URL . 'lib/plugins/sphinxsearch/css/style.css">';
161            echo '<script type="text/javascript" src="' . DOKU_URL . 'lib/plugins/sphinxsearch/js/search.js"></script>';
162            echo '<div class="search">';
163            echo '<div class="search_sidebar">';
164            echo '<h1>Pages Containing Search Terms</h1>';
165            printNamespacesNew($this->_getMatchingPagenames($keywords, $categories));
166            echo '</div>';
167            echo '<h1>Found ' . $totalFound . ($totalFound == 1  ? ' match ' : ' matches ') . ' for query "' . hsc($query) . '"</h1>';
168            echo '<div class="search_result">';
169            // printout the results
170            $pageListGroupByPage = array();
171            foreach ($pagesList as $row) {
172                $page = $row['page'];
173                if (!isset($pageListGroupByPage[$page])) {
174                    $pageListGroupByPage[$page] = $row;
175                } else {
176                    $pageListGroupByPage[$page]['subpages'][] = $row;
177                }
178            }
179            foreach ($pageListGroupByPage as $row) {
180                $this->_showResult($row, $keywords, false);
181                if (!empty($row['subpages'])) {
182                    echo '<div id="more' . $row['page'] . '" class="hide">';
183                    foreach ($row['subpages'] as $sub) {
184                        $this->_showResult($sub, $keywords, true);
185                    }
186                    echo '</div>';
187                }
188            }
189            echo '</div>';
190            echo '<div class="sphinxsearch_nav">';
191            echo '<a class="searchhelp" href="?do=search&ssplugininfo=1&id=' . $query . '">Search help</a>';
192            if ($start > 1) {
193                if (false !== strpos($prev, ',')) {
194                    $prevArry = explode(",", $prev);
195                    $prevNum = $prevArry[count($prevArry) - 1];
196                    unset($prevArry[count($prevArry) - 1]);
197                    $prevString = implode(",", $prevArry);
198                } else {
199                    $prevNum = 0;
200                }
201
202                echo $this->external_link(
203                    wl('', array('do' => 'search', 'id' => $query, 'start' => $prevNum, 'prev' => $prevString), 'false', '&'),
204                    'prev',
205                    'wikilink1 gs_prev',
206                    $conf['target']['interwiki']
207                );
208            }
209
210            echo ' ';
211
212            //if($start + $this->getConf('maxresults') < $totalFound){
213            //$next = $start + $this->getConf('maxresults');
214            if ($start + $search->getOffset() < $totalFound) {
215                $next = $start + $search->getOffset();
216                if ($start > 1) {
217                    $prevString = $prev . ',' . $start;
218                }
219                echo $this->external_link(
220                    wl('', array('do' => 'search', 'id' => $query, 'start' => $next, 'prev' => $prevString), 'false', '&'),
221                    'next',
222                    'wikilink1 gs_next',
223                    $conf['target']['interwiki']
224                );
225            }
226            echo '</div>';
227            if ($this->getConf('showversion')) {
228                echo 'DokuWiki Sphinx Search plugin (version ' . $this->_versionNumber . ') by <a href="http://www.ivinco.com/software/dokuwiki-sphinx-search-plugin/">Ivinco</a>.';
229            }
230            echo '</div>';
231        }
232    }
233
234    function _showResult($row, $keywords, $subpages = false)
235    {
236        $page = $row['page'];
237        $bodyExcerpt = $row['bodyExcerpt'];
238        $titleTextExcerpt = $row['titleTextExcerpt'];
239        $hid = $row['hid'];
240
241        $metaData = p_get_metadata($page);
242
243        if (!empty($titleTextExcerpt)) {
244            $titleText = $titleTextExcerpt;
245        } elseif (!empty($row['title_text'])) {
246            $titleText = $row['title_text'];
247        } elseif (!empty($metaData['title'])) {
248            $titleText = hsc($metaData['title']);
249        } else {
250            $titleText = hsc($page);
251        }
252
253        $namespaces = getNsLinks($page, $keywords, $this->_search);
254        $href = !empty($hid) ? (wl($page) . '#' . $hid) : wl($page);
255
256        if ($subpages) {
257            echo '<div class="search_result_row_child">';
258        } else {
259            echo '<div class="search_result_row">';
260        }
261
262        echo '<a class="wikilink1 title" href="' . $href . '" title="" >' . $titleText . '</a><br/>';
263        echo '<div class="search_snippet">';
264        echo strip_tags($bodyExcerpt, '<b>,<strong>');
265        echo '</div>';
266        $sep = ':';
267        $i = 0;
268        echo '<span class="search_nmsp">';
269        foreach ($namespaces as $name) {
270            $link = $name['link'];
271            $pageTitle = $name['title'];
272            tpl_link($link, $pageTitle);
273            if ($i++ < count($namespaces) - 1) {
274                echo $sep;
275            }
276        }
277        if (!empty($hid)) {
278            echo '#' . $hid;
279        }
280        echo '</span>';
281
282        if (!empty($metaData['last_change']['date'])) {
283            echo '<span class="search_cnt"> - Last modified ' . date("Y-m-d H:i", $metaData['last_change']['date']) . '</span> ';
284        } else if (!empty($metaData['date']['created'])) {
285            echo '<span class="search_cnt"> - Last modified ' . date("Y-m-d H:i", $metaData['date']['created']) . '</span> ';
286        }
287
288        if (!empty($metaData['last_change']['user'])) {
289            echo '<span class="search_cnt">by ' . $metaData['last_change']['user'] . '</span> ';
290        } else if (!empty($metaData['creator'])) {
291            echo '<span class="search_cnt">by ' . $metaData['creator'] . '</span> ';
292        }
293
294        if (!empty($row['subpages'])) {
295            echo '<br />';
296            echo '<div class="morematches"><a href="javascript:void(0)" onClick="sh(' . "'more" . $page . "'" . ');" >Show more matches from this document</a></div>';
297        } else {
298            echo '<br />';
299        }
300        echo '<br />';
301        echo '</div>';
302    }
303
304    function searchform()
305    {
306        global $lang;
307        global $ACT;
308        global $QUERY;
309
310        // don't print the search form if search action has been disabled
311        if (!actionOk('search')) return false;
312
313        print '<form action="' . wl() . '" accept-charset="utf-8" class="search" id="dw__search"><div class="no">';
314        print '<input type="hidden" name="do" value="search" />';
315        print '<input type="text" ';
316        if ($ACT == 'search') print 'value="' . htmlspecialchars($QUERY) . '" ';
317        print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[ALT+F]" />';
318        print '<input type="submit" value="' . $lang['btn_search'] . '" class="button" title="' . $lang['btn_search'] . '" />';
319        print '</div></form>';
320        return true;
321    }
322
323    function _getCategories($query)
324    {
325        $categories = '';
326        $query = urldecode($query);
327        if (false !== ($pos = strpos($query, "@ns"))) {;
328            $categories = substr($query, $pos + strlen("@ns"));
329        }
330        return trim($categories);
331    }
332
333    function _getKeywords($query)
334    {
335        $keywords = $query;
336        $query = urldecode($query);
337        if (false !== ($pos = strpos($query, "-@ns"))) {;
338            $keywords = substr($keywords, 0, $pos);
339        } else if (false !== ($pos = strpos($query, "@ns"))) {;
340            $keywords = substr($keywords, 0, $pos);
341        }
342        return trim($keywords);
343    }
344
345    function _getMatchingPagenames($keywords, $categories)
346    {
347        //$this->_search->setSearchCategoryQuery($keywords, $categories);
348        $this->_search->setSearchOnlyPagename();
349        $this->_search->setNamespacePriority($this->getConf('mp_namespace_priority'));
350        $this->_search->setPagenamePriority($this->getConf('mp_pagename_priority'));
351
352        $res = $this->_search->search(0, 10);
353        if (!$res) {
354            return false;
355        }
356
357        return $this->_search->getPageNames();
358    }
359}
360