3 * @copyright Copyright (C) 2010-2022, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Test\src\Content\Text;
24 use Friendica\Content\Text\BBCode;
26 use Friendica\Network\HTTPException\InternalServerErrorException;
27 use Friendica\Test\FixtureTest;
29 class BBCodeTest extends FixtureTest
31 protected function setUp(): void
34 DI::config()->set('system', 'remove_multiplicated_lines', false);
35 DI::config()->set('system', 'no_oembed', false);
36 DI::config()->set('system', 'allowed_link_protocols', []);
37 DI::config()->set('system', 'url', 'friendica.local');
38 DI::config()->set('system', 'no_smilies', false);
39 DI::config()->set('system', 'big_emojis', false);
40 DI::config()->set('system', 'allowed_oembed', '');
42 DI::baseUrl()->save('friendica.local', DI::baseUrl()::SSL_POLICY_FULL, '');
44 $config = \HTMLPurifier_HTML5Config::createDefault();
45 $config->set('HTML.Doctype', 'HTML5');
46 $config->set('Attr.AllowedRel', [
50 $config->set('Attr.AllowedFrameTargets', [
54 $this->HTMLPurifier = new \HTMLPurifier($config);
57 public function dataLinks()
60 /** @see https://github.com/friendica/friendica/issues/2487 */
62 'data' => 'https://de.wikipedia.org/wiki/Juha_Sipilä',
66 'data' => 'https://de.wikipedia.org/wiki/Dnepr_(Motorradmarke)',
70 'data' => 'https://friendica.wäckerlin.ch/friendica',
74 'data' => 'https://mastodon.social/@morevnaproject',
77 /** @see https://github.com/friendica/friendica/issues/5795 */
79 'data' => 'https://social.nasqueron.org/@liw/100798039015010628',
82 /** @see https://github.com/friendica/friendica/issues/6095 */
84 'data' => 'https://en.wikipedia.org/wiki/Solid_(web_decentralization_project)',
88 'data' => 'example.com/path',
92 'data' => 'ftp://example.com',
95 'wrong-domain-without-path' => [
96 'data' => 'http://example',
99 'wrong-domain-with-path' => [
100 'data' => 'http://example/path',
101 'assertHTML' => false
103 'bug-6857-domain-start' => [
104 'data' => "http://\nexample.com",
105 'assertHTML' => false
107 'bug-6857-domain-end' => [
108 'data' => "http://example\n.com",
109 'assertHTML' => false
112 'data' => "http://example.\ncom",
113 'assertHTML' => false
116 'data' => "http://example.com\ntest",
117 'assertHTML' => false
120 'data' => "http://example.com<ul>",
121 'assertHTML' => false
124 'data' => html_entity_decode('http://example.com ', ENT_QUOTES, 'UTF-8'),
125 'assertHTML' => false
127 'bug-7271-query-string-brackets' => [
128 'data' => 'https://example.com/search?q=square+brackets+[url]',
131 'bug-7271-path-brackets' => [
132 'data' => 'http://example.com/path/to/file[3].html',
139 * Test convert different links inside a text
141 * @dataProvider dataLinks
143 * @param string $data The data to text
144 * @param bool $assertHTML True, if the link is a HTML link (<a href...>...</a>)
146 * @throws InternalServerErrorException
148 public function testAutoLinking(string $data, bool $assertHTML)
150 $output = BBCode::convert($data);
151 $assert = $this->HTMLPurifier->purify('<a href="' . $data . '" target="_blank" rel="noopener noreferrer">' . $data . '</a>');
153 self::assertEquals($assert, $output);
155 self::assertNotEquals($assert, $output);
159 public function dataBBCodes()
162 'bug-7271-condensed-space' => [
163 'expectedHtml' => '<ul class="listdecimal" style="list-style-type:decimal;"><li> <a href="http://example.com/" target="_blank" rel="noopener noreferrer">http://example.com/</a></li></ul>',
164 'text' => '[ol][*] http://example.com/[/ol]',
166 'bug-7271-condensed-nospace' => [
167 'expectedHtml' => '<ul class="listdecimal" style="list-style-type:decimal;"><li><a href="http://example.com/" target="_blank" rel="noopener noreferrer">http://example.com/</a></li></ul>',
168 'text' => '[ol][*]http://example.com/[/ol]',
170 'bug-7271-indented-space' => [
171 'expectedHtml' => '<ul class="listbullet" style="list-style-type:circle;"><li> <a href="http://example.com/" target="_blank" rel="noopener noreferrer">http://example.com/</a></li></ul>',
173 [*] http://example.com/
176 'bug-7271-indented-nospace' => [
177 'expectedHtml' => '<ul class="listbullet" style="list-style-type:circle;"><li><a href="http://example.com/" target="_blank" rel="noopener noreferrer">http://example.com/</a></li></ul>',
179 [*]http://example.com/
182 'bug-2199-named-size' => [
183 'expectedHtml' => '<span style="font-size:xx-large;line-height:normal;">Test text</span>',
184 'text' => '[size=xx-large]Test text[/size]',
186 'bug-2199-numeric-size' => [
187 'expectedHtml' => '<span style="font-size:24px;line-height:normal;">Test text</span>',
188 'text' => '[size=24]Test text[/size]',
190 'bug-2199-diaspora-no-named-size' => [
191 'expectedHtml' => 'Test text',
192 'text' => '[size=xx-large]Test text[/size]',
193 'try_oembed' => false,
194 // Triggers the diaspora compatible output
195 'simpleHtml' => BBCode::DIASPORA,
197 'bug-2199-diaspora-no-numeric-size' => [
198 'expectedHtml' => 'Test text',
199 'text' => '[size=24]Test text[/size]',
200 'try_oembed' => false,
201 // Triggers the diaspora compatible output
202 'simpleHtml' => BBCode::DIASPORA,
204 'bug-7665-audio-tag' => [
205 'expectedHtml' => '<audio src="http://www.cendrones.fr/colloque2017/jonathanbocquet.mp3" controls><a href="http://www.cendrones.fr/colloque2017/jonathanbocquet.mp3">http://www.cendrones.fr/colloque2017/jonathanbocquet.mp3</a></audio>',
206 'text' => '[audio]http://www.cendrones.fr/colloque2017/jonathanbocquet.mp3[/audio]',
207 'try_oembed' => true,
209 'bug-7808-code-lt' => [
210 'expectedHtml' => '<code><</code>',
211 'text' => '[code]<[/code]',
213 'bug-7808-code-gt' => [
214 'expectedHtml' => '<code>></code>',
215 'text' => '[code]>[/code]',
217 'bug-7808-code-amp' => [
218 'expectedHtml' => '<code>&</code>',
219 'text' => '[code]&[/code]',
221 'task-8800-pre-spaces-notag' => [
222 'expectedHtml' => '[test] Space',
223 'text' => '[test] Space',
225 'task-8800-pre-spaces' => [
226 'expectedHtml' => ' Spaces',
227 'text' => '[pre] Spaces[/pre]',
229 'bug-9611-purify-xss-nobb' => [
230 'expectedHTML' => '<span>dare to move your mouse here</span>',
231 'text' => '[nobb]<span onmouseover="alert(0)">dare to move your mouse here</span>[/nobb]'
233 'bug-9611-purify-xss-noparse' => [
234 'expectedHTML' => '<span>dare to move your mouse here</span>',
235 'text' => '[noparse]<span onmouseover="alert(0)">dare to move your mouse here</span>[/noparse]'
237 'bug-9611-purify-xss-attributes' => [
238 'expectedHTML' => '<span>dare to move your mouse here</span>',
239 'text' => '[color="onmouseover=alert(0) style="]dare to move your mouse here[/color]'
241 'bug-9611-purify-attributes-correct' => [
242 'expectedHTML' => '<span style="color:#FFFFFF;">dare to move your mouse here</span>',
243 'text' => '[color=FFFFFF]dare to move your mouse here[/color]'
245 'bug-9639-span-classes' => [
246 'expectedHTML' => '<span class="arbitrary classes">Test</span>',
247 'text' => '[class=arbitrary classes]Test[/class]',
249 'bug-10772-duplicated-links' => [
250 'expectedHTML' => 'Jetzt wird mir klar, warum Kapitalisten jedes Mal durchdrehen wenn Marx und das Kapital ins Gespräch kommt. Soziopathen.<br>Karl Marx - Die ursprüngliche Akkumulation<br><a href="https://wohlstandfueralle.podigee.io/107-urspruengliche-akkumulation" target="_blank" rel="noopener noreferrer">https://wohlstandfueralle.podigee.io/107-urspruengliche-akkumulation</a><br>#Podcast #Kapitalismus',
251 'text' => "Jetzt wird mir klar, warum Kapitalisten jedes Mal durchdrehen wenn Marx und das Kapital ins Gespräch kommt. Soziopathen.
252 Karl Marx - Die ursprüngliche Akkumulation
253 [url=https://wohlstandfueralle.podigee.io/107-urspruengliche-akkumulation]https://wohlstandfueralle.podigee.io/107-urspruengliche-akkumulation[/url]
254 #[url=https://horche.demkontinuum.de/search?tag=Podcast]Podcast[/url] #[url=https://horche.demkontinuum.de/search?tag=Kapitalismus]Kapitalismus[/url]
255 [attachment type='link' url='https://wohlstandfueralle.podigee.io/107-urspruengliche-akkumulation' title='Ep. 107: Karl Marx #8 - Die ursprüngliche Akkumulation' publisher_name='Wohlstand für Alle' preview='https://images.podigee-cdn.net/0x,s6LXshYO7uhG23H431B30t4hxj1bQuzlTsUlze0F_-H8=/https://cdn.podigee.com/uploads/u8126/bd5fe4f4-38b7-4f3f-b269-6a0080144635.jpg']Wie der Kapitalismus funktioniert und inwieweit Menschen darin ausgebeutet werden, haben wir bereits besprochen. Immer wieder verweisen wir auch darauf, dass der Kapitalismus nicht immer schon existierte, sondern historisiert werden muss.[/attachment]",
256 'try_oembed' => false,
257 'simpleHtml' => BBCode::TWITTER,
259 'task-10886-deprecate-class' => [
260 'expectedHTML' => '<span class="mastodon emoji"><img src="https://fedi.underscore.world/emoji/custom/custom/heart_nb.png" alt=":heart_nb:" title=":heart_nb:"></span>',
261 'text' => '[emoji=https://fedi.underscore.world/emoji/custom/custom/heart_nb.png]:heart_nb:[/emoji]',
267 * Test convert bbcodes to HTML
269 * @dataProvider dataBBCodes
271 * @param string $expectedHtml Expected HTML output
272 * @param string $text BBCode text
273 * @param bool $try_oembed Whether to convert multimedia BBCode tag
274 * @param int $simpleHtml BBCode::convert method $simple_html parameter value, optional.
275 * @param bool $forPlaintext BBCode::convert method $for_plaintext parameter value, optional.
277 * @throws InternalServerErrorException
279 public function testConvert(string $expectedHtml, string $text, $try_oembed = false, int $simpleHtml = 0, bool $forPlaintext = false)
281 $actual = BBCode::convert($text, $try_oembed, $simpleHtml, $forPlaintext);
283 self::assertEquals($expectedHtml, $actual);
286 public function dataBBCodesToMarkdown()
290 'expected' => '>`>`',
291 'text' => '>[code]>[/code]',
294 'expected' => '<`<`',
295 'text' => '<[code]<[/code]',
298 'expected' => '&`&`',
299 'text' => '&[code]&[/code]',
305 * Test convert bbcodes to Markdown
307 * @dataProvider dataBBCodesToMarkdown
309 * @param string $expected Expected Markdown output
310 * @param string $text BBCode text
311 * @param bool $for_diaspora
313 * @throws InternalServerErrorException
315 public function testToMarkdown(string $expected, string $text, $for_diaspora = false)
317 $actual = BBCode::toMarkdown($text, $for_diaspora);
319 self::assertEquals($expected, $actual);
322 public function dataExpandTags()
325 'bug-10692-non-word' => [
326 '[url=https://github.com/friendica/friendica/blob/2021.09-rc/src/Util/Logger/StreamLogger.php#L160]https://github.com/friendica/friendica/blob/2021.09-rc/src/Util/Logger/StreamLogger.php#L160[/url]',
327 '[url=https://github.com/friendica/friendica/blob/2021.09-rc/src/Util/Logger/StreamLogger.php#L160]https://github.com/friendica/friendica/blob/2021.09-rc/src/Util/Logger/StreamLogger.php#L160[/url]',
329 'bug-10692-start-line' => [
330 '#[url=https://friendica.local/search?tag=L160]L160[/url]',
337 * @dataProvider dataExpandTags
339 * @param string $expected Expected BBCode output
340 * @param string $text Input text
342 public function testExpandTags(string $expected, string $text)
344 $actual = BBCode::expandTags($text);
346 self::assertEquals($expected, $actual);