1<?php
2
3use dokuwiki\Extension\SyntaxPlugin;
4use dokuwiki\File\MediaResolver;
5
6/**
7 * @license    See LICENSE file
8 */
9// See help: https://www.dokuwiki.org/devel:syntax_plugins
10// The HTML structure generated by this syntax plugin is:
11//
12// <div class="plugin-bpmnio" id="__(bpmn|dmn)_js_<hash>">
13//   <div class="bpmn_js_data">
14//      ... base64 encoded xml
15//   </div>
16//   <div class="bpmn_js_canvas {$class}">
17//     <div class="bpmn_js_container">... rendered herein</div>
18//   </div>
19// </div>
20class syntax_plugin_bpmnio_bpmnio extends SyntaxPlugin
21{
22    protected string $type = ''; // 'bpmn' or 'dmn'
23    protected string $src = ''; // media file
24
25    public function getPType()
26    {
27        return 'block';
28    }
29
30    public function getType()
31    {
32        return 'protected';
33    }
34
35    public function getSort()
36    {
37        return 0;
38    }
39
40    public function connectTo($mode)
41    {
42        $this->Lexer->addEntryPattern('<bpmnio.*?>(?=.*?</bpmnio>)', $mode, 'plugin_bpmnio_bpmnio');
43    }
44
45    public function postConnect()
46    {
47        $this->Lexer->addExitPattern('</bpmnio>', 'plugin_bpmnio_bpmnio');
48    }
49
50    public function handle($match, $state, $pos, Doku_Handler $handler)
51    {
52        switch ($state) {
53            case DOKU_LEXER_ENTER:
54                $matched = [];
55                preg_match('/<bpmnio\s+([^>]+)>/', $match, $matched);
56
57                $attrs = [];
58                if (!empty($matched[1])) {
59                    $attrs = $this->buildAttributes($matched[1]);
60                }
61
62                $this->type = $attrs['type'] ?? 'bpmn';
63                $this->src = $attrs['src'] ?? '';
64
65                return [$state, $this->type, '', $pos, '', false];
66
67            case DOKU_LEXER_UNMATCHED:
68                $posStart = $pos;
69                $posEnd = $pos + strlen($match);
70
71                $inline = empty($this->src);
72                if (!$inline) {
73                    $match = $this->getMedia($this->src);
74                }
75                return [$state, $this->type, base64_encode($match), $posStart, $posEnd, $inline];
76
77            case DOKU_LEXER_EXIT:
78                $this->type = '';
79                $this->src = '';
80                return [$state, '', '', '', '', '', false];
81        }
82        return [];
83    }
84
85    private function buildAttributes($string)
86    {
87        $attrs = [];
88        preg_match_all('/(\w+)=["\'](.*?)["\']/', $string, $matches, PREG_SET_ORDER);
89        foreach ($matches as $match) {
90            $attrs[$match[1]] = $match[2];
91        }
92        return $attrs;
93    }
94
95    private function getMedia($src)
96    {
97        global $ID;
98
99        $id = (new MediaResolver($ID))->resolveId($src);
100        if (auth_quickaclcheck($id) < AUTH_READ) {
101            return "Error: Access denied for file $src";
102        }
103
104        $file = mediaFN($id);
105        if (!file_exists($file) || !is_readable($file)) {
106            return "Error: Cannot load file $src";
107        }
108
109        return file_get_contents($file);
110    }
111
112    public function render($mode, Doku_Renderer $renderer, $data)
113    {
114        [$state, $type, $match, $posStart, $posEnd, $inline] = $data;
115
116        if (is_a($renderer, 'renderer_plugin_dw2pdf')) {
117            if ($state == DOKU_LEXER_EXIT) {
118                $renderer->doc .= <<<HTML
119                    <div class="plugin-bpmnio">
120                        <a href="https://github.com/Color-Of-Code/dokuwiki-plugin-bpmnio/issues/4">
121                            DW2PDF support missing: Help wanted
122                        </a>
123                    </div>
124                    HTML;
125            }
126            return true;
127        }
128
129        if ($mode == 'xhtml' || $mode == 'odt') {
130            switch ($state) {
131                case DOKU_LEXER_ENTER:
132                    $bpmnid = "__{$type}_js_{$posStart}";
133                    $renderer->doc .= <<<HTML
134                        <div class="plugin-bpmnio" id="{$bpmnid}">
135                        HTML;
136                    break;
137
138                case DOKU_LEXER_UNMATCHED:
139                    $renderer->doc .= <<<HTML
140                        <div class="{$type}_js_data">
141                            {$match}
142                        </div>
143                        HTML;
144                    if ($inline) {
145                        $target = "plugin_bpmnio_{$type}";
146                        $sectionEditData = ['target' => $target];
147                        $class = $renderer->startSectionEdit($posStart, $sectionEditData);
148                    } else {
149                        $class = '';
150                    }
151                    $renderer->doc .= <<<HTML
152                        <div class="{$type}_js_canvas {$class}">
153                            <div class="{$type}_js_container"></div>
154                        </div>
155                        HTML;
156                    if ($inline) {
157                        $renderer->finishSectionEdit($posEnd);
158                    }
159                    break;
160
161                case DOKU_LEXER_EXIT:
162                    $renderer->doc .= <<<HTML
163                        </div>
164                        HTML;
165                    break;
166            }
167            return true;
168        }
169        return false;
170    }
171}
172