1<?php
2
3use dokuwiki\Extension\SyntaxPlugin;
4use dokuwiki\Extension\PluginInterface;
5
6/**
7 * Info Plugin: Displays information about various DokuWiki internals
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author     Andreas Gohr <andi@splitbrain.org>
11 * @author     Esther Brunner <wikidesign@gmail.com>
12 */
13class syntax_plugin_info extends SyntaxPlugin
14{
15    /**
16     * What kind of syntax are we?
17     */
18    public function getType()
19    {
20        return 'substition';
21    }
22
23    /**
24     * What about paragraphs?
25     */
26    public function getPType()
27    {
28        return 'block';
29    }
30
31    /**
32     * Where to sort in?
33     */
34    public function getSort()
35    {
36        return 155;
37    }
38
39    /**
40     * Connect pattern to lexer
41     */
42    public function connectTo($mode)
43    {
44        $this->Lexer->addSpecialPattern('~~INFO:\w+~~', $mode, 'plugin_info');
45    }
46
47    /**
48     * Handle the match
49     *
50     * @param string $match The text matched by the patterns
51     * @param int $state The lexer state for the match
52     * @param int $pos The character position of the matched text
53     * @param Doku_Handler $handler The Doku_Handler object
54     * @return  array Return an array with all data you want to use in render
55     */
56    public function handle($match, $state, $pos, Doku_Handler $handler)
57    {
58        $match = substr($match, 7, -2); //strip ~~INFO: from start and ~~ from end
59        return [strtolower($match)];
60    }
61
62    /**
63     * Create output
64     *
65     * @param string $format string     output format being rendered
66     * @param Doku_Renderer $renderer the current renderer object
67     * @param array $data data created by handler()
68     * @return  boolean                 rendered correctly?
69     */
70    public function render($format, Doku_Renderer $renderer, $data)
71    {
72        if ($format == 'xhtml') {
73            /** @var Doku_Renderer_xhtml $renderer */
74            //handle various info stuff
75            switch ($data[0]) {
76                case 'syntaxmodes':
77                    $renderer->doc .= $this->renderSyntaxModes();
78                    break;
79                case 'syntaxtypes':
80                    $renderer->doc .= $this->renderSyntaxTypes();
81                    break;
82                case 'syntaxplugins':
83                    $this->renderPlugins('syntax', $renderer);
84                    break;
85                case 'adminplugins':
86                    $this->renderPlugins('admin', $renderer);
87                    break;
88                case 'actionplugins':
89                    $this->renderPlugins('action', $renderer);
90                    break;
91                case 'rendererplugins':
92                    $this->renderPlugins('renderer', $renderer);
93                    break;
94                case 'helperplugins':
95                    $this->renderPlugins('helper', $renderer);
96                    break;
97                case 'authplugins':
98                    $this->renderPlugins('auth', $renderer);
99                    break;
100                case 'remoteplugins':
101                    $this->renderPlugins('remote', $renderer);
102                    break;
103                case 'helpermethods':
104                    $this->renderHelperMethods($renderer);
105                    break;
106                case 'hooks':
107                    $this->renderHooks($renderer);
108                    break;
109                case 'datetime':
110                    $renderer->doc .= date('r');
111                    break;
112                default:
113                    $renderer->doc .= "no info about " . htmlspecialchars($data[0]);
114            }
115            return true;
116        }
117        return false;
118    }
119
120    /**
121     * list all installed plugins
122     *
123     * uses some of the original renderer methods
124     *
125     * @param string $type
126     * @param Doku_Renderer $renderer
127     */
128    protected function renderPlugins($type, Doku_Renderer $renderer)
129    {
130        global $lang;
131        $plugins = plugin_list($type);
132        $plginfo = [];
133
134        // remove subparts
135        foreach ($plugins as $p) {
136            $po = plugin_load($type, $p);
137            if (! $po instanceof PluginInterface) continue;
138            [$name, /* part */] = explode('_', $p, 2);
139            $plginfo[$name] = $po->getInfo();
140        }
141
142        // list them
143        $renderer->listu_open();
144        foreach ($plginfo as $info) {
145            $renderer->listitem_open(1);
146            $renderer->listcontent_open();
147            $renderer->externallink($info['url'], $info['name']);
148            $renderer->cdata(' ');
149            $renderer->emphasis_open();
150            $renderer->cdata($info['date']);
151            $renderer->emphasis_close();
152            $renderer->cdata(' ' . $lang['by'] . ' ');
153            $renderer->emaillink($info['email'], $info['author']);
154            $renderer->linebreak();
155            $renderer->cdata($info['desc']);
156            $renderer->listcontent_close();
157            $renderer->listitem_close();
158        }
159        $renderer->listu_close();
160    }
161
162    /**
163     * list all installed plugins
164     *
165     * uses some of the original renderer methods
166     *
167     * @param Doku_Renderer_xhtml $renderer
168     */
169    protected function renderHelperMethods(Doku_Renderer_xhtml $renderer)
170    {
171        $plugins = plugin_list('helper');
172        foreach ($plugins as $p) {
173            $po = plugin_load('helper', $p);
174            if (!$po instanceof PluginInterface) continue;
175
176            if (!method_exists($po, 'getMethods')) continue;
177            $methods = $po->getMethods();
178            $info = $po->getInfo();
179
180            $hid = $this->addToToc($info['name'], 2, $renderer);
181            $doc = '<h2><a name="' . $hid . '" id="' . $hid . '">' . hsc($info['name']) . '</a></h2>';
182            $doc .= '<div class="level2">';
183            $doc .= '<p>' . strtr(hsc($info['desc']), ["\n" => "<br />"]) . '</p>';
184            $doc .= '<pre class="code">$' . $p . " = plugin_load('helper', '" . $p . "');</pre>";
185            $doc .= '</div>';
186            foreach ($methods as $method) {
187                $title = '$' . $p . '->' . $method['name'] . '()';
188                $hid = $this->addToToc($title, 3, $renderer);
189                $doc .= '<h3><a name="' . $hid . '" id="' . $hid . '">' . hsc($title) . '</a></h3>';
190                $doc .= '<div class="level3">';
191                $doc .= '<div class="table"><table class="inline"><tbody>';
192                $doc .= '<tr><th>Description</th><td colspan="2">' . $method['desc'] .
193                    '</td></tr>';
194                if ($method['params']) {
195                    $c = count($method['params']);
196                    $doc .= '<tr><th rowspan="' . $c . '">Parameters</th><td>';
197                    $params = [];
198                    foreach ($method['params'] as $desc => $type) {
199                        $params[] = hsc($desc) . '</td><td>' . hsc($type);
200                    }
201                    $doc .= implode('</td></tr><tr><td>', $params) . '</td></tr>';
202                }
203                if ($method['return']) {
204                    $doc .= '<tr><th>Return value</th><td>' . hsc(key($method['return'])) .
205                        '</td><td>' . hsc(current($method['return'])) . '</td></tr>';
206                }
207                $doc .= '</tbody></table></div>';
208                $doc .= '</div>';
209            }
210            unset($po);
211
212            $renderer->doc .= $doc;
213        }
214    }
215
216    /**
217     * lists all known syntax types and their registered modes
218     *
219     * @return string
220     */
221    protected function renderSyntaxTypes()
222    {
223        global $PARSER_MODES;
224        $doc = '';
225
226        $doc .= '<div class="table"><table class="inline"><tbody>';
227        foreach ($PARSER_MODES as $mode => $modes) {
228            $doc .= '<tr>';
229            $doc .= '<td class="leftalign">';
230            $doc .= $mode;
231            $doc .= '</td>';
232            $doc .= '<td class="leftalign">';
233            $doc .= implode(', ', $modes);
234            $doc .= '</td>';
235            $doc .= '</tr>';
236        }
237        $doc .= '</tbody></table></div>';
238        return $doc;
239    }
240
241    /**
242     * lists all known syntax modes and their sorting value
243     *
244     * @return string
245     */
246    protected function renderSyntaxModes()
247    {
248        $modes = p_get_parsermodes();
249
250        $compactmodes = [];
251        foreach ($modes as $mode) {
252            $compactmodes[$mode['sort']][] = $mode['mode'];
253        }
254        $doc = '';
255        $doc .= '<div class="table"><table class="inline"><tbody>';
256
257        foreach ($compactmodes as $sort => $modes) {
258            $rowspan = '';
259            if (count($modes) > 1) {
260                $rowspan = ' rowspan="' . count($modes) . '"';
261            }
262
263            foreach ($modes as $index => $mode) {
264                $doc .= '<tr>';
265                $doc .= '<td class="leftalign">';
266                $doc .= $mode;
267                $doc .= '</td>';
268
269                if ($index === 0) {
270                    $doc .= '<td class="rightalign" ' . $rowspan . '>';
271                    $doc .= $sort;
272                    $doc .= '</td>';
273                }
274                $doc .= '</tr>';
275            }
276        }
277
278        $doc .= '</tbody></table></div>';
279        return $doc;
280    }
281
282    /**
283     * Render all currently registered event handlers
284     *
285     * @param Doku_Renderer $renderer
286     */
287    protected function renderHooks(Doku_Renderer $renderer)
288    {
289        global $EVENT_HANDLER;
290
291        $list = $EVENT_HANDLER->getEventHandlers();
292        ksort($list);
293
294        $renderer->listu_open();
295        foreach ($list as $event => $handlers) {
296            $renderer->listitem_open(1);
297            $renderer->listcontent_open();
298            $renderer->cdata($event);
299            $renderer->listcontent_close();
300
301            $renderer->listo_open();
302            foreach ($handlers as $sequence) {
303                foreach ($sequence as $handler) {
304                    $renderer->listitem_open(2);
305                    $renderer->listcontent_open();
306                    $renderer->cdata(get_class($handler[0]) . '::' . $handler[1] . '()');
307                    $renderer->listcontent_close();
308                    $renderer->listitem_close();
309                }
310            }
311            $renderer->listo_close();
312            $renderer->listitem_close();
313        }
314        $renderer->listu_close();
315    }
316
317    /**
318     * Adds a TOC item
319     *
320     * @param string $text
321     * @param int $level
322     * @param Doku_Renderer_xhtml $renderer
323     * @return string
324     */
325    protected function addToToc($text, $level, Doku_Renderer_xhtml $renderer)
326    {
327        global $conf;
328
329        $hid = '';
330        if (($level >= $conf['toptoclevel']) && ($level <= $conf['maxtoclevel'])) {
331            $hid = $renderer->_headerToLink($text, true);
332            $renderer->toc[] = [
333                'hid' => $hid,
334                'title' => $text,
335                'type' => 'ul',
336                'level' => $level - $conf['toptoclevel'] + 1
337            ];
338        }
339        return $hid;
340    }
341}
342