[BUGFIX] RTE: Empty paragraphs are not correctly transformed 21/11621/5
authorStanislas Rolland <typo3@sjbr.ca>
Tue, 11 Dec 2012 16:00:36 +0000 (11:00 -0500)
committerChristian Kuhn <lolli@schwarzbu.ch>
Thu, 7 Mar 2013 22:37:57 +0000 (23:37 +0100)
Problem: Empty paragraphs entered in the RTE may get lost on way to
database if no block is found in content, as well as on way to the RTE
if followed by a block.
Solution: Fix both transformations and add multiple unit test
cases.

Releases: 4.5, 4.7, 6.0, 6.1
Resolves: #36904
Resolves: #26141

Change-Id: I6c5495fb668f29267bcdeb7b44814a4bb9dc0f96
Reviewed-on: https://review.typo3.org/11621
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Tested-by: Mario Rimann
Reviewed-by: Mattias Nilsson
Tested-by: Mattias Nilsson
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/core/Classes/Html/RteHtmlParser.php
typo3/sysext/core/Tests/Unit/Html/RteHtmlParserTest.php

index ed47825..bd986eb 100644 (file)
@@ -277,9 +277,13 @@ class RteHtmlParser extends \TYPO3\CMS\Core\Html\HtmlParser {
                                        case 'ts_transform':
 
                                        case 'css_transform':
-                                               // Has a very disturbing effect, so just remove all '13' - depend on '10'
-                                               $value = str_replace(CR, '', $value);
                                                $this->allowedClasses = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->procOptions['allowedClasses'], 1);
+                                               // CR has a very disturbing effect, so just remove all CR and rely on LF
+                                               $value = str_replace(CR, '', $value);
+                                               // Transform empty paragraphs into spacing paragraphs
+                                               $value = str_replace('<p></p>', '<p>&nbsp;</p>', $value);
+                                               // Double any trailing spacing paragraph so that it does not get removed by divideIntoLines()
+                                               $value = preg_replace('/<p>&nbsp;<\/p>$/', '<p>&nbsp;</p>' . '<p>&nbsp;</p>', $value);
                                                $value = $this->TS_transform_db($value, $cmd == 'css_transform');
                                                break;
                                        case 'ts_strip':
