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