diff --git a/public/lib/table/classes/base_export_format.php b/public/lib/table/classes/base_export_format.php index bd50af59b29..d07d4a728df 100644 --- a/public/lib/table/classes/base_export_format.php +++ b/public/lib/table/classes/base_export_format.php @@ -80,7 +80,11 @@ class base_export_format { * @param null|int $courseid */ public function format_text($text, $format = FORMAT_MOODLE, $options = null, $courseid = null) { - return html_entity_decode(strip_tags($text), ENT_COMPAT); + // Decode all HTML entities first (e.g. & -> &). + $text = html_entity_decode($text, ENT_COMPAT); + // Detect and remove HTML tags. Unlike strip_tags, this will allow things like '2 > 1' through. + $text = preg_replace('/<\/?[A-Za-z][^>\r\n]*>/', '', $text); + return $text; } /** diff --git a/public/lib/table/tests/tablelib_test.php b/public/lib/table/tests/tablelib_test.php index 5c10a78b318..182b1adc2c6 100644 --- a/public/lib/table/tests/tablelib_test.php +++ b/public/lib/table/tests/tablelib_test.php @@ -687,24 +687,37 @@ final class tablelib_test extends \advanced_testcase { } /** - * Test export in CSV format + * Test export in CSV format. + * + * By default, CSV strips HTML tags. Ensure inputs are exported as expected. */ public function test_table_export(): void { $table = new flexible_table('tablelib_test_export'); $table->define_baseurl('/invalid.php'); - $table->define_columns(['c1', 'c2', 'c3']); - $table->define_headers(['Col1', 'Col2', 'Col3']); + $table->define_columns(['c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8']); + $table->define_headers(['Col1', 'Col2', 'Col3', 'Col4', 'Col5', 'Col6', 'Col7', 'Col8']); ob_start(); $table->is_downloadable(true); $table->is_downloading('csv'); $table->setup(); - $table->add_data(['column0' => 'a', 'column1' => 'b', 'column2' => 'c']); + $table->add_data([ + 'Hello', // Simple string. + '

My

Contents
', // Tags should be removed, leaving the contents. + '2>1', // Mathematical statements should not be misinterpreted as tags. + '3 < 4', // Mathematical statements should not be misinterpreted as tags (with spaces). + 'Tag was here', // Self-closing tag should be removed. + '<span>htmlentities</span>', // HTML entities should be detected too. + "1 2", // Multiline pseudo-mathematical should not be removed. + "removed", // Tag-likes will also be removed. + ]); $output = ob_get_contents(); ob_end_clean(); - $this->assertEquals("Col1,Col2,Col3\na,b,c\n", substr($output, 3)); + $expected = "Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8\n"; + $expected .= "Hello,MyContents,2>1,\"3 < 4\",\"Tag was here\",htmlentities,\"1 2\",removed\n"; + $this->assertEquals($expected, substr($output, 3)); } /**