mirror of
https://github.com/phpbb/phpbb.git
synced 2025-05-09 00:55:23 +02:00
Merge branch '3.2.x'
This commit is contained in:
commit
52cde712f2
@ -26,6 +26,11 @@ services:
|
|||||||
text_formatter.utils:
|
text_formatter.utils:
|
||||||
alias: text_formatter.s9e.utils
|
alias: text_formatter.s9e.utils
|
||||||
|
|
||||||
|
text_formatter.s9e.bbcode_merger:
|
||||||
|
class: phpbb\textformatter\s9e\bbcode_merger
|
||||||
|
arguments:
|
||||||
|
- '@text_formatter.s9e.factory'
|
||||||
|
|
||||||
text_formatter.s9e.factory:
|
text_formatter.s9e.factory:
|
||||||
class: phpbb\textformatter\s9e\factory
|
class: phpbb\textformatter\s9e\factory
|
||||||
arguments:
|
arguments:
|
||||||
|
180
phpBB/phpbb/textformatter/s9e/bbcode_merger.php
Normal file
180
phpBB/phpbb/textformatter/s9e/bbcode_merger.php
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This file is part of the phpBB Forum Software package.
|
||||||
|
*
|
||||||
|
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||||
|
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||||
|
*
|
||||||
|
* For full copyright and license information, please see
|
||||||
|
* the docs/CREDITS.txt file.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace phpbb\textformatter\s9e;
|
||||||
|
|
||||||
|
use phpbb\textformatter\s9e\factory;
|
||||||
|
use s9e\TextFormatter\Configurator\Helpers\TemplateHelper;
|
||||||
|
use s9e\TextFormatter\Configurator\Items\UnsafeTemplate;
|
||||||
|
|
||||||
|
class bbcode_merger
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \s9e\TextFormatter\Configurator $configurator Configurator instance used to inspect BBCodes
|
||||||
|
*/
|
||||||
|
protected $configurator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \phpbb\textformatter\s9e\factory $factory
|
||||||
|
*/
|
||||||
|
public function __construct(factory $factory)
|
||||||
|
{
|
||||||
|
$this->configurator = $factory->get_configurator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge two BBCode definitions
|
||||||
|
*
|
||||||
|
* All of the arrays contain a "usage" element and a "template" element
|
||||||
|
*
|
||||||
|
* @param array $without BBCode definition without an attribute
|
||||||
|
* @param array $with BBCode definition with an attribute
|
||||||
|
* @return array Merged definition
|
||||||
|
*/
|
||||||
|
public function merge_bbcodes(array $without, array $with)
|
||||||
|
{
|
||||||
|
$without = $this->create_bbcode($without);
|
||||||
|
$with = $this->create_bbcode($with);
|
||||||
|
|
||||||
|
// Select the appropriate strategy for merging this BBCode
|
||||||
|
if ($this->is_content_bbcode($without, $with))
|
||||||
|
{
|
||||||
|
$merged = $this->merge_content_bbcode($without, $with);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$merged = $this->merge_optional_bbcode($without, $with);
|
||||||
|
}
|
||||||
|
|
||||||
|
$merged['template'] = $this->normalize_template($merged['template']);
|
||||||
|
|
||||||
|
return $merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a custom BBCode for inspection
|
||||||
|
*
|
||||||
|
* @param array $definition Original BBCode definition
|
||||||
|
* @return array Updated definition containing a BBCode object and a Tag
|
||||||
|
*/
|
||||||
|
protected function create_bbcode(array $definition)
|
||||||
|
{
|
||||||
|
$bbcode = $this->configurator->BBCodes->addCustom(
|
||||||
|
$definition['usage'],
|
||||||
|
new UnsafeTemplate($definition['template'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$definition['bbcode'] = $bbcode;
|
||||||
|
$definition['tag'] = $this->configurator->tags[$bbcode->tagName];
|
||||||
|
|
||||||
|
return $definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indent given template for readability
|
||||||
|
*
|
||||||
|
* @param string $template
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function indent_template($template)
|
||||||
|
{
|
||||||
|
$dom = TemplateHelper::loadTemplate($template);
|
||||||
|
$dom->formatOutput = true;
|
||||||
|
$template = TemplateHelper::saveTemplate($dom);
|
||||||
|
|
||||||
|
// Remove the first level of indentation if the template starts with whitespace
|
||||||
|
if (preg_match('(^\\n +)', $template, $m))
|
||||||
|
{
|
||||||
|
$template = str_replace($m[0], "\n", $template);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the two definitions form a "content"-style BBCode
|
||||||
|
*
|
||||||
|
* Such BBCodes include the [URL] BBCode, which uses its text content as
|
||||||
|
* attribute if none is provided
|
||||||
|
*
|
||||||
|
* @param array $without BBCode definition without an attribute
|
||||||
|
* @param array $with BBCode definition with an attribute
|
||||||
|
* @return array Merged definition
|
||||||
|
*/
|
||||||
|
protected function is_content_bbcode(array $without, array $with)
|
||||||
|
{
|
||||||
|
// Test whether we find the same non-TEXT token between "]" and "[" in the usage
|
||||||
|
// as between ">" and "<" in the template
|
||||||
|
return (preg_match('(\\]\\s*(\\{(?!TEXT)[^}]+\\})\\s*\\[)', $without['usage'], $m)
|
||||||
|
&& preg_match('(>[^<]*?' . preg_quote($m[1]) . '[^>]*?<)s', $without['template']));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge the two BBCode definitions of a "content"-style BBCode
|
||||||
|
*
|
||||||
|
* @param array $without BBCode definition without an attribute
|
||||||
|
* @param array $with BBCode definition with an attribute
|
||||||
|
* @return array Merged definition
|
||||||
|
*/
|
||||||
|
protected function merge_content_bbcode(array $without, array $with)
|
||||||
|
{
|
||||||
|
// Convert [X={X}] into [X={X;useContent}]
|
||||||
|
$usage = preg_replace('(\\})', ';useContent}', $with['usage'], 1);
|
||||||
|
|
||||||
|
// Use the template from the definition that uses an attribute
|
||||||
|
$template = $with['tag']->template;
|
||||||
|
|
||||||
|
return ['usage' => $usage, 'template' => $template];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge the two BBCode definitions of a BBCode with an optional argument
|
||||||
|
*
|
||||||
|
* Such BBCodes include the [QUOTE] BBCode, which takes an optional argument
|
||||||
|
* but otherwise does not behave differently
|
||||||
|
*
|
||||||
|
* @param array $without BBCode definition without an attribute
|
||||||
|
* @param array $with BBCode definition with an attribute
|
||||||
|
* @return array Merged definition
|
||||||
|
*/
|
||||||
|
protected function merge_optional_bbcode(array $without, array $with)
|
||||||
|
{
|
||||||
|
// Convert [X={X}] into [X={X?}]
|
||||||
|
$usage = preg_replace('(\\})', '?}', $with['usage'], 1);
|
||||||
|
|
||||||
|
// Build a template for both versions
|
||||||
|
$template = '<xsl:choose><xsl:when test="@' . $with['bbcode']->defaultAttribute . '">' . $with['tag']->template . '</xsl:when><xsl:otherwise>' . $without['tag']->template . '</xsl:otherwise></xsl:choose>';
|
||||||
|
|
||||||
|
return ['usage' => $usage, 'template' => $template];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a template
|
||||||
|
*
|
||||||
|
* @param string $template
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function normalize_template($template)
|
||||||
|
{
|
||||||
|
// Normalize the template to simplify it
|
||||||
|
$template = $this->configurator->templateNormalizer->normalizeTemplate($template);
|
||||||
|
|
||||||
|
// Convert xsl:value-of elements back to {L_} tokens where applicable
|
||||||
|
$template = preg_replace('(<xsl:value-of select="\\$(L_\\w+)"/>)', '{$1}', $template);
|
||||||
|
|
||||||
|
// Beautify the template
|
||||||
|
$template = $this->indent_template($template);
|
||||||
|
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
}
|
280
tests/text_formatter/s9e/bbcode_merger_test.php
Normal file
280
tests/text_formatter/s9e/bbcode_merger_test.php
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This file is part of the phpBB Forum Software package.
|
||||||
|
*
|
||||||
|
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||||
|
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||||
|
*
|
||||||
|
* For full copyright and license information, please see
|
||||||
|
* the docs/CREDITS.txt file.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class phpbb_textformatter_s9e_bbcode_merger_test extends phpbb_test_case
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider get_merge_bbcodes_tests
|
||||||
|
*/
|
||||||
|
public function test_merge_bbcodes($usage_without, $template_without, $usage_with, $template_with, $expected_usage, $expected_template)
|
||||||
|
{
|
||||||
|
$container = $this->get_test_case_helpers()->set_s9e_services();
|
||||||
|
$factory = $container->get('text_formatter.s9e.factory');
|
||||||
|
$bbcode_merger = new \phpbb\textformatter\s9e\bbcode_merger($factory);
|
||||||
|
|
||||||
|
$without = ['usage' => $usage_without, 'template' => $template_without];
|
||||||
|
$with = ['usage' => $usage_with, 'template' => $template_with];
|
||||||
|
$merged = $bbcode_merger->merge_bbcodes($without, $with);
|
||||||
|
|
||||||
|
// Normalize the expected template's whitespace to match the default indentation
|
||||||
|
$expected_template = str_replace("\n\t\t\t\t", "\n", $expected_template);
|
||||||
|
$expected_template = str_replace("\t", ' ', $expected_template);
|
||||||
|
|
||||||
|
$this->assertSame($expected_usage, $merged['usage']);
|
||||||
|
$this->assertSame($expected_template, $merged['template']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_merge_bbcodes_tests()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'[x]{TEXT}[/x]',
|
||||||
|
'<b>{TEXT}</b>',
|
||||||
|
|
||||||
|
'[x={TEXT1}]{TEXT}[/x]',
|
||||||
|
'<b title="{TEXT1}">{TEXT}</b>',
|
||||||
|
|
||||||
|
'[x={TEXT1?}]{TEXT}[/x]',
|
||||||
|
'<b>
|
||||||
|
<xsl:if test="@x">
|
||||||
|
<xsl:attribute name="title">
|
||||||
|
<xsl:value-of select="@x"/>
|
||||||
|
</xsl:attribute>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</b>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// The tokens' numbering differs between versions
|
||||||
|
'[x]{TEXT}[/x]',
|
||||||
|
'<b>{TEXT}</b>',
|
||||||
|
|
||||||
|
'[x={TEXT1}]{TEXT2}[/x]',
|
||||||
|
'<b title="{TEXT1}">{TEXT2}</b>',
|
||||||
|
|
||||||
|
'[x={TEXT1?}]{TEXT2}[/x]',
|
||||||
|
'<b>
|
||||||
|
<xsl:if test="@x">
|
||||||
|
<xsl:attribute name="title">
|
||||||
|
<xsl:value-of select="@x"/>
|
||||||
|
</xsl:attribute>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</b>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'[x]{URL}[/x]',
|
||||||
|
'<a href="{URL}">{URL}</a>',
|
||||||
|
|
||||||
|
'[x={URL}]{TEXT}[/x]',
|
||||||
|
'<a href="{URL}">{TEXT}</a>',
|
||||||
|
|
||||||
|
'[x={URL;useContent}]{TEXT}[/x]',
|
||||||
|
'<a href="{@x}">
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</a>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'[x]{URL}[/x]',
|
||||||
|
'<a href="{URL}">{L_GO_TO}: {URL}</a>',
|
||||||
|
|
||||||
|
'[x={URL}]{TEXT}[/x]',
|
||||||
|
'<a href="{URL}">{L_GO_TO}: {TEXT}</a>',
|
||||||
|
|
||||||
|
'[x={URL;useContent}]{TEXT}[/x]',
|
||||||
|
'<a href="{@x}">{L_GO_TO}: <xsl:apply-templates/></a>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// Test that unsafe BBCodes can still be merged
|
||||||
|
'[script]{TEXT}[/script]',
|
||||||
|
'<script>{TEXT}</script>',
|
||||||
|
|
||||||
|
'[script={TEXT1}]{TEXT2}[/script]',
|
||||||
|
'<script type="{TEXT1}">{TEXT2}</script>',
|
||||||
|
|
||||||
|
'[script={TEXT1?}]{TEXT2}[/script]',
|
||||||
|
'<script>
|
||||||
|
<xsl:if test="@script">
|
||||||
|
<xsl:attribute name="type">
|
||||||
|
<xsl:value-of select="@script"/>
|
||||||
|
</xsl:attribute>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</script>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// https://www.phpbb.com/community/viewtopic.php?p=14848281#p14848281
|
||||||
|
'[note]{TEXT}[/note]',
|
||||||
|
'<span class="prime_bbcode_note_spur" onmouseover="show_note(this);" onmouseout="hide_note(this);" onclick="lock_note(this);"></span><span class="prime_bbcode_note">{TEXT}</span>',
|
||||||
|
|
||||||
|
'[note={TEXT1}]{TEXT2}[/note]',
|
||||||
|
'<span class="prime_bbcode_note_text" onmouseover="show_note(this);" onmouseout="hide_note(this);" onclick="lock_note(this);">{TEXT1}</span><span class="prime_bbcode_note_spur" onmouseover="show_note(this);" onmouseout="hide_note(this);" onclick="lock_note(this);"></span><span class="prime_bbcode_note">{TEXT2}</span>',
|
||||||
|
|
||||||
|
'[note={TEXT1?}]{TEXT2}[/note]',
|
||||||
|
'<xsl:if test="@note">
|
||||||
|
<span class="prime_bbcode_note_text" onmouseover="show_note(this);" onmouseout="hide_note(this);" onclick="lock_note(this);">
|
||||||
|
<xsl:value-of select="@note"/>
|
||||||
|
</span>
|
||||||
|
</xsl:if>
|
||||||
|
<span class="prime_bbcode_note_spur" onmouseover="show_note(this);" onmouseout="hide_note(this);" onclick="lock_note(this);"/>
|
||||||
|
<span class="prime_bbcode_note">
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</span>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// https://www.phpbb.com/community/viewtopic.php?p=14768441#p14768441
|
||||||
|
'[MI]{TEXT}[/MI]',
|
||||||
|
'<span style="color:red">MI:</span> <span style="color:#f6efe2">{TEXT}</span>',
|
||||||
|
|
||||||
|
'[MI={TEXT2}]{TEXT1}[/MI]',
|
||||||
|
'<span style="color:red">MI for: "{TEXT2}":</span> <span style="color:#f6efe2">{TEXT1}</span>',
|
||||||
|
|
||||||
|
'[MI={TEXT2?}]{TEXT1}[/MI]',
|
||||||
|
'<span style="color:red">MI<xsl:if test="@mi"> for: "<xsl:value-of select="@mi"/>"</xsl:if>:</span>
|
||||||
|
<xsl:text> </xsl:text>
|
||||||
|
<span style="color:#f6efe2">
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</span>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// https://www.phpbb.com/community/viewtopic.php?p=14700506#p14700506
|
||||||
|
'[spoiler]{TEXT}[/spoiler]',
|
||||||
|
'<span class="spoiler"> {TEXT}</span>',
|
||||||
|
|
||||||
|
'[spoiler={TEXT1}]{TEXT2}[/spoiler]',
|
||||||
|
'<div class="spoiler"><small> {TEXT1}</small>{TEXT2}</div>',
|
||||||
|
|
||||||
|
'[spoiler={TEXT1?}]{TEXT2}[/spoiler]',
|
||||||
|
'<xsl:choose>
|
||||||
|
<xsl:when test="@spoiler">
|
||||||
|
<div class="spoiler">
|
||||||
|
<small>
|
||||||
|
<xsl:text> </xsl:text>
|
||||||
|
<xsl:value-of select="@spoiler"/>
|
||||||
|
</small>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</div>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>
|
||||||
|
<span class="spoiler">
|
||||||
|
<xsl:text> </xsl:text>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</span>
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// https://www.phpbb.com/community/viewtopic.php?p=14859676#p14859676
|
||||||
|
'[AE]{TEXT}[/AE]',
|
||||||
|
'<table width="100%" border="1">
|
||||||
|
<tr><td width="100%" align="center">
|
||||||
|
<table width="100%" border="0">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" bgcolor="#E1E4F2">
|
||||||
|
<table width="100%" border="0" bgcolor="#F5F5FF">
|
||||||
|
<tr>
|
||||||
|
<td width="1%" bgcolor="#000000" nowrap align="left">
|
||||||
|
<font color="#FFFFFF" face="Arial"><font size="1"><b> ACTIVE EFFECTS & CONDITIONS </b></font></font></td>
|
||||||
|
<td width="99%"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="100%" bgcolor="#FFE5BA" colspan="2">
|
||||||
|
<table width="100%" cellpadding="2">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" align="left" valign="top">
|
||||||
|
{TEXT}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
<p> </p>',
|
||||||
|
|
||||||
|
'[AE={TEXT1}]{TEXT2}[/AE]',
|
||||||
|
'<table width="100%" border="1">
|
||||||
|
<tr><td width="100%" align="center">
|
||||||
|
<table width="100%" border="0">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" bgcolor="#E1E4F2">
|
||||||
|
<table width="100%" border="0" bgcolor="#F5F5FF">
|
||||||
|
<tr>
|
||||||
|
<td width="1%" bgcolor="#000000" nowrap align="left">
|
||||||
|
<font color="#FFFFFF" face="Arial"><font size="1"><b> {TEXT1} </b></font></font></td>
|
||||||
|
<td width="99%"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="100%" bgcolor="#FFE5BA" colspan="2">
|
||||||
|
<table width="100%" cellpadding="2">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" align="left" valign="top">
|
||||||
|
{TEXT2}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
<p> </p>',
|
||||||
|
|
||||||
|
'[AE={TEXT1?}]{TEXT2}[/AE]',
|
||||||
|
'<table width="100%" border="1">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" align="center">
|
||||||
|
<table width="100%" border="0">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" bgcolor="#E1E4F2">
|
||||||
|
<table width="100%" border="0" bgcolor="#F5F5FF">
|
||||||
|
<tr>
|
||||||
|
<td width="1%" bgcolor="#000000" nowrap="nowrap" align="left">
|
||||||
|
<font color="#FFFFFF" face="Arial">
|
||||||
|
<font size="1">
|
||||||
|
<b> <xsl:choose><xsl:when test="@ae"><xsl:text> </xsl:text><xsl:value-of select="@ae"/></xsl:when><xsl:otherwise>ACTIVE EFFECTS & CONDITIONS</xsl:otherwise></xsl:choose> </b>
|
||||||
|
</font>
|
||||||
|
</font>
|
||||||
|
</td>
|
||||||
|
<td width="99%"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="100%" bgcolor="#FFE5BA" colspan="2">
|
||||||
|
<table width="100%" cellpadding="2">
|
||||||
|
<tr>
|
||||||
|
<td width="100%" align="left" valign="top">
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p> </p>'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user