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