You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

988 lines
22 KiB

  1. <?php
  2. /**
  3. * Renderer output base class
  4. *
  5. * @author Harry Fuecks <hfuecks@gmail.com>
  6. * @author Andreas Gohr <andi@splitbrain.org>
  7. */
  8. use dokuwiki\Extension\Plugin;
  9. use dokuwiki\Extension\SyntaxPlugin;
  10. /**
  11. * Allowed chars in $language for code highlighting
  12. * @see GeSHi::set_language()
  13. */
  14. define('PREG_PATTERN_VALID_LANGUAGE', '#[^a-zA-Z0-9\-_]#');
  15. /**
  16. * An empty renderer, produces no output
  17. *
  18. * Inherits from dokuwiki\Extension\Plugin for giving additional functions to render plugins
  19. *
  20. * The renderer transforms the syntax instructions created by the parser and handler into the
  21. * desired output format. For each instruction a corresponding method defined in this class will
  22. * be called. That method needs to produce the desired output for the instruction and add it to the
  23. * $doc field. When all instructions are processed, the $doc field contents will be cached by
  24. * DokuWiki and sent to the user.
  25. */
  26. abstract class Doku_Renderer extends Plugin
  27. {
  28. /** @var array Settings, control the behavior of the renderer */
  29. public $info = [
  30. 'cache' => true, // may the rendered result cached?
  31. 'toc' => true, // render the TOC?
  32. ];
  33. /** @var array contains the smiley configuration, set in p_render() */
  34. public $smileys = [];
  35. /** @var array contains the entity configuration, set in p_render() */
  36. public $entities = [];
  37. /** @var array contains the acronym configuration, set in p_render() */
  38. public $acronyms = [];
  39. /** @var array contains the interwiki configuration, set in p_render() */
  40. public $interwiki = [];
  41. /** @var string|int link pages and media against this revision */
  42. public $date_at = '';
  43. /** @var array the list of headers used to create unique link ids */
  44. protected $headers = [];
  45. /**
  46. * @var string the rendered document, this will be cached after the renderer ran through
  47. */
  48. public $doc = '';
  49. /**
  50. * clean out any per-use values
  51. *
  52. * This is called before each use of the renderer object and should be used to
  53. * completely reset the state of the renderer to be reused for a new document
  54. */
  55. public function reset()
  56. {
  57. $this->headers = [];
  58. $this->doc = '';
  59. $this->info['cache'] = true;
  60. $this->info['toc'] = true;
  61. }
  62. /**
  63. * Allow the plugin to prevent DokuWiki from reusing an instance
  64. *
  65. * Since most renderer plugins fail to implement Doku_Renderer::reset() we default
  66. * to reinstantiating the renderer here
  67. *
  68. * @return bool false if the plugin has to be instantiated
  69. */
  70. public function isSingleton()
  71. {
  72. return false;
  73. }
  74. /**
  75. * Returns the format produced by this renderer.
  76. *
  77. * Has to be overidden by sub classes
  78. *
  79. * @return string
  80. */
  81. abstract public function getFormat();
  82. /**
  83. * Disable caching of this renderer's output
  84. */
  85. public function nocache()
  86. {
  87. $this->info['cache'] = false;
  88. }
  89. /**
  90. * Disable TOC generation for this renderer's output
  91. *
  92. * This might not be used for certain sub renderer
  93. */
  94. public function notoc()
  95. {
  96. $this->info['toc'] = false;
  97. }
  98. /**
  99. * Handle plugin rendering
  100. *
  101. * Most likely this needs NOT to be overwritten by sub classes
  102. *
  103. * @param string $name Plugin name
  104. * @param mixed $data custom data set by handler
  105. * @param string $state matched state if any
  106. * @param string $match raw matched syntax
  107. */
  108. public function plugin($name, $data, $state = '', $match = '')
  109. {
  110. /** @var SyntaxPlugin $plugin */
  111. $plugin = plugin_load('syntax', $name);
  112. if ($plugin != null) {
  113. $plugin->render($this->getFormat(), $this, $data);
  114. }
  115. }
  116. /**
  117. * handle nested render instructions
  118. * this method (and nest_close method) should not be overloaded in actual renderer output classes
  119. *
  120. * @param array $instructions
  121. */
  122. public function nest($instructions)
  123. {
  124. foreach ($instructions as $instruction) {
  125. // execute the callback against ourself
  126. if (method_exists($this, $instruction[0])) {
  127. call_user_func_array([$this, $instruction[0]], $instruction[1] ?: []);
  128. }
  129. }
  130. }
  131. /**
  132. * dummy closing instruction issued by Doku_Handler_Nest
  133. *
  134. * normally the syntax mode should override this instruction when instantiating Doku_Handler_Nest -
  135. * however plugins will not be able to - as their instructions require data.
  136. */
  137. public function nest_close()
  138. {
  139. }
  140. #region Syntax modes - sub classes will need to implement them to fill $doc
  141. /**
  142. * Initialize the document
  143. */
  144. public function document_start()
  145. {
  146. }
  147. /**
  148. * Finalize the document
  149. */
  150. public function document_end()
  151. {
  152. }
  153. /**
  154. * Render the Table of Contents
  155. *
  156. * @return string
  157. */
  158. public function render_TOC()
  159. {
  160. return '';
  161. }
  162. /**
  163. * Add an item to the TOC
  164. *
  165. * @param string $id the hash link
  166. * @param string $text the text to display
  167. * @param int $level the nesting level
  168. */
  169. public function toc_additem($id, $text, $level)
  170. {
  171. }
  172. /**
  173. * Render a heading
  174. *
  175. * @param string $text the text to display
  176. * @param int $level header level
  177. * @param int $pos byte position in the original source
  178. */
  179. public function header($text, $level, $pos)
  180. {
  181. }
  182. /**
  183. * Open a new section
  184. *
  185. * @param int $level section level (as determined by the previous header)
  186. */
  187. public function section_open($level)
  188. {
  189. }
  190. /**
  191. * Close the current section
  192. */
  193. public function section_close()
  194. {
  195. }
  196. /**
  197. * Render plain text data
  198. *
  199. * @param string $text
  200. */
  201. public function cdata($text)
  202. {
  203. }
  204. /**
  205. * Open a paragraph
  206. */
  207. public function p_open()
  208. {
  209. }
  210. /**
  211. * Close a paragraph
  212. */
  213. public function p_close()
  214. {
  215. }
  216. /**
  217. * Create a line break
  218. */
  219. public function linebreak()
  220. {
  221. }
  222. /**
  223. * Create a horizontal line
  224. */
  225. public function hr()
  226. {
  227. }
  228. /**
  229. * Start strong (bold) formatting
  230. */
  231. public function strong_open()
  232. {
  233. }
  234. /**
  235. * Stop strong (bold) formatting
  236. */
  237. public function strong_close()
  238. {
  239. }
  240. /**
  241. * Start emphasis (italics) formatting
  242. */
  243. public function emphasis_open()
  244. {
  245. }
  246. /**
  247. * Stop emphasis (italics) formatting
  248. */
  249. public function emphasis_close()
  250. {
  251. }
  252. /**
  253. * Start underline formatting
  254. */
  255. public function underline_open()
  256. {
  257. }
  258. /**
  259. * Stop underline formatting
  260. */
  261. public function underline_close()
  262. {
  263. }
  264. /**
  265. * Start monospace formatting
  266. */
  267. public function monospace_open()
  268. {
  269. }
  270. /**
  271. * Stop monospace formatting
  272. */
  273. public function monospace_close()
  274. {
  275. }
  276. /**
  277. * Start a subscript
  278. */
  279. public function subscript_open()
  280. {
  281. }
  282. /**
  283. * Stop a subscript
  284. */
  285. public function subscript_close()
  286. {
  287. }
  288. /**
  289. * Start a superscript
  290. */
  291. public function superscript_open()
  292. {
  293. }
  294. /**
  295. * Stop a superscript
  296. */
  297. public function superscript_close()
  298. {
  299. }
  300. /**
  301. * Start deleted (strike-through) formatting
  302. */
  303. public function deleted_open()
  304. {
  305. }
  306. /**
  307. * Stop deleted (strike-through) formatting
  308. */
  309. public function deleted_close()
  310. {
  311. }
  312. /**
  313. * Start a footnote
  314. */
  315. public function footnote_open()
  316. {
  317. }
  318. /**
  319. * Stop a footnote
  320. */
  321. public function footnote_close()
  322. {
  323. }
  324. /**
  325. * Open an unordered list
  326. */
  327. public function listu_open()
  328. {
  329. }
  330. /**
  331. * Close an unordered list
  332. */
  333. public function listu_close()
  334. {
  335. }
  336. /**
  337. * Open an ordered list
  338. */
  339. public function listo_open()
  340. {
  341. }
  342. /**
  343. * Close an ordered list
  344. */
  345. public function listo_close()
  346. {
  347. }
  348. /**
  349. * Open a list item
  350. *
  351. * @param int $level the nesting level
  352. * @param bool $node true when a node; false when a leaf
  353. */
  354. public function listitem_open($level, $node = false)
  355. {
  356. }
  357. /**
  358. * Close a list item
  359. */
  360. public function listitem_close()
  361. {
  362. }
  363. /**
  364. * Start the content of a list item
  365. */
  366. public function listcontent_open()
  367. {
  368. }
  369. /**
  370. * Stop the content of a list item
  371. */
  372. public function listcontent_close()
  373. {
  374. }
  375. /**
  376. * Output unformatted $text
  377. *
  378. * Defaults to $this->cdata()
  379. *
  380. * @param string $text
  381. */
  382. public function unformatted($text)
  383. {
  384. $this->cdata($text);
  385. }
  386. /**
  387. * Output preformatted text
  388. *
  389. * @param string $text
  390. */
  391. public function preformatted($text)
  392. {
  393. }
  394. /**
  395. * Start a block quote
  396. */
  397. public function quote_open()
  398. {
  399. }
  400. /**
  401. * Stop a block quote
  402. */
  403. public function quote_close()
  404. {
  405. }
  406. /**
  407. * Display text as file content, optionally syntax highlighted
  408. *
  409. * @param string $text text to show
  410. * @param string $lang programming language to use for syntax highlighting
  411. * @param string $file file path label
  412. */
  413. public function file($text, $lang = null, $file = null)
  414. {
  415. }
  416. /**
  417. * Display text as code content, optionally syntax highlighted
  418. *
  419. * @param string $text text to show
  420. * @param string $lang programming language to use for syntax highlighting
  421. * @param string $file file path label
  422. */
  423. public function code($text, $lang = null, $file = null)
  424. {
  425. }
  426. /**
  427. * Format an acronym
  428. *
  429. * Uses $this->acronyms
  430. *
  431. * @param string $acronym
  432. */
  433. public function acronym($acronym)
  434. {
  435. }
  436. /**
  437. * Format a smiley
  438. *
  439. * Uses $this->smiley
  440. *
  441. * @param string $smiley
  442. */
  443. public function smiley($smiley)
  444. {
  445. }
  446. /**
  447. * Format an entity
  448. *
  449. * Entities are basically small text replacements
  450. *
  451. * Uses $this->entities
  452. *
  453. * @param string $entity
  454. */
  455. public function entity($entity)
  456. {
  457. }
  458. /**
  459. * Typographically format a multiply sign
  460. *
  461. * Example: ($x=640, $y=480) should result in "640×480"
  462. *
  463. * @param string|int $x first value
  464. * @param string|int $y second value
  465. */
  466. public function multiplyentity($x, $y)
  467. {
  468. }
  469. /**
  470. * Render an opening single quote char (language specific)
  471. */
  472. public function singlequoteopening()
  473. {
  474. }
  475. /**
  476. * Render a closing single quote char (language specific)
  477. */
  478. public function singlequoteclosing()
  479. {
  480. }
  481. /**
  482. * Render an apostrophe char (language specific)
  483. */
  484. public function apostrophe()
  485. {
  486. }
  487. /**
  488. * Render an opening double quote char (language specific)
  489. */
  490. public function doublequoteopening()
  491. {
  492. }
  493. /**
  494. * Render an closinging double quote char (language specific)
  495. */
  496. public function doublequoteclosing()
  497. {
  498. }
  499. /**
  500. * Render a CamelCase link
  501. *
  502. * @param string $link The link name
  503. * @see http://en.wikipedia.org/wiki/CamelCase
  504. */
  505. public function camelcaselink($link)
  506. {
  507. }
  508. /**
  509. * Render a page local link
  510. *
  511. * @param string $hash hash link identifier
  512. * @param string $name name for the link
  513. */
  514. public function locallink($hash, $name = null)
  515. {
  516. }
  517. /**
  518. * Render a wiki internal link
  519. *
  520. * @param string $link page ID to link to. eg. 'wiki:syntax'
  521. * @param string|array $title name for the link, array for media file
  522. */
  523. public function internallink($link, $title = null)
  524. {
  525. }
  526. /**
  527. * Render an external link
  528. *
  529. * @param string $link full URL with scheme
  530. * @param string|array $title name for the link, array for media file
  531. */
  532. public function externallink($link, $title = null)
  533. {
  534. }
  535. /**
  536. * Render the output of an RSS feed
  537. *
  538. * @param string $url URL of the feed
  539. * @param array $params Finetuning of the output
  540. */
  541. public function rss($url, $params)
  542. {
  543. }
  544. /**
  545. * Render an interwiki link
  546. *
  547. * You may want to use $this->_resolveInterWiki() here
  548. *
  549. * @param string $link original link - probably not much use
  550. * @param string|array $title name for the link, array for media file
  551. * @param string $wikiName indentifier (shortcut) for the remote wiki
  552. * @param string $wikiUri the fragment parsed from the original link
  553. */
  554. public function interwikilink($link, $title, $wikiName, $wikiUri)
  555. {
  556. }
  557. /**
  558. * Link to file on users OS
  559. *
  560. * @param string $link the link
  561. * @param string|array $title name for the link, array for media file
  562. */
  563. public function filelink($link, $title = null)
  564. {
  565. }
  566. /**
  567. * Link to windows share
  568. *
  569. * @param string $link the link
  570. * @param string|array $title name for the link, array for media file
  571. */
  572. public function windowssharelink($link, $title = null)
  573. {
  574. }
  575. /**
  576. * Render a linked E-Mail Address
  577. *
  578. * Should honor $conf['mailguard'] setting
  579. *
  580. * @param string $address Email-Address
  581. * @param string|array $name name for the link, array for media file
  582. */
  583. public function emaillink($address, $name = null)
  584. {
  585. }
  586. /**
  587. * Render an internal media file
  588. *
  589. * @param string $src media ID
  590. * @param string $title descriptive text
  591. * @param string $align left|center|right
  592. * @param int $width width of media in pixel
  593. * @param int $height height of media in pixel
  594. * @param string $cache cache|recache|nocache
  595. * @param string $linking linkonly|detail|nolink
  596. */
  597. public function internalmedia(
  598. $src,
  599. $title = null,
  600. $align = null,
  601. $width = null,
  602. $height = null,
  603. $cache = null,
  604. $linking = null
  605. ) {
  606. }
  607. /**
  608. * Render an external media file
  609. *
  610. * @param string $src full media URL
  611. * @param string $title descriptive text
  612. * @param string $align left|center|right
  613. * @param int $width width of media in pixel
  614. * @param int $height height of media in pixel
  615. * @param string $cache cache|recache|nocache
  616. * @param string $linking linkonly|detail|nolink
  617. */
  618. public function externalmedia(
  619. $src,
  620. $title = null,
  621. $align = null,
  622. $width = null,
  623. $height = null,
  624. $cache = null,
  625. $linking = null
  626. ) {
  627. }
  628. /**
  629. * Render a link to an internal media file
  630. *
  631. * @param string $src media ID
  632. * @param string $title descriptive text
  633. * @param string $align left|center|right
  634. * @param int $width width of media in pixel
  635. * @param int $height height of media in pixel
  636. * @param string $cache cache|recache|nocache
  637. */
  638. public function internalmedialink(
  639. $src,
  640. $title = null,
  641. $align = null,
  642. $width = null,
  643. $height = null,
  644. $cache = null
  645. ) {
  646. }
  647. /**
  648. * Render a link to an external media file
  649. *
  650. * @param string $src media ID
  651. * @param string $title descriptive text
  652. * @param string $align left|center|right
  653. * @param int $width width of media in pixel
  654. * @param int $height height of media in pixel
  655. * @param string $cache cache|recache|nocache
  656. */
  657. public function externalmedialink(
  658. $src,
  659. $title = null,
  660. $align = null,
  661. $width = null,
  662. $height = null,
  663. $cache = null
  664. ) {
  665. }
  666. /**
  667. * Start a table
  668. *
  669. * @param int $maxcols maximum number of columns
  670. * @param int $numrows NOT IMPLEMENTED
  671. * @param int $pos byte position in the original source
  672. */
  673. public function table_open($maxcols = null, $numrows = null, $pos = null)
  674. {
  675. }
  676. /**
  677. * Close a table
  678. *
  679. * @param int $pos byte position in the original source
  680. */
  681. public function table_close($pos = null)
  682. {
  683. }
  684. /**
  685. * Open a table header
  686. */
  687. public function tablethead_open()
  688. {
  689. }
  690. /**
  691. * Close a table header
  692. */
  693. public function tablethead_close()
  694. {
  695. }
  696. /**
  697. * Open a table body
  698. */
  699. public function tabletbody_open()
  700. {
  701. }
  702. /**
  703. * Close a table body
  704. */
  705. public function tabletbody_close()
  706. {
  707. }
  708. /**
  709. * Open a table footer
  710. */
  711. public function tabletfoot_open()
  712. {
  713. }
  714. /**
  715. * Close a table footer
  716. */
  717. public function tabletfoot_close()
  718. {
  719. }
  720. /**
  721. * Open a table row
  722. */
  723. public function tablerow_open()
  724. {
  725. }
  726. /**
  727. * Close a table row
  728. */
  729. public function tablerow_close()
  730. {
  731. }
  732. /**
  733. * Open a table header cell
  734. *
  735. * @param int $colspan
  736. * @param string $align left|center|right
  737. * @param int $rowspan
  738. */
  739. public function tableheader_open($colspan = 1, $align = null, $rowspan = 1)
  740. {
  741. }
  742. /**
  743. * Close a table header cell
  744. */
  745. public function tableheader_close()
  746. {
  747. }
  748. /**
  749. * Open a table cell
  750. *
  751. * @param int $colspan
  752. * @param string $align left|center|right
  753. * @param int $rowspan
  754. */
  755. public function tablecell_open($colspan = 1, $align = null, $rowspan = 1)
  756. {
  757. }
  758. /**
  759. * Close a table cell
  760. */
  761. public function tablecell_close()
  762. {
  763. }
  764. #endregion
  765. #region util functions, you probably won't need to reimplement them
  766. /**
  767. * Creates a linkid from a headline
  768. *
  769. * @param string $title The headline title
  770. * @param boolean $create Create a new unique ID?
  771. * @return string
  772. * @author Andreas Gohr <andi@splitbrain.org>
  773. */
  774. public function _headerToLink($title, $create = false)
  775. {
  776. if ($create) {
  777. return sectionID($title, $this->headers);
  778. } else {
  779. $check = false;
  780. return sectionID($title, $check);
  781. }
  782. }
  783. /**
  784. * Removes any Namespace from the given name but keeps
  785. * casing and special chars
  786. *
  787. * @param string $name
  788. * @return string
  789. * @author Andreas Gohr <andi@splitbrain.org>
  790. *
  791. */
  792. public function _simpleTitle($name)
  793. {
  794. global $conf;
  795. //if there is a hash we use the ancor name only
  796. [$name, $hash] = sexplode('#', $name, 2);
  797. if ($hash) return $hash;
  798. if ($conf['useslash']) {
  799. $name = strtr($name, ';/', ';:');
  800. } else {
  801. $name = strtr($name, ';', ':');
  802. }
  803. return noNSorNS($name);
  804. }
  805. /**
  806. * Resolve an interwikilink
  807. *
  808. * @param string $shortcut identifier for the interwiki link
  809. * @param string $reference fragment that refers the content
  810. * @param null|bool $exists reference which returns if an internal page exists
  811. * @return string interwikilink
  812. */
  813. public function _resolveInterWiki(&$shortcut, $reference, &$exists = null)
  814. {
  815. //get interwiki URL
  816. if (isset($this->interwiki[$shortcut])) {
  817. $url = $this->interwiki[$shortcut];
  818. } elseif (isset($this->interwiki['default'])) {
  819. $shortcut = 'default';
  820. $url = $this->interwiki[$shortcut];
  821. } else {
  822. // not parsable interwiki outputs '' to make sure string manipluation works
  823. $shortcut = '';
  824. $url = '';
  825. }
  826. //split into hash and url part
  827. $hash = strrchr($reference, '#');
  828. if ($hash) {
  829. $reference = substr($reference, 0, -strlen($hash));
  830. $hash = substr($hash, 1);
  831. }
  832. //replace placeholder
  833. if (preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#', $url)) {
  834. //use placeholders
  835. $url = str_replace('{URL}', rawurlencode($reference), $url);
  836. //wiki names will be cleaned next, otherwise urlencode unsafe chars
  837. $url = str_replace(
  838. '{NAME}',
  839. ($url[0] === ':') ? $reference : preg_replace_callback(
  840. '/[[\\\\\]^`{|}#%]/',
  841. static fn($match) => rawurlencode($match[0]),
  842. $reference
  843. ),
  844. $url
  845. );
  846. $parsed = parse_url($reference);
  847. if (empty($parsed['scheme'])) $parsed['scheme'] = '';
  848. if (empty($parsed['host'])) $parsed['host'] = '';
  849. if (empty($parsed['port'])) $parsed['port'] = 80;
  850. if (empty($parsed['path'])) $parsed['path'] = '';
  851. if (empty($parsed['query'])) $parsed['query'] = '';
  852. $url = strtr($url, [
  853. '{SCHEME}' => $parsed['scheme'],
  854. '{HOST}' => $parsed['host'],
  855. '{PORT}' => $parsed['port'],
  856. '{PATH}' => $parsed['path'],
  857. '{QUERY}' => $parsed['query'],
  858. ]);
  859. } elseif ($url != '') {
  860. // make sure when no url is defined, we keep it null
  861. // default
  862. $url .= rawurlencode($reference);
  863. }
  864. //handle as wiki links
  865. if ($url && $url[0] === ':') {
  866. $urlparam = '';
  867. $id = $url;
  868. if (strpos($url, '?') !== false) {
  869. [$id, $urlparam] = sexplode('?', $url, 2, '');
  870. }
  871. $url = wl(cleanID($id), $urlparam);
  872. $exists = page_exists($id);
  873. }
  874. if ($hash) $url .= '#' . rawurlencode($hash);
  875. return $url;
  876. }
  877. #endregion
  878. }
  879. //Setup VIM: ex: et ts=4 :