@@ -1065,8 +1069,9 @@ class RteHtmlParser extends \TYPO3\CMS\Core\Html\HtmlParser {
         * @todo Define visibility
         */
        public function TS_transform_rte($value, $css = 0) {
-               // Split the content from Database by the occurence of these blocks:
-               $blockSplit = $this->splitIntoBlock('TABLE,BLOCKQUOTE,TYPOLIST,TYPOHEAD,' . ($this->procOptions['preserveDIVSections'] ? 'DIV,' : '') . $this->blockElementList, $value);
+               // Split the content from database by the occurence of the block elements
+               $blockElementList = 'TABLE,BLOCKQUOTE,TYPOLIST,TYPOHEAD,' . ($this->procOptions['preserveDIVSections'] ? 'DIV,' : '') . $this->blockElementList;
+               $blockSplit = $this->splitIntoBlock($blockElementList, $value);
                // Traverse the blocks
                foreach ($blockSplit as $k => $v) {
                        if ($k % 2) {
@@ -1125,13 +1130,19 @@ class RteHtmlParser extends \TYPO3\CMS\Core\Html\HtmlParser {
                        } else {
                                // NON-block:
                                $nextFTN = $this->getFirstTagName($blockSplit[$k + 1]);
-                               $singleLineBreak = $blockSplit[$k] == LF;
-                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('TABLE,BLOCKQUOTE,TYPOLIST,TYPOHEAD,' . ($this->procOptions['preserveDIVSections'] ? 'DIV,' : '') . $this->blockElementList, $nextFTN)) {
-                                       // Removing linebreak if typolist/typohead
-                                       $blockSplit[$k] = preg_replace('/' . LF . '[ ]*$/', '', $blockSplit[$k]);
+                               $onlyLineBreaks = (preg_match('/^[ ]*' . LF . '+[ ]*$/', $blockSplit[$k]) == 1);
+                               // If the line is followed by a block or is the last line:
+                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($blockElementList, $nextFTN) || !isset($blockSplit[$k + 1])) {
+                                       // If the line contains more than just linebreaks, reduce the number of trailing linebreaks by 1
+                                       if (!$onlyLineBreaks) {
+                                               $blockSplit[$k] = preg_replace('/(' . LF . '*)' . LF . '[ ]*$/', '$1', $blockSplit[$k]);
+                                       } else {
+                                               // If the line contains only linebreaks, remove the leading linebreak
+                                               $blockSplit[$k] = preg_replace('/^[ ]*' . LF . '/', '', $blockSplit[$k]);
+                                       }
                                }
-                               // If $blockSplit[$k] is blank then unset the line. UNLESS the line happend to be a single line break.
-                               if (!strcmp($blockSplit[$k], '') && !$singleLineBreak) {
+                               // If $blockSplit[$k] is blank then unset the line, unless the line only contained linebreaks
+                               if (!strcmp($blockSplit[$k], '') && !$onlyLineBreaks) {
                                        unset($blockSplit[$k]);
                                } else {
                                        $blockSplit[$k] = $this->setDivTags($blockSplit[$k], $this->procOptions['useDIVasParagraphTagForRTE'] ? 'div' : 'p');
index b6ddc8f..56779b4 100644 (file)
@@ -42,6 +42,7 @@ class RteHtmlParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        'dontConvBRtoParagraph' => '1',
                        'preserveDIVSections' => '1',
                        'allowTagsOutside' => 'hr, address',
+                       'disableUnifyLineBreaks' => '0',
                        'overruleMode' => 'ts_css'
                );
        }
@@ -51,66 +52,66 @@ class RteHtmlParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        }
 
        /**
-        * Data provider for TS_transform_db
+        * Data provider for hrTagCorrectlyTransformedOnWayToDataBase
         */
-       static public function hrTagCorrectlyTransformedOnWayToDataBaseDataProvider() {
+       public static function hrTagCorrectlyTransformedOnWayToDataBaseDataProvider() {
                return array(
-                       'single hr' => array(
+                       'Single hr' => array(
+                               '<hr />',
                                '<hr />',
-                               '<hr />'
                        ),
-                       'non-xhtml single hr' => array(
+                       'Non-xhtml single hr' => array(
                                '<hr/>',
-                               '<hr />'
+                               '<hr />',
                        ),
-                       'double hr' => array(
+                       'Double hr' => array(
                                '<hr /><hr />',
-                               '<hr />' . LF . '<hr />'
+                               '<hr />' . CRLF . '<hr />',
                        ),
-                       'linefeed followed by hr' => array(
-                               LF . '<hr />',
-                               '<hr />'
+                       'Linebreak followed by hr' => array(
+                               CRLF . '<hr />',
+                               '<hr />',
                        ),
-                       'white space followed by hr' => array(
+                       'White space followed by hr' => array(
                                ' <hr />',
-                               ' ' . LF . '<hr />'
+                               ' ' . CRLF . '<hr />',
                        ),
-                       'white space followed linefeed and hr' => array(
-                               ' ' . LF . '<hr />',
-                               ' ' . LF . '<hr />'
+                       'White space followed linebreak and hr' => array(
+                               ' ' . CRLF . '<hr />',
+                               ' ' . CRLF . '<hr />',
                        ),
                        'br followed by hr' => array(
                                '<br /><hr />',
-                               '<br />' . LF . '<hr />'
+                               '<br />' . CRLF . '<hr />',
                        ),
-                       'br followed by linefeed and hr' => array(
-                               '<br />' . LF . '<hr />',
-                               '<br />' . LF . '<hr />'
+                       'br followed by linebreak and hr' => array(
+                               '<br />' . CRLF . '<hr />',
+                               '<br />' . CRLF . '<hr />',
                        ),
-                       'preserved div followed by hr' => array(
+                       'Preserved div followed by hr' => array(
                                '<div>Some text</div><hr />',
-                               '<div>Some text</div>' . LF . '<hr />'
+                               '<div>Some text</div>' . CRLF . '<hr />',
                        ),
-                       'preserved div followed by linefeed and hr' => array(
-                               '<div>Some text</div>' . LF . '<hr />',
-                               '<div>Some text</div>' . LF . '<hr />'
+                       'Preserved div followed by linebreak and hr' => array(
+                               '<div>Some text</div>' . CRLF . '<hr />',
+                               '<div>Some text</div>' . CRLF . '<hr />',
                        ),
-                       'h1 followed by linefeed and hr' => array(
-                               '<h1>Some text</h1>' . LF . '<hr />',
-                               '<h1>Some text</h1>' . LF . '<hr />'
+                       'h1 followed by linebreak and hr' => array(
+                               '<h1>Some text</h1>' . CRLF . '<hr />',
+                               '<h1>Some text</h1>' . CRLF . '<hr />',
                        ),
-                       'paragraph followed by linefeed and hr' => array(
-                               '<p>Some text</p>' . LF . '<hr />',
-                               'Some text' . LF . '<hr />'
+                       'Paragraph followed by linebreak and hr' => array(
+                               '<p>Some text</p>' . CRLF . '<hr />',
+                               'Some text' . CRLF . '<hr />',
                        ),
-                       'some text followed by hr' => array(
+                       'Some text followed by hr' => array(
                                'Some text<hr />',
-                               'Some text' . LF . '<hr />'
+                               'Some text' . CRLF . '<hr />',
+                       ),
+                       'Some text followed by linebreak and hr' => array(
+                               'Some text' . CRLF . '<hr />',
+                               'Some text' . CRLF . '<hr />',
                        ),
-                       'some text followed by linefeed and hr' => array(
-                               'Some text' . LF . '<hr />',
-                               'Some text' . LF . '<hr />'
-                       )
                );
        }
 
@@ -119,8 +120,81 @@ class RteHtmlParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @dataProvider hrTagCorrectlyTransformedOnWayToDataBaseDataProvider
         */
        public function hrTagCorrectlyTransformedOnWayToDataBase($content, $expectedResult) {
-                       // Assume the transformation is ts_css
-               $this->assertEquals($expectedResult, $this->fixture->TS_transform_db($content, TRUE));
+               $thisConfig = array('proc.' => $this->fixture->procOptions);
+               $this->assertEquals($expectedResult, $this->fixture->RTE_transform($content, array(), 'db', $thisConfig));
+       }
+
+       /**
+        * Data provider for hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider
+        */
+       public static function hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider() {
+               return array(
+                       'Single hr' => array(
+                               '<hr />',
+                               '<hr />',
+                       ),
+                       'Non-xhtml single hr' => array(
+                               '<hr/>',
+                               '<hr />',
+                       ),
+                       'Double hr' => array(
+                               '<hr /><hr />',
+                               '<hr />' . CRLF . '<hr />',
+                       ),
+                       'Linebreak followed by hr' => array(
+                               CRLF . '<hr />',
+                               '<hr />',
+                       ),
+                       'White space followed by hr' => array(
+                               ' <hr />',
+                               '<p>&nbsp;</p>' . CRLF . '<hr />',
+                       ),
+                       'White space followed by linebreak and hr' => array(
+                               ' ' . CRLF . '<hr />',
+                               '<p>&nbsp;</p>' . CRLF . '<hr />',
+                       ),
+                       'br followed by hr' => array(
+                               '<br /><hr />',
+                               '<p><br /></p>' . CRLF . '<hr />',
+                       ),
+                       'br followed by linebreak and hr' => array(
+                               '<br />' . CRLF . '<hr />',
+                               '<p><br /></p>' . CRLF . '<hr />',
+                       ),
+                       'Preserved div followed by hr' => array(
+                               '<div>Some text</div>' . '<hr />',
+                               '<div><p>Some text</p></div>' . CRLF . '<hr />',
+                       ),
+                       'Preserved div followed by linebreak and hr' => array(
+                               '<div>Some text</div>' . CRLF . '<hr />',
+                               '<div><p>Some text</p></div>' . CRLF . '<hr />',
+                       ),
+                       'h1 followed by linebreak and hr' => array(
+                               '<h1>Some text</h1>' . CRLF . '<hr />',
+                               '<h1>Some text</h1>' . CRLF . '<hr />',
+                       ),
+                       'Paragraph followed by linebreak and hr' => array(
+                               '<p>Some text</p>' . CRLF . '<hr />',
+                               '<p>Some text</p>' . CRLF . '<hr />',
+                       ),
+                       'Some text followed by hr' => array(
+                               'Some text<hr />',
+                               '<p>Some text</p>' . CRLF . '<hr />',
+                       ),
+                       'Some text followed by linebreak and hr' => array(
+                               'Some text' . CRLF . '<hr />',
+                               '<p>Some text</p>' . CRLF . '<hr />',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider
+        */
+       public function hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult) {
+               $thisConfig = array('proc.' => $this->fixture->procOptions);
+               $this->assertEquals($expectedResult, $this->fixture->RTE_transform($this->fixture->RTE_transform($content, array(), 'db', $thisConfig), array(), 'rte', $thisConfig));
        }
 
        /**
@@ -147,5 +221,444 @@ class RteHtmlParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $thisConfig = array('proc.' => $this->fixture->procOptions);
                $this->assertEquals($expectedResult, $this->fixture->RTE_transform($content, array(), 'rte', $thisConfig));
        }
+
+       /**
+        * Data provider for paragraphCorrectlyTransformedOnWayToDatabase
+        */
+       public static function paragraphCorrectlyTransformedOnWayToDatabaseProvider() {
+               return array(
+                       'Empty string' => array(
+                               '',
+                               '',
+                       ),
+                       'Linebreak' => array(
+                               CRLF,
+                               '',
+                       ),
+                       'Double linebreak' => array(
+                               CRLF . CRLF,
+                               '',
+                       ),
+                       'Empty paragraph' => array(
+                               '<p></p>',
+                               CRLF,
+                       ),
+                       'Double empty paragraph' => array(
+                               '<p></p><p></p>',
+                               CRLF . CRLF,
+                       ),
+                       'Spacing paragraph' => array(
+                               '<p>&nbsp;</p>',
+                               CRLF,
+                       ),
+                       'Double spacing paragraph' => array(
+                               '<p>&nbsp;</p>' . '<p>&nbsp;</p>',
+                               CRLF . CRLF,
+                       ),
+                       'Plain text' => array(
+                               'plain text',
+                               'plain text',
+                       ),
+                       'Plain text followed by linebreak' => array(
+                               'plain text' . CRLF,
+                               'plain text ',
+                       ),
+                       'Paragraph' => array(
+                               '<p>paragraph</p>',
+                               'paragraph',
+                       ),
+                       'Paragraph followed by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . 'paragraph2',
+                       ),
+                       'Double spacing paragraph' => array(
+                               '<p>&nbsp;</p><p>&nbsp;</p><p>paragraph1</p>',
+                               CRLF . CRLF . paragraph1,
+                       ),
+                       'Paragraph followed by linebreak' => array(
+                               '<p>paragraph</p>' . CRLF,
+                               'paragraph',
+                       ),
+                       'Paragraph followed by spacing paragraph' => array(
+                               '<p>paragraph</p>' . '<p>&nbsp;</p>',
+                               'paragraph' . CRLF . CRLF,
+                       ),
+                       'Paragraph followed by spacing paragraph, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
+                               'paragraph' . CRLF . CRLF,
+                       ),
+                       'Paragraph followed by double spacing paragraph' => array(
+                               '<p>paragraph</p>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>',
+                               'paragraph' . CRLF . CRLF . CRLF,
+                       ),
+                       'Paragraph followed by double spacing paragraph, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                               'paragraph' . CRLF . CRLF . CRLF,
+                       ),
+                       'Paragraph followed by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by spacing paragraph and by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>&nbsp;</p>' . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by spacing paragraph and by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by double spacing paragraph and by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>' . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . CRLF . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by double spacing paragraph and by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                               'paragraph1' . CRLF . CRLF . CRLF . 'paragraph2',
+                       ),
+                       'Paragraph followed by block' => array(
+                               '<p>paragraph</p>' . '<h1>block</h1>',
+                               'paragraph' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by block, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
+                               'paragraph' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by spacing paragraph and block' => array(
+                               '<p>paragraph</p>' . '<p>&nbsp;</p>' . '<h1>block</h1>',
+                               'paragraph' . CRLF . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by spacing paragraph and block, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                               'paragraph' . CRLF . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by double spacing paragraph and block' => array(
+                               '<p>paragraph</p>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>' . '<h1>block</h1>',
+                               'paragraph' . CRLF . CRLF . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by double spacing paragraph and block, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                               'paragraph' . CRLF . CRLF . CRLF . '<h1>block</h1>',
+                       ),
+                       'Block followed by block' => array(
+                               '<h1>block1</h1>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by empty paragraph and block' => array(
+                               '<h1>block1</h1>' . '<p></p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by empty paragraph aand block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p></p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by spacing paragraph' => array(
+                               '<h1>block1</h1>' . '<p>&nbsp;</p>',
+                               '<h1>block1</h1>' . CRLF . CRLF,
+                       ),
+                       'Block followed by spacing paragraph, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>',
+                               '<h1>block1</h1>' . CRLF . CRLF,
+                       ),
+                       'Block followed by spacing paragraph and block' => array(
+                               '<h1>block1</h1>' . '<p>&nbsp;</p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by spacing paragraph and block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by double spacing paragraph and by block' => array(
+                               '<h1>block1</h1>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by double spacing paragraph and by block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by paragraph and block' => array(
+                               '<h1>block1</h1>' . '<p>paragraph</p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . 'paragraph' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by paragraph and block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . 'paragraph' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by paragraph, spacing paragraph and block' => array(
+                               '<h1>block1</h1>' . '<p>paragraph</p>' . '<p>&nbsp;</p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . 'paragraph' . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by paragraph, spacing paragraph and block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . 'paragraph' . CRLF . CRLF . '<h1>block2</h1>',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider paragraphCorrectlyTransformedOnWayToDatabaseProvider
+        */
+       public function paragraphCorrectlyTransformedOnWayToDatabase($content, $expectedResult) {
+               $thisConfig = array('proc.' => $this->fixture->procOptions);
+               $this->assertEquals($expectedResult, $this->fixture->RTE_transform($content, array(), 'db', $thisConfig));
+       }
+
+       /**
+        * Data provider for lineBreakCorrectlyTransformedOnWayToRte
+        */
+       public static function lineBreakCorrectlyTransformedOnWayToRteProvider() {
+               return array(
+                       'Empty string' => array(
+                               '',
+                               '',
+                       ),
+                       'Single linebreak' => array(
+                               CRLF,
+                               '<p>&nbsp;</p>',
+                       ),
+                       'Double linebreak' => array(
+                               CRLF . CRLF,
+                               '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Triple linebreak' => array(
+                               CRLF . CRLF . CRLF,
+                               '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Paragraph' => array(
+                               'paragraph',
+                               '<p>paragraph</p>',
+                       ),
+                       'Paragraph followed by single linebreak' => array(
+                               'paragraph' . CRLF,
+                               '<p>paragraph</p>',
+                       ),
+                       'Paragraph followed by double linebreak' => array(
+                               'paragraph' . CRLF . CRLF,
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Paragraph followed by triple linebreak' => array(
+                               'paragraph' . CRLF . CRLF . CRLF,
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Paragraph followed by paragraph' => array(
+                               'paragraph1' . CRLF . 'paragraph2',
+                               '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by double linebreak and paragraph' => array(
+                               'paragraph1' . CRLF . CRLF . 'paragraph2',
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by triple linebreak and paragraph' => array(
+                               'paragraph1' . CRLF . CRLF . CRLF . 'paragraph2',
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by block' => array(
+                               'paragraph' . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by linebreak and block' => array(
+                               'paragraph' . CRLF . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by double linebreak and block' => array(
+                               'paragraph' . CRLF . CRLF . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by triple linebreak and block' => array(
+                               'paragraph' . CRLF . CRLF . CRLF . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Block followed by block' => array(
+                               '<h1>block1</h1>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by single linebreak and block' => array(
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by double linebreak and block' => array(
+                               '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by triple linebreak and block' => array(
+                               '<h1>block1</h1>' . CRLF . CRLF . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by paragraph and block' => array(
+                               '<h1>block1</h1>' . CRLF . 'paragraph' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider lineBreakCorrectlyTransformedOnWayToRTEProvider
+        */
+       public function lineBreakCorrectlyTransformedOnWayToRTE($content, $expectedResult) {
+               $thisConfig = array('proc.' => $this->fixture->procOptions);
+               $this->assertEquals($expectedResult, $this->fixture->RTE_transform($content, array(), 'rte', $thisConfig));
+       }
+
+       /**
+        * Data provider for paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRte
+        */
+       public static function paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider() {
+               return array(
+                       'Empty string' => array(
+                               '',
+                               '',
+                       ),
+                       'Empty paragraph' => array(
+                               '<p></p>',
+                               '<p>&nbsp;</p>',
+                       ),
+                       'Double empty paragraph' => array(
+                               '<p></p><p></p>',
+                               '<p>&nbsp;</p>'. CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Triple empty paragraph' => array(
+                               '<p></p><p></p><p></p>',
+                               '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Plain text' => array(
+                               'plain text',
+                               '<p>plain text</p>',
+                       ),
+                       'Plain text followed by linebreak' => array(
+                               'plain text' . CRLF,
+                               '<p>plain text </p>',
+                       ),
+                       'Plain text followed by paragraph' => array(
+                               'plain text' . '<p>paragraph</p>',
+                               '<p>plain text</p>' . CRLF . '<p>paragraph</p>',
+                       ),
+                       'Spacing paragraph' => array(
+                               '<p>&nbsp;</p>',
+                               '<p>&nbsp;</p>',
+                       ),
+                       'Double spacing paragraph' => array(
+                               '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                               '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Paragraph' => array(
+                               '<p>paragraph</p>',
+                               '<p>paragraph</p>',
+                       ),
+                       'Paragraph followed by linebreak' => array(
+                               '<p>paragraph</p>' . CRLF,
+                               '<p>paragraph</p>',
+                       ),
+                       'Paragraph followed by spacing paragraph' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Paragraph followed by double spacing paragraph' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
+                       ),
+                       'Paragraph followed by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>paragraph2</p>',
+                               '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
+                               '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by spacing paragraph and by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>&nbsp;</p>' . '<p>paragraph2</p>',
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by spacing paragraph and by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by double spacing paragraph and by paragraph' => array(
+                               '<p>paragraph1</p>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>' . '<p>paragraph2</p>',
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by double spacing paragraph and by paragraph, linebreak-separated' => array(
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                               '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
+                       ),
+                       'Paragraph followed by block' => array(
+                               '<p>paragraph</p>' . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by block, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by spacing paragraph and by block' => array(
+                               '<p>paragraph</p>' . '<p>&nbsp;</p>' . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by spacing paragraph and by block, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by double spacing paragraph and by block' => array(
+                               '<p>paragraph</p>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>' . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Paragraph followed by double spacing paragraph and by block, linebreak-separated' => array(
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                               '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
+                       ),
+                       'Block followed by block' => array(
+                               '<h1>block1</h1>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by empty paragraph and by block' => array(
+                               '<h1>block1</h1>' . '<p></p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by empty paragraph and by block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p></p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by spacing paragraph and by block' => array(
+                               '<h1>block1</h1>' . '<p>&nbsp;</p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by spacing paragraph and by block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by double spacing paragraph and by block' => array(
+                               '<h1>block1</h1>' . '<p>&nbsp;</p>' . '<p>&nbsp;</p>' . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+                       'Block followed by double spacing paragraph and by block, linebreak-separated' => array(
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                               '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider
+        */
+       public function paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult) {
+               $thisConfig = array('proc.' => $this->fixture->procOptions);
+               $this->assertEquals($expectedResult, $this->fixture->RTE_transform($this->fixture->RTE_transform($content, array(), 'db', $thisConfig), array(), 'rte', $thisConfig));
+       }
 }
 ?>
\ No newline at end of file