Failed Conditions
Pull Request — master (#2943)
by
unknown
03:12
created

MetadataSearch::pageLookup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 5
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
namespace dokuwiki\Search;
4
5
use dokuwiki\Extension\Event;
6
use dokuwiki\Search\MetadataIndex;
7
use dokuwiki\Search\QueryParser;
8
9
/**
10
 * Class DokuWiki Metadata Search
11
 *
12
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
13
 * @author     Andreas Gohr <[email protected]>
14
 */
15
class MetadataSearch
16
{
17
    /**
18
     * Quicksearch for pagenames
19
     *
20
     * By default it only matches the pagename and ignores the namespace.
21
     * This can be changed with the second parameter.
22
     * The third parameter allows to search in titles as well.
23
     *
24
     * The function always returns titles as well
25
     *
26
     * @triggers SEARCH_QUERY_PAGELOOKUP
27
     * @author   Andreas Gohr <[email protected]>
28
     * @author   Adrian Lang <[email protected]>
29
     *
30
     * @param string     $id       page id
31
     * @param bool       $in_ns    match against namespace as well?
32
     * @param bool       $in_title search in title?
33
     * @param int|string $after    only show results with mtime after this date,
34
     *                             accepts timestap or strtotime arguments
35
     * @param int|string $before   only show results with mtime before this date,
36
     *                             accepts timestap or strtotime arguments
37
     *
38
     * @return string[]
39
     */
40
    public function pageLookup($id, $in_ns = false, $in_title = false, $after = null, $before = null)
41
    {
42
        $data = [
43
            'id' => $id,
44
            'in_ns' => $in_ns,
45
            'in_title' => $in_title,
46
            'after' => $after,
47
            'before' => $before
48
        ];
49
        $data['has_titles'] = true; // for plugin backward compatibility check
50
        $action = [$this, 'pageLookupCallBack'];
51
        return Event::createAndTrigger('SEARCH_QUERY_PAGELOOKUP', $data, $action);
52
    }
53
54
    /**
55
     * Returns list of pages as array(pageid => First Heading)
56
     *
57
     * @param array $data  event data
58
     * @return string[]
59
     */
60
    public function pageLookupCallBack(&$data)
61
    {
62
        // split out original parameters
63
        $id = $data['id'];
64
        $parsedQuery = (new QueryParser)->convert($id);
65
66
        if (count($parsedQuery['ns']) > 0) {
67
            $ns = cleanID($parsedQuery['ns'][0]) . ':';
68
            $id = implode(' ', $parsedQuery['highlight']);
69
        }
70
71
        $in_ns    = $data['in_ns'];
72
        $in_title = $data['in_title'];
73
        $cleaned = cleanID($id);
74
75
        $pages = array();
76
        if ($id !== '' && $cleaned !== '') {
77
            $MetadataIndex = new MetadataIndex();
78
            $page_idx = $MetadataIndex->getPages();
79
            foreach ($page_idx as $p_id) {
80
                if ((strpos($in_ns ? $p_id : noNSorNS($p_id), $cleaned) !== false)) {
81
                    if (!isset($pages[$p_id])) {
82
                        $pages[$p_id] = p_get_first_heading($p_id, METADATA_DONT_RENDER);
83
                    }
84
                }
85
            }
86
            if ($in_title) {
87
                $func = [$this, 'pageLookupTitleCompare'];
88
                foreach ($MetadataIndex->lookupKey('title', $id, $func) as $p_id) {
89
                    if (!isset($pages[$p_id])) {
90
                        $pages[$p_id] = p_get_first_heading($p_id, METADATA_DONT_RENDER);
91
                    }
92
                }
93
            }
94
        }
95
96
        if (isset($ns)) {
97
            foreach (array_keys($pages) as $p_id) {
98
                if (strpos($p_id, $ns) !== 0) {
99
                    unset($pages[$p_id]);
100
                }
101
            }
102
        }
103
104
        // discard hidden pages
105
        // discard nonexistent pages
106
        // check ACL permissions
107
        foreach (array_keys($pages) as $idx) {
108
            if (!isVisiblePage($idx) || !page_exists($idx) || auth_quickaclcheck($idx) < AUTH_READ) {
109
                unset($pages[$idx]);
110
            }
111
        }
112
113
        $pages = $this->filterResultsByTime($pages, $data['after'], $data['before']);
114
115
        uksort($pages, [$this, 'pagesorter']);
116
        return $pages;
117
    }
118
119
    /**
120
     * Tiny helper function for comparing the searched title with the title
121
     * from the search index. This function is a wrapper around stripos with
122
     * adapted argument order and return value.
123
     *
124
     * @param string $search searched title
125
     * @param string $title  title from index
126
     * @return bool
127
     */
128
    protected function pageLookupTitleCompare($search, $title)
129
    {
130
        return stripos($title, $search) !== false;
131
    }
132
133
    /**
134
     * Sort pages based on their namespace level first, then on their string
135
     * values. This makes higher hierarchy pages rank higher than lower hierarchy
136
     * pages.
137
     *
138
     * @param string $a
139
     * @param string $b
140
     * @return int Returns < 0 if $a is less than $b; > 0 if $a is greater than $b,
141
     *             and 0 if they are equal.
142
     */
143
    protected function pagesorter($a, $b)
144
    {
145
        $ac = count(explode(':',$a));
146
        $bc = count(explode(':',$b));
147
        if ($ac < $bc) {
148
            return -1;
149
        } elseif ($ac > $bc) {
150
            return 1;
151
        }
152
        return strcmp ($a,$b);
153
    }
154
155
    /**
156
     * @param array      $results search results in the form pageid => value
157
     * @param int|string $after   only returns results with mtime after this date,
158
     *                            accepts timestap or strtotime arguments
159
     * @param int|string $before  only returns results with mtime after this date,
160
     *                            accepts timestap or strtotime arguments
161
     *
162
     * @return array
163
     */
164
    protected function filterResultsByTime(array $results, $after, $before)
165
    {
166
        if ($after || $before) {
167
            $after = is_int($after) ? $after : strtotime($after);
168
            $before = is_int($before) ? $before : strtotime($before);
169
170
            foreach ($results as $id => $value) {
171
                $mTime = filemtime(wikiFN($id));
172
                if ($after && $after > $mTime) {
173
                    unset($results[$id]);
174
                    continue;
175
                }
176
                if ($before && $before < $mTime) {
177
                    unset($results[$id]);
178
                }
179
            }
180
        }
181
        return $results;
182
    }
183
}
184