xref: /plugin/simplenavi/syntax.php (revision d8ce5486268fd1c02234429a87c5de7aaa6425c7)
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