1<?php 2/** 3 * RestructuredText plugin for DokuWiki. 4 * 5 * @license GPL 3 (http://www.gnu.org/licenses/gpl.html) 6 * @version 1.0 - 2015-10-07 7 * @author Chris Green <chris@isbd.co.uk> 8 */ 9 10if (!defined('DOKU_INC')) die(); 11if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 12require_once (DOKU_PLUGIN . 'syntax.php'); 13 14class syntax_plugin_rst extends DokuWiki_Syntax_Plugin { 15 16 function getType() { 17 return 'protected'; 18 } 19 20 function getPType() { 21 return 'block'; 22 } 23 24 function getSort() { 25 return 69; 26 } 27 28 function connectTo($mode) { 29 $this->Lexer->addEntryPattern('<rst>(?=.*</rst>)', $mode, 'plugin_rst'); 30 } 31 32 function postConnect() { 33 $this->Lexer->addExitPattern('</rst>', 'plugin_rst'); 34 } 35 36 function handle($match, $state, $pos, &$handler) { 37 switch ($state) { 38 case DOKU_LEXER_ENTER : return array($state, ''); 39 case DOKU_LEXER_UNMATCHED : return array($match); 40 case DOKU_LEXER_EXIT : return array($state, ''); 41 } 42 return array($state,''); 43 } 44 45 function render($mode, &$renderer, $data) { 46 dbglog("mode is $mode, data[0] is $data[0]"); 47 if ($mode == 'xhtml' && $data[0] != DOKU_LEXER_ENTER && $data[0] != DOKU_LEXER_EXIT) 48 { 49 $descriptorspec = array( 50 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 51 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 52 2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to 53 ); 54 55 $cwd = '/tmp'; 56 $env = NULL; 57 58 $process = proc_open('rst2html --no-doc-title', $descriptorspec, $pipes, $cwd, $env); 59 60 if (is_resource($process)) { 61 // $pipes now looks like this: 62 // 0 => writeable handle connected to child stdin 63 // 1 => readable handle connected to child stdout 64 // Any error output will be appended to /tmp/error-output.txt 65 66 fwrite($pipes[0], $data[0]); 67 fclose($pipes[0]); 68 69 $output = stream_get_contents($pipes[1]); 70 fclose($pipes[1]); 71 72 // It is important that you close any pipes before calling 73 // proc_close in order to avoid a deadlock 74 $return_value = proc_close($process); 75 76 } 77 $renderer->doc .= $output; 78 return true; 79 } else { 80 return false; 81 } 82 } 83 84 /* 85 function render($mode, &$renderer, $data) { 86 //dbg('function render($mode, &$renderer, $data)-->'.' mode = '.$mode.' data = '.$data); 87 //dbg($data); 88 if ($mode == 'xhtml') { 89 list($state,$match) = $data; 90 switch ($state) { 91 case DOKU_LEXER_ENTER : break; 92 case DOKU_LEXER_UNMATCHED : 93 dbglog("In render, $match is:" . $match); 94 $match = $this->_toc($renderer, $match); 95 $renderer->doc .= $match; 96 break; 97 case DOKU_LEXER_EXIT : break; 98 } 99 return true; 100 }else if ($mode == 'metadata') { 101 //dbg('function render($mode, &$renderer, $data)-->'.' mode = '.$mode.' data = '.$data); 102 //dbg($data); 103 list($state,$match) = $data; 104 switch ($state) { 105 case DOKU_LEXER_ENTER : break; 106 case DOKU_LEXER_UNMATCHED : 107 if (!$renderer->meta['title']){ 108 $renderer->meta['title'] = $this->_rst_header($match); 109 } 110 $this->_toc($renderer, $match); 111 $internallinks = $this->_internallinks($match); 112 #dbg($internallinks); 113 if (count($internallinks)>0){ 114 foreach($internallinks as $internallink) 115 { 116 $renderer->internallink($internallink); 117 } 118 } 119 break; 120 case DOKU_LEXER_EXIT : break; 121 } 122 return true; 123 } else { 124 return false; 125 } 126 } 127 */ 128 129 function _rst_header($text) 130 { 131 $doc = new DOMDocument('1.0','UTF-8'); 132 //dbg($doc); 133 $meta = '<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>'; 134 $doc->loadHTML($meta.$text); 135 //dbg($doc->saveHTML()); 136 if ($nodes = $doc->getElementsByTagName('h1')){ 137 return $nodes->item(0)->nodeValue; 138 } 139 return false; 140 } 141 142 function _internallinks($text) 143 { 144 $links = array(); 145 if ( ! $text ) return $links; 146 $doc = new DOMDocument('1.0', 'UTF-8'); 147 $doc->loadHTML($text); 148 if ($nodes = $doc->getElementsByTagName('a')){ 149 foreach($nodes as $atag) 150 { 151 $href = $atag->getAttribute('href'); 152 if (!preg_match('/^(https{0,1}:\/\/|ftp:\/\/|mailto:)/i',$href)){ 153 $links[] = $href; 154 } 155 } 156 } 157 return $links; 158 } 159 160 function _toc(&$renderer, $text) 161 { 162 $doc = new DOMDocument('1.0','UTF-8'); 163 //dbg($doc); 164 $meta = '<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>'; 165 $doc->loadHTML($meta.$text); 166 if ($nodes = $doc->getElementsByTagName("*")){ 167 foreach($nodes as $node) 168 { 169 if (preg_match('/h([1-7])/',$node->tagName,$match)) 170 { 171 #dbg($node); 172 $node->setAttribute('class', 'sectionedit'.$match[1]); 173 $hid = $renderer->_headerToLink($node->nodeValue,'true'); 174 $node->setAttribute('id',$hid); 175 $renderer->toc_additem($hid, $node->nodeValue, $match[1]); 176 } 177 178 } 179 } 180 //remove outer tags of content 181 $html = $doc->saveHTML(); 182 $html = str_replace('<!DOCTYPE html>','',$html); 183 $html = preg_replace('/.+<body>/', '', $html); 184 $html = preg_replace('@</body>.*</html>@','', $html); 185 return $html; 186 } 187 188}