1<?php 2 3use dokuwiki\Extension\SyntaxPlugin; 4 5/** 6 * DokuWiki Plugin dbquery (Syntax Component) 7 * 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author Andreas Gohr <dokuwiki@cosmocode.de> 10 */ 11class syntax_plugin_dbquery_query extends SyntaxPlugin 12{ 13 /** @inheritDoc */ 14 public function getType() 15 { 16 return 'substition'; 17 } 18 19 /** @inheritDoc */ 20 public function getPType() 21 { 22 return 'block'; 23 } 24 25 /** @inheritDoc */ 26 public function getSort() 27 { 28 return 135; 29 } 30 31 /** @inheritDoc */ 32 public function connectTo($mode) 33 { 34 $this->Lexer->addSpecialPattern('{{QUERY:\w+}}', $mode, 'plugin_dbquery_query'); 35 } 36 37 /** @inheritDoc */ 38 public function handle($match, $state, $pos, Doku_Handler $handler) 39 { 40 return ['name' => substr($match, 8, -2)]; 41 } 42 43 /** @inheritDoc */ 44 public function render($mode, Doku_Renderer $renderer, $data) 45 { 46 if ($mode !== 'xhtml') { 47 return false; 48 } 49 50 /** @var helper_plugin_dbquery $hlp */ 51 $hlp = plugin_load('helper', 'dbquery'); 52 try { 53 $qdata = $hlp->loadDataFromPage($data['name']); 54 $result = $hlp->executeQuery($qdata['codeblocks']['_'], $qdata['macros']['dsn'] ?? null); 55 } catch (\Exception $e) { 56 msg(hsc($e->getMessage()), -1); 57 return true; 58 } 59 60 if (count($result) === 1 && isset($result[0]['status']) && isset($qdata['codeblocks'][$result[0]['status']])) { 61 $this->renderStatus($result, $qdata['codeblocks'][$result[0]['status']], $renderer); 62 } elseif ($qdata['macros']['transpose']) { 63 $this->renderTransposedResultTable($result, $renderer); 64 } else { 65 $this->renderResultTable($result, $renderer); 66 } 67 68 return true; 69 } 70 71 /** 72 * Render given result via the given status HTML 73 * 74 * @param string[][] $result 75 * @param string $html 76 * @param Doku_Renderer $R 77 */ 78 public function renderStatus($result, $html, Doku_Renderer $R) 79 { 80 $value = $result[0]['result'] ?? ''; 81 $html = str_replace(':result', hsc($value), $html); 82 $R->doc .= $html; 83 } 84 85 /** 86 * Render the given result as a table 87 * 88 * @param string[][] $result 89 * @param Doku_Renderer $R 90 */ 91 public function renderResultTable($result, Doku_Renderer $R) 92 { 93 global $lang; 94 95 if (!count($result)) { 96 $R->cdata($lang['nothingfound']); 97 return; 98 } 99 100 $R->table_open(); 101 $R->tablethead_open(); 102 $R->tablerow_open(); 103 foreach (array_keys($result[0]) as $header) { 104 $header = preg_replace('/_wiki$/', ' ', $header); // remove _wiki type suffix 105 $R->tableheader_open(); 106 $R->cdata($header); 107 $R->tableheader_close(); 108 } 109 $R->tablerow_close(); 110 $R->tablethead_close(); 111 112 $R->tabletbody_open(); 113 foreach ($result as $row) { 114 $R->tablerow_open(); 115 foreach ($row as $col => $cell) { 116 $R->tablecell_open(); 117 $this->cellFormat($cell, $R, $col); 118 $R->tablecell_close(); 119 } 120 $R->tablerow_close(); 121 } 122 $R->tabletbody_close(); 123 $R->table_close(); 124 } 125 126 /** 127 * Render the given result as a table, but turned 90 degrees 128 * 129 * @param string[][] $result 130 * @param Doku_Renderer $R 131 */ 132 public function renderTransposedResultTable($result, Doku_Renderer $R) 133 { 134 global $lang; 135 136 if (!count($result)) { 137 $R->cdata($lang['nothingfound']); 138 return; 139 } 140 141 $width = count($result[0]); 142 $height = count($result); 143 144 $R->table_open(); 145 for ($x = 0; $x < $width; $x++) { 146 $col = array_keys($result[0])[$x]; 147 $header = preg_replace('/_wiki$/', ' ', $col); // remove _wiki type suffix 148 149 $R->tablerow_open(); 150 $R->tableheader_open(); 151 $R->cdata($header); 152 $R->tableheader_close(); 153 154 for ($y = 0; $y < $height; $y++) { 155 $R->tablecell_open(); 156 $this->cellFormat(array_values($result[$y])[$x], $R, $col); 157 $R->tablecell_close(); 158 } 159 $R->tablerow_close(); 160 } 161 $R->table_close(); 162 } 163 164 /** 165 * Pass the given cell content to the correct renderer call 166 * 167 * Detects a subset of the wiki link syntax 168 * 169 * @param string $content 170 * @param Doku_Renderer $R 171 * @param string $name Name of the selected column 172 * @return void 173 */ 174 protected function cellFormat($content, Doku_Renderer $R, $name) 175 { 176 if(trim($content) === '') { 177 return; 178 } 179 180 // parse wiki syntax 181 if(str_ends_with($name, '_wiki')) { 182 $this->renderInject($R, $content); 183 return; 184 } 185 186 // external urls 187 if (preg_match('/^\[\[(https?:\/\/[^|\]]+)(|.*?)?]]$/', $content, $m)) { 188 $url = $m[1]; 189 $title = $m[2] ?? ''; 190 $title = trim($title, '|'); 191 $R->externallink($url, $title); 192 return; 193 } 194 195 // internal urls 196 if (preg_match('/^\[\[([^|\]]+)(|.*?)?]]$/', $content, $m)) { 197 $page = cleanID($m[1]); 198 $title = $m[2] ?? ''; 199 $title = trim($title, '|'); 200 $R->internallink($page, $title); 201 return; 202 } 203 204 $R->cdata($content); 205 } 206 207 /** 208 * Injects the given syntax into the current renderer 209 * 210 * @param Doku_Renderer $R 211 * @param string $syntax 212 * @return void 213 */ 214 protected function renderInject(Doku_Renderer $R, $syntax) 215 { 216 $instructions = p_get_instructions($syntax); 217 foreach ($instructions as $instruction) { 218 // not these 219 if(in_array($instruction[0], ['document_start', 'document_end'])) { 220 continue; 221 } 222 223 // no headers 224 if($instruction[0] === 'header') { 225 $R->p_open(); 226 $R->strong_open(); 227 $R->cdata($instruction[1][0]); 228 $R->strong_close(); 229 $R->p_close(); 230 continue; 231 } 232 233 call_user_func_array([$R, $instruction[0]], $instruction[1]); 234 } 235 } 236} 237