1<?php 2 3use dokuwiki\Extension\SyntaxPlugin; 4use dokuwiki\Parsing\Handler; 5 6/** 7 * BBCode plugin: allows BBCode markup familiar from forum software 8 * 9 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 10 * @author Esther Brunner <esther@kaffeehaus.ch> 11 * @author Christopher Smith <chris@jalakai.co.uk> 12 * @author Luis Machuca Bezzaza <luis.machuca@gulix.cl> 13 */ 14class syntax_plugin_bbcode_color extends SyntaxPlugin 15{ 16 protected const BROWSERCOLORS = [ 17 'aliceblue' => '#f0f8ff' , 18 'antiquewhite' => '#faebd7' , 19 'aqua' => '#00ffff' , 20 'aquamarine' => '#7fffd4' , 21 'azure' => '#f0ffff' , 22 'beige' => '#f5f5dc' , 23 'bisque' => '#ffe4c4' , 24 'black' => '#000000' , 25 'blanchedalmond' => '#ffebcd' , 26 'blue' => '#0000ff' , 27 'blueviolet' => '#8a2be2' , 28 'brown' => '#a52a2a' , 29 'burlywood' => '#deb887' , 30 'cadetblue' => '#5f9ea0' , 31 'chartreuse' => '#7fff00' , 32 'chocolate' => '#d2691e' , 33 'coral' => '#ff7f50' , 34 'cornflowerblue' => '#6495ed' , 35 'cornsilk' => '#fff8dc' , 36 'crimson' => '#dc143c' , 37 'cyan' => '#00ffff' , 38 'darkblue' => '#00008b' , 39 'darkcyan' => '#008b8b' , 40 'darkgoldenrod' => '#b8860b' , 41 'darkgray' => '#a9a9a9' , 42 'darkgreen' => '#006400' , 43 'darkkhaki' => '#bdb76b' , 44 'darkmagenta' => '#8b008b' , 45 'darkolivegreen' => '#556b2f' , 46 'darkorange' => '#ff8c00' , 47 'darkorchid' => '#9932cc' , 48 'darkred' => '#8b0000' , 49 'darksalmon' => '#e9967a' , 50 'darkseagreen' => '#8fbc8f' , 51 'darkslateblue' => '#483d8b' , 52 'darkslategray' => '#2f4f4f' , 53 'darkturquoise' => '#00ced1' , 54 'darkviolet' => '#9400d3' , 55 'deeppink' => '#ff1493' , 56 'deepskyblue' => '#00bfff' , 57 'dimgray' => '#696969' , 58 'dodgerblue' => '#1e90ff' , 59 'firebrick' => '#b22222' , 60 'floralwhite' => '#fffaf0' , 61 'forestgreen' => '#228b22' , 62 'fuchsia' => '#ff00ff' , 63 'gainsboro' => '#dcdcdc' , 64 'ghostwhite' => '#f8f8ff' , 65 'gold' => '#ffd700' , 66 'goldenrod' => '#daa520' , 67 'gray' => '#808080' , 68 'green' => '#008000' , 69 'greenyellow' => '#adff2f' , 70 'honeydew' => '#f0fff0' , 71 'hotpink' => '#ff69b4' , 72 'indianred' => '#cd5c5c' , 73 'indigo' => '#4b0082' , 74 'ivory' => '#fffff0' , 75 'khaki' => '#f0e68c' , 76 'lavender' => '#e6e6fa' , 77 'lavenderblush' => '#fff0f5' , 78 'lawngreen' => '#7cfc00' , 79 'lemonchiffon' => '#fffacd' , 80 'lightblue' => '#add8e6' , 81 'lightcoral' => '#f08080' , 82 'lightcyan' => '#e0ffff' , 83 'lightgoldenrodyellow' => '#fafad2' , 84 'lightgrey' => '#d3d3d3' , 85 'lightgreen' => '#90ee90' , 86 'lightpink' => '#ffb6c1' , 87 'lightsalmon' => '#ffa07a' , 88 'lightseagreen' => '#20b2aa' , 89 'lightskyblue' => '#87cefa' , 90 'lightslategray' => '#778899' , 91 'lightsteelblue' => '#b0c4de' , 92 'lightyellow' => '#ffffe0' , 93 'lime' => '#00ff00' , 94 'limegreen' => '#32cd32' , 95 'linen' => '#faf0e6' , 96 'magenta' => '#ff00ff' , 97 'maroon' => '#800000' , 98 'mediumaquamarine' => '#66cdaa' , 99 'mediumblue' => '#0000cd' , 100 'mediumorchid' => '#ba55d3' , 101 'mediumpurple' => '#9370d8' , 102 'mediumseagreen' => '#3cb371' , 103 'mediumslateblue' => '#7b68ee' , 104 'mediumspringgreen' => '#00fa9a' , 105 'mediumturquoise' => '#48d1cc' , 106 'mediumvioletred' => '#c71585' , 107 'midnightblue' => '#191970' , 108 'mintcream' => '#f5fffa' , 109 'mistyrose' => '#ffe4e1' , 110 'moccasin' => '#ffe4b5' , 111 'navajowhite' => '#ffdead' , 112 'navy' => '#000080' , 113 'oldlace' => '#fdf5e6' , 114 'olive' => '#808000' , 115 'olivedrab' => '#6b8e23' , 116 'orange' => '#ffa500' , 117 'orangered' => '#ff4500' , 118 'orchid' => '#da70d6' , 119 'palegoldenrod' => '#eee8aa' , 120 'palegreen' => '#98fb98' , 121 'paleturquoise' => '#afeeee' , 122 'palevioletred' => '#d87093' , 123 'papayawhip' => '#ffefd5' , 124 'peachpuff' => '#ffdab9' , 125 'peru' => '#cd853f' , 126 'pink' => '#ffc0cb' , 127 'plum' => '#dda0dd' , 128 'powderblue' => '#b0e0e6' , 129 'purple' => '#800080' , 130 'red' => '#ff0000' , 131 'rosybrown' => '#bc8f8f' , 132 'royalblue' => '#4169e1' , 133 'saddlebrown' => '#8b4513' , 134 'salmon' => '#fa8072' , 135 'sandybrown' => '#f4a460' , 136 'seagreen' => '#2e8b57' , 137 'seashell' => '#fff5ee' , 138 'sienna' => '#a0522d' , 139 'silver' => '#c0c0c0' , 140 'skyblue' => '#87ceeb' , 141 'slateblue' => '#6a5acd' , 142 'slategray' => '#708090' , 143 'snow' => '#fffafa' , 144 'springgreen' => '#00ff7f' , 145 'steelblue' => '#4682b4' , 146 'tan' => '#d2b48c' , 147 'teal' => '#008080' , 148 'thistle' => '#d8bfd8' , 149 'tomato' => '#ff6347' , 150 'turquoise' => '#40e0d0' , 151 'violet' => '#ee82ee' , 152 'wheat' => '#f5deb3' , 153 'white' => '#ffffff' , 154 'whitesmoke' => '#f5f5f5' , 155 'yellow' => '#ffff00' , 156 'yellowgreen' => '#9acd32' , 157 ]; 158 159 /** @inheritdoc */ 160 public function getType() 161 { 162 return 'formatting'; 163 } 164 /** @inheritdoc */ 165 public function getAllowedTypes() 166 { 167 return ['formatting', 'substition', 'disabled']; 168 } 169 /** @inheritdoc */ 170 public function getSort() 171 { 172 return 105; 173 } 174 /** @inheritdoc */ 175 public function connectTo($mode) 176 { 177 $this->Lexer->addEntryPattern('\[color=.*?\](?=.*?\x5B/color\x5D)', $mode, 'plugin_bbcode_color'); 178 } 179 /** @inheritdoc */ 180 public function postConnect() 181 { 182 $this->Lexer->addExitPattern('\[/color\]', 'plugin_bbcode_color'); 183 } 184 185 /** @inheritdoc */ 186 public function handle($match, $state, $pos, Handler $handler) 187 { 188 switch ($state) { 189 case DOKU_LEXER_ENTER: 190 $match = substr($match, 7, -1); 191 if (preg_match('/".+?"/', $match)) $match = substr($match, 1, -1); 192 return [$state, $match]; 193 194 case DOKU_LEXER_UNMATCHED: 195 return [$state, $match]; 196 197 case DOKU_LEXER_EXIT: 198 return [$state, '']; 199 } 200 return []; 201 } 202 203 /** @inheritdoc */ 204 public function render($format, Doku_Renderer $renderer, $data) 205 { 206 if ($format == 'xhtml') { 207 [$state, $match] = $data; 208 switch ($state) { 209 case DOKU_LEXER_ENTER: 210 if ($match = $this->isValid($match)) { 211 $renderer->doc .= '<span style="color:' . $renderer->_xmlEntities($match) . '">'; 212 } else { 213 $renderer->doc .= '<span>'; 214 } 215 break; 216 217 case DOKU_LEXER_UNMATCHED: 218 $renderer->doc .= $renderer->_xmlEntities($match); 219 break; 220 221 case DOKU_LEXER_EXIT: 222 $renderer->doc .= '</span>'; 223 break; 224 } 225 return true; 226 } 227 return false; 228 } 229 230 /** 231 * validate color value $c 232 * 233 * this is cut price validation - only to ensure the basic format is correct and there is nothing harmful 234 * three basic formats "colorname", "#fff[fff]", "rgb(255[%],255[%],255[%])" 235 * 236 * @param string $c color value to validate 237 * @return string valid color value or empty string if invalid 238 */ 239 protected function isValid($c) 240 { 241 $c = trim($c); 242 243 $pattern = "/^( 244 ([a-zA-Z]+)| #colorname - not verified 245 (\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))| #colorvalue 246 (rgb\(([0-9]{1,3}%?,){2}[0-9]{1,3}%?\)) #rgb triplet 247 )$/xD"; 248 249 if (preg_match($pattern, $c)) return $c; 250 if (!empty(self::BROWSERCOLORS[$c])) return self::BROWSERCOLORS[$c]; 251 return ''; 252 } 253} 254