11169a1acSAndreas Gohr<?php 2d8ce5486SAndreas Gohr 3d8ce5486SAndreas Gohruse dokuwiki\File\PageResolver; 4d8ce5486SAndreas Gohr 51169a1acSAndreas Gohr/** 61169a1acSAndreas Gohr * DokuWiki Plugin simplenavi (Syntax Component) 71169a1acSAndreas Gohr * 81169a1acSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 91169a1acSAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 101169a1acSAndreas Gohr */ 11d8ce5486SAndreas Gohrclass syntax_plugin_simplenavi extends DokuWiki_Syntax_Plugin 12d8ce5486SAndreas Gohr{ 13d8ce5486SAndreas Gohr private $startpages = []; 141169a1acSAndreas Gohr 15d8ce5486SAndreas Gohr /** @inheritdoc */ 16d8ce5486SAndreas Gohr public function getType() 17d8ce5486SAndreas Gohr { 181169a1acSAndreas Gohr return 'substition'; 191169a1acSAndreas Gohr } 201169a1acSAndreas Gohr 21d8ce5486SAndreas Gohr /** @inheritdoc */ 22d8ce5486SAndreas Gohr public function getPType() 23d8ce5486SAndreas Gohr { 241169a1acSAndreas Gohr return 'block'; 251169a1acSAndreas Gohr } 261169a1acSAndreas Gohr 27d8ce5486SAndreas Gohr /** @inheritdoc */ 28d8ce5486SAndreas Gohr public function getSort() 29d8ce5486SAndreas Gohr { 301169a1acSAndreas Gohr return 155; 311169a1acSAndreas Gohr } 321169a1acSAndreas Gohr 33d8ce5486SAndreas Gohr /** @inheritdoc */ 34d8ce5486SAndreas Gohr public function connectTo($mode) 35d8ce5486SAndreas Gohr { 361169a1acSAndreas Gohr $this->Lexer->addSpecialPattern('{{simplenavi>[^}]*}}', $mode, 'plugin_simplenavi'); 371169a1acSAndreas Gohr } 381169a1acSAndreas Gohr 39d8ce5486SAndreas Gohr /** @inheritdoc */ 40d8ce5486SAndreas Gohr public function handle($match, $state, $pos, Doku_Handler $handler) 41d8ce5486SAndreas Gohr { 425655937aSAndreas Gohr return explode(' ', substr($match, 13, -2)); 431169a1acSAndreas Gohr } 441169a1acSAndreas Gohr 45d8ce5486SAndreas Gohr /** @inheritdoc */ 46d8ce5486SAndreas Gohr public function render($format, Doku_Renderer $renderer, $data) 47d8ce5486SAndreas Gohr { 48d8ce5486SAndreas Gohr if ($format != 'xhtml') return false; 491169a1acSAndreas Gohr 501169a1acSAndreas Gohr global $conf; 511169a1acSAndreas Gohr global $INFO; 52b3e02951SAndreas Gohr $renderer->nocache(); 531169a1acSAndreas Gohr 54b3e02951SAndreas Gohr // first data is namespace, rest is options 555655937aSAndreas Gohr $ns = array_shift($data); 565655937aSAndreas Gohr if ($ns && $ns[0] === '.') { 575655937aSAndreas Gohr // resolve relative to current page 585655937aSAndreas Gohr $ns = getNS((new PageResolver($INFO['id']))->resolveId("$ns:xxx")); 595655937aSAndreas Gohr } else { 605655937aSAndreas Gohr $ns = cleanID($ns); 615655937aSAndreas Gohr } 625655937aSAndreas Gohr // convert to path 635655937aSAndreas Gohr $ns = utf8_encodeFN(str_replace(':', '/', $ns)); 64b3e02951SAndreas Gohr 65d8ce5486SAndreas Gohr $items = []; 66d8ce5486SAndreas Gohr search($items, $conf['datadir'], [$this, 'cbSearch'], ['ns' => $INFO['id']], $ns, 1, 'natural'); 67d8ce5486SAndreas Gohr if ($this->getConf('sortByTitle')) { 68d8ce5486SAndreas Gohr $this->sortByTitle($items, "id"); 69daf2dc98SOliver } else { 70db559ff9SMichael Große if ($this->getConf('sort') == 'ascii') { 71d8ce5486SAndreas Gohr uksort($items, [$this, 'pathCompare']); 72db559ff9SMichael Große } 73daf2dc98SOliver } 741169a1acSAndreas Gohr 75b3e02951SAndreas Gohr $class = 'plugin__simplenavi'; 76b3e02951SAndreas Gohr if (in_array('filter', $data)) $class .= ' plugin__simplenavi_filter'; 77b3e02951SAndreas Gohr 78b3e02951SAndreas Gohr $renderer->doc .= '<div class="' . $class . '">'; 79d8ce5486SAndreas Gohr $renderer->doc .= html_buildlist($items, 'idx', [$this, 'cbList'], [$this, 'cbListItem']); 80d8ce5486SAndreas Gohr $renderer->doc .= '</div>'; 811169a1acSAndreas Gohr 821169a1acSAndreas Gohr return true; 831169a1acSAndreas Gohr } 841169a1acSAndreas Gohr 85d8ce5486SAndreas Gohr /** 86d8ce5486SAndreas Gohr * Create a list openening 87d8ce5486SAndreas Gohr * 88d8ce5486SAndreas Gohr * @param array $item 89d8ce5486SAndreas Gohr * @return string 90d8ce5486SAndreas Gohr * @see html_buildlist() 91d8ce5486SAndreas Gohr */ 92d8ce5486SAndreas Gohr public function cbList($item) 93d8ce5486SAndreas Gohr { 94492ddc4eSAndreas Gohr global $INFO; 95492ddc4eSAndreas Gohr 96492ddc4eSAndreas Gohr if (($item['type'] == 'd' && $item['open']) || $INFO['id'] == $item['id']) { 97d8ce5486SAndreas Gohr return '<strong>' . html_wikilink(':' . $item['id'], $this->getTitle($item['id'])) . '</strong>'; 98492ddc4eSAndreas Gohr } else { 99d8ce5486SAndreas Gohr return html_wikilink(':' . $item['id'], $this->getTitle($item['id'])); 100492ddc4eSAndreas Gohr } 1011169a1acSAndreas Gohr 1021169a1acSAndreas Gohr } 1031169a1acSAndreas Gohr 104d8ce5486SAndreas Gohr /** 105d8ce5486SAndreas Gohr * Create a list item 106d8ce5486SAndreas Gohr * 107d8ce5486SAndreas Gohr * @param array $item 108d8ce5486SAndreas Gohr * @return string 109d8ce5486SAndreas Gohr * @see html_buildlist() 110d8ce5486SAndreas Gohr */ 111d8ce5486SAndreas Gohr public function cbListItem($item) 112d8ce5486SAndreas Gohr { 1131169a1acSAndreas Gohr if ($item['type'] == "f") { 1141169a1acSAndreas Gohr return '<li class="level' . $item['level'] . '">'; 1151169a1acSAndreas Gohr } elseif ($item['open']) { 1161169a1acSAndreas Gohr return '<li class="open">'; 1171169a1acSAndreas Gohr } else { 1181169a1acSAndreas Gohr return '<li class="closed">'; 1191169a1acSAndreas Gohr } 1201169a1acSAndreas Gohr } 1211169a1acSAndreas Gohr 122d8ce5486SAndreas Gohr /** 123d8ce5486SAndreas Gohr * Custom search callback 124d8ce5486SAndreas Gohr * 125d8ce5486SAndreas Gohr * @param $data 126d8ce5486SAndreas Gohr * @param $base 127d8ce5486SAndreas Gohr * @param $file 128d8ce5486SAndreas Gohr * @param $type 129d8ce5486SAndreas Gohr * @param $lvl 130d8ce5486SAndreas Gohr * @param $opts 131d8ce5486SAndreas Gohr * @return bool 132d8ce5486SAndreas Gohr */ 133d8ce5486SAndreas Gohr public function cbSearch(&$data, $base, $file, $type, $lvl, $opts) 134d8ce5486SAndreas Gohr { 1351169a1acSAndreas Gohr global $conf; 1361169a1acSAndreas Gohr $return = true; 1371169a1acSAndreas Gohr 1381169a1acSAndreas Gohr $id = pathID($file); 1391169a1acSAndreas Gohr 1401169a1acSAndreas Gohr if ($type == 'd' && !( 1411169a1acSAndreas Gohr preg_match('#^' . $id . '(:|$)#', $opts['ns']) || 1421169a1acSAndreas Gohr preg_match('#^' . $id . '(:|$)#', getNS($opts['ns'])) 1431169a1acSAndreas Gohr 1441169a1acSAndreas Gohr )) { 1451169a1acSAndreas Gohr //add but don't recurse 1461169a1acSAndreas Gohr $return = false; 147303e1405SMichael Große } elseif ($type == 'f' && (!empty($opts['nofiles']) || substr($file, -4) != '.txt')) { 1481169a1acSAndreas Gohr //don't add 1491169a1acSAndreas Gohr return false; 1501169a1acSAndreas Gohr } 1511169a1acSAndreas Gohr 152*660b56c3SAndreas Gohr // for sneaky index, check access to the namespace's start page 153*660b56c3SAndreas Gohr if ($type == 'd' && $conf['sneaky_index']) { 154*660b56c3SAndreas Gohr $sp = (new PageResolver(''))->resolveId($id . ':'); 155*660b56c3SAndreas Gohr if (auth_quickaclcheck($sp) < AUTH_READ) { 1561169a1acSAndreas Gohr return false; 1571169a1acSAndreas Gohr } 158*660b56c3SAndreas Gohr } 1591169a1acSAndreas Gohr 1601169a1acSAndreas Gohr if ($type == 'd') { 1611169a1acSAndreas Gohr // link directories to their start pages 1621169a1acSAndreas Gohr $id = "$id:"; 163d8ce5486SAndreas Gohr $id = (new PageResolver(''))->resolveId($id); 1641169a1acSAndreas Gohr $this->startpages[$id] = 1; 165303e1405SMichael Große } elseif (!empty($this->startpages[$id])) { 1661169a1acSAndreas Gohr // skip already shown start pages 1671169a1acSAndreas Gohr return false; 1681169a1acSAndreas Gohr } elseif (noNS($id) == $conf['start']) { 1691169a1acSAndreas Gohr // skip the main start page 1701169a1acSAndreas Gohr return false; 1711169a1acSAndreas Gohr } 1721169a1acSAndreas Gohr 1731169a1acSAndreas Gohr //check hidden 1741169a1acSAndreas Gohr if (isHiddenPage($id)) { 1751169a1acSAndreas Gohr return false; 1761169a1acSAndreas Gohr } 1771169a1acSAndreas Gohr 1781169a1acSAndreas Gohr //check ACL 1791169a1acSAndreas Gohr if ($type == 'f' && auth_quickaclcheck($id) < AUTH_READ) { 1801169a1acSAndreas Gohr return false; 1811169a1acSAndreas Gohr } 1821169a1acSAndreas Gohr 183d8ce5486SAndreas Gohr $data[$id] = array( 184d8ce5486SAndreas Gohr 'id' => $id, 1851169a1acSAndreas Gohr 'type' => $type, 1861169a1acSAndreas Gohr 'level' => $lvl, 187d8ce5486SAndreas Gohr 'open' => $return, 188d8ce5486SAndreas Gohr ); 1891169a1acSAndreas Gohr return $return; 1901169a1acSAndreas Gohr } 1911169a1acSAndreas Gohr 192d8ce5486SAndreas Gohr /** 193d8ce5486SAndreas Gohr * Get the title for the given page ID 194d8ce5486SAndreas Gohr * 195d8ce5486SAndreas Gohr * @param string $id 196d8ce5486SAndreas Gohr * @return string 197d8ce5486SAndreas Gohr */ 198d8ce5486SAndreas Gohr protected function getTitle($id) 199d8ce5486SAndreas Gohr { 200e306992cSAndreas Gohr global $conf; 201e306992cSAndreas Gohr 202e306992cSAndreas Gohr if (useHeading('navigation')) { 203e306992cSAndreas Gohr $p = p_get_first_heading($id); 204e306992cSAndreas Gohr } 205303e1405SMichael Große if (!empty($p)) return $p; 206e306992cSAndreas Gohr 207e306992cSAndreas Gohr $p = noNS($id); 208d8ce5486SAndreas Gohr if ($p == $conf['start'] || !$p) { 209e306992cSAndreas Gohr $p = noNS(getNS($id)); 210d8ce5486SAndreas Gohr if (!$p) { 211e306992cSAndreas Gohr return $conf['start']; 212e306992cSAndreas Gohr } 213e306992cSAndreas Gohr } 214e306992cSAndreas Gohr return $p; 215e306992cSAndreas Gohr } 2161169a1acSAndreas Gohr 217d8ce5486SAndreas Gohr /** 218d8ce5486SAndreas Gohr * Custom comparator to compare IDs 219d8ce5486SAndreas Gohr * 220d8ce5486SAndreas Gohr * @param string $a 221d8ce5486SAndreas Gohr * @param string $b 222d8ce5486SAndreas Gohr * @return int 223d8ce5486SAndreas Gohr */ 224d8ce5486SAndreas Gohr public function pathCompare($a, $b) 225d8ce5486SAndreas Gohr { 2264591df21Slainme global $conf; 227c9936b5bSMichael Große $a = preg_replace('/' . preg_quote($conf['start'], '/') . '$/', '', $a); 228c9936b5bSMichael Große $b = preg_replace('/' . preg_quote($conf['start'], '/') . '$/', '', $b); 2292122c0d0SMichael Große $a = str_replace(':', '/', $a); 2302122c0d0SMichael Große $b = str_replace(':', '/', $b); 2314591df21Slainme 2324591df21Slainme return strcmp($a, $b); 2334591df21Slainme } 234daf2dc98SOliver 235d8ce5486SAndreas Gohr /** 236d8ce5486SAndreas Gohr * Sort items by title 237d8ce5486SAndreas Gohr * 238d8ce5486SAndreas Gohr * @param array[] $array a list of items 239d8ce5486SAndreas Gohr * @param string $key the key that contains the page ID in each item 240d8ce5486SAndreas Gohr * @return void 241d8ce5486SAndreas Gohr */ 242d8ce5486SAndreas Gohr protected function sortByTitle(&$array, $key) 243d8ce5486SAndreas Gohr { 244d8ce5486SAndreas Gohr $sorter = []; 245d8ce5486SAndreas Gohr $ret = []; 246daf2dc98SOliver reset($array); 247daf2dc98SOliver foreach ($array as $ii => $va) { 248d8ce5486SAndreas Gohr $sorter[$ii] = $this->getTitle($va[$key]); 249daf2dc98SOliver } 250daf2dc98SOliver if ($this->getConf('sort') == 'ascii') { 251d8ce5486SAndreas Gohr uksort($sorter, [$this, 'pathCompare']); 252daf2dc98SOliver } else { 253daf2dc98SOliver natcasesort($sorter); 254daf2dc98SOliver } 255daf2dc98SOliver foreach ($sorter as $ii => $va) { 256daf2dc98SOliver $ret[$ii] = $array[$ii]; 257daf2dc98SOliver } 258daf2dc98SOliver $array = $ret; 259daf2dc98SOliver } 260daf2dc98SOliver 2611169a1acSAndreas Gohr} 262