diff --git a/lib/tests/weblib_format_text_test.php b/lib/tests/weblib_format_text_test.php index deecb6fd94377..a7aa3bfdfe5ae 100644 --- a/lib/tests/weblib_format_text_test.php +++ b/lib/tests/weblib_format_text_test.php @@ -95,4 +95,56 @@ public function test_format_text_overflowdiv() { $this->assertEquals('

:-)

', format_text('

:-)

', FORMAT_HTML, array('overflowdiv' => true))); } + + /** + * Test adding blank target attribute to links + * + * @dataProvider format_text_blanktarget_testcases + * @param string $link The link to add target="_blank" to + * @param string $expected The expected filter value + */ + public function test_format_text_blanktarget($link, $expected) { + $actual = format_text($link, FORMAT_MOODLE, array('blanktarget' => true, 'filter' => false, 'noclean' => true)); + $this->assertEquals($expected, $actual); + } + + /** + * Data provider for the test_format_text_blanktarget testcase + * + * @return array of testcases + */ + public function format_text_blanktarget_testcases() { + return [ + 'Simple link' => + [ + 'Hey, that\'s pretty good!', + '
Hey, that\'s pretty good!
' + ], + 'Link with rel' => + [ + 'Hey, that\'s pretty good!', + '
Hey, that\'s pretty good!
' + ], + 'Link with rel noreferrer' => + [ + 'Hey, that\'s pretty good!', + '
Hey, that\'s pretty good!
' + ], + 'Link with target' => + [ + 'Hey, that\'s pretty good!', + '
' . + 'Hey, that\'s pretty good!
' + ], + 'Link with target blank' => + [ + 'Hey, that\'s pretty good!', + '
Hey, that\'s pretty good!
' + ] + ]; + } } diff --git a/lib/weblib.php b/lib/weblib.php index fe5b2c3551500..b1a9732df92c4 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1175,6 +1175,7 @@ function format_text_menu() { * with the class no-overflow before being returned. Default false. * allowid : If true then id attributes will not be removed, even when * using htmlpurifier. Default false. + * blanktarget : If true all tags will have target="_blank" added unless target is explicitly specified. * * * @staticvar array $croncache @@ -1222,6 +1223,7 @@ function format_text($text, $format = FORMAT_MOODLE, $options = null, $courseidd if (!isset($options['overflowdiv'])) { $options['overflowdiv'] = false; } + $options['blanktarget'] = !empty($options['blanktarget']); // Calculate best context. if (empty($CFG->version) or $CFG->version < 2013051400 or during_initial_install()) { @@ -1318,6 +1320,26 @@ function format_text($text, $format = FORMAT_MOODLE, $options = null, $courseidd $text = html_writer::tag('div', $text, array('class' => 'no-overflow')); } + if ($options['blanktarget']) { + $domdoc = new DOMDocument(); + $domdoc->loadHTML($text); + foreach ($domdoc->getElementsByTagName('a') as $link) { + if ($link->hasAttribute('target') && strpos($link->getAttribute('target'), '_blank') === false) { + continue; + } + $link->setAttribute('target', '_blank'); + if (strpos($link->getAttribute('rel'), 'noreferrer') === false) { + $link->setAttribute('rel', trim($link->getAttribute('rel') . ' noreferrer')); + } + } + + // This regex is nasty and I don't like it. The correct way to solve this is by loading the HTML like so: + // $domdoc->loadHTML($text, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); however it seems like the libxml + // version that travis uses doesn't work properly and ends up leaving , so I'm forced to use + // this regex to remove those tags. + $text = trim(preg_replace('~<(?:!DOCTYPE|/?(?:html|body))[^>]*>\s*~i', '', $domdoc->saveHTML())); + } + return $text; }