xref: /dokuwiki/inc/Action/Search.php (revision 8788dbbd585b42284320d64cc932f3c875eab6b2)
1<?php
2
3namespace dokuwiki\Action;
4
5use dokuwiki\Action\Exception\ActionAbort;
6use dokuwiki\Search\FulltextSearch;
7use dokuwiki\Search\MetadataSearch;
8use dokuwiki\Search\Query\QueryParser;
9
10/**
11 * Class Search
12 *
13 * Search for pages and content
14 *
15 * @package dokuwiki\Action
16 */
17class Search extends AbstractAction
18{
19    protected $pageLookupResults = [];
20    protected $fullTextResults = [];
21    protected $highlight = [];
22
23    /** @inheritdoc */
24    public function minimumPermission()
25    {
26        return AUTH_NONE;
27    }
28
29    /**
30     * we only search if a search word was given
31     *
32     * @inheritdoc
33     */
34    public function checkPreconditions()
35    {
36        parent::checkPreconditions();
37    }
38
39    public function preProcess()
40    {
41        global $QUERY, $ID, $conf, $INPUT;
42        $s = cleanID($QUERY);
43
44        if ($ID !== $conf['start'] && !$INPUT->has('q')) {
45            parse_str($INPUT->server->str('QUERY_STRING'), $urlParts);
46            $urlParts['q'] = $urlParts['id'];
47            unset($urlParts['id']);
48            $url = wl($ID, $urlParts, true, '&');
49            send_redirect($url);
50        }
51
52        if ($s === '') throw new ActionAbort();
53        $this->adjustGlobalQuery();
54    }
55
56    /** @inheritdoc */
57    public function tplContent()
58    {
59        $this->execute();
60
61        $search = new \dokuwiki\Ui\Search($this->pageLookupResults, $this->fullTextResults, $this->highlight);
62        $search->show();
63    }
64
65
66    /**
67     * run the search
68     */
69    protected function execute()
70    {
71        global $INPUT, $QUERY;
72        $after = $INPUT->str('min');
73        $before = $INPUT->str('max');
74        $this->pageLookupResults = (new MetadataSearch())->pageLookup(
75            $QUERY,
76            true,
77            useHeading('navigation'),
78            $after,
79            $before
80        );
81        $highlight = [];
82        $this->fullTextResults = (new FulltextSearch())->pageSearch(
83            $QUERY,
84            $highlight,
85            $INPUT->str('srt'),
86            $after,
87            $before
88        );
89        $this->highlight = $highlight;
90    }
91
92    /**
93     * Adjust the global query accordingly to the config search_nslimit and search_fragment
94     *
95     * This will only do something if the search didn't originate from the form on the searchpage itself
96     */
97    protected function adjustGlobalQuery()
98    {
99        global $conf, $INPUT, $QUERY, $ID;
100
101        if ($INPUT->bool('sf')) {
102            return;
103        }
104
105        $parsedQuery = (new QueryParser())->convert($QUERY);
106
107        if (empty($parsedQuery['ns']) && empty($parsedQuery['notns'])) {
108            if ($conf['search_nslimit'] > 0) {
109                if (getNS($ID) !== false) {
110                    $nsParts = explode(':', getNS($ID));
111                    $ns = implode(':', array_slice($nsParts, 0, $conf['search_nslimit']));
112                    $QUERY .= " @$ns";
113                }
114            }
115        }
116
117        if ($conf['search_fragment'] !== 'exact') {
118            if (empty(array_diff($parsedQuery['words'], $parsedQuery['and']))) {
119                if (!str_contains($QUERY, '*')) {
120                    $queryParts = explode(' ', $QUERY);
121                    $queryParts = array_map(function ($part) {
122                        if (str_starts_with($part, '@')) {
123                            return $part;
124                        }
125                        if (str_starts_with($part, 'ns:')) {
126                            return $part;
127                        }
128                        if (str_starts_with($part, '^')) {
129                            return $part;
130                        }
131                        if (str_starts_with($part, '-ns:')) {
132                            return $part;
133                        }
134
135                        global $conf;
136
137                        if ($conf['search_fragment'] === 'starts_with') {
138                            return $part . '*';
139                        }
140                        if ($conf['search_fragment'] === 'ends_with') {
141                            return '*' . $part;
142                        }
143
144                        return '*' . $part . '*';
145                    }, $queryParts);
146                    $QUERY = implode(' ', $queryParts);
147                }
148            }
149        }
150    }
151}
152