aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--phpBB/config/default/container/services_text_formatter.yml8
-rw-r--r--phpBB/includes/ucp/ucp_pm_compose.php11
-rw-r--r--phpBB/phpbb/textformatter/s9e/factory.php28
-rw-r--r--phpBB/phpbb/textformatter/s9e/quote_helper.php81
-rw-r--r--phpBB/phpbb/textformatter/s9e/renderer.php19
-rw-r--r--phpBB/phpbb/textformatter/s9e/utils.php1
-rw-r--r--phpBB/posting.php7
-rw-r--r--phpBB/styles/prosilver/template/bbcode.html33
-rw-r--r--tests/functional/posting_test.php17
-rw-r--r--tests/functional/private_messages_test.php20
-rw-r--r--tests/test_framework/phpbb_test_case_helpers.php32
-rw-r--r--tests/text_formatter/s9e/default_formatting_test.php22
12 files changed, 248 insertions, 31 deletions
diff --git a/phpBB/config/default/container/services_text_formatter.yml b/phpBB/config/default/container/services_text_formatter.yml
index 20436f0f64..ae698af9e4 100644
--- a/phpBB/config/default/container/services_text_formatter.yml
+++ b/phpBB/config/default/container/services_text_formatter.yml
@@ -44,6 +44,13 @@ services:
- @text_formatter.s9e.factory
- @dispatcher
+ text_formatter.s9e.quote_helper:
+ class: phpbb\textformatter\s9e\quote_helper
+ arguments:
+ - @user
+ - %core.root_path%
+ - %core.php_ext%
+
text_formatter.s9e.renderer:
class: phpbb\textformatter\s9e\renderer
arguments:
@@ -53,6 +60,7 @@ services:
- @text_formatter.s9e.factory
- @dispatcher
calls:
+ - [configure_quote_helper, [@text_formatter.s9e.quote_helper]]
- [configure_smilies_path, [@config, @path_helper]]
- [configure_user, [@user, @config, @auth]]
diff --git a/phpBB/includes/ucp/ucp_pm_compose.php b/phpBB/includes/ucp/ucp_pm_compose.php
index 0989539a0f..c00363bee9 100644
--- a/phpBB/includes/ucp/ucp_pm_compose.php
+++ b/phpBB/includes/ucp/ucp_pm_compose.php
@@ -941,9 +941,18 @@ function compose_pm($id, $mode, $action, $user_folders = array())
{
$message_link = '';
}
+ $quote_attributes = array(
+ 'author' => $quote_username,
+ 'time' => $post['message_time'],
+ 'user_id' => $post['author_id'],
+ );
+ if ($action === 'quotepost')
+ {
+ $quote_attributes['post_id'] = $post['msg_id'];
+ }
$quote_text = $phpbb_container->get('text_formatter.utils')->generate_quote(
censor_text($message_parser->message),
- array('author' => $quote_username)
+ $quote_attributes
);
$message_parser->message = $message_link . $quote_text . "\n\n";
}
diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php
index e07a1b52ca..cd4e593fb1 100644
--- a/phpBB/phpbb/textformatter/s9e/factory.php
+++ b/phpBB/phpbb/textformatter/s9e/factory.php
@@ -77,7 +77,12 @@ class factory implements \phpbb\textformatter\cache_interface
'quote' =>
"[QUOTE
author={TEXT1;optional}
+ post_id={UINT;optional}
+ post_url={URL;optional;postFilter=#false}
+ profile_url={URL;optional;postFilter=#false}
+ time={UINT;optional}
url={URL;optional}
+ user_id={UINT;optional}
author={PARSE=/^\\[url=(?'url'.*?)](?'author'.*)\\[\\/url]$/i}
author={PARSE=/^\\[url](?'author'(?'url'.*?))\\[\\/url]$/i}
author={PARSE=/(?'url'https?:\\/\\/[^[\\]]+)/i}
@@ -471,24 +476,11 @@ class factory implements \phpbb\textformatter\cache_interface
$templates['li'] = $fragments['listitem'] . '<xsl:apply-templates/>' . $fragments['listitem_close'];
- $fragments['quote_username_open'] = str_replace(
- '{USERNAME}',
- '<xsl:choose>
- <xsl:when test="@url">' . str_replace('{DESCRIPTION}', '{USERNAME}', $fragments['url']) . '</xsl:when>
- <xsl:otherwise>{USERNAME}</xsl:otherwise>
- </xsl:choose>',
- $fragments['quote_username_open']
- );
-
- $templates['quote'] =
- '<xsl:choose>
- <xsl:when test="@author">
- ' . $fragments['quote_username_open'] . '<xsl:apply-templates/>' . $fragments['quote_close'] . '
- </xsl:when>
- <xsl:otherwise>
- ' . $fragments['quote_open'] . '<xsl:apply-templates/>' . $fragments['quote_close'] . '
- </xsl:otherwise>
- </xsl:choose>';
+ // Replace the regular quote template with the extended quote template if available
+ if (isset($fragments['quote_extended']))
+ {
+ $templates['quote'] = $fragments['quote_extended'];
+ }
// The [attachment] BBCode uses the inline_attachment template to output a comment that
// is post-processed by parse_attachments()
diff --git a/phpBB/phpbb/textformatter/s9e/quote_helper.php b/phpBB/phpbb/textformatter/s9e/quote_helper.php
new file mode 100644
index 0000000000..24109ac8cc
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/quote_helper.php
@@ -0,0 +1,81 @@
+<?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;
+
+class quote_helper
+{
+ /**
+ * @var string Base URL for a post link, uses {POST_ID} as placeholder
+ */
+ protected $post_url;
+
+ /**
+ * @var string Base URL for a profile link, uses {USER_ID} as placeholder
+ */
+ protected $profile_url;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user
+ * @param string $root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\user $user, $root_path, $php_ext)
+ {
+ $this->post_url = append_sid($root_path . 'viewtopic.' . $php_ext, 'p={POST_ID}#p{POST_ID}');
+ $this->profile_url = append_sid($root_path . 'memberlist.' . $php_ext, 'mode=viewprofile&u={USER_ID}');
+ $this->user = $user;
+ }
+
+ /**
+ * Inject dynamic metadata into QUOTE tags in given XML
+ *
+ * @param string $xml Original XML
+ * @return string Modified XML
+ */
+ public function inject_metadata($xml)
+ {
+ $post_url = $this->post_url;
+ $profile_url = $this->profile_url;
+ $user = $this->user;
+
+ return \s9e\TextFormatter\Utils::replaceAttributes(
+ $xml,
+ 'QUOTE',
+ function ($attributes) use ($post_url, $profile_url, $user)
+ {
+ if (isset($attributes['post_id']))
+ {
+ $attributes['post_url'] = str_replace('{POST_ID}', $attributes['post_id'], $post_url);
+ }
+ if (isset($attributes['time']))
+ {
+ $attributes['date'] = $user->format_date($attributes['time']);
+ }
+ if (isset($attributes['user_id']))
+ {
+ $attributes['profile_url'] = str_replace('{USER_ID}', $attributes['user_id'], $profile_url);
+ }
+
+ return $attributes;
+ }
+ );
+ }
+}
diff --git a/phpBB/phpbb/textformatter/s9e/renderer.php b/phpBB/phpbb/textformatter/s9e/renderer.php
index 51bc44f339..2206605ba2 100644
--- a/phpBB/phpbb/textformatter/s9e/renderer.php
+++ b/phpBB/phpbb/textformatter/s9e/renderer.php
@@ -29,6 +29,11 @@ class renderer implements \phpbb\textformatter\renderer_interface
protected $dispatcher;
/**
+ * @var quote_helper
+ */
+ protected $quote_helper;
+
+ /**
* @var \s9e\TextFormatter\Renderer
*/
protected $renderer;
@@ -113,6 +118,16 @@ class renderer implements \phpbb\textformatter\renderer_interface
}
/**
+ * Configure the quote_helper object used to display extended information in quotes
+ *
+ * @param quote_helper $quote_helper
+ */
+ public function configure_quote_helper(quote_helper $quote_helper)
+ {
+ $this->quote_helper = $quote_helper;
+ }
+
+ /**
* Automatically set the smilies path based on config
*
* @param \phpbb\config\config $config
@@ -214,6 +229,10 @@ class renderer implements \phpbb\textformatter\renderer_interface
*/
public function render($xml)
{
+ if (isset($this->quote_helper))
+ {
+ $xml = $this->quote_helper->inject_metadata($xml);
+ }
$renderer = $this;
/**
diff --git a/phpBB/phpbb/textformatter/s9e/utils.php b/phpBB/phpbb/textformatter/s9e/utils.php
index 803c71a5a2..df1966fa32 100644
--- a/phpBB/phpbb/textformatter/s9e/utils.php
+++ b/phpBB/phpbb/textformatter/s9e/utils.php
@@ -64,6 +64,7 @@ class utils implements \phpbb\textformatter\utils_interface
$quote .= '=' . $this->enquote($attributes['author']);
unset($attributes['author']);
}
+ ksort($attributes);
foreach ($attributes as $name => $value)
{
$quote .= ' ' . $name . '=' . $this->enquote($value);
diff --git a/phpBB/posting.php b/phpBB/posting.php
index 2f9beefcf9..327004b1bf 100644
--- a/phpBB/posting.php
+++ b/phpBB/posting.php
@@ -1605,7 +1605,12 @@ if ($generate_quote)
{
$message_parser->message = $phpbb_container->get('text_formatter.utils')->generate_quote(
censor_text($message_parser->message),
- array('author' => $post_data['quote_username'])
+ array(
+ 'author' => $post_data['quote_username'],
+ 'post_id' => $post_data['post_id'],
+ 'time' => $post_data['post_time'],
+ 'user_id' => $post_data['poster_id'],
+ )
);
$message_parser->message .= "\n\n";
}
diff --git a/phpBB/styles/prosilver/template/bbcode.html b/phpBB/styles/prosilver/template/bbcode.html
index af8e6ae4b0..6adbdb6aba 100644
--- a/phpBB/styles/prosilver/template/bbcode.html
+++ b/phpBB/styles/prosilver/template/bbcode.html
@@ -11,6 +11,39 @@
<!-- BEGIN quote_username_open --><blockquote><div><cite>{USERNAME} {L_WROTE}{L_COLON}</cite><!-- END quote_username_open -->
<!-- BEGIN quote_open --><blockquote class="uncited"><div><!-- END quote_open -->
<!-- BEGIN quote_close --></div></blockquote><!-- END quote_close -->
+<!-- BEGIN quote_extended -->
+<blockquote>
+ <xsl:if test="not(@author)">
+ <xsl:attribute name="class">uncited</xsl:attribute>
+ </xsl:if>
+ <div>
+ <xsl:if test="@author">
+ <cite>
+ <xsl:if test="@date"><xsl:value-of select="@date"/> </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@url">
+ <a href="{@url}" class="postlink"><xsl:value-of select="@author"/></a>
+ </xsl:when>
+ <xsl:when test="@profile_url">
+ <a href="{@profile_url}"><xsl:value-of select="@author"/></a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@author"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="$L_WROTE"/>
+ <xsl:value-of select="$L_COLON"/>
+ <xsl:if test="@post_url">
+ <xsl:text> </xsl:text>
+ <a href="{@post_url}" data-post-id="{@post_id}" onclick="if(document.getElementById(hash.substr(1)))href=hash">&#8593;</a>
+ </xsl:if>
+ </cite>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </div>
+</blockquote>
+<!-- END quote_extended -->
<!-- BEGIN code_open --><div class="codebox"><p>{L_CODE}{L_COLON} <a href="#" onclick="selectCode(this); return false;">{L_SELECT_ALL_CODE}</a></p><pre><code><!-- END code_open -->
<!-- BEGIN code_close --></code></pre></div><!-- END code_close -->
diff --git a/tests/functional/posting_test.php b/tests/functional/posting_test.php
index ccfeb10deb..d9e6cc5ab3 100644
--- a/tests/functional/posting_test.php
+++ b/tests/functional/posting_test.php
@@ -75,7 +75,7 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case
public function test_quote()
{
$text = 'Test post </textarea>"\' &&amp;amp;';
- $expected = '[quote="admin"]' . $text . '[/quote]';
+ $expected = '([quote="admin"[^]]*\\]' . preg_quote($text) . '\\[/quote\\])';
$this->login();
$topic = $this->create_topic(2, 'Test Topic 1', 'Test topic');
@@ -83,7 +83,7 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case
$crawler = self::request('GET', "posting.php?mode=quote&f=2&t={$post['topic_id']}&p={$post['post_id']}&sid={$this->sid}");
- $this->assertContains($expected, $crawler->filter('textarea#message')->text());
+ $this->assertRegexp($expected, $crawler->filter('textarea#message')->text());
}
/**
@@ -93,10 +93,10 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case
{
$text = '0[quote]1[quote]2[/quote]1[/quote]0';
$expected = array(
- 0 => '[quote="admin"]0[quote]1[quote]2[/quote]1[/quote]0[/quote]',
- 1 => '[quote="admin"]00[/quote]',
- 2 => '[quote="admin"]0[quote]11[/quote]0[/quote]',
- 3 => '[quote="admin"]0[quote]1[quote]2[/quote]1[/quote]0[/quote]',
+ 0 => '0[quote]1[quote]2[/quote]1[/quote]0',
+ 1 => '00',
+ 2 => '0[quote]11[/quote]0',
+ 3 => '0[quote]1[quote]2[/quote]1[/quote]0',
);
$this->login();
@@ -109,7 +109,10 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case
{
$this->set_quote_depth($quote_depth);
$crawler = self::request('GET', $quote_url);
- $this->assertContains($expected_text, $crawler->filter('textarea#message')->text());
+ $this->assertRegexp(
+ '(\\[quote="admin"[^]]*\\]' . preg_quote($expected_text) . '\\[/quote\\])',
+ $crawler->filter('textarea#message')->text()
+ );
}
}
diff --git a/tests/functional/private_messages_test.php b/tests/functional/private_messages_test.php
index be584c20c1..9bfb5bc7ad 100644
--- a/tests/functional/private_messages_test.php
+++ b/tests/functional/private_messages_test.php
@@ -69,16 +69,30 @@ class phpbb_functional_private_messages_test extends phpbb_functional_test_case
public function test_quote_post()
{
- $text = 'Test post';
- $expected = '[quote="admin"]' . $text . '[/quote]';
+ $text = 'Test post';
$this->login();
$topic = $this->create_topic(2, 'Test Topic 1', 'Test topic');
$post = $this->create_post(2, $topic['topic_id'], 'Re: Test Topic 1', $text);
+ $expected = '(\\[quote="admin" post_id="' . $post['post_id'] . '" time="\\d+" user_id="2"\\]' . $text . '\\[/quote\\])';
+
$crawler = self::request('GET', 'ucp.php?i=pm&mode=compose&action=quotepost&p=' . $post['post_id'] . '&sid=' . $this->sid);
- $this->assertContains($expected, $crawler->filter('textarea#message')->text());
+ $this->assertRegexp($expected, $crawler->filter('textarea#message')->text());
+ }
+
+ public function test_quote_pm()
+ {
+ $text = 'This is a test private message sent by the testing framework.';
+ $expected = '(\\[quote="admin" time="\\d+" user_id="2"\\]' . $text . '\\[/quote\\])';
+
+ $this->login();
+ $message_id = $this->create_private_message('Test', $text, array(2));
+
+ $crawler = self::request('GET', 'ucp.php?i=pm&mode=compose&action=quote&p=' . $message_id . '&sid=' . $this->sid);
+
+ $this->assertRegexp($expected, $crawler->filter('textarea#message')->text());
}
public function test_quote_forward()
diff --git a/tests/test_framework/phpbb_test_case_helpers.php b/tests/test_framework/phpbb_test_case_helpers.php
index cf530cc5be..62a56ed693 100644
--- a/tests/test_framework/phpbb_test_case_helpers.php
+++ b/tests/test_framework/phpbb_test_case_helpers.php
@@ -476,11 +476,21 @@ class phpbb_test_case_helpers
{
$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
$lang = new \phpbb\language\language($lang_loader);
- $user = new \phpbb\user($lang, '\phpbb\datetime');
+
+ $user = $this->test_case->getMockBuilder('\phpbb\user')
+ ->setConstructorArgs(array($lang, '\phpbb\datetime'))
+ ->setMethods(array('format_date'))
+ ->getMock();
+ $user->expects($this->test_case->any())
+ ->method('format_date')
+ ->will($this->test_case->returnCallback(__CLASS__ . '::format_date'));
+
+ $user->date_format = 'Y-m-d H:i:s';
$user->optionset('viewcensors', true);
$user->optionset('viewflash', true);
$user->optionset('viewimg', true);
$user->optionset('viewsmilies', true);
+ $user->timezone = new \DateTimeZone('UTC');
$container->set('user', $user);
}
$user->add_lang('common');
@@ -490,6 +500,14 @@ class phpbb_test_case_helpers
$user->style = array('style_id' => 1);
}
+ // Create and register a quote_helper
+ $quote_helper = new \phpbb\textformatter\s9e\quote_helper(
+ $container->get('user'),
+ $phpbb_root_path,
+ $phpEx
+ );
+ $container->set('text_formatter.s9e.quote_helper', $quote_helper);
+
// Create and register the text_formatter.s9e.parser service and its alias
$parser = new \phpbb\textformatter\s9e\parser(
$cache,
@@ -515,6 +533,7 @@ class phpbb_test_case_helpers
$auth = ($container->has('auth')) ? $container->get('auth') : new \phpbb\auth\auth;
// Calls configured in services.yml
+ $renderer->configure_quote_helper($quote_helper);
$renderer->configure_smilies_path($config, $container->get('path_helper'));
$renderer->configure_user($user, $config, $auth);
@@ -528,4 +547,15 @@ class phpbb_test_case_helpers
return $container;
}
+
+ /**
+ * Mocked replacement for \phpbb\user::format_date()
+ *
+ * @param integer $gmepoch unix timestamp
+ * @return string
+ */
+ static public function format_date($gmepoch)
+ {
+ return gmdate('Y-m-d H:i:s', $gmepoch);
+ }
}
diff --git a/tests/text_formatter/s9e/default_formatting_test.php b/tests/text_formatter/s9e/default_formatting_test.php
index 67921d5b1e..3f8e375ad1 100644
--- a/tests/text_formatter/s9e/default_formatting_test.php
+++ b/tests/text_formatter/s9e/default_formatting_test.php
@@ -225,6 +225,28 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case
"[quote]\nThis is a long quote that is definitely going to exceed 80 characters\n[/quote]\n\nFollowed by a reply",
"<blockquote class=\"uncited\"><div>\nThis is a long quote that is definitely going to exceed 80 characters\n</div></blockquote>\n\nFollowed by a reply"
),
+ array(
+ '[quote="Username" post_id="123"]...[/quote]',
+ '<blockquote><div><cite>Username wrote: <a href="phpBB/viewtopic.php?p=123#p123" data-post-id="123" onclick="if(document.getElementById(hash.substr(1)))href=hash">↑</a></cite>...</div></blockquote>'
+ ),
+ array(
+ // Users are not allowed to submit their own URL for the post
+ '[quote="Username" post_url="http://fake.example.org"]...[/quote]',
+ '<blockquote><div><cite>Username wrote:</cite>...</div></blockquote>'
+ ),
+ array(
+ '[quote="Username" time="58705871"]...[/quote]',
+ '<blockquote><div><cite>1971-11-11 11:11:11 Username wrote:</cite>...</div></blockquote>'
+ ),
+ array(
+ '[quote="Username" user_id="123"]...[/quote]',
+ '<blockquote><div><cite><a href="phpBB/memberlist.php?mode=viewprofile&amp;u=123">Username</a> wrote:</cite>...</div></blockquote>'
+ ),
+ array(
+ // Users are not allowed to submit their own URL for the profile
+ '[quote="Username" profile_url="http://fake.example.org"]...[/quote]',
+ '<blockquote><div><cite>Username wrote:</cite>...</div></blockquote>'
+ ),
);
}
}