diff options
-rw-r--r-- | include/html2bbcode.php | 38 | ||||
-rw-r--r-- | tests/unit/includes/BBCodeTest.php | 58 | ||||
-rw-r--r-- | tests/unit/includes/MarkdownTest.php | 129 |
3 files changed, 174 insertions, 51 deletions
diff --git a/include/html2bbcode.php b/include/html2bbcode.php index aca3ff4f8..e2fa94326 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -10,6 +10,10 @@ Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom function node2bbcode(&$doc, $oldnode, $attributes, $startbb, $endbb) { do { + if (empty($startbb) && empty($endbb)) { + break; + } + $done = node2bbcodesub($doc, $oldnode, $attributes, $startbb, $endbb); } while ($done); } @@ -65,6 +69,22 @@ function node2bbcodesub(&$doc, $oldnode, $attributes, $startbb, $endbb) if ($oldNode->hasChildNodes()) { foreach ($oldNode->childNodes as $child) { $newNode = $child->cloneNode(true); + + // Newlines are insignificant in HTML, but not so in BBCode, so let's + // unwrap the child nodes of when converting them. Also we compress + // consecutive whitespace chars to one. + // + // The exception is `<pre>` and `<code>` elements which + // should keep both newlines and whitespace intact. + if ($oldNode->nodeName != 'pre' && $oldNode->nodeName != 'code') { + $newNode->nodeValue = str_replace( + array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), + array("<", ">", "<br />", " ", ""), + $newNode->nodeValue); + + $newNode->nodeValue = preg_replace('=[\s]{2,}=i', " ", $newNode->nodeValue); + } + $oldNode->parentNode->insertBefore($newNode, $oldNode); } } @@ -125,16 +145,6 @@ function html2bbcode($message) deletenode($doc, 'xml'); deletenode($doc, 'removeme'); - $xpath = new DomXPath($doc); - $list = $xpath->query("//pre"); - foreach ($list as $node) - $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); - - $message = $doc->saveHTML(); - $message = str_replace(array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), array("<", ">", "<br />", " ", ""), $message); - $message = preg_replace('= [\s]*=i', " ", $message); - @$doc->loadHTML($message); - node2bbcode($doc, 'html', array(), "", ""); node2bbcode($doc, 'body', array(), "", ""); @@ -182,7 +192,8 @@ function html2bbcode($message) node2bbcode($doc, 'blockquote', array(), '[quote]', '[/quote]'); - node2bbcode($doc, 'br', array(), "\n", ''); + // Use a temporary tag to keep line breaks + node2bbcode($doc, 'br', array(), '[br]', ''); node2bbcode($doc, 'p', array('class'=>'MsoNormal'), "\n", ""); node2bbcode($doc, 'div', array('class'=>'MsoNormal'), "\r", ""); @@ -219,6 +230,7 @@ function html2bbcode($message) node2bbcode($doc, 'a', array('href'=>'/(.+)/'), '[url=$1]', '[/url]'); node2bbcode($doc, 'img', array('src'=>'/(.+)/', 'width'=>'/(\d+)/', 'height'=>'/(\d+)/'), '[img=$2x$3]$1', '[/img]'); + node2bbcode($doc, 'img', array('src'=>'/(.+)/', 'alt'=>'/(.+)/'), '[img=$1]$2', '[/img]'); node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '[img]$1', '[/img]'); @@ -226,6 +238,7 @@ function html2bbcode($message) node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]'); // node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); + node2bbcode($doc, 'code', array('class'=>'/(.+)/'), '[code=$1]', '[/code]'); node2bbcode($doc, 'code', array(), '[code]', '[/code]'); $message = $doc->saveHTML(); @@ -291,6 +304,9 @@ function html2bbcode($message) $message = str_replace(array('[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'), array('[b]', '[/b]', '[i]', '[/i]'), $message); + // Restore linebreaks from temp tag + $message = str_replace('[br] ', "\n", $message); + // Handling Yahoo style of mails // $message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message); diff --git a/tests/unit/includes/BBCodeTest.php b/tests/unit/includes/BBCodeTest.php new file mode 100644 index 000000000..035bcbdc7 --- /dev/null +++ b/tests/unit/includes/BBCodeTest.php @@ -0,0 +1,58 @@ +<?php +/* + * Copyright (c) 2024 Hubzilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Zotlabs\Tests\Unit\includes; + +use Zotlabs\Tests\Unit\UnitTestCase; + +class BBCodeTest extends UnitTestCase { + /** + * Test converting html to BBCode. + * + * @dataProvider html2bbcode_provider + */ + public function test_html2bbcode(string $src, string $expected): void { + $this->assertEquals($expected, html2bbcode($src)); + } + + private function html2bbcode_provider(): array { + return [ + 'paragraph over multiple lines' => [ + "<p>A paragraph over\nmultiple lines\nshould be unwrapped</p>", + 'A paragraph over multiple lines should be unwrapped' + ], + 'image with alt text' => [ + '<img src="https://example.com/image.jpg" alt="Alt text">', + '[img=https://example.com/image.jpg]Alt text[/img]' + ], + 'code block' => [ + "<pre><code>some\ncode</code></pre>", + "[code]some\ncode[/code]" + ], + 'code block with indentation' => [ + "<pre><code>some\n indented\ncode</code></pre>", + "[code]some\n indented\ncode[/code]" + ], + ]; + } +} diff --git a/tests/unit/includes/MarkdownTest.php b/tests/unit/includes/MarkdownTest.php index 953305074..960c15139 100644 --- a/tests/unit/includes/MarkdownTest.php +++ b/tests/unit/includes/MarkdownTest.php @@ -1,30 +1,29 @@ <?php /* * Copyright (c) 2017 Hubzilla -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ namespace Zotlabs\Tests\Unit\includes; use Zotlabs\Tests\Unit\UnitTestCase; -use phpmock\phpunit\PHPMock; require_once 'include/markdown.php'; @@ -32,17 +31,80 @@ require_once 'include/markdown.php'; * @brief Unit Test case for markdown functions. */ class MarkdownTest extends UnitTestCase { - use PHPMock; + + /** + * @dataProvider markdown_to_bbcode_provider + */ + public function test_markdown_to_bbcode(string $expected, string $src): void { + $this->assertEquals($expected, markdown_to_bb($src)); + } + + private function markdown_to_bbcode_provider(): array { + return [ + 'empty text' => [ + '', + '' + ], + 'plain text' => [ + 'This is a test', + 'This is a test' + ], + 'bold and italic' => [ + 'This is a test of [b]bold text[/b], [i]italic text[/i] and [b][i]bold and italic text[/i][/b]', + 'This is a test of **bold text**, *italic text* and ***bold and italic text***' + ], + 'multiline text' => [ + 'This text is text wrapped over multiple lines.', + "This text is\ntext wrapped\nover multiple\nlines." + ], + 'text with hard linebreak' => [ + "Line one\nLine two", + "Line one \nLine two" + ], + 'paragraphs' => [ + "Paragraph one\n\nParagraph two", + "Paragraph one\n\nParagraph two", + ], + 'inline image' => [ + '[img=https://example.com/image.jpg]https://example.com/image.jpg[/img]', + '![](https://example.com/image.jpg)' + ], + 'inline image with alt text' => [ + '[img=https://example.com/image.jpg]Alt text[/img]', + '![Alt text](https://example.com/image.jpg)' + ], + 'inline code' => [ + '[code]some code[/code]', + '`some code`' + ], + 'inline code with wrapped text' => [ + '[code]some code unwrapped[/code]', + "`some code\n unwrapped`" + ], + 'code block no language' => [ + "[code]some code\nover multiple lines[/code]", + "```\nsome code\nover multiple lines\n```" + ], + 'code block no language indented' => [ + "[code]some code\n over multiple lines\n with indentation[/code]", + "```\nsome code\n over multiple lines\n with indentation\n```" + ], + 'code block with language' => [ + "[code=php]<?php\necho phpinfo();[/code]", + "```php\n<?php\necho phpinfo();\n```" + ], + ]; + } /** * @covers ::html2markdown * @dataProvider html2markdownProvider */ - public function testHtml2markdown($html, $markdown) { + public function testHtml2markdown(string $html, string $markdown): void { $this->assertEquals($markdown, html2markdown($html)); } - public function html2markdownProvider() { + public function html2markdownProvider(): array { return [ 'empty text' => [ '', @@ -125,23 +187,10 @@ class MarkdownTest extends UnitTestCase { ]; } - /*public function testHtml2markdownException() { - //$this->expectException(\InvalidArgumentException::class); - // need to stub logger() for this to work - $this->assertEquals('', html2markdown('<<invalid')); - }*/ - -/* public function testBB2diasporaMardown() { - //stub bbcode() and return our HTML, we just need to test the HTML2Markdown library. - $html1 = 'test<b>bold</b><br><i>i</i><ul><li>li1</li><li>li2</li></ul><br>'; - $bb1 = 'test'; - - // php-mock can not mock global functions which is called by a global function. - // If the calling function is in a namespace it does work. - $bbc = $this->getFunctionMock(__NAMESPACE__, "bbcode"); - $bbc->expects($this->once())->willReturn('test<b>bold</b><br><i>i</i><ul><li>li1</li><li>li2</li></ul><br>'); + public function test_bb_to_markdown(): void { + $input = "test[b]bold[/b]\n[i]i[/i][ul][li]li1[/li][li]li2[/li][/ul]\n"; + $expected = "test**bold** \n*i*\n\n- li1\n- li2"; - $this->assertEquals($bb1, bb2diaspora($html1)); + $this->assertEquals($expected, bb_to_markdown($input)); } -*/ } |