Merge branch 'MDL-70094-310' of https://github.com/SangNguyen2601/moodle into MOODLE_...
[moodle.git] / question / type / gapselect / rendererbase.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Base class for rendering question types like this one.
19  *
20  * @package    qtype_gapselect
21  * @copyright  2011 The Open University
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
26 defined('MOODLE_INTERNAL') || die();
29 /**
30  * Renders question types where the question includes embedded interactive elements.
31  *
32  * @copyright  2011 The Open University
33  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34  */
35 abstract class qtype_elements_embedded_in_question_text_renderer
36         extends qtype_with_combined_feedback_renderer {
37     public function formulation_and_controls(question_attempt $qa,
38             question_display_options $options) {
40         $question = $qa->get_question();
42         $questiontext = '';
43         // Glue question fragments together using unique placeholders, apply format_text to the result
44         // and then substitute each placeholder with the embedded element.
45         // This will ensure that format_text() is applied to the whole question but not to the embedded elements.
46         $placeholders = $this->get_fragments_glue_placeholders($question->textfragments);
47         foreach ($question->textfragments as $i => $fragment) {
48             if ($i > 0) {
49                 $questiontext .= $placeholders[$i];
50                 // There is a preg_replace 11 lines ahead where the $embeddedelements is used as the replace.
51                 // If there are currency like options ($4) in the select then the preg_replace treats them as backreferences.
52                 // So we need to escape the backreferences here.
53                 $embeddedelements[$placeholders[$i]] =
54                         preg_replace('/\$(\d)/', '\\\$$1', $this->embedded_element($qa, $i, $options));
55             }
56             $questiontext .= $fragment;
57         }
58         $questiontext = $question->format_text($questiontext,
59             $question->questiontextformat, $qa, 'question', 'questiontext', $question->id);
60         foreach ($placeholders as $i => $placeholder) {
61             $questiontext = preg_replace('/'. preg_quote($placeholder, '/') . '/',
62                 $embeddedelements[$placeholder], $questiontext);
63         }
65         $result = '';
66         $result .= html_writer::tag('div', $questiontext, array('class' => 'qtext'));
68         $result .= $this->post_qtext_elements($qa, $options);
70         if ($qa->get_state() == question_state::$invalid) {
71             $result .= html_writer::nonempty_tag('div',
72                     $question->get_validation_error($qa->get_last_qt_data()),
73                     array('class' => 'validationerror'));
74         }
76         return $result;
77     }
79     /**
80      * Find strings that we can use to glue the fragments with
81      *
82      * These strings have to be all different and neither of them can be present in the text
83      *
84      * @param array $fragments
85      * @return array array with indexes from 1 to count($fragments)-1
86      */
87     protected function get_fragments_glue_placeholders($fragments) {
88         $fragmentscount = count($fragments);
89         if ($fragmentscount <= 1) {
90             return [];
91         }
92         $prefix = '[[$';
93         $postfix = ']]';
94         $text = join('', $fragments);
95         while (preg_match('/' . preg_quote($prefix, '/') . '\\d+' . preg_quote($postfix, '/') . '/', $text)) {
96             $prefix .= '$';
97         }
98         $glues = [];
99         for ($i = 1; $i < $fragmentscount; $i++) {
100             $glues[$i] = $prefix . $i . $postfix;
101         }
102         return $glues;
103     }
105     protected function qtext_id($qa) {
106         return str_replace(':', '_', $qa->get_qt_field_name(''));
107     }
109     protected abstract function embedded_element(question_attempt $qa, $place,
110             question_display_options $options);
112     protected function post_qtext_elements(question_attempt $qa,
113             question_display_options $options) {
114         return '';
115     }
117     protected function box_id(question_attempt $qa, $place) {
118         return str_replace(':', '_', $qa->get_qt_field_name($place));
119     }
121     public function specific_feedback(question_attempt $qa) {
122         return $this->combined_feedback($qa);
123     }
125     public function correct_response(question_attempt $qa) {
126         $question = $qa->get_question();
128         $correctanswer = '';
129         foreach ($question->textfragments as $i => $fragment) {
130             if ($i > 0) {
131                 $group = $question->places[$i];
132                 $choice = $question->choices[$group][$question->rightchoices[$i]];
133                 $correctanswer .= '[' . str_replace('-', '&#x2011;',
134                         $choice->text) . ']';
135             }
136             $correctanswer .= $fragment;
137         }
139         if (!empty($correctanswer)) {
140             return get_string('correctansweris', 'qtype_gapselect',
141                     $question->format_text($correctanswer, $question->questiontextformat,
142                             $qa, 'question', 'questiontext', $question->id));
143         }
144     }