1<?php 2/** 3 * DokuWiki Syntax Plugin Web Component. 4 * Implementatiojn of https://getbootstrap.com/docs/4.1/content/typography/#blockquotes 5 * 6 */ 7if (!defined('DOKU_INC')) { 8 die(); 9} 10 11require_once(__DIR__ . '/../webcomponent.php'); 12 13/** 14 * All DokuWiki plugins to extend the parser/rendering mechanism 15 * need to inherit from this class 16 * 17 * The name of the class must follow a pattern (don't change it) 18 * ie: 19 * syntax_plugin_PluginName_ComponentName 20 */ 21class syntax_plugin_webcomponent_blockquote extends DokuWiki_Syntax_Plugin 22{ 23 24 25 const IMAGE_PATTERN = "\{\{(?:[^\}]|(?:\}[^\}]))+\}\}"; 26 27 28 /** 29 * Syntax Type. 30 * 31 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 32 * @see DokuWiki_Syntax_Plugin::getType() 33 */ 34 function getType() 35 { 36 return 'protected'; 37 } 38 39 /** 40 * @return array 41 * Allow which kind of plugin inside 42 * 43 * No one of array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 44 * because we manage self the content and we call self the parser 45 */ 46 public function getAllowedTypes() 47 { 48 return array(); 49 } 50 51 /** 52 * How Dokuwiki will add P element 53 * 54 * * 'normal' - The plugin can be used inside paragraphs 55 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 56 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 57 * 58 * @see DokuWiki_Syntax_Plugin::getPType() 59 */ 60 function getPType() 61 { 62 return 'block'; 63 } 64 65 /** 66 * @see Doku_Parser_Mode::getSort() 67 * Higher number than the teaser-columns 68 * because the mode with the lowest sort number will win out 69 */ 70 function getSort() 71 { 72 return 200; 73 } 74 75 /** 76 * Create a pattern that will called this plugin 77 * 78 * @see Doku_Parser_Mode::connectTo() 79 * @param string $mode 80 */ 81 function connectTo($mode) 82 { 83 84 $pattern = webcomponent::getLookAheadPattern($this->getPluginComponent()); 85 $this->Lexer->addEntryPattern($pattern, $mode, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 86 87 } 88 89 public function postConnect() 90 { 91 92 $this->Lexer->addExitPattern('</' . self::getTagName() . '>', 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 93 // Lookahead 94 95 // Receive the cite 96 $lookaheadPattern = webcomponent::getIncludeTagPattern(syntax_plugin_webcomponent_cite::getTag()); 97 $this->Lexer->addPattern($lookaheadPattern, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 98 99 // Receive the image 100 $this->Lexer->addPattern(self::IMAGE_PATTERN, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent()); 101 102 } 103 104 /** 105 * 106 * The handle function goal is to parse the matched syntax through the pattern function 107 * and to return the result for use in the renderer 108 * This result is always cached until the page is modified. 109 * @see DokuWiki_Syntax_Plugin::handle() 110 * 111 * @param string $match 112 * @param int $state 113 * @param int $pos 114 * @param Doku_Handler $handler 115 * @return array|bool 116 */ 117 function handle($match, $state, $pos, Doku_Handler $handler) 118 { 119 120 switch ($state) { 121 122 case DOKU_LEXER_ENTER: 123 // Suppress the component name 124 $match = utf8_substr($match, strlen($this->getPluginComponent()) + 1, -1); 125 $parameters = webcomponent::parseMatch($match); 126 return array($state, $parameters); 127 128 case DOKU_LEXER_UNMATCHED : 129 return array($state, $match); 130 131 case DOKU_LEXER_MATCHED : 132 133 $parameters = array(); 134 $citeTag = syntax_plugin_webcomponent_cite::getTag(); 135 if (preg_match('/<' . $citeTag . '>(.*)<\/' . $citeTag . '>/msSi', $match, $matches)) { 136 // We have a citation 137 $parameters['cite']['content'] = $matches[1]; 138 } 139 140 if (preg_match('/' . self::IMAGE_PATTERN . '/msSi', $match, $matches)) { 141 // We have an image, we parse it (Doku_Handler_Parse_Media in handler.php) 142 $parameters['image'] = Doku_Handler_Parse_Media($match); 143 } 144 return array($state, $parameters); 145 146 147 case DOKU_LEXER_EXIT : 148 // Important to get an exit in the render phase 149 return array($state, ''); 150 151 } 152 153 return array(); 154 155 } 156 157 /** 158 * Render the output 159 * @see DokuWiki_Syntax_Plugin::render() 160 * 161 * @param string $mode 162 * @param Doku_Renderer $renderer 163 * @param array $data - what the function handle() return'ed 164 * @return bool 165 */ 166 function render($mode, Doku_Renderer $renderer, $data) 167 { 168 169 if ($mode == 'xhtml') { 170 171 /** @var Doku_Renderer_xhtml $renderer */ 172 list($state, $parameters) = $data; 173 switch ($state) { 174 175 case DOKU_LEXER_ENTER : 176 // w-75 for width 75 177 $class = "m-3"; 178 if (array_key_exists("class", $parameters)) { 179 $class = $parameters["class"]; 180 } 181 if ($class != "") { 182 $class = " " . $class; 183 } 184 $renderer->doc .= '<div class="card' . $class . '">' . DOKU_LF 185 . DOKU_TAB . '<div class="card-body">' . DOKU_LF 186 . DOKU_TAB . DOKU_TAB . '<' . self::getTagName() . ' class="' . self::getTagName() . ' m-0"'; 187 // m-0 on the blockquote element is to correct a bottom margin from bootstrap of 1em that we don't want 188 $renderer->doc .= '>' . DOKU_LF; 189 break; 190 191 case DOKU_LEXER_UNMATCHED : 192 193 $renderer->doc .= DOKU_TAB . DOKU_TAB . webcomponent::render($parameters) . DOKU_LF; 194 break; 195 196 case DOKU_LEXER_MATCHED: 197 198 if (array_key_exists('cite', $parameters)) { 199 $content = $parameters['cite']['content']; 200 $renderer->doc .= DOKU_TAB . DOKU_TAB . '<footer class="blockquote-footer text-right"><cite>'; 201 $renderer->doc .= webcomponent::render($content); 202 $renderer->doc .= "</cite></footer>" . DOKU_LF; 203 } 204 205 if (array_key_exists('image', $parameters)) { 206 $src = $parameters['image']['src']; 207 $width = $parameters['image']['width']; 208 $height = $parameters['image']['height']; 209 $title = $parameters['image']['title']; 210 //Snippet taken from $renderer->doc .= $renderer->internalmedia($src, $linking = 'nolink'); 211 $renderer->doc .= '<img class="media my-3" src="' . ml($src, array('w' => $width, 'h' => $height, 'cache' => true)) . '" alt="' . $title . '" width="' . $width . '">'; 212 } 213 break; 214 215 case DOKU_LEXER_EXIT : 216 217 // Close the blockquote 218 $renderer->doc .= DOKU_TAB . DOKU_TAB . '</' . $this->getPluginComponent() . '>' . DOKU_LF 219 . DOKU_TAB . '</div>' . DOKU_LF; 220 221 // Close the card 222 $renderer->doc .= '</div>'; 223 224 // Reinit 225 $this->content = ""; 226 $this->img = ""; 227 $this->cite = ""; 228 break; 229 } 230 return true; 231 } 232 return false; 233 } 234 235 236 public static function getTagName() 237 { 238 list(/* $t */, /* $p */, /* $n */, $c) = explode('_', get_called_class(), 4); 239 return (isset($c) ? $c : ''); 240 } 241 242 243} 244