1<?php 2 3use dokuwiki\File\MediaResolver; 4use dokuwiki\File\PageResolver; 5 6/** 7 * DokuWiki Plugin gemini (Renderer Component) 8 * 9 * This implements rendering to gemtext 10 * 11 * @link https://gemini.circumlunar.space/docs/gemtext.gmi 12 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 13 * @author Andreas Gohr <andi@splitbrain.org> 14 */ 15class renderer_plugin_gemini extends Doku_Renderer 16{ 17 const NL = "\n"; 18 19 protected $linklist = []; 20 protected $schemes = null; 21 22 /** @inheritDoc */ 23 public function getFormat() 24 { 25 return 'gemini'; 26 } 27 28 /** @inheritdoc */ 29 public function plugin($name, $data, $state = '', $match = '') 30 { 31 $this->doc .= $match; // FIXME always? 32 } 33 34 /** @inheritdoc */ 35 public function header($text, $level, $pos) 36 { 37 if ($level > 3) $level = 3; 38 $this->doc .= str_pad('', $level, '#'); 39 $this->doc .= $text; 40 $this->linebreak(); 41 } 42 43 /** 44 * Output links at the end of the section 45 * @inheritdoc 46 */ 47 public function section_close() 48 { 49 if (empty($this->linklist)) return; 50 51 $this->linebreak(); 52 foreach ($this->linklist as $number => $link) { 53 $this->doc .= '=> ' . $link['url'] . " [$number] " . $link['title']; 54 $this->linebreak(); 55 } 56 $this->linebreak(); 57 58 $this->linklist = []; 59 } 60 61 /** @inheritdoc */ 62 public function cdata($text) 63 { 64 $this->doc .= $text; 65 } 66 67 /** @inheritdoc */ 68 public function p_close() 69 { 70 $this->linebreak(); 71 $this->linebreak(); 72 } 73 74 /** 75 * @inheritdoc 76 * @params bool $optional only output linebreak if not already one there 77 */ 78 public function linebreak($optional = false) 79 { 80 if ($optional && $this->doc[-1] == self::NL) return; 81 $this->doc .= self::NL; 82 } 83 84 /** @inheritdoc */ 85 public function hr() 86 { 87 $this->linebreak(); 88 $this->doc .= str_pad('', 70, '━'); 89 $this->linebreak(); 90 } 91 92 /** @inheritdoc */ 93 public function footnote_open() 94 { 95 $this->doc .= ' ❲'; 96 } 97 98 /** @inheritdoc */ 99 public function footnote_close() 100 { 101 $this->doc .= '❳ '; 102 } 103 104 /** @inheritdoc */ 105 public function listu_close() 106 { 107 $this->linebreak(); 108 } 109 110 /** @inheritdoc */ 111 public function listo_close() 112 { 113 $this->linebreak(); 114 } 115 116 /** @inheritdoc */ 117 public function listitem_open($level, $node = false) 118 { 119 $this->doc .= '*'; 120 } 121 122 public function listcontent_close() 123 { 124 $this->linebreak(); 125 } 126 127 /** @inheritdoc */ 128 public function php($text) 129 { 130 $this->cdata($text); 131 } 132 133 /** @inheritdoc */ 134 public function phpblock($text) 135 { 136 $this->preformatted($text); 137 } 138 139 /** @inheritdoc */ 140 public function html($text) 141 { 142 $this->cdata($text); 143 } 144 145 /** @inheritdoc */ 146 public function htmlblock($text) 147 { 148 $this->preformatted($text); 149 } 150 151 /** @inheritdoc */ 152 public function preformatted($text) 153 { 154 $this->file($text); 155 } 156 157 /** @inheritdoc */ 158 public function quote_open() 159 { 160 $this->doc .= '> '; 161 } 162 163 /** @inheritdoc */ 164 public function quote_close() 165 { 166 $this->linebreak(true); 167 } 168 169 /** @inheritdoc */ 170 public function file($text, $lang = null, $file = null) 171 { 172 $this->linebreak(true); 173 $this->doc .= "```$file"; 174 $this->linebreak(); 175 $this->cdata($text); 176 $this->linebreak(true); 177 $this->doc .= '```'; 178 $this->linebreak(); 179 } 180 181 /** @inheritdoc */ 182 public function code($text, $lang = null, $file = null) 183 { 184 $this->file($text, $lang, $file); 185 } 186 187 /** @inheritdoc */ 188 public function acronym($acronym) 189 { 190 $this->cdata($acronym); 191 } 192 193 /** @inheritdoc */ 194 public function smiley($smiley) 195 { 196 $this->cdata($smiley); 197 } 198 199 /** @inheritdoc */ 200 public function entity($entity) 201 { 202 if (array_key_exists($entity, $this->entities)) { 203 $this->doc .= $this->entities[$entity]; 204 } else { 205 $this->cdata($entity); 206 } 207 } 208 209 /** @inheritdoc */ 210 public function multiplyentity($x, $y) 211 { 212 $this->cdata($x . '×' . $y); 213 } 214 215 /** @inheritdoc */ 216 public function singlequoteopening() 217 { 218 global $lang; 219 $this->doc .= $lang['singlequoteopening']; 220 } 221 222 /** @inheritdoc */ 223 public function singlequoteclosing() 224 { 225 global $lang; 226 $this->doc .= $lang['singlequoteclosing']; 227 } 228 229 /** @inheritdoc */ 230 public function apostrophe() 231 { 232 global $lang; 233 $this->doc .= $lang['apostrophe']; 234 } 235 236 /** @inheritdoc */ 237 public function doublequoteopening() 238 { 239 global $lang; 240 $this->doc .= $lang['doublequoteopening']; 241 } 242 243 /** @inheritdoc */ 244 public function doublequoteclosing() 245 { 246 global $lang; 247 $this->doc .= $lang['doublequoteclosing']; 248 } 249 250 /** @inheritdoc */ 251 public function camelcaselink($link) 252 { 253 $this->internallink($link); 254 } 255 256 /** @inheritdoc */ 257 public function locallink($hash, $name = null) 258 { 259 if (!$name) $name = $hash; 260 $this->cdata($name); 261 } 262 263 /** @inheritdoc */ 264 public function internallink($id, $title = null) 265 { 266 global $ID; 267 268 $id = explode('?', $id, 2)[0]; 269 $id = explode('#', $id, 2)[0]; 270 if ($id === '') $id = $ID; 271 $id = (new PageResolver($ID))->resolveId($id); 272 273 if (page_exists($id)) { 274 $url = '//' . $_SERVER['HTTP_HOST'] . '/' . $id; 275 } else { 276 $url = ''; 277 } 278 279 // reuse externallink - handles media titles 280 $this->externallink($url, $title); 281 } 282 283 /** 284 * Note: $url may be empty when passed from internalmedia and pages does not exist 285 * @inheritdoc 286 */ 287 public function externallink($url, $title = null) 288 { 289 // image in link title - print it first 290 if (is_array($title)) { 291 if ($title['type'] == 'internalmedia') { 292 $this->internalmedia($title['src'], $title['title']); 293 } else { 294 $this->externalmedia($title['src'], $title['title']); 295 } 296 $title = $title['title']; 297 $ismedia = true; 298 } else { 299 $ismedia = false; 300 } 301 if (!$title) $title = $url; 302 303 // add to list of links 304 if ($url) { 305 $count = count($this->linklist); 306 $count++; 307 $this->linklist[$count] = [ 308 'url' => $url, 309 'title' => $title, 310 ]; 311 } 312 313 // output 314 if (!$ismedia) { 315 // print the title and count 316 $this->cdata($title); 317 if ($url) $this->doc .= "[$count]"; 318 } elseif ($url) { 319 // remove newline, print count, add newline 320 $this->doc = substr($this->doc, 0, -1); 321 $this->doc .= "[$count]"; 322 $this->linebreak(); 323 } 324 } 325 326 /** @inheritdoc */ 327 public function rss($url, $params) 328 { 329 global $conf; 330 $feed = new FeedParser(); 331 $feed->set_feed_url($url); 332 333 $rc = $feed->init(); 334 if (!$rc) return; 335 336 if ($params['nosort']) $feed->enable_order_by_date(false); 337 338 //decide on start and end 339 if ($params['reverse']) { 340 $mod = -1; 341 $start = $feed->get_item_quantity() - 1; 342 $end = $start - ($params['max']); 343 $end = ($end < -1) ? -1 : $end; 344 } else { 345 $mod = 1; 346 $start = 0; 347 $end = $feed->get_item_quantity(); 348 $end = ($end > $params['max']) ? $params['max'] : $end; 349 } 350 351 for ($x = $start; $x != $end; $x += $mod) { 352 $item = $feed->get_item($x); 353 $this->doc .= '=> ' . $item->get_permalink() . ' ' . $item->get_title(); 354 $this->linebreak(); 355 if ($params['author'] || $params['date']) { 356 if ($params['author']) $this->cdata($item->get_author(0)->get_name()); 357 if ($params['date']) $this->cdata(' (' . $item->get_local_date($conf['dformat']) . ')'); 358 $this->linebreak(); 359 } 360 if ($params['details']) { 361 $this->doc .= '> ' . strip_tags($item->get_description()); 362 $this->linebreak(); 363 } 364 } 365 } 366 367 /** @inheritdoc */ 368 public function interwikilink($link, $title, $wikiName, $wikiUri) 369 { 370 $exists = null; 371 $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 372 if (!$title) $title = $wikiUri; 373 if ($exists === null) { 374 $this->externallink($url, $title); 375 } elseif ($exists === true) { 376 $this->internallink($url, $title); 377 } else { 378 $this->cdata($title); 379 } 380 } 381 382 public function windowssharelink($link, $title = null) 383 { 384 parent::windowssharelink($link, $title); // TODO: Change the autogenerated stub 385 } 386 387 /** @inheritdoc */ 388 public function emaillink($address, $name = null) 389 { 390 if (!$name) $name = $address; 391 $this->externallink('mailto:' . $address, $name); 392 } 393 394 /** @inheritdoc */ 395 public function internalmedia( 396 $src, 397 $title = null, 398 $align = null, 399 $width = null, 400 $height = null, 401 $cache = null, 402 $linking = null 403 ) { 404 global $ID; 405 406 $src = (new MediaResolver($ID))->resolveId($src); 407 if (!media_exists($src)) return; 408 $src = '//' . $_SERVER['HTTP_HOST'] . '/_media/' . $src; 409 410 $this->externalmedia($src, $title); 411 } 412 413 /** @inheritdoc */ 414 public function externalmedia( 415 $src, 416 $title = null, 417 $align = null, 418 $width = null, 419 $height = null, 420 $cache = null, 421 $linking = null 422 ) { 423 if (!$title) $title = basename($src); 424 $title = "[$title]"; 425 426 $this->linebreak(true); 427 $this->doc .= '=> ' . $src . ' ' . $title; 428 $this->linebreak(); 429 } 430 431 /** @inheritdoc */ 432 public function internalmedialink($src, $title = null, $align = null, $width = null, $height = null, $cache = null) 433 { 434 $this->internalmedia($src, $title, $align, $width, $height, $cache); 435 } 436 437 /** @inheritdoc */ 438 public function externalmedialink($src, $title = null, $align = null, $width = null, $height = null, $cache = null) 439 { 440 parent::externalmedia($src, $title, $align, $width, $height, $cache); 441 } 442 443 public function table_open($maxcols = null, $numrows = null, $pos = null) 444 { 445 $this->linebreak(true); 446 $this->doc .= '```'; 447 $this->linebreak(); 448 449 } 450 451 public function table_close($pos = null) 452 { 453 $this->doc .= '```'; 454 $this->linebreak(); 455 } 456 457 public function tablerow_close() 458 { 459 $this->linebreak(); 460 } 461 462 public function tableheader_close() 463 { 464 $this->doc .= "\t"; 465 } 466 467 public function tablecell_close() 468 { 469 $this->doc .= "\t"; 470 } 471 472} 473 474