diff options
| author | Tristan Darricau <tristan.darricau@sensiolabs.com> | 2015-06-29 11:33:00 +0200 | 
|---|---|---|
| committer | Tristan Darricau <tristan.darricau@sensiolabs.com> | 2015-06-29 11:33:00 +0200 | 
| commit | 2be39c5a8f3b587f2fe091862e7021ae70d9fcda (patch) | |
| tree | 27ee4c883d69aa541977f795ab64831948ff40db | |
| parent | 7b903919bf17856fa1f16d9ad386c4b47e48bc81 (diff) | |
| parent | ae2237f640c5f08924b01e780d9549dddcb1b7da (diff) | |
| download | forums-2be39c5a8f3b587f2fe091862e7021ae70d9fcda.tar forums-2be39c5a8f3b587f2fe091862e7021ae70d9fcda.tar.gz forums-2be39c5a8f3b587f2fe091862e7021ae70d9fcda.tar.bz2 forums-2be39c5a8f3b587f2fe091862e7021ae70d9fcda.tar.xz forums-2be39c5a8f3b587f2fe091862e7021ae70d9fcda.zip  | |
Merge pull request #3654 from s9e/ticket/13880
[ticket/13880] Automatically remove quotes that are nested too deep
| -rw-r--r-- | phpBB/includes/message_parser.php | 72 | ||||
| -rw-r--r-- | phpBB/posting.php | 12 | ||||
| -rw-r--r-- | tests/functional/posting_test.php | 81 | 
3 files changed, 142 insertions, 23 deletions
diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php index 42ca9bf09d..8b3d8d9fd5 100644 --- a/phpBB/includes/message_parser.php +++ b/phpBB/includes/message_parser.php @@ -791,28 +791,6 @@ class bbcode_firstpass extends bbcode  				else if (preg_match('#^quote(?:="(.*?)")?$#is', $buffer, $m) && substr($out, -1, 1) == '[')  				{  					$this->parsed_items['quote']++; - -					// the buffer holds a valid opening tag -					if ($config['max_quote_depth'] && sizeof($close_tags) >= $config['max_quote_depth']) -					{ -						if ($config['max_quote_depth'] == 1) -						{ -							// Depth 1 - no nesting is allowed -							$error_ary['quote_depth'] = $user->lang('QUOTE_NO_NESTING'); -						} -						else -						{ -							// There are too many nested quotes -							$error_ary['quote_depth'] = $user->lang('QUOTE_DEPTH_EXCEEDED', (int) $config['max_quote_depth']); -						} - -						$out .= $buffer . $tok; -						$tok = '[]'; -						$buffer = ''; - -						continue; -					} -  					array_push($close_tags, '/quote:' . $this->bbcode_uid);  					if (isset($m[1]) && $m[1]) @@ -1277,6 +1255,12 @@ class parse_message extends bbcode_firstpass  			return $update_this_message ? $this->warn_msg : $return_message;  		} +		// Remove quotes that are nested too deep +		if ($config['max_quote_depth'] > 0) +		{ +			$this->remove_nested_quotes($config['max_quote_depth']); +		} +  		// Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length.  		// The maximum length check happened before any parsings.  		if ($mode === 'post' && utf8_clean_string($this->message) === '') @@ -1856,6 +1840,50 @@ class parse_message extends bbcode_firstpass  	}  	/** +	* Remove nested quotes at given depth in current parsed message +	* +	* @param  integer $max_depth Depth limit +	* @return null +	*/ +	public function remove_nested_quotes($max_depth) +	{ +		// Capture all [quote] and [/quote] tags +		preg_match_all('(\\[/?quote(?:="(.*?)")?:' . $this->bbcode_uid . '\\])', $this->message, $matches, PREG_OFFSET_CAPTURE); + +		// Iterate over the quote tags to mark the ranges that must be removed +		$depth = 0; +		$ranges = array(); +		$start_pos = 0; +		foreach ($matches[0] as $match) +		{ +			if ($match[0][1] === '/') +			{ +				--$depth; +				if ($depth == $max_depth) +				{ +					$end_pos = $match[1] + strlen($match[0]); +					$length = $end_pos - $start_pos; +					$ranges[] = array($start_pos, $length); +				} +			} +			else +			{ +				++$depth; +				if ($depth == $max_depth + 1) +				{ +					$start_pos = $match[1]; +				} +			} +		} + +		foreach (array_reverse($ranges) as $range) +		{ +			list($start_pos, $length) = $range; +			$this->message = substr_replace($this->message, '', $start_pos, $length); +		} +	} + +	/**  	* Setter function for passing the plupload object  	*  	* @param \phpbb\plupload\plupload $plupload The plupload object diff --git a/phpBB/posting.php b/phpBB/posting.php index 651f674ef9..d994811a91 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -1579,11 +1579,21 @@ if (!sizeof($error) && $preview)  	}  } +// Remove quotes that would become nested too deep before decoding the text +$generate_quote = ($mode == 'quote' && !$submit && !$preview && !$refresh); +if ($generate_quote && $config['max_quote_depth'] > 0) +{ +	$tmp_bbcode_uid = $message_parser->bbcode_uid; +	$message_parser->bbcode_uid = $post_data['bbcode_uid']; +	$message_parser->remove_nested_quotes($config['max_quote_depth'] - 1); +	$message_parser->bbcode_uid = $tmp_bbcode_uid; +} +  // Decode text for message display  $post_data['bbcode_uid'] = ($mode == 'quote' && !$preview && !$refresh && !sizeof($error)) ? $post_data['bbcode_uid'] : $message_parser->bbcode_uid;  $message_parser->decode_message($post_data['bbcode_uid']); -if ($mode == 'quote' && !$submit && !$preview && !$refresh) +if ($generate_quote)  {  	if ($config['allow_bbcode'])  	{ diff --git a/tests/functional/posting_test.php b/tests/functional/posting_test.php index fd802eed45..33632a01e1 100644 --- a/tests/functional/posting_test.php +++ b/tests/functional/posting_test.php @@ -59,4 +59,85 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case  			'Your subject contains the following unsupported characters'  		);  	} + +	/** +	* @testdox max_quote_depth is applied to the text populating the posting form +	*/ +	public function test_quote_depth_form() +	{ +		$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]', +		); + +		$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); +		$quote_url = "posting.php?mode=quote&f=2&t={$post['topic_id']}&p={$post['post_id']}&sid={$this->sid}"; + +		$this->admin_login(); +		foreach ($expected as $quote_depth => $expected_text) +		{ +			$this->set_quote_depth($quote_depth); +			$crawler = self::request('GET', $quote_url); +			$this->assertContains($expected_text, $crawler->filter('textarea#message')->text()); +		} +	} + +	/** +	* @testdox max_quote_depth is applied to the submitted text +	*/ +	public function test_quote_depth_submit() +	{ +		$text = 'depth:0[quote]depth:1[quote]depth:2[quote]depth:3[/quote][/quote][/quote]'; +		$contains = array( +			0 => array('depth:0', 'depth:1', 'depth:2', 'depth:3'), +			1 => array('depth:0', 'depth:1'), +			2 => array('depth:0', 'depth:1', 'depth:2'), +			3 => array('depth:0', 'depth:1', 'depth:2', 'depth:3'), +		); +		$not_contains = array( +			0 => array(), +			1 => array('depth:2', 'depth:3'), +			2 => array('depth:3'), +			3 => array(), +		); + +		$this->login(); +		$this->admin_login(); +		$topic = $this->create_topic(2, 'Test Topic 1', 'Test topic'); + +		for ($quote_depth = 0; $quote_depth <= 2; ++$quote_depth) +		{ +			$this->set_quote_depth($quote_depth); + +			$post = $this->create_post(2, $topic['topic_id'], 'Re: Test Topic 1', $text); +			$url  = "viewtopic.php?p={$post['post_id']}&sid={$this->sid}"; + +			$crawler = self::request('GET', $url); +			$text_content = $crawler->filter('#p' . $post['post_id'])->text(); +			foreach ($contains[$quote_depth] as $contains_text) +			{ +				$this->assertContains($contains_text, $text_content); +			} +			foreach ($not_contains[$quote_depth] as $not_contains_text) +			{ +				$this->assertNotContains($not_contains_text, $text_content); +			} +		} +	} + +	protected function set_quote_depth($depth) +	{ +		$crawler = self::request('GET', 'adm/index.php?sid=' . $this->sid . '&i=acp_board&mode=post'); +		$form = $crawler->selectButton('Submit')->form(); +		$values = $form->getValues(); +		$values['config[max_quote_depth]'] = $depth; +		$form->setValues($values); +		$crawler = self::submit($form); +		$this->assertEquals(1, $crawler->filter('.successbox')->count()); +	}  }  | 
