Merge branch 'MDL-85111-500' of https://github.com/muhammadarnaldo/moodle into MOODLE_500_STABLE
This commit is contained in:
+11
-2
@@ -7838,14 +7838,23 @@ function moodle_setlocale($locale='') {
|
||||
/**
|
||||
* Count words in a string.
|
||||
*
|
||||
* Words are defined as things between whitespace.
|
||||
* Words are defined as things between whitespace. Developments have tried to ensure that this
|
||||
* method gives the same results as Libre Office, MS Word, etc. However, word-counting rules are
|
||||
* subtle, and not identical between languages, so there may be differences in non-English languages.
|
||||
*
|
||||
* @category string
|
||||
* @param string $string The text to be searched for words. May be HTML.
|
||||
* @param int|null $format
|
||||
* @param int|null $format a FORMAT_... constant. In the API this is optional,
|
||||
* but really, it is required to get accurate results, so should be passed.
|
||||
* @return int The count of words in the specified string
|
||||
*/
|
||||
function count_words($string, $format = null) {
|
||||
// If format is plain, remove < characters that are attached to non-HTML words.
|
||||
if ($format === null || $format == FORMAT_PLAIN) {
|
||||
// Remove < that is attached to a word but doesn't form a valid HTML tag.
|
||||
// This matches < followed by word characters that don't have a closing >.
|
||||
$string = preg_replace('/(\w|^|\s)<(?![^<>]*>)(?=\w)/u', '$1', $string);
|
||||
}
|
||||
// Before stripping tags, add a space after the close tag of anything that is not obviously inline.
|
||||
// Also, br is a special case because it definitely delimits a word, but has no close tag.
|
||||
$string = preg_replace('~
|
||||
|
||||
@@ -3897,7 +3897,7 @@ EOF;
|
||||
* @dataProvider count_words_testcases
|
||||
* @param int $expectedcount number of words in $string.
|
||||
* @param string $string the test string to count the words of.
|
||||
* @param int|null $format
|
||||
* @param int|null $format FORMAT_... constant to pass to count_words.
|
||||
*/
|
||||
public function test_count_words(int $expectedcount, string $string, $format = null): void {
|
||||
$this->assertEquals($expectedcount, count_words($string, $format),
|
||||
@@ -3957,8 +3957,12 @@ EOT;
|
||||
[1, '<span>a</span><span>b</span>', FORMAT_HTML],
|
||||
[1, '<span>a</span><span>b</span>', FORMAT_MOODLE],
|
||||
[1, '<span>a</span><span>b</span>', FORMAT_MARKDOWN],
|
||||
[1, 'aa <argh <bleh>pokus</bleh>'],
|
||||
[3, 'aa <argh <bleh>pokus</bleh>'],
|
||||
[2, 'aa <argh <bleh>pokus</bleh>', FORMAT_HTML],
|
||||
[3, 'x < 1', FORMAT_PLAIN],
|
||||
[3, 'quam justo<lectus commodo', FORMAT_PLAIN],
|
||||
[5, 'lorem ipsum< dolor sit amet', FORMAT_PLAIN],
|
||||
[4, 'word starting <less than', FORMAT_PLAIN],
|
||||
[6, $copypasted],
|
||||
[6, $copypasted, FORMAT_PLAIN],
|
||||
[3, $copypasted, FORMAT_HTML],
|
||||
|
||||
@@ -132,7 +132,7 @@ class qtype_essay_question extends question_with_responses {
|
||||
|
||||
// If there is a response and min/max word limit is set in the form then validate the number of words in response.
|
||||
if ($hasinlinetext) {
|
||||
if ($this->check_input_word_count($response['answer'])) {
|
||||
if ($this->check_input_word_count($response['answer'], $response['answerformat'] ?? FORMAT_PLAIN)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,7 @@ class qtype_essay_question extends question_with_responses {
|
||||
if ($this->is_complete_response($response)) {
|
||||
return '';
|
||||
}
|
||||
return $this->check_input_word_count($response['answer']);
|
||||
return $this->check_input_word_count($response['answer'], $response['answerformat'] ?? FORMAT_PLAIN);
|
||||
}
|
||||
|
||||
public function is_gradable_response(array $response) {
|
||||
@@ -262,10 +262,11 @@ class qtype_essay_question extends question_with_responses {
|
||||
* Check the input word count and return a message to user
|
||||
* when the number of words are outside the boundary settings.
|
||||
*
|
||||
* @param string $responsestring
|
||||
* @return string|null
|
||||
* @param string $responsestring the student's response to count the words in.
|
||||
* @param int $responseformat the FORMAT_... constant for what format $responsestring is.
|
||||
* @return string|null null if the word-count is in range, otherwise a string message about how it is not.
|
||||
.*/
|
||||
private function check_input_word_count($responsestring) {
|
||||
private function check_input_word_count(string $responsestring, int $responseformat) {
|
||||
if (!$this->responserequired) {
|
||||
return null;
|
||||
}
|
||||
@@ -275,7 +276,7 @@ class qtype_essay_question extends question_with_responses {
|
||||
}
|
||||
|
||||
// Count the number of words in the response string.
|
||||
$count = count_words($responsestring);
|
||||
$count = count_words($responsestring, $responseformat);
|
||||
if ($this->maxwordlimit && $count > $this->maxwordlimit) {
|
||||
return get_string('maxwordlimitboundary', 'qtype_essay',
|
||||
['limit' => $this->maxwordlimit, 'count' => $count]);
|
||||
@@ -306,7 +307,7 @@ class qtype_essay_question extends question_with_responses {
|
||||
return '';
|
||||
}
|
||||
|
||||
$count = count_words($response['answer']);
|
||||
$count = count_words($response['answer'], $response['answerformat'] ?? FORMAT_PLAIN);
|
||||
if ($this->maxwordlimit && $count > $this->maxwordlimit) {
|
||||
return get_string('wordcounttoomuch', 'qtype_essay',
|
||||
['limit' => $this->maxwordlimit, 'count' => $count]);
|
||||
|
||||
@@ -26,12 +26,12 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for the matching question definition class.
|
||||
* Unit tests for the essay question definition class.
|
||||
*
|
||||
* @package qtype_essay
|
||||
* @copyright 2009 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \qtype_essay_question
|
||||
* @package qtype_essay
|
||||
* @copyright 2009 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \qtype_essay_question
|
||||
*/
|
||||
final class question_test extends \advanced_testcase {
|
||||
public function test_get_question_summary(): void {
|
||||
@@ -401,6 +401,21 @@ final class question_test extends \advanced_testcase {
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a less-than symbol attached to a word ('<1.') in plain text
|
||||
* is handled correctly and doesn't trigger HTML validation errors.
|
||||
*/
|
||||
public function test_get_validation_error_plain_text_with_html_chars(): void {
|
||||
$question = \test_question_maker::make_an_essay_question();
|
||||
$question->responseformat = 'plain';
|
||||
$question->responserequired = 1;
|
||||
$question->minwordlimit = 2;
|
||||
$question->maxwordlimit = 2;
|
||||
|
||||
$response = ['answer' => 'x <1.', 'answerformat' => FORMAT_PLAIN];
|
||||
$this->assertEquals('', $question->get_validation_error($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for get_validation_error test.
|
||||
*
|
||||
@@ -437,6 +452,31 @@ final class question_test extends \advanced_testcase {
|
||||
$this->assertEquals($expected, $question->get_word_count_message_for_review($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test word count calculation when input contains less-than symbols.
|
||||
*
|
||||
* Verifies that words with leading less-than symbols (like '<1.' or '<adjacent')
|
||||
* are counted correctly and don't affect word boundary detection.
|
||||
*/
|
||||
public function test_get_word_count_message_for_review_plain_text_with_html_chars(): void {
|
||||
$question = \test_question_maker::make_an_essay_question();
|
||||
$question->responseformat = 'plain';
|
||||
$question->responserequired = 1;
|
||||
$question->minwordlimit = 2;
|
||||
$question->maxwordlimit = 2;
|
||||
|
||||
$expected = 'Word count: 2';
|
||||
$response = ['answer' => 'x <1.', 'answerformat' => FORMAT_PLAIN];
|
||||
$this->assertEquals($expected, $question->get_word_count_message_for_review($response));
|
||||
|
||||
$expected = 'Word count: 8, more than the limit of 2 words.';
|
||||
$response = [
|
||||
'answer' => 'What about an <adjacent to the following word?',
|
||||
'answerformat' => FORMAT_PLAIN,
|
||||
];
|
||||
$this->assertEquals($expected, $question->get_word_count_message_for_review($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_get_word_count_message_for_review.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user