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