Merge branch 'MDL-84907-405' of https://github.com/HuongNV13/moodle into MOODLE_405_STABLE

This commit is contained in:
Jun Pataleta 2025-04-04 11:29:05 +08:00
commit ccc4940193
No known key found for this signature in database
GPG Key ID: F83510526D99E2C7
6 changed files with 25 additions and 12 deletions

View File

@ -0,0 +1,6 @@
issueNumber: MDL-84907
notes:
core:
- message: |2
A new method, `core_text::trim_ctrl_chars()`, has been introduced to clean control characters from text. This ensures cleaner input handling and prevents issues caused by invisible or non-printable characters
type: improved

View File

@ -197,8 +197,7 @@ abstract class restore_qtype_plugin extends restore_plugin {
$this->questionanswercacheid = $newquestionid;
// Cache all cleaned answers for a simple text match.
foreach ($answers as $answer) {
// MDL-30018: Clean in the same way as {@link xml_writer::xml_safe_utf8()}.
$clean = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $answer->answer); // Clean CTRL chars.
$clean = core_text::trim_ctrl_chars($answer->answer); // Clean CTRL chars.
$clean = preg_replace("/\r\n|\r/", "\n", $clean); // Normalize line ending.
$this->questionanswercache[$clean] = $answer->id;
}

View File

@ -5356,8 +5356,7 @@ class restore_create_categories_and_questions extends restore_structure_step {
$potentialhints = $DB->get_records('question_hints',
array('questionid' => $newquestionid), '', 'id, hint');
foreach ($potentialhints as $potentialhint) {
// Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
$cleanhint = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $potentialhint->hint); // Clean CTRL chars.
$cleanhint = core_text::trim_ctrl_chars($potentialhint->hint); // Clean CTRL chars.
$cleanhint = preg_replace("/\r\n|\r/", "\n", $cleanhint); // Normalize line ending.
if ($cleanhint === $data->hint) {
$newitemid = $data->id;

View File

@ -253,14 +253,14 @@ class xml_writer {
}
/**
* Perform some UTF-8 cleaning, stripping the control chars (\x0-\x1f)
* but tabs (\x9), newlines (\xa) and returns (\xd). The delete control
* Perform some UTF-8 cleaning, stripping the control chars (\x00-\x1f)
* but tabs (\x09), newlines (\xa) and returns (\xd). The delete control
* char (\x7f) is also included. All them are forbiden in XML 1.0 specs.
* The expression below seems to be UTF-8 safe too because it simply
* ignores the rest of characters. Also normalize linefeeds and return chars.
*/
protected function xml_safe_utf8($content) {
$content = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is', '', $content ?? ''); // clean CTRL chars.
$content = core_text::trim_ctrl_chars($content ?? '');
$content = preg_replace("/\r\n|\r/", "\n", $content); // Normalize line&return=>line
return fix_utf8($content);
}

View File

@ -680,4 +680,16 @@ class core_text {
return mb_convert_case($text, MB_CASE_TITLE, 'UTF-8');
}
/**
* Trims control characters out of a string.
* Example: (\x00-\x1f) and (\x7f)
*
* @param string $text Input string
* @return string Cleaned string value
*/
public static function trim_ctrl_chars(string $text): string {
// Remove control characters text.
return preg_replace('/[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]/i', '', $text);
}
}

View File

@ -157,13 +157,10 @@ class restore_qtype_match_plugin extends restore_qtype_plugin {
$this->questionsubcacheid = $newquestionid;
// Cache all cleaned answers and questiontext.
foreach ($potentialsubs as $potentialsub) {
// Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
$cleanquestion = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is',
'', $potentialsub->questiontext); // Clean CTRL chars.
$cleanquestion = core_text::trim_ctrl_chars($potentialsub->questiontext); // Clean CTRL chars.
$cleanquestion = preg_replace("/\r\n|\r/", "\n", $cleanquestion); // Normalize line ending.
$cleananswer = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is',
'', $potentialsub->answertext); // Clean CTRL chars.
$cleananswer = core_text::trim_ctrl_chars($potentialsub->answertext); // Clean CTRL chars.
$cleananswer = preg_replace("/\r\n|\r/", "\n", $cleananswer); // Normalize line ending.
$this->questionsubcache[$cleanquestion][$cleananswer] = $potentialsub->id;