1<?php 2 3use dokuwiki\File\PageResolver; 4 5/** 6 * DokuWiki Plugin simplenavi (Syntax Component) 7 * 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author Andreas Gohr <gohr@cosmocode.de> 10 */ 11 12class syntax_plugin_simplenavi extends DokuWiki_Syntax_Plugin 13{ 14 private $startpages = []; 15 16 /** @inheritdoc */ 17 public function getType() 18 { 19 return 'substition'; 20 } 21 22 /** @inheritdoc */ 23 public function getPType() 24 { 25 return 'block'; 26 } 27 28 /** @inheritdoc */ 29 public function getSort() 30 { 31 return 155; 32 } 33 34 /** @inheritdoc */ 35 public function connectTo($mode) 36 { 37 $this->Lexer->addSpecialPattern('{{simplenavi>[^}]*}}', $mode, 'plugin_simplenavi'); 38 } 39 40 /** @inheritdoc */ 41 public function handle($match, $state, $pos, Doku_Handler $handler) 42 { 43 $data = [cleanID(substr($match, 13, -2))]; 44 45 return $data; 46 } 47 48 /** @inheritdoc */ 49 public function render($format, Doku_Renderer $renderer, $data) 50 { 51 if ($format != 'xhtml') return false; 52 53 global $conf; 54 global $INFO; 55 $renderer->info['cache'] = false; 56 57 $ns = utf8_encodeFN(str_replace(':', '/', $data[0])); 58 $items = []; 59 search($items, $conf['datadir'], [$this, 'cbSearch'], ['ns' => $INFO['id']], $ns, 1, 'natural'); 60 if ($this->getConf('sortByTitle')) { 61 $this->sortByTitle($items, "id"); 62 } else { 63 if ($this->getConf('sort') == 'ascii') { 64 uksort($items, [$this, 'pathCompare']); 65 } 66 } 67 68 $renderer->doc .= '<div class="plugin__simplenavi">'; 69 $renderer->doc .= html_buildlist($items, 'idx', [$this, 'cbList'], [$this, 'cbListItem']); 70 $renderer->doc .= '</div>'; 71 72 return true; 73 } 74 75 /** 76 * Create a list openening 77 * 78 * @param array $item 79 * @return string 80 * @see html_buildlist() 81 */ 82 public function cbList($item) 83 { 84 global $INFO; 85 86 if (($item['type'] == 'd' && $item['open']) || $INFO['id'] == $item['id']) { 87 return '<strong>' . html_wikilink(':' . $item['id'], $this->getTitle($item['id'])) . '</strong>'; 88 } else { 89 return html_wikilink(':' . $item['id'], $this->getTitle($item['id'])); 90 } 91 92 } 93 94 /** 95 * Create a list item 96 * 97 * @param array $item 98 * @return string 99 * @see html_buildlist() 100 */ 101 public function cbListItem($item) 102 { 103 if ($item['type'] == "f") { 104 return '<li class="level' . $item['level'] . '">'; 105 } elseif ($item['open']) { 106 return '<li class="open">'; 107 } else { 108 return '<li class="closed">'; 109 } 110 } 111 112 /** 113 * Custom search callback 114 * 115 * @param $data 116 * @param $base 117 * @param $file 118 * @param $type 119 * @param $lvl 120 * @param $opts 121 * @return bool 122 */ 123 public function cbSearch(&$data, $base, $file, $type, $lvl, $opts) 124 { 125 global $conf; 126 $return = true; 127 128 $id = pathID($file); 129 130 if ($type == 'd' && !( 131 preg_match('#^' . $id . '(:|$)#', $opts['ns']) || 132 preg_match('#^' . $id . '(:|$)#', getNS($opts['ns'])) 133 134 )) { 135 //add but don't recurse 136 $return = false; 137 } elseif ($type == 'f' && (!empty($opts['nofiles']) || substr($file, -4) != '.txt')) { 138 //don't add 139 return false; 140 } 141 142 if ($type == 'd' && $conf['sneaky_index'] && auth_quickaclcheck($id . ':') < AUTH_READ) { 143 return false; 144 } 145 146 if ($type == 'd') { 147 // link directories to their start pages 148 $id = "$id:"; 149 $id = (new PageResolver(''))->resolveId($id); 150 $this->startpages[$id] = 1; 151 } elseif (!empty($this->startpages[$id])) { 152 // skip already shown start pages 153 return false; 154 } elseif (noNS($id) == $conf['start']) { 155 // skip the main start page 156 return false; 157 } 158 159 //check hidden 160 if (isHiddenPage($id)) { 161 return false; 162 } 163 164 //check ACL 165 if ($type == 'f' && auth_quickaclcheck($id) < AUTH_READ) { 166 return false; 167 } 168 169 $data[$id] = array( 170 'id' => $id, 171 'type' => $type, 172 'level' => $lvl, 173 'open' => $return, 174 ); 175 return $return; 176 } 177 178 /** 179 * Get the title for the given page ID 180 * 181 * @param string $id 182 * @return string 183 */ 184 protected function getTitle($id) 185 { 186 global $conf; 187 188 if (useHeading('navigation')) { 189 $p = p_get_first_heading($id); 190 } 191 if (!empty($p)) return $p; 192 193 $p = noNS($id); 194 if ($p == $conf['start'] || !$p) { 195 $p = noNS(getNS($id)); 196 if (!$p) { 197 return $conf['start']; 198 } 199 } 200 return $p; 201 } 202 203 /** 204 * Custom comparator to compare IDs 205 * 206 * @param string $a 207 * @param string $b 208 * @return int 209 */ 210 public function pathCompare($a, $b) 211 { 212 global $conf; 213 $a = preg_replace('/' . preg_quote($conf['start'], '/') . '$/', '', $a); 214 $b = preg_replace('/' . preg_quote($conf['start'], '/') . '$/', '', $b); 215 $a = str_replace(':', '/', $a); 216 $b = str_replace(':', '/', $b); 217 218 return strcmp($a, $b); 219 } 220 221 /** 222 * Sort items by title 223 * 224 * @param array[] $array a list of items 225 * @param string $key the key that contains the page ID in each item 226 * @return void 227 */ 228 protected function sortByTitle(&$array, $key) 229 { 230 $sorter = []; 231 $ret = []; 232 reset($array); 233 foreach ($array as $ii => $va) { 234 $sorter[$ii] = $this->getTitle($va[$key]); 235 } 236 if ($this->getConf('sort') == 'ascii') { 237 uksort($sorter, [$this, 'pathCompare']); 238 } else { 239 natcasesort($sorter); 240 } 241 foreach ($sorter as $ii => $va) { 242 $ret[$ii] = $array[$ii]; 243 } 244 $array = $ret; 245 } 246 247} 248