[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 namespace phpbb\textformatter\s9e; 15 16 use s9e\TextFormatter\Configurator; 17 use s9e\TextFormatter\Configurator\Items\AttributeFilters\RegexpFilter; 18 use s9e\TextFormatter\Configurator\Items\UnsafeTemplate; 19 20 /** 21 * Creates s9e\TextFormatter objects 22 */ 23 class factory implements \phpbb\textformatter\cache_interface 24 { 25 /** 26 * @var \phpbb\textformatter\s9e\link_helper 27 */ 28 protected $link_helper; 29 30 /** 31 * @var \phpbb\cache\driver\driver_interface 32 */ 33 protected $cache; 34 35 /** 36 * @var string Path to the cache dir 37 */ 38 protected $cache_dir; 39 40 /** 41 * @var string Cache key used for the parser 42 */ 43 protected $cache_key_parser; 44 45 /** 46 * @var string Cache key used for the renderer 47 */ 48 protected $cache_key_renderer; 49 50 /** 51 * @var \phpbb\config\config 52 */ 53 protected $config; 54 55 /** 56 * @var array Custom tokens used in bbcode.html and their corresponding token from the definition 57 */ 58 protected $custom_tokens = array( 59 'email' => array('{DESCRIPTION}' => '{TEXT}'), 60 'flash' => array('{WIDTH}' => '{NUMBER1}', '{HEIGHT}' => '{NUMBER2}'), 61 'img' => array('{URL}' => '{IMAGEURL}'), 62 'list' => array('{LIST_TYPE}' => '{HASHMAP}'), 63 'quote' => array('{USERNAME}' => '{TEXT1}'), 64 'size' => array('{SIZE}' => '{FONTSIZE}'), 65 'url' => array('{DESCRIPTION}' => '{TEXT}'), 66 ); 67 68 /** 69 * @var \phpbb\textformatter\data_access 70 */ 71 protected $data_access; 72 73 /** 74 * @var array Default BBCode definitions 75 */ 76 protected $default_definitions = array( 77 'attachment' => '[ATTACHMENT index={NUMBER} filename={TEXT;useContent}]', 78 'b' => '[B]{TEXT}[/B]', 79 'code' => '[CODE lang={IDENTIFIER;optional}]{TEXT}[/CODE]', 80 'color' => '[COLOR={COLOR}]{TEXT}[/COLOR]', 81 'email' => '[EMAIL={EMAIL;useContent} subject={TEXT1;optional;postFilter=rawurlencode} body={TEXT2;optional;postFilter=rawurlencode}]{TEXT}[/EMAIL]', 82 'flash' => '[FLASH={NUMBER1},{NUMBER2} width={NUMBER1;postFilter=#flashwidth} height={NUMBER2;postFilter=#flashheight} url={URL;useContent} /]', 83 'i' => '[I]{TEXT}[/I]', 84 'img' => '[IMG src={IMAGEURL;useContent}]', 85 'list' => '[LIST type={HASHMAP=1:decimal,a:lower-alpha,A:upper-alpha,i:lower-roman,I:upper-roman;optional;postFilter=#simpletext} #createChild=LI]{TEXT}[/LIST]', 86 'li' => '[* $tagName=LI]{TEXT}[/*]', 87 'quote' => 88 "[QUOTE 89 author={TEXT1;optional} 90 post_id={UINT;optional} 91 post_url={URL;optional;postFilter=#false} 92 msg_id={UINT;optional} 93 msg_url={URL;optional;postFilter=#false} 94 profile_url={URL;optional;postFilter=#false} 95 time={UINT;optional} 96 url={URL;optional} 97 user_id={UINT;optional} 98 author={PARSE=/^\\[url=(?'url'.*?)](?'author'.*)\\[\\/url]$/i} 99 author={PARSE=/^\\[url](?'author'(?'url'.*?))\\[\\/url]$/i} 100 author={PARSE=/(?'url'https?:\\/\\/[^[\\]]+)/i} 101 ]{TEXT2}[/QUOTE]", 102 'size' => '[SIZE={FONTSIZE}]{TEXT}[/SIZE]', 103 'u' => '[U]{TEXT}[/U]', 104 'url' => '[URL={URL;useContent} $forceLookahead=true]{TEXT}[/URL]', 105 ); 106 107 /** 108 * @var array Default templates, taken from bbcode::bbcode_tpl() 109 */ 110 protected $default_templates = array( 111 'b' => '<span style="font-weight: bold"><xsl:apply-templates/></span>', 112 'i' => '<span style="font-style: italic"><xsl:apply-templates/></span>', 113 'u' => '<span style="text-decoration: underline"><xsl:apply-templates/></span>', 114 'img' => '<img src="{IMAGEURL}" class="postimage" alt="{L_IMAGE}"/>', 115 'size' => '<span><xsl:attribute name="style"><xsl:text>font-size: </xsl:text><xsl:value-of select="substring(@size, 1, 4)"/><xsl:text>%; line-height: normal</xsl:text></xsl:attribute><xsl:apply-templates/></span>', 116 'color' => '<span style="color: {COLOR}"><xsl:apply-templates/></span>', 117 'email' => '<a> 118 <xsl:attribute name="href"> 119 <xsl:text>mailto:</xsl:text> 120 <xsl:value-of select="@email"/> 121 <xsl:if test="@subject or @body"> 122 <xsl:text>?</xsl:text> 123 <xsl:if test="@subject">subject=<xsl:value-of select="@subject"/></xsl:if> 124 <xsl:if test="@body"><xsl:if test="@subject">&</xsl:if>body=<xsl:value-of select="@body"/></xsl:if> 125 </xsl:if> 126 </xsl:attribute> 127 <xsl:apply-templates/> 128 </a>', 129 ); 130 131 /** 132 * @var \phpbb\event\dispatcher_interface 133 */ 134 protected $dispatcher; 135 136 /** 137 * @var \phpbb\log\log_interface 138 */ 139 protected $log; 140 141 /** 142 * Constructor 143 * 144 * @param \phpbb\textformatter\data_access $data_access 145 * @param \phpbb\cache\driver\driver_interface $cache 146 * @param \phpbb\event\dispatcher_interface $dispatcher 147 * @param \phpbb\config\config $config 148 * @param \phpbb\textformatter\s9e\link_helper $link_helper 149 * @param \phpbb\log\log_interface $log 150 * @param string $cache_dir Path to the cache dir 151 * @param string $cache_key_parser Cache key used for the parser 152 * @param string $cache_key_renderer Cache key used for the renderer 153 */ 154 public function __construct(\phpbb\textformatter\data_access $data_access, \phpbb\cache\driver\driver_interface $cache, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\config\config $config, \phpbb\textformatter\s9e\link_helper $link_helper, \phpbb\log\log_interface $log, $cache_dir, $cache_key_parser, $cache_key_renderer) 155 { 156 $this->link_helper = $link_helper; 157 $this->cache = $cache; 158 $this->cache_dir = $cache_dir; 159 $this->cache_key_parser = $cache_key_parser; 160 $this->cache_key_renderer = $cache_key_renderer; 161 $this->config = $config; 162 $this->data_access = $data_access; 163 $this->dispatcher = $dispatcher; 164 $this->log = $log; 165 } 166 167 /** 168 * {@inheritdoc} 169 */ 170 public function invalidate() 171 { 172 $this->regenerate(); 173 } 174 175 /** 176 * {@inheritdoc} 177 * 178 * Will remove old renderers from the cache dir but won't touch the current renderer 179 */ 180 public function tidy() 181 { 182 // Get the name of current renderer 183 $renderer_data = $this->cache->get($this->cache_key_renderer); 184 $renderer_file = ($renderer_data) ? $renderer_data['class'] . '.php' : null; 185 186 foreach (glob($this->cache_dir . 's9e_*') as $filename) 187 { 188 // Only remove the file if it's not the current renderer 189 if (!$renderer_file || substr($filename, -strlen($renderer_file)) !== $renderer_file) 190 { 191 unlink($filename); 192 } 193 } 194 } 195 196 /** 197 * Generate and return a new configured instance of s9e\TextFormatter\Configurator 198 * 199 * @return Configurator 200 */ 201 public function get_configurator() 202 { 203 // Create a new Configurator 204 $configurator = new Configurator; 205 206 /** 207 * Modify the s9e\TextFormatter configurator before the default settings are set 208 * 209 * @event core.text_formatter_s9e_configure_before 210 * @var \s9e\TextFormatter\Configurator configurator Configurator instance 211 * @since 3.2.0-a1 212 */ 213 $vars = array('configurator'); 214 extract($this->dispatcher->trigger_event('core.text_formatter_s9e_configure_before', compact($vars))); 215 216 // Reset the list of allowed schemes 217 foreach ($configurator->urlConfig->getAllowedSchemes() as $scheme) 218 { 219 $configurator->urlConfig->disallowScheme($scheme); 220 } 221 foreach (array_filter(explode(',', $this->config['allowed_schemes_links'])) as $scheme) 222 { 223 $configurator->urlConfig->allowScheme(trim($scheme)); 224 } 225 226 // Convert newlines to br elements by default 227 $configurator->rootRules->enableAutoLineBreaks(); 228 229 // Don't automatically ignore text in places where text is not allowed 230 $configurator->rulesGenerator->remove('IgnoreTextIfDisallowed'); 231 232 // Don't remove comments and instead convert them to xsl:comment elements 233 $configurator->templateNormalizer->remove('RemoveComments'); 234 $configurator->templateNormalizer->add('TransposeComments'); 235 236 // Set the rendering engine and configure it to save to the cache dir 237 $configurator->rendering->engine = 'PHP'; 238 $configurator->rendering->engine->cacheDir = $this->cache_dir; 239 $configurator->rendering->engine->defaultClassPrefix = 's9e_renderer_'; 240 $configurator->rendering->engine->enableQuickRenderer = true; 241 242 // Create custom filters for BBCode tokens that are supported in phpBB but not in 243 // s9e\TextFormatter 244 $filter = new RegexpFilter('#^' . get_preg_expression('relative_url') . '$#Du'); 245 $configurator->attributeFilters->add('#local_url', $filter); 246 $configurator->attributeFilters->add('#relative_url', $filter); 247 248 // INTTEXT regexp from acp_bbcodes 249 $filter = new RegexpFilter('!^([\p{L}\p{N}\-+,_. ]+)$!Du'); 250 $configurator->attributeFilters->add('#inttext', $filter); 251 252 // Create custom filters for Flash restrictions, which use the same values as the image 253 // restrictions but have their own error message 254 $configurator->attributeFilters 255 ->add('#flashheight', __NAMESPACE__ . '\\parser::filter_flash_height') 256 ->addParameterByName('max_img_height') 257 ->addParameterByName('logger'); 258 259 $configurator->attributeFilters 260 ->add('#flashwidth', __NAMESPACE__ . '\\parser::filter_flash_width') 261 ->addParameterByName('max_img_width') 262 ->addParameterByName('logger'); 263 264 // Create a custom filter for phpBB's per-mode font size limits 265 $configurator->attributeFilters 266 ->add('#fontsize', __NAMESPACE__ . '\\parser::filter_font_size') 267 ->addParameterByName('max_font_size') 268 ->addParameterByName('logger') 269 ->markAsSafeInCSS(); 270 271 // Create a custom filter for image URLs 272 $configurator->attributeFilters 273 ->add('#imageurl', __NAMESPACE__ . '\\parser::filter_img_url') 274 ->addParameterByName('urlConfig') 275 ->addParameterByName('logger') 276 ->markAsSafeAsURL() 277 ->setJS('UrlFilter.filter'); 278 279 // Add default BBCodes 280 foreach ($this->get_default_bbcodes($configurator) as $bbcode) 281 { 282 $this->add_bbcode($configurator, $bbcode['usage'], $bbcode['template']); 283 } 284 if (isset($configurator->tags['QUOTE'])) 285 { 286 // Remove the nesting limit and let other services remove quotes at parsing time 287 $configurator->tags['QUOTE']->nestingLimit = PHP_INT_MAX; 288 } 289 290 // Modify the template to disable images/flash depending on user's settings 291 foreach (array('FLASH', 'IMG') as $name) 292 { 293 $tag = $configurator->tags[$name]; 294 $tag->template = '<xsl:choose><xsl:when test="$S_VIEW' . $name . '">' . $tag->template . '</xsl:when><xsl:otherwise><xsl:apply-templates/></xsl:otherwise></xsl:choose>'; 295 } 296 297 // Load custom BBCodes 298 foreach ($this->data_access->get_bbcodes() as $row) 299 { 300 // Insert the board's URL before {LOCAL_URL} tokens 301 $tpl = preg_replace_callback( 302 '#\\{LOCAL_URL\\d*\\}#', 303 function ($m) 304 { 305 return generate_board_url() . '/' . $m[0]; 306 }, 307 $row['bbcode_tpl'] 308 ); 309 $this->add_bbcode($configurator, $row['bbcode_match'], $tpl); 310 } 311 312 // Load smilies 313 foreach ($this->data_access->get_smilies() as $row) 314 { 315 $configurator->Emoticons->set( 316 $row['code'], 317 '<img class="smilies" src="{$T_SMILIES_PATH}/' . $this->escape_html_attribute($row['smiley_url']) . '" width="' . $row['smiley_width'] . '" height="' . $row['smiley_height'] . '" alt="{.}" title="' . $this->escape_html_attribute($row['emotion']) . '"/>' 318 ); 319 } 320 321 if (isset($configurator->Emoticons)) 322 { 323 // Force emoticons to be rendered as text if $S_VIEWSMILIES is not set 324 $configurator->Emoticons->notIfCondition = 'not($S_VIEWSMILIES)'; 325 326 // Only parse emoticons at the beginning of the text or if they're preceded by any 327 // one of: a new line, a space, a dot, or a right square bracket 328 $configurator->Emoticons->notAfter = '[^\\n .\\]]'; 329 330 // Ignore emoticons that are immediately followed by a "word" character 331 $configurator->Emoticons->notBefore = '\\w'; 332 } 333 334 // Load the censored words 335 $censor = $this->data_access->get_censored_words(); 336 if (!empty($censor)) 337 { 338 // Use a namespaced tag to avoid collisions 339 $configurator->plugins->load('Censor', array('tagName' => 'censor:tag')); 340 foreach ($censor as $row) 341 { 342 $configurator->Censor->add($row['word'], $row['replacement']); 343 } 344 } 345 346 // Load the magic links plugins. We do that after BBCodes so that they use the same tags 347 $this->configure_autolink($configurator); 348 349 // Register some vars with a default value. Those should be set at runtime by whatever calls 350 // the parser 351 $configurator->registeredVars['max_font_size'] = 0; 352 $configurator->registeredVars['max_img_height'] = 0; 353 $configurator->registeredVars['max_img_width'] = 0; 354 355 // Load the Emoji plugin and modify its tag's template to obey viewsmilies 356 $tag = $configurator->Emoji->getTag(); 357 $tag->template = '<xsl:choose> 358 <xsl:when test="@tseq"> 359 <img alt="{.}" class="emoji" draggable="false" src="//twemoji.maxcdn.com/2/svg/{@tseq}.svg"/> 360 </xsl:when> 361 <xsl:otherwise> 362 <img alt="{.}" class="emoji" draggable="false" src="https://cdn.jsdelivr.net/gh/s9e/emoji-assets-twemoji@11.2/dist/svgz/{@seq}.svgz"/> 363 </xsl:otherwise> 364 </xsl:choose>'; 365 $tag->template = '<xsl:choose><xsl:when test="$S_VIEWSMILIES">' . str_replace('class="emoji"', 'class="emoji smilies"', $tag->template) . '</xsl:when><xsl:otherwise><xsl:value-of select="."/></xsl:otherwise></xsl:choose>'; 366 367 /** 368 * Modify the s9e\TextFormatter configurator after the default settings are set 369 * 370 * @event core.text_formatter_s9e_configure_after 371 * @var \s9e\TextFormatter\Configurator configurator Configurator instance 372 * @since 3.2.0-a1 373 */ 374 $vars = array('configurator'); 375 extract($this->dispatcher->trigger_event('core.text_formatter_s9e_configure_after', compact($vars))); 376 377 return $configurator; 378 } 379 380 /** 381 * Regenerate and cache a new parser and renderer 382 * 383 * @return array Associative array with at least two elements: "parser" and "renderer" 384 */ 385 public function regenerate() 386 { 387 $configurator = $this->get_configurator(); 388 389 // Get the censor helper and remove the Censor plugin if applicable 390 if (isset($configurator->Censor)) 391 { 392 $censor = $configurator->Censor->getHelper(); 393 unset($configurator->Censor); 394 unset($configurator->tags['censor:tag']); 395 } 396 397 $objects = $configurator->finalize(); 398 399 /** 400 * Access the objects returned by finalize() before they are saved to cache 401 * 402 * @event core.text_formatter_s9e_configure_finalize 403 * @var array objects Array containing a "parser" object, a "renderer" object and optionally a "js" string 404 * @since 3.2.2-RC1 405 */ 406 $vars = array('objects'); 407 extract($this->dispatcher->trigger_event('core.text_formatter_s9e_configure_finalize', compact($vars))); 408 409 $parser = $objects['parser']; 410 $renderer = $objects['renderer']; 411 412 // Cache the parser as-is 413 $this->cache->put($this->cache_key_parser, $parser); 414 415 // We need to cache the name of the renderer's generated class 416 $renderer_data = array('class' => get_class($renderer)); 417 if (isset($censor)) 418 { 419 $renderer_data['censor'] = $censor; 420 } 421 $this->cache->put($this->cache_key_renderer, $renderer_data); 422 423 return array('parser' => $parser, 'renderer' => $renderer); 424 } 425 426 /** 427 * Add a BBCode to given configurator 428 * 429 * @param Configurator $configurator 430 * @param string $usage 431 * @param string $template 432 * @return void 433 */ 434 protected function add_bbcode(Configurator $configurator, $usage, $template) 435 { 436 try 437 { 438 $configurator->BBCodes->addCustom($usage, new UnsafeTemplate($template)); 439 } 440 catch (\Exception $e) 441 { 442 $this->log->add('critical', null, null, 'LOG_BBCODE_CONFIGURATION_ERROR', false, [$usage, $e->getMessage()]); 443 } 444 } 445 446 /** 447 * Configure the Autolink / Autoemail plugins used to linkify text 448 * 449 * @param \s9e\TextFormatter\Configurator $configurator 450 * @return void 451 */ 452 protected function configure_autolink(Configurator $configurator) 453 { 454 $configurator->plugins->load('Autoemail'); 455 $configurator->plugins->load('Autolink', array('matchWww' => true)); 456 457 // Add a tag filter that creates a tag that stores and replace the 458 // content of a link created by the Autolink plugin 459 $configurator->Autolink->getTag()->filterChain 460 ->add(array($this->link_helper, 'generate_link_text_tag')) 461 ->resetParameters() 462 ->addParameterByName('tag') 463 ->addParameterByName('parser'); 464 465 // Create a tag that will be used to display the truncated text by 466 // replacing the original content with the content of the @text attribute 467 $tag = $configurator->tags->add('LINK_TEXT'); 468 $tag->attributes->add('text'); 469 $tag->template = '<xsl:value-of select="@text"/>'; 470 471 $tag->filterChain 472 ->add(array($this->link_helper, 'truncate_local_url')) 473 ->resetParameters() 474 ->addParameterByName('tag') 475 ->addParameterByValue(generate_board_url() . '/'); 476 $tag->filterChain 477 ->add(array($this->link_helper, 'truncate_text')) 478 ->resetParameters() 479 ->addParameterByName('tag'); 480 $tag->filterChain 481 ->add(array($this->link_helper, 'cleanup_tag')) 482 ->resetParameters() 483 ->addParameterByName('tag') 484 ->addParameterByName('parser'); 485 } 486 487 /** 488 * Escape a literal to be used in an HTML attribute in an XSL template 489 * 490 * Escapes "HTML special chars" for obvious reasons and curly braces to avoid them 491 * being interpreted as an attribute value template 492 * 493 * @param string $value Original string 494 * @return string Escaped string 495 */ 496 protected function escape_html_attribute($value) 497 { 498 return htmlspecialchars(strtr($value, ['{' => '{{', '}' => '}}']), ENT_COMPAT | ENT_XML1, 'UTF-8'); 499 } 500 501 /** 502 * Return the default BBCodes configuration 503 * 504 * @return array 2D array. Each element has a 'usage' key, a 'template' key, and an optional 'options' key 505 */ 506 protected function get_default_bbcodes($configurator) 507 { 508 // For each BBCode, build an associative array matching style_ids to their template 509 $templates = array(); 510 foreach ($this->data_access->get_styles_templates() as $style_id => $data) 511 { 512 foreach ($this->extract_templates($data['template']) as $bbcode_name => $template) 513 { 514 $templates[$bbcode_name][$style_id] = $template; 515 } 516 517 // Add default templates wherever missing, or for BBCodes that were not specified in 518 // this template's bitfield. For instance, prosilver has a custom template for b but its 519 // bitfield does not enable it so the default template is used instead 520 foreach ($this->default_templates as $bbcode_name => $template) 521 { 522 if (!isset($templates[$bbcode_name][$style_id]) || !in_array($bbcode_name, $data['bbcodes'], true)) 523 { 524 $templates[$bbcode_name][$style_id] = $template; 525 } 526 } 527 } 528 529 // Replace custom tokens and normalize templates 530 foreach ($templates as $bbcode_name => $style_templates) 531 { 532 foreach ($style_templates as $i => $template) 533 { 534 if (isset($this->custom_tokens[$bbcode_name])) 535 { 536 $template = strtr($template, $this->custom_tokens[$bbcode_name]); 537 } 538 539 $templates[$bbcode_name][$i] = $configurator->templateNormalizer->normalizeTemplate($template); 540 } 541 } 542 543 $bbcodes = array(); 544 foreach ($this->default_definitions as $bbcode_name => $usage) 545 { 546 $bbcodes[$bbcode_name] = array( 547 'usage' => $usage, 548 'template' => $this->merge_templates($templates[$bbcode_name]), 549 ); 550 } 551 552 return $bbcodes; 553 } 554 555 /** 556 * Extract and recompose individual BBCode templates from a style's template file 557 * 558 * @param string $template Style template (bbcode.html) 559 * @return array Associative array matching BBCode names to their template 560 */ 561 protected function extract_templates($template) 562 { 563 // Capture the template fragments 564 // Allow either phpBB template or the Twig syntax 565 preg_match_all('#<!-- BEGIN (.*?) -->(.*?)<!-- END .*? -->#s', $template, $matches, PREG_SET_ORDER) ?: 566 preg_match_all('#{% for (.*?) in .*? %}(.*?){% endfor %}#s', $template, $matches, PREG_SET_ORDER); 567 568 $fragments = array(); 569 foreach ($matches as $match) 570 { 571 // Normalize the whitespace 572 $fragment = preg_replace('#>\\n\\t*<#', '><', trim($match[2])); 573 574 $fragments[$match[1]] = $fragment; 575 } 576 577 // Automatically recompose templates split between *_open and *_close 578 foreach ($fragments as $fragment_name => $fragment) 579 { 580 if (preg_match('#^(\\w+)_close$#', $fragment_name, $match)) 581 { 582 $bbcode_name = $match[1]; 583 584 if (isset($fragments[$bbcode_name . '_open'])) 585 { 586 $templates[$bbcode_name] = $fragments[$bbcode_name . '_open'] . '<xsl:apply-templates/>' . $fragment; 587 } 588 } 589 } 590 591 // Manually recompose and overwrite irregular templates 592 $templates['list'] = 593 '<xsl:choose> 594 <xsl:when test="not(@type)"> 595 ' . $fragments['ulist_open_default'] . '<xsl:apply-templates/>' . $fragments['ulist_close'] . ' 596 </xsl:when> 597 <xsl:when test="contains(\'upperlowerdecim\',substring(@type,1,5))"> 598 ' . $fragments['olist_open'] . '<xsl:apply-templates/>' . $fragments['olist_close'] . ' 599 </xsl:when> 600 <xsl:otherwise> 601 ' . $fragments['ulist_open'] . '<xsl:apply-templates/>' . $fragments['ulist_close'] . ' 602 </xsl:otherwise> 603 </xsl:choose>'; 604 605 $templates['li'] = $fragments['listitem'] . '<xsl:apply-templates/>' . $fragments['listitem_close']; 606 607 // Replace the regular quote template with the extended quote template if available 608 if (isset($fragments['quote_extended'])) 609 { 610 $templates['quote'] = $fragments['quote_extended']; 611 } 612 613 // The [attachment] BBCode uses the inline_attachment template to output a comment that 614 // is post-processed by parse_attachments() 615 $templates['attachment'] = $fragments['inline_attachment_open'] . '<xsl:comment> ia<xsl:value-of select="@index"/> </xsl:comment><xsl:value-of select="@filename"/><xsl:comment> ia<xsl:value-of select="@index"/> </xsl:comment>' . $fragments['inline_attachment_close']; 616 617 // Add fragments as templates 618 foreach ($fragments as $fragment_name => $fragment) 619 { 620 if (preg_match('#^\\w+$#', $fragment_name)) 621 { 622 $templates[$fragment_name] = $fragment; 623 } 624 } 625 626 // Keep only templates that are named after an existing BBCode 627 $templates = array_intersect_key($templates, $this->default_definitions); 628 629 return $templates; 630 } 631 632 /** 633 * Merge the templates from any number of styles into one BBCode template 634 * 635 * When multiple templates are available for the same BBCode (because of multiple styles) we 636 * merge them into a single template that uses an xsl:choose construct that determines which 637 * style to use at rendering time. 638 * 639 * @param array $style_templates Associative array matching style_ids to their template 640 * @return string 641 */ 642 protected function merge_templates(array $style_templates) 643 { 644 // Return the template as-is if there's only one style or all styles share the same template 645 if (count(array_unique($style_templates)) === 1) 646 { 647 return end($style_templates); 648 } 649 650 // Group identical templates together 651 $grouped_templates = array(); 652 foreach ($style_templates as $style_id => $style_template) 653 { 654 $grouped_templates[$style_template][] = '$STYLE_ID=' . $style_id; 655 } 656 657 // Sort templates by frequency descending 658 $templates_cnt = array_map('sizeof', $grouped_templates); 659 array_multisort($grouped_templates, $templates_cnt); 660 661 // Remove the most frequent template from the list; It becomes the default 662 reset($grouped_templates); 663 $default_template = key($grouped_templates); 664 unset($grouped_templates[$default_template]); 665 666 // Build an xsl:choose switch 667 $template = '<xsl:choose>'; 668 foreach ($grouped_templates as $style_template => $exprs) 669 { 670 $template .= '<xsl:when test="' . implode(' or ', $exprs) . '">' . $style_template . '</xsl:when>'; 671 } 672 $template .= '<xsl:otherwise>' . $default_template . '</xsl:otherwise></xsl:choose>'; 673 674 return $template; 675 } 676 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |