1<?php
2
3use dokuwiki\plugin\sql2wiki\Csv;
4
5/**
6 * DokuWiki Plugin sql2wiki (Syntax Component)
7 *
8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
9 * @author  Szymon Olewniczak <it@rid.pl>
10 */
11class syntax_plugin_sql2wiki_query extends \dokuwiki\Extension\SyntaxPlugin
12{
13    /** @inheritDoc */
14    public function getType()
15    {
16        return 'protected';
17    }
18
19    /** @inheritDoc */
20    public function getPType()
21    {
22        return 'block';
23    }
24
25    /** @inheritDoc */
26    public function getSort()
27    {
28        return 0;
29    }
30
31    /** @inheritDoc */
32    public function connectTo($mode)
33    {
34        $this->Lexer->addSpecialPattern('<sql2wiki.*?>.*?</sql2wiki>',$mode,'plugin_sql2wiki_query');
35    }
36
37    /** @inheritDoc */
38    public function handle($match, $state, $pos, Doku_Handler $handler)
39    {
40        libxml_use_internal_errors(true);
41        $xml = simplexml_load_string($match);
42        if ($xml === false) {
43            msg('Syntax: "'.hsc($match) . '" is not valid xml', -1);
44            return null;
45        }
46        $attributes = [];
47        foreach($xml[0]->attributes() as $a => $b) {
48            $attributes[$a] = (string) $b;
49        }
50        if (!isset($attributes['db']) || !isset($attributes['query'])) {
51            msg('"db" and "query" attributes are required.', -1);
52            return null;
53        }
54
55        // we use substr instead of simplexml to get the raw content
56        $content_start = strpos($match, '>') + 1;
57        $tag_value = substr($match, $content_start, -strlen('</sql2wiki>'));
58
59        $parsers = [];
60        $needle = 'parser_';
61        foreach ($attributes as $name => $value) {
62            $length = strlen($needle);
63            if (substr($name, 0, $length) === $needle) {
64                list($_, $col) = explode('_', $name);
65                if (preg_match('/([[:alpha:]]+)\((.*)\)/', $value, $matches)) {
66                    $class = $matches[1];
67                    $config = json_decode($matches[2], true);
68                    $parsers[$col] = ['class' => $class, 'config' => $config];
69                } else {
70                    $parsers[$col] = ['class' => $value, 'config' => null];
71                }
72            }
73        }
74
75        $args = [];
76        if (isset($attributes['args'])) {
77            $args = array_map('trim', explode(',', $attributes['args']));
78        }
79
80        $start = $pos + strpos($match, '>'); // closing char of the opening tag
81        $end = $pos + strlen($match) - strlen('</sql2wiki>') - 1;
82        $data = [
83            'db' => $attributes['db'],
84            'query_name' => $attributes['query'],
85            'parsers' => $parsers,
86            'args' => $args,
87            'value' => $tag_value,
88            'start' => $start,
89            'end' => $end,
90            'pos' => $pos,
91            'match' => $match
92        ];
93        return $data;
94    }
95
96    /** @inheritDoc */
97    public function render($mode, Doku_Renderer $renderer, $data)
98    {
99        if ($data === null) return false;
100
101        if ($mode == 'metadata') {
102            if (!isset($renderer->meta['plugin_sql2wiki'])) {
103                $renderer->meta['plugin_sql2wiki'] = [];
104            }
105            $renderer->meta['plugin_sql2wiki'][] = $data;
106            return true;
107        }
108        if ($mode == 'xhtml') {
109            $result = Csv::csv2arr($data['value']);
110
111            if (count($result) == 0) {
112                $renderer->p_open();
113                $renderer->cdata($this->getLang('none'));
114                $renderer->p_close();
115                return true;
116            }
117
118            // check if we use any parsers
119            $parsers = $data['parsers'];
120            if (count($parsers) > 0) {
121                $class_name = '\dokuwiki\plugin\struct\meta\Column';
122                if (!class_exists($class_name)) {
123                    msg('Install struct plugin to use parsers', -1);
124                    return false;
125                }
126                $parser_types = $class_name::allTypes();
127            }
128
129            $renderer->table_open();
130            $renderer->tablethead_open();
131            $renderer->tablerow_open();
132            $headers = array_shift($result);
133            foreach ($headers as $header) {
134                $renderer->tableheader_open();
135                $renderer->cdata($header);
136                $renderer->tableheader_close();
137            }
138            $renderer->tablerow_close();
139            $renderer->tablethead_close();
140
141            $renderer->tabletbody_open();
142            foreach ($result as $row) {
143                $renderer->tablerow_open();
144                $tds = array_values($row);
145                foreach ($tds as $i => $td) {
146                    if ($td === null) $td = '␀';
147                    if (isset($parsers[$i])) {
148                        $parser_class = $parsers[$i]['class'];
149                        $parser_config = $parsers[$i]['config'];
150                        if (!isset($parser_types[$parser_class])) {
151                            msg('Unknown parser: ' . $parser_class, -1);
152                            $renderer->tablecell_open();
153                            $renderer->cdata($td);
154                            $renderer->tablecell_close();
155                        } else {
156                            /** @var \dokuwiki\plugin\struct\types\AbstractBaseType $parser */
157                            $parser = new $parser_types[$parser_class]($parser_config);
158                            $renderer->tablecell_open();
159                            $parser->renderValue($td, $renderer, $mode);
160                            $renderer->tablecell_close();
161                        }
162                    } else {
163                        $renderer->tablecell_open();
164                        $renderer->cdata($td);
165                        $renderer->tablecell_close();
166                    }
167                }
168                $renderer->tablerow_close();
169            }
170            $renderer->tabletbody_close();
171            $renderer->table_close();
172            return true;
173        }
174        return false;
175    }
176}
177
178