1<?php 2/** 3 * Allows markup syntax in the header 4 */ 5 6// must be run within Dokuwiki 7if(!defined('DOKU_INC')) die(); 8 9/** 10 * All DokuWiki plugins to extend the parser/rendering mechanism 11 * need to inherit from this class 12 */ 13class syntax_plugin_header2 extends DokuWiki_Syntax_Plugin { 14 15 function getType() { return 'baseonly';} 16 function getPType() { return 'block';} 17 function getAllowedTypes() { return array('formatting', 'substition', 'disabled'); } 18 function getSort() { return 49; } 19 20 /** 21 * Connect pattern to lexer 22 */ 23 function connectTo($mode) { 24 // For compatibility with old wiki versions: 25 // xhtml renderer crashes with this syntax 26 // so only apply it when header2 renderer is configured 27 global $updateVersion, $conf; 28 if ($updateVersion<38 and $conf['renderer_xhtml']!='header2') return; 29 30 $this->Lexer->addEntryPattern("[ \t]*={2,}(?=[^\n]+={2,}[ \t]*\n)", $mode, 'plugin_header2'); 31 } 32 33 function postConnect() { 34 // For compatibility with old wiki versions 35 global $updateVersion, $conf; 36 if ($updateVersion<38 and $conf['renderer_xhtml']!='header2') return; 37 38 $this->Lexer->addExitPattern('={2,}[ \t]*(?=\n)', 'plugin_header2'); 39 } 40 41 /** 42 * Handle the match 43 */ 44 function handle($match, $state, $pos, Doku_Handler $handler){ 45 switch ($state) { 46 case DOKU_LEXER_ENTER : 47 $this->h_level = 7 - strspn($match,"="); 48 $this->h_pos = $pos; 49 if ($handler->status['section']) $handler->_addCall('section_close',array(),$pos); 50 $handler->addPluginCall('header2',array($state),$state,$pos,$match); 51 $handler->CallWriter = & new Doku_Handler_Nest($handler->CallWriter,'nest_close'); 52 return false; 53 case DOKU_LEXER_UNMATCHED : 54 $handler->_addCall('cdata', array($match), $pos); 55 return false; 56 case DOKU_LEXER_EXIT : 57 $handler->_addCall('nest_close', array(), $pos); 58 $handler->CallWriter->process(); 59 $handler->CallWriter = & $handler->CallWriter->CallWriter; 60 $handler->addPluginCall('header2',array($state,$this->h_level,$this->h_pos),$state,$pos,$match); 61 $handler->_addCall('section_open',array($this->h_level),$pos); 62 $handler->status['section'] = true; 63 return false; 64 } 65 return false; 66 } 67 68 /** 69 * Create output 70 */ 71 function render($format, Doku_Renderer $renderer, $data) { 72 list($state,$level,$pos) = $data; 73 switch ($state) { 74 case DOKU_LEXER_ENTER : 75 // store current parsed content 76 $this->store = $renderer->doc; 77 $renderer->doc = ''; 78 // metadata renderer should always parse content in the header 79 if ($format=='metadata') { 80 $this->capture = $renderer->capture; 81 $renderer->capture = true; 82 } 83 break; 84 case DOKU_LEXER_EXIT : 85 // retrieve content parsed by nest parser (i.e. in the header) 86 $title = trim($renderer->doc); 87 $renderer->doc = $this->store; 88 $this->store = ''; 89 // restore variable 90 if ($format=='metadata') { 91 $renderer->capture = $this->capture; 92 } 93 // create header 94 if($level < 1) $level = 1; 95 $method = '_' . $format . '_header'; 96 if (method_exists($this,$method)) { 97 // we have special procedure for the renderer 98 $this->$method($title, $level, $pos, $renderer); 99 } else { 100 // fall back to default renderer behavior 101 $renderer->header($title,$level,$pos); 102 } 103 break; 104 } 105 return true; 106 } 107 108 // simple function to strip all html tags 109 function _remove_html_tags($text) { 110 return preg_replace( "#<[^>]*?>#", "" , $text); 111 } 112 113 /** 114 * Revised procedures for renderers 115 * 116 * Basically adds &$renderer argument and replaces $this to $renderer, 117 * and our procedures of course. 118 */ 119 120 function _xhtml_header($text, $level, $pos, &$renderer) { 121 global $conf; 122 123 $displaytext = $text; // <= added 124 $text = htmlspecialchars_decode($this->_remove_html_tags($text),ENT_QUOTES); // <= added 125 if(!$text) return; //skip empty headlines 126 127 $hid = $renderer->_headerToLink($text,true); 128 129 //only add items within configured levels 130 $renderer->toc_additem($hid, $text, $level); 131 132 // adjust $node to reflect hierarchy of levels 133 $renderer->node[$level-1]++; 134 if ($level < $renderer->lastlevel) { 135 for ($i = 0; $i < $renderer->lastlevel-$level; $i++) { 136 $renderer->node[$renderer->lastlevel-$i-1] = 0; 137 } 138 } 139 $renderer->lastlevel = $level; 140 141 if ($level <= $conf['maxseclevel'] && 142 count($renderer->sectionedits) > 0 && 143 $renderer->sectionedits[count($renderer->sectionedits) - 1][2] === 'section') { 144 $renderer->finishSectionEdit($pos - 1); 145 } 146 147 // write the header 148 $renderer->doc .= DOKU_LF.'<h'.$level; 149 if ($level <= $conf['maxseclevel']) { 150 $renderer->doc .= ' class="' . $renderer->startSectionEdit($pos, 'section', $text) . '"'; 151 } 152 $renderer->doc .= '><a name="'.$hid.'" id="'.$hid.'">'; 153 $renderer->doc .= $displaytext; // <= revised 154 $renderer->doc .= "</a></h$level>".DOKU_LF; 155 } 156 157 function _odt_header($text, $level, $pos, &$renderer){ 158 $displaytext = $text; // <= added 159 $text = $this->_remove_html_tags($text); // <= added 160 $hid = $renderer->_headerToLink($text,true); 161 $renderer->doc .= '<text:h text:style-name="Heading_20_'.$level.'" text:outline-level="'.$level.'">'; 162 $renderer->doc .= '<text:bookmark-start text:name="'.$hid.'"/>'; 163 $renderer->doc .= $displaytext; // <= revised 164 $renderer->doc .= '<text:bookmark-end text:name="'.$hid.'"/>'; 165 $renderer->doc .= '</text:h>'; 166 } 167 168 function _xml_header($text, $level, $pos, &$renderer){ 169 if (!$text) return; //skip empty headlines 170 $renderer->nextHeader = '<header level="' . $level . '" pos="' . $pos . '">'. 171 $renderer->nextHeader .= $text; // <= revised 172 $renderer->nextHeader .= '</header>'.DOKU_LF; 173 } 174} 175