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 protected string $zoom = ''; // optional scaling factor 25 26 public function getPType(): string 27 { 28 return 'block'; 29 } 30 31 public function getType(): string 32 { 33 return 'protected'; 34 } 35 36 public function getSort(): int 37 { 38 return 0; 39 } 40 41 public function connectTo($mode): void 42 { 43 $this->Lexer->addEntryPattern('<bpmnio.*?>(?=.*?</bpmnio>)', $mode, 'plugin_bpmnio_bpmnio'); 44 } 45 46 public function postConnect(): void 47 { 48 $this->Lexer->addExitPattern('</bpmnio>', 'plugin_bpmnio_bpmnio'); 49 } 50 51 public function handle($match, $state, $pos, Doku_Handler $handler): array 52 { 53 switch ($state) { 54 case DOKU_LEXER_ENTER: 55 $matched = []; 56 preg_match('/<bpmnio\s+([^>]+)>/', $match, $matched); 57 58 $attrs = []; 59 if (!empty($matched[1])) { 60 $attrs = $this->buildAttributes($matched[1]); 61 } 62 63 $this->type = $attrs['type'] ?? 'bpmn'; 64 $this->src = $attrs['src'] ?? ''; 65 $this->zoom = $this->normalizeZoom($attrs['zoom'] ?? null) ?? ''; 66 67 return [$state, $this->type, '', $pos, '', false, $this->zoom]; 68 69 case DOKU_LEXER_UNMATCHED: 70 $posStart = $pos; 71 $posEnd = $pos + strlen($match); 72 73 $inline = empty($this->src); 74 if (!$inline) { 75 $match = $this->getMedia($this->src); 76 } 77 return [$state, $this->type, base64_encode($match), $posStart, $posEnd, $inline, $this->zoom]; 78 79 case DOKU_LEXER_EXIT: 80 $this->type = ''; 81 $this->src = ''; 82 $this->zoom = ''; 83 return [$state, '', '', '', '', '', false, '']; 84 } 85 return []; 86 } 87 88 private function buildAttributes($string) 89 { 90 $attrs = []; 91 preg_match_all('/(\w+)=["\'](.*?)["\']/', $string, $matches, PREG_SET_ORDER); 92 foreach ($matches as $match) { 93 $attrs[$match[1]] = $match[2]; 94 } 95 return $attrs; 96 } 97 98 private function normalizeZoom($zoom): ?string 99 { 100 if ($zoom === null || $zoom === '') { 101 return null; 102 } 103 104 if (!is_numeric($zoom)) { 105 return null; 106 } 107 108 $zoom = (float) $zoom; 109 if ($zoom <= 0) { 110 return null; 111 } 112 113 return rtrim(rtrim(number_format($zoom, 4, '.', ''), '0'), '.'); 114 } 115 116 private function getMedia($src) 117 { 118 global $ID; 119 120 $id = (new MediaResolver($ID))->resolveId($src); 121 if (auth_quickaclcheck($id) < AUTH_READ) { 122 return "Error: Access denied for file $src"; 123 } 124 125 $file = mediaFN($id); 126 if (!file_exists($file) || !is_readable($file)) { 127 return "Error: Cannot load file $src"; 128 } 129 130 return file_get_contents($file); 131 } 132 133 public function render($mode, Doku_Renderer $renderer, $data): bool 134 { 135 [$state, $type, $match, $posStart, $posEnd, $inline, $zoom] = array_pad($data, 7, ''); 136 137 if (is_a($renderer, 'renderer_plugin_dw2pdf')) { 138 if ($state == DOKU_LEXER_EXIT) { 139 $renderer->doc .= <<<HTML 140 <div class="plugin-bpmnio"> 141 <a href="https://github.com/Color-Of-Code/dokuwiki-plugin-bpmnio/issues/4"> 142 DW2PDF support missing: Help wanted 143 </a> 144 </div> 145 HTML; 146 } 147 return true; 148 } 149 150 if ($mode == 'xhtml' || $mode == 'odt') { 151 switch ($state) { 152 case DOKU_LEXER_ENTER: 153 $bpmnid = "__{$type}_js_{$posStart}"; 154 $renderer->doc .= <<<HTML 155 <div class="plugin-bpmnio" id="{$bpmnid}"> 156 HTML; 157 break; 158 159 case DOKU_LEXER_UNMATCHED: 160 $renderer->doc .= <<<HTML 161 <div class="{$type}_js_data"> 162 {$match} 163 </div> 164 HTML; 165 if ($inline) { 166 $target = "plugin_bpmnio_{$type}"; 167 $sectionEditData = ['target' => $target]; 168 $class = $renderer->startSectionEdit($posStart, $sectionEditData); 169 } else { 170 $class = ''; 171 } 172 $zoomAttr = $zoom !== '' ? " data-zoom=\"{$zoom}\"" : ''; 173 $renderer->doc .= <<<HTML 174 <div class="{$type}_js_canvas {$class}"> 175 <div class="{$type}_js_container"{$zoomAttr}></div> 176 </div> 177 HTML; 178 if ($inline) { 179 $renderer->finishSectionEdit($posEnd); 180 } 181 break; 182 183 case DOKU_LEXER_EXIT: 184 $renderer->doc .= <<<HTML 185 </div> 186 HTML; 187 break; 188 } 189 return true; 190 } 191 return false; 192 } 193} 194