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. + '
Tag was here', // Self-closing tag should be removed.
+ '<span>htmlentities</span>', // HTML entities should be detected too.
+ "1