Merge branch 'MDL-86559-501-test2' of https://github.com/andimendunia/moodle into MOODLE_501_STABLE

This commit is contained in:
Huong Nguyen
2025-10-21 09:39:21 +07:00
2 changed files with 95 additions and 3 deletions
@@ -54,12 +54,36 @@ class text_filter extends \core_filters\text_filter {
}
if ($filterslist) {
return $text = filter_phrases($text, $filterslist);
// Modify each filter's regex pattern to match any whitespace sequence where there are spaces.
// This allows matching activity names regardless of whether users type single spaces, double spaces,
// or non-breaking spaces (which HTML editors often insert).
$filterslist = filter_prepare_phrases_for_filtering($filterslist);
foreach ($filterslist as $filterobject) {
if ($filterobject->workregexp !== null) {
$filterobject->workregexp = self::replace_spaces_with_whitespace($filterobject->workregexp);
}
}
return filter_phrases($text, $filterslist, null, null, false, true);
} else {
return $text;
}
}
/**
* Replace literal spaces in a regex with general whitespace match.
*
* @param string $regex The regex pattern containing literal spaces.
* @return string The regex pattern with spaces replaced.
*/
protected static function replace_spaces_with_whitespace($regex): string {
return preg_replace_callback('/ +/', function($matches): string {
$count = strlen($matches[0]);
// Matches regular space, non-breaking space (U+00A0), or other whitespace.
return '(?:[\s\xC2\xA0]{' . $count . '})';
}, $regex);
}
/**
* Get all the cached activity list for a course
*
@@ -115,6 +139,9 @@ class text_filter extends \core_filters\text_filter {
foreach ($sortedactivities as $cm) {
$title = s(trim(strip_tags($cm->name)));
$currentname = trim($cm->name);
// Normalize whitespace in activity names to handle double spaces and non-breaking spaces.
// This ensures that activities with multiple consecutive spaces can still be matched
// even when users type the name with different whitespace (e.g., single space, NBSP).
$entitisedname = s($currentname);
// Avoid empty or unlinkable activity names.
if (!empty($title)) {
@@ -133,4 +160,4 @@ class text_filter extends \core_filters\text_filter {
}
return $activitylist;
}
}
}
@@ -102,6 +102,71 @@ final class text_filter_test extends \advanced_testcase {
$this->assertEquals($page->name, $matches[3][0]);
}
/**
* Data provider for the test_links_with_whitespace.
*
* @return array
*/
public static function links_with_whitespace_provider(): array {
return [
'Regular spaces' => [
'Assignment 1',
'<p>Go to Assignment 1</p>',
true,
],
'Two regular spaces' => [
'Assignment 1',
'<p>Go to Assignment 1</p>',
true,
],
'NBSP + regular spaces' => [
'Assignment 1',
"<p>Go to Assignment\xC2\xA0 1</p>",
true,
],
'Multiple spaces' => [
'Assignment 1 - History',
'<p>Go to Assignment 1 - History</p>',
true,
],
'Mismatched spaces 1' => [
'Assignment 1',
'<p>Go to Assignment 1</p>',
false,
],
'Mismatched spaces 2' => [
'Assignment 1',
'<p>Go to Assignment 1</p>',
false,
],
];
}
/**
* Test that links can be matched with various whitespace combinations.
*
* @dataProvider links_with_whitespace_provider
* @param string $activityname
* @param string $html
* @param bool $expectedresult
*/
public function test_links_with_whitespace(string $activityname, string $html, bool $expectedresult): void {
$this->resetAfterTest(true);
$course = $this->getDataGenerator()->create_course();
$context = \context_course::instance($course->id);
$this->getDataGenerator()->create_module(
'page',
['course' => $course->id, 'name' => $activityname]
);
$filtered = format_text($html, FORMAT_HTML, ['context' => $context]);
$haslink = strpos($filtered, '<a class="autolink"') !== false;
$this->assertEquals($expectedresult, $haslink);
}
public function test_cache(): void {
$this->resetAfterTest(true);
@@ -166,4 +231,4 @@ final class text_filter_test extends \advanced_testcase {
$this->assertEquals($page1->name, $matches[1][0]);
$this->assertEquals($page2->name, $matches[1][1]);
}
}
}